Aider in a monorepo: scoping context to one package without losing the cross-package picture
Published 2026-04-21 by Owner
Aider was designed around single repos. Run it in a monorepo with 12 packages and 800k lines of code, and the default behavior is painful: the repo map tries to summarize everything, the auto-commit logic fights with the workspace structure, and your token bill triples because the model keeps loading shared types from packages you’re not editing.
I’ve been running aider on a Turborepo monorepo for the past four months. Here’s the working setup.
The structure
For context, the repo:
packages/
ui/ # shared component library
api-types/ # generated TypeScript types from OpenAPI
utils/ # shared utilities
apps/
web/ # Next.js app
admin/ # internal admin panel
worker/ # background job processor
The classic problem: you’re editing apps/web and need to import a type from packages/api-types. Aider needs to see the type to generate correct code, but doesn’t need to load every other shared package.
Step 1: subtree scoping
Run aider from within the package you’re editing:
cd apps/web
aider --subtree-only
The --subtree-only flag tells aider to treat apps/web as the project root for the repo map. Files outside this directory aren’t auto-included. Git operations still work against the parent repo, which is what you want — commits land in the monorepo’s main branch.
This alone reduces the repo map from “all 12 packages” to “just apps/web.” Token usage drops by roughly 60% on a typical session, and aider stops suggesting changes to unrelated packages.
Step 2: explicit imports for cross-package types
When you need types from packages/api-types, add them via the /add command with the relative path:
> /add ../../packages/api-types/src/users.ts
> /add ../../packages/api-types/src/orders.ts
Aider supports paths outside the subtree when added explicitly. The model now sees the type definitions without loading the entire api-types package.
The pattern: subtree scope by default, explicit add for cross-package files you actually need. This forces you to think about what types you need, which is a feature, not a bug.
Step 3: a .aiderignore that knows about the workspace
At the monorepo root, create .aiderignore:
# Build outputs
**/dist/
**/.next/
**/build/
**/.turbo/
# Generated files
packages/api-types/src/generated/
**/*.generated.ts
# Lockfiles
**/pnpm-lock.yaml
**/package-lock.json
**/yarn.lock
# Test outputs
**/coverage/
**/playwright-report/
# Dependency graphs
**/node_modules/
The two lines that matter most for monorepos: ignoring **/dist/ (so you don’t get auto-completion suggesting symbols from compiled output) and ignoring generated files like packages/api-types/src/generated/. Generated files often have idiomatic patterns that the model picks up and applies to your hand-written code, which is wrong.
.aiderignore works at any directory level, but putting it at the monorepo root means it applies regardless of which package you’re working in.
Step 4: package-specific .aider.conf.yml
Each package can have its own .aider.conf.yml for package-specific settings:
# apps/web/.aider.conf.yml
model: claude-3-5-sonnet-20241022
edit-format: diff
auto-commits: false
read:
- tsconfig.json
- next.config.mjs
- tailwind.config.ts
The read field is the lever. These files load on aider start, and they’re exactly the configuration the model needs to suggest changes that match your stack — TypeScript paths, Next.js routing rules, Tailwind class names. Without these in context, the model defaults to whatever pattern was popular in its training data.
For apps/admin, you might use a different read set; for apps/worker, you don’t need Tailwind config. Each package loads only what it needs.
What this fixes in practice
A real example from my codebase. I wanted to add a new API route in apps/web that calls a service in packages/utils and returns a typed response defined in packages/api-types.
Before this setup:
- aider loaded the whole monorepo’s repo map (140k tokens)
- Generated code referenced
apps/admin-style patterns even though I was inapps/web(wrong middleware) - Auto-commits kept committing to the parent repo with messages mentioning unrelated packages
- Total cost for the task: $4.20
After:
- Repo map scoped to
apps/webonly (28k tokens) - I added the two relevant cross-package files explicitly with
/add - Code matched
apps/webconventions - Total cost: $1.10
The cost reduction was nice. The reduction in wrong-pattern suggestions was more valuable.
What still doesn’t work great
Refactors that span multiple packages. If you need to rename a function in packages/utils and update all callers, subtree scoping makes this harder. For these tasks, run aider from the monorepo root without --subtree-only, but be prepared to spend more on tokens.
Turborepo task awareness. Aider doesn’t know about your turbo.json task graph. If your change requires a build in a dependent package before the typecheck passes, aider won’t run the build. You handle that yourself.
Workspace dependencies. When apps/web imports from @org/utils (a workspace dependency), aider sees the import but doesn’t always resolve it correctly to packages/utils/src/index.ts. Adding packages/utils/src/index.ts to the read list of your package config fixes this for the most-used cases.
Worth it?
For a monorepo with more than three packages and active cross-package work, yes. The setup takes 30 minutes. The payoff is aider that produces relevant suggestions at a fraction of the token cost.
For a monorepo where everyone works in one or two packages and rarely touches others, the default --subtree-only is probably enough. You don’t need the full pattern.