Skip to content

improve help: section for subcommands; if subcommand is missing, display nice help page, not click default#2442

Open
evgunter wants to merge 11 commits into
ev/azure-updatefrom
ev/improve-help
Open

improve help: section for subcommands; if subcommand is missing, display nice help page, not click default#2442
evgunter wants to merge 11 commits into
ev/azure-updatefrom
ev/improve-help

Conversation

@evgunter

@evgunter evgunter commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Two related improvements to the help output for command groups.

Note on base branch: based on ev/azure-update (PR #2432), which already made config/plugin require a subcommand and exit 2 with plain click help. This PR builds the unified git-style rendering on top of that groundwork. It should merge after (or together with) #2432.

Group --help now lists subcommands

The git-style help page (mngr <group> --help, mngr help <group>) previously rendered NAME / SYNOPSIS / DESCRIPTION / OPTIONS but no command list, so mngr config --help never showed list/get/set/... This adds a COMMANDS section (placed before OPTIONS for prominence) listing each subcommand with its one-line description and inline aliases.

Unified bare-invocation behavior

Bare group invocation is centralized in DefaultCommandGroup: with no subcommand it renders the same rich, pageable git-style page to stdout and exits 2, instead of click's plain stderr usage line. config and plugin are migrated onto DefaultCommandGroup so they behave identically to snapshot/git/top-level mngr.

Because the new COMMANDS section lands too, switching bare invocation to the git-style page is a strict improvement (it no longer loses the command list that plain click help showed).

The config/plugin synopsis now enumerates the common subcommands ([list|get|set|unset|edit|...], [list|add|remove|enable|disable|...]) rather than a bare COMMAND, matching how snapshot/git already read.

Known minor limitation

mngr config --scope user (a group-level option but no subcommand) still shows click's terse Error: Missing command. rather than the git-style page, because click intercepts before the bare-args path. This is a genuine usage error and an uncommon invocation; left as-is.

Testing

  • Updated snapshot_test, default_command_group_test, test_plugin to assert the git-style page + exit 2; added help_formatter_test cases for the COMMANDS section.
  • Regenerated libs/mngr/docs/commands/{config,plugin}.md; make_cli_docs.py --check passes.
  • Ran help/config/plugin/snapshot/default_command_group suites + ratchets + ty locally (green; 25 unrelated errors on this machine are an old-git git init -b main fixture issue that passes in CI).

🤖 Generated with Claude Code

evgunter and others added 6 commits July 11, 2026 20:07
…ion page

Fixes issues #10 and #15 from the local tracker.

#10: the git-style help page (mngr <group> --help, mngr help <group>) now
renders a COMMANDS section listing a group's subcommands with descriptions
and inline aliases, so config/snapshot/git/plugin help surface what you can
do. Placed before OPTIONS for prominence.

#15: centralize bare group invocation in DefaultCommandGroup -- with no
subcommand it renders the same rich, pageable git-style page to stdout and
exits 2, instead of click's plain stderr usage line. config and plugin are
migrated onto DefaultCommandGroup so they behave identically to snapshot/git
and the top-level mngr group. config/plugin synopsis switched to the
click-standard "COMMAND [ARGS]...".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…MAND

With many subcommands, enumerate the most common few plus "..." (config:
list|get|set, plugin: list|add|remove) rather than a bare COMMAND -- more
concrete and consistent with snapshot/git, which enumerate. The COMMANDS
section lists the full set below.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
config: [get|set|unset|list|...] (CRUD basics, mutating verbs first).
plugin: [add|remove|enable|disable|...] (lifecycle verbs; ... covers list
and the user-facing install-wizard).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
config: [list|get|set|unset|...]; plugin: [list|add|remove|enable|disable|...].
list leads (the discovery command) with the full width still under 80 cols.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
config: [list|get|set|unset|edit|...] -- surfaces the whole-file editor
escape hatch alongside the CRUD basics; still under 80 cols.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
evgunter and others added 4 commits July 11, 2026 21:52
Dropped inadvertently when switching from "<subcommand> [OPTIONS]" to the
enumerated form. Placed after [ARGS]..., matching snapshot's synopsis; the
group and its subcommands do take options (--scope, etc.).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Keep the original phrasing from #2432; it's still accurate and doesn't need
the added mechanism detail.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Use an isinstance(MngrContext) check instead of hasattr, and assign config
in an if/else rather than initializing-then-reassigning.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
output.write(f" $ {example}\n\n")


def _write_commands_section(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do all these _write_*_section commands write into a buffer instead of being pure functions that return a string and are assembled later

@evgunter evgunter changed the title Improve group help: COMMANDS section + unified git-style bare-invocation page improve help: section for subcommands; if subcommand is missing, display nice help page, not click default Jul 12, 2026
@evgunter evgunter marked this pull request as ready for review July 12, 2026 05:17
@evgunter evgunter requested a review from joshalbrecht July 12, 2026 05:17
Problem: In help_formatter._write_commands_section, a subcommand's name was
passed as the initial_indent to _wrap_text and only emitted as part of the
wrapped description. _wrap_text returns '' for empty input, so a subcommand
with no metadata and no click short-help (get_short_help_str == '') had its
name silently dropped, rendering a blank row (and a spurious extra blank line
in the wide-name branch).

Fix: Write the bare name line and continue when the description is empty, so
the command is never dropped. Add a regression unit test covering a group
whose subcommand has no help text.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant