Setting Up Claude Code for Monorepos: Multi-Package Context Management

Setting Up Claude Code for Monorepos: Multi-Package Context Management
Monorepos are where AI coding agents go to die. A single repository with 5 packages, shared libraries, and cross-package imports creates a context management nightmare that turns Claude Code from a productivity multiplier into a token-burning confusion machine.
The symptoms are unmistakable: Claude Code suggests importing from `@org/shared-utils` but writes the path as a relative import. It reads the wrong package's `tsconfig.json`. It creates a React component using patterns from the Express API package that lives three directories over. Every fix creates two new problems because the agent doesn't understand package boundaries.
Here's how to configure Claude Code for monorepos so it actually works — and how to manage context across packages without burning your entire token budget on irrelevant code.
Why Monorepos Confuse AI Agents
The root problem is structural ambiguity. In a single-package project, the file tree is the architecture. In a monorepo, the file tree is misleading — packages that sit next to each other in the directory may have completely different runtimes, dependencies, and coding patterns.
Consider a typical monorepo:
```
packages/
web/ → Next.js frontend (React, Tailwind)
api/ → Express backend (Node.js, Prisma)
shared/ → Shared types and utilities (pure TS)
mobile/ → React Native app
workers/ → Cloudflare Workers (edge runtime)
```
When you ask Claude Code to "add input validation to the user form," it needs to know:
- The form is in `packages/web/`
- The validation schema might live in `packages/shared/`
- The API endpoint it submits to is in `packages/api/`
- The validation library (Zod) is a dependency of `shared`, not `web`
- Import paths use `@org/shared` workspace aliases, not relative paths
Without explicit guidance, Claude Code has a 50/50 chance of reading from the wrong package. It might find a validation utility in `packages/api/src/utils/validate.ts` and suggest importing it into the web frontend — an import that will compile but crash at runtime because the API package uses Node.js APIs unavailable in the browser.
The Three Core Problems
Problem 1: Wrong-package reads. Claude Code explores by proximity in the file tree. When it reads `packages/web/src/components/UserForm.tsx` and needs to understand the validation logic, it may scan `packages/api/src/` before checking `packages/shared/` because the API package has more files and appears more relevant based on filename matching.
Problem 2: Import confusion. Monorepos use workspace aliases (`@org/shared`), path mappings (`tsconfig.json` paths), or package.json `exports` fields. Claude Code defaults to relative imports unless told otherwise, producing import statements that look correct but fail resolution.
Problem 3: Pattern contamination. Each package may follow different patterns. The API uses service classes and dependency injection. The web app uses React hooks and functional components. The workers use a minimal handler pattern. Claude Code picks up patterns from whatever files it reads first, regardless of which package it's actually working in.
The Monorepo Setup Checklist
Fix these configuration issues before writing a single prompt, and 80% of monorepo-related Claude Code problems disappear.
1. Root CLAUDE.md — The Map
Create a `CLAUDE.md` at the repository root that describes the monorepo structure:
```markdown
Monorepo Structure
Packages
- `packages/web` — Next.js frontend (React 19, Tailwind v4)
- `packages/api` — Express API server (Node.js 22, Prisma)
- `packages/shared` — Shared types, validation schemas, utilities
- `packages/mobile` — React Native app (Expo)
- `packages/workers` — Cloudflare Workers (edge runtime)
Import Rules
- Cross-package imports MUST use workspace aliases: @org/shared, @org/api
- NEVER use relative paths across package boundaries
- packages/shared is the ONLY package that can be imported by other packages
- packages/api and packages/web MUST NOT import from each other
Package Manager
- pnpm with workspaces
- Run commands from package directories: cd packages/web && pnpm dev
```
This file costs ~200 tokens to read and prevents thousands of tokens of wrong-package exploration.
2. Per-Package CLAUDE.md — The Conventions
Each package gets its own `CLAUDE.md` with package-specific conventions:
```markdown
packages/web
Stack
- Next.js 15 App Router
- React 19 with Server Components
- Tailwind CSS v4
- Zod (via @org/shared) for validation
Commands
- `pnpm dev` — start dev server (port 3000)
- `pnpm test` — vitest
- `pnpm lint` — eslint + prettier
Patterns
- Use Server Components by default, 'use client' only when needed
- Co-locate tests in __tests__/ directories
- Import validation schemas from @org/shared/validation
- State management: Zustand for client state, React Query for server state
```
When Claude Code works in `packages/web/`, it reads both the root and package-level `CLAUDE.md`. The per-package file overrides general patterns with package-specific ones.
3. Workspace Configuration
Ensure your workspace tooling is properly configured so Claude Code's commands work correctly:
`package.json` (root):
```json
{
"workspaces": ["packages/*"]
}
```
`tsconfig.json` (root):
```json
{
"compilerOptions": {
"paths": {
"@org/shared/*": ["packages/shared/src/*"],
"@org/api/*": ["packages/api/src/*"]
}
}
}
```
Claude Code reads these files to understand import resolution. If your workspace aliases aren't defined in `tsconfig.json`, Claude Code will fall back to guessing import paths — and it will guess wrong.
Context Management Strategies
Even with proper setup, monorepo context management requires discipline. The goal: keep Claude Code focused on one package at a time, with minimal cross-package context.
Strategy 1: Scope Every Prompt
Never say "add validation." Always say "add validation to `packages/web/src/components/UserForm.tsx` using the schema from `@org/shared/validation/user.ts`."
Every prompt should include:
- Which package you're working in
- Which files are relevant
- Which cross-package imports are needed (with exact paths)
This isn't pedantic — it's token-efficient. A scoped prompt costs 50 tokens. An unscoped prompt triggers 5,000+ tokens of exploration as Claude Code figures out which package you mean.
Strategy 2: Use /clear When Switching Packages
If you've been working in `packages/web/` and need to switch to `packages/api/`, use `/clear` first. The web package context (component patterns, React hooks, Tailwind classes) will contaminate Claude Code's reasoning about the API package. A clean context window produces better results than a polluted one.
Cost of clearing: You lose accumulated context from the previous package (5-10 minutes to rebuild).
Cost of NOT clearing: Pattern contamination that produces wrong-package suggestions (15-30 minutes to debug and fix).
The math always favors clearing.
Strategy 3: Reference Shared Libraries Explicitly
When your task involves shared code, tell Claude Code exactly what to reference:
```
I'm working in packages/web. For this task, you'll need:
- The UserProfile type from @org/shared/types/user.ts
- The validateEmail schema from @org/shared/validation/user.ts
Do NOT read any other files in packages/shared unless needed.
```
This surgical context loading prevents Claude Code from reading the entire shared package (~15,000 tokens) when it only needs two type definitions (~200 tokens).
Strategy 4: Per-Package Terminal Sessions
For long monorepo work sessions, run separate Claude Code instances per package. Each instance stays scoped to its package's context, conventions, and patterns. When you need cross-package coordination, pass specific information between sessions manually.
This approach mirrors how human developers work in monorepos — you don't keep the entire monorepo in your head. You focus on one package, check the shared types when needed, and switch context deliberately.
How vexp Handles Monorepos
Context engines designed for monorepos solve the cross-package problem at the index level. vexp, for example, supports multi-repo indexing — each package gets its own dependency graph, and cross-package imports are tracked as inter-graph edges.
When you ask vexp for context in a monorepo, it does three things that manual exploration can't:
1. Package-aware scoping. It knows that `packages/web/src/components/UserForm.tsx` depends on `@org/shared/validation/user.ts` but NOT on `packages/api/src/routes/user.ts`. It serves the shared types without the API package.
2. Cross-package dependency tracking. If you're refactoring a shared type, vexp's impact graph shows which files in EVERY package are affected — not just the package you're working in. Manual search would require grepping each package separately and understanding each one's import resolution rules.
3. Workspace-aware import paths. Context returned by vexp includes the correct workspace alias paths (`@org/shared/validation`) rather than relative paths that break cross-package boundaries.
The setup is straightforward:
```bash
cd monorepo-root
vexp init # initializes at root level
vexp index # indexes all packages
```
vexp detects the workspace configuration from your `package.json` workspaces field or `pnpm-workspace.yaml` and indexes each package as a separate graph with cross-package edges. When Claude Code queries `run_pipeline` from any package, it receives context that respects package boundaries automatically.
Token savings in monorepos are even larger than single-package projects. In a single-package project, wrong-file reads waste maybe 500-2,000 tokens each. In a monorepo, wrong-package reads waste 5,000-15,000 tokens because Claude Code reads multiple files in the wrong package before realizing its mistake. A context engine that prevents wrong-package reads saves 60-75% of exploration tokens in monorepo workflows.
Practical Monorepo Workflow
Here's a real-world workflow for adding a feature that touches multiple packages: adding email verification to the user signup flow.
Step 1: Plan in the Root Context
```
I'm adding email verification to user signup. This will touch:
- packages/shared (new email verification types and schemas)
- packages/api (new verification endpoint and email sending)
- packages/web (verification status UI and redirect flow)
Plan the changes needed in each package and the order of implementation.
```
Claude Code produces an implementation plan. Key insight: changes should go shared → api → web because each package depends on the previous one.
Step 2: Implement Shared Types First
```
Working in packages/shared. Add:
- VerificationStatus type to src/types/user.ts
- emailVerificationSchema to src/validation/user.ts
- verifyEmailToken utility to src/utils/email.ts
```
Clear context after this step.
Step 3: Implement API Changes
```
Working in packages/api. Add an email verification endpoint:
- POST /api/verify-email/:token
- Uses VerificationStatus from @org/shared/types/user
- Uses emailVerificationSchema from @org/shared/validation/user
- Updates user record via Prisma
Follow existing endpoint patterns in src/routes/auth.ts
```
Clear context after this step.
Step 4: Implement Web Changes
```
Working in packages/web. Add email verification UI:
- Verification pending banner in src/components/auth/VerificationBanner.tsx
- Token verification page at src/app/verify-email/[token]/page.tsx
- Uses VerificationStatus from @org/shared/types/user
Follow existing page patterns and use the project's UI components.
```
Step 5: Integration Test
```
Run tests across all packages:
- cd packages/shared && pnpm test
- cd packages/api && pnpm test
- cd packages/web && pnpm test
Report any failures.
```
Total tokens across all steps: ~40,000-60,000. An unscoped approach ("add email verification to the signup flow" without package specification) would cost 120,000-180,000 tokens as Claude Code explores all five packages trying to understand the monorepo structure.
Common Monorepo Pitfalls
Pitfall 1: Forgetting to clear context between packages. Patterns from `packages/api` (Express middleware, database queries) leak into Claude Code's suggestions for `packages/web` (React components, client-side state). Always clear.
Pitfall 2: Letting Claude Code discover package structure. Don't ask "what packages are in this monorepo?" — tell it. The `CLAUDE.md` at the root should document every package so Claude Code never needs to run `ls packages/` and then read each package.json.
Pitfall 3: Cross-package barrel exports. If `packages/shared/src/index.ts` re-exports everything from 20 submodules, Claude Code reads the entire barrel file (and potentially all 20 submodules) when it only needs one type. Reference specific submodule paths in your prompts: `@org/shared/types/user` instead of `@org/shared`.
Pitfall 4: Inconsistent naming across packages. If the API calls it `userId` and the web app calls it `user_id` and shared types call it `id`, Claude Code picks whichever convention it saw most recently. Standardize naming in shared types and document the convention in CLAUDE.md.
The pattern is consistent: explicit configuration and scoped prompts beat hoping Claude Code figures out your monorepo structure on its own. The 30 minutes you spend on CLAUDE.md files and workspace configuration save hours of debugging wrong-package suggestions.
Frequently Asked Questions
How do I set up Claude Code for a monorepo?
Why does Claude Code suggest wrong imports in monorepos?
Should I use one Claude Code session or multiple for monorepo work?
How does vexp handle cross-package dependencies in monorepos?
What's the most common monorepo mistake with Claude Code?
Nicola
Developer and creator of vexp — a context engine for AI coding agents. I build tools that make AI coding assistants faster, cheaper, and actually useful on real codebases.
Related Articles

Vibe Coding Is Fun Until the Bill Arrives: Token Optimization Guide
Vibe coding with AI is addictive but expensive. Freestyle prompting without context management burns tokens 3-5x faster than structured workflows.

Windsurf Credits Running Out? How to Use Fewer Tokens Per Task
Windsurf credits deplete fast because the AI processes too much irrelevant context. Reduce what it needs to read and your credits last 2-3x longer.

Best AI Coding Tool for Startups: Balancing Cost, Speed, and Quality
Startups need speed and budget control. The ideal AI coding stack combines a free/cheap agent with context optimization — here's how to set it up.