5.7 KiB
Monorepo guide
This guide shows how to run lean-ctx in a monorepo without letting one package's cache, index, or agent activity spill into another.
When you need this
Use this setup if your repo has multiple apps or packages, for example:
apps/web+apps/apiin a pnpm or Turborepo workspacecrates/*in a Cargo workspaceservices/*managed by Bazel or Buck2
The goal is simple:
- keep config local to the package an agent is working on
- avoid indexing build artifacts and vendor trees
- keep concurrent agents from stepping on each other
- preserve fast search and cache behavior in large trees
1. Put .lean-ctx.toml at the workspace you want to tune
lean-ctx auto-merges a project-local .lean-ctx.toml with your global config.
That means each package can keep its own ignore rules and performance settings.
Example: pnpm / Turborepo
repo/
├─ .git/
├─ apps/
│ ├─ web/
│ │ └─ .lean-ctx.toml
│ └─ api/
│ └─ .lean-ctx.toml
├─ packages/
│ ├─ ui/
│ └─ config/
└─ pnpm-workspace.yaml
apps/web/.lean-ctx.toml
extra_ignore_patterns = [
"dist/**",
".next/**",
"coverage/**",
"playwright-report/**"
]
memory_profile = "balanced"
memory_cleanup = "shared"
graph_index_max_files = 12000
apps/api/.lean-ctx.toml
extra_ignore_patterns = [
"dist/**",
"tmp/**",
"coverage/**",
"generated/**"
]
memory_profile = "balanced"
memory_cleanup = "shared"
Example: Cargo workspace
repo/
├─ Cargo.toml
├─ crates/
│ ├─ gateway/
│ │ └─ .lean-ctx.toml
│ └─ worker/
│ └─ .lean-ctx.toml
└─ target/
extra_ignore_patterns = ["target/**", "coverage/**"]
graph_index_max_files = 15000
Example: Bazel / Buck2
extra_ignore_patterns = [
"bazel-bin/**",
"bazel-out/**",
"bazel-testlogs/**",
"buck-out/**"
]
memory_profile = "low"
2. Scope BM25 and graph indexing aggressively
Large monorepos get slow when indexes include generated output, vendored code, or build caches.
extra_ignore_patterns is the main lever.
Good candidates to ignore:
- JS:
node_modules/**,dist/**,.next/**,coverage/** - Rust:
target/** - Python:
.venv/**,__pycache__/** - Bazel/Buck2:
bazel-*/**,buck-out/** - Generic generated output:
generated/**,vendor/**,.cache/**
A solid starting point for a mixed monorepo:
extra_ignore_patterns = [
"node_modules/**",
"dist/**",
".next/**",
"coverage/**",
"target/**",
"vendor/**",
"generated/**",
"bazel-bin/**",
"bazel-out/**",
"buck-out/**"
]
If lean-ctx doctor warns about large BM25 indexes, tighten these patterns before increasing limits.
3. Let different agents work on different packages
The easiest pattern is one agent per package root.
Examples:
- Cursor on
apps/web - Claude Code on
apps/api - Codex on
crates/gateway
That keeps read caches, session history, and search results naturally scoped to the folder each agent opened.
When you do need cross-package awareness, prefer explicit scoping over opening the whole repo and hoping for the best:
- run tools from the package root the agent owns
- use tool
pathparameters to narrow searches to one service or package - keep package-specific
.lean-ctx.tomlfiles small and boring
If you intentionally want one workspace to see sibling projects, add a .lean-ctx.json file with linkedProjects:
{
"linkedProjects": ["../packages/ui", "../packages/config"]
}
Use this sparingly. It is useful for shared libraries, but it also widens the search surface.
4. Use .lean-ctx-id when paths are reused
This matters most in Docker, devcontainers, Codespaces, and remote sandboxes where multiple repos all mount at the same path such as /workspace.
Create a .lean-ctx-id file at the project root:
my-monorepo-web
or:
my-monorepo-api
lean-ctx uses this as an explicit project identity, which prevents cache and knowledge collisions between unrelated projects that happen to share the same mount path.
Use a different .lean-ctx-id for each logical project root that may appear at the same filesystem path.
5. Performance defaults for 1000+ files
For large repos, start with:
extra_ignore_patterns = [
"node_modules/**",
"dist/**",
".next/**",
"coverage/**",
"target/**",
"vendor/**",
"generated/**"
]
graph_index_max_files = 12000
memory_profile = "balanced"
memory_cleanup = "shared"
Then adjust from symptoms:
- Index too large: add more ignore patterns first
- Several IDEs/agents share the repo: keep
memory_cleanup = "shared" - Machine is RAM-constrained: switch
memory_profile = "low" - Graph coverage feels too shallow: raise
graph_index_max_files
Recommended layouts
Turborepo / pnpm workspaces
- keep one
.lean-ctx.tomlper app or service - ignore
.next,dist, coverage, generated client code - only link shared packages if the agent truly needs them
Cargo workspaces
- ignore
target/** - keep service-specific config in
crates/<name>/.lean-ctx.toml - open the crate you are editing when possible instead of the full repo root
Bazel / Buck2 polyglot repos
- ignore
bazel-*/buck-out - prefer one agent per subsystem
- use explicit
pathscoping for search-heavy workflows
A practical default
If you are not sure where to start, do this:
- add
.lean-ctx.tomlin each active package - fill
extra_ignore_patternsfirst - set
memory_cleanup = "shared"if multiple tools touch the repo - add
.lean-ctx-idfor containerized/workspacesetups - only add
linkedProjectswhen cross-package search is genuinely useful
That gets most monorepos into the fast and predictable zone without much tuning.