Skip to content

Feat: transition to @vr_patel/tui Spinner#17

Merged
utkarsh232005 merged 7 commits into
KDM-cli:mainfrom
utkarsh232005:feat/tui-spinner
May 14, 2026
Merged

Feat: transition to @vr_patel/tui Spinner#17
utkarsh232005 merged 7 commits into
KDM-cli:mainfrom
utkarsh232005:feat/tui-spinner

Conversation

@utkarsh232005
Copy link
Copy Markdown
Member

@utkarsh232005 utkarsh232005 commented May 14, 2026

This PR replaces the ora-based spinner with the Spinner from @vr_patel/tui.
Key changes:

  • Centralized spinner utility updated to use the new library.
  • Updated show commands to utilize stop(), fail(), and warn() with descriptive messages.
  • Added loading spinners to the root command (connection check), health command, and logs command.
  • Removed the deprecated ora dependency.

Summary by CodeRabbit

  • New Features

    • Loading spinners added across startup and several commands to show progress and outcomes.
  • Bug Fixes / UX

    • Command outputs include clearer success, failure and warning messages; error reporting now surfaces additional error details.
  • Tests

    • Test suite updated to mock spinner behavior for deterministic runs.
  • Chores

    • Dependency updates: spinner implementation swapped and React added; CI checkout depth adjusted.

Review Change Stack

Copilot AI review requested due to automatic review settings May 14, 2026 08:25
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 14, 2026

Caution

Review failed

Pull request was closed or merged during review

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: db096b33-3587-4832-8814-e0ae20391191

📥 Commits

Reviewing files that changed from the base of the PR and between 9edfed1 and 514514a.

📒 Files selected for processing (4)
  • .github/workflows/publish.yml
  • src/commands/health.ts
  • src/commands/logs.ts
  • src/utils/logger.ts

📝 Walkthrough

Walkthrough

Replace ora with @vr_patel/tui Spinner, wire spinner lifecycle (start/stop/fail/warn) into CLI commands (root, health, logs, show), update logger.error, tweak CI checkout, and mock the spinner API in tests.

Changes

Spinner Migration and CLI Integration

Layer / File(s) Summary
Spinner library replacement
src/ui/spinner.ts, package.json, .github/workflows/publish.yml
createSpinner now constructs a Spinner from @vr_patel/tui with { text, style: 'dots' }; ora removed, react added; checkout step uses fetch-depth: 0 and removed GH_PAT token.
Async spinner integration in health, logs, and root commands
src/commands/health.ts, src/commands/logs.ts, src/commands/root.ts, src/utils/logger.ts
Health and logs command handlers converted to async with spinner start/stop/fail wrapping; root command wraps parallel connection checks in a spinner and fails it on errors; logger.error accepts an optional error parameter and prints it.
Status messages and partial-failure handling in show command
src/commands/show.ts
showContainers/showPods emit specific success/fail messages; showRunners uses Promise.allSettled to warn on partial failures; showMinikube gains try/catch and explicit fail/success spinner messages.
Test mocks for spinner integration
src/__tests__/health.test.ts, src/__tests__/logs.test.ts, src/__tests__/show.test.ts
Mock createSpinner to return chainable start, stop, fail, warn, info, and update methods so tests run deterministically without a real spinner.

Sequence Diagram

sequenceDiagram
  participant CLI
  participant HealthCmd as healthCommand
  participant LogsCmd as logsCommand
  participant SpinnerFactory as createSpinner
  participant TUI_Spinner as Spinner@vr_patel/tui
  participant Logger as logger

  CLI->>HealthCmd: run health <target>
  HealthCmd->>SpinnerFactory: createSpinner("Checking health...")
  SpinnerFactory->>TUI_Spinner: returns Spinner instance
  HealthCmd->>TUI_Spinner: spinner.start()
  HealthCmd->>HealthCmd: perform health checks
  HealthCmd-->>TUI_Spinner: spinner.stop("Health fetched")
  HealthCmd->>Logger: logger.info(...)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A spinner spins, ora hops away,
New TUI dots guide each command's play,
Start, stop, fail — the CLI sings along,
Tests mock the dance to keep checks strong,
✨🌀

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: migrating from the ora spinner library to @vr_patel/tui's Spinner component, which aligns with the substantial refactoring across the codebase.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the CLI’s user feedback by replacing the existing ora spinner usage with the Spinner from @vr_patel/tui, and wires spinner lifecycle messages into several commands.

Changes:

  • Replaced the centralized spinner helper to construct @vr_patel/tui’s Spinner and removed the ora dependency.
  • Updated show flows to stop/fail/warn spinners with descriptive messages.
  • Added spinners to the root (connection check), health, and logs commands.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/ui/spinner.ts Switches spinner factory from ora to @vr_patel/tui Spinner.
src/commands/show.ts Updates spinner stop/fail/warn messaging across show subcommands.
src/commands/root.ts Adds a spinner around initial connection checks when no args are provided.
src/commands/logs.ts Adds spinner to logs command, but also introduces an artificial delay.
src/commands/health.ts Adds spinner to health command, but also introduces an artificial delay.
src/tests/show.test.ts Extends spinner mock with additional methods used by updated show flows.
src/tests/logs.test.ts Adds spinner mock for logs tests.
src/tests/health.test.ts Adds spinner mock for health tests.
package.json Removes ora from dependencies.
package-lock.json Removes ora (and transitive deps) from the lockfile.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/commands/logs.ts Outdated
Comment on lines 11 to 14
// Simulate some work
await new Promise(resolve => setTimeout(resolve, 500));
spinner.stop(`Logs for ${name} fetched`);
logger.info(`Showing logs for ${name}...`);
Comment thread src/commands/health.ts Outdated
Comment on lines 11 to 14
// Simulate some work
await new Promise(resolve => setTimeout(resolve, 1000));
spinner.stop(`Health check for ${target} complete`);
logger.info(`Showing health for ${target}...`);
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/commands/show.ts (1)

134-160: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add error handling to ensure spinner cleanup in showMinikube.

If checkMinikubeConnection() (line 136) or getMinikubeStatus() (line 142) throw, the spinner will not stop, leaving the terminal in an inconsistent state.

🛡️ Proposed fix to add try-catch wrapper
 const showMinikube = async () => {
   const spinner = createSpinner('Fetching Minikube status...').start();
-  const conn = await checkMinikubeConnection();
-  if (!conn.installed) {
-    spinner.fail('Minikube is not installed on this system');
-    return;
-  }
-  
-  const statusList = await getMinikubeStatus();
-  spinner.stop('Minikube status fetched successfully');
+  try {
+    const conn = await checkMinikubeConnection();
+    if (!conn.installed) {
+      spinner.fail('Minikube is not installed on this system');
+      return;
+    }
+    
+    const statusList = await getMinikubeStatus();
+    spinner.stop('Minikube status fetched successfully');
 
-  if (statusList.length === 0) {
-    logger.warn('No Minikube profiles found or status is unknown.');
-    return;
-  }
+    if (statusList.length === 0) {
+      logger.warn('No Minikube profiles found or status is unknown.');
+      return;
+    }
 
-  renderTable({
-    head: ['NAME', 'HOST', 'KUBELET', 'APISERVER', 'MESSAGE'],
-    rows: statusList.map((s) => [
-      s.Name || '-',
-      s.Host === 'Running' ? chalk.green(s.Host) : (s.Host === 'Stopped' ? chalk.red(s.Host) : chalk.yellow(s.Host || '-')),
-      s.Kubelet === 'Running' ? chalk.green(s.Kubelet) : chalk.yellow(s.Kubelet || '-'),
-      s.APIServer === 'Running' ? chalk.green(s.APIServer) : chalk.yellow(s.APIServer || '-'),
-      s.Message || '-',
-    ]),
-  });
+    renderTable({
+      head: ['NAME', 'HOST', 'KUBELET', 'APISERVER', 'MESSAGE'],
+      rows: statusList.map((s) => [
+        s.Name || '-',
+        s.Host === 'Running' ? chalk.green(s.Host) : (s.Host === 'Stopped' ? chalk.red(s.Host) : chalk.yellow(s.Host || '-')),
+        s.Kubelet === 'Running' ? chalk.green(s.Kubelet) : chalk.yellow(s.Kubelet || '-'),
+        s.APIServer === 'Running' ? chalk.green(s.APIServer) : chalk.yellow(s.APIServer || '-'),
+        s.Message || '-',
+      ]),
+    });
+  } catch (error) {
+    spinner.fail('Failed to fetch Minikube status');
+    logger.error('Error fetching Minikube status', error);
+  }
 };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/show.ts` around lines 134 - 160, The showMinikube function
currently starts a spinner but does not guarantee it is stopped if
checkMinikubeConnection or getMinikubeStatus throws; wrap the main logic of
showMinikube in a try/catch/finally so any thrown error is caught, call
spinner.fail(...) in the catch with a helpful message plus rethrow or handle as
appropriate, and ensure spinner.stop() or spinner.clear() is called in the
finally block to always clean up; reference the spinner variable and the calls
to checkMinikubeConnection and getMinikubeStatus when making this change.
🧹 Nitpick comments (2)
src/commands/health.ts (1)

9-15: ⚡ Quick win

Consider adding error handling around future real health check logic.

When the simulated work (lines 11-12) is replaced with actual health checks, wrap that logic in try-catch to ensure spinner.fail(...) is called on errors, preventing the terminal from being left in an inconsistent state.

♻️ Example structure for future implementation
     .action(async (target) => {
       const spinner = createSpinner(`Checking health for ${target}...`).start();
-      // Simulate some work
-      await new Promise(resolve => setTimeout(resolve, 1000));
-      spinner.stop(`Health check for ${target} complete`);
-      logger.info(`Showing health for ${target}...`);
+      try {
+        // TODO: Replace with actual health check logic
+        await new Promise(resolve => setTimeout(resolve, 1000));
+        spinner.stop(`Health check for ${target} complete`);
+        logger.info(`Showing health for ${target}...`);
+      } catch (error) {
+        spinner.fail(`Health check for ${target} failed`);
+        logger.error(`Error checking health for ${target}`, error);
+      }
     });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/health.ts` around lines 9 - 15, The action handler’s simulated
work should be wrapped in a try-catch so future real health check code does not
leave the spinner in a bad state; inside the .action(async (target) => { ... })
block, wrap the async health-check logic (currently the setTimeout) in try { ...
} finally/ catch { ... } so on success you call spinner.stop(`Health check for
${target} complete`) and on error you call spinner.fail(`Health check for
${target} failed: <error message>`) (use spinner, createSpinner, target, and
logger.info/logger.error) and rethrow or log the error as appropriate.
src/commands/logs.ts (1)

9-16: ⚡ Quick win

Consider adding error handling around future real log-fetching logic.

When the simulated work (lines 11-12) is replaced with actual log fetching, wrap that logic in try-catch to ensure spinner.fail(...) is called on errors, preventing the terminal from being left in an inconsistent state.

♻️ Example structure for future implementation
     .action(async (name) => {
       const spinner = createSpinner(`Fetching logs for ${name}...`).start();
-      // Simulate some work
-      await new Promise(resolve => setTimeout(resolve, 500));
-      spinner.stop(`Logs for ${name} fetched`);
-      logger.info(`Showing logs for ${name}...`);
+      try {
+        // TODO: Replace with actual log fetching logic
+        await new Promise(resolve => setTimeout(resolve, 500));
+        spinner.stop(`Logs for ${name} fetched`);
+        logger.info(`Showing logs for ${name}...`);
+      } catch (error) {
+        spinner.fail(`Failed to fetch logs for ${name}`);
+        logger.error(`Error fetching logs for ${name}`, error);
+      }
     });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/logs.ts` around lines 9 - 16, The action handler passed to
.action (the async (name) => { ... }) should wrap the real log-fetching work in
a try/catch: call createSpinner(...) and start it, perform the fetch inside try
and call spinner.stop(...) and logger.info(...) on success, and in catch call
spinner.fail(...) and logger.error(...) (optionally rethrow or return a non-zero
exit code). Ensure spinner.fail is always invoked on errors to avoid leaving the
terminal spinner in an inconsistent state and reference the existing
createSpinner, spinner.stop, spinner.fail, and logger.info/logger.error symbols
when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/commands/root.ts`:
- Around line 31-37: Wrap the Promise.all call that invokes
checkDockerConnection, checkK8sConnection, and checkMinikubeConnection in
try/catch/finally so the spinner created by createSpinner('Checking
connections...') is always stopped; call spinner.stop() (or spinner.fail() on
error) inside finally to guarantee cleanup, handle or rethrow the caught error
after stopping the spinner to preserve existing error behavior, and ensure
references to spinner and the three functions (checkDockerConnection,
checkK8sConnection, checkMinikubeConnection) are used so the cleanup executes
regardless of exceptions.

In `@src/ui/spinner.ts`:
- Around line 1-10: The Spinner constructor call in createSpinner currently
passes an unsupported color property; remove the color field so the Spinner is
instantiated with only the supported options (text and style). Locate the
createSpinner function and update the new Spinner({...}) invocation to omit
color (keep text and style as used by Spinner from '@vr_patel/tui') so
construction matches the v1.0.0 API.

---

Outside diff comments:
In `@src/commands/show.ts`:
- Around line 134-160: The showMinikube function currently starts a spinner but
does not guarantee it is stopped if checkMinikubeConnection or getMinikubeStatus
throws; wrap the main logic of showMinikube in a try/catch/finally so any thrown
error is caught, call spinner.fail(...) in the catch with a helpful message plus
rethrow or handle as appropriate, and ensure spinner.stop() or spinner.clear()
is called in the finally block to always clean up; reference the spinner
variable and the calls to checkMinikubeConnection and getMinikubeStatus when
making this change.

---

Nitpick comments:
In `@src/commands/health.ts`:
- Around line 9-15: The action handler’s simulated work should be wrapped in a
try-catch so future real health check code does not leave the spinner in a bad
state; inside the .action(async (target) => { ... }) block, wrap the async
health-check logic (currently the setTimeout) in try { ... } finally/ catch {
... } so on success you call spinner.stop(`Health check for ${target} complete`)
and on error you call spinner.fail(`Health check for ${target} failed: <error
message>`) (use spinner, createSpinner, target, and logger.info/logger.error)
and rethrow or log the error as appropriate.

In `@src/commands/logs.ts`:
- Around line 9-16: The action handler passed to .action (the async (name) => {
... }) should wrap the real log-fetching work in a try/catch: call
createSpinner(...) and start it, perform the fetch inside try and call
spinner.stop(...) and logger.info(...) on success, and in catch call
spinner.fail(...) and logger.error(...) (optionally rethrow or return a non-zero
exit code). Ensure spinner.fail is always invoked on errors to avoid leaving the
terminal spinner in an inconsistent state and reference the existing
createSpinner, spinner.stop, spinner.fail, and logger.info/logger.error symbols
when making the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: be3d24d6-1c60-4bcc-93d3-1957f811c620

📥 Commits

Reviewing files that changed from the base of the PR and between 6d59819 and 13ebe0d.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (9)
  • package.json
  • src/__tests__/health.test.ts
  • src/__tests__/logs.test.ts
  • src/__tests__/show.test.ts
  • src/commands/health.ts
  • src/commands/logs.ts
  • src/commands/root.ts
  • src/commands/show.ts
  • src/ui/spinner.ts
💤 Files with no reviewable changes (1)
  • package.json

Comment thread src/commands/root.ts Outdated
Comment thread src/ui/spinner.ts
Copilot AI review requested due to automatic review settings May 14, 2026 08:34
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 11 changed files in this pull request and generated 2 comments.

Comment thread src/commands/logs.ts
Comment on lines +9 to +13
.action(async (name) => {
const spinner = createSpinner(`Fetching logs for ${name}...`).start();
try {
// Spinner should be driven by actual work here in the future
spinner.stop(`Logs for ${name} fetched`);
Comment thread src/commands/health.ts
Comment on lines +9 to +13
.action(async (target) => {
const spinner = createSpinner(`Checking health for ${target}...`).start();
try {
// Spinner should be driven by actual work here in the future
spinner.stop(`Health check for ${target} complete`);
Copilot AI review requested due to automatic review settings May 14, 2026 08:42
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/__tests__/show.test.ts (1)

22-31: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Consider asserting spinner lifecycle methods in tests.

The mock now supports fail, warn, info, and update, but the test cases don't verify these methods are called. For example, in the test at lines 41-52, you could assert:

const { createSpinner } = await import('../ui/spinner');
expect(createSpinner).toHaveBeenCalled();
const mockSpinner = (createSpinner as any).mock.results[0].value;
expect(mockSpinner.warn).toHaveBeenCalledWith('Some runners could not be fetched');

This ensures the spinner UX is maintained as the command evolves.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/__tests__/show.test.ts` around lines 22 - 31, Add assertions in the tests
that exercise the spinner to verify its lifecycle methods are invoked: import or
access the mocked createSpinner function (createSpinner) used in the failing
test, check expect(createSpinner).toHaveBeenCalled(), grab the mock spinner
instance via createSpinner.mock.results[0].value and assert its methods (warn,
fail, info, update, stop) were called with the expected messages where
applicable; update the test at the shown location to include those expect(...)
checks so spinner UX behavior is validated alongside existing assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/__tests__/health.test.ts`:
- Around line 12-17: The spinner mock created in the test only implements start
and stop but the health command calls spinner.fail() on errors; update the mock
returned by createSpinner in src/__tests__/health.test.ts to also include a fail
method (e.g., vi.fn().mockReturnThis()) so tests exercising the error path won't
throw due to a missing method; ensure the mock's createSpinner remains a vi.fn
and consistently returns an object with start, stop, and fail that each return
this.

In `@src/__tests__/logs.test.ts`:
- Around line 12-17: The spinner mock in the test only implements start and stop
but the logs command uses spinner.fail() in its catch path; update the vi.mock
for createSpinner in src/__tests__/logs.test.ts to include a fail method (e.g.,
fail: vi.fn().mockReturnThis()) so the mock supports createSpinner().start(),
.stop(), and .fail(), ensuring the error-path in the logs command can be
exercised without throwing undefined method errors.

In `@src/commands/root.ts`:
- Around line 67-72: The finally block always calls process.exit(0) regardless
of errors; change the control flow to track failure and exit with non-zero on
errors: introduce a boolean flag (e.g., hadError) initialized false, set
hadError = true inside the catch where spinner.fail is called for connection
check failures, and in the finally replace process.exit(0) with
process.exit(hadError ? 1 : 0) (or otherwise return/throw based on hadError) so
program.outputHelp() still runs but the process exits non‑zero when the catch
was triggered.

---

Outside diff comments:
In `@src/__tests__/show.test.ts`:
- Around line 22-31: Add assertions in the tests that exercise the spinner to
verify its lifecycle methods are invoked: import or access the mocked
createSpinner function (createSpinner) used in the failing test, check
expect(createSpinner).toHaveBeenCalled(), grab the mock spinner instance via
createSpinner.mock.results[0].value and assert its methods (warn, fail, info,
update, stop) were called with the expected messages where applicable; update
the test at the shown location to include those expect(...) checks so spinner UX
behavior is validated alongside existing assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 91e9ee3f-f1f8-4912-8d14-5b2729cd3fbc

📥 Commits

Reviewing files that changed from the base of the PR and between c23f9d8 and 02cb101.

⛔ Files ignored due to path filters (3)
  • .coderabbit.yaml is excluded by none and included by none
  • package-lock.json is excluded by !**/package-lock.json, !package-lock.json and included by none
  • package.json is excluded by none and included by none
📒 Files selected for processing (8)
  • src/__tests__/health.test.ts
  • src/__tests__/logs.test.ts
  • src/__tests__/show.test.ts
  • src/commands/health.ts
  • src/commands/logs.ts
  • src/commands/root.ts
  • src/commands/show.ts
  • src/ui/spinner.ts

Comment on lines +12 to +17
vi.mock('../ui/spinner', () => ({
createSpinner: vi.fn(() => ({
start: vi.fn().mockReturnThis(),
stop: vi.fn().mockReturnThis(),
})),
}));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add fail method to spinner mock for error-path coverage.

The health command uses spinner.fail() in its catch block (line 16 in health.ts), but the mock only provides start and stop. If you add error-path tests, the mock will be incomplete.

♻️ Extend the mock
 vi.mock('../ui/spinner', () => ({
   createSpinner: vi.fn(() => ({
     start: vi.fn().mockReturnThis(),
     stop: vi.fn().mockReturnThis(),
+    fail: vi.fn().mockReturnThis(),
   })),
 }));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/__tests__/health.test.ts` around lines 12 - 17, The spinner mock created
in the test only implements start and stop but the health command calls
spinner.fail() on errors; update the mock returned by createSpinner in
src/__tests__/health.test.ts to also include a fail method (e.g.,
vi.fn().mockReturnThis()) so tests exercising the error path won't throw due to
a missing method; ensure the mock's createSpinner remains a vi.fn and
consistently returns an object with start, stop, and fail that each return this.

Comment on lines +12 to +17
vi.mock('../ui/spinner', () => ({
createSpinner: vi.fn(() => ({
start: vi.fn().mockReturnThis(),
stop: vi.fn().mockReturnThis(),
})),
}));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add fail method to spinner mock for error-path coverage.

The logs command uses spinner.fail() in its catch block (line 16 in logs.ts), but the mock only provides start and stop.

♻️ Extend the mock
 vi.mock('../ui/spinner', () => ({
   createSpinner: vi.fn(() => ({
     start: vi.fn().mockReturnThis(),
     stop: vi.fn().mockReturnThis(),
+    fail: vi.fn().mockReturnThis(),
   })),
 }));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
vi.mock('../ui/spinner', () => ({
createSpinner: vi.fn(() => ({
start: vi.fn().mockReturnThis(),
stop: vi.fn().mockReturnThis(),
})),
}));
vi.mock('../ui/spinner', () => ({
createSpinner: vi.fn(() => ({
start: vi.fn().mockReturnThis(),
stop: vi.fn().mockReturnThis(),
fail: vi.fn().mockReturnThis(),
})),
}));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/__tests__/logs.test.ts` around lines 12 - 17, The spinner mock in the
test only implements start and stop but the logs command uses spinner.fail() in
its catch path; update the vi.mock for createSpinner in
src/__tests__/logs.test.ts to include a fail method (e.g., fail:
vi.fn().mockReturnThis()) so the mock supports createSpinner().start(), .stop(),
and .fail(), ensuring the error-path in the logs command can be exercised
without throwing undefined method errors.

Comment thread src/commands/root.ts
Comment on lines +67 to +72
} catch (error) {
spinner.fail(`Connection check failed: ${(error as Error).message}`);
} finally {
program.outputHelp();
process.exit(0);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Exit with non-zero code on connection failure.

The finally block always exits with code 0 (line 71), even when connection checks fail. This breaks CI/CD pipelines and scripts that rely on exit codes to detect failures.

🛡️ Track failure state and exit appropriately
     console.log(`  kdm show runners\n  kdm health all\n  kdm watch\n  kdm logs <name>\n`);
   } catch (error) {
     spinner.fail(`Connection check failed: ${(error as Error).message}`);
+    program.outputHelp();
+    process.exit(1);
   } finally {
-    program.outputHelp();
-    process.exit(0);
+    // Help already shown in try or catch
   }
 }

Alternatively, use a flag:

+  let hasError = false;
   try {
     // ... existing code ...
   } catch (error) {
     spinner.fail(`Connection check failed: ${(error as Error).message}`);
+    hasError = true;
   } finally {
     program.outputHelp();
-    process.exit(0);
+    process.exit(hasError ? 1 : 0);
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/root.ts` around lines 67 - 72, The finally block always calls
process.exit(0) regardless of errors; change the control flow to track failure
and exit with non-zero on errors: introduce a boolean flag (e.g., hadError)
initialized false, set hadError = true inside the catch where spinner.fail is
called for connection check failures, and in the finally replace process.exit(0)
with process.exit(hadError ? 1 : 0) (or otherwise return/throw based on
hadError) so program.outputHelp() still runs but the process exits non‑zero when
the catch was triggered.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/commands/health.ts`:
- Around line 15-17: The catch block that currently calls spinner.fail(`Health
check for ${target} failed: ${(error as Error).message}`) should also log the
full error via the project's logger (e.g., logger.error) and propagate failure
instead of resolving as success; update the catch in the health command to call
logger.error with contextual message and the error object and then rethrow the
error (or call process.exit(1) if this command is expected to terminate the
process) so the failure is observable and returns a non-zero exit status.

In `@src/commands/logs.ts`:
- Around line 15-17: The catch block currently only calls spinner.fail with the
error message and swallows the error; update the catch in src/commands/logs.ts
(the block that does spinner.fail(`Failed to fetch logs for ${name}: ${(error as
Error).message}`)) to also call the project's logger.error with the full error
object/message (ensure the module has access to the shared logger instance or
import it) and then rethrow the error (or return a rejected promise) so
callers/CI can observe the failure; make sure to preserve the existing
spinner.fail output while adding logger.error and throwing the original error.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7a89f2bd-fe4e-451c-b126-d8003bb655b2

📥 Commits

Reviewing files that changed from the base of the PR and between 02cb101 and 9edfed1.

📒 Files selected for processing (2)
  • src/commands/health.ts
  • src/commands/logs.ts

Comment thread src/commands/health.ts
Comment thread src/commands/logs.ts
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 12 changed files in this pull request and generated 3 comments.

vi.mock('../ui/spinner', () => ({
createSpinner: vi.fn(() => ({
start: vi.fn().mockReturnThis(),
stop: vi.fn().mockReturnThis(),
vi.mock('../ui/spinner', () => ({
createSpinner: vi.fn(() => ({
start: vi.fn().mockReturnThis(),
stop: vi.fn().mockReturnThis(),
@@ -22,8 +22,6 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
@utkarsh232005 utkarsh232005 merged commit 16063ab into KDM-cli:main May 14, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants