agentleFS
huangzehaohuangzehao/skills/tmux-multi-agent/SKILL.md

tmux-multi-agent

Skill1 starsChanged 3 months agoMITView on GitHub

Drive and coordinate other interactive agent CLIs (Codex, Claude Code, aider, gemini, a plain shell, etc.) running in separate tmux panes — send them prompts/instructions, read their output, and hand work back and forth. Use this whenever you need to delegate to or talk with another agent or process living in a tmux pane, run a multi-agent workflow over tmux, "send this to the other agent / the codex pane / the right pane", relay a review or a handoff between agents, or whenever a message you sent with `tmux send-keys` to an agent CLI does not seem to have submitted. Covers pane discovery, robust message delivery (including the paste-detection double-Enter quirk), reading a pane's state, and bidirectional ping-back.

The file

We split this skill into the rules it gives an agent

6 rules. About 0 are common, 3 are distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Never assume a send-keys worked.

    line 33
  2. Distinctive:

    Look at the target before sending so you know what state it's in:

    line 60
  3. Distinctive:

    If your message genuinely needs apostrophes, backticks, or is long/structured,

    line 98
  4. Very specific:

    When you're handing off real work, prefer having the other agent record results in files/commits you can inspect directly (git show, Read) …

    line 163
  5. Distinctive:

    When you delegate, tell the other agent your pane id and ask it to ping you when done — and remind it of the double-Enter, or its reply to …

    line 179
  6. Very specific:

    If you're *expecting* a ping and it never comes, don't just wait — capture-pane your own pane and the other agent's pane: a common cause is …

    line 187

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Coordinating Multiple Agents over tmux

You are one agent in a tmux session; one or more other agents (or processes) run in sibling panes. This skill is how you talk to them reliably: find them, send them work, read what they did, and have them ping you back.

The whole thing is just tmux send-keys and tmux capture-pane — but a few non-obvious failure modes will silently eat your messages if you don't account for them. The rules below exist because each one burned a real handoff.

The loop

detect panes  →  inspect target state  →  send message  →  verify it submitted  →  read result
                                                ↑___________ (it didn't) _________|

Never assume a send-keys worked. Always capture-pane afterward and confirm the message actually left the input box. A swallowed message sits unsent and invisible — you'll wait forever for a reply that was never triggered.

1. Detect panes — every session, never hardcode

Pane IDs (%0, %1, …) are stable within a session but change between sessions. Re-discover them at the start of every coordination task; don't trust an ID you remember from before.

tmux list-panes -a -F '#{pane_id} #{session_name}:#{window_index}.#{pane_index} #{pane_current_command} title=#{pane_title}'

Identify the target by its pane_current_command (codex, claude, node, python…) and/or pane_title, then refer to it by pane_id (e.g. %1) for the rest of the task — pane_id is stable for the session and unambiguous, whereas index-based targets (0.1) can shift if panes are added/closed.

Know your own pane id too (you'll hand it to the other agent so it can reply):

tmux display-message -p '#{pane_id}'

2. Inspect before you send

Look at the target before sending so you know what state it's in:

tmux capture-pane -p -t %1            # full visible pane
tmux capture-pane -p -t %1 | grep -vE '^\s*$' | tail -12   # last non-blank lines
  • Idle (agent CLI showing an empty input box / prompt) → safe to send and submit; it will start working.
  • Working (e.g. Codex shows • Working (… esc to interrupt)) → you can still send; the message gets queued and picked up after the current turn. This is the normal way to "leave a note" for a busy agent.

3. Send a message — robustly

Lead every message with who you are. The receiving agent gets your text dropped into the same input box a human types into — there is no built-in sender header. Start each message with your identity and pane id, e.g. [From Claude, pane %0] …. Without it the other agent can't tell your message apart from its own user's, doesn't know who it's collaborating with, and can't address its reply or know which pane to ping back. (When you ask it to ping you, that "from + pane" line is also exactly what tells it where to send the reply.)

Beyond what you say, two delivery hazards: the shell mangling your text, and the agent CLI's input box swallowing your Enter.

3a. Don't let the shell rewrite your message

Build the message in a single-quoted variable so the shell doesn't expand $, backticks, &&, etc. Keep single quotes and backticks out of the message text itself (they break the quoting). Send it with -l (literal) so words like Enter are sent as text, not interpreted as the Enter key:

MSG='Hello other agent — please run the test suite and report back.'
tmux send-keys -t %1 -l "$MSG"

If your message genuinely needs apostrophes, backticks, or is long/structured, don't fight the quoting — put it in a file and send a short pointer instead (see §3c).

3b. Submit it — and expect to send Enter twice

Rich agent input boxes (Codex, Claude Code) run paste detection: a long or pasted-in message gets collapsed into a [Pasted Content N chars] blob, and the first Enter is consumed by that collapse instead of submitting. The fix is a second Enter after a short beat:

tmux send-keys -t %1 Enter
sleep 1
tmux send-keys -t %1 Enter

A plain shell pane (bash/zsh) submits on the first Enter — the double-Enter is specifically for agent CLIs with a fancy input box. When in doubt, send the second Enter; an extra Enter at an empty prompt is harmless.

3c. Long or structured briefs → send a file pointer

For anything beyond a couple of sentences, write the brief to a file (or, if it belongs in the project's durable record, a committed spec/doc) and tell the other agent to read and act on it. This sidesteps all quoting/paste issues and gives the brief a stable home:

cat > /tmp/handoff.md <<'EOF'
# Task for you
... full instructions, code blocks, anything ...
EOF
tmux send-keys -t %1 -l '[From Claude, pane %0] Read /tmp/handoff.md — it is your task brief. Execute it, then ping me in pane %0.'
tmux send-keys -t %1 Enter; sleep 1; tmux send-keys -t %1 Enter

3d. Verify it submitted

sleep 2
tmux capture-pane -p -t %1 | grep -vE '^\s*$' | tail -8

You're looking for the message to have left the input box — either the agent is now Working, or (if it was busy) your text shows as a queued entry above the working indicator while the input box returns to its empty placeholder. If your full message is still sitting in the input box, the Enter didn't take — send Enter again.

4. Read what the other agent did

capture-pane is your window into the other agent. Snapshot it to see progress, results, or its "done" message:

tmux capture-pane -p -t %1 | grep -vE '^\s*$' | tail -25

For long output, capture the scrollback with -S:

tmux capture-pane -p -t %1 -S -200       # last 200 lines of history

When you're handing off real work, prefer having the other agent record results in files/commits you can inspect directly (git show, Read) over scraping its pane — the pane is for state and short status, not the source of truth.

Notice quota/usage fields while you're reading the pane. Some agent CLIs surface account state in their pane or status line — e.g. a remaining-quota / weekly-limit percentage, a credits/balance figure, a rate-limit warning. If you spot one (especially trending low), don't act on it silently: surface it to the user at a natural moment ("the codex pane shows ~12% weekly quota left — want me to pace the handoffs?"). It's their account to manage, not yours; your job is to flag it so they can decide. Read such a figure as whatever it's labelled — "remaining/left/available" is a balance, not consumption — and quote the label rather than guessing.

5. Bidirectional ping-back

When you delegate, tell the other agent your pane id and ask it to ping you when done — and remind it of the double-Enter, or its reply to you will be swallowed exactly the way yours would be:

"When you're done, ping me in pane %0. Note: my input box swallows the first Enter after a pasted message, so send your text, then Enter, wait ~1s, then Enter again, and capture-pane %0 to confirm it submitted."

If you're expecting a ping and it never comes, don't just wait — capture-pane your own pane and the other agent's pane: a common cause is the other agent's ping sitting unsubmitted in your input box.

Gotchas — quick reference

SymptomCauseFix
Reply never arrivesFirst Enter swallowed by paste detectionSend Enter twice (text → Enter → sleep 1 → Enter)
Recipient treats your message as its human user's / replies to the wrong placeNo sender attributionPrefix every message with [From <you>, pane <id>]
Message text got corrupted/expandedShell interpreted $, backticks, &&Single-quote the var; keep ' and backticks out; or use a file
send-keys "Enter" typed as the word EnterMissing -l semantics / mixed argsSend text with -l, send Enter as its own send-keys call
Targeting the wrong paneHardcoded a stale %N from last sessionRe-run list-panes each session; target by pane_id
Agent didn't reactIt was working; message queuedExpected — it picks up queued input after its turn; verify via capture-pane
Can't tell if it submittedDidn't verifyAlways capture-pane after sending

Worked example (full round-trip)

# 1. find the other agent and my own pane
tmux list-panes -a -F '#{pane_id} #{pane_current_command} #{pane_title}'
#   %0 claude ...   %1 codex ...
MINE=%0; THEM=%1

# 2. check it's ready
tmux capture-pane -p -t $THEM | tail -5

# 3. send a short instruction
MSG='[From Claude, pane %0] Run npm test and tell me pass/fail. Ping me in pane %0 when done (send Enter twice).'
tmux send-keys -t $THEM -l "$MSG"
tmux send-keys -t $THEM Enter; sleep 1; tmux send-keys -t $THEM Enter

# 4. confirm it took
sleep 2; tmux capture-pane -p -t $THEM | grep -vE '^\s*$' | tail -6   # expect "Working"

# 5. later: read the result
tmux capture-pane -p -t $THEM | grep -vE '^\s*$' | tail -25

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.