@colbymchenry/codegraph 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,7 +10,7 @@ Follow [@getcodegraph](https://x.com/getcodegraph) on X for updates.
10
10
 
11
11
  ### Supercharge Claude Code, Cursor, Codex, OpenCode, Hermes Agent, Gemini, Antigravity, and Kiro with Semantic Code Intelligence
12
12
 
13
- **~16% cheaper · ~58% fewer tool calls · 100% local**
13
+ **Surgical context · fewer tool calls · faster answers · 100% local**
14
14
 
15
15
  ### [Documentation & Website →](https://colbymchenry.github.io/codegraph/)
16
16
 
@@ -76,7 +76,7 @@ In a **new terminal**, run the installer to connect CodeGraph to the agents you
76
76
  codegraph install
77
77
  ```
78
78
 
79
- <sub>Detects and auto-configures Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, and Kiro — wiring the CodeGraph MCP server into each. **This is the step that connects CodeGraph to your agent;** installing the CLI in step 1 does not do it on its own. (Shortcut: `npx @colbymchenry/codegraph` downloads and runs this in one go.)</sub>
79
+ <sub>Detects and auto-configures Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, and Kiro — wiring the CodeGraph MCP server into each. **This is the step that connects CodeGraph to your agent;** installing the CLI in step 1 does not do it on its own. It only wires up your agent — it does **not** index any code; building each project's graph is the separate `codegraph init` in step 3. (Shortcut: `npx @colbymchenry/codegraph` downloads and runs this in one go.)</sub>
80
80
 
81
81
  ### 3. Initialize each project
82
82
 
@@ -111,27 +111,33 @@ codegraph uninstall
111
111
 
112
112
  ## Why CodeGraph?
113
113
 
114
- When Claude Code explores a codebase, it spawns **Explore agents** that scan files with grep, glob, and Read consuming tokens on every tool call.
114
+ When an AI agent needs to understand code — to answer a question or make a change it discovers structure the slow way: grep, glob, and Read, one file at a time, rebuilding call paths and dependencies by hand. That's a pile of tool calls and round-trips before it even starts the real work.
115
115
 
116
- **CodeGraph gives those agents a pre-indexed knowledge graph** symbol relationships, call graphs, and code structure. Agents query the graph instantly instead of scanning files.
116
+ **CodeGraph hands the agent the exact code it needs in one call.** It's a pre-built knowledge graph of every symbol, call edge, and dependency in your codebase — so instead of crawling files, the agent asks one question and gets back the relevant source, the call paths between those symbols (including dynamic-dispatch hops grep can't follow), and the blast radius of a change. **Surgical context, not a file-by-file search** — which means fewer tool calls and faster answers on every codebase, large or small.
117
+
118
+ <img width="1536" height="1024" alt="token-cost-savings-scale" src="https://github.com/user-attachments/assets/eb74a11a-a3ab-4b01-80a6-19f78352ae8e" />
119
+
120
+ > **A note on cost:** CodeGraph's win on *every* codebase is precision and speed — fewer tool calls, faster answers. It cuts token and dollar cost too, but those savings are **scale-dependent**: small and noisy on a modest codebase, and material only once a repo is large and tangled — at the scale of a Google or Microsoft monorepo, multiplied by a whole team's daily agent usage — for them to compound into a real line item. On a 500-file project, adopt CodeGraph for the speed; the cost savings show up when the codebase (and the team) gets big.
117
121
 
118
122
  ### Benchmark Results
119
123
 
120
- Tested across **7 real-world open-source codebases** spanning 7 languages, comparing an agent (Claude Code, headless) answering one architecture question **with** and **without** CodeGraph. Each cell is the savings at the **median of 4 runs per arm**. _Re-validated on Opus 4.8 (2026-06-02), on the current build (`codegraph_explore` as the primary tool)._
124
+ Tested across **7 real-world open-source codebases** spanning 7 languages, comparing an agent (Claude Code, headless) answering one architecture question **with** and **without** CodeGraph, at the **median of 4 runs per arm**. _Re-validated on Opus 4.8 (2026-06-02), on the current build (`codegraph_explore` as the primary tool)._
121
125
 
122
- > **Average: 16% cheaper · 47% fewer tokens · 22% faster · 58% fewer tool calls**
126
+ > **The universal win every repo, every size: 58% fewer tool calls · 22% faster · file reads cut to ~zero.**
123
127
 
124
- | Codebase | Language | Cost | Tokens | Time | Tool calls |
125
- |----------|----------|------|--------|------|------------|
126
- | **VS Code** | TypeScript · ~10k files | 18% cheaper | 64% fewer | 11% faster | 81% fewer |
127
- | **Excalidraw** | TypeScript · ~640 | even | 25% fewer | 27% faster | 40% fewer |
128
- | **Django** | Python · ~3k | 8% cheaper | 60% fewer | 13% faster | 77% fewer |
129
- | **Tokio** | Rust · ~790 | even | 38% fewer | 18% faster | 57% fewer |
130
- | **OkHttp** | Java · ~645 | 25% cheaper | 54% fewer | 31% faster | 50% fewer |
131
- | **Gin** | Go · ~110 | 19% cheaper | 23% fewer | 24% faster | 44% fewer |
132
- | **Alamofire** | Swift · ~110 | 40% cheaper | 64% fewer | 33% faster | 58% fewer |
128
+ The reliable, universal payoff is **surgical context and speed**: CodeGraph collapses the agent's grep/find/Read crawl into a few direct queries — returning the exact methods you asked about even when they're buried in a multi-thousand-line file — so it answers with **near-zero file reads** while the no-CodeGraph agent spends its budget on discovery. The **Tokens** and **Cost** columns are real too, but — as noted above — they're **scale-dependent**: small and noisy per query, compounding into real money only at large-codebase, high-volume scale.
133
129
 
134
- CodeGraph cuts **tokens, tool calls, and wall-clock time on every repo** — across small, medium, and large codebases — and answers them with **near-zero file reads**, while the no-CodeGraph agent spends its budget on grep/find/Read discovery. `codegraph_explore` shows the answer in full — the mechanism plus the exact methods you asked about, even when they're buried in a multi-thousand-line file — while collapsing redundant interchangeable implementations to signatures, so the response is sized to the *answer* rather than the file count. **Cost stays flat-to-cheaper everywhere** — largest on the small repos (Alamofire, OkHttp), roughly break-even on the most response-heavy ones (Excalidraw, Tokio), where CodeGraph trades the no-CodeGraph agent's many small grep/read round-trips for a few large, cache-heavy tool responses.
130
+ | Codebase | Language | Tool calls | Time | File reads | Tokens | Cost |
131
+ |----------|----------|------------|------|------------|--------|------|
132
+ | **VS Code** | TypeScript · ~10k files | 81% fewer | 11% faster | 0 vs 9 | 64% fewer | 18% cheaper |
133
+ | **Excalidraw** | TypeScript · ~640 | 40% fewer | 27% faster | 0 vs 7 | 25% fewer | even |
134
+ | **Django** | Python · ~3k | 77% fewer | 13% faster | 0 vs 9 | 60% fewer | 8% cheaper |
135
+ | **Tokio** | Rust · ~790 | 57% fewer | 18% faster | 0 vs 8 | 38% fewer | even |
136
+ | **OkHttp** | Java · ~645 | 50% fewer | 31% faster | 0 vs 4 | 54% fewer | 25% cheaper |
137
+ | **Gin** | Go · ~110 | 44% fewer | 24% faster | 1 vs 6 | 23% fewer | 19% cheaper |
138
+ | **Alamofire** | Swift · ~110 | 58% fewer | 33% faster | 0 vs 9 | 64% fewer | 40% cheaper |
139
+
140
+ <sub>**File reads** = median files the agent opened **with** vs **without** CodeGraph — the surgical-context win in one column. **Tokens** and **Cost** are the same with-vs-without deltas; they're directional (they move run-to-run) and, per query, small in absolute terms — which is why they only become a line item at scale. `codegraph_explore` also collapses redundant interchangeable implementations to signatures, so a response is sized to the *answer* rather than the file count.</sub>
135
141
 
136
142
  <details>
137
143
  <summary><strong>Per-repo breakdown — WITH vs WITHOUT (median of 4)</strong></summary>
@@ -234,7 +240,7 @@ CodeGraph cuts **tokens, tool calls, and wall-clock time on every repo** — acr
234
240
 
235
241
  | | |
236
242
  |---|---|
237
- | **Smart Context Building** | One tool call returns entry points, related symbols, and code snippets — no expensive exploration agents |
243
+ | **Surgical Context** | One tool call returns entry points, related symbols, and code snippets — no slow file-by-file exploration |
238
244
  | **Full-Text Search** | Find code by name instantly across your entire codebase, powered by FTS5 |
239
245
  | **Impact Analysis** | Trace callers, callees, and the full impact radius of any symbol before making changes |
240
246
  | **Always Fresh** | File watcher uses native OS events (FSEvents/inotify/ReadDirectoryChangesW) with debounced auto-sync — the graph stays current as you code, zero config |
@@ -262,7 +268,7 @@ agent writes src/Widget.ts
262
268
  → next agent query sees it
263
269
  ```
264
270
 
265
- **Verify any time** with `codegraph_status` (via MCP) or `codegraph status` (CLI). If anything is pending, you'll see a `### Pending sync:` section naming the files and their edit age.
271
+ **Verify any time** with `codegraph status` (CLI). If anything is pending, you'll see a `### Pending sync:` section naming the files and their edit age.
266
272
 
267
273
  The handful of cases where manual `codegraph sync` makes sense: the watcher is disabled (sandboxed environments, or `CODEGRAPH_NO_DAEMON=1`), or you're scripting against the index outside an agent session and want a pre-flight sync at the start of your script.
268
274
 
@@ -300,7 +306,7 @@ CodeGraph detects web-framework routing files and emits `route` nodes linked by
300
306
 
301
307
  ## Mixed iOS / React Native / Expo bridging
302
308
 
303
- Real iOS and React Native codebases live across multiple languages — a Swift caller invokes an Objective-C selector that's been auto-bridged, a JS file calls into a native module via the React Native bridge, a JSX component delegates to a native view manager. Static tree-sitter extraction stops at each language boundary. CodeGraph bridges them so `trace`, `callers`, `callees`, and `impact` connect end-to-end across the gap.
309
+ Real iOS and React Native codebases live across multiple languages — a Swift caller invokes an Objective-C selector that's been auto-bridged, a JS file calls into a native module via the React Native bridge, a JSX component delegates to a native view manager. Static tree-sitter extraction stops at each language boundary. CodeGraph bridges them so `codegraph_explore` connects the flow end-to-end across the gap — call paths and blast radius cross the boundary instead of stopping at it.
304
310
 
305
311
  | Boundary | JS / Swift side | Native side | How |
306
312
  |---|---|---|---|
@@ -339,9 +345,10 @@ The installer will:
339
345
  - Ask which agent(s) to configure — auto-detects installed ones from: **Claude Code**, **Cursor**, **Codex CLI**, **opencode**, **Hermes Agent**, **Gemini CLI**, **Antigravity IDE**, **Kiro**
340
346
  - Prompt to install `codegraph` on your PATH (so agents can launch the MCP server)
341
347
  - Ask whether configs apply to all your projects or just this one
342
- - Write each chosen agent's MCP server config, plus a small marker-fenced CodeGraph section in the agent's instructions file (`CLAUDE.md` / `AGENTS.md` / `GEMINI.md`) — that's how subagents and non-MCP agents learn the `codegraph explore` / `codegraph node` commands, since the MCP server's own guidance only reaches the main agent. Removed cleanly by `codegraph uninstall`.
348
+ - Write each chosen agent's MCP server config, plus a small marker-fenced CodeGraph section in the agent's instructions file (`CLAUDE.md` / `AGENTS.md` / `GEMINI.md`) — that's how subagents and non-MCP agents learn the `codegraph explore` command, since the MCP server's own guidance only reaches the main agent. Removed cleanly by `codegraph uninstall`.
343
349
  - Set up auto-allow permissions when Claude Code is one of the targets
344
- - Initialize your current project (local installs only)
350
+
351
+ The installer **wires up your agents only — it does not index your code.** After it finishes, build each project's graph yourself with `codegraph init` (step 3). One global `codegraph install` covers every project; you run `codegraph init` once per project.
345
352
 
346
353
  **Non-interactive (scripting / CI):**
347
354
 
@@ -401,19 +408,14 @@ npm install -g @colbymchenry/codegraph
401
408
  {
402
409
  "permissions": {
403
410
  "allow": [
404
- "mcp__codegraph__codegraph_search",
405
- "mcp__codegraph__codegraph_explore",
406
- "mcp__codegraph__codegraph_callers",
407
- "mcp__codegraph__codegraph_callees",
408
- "mcp__codegraph__codegraph_impact",
409
- "mcp__codegraph__codegraph_node",
410
- "mcp__codegraph__codegraph_status",
411
- "mcp__codegraph__codegraph_files"
411
+ "mcp__codegraph__*"
412
412
  ]
413
413
  }
414
414
  }
415
415
  ```
416
416
 
417
+ <sub>One wildcard auto-approves every CodeGraph tool — `codegraph_explore` is the only one listed by default, but if you re-enable others via `CODEGRAPH_MCP_TOOLS` they're already permitted, no prompt.</sub>
418
+
417
419
  </details>
418
420
 
419
421
  <details>
@@ -422,11 +424,11 @@ npm install -g @colbymchenry/codegraph
422
424
  CodeGraph's MCP server delivers its usage guidance to your agent **automatically**, in the MCP `initialize` response. In short, it tells the agent to:
423
425
 
424
426
  - **Answer structural questions directly with CodeGraph** — it *is* the pre-built index, so a grep/read loop just repeats work it already did. Treat the returned source as already read.
425
- - **Pick the tool by intent:** `codegraph_explore` for almost anything — "how does X work", a flow/"how does X reach Y", or surveying an area (one call returns the relevant symbols' source grouped by file); `codegraph_search` to just locate a symbol; `codegraph_callers` for every call site (including callback registrations); `codegraph_node` for one symbol's full source + callers, or to read a file like the Read tool.
427
+ - **Reach for `codegraph_explore` for almost anything** — "how does X work", a flow/"how does X reach Y", or surveying an area. One call returns the relevant symbols' verbatim source grouped by file, the call paths between them (dynamic-dispatch hops included), and a blast-radius summary. Name a file or symbol in the query to read its current line-numbered source.
426
428
  - **Trust the results — don't re-verify with grep**, and check the staleness banner after edits.
427
- - In a workspace with no index, CodeGraph announces itself inactive and serves no tools indexing stays your decision.
429
+ - Works **per project**: query any project that has a `.codegraph/` index by passing `projectPath` — so a monorepo where only some services are indexed, or a second repo, works in one session. A path with no index returns clean guidance to use built-in tools; indexing stays your decision.
428
430
 
429
- The exact text is `src/mcp/server-instructions.ts` — the single source of truth for the main agent. Because subagents and non-MCP harnesses never see the MCP guidance, the installer also writes a four-line marker-fenced section into the agent's instructions file pointing at the `codegraph explore` / `codegraph node` CLI equivalents.
431
+ The exact text is `src/mcp/server-instructions.ts` — the single source of truth for the main agent. Because subagents and non-MCP harnesses never see the MCP guidance, the installer also writes a short marker-fenced section into the agent's instructions file pointing at the `codegraph explore` CLI equivalent.
430
432
 
431
433
  </details>
432
434
 
@@ -447,7 +449,7 @@ The exact text is `src/mcp/server-instructions.ts` — the single source of trut
447
449
  ┌───────────────────────────────────────────────────────────────────┐
448
450
  │ CodeGraph MCP Server │
449
451
  │ │
450
- explore · search · callers · callees · impact · node
452
+ explore · one call verbatim source + call flow + blast radius
451
453
  │ │ │
452
454
  │ ▼ │
453
455
  │ SQLite knowledge graph │
@@ -471,7 +473,7 @@ The exact text is `src/mcp/server-instructions.ts` — the single source of trut
471
473
  codegraph # Run interactive installer
472
474
  codegraph install # Run installer (explicit)
473
475
  codegraph uninstall # Remove CodeGraph from your agents (inverse of install)
474
- codegraph init [path] # Initialize in a project (--index to also index)
476
+ codegraph init [path] # Initialize a project + build its graph (one step)
475
477
  codegraph uninit [path] # Remove CodeGraph from a project (--force to skip prompt)
476
478
  codegraph index [path] # Full index (--force to re-index, --quiet for less output)
477
479
  codegraph sync [path] # Incremental update
@@ -524,18 +526,15 @@ fi
524
526
 
525
527
  ## MCP Tools
526
528
 
527
- When running as an MCP server, CodeGraph exposes a focused set of four tools — measured agent behavior showed a leaner list steers agents to the right tool and saves context every session:
529
+ When running as an MCP server, CodeGraph exposes a **single tool** `codegraph_explore`. Measured agent behavior showed that one strong tool steers agents better than a menu of narrower ones — fewer mis-picks, and it saves context every session:
528
530
 
529
531
  | Tool | Purpose |
530
532
  |------|---------|
531
- | `codegraph_explore` | **Primary.** Answer almost any question in one call — "how does X work", a flow ("how does X reach Y"), or surveying an area — returning the relevant symbols' verbatim source grouped by file, plus a relationship map and blast radius. Surfaces dynamic-dispatch hops (callbacks, React re-render, interface→impl) grep can't follow. |
532
- | `codegraph_node` | One symbol's full source + caller/callee trail (every overload for an ambiguous name) — or pass a file path to **read a whole file like the Read tool** (same line-numbered output, `offset`/`limit`), with its dependents attached. |
533
- | `codegraph_search` | Find symbols by name across the codebase |
534
- | `codegraph_callers` | Every call site of a function — including where it's registered as a callback — with one section per definition when several share a name |
533
+ | `codegraph_explore` | Answer almost any question in one call — "how does X work", a flow ("how does X reach Y"), or surveying an area — returning the relevant symbols' verbatim source grouped by file, plus the call paths between them and a blast-radius summary. Surfaces dynamic-dispatch hops (callbacks, React re-render, interface→impl) grep can't follow. Name a file or symbol in the query to read its current line-numbered source, the same shape the Read tool gives you. |
535
534
 
536
- Four more tools (`codegraph_callees`, `codegraph_impact`, `codegraph_files`, `codegraph_status`) stay fully functional but unlisted by default — measured across eval runs, agents never or rarely picked them, and their information already arrives inline on the four above (explore's blast-radius section, node's dependents note, a symbol's body as its callee list). Re-enable any of them with the `CODEGRAPH_MCP_TOOLS` environment variable (e.g. `CODEGRAPH_MCP_TOOLS=explore,node,search,callers,impact`), or use their CLI equivalents (`codegraph callees` / `impact` / `files` / `status`).
535
+ The other tools (`codegraph_node`, `codegraph_search`, `codegraph_callers`, `codegraph_callees`, `codegraph_impact`, `codegraph_files`, `codegraph_status`) stay fully functional but **unlisted by default**everything they return already arrives inline on `codegraph_explore` (its blast-radius section, the relationship map, a symbol's body as its callee list). Re-enable any of them for the MCP surface with the `CODEGRAPH_MCP_TOOLS` environment variable (e.g. `CODEGRAPH_MCP_TOOLS=explore,node,search,callers`), or use their CLI equivalents (`codegraph node` / `query` / `callers` / `callees` / `impact` / `files` / `status`).
537
536
 
538
- In a workspace with no `.codegraph/` index, the server announces itself inactive and lists **no** toolsagents work normally with their built-in tools, and indexing stays your decision.
537
+ Even when the server's own root has no `.codegraph/` index, the tools stay available: pass `projectPath` to query any indexed project a sub-service in a monorepo, or a second repo — in the same session. A path that has no index returns clean guidance to use built-in tools instead, so nothing fails loudly, and indexing stays your decision.
539
538
 
540
539
  ---
541
540
 
@@ -586,9 +585,10 @@ that drive the graph directly: `DatabaseConnection`, `QueryBuilder`,
586
585
 
587
586
  ## Configuration
588
587
 
589
- There isn't any — CodeGraph is zero-config, with **no config file** to write or
590
- keep in sync. Language support is automatic from the file extension; there's
591
- nothing to wire up per language.
588
+ Next to none — CodeGraph is **zero-config by default**, with nothing to write or
589
+ keep in sync to get started. Language support is automatic from the file
590
+ extension; there's nothing to wire up per language. The one optional file is for
591
+ mapping [custom file extensions](#custom-file-extensions).
592
592
 
593
593
  What it skips out of the box:
594
594
 
@@ -606,6 +606,29 @@ add a negation — `!vendor/`. The defaults apply uniformly, so committing a
606
606
  dependency or build directory doesn't force it into the graph; the `.gitignore`
607
607
  negation is the explicit opt-in.
608
608
 
609
+ ### Custom file extensions
610
+
611
+ If your project uses a non-standard extension for a [supported
612
+ language](#supported-languages) — say `.dota_lua` for Lua, or `.tpl` for PHP —
613
+ those files are skipped by default, because the extension isn't one CodeGraph
614
+ recognizes. Map them with an optional **`codegraph.json`** at your project root:
615
+
616
+ ```json
617
+ {
618
+ "extensions": {
619
+ ".dota_lua": "lua",
620
+ ".tpl": "php"
621
+ }
622
+ }
623
+ ```
624
+
625
+ Each value is a supported language id. The mappings merge on top of the built-in
626
+ defaults and win on conflict, so you can also re-point a built-in (e.g.
627
+ `".h": "cpp"`). Commit the file to share the mapping with your team. A typo'd
628
+ language or a malformed file is warned about and skipped — it never breaks
629
+ indexing — and a project with no `codegraph.json` behaves exactly as before.
630
+ Re-index (`codegraph index`) after adding or changing mappings.
631
+
609
632
  ## Telemetry
610
633
 
611
634
  CodeGraph collects **anonymous usage statistics** — which tools and commands get
@@ -13,6 +13,15 @@ export declare class DatabaseConnection {
13
13
  private db;
14
14
  private dbPath;
15
15
  private backend;
16
+ /**
17
+ * `dev:ino` of the DB file at the moment we opened it (or null when the
18
+ * platform/filesystem reports no usable inode). Lets us notice when the file
19
+ * we hold open has been unlinked and REPLACED by a new file at the same path
20
+ * — a git worktree removed and re-added, or `.codegraph/` deleted and
21
+ * re-`init`ed under a long-lived server — at which point our fd reads a now
22
+ * dead inode forever (#925). See `isReplacedOnDisk`.
23
+ */
24
+ private openedInode;
16
25
  private constructor();
17
26
  /**
18
27
  * Initialize a new database at the given path
@@ -41,7 +50,7 @@ export declare class DatabaseConnection {
41
50
  *
42
51
  * SQLite silently keeps the prior mode if WAL can't be enabled — e.g. on
43
52
  * filesystems without shared-memory support (some network/virtualized mounts,
44
- * WSL2 /mnt), and always on the wasm backend. So the effective mode can differ
53
+ * WSL2 /mnt). So the effective mode can differ
45
54
  * from what `configureConnection` requested. Surfaced in `codegraph status` so
46
55
  * a "database is locked" report is triageable: 'wal' ⇒ readers never block on a
47
56
  * writer; anything else ⇒ they can. See issue #238.
@@ -89,6 +98,18 @@ export declare class DatabaseConnection {
89
98
  * Check if the database connection is open
90
99
  */
91
100
  isOpen(): boolean;
101
+ /**
102
+ * True when the DB file at our path has been REPLACED on disk since we opened
103
+ * it — a different inode now lives at the same path, so the fd we still hold
104
+ * points at a now-unlinked inode that can never receive new writes (#925).
105
+ * The trigger is removing and recreating `.codegraph/` at the same path under
106
+ * a long-lived process (`git worktree remove` + re-add, or `rm -rf
107
+ * .codegraph` + `codegraph init`). Returns false when the inode is unchanged,
108
+ * when the file is momentarily absent (mid-recreate — nothing to reopen onto
109
+ * yet), or when the platform doesn't report a usable inode (Windows can't
110
+ * unlink an open file and its st_ino is unreliable, so this never fires there).
111
+ */
112
+ isReplacedOnDisk(): boolean;
92
113
  }
93
114
  /**
94
115
  * Default database filename
@@ -268,6 +268,20 @@ export declare class QueryBuilder {
268
268
  * (all kinds except `contains`); same reason imports-only is insufficient.
269
269
  */
270
270
  getDependencyFilePaths(filePath: string): string[];
271
+ /**
272
+ * Cross-file edges whose TARGET is a node in `filePath` and whose SOURCE is a
273
+ * node in a *different* file, paired with the target node's (name, kind) so a
274
+ * caller can re-resolve the edge to the re-indexed target's new ID (node IDs
275
+ * are `sha256(filePath:kind:name:line)`, so any line shift in the callee file
276
+ * changes target IDs and a naive re-insert by old ID silently drops them).
277
+ * Used by `storeExtractionResult` to preserve incoming edges across a file
278
+ * re-index (issue #899). Same edge-kind rules as
279
+ * {@link getDependentFilePaths}: all kinds except `contains`.
280
+ */
281
+ getCrossFileIncomingEdgesWithTarget(filePath: string): Array<Edge & {
282
+ targetName: string;
283
+ targetKind: NodeKind;
284
+ }>;
271
285
  /**
272
286
  * Insert or update a file record
273
287
  */
@@ -73,6 +73,49 @@ export declare function isInitialized(projectRoot: string): boolean;
73
73
  */
74
74
  export declare function unsafeIndexRootReason(projectRoot: string): string | null;
75
75
  export declare function findNearestCodeGraphRoot(startPath: string): string | null;
76
+ /**
77
+ * Indexed sub-project roots beneath `root` (bounded breadth-first scan). For
78
+ * the monorepo case behind #964: the index lives in a CHILD
79
+ * (`packages/x/.codegraph/`), not at the workspace root the agent's cwd points
80
+ * at. Descent stops at the first indexed directory on a branch (a project's
81
+ * own sub-dirs aren't separate projects) and is bounded by depth + count so it
82
+ * never turns into a full-tree crawl on a large repo.
83
+ */
84
+ export declare function findIndexedSubprojectRoots(root: string, opts?: {
85
+ maxDepth?: number;
86
+ max?: number;
87
+ }): string[];
88
+ /**
89
+ * What the front-load hook should do for a prompt issued from a directory.
90
+ */
91
+ export interface FrontloadPlan {
92
+ /** Open + explore this project and inject its source as context. `null` when
93
+ * there's no single project to front-load (none indexed, or several indexed
94
+ * sub-projects with no clear match — see {@link nudgeProjects}). */
95
+ exploreRoot: string | null;
96
+ /** Indexed sub-projects to surface in a "pass `projectPath`" nudge: the rest
97
+ * of a monorepo's indexed projects alongside `exploreRoot`, or — when no one
98
+ * project clearly matches — the full list (with `exploreRoot` null). */
99
+ nudgeProjects: string[];
100
+ /** True when the plan came from scanning DOWN into sub-projects (cwd itself
101
+ * is not under any index) — the monorepo case, where a follow-up
102
+ * `codegraph_explore` needs an explicit `projectPath`. */
103
+ viaSubScan: boolean;
104
+ }
105
+ /**
106
+ * Decide what the front-load hook injects for a `prompt` issued from `cwd`,
107
+ * shaped by where the `.codegraph/` index(es) actually are:
108
+ * 1. **cwd (or an ancestor) is indexed** → front-load that project. The
109
+ * normal single-project / nested-file case.
110
+ * 2. **cwd isn't indexed but looks like a workspace root** → the indexes live
111
+ * in sub-projects (the monorepo case behind #964). One indexed
112
+ * sub-project → front-load it; several → front-load the one the prompt
113
+ * names (by relative path like `packages/api`, or package directory name)
114
+ * and nudge about the rest; several with no match → nudge the full list so
115
+ * the agent passes `projectPath`, rather than guessing wrong.
116
+ * 3. **nothing indexed reachable** → do nothing (the agent's own tools apply).
117
+ */
118
+ export declare function planFrontload(cwd: string, prompt: string): FrontloadPlan;
76
119
  /**
77
120
  * Create the .codegraph directory structure
78
121
  * Note: Only throws if codegraph.db already exists, not just if .codegraph/ exists.
@@ -16,8 +16,12 @@ export declare const EXTENSION_MAP: Record<string, Language>;
16
16
  * Whether a file is one CodeGraph can parse, based purely on its extension.
17
17
  * This is the single source of truth for "should we index this file" — derived
18
18
  * from EXTENSION_MAP so parser support and indexing selection never drift.
19
+ *
20
+ * `overrides` is the project's validated custom extension → language map (from
21
+ * `codegraph.json`); when present its extensions count as indexable in addition
22
+ * to the built-ins. Omitting it is byte-identical to the zero-config behavior.
19
23
  */
20
- export declare function isSourceFile(filePath: string): boolean;
24
+ export declare function isSourceFile(filePath: string, overrides?: Record<string, Language>): boolean;
21
25
  /**
22
26
  * Shopify OS 2.0 JSON template (`templates/*.json`) or section group
23
27
  * (`sections/*.json`) — these reference sections by `"type"`, so the Liquid
@@ -57,9 +61,13 @@ export declare function isGrammarsInitialized(): boolean;
57
61
  */
58
62
  export declare function getParser(language: Language): Parser | null;
59
63
  /**
60
- * Detect language from file extension
64
+ * Detect language from file extension.
65
+ *
66
+ * `overrides` is the project's validated custom extension → language map (from
67
+ * `codegraph.json`); when present its mappings take precedence over the built-in
68
+ * `EXTENSION_MAP`. Omitting it is byte-identical to the zero-config behavior.
61
69
  */
62
- export declare function detectLanguage(filePath: string, source?: string): Language;
70
+ export declare function detectLanguage(filePath: string, source?: string, overrides?: Record<string, Language>): Language;
63
71
  /**
64
72
  * Check if a language is supported (has a grammar defined).
65
73
  * Returns true if the grammar exists, even if not yet loaded.
@@ -147,6 +147,19 @@ export interface LanguageExtractor {
147
147
  * Used by languages with fundamentally different AST structures (e.g. Pascal).
148
148
  */
149
149
  visitNode?: (node: SyntaxNode, ctx: ExtractorContext) => boolean;
150
+ /**
151
+ * Synthesize members that exist at compile time but not in the source AST,
152
+ * called at the end of class extraction with the class still on the scope
153
+ * stack (so `ctx.createNode` attaches containment + qualified names) and the
154
+ * class's real members already extracted (so the hook can skip a member the
155
+ * source explicitly declares). Used by Java for Lombok-generated accessors
156
+ * (`@Getter`/`@Setter`/`@Data`/`@Value`/`@Builder` → `getX`/`setX`/`builder`/
157
+ * `equals`/`hashCode`/`toString` + the `log` field), which are otherwise
158
+ * invisible and break call-chain analysis (#912). The created nodes carry a
159
+ * `lombok` decorator + a docstring naming the generating annotation, so an
160
+ * agent can tell them apart from hand-written code.
161
+ */
162
+ synthesizeMembers?: (classNode: SyntaxNode, ctx: ExtractorContext) => void;
150
163
  /**
151
164
  * Classify a class_declaration node when the grammar reuses one node type
152
165
  * for multiple concepts (e.g. Swift uses class_declaration for classes, structs, and enums).
@@ -16,12 +16,19 @@ export declare class TreeSitterExtractor {
16
16
  private nodes;
17
17
  private edges;
18
18
  private unresolvedReferences;
19
+ private static readonly VALUE_REF_LANGS;
20
+ private static readonly MAX_VALUE_REF_NODES;
21
+ private readonly valueRefsEnabled;
22
+ private fileScopeValues;
23
+ private fileScopeValueCounts;
24
+ private valueRefScopes;
19
25
  private errors;
20
26
  private extractor;
21
27
  private nodeStack;
22
28
  private methodIndex;
23
29
  private fnRefSpec;
24
30
  private fnRefCandidates;
31
+ private vueStoreFile;
25
32
  constructor(filePath: string, source: string, language?: Language);
26
33
  /**
27
34
  * Parse and extract from the source code
@@ -59,6 +66,21 @@ export declare class TreeSitterExtractor {
59
66
  * callback + same-file ops struct) — is.
60
67
  */
61
68
  private flushFnRefCandidates;
69
+ /**
70
+ * Record value-reference bookkeeping as nodes are created: file-scope const/var symbols with
71
+ * distinctive names become reference targets; function/method/const/var symbols become reader
72
+ * scopes whose bodies flushValueRefs scans.
73
+ */
74
+ private captureValueRefScope;
75
+ /**
76
+ * Emit same-file `references` edges from a symbol to the file-scope const/var it reads (TS/JS).
77
+ * The engine doesn't edge const→consumer, so impact analysis misses "change this table, affect
78
+ * its readers" (the ReScript-PR false positive). Same-file only (resolution is unambiguous),
79
+ * distinctive target names only (dodges the local-shadowing precision trap documented on
80
+ * function_ref), deduped per (reader, target). Default on (CODEGRAPH_VALUE_REFS=0 disables) +
81
+ * additive. Shadowed targets are pruned — see below.
82
+ */
83
+ private flushValueRefs;
62
84
  /**
63
85
  * Visit a node and extract information
64
86
  */
@@ -92,10 +114,41 @@ export declare class TreeSitterExtractor {
92
114
  * (class, struct, interface, trait). File nodes do not count as class-like.
93
115
  */
94
116
  private isInsideClassLikeNode;
117
+ /**
118
+ * Ruby `CONST = …` assignment whose LHS is a `constant` node — a class/module
119
+ * (or top-level) constant worth extracting as a symbol even inside a class.
120
+ * Other languages don't give an assignment a `constant`-typed LHS, so this
121
+ * gate is effectively Ruby-only.
122
+ */
123
+ private isClassScopeConstantAssignment;
95
124
  /**
96
125
  * Extract a function
97
126
  */
98
127
  private extractFunction;
128
+ /**
129
+ * Detect a React component declared via an HOC wrapper whose result is itself a
130
+ * component: `forwardRef(...)`, `memo(...)`, `React.forwardRef/memo(...)`, and
131
+ * styled-components / emotion `styled.tag\`…\`` / `styled(Base)\`…\``. These
132
+ * initializers are a call / tagged-template (not a bare arrow), so the const is
133
+ * otherwise classified `constant` — and a constant is skipped by both the
134
+ * JSX-render edge synthesizer and component resolution, so `<Button/>` usages
135
+ * get no edge and callers/impact silently return empty (#841).
136
+ *
137
+ * Returns `{ inner }` — the inline render function to extract as the component
138
+ * body, or `null` when the wrapper has no inline function (`memo(Imported)`,
139
+ * `styled.button\`…\``) and only a bodyless component node is minted — or
140
+ * `undefined` when this initializer is not a recognized component wrapper.
141
+ */
142
+ private reactComponentHoc;
143
+ /**
144
+ * Emit a `component` node for an HOC-wrapped React component declaration (see
145
+ * reactComponentHoc). Named by the declarator (`Button`) and located at it so
146
+ * the node range spans the body. When the wrapper has an inline render
147
+ * function, its body is walked so the component's callees (hooks, helpers) are
148
+ * captured under the component node — matching how a plain
149
+ * `const Foo = () => …` arrow component already behaves.
150
+ */
151
+ private extractReactComponentNode;
99
152
  /**
100
153
  * Extract a class
101
154
  */
@@ -157,6 +210,72 @@ export declare class TreeSitterExtractor {
157
210
  * `=> { return {...} }` block. Returns null for any other body shape.
158
211
  */
159
212
  private functionReturnedObject;
213
+ /**
214
+ * RTK Query: from a `createApi({ ..., endpoints: build => ({...}) })` or a
215
+ * `baseApi.injectEndpoints({ endpoints: build => ({...}) })` call initializer,
216
+ * return the object literal of endpoint definitions (the object the `endpoints`
217
+ * arrow returns). Returns null for any other call — the common case — so this
218
+ * stays cheap and silent. Keyed on the RTK entry-point names (`createApi` /
219
+ * `injectEndpoints`) like the framework extractors key on their library APIs.
220
+ */
221
+ private findRtkEndpointsObject;
222
+ /**
223
+ * Extract each RTK Query endpoint (`getX: build.query({...})` / `build.mutation`)
224
+ * as a function node named by the endpoint key, spanning its primary handler
225
+ * (the `queryFn`/`query` arrow) so the fetch logic's calls attribute to the
226
+ * endpoint. Without this an endpoint exists only as an object-literal property —
227
+ * never a node — so the generated `useXQuery` hook can't be bridged to it.
228
+ */
229
+ private extractRtkEndpoints;
230
+ /**
231
+ * The primary handler arrow of a `build.query({ queryFn|query: (…) => … })`
232
+ * endpoint — prefers `queryFn`, then `query`, else the first function-valued
233
+ * property. Returns null when the endpoint is config-only (no handler arrow).
234
+ */
235
+ private rtkEndpointHandler;
236
+ /**
237
+ * RTK Query generated-hook bindings. `export const { useGetXQuery,
238
+ * useUpdateYMutation } = someApi` destructures the hooks RTK generates per
239
+ * endpoint off a createApi result. They are real exported symbols that
240
+ * components import, but destructured bindings aren't otherwise extracted —
241
+ * mint a function node per binding matching the RTK hook convention so the hook
242
+ * resolves and the synthesizer can bridge it to its endpoint. Gated tight by the
243
+ * caller (object-pattern off a bare identifier) + the name convention here, so
244
+ * ordinary destructures stay unextracted.
245
+ */
246
+ private extractRtkHookBindings;
247
+ /** Cheap per-file heuristic: the file carries ≥2 distinct Vue-store signals
248
+ * (defineStore/createStore/Vuex, or the actions/mutations/getters/namespaced
249
+ * vocabulary). Gates the non-exported `const actions = {…}` Vuex-module form so
250
+ * a stray `const actions` in unrelated code is never mistaken for a store. */
251
+ private looksLikeVueStoreFile;
252
+ /** True if an object literal has ≥1 inline function member (`key: () => …` /
253
+ * `method(){}`) — distinguishes an inline action map (zustand/SvelteKit form
254
+ * actions) from a Pinia SETUP store's all-shorthand `return { foo, bar }`
255
+ * (whose functions are body-local consts, walked normally instead). */
256
+ private objectHasInlineFunctions;
257
+ /** Vue store action/mutation/getter collections defined INLINE in a store call:
258
+ * `defineStore({ actions: {…}, getters: {…} })` (Pinia options form),
259
+ * `defineStore('id', { actions: {…} })`, `createStore({ mutations: {…} })`,
260
+ * `new Vuex.Store({ actions: {…} })`. Returns the object literals under those
261
+ * keys so their methods become nodes. Gated on the store-factory callee. */
262
+ private findVueStoreCollectionObjects;
263
+ /** Extract the methods of a store-config object's `actions`/`mutations`/`getters`
264
+ * properties. Used for the canonical Vuex MODULE shape `export default {
265
+ * namespaced, actions: {…}, mutations: {…} }` — object-literal methods aren't
266
+ * otherwise extracted, so the actions/mutations would never be nodes. */
267
+ private extractStoreCollectionMethods;
268
+ /** The SETUP function of a Pinia setup store (`defineStore('id', () => {…})`)
269
+ * — an arrow/function arg with a block body. Returns null for the options form
270
+ * (`defineStore({…})`) and for any non-defineStore call. The setup body's local
271
+ * function consts are the store's actions; the generic body walk doesn't reach
272
+ * them (nested functions are separate scopes), so they're extracted explicitly. */
273
+ private findPiniaSetupFn;
274
+ /** Extract a Pinia setup store's actions: the body-local `const foo = () => …`
275
+ * / `function foo(){}` declarations, named by the binding. (State refs and other
276
+ * consts are left to the normal value-extraction; only the functions matter as
277
+ * the store's callable surface.) */
278
+ private extractPiniaSetupBody;
160
279
  /**
161
280
  * Extract a variable declaration (const, let, var, etc.)
162
281
  *
package/dist/index.d.ts CHANGED
@@ -66,6 +66,28 @@ export declare class CodeGraph {
66
66
  private fileLock;
67
67
  private watcher;
68
68
  private constructor();
69
+ /**
70
+ * (Re)build the query/extraction/graph layers over the current `this.queries`
71
+ * (which wraps `this.db`). Factored out of the constructor so `reopenIfReplaced`
72
+ * can rebuild them against a fresh connection without duplicating the wiring.
73
+ * The path-based `fileLock` is independent of the DB handle, so it stays put.
74
+ */
75
+ private wireLayers;
76
+ /**
77
+ * Heal a stale database handle in place. If `.codegraph/` was removed and
78
+ * recreated at the SAME path while this instance held the DB open — a git
79
+ * worktree removed and re-added, or `rm -rf .codegraph` + `codegraph init` —
80
+ * our open fd points at the now-unlinked inode and can never see the new
81
+ * index, so every query returns the pre-removal snapshot until the process
82
+ * restarts (#925). When that's detected, open the live file at the same path,
83
+ * rebuild the query layers, and swap them IN PLACE, so every holder of this
84
+ * instance (the MCP daemon's default project, cached projectPath connections)
85
+ * heals without a restart. Returns true iff it reopened.
86
+ *
87
+ * POSIX-only in practice: `isReplacedOnDisk` never fires on Windows (an open
88
+ * file can't be unlinked there, and st_ino is unreliable).
89
+ */
90
+ reopenIfReplaced(): boolean;
69
91
  /**
70
92
  * Initialize a new CodeGraph project
71
93
  *
@@ -144,6 +166,17 @@ export declare class CodeGraph {
144
166
  * Check if the file watcher is active.
145
167
  */
146
168
  isWatching(): boolean;
169
+ /**
170
+ * True once live watching has permanently degraded (OS watch-resource
171
+ * exhaustion, or a write lock held past the retry budget) and auto-sync is
172
+ * disabled until the next {@link watch} call. Distinct from `!isWatching()`:
173
+ * a stopped/never-started watcher is inactive but NOT degraded. MCP tools use
174
+ * this to surface a whole-index "results may be stale" notice, since
175
+ * `getPendingFiles()` goes empty once watching stops (#876).
176
+ */
177
+ isWatcherDegraded(): boolean;
178
+ /** The reason live watching degraded, or null if it is healthy (#876). */
179
+ getWatcherDegradedReason(): string | null;
147
180
  /**
148
181
  * Files seen by the file watcher since the last successful sync —
149
182
  * the per-file "stale" signal MCP tools attach to responses so an agent
@@ -17,8 +17,8 @@
17
17
  * runs without this block, and consistently with it — including runs
18
18
  * with zero Read/grep fallback.
19
19
  * - **Non-MCP harnesses** — agents with no MCP client at all can still
20
- * run the `codegraph explore` / `codegraph node` CLI, which prints the
21
- * same output as the MCP tools.
20
+ * run the `codegraph explore` CLI, which prints the same output as the
21
+ * MCP tool.
22
22
  *
23
23
  * Keep this block SHORT. The main agent reads it every turn on top of the
24
24
  * server instructions — the #529 duplication-cost argument still bounds
@@ -37,5 +37,5 @@ export declare const CODEGRAPH_SECTION_END = "<!-- CODEGRAPH_END -->";
37
37
  * indexed" claim would send subagents into failing codegraph calls (the
38
38
  * noise the unindexed-session policy exists to prevent).
39
39
  */
40
- export declare const CODEGRAPH_INSTRUCTIONS_BLOCK = "<!-- CODEGRAPH_START -->\n## CodeGraph\n\nIn repositories indexed by CodeGraph (a `.codegraph/` directory exists at the repo root), reach for it BEFORE grep/find or reading files when you need to understand or locate code:\n\n- **MCP tools** (when available): `codegraph_explore` answers most code questions in one call \u2014 the relevant symbols' verbatim source plus the call paths between them. `codegraph_node` returns one symbol's source + callers, or reads a whole file with line numbers. If the tools are listed but deferred, load them by name via tool search.\n- **Shell** (always works): `codegraph explore \"<symbol names or question>\"` and `codegraph node <symbol-or-file>` print the same output.\n\nIf there is no `.codegraph/` directory, skip CodeGraph entirely \u2014 indexing is the user's decision.\n<!-- CODEGRAPH_END -->";
40
+ export declare const CODEGRAPH_INSTRUCTIONS_BLOCK = "<!-- CODEGRAPH_START -->\n## CodeGraph\n\nIn repositories indexed by CodeGraph (a `.codegraph/` directory exists at the repo root), reach for it BEFORE grep/find or reading files when you need to understand or locate code:\n\n- **MCP tool** (when available): `codegraph_explore` answers most code questions in one call \u2014 the relevant symbols' verbatim source plus the call paths between them, including dynamic-dispatch hops grep can't follow. Name a file or symbol in the query to read its current line-numbered source. If it's listed but deferred, load it by name via tool search.\n- **Shell** (always works): `codegraph explore \"<symbol names or question>\"` prints the same output.\n\nIf there is no `.codegraph/` directory, skip CodeGraph entirely \u2014 indexing is the user's decision.\n<!-- CODEGRAPH_END -->";
41
41
  //# sourceMappingURL=instructions-template.d.ts.map