feat(cli): let declarative command flags imply other request body values
Why
Some APIs pair a background: true request flag with a stream property whose schema default is true. A declarative intent command can expose both as flags, but there was no way to say that --background should turn streaming off. The command therefore sent {"background": true, "stream": true}, stayed attached to the event stream and blocked until the run finished, instead of returning the created resource and its ID straight away. Dispatch (multi-route) commands also have no stream toggle, so neither a preset nor a flag default could express it.
What changed
Flags in x-speakeasy-cli-commands accept an implies: map of root-level body values to fill in when the request body sets the flag's own property to true:
flags:
background: {to: $.background, implies: {$.stream: false}}
- Decode (
internal/extensions/cli_commands.go):impliesis rejected on positional args and onoperations:flags, requires the flag to bind a root-level body property, and cannot set the flag's own property. - Link (
cli_commands_link.go): the flag must be a bool, and every implied property must exist on each request variant that declares the flag and normalise to the same value, so one runtime rule serves every route. - Runtime:
flagutil.ApplyImpliedBodyruns after--body/stdin, declared inputs and presets are merged. It applies to both--backgroundand--body '{"background":true}', and never overrides a key the request already carries (an explicit--streamwins). - Wiring in
includes/intents.tsandintentcmd.go.stmpl, documented in the CLI README, covered byTestCLICommands_FlagImplies. - Review SDK regenerated (only the new
flagutilhelper changes, since the review spec does not useimplies:); changeset added.
Testing
go test ./internal/extensions/: passes.TARGET=review make test-cli,make check-template-cli,make lint: pass.- Generated a CLI from a document with a two-route create operation (
streamdefaulting totrue,backgroundon both routes) plusimplies: {$.stream: false}onbackground, and checked the--dry-run -o jsonbody:
| Invocation | background | stream |
|---|---|---|
run hi --background | true | false |
run hi --background on the second route | true | false |
run hi --background --stream | true | true |
run hi | (unset) | true |
run hi --background=false | false | true |
--body '{"input":"hi","background":true}' (and via stdin) | true | false |
--body '{"input":"hi","background":true,"stream":true}' | true | true |
With stream: false the request also negotiates the JSON response, so the command prints the created resource and exits.
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.
Summary by cubic
Adds an implies map to declarative command flags (x-speakeasy-cli-commands) so a bool flag can fill other root-level body values when set. This lets --background also send stream: false, so the command returns the created resource instead of blocking on the event stream.
New Features
- Flags may declare
implies: {$.stream: false}; the runtime applies it after merging--body/stdin, declared inputs, and presets, and never overrides a key already set. - The linker requires every implied property to exist on each request variant that declares the flag and normalize to the same value, so one rule works for all routes.
impliesis rejected on positional args,operations:flags, non-bool flags, and flags not bound to a root-level body property.- Added README documentation and
TestCLICommands_FlagImpliescovering the behavior.