How to Script Terminal Splits and Panes Using cmux JSON API
The cmux terminal multiplexer exposes a version-2 JSON-RPC API over a local Unix socket, enabling you to automate pane creation and layout splits programmatically using the surface.split and pane.create methods.
The manaflow-ai/cmux repository provides a powerful JSON API for terminal automation that allows precise control over window layouts and pane management. By leveraging the JSON-RPC interface exposed via local socket, developers can script terminal splits and panes using cmux JSON API to build complex, automated workspace configurations without manual interaction.
Understanding the cmux JSON-RPC Architecture
The cmux API follows a standard JSON-RPC protocol routed through TerminalController.handleV2Call, which dispatches requests based on the "method" field. When scripting terminal splits, you interact with two primary entry points that interface with the underlying Bonsplit layout engine.
Core API Methods for Layout Control
Two methods handle automated pane creation:
-
surface.split(implemented inv2SurfaceSplit): Creates a new split from an existing surface, accepting parameters for direction, workspace, surface ID, and focus behavior. -
pane.create(implemented inv2PaneCreate): A higher-level wrapper that returns a dedicated pane identifier, handling orientation logic and insert positioning before calling the same Bonsplit APIs.
Both methods accept a JSON payload specifying direction (left, right, up, down), optional workspace_id and surface_id, and a focus boolean. The server validates the request through parseSplitDirection (source), which accepts full names or single-letter abbreviations (l, r, u, d).
Request Routing and Context Resolution
When a request arrives at the socket listener, the controller performs the following sequence:
- Resolves the TabManager for the current UI session.
- Validates the split direction against the
SplitDirectionenum. - Derives target workspace and surface from the request parameters or falls back to currently focused contexts.
- Invokes
tabManager.newSplit(tabId:surfaceId:direction:focus:)to create the panel and update the Bonsplit tree.
The method returns a rich JSON response containing:
{
"window_id": "...",
"workspace_id": "...",
"pane_id": "...",
"surface_id": "...",
"type": "terminal|browser"
}
All identifiers are exposed as both raw UUIDs and stable reference strings (pane_ref, surface_ref) used by the CLI.
Scripting Terminal Splits: Practical Implementations
The cmux CLI is a thin wrapper around this API. You can script splits using the CLI's JSON mode, direct socket calls, or the bundled Swift client library.
Method 1: CLI One-Liners with JSON Output
For quick automation, use the new-split command with --json flag to parse the response:
# Split the currently focused pane to the right and capture the output
cmux new-split right --json | jq .
Example response:
{
"window_id": "c7a1...",
"workspace_id": "b9f2...",
"pane_id": "d4e3...",
"surface_id": "d4e3...",
"type": "terminal"
}
Chain commands to focus the new pane using its reference:
NEW_PANE=$(cmux new-split right --json | jq -r .pane_ref)
cmux focus-pane --pane "$NEW_PANE"
Method 2: Bash Scripting with pane.create
For complex workflows, construct JSON payloads manually and use the pane.create method. This example creates a browser split to the left of the current surface:
#!/usr/bin/env bash
set -euo pipefail
DIR="left"
URL="https://developer.apple.com"
# Build the JSON payload
payload=$(cat <<EOF
{
"direction": "$DIR",
"type": "browser",
"url": "$URL"
}
EOF
)
# Send the request via the built-in client library
response=$(cmux --json pane.create "$(printf '%s' "$payload")")
echo "Created pane:"
echo "$response" | jq .
# Focus the newly created pane using its reference
pane_ref=$(echo "$response" | jq -r .pane_ref)
cmux focus-pane --pane "$pane_ref"
This approach leverages the higher-level pane.create endpoint which automatically determines orientation (horizontal vs vertical) based on direction and manages the insertFirst logic for positioning.
Method 3: Swift Client Integration
For native macOS automation, use the bundled V2Client library to communicate directly with the socket at $CMUX_SOCKET:
import Foundation
import cmux
let client = V2Client() // Connects to $CMUX_SOCKET automatically
let params: [String: Any] = [
"direction": "down",
"type": "terminal"
]
do {
let response = try client.sendV2(method: "surface.split", params: params)
print("Split created →", response)
} catch {
print("Failed:", error)
}
Compile this using the repository's Swift package manager (swift build -c release) to create a binary that communicates natively with the running cmux process.
Key Source Files and Implementation Details
Understanding the source implementation helps debug automation scripts:
| File | Purpose | Key Functions |
|---|---|---|
Sources/TerminalController.swift |
Core JSON-RPC handlers | v2SurfaceSplit (L4595-L4660), v2PaneCreate (L5977-L6030), parseSplitDirection (L12995-L13008) |
CLI/cmux.swift |
CLI argument parsing and payload construction | New-split handling (L1718-L1734) |
docs/v2-api-migration.md |
API versioning documentation | Migration from legacy split commands to surface.split |
docs/remote-daemon-spec.md |
Socket protocol specification | JSON-RPC format and authentication |
Summary
- The
surface.splitandpane.createJSON-RPC methods are the primary endpoints for scripting terminal layouts in cmux. - Both methods accept
direction(left/right/up/down), optionalworkspace_id/surface_id,type(terminal/browser), andfocusparameters. - The API returns comprehensive identifiers including
pane_id,surface_id,workspace_id, and human-readable*_refstrings for subsequent CLI commands. - You can automate splits via the
cmuxCLI with--jsonoutput, direct socket communication using the SwiftV2Client, or any language capable of writing to the Unix domain socket at$CMUX_SOCKET.
Frequently Asked Questions
What is the difference between surface.split and pane.create?
The surface.split method creates a new split from an existing surface and returns surface and workspace identifiers, while pane.create is a higher-level wrapper that additionally determines orientation (horizontal vs vertical), manages insert positioning, and returns a dedicated pane_id suitable for pane-specific operations like focusing or closing.
How do I specify split directions in the cmux JSON API?
The API accepts direction strings (left, right, up, down) or single-letter abbreviations (l, r, u, d) in the request payload. The parseSplitDirection function in TerminalController.swift validates these against the SplitDirection enum before executing the split on the Bonsplit layout engine.
Can I create browser panes using the JSON API?
Yes, include "type": "browser" in your JSON payload when calling surface.split or pane.create, and provide a "url" field to specify the initial webpage. The server will instantiate a browser surface instead of a terminal surface within the split layout.
How do I focus a newly created pane programmatically?
Extract the pane_ref or pane_id from the JSON response and pass it to the focus-pane CLI command: cmux focus-pane --pane "$PANE_REF". When creating the split, you can also set "focus": true in the initial request to automatically shift focus to the new pane upon creation.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →