Skip to content

Latest commit

 

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

Claude Code tmux Orchestration - Parallel AI Sessions with Full Context

Spawn full Claude Code sessions as workers, coordinated through a bash heartbeat and file-based state. Attach to any of them mid-run and take over by hand.

Blog Post Free Guide

claude-tmux-orchestration


Status: read this before you clone

Built March 2026. Reviewed 2026-07-31 against current Claude Code. Still runs, but the reason to use it has changed.

This was built when subagents could not do much. Since then Claude Code absorbed most of it. Per Anthropic's subagent docs, subagent frontmatter now accepts maxTurns, hooks, mcpServers, skills, memory, effort, background and isolation, and each subagent runs in its own context window.

So the original pitch - "subagents are crippled, use full sessions instead" - is no longer true. Try subagents first. They are one file instead of a bash control loop.

Original claim (March 2026) Status today
Subagents cap at ~25 tool calls False. maxTurns is a frontmatter field.
Subagents get no hook events False. hooks is configurable per agent; hook events carry agent_id.
Subagents cannot reach MCP servers False. mcpServers is configurable per agent.
Subagents share the parent context window False. Each runs in its own.

What this still buys you

Three things survive, and only three. If none of them matter, use the native tools.

  • A session you can attach to. tmux attach, watch it work, interrupt it, answer its question by hand, let it continue. No lost context. When a long autonomous run goes sideways at minute 40, taking the wheel is worth the whole control loop.
  • Full session lifecycle. Compaction, /clear, /resume, slash commands, mid-run model switching via send-keys.
  • An orchestration loop you own. Bash and JSON files on disk. Runs on a server, across repos, over SSH, on a schedule. No dependency on a product feature staying put.

Also worth knowing: Agent Teams is the first-party option, still behind CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1, and its split-pane display mode needs tmux or iTerm2 anyway. Dynamic workflows orchestrate subagents at scale from a script, up to 16 concurrent and 1,000 per run.


Architecture Overview

┌─────────────────────────────────────────────────────────┐
│                    tmux Session                         │
│                                                         │
│  ┌──────────────┐  ┌──────────┐  ┌──────────┐          │
│  │ Window 0     │  │ Window 1 │  │ Window 2 │          │
│  │ Orchestrator │  │ Worker 1 │  │ Worker 2 │   ...    │
│  │ (Claude Code)│  │ (Claude) │  │ (Claude) │          │
│  │              │  │ Sonnet   │  │ Haiku    │          │
│  └──────┬───────┘  └────┬─────┘  └────┬─────┘          │
│         │               │              │                │
│         └───────────┬───┴──────────────┘                │
│                     │                                   │
│              _orchestrator/                             │
│              ├── config.json      (rules & intervals)   │
│              ├── session.json     (runtime state)       │
│              ├── log.jsonl        (audit trail)         │
│              ├── workers/                               │
│              │   ├── w1.json      (worker 1 status)     │
│              │   └── w2.json      (worker 2 status)     │
│              ├── results/                               │
│              │   ├── w1/          (worker 1 output)     │
│              │   └── w2/          (worker 2 output)     │
│              └── inbox/                                 │
│                  ├── w1/          (escalations)         │
│                  └── w2/          (escalations)         │
└─────────────────────────────────────────────────────────┘

         ┌──────────────┐
         │ heartbeat.sh │  (external bash loop)
         │              │
         │  sleep → check idle → send /cycle │
         │  adaptive: 30s/120s/300s          │
         └──────────────┘

The 4-Step Monitoring Cycle

Every interval, the orchestrator runs:

COLLECT ──→ EVALUATE ──→ ACT ──→ LOG
  │            │           │        │
  │  Read      │  Map to   │  Send  │  Write
  │  worker    │  6 states │  cmds  │  audit
  │  status    │  + check  │  merge │  entry
  │  files     │  escala-  │  review│  update
  │  + pane    │  tions    │  gate  │  .ready
  │  fallback  │           │        │

Worker States: SAFE_TO_RESTART · DO_NOT_INTERRUPT · CONTEXT_LOW_CONTINUE · RATE_LIMITED_WAIT · ERROR_STATE · UNKNOWN


Quick Start

Prerequisites

Setup

# 1. Clone this repo
git clone https://github.com/primeline-ai/claude-tmux-orchestration.git
cd claude-tmux-orchestration

# 2. Copy files to your project
cp -r _orchestrator/ /path/to/your/project/

# 3. Make scripts executable
chmod +x /path/to/your/project/_orchestrator/*.sh

# 4. Start the orchestrator
cd /path/to/your/project
./_orchestrator/orch-bootstrap.sh

Spawn a Worker

# Set ENV (bootstrap exports these, or set manually)
export SESSION_NAME="orch-your-project"
export ORCH_DIR="$(pwd)/_orchestrator"
export PROJECT_ROOT="$(pwd)"

# Spawn a worker with Sonnet
./_orchestrator/spawn-worker.sh w1 sonnet "Implement the auth module"

# Spawn a second worker with Haiku (for research)
./_orchestrator/spawn-worker.sh w2 haiku "Research OAuth2 best practices"

Monitor & Stop

# Attach to the tmux session
tmux attach -t orch-your-project

# Switch between windows
# Ctrl+B, 0 = Orchestrator | Ctrl+B, 1 = Worker 1 | Ctrl+B, 2 = Worker 2

# Watch the heartbeat log
tail -f _orchestrator/heartbeat.log

# Graceful stop
touch _orchestrator/.stop

How It Works

1. Bootstrap (orch-bootstrap.sh)

The entry point checks dependencies (tmux, jq), creates or reuses a tmux session named orch-<project>, initializes state files, and starts the heartbeat as a background process.

2. Heartbeat (heartbeat.sh)

A bash loop that keeps the orchestrator alive. It:

  • Sleeps for an adaptive interval (30s when stuck, 120s normal, 300s idle)
  • Collects worker statuses from _orchestrator/workers/*.json
  • Checks if the orchestrator pane is idle (via tmux capture-pane)
  • Sends the /orchestrate-cycle command via tmux send-keys when idle
  • Logs every cycle to log.jsonl

The heartbeat uses a .ready file handshake - it only sends commands when the orchestrator pane is actually idle. This prevents prompt collisions.

3. Worker Spawn (spawn-worker.sh)

A 6-step sequence:

Step Action Detail
1 Create tmux window tmux new-window -n w1
2 Start Claude Code With ORCHESTRATOR_WORKER_ID ENV var
3 Wait for boot Poll capture-pane for idle prompt (max 60s)
4 Switch model Send /sonnet or /haiku via send-keys
5 Inject task prompt Multiline via tmux load-buffer + paste-buffer
6 Confirm status Poll for workers/w1.json (max 90s)

4. File-Based Coordination

Workers communicate through files - no custom servers, no daemons, no IPC:

Worker status file (_orchestrator/workers/w1.json):

{
  "worker_id": "w1",
  "status": "working",
  "task": "Implement auth module",
  "model": "sonnet",
  "branch": "orch/feature/w1",
  "started": "2026-03-22T10:00:00Z",
  "updated": "2026-03-22T10:15:00Z",
  "progress": "Created JWT middleware, writing tests",
  "turns": 42,
  "blockers": []
}

Workers can escalate to the orchestrator:

Escalation file (_orchestrator/inbox/w1/escalation.json):

{
  "type": "guidance",
  "question": "Should I use RS256 or HS256 for JWT signing?",
  "context": "RS256 is more secure but requires key management",
  "blocking": true
}

Key Technical Details

tmux send-keys: Getting It Right

The entire architecture depends on reliably delivering prompts to Claude Code via tmux. Three things matter:

# 1. Use send-keys -l (literal mode) + separate Enter
tmux send-keys -t "$SESSION:w1" -l "$PROMPT_TEXT"
sleep 0.5
tmux send-keys -t "$SESSION:w1" Enter

# 2. Strip ANSI before parsing capture-pane output
OUTPUT=$(tmux capture-pane -t "$SESSION:w1" -p | strip_ansi)

# 3. For multiline prompts, use load-buffer + paste-buffer
echo "$PROMPT" | tmux load-buffer -b "buf-w1" -
tmux paste-buffer -p -d -b "buf-w1" -t "$SESSION:w1"
tmux send-keys -t "$SESSION:w1" Enter

Why literal mode? Without -l, tmux interprets special characters. A prompt containing # or ! would break.

Why separate Enter? Combining the message and Enter in one send-keys call can cause race conditions with Claude Code's input buffer.

Why paste-buffer for multiline? send-keys breaks on newlines. load-buffer loads the full text into a tmux buffer, and paste-buffer injects it cleanly.

ANSI Stripping

Claude Code's terminal output contains color codes, cursor movements, and OSC sequences. All regex matching (idle detection, send verification) must strip these first:

strip_ansi() {
  sed -E \
    -e 's/\x1b\[[0-9;:?<=>]*[a-zA-Z]//g' \
    -e 's/\x1b\][^\x07\x1b]*(\x07|\x1b\\)//g' \
    -e 's/\x1bP[^\x1b]*(\x1b\\|$)//g' \
    -e 's/\x1b[()][0-9A-Za-z]//g' \
    -e 's/[\x0e\x0f]//g'
}

Idle Detection

The heartbeat checks if Claude Code is ready for input by examining the last 12 lines of terminal output:

# Spinner = busy (check first, overrides idle patterns)
grep -qE '(Running|thinking|Searching|Reading|Writing|Editing)'

# Idle patterns
grep -qE '(❯[\s ]*$|>\s*$|waiting for input)'

Security Note: --dangerously-skip-permissions

Workers are started with --dangerously-skip-permissions so they can operate without manual approval for every tool call. This is required for unattended operation but means workers can read, write, and execute anything in your project directory without confirmation.

Mitigations:

  • Workers run in your project directory only (scoped via cd $PROJECT_ROOT)
  • The ORCHESTRATOR_WORKER_ID ENV var lets hooks restrict worker behavior
  • Workers are instructed not to push code or end their own sessions
  • The orchestrator's review gate catches issues before code reaches main

Do not run this on production systems or with access to sensitive credentials. Use on development machines with version-controlled code.

Adaptive Intervals

The heartbeat adjusts its polling frequency based on worker state:

State Interval When
Stuck 30s Worker hasn't updated in 3+ normal intervals
Normal 120s Active workers, regular updates
Idle 300s No workers or all done

Rate Limit Watchdog

When running multiple workers, you will hit API rate limits. Without the watchdog, a rate-limited session stops and waits for user input. Worse: if you paste the error back, Claude interprets it as "the command is broken" and uses a workaround instead of retrying.

The watchdog (rate-limit-watchdog.sh) solves this:

  1. Monitors all tmux panes every 15s for rate limit patterns (429, Rate limit, overloaded)
  2. Waits 65s for the rate limit to pass (API limits are typically 60s)
  3. Detects which command failed (e.g. /commit, /whats-next) from the pane output
  4. Sends an explicit retry message: "This was a TEMPORARY rate limit, not a bug. Run the exact same command again - no workaround."
  5. Backs off exponentially after repeated failures (up to 10 min cap)

The watchdog starts automatically with orch-bootstrap.sh and stops on the .stop signal.

# Manual control
./_orchestrator/rate-limit-watchdog.sh --status   # Check if running
./_orchestrator/rate-limit-watchdog.sh --stop     # Kill it
tail -f _orchestrator/watchdog.log                # Watch live

# Disable in config.json
{ "watchdog": { "enabled": false } }

Why the retry message matters: A simple "continue" causes Claude to skip the failed step or find creative alternatives. The watchdog's message explicitly says "NOT a bug, NO workaround, retry the EXACT command" - this is the key difference that makes it work.


Configuration

See config.json for all settings:

Key Default Purpose
intervals.stuck_seconds 30 Fast polling when workers are stuck
intervals.normal_seconds 120 Standard monitoring interval
intervals.idle_seconds 300 Slow polling, no active workers
thresholds.max_workers 6 Maximum concurrent workers
thresholds.turns_for_tmux 25 When to suggest tmux over sub-agents
rules.enforce_code_review_after_turns 30 Auto-remind for code review
watchdog.enabled true Auto-start watchdog with bootstrap
watchdog.check_interval 15 How often to scan panes (seconds)
watchdog.cooldown_seconds 65 Wait after rate limit before retry
watchdog.max_retries 5 Consecutive retries before backoff
watchdog.backoff_multiplier 2 Exponential backoff factor

Scaling: How Many Workers?

A worker is a full session, not a lightweight process. Each one loads the system prompt, your CLAUDE.md, every hook and the tool definitions of every MCP server before it does any work, and it pays that once per worker.

Workers Practical Use
1 Long-running single task
2 Implementation + research in parallel
3 Multi-phase project execution
4+ Test rate limits on your plan first

Recommended: start with 2 and measure your own overhead with /context. The number depends on how much you have loaded, not on a figure in a README. Your plan's rate limits bind as well.


Differences from Other Approaches

Approach Where it stops tmux Orchestration
Subagents Not attachable mid-run. Everything else (turns, hooks, MCP, own context, worktree isolation) is now configurable. Start here. Attachable session you can take over
Agent Teams Experimental, behind CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1; split-pane needs tmux or iTerm2 Not gated by a feature flag
Dynamic workflows Deterministic fan-out, but the agents are still subagents Full interactive sessions
--worktree Git isolation, which composes with either approach Git + session + attachability
Manual tabs No monitoring, no coordination, no rules Automated heartbeat + file state

Credits & References

  • Architecture: absmartly/Tmux-Orchestrator - send-keys literal mode, verify-retry pattern, hub-spoke coordination
  • Idle Detection: Dicklesworthstone/ntm - 12-line capture window, spinner override, paste-buffer for multiline, ANSI strip, Double-Enter Protocol

Learn More

This Gist is a self-contained starter kit. For the full system with adaptive delegation, hook matrix, review gates, and inter-worker communication:

Part of the PrimeLine Ecosystem

Tool What It Does Deep Dive
Evolving Lite Self-improving Claude Code plugin - memory, delegation, self-correction Blog
Kairn Persistent knowledge graph with context routing for AI Blog
tmux Orchestration Parallel Claude Code sessions with heartbeat monitoring Blog
UPF 3-stage planning with adversarial hardening Blog
Quantum Lens 7 cognitive lenses for multi-perspective analysis Blog
PrimeLine Skills 5 production-grade workflow skills for Claude Code Blog
Starter System Lightweight session memory and handoffs Blog

@PrimeLineAI · primeline.cc · Free Guide


License

MIT - use it, modify it, build on it. Attribution appreciated.

About

Claude Code tmux orchestration — spawn full AI sessions as parallel workers with heartbeat monitoring and file-based coordination

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

39 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages