Claude Code is a powerful agentic CLI for editing real codebases — but the default behavior assumes a small, focused project. Drop it into a Turborepo monorepo with 80 packages, and without proper scoping it can wander through tens of thousands of files, waste a significant chunk of your context window on generated output and lock files, and produce confused suggestions that blend concerns from unrelated packages.
This guide covers the two core tools for keeping Claude surgical in large codebases — --add-dir and documented permission rules — plus monorepo-specific patterns for per-package context and token budgeting.
The Problem: Large Codebases Without Scope Are Expensive
A typical enterprise Next.js monorepo might have:
apps/web— the main appapps/admin— a separate admin panelpackages/ui— shared component librarypackages/db— Prisma schema and database utilitiespackages/shared— type definitions and constantsnode_modules/— hundreds of megabytes of dependenciesdist/,build/,.next/,.turbo/— compiled output
If you launch claude from the repo root and ask it to "add a new field to the user settings form," Claude may explore broadly through tools rather than maintaining a guaranteed full-repository index. Without boundaries, it might read lock files, compiled artifacts, and packages completely unrelated to the task at hand — every token spent there is a token not available for the actual code you want changed.
Two features address this directly.
By Default: Claude Works Inside Your Current Directory
When you run claude from a terminal, The launch directory is the primary working directory. Reads and edits follow the configured permission rules and mode; the working directory alone should not be treated as an OS-level isolation boundary.
If you are in apps/web/, Claude can see everything under apps/web/. Use additional directories when shared packages belong to the task. Review the effective permissions before assuming paths outside the working directory are inaccessible.
The challenge is that in a monorepo, the package you are editing almost always imports from shared packages. When Claude cannot read those packages, it either guesses at type signatures or asks you to paste code inline — both of which are friction.
Expanding Scope with --add-dir
The --add-dir flag adds working directories. Files there follow the same permission rules as the primary directory; editing still depends on the current permission mode. You can add multiple directories in a single invocation:
claude --add-dir ../shared-utils --add-dir ../design-system
Practical Monorepo Example (Turborepo / pnpm workspace)
Say you are working on apps/web but your components import from packages/ui and your API routes import from packages/db:
# Working on apps/web, but need access to packages/ui and packages/db
cd apps/web
claude --add-dir ../../packages/ui --add-dir ../../packages/db
Now Claude can read the component types in packages/ui and understand the Prisma schema in packages/db without you needing to paste anything manually. It can also write to those packages if a fix needs to land there — for instance, adding an exported type that both apps/web and apps/admin need.
Persistent Configuration via settings.json
If you always want certain packages available when working from a directory, codify it in .claude/settings.json at the package level:
{
"permissions": {
"additionalDirectories": ["../../packages/ui", "../../packages/db"]
}
}
This file lives inside apps/web/.claude/settings.json. Every time someone runs claude from apps/web/, those directories are automatically available without typing the flags. Commit this file so the whole team benefits.
Limit file access with documented permission rules
Correction — October 3, 2026: Earlier versions of this guide described .claudeignore as a supported exclusion mechanism. Anthropic's current permissions documentation explicitly says that this file has no effect. Move intended exclusions into documented permission rules and verify them on the version you run.
A project-level example for built-in file tools is:
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Read(./.next/**)",
"Edit(./secrets/**)"
]
}
}
Adapt paths to the location of your settings file and current working directory. The documentation distinguishes relative, settings-anchored, home-relative, and absolute patterns. A rule that appears correct can target the wrong location if you misunderstand which directory anchors it.
Keep three goals separate. Task instructions tell the agent which files matter. Discovery exclusions reduce search noise. Permission rules restrict covered tools. Those controls are related, but none should be described as a complete filesystem sandbox simply because it uses familiar ignore-pattern syntax.
What do deny rules leave outside their coverage?
Built-in file-tool restrictions do not establish that every arbitrary subprocess or MCP server has the same boundary. A Python script, a custom tool, or another process may read files indirectly. When your requirement is that no process can access a path, use the documented sandbox or an isolated environment appropriate to that requirement.
Test the intended rule using harmless fixture files before relying on it for sensitive material. Confirm both that the task's allowed file can be read and that the covered tool refuses the excluded path. Record the host version and effective settings. Avoid testing a rule by printing a real secret into a conversation.
Should generated files and lockfiles always be excluded?
No. They are often noisy, but some tasks require them. A dependency-resolution failure can require the lockfile; a generated-client mismatch can require the generated contract. Begin with focused source discovery and expand when the diagnosis calls for it. Permanently denying every generated artifact can hide exactly the evidence needed to resolve a build failure.
Use the repository's ignore rules for ordinary discovery where the selected tool respects them, and use targeted searches to keep output bounded. Tell the agent why a directory is usually irrelevant so it can explain when an exception is necessary instead of silently widening its task.
Monorepo-Specific Strategies
1. Launch from the Package, Not the Root
Always cd into the specific package before launching Claude:
# Do this
cd apps/web && claude
# Not this (unless you specifically need repo-wide context)
claude # from repo root
Starting from the package directory means the default scope is already narrow. Add packages on demand with --add-dir rather than trying to limit a root-level launch.
2. Per-Package CLAUDE.md Files
Each package in a monorepo can have its own CLAUDE.md file. When you launch Claude from apps/web, it reads apps/web/CLAUDE.md for package-specific context:
# apps/web CLAUDE.md
## Stack
- Next.js 15 App Router
- Tailwind CSS + shadcn/ui components from ../../packages/ui
- Auth via NextAuth, session types in ../../packages/shared/auth.ts
## Key conventions
- Components go in src/components/, pages in src/app/
- Use the Button component from @company/ui, not a local one
- All DB access must go through the repository pattern in ../../packages/db/src/
## Do not touch
- src/generated/ — auto-generated by graphql-codegen, run pnpm codegen instead
The root CLAUDE.md handles repo-wide conventions (commit format, PR templates, testing requirements), while package-level files handle the specific context for that package.
3. Reference Specific Files with @ in CLAUDE.md
The --add-dir flag grants directory access. A separate context feature is the @file reference syntax inside CLAUDE.md:
# CLAUDE.md
@../../packages/shared/types/user.ts
@../../packages/db/prisma/schema.prisma
This loads the content of those files into Claude's context at startup — useful for small, critical files like type definitions or schemas that Claude should always have in mind. Combine both:
permissions.additionalDirectoriesinsettings.json— so Claude can navigate and edit those packages@filereferences inCLAUDE.md— so key type files are always pre-loaded in context
Working with Large Files
Some files are too large to read wholesale without burning significant token budget:
- Log files — rarely need full content; let Claude use
Bash(grep)orBash(tail)to find relevant lines instead of reading the whole file - Generated data — database seeds, fixture files, large JSON — exclude them from routine searches unless the task needs them
- Large config files — if your
next.config.tsorwebpack.config.jsis very long, split it into focused modules that Claude can read one at a time
Tell Claude explicitly when you want targeted reads:
"Only look at the files in
src/auth/— don't read anything else unless I specifically ask."
Explicit scoping in your prompt costs almost nothing but saves Claude from exploring broadly.
Pick the right scoping tool for each monorepo situation:
--add-dir vs @file Reference: When to Use Each
--add-dir / additionalDirectories | @file in CLAUDE.md | |
|---|---|---|
| What it does | Grants read/write permission to a directory | Loads a file's content into context at startup |
| Use for | Shared packages Claude may need to navigate or edit | Small, always-relevant files (type defs, schema) |
| Scope | Entire directory tree | One specific file |
| Token cost | Low (only reads what it needs) | Fixed per file loaded |
| Persists? | Via settings.json | Via CLAUDE.md commit |
Use --add-dir broadly for packages Claude might need to navigate. Use @file selectively for the small set of files Claude should always have memorized.
Checking What Claude Can See
At any point in a session, ask Claude directly:
"What directories do you have access to?"
Use the answer as a starting point, then inspect /permissions, /status, and the effective configuration; a model-generated inventory is not proof that a restriction is enforced. This is useful for debugging why Claude says it "can't find" a file — often the directory simply hasn't been granted access.
Token Budgeting Tips for Large Codebases
Effective token management in large projects comes down to four habits:
1. Start sessions scoped to one module or feature at a time
Instead of "fix the auth system," try "fix the JWT refresh logic in src/auth/refresh.ts." The narrower the task, the less Claude needs to explore.
2. Use /clear between unrelated tasks
The /clear command resets context between tasks. If you just finished work on packages/ui and are now switching to packages/db, clear the session rather than carrying forward unrelated context.
3. Be explicit about file scope in your prompts
"Only look at files in
src/api/routes/— do not read anything outside that directory."
Claude Code respects this kind of explicit instruction. It avoids broad exploration and goes straight to the relevant files.
4. Distinguish search scope from access restrictions
Start with relevant source paths and explicit search exclusions. Use documented permission rules for paths that covered tools must not access. Revisit noisy files when they become relevant to the diagnosis, and keep sensitive-file restrictions separate from convenience exclusions.
Complete Setup: Turborepo + pnpm Example
Here is a configuration example to adapt and verify for a Turborepo workspace:
File: apps/web/.claude/settings.json
{
"permissions": {
"additionalDirectories": [
"../../packages/ui",
"../../packages/db",
"../../packages/shared"
],
"deny": ["Read(./.env)", "Read(./.env.*)"]
}
}
File: apps/web/CLAUDE.md
# apps/web
## Key dependencies
- UI components: ../../packages/ui (import from @company/ui)
- DB access: ../../packages/db (use repository pattern, not raw Prisma)
- Shared types: ../../packages/shared
@../../packages/shared/types/index.ts
## Development
- Run: pnpm dev (from this directory)
- Tests: pnpm test
- Build: pnpm build
## Do NOT modify
- src/generated/ — run `pnpm codegen` instead
- prisma/migrations/ — run `pnpm db:migrate` instead
Launching Claude:
cd apps/web
claude
# or, for a one-off session where you also need packages/analytics:
claude --add-dir ../../packages/analytics
A worked scoping plan for an authentication bug
Suppose the web application's session refresh fails after a tab stays idle. Start with the route or client module involved, its shared session type, and the existing reproduction. Ask the agent to identify the path from the browser action to the response before editing. This is a concrete investigation scope, rather than an instruction to understand the entire monorepo.
If the trace reaches a shared package, add that package deliberately and identify which files may change. Reading a shared type does not require rewriting every consumer. When a proposed fix changes a public interface, inspect representative callers and name the broader scope in the review report.
After the edit, run the owning check and repeat the idle-tab reproduction. A passing package typecheck is evidence about types, while a successful browser interaction is evidence about the reported behavior. Keep both observations visible and avoid describing a narrow fix as verification of the whole authentication system.
What should the task handoff preserve?
Write down the relevant paths, the reproduction, the accepted diagnosis, the changed files, and the checks run. Include unresolved questions, such as whether another application consumes the same session type. That account lets a later session continue from evidence instead of re-reading the entire workspace or trusting an old conversational summary blindly.
Review the final diff against the intended ownership boundaries. If an agent has modified a generated file or a sibling application, determine why before accepting the change. Narrow launch context helps the workflow, but preserving scope still requires an inspectable task and a deliberate review.
Related explainx.ai Guides
- What is CLAUDE.md? — persistent memory for Claude Code sessions
- Claude Code slash commands reference — full command index including
/add-dirand/compact - Claude Code hooks — automate actions on tool calls
- Loop engineering with Claude Code — long-running agents and guardrails
Primary source: Claude Code documentation · Settings reference
Configuration details and flag availability reflect Claude Code as of June 2026. Check the official Claude Code docs for the latest settings reference before writing team runbooks.
