fix(cli): name the missing flag instead of printing a Zod dump - #648
Open
filip131311 wants to merge 1 commit into
Open
fix(cli): name the missing flag instead of printing a Zod dump#648filip131311 wants to merge 1 commit into
filip131311 wants to merge 1 commit into
Conversation
…od dump
`argent run <tool>` with a required flag omitted answered with the raw validation
issue list — a JSON array of `invalid_type` objects. The information needed for a
useful message was already there: the same schema that renders `--help` says which
fields are required and what each flag is called.
Missing flags are now named, in the voice the CLI already uses for a bad value:
Error: missing required flags --udid, --x, --y
argent run gesture-tap [flags]
...
The check runs against the merged payload, so a field supplied through `--args` or
`--<field>-json` counts, and it runs before the call goes out — which also spares a
tool that takes file inputs from uploading them only to be told a flag was missing.
A value the tool itself rejects is reported the same way rather than dumped, since
constraints and rules spanning several fields are only knowable from the response:
Error: --scale Too big: expected number to be <=1
A rule that spans the whole payload reports no field, so it is stated on its own
rather than attributed to an invented flag. A rule that fires on an optional field
is not called a missing required flag, which would contradict the help printed
directly underneath it.
Recognising a rejection is structural — the shape of the issue list and the tool's
own schema decide it, never the wording of a message. Anything that is not provably
this tool's own input validation keeps its existing handling exactly: a runtime
failure still prints its bare message and exits 1.
Both paths render through one function and report through one call site, so the
message, the channel and the exit code cannot drift apart between them. `--json`
gets one object on stderr carrying that same message, the missing flag names, and
the tool's issue list verbatim.
Also guards the global-option parse, which ran outside the error handler: `--out`
with no value surfaced as a raw stack trace.
Fixes #645
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #645.
Before
After
Scope
The issue names the missing-flag case. Reproducing it showed the same raw dump has a second
trigger: any constraint the tool itself rejects, which the CLI cannot know in advance.
gesture-tap(no flags)missing required flags --udid, --x, --yscreenshot --scale 99--scale Too big: expected number to be <=1gesture-scroll --udid X --x .5 --y .5(zero deltas)Pass a non-zero deltaX and/or deltaY …run-sequence --udid Xmissing required flag --steps-jsongesture-tap --x abcFixing only the first row would have left the CLI's error voice less predictable than before, so
both go through the same renderer.
How it works
Two sources, one output. Missing flags are found locally from the tool's published schema — a
presence check only, never a type or range judgement, which stays exclusively the server's.
That is sound because the schema generator excludes defaulted fields from
required(
debugger-connectrequiresdevice_id, notport), so it cannot reject a call the server wouldhave accepted. Everything else is read off the rejection the tool-server already returns.
Both produce one report rendered by one function and reported through one call site, so the
wording, the output channel and the exit code cannot drift between them.
Recognition of a rejection is structural — the shape of the issue list plus the tool's own
schema — never the wording of a message, which is not ours to depend on and changes between Zod
releases. Four gates must all hold, and any failure leaves the existing handling untouched.
Details worth a reviewer's attention:
gesture-scrollandgesture-rotatevalidate at the object level, and
screenshot-diffis.strict(). Treating those asfield-scoped would print
Error: --undefined …; they are stated on their own instead.await-ui-elementrefines onexpectedText, which it does not require — reporting that asmissing would contradict the help block printed directly underneath, where the field carries no
(required)marker.--argsis merged, soargent run gesture-tap --args '{...}'is notfalsely reported as missing everything. Verified live: it reaches the device and taps.
constructoris not treated assupplied by the prototype chain.
Behaviour changes a reviewer should weigh
run.tsalready uses 2 for bad input and 1for a failed run; a Zod 400 means the tool never ran. No in-repo consumer tests for a specific
code —
scripts/e2e-fullonly checks zero/non-zero. Note this does not make 2 universal:an unknown tool name still exits 1, which is untouched here.
--jsongains a defined failure shape (it had none — the blob printed regardless). Oneobject on stderr, nothing on stdout, so
--json | jqon a failed run reads an empty stream anda non-zero status:
{ "error": "missing required flags --udid, --x, --y", "missing": ["--udid", "--x", "--y"], "issues": [] }issuescarries the tool's list verbatim, which is the raw data the issue asked to keep.outside the error handler, so
argent run screenshot --outprinted a raw stack trace. Samebad-input-to-ugly-output defect, one line above the fix.
Not done
argent run gesture-tap --bogusflag 1is silently accepted and the toolruns). The pass-through is deliberate — see the comment in
flag-parser.ts— and belongs withBoolean flags silently ignore a space-separated value (--includeChildren false stays true) #586, the same CLI-argument family.
argent flowbuilds its own payload and has its own required check; it cannot reach this path.prettifying is CLI-side.
Verification
Live against the built CLI for every row of the table above, plus:
debugger-connectasks onlyfor
--device_idand never--port;--args '{...}'reaches the device and taps; a thrownruntime error still prints its bare message with no
Error:prefix, no help block, exit 1.43 new tests.
run-telemetry.test.tspasses unedited — it already asserted the non-Zodpassthrough, which makes it the regression guard for the untouched path. Full CLI suite 320
passed, registry 87 passed.
🤖 Generated with Claude Code