How the sausage gets made.
- Observable - Everything happens in tmux. Watch it. Poke it. Intervene if you want.
- Isolated - Each agent gets its own git worktree. No stepping on toes.
- Recoverable - State lives on disk. Daemon crashes? It comes back.
- Safe - Agents can't weaken CI or bypass humans. That's the deal.
- Simple - Files for state. tmux for visibility. git for isolation. No magic.
┌─────────────────────────────────────────────────────────────────┐
│ CLI (cmd/multiclaude) │
└────────────────────────────────┬────────────────────────────────┘
│ Unix Socket
┌────────────────────────────────▼────────────────────────────────┐
│ Daemon (internal/daemon) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Health │ │ Message │ │ Wake/ │ │ Socket │ │
│ │ Check │ │ Router │ │ Nudge │ │ Server │ │
│ │ (2min) │ │ (2min) │ │ (2min) │ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└────────────────────────────────┬────────────────────────────────┘
│
┌────────────────────────────┼────────────────────────────────┐
│ │ │
┌───▼───┐ ┌───────────┐ ┌─────▼─────┐ ┌──────────┐ ┌────────┐
│super- │ │merge- │ │workspace │ │worker-N │ │review │
│visor │ │queue │ │ │ │ │ │ │
└───────┘ └───────────┘ └───────────┘ └──────────┘ └────────┘
│ │ │ │ │
└───────────┴──────────────┴──────────────┴─────────────┘
tmux session: mc-<repo> (one window per agent)
| Package | What It Does |
|---|---|
cmd/multiclaude |
Entry point. The main() lives here. |
internal/cli |
All the CLI commands. It's a big file. |
internal/daemon |
The brain. Runs the loops, manages everything. |
internal/state |
Persistence. state.json lives and breathes here. |
internal/messages |
How agents talk to each other. |
internal/prompts |
Embedded system prompts for agents. |
internal/worktree |
Git worktree wrangling. |
internal/socket |
Unix socket IPC between CLI and daemon. |
internal/errors |
Nice error messages for humans. |
internal/names |
Generates worker names (adjective-animal style). |
pkg/tmux |
Public library - programmatic tmux control. |
pkg/claude |
Public library - launch and talk to Claude Code. |
- CLI parses your command → sends request over Unix socket
- Daemon handles it → updates
state.json→ pokes tmux - Agents run in tmux windows with their prompts and slash commands
- Messages flow through JSON files, daemon routes them
- Health checks run every 2 min, clean up the dead, resurrect the fallen
~/.multiclaude/
├── daemon.pid # Is the daemon alive?
├── daemon.sock # CLI talks to daemon here
├── daemon.log # What the daemon is thinking
├── state.json # The source of truth
├── repos/<repo>/ # Cloned repos
│ └── agents/ # Local agent customizations
├── wts/<repo>/ # Git worktrees (one per agent)
├── messages/<repo>/ # Agent DMs
└── claude-config/<repo>/<agent>/ # Slash commands per agent
Check .multiclaude/agents/ in your repo to share custom agents with your team. Those take priority over local ones.
Everything the daemon knows lives in ~/.multiclaude/state.json:
{
"repos": {
"my-repo": {
"github_url": "https://github.com/owner/repo",
"tmux_session": "mc-my-repo",
"agents": {
"supervisor": {
"type": "supervisor",
"worktree_path": "/path/to/repo",
"tmux_window": "supervisor"
},
"clever-fox": {
"type": "worker",
"task": "Implement auth feature",
"ready_for_cleanup": false
}
}
}
}
}Writes are atomic: temp file → rename. No corruption.
The daemon doesn't give up easily. Every 2 minutes it:
- Checks if tmux sessions exist
- If something died, tries to bring it back
- Only gives up if restoration fails
- Cleans up anything marked for cleanup
- Prunes orphaned worktrees and message directories
Kill tmux accidentally? Daemon will notice and rebuild.
Agents can get stuck. The daemon pokes them every 2 minutes:
| Agent | Nudge |
|---|---|
| supervisor | "Status check: Review worker progress and check merge queue." |
| merge-queue | "Status check: Review open PRs and check CI status." |
| worker | "Status check: Update on your progress?" |
| workspace | Never nudged - that's your space |
Want to use our building blocks? Go for it.
go get github.com/dlorenc/multiclaude/pkg/tmuxProgrammatic tmux control with multiline support. Send complex input atomically. Capture output. Monitor processes.
client := tmux.NewClient()
client.SendKeysLiteral("session", "window", "multi\nline\ntext")
pid, _ := client.GetPanePID("session", "window")go get github.com/dlorenc/multiclaude/pkg/claudeLaunch and interact with Claude Code instances.
runner := claude.NewRunner(
claude.WithTerminal(tmuxClient),
claude.WithBinaryPath(claude.ResolveBinaryPath()),
)
runner.Start("session", "window", claude.Config{
SystemPromptFile: "/path/to/prompt.md",
})
runner.SendMessage("session", "window", "Hello, Claude!")