feat(cli): let declarative command flags imply other request body values

TR
1d ago
+351-2

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): implies is rejected on positional args and on operations: 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.ApplyImpliedBody runs after --body/stdin, declared inputs and presets are merged. It applies to both --background and --body '{"background":true}', and never overrides a key the request already carries (an explicit --stream wins).
  • Wiring in includes/intents.ts and intentcmd.go.stmpl, documented in the CLI README, covered by TestCLICommands_FlagImplies.
  • Review SDK regenerated (only the new flagutil helper changes, since the review spec does not use implies:); 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 (stream defaulting to true, background on both routes) plus implies: {$.stream: false} on background, and checked the --dry-run -o json body:
Invocationbackgroundstream
run hi --backgroundtruefalse
run hi --background on the second routetruefalse
run hi --background --streamtruetrue
run hi(unset)true
run hi --background=falsefalsetrue
--body '{"input":"hi","background":true}' (and via stdin)truefalse
--body '{"input":"hi","background":true,"stream":true}'truetrue

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.
  • implies is rejected on positional args, operations: flags, non-bool flags, and flags not bound to a root-level body property.
  • Added README documentation and TestCLICommands_FlagImplies covering the behavior.