Skip to content

Commit 3e5e732

Browse files
yeldarbyclaude
andcommitted
fix(cli): consistent Rich-formatted flattened help across all help paths
All four help paths (--help, -h, no args, 'help' command) now show the same Rich-formatted flattened command listing. Subcommand --help still uses typer's default grouped view. Root help intercept happens in main() before typer processes --help, so the flattened output is shown instead of typer's grouped view. Rich panels and tables match the subcommand help styling. 374 tests pass, all linting clean. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent eedc6bc commit 3e5e732

1 file changed

Lines changed: 48 additions & 38 deletions

File tree

roboflow/cli/__init__.py

Lines changed: 48 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,13 @@ def _root_callback(
8888

8989

9090
def _print_flattened_help() -> None:
91-
"""Print a custom help screen with all commands flattened and alphabetized."""
92-
import shutil
91+
"""Print a Rich-formatted help screen with all commands flattened and alphabetized."""
92+
from rich.console import Console
93+
from rich.panel import Panel
94+
from rich.table import Table
95+
from rich.text import Text
96+
97+
console = Console()
9398

9499
click_app = typer.main.get_command(app)
95100

@@ -99,50 +104,45 @@ def _print_flattened_help() -> None:
99104
def _walk(group: Any, prefix: str = "") -> None:
100105
for name in sorted(group.list_commands(None) or []): # type: ignore[arg-type]
101106
cmd = group.get_command(None, name) # type: ignore[arg-type]
102-
if cmd is None:
103-
continue
104-
# Skip hidden commands
105-
if getattr(cmd, "hidden", False):
107+
if cmd is None or getattr(cmd, "hidden", False):
106108
continue
107109
full = f"{prefix} {name}".strip() if prefix else name
108110
if hasattr(cmd, "list_commands") and cmd.list_commands(None):
109111
_walk(cmd, full)
110112
else:
111-
help_text = cmd.get_short_help_str() or ""
112-
commands.append((full, help_text))
113+
commands.append((full, cmd.get_short_help_str() or ""))
113114

114115
_walk(click_app)
115116
commands.sort(key=lambda x: x[0])
116117

117-
# Calculate column width
118-
width = shutil.get_terminal_size((80, 24)).columns
119-
name_width = max((len(c[0]) for c in commands), default=20) + 2
120-
desc_width = max(width - name_width - 4, 20)
121-
122-
# Print
123-
print(f"\n \033[1mroboflow\033[0m v{roboflow.__version__}") # noqa: T201
124-
print(f" {_DESCRIPTION}\n") # noqa: T201
125-
126-
print(" \033[1mUsage:\033[0m roboflow [OPTIONS] COMMAND [ARGS]\n") # noqa: T201
127-
128-
print(" \033[1mOptions:\033[0m") # noqa: T201
129-
options = [
130-
(" --api-key, -k <KEY>", "API key override"),
131-
(" --json, -j", "Output as JSON (for agents and piping)"),
132-
(" --quiet, -q", "Suppress progress bars and status messages"),
133-
(" --version, -v", "Show version"),
134-
(" --workspace, -w <ID>", "Workspace override"),
135-
(" --help, -h", "Show this help"),
136-
]
137-
opt_name_width = max(len(o[0]) for o in options) + 2
138-
for opt_name, opt_help in options:
139-
print(f" {opt_name:<{opt_name_width}} {opt_help}") # noqa: T201
140-
141-
print("\n \033[1mCommands:\033[0m") # noqa: T201
118+
# Usage line
119+
console.print()
120+
console.print(" Usage: roboflow [OPTIONS] COMMAND [ARGS]...", highlight=False)
121+
console.print()
122+
console.print(f" {_DESCRIPTION}", highlight=False)
123+
console.print()
124+
125+
# Options panel
126+
opt_table = Table(show_header=False, box=None, padding=(0, 2))
127+
opt_table.add_column(style="bold", no_wrap=True)
128+
opt_table.add_column()
129+
opt_table.add_row("--api-key -k TEXT", "API key override (default: $ROBOFLOW_API_KEY or config file)")
130+
opt_table.add_row("--json -j", "Output results as JSON (stable schema, for agents and piping)")
131+
opt_table.add_row("--quiet -q", "Suppress non-essential output (progress bars, status messages)")
132+
opt_table.add_row("--version -v", "Show package version and exit")
133+
opt_table.add_row("--workspace -w TEXT", "Workspace URL or ID override (default: configured default)")
134+
opt_table.add_row("--help -h", "Show this message and exit.")
135+
console.print(Panel(opt_table, title="Options", title_align="left", border_style="dim"))
136+
137+
# Commands panel
138+
cmd_table = Table(show_header=False, box=None, padding=(0, 2))
139+
cmd_table.add_column(style="bold", no_wrap=True)
140+
cmd_table.add_column()
142141
for cmd_name, help_text in commands:
143-
truncated = help_text[:desc_width] if len(help_text) > desc_width else help_text
144-
print(f" {cmd_name:<{name_width}} {truncated}") # noqa: T201
145-
print() # noqa: T201
142+
cmd_table.add_column
143+
cmd_table.add_row(cmd_name, help_text)
144+
console.print(Panel(cmd_table, title="Commands", title_align="left", border_style="dim"))
145+
console.print()
146146

147147

148148
# ---------------------------------------------------------------------------
@@ -206,9 +206,9 @@ def _walk(group: Any, prefix: str = "") -> None:
206206

207207
# "roboflow help" command
208208
@app.command("help", hidden=True)
209-
def help_command(ctx: typer.Context) -> None:
209+
def help_command(ctx: typer.Context) -> None: # noqa: ARG001
210210
"""Show help information."""
211-
print(ctx.parent.get_help() if ctx.parent else ctx.get_help()) # noqa: T201
211+
_print_flattened_help()
212212

213213

214214
# ---------------------------------------------------------------------------
@@ -343,6 +343,16 @@ def main() -> None:
343343

344344
sys.argv[1:] = _reorder_argv(sys.argv[1:])
345345

346+
# Intercept root-level --help/-h: show our flattened help instead of typer's grouped view.
347+
# Only for the ROOT command (not subcommands like 'roboflow project --help').
348+
if "--help" in sys.argv[1:] or "-h" in sys.argv[1:]:
349+
argv = sys.argv[1:]
350+
help_idx = next((i for i, a in enumerate(argv) if a in ("--help", "-h")), -1)
351+
pre_help = [a for a in argv[:help_idx] if not a.startswith("-")]
352+
if not pre_help:
353+
_print_flattened_help()
354+
sys.exit(0)
355+
346356
# In --json mode, intercept Click/typer validation errors and emit
347357
# structured JSON on stderr instead of Rich-formatted text.
348358
json_mode = "--json" in sys.argv or "-j" in sys.argv

0 commit comments

Comments
 (0)