Skip to content

Improve setup fallback handling and launcher install output#10

Open
batuhan wants to merge 22 commits into
mainfrom
batuhan/fix
Open

Improve setup fallback handling and launcher install output#10
batuhan wants to merge 22 commits into
mainfrom
batuhan/fix

Conversation

@batuhan
Copy link
Copy Markdown
Member

@batuhan batuhan commented May 21, 2026

Summary

  • Prefer an already installed local Beeper Server in setup fallback flows, including JSON setup actions, instead of offering to reinstall it.
  • Add message.stream to beeper watch include/exclude event filters and update the generated CLI README docs.
  • Make the npm launcher install path easier to debug with download, redirect, verification, extraction, cache, and optional launch logs.

Testing

  • GitHub Actions test passed on macOS and Ubuntu with bun 1.3.10.
  • Added CLI smoke coverage for installed-server setup output.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Review Change Stack

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
📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • New Features

    • Added resolve commands to match selectors (chats, accounts, contacts, targets, bridges) to concrete IDs
    • Added schema command for machine-readable CLI introspection
    • Added --dry-run flag support across all mutating commands
    • Added new output formats: --format text, --format ids, --format table
  • Documentation

    • Launched new documentation site at beeper.github.io/cli with comprehensive command guides
    • Updated beeper setup and server installation flows
  • Improvements

    • Standardized server environment configuration (local|dev|staging|prod)
    • Enhanced error messages with structured diagnostics and exit codes

Walkthrough

Large PR that (1) replaces/hosts the CLI docs with an Astro site and regenerates README, (2) adds machine-readable schema and resolve commands and updates generated command registry, (3) centralizes output formats and adds printDryRun + expanded flags, (4) introduces server-env normalization and setup/server-install detection, (5) updates many commands to support --dry-run and format handling, and (6) improves launcher install logging and updates smoke tests.

Changes

One cohesive cohort

Layer / File(s) Summary
Full change set
docs/*, README.md, packages/cli/*, packages/npm/scripts/build.ts, packages/cli/test/*
Adds a hosted docs site (Astro/Starlight) and README regeneration; introduces schema and resolve:* commands and updates generated command map; centralizes output routing and new formats plus printDryRun and machine-readable helpers; expands CLI base flags (dry-run/format/no-input/read-only/results-only/select/force); extends error metadata; centralizes server-env handling and updates setup/install flows to detect/use installed server; applies --dry-run across many commands; enhances launcher script logging; updates smoke/tests and command manifests.

Sequence Diagram(s)

(Skipped — changes are broad and cover many independent flows.)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • beeper/cli#8: Related to command alias/registry generation and generated command surface changes.

I can produce a split/cohorted review stack (multiple targeted layers) if you want the large single layer broken into reviewer checkpoints.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch batuhan/fix

Copy link
Copy Markdown

@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)
packages/cli/src/commands/setup.ts (1)

283-304: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Non-interactive “current target unreachable” output still ignores installed server state.

You added serverInstalled for interactive recovery here, but the JSON/non-TTY path still calls currentTargetBrokenOutput(...), which always emits install-server. This makes automation output inconsistent and can incorrectly suggest reinstalling an already installed server.

Suggested fix
-      if (flags.json || !process.stdin.isTTY) {
-        await printData(currentTargetBrokenOutput(target, readiness), flags.json ? 'json' : 'human')
+      if (flags.json || !process.stdin.isTTY) {
+        const serverInstalled = await isServerInstalled()
+        await printData(currentTargetBrokenOutput(target, readiness, serverInstalled), flags.json ? 'json' : 'human')
         return
       }
-function currentTargetBrokenOutput(target: Target, readiness: Readiness): Record<string, unknown> {
+function currentTargetBrokenOutput(target: Target, readiness: Readiness, serverInstalled: boolean): Record<string, unknown> {
+  const serverAction = installedServerAction(serverInstalled)
   return {
     state: 'current-target-unreachable',
     message: `Beeper CLI is set up for ${target.name ?? target.id}, but it is not reachable.`,
     target: publicTarget(target),
     readiness,
     recommendedAction: action('retry-current', `beeper setup -t ${target.id}`),
     availableActions: [
       action('retry-current', `beeper setup -t ${target.id}`),
       action('use-desktop', 'beeper setup --desktop'),
-      action('install-server', 'beeper setup --server --install --yes'),
+      serverAction,
       action('connect-remote', 'beeper setup --remote <url>'),
     ],
   }
 }
🤖 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 `@packages/cli/src/commands/setup.ts` around lines 283 - 304, The
non-interactive path always outputs "install-server" because
currentTargetBrokenOutput is called without the actual installed state; update
the flow so the result of isServerInstalled() (serverInstalled) is used to
determine the JSON/non-TTY output—either by passing serverInstalled into
currentTargetBrokenOutput or by adding logic in the non-interactive branch to
emit "use-installed-server" when serverInstalled is true (identify the
isServerInstalled() call and the currentTargetBrokenOutput(...) invocation to
modify). Ensure the JSON/TTY output mirrors the interactive prompt choice text
for option 3 instead of always suggesting installation.
🤖 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 `@packages/npm/scripts/build.ts`:
- Around line 132-137: The milestone logic in the download progress handler
(variables percent, nextLoggedPercent, downloaded, total, function logStep) can
skip intermediate thresholds when percent jumps past multiple milestones; change
the check to a loop that while percent >= nextLoggedPercent (or percent === 100)
emits logStep for each milestone and increments nextLoggedPercent by 25 each
iteration (ensuring the final 100% is emitted with milestone 100), so every 25%
milestone is logged even if a single data event advances progress across several
thresholds.
- Around line 113-118: The download function follows redirects recursively
without a cap; add a maxRedirects parameter (e.g., maxRedirects = 10) and a
currentRedirects counter to the download function signature, increment it on
each redirect and if it exceeds the limit reject with a clear error instead of
recursing forever; update the redirect branch that calls download(nextURL,
destination) to call download(nextURL, destination, currentRedirects + 1,
maxRedirects) (or pass a default when initiating), and ensure all caller sites
provide the new parameter or rely on the default.

---

Outside diff comments:
In `@packages/cli/src/commands/setup.ts`:
- Around line 283-304: The non-interactive path always outputs "install-server"
because currentTargetBrokenOutput is called without the actual installed state;
update the flow so the result of isServerInstalled() (serverInstalled) is used
to determine the JSON/non-TTY output—either by passing serverInstalled into
currentTargetBrokenOutput or by adding logic in the non-interactive branch to
emit "use-installed-server" when serverInstalled is true (identify the
isServerInstalled() call and the currentTargetBrokenOutput(...) invocation to
modify). Ensure the JSON/TTY output mirrors the interactive prompt choice text
for option 3 instead of always suggesting installation.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 33f5d751-b2d8-4103-bce8-43f1439e85eb

📥 Commits

Reviewing files that changed from the base of the PR and between 9502715 and 879f92a.

📒 Files selected for processing (5)
  • packages/cli/README.md
  • packages/cli/src/commands/setup.ts
  • packages/cli/src/commands/watch.ts
  • packages/cli/test/cli-smoke.ts
  • packages/npm/scripts/build.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: test (ubuntu-latest / bun 1.3.10)
  • GitHub Check: test (macos-latest / bun 1.3.10)
🧰 Additional context used
🪛 ESLint
packages/cli/src/commands/setup.ts

[error] 416-416: Do not use useless undefined.

(unicorn/no-useless-undefined)


[error] 641-641: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)


[error] 656-656: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)


[error] 672-672: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)

🔇 Additional comments (4)
packages/cli/src/commands/watch.ts (1)

17-18: LGTM!

packages/cli/README.md (1)

2418-2419: LGTM!

packages/cli/src/commands/setup.ts (1)

106-107: LGTM!

Also applies to: 254-275, 347-351, 406-435, 513-530, 626-690

packages/cli/test/cli-smoke.ts (1)

3-3: LGTM!

Also applies to: 238-262

Comment thread packages/npm/scripts/build.ts Outdated
Comment thread packages/npm/scripts/build.ts Outdated
@batuhan
Copy link
Copy Markdown
Member Author

batuhan commented May 30, 2026

@indent review

@indent
Copy link
Copy Markdown

indent Bot commented May 30, 2026

PR Summary

Originally a small "improve setup fallback + launcher logs" patch (commit 879f92a). Commit 6d302f3 then expanded the PR to a CLI-wide overhaul (new resolve/schema commands, new global flags, output/error envelope rewrite, dry-run wiring across ~60 commands, new Astro docs site). The latest three commits (b8fa69a / 0ec1a22 / 12ed611) refactor the server-env model: introduce a new lib/server-env.ts with four environments (local, dev, staging, prod — plus production as an alias), drop the explicit options:[…] enum from every --server-env flag, and remove the normalizeInstallRequest TODO workaround that previously forced Server installs to staging/nightly because the prod download endpoint was reportedly serving the Desktop bundle.

  • packages/cli/src/lib/server-env.ts (new): SERVER_ENVIRONMENTS, SERVER_ENV_API_BASE_URLS (maps localapi.beeper.localtest.me, devapi.beeper-dev.com, stagingapi.beeper-staging.com, prodapi.beeper.com), and a runtime normalizeServerEnv validator.
  • packages/cli/src/lib/installations.ts: re-exports ServerEnv; normalizeInstallRequest no longer force-maps Server installs to staging/nightly; installDesktop now rejects any non-prod env (not just staging); downloadURLFor drops the special-cased channel segment.
  • packages/cli/src/lib/targets.ts: createProfileTarget validates serverEnv via normalizeServerEnv.
  • packages/cli/src/commands/{setup,install/server,targets/add/desktop,targets/add/server}.ts: drop the options:['production','staging'] enum on --server-env, default to prod, change description to "Server feed environment: prod or staging" — stale, doesn't mention local/dev.
  • packages/cli/src/commands/setup.ts installWithCopy: shows the actual API host in the "Installing … from " log; interactive menu choice still passes server-env: 'prod'/channel: 'stable'.
  • packages/cli/README.md + docs/src/content/docs/targets.mdx: regenerated to reflect prod as canonical; same "prod or staging" omission.
  • packages/cli/test/cli-smoke.ts: new asserts cover prod/staging/local/dev/production-alias request normalization, plus a staging+nightly regression case.

Previously reported issues from prior commits (currentTargetBrokenOutput still hardcoded install-server, schema <command> arg filter dead code, inferExitCode reclassifies bugs as aborts, breaking JSON envelope shape change undocumented, ensureNotDryRun dead code, dry-run-after-createClient/ensureWritable in ~60 commands, watch docs missing message.stream, launcher progress lag) are all still present — none were touched by b8fa69a/0ec1a22/12ed611.

Issues

5 potential issues found:

  • Validation throw new Error(...) sites in commands render as kind:"bug" / internal_error with the bug panel — pre-existing, not new in this PR. 3bf94b7 correctly restored the && inferredCode === undefined guard and tightened inferExitCode (fixing the library-error reclassification from 49b0ca88), but the strict regex still doesn't match the user-facing validation messages: setup.ts:56-60 "Specify at most one of …", chats/{avatar,draft,disappear}.ts "Provide --…" / "--clear cannot be combined …" / "--seconds must be a positive integer …", presence.ts:20-21 "--duration must be a positive integer …", messages/export.ts:25,47-48 "Use only one of --before-cursor …" / "--after is not a valid ISO timestamp …", api/post.ts:26 "--body is not valid JSON …", setup.ts:236 "Install requires --install --yes …", every validation in accounts/add.ts. All 58 sites should be converted to throw usageError(...) (already exported from packages/cli/src/lib/errors.ts:65) so they render as one-line aborts with exit 2 / code:"usage_error" for JSON consumers. → Autofix
  • The JSON envelope shape changed from {success, data, error} (string) to {ok, data, error: {code, message, exitCode, kind, hint}, meta} but this breaking contract change isn't called out in the PR body or any docs page (e.g. docs/src/content/docs/scripting.mdx); external automation that reads envelope.success / treats envelope.error as a string will silently break. → Autofix
  • 7b17068 only relocated the dry-run guard in 8 commands (accounts/add, auth/logout, chats/disappear, messages/export, presence, send/voice, update, verify/approve). 54 commands still execute ensureWritable(flags)/createClient(flags)/resolveChatID(...) before the flags['dry-run'] early return — e.g. send/text, send/file, send/react, all 16 chats/* except disappear, messages/delete, messages/edit, every verify/* except approve, every targets/*, both install/*, config/{set,reset}, etc. beeper send text … --dry-run --read-only --json against a config dir with no targets/auth still fails because ensureWritable and createClient run first. → Autofix
  • docs/src/content/docs/watch.mdx (the new Starlight docs page that replaces packages/cli/docs/watch.md) still lists only chat.upserted, chat.deleted, message.upserted, message.deleted as event types — message.stream was added to the flag options and the auto-generated CLI README but the maintained docs page wasn't updated. → Autofix
  • 12ed611 removes the normalizeInstallRequest workaround that explicitly forced kind: 'server' to serverEnv: 'staging'/channel: 'nightly' because the production endpoint "returns a beeper-server artifact instead of the Desktop app bundle" (per the removed TODO). The default interactive setupFromChoice/handleBrokenCurrentTarget now passes server-env: 'prod'/channel: 'stable', so every menu-driven "Install local Beeper Server" will hit https://api.beeper.com/desktop/download/.../stable/com.automattic.beeper.server — if the prod endpoint hasn't actually been fixed externally, users get a broken artifact and the smoke test (which only asserts URL strings) won't catch it. → Autofix
10 issues already resolved
  • beeper schema <command> never filters: Args.string({ multiple: true }) isn't a valid oclif v4 args option so args.command is a string (not an array), and the handler guards with Array.isArray(args.command) ? args.command.join(' ') : undefined — always false, so requested is always undefined and the full schema is returned even for the documented beeper schema send --results-only example. (fixed by commit c5cfb05)
  • inferExitCode regexes match common substrings (required, parse, invalid, usage, not found, etc.) in any non-CLIError message and flip isBug to false, suppressing the bug panel for real programming errors — e.g. node's Cannot find required module 'foo' is reclassified as a usage error (exit 2) instead of a bug with stack trace and report link. (fixed by commit ffcff36)
  • In the npm launcher's download progress logger, the logged value is the next threshold (nextLoggedPercent), not the actual percent — so a single chunk that takes the download from 0% to 80% will print downloaded 25% while the file is actually 80% done; milestones are still emitted in order and 100% is correct, but intermediate labels lag actual progress. (fixed by commit 3212e03)
  • 3212e03 broke the npm launcher build: packages/npm/scripts/build.ts builds the launcher source as a template literal returned from launcher() (outer backticks on L32/L169), but every nested template literal inside that string (L48, 58, 60-61, 69, 76, 84, 92, 100, 110, 112, 117-118, 124, 136, 156, 165) now uses bare backticks instead of the previous \`` escapes. The outer template terminates at the first inner backtick on L48, producing invalid JavaScript — node --checkagainst the file rejects it withSyntaxError: Unexpected identifier 'beeper'. This blocks bun run buildinpackages/npm` and prevents publishing the npm wrapper. (fixed by commit a819a5a)
  • db163a2 accidentally commits .claude/scheduled_tasks.lock — a Claude Code runtime lock file containing the author's local session ID, PID, and acquire timestamp. The repo has no .claude/ entry in .gitignore, so this kind of dev-environment state will keep leaking into commits. Should be removed from the tree and the .claude/ directory added to .gitignore. (fixed by commit fefbf4e)
  • schema reads positional command-path tokens from this.argv (raw input incl. flag values), not the parsed argv returned by this.parse(...). The arg.startsWith('-') filter strips flag names but not flag values, so beeper schema send --select id produces pathArgs=['send','id']requested='send id' → no match — same dead-filter symptom my original review reported, just now triggered by any value-taking flag (--select, --format, --timeout, --target, --base-url). (fixed by commit 31c9d55)
  • --server-env flag descriptions in setup, install server, targets add desktop, targets add server, and packages/cli/README.md all say Server feed environment: prod or staging, but the new normalizeServerEnv accepts four envs (local, dev, staging, prod) plus production as an alias — local/dev are silently undocumented, and removing options:[...] also drops tab-completion suggestions. (fixed by commit 337c94c)
  • currentTargetBrokenOutput still hardcodes the install-server action in its JSON envelope, so users hitting setup --json (or non-TTY) with an unreachable current target are told to reinstall the Server even when it is already installed — the interactive handleBrokenCurrentTarget was updated to use installedServerAction but this JSON path was missed, contradicting the PR's stated goal of preferring the installed Server "including JSON setup actions". (fixed by commit 337c94c)
  • ensureNotDryRun throws new CLIError('dry-run: …', ExitCodes.Success), which the new catch() handler will turn into {ok:false, error:{exitCode:0, …}} on stderr while the process exits with code 0 — a contract mismatch. The helper is currently unused, but the first command that adopts it will report successful dry-runs as failures to JSON consumers. (fixed by commit ffcff36)
  • packages/cli/README.md has https://example.com placeholders in the docs-site badge (L9), the "Read the docs" link (L25), and every cell of the documentation topic table (L205-217) — these will render as broken links on npm and GitHub when the package publishes. The other example.com occurrences in the same file (e.g. https://desktop.example.com, you@example.com, webhook example) are legitimate code-sample placeholders and should be left alone. (fixed by commit f22ce03)

CI Checks

All CI checks pass on fefbf4e. The launcher-build syntax error that blocked the previous run is resolved by a819a5a (JSON-encoded launcher source).


⚡ Autofix All Issues

@batuhan batuhan changed the title more fixes Improve setup fallback handling and launcher install output May 30, 2026
Comment thread packages/npm/scripts/build.ts Outdated
Comment thread packages/cli/src/commands/schema.ts Outdated
Comment thread packages/cli/src/lib/command.ts
Comment thread packages/cli/src/lib/command.ts Outdated
Copy link
Copy Markdown

@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: 12

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

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

⚠️ Outside diff range comments (4)
packages/cli/src/commands/setup.ts (1)

170-173: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

JSON fallback still hardcodes install-server for broken current target.

The non-interactive/JSON branch still emits install-server in availableActions instead of preferring use-installed-server when a local server is already present, which conflicts with this PR’s fallback objective.

Suggested fix
-        await printData(currentTargetBrokenOutput(target, readiness), flags.json ? 'json' : 'human')
+        const serverInstalled = await isServerInstalled()
+        await printData(currentTargetBrokenOutput(target, readiness, serverInstalled), flags.json ? 'json' : 'human')
         return
       }

-function currentTargetBrokenOutput(target: Target, readiness: Readiness): Record<string, unknown> {
+function currentTargetBrokenOutput(target: Target, readiness: Readiness, serverInstalled: boolean): Record<string, unknown> {
   return {
@@
     availableActions: [
       action('retry-current', `beeper setup -t ${target.id}`),
       action('use-desktop', 'beeper setup --desktop'),
-      action('install-server', 'beeper setup --server --install --yes'),
+      installedServerAction(serverInstalled),
       action('connect-remote', 'beeper setup --remote <url>'),
     ],
   }
 }

Also applies to: 708-720

packages/cli/src/commands/auth/logout.ts (1)

12-19: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Skip the env-token hard error in dry-run mode.

Line 12’s guard runs before the dry-run return, so auth logout --dry-run can error even though it performs no mutation. Move the dry-run branch earlier (or gate the throw with !flags['dry-run']).

Proposed fix
     const { flags } = await this.parse(AuthLogout)
     ensureWritable(flags)
     const target = await resolveTarget({ target: flags.target, baseURL: flags['base-url'] })
+    const token = target.auth?.accessToken
+    if (flags['dry-run']) {
+      await printDryRun('auth.logout', { target: target.id, baseURL: target.baseURL, hadToken: Boolean(token), revokeToken: Boolean(token) }, flags.json ? 'json' : 'human')
+      return
+    }
     if (process.env.BEEPER_ACCESS_TOKEN && !target.auth?.accessToken) {
       throw new Error('auth logout cannot clear BEEPER_ACCESS_TOKEN from the environment; unset it in the calling process.')
     }
-    const token = target.auth?.accessToken
-    if (flags['dry-run']) {
-      await printDryRun('auth.logout', { target: target.id, baseURL: target.baseURL, hadToken: Boolean(token), revokeToken: Boolean(token) }, flags.json ? 'json' : 'human')
-      return
-    }
🤖 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 `@packages/cli/src/commands/auth/logout.ts` around lines 12 - 19, The guard
that throws when process.env.BEEPER_ACCESS_TOKEN is set runs before the dry-run
branch, causing `auth logout --dry-run` to error; to fix, ensure the dry-run
short-circuit happens before that throw (or wrap the throw in a `if
(!flags['dry-run'])` check). Concretely, in the logout command move the `if
(flags['dry-run']) { await printDryRun('auth.logout', ...) ; return }` block to
run before checking `process.env.BEEPER_ACCESS_TOKEN &&
!target.auth?.accessToken`, or add `&& !flags['dry-run']` to that throw
condition so `auth.logout --dry-run` does not throw while still preserving the
original guard for real runs.
packages/cli/src/commands/send/voice.ts (1)

23-25: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

send voice --dry-run should not require writable mode.

The command checks writability before the dry-run early return, so read-only mode can prevent preview-only execution.

Proposed fix
   async run(): Promise<void> {
     const { flags } = await this.parse(SendVoice)
-    ensureWritable(flags)
+    if (!flags['dry-run']) ensureWritable(flags)
     const client = await createClient(flags)

Also applies to: 39-42

🤖 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 `@packages/cli/src/commands/send/voice.ts` around lines 23 - 25, The command
enforces writable mode before checking dry-run; update SendVoice command so that
the dry-run path bypasses writability: parse flags via this.parse(SendVoice),
check flags.dryRun (or flags["dry-run"]) and return the preview path before
calling ensureWritable(flags) or createClient(flags), and apply the same change
to the other occurrence that currently calls ensureWritable before the dry-run
check; keep references to ensureWritable, createClient and SendVoice when
locating the code to reorder the checks.
packages/cli/src/commands/accounts/add.ts (1)

30-33: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

--dry-run is blocked by the write guard.

ensureWritable(flags) runs before the dry-run early return, so read-only mode prevents previewing this command. Gate the write check when --dry-run is set.

Proposed fix
   async run(): Promise<void> {
     const { args, flags } = await this.parse(AccountsAdd)
-    ensureWritable(flags)
     const client = await createClient(flags)
@@
     if (flags['dry-run']) {
       await printDryRun('accounts.add', {
@@
       return
     }
+
+    ensureWritable(flags)
 
     const step = await client.bridges.loginSessions.create(accountType.id, {

Also applies to: 70-84

🤖 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 `@packages/cli/src/commands/accounts/add.ts` around lines 30 - 33, The current
run() in AccountsAdd calls ensureWritable(flags) before handling the --dry-run
flag, which prevents previewing in read-only mode; modify run() (and any other
locations in the same file around the 70-84 block) to check for flags['dry-run']
(or flags.dryRun) first and early-return/preview when set, and only call
ensureWritable(flags) for non-dry-run flows; keep the same behavior and
messaging but gate the write-guard behind the dry-run check so ensureWritable is
skipped during dry runs.
🟡 Minor comments (25)
packages/cli/src/commands/chats/notify-anyway.ts-5-5 (1)

5-5: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Drop unused printSuccess import.

printSuccess isn’t used and currently causes a lint error.

Suggested fix
-import { printData, printDryRun, printSuccess } from '../../lib/output.js'
+import { printData, printDryRun } from '../../lib/output.js'
🤖 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 `@packages/cli/src/commands/chats/notify-anyway.ts` at line 5, Remove the
unused import symbol printSuccess from the import list in the notify-anyway
module to resolve the lint error: update the import statement that currently
reads import { printData, printDryRun, printSuccess } from '../../lib/output.js'
to only import the symbols actually used (printData, printDryRun) so that
printSuccess is no longer referenced in the file.
packages/cli/src/commands/chats/remind.ts-5-5 (1)

5-5: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove unused printData import.

printData is no longer used in this file and currently triggers lint failure.

Suggested fix
-import { printData, printDryRun, printSuccess } from '../../lib/output.js'
+import { printDryRun, printSuccess } from '../../lib/output.js'
🤖 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 `@packages/cli/src/commands/chats/remind.ts` at line 5, The import list
includes an unused symbol printData in the import statement that causes a lint
error; remove printData from the import (keep printDryRun and printSuccess) in
the top-level import line where printData is referenced so the file no longer
imports an unused identifier (or alternatively use printData where intended),
ensuring the import matches actual usage in the remind.ts module.
packages/cli/src/commands/chats/draft.ts-5-5 (1)

5-5: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove unused printSuccess import.

printSuccess is unused and currently fails lint.

Suggested fix
-import { printData, printDryRun, printSuccess } from '../../lib/output.js'
+import { printData, printDryRun } from '../../lib/output.js'
🤖 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 `@packages/cli/src/commands/chats/draft.ts` at line 5, The import list in
draft.ts includes an unused symbol printSuccess which triggers the linter;
remove printSuccess from the import statement (leave printData and printDryRun
intact) in the import from '../../lib/output.js' so the file only imports the
symbols actually used.
packages/cli/src/lib/output.ts-102-102 (1)

102-102: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove unnecessary fallback object in spread.

The ?? {} fallback is redundant in object spread and currently hits lint.

Suggested fix
-    writeJSON(jsonPayload({ message: opts.message, detail: opts.detail, entity: opts.entity, ...(opts.data ?? {}) }), format)
+    writeJSON(jsonPayload({ message: opts.message, detail: opts.detail, entity: opts.entity, ...opts.data }), format)
🤖 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 `@packages/cli/src/lib/output.ts` at line 102, The object spread uses a
redundant fallback; update the call to writeJSON/jsonPayload to spread opts.data
directly instead of using the unnecessary "...(opts.data ?? {})" fallback—i.e.,
change the spread to "...(opts.data)" within the jsonPayload invocation so
writeJSON(jsonPayload({ message: opts.message, detail: opts.detail, entity:
opts.entity, ...(opts.data) }), format) and remove the "?? {}" portion.
packages/cli/src/lib/output.ts-281-281 (1)

281-281: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use strict null checks in printText.

The nullish check uses ==, which violates the configured lint rules.

Suggested fix
-    if (item == null) continue
+    if (item === null || item === undefined) continue
🤖 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 `@packages/cli/src/lib/output.ts` at line 281, In function printText replace
the loose nullish check on the variable item (currently using ==) with a strict
check that explicitly tests for null and undefined so lint rules pass; locate
the line "if (item == null) continue" in printText and change it to an explicit
null and undefined check for item (i.e., check both null and undefined) so the
branch behavior remains the same but uses strict comparison.
packages/cli/src/commands/chats/mark-unread.ts-5-5 (1)

5-5: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove unused printSuccess import.

printSuccess is no longer used and currently triggers lint failure.

Suggested fix
-import { printData, printDryRun, printSuccess } from '../../lib/output.js'
+import { printData, printDryRun } from '../../lib/output.js'
🤖 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 `@packages/cli/src/commands/chats/mark-unread.ts` at line 5, Remove the unused
import to fix the lint error: in the import statement that currently imports
printData, printDryRun, printSuccess (from '../../lib/output.js') drop
printSuccess so only printData and printDryRun are imported; update the import
line where printSuccess is referenced to ensure no other code uses it (e.g.,
check for any occurrences of printSuccess in mark-unread.ts) and run linter to
confirm the error is resolved.
packages/cli/src/commands/chats/archive.ts-5-5 (1)

5-5: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Drop unused printSuccess import.

printSuccess is unused in this command and should be removed.

Suggested fix
-import { printData, printDryRun, printSuccess } from '../../lib/output.js'
+import { printData, printDryRun } from '../../lib/output.js'
🤖 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 `@packages/cli/src/commands/chats/archive.ts` at line 5, The import list
includes an unused symbol printSuccess from the module imported in the top-level
import statement (the line that currently reads import { printData, printDryRun,
printSuccess } from '../../lib/output.js'); remove printSuccess from that import
so only printData and printDryRun are imported, and run a quick lint/type check
to ensure no other references to printSuccess remain in the chats/archive
command.
packages/cli/src/commands/chats/mark-read.ts-5-5 (1)

5-5: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove unused printSuccess from imports.

printSuccess is not used and currently triggers no-unused-vars.

Suggested fix
-import { printData, printDryRun, printSuccess } from '../../lib/output.js'
+import { printData, printDryRun } from '../../lib/output.js'
🤖 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 `@packages/cli/src/commands/chats/mark-read.ts` at line 5, The import list in
mark-read.ts includes an unused symbol printSuccess which triggers
no-unused-vars; remove printSuccess from the import statement that currently
reads import { printData, printDryRun, printSuccess } from '../../lib/output.js'
so the import only includes the actually used symbols (printData, printDryRun)
and save the file.
packages/cli/src/commands/send/file.ts-33-37 (1)

33-37: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add required blank line before the final statement.

This block currently violates @stylistic/padding-line-between-statements.

Suggested fix
     if (flags['dry-run']) {
       await printDryRun('send.file', request, flags.json ? 'json' : 'human')
       return
     }
+
     await printData(await sendMessage(client, request), flags.json ? 'json' : 'human')
🤖 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 `@packages/cli/src/commands/send/file.ts` around lines 33 - 37, Add a blank
line between the dry-run early-return block and the final await so the file
complies with padding-line-between-statements: after the if (flags['dry-run']) {
... return } block (which calls printDryRun) insert one empty line before the
final await printData(await sendMessage(client, request), flags.json ? 'json' :
'human'); ensure the surrounding code using sendMessage, printData and
printDryRun remains unchanged.
packages/cli/src/commands/chats/description.ts-5-5 (1)

5-5: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove the unused printSuccess import.

printSuccess isn’t referenced and should be dropped to satisfy lint rules.

Suggested fix
-import { printData, printDryRun, printSuccess } from '../../lib/output.js'
+import { printData, printDryRun } from '../../lib/output.js'
🤖 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 `@packages/cli/src/commands/chats/description.ts` at line 5, Remove the unused
import symbol printSuccess from the import statement that currently reads import
{ printData, printDryRun, printSuccess } from '../../lib/output.js'; update the
import to only include referenced symbols (printData, printDryRun) so the unused
printSuccess is eliminated and lint errors are resolved.
packages/cli/src/commands/resolve/account.ts-25-26 (1)

25-26: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

--pick 0 is incorrectly treated as “not provided”.

Using a truthy check lets 0 bypass validation and fall into default selection logic.

Suggested fix
-    const selected = flags.pick ? candidates[flags.pick - 1] : candidates.length === 1 ? candidates[0] : undefined
-    if (flags.pick && !selected) throw notFound(`--pick ${flags.pick} is outside the ${candidates.length} matching accounts`, { selector: args.selector, pick: flags.pick, count: candidates.length })
+    const pick = flags.pick
+    const selected = pick !== undefined ? candidates[pick - 1] : candidates.length === 1 ? candidates[0] : undefined
+    if (pick !== undefined && !selected) throw notFound(`--pick ${pick} is outside the ${candidates.length} matching accounts`, { selector: args.selector, pick, count: candidates.length })
🤖 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 `@packages/cli/src/commands/resolve/account.ts` around lines 25 - 26, The code
treats flags.pick==0 as "not provided" due to a truthy check; update the
selection logic to explicitly detect when a pick was provided (e.g., use typeof
flags.pick !== 'undefined' or flags.pick != null) and validate it as an integer
in range 1..candidates.length before indexing candidates (use flags.pick - 1).
Replace the current ternary that references flags.pick with this explicit check
and ensure the notFound error (and its message) is thrown when the provided pick
is outside the valid range.
packages/cli/src/commands/resolve/contact.ts-33-34 (1)

33-34: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

--pick 0 bypasses candidate range validation.

Because selection is guarded by truthiness, 0 behaves like “unset” instead of invalid input.

Suggested fix
-    const selected = flags.pick ? candidates[flags.pick - 1] : candidates.length === 1 ? candidates[0] : undefined
-    if (flags.pick && !selected) throw notFound(`--pick ${flags.pick} is outside the ${candidates.length} matching contacts`, { selector: args.selector, pick: flags.pick, count: candidates.length })
+    const pick = flags.pick
+    const selected = pick !== undefined ? candidates[pick - 1] : candidates.length === 1 ? candidates[0] : undefined
+    if (pick !== undefined && !selected) throw notFound(`--pick ${pick} is outside the ${candidates.length} matching contacts`, { selector: args.selector, pick, count: candidates.length })
🤖 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 `@packages/cli/src/commands/resolve/contact.ts` around lines 33 - 34, The
validation incorrectly treats flags.pick as truthy so a value of 0 bypasses
range checking; update the logic around selected and the subsequent throw to
explicitly detect when flags.pick is provided (e.g., flags.pick !== undefined &&
flags.pick !== null), compute the index as flags.pick - 1 to set selected from
candidates, and validate that flags.pick is between 1 and candidates.length
(throw notFound using the existing notFound, args.selector, and flags.pick if
out of range); ensure selected remains undefined when no pick was provided and
keep references to selected, flags.pick, candidates, notFound, and
args.selector.
packages/cli/src/commands/targets/start.ts-20-27 (1)

20-27: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Address padding-line lint failures in the new dry-run branches.

Line 20 and Line 24/27 need the blank-line separation required by @stylistic/padding-line-between-statements; this will likely fail lint as-is.

🤖 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 `@packages/cli/src/commands/targets/start.ts` around lines 20 - 27, The new
branches around launchDesktopApp/printSuccess and the subsequent conditionals
violate padding-line rules; add the required blank lines to separate statement
groups: insert a blank line after the "return" following printSuccess (the
launchDesktopApp/printSuccess block) and ensure there's a blank line before the
if that checks "if (!target.managed || target.type !== 'server')" and before "if
(flags['dry-run'])" so each logical statement group
(launchDesktopApp/printSuccess/return, the non-managed/type check, and the
dry-run branch) is separated; update the block containing launchDesktopApp,
printSuccess, the return, the non-managed/type if, and the flags['dry-run'] if
to include those blank lines.
packages/cli/src/lib/command.ts-148-158 (1)

148-158: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix switch-case lint violations in errorCode.

Line 151–157 currently violates unicorn/switch-case-braces (and ordering if your lint config enforces it), which can block CI.

Proposed fix
 function errorCode(code: number, isBug: boolean): string {
   if (isBug) return 'internal_error'
   switch (code) {
-    case ExitCodes.Usage: return 'usage_error'
-    case ExitCodes.AuthRequired: return 'auth_required'
-    case ExitCodes.NotReady: return 'not_ready'
-    case ExitCodes.NotFound: return 'not_found'
-    case ExitCodes.Ambiguous: return 'ambiguous_selector'
-    case ExitCodes.CommandNotFound: return 'command_not_found'
-    default: return 'runtime_error'
+    case ExitCodes.Ambiguous: { return 'ambiguous_selector' }
+    case ExitCodes.AuthRequired: { return 'auth_required' }
+    case ExitCodes.CommandNotFound: { return 'command_not_found' }
+    case ExitCodes.NotFound: { return 'not_found' }
+    case ExitCodes.NotReady: { return 'not_ready' }
+    case ExitCodes.Usage: { return 'usage_error' }
+    default: { return 'runtime_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 `@packages/cli/src/lib/command.ts` around lines 148 - 158, The switch in
function errorCode violates unicorn/switch-case-braces—wrap each case body in
braces and use explicit breaks (or returns inside braces) to satisfy the rule;
update the cases for ExitCodes.Usage, ExitCodes.AuthRequired,
ExitCodes.NotReady, ExitCodes.NotFound, ExitCodes.Ambiguous, and
ExitCodes.CommandNotFound inside errorCode to use { return '...'; } for each
case (and reorder cases if your linter enforces a specific order) so the switch
follows the lint rule and CI will pass.
packages/cli/src/commands/chats/unpin.ts-5-5 (1)

5-5: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove unused printSuccess import.

printSuccess is unused and should be dropped to satisfy lint.

Suggested fix
-import { printData, printDryRun, printSuccess } from '../../lib/output.js'
+import { printData, printDryRun } from '../../lib/output.js'
🤖 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 `@packages/cli/src/commands/chats/unpin.ts` at line 5, The import list includes
an unused symbol printSuccess which triggers a lint error; remove printSuccess
from the import statement that currently imports printData, printDryRun,
printSuccess so the import only includes the used symbols (printData and
printDryRun).
packages/cli/src/commands/chats/unmute.ts-5-5 (1)

5-5: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove unused printSuccess import.

printSuccess is not used and triggers the reported ESLint error.

Suggested fix
-import { printData, printDryRun, printSuccess } from '../../lib/output.js'
+import { printData, printDryRun } from '../../lib/output.js'
🤖 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 `@packages/cli/src/commands/chats/unmute.ts` at line 5, The import statement
currently brings in printData, printDryRun and printSuccess but printSuccess is
unused; edit the import in the module to remove the unused symbol (remove
printSuccess from the import list in the import { printData, printDryRun,
printSuccess } line) and run linting to confirm no remaining references to
printSuccess in the file (check functions or usages around unmute command
handlers).
packages/cli/src/commands/chats/pin.ts-5-5 (1)

5-5: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove unused printSuccess import.

printSuccess is unused in this command and should be removed.

Suggested fix
-import { printData, printDryRun, printSuccess } from '../../lib/output.js'
+import { printData, printDryRun } from '../../lib/output.js'
🤖 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 `@packages/cli/src/commands/chats/pin.ts` at line 5, The import list in the
chats pin command currently includes an unused symbol `printSuccess`; remove
`printSuccess` from the import statement that reads `import { printData,
printDryRun, printSuccess } from '../../lib/output.js'` so only used functions
(e.g., `printData` and `printDryRun`) are imported to eliminate the unused
import and lint warnings.
packages/cli/src/commands/chats/focus.ts-5-5 (1)

5-5: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove unused printSuccess import.

printSuccess is not used in this command and is currently reported by ESLint.

Suggested fix
-import { printData, printDryRun, printSuccess } from '../../lib/output.js'
+import { printData, printDryRun } from '../../lib/output.js'
🤖 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 `@packages/cli/src/commands/chats/focus.ts` at line 5, Remove the unused
printSuccess import from the import list in the focus command; edit the import
line that currently reads "import { printData, printDryRun, printSuccess } from
'../../lib/output.js'" and drop printSuccess so only used symbols (printData,
printDryRun) are imported to satisfy ESLint.
packages/cli/src/commands/chats/avatar.ts-5-5 (1)

5-5: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove unused printSuccess import.

printSuccess is not referenced and is flagged by ESLint.

Suggested fix
-import { printData, printDryRun, printSuccess } from '../../lib/output.js'
+import { printData, printDryRun } from '../../lib/output.js'
🤖 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 `@packages/cli/src/commands/chats/avatar.ts` at line 5, The import statement in
avatar.ts includes an unused symbol printSuccess which ESLint flags; remove
printSuccess from the import list (leave printData and printDryRun intact) so
the import becomes only the used symbols and run lint to confirm no other unused
imports remain.
packages/cli/src/commands/chats/unarchive.ts-5-5 (1)

5-5: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove unused printSuccess import.

printSuccess is unused in this file and currently lint-failing.

Suggested fix
-import { printData, printDryRun, printSuccess } from '../../lib/output.js'
+import { printData, printDryRun } from '../../lib/output.js'
🤖 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 `@packages/cli/src/commands/chats/unarchive.ts` at line 5, The import list in
unarchive.ts unnecessarily includes printSuccess which is not used and causes a
linter error; remove printSuccess from the import statement that currently reads
"import { printData, printDryRun, printSuccess } from '../../lib/output.js'" so
it only imports the used symbols (printData, printDryRun).
packages/cli/src/commands/send/text.ts-36-36 (1)

36-36: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add blank line before this statement.

The project's ESLint configuration requires a blank line between the preceding statement block and this line.

📏 Proposed fix
     if (flags['dry-run']) {
       await printDryRun('send.text', request, flags.json ? 'json' : 'human')
       return
     }
+
     await printData(await sendMessage(client, request), flags.json ? 'json' : 'human')
🤖 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 `@packages/cli/src/commands/send/text.ts` at line 36, Add a blank line before
the statement that awaits printData(await sendMessage(client, request),
flags.json ? 'json' : 'human'); specifically insert a newline separating the
preceding block from the call that uses sendMessage and printData (which
reference client, request, and flags.json) so the ESLint rule requiring a blank
line before that statement is satisfied.
packages/cli/src/commands/targets/remove.ts-7-7 (1)

7-7: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove unused import.

printData is imported but never used in this file.

🧹 Proposed fix
-import { printData, printDryRun, printSuccess } from '../../lib/output.js'
+import { printDryRun, printSuccess } from '../../lib/output.js'
🤖 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 `@packages/cli/src/commands/targets/remove.ts` at line 7, Remove the unused
imported symbol printData from the import statement in remove.ts: locate the
import line that brings in printData, printDryRun, and printSuccess and delete
printData so only printDryRun and printSuccess are imported; ensure no other
references to printData remain in the file (e.g., in any helper functions or
exports).
packages/cli/src/commands/resolve/bridge.ts-32-33 (1)

32-33: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Validate --pick as 1-indexed explicitly.

--pick 0 is currently treated as “not provided” due the truthy check, which breaks the documented 1-indexed behavior.

Suggested fix
-    const selected = flags.pick ? candidates[flags.pick - 1] : candidates.length === 1 ? candidates[0] : undefined
-    if (flags.pick && !selected) throw notFound(`--pick ${flags.pick} is outside the ${candidates.length} matching bridges`, { selector: args.selector, pick: flags.pick, count: candidates.length })
+    if (flags.pick !== undefined && flags.pick < 1) {
+      throw new Error('--pick must be >= 1')
+    }
+    const selected = flags.pick !== undefined ? candidates[flags.pick - 1] : candidates.length === 1 ? candidates[0] : undefined
+    if (flags.pick !== undefined && !selected) throw notFound(`--pick ${flags.pick} is outside the ${candidates.length} matching bridges`, { selector: args.selector, pick: flags.pick, count: candidates.length })
🤖 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 `@packages/cli/src/commands/resolve/bridge.ts` around lines 32 - 33, The code
treats flags.pick as truthy, so --pick 0 is ignored; change the logic to
explicitly detect when a pick was provided and validate it as a 1-indexed
integer within range: use a check like "typeof flags.pick !== 'undefined'" (or
Number.isInteger(flags.pick)) to decide selection (set selected =
candidates[flags.pick - 1] only when pick is a valid integer between 1 and
candidates.length), keep the fallback of selecting the sole candidate when
candidates.length === 1, and if a pick was provided but is out of range throw
the existing notFound(...) with the same context (selector, pick, count).
docs/astro.config.mjs-30-36 (1)

30-36: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Repository links appear to target the wrong GitHub repo.

The docs config points to beeper/desktop-api-cli, while this project context is beeper/cli. Social, edit-link, and command-reference links should be aligned to avoid broken/misdirected docs navigation.

Proposed fix
-          href: 'https://github.com/beeper/desktop-api-cli',
+          href: 'https://github.com/beeper/cli',
@@
-          'https://github.com/beeper/desktop-api-cli/edit/main/docs/',
+          'https://github.com/beeper/cli/edit/main/docs/',
@@
-              link: 'https://github.com/beeper/desktop-api-cli/blob/main/packages/cli/README.md',
+              link: 'https://github.com/beeper/cli/blob/main/packages/cli/README.md',

Also applies to: 87-87

🤖 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 `@docs/astro.config.mjs` around lines 30 - 36, The repo links in the docs
config point to the wrong GitHub repository; update all references that use
'beeper/desktop-api-cli' to 'beeper/cli' (including the social href entries,
editLink.baseUrl, and any command-reference or similar link fields) so
navigation and edit links resolve to the correct repo; search for occurrences of
the string 'beeper/desktop-api-cli' in this file and replace them with
'beeper/cli' (ensure editLink.baseUrl remains correctly suffixed with 'docs/'
and other link templates keep their existing path structure).
packages/cli/src/commands/send/voice.ts-43-45 (1)

43-45: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Lint error: missing blank line before await printData.

This matches the reported ESLint failure and should be fixed to avoid pipeline breakage.

Proposed fix
     if (flags['dry-run']) {
       await printDryRun('send.voice', request, flags.json ? 'json' : 'human')
       return
     }
+
     await printData(
       await sendMessage(client, request),
       flags.json ? 'json' : 'human',
🤖 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 `@packages/cli/src/commands/send/voice.ts` around lines 43 - 45, The linter
flagged a missing blank line before the call to await printData; fix it by
inserting a single blank line immediately above the statement that calls await
printData(...) (the invocation that awaits sendMessage(client, request) and uses
flags.json ? 'json' : 'human'), keeping the surrounding code and indentation
intact so the statements referencing sendMessage, client, request, printData,
and flags.json remain unchanged.
🧹 Nitpick comments (3)
packages/cli/src/commands/messages/export.ts (1)

26-41: ⚡ Quick win

Dry-run check happens after API calls.

The --dry-run check at line 29 occurs after resolveChatID (line 28), which performs an API call to look up the chat. For a true dry-run that avoids side effects, consider checking flags['dry-run'] before createClient and resolveChatID, and printing a dry-run output with the raw selector string instead of the resolved chatID.

If validating the chat selector in dry-run mode is intentional, this is acceptable, but it's inconsistent with other commands in this PR (e.g., verify/start.ts checks dry-run before API calls).

♻️ Suggested refactor to check dry-run earlier
 async run(): Promise<void> {
   const { flags } = await this.parse(MessagesExport)
   if (flags['before-cursor'] && flags['after-cursor']) throw new Error('Use only one of --before-cursor or --after-cursor')
-  if (flags.output !== '-') ensureWritable(flags)
-  const client = await createClient(flags)
-  const chatID = await resolveChatID(client, flags.chat, { pick: flags.pick })
   if (flags['dry-run']) {
     await printDryRun('messages.export', {
-      chatID,
+      chat: flags.chat,
+      pick: flags.pick,
       output: flags.output,
       beforeCursor: flags['before-cursor'],
       afterCursor: flags['after-cursor'],
       after: flags.after,
       before: flags.before,
       limit: flags.limit,
       asc: flags.asc,
     }, flags.json ? 'json' : 'human')
     return
   }
+  if (flags.output !== '-') ensureWritable(flags)
+  const client = await createClient(flags)
+  const chatID = await resolveChatID(client, flags.chat, { pick: flags.pick })
🤖 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 `@packages/cli/src/commands/messages/export.ts` around lines 26 - 41, The
dry-run flag is checked after performing API calls (createClient and
resolveChatID), causing side effects; move the flags['dry-run'] check to occur
before calling createClient and resolveChatID in messages.export, and when in
dry-run mode call printDryRun('messages.export', {...}) using the raw selector
(flags.chat) and the other CLI flags (output, before-cursor, after-cursor,
after, before, limit, asc) rather than the resolved chatID so no API lookup is
performed; keep printDryRun usage and the json/human format selection the same.
packages/cli/src/commands/contacts/search.ts (1)

35-35: 💤 Low value

Consider simplifying the machine-readable output check.

The expression !isMachineReadableOutput(flags.json ? 'json' : 'human') is functionally equivalent to !flags.json since those are the only two output formats. The current implementation adds indirection without clear benefit.

If additional output formats are planned, the current approach provides future flexibility. Otherwise, consider using !flags.json directly.

🤖 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 `@packages/cli/src/commands/contacts/search.ts` at line 35, Replace the
indirection in constructing useSpinner by directly using the JSON flag: instead
of computing useSpinner with isMachineReadableOutput(flags.json ? 'json' :
'human'), set useSpinner based on flags.json (i.e., useSpinner should be the
negation of flags.json); if you intend to support more output formats later,
keep the isMachineReadableOutput call but document that intent—refer to the
useSpinner variable and isMachineReadableOutput(flags.json ? 'json' : 'human')
expression and the flags.json flag when making the change.
packages/cli/src/commands/presence.ts (1)

20-28: ⚡ Quick win

Dry-run check happens after API calls.

The --dry-run check at line 25 occurs after resolveChatID (line 24), which performs an API call. For a true dry-run that avoids side effects, consider checking flags['dry-run'] before createClient and resolveChatID.

This is the same pattern as in messages/export.ts. If you want dry-run to validate selectors, this is acceptable, but it means dry-run mode still makes API calls.

🤖 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 `@packages/cli/src/commands/presence.ts` around lines 20 - 28, The dry-run
branch currently runs after createClient and resolveChatID which cause API
calls; move the flags['dry-run'] check up so it runs before createClient and
resolveChatID to avoid side effects. Specifically, in presence command (around
ensureWritable, createClient, resolveChatID, printDryRun) check flags['dry-run']
immediately after ensureWritable (or before any API-creating calls), and if true
call printDryRun and return; only call createClient and resolveChatID in the
non-dry-run path. If you still need selector validation without API calls,
replace resolveChatID with a local validation step for flags.chat/pick before
exiting in dry-run.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e1909ded-8979-41e7-934b-9c9261157a6e

📥 Commits

Reviewing files that changed from the base of the PR and between 879f92a and 6d302f3.

⛔ Files ignored due to path filters (3)
  • docs/bun.lock is excluded by !**/*.lock
  • docs/public/favicon.svg is excluded by !**/*.svg
  • docs/src/assets/logo.svg is excluded by !**/*.svg
📒 Files selected for processing (122)
  • README.md
  • docs/.gitignore
  • docs/astro.config.mjs
  • docs/package.json
  • docs/src/content.config.ts
  • docs/src/content/docs/accounts.mdx
  • docs/src/content/docs/api.mdx
  • docs/src/content/docs/auth.mdx
  • docs/src/content/docs/chats.mdx
  • docs/src/content/docs/config.mdx
  • docs/src/content/docs/connect.mdx
  • docs/src/content/docs/contacts.mdx
  • docs/src/content/docs/exit-codes.mdx
  • docs/src/content/docs/export.mdx
  • docs/src/content/docs/index.mdx
  • docs/src/content/docs/install.mdx
  • docs/src/content/docs/media.mdx
  • docs/src/content/docs/messages.mdx
  • docs/src/content/docs/plugins.mdx
  • docs/src/content/docs/presence.mdx
  • docs/src/content/docs/quickstart.mdx
  • docs/src/content/docs/rpc.mdx
  • docs/src/content/docs/scripting.mdx
  • docs/src/content/docs/send.mdx
  • docs/src/content/docs/targets.mdx
  • docs/src/content/docs/update.mdx
  • docs/src/content/docs/watch.mdx
  • docs/src/styles/theme.css
  • docs/tsconfig.json
  • packages/cli/README.md
  • packages/cli/docs/api.md
  • packages/cli/docs/config.md
  • packages/cli/docs/export.md
  • packages/cli/docs/setup.md
  • packages/cli/docs/update.md
  • packages/cli/docs/watch.md
  • packages/cli/package.json
  • packages/cli/scripts/generate-command-map.ts
  • packages/cli/scripts/generate-readme.ts
  • packages/cli/src/commands.generated.ts
  • packages/cli/src/commands/accounts/add.ts
  • packages/cli/src/commands/accounts/remove.ts
  • packages/cli/src/commands/accounts/use.ts
  • packages/cli/src/commands/api/post.ts
  • packages/cli/src/commands/api/request.ts
  • packages/cli/src/commands/auth/email/response.ts
  • packages/cli/src/commands/auth/logout.ts
  • packages/cli/src/commands/chats/archive.ts
  • packages/cli/src/commands/chats/avatar.ts
  • packages/cli/src/commands/chats/description.ts
  • packages/cli/src/commands/chats/disappear.ts
  • packages/cli/src/commands/chats/draft.ts
  • packages/cli/src/commands/chats/focus.ts
  • packages/cli/src/commands/chats/mark-read.ts
  • packages/cli/src/commands/chats/mark-unread.ts
  • packages/cli/src/commands/chats/mute.ts
  • packages/cli/src/commands/chats/notify-anyway.ts
  • packages/cli/src/commands/chats/pin.ts
  • packages/cli/src/commands/chats/priority.ts
  • packages/cli/src/commands/chats/remind.ts
  • packages/cli/src/commands/chats/rename.ts
  • packages/cli/src/commands/chats/start.ts
  • packages/cli/src/commands/chats/unarchive.ts
  • packages/cli/src/commands/chats/unmute.ts
  • packages/cli/src/commands/chats/unpin.ts
  • packages/cli/src/commands/chats/unremind.ts
  • packages/cli/src/commands/config/reset.ts
  • packages/cli/src/commands/config/set.ts
  • packages/cli/src/commands/contacts/search.ts
  • packages/cli/src/commands/export.ts
  • packages/cli/src/commands/install/desktop.ts
  • packages/cli/src/commands/install/server.ts
  • packages/cli/src/commands/man.ts
  • packages/cli/src/commands/media/download.ts
  • packages/cli/src/commands/messages/delete.ts
  • packages/cli/src/commands/messages/edit.ts
  • packages/cli/src/commands/messages/export.ts
  • packages/cli/src/commands/messages/search.ts
  • packages/cli/src/commands/presence.ts
  • packages/cli/src/commands/resolve/account.ts
  • packages/cli/src/commands/resolve/bridge.ts
  • packages/cli/src/commands/resolve/chat.ts
  • packages/cli/src/commands/resolve/contact.ts
  • packages/cli/src/commands/resolve/target.ts
  • packages/cli/src/commands/schema.ts
  • packages/cli/src/commands/send/file.ts
  • packages/cli/src/commands/send/react.ts
  • packages/cli/src/commands/send/sticker.ts
  • packages/cli/src/commands/send/text.ts
  • packages/cli/src/commands/send/unreact.ts
  • packages/cli/src/commands/send/voice.ts
  • packages/cli/src/commands/setup.ts
  • packages/cli/src/commands/targets/add/desktop.ts
  • packages/cli/src/commands/targets/add/remote.ts
  • packages/cli/src/commands/targets/add/server.ts
  • packages/cli/src/commands/targets/disable.ts
  • packages/cli/src/commands/targets/enable.ts
  • packages/cli/src/commands/targets/remove.ts
  • packages/cli/src/commands/targets/restart.ts
  • packages/cli/src/commands/targets/start.ts
  • packages/cli/src/commands/targets/stop.ts
  • packages/cli/src/commands/targets/use.ts
  • packages/cli/src/commands/update.ts
  • packages/cli/src/commands/verify.ts
  • packages/cli/src/commands/verify/approve.ts
  • packages/cli/src/commands/verify/cancel.ts
  • packages/cli/src/commands/verify/qr-confirm.ts
  • packages/cli/src/commands/verify/qr-scan.ts
  • packages/cli/src/commands/verify/recovery-key.ts
  • packages/cli/src/commands/verify/reset-recovery-key.ts
  • packages/cli/src/commands/verify/sas-confirm.ts
  • packages/cli/src/commands/verify/sas.ts
  • packages/cli/src/commands/verify/start.ts
  • packages/cli/src/commands/watch.ts
  • packages/cli/src/lib/command-metadata.ts
  • packages/cli/src/lib/command.ts
  • packages/cli/src/lib/errors.ts
  • packages/cli/src/lib/manifest.ts
  • packages/cli/src/lib/output.ts
  • packages/cli/src/lib/resolve.ts
  • packages/cli/test/cli-smoke.ts
  • packages/cli/test/messages-search-validation.test.ts
💤 Files with no reviewable changes (6)
  • packages/cli/docs/api.md
  • packages/cli/docs/update.md
  • packages/cli/docs/watch.md
  • packages/cli/docs/export.md
  • packages/cli/docs/setup.md
  • packages/cli/docs/config.md
✅ Files skipped from review due to trivial changes (18)
  • docs/tsconfig.json
  • docs/.gitignore
  • docs/src/content.config.ts
  • docs/src/content/docs/contacts.mdx
  • docs/src/content/docs/media.mdx
  • docs/src/content/docs/exit-codes.mdx
  • docs/src/content/docs/presence.mdx
  • docs/src/content/docs/export.mdx
  • docs/src/content/docs/watch.mdx
  • docs/src/content/docs/rpc.mdx
  • docs/src/content/docs/auth.mdx
  • docs/src/content/docs/update.mdx
  • docs/src/content/docs/connect.mdx
  • docs/src/content/docs/api.mdx
  • docs/src/content/docs/plugins.mdx
  • README.md
  • packages/cli/src/lib/manifest.ts
  • packages/cli/README.md

Comment thread packages/cli/src/commands/chats/disappear.ts Outdated
Comment thread packages/cli/src/commands/chats/unremind.ts Outdated
Comment thread packages/cli/src/commands/resolve/contact.ts
Comment thread packages/cli/src/commands/schema.ts Outdated
Comment thread packages/cli/src/commands/schema.ts Outdated
Comment thread packages/cli/src/commands/verify/approve.ts Outdated
Comment thread packages/cli/src/lib/command-metadata.ts Outdated
Comment thread packages/cli/src/lib/command-metadata.ts Outdated
Comment thread packages/cli/src/lib/output.ts
Comment thread packages/cli/src/lib/resolve.ts Outdated
Comment thread packages/cli/src/lib/server-env.ts
Copy link
Copy Markdown

@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: 1

Caution

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

⚠️ Outside diff range comments (1)
packages/cli/README.md (1)

9-9: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Replace example.com placeholder with actual documentation URL.

The README contains multiple references to example.com as a placeholder for the documentation site. Before merging, these should be updated to the actual documentation URL.

Occurrences:

  • Line 9: docs badge
  • Line 25: "Read the docs" link
  • Lines 205-220: Documentation topic table

Also applies to: 25-25, 205-220

🤖 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 `@packages/cli/README.md` at line 9, Update the placeholder documentation URL
in packages/cli/README.md by replacing all occurrences of "example.com" with the
actual docs URL: update the docs badge link (the line containing the badge
image), the "Read the docs" anchor, and every link in the documentation topic
table (previously lines showing topics) so they point to the real documentation
site; double-check for any other "example.com" occurrences in the file and
replace them consistently.
🤖 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 `@packages/cli/src/lib/installations.ts`:
- Line 151: installDesktop() currently throws when request.serverEnv !== 'prod',
which breaks non-prod installs; change the install behavior to stop hard-failing
and instead map the desktop update feed to 'prod' while still passing the
original request.serverEnv to the launched app: in installDesktop(), remove the
throw for request.serverEnv !== 'prod', introduce a local variable (e.g.,
feedEnv) that is set to 'prod' when request.serverEnv !== 'prod' and use feedEnv
for any feed/update URLs, but continue to pass request.serverEnv (or
target.serverEnv) into the app launch/config so the app sees the requested
environment; also add a short warning log when mapping non-prod -> prod so users
know the feed was remapped.

---

Outside diff comments:
In `@packages/cli/README.md`:
- Line 9: Update the placeholder documentation URL in packages/cli/README.md by
replacing all occurrences of "example.com" with the actual docs URL: update the
docs badge link (the line containing the badge image), the "Read the docs"
anchor, and every link in the documentation topic table (previously lines
showing topics) so they point to the real documentation site; double-check for
any other "example.com" occurrences in the file and replace them consistently.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 189263df-a84a-4970-9808-6c723d61c15d

📥 Commits

Reviewing files that changed from the base of the PR and between 6d302f3 and 12ed611.

📒 Files selected for processing (11)
  • docs/src/content/docs/targets.mdx
  • packages/cli/README.md
  • packages/cli/src/commands/install/server.ts
  • packages/cli/src/commands/setup.ts
  • packages/cli/src/commands/targets/add/desktop.ts
  • packages/cli/src/commands/targets/add/server.ts
  • packages/cli/src/lib/installations.ts
  • packages/cli/src/lib/manifest.ts
  • packages/cli/src/lib/server-env.ts
  • packages/cli/src/lib/targets.ts
  • packages/cli/test/cli-smoke.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/src/content/docs/targets.mdx
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/cli/src/commands/install/server.ts
  • packages/cli/src/commands/targets/add/server.ts
  • packages/cli/src/lib/manifest.ts
  • packages/cli/src/commands/setup.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: test (ubuntu-latest / bun 1.3.10)
  • GitHub Check: test (ubuntu-latest / bun 1.3.10)
  • GitHub Check: test (macos-latest / bun 1.3.10)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-05-27T22:40:16.795Z
Learnt from: juulieen
Repo: beeper/cli PR: 13
File: README.md:178-178
Timestamp: 2026-05-27T22:40:16.795Z
Learning: In beeper CLI documentation, the correct recovery-key verification command syntax is:
`beeper verify recovery-key -t <target> --key "<key>"`.
Use the top-level `verify` subcommand and pass the recovery key via the `--key` flag. Do not use the previously incorrect form like `beeper auth verify recovery-key --code KEY`—update any doc snippets to match the supported syntax.

Applied to files:

  • packages/cli/README.md
🔇 Additional comments (7)
packages/cli/src/lib/targets.ts (1)

6-6: LGTM!

Also applies to: 205-205

packages/cli/src/lib/server-env.ts (1)

1-16: LGTM!

packages/cli/test/cli-smoke.ts (1)

252-252: LGTM!

Also applies to: 284-311, 346-346

packages/cli/src/commands/targets/add/desktop.ts (1)

12-12: LGTM!

Also applies to: 19-22

packages/cli/src/lib/installations.ts (1)

92-93: LGTM!

Also applies to: 106-106, 120-120

packages/cli/README.md (2)

452-452: LGTM!

Also applies to: 501-501, 594-594, 623-623


2578-2578: LGTM!

Comment thread packages/cli/src/lib/installations.ts Outdated
Comment thread packages/cli/README.md Outdated
Copy link
Copy Markdown

@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: 5

♻️ Duplicate comments (1)
packages/cli/src/lib/command.ts (1)

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

Non-CLIError validation failures are being mislabeled as internal bugs.

Line 50 now marks every non-CLIError as isBug. Several commands still throw plain Error for user validation, so expected usage failures can surface as kind: "bug" / internal_error and show the bug panel.

Proposed fix
-    const isBug = error instanceof BugError || !(error instanceof CLIError)
+    const isBug = error instanceof BugError || (!(error instanceof CLIError) && inferredCode === undefined)
🤖 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 `@packages/cli/src/lib/command.ts` at line 50, The current logic sets isBug for
any error that isn't a CLIError, causing plain Error-based validation failures
to be treated as internal bugs; update the check in command.ts so only explicit
BugError instances are treated as bugs (e.g., replace const isBug = error
instanceof BugError || !(error instanceof CLIError) with const isBug = error
instanceof BugError) and ensure any user-facing validation errors are thrown as
CLIError (or converted earlier) so they aren't misclassified.
🤖 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 `@packages/cli/src/commands/presence.ts`:
- Line 26: Add a blank line before the ensureWritable(flags) statement to
satisfy the `@stylistic/padding-line-between-statements` lint rule; locate the
ensureWritable(flags) call in the presence command (ensureWritable) and insert
one empty line above it so there is padding between the previous statement/block
and the ensureWritable(flags) invocation.

In `@packages/cli/src/commands/schema.ts`:
- Around line 27-29: The code builds requested by filtering this.argv which
contains raw tokens (flags and their values); change it to use the parsed argv
returned from await this.parse(Schema) (e.g., const parsed = await
this.parse(Schema)) and filter parsed.argv instead of this.argv so flag values
like "json" aren’t mistaken for positional paths; update the pathArgs
computation (variable name pathArgs) and the requested assignment to use
parsed.argv.

In `@packages/cli/src/commands/verify/approve.ts`:
- Line 16: Add a blank line before the ensureWritable(flags) call in the
approve.ts file to satisfy the `@stylistic/padding-line-between-statements` rule;
locate the ensureWritable(flags) invocation (and the surrounding write/client
setup code that uses flags) and insert a single empty line immediately above it
so the statement is separated from the preceding block.

In `@packages/cli/src/lib/command.ts`:
- Around line 147-167: The switch in errorCode is triggering stylistic lint
errors because there are no blank lines between case blocks; update the switch
in the errorCode function (the case entries for ExitCodes.Ambiguous,
AuthRequired, CommandNotFound, NotFound, NotReady, Usage, and the default) to
include a blank line between each case block (i.e., a blank line after each
closing brace/return) so `@stylistic/padding-line-between-statements` is
satisfied.

In `@packages/npm/scripts/build.ts`:
- Line 48: The generated launcher() function returns a template literal that
itself contains unescaped/backticked template literals (e.g., the line
containing console.error(`beeper-cli does not ship a binary for
${process.platform}/${process.arch}.`)), which prematurely close the outer
string; fix by replacing inner backticks with regular quotes or string
concatenation (or escape the inner backticks) inside launcher() so the outer
`return \`...\`` remains valid; search for other occurrences inside launcher()
of nested backticks and change them to use single/double quotes or +
concatenation, ensuring any ${...} interpolations intended in the generated code
remain correct.

---

Duplicate comments:
In `@packages/cli/src/lib/command.ts`:
- Line 50: The current logic sets isBug for any error that isn't a CLIError,
causing plain Error-based validation failures to be treated as internal bugs;
update the check in command.ts so only explicit BugError instances are treated
as bugs (e.g., replace const isBug = error instanceof BugError || !(error
instanceof CLIError) with const isBug = error instanceof BugError) and ensure
any user-facing validation errors are thrown as CLIError (or converted earlier)
so they aren't misclassified.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 80a53ca3-850f-4182-af2c-c5f19ff59e28

📥 Commits

Reviewing files that changed from the base of the PR and between 12ed611 and f22ce03.

📒 Files selected for processing (45)
  • README.md
  • docs/astro.config.mjs
  • packages/cli/README.md
  • packages/cli/scripts/generate-readme.ts
  • packages/cli/src/commands/accounts/add.ts
  • packages/cli/src/commands/auth/logout.ts
  • packages/cli/src/commands/chats/archive.ts
  • packages/cli/src/commands/chats/avatar.ts
  • packages/cli/src/commands/chats/description.ts
  • packages/cli/src/commands/chats/disappear.ts
  • packages/cli/src/commands/chats/draft.ts
  • packages/cli/src/commands/chats/focus.ts
  • packages/cli/src/commands/chats/mark-read.ts
  • packages/cli/src/commands/chats/mark-unread.ts
  • packages/cli/src/commands/chats/notify-anyway.ts
  • packages/cli/src/commands/chats/pin.ts
  • packages/cli/src/commands/chats/remind.ts
  • packages/cli/src/commands/chats/unarchive.ts
  • packages/cli/src/commands/chats/unmute.ts
  • packages/cli/src/commands/chats/unpin.ts
  • packages/cli/src/commands/chats/unremind.ts
  • packages/cli/src/commands/install/server.ts
  • packages/cli/src/commands/messages/export.ts
  • packages/cli/src/commands/presence.ts
  • packages/cli/src/commands/resolve/account.ts
  • packages/cli/src/commands/resolve/bridge.ts
  • packages/cli/src/commands/resolve/contact.ts
  • packages/cli/src/commands/schema.ts
  • packages/cli/src/commands/send/file.ts
  • packages/cli/src/commands/send/text.ts
  • packages/cli/src/commands/send/voice.ts
  • packages/cli/src/commands/setup.ts
  • packages/cli/src/commands/targets/add/desktop.ts
  • packages/cli/src/commands/targets/add/server.ts
  • packages/cli/src/commands/targets/remove.ts
  • packages/cli/src/commands/targets/start.ts
  • packages/cli/src/commands/targets/use.ts
  • packages/cli/src/commands/update.ts
  • packages/cli/src/commands/verify/approve.ts
  • packages/cli/src/lib/command-metadata.ts
  • packages/cli/src/lib/command.ts
  • packages/cli/src/lib/installations.ts
  • packages/cli/src/lib/output.ts
  • packages/cli/src/lib/resolve.ts
  • packages/npm/scripts/build.ts
🚧 Files skipped from review as they are similar to previous changes (22)
  • packages/cli/src/commands/chats/mark-unread.ts
  • packages/cli/src/commands/chats/unarchive.ts
  • packages/cli/src/commands/chats/notify-anyway.ts
  • packages/cli/src/commands/chats/avatar.ts
  • packages/cli/src/commands/targets/use.ts
  • packages/cli/src/commands/chats/draft.ts
  • packages/cli/src/commands/chats/unremind.ts
  • packages/cli/src/commands/chats/unmute.ts
  • packages/cli/src/commands/send/text.ts
  • packages/cli/src/commands/targets/start.ts
  • packages/cli/src/commands/chats/focus.ts
  • packages/cli/src/commands/send/file.ts
  • packages/cli/src/commands/targets/add/server.ts
  • packages/cli/src/commands/chats/pin.ts
  • docs/astro.config.mjs
  • packages/cli/src/commands/targets/add/desktop.ts
  • packages/cli/src/lib/command-metadata.ts
  • packages/cli/src/commands/chats/unpin.ts
  • packages/cli/src/commands/targets/remove.ts
  • packages/cli/scripts/generate-readme.ts
  • packages/cli/src/commands/chats/remind.ts
  • packages/cli/src/commands/accounts/add.ts
📜 Review details
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-05-27T22:40:16.795Z
Learnt from: juulieen
Repo: beeper/cli PR: 13
File: README.md:178-178
Timestamp: 2026-05-27T22:40:16.795Z
Learning: In beeper CLI documentation, the correct recovery-key verification command syntax is:
`beeper verify recovery-key -t <target> --key "<key>"`.
Use the top-level `verify` subcommand and pass the recovery key via the `--key` flag. Do not use the previously incorrect form like `beeper auth verify recovery-key --code KEY`—update any doc snippets to match the supported syntax.

Applied to files:

  • README.md
  • packages/cli/README.md
🪛 Biome (2.4.15)
packages/npm/scripts/build.ts

[error] 48-48: Expected a semicolon or an implicit semicolon after a statement, but found none

(parse)


[error] 48-48: Expected a semicolon or an implicit semicolon after a statement, but found none

(parse)


[error] 48-48: Expected a semicolon or an implicit semicolon after a statement, but found none

(parse)


[error] 48-48: Expected a semicolon or an implicit semicolon after a statement, but found none

(parse)


[error] 48-48: Expected a semicolon or an implicit semicolon after a statement, but found none

(parse)


[error] 48-48: Expected a semicolon or an implicit semicolon after a statement, but found none

(parse)


[error] 48-48: Expected a semicolon or an implicit semicolon after a statement, but found none

(parse)


[error] 48-48: expected ( but instead found $

(parse)


[error] 48-48: expected ; but instead found {

(parse)


[error] 48-48: expected , but instead found .

(parse)


[error] 48-48: expected ; but instead found {

(parse)


[error] 48-48: expected , but instead found .

(parse)


[error] 48-48: Expected an identifier but instead found '`'.

(parse)


[error] 58-58: expected ) but instead found beeper

(parse)


[error] 58-58: Expected a semicolon or an implicit semicolon after a statement, but found none

(parse)


[error] 58-58: Expected a semicolon or an implicit semicolon after a statement, but found none

(parse)


[error] 60-60: Expected a semicolon or an implicit semicolon after a statement, but found none

(parse)


[error] 62-62: await is only allowed within async functions and at the top levels of modules.

(parse)


[error] 63-63: await is only allowed within async functions and at the top levels of modules.

(parse)


[error] 64-64: await is only allowed within async functions and at the top levels of modules.

(parse)


[error] 66-66: await is only allowed within async functions and at the top levels of modules.

(parse)


[error] 68-68: await is only allowed within async functions and at the top levels of modules.

(parse)


[error] 73-73: await is only allowed within async functions and at the top levels of modules.

(parse)


[error] 75-75: await is only allowed within async functions and at the top levels of modules.

(parse)


[error] 77-77: await is only allowed within async functions and at the top levels of modules.

(parse)


[error] 78-78: await is only allowed within async functions and at the top levels of modules.

(parse)


[error] 79-79: await is only allowed within async functions and at the top levels of modules.

(parse)


[error] 80-80: await is only allowed within async functions and at the top levels of modules.

(parse)

🪛 ESLint
packages/cli/src/commands/presence.ts

[error] 26-26: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)

packages/cli/src/commands/chats/disappear.ts

[error] 26-26: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)

packages/cli/src/commands/messages/export.ts

[error] 40-40: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)

packages/cli/src/commands/resolve/account.ts

[error] 25-25: Use object destructuring.

(prefer-destructuring)


[error] 26-26: Unexpected negated condition.

(unicorn/no-negated-condition)

packages/cli/src/commands/resolve/contact.ts

[error] 33-33: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)


[error] 33-33: Use .length === 0 when checking length is zero.

(unicorn/explicit-length-check)


[error] 34-34: Use object destructuring.

(prefer-destructuring)


[error] 35-35: Unexpected negated condition.

(unicorn/no-negated-condition)

packages/cli/src/lib/installations.ts

[error] 157-157: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)

packages/cli/src/lib/command.ts

[error] 150-150: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)


[error] 153-153: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)


[error] 156-156: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)


[error] 159-159: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)


[error] 162-162: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)


[error] 165-165: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)

packages/cli/src/commands/auth/logout.ts

[error] 17-17: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)

packages/cli/src/commands/resolve/bridge.ts

[error] 32-32: Use object destructuring.

(prefer-destructuring)


[error] 33-33: Unexpected negated condition.

(unicorn/no-negated-condition)

packages/cli/src/commands/schema.ts

[error] 124-124: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)


[error] 127-127: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)


[error] 130-130: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)


[error] 133-133: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)

packages/cli/src/commands/verify/approve.ts

[error] 16-16: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)

packages/cli/src/lib/output.ts

[error] 127-127: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)


[error] 131-131: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)

🪛 GitHub Actions: CI / 0_test (ubuntu-latest _ bun 1.3.10).txt
packages/npm/scripts/build.ts

[error] 48-48: Typecheck failed at console.error template string. Error(s): Expected ';' but found 'beeper'; Expected ';' but found 'does'; Expected ';' but found 'ship'; Expected ';' but found 'binary'; Expected '(' but found '$'; Expected '}' but found '.'; Expected ';' but found 'platform'; Unexpected '}'.

🪛 GitHub Actions: CI / 1_test (macos-latest _ bun 1.3.10).txt
packages/npm/scripts/build.ts

[error] 48-48: typecheck failed: Syntax error in console.error template string. Error: Expected ";" but found "beeper". (at packages/npm/scripts/build.ts:48:18)


[error] 48-48: typecheck failed: Syntax error. Error: Expected ";" but found "does". (at packages/npm/scripts/build.ts:48:29)


[error] 48-48: typecheck failed: Syntax error. Error: Expected ";" but found "ship". (at packages/npm/scripts/build.ts:48:38)


[error] 48-48: typecheck failed: Syntax error. Error: Expected ";" but found "binary". (at packages/npm/scripts/build.ts:48:45)


[error] 48-48: typecheck failed: Syntax error. Error: Expected "(" but found "$". (at packages/npm/scripts/build.ts:48:56)


[error] 48-48: typecheck failed: Syntax error. Error: Expected "}" but found ".". (at packages/npm/scripts/build.ts:48:65)


[error] 48-48: typecheck failed: Syntax error. Error: Expected ";" but found "platform". (at packages/npm/scripts/build.ts:48:66)


[error] 48-48: typecheck failed: Syntax error. Error: Unexpected "}". (at packages/npm/scripts/build.ts:48:74)

🪛 GitHub Actions: CI / test (macos-latest _ bun 1.3.10)
packages/npm/scripts/build.ts

[error] 48-48: Typecheck failed: Expected ';' but found 'beeper'.


[error] 48-48: Typecheck failed: Expected ';' but found 'does'.


[error] 48-48: Typecheck failed: Expected ';' but found 'ship'.


[error] 48-48: Typecheck failed: Expected ';' but found 'binary'.


[error] 48-48: Typecheck failed: Expected '(' but found '$'.


[error] 48-48: Typecheck failed: Expected '}' but found '.'.


[error] 48-48: Typecheck failed: Expected ';' but found 'platform'.


[error] 48-48: Typecheck failed: Expected ';' but found 'does'. (Also reported: Unexpected '}' at line 48.)

🪛 GitHub Actions: CI / test (ubuntu-latest _ bun 1.3.10)
packages/npm/scripts/build.ts

[error] 48-48: TypeScript typecheck failed: Expected ";" but found "beeper". (at /home/runner/work/cli/cli/packages/npm/scripts/build.ts:48:18)


[error] 48-48: TypeScript typecheck failed: Expected ";" but found "does". (at /home/runner/work/cli/cli/packages/npm/scripts/build.ts:48:29)


[error] 48-48: TypeScript typecheck failed: Expected ";" but found "ship". (at /home/runner/work/cli/cli/packages/npm/scripts/build.ts:48:38)


[error] 48-48: TypeScript typecheck failed: Expected ";" but found "a". (at /home/runner/work/cli/cli/packages/npm/scripts/build.ts:48:45)


[error] 48-48: TypeScript typecheck failed: Expected "(" but found "$". (at /home/runner/work/cli/cli/packages/npm/scripts/build.ts:48:56)


[error] 48-48: TypeScript typecheck failed: Expected "}" but found ".". (at /home/runner/work/cli/cli/packages/npm/scripts/build.ts:48:65)


[error] 48-48: TypeScript typecheck failed: Expected ";" but found "platform". (at /home/runner/work/cli/cli/packages/npm/scripts/build.ts:48:66)


[error] 48-48: TypeScript typecheck failed: Unexpected "}". (at /home/runner/work/cli/cli/packages/npm/scripts/build.ts:48:74)

🔇 Additional comments (19)
packages/cli/src/commands/send/voice.ts (1)

24-26: LGTM!

Also applies to: 38-38, 42-44, 46-46

packages/cli/src/commands/chats/description.ts (1)

5-5: LGTM!

packages/cli/src/commands/chats/archive.ts (1)

5-5: LGTM!

packages/cli/src/commands/update.ts (1)

26-26: LGTM!

README.md (1)

8-10: LGTM!

Also applies to: 25-25, 205-205, 210-217

packages/cli/src/commands/chats/disappear.ts (1)

23-29: LGTM!

packages/cli/src/commands/install/server.ts (1)

6-6: LGTM!

Also applies to: 12-12

packages/cli/src/commands/setup.ts (1)

39-39: LGTM!

Also applies to: 173-174, 711-722

packages/cli/README.md (1)

9-10: LGTM!

Also applies to: 25-25, 205-217, 452-452, 501-501, 594-594, 623-623, 2578-2579

packages/cli/src/commands/auth/logout.ts (1)

10-10: LGTM!

Also applies to: 17-19

packages/cli/src/commands/resolve/bridge.ts (1)

32-34: LGTM!

packages/cli/src/commands/schema.ts (1)

121-135: LGTM!

packages/cli/src/lib/output.ts (1)

102-103: LGTM!

Also applies to: 127-134, 289-289

packages/cli/src/commands/messages/export.ts (1)

28-30: LGTM!

Also applies to: 40-42

packages/cli/src/commands/resolve/account.ts (1)

25-27: LGTM!

packages/cli/src/lib/resolve.ts (1)

39-40: LGTM!

packages/cli/src/commands/resolve/contact.ts (1)

28-30: LGTM!

Also applies to: 34-36, 58-63

packages/cli/src/commands/chats/mark-read.ts (1)

5-5: LGTM!

Also applies to: 17-19

packages/cli/src/lib/installations.ts (1)

151-157: LGTM!

Comment thread packages/cli/src/commands/presence.ts
Comment thread packages/cli/src/commands/schema.ts Outdated
Comment thread packages/cli/src/commands/verify/approve.ts
Comment thread packages/cli/src/lib/command.ts
Comment thread packages/npm/scripts/build.ts Outdated
Comment thread packages/cli/src/lib/command.ts Outdated
process.exitCode = process.exitCode ?? code
const tryMessage = error instanceof CLIError ? error.tryMessage : undefined
const isBug = !(error instanceof CLIError) || error instanceof BugError
const isBug = error instanceof BugError || !(error instanceof CLIError)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

My earlier suggestion overshot — plain throw new Error(...) is now misclassified as kind:'bug'/internal_error: there are 58 sites under packages/cli/src/commands that throw plain Error for user-validation failures (e.g. setup.ts:56,58,60 “Specify at most one of …”, chats/avatar.ts:14 “Provide --file or --clear”, presence.ts:20-21 “--duration must be a positive integer”, every accounts/add.ts validation). With isBug = error instanceof BugError || !(error instanceof CLIError), all of those now print the bug panel and emit internal_error to JSON consumers. Two clean fixes: (A) restore && inferredCode === undefined and tighten the inferExitCode regexes with \b boundaries (drop the bare usage/invalid/required/parse tokens that match library messages like Cannot find required module); or (B) convert every throw new Error(...) in packages/cli/src/commands/** to throw usageError(...)/notFound/etc. from packages/cli/src/lib/errors.ts and add a lint rule banning plain throw new Error(...) in command files.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@indent Fixed in 3bf94b7. I restored non-CLIError bug classification to depend on inferredCode === undefined and tightened the usage inference regex so ordinary runtime messages like “Cannot find required module” do not get classified as usage errors.

  • Batuhan's agent

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The && inferredCode === undefined guard is restored, and the tightened regex (\binvalid (?:argument|option|flag|value|input)\b, \brequired (?:flag|argument|option|value)\b, \bparse error\b, etc.) correctly stops library messages like Cannot find required module 'foo' from being downgraded to usage errors. Good fix for the library-error regression I called out in 49b0ca88.

Heads up though: my framing of the "58 plain throws" was misleading — I should have flagged this as a pre-existing classification gap rather than a new regression. Re-checking against the actual messages:

  • setup.ts:56-60 "Specify at most one of …" / "Use --local or --oauth with …"
  • chats/avatar.ts:14 "Provide --file or --clear", chats/draft.ts:22-23 "Provide --text TEXT …" / "--clear cannot be combined …"
  • chats/disappear.ts:21 "--seconds must be a positive integer …"
  • presence.ts:20-21 "--duration must be a positive integer …" / "--duration only applies when …"
  • messages/export.ts:25,47-48 "Use only one of --before-cursor or --after-cursor" / "--after is not a valid ISO timestamp"
  • api/post.ts:26 "--body is not valid JSON …"
  • accounts/add.ts "Multiple sign-in methods …", "No login flows returned …", "… must use name=value form."
  • setup.ts:236 "Install requires --install --yes in non-interactive mode."

None of these match either the old loose regex or the new tight one, so they've been classified as kind:"bug" / internal_error the whole time (not something ffcff36 introduced). The proper fix is option (B) from my earlier prompt — swap each of these throw new Error(...) sites for throw usageError(...) (already exported from packages/cli/src/lib/errors.ts:65) so they render as one-line aborts with exit 2 / code:"usage_error". Happy to leave it out of this PR if you want a follow-up, but I'll keep it tracked as a separate functional issue since the current behavior is misleading for JSON consumers.

Comment thread packages/npm/scripts/build.ts Outdated
Comment thread packages/cli/src/commands/schema.ts Outdated
Comment thread .claude/scheduled_tasks.lock Outdated
Copy link
Copy Markdown

@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

🧹 Nitpick comments (4)
packages/cli/src/commands/api/request.ts (1)

28-32: ⚡ Quick win

Resolve the @stylistic/padding-line-between-statements lint errors.

Static analysis flags missing blank lines before the statements at Lines 28 and 32. Add the expected blank lines to pass the lint check.

🤖 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 `@packages/cli/src/commands/api/request.ts` around lines 28 - 32, Add the
missing blank lines to satisfy the padding-line-between-statements lint rule by
inserting a blank line before the conditional branch and before the final await
call: ensure there is an empty line before the "if (flags['no-auth']) { … }"
block's first statement that calls printData/appRequest and also an empty line
before the final "await printData(await appRequest(...), format)" call; locate
the usage of flags['no-auth'], appRequest, printData, method and args.path in
request.ts and add the blank lines accordingly.
packages/cli/src/commands/api/post.ts (1)

28-37: ⚡ Quick win

Resolve the @stylistic/padding-line-between-statements lint errors.

Static analysis flags missing blank lines before the statements at Lines 28, 32, and 36, which will fail the lint check. Add the expected blank lines before these block statements.

♻️ Proposed fix
     } catch {
       throw new Error(`--body is not valid JSON: ${flags.body}`)
     }
+
     if (flags['dry-run']) {
       await printDryRun('api.post', { method: 'POST', path: args.path, body, noAuth: flags['no-auth'], target: flags.target, baseURL: flags['base-url'] }, format)
       return
     }
+
     if (flags['no-auth']) {
       await printData(await appRequest('POST', args.path, { baseURL: flags['base-url'], body, target: flags.target, token: false }), format)
       return
     }
+
     const client = await createClient(flags)
🤖 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 `@packages/cli/src/commands/api/post.ts` around lines 28 - 37, The lint rule
requires blank lines separating top-level statement blocks; add a single empty
line before the "if (flags['dry-run'])" block, before the "if
(flags['no-auth'])" block, and before the "const client = await
createClient(flags)" statement in this function so there is a blank line between
each branch; keep the existing code and callsites (printDryRun, appRequest,
printData, createClient, client.post) unchanged and only insert those padding
blank lines.
packages/cli/src/commands/auth/logout.ts (1)

32-32: ⚡ Quick win

Resolve the @stylistic/padding-line-between-statements lint error.

Static analysis flags a missing blank line before the printSuccess statement at Line 32.

♻️ Proposed fix
       await clearTargetAuth(target)
     }
+
     await printSuccess({ message: 'Logged out', detail: token ? 'local token cleared' : 'no token was stored', data: { revoked, hadToken: Boolean(token) } }, format)
🤖 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 `@packages/cli/src/commands/auth/logout.ts` at line 32, Add a blank line before
the printSuccess invocation to satisfy the
`@stylistic/padding-line-between-statements` rule: inside the logout command in
logout.ts, insert a single empty line immediately above the await
printSuccess(...) call (the call that uses token, revoked and hadToken) so there
is a separating blank line between the preceding statement and the printSuccess
await.
packages/cli/src/commands/config/set.ts (1)

18-22: ⚡ Quick win

Resolve the @stylistic/padding-line-between-statements lint error.

Static analysis flags a missing blank line before the updateConfig statement at Line 22.

♻️ Proposed fix
     if (flags['dry-run']) {
       await printDryRun('config.set', { [args.key]: nextValue }, format)
       return
     }
+
     await updateConfig(config => ({ ...config, [args.key]: nextValue }))
🤖 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 `@packages/cli/src/commands/config/set.ts` around lines 18 - 22, The linter
wants a blank line separating the conditional dry-run branch from the subsequent
side-effect call; inside the config.set logic, add one empty line between the
end of the if (flags['dry-run']) { ... return } block and the await
updateConfig(...) call so that printDryRun/return is visually separated from
updateConfig; locate the flags['dry-run'] check and the await updateConfig(...)
invocation and insert a single blank line before updateConfig.
🤖 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 `@packages/cli/src/commands/api/get.ts`:
- Around line 19-26: Add a blank line before the declaration of const client to
satisfy the padding-line-between-statements rule: after the early-return branch
that calls printData(await appRequest(...)) leave one empty line, then declare
const client = await createClient(flags) and continue with await printData(await
client.get(args.path), format); this touches the block using format, appRequest,
createClient, printData, flags and args.path.

In `@packages/cli/src/commands/config/reset.ts`:
- Around line 12-17: The padding-line lint rule is failing because there is no
blank line between the early return block and the subsequent reset call; add a
single blank line between the if-block that handles flags['dry-run']/printDryRun
and the await resetConfig() call so the sequence (printDryRun -> return) is
visually separated from resetConfig(), then keep the following await
printSuccess({ message: 'Config reset' }, format) as-is; this involves editing
the code around printDryRun, resetConfig, and printSuccess in
packages/cli/src/commands/config/reset.ts.

In `@packages/cli/src/commands/verify/cancel.ts`:
- Around line 16-18: Add a blank line before the declaration of const client in
the verify cancel command to satisfy the
`@stylistic/padding-line-between-statements` ESLint rule: locate the function in
cancel.ts where createClient(flags) is called, and insert an empty line between
the preceding statement/block and the line calling createClient so the sequence
reads with a blank line before const client; keep the rest of the logic using
createClient, client.app.verifications.cancel(flags.id ?? 'active', {}), and
printData unchanged.

---

Nitpick comments:
In `@packages/cli/src/commands/api/post.ts`:
- Around line 28-37: The lint rule requires blank lines separating top-level
statement blocks; add a single empty line before the "if (flags['dry-run'])"
block, before the "if (flags['no-auth'])" block, and before the "const client =
await createClient(flags)" statement in this function so there is a blank line
between each branch; keep the existing code and callsites (printDryRun,
appRequest, printData, createClient, client.post) unchanged and only insert
those padding blank lines.

In `@packages/cli/src/commands/api/request.ts`:
- Around line 28-32: Add the missing blank lines to satisfy the
padding-line-between-statements lint rule by inserting a blank line before the
conditional branch and before the final await call: ensure there is an empty
line before the "if (flags['no-auth']) { … }" block's first statement that calls
printData/appRequest and also an empty line before the final "await
printData(await appRequest(...), format)" call; locate the usage of
flags['no-auth'], appRequest, printData, method and args.path in request.ts and
add the blank lines accordingly.

In `@packages/cli/src/commands/auth/logout.ts`:
- Line 32: Add a blank line before the printSuccess invocation to satisfy the
`@stylistic/padding-line-between-statements` rule: inside the logout command in
logout.ts, insert a single empty line immediately above the await
printSuccess(...) call (the call that uses token, revoked and hadToken) so there
is a separating blank line between the preceding statement and the printSuccess
await.

In `@packages/cli/src/commands/config/set.ts`:
- Around line 18-22: The linter wants a blank line separating the conditional
dry-run branch from the subsequent side-effect call; inside the config.set
logic, add one empty line between the end of the if (flags['dry-run']) { ...
return } block and the await updateConfig(...) call so that printDryRun/return
is visually separated from updateConfig; locate the flags['dry-run'] check and
the await updateConfig(...) invocation and insert a single blank line before
updateConfig.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 888ea263-2436-4542-a190-b1662ae9d9a0

📥 Commits

Reviewing files that changed from the base of the PR and between f22ce03 and fefbf4e.

📒 Files selected for processing (53)
  • .gitignore
  • packages/cli/src/commands/api/get.ts
  • packages/cli/src/commands/api/post.ts
  • packages/cli/src/commands/api/request.ts
  • packages/cli/src/commands/auth/email/response.ts
  • packages/cli/src/commands/auth/logout.ts
  • packages/cli/src/commands/bridges/show.ts
  • packages/cli/src/commands/chats/archive.ts
  • packages/cli/src/commands/chats/avatar.ts
  • packages/cli/src/commands/chats/description.ts
  • packages/cli/src/commands/chats/focus.ts
  • packages/cli/src/commands/chats/mark-read.ts
  • packages/cli/src/commands/chats/mark-unread.ts
  • packages/cli/src/commands/chats/notify-anyway.ts
  • packages/cli/src/commands/chats/pin.ts
  • packages/cli/src/commands/chats/remind.ts
  • packages/cli/src/commands/chats/unarchive.ts
  • packages/cli/src/commands/chats/unmute.ts
  • packages/cli/src/commands/chats/unpin.ts
  • packages/cli/src/commands/chats/unremind.ts
  • packages/cli/src/commands/config/reset.ts
  • packages/cli/src/commands/config/set.ts
  • packages/cli/src/commands/doctor.ts
  • packages/cli/src/commands/media/download.ts
  • packages/cli/src/commands/messages/search.ts
  • packages/cli/src/commands/plugins/available.ts
  • packages/cli/src/commands/presence.ts
  • packages/cli/src/commands/resolve/chat.ts
  • packages/cli/src/commands/schema.ts
  • packages/cli/src/commands/setup.ts
  • packages/cli/src/commands/targets/disable.ts
  • packages/cli/src/commands/targets/enable.ts
  • packages/cli/src/commands/targets/list.ts
  • packages/cli/src/commands/targets/logs.ts
  • packages/cli/src/commands/targets/remove.ts
  • packages/cli/src/commands/targets/restart.ts
  • packages/cli/src/commands/targets/show.ts
  • packages/cli/src/commands/targets/start.ts
  • packages/cli/src/commands/targets/status.ts
  • packages/cli/src/commands/targets/stop.ts
  • packages/cli/src/commands/targets/use.ts
  • packages/cli/src/commands/verify/approve.ts
  • packages/cli/src/commands/verify/cancel.ts
  • packages/cli/src/lib/command-metadata.ts
  • packages/cli/src/lib/command.ts
  • packages/cli/src/lib/export/index.ts
  • packages/cli/src/lib/ink/render.tsx
  • packages/cli/src/lib/ink/theme.ts
  • packages/cli/src/lib/installations.ts
  • packages/cli/src/lib/profiles.ts
  • packages/cli/src/lib/resolve.ts
  • packages/cli/src/lib/setup-login.ts
  • packages/npm/scripts/build.ts
💤 Files with no reviewable changes (14)
  • packages/cli/src/lib/export/index.ts
  • packages/cli/src/commands/chats/unmute.ts
  • packages/cli/src/commands/chats/unarchive.ts
  • packages/cli/src/commands/chats/unpin.ts
  • packages/cli/src/commands/chats/mark-read.ts
  • packages/cli/src/commands/chats/mark-unread.ts
  • packages/cli/src/commands/chats/archive.ts
  • packages/cli/src/commands/chats/pin.ts
  • packages/cli/src/commands/chats/focus.ts
  • packages/cli/src/commands/chats/remind.ts
  • packages/cli/src/commands/chats/notify-anyway.ts
  • packages/cli/src/commands/chats/avatar.ts
  • packages/cli/src/commands/chats/unremind.ts
  • packages/cli/src/commands/chats/description.ts
✅ Files skipped from review due to trivial changes (7)
  • .gitignore
  • packages/cli/src/commands/plugins/available.ts
  • packages/cli/src/commands/targets/list.ts
  • packages/cli/src/lib/ink/theme.ts
  • packages/cli/src/commands/targets/logs.ts
  • packages/cli/src/commands/targets/show.ts
  • packages/cli/src/commands/targets/status.ts
🚧 Files skipped from review as they are similar to previous changes (14)
  • packages/cli/src/commands/media/download.ts
  • packages/cli/src/commands/targets/enable.ts
  • packages/cli/src/commands/targets/start.ts
  • packages/cli/src/commands/targets/disable.ts
  • packages/cli/src/commands/targets/restart.ts
  • packages/cli/src/commands/verify/approve.ts
  • packages/cli/src/commands/presence.ts
  • packages/cli/src/commands/schema.ts
  • packages/cli/src/lib/command-metadata.ts
  • packages/npm/scripts/build.ts
  • packages/cli/src/commands/auth/email/response.ts
  • packages/cli/src/commands/targets/remove.ts
  • packages/cli/src/lib/installations.ts
  • packages/cli/src/lib/command.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: test (macos-latest / bun 1.3.10)
  • GitHub Check: test (ubuntu-latest / bun 1.3.10)
  • GitHub Check: test (ubuntu-latest / bun 1.3.10)
🧰 Additional context used
🪛 ESLint
packages/cli/src/commands/config/set.ts

[error] 22-22: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)

packages/cli/src/commands/auth/logout.ts

[error] 32-32: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)

packages/cli/src/commands/api/post.ts

[error] 28-28: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)


[error] 32-32: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)


[error] 36-36: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)

packages/cli/src/commands/setup.ts

[error] 140-140: Use switch instead of multiple else-if.

(unicorn/prefer-switch)

packages/cli/src/commands/api/get.ts

[error] 24-24: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)

packages/cli/src/commands/api/request.ts

[error] 28-28: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)


[error] 32-32: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)

packages/cli/src/commands/config/reset.ts

[error] 16-16: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)

packages/cli/src/commands/verify/cancel.ts

[error] 17-17: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)

🔇 Additional comments (13)
packages/cli/src/commands/resolve/chat.ts (1)

19-59: LGTM!

packages/cli/src/commands/setup.ts (2)

110-197: LGTM!


39-39: ⚡ Quick win

Update concern: default/alias regression scope and oclif ordering. This CLI defines --server-env with default: 'prod' (not 'production'), and options: [...SERVER_ENVIRONMENTS] plus parse: normalizeServerEnv. normalizeServerEnv maps 'production''prod', but whether 'production' is accepted depends on whether oclif validates the options list against the raw input before running the custom parse function; the available evidence doesn’t show that ordering precisely.

packages/cli/src/commands/targets/stop.ts (1)

13-22: LGTM!

packages/cli/src/commands/targets/use.ts (1)

9-21: LGTM!

packages/cli/src/commands/api/request.ts (1)

24-27: 💤 Low value

Confirm intentional GET pass-through under --dry-run.

--dry-run only short-circuits for non-GET methods, so api request GET … --dry-run still performs the live request. This is consistent with the ensureWritable gating at Line 21 (reads are side-effect-free), so it looks intentional—just confirming it's expected.

packages/cli/src/commands/bridges/show.ts (1)

33-39: LGTM!

packages/cli/src/commands/doctor.ts (1)

13-19: LGTM!

packages/cli/src/commands/messages/search.ts (1)

61-68: LGTM!

packages/cli/src/lib/ink/render.tsx (1)

1-1: LGTM!

Also applies to: 31-31, 130-143

packages/cli/src/lib/profiles.ts (1)

148-162: LGTM!

packages/cli/src/lib/resolve.ts (1)

60-103: LGTM!

packages/cli/src/lib/setup-login.ts (1)

31-43: LGTM!

Comment on lines +19 to 26
const format = flags.json ? 'json' : 'human'
if (flags['no-auth']) {
await printData(await appRequest('GET', args.path, { baseURL: flags['base-url'], target: flags.target, token: false }), flags.json ? 'json' : 'human')
await printData(await appRequest('GET', args.path, { baseURL: flags['base-url'], target: flags.target, token: false }), format)
return
}
const client = await createClient(flags)
await printData(await client.get(args.path), flags.json ? 'json' : 'human')
await printData(await client.get(args.path), format)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Centralizing format is a clean improvement. One lint blocker: ESLint flags a missing blank line before const client on Line 24 (@stylistic/padding-line-between-statements, error severity), which will fail CI.

🔧 Proposed fix
       return
     }
+
     const client = await createClient(flags)
     await printData(await client.get(args.path), format)
📝 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
const format = flags.json ? 'json' : 'human'
if (flags['no-auth']) {
await printData(await appRequest('GET', args.path, { baseURL: flags['base-url'], target: flags.target, token: false }), flags.json ? 'json' : 'human')
await printData(await appRequest('GET', args.path, { baseURL: flags['base-url'], target: flags.target, token: false }), format)
return
}
const client = await createClient(flags)
await printData(await client.get(args.path), flags.json ? 'json' : 'human')
await printData(await client.get(args.path), format)
}
const format = flags.json ? 'json' : 'human'
if (flags['no-auth']) {
await printData(await appRequest('GET', args.path, { baseURL: flags['base-url'], target: flags.target, token: false }), format)
return
}
const client = await createClient(flags)
await printData(await client.get(args.path), format)
}
🧰 Tools
🪛 ESLint

[error] 24-24: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)

🤖 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 `@packages/cli/src/commands/api/get.ts` around lines 19 - 26, Add a blank line
before the declaration of const client to satisfy the
padding-line-between-statements rule: after the early-return branch that calls
printData(await appRequest(...)) leave one empty line, then declare const client
= await createClient(flags) and continue with await printData(await
client.get(args.path), format); this touches the block using format, appRequest,
createClient, printData, flags and args.path.

Comment on lines +12 to +17
if (flags['dry-run']) {
await printDryRun('config.reset', {}, format)
return
}
await resetConfig()
await printSuccess({ message: 'Config reset' }, flags.json ? 'json' : 'human')
await printSuccess({ message: 'Config reset' }, format)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add a blank line before resetConfig() to satisfy the padding-line lint rule.

ESLint (@stylistic/padding-line-between-statements) flags Line 16. The dry-run/format logic itself is correct.

🎨 Proposed fix
     if (flags['dry-run']) {
       await printDryRun('config.reset', {}, format)
       return
     }
+
     await resetConfig()
     await printSuccess({ message: 'Config reset' }, format)
📝 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
if (flags['dry-run']) {
await printDryRun('config.reset', {}, format)
return
}
await resetConfig()
await printSuccess({ message: 'Config reset' }, flags.json ? 'json' : 'human')
await printSuccess({ message: 'Config reset' }, format)
if (flags['dry-run']) {
await printDryRun('config.reset', {}, format)
return
}
await resetConfig()
await printSuccess({ message: 'Config reset' }, format)
🧰 Tools
🪛 ESLint

[error] 16-16: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)

🤖 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 `@packages/cli/src/commands/config/reset.ts` around lines 12 - 17, The
padding-line lint rule is failing because there is no blank line between the
early return block and the subsequent reset call; add a single blank line
between the if-block that handles flags['dry-run']/printDryRun and the await
resetConfig() call so the sequence (printDryRun -> return) is visually separated
from resetConfig(), then keep the following await printSuccess({ message:
'Config reset' }, format) as-is; this involves editing the code around
printDryRun, resetConfig, and printSuccess in
packages/cli/src/commands/config/reset.ts.

Comment on lines +16 to 18
}
const client = await createClient(flags)
await printData(await client.app.verifications.cancel(flags.id ?? 'active', {}), flags.json ? 'json' : 'human')
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Dry-run wiring looks correct. One lint blocker: ESLint requires a blank line before const client on Line 17 (@stylistic/padding-line-between-statements, error severity), which will fail CI.

🔧 Proposed fix
       return
     }
+
     const client = await createClient(flags)
📝 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
}
const client = await createClient(flags)
await printData(await client.app.verifications.cancel(flags.id ?? 'active', {}), flags.json ? 'json' : 'human')
}
const client = await createClient(flags)
await printData(await client.app.verifications.cancel(flags.id ?? 'active', {}), flags.json ? 'json' : 'human')
🧰 Tools
🪛 ESLint

[error] 17-17: Expected blank line before this statement.

(@stylistic/padding-line-between-statements)

🤖 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 `@packages/cli/src/commands/verify/cancel.ts` around lines 16 - 18, Add a blank
line before the declaration of const client in the verify cancel command to
satisfy the `@stylistic/padding-line-between-statements` ESLint rule: locate the
function in cancel.ts where createClient(flags) is called, and insert an empty
line between the preceding statement/block and the line calling createClient so
the sequence reads with a blank line before const client; keep the rest of the
logic using createClient, client.app.verifications.cancel(flags.id ?? 'active',
{}), and printData unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant