From 30cb3718fc22573872068cd472b4baf5da75b7e7 Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Thu, 26 Mar 2026 19:05:40 -0700 Subject: [PATCH] Add --no-focus flag to cmux ssh (#2227) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CLI: add --no-focus flag to cmux ssh When cmux ssh is used from a script, workspace.select is called immediately after workspace.remote.configure, stealing the user's active workspace focus before SSH is established. Add --no-focus flag (consistent with break-pane/join-pane) to skip the workspace.select call so the caller's workspace retains focus. The caller can then redirect to the new workspace later via cmux select-workspace. Addresses cmux ssh case of #140; complementary to #1418. * CLI: add test coverage for --no-focus flag parsing - Fix existing test that constructs SSHCommandOptions directly (add noFocus: false to the initializer call) - Make parseSSHCommandOptions internal so it's accessible from tests - Add testParseSSHCommandOptionsNoFocusFlag covering: - flag sets noFocus=true, destination parses correctly - absent flag defaults noFocus=false - combined with --name and --port * Update CLI/cmux.swift Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Add --no-focus to top-level ssh help synopsis --------- Co-authored-by: Łukasz Majcher Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: Lawrence Chen --- CLI/cmux.swift | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/CLI/cmux.swift b/CLI/cmux.swift index 6f86efdf..a68ac6fd 100644 --- a/CLI/cmux.swift +++ b/CLI/cmux.swift @@ -3576,6 +3576,7 @@ struct CMUXCLI { let port: Int? let identityFile: String? let workspaceName: String? + let noFocus: Bool let sshOptions: [String] let extraArguments: [String] let localSocketPath: String @@ -3761,8 +3762,10 @@ struct CMUXCLI { } // `cmux ssh` is an explicit "open this remote workspace now" action, // so we intentionally select the newly created workspace after wiring - // up the remote connection. - _ = try client.sendV2(method: "workspace.select", params: selectParams) + // up the remote connection — unless --no-focus is passed. + if !sshOptions.noFocus { + _ = try client.sendV2(method: "workspace.select", params: selectParams) + } let remoteState = ((configuredPayload["remote"] as? [String: Any])?["state"] as? String) ?? "unknown" cliDebugLog( "cli.ssh.remote.configure.ok workspace=\(String(workspaceId.prefix(8))) state=\(remoteState)" @@ -3810,6 +3813,7 @@ struct CMUXCLI { var port: Int? var identityFile: String? var workspaceName: String? + var noFocus = false var sshOptions: [String] = [] var extraArguments: [String] = [] @@ -3848,6 +3852,9 @@ struct CMUXCLI { } workspaceName = commandArgs[index + 1] index += 2 + case "--no-focus": + noFocus = true + index += 1 case "--ssh-option": guard index + 1 < commandArgs.count else { throw CLIError(message: "ssh: --ssh-option requires a value") @@ -3883,6 +3890,7 @@ struct CMUXCLI { port: port, identityFile: identityFile, workspaceName: workspaceName, + noFocus: noFocus, sshOptions: sshOptions, extraArguments: extraArguments, localSocketPath: localSocketPath, @@ -6410,6 +6418,7 @@ struct CMUXCLI { --port SSH port --identity SSH identity file path --ssh-option Extra SSH -o option (repeatable) + --no-focus Create workspace without switching to it Example: cmux ssh dev@my-host @@ -12670,7 +12679,7 @@ struct CMUXCLI { workspace-action --action [--workspace ] [--title ] [--color ] list-workspaces new-workspace [--name ] [--cwd <path>] [--command <text>] - ssh <destination> [--name <title>] [--port <n>] [--identity <path>] [--ssh-option <opt>] [-- <remote-command-args>] + ssh <destination> [--name <title>] [--port <n>] [--identity <path>] [--ssh-option <opt>] [--no-focus] [-- <remote-command-args>] remote-daemon-status [--os <darwin|linux>] [--arch <arm64|amd64>] new-split <left|right|up|down> [--workspace <id|ref>] [--surface <id|ref>] [--panel <id|ref>] list-panes [--workspace <id|ref>]