Skip to content

Monorepo Setup: Large Codebases with Nested CLAUDE.md

Core Concept LearningAugust 18, 202611 min read

A monorepo (one Git repo, many packages) amplifies Claude Code's complexity. A root CLAUDE.md controls global behavior, but each package (ui/, api/, core/) can have its own CLAUDE.md with package-specific skills, permissions, and context rules. This hierarchy lets you enforce consistency while giving teams autonomy.

The challenge: context overload. With 1M+ lines across 20 packages, indexing everything exhausts Claude's context window. The solution: sparse worktrees, package-scoped CLAUDE.md files, and path-gated rules that limit tool access to package boundaries. We'll show the 3-repo example: root rules, package-level rules, and how permissions compose.

Multiple CLAUDE.md Files (Root + Per-Package)

A monorepo can have: 1. Root CLAUDE.md: global rules (all agents must follow). 2. Package CLAUDE.md: local overrides (this package's rules).

When Claude Code starts in a package directory, it reads the local CLAUDE.md first. If a rule is missing, it inherits from the root. This lets the root enforce guardrails (no destructive operations, no credentials in code) while packages define their own tools and skills.

Example: root forbids rm -rf globally. But packages/build/ has a CLAUDE.md that permits npm run clean (which safely removes dist/). The package rule refines the root rule; it doesn't override the safety intent.

Quick reference

  • Root .claude/CLAUDE.md: global rules for all packages.
  • Each package can have packages/{name}/.claude/CLAUDE.md.
  • Hierarchy: root rules + package overrides.
  • Config merge: package-specific settings override root defaults.
  • Path-gated rules: rules apply only to this package's paths.
Root CLAUDE.md (Global)
1# .claude/CLAUDE.md (Root)2 3name: myrepo4description: Monorepo with UI, API, and Core5 6permissions:7  - tool: "shell"8    action: "allow"9    patterns: ["npm run *", "git *", "node *"]10  - tool: "file"11    action: "allow"12    patterns: ["src/**", "**/*.json", "**/*.md"]13 14skills:15  - code-review16  - testing17  - docs18 19subagents:20  - name: auditor21    skills: [code-review]
Package CLAUDE.md (Local)
1# packages/api/.claude/CLAUDE.md (Package-Specific)2 3name: api4description: GraphQL API service5 6permissions:7  # Inherit from root, add package-specific8  - tool: "file"9    action: "allow"10    paths: ["packages/api/src/**"]11  - tool: "shell"12    action: "allow"13    patterns: ["npm run *"]14 15skills:16  - graphql-schema-design17  - database-migrations18 19subagents:20  - name: schema-reviewer21    skills: [graphql-schema-design]

Remember this

Root CLAUDE.md sets global rules; package CLAUDE.md refines them. Inheritance + override model.

Per-Package Skills and Scoped Permissions

A skill is a tool or routine that an agent can invoke. Skills can be scoped to a package. Example: only agents in packages/db/ can run database migrations. Agents in packages/ui/ can't.

Path-gating implements this: a rule has a paths field that restricts where it applies. An agent running in packages/ui/ sees skills defined globally + skills in packages/ui/.claude/CLAUDE.md, but not skills from packages/db/.claude/CLAUDE.md.

Quick reference

  • Skills defined in root CLAUDE.md are global.
  • Skills in package CLAUDE.md are local to that package.
  • Path-gated tool: 'allow shell only in packages/api/src'.
  • Inheritance: agent in api/ sees root skills + api skills.
  • Isolation: ui/ agent cannot invoke db-migration skill.
Global vs. Package Skills
1# Root: Global Skills2skills:3  - code-review       # All packages can use4  - testing           # All packages can use5 6# packages/api/.claude/CLAUDE.md7skills:8  - graphql-schema-design   # Only api/ can use9  - rest-endpoint-design    # Only api/ can use10 11# packages/db/.claude/CLAUDE.md12skills:13  - database-migration      # Only db/ can use14  - schema-review           # Only db/ can use
Path-Gated Tool Permission
1permissions:2  # Database migrations: only in db/ package3  - tool: "shell"4    action: "allow"5    patterns: ["npm run migrate:*", "npm run seed:*"]6    paths: ["packages/db/**"]  # GATE: only db/7 8  # UI build: only in ui/ package9  - tool: "shell"10    action: "allow"11    patterns: ["npm run build", "npm run dev"]12    paths: ["packages/ui/**"]  # GATE: only ui/

Remember this

Skills are global or path-gated. Agents inherit skills for their package. Isolation prevents cross-package accidents.

Sparse Worktrees for Focused Work

A monorepo has 1M+ lines. Claude Code doesn't need to index all of it to work on one package. Sparse worktrees check out only the files you need, reducing disk I/O and indexing time.

The pattern: define .worktreeinclude that lists which paths to include (e.g., packages/ui/, packages/shared/). When you create a worktree with --sparse, only those paths are downloaded. Claude Code indexes only what's needed. Time to ready: 2s (sparse) vs. 20s (full).

Quick reference

  • Root repo has .worktreeinclude listing paths.
  • Format: one path per line. Wildcards allowed.
  • Create worktree: git worktree add --sparse
  • Sparse worktree sees only included paths.
  • Note: subagent inherits sparse rules from parent worktree.
Define Sparse Checkout Paths
1# .worktreeinclude (repo root)2packages/ui/3packages/shared/4src/types/5package.json6tsconfig.json7README.md8 9# Excluded (not checked out):10# packages/api/11# packages/db/12# packages/mobile/13# docs/
Create Sparse Worktree
1# Create worktree with sparse checkout2git worktree add --sparse ../worktrees/feature-ui feature-ui3 4cd ../worktrees/feature-ui5 6# Only these paths exist:7ls8# packages/ui/9# packages/shared/10# src/types/11# package.json12# ...13 14# These don't exist:15ls packages/api  # Not found

Remember this

Sparse worktrees check out only needed paths. Faster indexing, reduced context. Define .worktreeinclude.

Code Intelligence and Indexing

Claude Code builds an index of symbols (functions, classes, types) to enable fast navigation and dependency discovery. In a monorepo, the index can balloon. With path-gating and sparse worktrees, you control index scope.

Best practice: index only the current package. The index includes: exported types from shared/, internal implementation of the current package, and test files. It excludes other packages' internals. This keeps the index compact and focused.

Quick reference

  • Claude Code builds symbol index on startup.
  • Sparse checkout reduces index size (fewer files).
  • Path-gated index: include current package + shared, exclude others.
  • Symbol search (Cmd+K): searches indexed symbols.
  • Reindex: if you add new files, reindex manually or on file change.

Remember this

Index scope = current package + shared. Sparse checkout keeps it manageable. Reindex after major changes.

Avoiding Context Overload

Claude Code reads files on demand. But if you ask an agent to 'understand the entire repo', it will try to read everything, consuming tokens and context. The boundary: be explicit about scope.

Instead of 'implement feature X', say 'in packages/ui/, implement feature X'. This scopes the agent to one package. If it needs types from shared/, the package's tsconfig.json guides the import; the agent doesn't need to read other packages.

For true monorepo understanding (which packages import which), use a separate analysis pass: a subagent that maps the dependency graph, returns a report, and other agents use that report as input.

Quick reference

  • Be explicit: 'in packages/ui/, implement X' not 'implement X'.
  • Let tsconfig.json and package.json guide imports.
  • Use path-gated CLAUDE.md to prevent reading other packages.
  • For cross-package analysis: dedicated subagent → dependency report → use report as input.
  • Token budget: cap file reads per agent to avoid runaway consumption.
Context Overload (Bad)
1# Agent prompt (too vague)2"Understand our monorepo and implement feature X"3 4# Agent tries to read:5# - All packages (1M LOC)6# - All tests7# - All docs8# → Exhausts context, returns error
Scoped Context (Good)
1# Agent prompt (explicit scope)2"In packages/ui/, implement feature X.3Use types from packages/shared/ via the package.json imports.4Do not modify other packages."5 6# Agent reads:7# - packages/ui/ (50k LOC)8# - packages/shared/ types (10k LOC)9# - Relevant config10# → Stays within context, completes successfully

Remember this

Scope explicitly. Use package boundaries. Separate analysis from implementation. Cap file reads per agent.

Real Example: 3-Repo Monorepo Structure

Three repos: frontend (Next.js, 200k LOC), backend (Go, 300k LOC), shared (TypeScript types, 50k LOC). Organized as a monorepo: root CLAUDE.md sets global rules, each package has its own.

Scenario: implement a new API endpoint (backend) + UI page (frontend) + shared types (types). Traditionally: 3 developers, 3 PRs, coordinate types. With Claude Code: 1 agent orchestrates, spawns 3 subagents (one per package), passes types through state, validates, merges.

Time: 30 min (instead of 2 hours of coordination). Cost: ~$1 in tokens.

Quick reference

  • Root repo/CLAUDE.md: defines shared rules (no secrets, tests required).
  • Root repo/.worktreeinclude: lists packages to include.
  • packages/frontend/.claude/CLAUDE.md: Next.js skills, no Go tools.
  • packages/backend/.claude/CLAUDE.md: Go tools, database access, no UI.
  • packages/shared/.claude/CLAUDE.md: type generation, exports.

Remember this

Hierarchy: global + package-level rules. Parallel work on features. Orchestrate with state passing.

Scaling to 1M+ LOC

At 1M+ lines, the monorepo is too large for one agent to comprehend. Instead, decompose into agents:

1. Indexer agent: maps repo structure, exports (packages, types, public APIs). 2. Planner agent: reads indexer output, breaks down the feature into subtasks. 3. Worker agents: each handles one subtask (e.g., implement API, add UI, add types). 4. Validator agent: checks correctness (tests pass, types valid, no conflicts). 5. Merger agent: coordinates merges, resolves conflicts.

This pipeline is more reliable and scalable than a single monolithic agent. Each agent is specialized, smaller context, easier to debug.

Quick reference

  • Indexer: analyzes repo structure, exports as JSON.
  • Planner: reads index, generates task list.
  • Workers: parallel, one task per worker, scoped to package.
  • Validator: runs tests, type checks, integration tests.
  • Merger: merges all branches, handles conflicts.

Remember this

Scale with decomposition: indexer → planner → workers (parallel) → validator → merger.

Key takeaway

Monorepos scale Claude Code via hierarchy: root CLAUDE.md + package overrides, sparse worktrees, path-gated permissions, and decomposed agents. For orchestrating multi-agent workflows, read Dynamic Workflows. For managing hundreds of sessions, see Agent View. For agent fundamentals, see Agent SDK Quickstart.

Share:
PK

Polo Khan

Lead Author & Systems Architect

Software engineer and distributed systems architect specializing in backend scalability, cloud-native infrastructure, databases, and AI engineering workflows. Author and maintainer of Core Concept Learning.

Human-Engineered & Fact-CheckedOriginal Visual DiagramsEditorial Standards →Send Feedback

Related Articles

AI coding agents change where engineering effort is spent: the hard problem moves from typing code to defining boundarie

Read

Enterprise teams running Claude Code across 100+ developers face a critical problem: how do you enforce company policies

Read

Terminal AI agents are replacing simple code completion extensions, providing developers with autonomous command-line as

Read

Explore this topic

Keep learning

Follow a structured path or browse all courses to go deeper.