Agent-First Data v0.12: Help Scope and Format Are Two Knobs, Not One

by Agent-First Kit Contributors

v0.7 made --help expand the whole command tree so agents could read a CLI in one call. That conflated two decisions — how much to show and how to render it. v0.12 splits them: --recursive controls scope, --output controls format, and the two compose. Same model across Rust, Go, Python, and TypeScript.

Back in v0.7 we argued that --help should be complete: one call, the whole command tree, so an agent never has to crawl subcommands one at a time. That was the right instinct for agents. But baking it into --help itself was the wrong mechanism.

Making --help always-recursive solved the agent’s problem by breaking the human’s. A person typing tool --help to remember one flag now gets a wall of every descendant subcommand. And a separate --help-markdown flag had quietly appeared to cover doc generation — which meant “how much help” and “what format” were tangled into two overlapping flags that didn’t compose.

v0.12 untangles them into two orthogonal knobs.

Scope and format are different questions

They compose cleanly:

tool --help                              # one level, plain text (the human default)
tool sub --help                          # one level, scoped to `sub`
tool --help --recursive                  # whole tree, plain text (the agent read)
tool --help --recursive --output json    # whole tree, structured schema
tool --help --recursive --output yaml    # whole tree, structured schema
tool --help --recursive --output markdown  # whole tree, docs export

The --help-markdown flag is gone. Markdown is just one value of --output, reachable only in a help context — it never becomes a general business output format.

Humans get a short help back

The default --help is one level again. A person sees the current command, its flags, and the names of its direct subcommands — the conventional shape they expect. Drilling in with tool sub --help still works exactly as before.

Agents lose nothing: --help --recursive gives them the complete map the v0.7 post asked for, and --output json|yaml gives them that map as a parseable schema instead of text they have to scrape.

One subtlety: --recursive without --help

--recursive on its own is a scope modifier, not a help trigger. Many tools already use --recursive for their own operations. So a bare --recursive without --help falls straight through to the application’s own parser — the help layer leaves it alone. It only changes scope when --help is also present.

The handler that makes it work

clap (and the equivalent in each language) wants to short-circuit on --help before you can inspect the other flags. So the v0.12 helpers scan argv before parsing:

match cli_handle_help_or_continue(&raw, &Cli::command(), &HelpConfig::human_cli_default()) {
    Ok(Some(help)) => { print!("{help}"); std::process::exit(0); }
    Ok(None) => {}                      // not a help request — keep parsing
    Err(err) => { println!("{}", output_json(&err)); std::process::exit(2); }
}

HelpConfig::human_cli_default() makes --help one-level plain; HelpConfig::agent_cli_default() flips the default scope to recursive for tools whose primary caller is an agent. Either way, --recursive and --output still override per invocation. Under the hood, cli_render_help_with_options(cmd, path, &HelpOptions { scope, format }) does the rendering, and the old cli_render_help / cli_render_help_markdown remain as recursive wrappers.

Same model in four languages

As with every AFDATA capability, the scope/format split ships identically in Rust, Go, Python, and TypeScript — same flags, same precedence, same structured schema for --output json|yaml. A team standardizing CLIs across languages gets one help contract, not four dialects of it.

v0.7 was right that --help is part of the API. v0.12 makes it an API with two parameters instead of one overloaded flag.