Compiler-accurate code graph for monorepos with an agent-friendly query CLI.
Unlike syntax-only indexers (tree-sitter based), pavouk resolves symbols through the
language's own semantic tooling — imports through barrel re-export chains, method calls
through the type checker. What's in the graph is real; queries answer with path:line.
# build the graph (writes <root>/.pavouk/pavouk.db)
pavouk index packages/my-app
# monorepo: pick tsconfig + file sets explicitly...
pavouk index . --project packages/app/tsconfig.json \
--glob 'packages/*/src/**/*.{ts,tsx}'
# ...or keep them in pavouk.yaml next to the root and just run `pavouk index`:
# project: packages/app/tsconfig.json
# globs:
# - packages/*/src/**/*.{ts,tsx}
# who defines/uses a symbol (imports, calls, extends, implements)
pavouk symbol Environment
pavouk refs useDataBinding --kind calls -q # bare path:line, pipeable
# file-level import cycles
pavouk cycles
# machine-readable command catalog
pavouk commands --json- stdout is data, stderr is diagnostics — always.
--jsonon every command for structured output;-qfor bare one-per-line values.- Exit codes:
0ok,1error,2not found,3no index built yet. - No interactive prompts, ever.
The core (SQLite store + queries) is language-neutral: files and symbols carry a
language, edges are neutral kinds (contains, imports, reexports, calls,
extends, implements). Languages plug in as extractors emitting into a GraphSink:
- TypeScript (shipped): ts-morph over the TS 5/6 JS compiler API.
- Rust / Go / Java (planned): ingest SCIP
indexes (
rust-analyzer scip .,scip-go, ...) — one ingest extractor unlocks every language with a SCIP emitter. - Fallback tier (maybe): tree-sitter for syntactic outline only (files, symbols,
contains) — never for resolution edges.
# more queries
pavouk path EntityKeyProvider TreeStore # shortest connection between two symbols
pavouk summary @contember/binding-common # hubs by fan-in + package deps both ways
pavouk dead-exports --package @scope/pkg # exported symbols nobody referencesWorking prototype, measured against grep on the Contember monorepo (72 packages):
95–100% reference-site coverage on tested symbols with zero phantoms — the remaining
deltas are comments/strings (grep noise) and export * barrels (module-level edges).
Full monorepo (3005 files) indexes in ~30 s; incremental re-index after touching one
hub file takes ~7 s (changed files + their dependents only).