fix(cli): list the flags each command registers in --usage
Merged
2Y
7d ago+310+310+012345678901234567890123456789-143-143-012345678901234567890123456789
Why
--usage should list exactly the flags each command accepts. Three gaps remained:
- Whole-body flag name. A body-only operation whose JSON/form body is not expanded into field flags (a union, map, array or scalar body) registers one flag named after the body (
--request,--vehicle, …).--usagelisted--bodyinstead, which the command rejects withunknown flag: --body, and never listed the real, often required, flag. A multipart body-only operation without flag metadata had the same missing flag and no replacement.--usageusedtemplateHasBodyFlag, whileopcmd.go.stmpldecided the registered flag with separate inline logic. In the primary test CLI, 113 commands advertised a--bodythey reject. completion <shell> --no-descriptions. Cobra registers it onbash/zsh/fish/powershell; the hand-writtencompletionnodes inincludes/usage.tsdid not list it.- Flags added at runtime to a generated command (for example by
custom.Register) appeared in--usageonly when the command was also marked withusage.MarkDynamic, including on the root.
What changed
includes/metadata.ts: new side-effect-freeoperationUsesMetadataFlags, the request-strategy decision (flag metadata plus optional--body, or one whole-body flag). It replaces the inline logic inopcmd.go.stmplandintentOperationHasMetainincludes/intents.ts; the now unusedoperationHasFlagMetadatais removed. Generated command files are byte-identical apart from the[required]change below.includes/usage.ts:getOperationWholeBodyUsageFlagmirrors the flagopcmd.go.stmplregisters:--bodywith flag metadata; otherwise--<request body name>for body-only operations, with the same help text; otherwise--body. The fourcompletionshell nodes list--no-descriptions.includes/templating.ts,includes/metadata.ts:requestBodyDescriptionandbodyFlagDescriptionreturn unescaped help text for--usage; thetemplate*wrappers escape it for Go source.includes/templating.ts: the whole-body flag help says[required]only when the body is neither optional nor nullable, matching the requiredness the command enforces (templateRequestBodyRequired). This also corrects--helpfor nullable body-only operations.usage.go.stmpl: every command rendered from itsusageNodesentry, marked dynamic or not, takes its flags from cobra (usageFlagLines): generated flag lines for the flags it still shows (a flag hidden at runtime is dropped), then visible local flags added at runtime.MarkDynamicis now only needed when help, aliases or args change at runtime.usageFlagLinesreadsLocalFlags()first, which merges persistent flags intoFlags()before the lookups, so a root marked dynamic before its first render keeps its generated flags.usage_test.go.stmpl:TestUsageSchema_FlagsMatchRegisteredFlagswalks every command, root included, and asserts that each flag in its own--usageis registered and each visible registered flag is listed.TestUsageSchema_RuntimeFlagsKeepGeneratedLinesreplacesTestUsageSchema_DynamicCommandKeepsGeneratedLinesand runs with and withoutMarkDynamic, adding a flag on a generated command and a persistent flag on the root.TestUsageSchema_RuntimeHiddenFlagDropped: a generated flag hidden at runtime leaves the command's--usage.TestUsageSchema_DynamicRootmarks a fresh root dynamic before its first render.
pkg/generate/snapshots/cli_empty_body_go_test.go: the generatedusageNodesentries of body-only operations list the whole-body flag the command registers (not--body), with[required]exactly when the command requires it, and thecompletionshell entries list--no-descriptions. These check the table itself; the runtime tests above check the rendered output, which also lists flags missing from the table.README.md:--usagesection describes the whole-body flag name,--no-descriptionsand runtime-added flags.- Review SDK
zSDKs/sdk-cliregenerated:chatlists--requestinstead of--body;completion <shell>lists--no-descriptions. All other--usageoutput is unchanged. - Changeset:
core, cli, fix, patch.
Testing
TARGET=primary make test-cli: 2189 tests pass.go test ./pkg/generate/snapshots -run TestSnapCLI: pass.make lint: pass.TARGET=review make build-cli: diff limited tointernal/usage/schema.go,tests/usage_test.goandgen.lock; generated command files are byte-identical.- The new
cli_empty_body_go_test.goassertions fail on the base branch templates (standaloneoptionallists--body <body>, not--empty-body) and pass with this change. - Against the previous review SDK
schema.go,TestUsageSchema_FlagsMatchRegisteredFlagsfails (chat --usage lists unregistered --body) and passes with this change. --usagecompared for every command path against the base branch:- review SDK: 7 of 90 paths differ (root,
chat,completionand its four shells), only by the lines above; - primary test CLI: 129 of 688 paths differ, only by
--bodyreplaced with the registered whole-body flag and--no-descriptions; generated command files differ only where a nullable whole-body flag's help loses[required](8 commands); - a generated CLI with no body-only, non-expanded operations: only
completionand its shells differ.
- review SDK: 7 of 90 paths differ (root,
Public-safety check
- This change contains no credentials, customer documents, private repository URLs, private filesystem paths, or unredacted private logs.
- Title, body, comments, and commit messages name no customers or customer-derived identifiers, private paths or trackers, or workflow provenance, and are understandable without private context (
.claude/skills/public-repo-communication/SKILL.md). - Generated fixtures and review SDK changes are public-safe.
- I reviewed
git diff --check.