Fix --help flag executing commands instead of showing help (#657)
* 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
This commit is contained in:
parent
c5f749640a
commit
181574586e
2 changed files with 603 additions and 48 deletions
504
CLI/cmux.swift
504
CLI/cmux.swift
|
|
@ -696,6 +696,8 @@ struct CMUXCLI {
|
|||
if dispatchSubcommandHelp(command: command, commandArgs: commandArgs) {
|
||||
return
|
||||
}
|
||||
print("Unknown command '\(command)'. Run 'cmux help' to see available commands.")
|
||||
return
|
||||
}
|
||||
|
||||
let client = SocketClient(path: socketPath)
|
||||
|
|
@ -3394,10 +3396,59 @@ struct CMUXCLI {
|
|||
throw CLIError(message: "Unable to resolve surface ID")
|
||||
}
|
||||
|
||||
/// Return the help/usage text for a subcommand, or nil if the command has no
|
||||
/// dedicated help (e.g. simple no-arg commands like `ping`).
|
||||
/// Return the help/usage text for a subcommand, or nil if the command is unknown.
|
||||
private func subcommandUsage(_ command: String) -> String? {
|
||||
switch command {
|
||||
case "ping":
|
||||
return """
|
||||
Usage: cmux ping
|
||||
|
||||
Check connectivity to the cmux socket server.
|
||||
"""
|
||||
case "capabilities":
|
||||
return """
|
||||
Usage: cmux capabilities
|
||||
|
||||
Print server capabilities as JSON.
|
||||
"""
|
||||
case "help":
|
||||
return """
|
||||
Usage: cmux help
|
||||
|
||||
Show top-level CLI usage and command list.
|
||||
"""
|
||||
case "identify":
|
||||
return """
|
||||
Usage: cmux identify [--workspace <id|ref|index>] [--surface <id|ref|index>] [--no-caller]
|
||||
|
||||
Print server identity and caller context details.
|
||||
|
||||
Flags:
|
||||
--workspace <id|ref|index> Caller workspace context (default: $CMUX_WORKSPACE_ID)
|
||||
--surface <id|ref|index> Caller surface context (default: $CMUX_SURFACE_ID)
|
||||
--no-caller Omit caller context from the request
|
||||
"""
|
||||
case "list-windows":
|
||||
return """
|
||||
Usage: cmux list-windows
|
||||
|
||||
List open windows.
|
||||
"""
|
||||
case "current-window":
|
||||
return """
|
||||
Usage: cmux current-window
|
||||
|
||||
Print the currently selected window ID.
|
||||
"""
|
||||
case "new-window":
|
||||
return """
|
||||
Usage: cmux new-window
|
||||
|
||||
Create a new window.
|
||||
|
||||
Example:
|
||||
cmux new-window
|
||||
"""
|
||||
case "focus-window":
|
||||
return """
|
||||
Usage: cmux focus-window --window <id|ref|index>
|
||||
|
|
@ -3426,47 +3477,56 @@ struct CMUXCLI {
|
|||
"""
|
||||
case "move-workspace-to-window":
|
||||
return """
|
||||
Usage: cmux move-workspace-to-window --workspace <id|ref> --window <id|ref>
|
||||
Usage: cmux move-workspace-to-window --workspace <id|ref|index> --window <id|ref|index>
|
||||
|
||||
Move a workspace to a different window.
|
||||
|
||||
Flags:
|
||||
--workspace <id|ref> Workspace to move (required)
|
||||
--window <id|ref> Target window (required)
|
||||
--workspace <id|ref|index> Workspace to move (required)
|
||||
--window <id|ref|index> Target window (required)
|
||||
|
||||
Example:
|
||||
cmux move-workspace-to-window --workspace workspace:2 --window window:1
|
||||
"""
|
||||
case "move-surface":
|
||||
return """
|
||||
Usage: cmux move-surface --surface <id|ref|index> [flags]
|
||||
Usage: cmux move-surface [--surface <id|ref|index> | <id|ref|index>] [flags]
|
||||
|
||||
Move a surface to a different pane, workspace, or window.
|
||||
|
||||
Flags:
|
||||
--surface <id|ref|index> Surface to move (required)
|
||||
--surface <id|ref|index> Surface to move (required unless passed positionally)
|
||||
--pane <id|ref|index> Target pane
|
||||
--workspace <id|ref|index> Target workspace
|
||||
--window <id|ref|index> Target window
|
||||
--before <id|ref|index> Place before this surface
|
||||
--before-surface <id|ref|index>
|
||||
Alias for --before
|
||||
--after <id|ref|index> Place after this surface
|
||||
--after-surface <id|ref|index>
|
||||
Alias for --after
|
||||
--index <n> Place at this index
|
||||
--focus <true|false> Focus the surface after moving
|
||||
|
||||
Example:
|
||||
cmux move-surface --surface surface:1 --workspace workspace:2
|
||||
cmux move-surface --surface 0 --pane pane:2 --index 0
|
||||
cmux move-surface surface:1 --pane pane:2 --index 0
|
||||
"""
|
||||
case "reorder-surface":
|
||||
return """
|
||||
Usage: cmux reorder-surface --surface <id|ref|index> [flags]
|
||||
Usage: cmux reorder-surface [--surface <id|ref|index> | <id|ref|index>] [flags]
|
||||
|
||||
Reorder a surface within its pane.
|
||||
|
||||
Flags:
|
||||
--surface <id|ref|index> Surface to reorder (required)
|
||||
--surface <id|ref|index> Surface to reorder (required unless passed positionally)
|
||||
--workspace <id|ref|index> Workspace context
|
||||
--before <id|ref|index> Place before this surface
|
||||
--before-surface <id|ref|index>
|
||||
Alias for --before
|
||||
--after <id|ref|index> Place after this surface
|
||||
--after-surface <id|ref|index>
|
||||
Alias for --after
|
||||
--index <n> Place at this index
|
||||
|
||||
Example:
|
||||
|
|
@ -3475,15 +3535,19 @@ struct CMUXCLI {
|
|||
"""
|
||||
case "reorder-workspace":
|
||||
return """
|
||||
Usage: cmux reorder-workspace --workspace <id|ref|index> [flags]
|
||||
Usage: cmux reorder-workspace [--workspace <id|ref|index> | <id|ref|index>] [flags]
|
||||
|
||||
Reorder a workspace within its window.
|
||||
|
||||
Flags:
|
||||
--workspace <id|ref|index> Workspace to reorder (required)
|
||||
--workspace <id|ref|index> Workspace to reorder (required unless passed positionally)
|
||||
--index <n> Place at this index
|
||||
--before <id|ref|index> Place before this workspace
|
||||
--before-workspace <id|ref|index>
|
||||
Alias for --before
|
||||
--after <id|ref|index> Place after this workspace
|
||||
--after-workspace <id|ref|index>
|
||||
Alias for --after
|
||||
--window <id|ref|index> Window context
|
||||
|
||||
Example:
|
||||
|
|
@ -3506,7 +3570,7 @@ struct CMUXCLI {
|
|||
Flags:
|
||||
--action <name> Action name (required if not positional)
|
||||
--workspace <id|ref|index> Target workspace (default: current/$CMUX_WORKSPACE_ID)
|
||||
--title <text> Title for rename
|
||||
--title <text> Title for rename (or pass trailing title text)
|
||||
|
||||
Example:
|
||||
cmux workspace-action --workspace workspace:2 --action pin
|
||||
|
|
@ -3529,10 +3593,10 @@ struct CMUXCLI {
|
|||
|
||||
Flags:
|
||||
--action <name> Action name (required if not positional)
|
||||
--tab <id|ref|index> Target tab (accepts tab:<n> or surface:<n>; alias: --surface)
|
||||
--tab <id|ref|index> Target tab (accepts tab:<n> or surface:<n>; default: $CMUX_TAB_ID, then $CMUX_SURFACE_ID, then focused tab)
|
||||
--surface <id|ref|index> Alias for --tab (backward compatibility)
|
||||
--workspace <id|ref|index> Workspace context (default: current/$CMUX_WORKSPACE_ID)
|
||||
--title <text> Title for rename
|
||||
--title <text> Title for rename (or pass trailing title text)
|
||||
--url <url> Optional URL for new-browser-right
|
||||
|
||||
Example:
|
||||
|
|
@ -3562,12 +3626,25 @@ struct CMUXCLI {
|
|||
"""
|
||||
case "new-workspace":
|
||||
return """
|
||||
Usage: cmux new-workspace
|
||||
Usage: cmux new-workspace [--command <text>]
|
||||
|
||||
Create a new workspace in the current window.
|
||||
|
||||
Flags:
|
||||
--command <text> Send text+Enter to the new workspace after creation
|
||||
|
||||
Example:
|
||||
cmux new-workspace
|
||||
cmux new-workspace --command "npm test"
|
||||
"""
|
||||
case "list-workspaces":
|
||||
return """
|
||||
Usage: cmux list-workspaces
|
||||
|
||||
List workspaces in the current window.
|
||||
|
||||
Example:
|
||||
cmux list-workspaces
|
||||
"""
|
||||
case "new-split":
|
||||
return """
|
||||
|
|
@ -3584,6 +3661,33 @@ struct CMUXCLI {
|
|||
cmux new-split right
|
||||
cmux new-split down --workspace workspace:1
|
||||
"""
|
||||
case "list-panes":
|
||||
return """
|
||||
Usage: cmux list-panes [--workspace <id|ref>]
|
||||
|
||||
List panes in a workspace.
|
||||
|
||||
Flags:
|
||||
--workspace <id|ref> Workspace context (default: $CMUX_WORKSPACE_ID)
|
||||
|
||||
Example:
|
||||
cmux list-panes
|
||||
cmux list-panes --workspace workspace:2
|
||||
"""
|
||||
case "list-pane-surfaces":
|
||||
return """
|
||||
Usage: cmux list-pane-surfaces [--workspace <id|ref>] [--pane <id|ref>]
|
||||
|
||||
List surfaces in a pane.
|
||||
|
||||
Flags:
|
||||
--workspace <id|ref> Workspace context (default: $CMUX_WORKSPACE_ID)
|
||||
--pane <id|ref> Restrict to a specific pane (default: focused pane)
|
||||
|
||||
Example:
|
||||
cmux list-pane-surfaces
|
||||
cmux list-pane-surfaces --workspace workspace:2 --pane pane:1
|
||||
"""
|
||||
case "tree":
|
||||
return """
|
||||
Usage: cmux tree [flags]
|
||||
|
|
@ -3612,16 +3716,17 @@ struct CMUXCLI {
|
|||
"""
|
||||
case "focus-pane":
|
||||
return """
|
||||
Usage: cmux focus-pane --pane <id|ref> [flags]
|
||||
Usage: cmux focus-pane [--pane <id|ref> | <id|ref>] [flags]
|
||||
|
||||
Focus the specified pane.
|
||||
|
||||
Flags:
|
||||
--pane <id|ref> Pane to focus (required)
|
||||
--pane <id|ref> Pane to focus (required unless passed positionally)
|
||||
--workspace <id|ref> Workspace context (default: $CMUX_WORKSPACE_ID)
|
||||
|
||||
Example:
|
||||
cmux focus-pane --pane pane:2
|
||||
cmux focus-pane pane:1
|
||||
cmux focus-pane --pane pane:1 --workspace workspace:2
|
||||
"""
|
||||
case "new-pane":
|
||||
|
|
@ -3685,26 +3790,87 @@ struct CMUXCLI {
|
|||
cmux drag-surface-to-split --surface surface:1 right
|
||||
cmux drag-surface-to-split --panel surface:2 down
|
||||
"""
|
||||
case "refresh-surfaces":
|
||||
return """
|
||||
Usage: cmux refresh-surfaces
|
||||
|
||||
Refresh surface snapshots for the focused workspace.
|
||||
"""
|
||||
case "surface-health":
|
||||
return """
|
||||
Usage: cmux surface-health [--workspace <id|ref>]
|
||||
|
||||
List health details for surfaces in a workspace.
|
||||
|
||||
Flags:
|
||||
--workspace <id|ref> Workspace context (default: $CMUX_WORKSPACE_ID)
|
||||
|
||||
Example:
|
||||
cmux surface-health
|
||||
cmux surface-health --workspace workspace:2
|
||||
"""
|
||||
case "trigger-flash":
|
||||
return """
|
||||
Usage: cmux trigger-flash [--workspace <id|ref>] [--surface <id|ref>] [--panel <id|ref>]
|
||||
|
||||
Trigger the unread flash indicator for a surface.
|
||||
|
||||
Flags:
|
||||
--workspace <id|ref> Workspace context (default: $CMUX_WORKSPACE_ID)
|
||||
--surface <id|ref> Target surface (default: $CMUX_SURFACE_ID)
|
||||
--panel <id|ref> Alias for --surface
|
||||
|
||||
Example:
|
||||
cmux trigger-flash
|
||||
cmux trigger-flash --workspace workspace:2 --surface surface:3
|
||||
"""
|
||||
case "list-panels":
|
||||
return """
|
||||
Usage: cmux list-panels [--workspace <id|ref>]
|
||||
|
||||
List surfaces (panels) in a workspace.
|
||||
|
||||
Flags:
|
||||
--workspace <id|ref> Workspace context (default: $CMUX_WORKSPACE_ID)
|
||||
|
||||
Example:
|
||||
cmux list-panels
|
||||
cmux list-panels --workspace workspace:2
|
||||
"""
|
||||
case "focus-panel":
|
||||
return """
|
||||
Usage: cmux focus-panel --panel <id|ref> [--workspace <id|ref>]
|
||||
|
||||
Focus a specific panel (surface).
|
||||
|
||||
Flags:
|
||||
--panel <id|ref> Panel/surface to focus (required)
|
||||
--workspace <id|ref> Workspace context (default: $CMUX_WORKSPACE_ID)
|
||||
|
||||
Example:
|
||||
cmux focus-panel --panel surface:2
|
||||
cmux focus-panel --panel surface:5 --workspace workspace:2
|
||||
"""
|
||||
case "close-workspace":
|
||||
return """
|
||||
Usage: cmux close-workspace --workspace <id|ref>
|
||||
Usage: cmux close-workspace --workspace <id|ref|index>
|
||||
|
||||
Close the specified workspace.
|
||||
|
||||
Flags:
|
||||
--workspace <id|ref> Workspace to close (required)
|
||||
--workspace <id|ref|index> Workspace to close (required)
|
||||
|
||||
Example:
|
||||
cmux close-workspace --workspace workspace:2
|
||||
"""
|
||||
case "select-workspace":
|
||||
return """
|
||||
Usage: cmux select-workspace --workspace <id|ref>
|
||||
Usage: cmux select-workspace --workspace <id|ref|index>
|
||||
|
||||
Select (switch to) the specified workspace.
|
||||
|
||||
Flags:
|
||||
--workspace <id|ref> Workspace to select (required)
|
||||
--workspace <id|ref|index> Workspace to select (required)
|
||||
|
||||
Example:
|
||||
cmux select-workspace --workspace workspace:2
|
||||
|
|
@ -3712,51 +3878,210 @@ struct CMUXCLI {
|
|||
"""
|
||||
case "rename-workspace", "rename-window":
|
||||
return """
|
||||
Usage: cmux rename-workspace [--workspace <id|ref>] [--] <title>
|
||||
Usage: cmux rename-workspace [--workspace <id|ref|index>] [--] <title>
|
||||
|
||||
Rename a workspace. Defaults to the current workspace.
|
||||
tmux-compatible alias: rename-window
|
||||
|
||||
Flags:
|
||||
--workspace <id|ref> Workspace to rename (default: current workspace)
|
||||
--workspace <id|ref|index> Workspace to rename (default: current/$CMUX_WORKSPACE_ID)
|
||||
|
||||
Example:
|
||||
cmux rename-workspace "backend logs"
|
||||
cmux rename-window --workspace workspace:2 "agent run"
|
||||
"""
|
||||
case "current-workspace":
|
||||
return """
|
||||
Usage: cmux current-workspace
|
||||
|
||||
Print the currently selected workspace ID.
|
||||
"""
|
||||
case "capture-pane":
|
||||
return """
|
||||
Usage: cmux capture-pane [--workspace <id|ref>] [--surface <id|ref>] [--scrollback] [--lines <n>]
|
||||
|
||||
tmux-compatible alias for reading terminal text from a pane.
|
||||
|
||||
Flags:
|
||||
--workspace <id|ref> Workspace context (default: $CMUX_WORKSPACE_ID)
|
||||
--surface <id|ref> Surface context (default: $CMUX_SURFACE_ID)
|
||||
--scrollback Include scrollback
|
||||
--lines <n> Return only the last N lines (implies --scrollback)
|
||||
|
||||
Example:
|
||||
cmux capture-pane --workspace workspace:2 --surface surface:1 --scrollback --lines 200
|
||||
"""
|
||||
case "resize-pane":
|
||||
return """
|
||||
Usage: cmux resize-pane --pane <id|ref> [--workspace <id|ref>] (-L|-R|-U|-D) [--amount <n>]
|
||||
Usage: cmux resize-pane [--pane <id|ref>] [--workspace <id|ref>] [-L|-R|-U|-D] [--amount <n>]
|
||||
|
||||
tmux-compatible pane resize command.
|
||||
Note: currently returns not_supported until programmable divider resize is implemented.
|
||||
|
||||
Flags:
|
||||
--pane <id|ref> Pane to resize (default: focused pane)
|
||||
--workspace <id|ref> Workspace context (default: $CMUX_WORKSPACE_ID)
|
||||
-L|-R|-U|-D Direction (default: -R)
|
||||
--amount <n> Resize amount (default: 1)
|
||||
"""
|
||||
case "pipe-pane":
|
||||
return """
|
||||
Usage: cmux pipe-pane --command <shell-command> [--workspace <id|ref>] [--surface <id|ref>]
|
||||
Usage: cmux pipe-pane [--workspace <id|ref>] [--surface <id|ref>] [--command <shell-command> | <shell-command>]
|
||||
|
||||
Capture pane text and pipe it to a shell command via stdin.
|
||||
|
||||
Flags:
|
||||
--workspace <id|ref> Workspace context (default: $CMUX_WORKSPACE_ID)
|
||||
--surface <id|ref> Surface context (default: focused surface)
|
||||
--command <command> Shell command to run (or pass as trailing text)
|
||||
"""
|
||||
case "wait-for":
|
||||
return """
|
||||
Usage: cmux wait-for [-S|--signal] <name> [--timeout <seconds>]
|
||||
|
||||
Wait for or signal a named synchronization token.
|
||||
"""
|
||||
case "swap-pane", "break-pane", "join-pane", "next-window", "previous-window", "last-window", "last-pane", "find-window", "clear-history", "set-hook", "popup", "bind-key", "unbind-key", "copy-mode", "set-buffer", "paste-buffer", "list-buffers", "respawn-pane", "display-message":
|
||||
return """
|
||||
Usage: cmux \(command) --help
|
||||
|
||||
tmux compatibility command. See `cmux --help` for exact syntax.
|
||||
Flags:
|
||||
-S, --signal Signal the token instead of waiting
|
||||
--timeout <seconds> Wait timeout (default: 30)
|
||||
"""
|
||||
case "swap-pane":
|
||||
return """
|
||||
Usage: cmux swap-pane --pane <id|ref> --target-pane <id|ref> [--workspace <id|ref>]
|
||||
|
||||
Swap two panes.
|
||||
|
||||
Flags:
|
||||
--pane <id|ref> Source pane (required)
|
||||
--target-pane <id|ref> Target pane (required)
|
||||
--workspace <id|ref> Workspace context (default: $CMUX_WORKSPACE_ID)
|
||||
"""
|
||||
case "break-pane":
|
||||
return """
|
||||
Usage: cmux break-pane [--workspace <id|ref>] [--pane <id|ref>] [--surface <id|ref>] [--no-focus]
|
||||
|
||||
Move a pane/surface out into its own pane context.
|
||||
|
||||
Flags:
|
||||
--workspace <id|ref> Workspace context (default: $CMUX_WORKSPACE_ID)
|
||||
--pane <id|ref> Source pane
|
||||
--surface <id|ref> Source surface
|
||||
--no-focus Do not focus the result
|
||||
"""
|
||||
case "join-pane":
|
||||
return """
|
||||
Usage: cmux join-pane --target-pane <id|ref> [--workspace <id|ref>] [--pane <id|ref>] [--surface <id|ref>] [--no-focus]
|
||||
|
||||
Join a pane/surface into another pane.
|
||||
|
||||
Flags:
|
||||
--target-pane <id|ref> Target pane (required)
|
||||
--workspace <id|ref> Workspace context (default: $CMUX_WORKSPACE_ID)
|
||||
--pane <id|ref> Source pane
|
||||
--surface <id|ref> Source surface
|
||||
--no-focus Do not focus the result
|
||||
"""
|
||||
case "next-window", "previous-window", "last-window":
|
||||
return """
|
||||
Usage: cmux \(command)
|
||||
|
||||
Switch workspace selection (next/previous/last) in the current window.
|
||||
"""
|
||||
case "last-pane":
|
||||
return """
|
||||
Usage: cmux last-pane [--workspace <id|ref>]
|
||||
|
||||
Focus the previously focused pane in a workspace.
|
||||
|
||||
Flags:
|
||||
--workspace <id|ref> Workspace context (default: $CMUX_WORKSPACE_ID)
|
||||
"""
|
||||
case "find-window":
|
||||
return """
|
||||
Usage: cmux find-window [--content] [--select] [query]
|
||||
|
||||
Find workspaces by title (and optionally terminal content).
|
||||
|
||||
Flags:
|
||||
--content Search terminal content in addition to workspace titles
|
||||
--select Select the first match
|
||||
"""
|
||||
case "clear-history":
|
||||
return """
|
||||
Usage: cmux clear-history [--workspace <id|ref>] [--surface <id|ref>]
|
||||
|
||||
Clear terminal scrollback history.
|
||||
|
||||
Flags:
|
||||
--workspace <id|ref> Workspace context (default: $CMUX_WORKSPACE_ID)
|
||||
--surface <id|ref> Surface context (default: focused surface)
|
||||
"""
|
||||
case "set-hook":
|
||||
return """
|
||||
Usage: cmux set-hook [--list] [--unset <event>] | <event> <command>
|
||||
|
||||
Manage tmux-compat hook definitions.
|
||||
|
||||
Flags:
|
||||
--list List configured hooks
|
||||
--unset <event> Remove a hook by event name
|
||||
"""
|
||||
case "popup":
|
||||
return """
|
||||
Usage: cmux popup
|
||||
|
||||
tmux compatibility placeholder. This command is currently not supported.
|
||||
"""
|
||||
case "bind-key", "unbind-key", "copy-mode":
|
||||
return """
|
||||
Usage: cmux \(command)
|
||||
|
||||
tmux compatibility placeholder. This command is currently not supported.
|
||||
"""
|
||||
case "set-buffer":
|
||||
return """
|
||||
Usage: cmux set-buffer [--name <name>] [--] <text>
|
||||
|
||||
Save text into a named tmux-compat buffer.
|
||||
|
||||
Flags:
|
||||
--name <name> Buffer name (default: default)
|
||||
"""
|
||||
case "paste-buffer":
|
||||
return """
|
||||
Usage: cmux paste-buffer [--name <name>] [--workspace <id|ref>] [--surface <id|ref>]
|
||||
|
||||
Paste a named tmux-compat buffer into a surface.
|
||||
|
||||
Flags:
|
||||
--name <name> Buffer name (default: default)
|
||||
--workspace <id|ref> Workspace context (default: $CMUX_WORKSPACE_ID)
|
||||
--surface <id|ref> Surface context (default: focused surface)
|
||||
"""
|
||||
case "list-buffers":
|
||||
return """
|
||||
Usage: cmux list-buffers
|
||||
|
||||
List tmux-compat buffers.
|
||||
"""
|
||||
case "respawn-pane":
|
||||
return """
|
||||
Usage: cmux respawn-pane [--workspace <id|ref>] [--surface <id|ref>] [--command <cmd> | <cmd>]
|
||||
|
||||
Send a command (or default shell restart command) to a surface.
|
||||
|
||||
Flags:
|
||||
--workspace <id|ref> Workspace context (default: $CMUX_WORKSPACE_ID)
|
||||
--surface <id|ref> Surface context (default: focused surface)
|
||||
--command <cmd> Command text (or pass trailing command text)
|
||||
"""
|
||||
case "display-message":
|
||||
return """
|
||||
Usage: cmux display-message [-p|--print] <text>
|
||||
|
||||
Print text (or show it via notification bridge in parity mode).
|
||||
|
||||
Flags:
|
||||
-p, --print Print to stdout only
|
||||
"""
|
||||
case "read-screen":
|
||||
return """
|
||||
|
|
@ -3846,6 +4171,18 @@ struct CMUXCLI {
|
|||
cmux notify --title "Build done" --body "All tests passed"
|
||||
cmux notify --title "Error" --subtitle "test.swift" --body "Line 42: syntax error"
|
||||
"""
|
||||
case "list-notifications":
|
||||
return """
|
||||
Usage: cmux list-notifications
|
||||
|
||||
List queued notifications.
|
||||
"""
|
||||
case "clear-notifications":
|
||||
return """
|
||||
Usage: cmux clear-notifications
|
||||
|
||||
Clear all queued notifications.
|
||||
"""
|
||||
case "set-status":
|
||||
return """
|
||||
Usage: cmux set-status <key> <value> [flags]
|
||||
|
|
@ -3970,16 +4307,35 @@ struct CMUXCLI {
|
|||
cmux sidebar-state
|
||||
cmux sidebar-state --workspace workspace:2
|
||||
"""
|
||||
case "set-app-focus":
|
||||
return """
|
||||
Usage: cmux set-app-focus <active|inactive|clear>
|
||||
|
||||
Override app focus state for notification routing tests.
|
||||
|
||||
Example:
|
||||
cmux set-app-focus inactive
|
||||
cmux set-app-focus clear
|
||||
"""
|
||||
case "simulate-app-active":
|
||||
return """
|
||||
Usage: cmux simulate-app-active
|
||||
|
||||
Trigger the app-active handler used by notification focus tests.
|
||||
"""
|
||||
case "claude-hook":
|
||||
return """
|
||||
Usage: cmux claude-hook <session-start|stop|notification> [flags]
|
||||
Usage: cmux claude-hook <session-start|active|stop|idle|notification|notify> [flags]
|
||||
|
||||
Hook for Claude Code integration. Reads JSON from stdin.
|
||||
|
||||
Subcommands:
|
||||
session-start Signal that a Claude session has started
|
||||
active Alias for session-start
|
||||
stop Signal that a Claude session has stopped
|
||||
idle Alias for stop
|
||||
notification Forward a Claude notification
|
||||
notify Alias for notification
|
||||
|
||||
Flags:
|
||||
--workspace <id|ref> Target workspace (default: $CMUX_WORKSPACE_ID)
|
||||
|
|
@ -3994,29 +4350,81 @@ struct CMUXCLI {
|
|||
Usage: cmux browser [--surface <id|ref|index> | <surface>] <subcommand> [args]
|
||||
|
||||
Browser automation commands. Most subcommands require a surface handle.
|
||||
A surface can be passed as `--surface <handle>` or as the first positional token.
|
||||
`open`/`open-split`/`new`/`identify` can run without an explicit surface.
|
||||
|
||||
Subcommands:
|
||||
open [url] Create browser split (or navigate if surface given)
|
||||
open-split [url] Create browser in a new split
|
||||
goto|navigate <url> Navigate to URL [--snapshot-after]
|
||||
back|forward|reload History navigation [--snapshot-after]
|
||||
url|get-url Get current URL
|
||||
snapshot Get DOM snapshot [--interactive|-i] [--cursor] [--compact] [--max-depth <n>] [--selector <css>]
|
||||
eval <script> Evaluate JavaScript
|
||||
wait Wait for condition [--selector] [--text] [--url-contains] [--timeout-ms]
|
||||
click|dblclick|hover <sel> Mouse actions [--snapshot-after]
|
||||
type <selector> <text> Type text [--snapshot-after]
|
||||
fill <selector> [text] Fill input [--snapshot-after]
|
||||
press|keydown|keyup <key> Keyboard actions [--snapshot-after]
|
||||
get <property> [selector] Get page properties (url|title|text|html|value|attr|count|box|styles)
|
||||
find <strategy> <query> Find elements (role|text|label|placeholder|testid|first|last|nth)
|
||||
identify Identify browser surface
|
||||
open|open-split|new [url] [--workspace <id|ref|index>] [--window <id|ref|index>]
|
||||
open/open-split/new default to $CMUX_WORKSPACE_ID when --workspace is omitted and --window is not set
|
||||
goto|navigate <url> [--snapshot-after]
|
||||
back|forward|reload [--snapshot-after]
|
||||
url|get-url
|
||||
focus-webview | is-webview-focused
|
||||
snapshot [--interactive|-i] [--cursor] [--compact] [--max-depth <n>] [--selector <css>]
|
||||
eval [--script <js> | <js>]
|
||||
wait [--selector <css>] [--text <text>] [--url-contains <text>|--url <text>] [--load-state <interactive|complete>] [--function <js>] [--timeout-ms <ms>|--timeout <seconds>]
|
||||
click|dblclick|hover|focus|check|uncheck|scroll-into-view [--selector <css> | <css>] [--snapshot-after]
|
||||
type|fill [--selector <css> | <css>] [--text <text> | <text>] [--snapshot-after]
|
||||
press|key|keydown|keyup [--key <key> | <key>] [--snapshot-after]
|
||||
select [--selector <css> | <css>] [--value <value> | <value>] [--snapshot-after]
|
||||
scroll [--selector <css>] [--dx <n>] [--dy <n>] [--snapshot-after]
|
||||
screenshot [--out <path>]
|
||||
get <url|title|text|html|value|attr|count|box|styles> [...]
|
||||
text|html|value|count|box|styles|attr: [--selector <css> | <css>]
|
||||
attr: [--attr <name> | <name>]
|
||||
styles: [--property <name>]
|
||||
is <visible|enabled|checked> [--selector <css> | <css>]
|
||||
find <role|text|label|placeholder|alt|title|testid|first|last|nth> [...]
|
||||
role: [--name <text>] [--exact] <role>
|
||||
text|label|placeholder|alt|title|testid: [--exact] <text>
|
||||
first|last: [--selector <css> | <css>]
|
||||
nth: [--index <n> | <n>] [--selector <css> | <css>]
|
||||
frame <main|selector> [--selector <css>]
|
||||
dialog <accept|dismiss> [text]
|
||||
download [wait] [--path <path>] [--timeout-ms <ms>|--timeout <seconds>]
|
||||
cookies <get|set|clear> [--name <name>] [--value <value>] [--url <url>] [--domain <domain>] [--path <path>] [--expires <unix>] [--secure] [--all]
|
||||
storage <local|session> <get|set|clear> [...]
|
||||
tab <new|list|switch|close|<index>> [...]
|
||||
console <list|clear>
|
||||
errors <list|clear>
|
||||
highlight [--selector <css> | <css>]
|
||||
state <save|load> <path>
|
||||
addinitscript|addscript [--script <js> | <js>]
|
||||
addstyle [--css <css> | <css>]
|
||||
viewport <width> <height>
|
||||
geolocation|geo <latitude> <longitude>
|
||||
offline <true|false>
|
||||
trace <start|stop> [path]
|
||||
network <route|unroute|requests> ...
|
||||
route <pattern> [--abort] [--body <text>]
|
||||
unroute <pattern>
|
||||
screencast <start|stop>
|
||||
input <mouse|keyboard|touch> [args...]
|
||||
input_mouse | input_keyboard | input_touch
|
||||
identify [--surface <id|ref|index>]
|
||||
|
||||
Example:
|
||||
cmux browser open https://example.com
|
||||
cmux browser surface:1 navigate https://google.com
|
||||
cmux browser --surface surface:1 snapshot --interactive
|
||||
"""
|
||||
// Legacy browser aliases — point users to `cmux browser --help`
|
||||
case "open-browser":
|
||||
return "Legacy alias for 'cmux browser open'. Run 'cmux browser --help' for details."
|
||||
case "navigate":
|
||||
return "Legacy alias for 'cmux browser navigate'. Run 'cmux browser --help' for details."
|
||||
case "browser-back":
|
||||
return "Legacy alias for 'cmux browser back'. Run 'cmux browser --help' for details."
|
||||
case "browser-forward":
|
||||
return "Legacy alias for 'cmux browser forward'. Run 'cmux browser --help' for details."
|
||||
case "browser-reload":
|
||||
return "Legacy alias for 'cmux browser reload'. Run 'cmux browser --help' for details."
|
||||
case "get-url":
|
||||
return "Legacy alias for 'cmux browser get-url'. Run 'cmux browser --help' for details."
|
||||
case "focus-webview":
|
||||
return "Legacy alias for 'cmux browser focus-webview'. Run 'cmux browser --help' for details."
|
||||
case "is-webview-focused":
|
||||
return "Legacy alias for 'cmux browser is-webview-focused'. Run 'cmux browser --help' for details."
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
147
tests/test_cli_subcommand_help_regressions.py
Normal file
147
tests/test_cli_subcommand_help_regressions.py
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
#!/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())
|
||||
Loading…
Add table
Add a link
Reference in a new issue