The egr CLI indexes a repository into the graph and queries it from the
shell or CI. It is a thin layer over the same tested functions the library and
MCP server use — zero LLM, deterministic.
egr <command> [args] [options]
Every command reads/writes one Kuzu database. Its path is resolved in this priority order:
- env
ENGRAM_DB(a full path; highest), else --graph <name>→./.engram/<name>.db, else--isolation git-branch(or envENGRAM_ISOLATION=git-branch) → a per-branch DB<git-common-dir>/engram/<branch>.db, else- the default single
./.engram/graph.db.
The directory is created on demand and the schema is ensured on every open
(idempotent), so the first index works against an empty repo. See
Branch / project isolation below.
| Option | Description |
|---|---|
--json |
Emit raw JSON instead of the human-readable summary |
--graph <name> |
Use ./.engram/<name>.db — an explicitly named project graph |
--isolation <mode> |
single (default) or git-branch (one graph per branch) |
-h, --help |
Show usage |
-v, --version |
Show the package version |
Recursively indexes source files under <dir> into the code graph
(tree-sitter → Function / Class / Module nodes + cross-file CALLS).
With --docs, also indexes *.md files into the knowledge graph
(front-matter → Spec / Decision + IMPACTS / SUPERSEDES).
- Code extensions:
.ts .tsx .js .jsx .mts .cts .mjs .cjs .cs .py .go .java .kt .kts .rs .cpp .cc .cxx .hpp .h .hh .rb .php .dart(.d.tsexcluded). - Skipped directories:
node_modules,dist,.engram,.git,coverage,bin,obj,__pycache__,.venv,venv,vendor,target,build. --clean: drop the graph's data before indexing. Indexing is otherwise an upsert (MERGE) that never deletes, so a node removed from the code lingers;--cleanrebuilds from scratch to prune it.
egr index ./src
egr index . --docs
egr index ./src --clean # rebuild, pruning deleted nodesOutput counts: files, functions, classes, calls, plus ambiguous
(callee name matched > 1 function — skipped) and unresolved (callee matched
none — skipped); with --docs, specs / decisions / impacts / supersedes.
tree-sitter's own name-based CALLS resolution is deliberately conservative:
when a callee name matches more than one function across the repo it skips
the call rather than guessing (ambiguous in the output above). A SCIP
index — produced by a real compiler/type-checker-backed indexer for the
language in question — carries unambiguous symbol references, so --scip
overlays it on top of the tree-sitter pass to resolve calls tree-sitter alone
can't, and to upgrade the confidence of ones it already resolved.
# 1. Produce the .scip file yourself, with an indexer for your language.
# egr does NOT invoke dotnet/java/maven or any other build toolchain —
# that step is entirely your own build environment's responsibility.
dotnet tool install --global scip-dotnet # once
scip-dotnet index MyProject.csproj --output index.scip
# 2. Point egr at it. --scip always runs a full tree-sitter pass first, then
# overlays the SCIP data — a single command is a complete, from-scratch
# index; you do not need to have run a plain `egr index` before this.
egr index . --scip index.scipRequirements and failure modes:
<dir>must be the SAME project root the external indexer was run against. A SCIP index's occurrence paths are relative to that root; if they don't match<dir>'s own file paths,egrfails with a "none of the N document path(s) ... matched any source file under<dir>" error rather than silently ingesting nothing. SCIP paths are always/-separated by spec;egr's own paths are normalized to/as well regardless of host OS, so this comparison is designed to line up on Windows too, not just POSIX — verified with string-level unit tests against simulated Windows-style paths (no real Windows host to test against in this project's own CI). If--scipreports 0 definitions/calls resolved despite matching files, a warning is printed; one possible cause is a stale.scipfile — generated before the source tree was subsequently edited, so it no longer matches on content even though the paths agree.- A missing or non-SCIP file at
<path>fails with a clear "file not found" or "could not be parsed as a SCIP protobuf index" error. - A graph DB whose
CALLS(orFunction/Class) table predates a schema change like this one'sprovider/confidencecolumns is migrated automatically and non-destructively the moment anyegrcommand opens it: missing columns are added in place viaALTER TABLE ... ADD(existing rows keep every other property they already have), after first checkpointing and backing up the DB file to a.pre-migration-backupsibling (never overwriting a prior backup). No--clean, no deleting the DB file required to stop hitting the schema error —--cleanstill only deletes row data viaDETACH DELETE, never table schema, but that's no longer the mechanism that closes this gap. When a migration actually runs,egrprints a one-line notice on stderr naming the column(s) added and the backup path.Function.provider/Class.provider/CALLS.providerare backfilled to"tree-sitter"on migrated rows (a known historical fact, not a guess — tree-sitter was the only extraction provider before these columns existed), so a plainegr indexre-index right afterwards fully un-freezes them; aCALLSedge'sconfidenceis deliberately leftNULL(we don't know which resolution tier a historical edge was), so a SCIP overlay's ability to upgrade that specific edge still needs one such plain re-index first. This does NOT change the separate, pre-existing fact that a plain re-index resetsFunction.confidence(SAGE's feedback-adjusted score) back to its1.0default regardless of migration — this feature only prevents that value from being destroyed by the migration itself, not by ordinary re-indexing after it. Seesrc/graph-db/schema-migration.tsfor the full mechanism and its documented limits. - Currently verified against
scip-dotnet(C#) andscip-java(Java) output; any SCIP-conformant indexer for a tree-sitter-supported language should work the same way in principle (the merge logic is language-generic), but this has not actually been tried against a third indexer.
Output adds a scip block: documentsInIndex (documents in the .scip
file), filesMatched (how many of those overlapped <dir>'s own files —
less than documentsInIndex is normal, e.g. compiler-generated files an
indexer sees but egr deliberately skips), definitionsResolved /
definitionsUnresolved, callsEmitted, and the two skip counters
callsSkippedNoEnclosingCaller / callsSkippedUnresolvedTarget. If files
matched but resolution came back at zero, the human-readable output adds a
WARNING line rather than silently reporting an all-zero result as success.
Functions that (transitively, up to --depth, default 1) call <symbol>.
"What breaks if I change this?"
egr callers callChain --depth 2Functions that <symbol> (transitively, up to --depth, default 1) calls.
egr callees createMcpServer
--depthis clamped to1..10. A symbol is matched by name; if a name is reused across files, all matches are considered.
Decisions in the impact chain of a spec — which Decision nodes affect this
Spec, via the direct IMPACTS edge plus a multi-hop SUPERSEDES chain
(--max-hops, default 3, clamped to 1..10).
egr impact SPEC-001
egr impact SPEC-001 --max-hops 5 --jsonEach result row shows the decision id, how it was reached (direct |
supersedes), and its title.
Evolve a node's SAGE confidence from one feedback event.
<type>:test_fail(negative, weight 1.0),test_pass(positive, 0.4),human_fix(positive, 0.6),status_change(neutral).--label:Function(default) |Spec|Decision|Doc.- The node is matched by id (for
Decision/Specthe id is e.g.ADR-1/SPEC-1; forFunctionit is the scope-qualified id such assrc/a.ts#a).
egr feedback test_fail "src/api/server.ts#createServer"
egr feedback human_fix ADR-002 --label DecisionPrints before → after, or "node not found" if the id/label miss.
Highest-confidence nodes of a label, confidence-descending.
<label>:Function|Spec|Decision|Doc.--limit: default 10, clamped to1..1000.
egr top Function --limit 20
egr top Decision --jsonGarbage-collect per-branch graphs whose branch no longer exists. Inspects
<git-common-dir>/engram/; a <name>.db is an orphan when no current local
branch maps to <name>. --dry-run lists without deleting. No-op outside a git
repo.
egr gc --dry-run
egr gcWhat this installation can actually do on this machine — the first thing to run when a language is missing from your graph.
egr doctor
egr doctor --jsonReports the egr/Node version and platform, the graph DB path, every language with whether its native module loaded (and the reason if it did not), which native dependencies this platform had to compile from source, which commands need network access, and the MCP registration command.
It does not open the graph, so it still answers when the graph is missing or unreadable — which is when people run it.
Why this is a command and not an install-time message. It was one, and it reached nobody: npm suppresses lifecycle-script output by default and npm ≥ 11 gates those scripts behind an approval prompt, so the notice appeared zero times on a real
npm install -g. A command you type cannot be suppressed by a package manager.
Run the REST server (Hono) over the graph DB. Routes are mounted under
/graph/* plus GET /health. Long-running — manages its own lifecycle.
See API.md for the route surface.
egr serve --port 3000Run the MCP server over stdio for coding assistants. Identical to the
egr-mcp bin. Long-running. See MCP.md for assistant setup.
egr mcpBy default all commands share one ./.engram/graph.db. Because .engram/
is gitignored and lives in the work tree, git checkout does not swap it —
different branches share the same graph. Three ways to isolate:
--isolation git-branch(or setENGRAM_ISOLATION=git-branchonce in your shell): each branch gets its own<git-common-dir>/engram/<branch>.db, which survives checkouts and never pollutes the work tree. Branch names are sanitized with a hash suffix sofeature/xandfeature-xnever collide. Useegr gcto reclaim graphs of deleted branches.--graph <name>: an explicit, git-independent project graph — handy for a detached HEAD or when branch names are ad-hoc.git worktree: each branch checked out in its own directory naturally gets its own./.engram/graph.db— zero flags, the cleanest isolation when branches map to long-lived separate projects.
MCP caveat: the MCP server binds to one graph at startup (it logs the path to stderr). It does not follow a later
git checkout— reconnect/restart the server (or launch it with--graph/ENGRAM_ISOLATION) to switch.
export ENGRAM_DB="$PWD/.engram/graph.db"
egr index ./src --docs
# Fail the job if a high-risk symbol gained new callers, query with --json, etc.
egr callers paymentGateway --depth 3 --json > callers.json0 on success; 1 on error (the message is written to stderr as
egr: <message>).