Group aimx --help subcommands by audience#202
Conversation
Splits the flat 14-command list into "Operations (as current user)" and
"Server administration" sections so newcomers can orient themselves
quickly. Per-user verbs (send, mailboxes, hooks, agents, mcp) are
clearly separated from operator/root verbs (setup, serve, doctor, logs,
dkim-keygen, portcheck, uninstall, upgrade).
Implementation notes:
- Clap 4.6 does not support `help_heading` on `Subcommand` variants, so
the parent's `help_template` omits `{subcommands}` and slots a hand-
rolled grouped list in via `{after-help}`. The list lives in a
`SUBCOMMAND_HELP` constant with a comment reminding maintainers to
keep it in sync with the `Command` enum.
- Replaced `about` with the new tagline ("The internet's oldest
protocol, rebuilt for AI agents.") and dropped `long_about` so `-h`
and `--help` render identically.
- Hid `ingest` from --help (it is only ever invoked by `aimx serve`
over stdin, never by humans). The subcommand still parses and runs.
- Added a `Cli::version` field purely so `-V, --version` renders in the
Options block; the existing `handle_version_flag` interceptor in
`main.rs` still handles the actual flag before `Cli::parse()` runs.
- Shortened every subcommand description to one tight line.
uzyn
left a comment
There was a problem hiding this comment.
Changes Overview
Splits aimx --help into two audience-grouped sections — Operations (as current user) for per-user verbs and Server administration for operator/root verbs — by replacing clap's auto-rendered subcommand list with a hand-rolled SUBCOMMAND_HELP constant injected via {after-help}. Also retitles the tagline, drops long_about so -h and --help render identically, hides the internal ingest subcommand from top-level help, adds an explicit -V, --version flag to the parser purely so it shows in the Options block, and tightens every subcommand's one-line description.
Scope Alignment
Implementation matches the PR description exactly. Every claim verified locally:
aimx --helpandaimx -hrender the new grouped layout,-V, --versionappears in Options.- New tagline renders.
aimx --versionandaimx -Vstill print the customversion_string()banner viahandle_version_flag(intercepted inmain.rsbeforeCli::parse()runs).aimx ingest --helpstill works (subcommand wired, just hidden from the top-level list).aimx mailboxes --helpand other subcommand help pages are unchanged.aimx serve --versionis correctly rejected by clap (the newversion: boolfield is parent-only, notglobal).cargo build,cargo test --test integration help_shows_subcommands,cargo clippy -- -D warnings,cargo fmt -- --checkall clean.
No scope creep: the diff touches exactly two files (src/cli.rs, tests/integration.rs) and is limited to help-surface concerns.
Potential Bugs
None found in the changed code.
One pre-existing edge case worth noting (not introduced by this PR, since handle_version_flag is unchanged): aimx --data-dir /tmp --version does not print the banner. handle_version_flag stops scanning at /tmp because it can't tell /tmp is the value for --data-dir. Before this PR clap rejected this with "unexpected argument --version"; after this PR the new parent-level version field is consumed but no subcommand follows, so clap errors with "command required" instead. Both are error states, both pre-date this PR, and the message change is a minor UX shift in an already-unfriendly path. Mentioning here only because the PR brings the --version flag into the parser surface. Non-blocker.
Test Coverage
The integration test help_shows_subcommands was updated correctly:
- Adds positive assertions for the two new headings.
- Adds a negative assertion that
ingestno longer appears in--help. - Keeps assertions for each visible subcommand name.
Two minor gaps, both non-blocking:
- The test does not verify the order of sections (Operations before Server administration) or that subcommands appear under their intended heading. A maintainer who accidentally swapped the two halves of
SUBCOMMAND_HELPwould still pass the test. A simplefind("Operations") < find("Server administration") < find("send")ordering check would close this. - No assertion that
-V, --versionappears in the Options block, even though the PR explicitly adds aCli::versionfield to make that line render.
Code Quality
SUBCOMMAND_HELPis a hand-rolled string that must stay in sync with theCommandenum. The PR comment incli.rs:6-10acknowledges this and asks maintainers to keep it in sync. For a 14-command surface that changes rarely this is acceptable; an automated drift guard (a unit test that walksCommand::clap()and asserts each non-hidden variant is mentioned inSUBCOMMAND_HELP) would make the constant self-maintaining, but it's a nice-to-have, not required.Cli::versionis#[allow(dead_code)]with a clear comment explaining why. Reasonable trade-off given that clap forces a struct field if you want the flag rendered.help_templatecorrectly omits{subcommands}since{after-help}now carries that role; rendered output has no double-blank-lines or trailing whitespace.
Security Issues
None — this PR does not touch any code path that handles input, auth, networking, or filesystem access.
Summary and Recommended Actions
Overall verdict: Ready to merge.
- Blockers: none
- Non-blockers: none
- Nice-to-haves:
- Add an ordering assertion to
help_shows_subcommandsso an accidental swap of the two headings is caught. - Add an assertion that
-V, --versionappears in the--helpOptions block. - Consider a unit test that compares
Commandvariants againstSUBCOMMAND_HELPto detect drift when new subcommands are added.
- Add an ordering assertion to
Recommended merge commit message
Group `aimx --help` subcommands by audience
Splits the flat 14-command list in `aimx --help` into "Operations (as
current user)" (send, mailboxes, hooks, agents, mcp) and "Server
administration" (setup, serve, doctor, logs, dkim-keygen, portcheck,
uninstall, upgrade) so newcomers can orient themselves at a glance.
Implementation:
- Clap 4 doesn't allow `help_heading` on subcommand variants, so the
parent's `help_template` omits `{subcommands}` and slots a hand-rolled
grouped list in via `{after-help}`. The list lives in a
`SUBCOMMAND_HELP` constant with a maintainer comment.
- New tagline ("The internet's oldest protocol, rebuilt for AI agents.")
replaces the old `about`/`long_about` pair; the multi-paragraph
description it carried lives in the README.
- Hides `ingest` from top-level `--help` (still wired and invoked by
`aimx serve` over stdin).
- Declares `Cli::version` purely so `-V, --version` renders in the
Options block; `handle_version_flag` in `main.rs` still intercepts
the flag before `Cli::parse()` runs.
- Tightens every subcommand's one-line description.
Integration test updated to assert the new headings and that `ingest`
is hidden.
The hand-rolled `after_help` listing in `src/cli.rs` replaces clap's
default `Commands:` heading with audience-grouped sections
("Operations (as current user):", "Server administration:"), so the
verb extractor stopped finding any subcommands and the script bailed
with the "could not extract" guard.
Switch the awk to match any `^ <verb> +<Description>` line so it
works against both the legacy and grouped layouts. Also allowlist
`ingest`, which is now `#[command(hide = true)]` but is still a real
CLI entry point documented in book/cli.md and book/hook-recipes.md.
Summary
Splits the flat 14-command list in
aimx --helpinto two groups so newcomers can orient themselves at a glance:send,mailboxes,hooks,agents,mcp— the per-user verbs that the daemon resolves viaSO_PEERCRED.setup,serve,doctor,logs,dkim-keygen,portcheck,uninstall,upgrade— the operator-class verbs (most root-gated,serveeffectively root because it binds:25).Also retitles the tagline to "The internet's oldest protocol, rebuilt for AI agents.", hides the internal-only
ingestsubcommand from top-level--help, and tightens every command's one-line description.Before / after
Before:
After:
Implementation notes
help_headingonSubcommandvariants — only onArg. The parent'shelp_templatetherefore omits{subcommands}and injects a hand-rolled grouped list via{after-help}. The list lives in aSUBCOMMAND_HELP&strconstant with a maintainer comment to keep it in sync with theCommandenum (the only real cost of this approach).long_aboutso-hand--helprender identically; the multi-paragraph description it carried lives inbook/and the README.Cli::version: boolfield purely so clap renders-V, --versionin the Options block. The existinghandle_version_flaginterceptor inmain.rsstill fires beforeCli::parse()runs, so the flag is intercepted exactly as before; the field is#[allow(dead_code)]and never read.ingestis now#[command(hide = true)]. The subcommand still parses and runs (it's invoked byaimx serveover stdin in production), it just no longer clutters top-level help.Test plan
cargo build— cleancargo test— 1175 passing, 0 failing across all suites (8/8 help-related integration tests pass)cargo clippy -- -D warnings— cleancargo fmt -- --check— cleanaimx --helpandaimx -hboth render the new grouped layoutaimx ingest <rcpt>still parses and runs (verified hidden, not removed)aimx --versionstill prints the custom banner viahandle_version_flagaimx <subcommand> --helpunchanged (each subcommand keeps its own auto-rendered help)🤖 Generated with Claude Code