Skip to content

fix(cli): validate --output uniformly across all command groups#14

Open
Davidson3556 wants to merge 1 commit into
TestSprite:mainfrom
Davidson3556:fix/uniform-output-validation
Open

fix(cli): validate --output uniformly across all command groups#14
Davidson3556 wants to merge 1 commit into
TestSprite:mainfrom
Davidson3556:fix/uniform-output-validation

Conversation

@Davidson3556

@Davidson3556 Davidson3556 commented Jun 24, 2026

Copy link
Copy Markdown

Fixes #13

Describe the changes you have made in this PR -

The global --output flag was validated inconsistently across command groups. test and project rejected an unrecognised value with a typed VALIDATION_ERROR (exit 5), but auth, usage, agent, and init resolved it with globals.output ?? 'text' — silently coercing anything that isn't json/text to text mode and exiting 0.

For the CLI's primary consumer (a coding agent), that's a footgun: a request for --output json with a typo (--output josn) returned a human-readable text payload, so the agent's downstream JSON.parse failed with no signal as to why. The two commands that did validate also used different wording.

Changes:

  • Add a single resolveOutputMode(raw) helper in src/lib/output.ts. It returns 'text' for an omitted flag and throws localValidationError('output', 'must be one of: json, text', ['json', 'text']) for any other value.
  • Route every command group's resolveCommonOptions (auth, usage, agent, init, project, test) through it — closing the validation gap on the four unvalidated groups and unifying the error wording on the canonical test-style message.
  • Add unit tests for resolveOutputMode and an end-to-end subprocess regression for two previously-unvalidated groups (agent list, auth status).

Note: when --output itself is the invalid value, the error envelope is rendered in text mode (the requested mode is unusable) — unchanged from the existing test/project behaviour.

Demo/Screenshot for feature changes and bug fixes -

Before (main) — invalid value silently coerced to text, exit 0:

Screenshot 2026-06-24 at 00 15 33

After (this branch) — rejected with an actionable message; valid values unaffected:

image

Tests:
Screenshot 2026-06-23 at 23 53 07


Code Understanding and AI Usage

Did you use AI assistance (ChatGPT, Claude, Copilot, etc.) to write any part of this code?

  • No, I wrote all the code myself
  • Yes, I used AI assistance (continue below)

If you used AI assistance:

  • I have reviewed every single line of the AI-generated code
  • I can explain the purpose and logic of each function/component I added
  • I have tested edge cases and understand how the code handles them
  • I have modified the AI output to follow this project's coding standards and conventions

Explain your implementation approach:

Problem: the global --output flag is the contract every machine consumer depends on, but four of the six command groups silently fell back to text on an unrecognised value instead of failing. An agent that mistyped --output json got text back and broke on parse, with no error to point at the cause.

Alternatives considered:

  1. Use Commander's .choices(['json','text']) on the root option. Rejected: it would change the error path (Commander's own error rendering / exit codes) rather than the project's typed VALIDATION_ERROR envelope, and the flag is read in each command via optsWithGlobals() — the existing test/project code already validates inside resolveCommonOptions, so matching that is lower-risk and consistent.
  2. Inline the same check in each of the four missing command files. Rejected: that perpetuates the duplication and the wording drift (the existing two copies already disagreed).
  3. (chosen) Extract one resolveOutputMode helper and route all six command groups through it. Fixes the gap and unifies wording in one place.

Key function: resolveOutputMode(raw)undefined'text' (flag omitted); 'json'/'text' → returned verbatim; anything else throws the typed VALIDATION_ERROR (exit 5) used everywhere else in the CLI. It lives in output.ts (no import cycle: output.tserrors.tsfacade.ts, none of which import output.ts).

Edge cases handled/tested: omitted flag defaults to text; json/text pass through; josn/yaml/JSON/Text/empty string all throw exit 5; the error itself renders in text mode because the requested mode is unusable.


Checklist before requesting a review

  • I have added proper PR title and linked to the issue
  • I have performed a self-review of my code
  • I can explain the purpose of every function, class, and logic block I added
  • I understand why my changes work and have tested them thoroughly
  • I have considered potential edge cases and how my code handles them
  • If it is a core feature, I have added thorough tests
  • My code follows the project's style guidelines and conventions

Note: Please check Allow edits from maintainers if you would like us to assist in the PR.

Only `test` and `project` validated the global `--output` flag. The
`auth`, `usage`, `agent`, and `init` command groups resolved it with
`globals.output ?? 'text'`, so an unrecognised value (e.g. a typo like
`--output josn`) was silently coerced to text mode instead of being
rejected. A coding agent that asked for `--output json` then received a
human-readable text payload and failed to parse it as JSON, with no
signal as to why.

Extract the validation into a shared `resolveOutputMode` helper in
`lib/output.js` and route every command group's `resolveCommonOptions`
through it. Invalid values now throw a typed VALIDATION_ERROR (exit 5)
with an actionable message everywhere. This also unifies the error
wording, which previously differed between `test`
("Flag `--output` is invalid: must be one of: json, text.") and
`project` ("--output must be one of: json, text").
@Davidson3556

Copy link
Copy Markdown
Author

@zeshi-du kindly review

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.

Invalid --output value is silently coerced to text in auth/usage/agent/init (should exit 5)

1 participant