Claude Code Setup Guide for Monorepo Workflows: Multi-Root Workspace Detection and Per-Package Rules
Claude Code Setup Guide for Monorepo Workflows
Managing a monorepo with Claude Code requires deliberate configuration to ensure AI-assisted development respects package boundaries, applies the right rules per module, and runs tests across shared dependencies. This guide walks you through setting up Claude Code for production-grade monorepo workflows with multi-root workspace detection, per-package CLAUDE.md rules, custom slash commands, and automated test runner integration.
Step 1: Install and Initialize Claude Code
Start by installing Claude Code globally and verifying the installation from your monorepo root.
npm install -g @anthropic-ai/claude-code
claude —version
cd ~/projects/my-monorepo
claude
On first launch, Claude Code will scan the directory tree and detect your monorepo structure automatically. It reads package.json workspaces, pnpm-workspace.yaml, or lerna.json to identify package roots.
Step 2: Configure Multi-Root Workspace Detection
Claude Code uses a root-level CLAUDE.md file as its primary configuration entry point. Create one at the repository root to declare workspace structure.
# ~/my-monorepo/CLAUDE.md
Monorepo Configuration
Workspace Layout
- packages/api — Express REST API (Node.js + TypeScript)
- packages/web — Next.js frontend application
- packages/shared — Shared utilities and types
- packages/db — Database models and migrations
Global Rules
- Always run
pnpm installfrom the repo root, never inside individual packages - Use workspace protocol for internal deps: “@myorg/shared”: “workspace:*”
- TypeScript strict mode is enforced across all packages
All commits must pass root-level CI checks before pushClaude Code walks up the directory tree from wherever you invoke it. If you open a terminal inside
packages/api, it will merge rules from bothpackages/api/CLAUDE.mdand the rootCLAUDE.md, with the nearest file taking precedence on conflicts.
Step 3: Set Up Per-Package CLAUDE.md Rules
Each package can have its own CLAUDE.md with scoped instructions. This ensures Claude Code applies context-aware behavior depending on which package you are editing.
# ~/my-monorepo/packages/api/CLAUDE.md
API Package Rules
Stack
- Express 5 + TypeScript 5.4
- Zod for request validation
- Drizzle ORM for database queries
Conventions
- All route handlers go in src/routes/
- Use dependency injection via src/container.ts
- Never import directly from @myorg/db internals; use the public barrel export
- Error responses must use the ApiError class from src/errors.ts
Testing
- Test runner: vitest
- Run tests: pnpm —filter @myorg/api test
Integration tests require DATABASE_URL set in .env.test# ~/my-monorepo/packages/web/CLAUDE.md
Web Package Rules
Stack
- Next.js 15 App Router
- Tailwind CSS v4
- React Server Components preferred
Conventions
- Pages go in app/ directory using file-based routing
- Client components must have “use client” directive
- Shared UI primitives live in @myorg/shared/components
Do not install component libraries; use project design system only
Step 4: Create Custom Slash Commands
Custom slash commands let you define reusable prompts for common monorepo workflows. Create a .claude/commands/ directory at the repo root.
mkdir -p .claude/commands
Create a command to scaffold a new package:
# .claude/commands/new-package.md
Scaffold a new package in the packages/ directory with the given name: $ARGUMENTS
- Create packages/$ARGUMENTS/ with a package.json using @myorg/$ARGUMENTS as the name
- Add tsconfig.json extending ../../tsconfig.base.json
- Create src/index.ts as the barrel export
- Add a basic vitest.config.ts
- Register the package in the root pnpm-workspace.yaml if not already listed
Run pnpm install from the root to link workspacesCreate a command for cross-package dependency impact analysis:
# .claude/commands/check-impact.md
Analyze the impact of changes in the current package on all dependent packages.
- Read the current package name from package.json
- Search all other packages for imports from this package
- List every file that imports from this package grouped by consuming package
- For each consuming package, run its test suite and report results
Summarize which downstream packages are affected and whether tests passUse the commands during a session:
/new-package analytics /check-impact
Step 5: Integrate Automated Test Runners Across Shared Dependencies
The most critical monorepo concern is ensuring changes to shared packages trigger tests in all dependents. Configure Claude Code to handle this through your root CLAUDE.md.
# Append to ~/my-monorepo/CLAUDE.md
Test Runner Integration
- When editing files in packages/shared, always run downstream tests: pnpm —filter “@myorg/api” —filter “@myorg/web” test
- When editing files in packages/db, also test the API layer: pnpm —filter “@myorg/api” test
- For full CI validation from root: pnpm -r test
- Before suggesting a PR, run: pnpm -r build && pnpm -r test
Use turbo for cached parallel execution when available: npx turbo run test —filter=…@myorg/sharedClaude Code reads these rules and automatically triggers the appropriate test suites after making changes. When you ask it to modify a utility in
packages/shared, it will run tests not only insharedbut also inapiandweb.
Pro Tips for Power Users
- Layer your CLAUDE.md files strategically. Put universal standards (linting, commit conventions) at the root. Put framework-specific rules (React patterns, API conventions) in each package. Claude Code merges them automatically.- Use
.claude/settings.jsonfor tool permissions. Pre-approve test commands so Claude Code does not prompt you every time:{ “permissions”: { “allow”: [ “Bash(pnpm -r test)”, “Bash(pnpm —filter @myorg/* test)”, “Bash(npx turbo run test*)”, “Bash(pnpm -r build)” ] } }- Scope sessions to a package. Launch Claude Code from within a specific package directory to keep its context focused and reduce token usage on irrelevant files.- Combine with Turborepo. If your monorepo uses Turborepo, referenceturbo.jsonpipeline definitions in yourCLAUDE.mdso Claude Code understands task dependencies and caching behavior.- Use project memories for team context. Store ongoing sprint goals or architecture decisions in Claude Code’s memory system so every team member’s session inherits the same context.
Troubleshooting Common Errors
| Problem | Cause | Solution |
|---|---|---|
CLAUDE.md rules from root not applied | Running Claude Code from a subdirectory without a parent CLAUDE.md chain | Ensure CLAUDE.md exists at the repo root and your working directory is inside the repo |
| Tests pass in one package but fail in dependents | Shared package was not rebuilt before running consumer tests | Add pnpm -r build before cross-package test commands in your CLAUDE.md rules |
| Slash command not found | Command file is in wrong directory or has wrong extension | Verify files are in .claude/commands/ with .md extension at the repo root |
| Claude Code suggests installing deps in a package subdirectory | Missing global rule about root-level installs | Add explicit instruction in root CLAUDE.md: never run npm/pnpm install inside a package |
| Permission denied on test runner | Bash tool not pre-approved for that command pattern | Add the command pattern to .claude/settings.json permissions allow list |
Can Claude Code detect monorepo workspace boundaries automatically?
Yes. Claude Code reads package.json workspaces fields, pnpm-workspace.yaml, and lerna.json to identify package boundaries. However, explicit documentation in your root CLAUDE.md significantly improves accuracy by providing semantic context about each package's purpose, stack, and interdependencies that cannot be inferred from configuration files alone.
How does CLAUDE.md inheritance work across nested directories?
Claude Code walks up the directory tree from your current working directory to the repository root, collecting every CLAUDE.md it finds. Rules are merged with the nearest file taking precedence. For example, if the root says “use Jest” but packages/api/CLAUDE.md says “use Vitest,” Claude Code will use Vitest when working inside the API package. Global rules that are not overridden remain active in all packages.
How do I ensure shared dependency changes trigger tests in all consuming packages?
Add explicit test runner instructions in your root CLAUDE.md that map source packages to their dependents. Use pnpm filter syntax like pnpm —filter “@myorg/api” —filter “@myorg/web” test as the command to run when packages/shared is modified. For large monorepos, integrate Turborepo with npx turbo run test —filter=…@myorg/shared to leverage caching and parallel execution, and document this in the CLAUDE.md so Claude Code follows the same workflow.