A cross-device tmux session manager for AI coding agents.
Run your coding agents from any device — even your phone.
ccmux is one TUI for every coding-agent session you've got running — Claude Code, Codex, Cursor, and more. If you've used tmux, the mental model is one sentence: ccmux is a tmux session manager built for coding agents. tmux keeps each session alive in the background; ccmux is the single dashboard over all of them — color-coded by what each agent is doing, on every device you own. Start, resume, and supervise them all from your Mac, your laptop, or your phone. No tmux session names to memorize, no claude --resume <hash> to type, no SSH-then-attach gymnastics. It runs over your tailnet and attaches over SSH or Mosh. Setup is one command.
curl -fsSL https://raw.githubusercontent.com/skzv/ccmux/main/scripts/install.sh | sh
ccmux setupDrops ccmux + ccmuxd in ~/.local/bin. ccmux setup finishes first-run setup for Tailscale, agent CLIs, and the background daemon.
Other paths:
-
Homebrew (macOS, or Linuxbrew on Linux):
brew trust --tap skzv/tap brew install skzv/tap/ccmux
Homebrew 6 refuses formulae from third-party taps until you vouch for the tap, so
brew trustcomes first — without it the install stops withRefusing to load formula skzv/tap/ccmux from untrusted tap skzv/tap. Once per machine. (It'sskzv/tap, notskzv/homebrew-tap— Homebrew drops the prefix.) -
From source:
git clone https://github.com/skzv/ccmux.git && cd ccmux && make setup.
Works on macOS, Linux, and Windows via WSL2. Source builds require Go 1.26+.
It's 11pm. The session you started this morning on your Mac is still running — your laptop's lid has been closed for hours, but the daemon held a caffeinate lock and Claude kept thinking.
Your phone vibrates: Claude is asking a question. You tap the notification, you're attached. You answer, hit Ctrl-b d, lock the phone.
Tomorrow morning on the train, you open your laptop, type ccmux — the same session is right there, exactly where you left it. Three device transitions. Zero CLI commands.
That's ccmux. The same session, the same TUI, on every device. No host config, no session-name memory, no claude --resume. Just press Enter on the row you want.
Color-coded by state — active, idle, needs your input. The Devices panel shows every other ccmux-running machine on your tailnet right next to it. The usage panel tallies Claude's 5-hour quota and per-agent prompt counts. Live updates as each session moves through states.
The "start on Mac, continue on phone" loop, end to end:
your Mac — attach with Enter, detach with Ctrl-b d
your phone — same TUI, narrower (Blink, Termius, or the Moshi app)
tmux is the session store; ccmux is the lens. The phone-width layout collapses everything into a single scrollable column when ccmux runs in a narrow terminal. Mosh makes the connection survive cell→wifi roaming, lid-close, walking-out-of-range — so you can detach the phone on the train and reattach two stops later.
Open the Projects tab, press n. Pick which device should host the new project — local, or any reachable peer running ccmuxd on your tailnet. The remote daemon creates the directory natively and starts the agent. ccmux SSH-attaches you in. You never typed ssh, never typed mkdir, never typed tmux new-session.
The new-project form also picks the agent (Claude, Codex, Cursor, and more) for this project — the choice sticks at <project>/.ccmux/agent.
ccmux remembers every past Claude / Codex / Antigravity thread, sorted by recency. Press 2 for the Conversations screen, navigate, press Enter. The daemon resumes the agent with the correct session ID. You don't type claude --resume <hash> ever again.
Headless agent runs (claude -p, codex exec, SDK invocations) are filtered out by default so a scripted workflow doesn't drown the list — press H to toggle them back on.
Pick per project which AI runs it — ccmux works with Claude Code, Codex, Antigravity CLI, Cursor, pi, Grok, and more, speaking each one's launch and resume dialect. The choice is sticky, stored at <project>/.ccmux/agent. The dashboard, daemon state-detection, and dispatch all follow per-project. Press a in the Projects tab to switch the selected project's agent (cycles claude → codex → antigravity → cursor → pi → grok).
Dashboard rows on non-default agents get a small [codex], [antigravity], [cursor], [pi], or [grok] tag so a single glance tells you what's running where.
Per-project Notes tab — every .md file in the project, organized as a collapsible folder tree, with markdown rendered inline by Glamour. Folders open collapsed so a deep notes tree doesn't bury you: →/l expands a folder (or drills in), ←/h collapses it (or jumps out to the parent). Want it all open? Launch with ccmux --expand-notes or set [notes] expand_folders = true. Ripgrep-backed / search. Plain markdown on disk is the source of truth. No sync service. No cloud. Press e to edit a note in $EDITOR; ccmux reads notes, writing them is the agent's job.
Notes follow you across devices: press H to toggle which machine you're viewing notes from — the local box or any reachable ccmuxd peer on your tailnet. The list, preview, and search all re-scope to the selected device (read-only for remote). Scriptable too: ccmux notes list|read|search <project> [--host <name>].
One screen for "is my agent installed and signed in?" Per-agent CLI version, config root, login status, and a command palette to re-run setup or open the config directory.
ccmux setup is the interactive wizard. It checks tmux / mosh / tailscale / claude / gh and offers to brew install whatever's missing. The first time you launch ccmux on an unconfigured machine it offers to run setup for you (decline once and it won't ask again; set CCMUX_NO_SETUP_NUDGE=1 to always skip the prompt). For dotfiles / provisioning, ccmux setup --yes runs non-interactively — it takes the recommended answer at every prompt (install deps, generate the SSH key, install the daemon autostart) and skips the integrations that can't be scripted (Moshi pairing, Tailscale/gh browser auth). ccmux doctor is the non-interactive health check — perfect for scripting or when something stops working.
Dashboard shows a banner when a new release lands. ccmux update git-pulls the checkout, rebuilds, reloads ccmuxd. Flags: --dry-run, --skip-pull, --no-restart. The Devices panel tags any tailnet peer running a behind-version of ccmuxd so you see at a glance which machines need an update.
The way to use ccmux on your phone today is the Moshi app. Moshi holds a Mosh connection to your Mac alive through roaming and lid-close, raises a real push notification when an agent needs you, and shows a live picker of your sessions — tap one and you're attached to ccmux's TUI on your phone.
ccmux moshi-setupInstalls moshi-hook on the Mac, runs Easy Pair (a QR code appears in your terminal — scan it with the Moshi iOS app, done), and wires the Claude Code hooks that turn needs_input events into categorized push notifications (approval_required vs task_complete). Tap the notification, the Moshi app shows your live tmux session list, pick one, attach, answer, detach. No tokens to copy.
Plain BEL fallback works in any iOS terminal client (Blink Shell, Termius) — you lose the categories, that's it. For headless / scripted setups: ccmux moshi-setup --token <token> bypasses the QR flow.
Building your own mobile client?
ccmuxdexposes an HTTP API (list / attach over a WebSocket PTY / send-keys / events / notes / usage, plus a pairing + push flow) — see the HTTP API reference.
# On the always-on Mac Mini:
ccmux daemon install # ccmuxd survives reboot
# edit ~/.config/ccmux/config.toml: listen_tailnet = true
# On the laptop — nothing to do:
ccmux # dashboard already lists the MiniEvery refresh, ccmux runs tailscale status --json, probes each online non-mobile peer for a ccmuxd /v1/health, and merges the responders into the host list. Install ccmux on a new device, start its daemon, and it shows up on every other device on your tailnet within seconds.
The Devices panel on the Dashboard shows every device on your tailnet:
- 🟢 peers running ccmuxd — with their reported version + an "update available" tag whenever they lag this build
- ⚪ peers NOT running ccmuxd (Macs/Linux boxes you haven't installed on yet) — with a one-line "ccmux not installed" hint
- 📱 phones / iPads — with a "connect via Moshi app" hint (the iOS Moshi app is their picker; they don't run ccmux directly)
Attaching to an auto-discovered peer execs ssh -t <host> -- tmux attach -t <name> (cross-platform PATH prepend so Homebrew/Snap/Linuxbrew tmux is found). If you've pinned a host with ccmux host add --mosh <name> …, ccmux uses mosh instead — tolerates roaming and stalls.
Manually pinning a host with
ccmux host addstill works — useful for non-Tailscale hosts, or to force a specific port. Discovered hosts and pinned hosts coexist on the dashboard without duplicates.
~/.config/ccmux/config.toml — the knobs that matter:
[projects]
root = "~/Projects" # where ccmux looks for projects
[daemon]
poll_interval_seconds = 2
idle_seconds_for_needs_input = 3
listen_tailnet = false # set true on your server-mode host
tailnet_port = 7474
[sleep]
mode = "safe" # "safe" | "dangerous" | "very_dangerous"
idle_release_minutes = 10
low_battery_cutoff = 20 # dangerous mode auto-downgrades below this
[notifications]
bell = true # ring local terminal BEL on needs_inputNotifications: the bell always rings on
needs_inputtransitions whenbell = true, regardless of whether moshi-hook is paired. The audible chime at your desk and the push on your phone are complementary, not duplicates. Setbell = falseif you'd rather rely on phone pushes alone.
Sleep-mode notes:
safe—caffeinate -son macOS (Apple's policy keeps it AC-only).systemd-inhibit --what=sleep:idleon Linux.dangerous—caffeinate -d -i -m -son macOS, works on battery too. Daemon polls battery once a minute and downgrades tosafewhen belowlow_battery_cutoff(so a forgotten laptop doesn't run flat).very_dangerous— addssudo -n pmset -a disablesleep 1(macOS) /sudo -n systemctl mask sleep.target …(Linux). Requires passwordless sudo for the specific command; silently degrades todangerousif sudo prompts. Always reverted when ccmuxd exits cleanly.Example sudoers entry (run
sudo visudo -f /etc/sudoers.d/ccmux):# macOS %admin ALL=(ALL) NOPASSWD: /usr/bin/pmset -a disablesleep * # Linux %sudo ALL=(ALL) NOPASSWD: /bin/systemctl mask *, /bin/systemctl unmask *
projects.root and subscription.tier are also editable inline from the Settings screen — ↑/↓ to move, Enter to edit, e to open $EDITOR for the prose-heavy fields. After editing, run ccmux update to reload the daemon with the new config.
Click to expand
- Live dashboard of every agent session across every project, with state (active / idle / needs_input) and per-row agent tags
- One-key attach, kill, rename — applies a styled tmux status bar so you always know where you are
- Per-session "keep awake" pin — the daemon holds a sleep-prevention lock while any pinned or active session is alive
- Three sleep-prevention modes —
safe,dangerous,very_dangerous(sudo-gated; system-wide override that survives lid-close)
ccmux new <name>— creates the directory + starts the agent. No CLAUDE.md, no docs/ tree, no git init. Bootstrapping is the agent's job.- Open a project = see its history. Enter on a project lists running sessions and past conversations.
- Conversations list hides automation noise. Headless
claude -p/ SDK runs andcodex execinvocations are filtered by default. PressHto toggle. Antigravity transcripts carry no headless tag, so those are always shown. - Create on any device. Press
nin Projects, pick a host, the remote daemon does the work. - Every subdirectory of your projects root shows up — no marker file required.
git · CLAUDE · docs/tags tell you which dirs are real software projects vs scratch dirs.
- Per-project agent stored in
<project>/.ccmux/agent— sticky across sessions - New-project form cycles Claude / Codex / Antigravity / Cursor / pi / Grok with
←/→ - Press
ain Projects to switch the selected project's agent - Dashboard rows on non-default agents get a small
[codex],[antigravity],[cursor],[pi], or[grok]tag - Daemon state-detection (active / idle / needs_input) dispatches per agent for correct heuristics
ccmux doctorenumerates installed agents; setup wizard points at the right install command for each- Moshi push integration is currently Claude-only — Codex / Antigravity sessions get the audible terminal bell (still triggers a generic iOS push). Phase-2 work tracked in
docs/01_Specs/02_Multi_Agent.md
ccmux-mcpships alongside ccmux in the same install — a Model Context Protocol server agents can plug into to see and act on every session- Wire it up in
~/.claude/settings.json(or any MCP-aware client):{"mcpServers": {"ccmux": {"command": "ccmux-mcp"}}} - Read-only by default:
list_sessions,read_pane,list_projects,list_conversations,get_usage,list_machines, notes ones, daemon health --allow-mutateexposesspawn_session,send_keys,kill_session— opt-in, hidden fromtools/listuntil the flag is onCCMUX_HOST=mini.tail-xxxxx.ts.net:7474 ccmux-mcppoints it at a tailnet peer's daemon — an agent on the laptop can drive sessions on the Mac mini- Full spec:
docs/01_Specs/04_MCP_Server.md
- Dedicated "Claude" screen for everything in
~/.claude/ - Model picker (Opus 4.7 / Sonnet 4.6 / Haiku 4.5 / opusplan / custom) — global or per-project
- Reasoning-effort picker (
low/medium/high/xhigh/max) +alwaysThinkingEnabledtoggle. WriteseffortLevelto~/.claude/settings.jsonso every new Claude Code session inherits your default. - Browse + create slash-command aliases, manage MCP servers, hooks, permission allowlists
- View & edit global and per-project
CLAUDE.mdfrom the TUI
- Per-project Notes tab — every
.mdfile in the project, as a collapsible folder tree, rendered by Glamour - Folders open collapsed;
→/←(orl/h) expand/collapse them — or open everything withccmux --expand-notes - Ripgrep-backed
/search; plain markdown on disk is the source of truth (no required cloud) - Browse, preview, edit-in-
$EDITOR— ccmux reads your notes; writing them is the agent's job - Cross-device:
Htoggles which device's notes you're viewing (local or any tailnet peer); also viaccmux notes list|read|search --host <name>
- Easy Pair via QR code —
ccmux moshi-setup; scan the QR code with the Moshi app, you're paired. No token paste. - Categorized push notifications via
moshi-hookplugging into Claude Code's hooks system (approval_required, task_complete) - In-app session picker — the Moshi app lists every tmux session on the paired host
- Auto-detection — ccmuxd suppresses its BEL trigger when moshi-hook is paired so you don't get duplicate notifications
- Local — manages tmux sessions on this machine; prevents sleep while sessions are active
- Server — daemon binds an HTTP API to your Tailscale interface for remote ccmux clients
- Mixed — dashboard shows local + remote sessions, color-coded by origin
ccmux setup— interactive wizard, checks every dep, offersbrew installfor missing pieces;--yesruns it non-interactively for scripts, and the first launch on a fresh machine offers to run it for youccmux doctor— non-interactive health check (great for scripting)ccmux update— pulls the git checkout, rebuilds, reloads ccmuxdccmux uninstall— clean removal, never touches your projects or~/.claude/
- Catppuccin Mocha by default; Dracula / Nord / Gruvbox / Tokyo Night planned
?opens contextual key help on every screen- Vim-style (
h/j/k/l) and arrow keys both work - Auto-switches to a curated narrow-terminal layout under 120 cols (phone mode) — reference detail is dropped so the essentials fit a phone screen
- Mouse support on by default (hold Option (⌥) while dragging to bypass tmux mouse reporting in iTerm2 / Terminal.app)
- Cross-device clipboard via OSC 52 — selecting text inside a remote tmux pane lands on your local clipboard. Works in iTerm2, Ghostty, WezTerm, Alacritty, kitty. Terminal.app does NOT support OSC 52 writes.
ccmux doctortells you exactly which side is missing. - No telemetry. Ever.
Six hands-on walkthroughs. Each is self-contained — pick whichever maps to what you're trying to do.
The core loop: create → talk to your agent → take notes.
ccmux new auth-redesign # or: ccmux new auth-redesign --agent codexThat command does two things, and only two things:
- Creates
~/Projects/auth-redesign/— an empty directory. - Opens a tmux session named
c-auth-redesignand starts your agent inside it (Claude by default).
ccmux does not scaffold the project — no CLAUDE.md, no docs/ tree, no git init, no GitHub repo. Bootstrapping is the agent's job, done inside the session: run /init to have the agent write CLAUDE.md, openspec to set up specs, git init whenever you want version control. ccmux opens the door; what the project becomes is up to you and your agent.
To check on the session without joining the conversation: ccmux list. To attach: ccmux attach auth-redesign. The session keeps running after you detach.
ccmuxThe Dashboard shows all sessions, color-coded by state:
- active — the agent is generating output right now.
- idle — finished, no prompt visible.
- needs_input — the agent's input box is up and the pane has been quiet for ≥ 3 seconds. This is the one to watch.
When a session transitions to needs_input, ccmuxd injects a terminal BEL. Any iOS terminal client that maps BEL→notification raises a push.
Useful keys on the Sessions screen:
Enter— attachx— kill the highlighted sessionR— rename?— full keymap
See the Mobile setup section above.
See the Config section above. ~/.config/ccmux/config.toml has the full set of knobs.
See the Multi-machine section above. Heavy users live in this flow: sessions on the Mini, clients on laptop/phone, no host config — auto-discovery handles it.
ccmux doctor # one-shot health check
ccmux update # git pull + rebuild + reinstall + restart daemon
ccmux uninstall # clean removalccmux update auto-detects your git checkout (defaults to ~/Projects/ccmux). Flags: --dry-run, --skip-pull, --no-restart.
ccmux uninstall # interactive: shows what it'll do, asks y/N
ccmux uninstall --yes # skip the prompt
ccmux uninstall --dry-run # preview onlyWhat gets removed:
- Running
ccmuxd(SIGTERM) ~/.local/bin/ccmuxand~/.local/bin/ccmuxd~/.local/state/ccmux/(socket, logs)~/.local/share/ccmux/(snapshots, daemon db)~/.config/ccmux/(unless--keep-config)- The ccmux-styled tmux status bar on every
c-*session (unless--keep-chrome)
What is never touched:
- Your project directories
- Notes under
<project>/docs/ ~/.claude/(Claude Code state + moshi-hook entries)
To also remove moshi-hook: brew services stop moshi-hook && brew uninstall moshi-hook && brew untap rjyo/moshi.
LAPTOP (client + local) MINI (local + server)
┌─────────────────────────────┐ ┌──────────────────────────────┐
│ ccmux TUI │ ──http──► ccmuxd HTTP │
│ ├─ local sessions ◄─unix──┤ tailnet │ ├─ sessions (mini-foo) │
│ │ • laptop-bar │ ────────► │ • mini-foo (active) │
│ │ • laptop-baz │ │ │ • mini-cas (waiting 🔔)│
│ └─ remote: mini │ │ └─ caffeinate -s while active
│ • mini-foo │ │ │
│ │ │ ccmuxd Unix socket │
│ │ │ (for local TUI on mini) │
└─────────────────────────────┘ └──────────────────────────────┘
▲
│ Mosh
│ (when phone connects)
┌────────┴──────────┐
│ iPhone (Moshi / │
│ Blink / Termius) │
└───────────────────┘
Full design: docs/02_Architecture/00_System_Design.md.
HTTP API (for integrators / mobile clients): docs/02_Architecture/05_HTTP_API.md.
TUI design system: docs/02_Architecture/04_TUI_Design_System.md.
Phasing in ROADMAP.md. Headline:
- v0.1 — TUI, sessions, notes, setup wizard, daemon, local + server + mixed modes, terminal-bell notifications, Homebrew tap
- v0.2 — Snapshots, themes, command palette, tailnet web viewer for notes, cost tracking from Claude transcripts
- v0.3 — Multi-select session ops, activity heatmap, daily-journal rollups, mDNS host discovery
- Long term — richer mobile clients on top of the ccmuxd HTTP API — the surface the Moshi app and other clients build on
Infrastructure follow-ups tracked in docs/01_Specs/03_Testing_And_CI.md:
- 🔁 CI integration — GitHub Actions matrix (test + cross-compile + integration) so PRs can't merge with regressions
- 💪 Stress testing —
cmd/ccmux-stress/harness for 20+ session loads, notification storms, 24h long-haul, with pprof + FD-leak detection - 🐛 Terminal crawling —
cmd/ccmux-crawl/monkey-tester + native fuzzers +rapidproperty tests to find the bugs no human would think to try
- Terminal-first, not terminal-only. Must work in a Mosh pane on an iPhone.
- One source of truth: tmux. ccmux is a view; tmux is the database.
- Plain markdown on disk beats vendor lock-in. No required cloud, no required sync.
- Notifications by terminal bell. Reuses what every iOS terminal client already supports.
- Setup is a flow, not a README. First-run wizard installs what it can, instructs for what it can't.
- No telemetry. Ever.
Alpha. Core flows (attach, new, kill, notes, daemon, Moshi) work end-to-end. Expect rough edges; file issues. Wait for v0.1 if you want it for real work.
Standing on the shoulders of Charm:
- Bubble Tea — the TUI framework
- Lipgloss — styling
- Bubbles — list, viewport, textinput, spinner, help
- Huh — forms for the setup wizard
- Glamour — markdown rendering
Plus Cobra for the CLI surface and SQLite for daemon state.
Issues and PRs welcome. See CONTRIBUTING.md (TBD). The short version:
- Code style:
gofmt,go vet,staticcheck - Tests:
make test(unit) andmake test-e2e(integration — requirestmux) - Bug reports: include
ccmux doctoroutput - Feature requests: read
docs/01_Specs/01_Feature_Catalog.mdfirst
FSL-1.1-MIT — Functional Source License with MIT future grant.
In plain English: you can use, modify, and redistribute ccmux freely for any purpose except offering it (or a substantially-similar feature set) as a competing commercial product or managed service. Two years after each release, that version automatically relicenses to plain MIT.
If you want to commercialize a derivative work sooner, get in touch.
The workflow this tool wraps was developed in public by the AI-first software engineering community over 2024–2026. Particular thanks to:
- Charm for the best TUI stack in any language
- The Tailscale and Mosh teams for the connectivity layers
- Anthropic for shipping Claude Code
- The Blink Shell and Moshi maintainers for making mobile terminals actually good








