Repository navigation
CLI: shorter unknown-command errors with suggestions, copy polish - #7329
Conversation
Unknown commands no longer dump the full usage; both dispatch paths now throw one short error with a 'Did you mean' suggestion (edit distance over topLevelCommandNames) and exit 2. The pre-socket --help path previously exited 0 for unknown commands. Replaced the three 'Unable to' messages with Failed to/Couldn't per failure class, standardized the help hint to "Run 'cmux --help' for the full command list.", and tightened the usage() header prose; the Commands and Environment blocks are byte-identical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR updates CLI error wording and help text, adds shared unknown-command suggestion handling, and changes CI Xcode selection to fall back to beta installations. ChangesCLI error messages and help text
CI Xcode selection
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error)
✅ Passed checks (24 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR replaces the noisy unknown-command error paths (full usage dump + exit 0) with a single short error that exits 2 and optionally suggests the nearest valid command via Levenshtein distance (≤2,
Confidence Score: 5/5CLI-only messaging change; no socket, auth, or data-path behavior is touched. Both dispatch paths consistently throw with exit 2 now. The Levenshtein implementation is a standard Wagner-Fischer two-row algorithm with correct guards. The topLevelCommandNames set is a verbatim move — including remote-daemon-status — with the access modifier relaxed from private to internal, which is acceptable for a static constant shared across extension files. The shell script change uses proper beta-demotion logic with no sleeps or hacks. No files require special attention. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["cmux <command> args"] --> B{Pre-socket dispatch}
B -->|known command| C[Run command handler]
B -->|dispatchSubcommandHelp succeeds| D[Print subcommand help → exit 0]
B -->|no handler found| E[throw unknownCommandError]
C -->|socket required| F{Socket dispatch switch}
F -->|matched case| G[Run socket command handler]
F -->|default| E
E --> H[suggestedCommandName]
H --> I{"Levenshtein distance ≤ 2\nand not __ prefix\nand not exact match?"}
I -->|yes| J["'Unknown command X. Did you mean Y?\nRun cmux --help for the full command list.'"]
I -->|no| K["'Unknown command X.\nRun cmux --help for the full command list.'"]
J --> L[exit 2]
K --> L
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A["cmux <command> args"] --> B{Pre-socket dispatch}
B -->|known command| C[Run command handler]
B -->|dispatchSubcommandHelp succeeds| D[Print subcommand help → exit 0]
B -->|no handler found| E[throw unknownCommandError]
C -->|socket required| F{Socket dispatch switch}
F -->|matched case| G[Run socket command handler]
F -->|default| E
E --> H[suggestedCommandName]
H --> I{"Levenshtein distance ≤ 2\nand not __ prefix\nand not exact match?"}
I -->|yes| J["'Unknown command X. Did you mean Y?\nRun cmux --help for the full command list.'"]
I -->|no| K["'Unknown command X.\nRun cmux --help for the full command list.'"]
J --> L[exit 2]
K --> L
Reviews (5): Last reviewed commit: "select-ci-xcode: never pick a beta Xcode..." | Re-trigger Greptile |
| private func unknownCommandError(_ command: String) -> CLIError { | ||
| var message = "Unknown command '\(command)'." | ||
| if let suggestion = suggestedCommandName(for: command) { | ||
| message += " Did you mean '\(suggestion)'?" | ||
| } | ||
| message += " Run 'cmux --help' for the full command list." | ||
| return CLIError(message: message, exitCode: 2) |
There was a problem hiding this comment.
New user-facing strings missing localization
unknownCommandError introduces two brand-new user-facing phrases — "Did you mean '\(suggestion)'?" and "Run 'cmux --help' for the full command list." — that are built with raw string interpolation rather than String(localized:defaultValue:). The repo's full-internationalization rule requires every new user-facing string in production Swift to go through a localized string API with matching catalog entries for every supported locale. These strings are not existing untranslated copy being worsened; they are net-new text appearing for the first time.
Rule Used: Flag production user-facing text that is not fully... (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
workflow-guard-tests failed because the new helpers pushed CLI/cmux.swift 47 lines over its length budget. Move unknownCommandError, suggestedCommandName, editDistance, and topLevelCommandNames into a new extension file (wired into the pbxproj) and ratchet the budget entry down to the new 34499-line count. No behavior change; verified on the rebuilt clicpy tag. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@CLI/CMUXCLI`+CommandSuggestions.swift:
- Around line 53-210: The command suggestion list is duplicated and can drift
from the real command dispatch in cmux.swift. Update
CMUXCLI+CommandSuggestions.topLevelCommandNames to be derived from a single
shared source used by the dispatch switch, or add a parity test that checks the
switch cases against topLevelCommandNames so additions/removals stay in sync.
Refer to topLevelCommandNames and the command dispatch in cmux.swift when making
the fix.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: bad3f8d0-90c0-4833-8f04-f1a3ac9b79ec
⛔ Files ignored due to path filters (1)
.github/swift-file-length-budget.tsvis excluded by!**/*.tsv
📒 Files selected for processing (3)
CLI/CMUXCLI+CommandSuggestions.swiftCLI/cmux.swiftcmux.xcodeproj/project.pbxproj
💤 Files with no reviewable changes (1)
- CLI/cmux.swift
| static let topLevelCommandNames: Set<String> = [ | ||
| "__codex-teams-watch", | ||
| "__tmux-compat", | ||
| "agent-hibernation", | ||
| "auth", | ||
| "bind-key", | ||
| "break-pane", | ||
| "browser", | ||
| "browser-back", | ||
| "browser-forward", | ||
| "browser-reload", | ||
| "browser-status", | ||
| "capabilities", | ||
| "capture-pane", | ||
| "claude-hook", | ||
| "claude-teams", | ||
| "clear-history", | ||
| "clear-log", | ||
| "clear-notifications", | ||
| "clear-progress", | ||
| "clear-status", | ||
| "close-surface", | ||
| "close-window", | ||
| "close-workspace", | ||
| "cloud", | ||
| "codex", | ||
| "codex-hook", | ||
| "codex-teams", | ||
| "config", | ||
| "copy-mode", | ||
| "current-window", | ||
| "current-workspace", | ||
| "debug-terminals", | ||
| "detach-tab", | ||
| "diff", | ||
| "disable-browser", | ||
| "dismiss-notification", | ||
| "display-message", | ||
| "docs", | ||
| "drag-surface-to-split", | ||
| "enable-browser", | ||
| "events", | ||
| "feedback", | ||
| "feed", | ||
| "feed-hook", | ||
| "find-window", | ||
| "focus-pane", | ||
| "focus-panel", | ||
| "focus-webview", | ||
| "focus-window", | ||
| "get-url", | ||
| "help", | ||
| "hooks", | ||
| "identify", | ||
| "is-webview-focused", | ||
| "join-pane", | ||
| "jump-to-unread", | ||
| "last-pane", | ||
| "last-window", | ||
| "list-buffers", | ||
| "list-log", | ||
| "list-notifications", | ||
| "list-pane-surfaces", | ||
| "list-panels", | ||
| "list-panes", | ||
| "list-status", | ||
| "list-windows", | ||
| "list-workspaces", | ||
| "log", | ||
| "login", | ||
| "logout", | ||
| "markdown", | ||
| "mark-notification-read", | ||
| "memory", | ||
| "mobile", | ||
| "move-surface", | ||
| "move-tab-to-new-workspace", | ||
| "move-workspace-to-window", | ||
| "navigate", | ||
| "new-pane", | ||
| "new-split", | ||
| "new-surface", | ||
| "new-window", | ||
| "new-workspace", | ||
| "next-window", | ||
| "notify", | ||
| "omc", | ||
| "omo", | ||
| "omx", | ||
| "open", | ||
| "open-browser", | ||
| "open-notification", | ||
| "paste-buffer", | ||
| "ping", | ||
| "pipe-pane", | ||
| "popup", | ||
| "previous-window", | ||
| "read-screen", | ||
| "refresh-surfaces", | ||
| "reload-config", | ||
| "remote-daemon-status", | ||
| "rename-tab", | ||
| "rename-window", | ||
| "rename-workspace", | ||
| "reorder-surface", | ||
| "reorder-workspace", | ||
| "reorder-workspaces", | ||
| "resize-pane", | ||
| "respawn-pane", | ||
| "restore-session", | ||
| "right-sidebar", | ||
| "rpc", | ||
| "select-workspace", | ||
| "send", | ||
| "send-key", | ||
| "send-key-panel", | ||
| "send-panel", | ||
| "set-app-focus", | ||
| "set-buffer", | ||
| "set-hook", | ||
| "set-progress", | ||
| "set-status", | ||
| "settings", | ||
| "setup-hooks", | ||
| "shortcuts", | ||
| "simulate-app-active", | ||
| "sidebar", | ||
| "sidebar-state", | ||
| "split-off", | ||
| "ssh", | ||
| "ssh-pty-attach", | ||
| "ssh-session-attach", | ||
| "ssh-session-cleanup", | ||
| "ssh-session-end", | ||
| "ssh-session-list", | ||
| "ssh-tmux", | ||
| "surface", | ||
| "surface-health", | ||
| "surface-resume", | ||
| "swap-pane", | ||
| "tab-action", | ||
| "themes", | ||
| "top", | ||
| "tree", | ||
| "trigger-flash", | ||
| "unbind-key", | ||
| "uninstall-hooks", | ||
| "version", | ||
| "vm", | ||
| "vm-pty-attach", | ||
| "vm-pty-connect", | ||
| "vm-ssh-attach", | ||
| "wait-for", | ||
| "welcome", | ||
| "workspace", | ||
| "workspace-action", | ||
| "workspace-group", | ||
| ] |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Duplicated source of truth for the command list.
topLevelCommandNames must be kept manually in sync with the actual command dispatch switch statements in cmux.swift. If a command is added/removed there without updating this list, suggestions silently go stale (missing suggestions for new commands, or suggesting removed ones) — a low-severity but easy-to-miss drift.
Do you want me to add a regression test that enumerates the real dispatch cases (via reflection/parsing or a shared constant) and asserts parity with topLevelCommandNames, so drift is caught in CI?
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@CLI/CMUXCLI`+CommandSuggestions.swift around lines 53 - 210, The command
suggestion list is duplicated and can drift from the real command dispatch in
cmux.swift. Update CMUXCLI+CommandSuggestions.topLevelCommandNames to be derived
from a single shared source used by the dispatch switch, or add a parity test
that checks the switch cases against topLevelCommandNames so additions/removals
stay in sync. Refer to topLevelCommandNames and the command dispatch in
cmux.swift when making the fix.
Blacksmith macOS-15 lane is backlogged (jobs queued 2h+); repo runner vars flipped back to warp for this run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MACOS_RUNNER_DISPLAY was still pointing at the backlogged Blacksmith macOS-15 lane. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
On the WarpBuild macos-26 image, Xcode_27.0_Beta.app outranks every stable 26.x by SDK version, so the release gate built against the 27.0 beta SDK and hit a Swift type-checker timeout in CMUXMobileCore. Skip beta-named Xcodes unless no stable Xcode exists on the runner. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ng, themes) Delta: #7318/#7319 mouse cursor-shape + right/middle drag forwarding, #7320 ghostty upstream, #6664 iOS themes, #7196 iOS scroll, #7257/#7307 web, #7329 CLI suggestions, #7222 codex resume update-suppression. Conflicts resolved keeping HEAD structure: - GhosttyTerminalView.swift: took main's viewDidEndLiveResize + resetCursorRects (cursor-shape #7318; ghosttyMouseShape/ghosttyMouseCursor already in HEAD). - Mobile/MobileHostService.swift: union (kept HEAD sharedRequestActivity + main publicStatusPayload theme payload #6664). - project.pbxproj: union-dedup, normalized; test-wiring green. - swift-file-length-budget.tsv regenerated. #7222 (codex update-check suppression) partially landed: the CMUXAgentLaunch package part (AgentResumeArgv.codexUpdateCheckSuppressionOverride) auto-merged and is present; the app-target files (+CodexUpdateCheck.swift + test) extend SurfaceResumeCommandCanonicalizer, a type HEAD dissolved into the CmuxWorkspaces package, so they can't compile and the suppression was never wired (call site was in the HEAD-deleted +PortableAgentExecutable). Removed those two files; re-wire recorded in merge-deferred-gaps. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Applies the useful parts of Vercel's CLI copywriting guidance (vercel/vercel#16774) to the cmux CLI's user-facing copy.
A typo'd command used to dump the entire ~200-line usage before the error, and
cmux <unknown> --helpprinted the error to stdout and exited 0. Both paths now throw one short error with a nearest-command suggestion (Levenshtein overtopLevelCommandNames, internal__names excluded, exact matches never self-suggest) and exit 2:The three
Unable tomessages are reclassified per failure type (Failed tofor system failures,Couldn'tfor user-state, with a recovery hint added to the surface-ID one), the help recovery hint is standardized to one exact sentence, and theusage()header prose is tightened (Handle Inputs:heading renamed toTargets:). TheCommands:andEnvironment:blocks of--helpare byte-identical to main, verified by diffing the built CLI's output against the release CLI.No test added: the CLI executable target has no wired unit-test path, and a source-shape test asserting help strings is disallowed by the repo test-quality policy. Verified behaviorally on the tagged build (
clicpy): typo suggestion + exit 2 on both the pre-socket and socket dispatch paths, no self-suggestion for valid commands lacking a help entry,ping/identify/--helpunaffected.🤖 Generated with Claude Code
Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Note
Low Risk
Mostly CLI UX and CI toolchain selection; behavior change is limited to unknown-command exit paths and error text, not socket or app logic.
Overview
Improves unknown-command handling: both pre-socket and socket dispatch paths now
throw unknownCommandError(exit 2) instead of printing full usage or exiting 0 oncmux <typo> --help. Errors are one line with optional “Did you mean …?” from Levenshtein distance (≤2) overtopLevelCommandNames, excluding__*commands and exact matches.topLevelCommandNamesand suggestion logic move into newCMUXCLI+CommandSuggestions.swift;cmux.swiftbudget is lowered accordingly.Several user-facing strings are tightened:
usage()header (Targets, socket auth, agent help); surface ID, git diff base, and Application Support errors use Failed to / Couldn't wording with clearer recovery hints.scripts/select-ci-xcode.shprefers stable Xcode installs and only falls back to Beta when no stable Xcode is present.Reviewed by Cursor Bugbot for commit 181f1b7. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by cubic
Shorten and improve unknown-command handling: show a single, clear error with a “Did you mean” suggestion and exit with code 2. Also polish CLI copy, tighten the
--helpheader, move suggestion helpers intoCMUXCLI+CommandSuggestions.swift, and update CI to prefer stable Xcode over betas.New Features
__commands excluded; no self-suggestions).usage()header (“Targets” section) with minor wording tweaks; Commands/Environment blocks unchanged.Bug Fixes
--help; both dispatch paths now error with exit code 2.scripts/select-ci-xcode.shnow selects stable Xcode over beta; falls back to beta only if no stable version is present.Written for commit 181f1b7. Summary will update on new commits.
Summary by CodeRabbit
--help.