Configure Canopy for a Monorepo
Monorepos work with Canopy out of the box — run canopy index at the repo root and Canopy indexes everything. The challenge is tuning configuration so Canopy skips the right paths and resolves cross-package imports correctly.
Index the monorepo root
Section titled “Index the monorepo root”Run canopy index from the monorepo root:
canopy index /path/to/monorepoCanopy walks the entire directory tree, parsing all supported source files. Full-text search and vector embeddings are built by default, and git commit history is ingested automatically when a .git/ directory is present. For a typical monorepo with 50–200 packages, initial indexing takes 30–120 seconds. Subsequent incremental runs are much faster.
Verify the index:
canopy doctorExpected output (excerpt):
Canopy v2.0.0-alpha.1 binary: /usr/local/bin/canopy data dir: /home/you/.canopy
License Solo
Index status: indexed database: /home/you/.canopy/7c4d2f8a91b4e6d2/forge.db
Summary backend (would-activate) backend: heuristic data flow: nothing leaves the boxConfigure ignore rules
Section titled “Configure ignore rules”Create .canopyignore in the monorepo root (gitignore syntax):
node_modulesdistbuild.next.turbocoverage**/*.test.ts**/*.spec.tsCanopy already honors .gitignore and a bundled default ignore set; use .canopyignore for paths that should be excluded from the index but not from git. Two more layers compose with it:
# <repo>/.canopy/config.toml — per-repo config[indexer]extra_ignore_patterns = ["**/*.generated.ts", "openapi/generated/**"]# <repo>/.canopy/team.yml — shared, checked into the repoignored_paths: - vendor/ - generated/After changing ignore rules, re-run canopy index to apply them. Preview the effect first with canopy index . --list-ignored, which prints what would be indexed vs skipped without indexing.
Package manager workspace support
Section titled “Package manager workspace support”Canopy recognizes workspace configurations automatically.
If package.json at the root defines a workspaces field, Canopy uses it to identify package boundaries:
{ "workspaces": [ "packages/*", "apps/*" ]}Canopy maps cross-package imports correctly — an import from @myorg/ui in apps/web resolves to packages/ui/src/index.ts, not an unresolved external.
Canopy reads Cargo.toml at the repo root to identify workspace members:
[workspace]members = [ "crates/core", "crates/cli", "crates/server",]Cross-crate imports resolve within the workspace.
Python monorepos (namespace packages, Poetry workspaces, uv) need no extra configuration. Canopy follows relative imports and sys.path-style references within the indexed tree.
Path aliases
Section titled “Path aliases”If your monorepo uses TypeScript path aliases (e.g., @app/*, ~/*), Canopy reads them from compilerOptions.paths and baseUrl in tsconfig.json (or jsconfig.json) automatically, alongside workspace package-name aliases from package.json. Aliases that can’t be resolved appear as unresolved imports and generate broken_imports findings.
Verify cross-package resolution
Section titled “Verify cross-package resolution”After indexing, verify that cross-package imports resolve correctly. On Pro tier, run the health check in CI mode:
canopy ci --format jsonLook for broken_imports findings:
{ "check_id": "broken_imports", "detail": "Import specifier '@shared/ui' in 'apps/web/src/components/Button.tsx' line 3 could not be resolved to any indexed file. Check that the target file exists and its extension is indexable.", "file_path": "apps/web/src/components/Button.tsx", "line_number": 3, "message": "Unresolved import '@shared/ui'", "severity": "warning"}A cluster of broken_imports findings on one alias means the path alias or workspace config isn’t set up. Fix the tsconfig.json paths (or the workspace definition) and re-index. On Solo tier, the same findings appear in the local dashboard (canopy dashboard).
You can also check a specific package’s wiring by asking the answer engine:
canopy answer "is \`apps/web/src/index.ts\` reachable?"The same question works through the canopy_answer MCP tool from your editor. The response names the reachability path from an entry point; an unreachable file gets an explicit unreachable verdict.
Large monorepos
Section titled “Large monorepos”For monorepos with 500K+ files (large company repos), tune .canopyignore aggressively:
node_modulesdistbuild.cache.turbostorybook-static**/*.min.js**/*.bundle.js**/__mocks__vendorthird_partyCommon pitfalls
Section titled “Common pitfalls”Duplicate symbol warnings after indexing
If the same package appears multiple times (e.g., packages/ui and node_modules/packages/ui), add the duplicate path to .canopyignore. node_modules is ignored by default; if you see duplicates, a symlink or unusual path is creating a second traversal.
Indexing takes over 5 minutes
Check canopy index . --list-ignored for the would index count. If it’s unexpectedly high (>100K files), a large directory is being indexed that shouldn’t be. Run canopy index . --log-level debug to see which paths are taking the most time, then add them to .canopyignore.