Claudemux, a new open-source tool from developer wastedcode, solves a gnarly problem that's been rattling around the Claude Code ecosystem: how do you reliably drive multiple real-login Claude sessions from code? The answer lives in tmux, and it's surprisingly clean.
What Claudemux Actually Does
If you've tried automating claude CLI calls with child_process.spawn(), you know the pain. Ad-hoc ANSI regex to detect readiness, sleep() hacks that break on every update, silent stalls on trust dialogs—the glue code rots faster than your tests. Claudemux retires that layer entirely. It boots a real Claude Code REPL inside tmux, waits until the agent is genuinely ready (not after some arbitrary timeout), and exposes structured primitives: create(), send(), wait(), ask(). Each session runs in an attachable tmux pane—watch the agent work or take the keyboard yourself.
The Fleet Is the Point
Sessions are named and independent, which means you can boot a coordinated fleet from one process. Want three Claude sessions tackling "api", "ui", and "docs" services simultaneously? Promise.all() that setup and fan tasks across them. Another process can adopt any session by name—the daemon-spawns / workers-drive split is built in. Names outlive your process, so a crashed daemon can resume or adopt running panes without losing context.
Reliability Without Screen Scraping
Here's where it gets interesting: wait() doesn't poll the TUI. It fuses signals from Claude's own hooks plus transcript analysis to determine when a turn has reached a terminal outcome—completed, awaiting-a-decision, aborted, or out of your patience budget. The result is deterministic and fast, not guesswork based on pane text. Budget-exceeded doesn't mean failed—it means your timeout hit while work was still in flight. Re-sending blindly into a live turn queues or duplicates side effects, which claudemux explicitly warns against.
Crash Recovery Is First-Class
Mid-turn box crash? No problem. resume() continues the same conversation in a fresh tmux pane with history intact—just persist the agentSessionId. recover() is the one-liner that tries adopt (pane survived) then falls back to resume (it crashed), returning status so you know which path fired. For forks, extraArgs: ["--resume", id, "--fork-session"] branches a new conversation off an existing one's history.
Delivery Guarantees Worth Knowing
send() returns a real cursor when confirmed—delivered into the agent's transcript. But if Claude was busy, your message gets queued (claude shows "Press up to edit queued messages"). Claudemux distinguishes DELIVERED_QUEUED from DELIVERY_UNCONFIRMED for exactly this reason: re-sending a queued message double-runs work, which is worse than a lost message. The library already handles Enter-retry if the paste landed but submit didn't register.
What This Is NOT
Claudemux drives the consumer-login claude CLI on a box you control—not API-key-based automation in CI or hosted services. That's what Anthropic's Agent SDK is for. Consumer login can't run in ephemeral environments (no interactive auth), and it's against their terms anyway. Claudemux makes the on-a-box, real-login case reliable.
Key Takeaways
- Each session runs in tmux—attach with tmux attach to watch or intervene mid-turn
- wait() returns a TurnOutcome discriminated union; branch on kind, never catch timeout exceptions
- Budget-exceeded ≠ failed; poll progress() before blindly re-sending into a live turn
- Workspace trust is fail-closed by default and persistent per folder—use --trust-workspace deliberately
- Fleet concurrency is yours to throttle; claudemux won't serialize create() calls for you
The Bottom Line
Claudemux is the infrastructure layer the Claude Code ecosystem needed but didn't have. If you're building orchestrators that coordinate multiple agent sessions—or just tired of brittle spawn+regex+sleep hacks—this is worth a serious look. It's MIT licensed, requires Node ≥20, and installs via npm install @wastedcode/claudemux.