Skip to content

Latest commit

 

History

19 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

multiclaude

cca — spawn parallel Claude Code agents, each in its own Terminal window, each with its own Chrome session, in about a second.

The problem

Running several Claude Code agents on one repo at the same time is awkward:

  • Subagents can't have their own Chrome session. Everything spawned inside a single Claude Code session shares one browser connection, so two subagents can't drive the browser independently — they fight over the same tabs. Anything involving a logged-in app, a dev server, or visual verification serializes.
  • Git worktrees solve isolation, but spawning several is slow. A fresh worktree means a fresh checkout, a fresh npm install, a fresh build. Doing that four or five times to fan out costs minutes before any agent starts working.
  • Subagents aren't interactive. They report back at the end. You can't watch one, interrupt it, or steer it mid-task.

What cca does

One command per agent. Each agent gets:

  • Its own full copy of the repo, made with APFS copy-on-write (cp -Rc). Near-instant and near-zero disk, because unchanged files share blocks with the original.
  • node_modules symlinked back to the original repo, so there's no reinstall — the single most expensive part of a worktree. It's skipped during the copy rather than copied and deleted afterwards: clonefile is O(1) in bytes but O(n) in files, so cloning an 86k-file node_modules and unlinking it again burns ~26s per agent even though almost no data moves.
  • Its own Terminal window running interactive claude --chrome, so it gets its own browser session and you can watch, interrupt, and steer it like a normal Claude Code session.
  • A fixed slot name (agent-1agent-8), set as the window title so you can tell the windows apart, with a lock file so a new cca invocation never lands on a slot that's already busy. The slot is claimed before the window opens, so you can fire all eight spawns at once and each still gets its own directory.
  • Its own background colour, if you have termcolor installed. The hue comes from the slot number, evenly spaced around the wheel, so eight windows are eight visibly different colours — and slot 3 is the same colour every time you use it. No termcolor, no colour, and nothing else changes.

Slot names are not configurable, and that's deliberate. Claude Code asks "do you trust the files in this folder?" once per directory and remembers the answer. Spawning into a fresh path would mean answering that prompt again on every single spawn. Reusing the same eight directories forever means you approve each one once, ever — so cca takes a prompt, optional flags for claude, and no name.

When an agent finishes: --non-interactive

Interactive Claude never exits by itself. It finishes the task, says so, and sits at its prompt — so its window keeps its slot until you close it by hand. When you are there, that is the point: the windows are visible precisely so you can answer an agent that has a real question, a blocker, or something worth reading. At three in the morning it means eight windows that stopped working hours ago hold every slot, and the ninth spawn fails.

So the handover is one flag:

cca "audit the API routes for missing auth checks" --non-interactive --dangerously-skip-permissions

--non-interactive belongs to cca and never reaches claude. It says nobody is going to read this window, which is the one thing cca cannot work out for itself. Two things follow.

The session ends when the agent stops talking. A Stop hook marks each time it does, and a watchdog ends the session if nobody replies within two minutes. Reply and the agent talks again, the marker moves, and the clock restarts — only a session nobody picked up gets ended. CCA_GRACE sets the wait. The hook is passed on the command line, so it belongs to that one session: it is never written into the copy's settings and cannot follow the work into a commit.

The window closes when the agent finishes. Claude exits, the window says it is done, waits 15 seconds, and closes itself. The slot is free. Press any key during the countdown to keep the window — it drops into your shell inside the agent's copy, so you can inspect the diff, run tests, or commit from there. CCA_CLOSE_DELAY sets the wait.

It closes whether or not the copy still holds work. Earlier versions kept the slot over uncommitted changes or unpushed commits, which meant an unattended run ended with eight open windows and no free slots — the opposite of what the flag is for.

The practical consequence: tell an unattended agent to push a branch. The next agent to take the slot deletes the directory, so anything left behind and unpushed is gone.

Pair the flag with --dangerously-skip-permissions. Without it the agent can still stop at a permission prompt, and a stopped agent is not a finished one — the Stop hook never fires and the watchdog waits forever. cca prints a warning if you pass one without the other.

Without --non-interactive, nothing changes: the window stays, holds its slot, and waits for you.

A window that closes itself needs no permission dialog. Terminal refuses to close a window that still has processes on its tty — it asks "terminate running processes?" instead — so the close is handed to a small helper that leaves the tty session and fires once the window's shell is gone.

Usage

cd ~/code/my-repo

cca "fix the failing auth tests"

It takes the next free slot automatically. There is no name argument.

Run it several times to fan out:

cca "port the dashboard to the new table component"
cca "write e2e tests for checkout"
cca "audit the API routes for missing auth checks"

Three Terminal windows, three isolated copies, three independent Chrome sessions.

Anything after the prompt is passed straight to claude — except --non-interactive, which cca keeps for itself — so you can tune autonomy and model per agent:

cca "run the e2e suite and fix what breaks" --dangerously-skip-permissions
cca "screenshot every page in the nav" --model haiku
cca "port the last three routes and push a branch" --non-interactive --dangerously-skip-permissions

--dangerously-skip-permissions stops the agent asking permission, so the window runs unattended (remember the copy inherits your real .env). --model picks a cheaper or stronger model for that agent; omit it to use your default. --non-interactive makes the agent give its window and slot back when it is done — see When an agent finishes.

Fire them back-to-back or in parallel (cca "..." &) — claiming a slot takes a millisecond and happens before the window opens, so spawns never collide.

Copies live in ~/.claude/agents/agent-N/. The generated launcher scripts live in ~/.claude/launchers/, and each slot's lock is ~/.claude/agents/agent-N.lock.

Install

curl -fsSL https://raw.githubusercontent.com/janwilmake/multiclaude/main/install.sh | sh

That drops cca on your PATH and installs the bundled Claude Code skill. Restart Claude Code afterwards so the skill is picked up, then check it's there:

cca
# usage: cca "prompt" [claude flags...]

Re-run the same line any time to update. It prints exactly what it wrote, and warns if the install directory isn't on your PATH or if claude isn't installed yet.

cca goes in ~/.local/bin or ~/bin — whichever is already on your PATH, defaulting to ~/.local/bin. The skill goes in ~/.claude/skills/multiclaude/. Override with environment variables:

# install somewhere specific, from a branch, without the skill
curl -fsSL https://raw.githubusercontent.com/janwilmake/multiclaude/main/install.sh \
  | CCA_BIN_DIR=/usr/local/bin CCA_REF=some-branch CCA_SKILL=0 sh

(The variables go on sh, not on curl — a VAR=x curl ... | sh would set them for the download instead of the installer.)

Or clone and symlink

If you'd rather track the repo — edits to your clone take effect immediately, and install.sh won't overwrite a symlinked skill:

git clone https://github.com/janwilmake/multiclaude.git
ln -s "$PWD/multiclaude/cca" ~/bin/cca
ln -s "$PWD/multiclaude/.claude/skills/multiclaude" ~/.claude/skills/multiclaude

What the skill does

.claude/skills/multiclaude/SKILL.md tells Claude Code to reach for cca instead of its own subagents whenever the fanned-out work involves the browser. The installer puts it in ~/.claude/skills/ so it applies everywhere; drop a copy in a single project's .claude/skills/ instead to scope it there.

Requirements

  • macOS. It uses open -a Terminal to spawn windows, and cp -Rc (APFS clonefile) for the fast copy. On a non-APFS volume the script falls back to a plain cp -R, which works but is slow.
  • Claude Code on your PATH, with the --chrome flag available (Claude in Chrome extension set up).

Caveats

  • The copy is a plain directory copy, not a git worktree. Each agent has the full .git from the moment of the copy, so committing and branching work fine — but the agents don't share a git dir, and you have to merge the work back yourself (git -C ~/.claude/agents/agent-1 diff, or push a branch from inside the copy).
  • node_modules is a symlink to the original repo. Agents share dependencies, so an agent running npm install mutates your main checkout's node_modules. That's the trade-off that makes spawning fast.
  • The script skips build output when copying (build/, .react-router/, coverage/) — edit that case line for your project's artifacts.
  • .env files are copied along with everything else, since it's a raw directory copy. Agents get your local credentials; keep that in mind before pointing one at production.
  • Claude Code's Bash sandbox denies writes under ~/.claude/, so a sandboxed cca call can't claim a slot. Run it with the sandbox disabled (the bundled skill tells Claude to do this). cca fails immediately with the underlying error rather than retrying, so it's obvious when this is what happened.
  • Slots cap at 8. When all eight are busy cca exits with all 8 slots busy rather than spilling into a ninth directory. CCA_SLOTS=12 cca "..." raises the cap; each new slot costs one folder-trust prompt, once, the first time an agent lands in it.
  • The lock is released when the agent's Terminal window closes (normal exit or SIGHUP). A SIGKILL'd window leaves ~/.claude/agents/agent-N.lock behind, but it holds the window's pid, so the next cca sees the process is gone and reclaims the slot — no manual cleanup.
  • A freed slot gets deleted. The next agent to take it wipes the directory and clones the repo again. A --non-interactive window closes itself even when the copy holds uncommitted changes, unpushed commits, or a scratch file the agent wrote outside git — so tell an unattended agent to push, or press a key during its countdown to keep the window.

License

MIT

About

Spawn parallel Claude Code agents on macOS — each in its own Terminal window, its own repo copy, and its own Chrome session, in about a second.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages