Skip to content

Add cmux window display to place a window on a named display - #5804

Merged
azooz2003-bit merged 3 commits into
mainfrom
feat-cli-window-display
Jun 10, 2026
Merged

azooz2003-bit merged 3 commits into
mainfrom
feat-cli-window-display

Conversation

@azooz2003-bit

@azooz2003-bit azooz2003-bit commented Jun 10, 2026 •

Copy link
Copy Markdown
Collaborator

What

Adds a cmux window display CLI command (and window.display / window.displays v2 socket methods) that move a running cmux instance's window(s) onto a chosen monitor by name or index.

  • cmux window displays — list connected displays (name, index, main flag).
  • cmux window display "<name>" — move the window(s) onto that display, preserving size and centering. Matches by case-insensitive exact name, then substring, then zero-based index. --list aliases window displays; --window <id> targets one window instead of all.

Why

To place dev builds on a specific monitor (e.g. an external 4K) without an external tiling window manager. A tiling WM continuously moves/resizes cmux windows and fights cmux's own window management. Here the app moves its own window once, on command, so there is no external manager and no fight.

How

The CLI sends a v2 socket command; the app resolves the NSScreen by name/index and calls setFrame to reposition the window onto that display's visible frame at its current size. The command is deliberately not a focus-intent method: it is kept out of focusIntentV2Methods and never calls activate / makeKeyAndOrderFront, so it repositions without stealing macOS focus (per the socket focus policy).

Testing

Dogfooded on a two-display Mac: lists both displays; moves by exact name, substring, and index; graceful not-found error listing available displays; exactly one window moved (no spawning); and a focus-steal check confirmed the frontmost app was unchanged across the move. No automated test yet (window placement needs real displays); the display-name resolution logic is a good unit-test follow-up.

🤖 Generated with Claude Code


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Adds cmux window commands to list displays and move the app’s window(s) to a chosen monitor by name or index without stealing macOS focus. Implements v2 socket methods to list and move displays and prevents pre-focus for window commands.

  • New Features

    • cmux window displays — list connected displays (name, index, main).
    • cmux window display "<name|index>" — move window(s) to a display; matches case-insensitive exact name, then substring, then zero-based index; preserves size and centers. Use --window <id> to target one window; --list aliases window displays. Backed by v2 methods window.displays and window.display; commands are not focus-intent and won’t activate or key the window.
  • Bug Fixes

    • CI: removed a redundant nil-coalesce in v2WindowDisplay and refreshed .github/swift-file-length-budget.tsv for touched files.

Written for commit 60e2359. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added window displays to list connected displays (name, index, main marker).
    • Added window display <name|index> to move one or all main windows to a specified display by name or index, preserving size and focus.
  • Bug Fixes
    • Prevented pre-focusing behavior for the window command so moves don’t steal focus.
  • Documentation
    • CLI contract updated with new window subcommands and a --list alias.

New v2 socket commands `window.display` / `window.displays` plus a `cmux
window` CLI namespace. `window display <name|index>` moves the instance's
window(s) onto a display matched by name (case-insensitive exact, then
substring) or zero-based index, preserving the window's size and centering
it. `window displays` lists connected displays.

The move is deliberately not a focus-intent command: it stays out of
`focusIntentV2Methods` and never calls activate/makeKeyAndOrderFront, so it
repositions without stealing macOS focus. This lets agent sessions place dev
builds on a chosen monitor (the app moves its own window) without an external
tiling window manager fighting cmux's window management.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 10, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jun 10, 2026 6:17pm
cmux-staging Building Building Preview, Comment Jun 10, 2026 6:17pm

@coderabbitai

coderabbitai Bot commented Jun 10, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a new window namespace: window displays lists connected displays; window display <name|index> moves one or all main instance windows to a matched display by name or index, preserving size and avoiding focus/activation.

Changes

Window Display Management

Layer / File(s) Summary
Display query and movement utilities
Sources/AppDelegate.swift
DisplayInfo struct and availableDisplays() enumerate connected screens with name, index, displayID, main-screen flag, and frame. Private screenMatching(_:) resolves NSScreen from exact name, substring, or numeric index. Public moveMainWindow(windowId:toDisplayMatching:) and moveAllMainWindows(toDisplayMatching:) reposition windows onto matched displays while preserving size; repositionPreservingSize(_:onto:) clamps and centers without focus activation.
RPC methods for display and window operations
Sources/TerminalController.swift
V2 RPC routing adds window.displays and window.display method names. v2WindowDisplays(params:) queries and returns available displays in structured JSON form. v2WindowDisplay(params:) validates the display parameter and moves a single window by ID or all main windows to the matched display, returning display name and moved window IDs. v2DisplayNotFound(_:) constructs not_found error with requested display and available names.
CLI command handlers for window management
CLI/cmux.swift
shouldFocusWindowBeforeDispatch returns false for window command to prevent focus theft. runWindowNamespace dispatcher validates and routes displays and display subcommands, throwing CLIError for unknown subcommands. runWindowDisplaysCommand calls window.displays RPC and formats output as JSON or human-readable display list. runWindowDisplayCommand supports --list alias, parses display name argument, optionally normalizes windowOverride to window_id, calls window.display RPC, and prints JSON or moved-window count.
CLI contract documentation
docs/cli-contract.md
Documents window displays (list connected displays) and window display <name|index> (move instance window(s) to display) commands, including --list alias.

Sequence Diagram(s)

sequenceDiagram
  participant User as CLI (cmux)
  participant Terminal as TerminalController
  participant App as AppDelegate

  User->>Terminal: request window.displays
  Terminal->>App: availableDisplays()
  App-->>Terminal: list of DisplayInfo
  Terminal-->>User: ok {displays: [...]}

  User->>Terminal: request window.display(display, window_id?)
  Terminal->>App: moveMainWindow / moveAllMainWindows
  App-->>Terminal: {display, window_ids}
  Terminal-->>User: ok {display, window_ids} / not_found
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hop between screens with a gentle nudge,
No focus stolen, no angry judge.
Name or index, I find the right place,
Preserve the size, keep a calm face.
Hooray — windows home, with graceful pace!


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (2 errors, 1 warning)

Check name Status Explanation Resolution
Cmux Full Internationalization ❌ Error PR adds user-facing Swift CLI text without String(localized:) for 17 supported locales and hardcoded error messages in TerminalController. Use String(localized:defaultValue:) for all user-facing strings in cmux.swift and TerminalController.swift, add complete translations to Resources/Localizable.xcstrings for all 17 locales.
Cmux Source Artifacts ❌ Error PR adds .claude/scheduled_tasks.lock (generated session/PID artifact) and local tool config dirs (.claude/, .cursor/, .agents/) violating source control artifacts rule. Remove .claude/scheduled_tasks.lock and add artifact patterns to .gitignore, or document as required project artifacts per .github/review-bot-rules/source-control-artifacts.md.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (18 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically summarizes the main feature: adding a CLI command to move windows to a named display.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed DisplayInfo is pure-value Sendable, not @MainActor; UI ops properly isolated on MainActor; v2MainSync handles cross-isolation calls correctly.
Cmux Swift Blocking Runtime ✅ Passed New code adds no blocking primitives (semaphores, locks, sleep). Uses pre-existing v2MainSync pattern which is allowed per rules ("existing blocking code that the PR does not introduce or worsen").
Cmux Expensive Synchronous Load ✅ Passed PR adds only lightweight NSScreen.screens queries and window repositioning via setFrame()—no expensive synchronous loaders like RestorableAgentSessionIndex.load() are present.
Cmux Cache Substitution Correctness ✅ Passed New window display functions read fresh display data directly from NSScreen.screens (live authoritative source) with no caching, and perform transient repositioning without persistence involvement.
Cmux No Hacky Sleeps ✅ Passed Check does not apply: the rule targets TypeScript, JavaScript, shell, and non-Swift runtime scripts. PR modifies only Swift files and documentation, which are out of scope.
Cmux Algorithmic Complexity ✅ Passed Fixed system collections only (displays ≤4, windows ≤10). Three sequential first(where:) in screenMatching(). No nested scans, no per-target rescans, socket methods not in loops.
Cmux Swift Concurrency ✅ Passed New display code uses synchronous AppKit APIs and pre-existing v2MainSync; no new DispatchQueue, Combine, completion handlers, or fire-and-forget Tasks.
Cmux Swift @Concurrent ✅ Passed All new Swift functions are synchronous @MainActor methods (no async/nonisolated async). No @concurrent annotations added or required. Operations are lightweight UI queries, not async-heavy work.
Cmux Swift File And Package Boundaries ✅ Passed All three files receive <250 lines; existing oversized files with small AppKit/UI glue additions; each preserves file responsibility; budget updated appropriately.
Cmux Swift Logging ✅ Passed print() in CLI/cmux.swift is CLI command output (allowed); app/runtime code (AppDelegate, TerminalController) has no logging violations; no sensitive data exposed.
Cmux User-Facing Error Privacy ✅ Passed All user-facing error messages in the new window display feature comply with privacy rules: no vendor names, credentials, or sensitive details exposed.
Cmux Swiftui State Layout ✅ Passed No SwiftUI state changes were made; PR adds only AppKit window-display routing (CLI, RPC) and AppDelegate display-management functions with AppKit APIs (NSScreen, NSWindow, NSRect).
Cmux Architecture Rethink ✅ Passed PR adds required platform bridge code with single AppDelegate ownership, clear focus invariants, explicit non-stealing behavior (no raise/key/activate), and no new problematic timing patterns.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR adds window display routing without creating any new NSWindow, NSPanel, NSWindowController, or SwiftUI Window/WindowGroup objects. Only moves existing windows.
Description check ✅ Passed PR description covers what changed, why it was needed, and testing performed; all major template sections are addressed with substantive details.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-cli-window-display

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 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 `@docs/cli-contract.md`:
- Line 95: Update the CLI docs line describing the window display command to
replace the awkward phrase "`--list` aliases `window displays`" with a clearer
wording; locate the entry for the `window display <name|index>` command and
change the trailing clause to one of the suggested rephrasings such as "`Use
--list to show available displays (same as the 'window displays' command).`" or
"`--list: show available displays (equivalent to the 'window displays'
command).`" to make the intent unambiguous.
- Around line 94-95: The docs mix new namespaced commands (window displays,
window display) with legacy hyphenated commands (list-windows, current-window,
new-window, focus-window, close-window) without explanation; add a "Window
subcommands:" section in the "Command Families" area (after the Auth/VM sections
around where other subcommand families are documented) that lists the window
namespace (window displays — List connected displays; window display
<name|index> — move instance windows, with --window and --list behaviors) and
include a brief note that the hyphenated top-level commands remain for backward
compatibility and point readers to the window subcommands for the new pattern,
or alternatively add an inline compatibility note next to each hyphenated entry
linking to the new namespace.

In `@Sources/AppDelegate.swift`:
- Around line 17937-17945: The isMain logic in the DisplayInfo mapping uses only
cmuxDisplayID equality and thus marks no main screen when cmuxDisplayID is nil;
update the isMain computation in the NSScreen.screens.enumerated() map so it
first checks if both displayID and mainID are non-nil and equal, otherwise fall
back to comparing the screen object to NSScreen.main (i.e., use (displayID !=
nil && displayID == mainID) || screen == NSScreen.main) to reliably detect the
main display; update the DisplayInfo creation site where isMain is set.
🪄 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: b740fe3c-ded3-4257-a80d-cb1a6e43fdac

📥 Commits

Reviewing files that changed from the base of the PR and between 972db97 and fa34e33.

📒 Files selected for processing (4)
  • CLI/cmux.swift
  • Sources/AppDelegate.swift
  • Sources/TerminalController.swift
  • docs/cli-contract.md

Comment thread docs/cli-contract.md
Comment on lines +94 to +95
| `window displays` | List connected displays (name, index, main flag). |
| `window display <name\|index>` | Move the instance's window(s) onto a display by name (exact, substring) or index, preserving size. Does not steal focus. With `--window`, targets that window; otherwise moves all main windows. `--list` aliases `window displays`. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Document the window namespace pattern alongside legacy hyphenated commands.

The new window displays and window display commands follow a namespaced subcommand pattern (like auth status, vm ls), while existing window-related commands (list-windows, current-window, new-window, focus-window, close-window at lines 89-93) use the hyphenated top-level pattern.

This creates two different command styles for window operations without explanation. Consider either:

  1. Preferred: Add a "Window subcommands:" section in the "Command Families" area (after line 176) to document the window namespace, similar to Auth subcommands (line 178) and VM subcommands (line 186).
  2. Alternative: Add an inline note explaining that window <subcommand> is the new pattern while legacy hyphenated commands remain for compatibility.

This will help users understand the migration path and keep the documentation organized consistently with other namespaced commands.

📋 Example "Window subcommands:" section

Add after line 176 (before "Auth subcommands:"):

+Window subcommands:
+
+| Command | Contract |
+| --- | --- |
+| `window displays` | List connected displays (name, index, main flag). |
+| `window display <name\|index>` | Move the instance's window(s) onto a display by name (exact, substring) or index, preserving size. Does not steal focus. With `--window`, targets that window; otherwise moves all main windows. `--list` aliases `window displays`. |
+
 Auth subcommands:

Then optionally add a note in the top-level table entries pointing to the Window subcommands section, or keep them in both places for discoverability.

🤖 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 `@docs/cli-contract.md` around lines 94 - 95, The docs mix new namespaced
commands (window displays, window display) with legacy hyphenated commands
(list-windows, current-window, new-window, focus-window, close-window) without
explanation; add a "Window subcommands:" section in the "Command Families" area
(after the Auth/VM sections around where other subcommand families are
documented) that lists the window namespace (window displays — List connected
displays; window display <name|index> — move instance windows, with --window and
--list behaviors) and include a brief note that the hyphenated top-level
commands remain for backward compatibility and point readers to the window
subcommands for the new pattern, or alternatively add an inline compatibility
note next to each hyphenated entry linking to the new namespace.

Comment thread docs/cli-contract.md
| `focus-window` | Focus a window by handle. |
| `close-window` | Close a window by handle. |
| `window displays` | List connected displays (name, index, main flag). |
| `window display <name\|index>` | Move the instance's window(s) onto a display by name (exact, substring) or index, preserving size. Does not steal focus. With `--window`, targets that window; otherwise moves all main windows. `--list` aliases `window displays`. |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clarify the --list alias phrasing.

The phrase "--list aliases window displays" is grammatically awkward and could be misinterpreted.

✏️ Suggested rephrasings

Choose one of these clearer alternatives:

-| `window display <name\|index>` | Move the instance's window(s) onto a display by name (exact, substring) or index, preserving size. Does not steal focus. With `--window`, targets that window; otherwise moves all main windows. `--list` aliases `window displays`. |
+| `window display <name\|index>` | Move the instance's window(s) onto a display by name (exact, substring) or index, preserving size. Does not steal focus. With `--window`, targets that window; otherwise moves all main windows. `window display --list` is an alias for `window displays`. |

Or:

-| `window display <name\|index>` | Move the instance's window(s) onto a display by name (exact, substring) or index, preserving size. Does not steal focus. With `--window`, targets that window; otherwise moves all main windows. `--list` aliases `window displays`. |
+| `window display <name\|index>` | Move the instance's window(s) onto a display by name (exact, substring) or index, preserving size. Does not steal focus. With `--window`, targets that window; otherwise moves all main windows. Supports `--list` to list displays (equivalent to `window displays`). |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| `window display <name\|index>` | Move the instance's window(s) onto a display by name (exact, substring) or index, preserving size. Does not steal focus. With `--window`, targets that window; otherwise moves all main windows. `--list` aliases `window displays`. |
| `window display <name\|index>` | Move the instance's window(s) onto a display by name (exact, substring) or index, preserving size. Does not steal focus. With `--window`, targets that window; otherwise moves all main windows. `window display --list` is an alias for `window displays`. |
🤖 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 `@docs/cli-contract.md` at line 95, Update the CLI docs line describing the
window display command to replace the awkward phrase "`--list` aliases `window
displays`" with a clearer wording; locate the entry for the `window display
<name|index>` command and change the trailing clause to one of the suggested
rephrasings such as "`Use --list to show available displays (same as the 'window
displays' command).`" or "`--list: show available displays (equivalent to the
'window displays' command).`" to make the intent unambiguous.

Comment thread Sources/AppDelegate.swift
Comment on lines +17937 to +17945
let mainID = NSScreen.main?.cmuxDisplayID
return NSScreen.screens.enumerated().map { index, screen in
let displayID = screen.cmuxDisplayID
return DisplayInfo(
name: screen.localizedName,
index: index,
displayID: displayID,
isMain: displayID != nil && displayID == mainID,
frame: screen.frame

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

isMain detection should not depend only on displayID.

Line 17944 marks main display only when both IDs are non-nil and equal. If cmuxDisplayID is unavailable, isMain becomes false for every display. Add a fallback to screen == NSScreen.main.

Suggested fix
-                isMain: displayID != nil && displayID == mainID,
+                isMain: (displayID != nil && displayID == mainID) || screen == NSScreen.main,
🤖 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 `@Sources/AppDelegate.swift` around lines 17937 - 17945, The isMain logic in
the DisplayInfo mapping uses only cmuxDisplayID equality and thus marks no main
screen when cmuxDisplayID is nil; update the isMain computation in the
NSScreen.screens.enumerated() map so it first checks if both displayID and
mainID are non-nil and equal, otherwise fall back to comparing the screen object
to NSScreen.main (i.e., use (displayID != nil && displayID == mainID) || screen
== NSScreen.main) to reliably detect the main display; update the DisplayInfo
creation site where isMain is set.

@greptile-apps

greptile-apps Bot commented Jun 10, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds cmux window display <name|index> and cmux window displays CLI commands (and matching window.display / window.displays v2 socket methods) to reposition the app's windows onto a chosen monitor without stealing macOS focus. The implementation resolves displays by case-insensitive exact name, substring, or zero-based index, then calls NSWindow.setFrame centered in the target screen's visible frame.

  • AppDelegate.swift: New DisplayInfo struct, availableDisplays(), screenMatching(), moveMainWindow(), moveAllMainWindows(), and repositionPreservingSize() — all on the @MainActor AppDelegate, safely dispatched via the existing v2MainSync pattern.
  • TerminalController.swift: New v2WindowDisplays and v2WindowDisplay handlers wired into the v2 dispatch table and advertised in v2Capabilities(); deliberately excluded from focusIntentV2Methods so the move never activates the window.
  • CLI/cmux.swift: New runWindowNamespace / runWindowDisplaysCommand / runWindowDisplayCommand functions; window command is now exempt from the pre-focus path in shouldFocusWindowBeforeDispatch.

Confidence Score: 5/5

Safe to merge; the window-placement logic is well-scoped, correctly dispatched through the existing v2MainSync/MainActor pattern, and deliberately kept out of the focus-intent path.

The actor-isolation model is correct: all AppKit calls happen inside v2MainSync on @mainactor AppDelegate. The focus-steal concern is properly addressed — window.display is absent from focusIntentV2Methods and setFrame is called without activate/makeKeyAndOrderFront. The only new finding is an edge-case where the all-windows path returns a success response with an empty moved list, which would print "Moved 0 windows to X." in the CLI; that can't happen in normal usage but is worth tightening.

Sources/TerminalController.swift — the all-windows branch of v2WindowDisplay can return .ok with an empty moved array.

Important Files Changed

Filename Overview
Sources/AppDelegate.swift Adds window-placement helpers in a new extension: DisplayInfo struct, availableDisplays(), screenMatching(), moveMainWindow(), moveAllMainWindows(), repositionPreservingSize(). All correctly on @mainactor; setFrame called without activate/makeKeyAndOrderFront as intended.
Sources/TerminalController.swift Adds v2WindowDisplays and v2WindowDisplay handlers. Correctly excluded from focusIntentV2Methods and registered in v2Capabilities. The single-window error-discrimination path uses two separate v2MainSync calls (TOCTOU already flagged in a previous review thread).
CLI/cmux.swift Adds runWindowNamespace / runWindowDisplaysCommand / runWindowDisplayCommand; correctly exempts "window" from the pre-focus path. Raw string literals for user-facing output (already flagged in a previous review thread).
docs/cli-contract.md Documents the two new window subcommands accurately; describes name/substring/index resolution, --window targeting, and --list alias.
.github/swift-file-length-budget.tsv Budget bumped for three touched files; also sorts two out-of-order rows. Mechanical bookkeeping, no concerns.

Reviews (3): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile

Comment thread CLI/cmux.swift
Comment on lines +7137 to 7210
throw CLIError(message: "window requires a subcommand. Try: display, displays")
}
let rest = Array(commandArgs.dropFirst())
switch sub {
case "displays":
try runWindowDisplaysCommand(client: client, jsonOutput: jsonOutput)
case "display":
try runWindowDisplayCommand(
commandArgs: rest,
client: client,
jsonOutput: jsonOutput,
idFormat: idFormat,
windowOverride: windowOverride
)
default:
throw CLIError(message: "Unknown window subcommand: \(sub). Try: display, displays")
}
}

/// `cmux window displays` — list connected displays (name + index).
private func runWindowDisplaysCommand(client: SocketClient, jsonOutput: Bool) throws {
let response = try client.sendV2(method: "window.displays")
if jsonOutput {
print(jsonString(response))
return
}
let displays = (response["displays"] as? [[String: Any]]) ?? []
if displays.isEmpty {
print("No displays found.")
return
}
for display in displays {
let name = (display["name"] as? String) ?? "(unknown)"
let index = (display["index"] as? Int) ?? -1
let isMain = (display["main"] as? Bool) ?? false
print("\(index): \(name)\(isMain ? " (main)" : "")")
}
}

/// `cmux window display "<name>"` — move this instance's window(s) onto the
/// named display, preserving size. `--list` is an alias for `window displays`.
private func runWindowDisplayCommand(
commandArgs: [String],
client: SocketClient,
jsonOutput: Bool,
idFormat: CLIIDFormat,
windowOverride: String?
) throws {
if commandArgs.contains("--list") || commandArgs.contains("-l") {
try runWindowDisplaysCommand(client: client, jsonOutput: jsonOutput)
return
}
let positional = commandArgs.filter { !$0.hasPrefix("-") }
guard let displayName = positional.first, !displayName.isEmpty else {
throw CLIError(message: "window display requires a display name. Usage: cmux window display \"LG HDR 4K\" (list names with: cmux window displays)")
}
var params: [String: Any] = ["display": displayName]
if let windowOverride {
let normalized = try normalizeWindowHandle(windowOverride, client: client) ?? windowOverride
params["window_id"] = normalized
}
let response = try client.sendV2(method: "window.display", params: params)
if jsonOutput {
print(jsonString(formatIDs(response, mode: idFormat)))
return
}
let resolvedDisplay = (response["display"] as? String) ?? displayName
let movedCount = (response["moved"] as? [Any])?.count ?? 0
print("Moved \(movedCount) window\(movedCount == 1 ? "" : "s") to \(resolvedDisplay).")
}

private func runWorkspaceNamespace(
commandArgs: [String],
client: SocketClient,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 New user-facing strings not routed through String(localized:)

runWindowNamespace, runWindowDisplaysCommand, and runWindowDisplayCommand all produce user-visible output with raw string literals — "window requires a subcommand. Try: display, displays", "No displays found.", "Moved \(movedCount) window\(movedCount == 1 ? "" : "s") to \(resolvedDisplay).", etc. The existing notification commands in this same file (e.g. dismiss-notification, mark-notification-read) use String(localized:defaultValue:) with catalog entries, so there is an established pattern to follow. Raw literals here skip localization for every non-English locale the app supports.

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!

Comment on lines +4102 to +4120

private func v2WindowDisplay(params: [String: Any]) -> V2CallResult {
guard let displayQuery = (params["display"] as? String)?
.trimmingCharacters(in: .whitespacesAndNewlines),
!displayQuery.isEmpty else {
return .err(code: "invalid_params", message: "Missing or invalid display", data: nil)
}

// Explicit window target moves just that window; otherwise move every main
// window of this instance (a dev build usually has one).
if let windowId = v2UUID(params, "window_id") {
let resolved = v2MainSync {
AppDelegate.shared?.moveMainWindow(windowId: windowId, toDisplayMatching: displayQuery)
}
if let display = resolved {
return .ok([
"display": display,
"window_id": windowId.uuidString,
"window_ref": v2Ref(kind: .window, uuid: windowId),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Two separate v2MainSync calls in error-discrimination path create a TOCTOU window

When moveMainWindow(windowId:toDisplayMatching:) returns nil, the code issues a second independent v2MainSync to check windowForMainWindowId. Between the two calls the window can be created or destroyed, causing the wrong error code: if a window is created between the calls, the response says "not_found" for the display even though the window didn't exist when the move was attempted. Folding both checks into a single v2MainSync closure — for example returning an enum that distinguishes windowNotFound/displayNotFound — would eliminate the race and remove the redundant round-trip to the main actor.

- TerminalController v2WindowDisplay: the windowExists closure already returns
  a non-optional Bool, so the outer `?? false` was dead code and tripped the
  zero-warning budget. Removed it.
- Refresh .github/swift-file-length-budget.tsv for the three files this feature
  grew (CLI/cmux.swift, TerminalController.swift, AppDelegate.swift). The
  additions are a cohesive ~90-line feature on already-large files; splitting
  into new files would require hand-wiring the Xcode project for three files
  across two targets, so the sanctioned budget refresh is the lower-risk fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts:
#	.github/swift-file-length-budget.tsv

This branch was successfully deployed

1 active deployment
Preview – cmux — 60e23598 Deployed Jun 10, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant