feat: agent-first root discovery, curated help, and TTY-aware output (DEVEX-695)#13
Conversation
Improves the bare-invocation and help experience for human users without
sacrificing machine-readable output for agents.
- Root discovery: new `CliConfig::with_root_next_actions` hook. On bare
invocation, human output appends a "Suggested next actions" section;
machine output emits a discovery envelope ({description, version} +
next_actions). With no hook configured, behavior is unchanged long-help.
- Curated root/group help: a root help template suppresses clap's duplicate
command list and the global-options wall (still shown on leaf commands);
group pages keep their subcommand list but drop the options wall.
Categories, and the commands within them, are sorted. The engine-injected
`auth` command is filed under an admin category
(`CliConfig::with_admin_category`, default "Admin") so it stays
discoverable; any uncategorized top-level command falls under a generic
"Commands" section.
- TTY-aware default output format: human on an interactive terminal, JSON
otherwise (pipes, files, CI, most agents). Precedence: explicit
--output/--json/--toon/--human > ${APP_ID}_OUTPUT env > TTY policy.
Uses std::io::IsTerminal (no new dependency).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ba08e11 to
bae3be3
Compare
There was a problem hiding this comment.
Pull request overview
This PR improves the “bare invocation” and help UX while preserving agent-friendly machine output by adding (1) an opt-in root discovery hook with next-action suggestions, (2) curated/sorted root & group help templates that suppress clap’s noisy sections, and (3) a TTY-aware default output-format resolver with an ${APP_ID}_OUTPUT override.
Changes:
- Add
CliConfig::with_root_next_actionsand render bare-root output as either human help + “Suggested next actions” or a JSON discovery envelope (when explicitly machine output / non-TTY default). - Introduce curated help templates for root and group commands; sort categories and commands; ensure the built-in
authcommand is listed under an admin category. - Implement default output format resolution with env + TTY policy; update public exports and adjust tests for the new flag-resolution APIs.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
src/cli.rs |
Adds root next-actions hook, curated help behavior, admin categorization for auth, and bare-root discovery rendering. |
src/cli/help.rs |
Adds root/group help templates, sorts help categories/entries, and renders human “Suggested next actions”. |
src/flags.rs |
Adds TTY/env-based default output resolver + env var derivation; adjusts flag extraction APIs and adds unit tests. |
src/lib.rs |
Re-exports new public APIs/types related to root actions and output-format resolution. |
tests/consumer_cli.rs |
Adds contract tests for bare-root discovery (human + JSON), group/leaf help shaping, and next-actions behavior. |
tests/foundation.rs |
Updates raw-arg output-format extraction tests and adds tests for auth help categorization (default + override). |
tests/exhaustive_public_api.rs |
Updates tests for the new global_flags_from_matches(..., default_format) signature. |
tests/exhaustive_cli_contract.rs |
Updates tests for the new global_flags_from_matches(..., default_format) signature. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- resolve_default_output_format: normalize the env override to lowercase
and ignore blank/invalid values, so a miscased or stray ${APP_ID}_OUTPUT
can't break all command output.
- bare-root discovery: skip pre_run so the bare command stays discoverable
even when pre_run would fail, matching the no-hook bare-root path.
- auth categorization: invoke register_auth_help_entry from
ensure_auth_command so auth is filed under the admin category even when a
provider is registered after construction (not only during Cli::new).
- tests: rename the resolver test to reflect env-then-TTY precedence; add
coverage for case-insensitive/invalid env values and post-construction
auth registration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
render_root parsed the output format via the infallible OutputFormat::from_str, which silently coerced an unrecognized explicit --output value (e.g. --output yaml) to JSON. Normal commands reject it via Middleware::render_envelope with CliCoreError::InvalidOutputFormat. Validate up front so the bare-root discovery path is consistent, and add a regression test.
|
Did a deep review pass. Pushed two changes directly to the branch:
fmt/clippy/full suite/doc-tests/missing-docs all green locally. One non-blocking item I'd rather discuss than just change: Back-compat wording vs. the always-JSON decision. The default output format change is TTY-aware for every consumer on dependency bump, and it also now applies to the Everything else looked sound: the |
…precedence Adds deterministic, TTY/env-independent coverage for the resolved-default wiring: global_flags_from_matches and extract_output_format must return the supplied default_format when no explicit format is given, while an explicit --output/--json/--toon/--human still wins. Uses a non-"json" default so the assertions distinguish the passed default from a hardcoded fallback, guarding the value_source == CommandLine gate against regression.
jgowdy-godaddy
left a comment
There was a problem hiding this comment.
Approved with comments for consideration
|
Reworded the back-compat note in the description: it now states explicitly that interactive-terminal invocations switch from JSON to human on dependency bump (including the Your two commits look correct. On the always-JSON one-way-door: agreed it should be settled before release. That's a product call for the CLI owner rather than something to decide in this PR — taking it back to them and will follow up here with the decision. |
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
🤖 I have created a release *beep* *boop* --- ## [0.1.3](cli-engine-v0.1.2...cli-engine-v0.1.3) (2026-06-05) ### Features * agent-first root discovery, curated help, and TTY-aware output (DEVEX-695) ([#13](#13)) ([791b335](791b335)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Addresses Rust-port review item #2 (PR #49 review) · DEVEX-695
Original concern (item 2): the TypeScript CLI root returned JSON discovery (command tree + environment/auth snapshot + next actions); the Rust port renders clap long-help text for the empty command path → an agent-first discovery regression.
How each part of that concern is handled here:
--output json).auth status,env get,application list) +tree.next_actionspointer totree(godaddy tree --output jsonreturns the full tree) instead of embedded — hypermedia model.env get/auth statuspointers instead of embedded.--output/${APP_ID}_OUTPUToverrides. Whether to force always-JSON is left for the team.Net: agent-first discovery is restored, in a leaner hypermedia form — the bare command returns pointers and the agent follows
tree/auth status/env getfor details, rather than one fat payload. Nothing from item 2 was dropped silently; each element is either restored or a deliberate pivot above.Scope: this PR is the cli-engine capability. The
godaddyCLI adopts it (callswith_root_next_actions(...)and rides the TTY-aware default) in a follow-up PR once this releases; that PR will be linked to DEVEX-695.Summary
Makes the bare-invocation and help experience friendlier for humans without sacrificing machine-readable output for agents. Three related engine changes:
1. Root discovery (
with_root_next_actions)New opt-in
CliConfig::with_root_next_actionshook. On bare invocation (no subcommand):{ data: { description, version }, next_actions }.Actions are pointers to existing commands (e.g.
auth status,env get,tree), not embedded snapshots — the agent follows only what it needs. With no hook configured, behavior is unchanged long-help (back-compat for existing consumers).2. Curated root/group help
authcommand is filed under an admin category (CliConfig::with_admin_category, default"Admin") so it stays discoverable once the auto list is suppressed; any uncategorized top-level command falls under a generic"Commands"section, so nothing is ever lost.3. TTY-aware default output format
Default output is now human on an interactive terminal, JSON otherwise (pipes, files, CI, most agents). Precedence:
--output/--json/--toon/--human${APP_ID}_OUTPUTenv (e.g.GODADDY_OUTPUT=json,GDX_OUTPUT=json)Uses
std::io::IsTerminal— no new dependency. Agents that capture output via pipes (the norm) keep getting JSON automatically; PTY-backed agents can force it via the env var or a flag.Why
Restores agent-first discoverability for the bare command (the original TypeScript GoDaddy CLI returned JSON discovery on bare invocation; ref DEVEX-695) while making the human terminal experience pleasant — the engine is shared by the godaddy CLI and gdx.
Consumers
No consumer code change is required. There is, however, one runtime behavior change on dependency bump: with the TTY-aware default, interactive-terminal invocations now default to human output where they previously got JSON — across all commands, including the
--searchand--schemaraw-bypass paths (previously hardcoded to JSON). Machine / piped / CI / agent output is unchanged (still JSON), so real-world breakage is low. Prior behavior is fully preserved via explicit--output/--json/--toon/--humanor the${APP_ID}_OUTPUTenv (e.g.GODADDY_OUTPUT=json). Whether to instead force always-JSON is an open one-way-door decision for the team to settle before release.The godaddy CLI adopts the
with_root_next_actionshook (and rides the"Admin"default) in a follow-up PR once this releases; gdx is unaffected until it bumps the dependency (it would setwith_admin_category("Administration")).Testing
clippy -D warningsclean;cargo fmtapplied.🤖 Generated with Claude Code