Run Claude across all your repos from one session
claude-muster is an orchestrator for Claude Code: it leaves every repo's .claude/ exactly where it is and runs that repo's own Claude inside it, while the Claude at your root decides who gets the work.
Here's the whole thing in one session:
$ cd ~/work
$ claude-muster repos
3 repos you can dispatch to:
webapp
api
mobile
$ claude-muster dispatch api "fix the failing test in handler.ts"
[api] ok
Found it: handler.ts called the old two-arg `parse()`.
Updated the call and the test passes.
That child ran claude inside api/ — with api's real working directory, environment, skills, agents, hooks, and permissions, exactly as if you'd opened Claude there yourself. Nothing was copied. Nothing was merged. There's nothing to drift or clean up.
The gap it fills
Say your work lives in one folder full of separate git repos, each carrying its own .claude/:
~/work/
├── webapp/ → .claude/skills/deploy, .claude/commands/release
├── api/ → .claude/skills/lint, .claude/agents/db-reviewer, .claude/hooks/pre-commit
└── mobile/ → .claude/commands/build
Open Claude Code inside api/ and you get all of api's tooling. Good. But open it in ~/work/ to work across all three at once and that tooling vanishes — because Claude Code reads .claude/ from the current folder and the folders above it, never the folders below.
The obvious fix is to drag everything up: copy or symlink each repo's .claude/ into ~/work/.claude/. That works for skills, and then quietly breaks the rest:
- A hook written to run inside
api/now fires from~/work/with the wrong working directory. - One repo's
denypermission silently blocks every other repo. - Two repos that both set
API_URLcollide into one value. - Agents have to be copied, so they drift out of date the moment the source changes.
You end up babysitting a merged .claude/ instead of working.
claude-muster takes the opposite approach. Instead of pulling every repo's tooling up into one session, it leaves each .claude/ in place and runs that repo's own Claude inside that repo. The Claude at your root becomes a dispatcher: it decides which repo a task belongs to, hands it off, and reads back the result.
| merge it all up | claude-muster | |
|---|---|---|
| Hook working directory | wrong (runs from root) | correct (the repo's own folder) |
Permissions / deny rules |
cross-fire across repos | scoped to each repo's session |
| Agents | copied, drift out of date | read live from the repo |
| Environment | collides on shared keys | isolated per child process |
| Cleanup | merged settings, symlinks, manifest | one skill; uninstall removes it |
Teach your root Claude to do it on its own
Calling dispatch by hand is the manual gear. Install the routing skill and your root Claude learns to delegate without being told which repo to hit:
$ claude-muster install # adds a small skill to ~/work/.claude/
$ claude
> fix the failing test in api and tell me what web's build command is
(Claude dispatches to api, dispatches to web, and reports both back)
Want your root Claude to know its repos the instant a session starts, instead of waiting for the skill to kick in? Add --hook:
$ claude-muster install --hook # also registers a SessionStart hook
Now every session here opens with a one-line briefing of which repos it can dispatch to. That hook is the only thing claude-muster writes to your settings.json, and uninstall takes it back out exactly — deleting the file if nothing else is left in it.
One repo, or fan out to all
dispatch <repo> "<task>" sends one self-contained task to one repo. Write it as you would to a fresh Claude opened in that repo, because that's exactly what it is — the child starts with no memory of your root conversation.
dispatch --all "<task>" sends the same task to every repo in parallel and collects the results. It's built for surveys and sweeps:
$ claude-muster dispatch --all --json "what's your test command?"
[
{ "repo": "webapp", "ok": true, "text": "npm test", "code": 0 },
{ "repo": "api", "ok": true, "text": "pytest -q", "code": 0 },
{ "repo": "mobile", "ok": true, "text": "flutter test", "code": 0 }
]
"is there a TODO about auth anywhere?", "bump the version to 2.0" — same shape. Pair it with --json when you want to aggregate the answers yourself.
By default every sibling repo with a .claude/ is included. Drop a claude-muster.json in the root to narrow it down:
{
"include": ["webapp", "api/*", "services/**"], // globs, relative to root
"exclude": ["legacy-*"],
"depth": 2, // how deep to look (default: 1)
"paths": ["../shared-tools", "/abs/path/to/repo"] // extra repos anywhere on this machine
}
How it works
Claude Code reads each repo's .claude/ from the repo's own folder and the folders above it. claude-muster never fights that — it just starts claude with the child repo as the working directory:
| Step | What happens |
|---|---|
| discover | Walk the root for sibling directories that contain a .claude/ (respecting claude-muster.json). |
| decide | The Claude at your root (or you on the command line) picks which repo a task belongs to. |
| dispatch | Run claude -p "<task>" --output-format json with cwd set to that repo. |
| collect | Parse the child's final result and hand it back to the orchestrator. |
Because the child is a real claude process rooted in its own repo, every problem the merge-it-all approach creates simply doesn't arise: the working directory is right, hooks and permissions stay scoped, agents are never stale, environments don't collide, and there's nothing to clean up.
Why it's safe to rely on
The most important line in the README is this one: claude-muster never calls an LLM itself. dispatch launches your local claude CLI, which runs on your own Anthropic auth and your own wallet, in Claude Code's documented headless mode (claude -p). No API keys of its own, no network, no telemetry. claude-muster only decides where to send work and collects what comes back.
It also barely touches disk. dispatch and repos only read your folders to discover repos. The one thing it ever writes is the routing skill from install (plus the optional SessionStart hook), and uninstall removes exactly that.
What it doesn't do yet
I'd rather you hit these knowingly:
-
Persistent child sessions. Each
dispatchis a fresh, single-shotclaude -prun, so a child doesn't remember the previous task you sent it. Warm, long-lived sessions per repo are a planned follow-up. -
Repos on other machines. Anywhere on your local filesystem works (see
paths), but remote or networked repos don't. - Guessing when it's ambiguous. If a task could belong to several repos, the routing skill is written to ask rather than guess.
One practical note: dispatch --all starts several claude processes at once, which can hit Anthropic's rate limits if you fan out widely. Keep concurrency reasonable.
Install
Not on npm yet — for now, clone and build:
git clone https://github.com/mk668a/claude-muster
cd claude-muster
npm install && npm run build
npm link # makes `claude-muster` available everywhere
cd ~/work
claude-muster repos
Node 18+. You also need the claude CLI on your PATH — that's what dispatch runs. Prefer not to npm link? Call the built file directly: node /path/to/claude-muster/dist/cli.js.
Repo: https://github.com/mk668a/claude-muster · MIT.
The pitch fits in a sentence: stop merging everyone's .claude/ into one session and let each repo's Claude do its own work, in its own folder, with its own tooling intact. If you run it across a workspace where the merge approach was biting you, I'd love to hear which of the five collisions it quietly fixed first.
Top comments (0)