Skip to content

feat(cli): add -q/--quiet to print only the result or the error - #2576

Merged
cliffhall merged 6 commits into
v2/mainfrom
v2/feat/2435-cli-quiet-flag
Oct 5, 2026
Merged

cliffhall merged 6 commits into
v2/mainfrom
v2/feat/2435-cli-quiet-flag

Conversation

@cliffhall

Copy link
Copy Markdown
Member

Closes #2435

Adds -q / --quiet to the CLI. With it, a run prints only the result payload on stdout, or on failure only the single-line error envelope on stderr.

What the CLI actually emitted, and what --quiet does with each

Output Where Under --quiet
A stdio server's own stderr (banners, logs): inherited by the child, so it landed in the CLI's stderr cli.ts (InspectorClient) Dropped. pipeStderr: true. InspectorClient drains the pipe into its stderrLog event, which nothing subscribes to, so the child never blocks on a full pipe. No core/ change.
Schema portability: … Re-run with --strict for details. schema-lint-report.ts Dropped
--verify one-line summary consume-outcome.ts Dropped. A failing run's envelope carries the same text as its message.
Authorization complete. / Authorization complete. Retrying… cliOAuth.ts Dropped
Warning: could not revoke the OAuth grant … (--relogin) cli.ts Dropped. The relogin and the revocation attempt still happen.
--strict report schema-lint-report.ts Kept. The caller asked for it explicitly, and it is the detail behind exit 6, which the envelope only counts.
OAuth authorization URL, the step-up [y/N], and "open it by hand" cli-oauth-navigation.ts, cliOAuth.ts Kept. Interactive login can't finish without them. For runs that must never prompt, use --quiet --stored-auth-only.
Error envelope error-handler.ts Kept

Decisions I made without asking:

  • --quiet is independent of --format: --format shapes stdout, and --quiet empties stderr.
  • Dropping the server's stderr also drops its own explanation when it fails to start. The README says so and suggests re-running without --quiet.

The flag is documented in clients/cli/README.md (options table, plus a new "Quiet output (--quiet)" section).

Tests

  • cli.test.ts: -q and --quiet against the stdio test server: valid JSON on stdout, empty stderr.
  • e2e.test.ts (built binary, out of process): a --import preload makes the real test server write a line to stderr before it starts. The line passes through without -q and is absent with -q. This half can't be asserted in-process, because the child's inherited fd 2 bypasses the patched process.stderr.write.
  • cliOAuth.test.ts: no status lines in quiet mode on both retry branches and on connect, and the step-up prompt still prints.
  • schema-lint-report.test.ts, skills-verify-cli.test.ts, relogin-revocation.test.ts: the hint, summary and warning are suppressed, and the --strict report and the exit code / envelope message are kept.

Gate

Run with the sandbox proxy env unset (env -u HTTP_PROXY -u HTTPS_PROXY …). With an ambient proxy set, relogin-revocation.test.ts fails both on v2/main and on this branch.

  • local:validate and verify:skills:cli: pass.
  • coverage:web: 1 environmental failure, unrelated to this change. secret-store-selection.test.ts > isOnMountPoint > is false when the path itself can't be resolved fails in this sandbox on its own, because the worktree sits on a mounted volume, so a relative path that can't be resolved still lands on a mount. This PR touches only clients/cli.
  • coverage:cli (459 tests, every file ≥ 90 on all four dimensions), coverage:tui, coverage:launcher, verify:build-gate, verify:bundle-externals: pass.
  • smoke, smoke:web:firefox, local:storybook: still queued behind the machine-wide gate lease (about 20 gates deep). I'll post their result as a comment.

🤖 Generated with Claude Code

`--quiet` drops the CLI's non-essential output so a scripted caller gets the
result payload on stdout and, on failure, only the error envelope on stderr:

- a stdio server's own stderr (piped and drained instead of inherited)
- the non-strict schema-portability hint on tools/list
- the --verify one-line summary (a failing run's envelope carries it)
- "Authorization complete." / "Authorization complete. Retrying…"
- the --relogin revocation-failure warning

Kept on purpose: the --strict report (explicitly requested; the detail
behind exit 6), and everything a human must act on for interactive OAuth
(authorization URL, step-up [y/N], "open it by hand").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
@cliffhall cliffhall added the v2 Issues and PRs for v2 label Oct 5, 2026
@cliffhall cliffhall linked an issue Oct 5, 2026 that may be closed by this pull request
2 tasks done
@cliffhall
cliffhall requested a balanced review from Copilot October 5, 2026 06:15

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Commander parse failures can still emit diagnostics before the error envelope, violating quiet mode’s single-line stderr contract.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Adds -q/--quiet to the CLI for script-friendly output, addressing #2435.

Changes:

  • Suppresses advisory output and stdio server stderr.
  • Preserves results, error envelopes, explicit strict reports, and interactive OAuth prompts.
  • Adds documentation and coverage across CLI, OAuth, linting, and E2E paths.
File Description
clients/​cli/​src/​cli.ts Defines and propagates quiet mode.
clients/​cli/​src/​cliOAuth.ts Suppresses OAuth status messages.
clients/​cli/​src/​handlers/​method-types.ts Adds the quiet method option.
clients/​cli/​src/​handlers/​emit-result.ts Passes quiet mode to schema reporting.
clients/​cli/​src/​handlers/​consume-outcome.ts Suppresses verification summaries.
clients/​cli/​src/​handlers/​schema-lint-report.ts Suppresses advisory lint hints.
clients/​cli/​README.md Documents quiet-mode behavior.
clients/​cli/​__tests__/​cli.test.ts Tests both flag spellings.
clients/​cli/​__tests__/​e2e.test.ts Tests child stderr suppression.
clients/​cli/​__tests__/​cliOAuth.test.ts Tests quiet OAuth behavior.
clients/​cli/​__tests__/​relogin-revocation.test.ts Tests warning suppression.
clients/​cli/​__tests__/​schema-lint-report.test.ts Tests lint output behavior.
clients/​cli/​__tests__/​skills-verify-cli.test.ts Tests verification summary suppression.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread clients/cli/src/cli.ts
A usage error made Commander write its own error line during parse(),
before the envelope, so --quiet stderr was two lines. The error is still
thrown and reaches the envelope with the same message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

HTTP/SSE quiet runs can still emit core secret-store warnings to stderr, violating the feature’s output contract.

Review effort: Balanced
Findings: None

Resolved since last review (1)
Previously missed (2)

In code that hasn't changed since last review

Medium severity Quiet mode leaks secret-store warnings on HTTP/SSE runs

clients/​cli/​src/​cli.ts:188

--quiet still leaks core secret-store warnings on HTTP/SSE runs. During InspectorClient.connect(), isOAuthAuthorized() reads NodeOAuthStorage; when the keychain is unavailable (or MCP_INSPECTOR_SECRET_STORE=memory is configured), resolveSecretStore() calls warnAboutSecretStorage() via console.warn, bypassing every new quiet check. A successful quiet invocation therefore has non-empty stderr. Route these diagnostics through a quiet-aware sink while preserving the intentional OAuth prompts/strict report, and cover an HTTP quiet run with the memory store selected.

Low severity Clarify quiet mode suppresses non-essential stderr

clients/​cli/​README.md:157

This says --quiet empties stderr, but the table immediately below documents intentional stderr output for --strict and interactive OAuth. Describe it as suppressing non-essential stderr instead so the user-facing contract is not contradictory.

…2435)

resolveSecretStore() announces a keychain fallback or a store caveat
(e.g. memory) with console.warn on first use, so an HTTP/SSE --quiet run
on a box without a keychain still printed it. Core caches the resolution,
so the CLI now settles it once at parse time with console.warn muted; no
core change, and the selected store is unaffected.

Also reword the README so it no longer says --quiet empties stderr, since
the table below lists what it deliberately keeps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
@cliffhall

Copy link
Copy Markdown
Member Author

Copilot review: rounds 1 and 2

Round 1:

  • Quiet mode leaks Commander parse diagnostics to stderr: fixed in 1851809 (answered in its thread).

Round 2 had no inline comments. Its two findings were in the body's "Previously missed" block, so they are answered here:

  • Quiet mode leaks secret-store warnings on HTTP/SSE runs: fixed in 37452a9. Core's resolveSecretStore() prints the keychain-fallback / memory caveat with console.warn on first use, and it caches the resolution. Under --quiet, parseArgs now settles it once with console.warn muted (clients/cli/src/quiet-secret-store.ts). No core/ change, and the selected store is unchanged. The test is out of process in e2e.test.ts (--relogin against a refused port, with the inherited MCP_INSPECTOR_SECRET_STORE=memory): the notice is present without -q, and with -q stderr is the envelope alone. The in-process runner can't observe this, because Vitest replaces console. I confirmed the -q case fails with the fix disabled.
  • Clarify quiet mode suppresses non-essential stderr: fixed in 37452a9. The README now says --quiet strips stderr of everything non-essential and points at the table of exceptions. The table also gained a row for the secret-store notice.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Quiet mode still leaks core warnings emitted outside the narrowly muted secret-store resolution.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)

Comment thread clients/cli/src/cli.ts Outdated
Core reports advisories with console.warn from many places the CLI
reaches (cleanRoots, OAuth endpoint overrides, oauth-persist, file-lock,
secret-store selection), so muting only the secret-store resolution left
the rest leaking. parseArgs now swaps console.warn for a no-op when -q is
in argv, and runCli restores it in finally so a module caller (the
launcher, the test runner) never inherits the mute. Replaces the narrower
quiet-secret-store helper. Everything --quiet keeps is written to the
streams directly, never through console.warn.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Combined short options such as -qe enable quiet mode without activating all pre-parse suppression.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
Resolved since last review (1)

Comment thread clients/cli/src/cli.ts Outdated
Commander accepts -qe KEY=V, which enabled quiet without the pre-parse
handling (Commander's writeErr and the console.warn mute). Read clusters
the way Commander does: q is the flag, and e takes a value, so anything
after it in the token is that value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Pre-parse flag detection incorrectly activates quiet behavior when -q is a required option value.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
Resolved since last review (1)

Comment thread clients/cli/src/cli.ts Outdated
…can (#2435)

A -q that is another option's value (--client-secret -q) was taken for the
flag. Commander's error output is now buffered and written from
exitOverride unless opts().quiet is already set, and the console.warn mute
moves to just after parse(). Combined -qe still counts because Commander
parses it natively; argvRequestsQuiet is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟢 Approval recommended

The quiet-output behavior is consistently propagated, documented, and covered across parsing, OAuth, schema, verification, and subprocess paths.

Review effort: Balanced
Findings: None

Resolved since last review (1)

@cliffhall

Copy link
Copy Markdown
Member Author

Gate follow-up. The stages that were still queued on the lease have now passed: smoke (launcher, cli, tui, web, web:browser/app/elicit/tabs on chromium), smoke:web:firefox and local:storybook (123 files / 528 tests). Every local:gate stage has now passed except the one environmental coverage:web test noted in the description (isOnMountPoint, which fails in this sandbox because the worktree sits on a mounted volume, and is unrelated to clients/cli). After each later review fix, the clients/cli check plus test:coverage were re-run green.

@cliffhall

Copy link
Copy Markdown
Member Author

Copilot review loop closed: round 6 was clean (no inline comments, no suppressed or previously-missed findings, and the headline is "Approval recommended"), which is the stop exit.

Rounds 3 to 5 (each answered in its own thread):

  • r3, apply quiet warning suppression across the full invocation: fixed in f003441. console.warn is muted for the whole run and restored in runCli's finally. This replaced the narrower secret-store helper.
  • r4, detect -q in combined short options: fixed in 7f616de. That fix was then superseded by r5.
  • r5, don't treat a dash-prefixed option value as the flag: fixed in 0a1c0d4. Quiet is now decided from Commander's parsed state (buffered writeErr and an exitOverride check), not an argv scan.

Nothing was declined, and no follow-up issues were filed.

@cliffhall
cliffhall merged commit 7a29605 into v2/main Oct 5, 2026
6 checks passed
@cliffhall
cliffhall deleted the v2/feat/2435-cli-quiet-flag branch October 5, 2026 14:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v2 Issues and PRs for v2

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CLI has no --quiet/--silent flag to suppress non-essential output

2 participants