Index a pnpm / nx / turbo monorepo
One database, one CLI, one MCP server. Multiple workspaces. Here is the recipe that keeps every package indexable while staying fast on subsequent runs.
Layout
my-monorepo/ apps/ web/ cli/ packages/ shared/ ui/ pnpm-workspace.yaml package.json nci.config.json
nci.config.json
{ "project_root": ".", "workspaces": ["apps/*", "packages/*"], "package_scope": ["dependencies", "dev_dependencies"], "max_hops": 10 }
That config does three things:
- Scans the root
node_modulesplus every workspace’snode_modules. - Includes both
dependenciesanddev_dependenciesso test/build types are indexed too. - Lets you query across the whole tree from any package.
Index incrementally
--package is repeatable and accepts SQLite-style globs. Use it to re-index only the workspace packages you control after a refactor — third-party dependencies stay cached.
Skip the root install
If your monorepo hoists everything into node_modules at the workspaces but keeps the root empty:
That tells the scanner to ignore <project_root>/node_modules and only walk the listed workspaces. Conflicts with --include-root-workspace (the override that forces the root in even when nci.config.json excluded it).
Stub noisy dependencies
Some dependencies show up everywhere. zod lives in nearly every workspace package, and almost every file does import { z } from "zod". Its types are deep generic chains — z.object({...}).strict().refine(...) builds large inference trees that NCI has to walk on every consumer file. You almost never query zod’s internals; you just want NCI to know your packages reference it.
Tell NCI not to crawl into zod. Every consumer-side import collapses into a single stub edge:
The flag (short: -s) is repeatable and accepts bare names or scoped specifiers like @aws-sdk/client-s3. Each consumer-side import becomes an npm::<specifier>::<member> edge — e.g. import { z } from "zod" resolves to npm::zod::z. Fast to write, queryable as evidence, never parsed.
Make it sticky by setting dependency_stub_packages in nci.config.json:
{ "project_root": ".", "workspaces": ["apps/*", "packages/*"], "package_scope": ["dependencies", "dev_dependencies"], "dependency_stub_packages": [ "zod", "@aws-sdk/client-s3", "googleapis" ] }
The CLI value is unioned with the config — CI can add more stubs without rewriting the file. Drop a name from the config and the next index re-parses it in full.
Wire up the agent
Pair this walkthrough with any client setup page — Claude, Cursor, Codex, Antigravity, or OpenCode. The MCP server reads the same nci.config.json and resolves to the same nci.sqlite regardless of client.