* Fix --help flag executing commands instead of showing help (#650) The --help check fell through to command dispatch when subcommandUsage returned nil for a command. Now --help always returns before dispatch, printing a generic fallback when no specific help text exists. Also adds missing subcommandUsage entries for: new-window, list-panes, list-pane-surfaces, surface-health, trigger-flash, list-panels, focus-panel, set-app-focus, ping, capabilities, identify, list-windows, current-window, refresh-surfaces, current-workspace, list-notifications, clear-notifications. Closes #650 * Document all flags and options in CLI --help output Every subcommand's help text now shows all accepted flags, options, environment variable fallbacks, and positional arguments matching the actual dispatch code. Also adds help entries for commands that were returning the generic fallback (list-workspaces, list-panes, list-pane-surfaces, surface-health, trigger-flash, list-panels, focus-panel, set-app-focus, etc.) * Show 'Unknown command' for invalid commands, add legacy alias help Unknown commands now show "Unknown command 'X'. Run 'cmux help' to see available commands." instead of the misleading "No detailed help available." Also adds help entries for legacy browser aliases (open-browser, navigate, etc.) pointing to 'cmux browser --help'. * Audit all 89 CLI commands for complete help coverage - Add missing `help` subcommandUsage entry - Expand id|ref → id|ref|index for move-workspace-to-window, close-workspace, select-workspace, rename-workspace - Document CMUX_WORKSPACE_ID/CMUX_TAB_ID/CMUX_SURFACE_ID env var defaults in tab-action, rename-workspace - Expand browser help: get (--selector, --attr, --property), find (--name, --exact, --index), network route (--abort, --body), open/open-split/new env var defaults - Remove duplicate rename-window help case (now handled by rename-workspace combined case) - Upgrade regression test to auto-extract dispatch+subcommandUsage switches and flag any commands missing help entries
147 lines
5.4 KiB
Python
147 lines
5.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Regression tests for CLI subcommand help coverage and accuracy."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
|
|
def get_repo_root() -> Path:
|
|
result = subprocess.run(
|
|
["git", "rev-parse", "--show-toplevel"],
|
|
capture_output=True,
|
|
text=True,
|
|
check=False,
|
|
)
|
|
if result.returncode == 0:
|
|
return Path(result.stdout.strip())
|
|
return Path.cwd()
|
|
|
|
|
|
def require(content: str, needle: str, message: str, failures: list[str]) -> None:
|
|
if needle not in content:
|
|
failures.append(message)
|
|
|
|
|
|
def extract_switch_commands(content: str, start_index: int = 0) -> tuple[set[str], int]:
|
|
marker = "switch command {"
|
|
marker_index = content.find(marker, start_index)
|
|
if marker_index == -1:
|
|
return set(), -1
|
|
|
|
open_brace = content.find("{", marker_index)
|
|
if open_brace == -1:
|
|
return set(), -1
|
|
|
|
depth = 1
|
|
cursor = open_brace + 1
|
|
while cursor < len(content) and depth > 0:
|
|
char = content[cursor]
|
|
if char == "{":
|
|
depth += 1
|
|
elif char == "}":
|
|
depth -= 1
|
|
cursor += 1
|
|
|
|
block = content[open_brace + 1:cursor - 1]
|
|
commands: set[str] = set()
|
|
collecting_case = False
|
|
case_lines: list[str] = []
|
|
|
|
for line in block.splitlines():
|
|
stripped = line.strip()
|
|
if stripped.startswith("case "):
|
|
collecting_case = True
|
|
case_lines = [line]
|
|
elif collecting_case:
|
|
case_lines.append(line)
|
|
|
|
if collecting_case and ":" in line:
|
|
case_text = "\n".join(case_lines)
|
|
commands.update(re.findall(r'"([^"]+)"', case_text))
|
|
collecting_case = False
|
|
case_lines = []
|
|
|
|
return commands, cursor
|
|
|
|
|
|
def main() -> int:
|
|
repo_root = get_repo_root()
|
|
cli_path = repo_root / "CLI" / "cmux.swift"
|
|
if not cli_path.exists():
|
|
print(f"FAIL: missing expected file: {cli_path}")
|
|
return 1
|
|
|
|
content = cli_path.read_text(encoding="utf-8")
|
|
failures: list[str] = []
|
|
|
|
require(
|
|
content,
|
|
'if commandArgs.contains("--help") || commandArgs.contains("-h") {',
|
|
"Subcommand help pre-dispatch gate is missing",
|
|
failures,
|
|
)
|
|
require(
|
|
content,
|
|
'if dispatchSubcommandHelp(command: command, commandArgs: commandArgs) {',
|
|
"Subcommand help dispatch call is missing",
|
|
failures,
|
|
)
|
|
require(
|
|
content,
|
|
"print(\"Unknown command '\\(command)'. Run 'cmux help' to see available commands.\")",
|
|
"Subcommand help fallback unknown-command line is missing",
|
|
failures,
|
|
)
|
|
require(
|
|
content,
|
|
"print(\"Unknown command '\\(command)'. Run 'cmux help' to see available commands.\")\n return",
|
|
"Subcommand help fallback must return before command execution",
|
|
failures,
|
|
)
|
|
|
|
dispatch_commands, next_index = extract_switch_commands(content, 0)
|
|
subcommand_usage_commands, _ = extract_switch_commands(content, next_index if next_index != -1 else 0)
|
|
if not dispatch_commands:
|
|
failures.append("Failed to parse main dispatch switch command list")
|
|
if not subcommand_usage_commands:
|
|
failures.append("Failed to parse subcommandUsage switch command list")
|
|
|
|
missing_help_entries = sorted(dispatch_commands - subcommand_usage_commands)
|
|
if missing_help_entries:
|
|
failures.append(
|
|
"Missing subcommandUsage entries for dispatch command(s): "
|
|
+ ", ".join(missing_help_entries)
|
|
)
|
|
|
|
# Regression checks for concrete help text that previously drifted from dispatch logic.
|
|
for needle, message in [
|
|
('case "help":', "Missing subcommandUsage entry for help"),
|
|
("Usage: cmux help", "help subcommand usage text is missing"),
|
|
("Usage: cmux move-workspace-to-window --workspace <id|ref|index> --window <id|ref|index>", "move-workspace-to-window help must document index handles"),
|
|
("--tab <id|ref|index> Target tab (accepts tab:<n> or surface:<n>; default: $CMUX_TAB_ID, then $CMUX_SURFACE_ID, then focused tab)", "tab-action help must document CMUX_TAB_ID/CMUX_SURFACE_ID fallback"),
|
|
("--workspace <id|ref|index> Workspace to rename (default: current/$CMUX_WORKSPACE_ID)", "rename-workspace help must document CMUX_WORKSPACE_ID fallback"),
|
|
("text|html|value|count|box|styles|attr: [--selector <css> | <css>]", "browser get help must document --selector"),
|
|
("attr: [--attr <name> | <name>]", "browser get attr help must document --attr"),
|
|
("styles: [--property <name>]", "browser get styles help must document --property"),
|
|
("role: [--name <text>] [--exact] <role>", "browser find role help must document --name/--exact"),
|
|
("text|label|placeholder|alt|title|testid: [--exact] <text>", "browser find text-like help must document --exact"),
|
|
("nth: [--index <n> | <n>] [--selector <css> | <css>]", "browser find nth help must document --index/--selector"),
|
|
("route <pattern> [--abort] [--body <text>]", "browser network route help must document --abort/--body"),
|
|
]:
|
|
require(content, needle, message, failures)
|
|
|
|
if failures:
|
|
print("FAIL: CLI subcommand help regression(s) detected")
|
|
for failure in failures:
|
|
print(f"- {failure}")
|
|
return 1
|
|
|
|
print("PASS: CLI subcommand help coverage and flag/env documentation are present")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|