tmux is the best agent orchestrator.

I covered workspaces — giving each agent a clean room with a task file. The next question: how do you watch what the agents are doing? Run five at once? Intervene when one is stuck?

tmux. It works the same way for me and for agents — I can attach and use a session directly, or my root agent can send-keys and capture-pane programmatically. Same tool, same interface. Everything is text, simple tools composed together.

Each agent gets a persistent tmux session. Attach, detach, monitor, kill — independently.

# start an agent
tmux new-session -d -s agents "ai 'do the thing'"

# check on it
tmux capture-pane -t agents -p -S -50

# talk to it
tmux send-keys -t agents "actually, do this instead" Enter

# kill it
tmux kill-session -t agents

the pattern #

One root agent orchestrates workspace agents:

graph TD
    Root[root agent<br/>tmux: agents] -->|create-workspace| WS1[ws-webapp<br/>builds UI]
    Root -->|create-workspace| WS2[ws-dbfix<br/>fixes bugs]
    Root -->|create-workspace| WS3[ws-docs<br/>writes docs]
    Root -->|capture-pane| WS1
    Root -->|capture-pane| WS2
    Root -->|capture-pane| WS3
    WS1 --> PR1[PR #81]
    WS2 --> PR2[PR #19]
    WS3 --> PR3[PR #100]

The root agent writes a task file, creates a workspace, monitors with capture-pane, sends guidance with send-keys if something’s off, and merges the PR when CI passes.

workspaces #

A workspace is just a directory — fresh repo clone, a branch, and a TASK.md:

workspaces/
  webapp/
    zorto/          # fresh clone on branch cody/ws-webapp
    TASK.md         # what the agent should do

create-workspace clones the repo, branches, writes the TASK.md, and starts a tmux session running ai 'execute on @TASK.md':

create-workspace --task tasks/webapp.md \
  --repo dkdc-io/zorto "webapp"

delete-workspace kills the session and nukes the directory:

delete-workspace webapp

monitoring #

The key command:

tmux capture-pane -t ws-webapp -p -S -500

Dumps the last 500 lines of the agent’s terminal. The root agent reads this to check progress without attaching.

Real example — checking if a workspace agent is done:

$ tmux capture-pane -t ws-zortowebapp -p -S -10
  Stats: +1286 lines, -100 lines, 49/49 tests passing,
  cargo fmt --check clean.

That’s it. Merge the PR.

sending guidance #

When an agent gets stuck:

tmux send-keys -t ws-webapp \
  "the template bug is in onboarding.rs — skip custom templates when a theme is selected" Enter

The agent reads it as user input and adjusts. Context is preserved, the session keeps going.

parallel execution #

tmux sessions are independent — run as many as your machine can handle:

create-workspace --task tasks/webapp.md --repo dkdc-io/zorto "webapp"
create-workspace --task tasks/dbfix.md --repo dkdc-io/db "dbfix"
create-workspace --task tasks/docs.md --repo dkdc-io/zorto "docs"

Three agents, parallel. The root agent watches all of them:

for ws in webapp dbfix docs; do
  echo "=== $ws ==="
  tmux capture-pane -t ws-$ws -p -S -5
done

aliases to bin scripts #

I used to have all of this as shell aliases and functions. ai, workspace helpers — all in .bash_aliases. Then I started running agents and realized: agents can’t see your aliases. They’re shell state, not filesystem state.

So I moved everything to ~/bin/ scripts on $PATH. Now the agent can run create-workspace the same way I do:

# before: alias in .bash_aliases (only I can use it)
function ai() {
  claude --dangerously-skip-permissions --permission-mode dontAsk "$@"
}

# after: bin script on $PATH (I can use it, agents can use it)
#!/usr/bin/env bash
exec claude --dangerously-skip-permissions --permission-mode dontAsk "$@"

Same scripts, same $PATH, humans and agents alike. One thing stays as an alias on purpose — but that’s a story for the next post.

naming #

  • agents for the root agent session
  • ws-<name> for workspace agents
  • iAgent for the iMessage agent
  • Workspace names: lowercase alphanumeric only
  • One agent per workspace, one task per workspace
  • Over-clone repos — cloning is cheap, missing context is expensive