DEV Community

Cover image for Run Claude across all your repos from one session — without merging anyone's .claude/
mk668a
mk668a

Posted on

Run Claude across all your repos from one session — without merging anyone's .claude/

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.
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 deny permission silently blocks every other repo.
  • Two repos that both set API_URL collide 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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 }
]
Enter fullscreen mode Exit fullscreen mode

"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
}
Enter fullscreen mode Exit fullscreen mode

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 dispatch is a fresh, single-shot claude -p run, 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
Enter fullscreen mode Exit fullscreen mode

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)