From 0a487acb4927a87f0c3bbce271d2d5c8699b2ee1 Mon Sep 17 00:00:00 2001 From: Lewis Tunstall Date: Fri, 8 May 2026 13:55:25 +0200 Subject: [PATCH 1/2] Align CLI help output Co-authored-by: OpenAI Codex --- agent/utils/terminal_display.py | 53 ++++++++++++++++++++++++-------- tests/unit/test_cli_rendering.py | 19 ++++++++++++ 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/agent/utils/terminal_display.py b/agent/utils/terminal_display.py index a10ac33f..e13c4819 100644 --- a/agent/utils/terminal_display.py +++ b/agent/utils/terminal_display.py @@ -6,6 +6,7 @@ import re from rich.console import Console +from rich.markup import escape from rich.markdown import Heading, Markdown from rich.panel import Panel from rich.theme import Theme @@ -446,18 +447,46 @@ def print_yolo_approve(count: int) -> None: # ── Help ─────────────────────────────────────────────────────────────── -HELP_TEXT = f"""\ -{_I}[bold]Commands[/bold] -{_I} [cyan]/help[/cyan] Show this help -{_I} [cyan]/undo[/cyan] Undo last turn -{_I} [cyan]/compact[/cyan] Compact context window -{_I} [cyan]/resume[/cyan] [index|id|path] Pick up from a log in ./session_logs -{_I} [cyan]/model[/cyan] [id] Show available models or switch -{_I} [cyan]/effort[/cyan] [level] Reasoning effort (minimal|low|medium|high|xhigh|max|off) -{_I} [cyan]/yolo[/cyan] Toggle auto-approve mode -{_I} [cyan]/status[/cyan] Current model & turn count -{_I} [cyan]/share-traces[/cyan] [public|private] Show/flip visibility of your HF trace dataset -{_I} [cyan]/quit[/cyan] Exit""" +HELP_ROWS: tuple[tuple[str, str, str], ...] = ( + ("/help", "", "Show this help"), + ("/undo", "", "Undo last turn"), + ("/compact", "", "Compact context window"), + ("/resume", "[index|id|path]", "Pick up from ./session_logs"), + ("/model", "[id]", "Show available models or switch"), + ( + "/effort", + "[level]", + "Set reasoning effort preference", + ), + ("/yolo", "", "Toggle auto-approve mode"), + ("/status", "", "Current model & turn count"), + ( + "/share-traces", + "[public|private]", + "Show or change HF trace visibility", + ), + ("/quit", "", "Exit"), +) + +_HELP_COMMAND_WIDTH = max(len(command) for command, _, _ in HELP_ROWS) +_HELP_ARGS_WIDTH = max(len(args) for _, args, _ in HELP_ROWS) + + +def _format_help_row(command: str, args: str, description: str) -> str: + command_gap = " " * (_HELP_COMMAND_WIDTH - len(command) + 2) + args_gap = " " * (_HELP_ARGS_WIDTH - len(args) + 2) + command_markup = f"[cyan]{escape(command)}[/cyan]" + args_markup = f"[muted]{escape(args)}[/muted]" if args else "" + return f"{_I} {command_markup}{command_gap}{args_markup}{args_gap}{description}" + + +HELP_TEXT = "\n".join( + [f"{_I}[bold]Commands[/bold]"] + + [ + _format_help_row(command, args, description) + for command, args, description in HELP_ROWS + ] +) def print_help() -> None: diff --git a/tests/unit/test_cli_rendering.py b/tests/unit/test_cli_rendering.py index e94700bf..cb0d3ac5 100644 --- a/tests/unit/test_cli_rendering.py +++ b/tests/unit/test_cli_rendering.py @@ -5,6 +5,7 @@ from types import SimpleNamespace import pytest +from rich.console import Console import agent.main as main_mod from agent.tools.research_tool import _get_research_model @@ -29,6 +30,24 @@ def test_non_anthropic_research_model_is_unchanged(): assert _get_research_model("openai/gpt-5.4") == "openai/gpt-5.4" +def test_help_output_keeps_descriptions_aligned(monkeypatch): + output = StringIO() + console = Console(file=output, color_system=None, width=120) + monkeypatch.setattr(terminal_display, "_console", console) + + terminal_display.print_help() + + lines = [line.rstrip() for line in output.getvalue().splitlines() if line.strip()] + description_columns = [] + for command, args, description in terminal_display.HELP_ROWS: + line = next(line for line in lines if command in line) + if args: + assert args in line + description_columns.append(line.index(description)) + + assert len(set(description_columns)) == 1 + + def test_subagent_display_does_not_spawn_background_redraw(monkeypatch): calls: list[object] = [] From 353cfcae7d16f8c83675a897ff4459c5a5cd3a75 Mon Sep 17 00:00:00 2001 From: Lewis Tunstall Date: Fri, 8 May 2026 14:14:06 +0200 Subject: [PATCH 2/2] Address CLI help review feedback Co-authored-by: OpenAI Codex --- agent/utils/terminal_display.py | 47 +++++++++++++++++++++++--------- tests/unit/test_cli_rendering.py | 28 ++++++++++++++++++- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/agent/utils/terminal_display.py b/agent/utils/terminal_display.py index e13c4819..a3cb742b 100644 --- a/agent/utils/terminal_display.py +++ b/agent/utils/terminal_display.py @@ -468,30 +468,51 @@ def print_yolo_approve(count: int) -> None: ("/quit", "", "Exit"), ) -_HELP_COMMAND_WIDTH = max(len(command) for command, _, _ in HELP_ROWS) -_HELP_ARGS_WIDTH = max(len(args) for _, args, _ in HELP_ROWS) + +def _help_column_widths( + rows: tuple[tuple[str, str, str], ...], +) -> tuple[int, int]: + return ( + max(len(command) for command, _, _ in rows), + max(len(args) for _, args, _ in rows), + ) -def _format_help_row(command: str, args: str, description: str) -> str: - command_gap = " " * (_HELP_COMMAND_WIDTH - len(command) + 2) - args_gap = " " * (_HELP_ARGS_WIDTH - len(args) + 2) +def _format_help_row( + command: str, + args: str, + description: str, + command_width: int, + args_width: int, +) -> str: + command_gap = " " * (command_width - len(command) + 2) + args_gap = " " * (args_width - len(args) + 2) command_markup = f"[cyan]{escape(command)}[/cyan]" args_markup = f"[muted]{escape(args)}[/muted]" if args else "" return f"{_I} {command_markup}{command_gap}{args_markup}{args_gap}{description}" -HELP_TEXT = "\n".join( - [f"{_I}[bold]Commands[/bold]"] - + [ - _format_help_row(command, args, description) - for command, args, description in HELP_ROWS - ] -) +def format_help_text(rows: tuple[tuple[str, str, str], ...] | None = None) -> str: + help_rows = HELP_ROWS if rows is None else rows + command_width, args_width = _help_column_widths(help_rows) + return "\n".join( + [f"{_I}[bold]Commands[/bold]"] + + [ + _format_help_row( + command, + args, + description, + command_width, + args_width, + ) + for command, args, description in help_rows + ] + ) def print_help() -> None: _console.print() - _console.print(HELP_TEXT) + _console.print(format_help_text()) _console.print() diff --git a/tests/unit/test_cli_rendering.py b/tests/unit/test_cli_rendering.py index cb0d3ac5..e56d8c20 100644 --- a/tests/unit/test_cli_rendering.py +++ b/tests/unit/test_cli_rendering.py @@ -32,7 +32,12 @@ def test_non_anthropic_research_model_is_unchanged(): def test_help_output_keeps_descriptions_aligned(monkeypatch): output = StringIO() - console = Console(file=output, color_system=None, width=120) + console = Console( + file=output, + color_system=None, + theme=terminal_display._THEME, + width=120, + ) monkeypatch.setattr(terminal_display, "_console", console) terminal_display.print_help() @@ -48,6 +53,27 @@ def test_help_output_keeps_descriptions_aligned(monkeypatch): assert len(set(description_columns)) == 1 +def test_help_output_recomputes_widths_from_rows(): + rows = terminal_display.HELP_ROWS + ( + ("/longer-command", "[longer-args]", "Synthetic help row"), + ) + output = StringIO() + Console( + file=output, + color_system=None, + theme=terminal_display._THEME, + width=140, + ).print(terminal_display.format_help_text(rows)) + + lines = [line.rstrip() for line in output.getvalue().splitlines() if line.strip()] + description_columns = [ + next(line for line in lines if command in line).index(description) + for command, _args, description in rows + ] + + assert len(set(description_columns)) == 1 + + def test_subagent_display_does_not_spawn_background_redraw(monkeypatch): calls: list[object] = []