Address @claude review of #1583:
- statusOf() now only treats a numeric `.code` as an HTTP status when it
is in the 100-599 range, so an MCP SDK McpError JSON-RPC code (e.g.
-32601 MethodNotFound) is no longer leaked into the envelope `status`
or misclassified as AUTH_REQUIRED (it falls back to USAGE).
- causeOf() now carries a depth cap (MAX_CAUSE_DEPTH) so a cyclic/
self-referential `error.cause` chain terminates instead of recursing
infinitely.
- Add tests for both: a -32601 McpError-shaped error (status undefined,
exit USAGE) and a self-referential cause chain (finite string).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S3fTN8H3R8YV4yUGvZjYnX
Give the CLI a stable machine-readable failure surface:
- EXIT_CODES map (0 ok / 1 usage / 2 no-app / 3 auth-required /
4 unreachable / 5 tool-error)
- ErrorEnvelope interface + CliExitCodeError for pre-classified exits
- pure classifyError() (status/cause/pattern heuristics) and
formatErrorOutput()/handleError() that emit one JSON line on stderr
- in-process cli-runner mirrors the binary via formatErrorOutput so tests
observe the real exit code and envelope
- document the exit-code map + envelope shape in clients/cli/README.md
Wave 1 foundation of the PR #1510 decomposition; shapes kept clean for
reuse by --app-info (exit 2), stored-auth (exit 3), and --format json.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S3fTN8H3R8YV4yUGvZjYnX
* test(cli,tui): add in-process source coverage gates (#1484)
CLI and TUI source had no coverage gate: CLI tests spawned the built
binary out-of-process (coverage never saw clients/cli/src) and the TUI
had only two smoke tests. Regressions there were invisible to CI.
CLI:
- Rewrite __tests__/helpers/cli-runner.ts to invoke runCli() in-process
(capture process.std*.write, map thrown errors to an exit code),
preserving the CliResult shape so all existing tests run unchanged and
clients/cli/src is now measured under v8 instrumentation.
- cli.ts: program.exitOverride() so a commander usage error throws
(catchable in-process) instead of tearing down the worker; --help /
--version still exit cleanly. Drop the dead, never-imported main().
- Add a thin out-of-process e2e.test.ts that spawns the built binary for
the shebang / process.exit paths the in-process runner can't reach.
- Add @vitest/coverage-v8, a per-file gate (90/85/80/50) excluding only
src/index.ts (binary bootstrap), and pool: 'forks' for capture
isolation. cli.ts lands at ~98% lines / 100% functions.
TUI:
- Add @vitest/coverage-v8 and an interim per-file gate over the
non-React logic (logger.ts, components/tabsConfig.ts, utils/*); the
Ink components, App.tsx, and hooks/ are excluded with a documented
follow-up (#1501). Add tests covering the included modules (100%).
Wiring + docs:
- CI runs each client's test:coverage; root test:cli / test:tui (used by
validate) and root test:coverage now enforce the gates.
- gitignore clients/**/coverage; update AGENTS.md and client READMEs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(cli): clarify in-process runner footguns from PR #1502 review
Review notes on the in-process CLI test runner (no behavior change):
- Warn in runCli's docblock that --help/--version must NOT go through the
in-process runner: exitOverride only re-throws non-zero exits, so exitCode
0 falls through to process.exit(0) and would kill the vitest worker. Those
belong in the out-of-process e2e.test.ts layer.
- Document that a timeout orphans the uncancellable invokeCli promise (its
stdio subprocess stays alive; later writes hit the restored stdout) — a
known, acceptable trade-off on the hang-only failure path.
- Fix a stale reference to the removed main() (errors now route through
index.ts).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(cli): fix remaining stale main() reference in vitest exclusion comment
PR #1502 re-review caught a second stale `main()` mention (the first was
fixed in 52c23e41). src/index.ts inlines the bootstrap as
runCli(process.argv).then(...).catch(handleError) — there is no main().
Comment-only; no behavior change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: compose root test:coverage from per-client scripts
Replace the brittle inline cd-chain with a composition of the existing
test:cli / test:tui scripts plus a new matching test:web. Behavior is
unchanged (web → cli → tui coverage gates) but there's a single source of
truth for each client's coverage invocation.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>