close
Skip to content

feat(cli): group the top-level help and lead with gateway/service/doctor - #5159

Merged
bolichen97 merged 1 commit into
mainfrom
chore/cli-help-groups
Aug 23, 2026
Merged

feat(cli): group the top-level help and lead with gateway/service/doctor#5159
bolichen97 merged 1 commit into
mainfrom
chore/cli-help-groups

Conversation

@bolichen97

Copy link
Copy Markdown
Collaborator

What

kirocrew --help was a wall. It listed ~40 subcommands as one flat argparse
block in registration order, so the three commands a new install actually
needs — gateway, service, doctor — sat in the middle of it. The usage line
was a ~400-character {chat,doctor,gateway,…} choice blob, 17 internal mcp-*
servers rendered as literal ==SUPPRESS== rows (passing help=argparse.SUPPRESS
to add_parser prints the sentinel — argparse only honours it on the parent's
own actions), and nothing anywhere said how gateway differs from
service install, or what port either one opens.

After

usage: kirocrew [-h] [--version] [-v] [--no-jail] <command> [<args>]

Start here:
  gateway      Start Kiro Crew in this terminal (dashboard + messaging channels)
  service      Run the gateway as a background service that starts on boot
  doctor       Verify this install and diagnose problems

gateway vs. service -- the same server, two lifetimes:
  kirocrew gateway          runs in the foreground and stops on Ctrl-C or when
                            the terminal closes. Best for a first look and for
                            development.
  kirocrew service install  registers a systemd unit (Linux, needs sudo) or a
                            launchd agent (macOS) that runs the SAME gateway
                            detached: it survives logout, restarts on crash and
                            starts at boot. Then use `kirocrew service status`,
                            `kirocrew restart`, `kirocrew logs`.
  Run only one of them at a time -- both bind the same port.

Ports: the dashboard is the only port Kiro Crew opens, and it binds loopback
  only -- http://localhost:5476. Messaging channels (Slack, Discord, ...)
  connect outbound, so nothing else needs to be reachable. Override the port
  with `kirocrew gateway --port N`, KIROCREW_PORT=N, or the `dashboard.url`
  config value; for the service, set KIROCREW_PORT when you run
  `service install` (later, edit /etc/kirocrew/kirocrew.env and restart).

Run the gateway:
  status       Show runtime stats
  ...

Nine sections follow: Run the gateway, Set it up, Work with the agent,
Memory and knowledge, Extend it, Security and privacy,
Move it and back it up, Develop Kiro Crew itself.

How

cli_help.py owns the taxonomy. COMMAND_GROUPS — ordered sections of ordered
(command, summary) pairs — is the single source of truth for the listing, so
registration order in cli.py no longer matters and no command block was
moved
(deliberately: cli.py is a hot file).

  • cli.py sets help=argparse.SUPPRESS on the subparsers action to hide
    argparse's flat block, passes cli_help.TOP_USAGE as usage= (the suppressed
    action would otherwise drop the placeholder from the usage line), and pins
    prog="kirocrew" on the action — without it argparse derives each
    subcommand's prog from the parent's usage=, prefixing every
    kirocrew <cmd> --help with the whole top-level usage string.
  • Every user-facing command registers through cli_help.add_command, which
    raises KeyError for a name that is in no section: a new command cannot be
    added without appearing in the help. The section summary becomes the
    subparser's description (what kirocrew <cmd> --help prints), so the
    sentence exists once. A caller may still pass a longer descriptionbench
    does.
  • mcp-* register with no help at all, and hide_internal_commands swaps the
    action's choices for a live Mapping view over the same parser map that
    iterates only user-facing commands, in section order. So a typo now gets
    choose from gateway, service, doctor, … instead of a list including 17 MCP
    servers. Membership is unfiltered and _name_parser_map is untouched, so
    kirocrew mcp-core still dispatches — the filter changes what argparse prints,
    never what it accepts.

Behaviour change worth calling out

Each command's one-line summary moved from help= to description=, so
kirocrew <cmd> --help now prints that sentence at the top. Previously it
appeared only in the parent listing.

Tests

test/test_cli_help.py (11 tests): offered-vs-grouped parity read off argparse's
own invalid-choice message; no command in two sections; add_command refuses an
ungrouped name; mcp-* absent from both the help and the error but still
dispatchable; Start here leads with the three; no ==SUPPRESS== / choice blob;
subcommand usage not prefixed with the top-level usage; and the help's spelled-out
port stays equal to config.loader._DEFAULT_PORT.

Local gates: check_black_formatting.py, isort, flake8, mypy (1031 files),
docs-lint.sh, brand and harness-parity gates, and 1140 existing CLI-adjacent
tests.

docs/system-specs/modules/cli.md documents the contract under a new
### Top-level help.

`kirocrew --help` listed ~40 subcommands as one flat argparse block in
registration order, headed by a `{chat,doctor,gateway,...}` choice blob
that filled the usage line, with 17 `mcp-*` internals rendered as
literal `==SUPPRESS==` rows. The three commands a new install actually
needs sat in the middle of it, and nothing said how `gateway` differs
from `service install` or what port either one opens.

The taxonomy now lives in `cli_help.py`: ordered sections, `Start here`
first with exactly gateway, service and doctor, followed by notes on the
two lifetimes (foreground vs. systemd/launchd, one at a time) and on the
single loopback dashboard port. argparse's own listing is suppressed and
the grouped listing is rendered into the epilog.

Every user-facing command registers through `cli_help.add_command`,
which refuses a name that is in no section, so a new command cannot be
added without appearing in the help; the section summary becomes the
subparser `description`, which is what `kirocrew <cmd> --help` prints.

The `mcp-*` MCP servers the agent backend spawns are dropped from the
listing and from the `invalid choice: 'x' (choose from ...)` message,
which otherwise answered a typo with all 17 of them. Their `choices`
view filters iteration only: membership and `_name_parser_map` are
untouched, so `kirocrew mcp-core` still dispatches.
@bolichen97
bolichen97 requested a review from a team as a code owner August 23, 2026 04:27
@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Aug 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor

GPT 5.6 Review — ✅ no blocking findings

GPT 5.6 completed its review of 1079b2d872362737b4a34cc3a705be65153ab6e0 and found no blocking issues.

This comment is updated in place on each push.

Review details

No findings.
[GPT-REVIEWED] 1079b2d

False positive or not applicable? A repository writer can comment:
/ai-review override gpt 1079b2d872362737b4a34cc3a705be65153ab6e0: <one-sentence reason>

@github-actions

Copy link
Copy Markdown
Contributor

Design Review (Fable 5) — ✅ PASS

Design-level review of 1079b2d872362737b4a34cc3a705be65153ab6e0 — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

Design-Verdict: PASS

Real usability harm, solved at the right layer with a fail-loud guard (add_command KeyError) and parity tests that pin every argparse-internal dependency.

The one genuine coupling — _VisibleCommandChoices relying on argparse validating via __contains__ and dispatching through _name_parser_map — is private-API dependent across Python versions, but the failure mode on drift is cosmetic (mcp-* names reappear in an error message) and test_cli_help.py reads the actual rendered message, so a stdlib change goes red instead of silent. Doc updated in the same commit per spec rules.

[DESIGN-REVIEWED] 1079b2d

@github-actions

Copy link
Copy Markdown
Contributor

First Principles Review (Fable 5) — 🟡 CONCERNS

Premise-level review of 1079b2d872362737b4a34cc3a705be65153ab6e0 — why this exists and whether the shipped surface is the smallest honest version. Updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

Both data files and the surrounding code are read; the review follows.

First-Principles-Verdict: CONCERNS

The grouped help earns its place; two small riders don't: a hand-duplicated port constant policed by a test, and a width knob no caller passes.

What this change ships

Intent: make kirocrew --help lead a new install to gateway/service/doctor. ADDITION (grouped-help taxonomy) carrying a FIX (the literal ==SUPPRESS== rows).

  1. Help shows nine named sections, "Start here" first — justified
  2. Help explains gateway-vs-service and the single loopback port — justified (named gap)
  3. ==SUPPRESS== rows and the 400-char usage blob are gone — justified (defect fix)
  4. mcp-* servers vanish from help and typo errors, still dispatch — justified
  5. Typo's "choose from …" list reordered to match the help — justified
  6. kirocrew <cmd> --help now opens with its one-line summary — declared, justified
  7. A command in no section cannot register (KeyError guard) — justified (drift guard)
  8. Port 5476 now spelled in two modules — duplicate of config/loader.py:316
  9. render_epilog(width=13) parameter — one consumer, generalized
  10. cli.md spec section — mandated by AGENTS.md same-commit rule

Watch

  • Same root cause, one unfixed sibling: argparse joins choices into the invalid-choice error, and pod's hidden _run/_cleanup (cli.py:1565, cli.py:1567) still leak into kirocrew pod <typo> — the pod metavar hides them from usage only. Grepped add_parser("_: 2 hits, both there. Fine to defer; the fix is reusing hide_internal_commands shape or nothing.

Subtractions

  • Drop _DEFAULT_PORT_TEXT (cli_help.py) and the assert cli_help._DEFAULT_PORT_TEXT == str(_DEFAULT_PORT) guard — interpolate config.loader._DEFAULT_PORT directly; cli.py, cli_help's only importer, already imports the loader, so the second spelling buys nothing except a test to police it.
  • Drop the width parameter of render_epilog — one call site (cli.py:936), never passed; inline the 13.

[FIRST-PRINCIPLES-REVIEWED] 1079b2d

@github-actions

Copy link
Copy Markdown
Contributor

Opus 4.8 Review — ✅ no blocking findings

Reviewed 1079b2d872362737b4a34cc3a705be65153ab6e0 — this comment is updated in place on each push.

Review details

No candidates were supplied, and I independently verified the load-bearing mechanisms: _VisibleCommandChoices.__contains__ delegates to the real _name_parser_map (so validation via _check_value and dispatch via _SubParsersAction.__call__ stay complete), hide_internal_commands runs after every add_parser and before parse_args, and all three externally-registered commands (perf/bench/desktop) are both called and present in SUMMARIES. Nothing survives.

No findings.

[OPUS-REVIEWED] 1079b2d

Verdict parsed from the review's SHA-scoped output markers for commit 1079b2d872362737b4a34cc3a705be65153ab6e0.

False positive or not applicable? A repository writer can comment:
/ai-review override fable 1079b2d872362737b4a34cc3a705be65153ab6e0: <one-sentence reason>

@github-actions github-actions Bot added readiness: passed Eligible automated validation passed for the current revision and removed readiness: checking Automated validation is still running labels Aug 23, 2026
@bolichen97
bolichen97 enabled auto-merge (squash) August 23, 2026 05:03
@bolichen97
bolichen97 merged commit 55814a8 into main Aug 23, 2026
62 checks passed
@bolichen97
bolichen97 deleted the chore/cli-help-groups branch August 23, 2026 05:11
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Aug 23, 2026
kyleseaman pushed a commit that referenced this pull request Aug 23, 2026
…tor (#5159) (#5353)

`kirocrew --help` listed ~40 subcommands as one flat argparse block in
registration order, headed by a `{chat,doctor,gateway,...}` choice blob
that filled the usage line, with 17 `mcp-*` internals rendered as
literal `==SUPPRESS==` rows. The three commands a new install actually
needs sat in the middle of it, and nothing said how `gateway` differs
from `service install` or what port either one opens.

The taxonomy now lives in `cli_help.py`: ordered sections, `Start here`
first with exactly gateway, service and doctor, followed by notes on the
two lifetimes (foreground vs. systemd/launchd, one at a time) and on the
single loopback dashboard port. argparse's own listing is suppressed and
the grouped listing is rendered into the epilog.

Every user-facing command registers through `cli_help.add_command`,
which refuses a name that is in no section, so a new command cannot be
added without appearing in the help; the section summary becomes the
subparser `description`, which is what `kirocrew <cmd> --help` prints.

The `mcp-*` MCP servers the agent backend spawns are dropped from the
listing and from the `invalid choice: 'x' (choose from ...)` message,
which otherwise answered a typo with all 17 of them. Their `choices`
view filters iteration only: membership and `_name_parser_map` are
untouched, so `kirocrew mcp-core` still dispatches.

(cherry picked from commit 55814a8)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants