A
Claude Code logoClaude CodeLarge monorepos (Nx / Turborepo)

Claude Code Monorepo Orchestra

Kai Renner@platform_kai
90.0Overall score

A dependency-mapper subagent keeps edits inside the right package, and the affected-only build/test hook avoids re-running the whole monorepo on every change. Designed for sprawling workspaces where blast radius matters most.

90.0Score
506Votes
5Components
2hUpdated

Install this build

Export
terminal
npx setuproll add claude-code-monorepo-orchestra

Components

Model

  • Claude Opus 4.8

MCP servers

  • filesystem
  • github
  • postgres
  • sentry

Subagents

  • dependency-mapper
  • package-owner
  • integration-tester
  • reviewer

Hooks

  • PreToolUse: scope-to-package
  • PostToolUse: affected build+test
  • Stop: cross-package diff

Rules

  • Respect package boundaries
  • Only rebuild affected projects
  • No circular dependencies
Setup

I let Claude loose in a 60-package monorepo. Here is the leash.

How I scope Claude Code to one package, rebuild only what changed, and review across boundaries before anything merges.

platform_kai9 min read2026-06-20

The first time I handed Claude Code a ticket in our monorepo, it did the right thing in completely the wrong place. The task was a tiny fix to a date helper in one package. Claude found a similar helper three packages over, decided that one was nicer, and started refactoring both to share a util. Reasonable instinct for a greenfield repo. In a workspace where 40 people push and the build graph has opinions, that is how you turn a 3 line PR into a cross-team incident.

So I stopped trying to make the model smarter and started making the environment narrower. The build I am sharing here, Monorepo Orchestra, is less a clever prompt and more a set of guardrails: keep edits inside one package, rebuild only what the change actually touches, and never let a diff cross a boundary without someone looking at the seam. It runs on Claude Opus 4.8 because the planning step is where most of the value is, and Opus is the one I trust to read the dependency graph and not panic.

The rules that matter

My CLAUDE.md is short on purpose. Long memory files get skimmed, same as long onboarding docs. I keep it to the three things that actually cause damage when they go wrong.

CLAUDE.md
# Workspace rules (Nx monorepo, ~60 packages)

## Package boundaries
- Respect package boundaries. Code lives in exactly one package.
- Import only across boundaries declared in the dependency graph.
- No circular dependencies. If a fix "wants" a cycle, stop and ask.

## Builds and tests
- Only rebuild affected projects. Never run a full `nx run-many`.
- Use `nx affected` against the merge base for build/test/lint.

## When in doubt
- Map the blast radius FIRST (run the dependency-mapper agent).
- A change that touches 2+ packages is a design decision, not a chore.
  Surface it. Do not silently refactor a shared util.
Boundaries are the whole game
Nx already encodes who can import whom. The model does not have to guess that. Half my CLAUDE.md is just pointing Claude at facts the repo already knows, instead of letting it infer architecture from vibes.

Four subagents, one job each

I run four subagents and resist the urge to add a fifth every week. dependency-mapper goes first and answers one question: if I touch this file, what breaks. package-owner makes sure edits stay inside the package that owns the code. integration-tester checks the seams between packages, because that is where monorepo bugs actually hide. reviewer reads the final cross-package diff like a grumpy teammate. Here is the mapper, which is the one that earns its keep.

.claude/agents/dependency-mapper.md
---
name: dependency-mapper
description: >
  Use BEFORE editing anything. Maps the blast radius of a change:
  which packages depend on the file/package being touched, and what
  rebuilds. Read-only. Returns affected package list + risk notes.
tools: Bash, Read, Grep
model: claude-opus-4-8
---

You map impact, you never edit.

1. Identify the package that owns the target file.
2. Run `nx graph --file=/tmp/graph.json` and read the reverse deps.
3. Run `nx affected:graph --base=origin/main --files=<changed>` to see
   what build/test would re-run.
4. Report:
   - owning package
   - direct + transitive dependents (the blast radius)
   - whether any boundary would be crossed by the proposed change
   - a one-line risk verdict: contained | crosses-boundary | cycle-risk

Stop after the report. Do not propose code. The main agent decides.
.claude/agents - dependency-mapper
You
Fix the off-by-one in formatRelative() in packages/date-utils.
Agent
Running dependency-mapper first. Owning package: date-utils. Dependents: web-app, admin, billing, notifications (4 packages, all via the public barrel).
Agent
Verdict: contained. The fix lives inside date-utils and the public signature does not change. Affected build/test: those 4 dependents only. Proceeding with the edit scoped to date-utils.
The mapper runs first, every time. If the verdict is crosses-boundary, the main agent has to stop and ask me.

Hooks do the boring enforcement

Rules in a markdown file are suggestions. Hooks are law, because the harness runs them whether the model feels like it or not. I lean on three. A PreToolUse hook scopes every write to the package the task is about and blocks edits that wander outside it. A PostToolUse hook runs the affected build and test, never the whole graph. And a Stop hook prints the cross-package diff so nothing crosses a seam silently.

.claude/settings.json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "scripts/scope-to-package.sh \"$CLAUDE_TOOL_FILE_PATH\""
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "nx affected -t build test --base=origin/main"
          }
        ]
      }
    ],
    "Stop": [
      {
        "type": "command",
        "command": "git diff --stat origin/main -- packages/ | scripts/cross-package-diff.sh"
      }
    ]
  }
}
scope-to-package exits non-zero on purpose
If the edit path is outside the active package, the PreToolUse script returns a non-zero exit and the write is rejected. That single line of shell has stopped more accidental cross-package refactors than any prompt I have written.
zsh - monorepo-orchestra
ticket: bump retry backoff in packages/http-client
$claude "raise the default retry backoff in http-client to 500ms"
dependency-mapper: owning package http-client, 11 dependents, verdict: contained
package-owner: edit scoped to packages/http-client/src/retry.ts
[PreToolUse] scope-to-package: ok (path inside http-client)
[PostToolUse] nx affected -t build test --base=origin/main
12 projects affected - build 12/12 test 12/12 (41s)
integration-tester: contract tests for http-client consumers green
[Stop] cross-package diff: 0 files outside http-client. clean.
one package touched, twelve verified, nothing leaked. that is the win.
$

The affected-only build is the part people underestimate. Our full graph takes about nine minutes to build and test. nx affected on a single-package change usually settles in well under a minute. When the agent runs that loop after every edit instead of once at the end, it catches the break while it still remembers why it made the change. That tight feedback is most of why this build lands at a 3.3s median step and an 87% pass rate without me babysitting it.

PieceWhat it doesWhy it is in the build
Opus 4.8Plans the change, reads the graphPlanning over a big graph is where a weaker model flails
dependency-mapperBlast radius, read-onlyStops the silent cross-package refactor
scope-to-package hookRejects out-of-package writesLaw, not a suggestion
nx affected hookBuilds and tests only what changedFast loop, catches breaks in context
cross-package diff on StopFlags any seam the change touchesI review boundaries, not every line

Where Sentry and postgres earn their slots

Two of my MCP servers are not about editing at all. The postgres server lets the integration-tester check a migration against a real schema instead of guessing column names, which matters when a single package owns the migrations everyone else reads from. The sentry server gives the reviewer a way to ask whether the file it is about to change is already throwing in production. A reviewer that knows a function is currently on fire prioritizes very differently from one reading cold code. github and filesystem are the obvious two, so I will not bore you.

Claude Code Best Practices - The Ultimate Guide26:11
Claude Code Best Practices - The Ultimate Guide· IndyDevDan

If you only watch one thing before copying my setup, watch that. A lot of what I do with boundaries and affected builds is just the explore-plan-code-commit loop applied to a repo that punishes you for skipping the explore step. The official subagents docs are also worth a slow read, because the isolated-context-per-agent model is the whole reason four small agents beat one big prompt here.

Create custom subagents - Claude Code DocsThe reference I keep open when tuning agent frontmatter, tool allowlists and per-agent context.code.claude.comdisler/claude-code-hooks-masteryEvery hook lifecycle event with working examples. My scope-to-package and cross-package-diff scripts started as forks of these.disler/claude-code-hooks-mastery4.2k

Honest about the limits

  • It assumes Nx or Turborepo. If your affected-graph command is wrong, the fast loop becomes a slow lie and you will trust green builds that did not test the right thing.
  • Big architectural changes that genuinely span packages still need a human in the plan. The build is tuned to refuse those, not to do them well.
  • The PreToolUse hook can be annoying on legitimate multi-package work. I keep a flag to relax it, and I am honest with myself about how often I reach for it.

I am not trying to make Claude an architect. I want it to be the best mid-level engineer on a team with strong conventions: fast, scoped, and quick to ask before it does something clever across a boundary. After two months on this setup our agent-authored PRs stopped being the ones I dreaded reviewing. They touch one package, the affected suite is green, and the cross-package diff is empty. That is the whole pitch.

If you want to try it, grab the build from Setuproll and run npx setuproll add claude-code-monorepo-orchestra, then point the affected commands at your own workspace and trim the CLAUDE.md to your three worst footguns. Steal the hooks, keep the boundaries honest, and tell me what breaks.

0 Reviews

Your rating
Sign in to post

Loading discussion...