CMUX session provider for Gas City.
Status: Early / experimental. Surface-per-session support is planned but not yet implemented. Currently, one workspace per rig with a shared surface.
This script implements the Exec Session Provider protocol, letting Gas City use cmux as its session backend on macOS.
Sessions are grouped by rig. A session named gastown.mayor shares a workspace with gastown.witness — they both open in the same CMUX workspace (gc-gastown). Sessions without dots (e.g. my-agent) get their own workspace.
This design matches the Gas Town mental model: one terminal workspace per project, with each agent as a surface (or in the future, a split) within it.
- cmux — macOS terminal app from cmux.com. Install and keep the app running.
- jq —
brew install jq - bash — macOS ships with this
The script talks to cmux over its Unix socket. If you use socket authentication, the provider needs access. The simplest setup:
- Open cmux → Settings → Advanced
- Set Socket Access to Allow All Incoming Connections (
CMUX_SOCKET_MODE=allowAll)
Or use a password and set CMUX_SOCKET_PASSWORD / --password.
cmux ships inside the macOS .app bundle at:
/Applications/cmux.app/Contents/Resources/bin/cmux
The provider auto-detects this path. If you've installed cmux elsewhere, override:
export GC_CMUX_BIN=/path/to/cmuxPut the script on your PATH:
cp gc-session-cmux /usr/local/bin/Or reference it directly:
export GC_SESSION=exec:/path/to/gc-session-cmux# Set the session provider (and optionally the binary path)
export GC_SESSION=exec:gc-session-cmux
# Start your city
gc start my-cityOr inline:
GC_SESSION=exec:gc-session-cmux gc start my-city- Derive workspace key from session name:
rig.role→rig - Create or reuse a CMUX workspace named
gc-<key> - Store state in
$GC_EXEC_STATE_DIR/gc-session-cmux/workspaces.json - Send commands into the workspace via
cmux send - Sync to bead metadata (best-effort): writes
gc.rig_workspace_uuidandgc.rig_workspace_nameto anygc:sessionbead matching the rig
{
"version": 1,
"workspaces": {
"gastown": {
"key": "gastown",
"name": "gc-gastown",
"ref": "workspace:3",
"uuid": "..."
}
}
}After creating a workspace, the script runs (if gc is on PATH):
gc bd update <bead-id> --set-metadata gc.rig_workspace_uuid=<uuid>
gc bd update <bead-id> --set-metadata gc.rig_workspace_name=<rig>This lets other tools discover the CMUX workspace associated with a session or rig.
| Operation | Status | Notes |
|---|---|---|
start |
✅ Implemented | Creates workspace, sends via cmux send |
stop |
✅ Implemented | Soft-stop (Ctrl+C), does NOT close shared workspace |
interrupt |
✅ Implemented | Sends Ctrl+C via cmux send |
is-running |
✅ Implemented | Checks state + CMUX list |
attach |
✅ Implemented | Focuses workspace via select-workspace |
process-alive |
✅ Implemented | Global pgrep (heuristic) |
nudge |
✅ Implemented | cmux send with trailing newline |
set-meta |
✅ Implemented | File-based |
get-meta |
✅ Implemented | File-based |
remove-meta |
✅ Implemented | File-based |
peek |
✅ Implemented | cmux read-screen --scrollback |
list-running |
✅ Implemented | Filters workspaces with gc- prefix |
get-last-activity |
⬜ Unsupported | Returns empty (per protocol) |
-
Surface-per-session: Each session gets its own CMUX surface within the shared workspace, rather than sharing one surface. This requires:
- Tracking
session_name → surface_refin state - Using
cmux new-splitto create dedicated surfaces - Routing
send,peek,interruptto the correct surface
- Tracking
-
Cleanup: Close workspace when all sessions in the rig have stopped
-
Theming: Map Gas Town role colors to CMUX workspace/config (if cmux exposes it)
MIT