Skip to content

fix: deliver USAGE exit + JSON envelope for bad flags; clean switch output#116

Open
tcerqueira wants to merge 3 commits into
mainfrom
tc/cli-contract-fixes
Open

fix: deliver USAGE exit + JSON envelope for bad flags; clean switch output#116
tcerqueira wants to merge 3 commits into
mainfrom
tc/cli-contract-fixes

Conversation

@tcerqueira

Copy link
Copy Markdown
Member

Fixes two CLI-contract rough edges from #112.

1. Bad flags now honor the agent/CI contract

Previously, an unknown/invalid flag dumped the full help text to stdout and a raw, ANSI-colored Cliffy error to stderr — even under --json — corrupting machine-readable output (the exit code already happened to be 2 because @cliffy/command's ValidationError.exitCode defaults to 2, but everything around it violated the contract).

Now the root parse() runs with .noExit() and thrown ValidationErrors (unknown/invalid/conflicting flags, missing values, and app-thrown ValidationErrors re-thrown by actionHandler) are funneled through the CLI error contract:

  • exit ExitCode.USAGE (2),
  • under --json: a single { "error": { "code": "VALIDATION_ERROR", "message": ... } } envelope on stderr, clean stdout, ANSI disabled,
  • otherwise: the standard human-readable error.

.reset() repoints Cliffy's builder to the root command before .noExit() (mounting/defining subcommands leaves the builder selecting a child, so the setting would otherwise land on the wrong command). --help and --version still exit 0.

2. switch no longer pollutes stdout

switch printed its confirmation to stdout via console.log and emitted nothing under --json. It now sends the human message to stderr and, under --json, writes a single { org, app } result to stdout (app is null for the sandbox variant, which doesn't resolve an app).

Tests

  • New token-free subprocess suite tests/cli_contract.test.ts: bad flag → 2 (deploy + standalone sandbox roots), --json bad flag → 2 with envelope on stderr and clean stdout, --help/--version → 0.
  • Tightened the existing invalid-flag test to assert USAGE (2) and a clean stdout.

Verified offline: bad flags (plain + --json), --help, --version, unknown subcommand-as-path, bad option values, and both switch output modes.

Run the root command's `parse()` with `.noExit()` and funnel thrown
`ValidationError`s (unknown/invalid/conflicting flags, missing values)
through the CLI error contract so they exit with `ExitCode.USAGE` (2)
and, under `--json`, emit the structured `{error:{code,message,...}}`
envelope on stderr instead of dumping help to stdout.

Previously a bad flag printed full help to stdout and a raw, ANSI-colored
Cliffy error to stderr even under `--json`, corrupting machine-readable
output. `.reset()` repoints the builder to the root command before
`.noExit()`, since mounting/defining subcommands leaves it selecting a
child. `--help`/`--version` still exit 0.

Adds a token-free subprocess test for the exit-code contract and tightens
the existing invalid-flag test to assert USAGE (2) plus a clean stdout.
`switch` printed its confirmation to stdout via console.log and emitted
nothing under `--json`, corrupting piped/machine-readable output. Route
the human-readable message to stderr and, under `--json`, write a single
`{ org, app }` result to stdout via `writeJsonResult` (app is null when
the sandbox variant doesn't resolve an application).
…dler

`handleCliError` picked the error output format with a `--json`/`-j`
substring-free includes check, missing `--json=...` and combined short
flags like `-jy`/`-yj`. A parse-time ValidationError in those invocations
printed the human-readable ANSI error instead of the structured
VALIDATION_ERROR envelope. Broaden detection to a parse-free heuristic
matching `--json`, `--json=...`, `-j`, and combined short flags containing
`j`. Adds a `-jy` envelope test case.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant