ccmux is a tmux-integrated dashboard for monitoring and managing multiple AI coding agent sessions (Claude Code, Cursor, Codex, etc.) with live state tracking, session grouping, and one-tap navigation. It runs as a background daemon that surfaces idle, working, and waiting agents through an interactive TUI, letting developers switch between agent sessions and approve actions without leaving tmux.
๐ฎ Track all your AI coding agents (Claude Code, Codex, Cursor, ...) in tmux and jump to the one that needs you
README
โ Why?
When running multiple AI coding agent sessions across tmux panes, it's hard to keep track of which session is idle, which is waiting for permission, and which pane to switch to. ccmux solves this with a background daemon that monitors session activity and an interactive TUI that shows live session states at a glance.
It works with your existing tmux workflow. You don't change how you launch or run your agents; ccmux discovers what's already running in your panes, so as long as you're in tmux with a supported agent, it just works.
Built-in support for: Claude Code, Codex, Cursor, OpenCode, Pi, Antigravity, Copilot, Gemini CLI, plus custom agent definitions via config.
โจ Features
- ๐ฏ Live Session States: Every agent tracked as idle, working, or waiting (permission / plan approval / question), flagged the moment one needs you
- ๐งฉ Multi-Agent: Claude Code, Codex, Cursor, OpenCode, Pi, Antigravity, Copilot, Gemini CLI, plus custom agents via config
- ๐ Real-Time: Background daemon streams state changes instantly over SSE, no polling, no refresh
- ๐๏ธ Live Preview: Split-pane view of the selected session's pane content
- โก Act in Place: Tab into the preview to approve, answer, or type, keys go straight to that pane
- ๐ Actionable Notifications: Approve, deny, or reply to a waiting agent straight from the desktop notification
- ๐ Sidebar Mode: Compact always-visible session rail docked beside your working panes
- ๐ Fuzzy Search: Fuzzy-match sessions by project, branch, or path; substring-match any recent prompt, captured pane content, and on-demand live transcripts
- ๐ Session Grouping: Collapsible project groups with reordering and pinning
- ๐ฟ Git & PR Aware: Branch and worktree detection, open PRs with live CI and review status
- ๐ Diff Review: Press d to review a session's working-tree diff with hunk, right in the pane
- ๐ค Background Agents & Subagents: Claude Code background agents get rows too; running subagents show as
agentswith a live list in the preview - ๐ Session Control: Spawn, kill, and restart sessions from the TUI;
ccmux invokefor scripted one-shot agent turns - โจ๏ธ Keyboard-First, Mouse-Friendly: Vim keys and number jumps, plus click-to-switch and right-click context actions
๐ฆ Installation
Prerequisites
- tmux with active sessions running AI coding agents
Homebrew
brew install epilande/tap/ccmux
ccmux setup
From Source
Requires Bun.
git clone https://github.com/epilande/ccmux.git
cd ccmux
bun install
bun link
ccmux setup
ccmux setup installs agent hooks for authoritative session matching. ccmux works without it, but it's recommended; see Session Matching with Hooks. Bare ccmux setup only configures agents whose executable is found on PATH; use ccmux setup --agent <name> to install for a specific agent even if it isn't detected.
๐ Quick Start
- Start your AI coding sessions in tmux panes as usual
- Launch the picker:
ccmux - Navigate with j/k, press Enter to switch to a session
[!TIP] Bind a tmux key so you can pop ccmux open from anywhere (add to
~/.tmux.conf):# Prefix + C-p: open ccmux in a centered popup bind-key C-p display-popup -E -w 80% -h 75% "ccmux" # Or skip the prefix entirely (Alt+p from any pane) bind-key -n M-p display-popup -E -w 80% -h 75% "ccmux"The picker exits after you select a session, so the popup closes itself and drops you straight into that pane. (
display-popuprequires tmux 3.2+.)
๐ฎ Usage
CLI Commands
| Command | Description |
|---|---|
ccmux |
Launch interactive TUI picker (default) |
ccmux picker |
Launch TUI with options (--preview, --icons <style>) |
ccmux picker --persistent |
Dashboard mode (stay open after switching sessions) |
ccmux spawn [agent] |
Spawn a new agent session in a tmux pane |
ccmux invoke [agent] "prompt" |
Run a single agent turn and write the response to stdout (docs) |
ccmux invoke list |
List active and recently-finished invocations (-j for JSON) |
ccmux invoke cancel <id> |
Cancel a running invocation by id (idempotent) |
ccmux invoke result <id> |
Print an invocation's full captured output (subprocess agents only) |
ccmux show |
List all active sessions |
ccmux show --json |
Output sessions as JSON |
ccmux status |
Show daemon and session overview |
ccmux switch <id> |
Switch tmux client to a session's pane |
ccmux review [id] |
Review a session's diff with hunk (defaults to cwd) |
ccmux kill <id> |
Kill a session's process |
ccmux restart <id> |
Kill and resume a session |
ccmux send <id> <text> |
Send text to a session's tmux pane (multiline pastes as one message; --no-enter skips submit) |
ccmux screen [id] |
Capture pane content |
ccmux screen --grep <pattern> |
Search across all session panes |
ccmux dismiss <id> |
Remove a session from tracking |
ccmux daemon start|stop|restart|status |
Manage the background daemon |
ccmux config set <key> <value> |
Set a preference |
ccmux config get <key> |
Get a single preference value |
ccmux config list |
List all preferences |
ccmux config themes |
List built-in themes (marks the active one) |
ccmux setup |
Install hooks for every supported agent found on PATH (Claude + Codex + Cursor + OpenCode + Pi + Antigravity) |
ccmux setup --agent <name> |
Limit install/uninstall/status to specific agent(s); forces install even if not found on PATH |
ccmux setup --status |
Report install state without writing anything |
ccmux setup --uninstall |
Remove hooks (preserves user-owned hook entries) |
ccmux debug |
Diagnose session tracking discrepancies |
ccmux notify [message] |
Send a notification via the configured backend (bare: test message + diagnostics) |
ccmux sidebar |
Launch narrow sidebar TUI (no preview/footer) |
ccmux sidebar --toggle |
Smart toggle: spawn/kill sidebars in every window across all tmux sessions |
The daemon starts automatically the first time you run a ccmux command (picker, show, invoke, etc.). It runs on 127.0.0.1:2269 and provides both a REST API and SSE event stream.
Preview Pane
Press P to split the picker and preview the highlighted session's live pane content side by side. Press Tab to focus the preview and act in place: your keystrokes go straight to that agent's pane, so you can approve a permission, answer a question, or type a follow-up without ever leaving ccmux.
When the session has agents running, an Agents section lists each one with its runtime. Finished agents drop off the list.
https://github.com/user-attachments/assets/7e0d42b3-4e7b-43b8-8d06-72a2d69dd694
Diff Review with Hunk
hunk is a terminal diff reviewer. With hunk on your PATH, press d in the picker to review the selected session's working-tree diff without leaving ccmux: the picker suspends, hunk diff --watch takes over the pane in the session's repository root, and the picker resumes when hunk exits. The same action is available from the right-click context menu. If the working tree has no changes, ccmux reports that instead of opening an empty review.
To send review feedback back to the agent:
- Press c in hunk to annotate a line, then Ctrl+S to save the note.
- Add any other review notes and quit hunk.
- Confirm Send review comments when the picker resumes. ccmux sends all captured notes, including short source snippets, to the agent as one prompt and stays in the picker so you can watch its status.
https://github.com/user-attachments/assets/4b729700-4903-44ff-8f1c-df4bc16b6f67
The offer relies on hunk's session JSON commands (hunk session list / session comment list). With an older hunk the review itself still works; the offer just doesn't appear.
The reviewHandback preference controls what happens when hunk exits:
confirm(default) asks before sending the prompt.autosends and submits the prompt immediately without a dialog.fillpastes the prompt into the agent's composer without submitting it. The text remains there until you jump to the session and submit or edit it; a later send or invoke may find it prepended.
The review also runs from the CLI:
ccmux review # Review the current directory's repository
ccmux review <id> # Review a session's repository by id
Install hunk with brew install hunk. The d footer hint and help entry appear only when hunk is detected on PATH at launch.
Sidebar Mode
A compact, always-visible session list that lives alongside your working panes. No preview panel, no footer, just status icons and project names.
ccmux sidebar --toggle # Toggle sidebars in all tmux windows
ccmux sidebar --toggle --width 40 # Custom width (default: 30)
ccmux sidebar --toggle --position right # Right side (default: left)
ccmux sidebar --resize --width 30 # Snap every existing sidebar pane to <width>
The smart toggle fills gaps when some windows are missing sidebars, and kills all sidebars when every window already has one. New windows automatically get a sidebar, and sidebars snap back to their configured width when a window is resized.
Configure defaults so --toggle uses your preferred layout:
ccmux config set sidebar.width 40
ccmux config set sidebar.position right
Suggested tmux keybinding (add to ~/.tmux.conf):
bind-key S run-shell "ccmux sidebar --toggle"
Notifications
Desktop notifications on waiting/finished transitions, disabled by default. When a session needs permission, or has a plan waiting for approval, the banner carries Approve / Deny buttons; permission, plan, question, and "finished" notifications also carry an inline Reply field, so you can answer, redirect, or send the next instruction without switching to its pane. Focusing a session's pane clears its notification.
| Permission โ Approve / Deny | Question โ inline Reply |
|---|---|
ccmux config set notifications.enabled true
ccmux notify # sends a test notification and prints setup diagnostics
Actionable Approve/Deny buttons work for Claude Code, OpenCode, Codex, Cursor, Gemini CLI, Antigravity, and Copilot; Pi has no tool-approval pause, so it never raises a waiting notification. Inline Reply on waiting-state notifications (permission, plan, question) is Claude Code only; Reply on finished notifications works for every built-in agent. A reply the agent's composer would misread as a command (e.g. leading ! or /) is refused instead of typed, and the text comes back in a follow-up notification so it isn't lost. Approve/Deny work on macOS and Linux; inline reply needs a notification server that advertises it (always on macOS, varies on Linux).
For OpenCode, one server can host several sessions folded into a single row, so when more than one is waiting at once the buttons are withheld (the keystroke could land on the wrong session's dialog) and the notification is delivered informational-only.
macOS: the buttons, ccmux's own name and icon, per-session grouping, and retraction come from a helper app Homebrew installs alongside ccmux, so brew install epilande/tap/ccmux for the full experience. Source installs fall back to osascript (posts as Script Editor, silenced by Focus / Do Not Disturb, no buttons or reply). macOS never shows a permission dialog for a CLI-launched app, so grant it once by hand: run ccmux notify and follow the printed steps (open the settings deep link, find ccmux, enable Allow notifications, set Alert Style to Persistent), then re-run ccmux notify to confirm.
Linux: dbus grouping, click-to-jump, and Approve/Deny are native (no extra binary); inline reply appears only when the server advertises it. A headless daemon (SSH, systemd) needs DBUS_SESSION_BUS_ADDRESS, plus DISPLAY for the notify-send fallback.
Configure further with ccmux config set notifications.<key> <value>, or edit ~/.config/ccmux/ccmux.json directly:
{
"notifications": {
"enabled": true, // default false (opt-in)
"events": ["waiting", "finished"], // default both
"sound": "Glass", // false (default) | true (platform default sound) | macOS sound name
"delayMs": 1000, // debounce for "finished" only; "waiting" always fires immediately
"backend": "auto", // "auto" | "ccmux-notifier" | "osascript" | "notify-send" | "dbus" | "osc" | "command"
"command": "ntfy publish agents \"$CCMUX_TITLE: $CCMUX_BODY\"", // used when backend = "command"
},
}
backend: "auto" picks ccmux-notifier (else osascript) on macOS, and D-Bus (else notify-send) on Linux. command runs your own shell command with CCMUX_* env set (EVENT, SESSION_ID, AGENT, PROJECT, BRANCH, TITLE, SUBTITLE, BODY, PANE), for ntfy, Pushover, and the like. CCMUX_BODY is the complete text (the event line plus any context), so a script reading only it still gets something meaningful; CCMUX_SUBTITLE is the bare event line on its own for structured consumers.
The osc backend delivers notifications through the terminal stream instead of a desktop API, for daemons running on a remote box; see Remote / SSH.
[!NOTE] Approve/Deny only send the mapped keystroke to that session's pane (for Claude, the same key you'd press yourself). Approve on a plan picks "manually approve edits" (edits stay gated), never Claude's auto-accept mode. Reply on a permission or plan notification denies the pending tool/plan and sends your text as the next message (it cancels the prompt first, then types). If the session moved on since the notification fired, the press sends nothing and you get a fresh "state changed" notification instead; dismissing a notification never approves anything.
The keystrokes behind the buttons come from a per-agent notificationActions map, overridable per Custom Agents.
Search Mode
Press / to filter the list as you type. ccmux searches several sources at once and highlights why each row matched:
- Metadata (project, branch, path) matches fuzzily, so
ccmxstill findsccmux. - Recent prompts, captured pane content, and live transcripts match by substring, so a content word matches only where it actually appears.
Prompts come from the daemon's in-memory index, which keeps the most recent prompts per session and is tail-bounded after a daemon restart (only recent prompts are re-read from disk). Transcript search closes that gap: it reads each session's transcript file on demand and covers the full session history, including assistant replies (Claude and Codex).
Three toggles control what gets scanned: searchPaneContent, searchPaneLines, and searchTranscript (see Configuration).
Spawning Sessions
Launch new agent sessions directly from the CLI:
ccmux spawn # Spawn claude (default) in a new tmux window
ccmux spawn codex # Spawn a specific agent
ccmux spawn --split # Split current pane instead of new window
ccmux spawn --detach # Don't switch to the new pane
ccmux spawn --cwd ~/proj # Set working directory
ccmux spawn --resume <id> # Resume an existing session
ccmux spawn --prompt "fix the tests" # Send an initial prompt
Programmatic Invocation
ccmux invoke runs a single agent turn and writes the response to stdout, so you can use real agents in shell pipelines and scripts. See docs/invoke.md for the full reference.
ccmux invoke claude "say hi in one word"
echo "what is 2 + 2" | ccmux invoke claude
git diff main | ccmux invoke claude "Review this diff"
Claude runs interactively in a dedicated tmux session and returns clean text parsed from the transcript JSONL. Codex, Cursor, OpenCode, Pi, Antigravity, Copilot, and Gemini run as non-interactive subprocesses (codex exec -o, cursor-agent --print, opencode run --format json, pi -p, agy -p, copilot -p --allow-all-tools, gemini -p) and return the agent's clean response text.
For orchestration, name an invocation with --id <id>, then use ccmux invoke list, ccmux invoke cancel <id>, and ccmux invoke result <id> to watch, cancel, or read its full captured output by that id. See docs/invoke.md for the fire-and-poll reference.
Dispatch Skill
This repo ships a dispatch Agent Skill that teaches your coding agent to orchestrate other agents through ccmux invoke (firing, fan-out, joining, cancelling, and reading worker output). For Claude Code it installs as a plugin (this repo doubles as a plugin marketplace):
/plugin marketplace add epilande/ccmux
/plugin install ccmux@ccmux
Other skills-capable agents (Codex, Cursor, OpenCode, and others) can use the same skill by copying it into their skills directory. The skill is additive glue for the ccmux CLI, which must be installed and on your PATH. See plugins/ccmux/README.md for details.
โจ๏ธ Keyboard Controls
| Action | Key | Description |
|---|---|---|
| Navigate | j / k or โ / โ | Move through session list |
| Jump to first/last | gg / G | Go to top / bottom |
| Jump to session | 1โ9 | Switch directly to session N |
| Switch to session | Enter | Switch tmux to the selected pane |
| Search | / | Enter fuzzy search mode |
| Toggle preview | P | Show/hide the preview panel |
| Scroll preview | Ctrl+D / Ctrl+U | Half-page scroll in preview |
| Resize preview | Alt+H / Alt+L | Increase/decrease preview width |
| Focus preview | Tab | Send keys directly to tmux pane |
| Restart session | r | Kill and resume the selected session |
| Reconnect | R | Reconnect to the daemon SSE stream |
| Kill session | x | Kill the selected session's process |
| Kill all | X | Kill all tracked sessions |
| Review and hand back | d | Review with hunk, then offer to send notes to the agent (requires hunk on PATH) |
| Collapse/expand | h / l or Space | Toggle group collapsed state |
| Move group | J / K | Reorder group down / up (persisted) |
| Move group top/bottom | < / > | Pin group to top / bottom |
| Collapse/expand all | zM / zR or - / = | Collapse or expand all groups |
| Hide idle | f | Toggle hiding idle sessions |
| Cycle prompt | p | Prompt display: inline โ own row โ off |
| Cycle group-by | b | Cycle through group-by modes |
| Help | ? | Show keyboard shortcuts overlay |
| Quit | q / Esc | Exit the picker |
| Action | Key |
|---|---|
| Navigate results | Ctrl+N / Ctrl+P |
| Select | Enter |
| Cancel | Esc |
When preview is focused (Tab), keystrokes are forwarded to the tmux pane. These keys still work:
| Action | Key |
|---|---|
| Navigate sessions | Ctrl+N / Ctrl+P |
| Resize preview | Alt+H / Alt+L |
| Scroll preview | Ctrl+D / Ctrl+U |
| Exit focus | Tab / Esc |
โ๏ธ Configuration
Preferences are stored in ~/.config/ccmux/ccmux.json and can be managed with:
ccmux config set <key> <value>
ccmux config get <key>
ccmux config list
| Key | Values | Default | Description |
|---|---|---|---|
iconStyle |
dot, emoji, nerdfont, none |
dot |
Status icon style |
theme |
catppuccin-*, tokyo-night*, dracula, gruvbox-*, nord, rose-pine* |
catppuccin-mocha |
TUI color theme (resolved at launch; see Theme) |
showPreview |
true, false |
false |
Show preview panel on launch |
previewWidth |
20โ80 |
40 |
Preview panel width (percentage) |
command |
any non-blank string | claude |
CLI command used for session restart |
groupBy |
project, cwd, session, window, none |
project |
How sessions are grouped in the TUI |
promptDisplay |
inline, row2, off |
inline |
Prompt display: inline on row 1, its own row, or hidden |
backgroundAgents |
true, false |
true |
Show Claude background agents as rows (daemon restart required) |
additionalClaudeConfigDirs |
array of paths | [] |
Additional Claude config dirs to watch (daemon restart required; see Multiple Claude Config Dirs) |
searchPaneContent |
true, false |
true |
Include captured pane content in TUI search |
searchPaneLines |
10โ500 |
100 |
Lines of pane content scanned in TUI search |
searchTranscript |
true, false |
true |
Search live Claude/Codex transcripts (full history + assistant text) via the daemon |
persistent |
true, false |
false |
Keep picker open after switching sessions (dashboard mode) |
reviewHandback |
confirm, auto, fill |
confirm |
After a hunk review, confirm delivery, send immediately, or fill the agent composer without submitting |
sidebar.width |
10โ80 |
30 |
Sidebar pane width in columns |
sidebar.position |
left, right |
left |
Which side of the window to place the sidebar |
For how these search knobs interact, see Search Mode.
๐ Column Configuration
Each session item has up to two rows (row1, row2), and each row has a left and right side. Each side is a comma-separated list of field entries. An entry is either <field> (use the field's default mode) or <field>:<mode> (override the mode).
ccmux config set columns.row1.left "index,status:icon,project"
ccmux config set columns.row1.right "agent:short,pane,time"
ccmux config set columns.row2.left "prompt"
ccmux config set columns.row2.right "branch"
Pass an empty string to clear a side: ccmux config set columns.row2.left "".
| Field | Modes | Default mode | Description |
|---|---|---|---|
index |
โ | โ | Row number (1โ9) |
status |
icon/short/full |
icon |
Status badge style |
project |
dirname/full |
dirname |
Project path (basename or full) |
agent |
short/full |
full |
Agent name (2-char code or full label) |
version |
โ | โ | Agent version |
pane |
โ | โ | Tmux pane target (session:window.pane) |
time |
โ | โ | Relative time since last input |
prompt |
โ | โ | Last user prompt (truncated) |
cwd |
โ | โ | Working directory |
branch |
โ | โ | Git branch |
pr |
short/full |
full |
Open PRs for the branch (#25/PR #25) |
Defaults: row1.left is index, status, project (status badge widens iconโshortโfull as the terminal grows). row1.right cascades by breakpoint: just pane below xs, then agent:short, pane at xs, agent:short, pane, time at sm, and agent:full, version, pane, time at md+. The prompt and pr cells are configured on row2, but promptDisplay (default inline, cycled live by p) controls how they render: inline flattens them onto row1 so each session stays a single line, row2 gives the prompt its own line with pr at the right edge, and off hides both. Sessions with no prompt stay single-line in inline mode; in row2 mode the second line still appears when another row-2 field (such as an open PR) has data.
Sidebar defaults differ to fit the narrow rail: row1 is status, project with pr:short, agent:short on the right (PR stays visible even with the prompt hidden), and row2 is prompt / time (a lone time never earns the row; it rides along when some other field has data). The 30-col rail has no room to inline, so the sidebar always uses the two-row layout (inline behaves like row2). Override these under the sidebar.columns key in ~/.config/ccmux/ccmux.json (e.g. "sidebar": { "columns": { "row2": { "left": ["pane"] } } } to bring the pane target back).
The CLI's comma-separated form sets one mode per entry. To vary the layout by terminal width (responsive cascade), edit ~/.config/ccmux/ccmux.json directly and use the default/xs/sm/md/lg keys on either a row side (whole array) or an entry's mode.
๐ Breakpoints
Named breakpoints control when responsive column layouts activate. A breakpoint value applies from that terminal width upward until a larger breakpoint overrides it.
| Name | Default width |
|---|---|
xs |
40 |
sm |
60 |
md |
80 |
lg |
100 |
ccmux config set breakpoints.sm 55
ccmux config set breakpoints.lg 120
๐จ Theme
The TUI ships 14 built-in palettes across six families, resolved once at launch (no in-TUI toggle).
ccmux config themes # list built-ins, mark the active one
ccmux config set theme tokyo-night # switch theme
| Theme | Background |
|---|---|
catppuccin-mocha |
dark (default) |
catppuccin-macchiato |
dark |
catppuccin-frappe |
dark |
catppuccin-latte |
light |
tokyo-night |
dark |
tokyo-night-storm |
dark |
tokyo-night-day |
light |
dracula |
dark |
gruvbox-dark |
dark |
gruvbox-light |
light |
nord |
dark |
rose-pine |
dark |
rose-pine-moon |
dark |
rose-pine-dawn |
light |
For per-key tweaks, set theme to an object in ~/.config/ccmux/ccmux.json: a built-in base plus colors (the 14 semantic keys) and/or ansi (the 16 terminal colors used to render the preview), deep-merged over the base.
{
"theme": {
"base": "catppuccin-mocha",
"colors": { "red": "#ff5555" },
"ansi": { "brightBlack": "#585b70" }
}
}
An unknown base name falls back to the default theme; an invalid hex value or unknown override key is dropped and the base value is kept. Each emits a warning. Run ccmux config themes to inspect any problems with the current config.
[!NOTE] ccmux paints no background fill, so theme colors sit on your terminal's own background. The light palettes (
catppuccin-latte,tokyo-night-day,gruvbox-light, `r
Comments (0)
Sign in to join the discussion.
No comments yet
Be the first to share your take.