Control cmux terminal workspaces and browser surfaces
Controls cmux panes, surfaces, workspaces, and embedded browser automation on macOS via its CLI and Unix-socket API.
Why it matters
Orchestrate multiple AI coding agents running in parallel cmux panes by inspecting topology, creating workspaces and surfaces, sending commands, monitoring output, and automating browser sessions-all through a Unix-socket JSON-RPC API.
Outcomes
What it gets done
Create and arrange cmux workspaces, panes, and surfaces for multi-agent coding workflows
Send input to specific terminal surfaces and poll agent output with short sleep intervals
Automate browser interactions via WKWebView including form fills, clicks, and snapshots
Manage markdown viewers and documentation tabs in dedicated right-side panes without spawning strays
Install
Add it to your toolbox
Run in your project directory:
curl -fsSL https://spark.entire.vc/get/ag-cmux | bash Overview
cmux Control
Controls cmux panes, surfaces, workspaces, and embedded WKWebView browser automation on macOS via its CLI and Unix-socket JSON-RPC API, covering topology, input targeting, notifications, and the shared markdown viewer pane. Use it whenever cmux panes, surfaces, or workspaces need inspecting or controlling. Always use prefixed refs and anchor to CMUX_WORKSPACE_ID - bare numbers and the focused workspace silently mislead.
What it does
Controls cmux, a native macOS terminal app for running multiple AI coding agents in parallel, through its CLI and a Unix-socket JSON-RPC API for full topology and browser control - inspecting, creating, closing, or rearranging panes, surfaces, and workspaces, and sending input to or monitoring agents running inside cmux. Its object model has four levels: a Window is the top-level macOS window, a Workspace is a sidebar tab tied to one git branch or project context, a Pane is a split region inside a workspace, and a Surface is a tab inside a pane, either terminal or browser. Handles default to short refs like a workspace or surface number, with UUIDs also accepted as input and available as output via a flag.
When to use - and when NOT to
Use it when you need to inspect, create, close, or rearrange cmux panes, surfaces, or workspaces, or when you need to send input to or monitor agents running inside cmux. It is macOS-only, with no Linux or Windows port. Getting reference syntax wrong fails silently rather than erroring loudly: always use a prefixed ref like pane:38 or surface:46, since a bare number is treated as an index into a list rather than an actual id, usually pointing at nothing and failing silently. Reading a pane's screen has no direct pane flag at all - read-screen and capture-pane only accept --workspace or --surface, and passing --pane errors while an omitted target quietly falls back to reading your own surface, leading to wrong conclusions drawn from your own footer instead of the pane you meant to inspect; the fix is to resolve the pane to a surface first, then read that surface. Never append a stderr-suppressing redirect to cmux commands, since errors carry the actual ref or flag mistake and suppressing them is the single biggest cause of a mysteriously empty result.
Inputs and outputs
Detecting whether you're inside cmux checks for the Unix socket and the workspace-id environment variable; every cmux-spawned terminal is injected with CMUX_WORKSPACE_ID, CMUX_SURFACE_ID, CMUX_SOCKET_PATH, and CMUX_PORT, and automation should always anchor to CMUX_WORKSPACE_ID specifically, since the visually focused workspace may not actually be the calling agent's own workspace. Core topology commands identify the caller, dump the full hierarchy, and list workspaces, panes, and surfaces; layout commands create a new workspace or pane, move or reorder a surface between panes, split a surface off, or close it. Sending input has its own naming trap: there is no send-surface or send-key-surface command - a specific surface is targeted by passing --surface to the plain send and send-key commands instead:
cmux send --surface surface:7 "npm run build" # specific surface (NOT send-surface)
cmux send-key --surface surface:7 enter # specific surface (NOT send-key-surface)
send-panel/send-key-panel exist only for panels, never for surfaces. Notifications and sidebar metadata commands can set a toast, a named status with an icon and color, a progress bar, a leveled log line, a focus-drawing flash cue, or dump the sidebar's full metadata state. Browser automation runs over WKWebView through a fixed workflow of open, wait, snapshot, act, re-snapshot, returning interactive elements as short handles to fill or click, plus navigation, inspection, wait-for-selector-or-url, cookie and storage-state session commands, and console, error, and screenshot diagnostics - though WKWebView explicitly does not support viewport emulation, geolocation or offline emulation, trace recording, network route interception, or raw input injection, all returning a not-supported result instead.
Integrations
The markdown and PDF viewer opens a live-watching right-side pane, but by default spawns a brand-new pane on every call even when a right pane already exists; keeping every doc as a tab in one shared right pane requires finding that pane's existing markdown surface first and opening the new file targeted at that surface id, then moving any stray new surface into the right pane if one still spawned. Swapping the file shown in that single right pane has a strict, order-dependent fix: the previous surface must be closed first, then the new file opened fresh - moving an existing viewer, or opening before closing, both leave the pane blank. Several hard-won lessons cut across all of this: surface refs are global across the whole app, not scoped to one workspace, so a ref from an earlier turn should always be re-verified rather than assumed still valid; moving a markdown viewer with move-surface frequently leaves it rendering blank even though its own health check looks fine, and the only reliable fix is closing it and reopening fresh rather than refreshing it; a markdown surface can never be screenshotted or read as a screen, so verifying it rendered means asking the user or opening the same file in a browser surface instead; and there is no list-surfaces command at all, only list-pane-surfaces. Settings live in a canonical JSON config with an optional project-local override, while terminal rendering itself, meaning font, cursor, theme, and scrollback, lives in a separate Ghostty config file rather than the cmux config - and any edit to the cmux config should be preceded by a timestamped backup copy so the user can revert. Installation goes through Homebrew plus a manual symlink into the local bin path, with hook setup available for all detected agents or a named subset, and native session-resume is supported across a long list of agents including Claude Code, Codex, Grok, OpenCode, Pi, Amp, Cursor CLI, Gemini, and several others. An advanced Unix-socket JSON-RPC v2 API exists for tight loops where subprocess spawn cost actually matters, gated by an access mode that defaults to only cmux-spawned processes, with automation, password, and an explicitly unsafe allow-all mode also available - external processes hitting a connection failure are almost always blocked by that default mode. A short set of non-disruptive-automation rules governs anything that could yank the user's focus: always anchor to the workspace id rather than assuming the focused one is the target, never call a focus-changing command speculatively, build a layout additively in one call rather than create-then-move-then-focus, reuse an existing helper pane before creating a new one, never send input to a surface outside the caller's own workspace without explicit request, and check surface health before routing input into UI state that might be stale.
Who it's for
Agents and developers orchestrating multiple parallel AI coding sessions inside cmux on macOS who need precise, non-disruptive control over panes, surfaces, workspaces, and embedded browser automation, without silently misfiring on cmux's ref-syntax and pane-versus-surface distinctions.
Source README
cmux Control
When to Use
- Use when you need to inspect, create, close, or rearrange cmux panes, surfaces, or workspaces.
- Use when you need to send input to or monitor agents running inside cmux.
cmux is a native macOS terminal app for running multiple AI coding agents in parallel. It exposes a CLI (cmux) and a Unix-socket JSON-RPC API (/tmp/cmux.sock) for full topology and browser control.
Core Concepts
- Window - top-level macOS cmux window
- Workspace - sidebar tab within a window (one git branch / project context)
- Pane - split region inside a workspace
- Surface - tab inside a pane (terminal or browser)
Handles default to short refs (workspace:2, pane:1, surface:7); UUIDs accepted as input. Add --id-format uuids|both for UUID output.
Ref syntax - get this right or fail silently
- Always use PREFIXED refs (
pane:38,surface:46). A bare number is treated as an INDEX, not an ID ---surface 46means "the surface at index 46" (usually nonexistent → silent failure), NOTsurface:46. read-screenandcapture-panehave NO--paneflag - they target--workspaceor--surfaceonly. Passing--paneerrors, and a bare/missing target falls back to your OWN surface (you'll read your own footer and draw wrong conclusions). To read a pane: resolve it to a surface FIRST withcmux list-pane-surfaces --pane pane:N, thencmux read-screen --surface surface:N.- Never append
2>/dev/nullto cmux commands. Errors go to stderr with exit code 1; suppressing them blinds you to your own ref/flag mistakes (the #1 cause of "(no output)").
Detect cmux in a Shell
[ -S "${CMUX_SOCKET_PATH:-/tmp/cmux.sock}" ] || exit 0 # bail if not in cmux
[ -n "${CMUX_WORKSPACE_ID:-}" ] && echo "inside cmux surface"
Injected env vars in every cmux-spawned terminal: CMUX_WORKSPACE_ID, CMUX_SURFACE_ID, CMUX_SOCKET_PATH, CMUX_PORT. Always anchor automation to CMUX_WORKSPACE_ID - the visually focused workspace may not be the agent's caller workspace.
Fast Start - Topology
cmux identify --json # who am I (window/workspace/pane/surface)
cmux tree # full hierarchy
cmux list-workspaces --json
cmux list-panes --workspace "$CMUX_WORKSPACE_ID"
cmux list-surfaces --workspace "$CMUX_WORKSPACE_ID"
cmux new-workspace --name "feature-x" --cwd /path/to/repo
cmux new-pane --workspace "$CMUX_WORKSPACE_ID" --type terminal --direction right --focus false
cmux new-pane --workspace "$CMUX_WORKSPACE_ID" --type browser --direction right --url http://localhost:3000
cmux move-surface --surface surface:7 --pane pane:2 --focus false
cmux split-off --surface surface:7 right
cmux reorder-surface --surface surface:7 --before surface:3
cmux close-surface --surface surface:7
Polling Pi Agents in Panes - Keep Sleeps Short
When launching a Pi Agent inside a cmux pane and polling for output, use short sleep intervals (2-5s). Pi is fast and minimal, and the user runs it on Opus 4.8 Fast via OpenRouter, which streams tokens extremely quickly. Do NOT use sleep 15 unless genuinely needed (a big build/refactor) - most of the time sleep 2-sleep 5 is more than enough.
After every agent check, send the user a one-line status update: what the agent is doing and whether it is on track. Keep it extremely concise.
Claude Code cmux note: after Claude finishes, it may prefill a predicted next user message; that draft is Claude, not the user speaking.
Send Input
Command names: there is NO send-surface / send-key-surface. Target a specific surface with the --surface flag on send / send-key (same commands as the focused terminal). send-panel / send-key-panel exist ONLY for panels (--panel), not surfaces.
cmux send "echo hi\n" # focused terminal
cmux send-key "ctrl+c" # enter|tab|esc|backspace|arrows|ctrl+x|shift+tab
cmux send --surface surface:7 "npm run build" # specific surface (NOT send-surface)
cmux send-key --surface surface:7 enter # specific surface (NOT send-key-surface)
Notifications & Sidebar Metadata
cmux notify --title "Done" --body "tests passed"
cmux set-status build "compiling" --icon hammer --color "#ff9500"
cmux set-progress 0.5 --label "Building..."
cmux log --level success "All 42 tests passed" # info|progress|success|warning|error
cmux trigger-flash --workspace "$CMUX_WORKSPACE_ID" # blue-ring attention cue
cmux sidebar-state --json # dump all sidebar metadata
Browser Automation (WKWebView)
Workflow: open → wait → snapshot → act → re-snapshot.
S=$(cmux --json browser open https://example.com | jq -r .result.surface_ref)
cmux browser "$S" wait --load-state complete --timeout-ms 15000
cmux browser "$S" snapshot --interactive # returns elements as e1, e2, ...
cmux browser "$S" fill e1 "<email-address>"
cmux browser "$S" click e2 --snapshot-after
### Navigation / inspection
cmux browser "$S" goto URL | back | forward | reload
cmux browser "$S" get url | get title | get text body | get value "#email" | get count ".row"
cmux browser "$S" eval 'return document.title'
### Waits
cmux browser "$S" wait --selector "#ready" --timeout-ms 10000
cmux browser "$S" wait --url-contains "/dashboard" --timeout-ms 10000
### Session
cmux browser "$S" cookies get | cookies set --name foo --value bar
cmux browser "$S" state save /tmp/auth.json | state load /tmp/auth.json
### Diagnostics
cmux browser "$S" console list | errors list | screenshot
Not supported by WKWebView (return not_supported): viewport emulation, geolocation/offline emulation, trace recording, network route interception, raw input injection.
Markdown Viewer
cmux markdown open plan.md --direction right # live-watching renderer
cmux open file.pdf # auto-routes to right viewer
cmux markdown open flags: --workspace, --surface, --window, --direction <right|down|left|up>, --focus <true|false>. There is NO --pane flag - passing it errors. To target a pane, pass --surface <existing-md-surface-in-that-pane>.
Reuse the existing right markdown pane (don't spawn strays)
Default behavior of markdown open is to create a new pane every time, even with --direction right. To keep all docs as tabs in ONE right pane, follow this exactly:
### 1. Find the right pane and its surfaces (anchor to THIS workspace)
cmux list-panes --workspace "$CMUX_WORKSPACE_ID"
cmux list-pane-surfaces --pane pane:10 # the right/helper pane
### 2. Open targeting an existing markdown surface IN that pane (reuses pane, adds tab)
cmux markdown open /abs/path/file.md --surface surface:12 --focus false
### 3. If it STILL spawned a new pane (it can), move the new surface in + verify
cmux move-surface --surface surface:NEW --pane pane:10 --focus false
cmux list-panes --workspace "$CMUX_WORKSPACE_ID" # confirm stray pane is gone
Swapping the file in the single right pane (close-FIRST, then open)
To replace the doc shown in your one right markdown pane, the ONLY reliable order is close the previous surface FIRST, then markdown open the new file fresh - never move an existing viewer, never open-then-close.
### 1. close the previous right markdown surface (right side goes empty)
cmux list-panes --workspace "$CMUX_WORKSPACE_ID"
cmux close-surface --surface surface:PREV
### 2. THEN open the new file fresh
cmux markdown open /abs/path/new.md --direction right --focus false
ORDER MATTERS: close-previous BEFORE open-new. Opening first then closing the old one, or move-surface-ing an existing viewer, leaves the right pane BLANK.
Hard-won lessons (avoid the trial-and-error)
- Surface refs are global, not per-workspace. A ref like
surface:126from an earliermarkdown openmay live in a different window/workspace. Always re-list (list-panes/list-pane-surfaces) before reusing a ref - never assume a ref from a previous turn is still in the right pane. move-surface-ing a markdown viewer often leaves it BLANK. The moved surface keepstype=markdownandsurface-healthlooks fine, but renders nothing. Fix:close-surfaceit andcmux markdown open <path>fresh, then move the fresh surface if needed. Don't waste time onrefresh-surfaces- it usually won't fix a moved-then-blank viewer.- You cannot screenshot or
read-screena markdown surface (Surface is not a terminal/ browser screenshot is WKWebView-only). To verify a markdown viewer rendered, ask the user or open the file in a browser surface instead. Don't burn turns trying to capture it. cmux list-surfacesdoes not exist. Usecmux list-pane-surfaces [--pane ...].
Settings & Config
cmux docs settings # prints paths, schema URL, reload cmd — read BEFORE editing
cmux settings path # path to cmux.json
cmux settings cmux-json # open in editor
cmux reload-config # hot-reload cmux.json + ~/.config/ghostty/config (Cmd+Shift+,)
Locations:
- cmux settings:
~/.config/cmux/cmux.json(canonical). Project-local override:.cmux/cmux.jsonor./cmux.json. - Terminal rendering (font, cursor, theme, scrollback, opacity, blur):
~/.config/ghostty/config- NOT cmux.json.
Before editing cmux.json, copy it to a timestamped .bak next to it so the user can revert. Schema: https://raw.githubusercontent.com/manaflow-ai/cmux/main/web/data/cmux.schema.json.
Agent Hooks & Install
brew tap manaflow-ai/cmux && brew install --cask cmux
sudo ln -sf /Applications/cmux.app/Contents/Resources/bin/cmux /usr/local/bin/cmux
cmux hooks setup # all detected agents
cmux hooks setup codex|grok|antigravity|opencode # specific agent
npx skills add manaflow-ai/cmux -g -y # install cmux skills for agents
Native session-resume supported for: Claude Code, Codex, Grok, OpenCode, Pi, Amp, Cursor CLI, Gemini, Antigravity, Rovo Dev, Hermes, Copilot, CodeBuddy, Factory, Qoder.
Socket API (advanced)
/tmp/cmux.sock - Unix socket, JSON-RPC v2. Use for tight loops where subprocess spawn cost matters; otherwise prefer the CLI.
echo '{"id":"1","method":"workspace.list","params":{}}' | nc -U /tmp/cmux.sock
Method prefixes: system.*, window.*, workspace.*, pane.*, surface.*, notification.*, browser.*. Full list and Python client example in references/socket-api.md.
Access modes: cmuxOnly (default - only cmux-spawned processes), automation (any local process), password, allowAll (unsafe). If you hit Failed to connect to socket, you're likely an external process under cmuxOnly - switch mode in Settings > Automation or run from inside a cmux terminal.
Critical Rules - Non-Disruptive Automation
These rules come from the cmux-workspace skill and prevent agents from yanking the user's focus:
- Anchor to
CMUX_WORKSPACE_ID. Never assume the visually focused workspace is the target. - Never call focus-changing verbs speculatively.
select-workspace,focus-pane,focus-panel,focus-surfaceonly on explicit user request. Pass--focus falsewhenever available. - Build layout additively in one call.
cmux new-pane --type … --focus falsebeats create-then-move-then-focus chains. - Right-side helper pane pattern. Reuse an existing non-caller helper pane if present; otherwise create exactly one right-side pane.
- Never send input to surfaces you don't own. Only target surfaces in the caller's workspace unless the user explicitly asks for cross-workspace routing.
- Check surface health before routing input when UI state may be stale:
cmux surface-health.
Common Pitfalls
- Pi/Pi-like socket connection failures from external processes → default
cmuxOnlymode; either run inside a cmux terminal or change socket mode. - macOS only. No Linux/Windows port.
- WKWebView ≠ CDP. Don't expect Playwright-equivalent network mocking or viewport emulation.
- Resume strips sensitive env vars. Re-inject tokens at resume time if the agent needs them.
- Skills snapshot at app start. Edits to skill files require a restart of the consuming agent.
- Legacy v1 socket payloads (
{"command":...}) rejected. Use v2 JSON-RPC only. - Don't
cat ~/.cmuxterm/*-hook-sessions.jsonexpecting secrets - they're scrubbed. Look there for session/surface mappings only.
Reference: Full CLI Help
For any command, cmux <cmd> --help is authoritative. Use cmux capabilities --json to enumerate available socket methods in the current build.
Keyboard Shortcuts (most-used)
Workspaces: ⌘N new, ⌘1-8 jump, ⌃⌘[ / ⌃⌘] prev/next, ⌘⇧W close, ⌘B sidebar.
Surfaces: ⌘T new, ⌘⇧[ / ⌘⇧] prev/next, ⌘W close, ⌃1-8 jump.
Splits: ⌘D right, ⌘⇧D down, ⌥⌘D browser right, ⌥⌘←→↑↓ focus directional, ⌘⇧↵ zoom.
Browser: ⌘⇧L open, ⌘L address bar, ⌘[/⌘] back/forward, ⌥⌘I devtools.
App: ⌘, settings, ⌘⇧, reload-config, ⌘⇧P palette, ⌘⇧O restore session, ⌃⌥⌘. system-wide show/hide.
Limitations
- Adapted from
davidondrej/skills; verify local paths, tools, credentials, and agent features before acting. - For commands, remote access, scheduling, browser automation, or file-changing workflows, get explicit user approval and confirm the target environment first.
FAQ
Common questions
Discussion
Questions & comments · 0
Sign In Sign in to leave a comment.