tmux is the multi-agent AI orchestration layer you already have

tmux is the multi-agent AI orchestration layer you already have

Everyone’s looking for the right “AI agent framework.” The orchestration layer that coordinates multiple specialized agents, manages state, handles persistence, routes work.

I’ve been running a multi-agent Claude Code setup for a while now. The orchestration layer? tmux. And tmuxinator for declarative config.

A 40-year-old terminal multiplexer. No dependencies to install, no API to learn. It just happens to be perfect for this.

Here’s why, and how.


The problem

When you run multiple Claude Code instances, a lead agent, a coding specialist, a research agent, a writing agent, you end up with a bunch of disconnected terminal sessions. Each agent is isolated. They can’t see what the others are doing. If your editor crashes, you lose the conversation. If you want to tell all agents “hey, re-read the shared config,” you’re copy-pasting into four different windows.

Editor-embedded agents (like Claude in a Zed or VS Code terminal panel) are even worse off. They’re completely off the grid from your terminal agents. No way to broadcast to them. No way to discover them programmatically.

This is a coordination problem. And tmux solves it natively.


tmuxinator: declarative agent topology

First, the startup story. tmuxinator lets you define your entire agent layout in YAML. Which agents, what working directories, what permission levels, how the panes are split.

A simplified version:

name: agents
root: ~/workspace

windows:
  - lead:
      layout: main-vertical
      options:
        main-pane-width: 60%
      panes:
        - claude --dangerously-skip-permissions
        - zsh

  - researcher:
      root: ~/workspace/research
      panes:
        - claude --dangerously-skip-permissions

  - coder:
      root: ~/workspace/dev
      layout: main-vertical
      options:
        main-pane-width: 60%
      panes:
        - claude
        - zsh

  - writer:
      layout: main-vertical
      options:
        main-pane-width: 60%
      panes:
        - claude --append-system-prompt ~/.claude/contexts/writer.md
        - zsh

One command, tmuxinator start agents, boots your entire squad. Named windows mean named agents. The lead agent gets full permissions. The coder runs standard permissions so it asks before acting. The writer gets a custom system prompt for its specialization.

Each agent can have its own working directory. The researcher is scoped to a research folder. The coder lives in your dev directory. They’re all in the same tmux session but they each have their own context.

I use 60/40 splits to give each agent a companion shell pane, so I can run commands, tail logs, or check git status without interrupting the conversation.

Nothing here is Claude Code-specific, by the way. Replace claude in those pane commands with Aider, Codex CLI, or any terminal-based AI agent and everything works the same.


Two coordination layers

Agents need to stay in sync. I use two layers for this:

Layer 1: async (shared files on disk)

All agents read from and write to shared markdown files. Session state, memory, observations. When one agent makes a decision or learns something, it writes to a shared state file. Other agents pick it up on their next sync.

This is the “eventually consistent” layer. Each agent has a sync command (a Claude Code slash command) that re-reads shared state files. No custom tooling, just markdown files and a /sync command in each agent’s skill set.

Layer 2: synchronous (tmux send-keys)

When you need every agent to sync right now, tmux send-keys lets you broadcast commands to any pane in any session:

tmux send-keys -t agents:1.1 '/sync' Enter

This sends the literal keystrokes /sync followed by Enter to window 1, pane 1 of the agents session. The agent receives it as if you typed it.

Want to sync every agent at once? Loop through all your agent panes:

for target in "agents:1.1" "agents:2.1" "agents:3.1" "agents:4.1"; do
  tmux send-keys -t "$target" '/sync' Enter
  sleep 0.5
  tmux send-keys -t "$target" Enter
done

Notice the sleep 0.5 and the second Enter. That’s an important gotcha.


The double-Enter trick

Claude Code’s TUI has autocomplete for slash commands. When you type /sync and hit Enter, the first Enter selects the autocomplete suggestion. The TUI then needs about 500ms to process it before a second Enter actually submits the command.

If you just send one Enter, the slash command gets selected but never submitted. Your agent sits there with the command in the input field, waiting.

The fix:

tmux send-keys -t agents:1.1 '/sync' Enter && sleep 0.5 && tmux send-keys -t agents:1.1 Enter

First Enter selects from autocomplete. Half-second pause. Second Enter submits. This took me an hour to debug and two seconds to fix once I understood it.


Editor agents: the separate session pattern

This one took me a while to figure out. If you run Claude Code inside your editor’s terminal panel (Zed, VS Code, etc.), you want that agent to also be in tmux, but NOT in the same session as your other agents.

Why? Because tmux sessions have a concept of the “active window.” When you attach a terminal to a tmux session, switching windows in one client can affect other clients (depending on your config). If your editor panel is attached to the same session as your terminal, navigating windows in your terminal could pull the editor panel to a different window.

The fix: create a separate tmuxinator config for your editor agent.

name: editor-agent
root: ~/workspace

windows:
  - editor:
      panes:
        - claude --dangerously-skip-permissions

Your editor’s terminal panel runs tmux attach -t editor-agent. Separate session. Never gets pulled away by window switches in your main session.

But you can still broadcast to it with send-keys:

tmux send-keys -t editor-agent:1.1 '/sync' Enter && sleep 0.5 && tmux send-keys -t editor-agent:1.1 Enter

Same coordination, full isolation. The editor is just a viewport into a tmux-backed agent.


Persistence is the real sell

This is the part that converted me. tmux sessions persist independently of any terminal or editor attached to them.

Close Zed? The agent conversation is still running in its tmux session. Reopen Zed, reattach, it’s all there. Terminal crashes? tmux attach -t agents and you’re back. Want to check on an agent from your iPad over SSH? Same command.

Editor-embedded agents without tmux backing die when you close the panel. With tmux, the agent lives in the session. The editor is just a viewport. Detach, reattach, switch editors, and the conversation is always there.


Controlling your agents from the couch

Since tmux doesn’t care what’s on the other end of the connection, you can control your agents from anywhere. I use Tailscale for networking and Terminus on my iPad. tmux attach -t agents from the couch, and I’m looking at my full squad. Switch windows, check on any agent, send commands. I’ve reviewed agent output from the coffee shop and sent follow-up instructions without the agent knowing I switched devices. It just sees keystrokes.

If you can SSH into your dev machine, you get mobile access for free. tmux attach works the same remotely as it does locally.


Agent discovery with list-panes

tmux gives you programmatic visibility into what’s running:

tmux list-panes -a -F '#{session_name}:#{window_index}.#{pane_index} #{window_name} #{pane_current_command}'

Output looks like:

agents:1.1 lead claude
agents:1.2 lead zsh
agents:2.1 researcher claude
agents:3.1 coder claude
agents:3.2 coder zsh
editor-agent:1.1 editor claude

Every agent, which session it’s in, what command it’s running. Pipe this into a script and you have automatic agent discovery. Only send sync commands to panes actually running Claude.

You can even capture what an agent is currently showing:

tmux capture-pane -t agents:1.1 -p

That dumps the visible contents of the pane. Crude, but functional for checking on agents without switching windows.


“Just use a real framework”

I can hear it already: “This is a hack. Use CrewAI. Use LangGraph. Use AutoGen. Use a proper agent orchestration framework.”

Those frameworks solve a different problem. They orchestrate LLM API calls. They manage chains of prompts, tool schemas, structured outputs, and agent-to-agent message passing in code. They’re libraries for building agent applications.

That’s not what I’m doing. I’m running multiple instances of Claude Code, a coding agent that already has file access, bash execution, tool use, and its own conversation context. Each instance is a complete agent. I don’t need a framework to manage prompt chains or tool schemas. Claude Code already does that.

What I need is process coordination. Starting multiple agents with the right configs. Sending them commands. Keeping them in sync. Making sure they survive crashes. Discovering which ones are running.

That’s process management. Unix has been solving process management for decades.

The frameworks also introduce real costs: dependency trees, breaking changes across versions, vendor lock-in, and abstraction layers between you and the model. tmux has been stable since 2007, and it’s probably already installed on your system. tmuxinator is a gem install away. Neither is going to break when someone pivots to a new paradigm next month.

If you’re building an agent product, a customer-facing system where agents coordinate autonomously, yes, use a framework. That’s what they’re for.

But if you’re a developer running multiple Claude Code instances as your daily workflow, a multiplexer is all you need.


What this gets you

The lead agent coordinates the whole squad: sending sync commands, reading shared state files to track what everyone is working on, capturing pane output to check progress.

Specialized agents share state through files but stay focused on their domain. The coder doesn’t get distracted by research context. The writer doesn’t see code diffs.

Agent conversations survive editor crashes, terminal closes, SSH disconnects. The tmux session is the source of truth, not whatever GUI happens to be displaying it.

And the entire topology is version-controllable YAML. Change your agent layout, commit it, tmuxinator start and you’re running the new config.


The punchline

tmux has named, addressable processes. Inter-process communication via send-keys. Session persistence through detach/reattach. Declarative configuration through tmuxinator. Programmatic introspection via list-panes and capture-pane. Multi-client access from anywhere.

Forty-year-old Unix primitives that solve a 2026 problem. That’s not a hack. That’s leveraging good engineering.

>_

For your agent

Everything above was written for you. This section is written for your AI coding agent.

Copy everything below into your agent’s context and ask it to set up a multi-agent tmux environment for your workflow. The checklists, configs, and commands are structured so an agent can parse and implement them directly. If you’re reading this on X, grab the raw markdown version [here] so your agent gets clean code blocks instead of screenshots.

Prerequisites

  • tmux installed (tmux -V to check; brew install tmux or apt install tmux if missing)
  • Ruby installed (tmuxinator dependency)
  • tmuxinator installed (gem install tmuxinator)
  • A CLI-based AI agent installed (Claude Code, Aider, Codex CLI, etc.)
  • A project directory to use as the root for your agents

Already doing this?

  • Already have tmux installed? Skip to creating the tmuxinator config.
  • Already use tmuxinator? Create a new config for your agent layout, skip install steps.
  • Already running multiple agents in tmux manually? Add tmuxinator for declarative startup and the broadcast script for coordination.
  • Already have shared state files between agents? Focus on the send-keys broadcast layer.

Implementation checklist

  1. Install prerequisites (tmux, Ruby, tmuxinator)
  2. Create main agent config at ~/.tmuxinator/agents.yml (see artifacts below)
  3. Define one window per agent role (lead, coder, researcher, writer, or whatever fits your workflow)
  4. Set per-agent working directories using the root: key in each window
  5. Set permission levels per agent (e.g., --dangerously-skip-permissions for trusted agents, standard for agents that should ask before acting)
  6. Add companion shell panes where useful (60/40 split via main-vertical layout)
  7. If using an editor-embedded agent (Zed, VS Code), create a separate config at ~/.tmuxinator/editor-agent.yml
  8. Create shared state files for agent coordination (e.g., shared/session-state.md, shared/memory.md)
  9. Create a sync command or slash command for each agent that re-reads the shared state files
  10. Create a broadcast script to send the sync command to all agent panes at once
  11. Run tmuxinator start agents and verify all agents boot
  12. Test sending a command to a specific agent via tmux send-keys
  13. If using an editor agent, attach your editor’s terminal panel to its session and verify isolation
  14. Test persistence: detach from a session, then reattach and confirm the conversation is intact

Key artifacts

Main agent config (~/.tmuxinator/agents.yml):

name: agents
root: ~/workspace

windows:
  - lead:
      layout: main-vertical
      options:
        main-pane-width: 60%
      panes:
        - claude --dangerously-skip-permissions
        - zsh

  - researcher:
      root: ~/workspace/research
      panes:
        - claude --dangerously-skip-permissions

  - coder:
      root: ~/workspace/dev
      layout: main-vertical
      options:
        main-pane-width: 60%
      panes:
        - claude
        - zsh

  - writer:
      layout: main-vertical
      options:
        main-pane-width: 60%
      panes:
        - claude --append-system-prompt ~/.claude/contexts/writer.md
        - zsh

Editor agent config (~/.tmuxinator/editor-agent.yml):

name: editor-agent
root: ~/workspace

windows:
  - editor:
      panes:
        - claude --dangerously-skip-permissions

Send a command to a specific agent:

tmux send-keys -t agents:1.1 'your command here' Enter

Send a slash command (requires double-Enter):

tmux send-keys -t agents:1.1 '/sync' Enter && sleep 0.5 && tmux send-keys -t agents:1.1 Enter

Broadcast sync to all agents:

for target in "agents:1.1" "agents:2.1" "agents:3.1" "agents:4.1" "editor-agent:1.1"; do
  tmux send-keys -t "$target" '/sync' Enter && sleep 0.5 && tmux send-keys -t "$target" Enter
done

List all running agents:

tmux list-panes -a -F '#{session_name}:#{window_index}.#{pane_index} #{window_name} #{pane_current_command}'

Capture an agent’s visible output:

tmux capture-pane -t agents:1.1 -p

Attach to sessions:

tmux attach -t agents          # main agent session
tmux attach -t editor-agent    # editor agent session

Concepts to remember

  • Double-Enter for slash commands. Claude Code’s TUI autocomplete requires Enter (select), then a 500ms pause, then Enter again (submit). A single Enter selects from autocomplete but does not submit.
  • Separate sessions for editor agents. Editor terminal panels and main terminal agents should live in different tmux sessions. If they share a session, switching windows in one client can pull the other client to a different window.
  • Layer 1 coordination (async). Agents share state through markdown files on disk. Each agent has a sync command that re-reads shared files to pick up changes from other agents.
  • Layer 2 coordination (sync). tmux send-keys broadcasts commands to agent panes in real-time. Use this when all agents need to sync immediately.
  • The editor is a viewport. The agent lives in the tmux session, not the editor. Closing the editor does not kill the agent. Reopen the editor and reattach to resume.
  • Agent discovery. tmux list-panes -a with format strings shows which panes are running which commands. Use this to build dynamic broadcast scripts that only target panes running your AI agent.
  • Provider-agnostic. Replace claude in pane commands with any CLI-based AI agent. The tmux layer doesn’t care what’s running inside the pane.

Pitfalls

  • Single Enter on slash commands will select the autocomplete suggestion but not submit it. The agent will sit with the command in the input field, waiting. Always use the double-Enter pattern with a 500ms delay between.
  • Editor agent in the same session as terminal agents causes window-focus conflicts. Use a separate tmux session for editor-embedded agents.
  • Stale context after config changes. When you update shared state files or system prompts, broadcast a sync to all agents. Otherwise they continue working with outdated context.
  • Pane index assumptions. tmux pane indices depend on your base-index config (default is 0 for panes, but some configs set it to 1). Verify with tmux list-panes before writing broadcast scripts.
  • Spaces in tmuxinator window names can cause issues. Use hyphens or underscores instead.