v0.32.0 Migration Guide
commands -o json is an index
sparkwing commands -o json emits index fields only. Every record is
now three fields:
| Field | Meaning |
|---|---|
path | the command, e.g. sparkwing runs list |
synopsis | its one-line summary |
subcommand_count | direct children in the listing; 0 means a leaf |
Records for hidden commands additionally carry "hidden": true, and
those only appear under --include-hidden.
description, flags, examples, positional_args, and the
subcommands array are gone from this listing. Nothing else about the
output changed: it is still NDJSON, still one record per line, still
sorted by path, and still filtered by --path.
Before:
{"path":"sparkwing cache prune","synopsis":"Evict least recently used binaries down to the ceilings","description":"Removes the least recently used cached binaries until the ca [...]","flags":[{"name":"max-bytes","argument":"SIZE","description":"Byte ceiling, e.g. 512MiB","group":"Limits"}, ...],"examples":[{"description":"Trim to the configured ceilings","command":"sparkwing cache prune"}, ...]}
After:
{"path":"sparkwing cache prune","synopsis":"Evict least recently used binaries down to the ceilings","subcommand_count":0}
Steps:
- If you read
pathorsynopsis, nothing changes. - If you read
description,flags,examples, orpositional_args, get them from the command's own help instead:sparkwing <path> --help --jsonreturns the full record for one command, in the same field names, and is unchanged by this release. Ask for the one command you are about to run rather than for all 150. - If you walked the
subcommandsarray to decide whether to descend, readsubcommand_countinstead and descend with--path <path>, which lists that subtree. - If you counted subcommand names from the array, list them:
sparkwing commands --path "<path>" -o plainis one path per line.
Why: the dropped fields are what <command> --help already prints,
from the same command registry, so the listing carried a second copy of
the help system that could disagree with the first. They were also 83%
of it -- of 207KB across 150 records, description was 76KB, flags
55KB, and examples 40KB, against 6.6KB of synopsis and 3.6KB of path.
An index exists to help a reader choose which page to open; it does not
have to be the page. The listing is now 17KB.
Gotchas:
subcommand_countcounts the children this listing shows, which is what--pathwill return. It is notCommand.Subcommandsfrom the help renderer, which is a hand-maintained display list and disagrees with the registry in a few places.- Hidden commands are still excluded, and that is deliberate: a hidden
command is dispatchable but not offered -- its own help names the
supported verb to use instead -- so listing it would put a "use
something else" entry in the index a reader consults to decide what
to use.
--include-hiddenlists them, marked"hidden": true, and a--paththat matches only hidden commands still errors saying so rather than reporting an empty subtree. -o pretty,-o plain, and-o markdown(including--split-dir, which generatesdocs/cli-reference.mdand thedocs/cli-*.mdpages) are unchanged, and so is<command> --help --json.
List output is NDJSON
Every list-shaped -o json output is now newline-delimited JSON: one
complete, independently parseable object per line, with no enclosing
array and no pretty-printing. The migration is mechanical -- decode line
by line instead of decoding the whole document -- and takes about a
minute per consumer. pretty, plain, markdown, and --quiet
non-JSON output are untouched, as are the single-object verbs
(runs status, runs get, runs receipt, pipeline describe,
queue, doctor, version, info, ...), which still emit one
pretty-printed object.
Before:
[
{
"path": "sparkwing runs list",
"synopsis": "Recent runs, newest first"
},
{
"path": "sparkwing runs status",
"synopsis": "One run's nodes and outcome"
}
]
After:
{"path":"sparkwing runs list","synopsis":"Recent runs, newest first"}
{"path":"sparkwing runs status","synopsis":"One run's nodes and outcome"}
Steps:
- Read the stream a line at a time --
json.Decoderin a loop,jq -c .with no-s,while read linein shell -- instead of unmarshalling the whole output into a slice. In Go,json.Decoderalready does this: callDecodein a loop untilio.EOFand drop the surrounding slice type. - Drop any
[0]-style indexing into the top-level array. Each line is a record, and every field it used to carry inside the array is still on it, unrenamed. jqconsumers:jq '.[] | .id'becomesjq '.id'. To get the old array back, pipe throughjq -s ..- Anything that counted records with
len(...)orlengthcounts lines instead.
Why: a caller's only defense against output too large for its
context is head, and head is line-oriented. sparkwing commands -o json was 258KB across 6,439 pretty-printed lines, and AGENTS.md
points an arriving agent straight at it -- so the repository's own
orientation path handed a fresh agent a quarter-megabyte document whose
first five lines parse as nothing at all. NDJSON makes a truncated read
lossy but still valid: sparkwing commands -o json | head -5 is now
five complete command records. This is house rule 12 of the CLI design
standard (list output is one record per line, in every mode).
Gotchas:
- An empty listing is now an empty stream -- zero bytes -- where it used
to be
[]or, at a few sites,null. A consumer that treated empty output as an error has to treat it as zero records; success is still carried by the exit code, which is where it always belonged. - Nothing was renamed and nothing was dropped. If a field was on a record inside the old array, it is on that record's line now.
--quiet -o jsononruns list,runs find, andruns grepwas a JSON array of id strings and is now one JSON-quoted id per line ("run-2026…"), which is the plain--quietoutput with quotes.
Affected commands: commands, runs list (including
--by-pipeline and --quiet), runs errors, runs failures
(including --group-by), runs stats (including --capacity), runs find, runs grep, runs annotations list, runs approvals list,
runs triggers list, pipeline list, pipeline discover, pipeline lint (findings and --rules), pipeline explain --all, pipeline publish, pipeline hooks survey, pipeline hooks fire, cluster tokens list, cluster agents list, cluster webhooks list, cluster webhooks deliveries, configure xrepo list, examples, docs list,
docs guides, docs search, docs versions, docs migrations list,
repos, and repos update.