@colbymchenry/codegraph 0.9.9 → 1.0.1

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.
Files changed (52) hide show
  1. package/README.md +120 -24
  2. package/dist/bin/codegraph.d.ts +1 -0
  3. package/dist/bin/fatal-handler.d.ts +20 -0
  4. package/dist/db/migrations.d.ts +1 -1
  5. package/dist/db/queries.d.ts +43 -0
  6. package/dist/db/sqlite-adapter.d.ts +7 -0
  7. package/dist/directory.d.ts +49 -2
  8. package/dist/extraction/astro-extractor.d.ts +79 -0
  9. package/dist/extraction/extraction-version.d.ts +25 -0
  10. package/dist/extraction/function-ref.d.ts +118 -0
  11. package/dist/extraction/grammars.d.ts +7 -1
  12. package/dist/extraction/index.d.ts +34 -0
  13. package/dist/extraction/languages/c-cpp.d.ts +8 -0
  14. package/dist/extraction/languages/csharp.d.ts +22 -0
  15. package/dist/extraction/languages/r.d.ts +3 -0
  16. package/dist/extraction/languages/typescript.d.ts +13 -0
  17. package/dist/extraction/liquid-extractor.d.ts +7 -0
  18. package/dist/extraction/razor-extractor.d.ts +42 -0
  19. package/dist/extraction/tree-sitter-types.d.ts +33 -0
  20. package/dist/extraction/tree-sitter.d.ts +211 -0
  21. package/dist/extraction/vue-extractor.d.ts +15 -0
  22. package/dist/index.d.ts +34 -2
  23. package/dist/installer/instructions-template.d.ts +34 -11
  24. package/dist/installer/targets/opencode.d.ts +9 -1
  25. package/dist/installer/targets/shared.d.ts +14 -0
  26. package/dist/mcp/daemon-manager.d.ts +42 -0
  27. package/dist/mcp/daemon-registry.d.ts +47 -0
  28. package/dist/mcp/daemon.d.ts +60 -1
  29. package/dist/mcp/dynamic-boundaries.d.ts +41 -0
  30. package/dist/mcp/index.d.ts +1 -0
  31. package/dist/mcp/liveness-watchdog.d.ts +18 -0
  32. package/dist/mcp/ppid-watchdog.d.ts +44 -0
  33. package/dist/mcp/proxy.d.ts +6 -0
  34. package/dist/mcp/server-instructions.d.ts +12 -1
  35. package/dist/mcp/session.d.ts +2 -0
  36. package/dist/mcp/stdin-teardown.d.ts +27 -0
  37. package/dist/mcp/tools.d.ts +71 -0
  38. package/dist/resolution/callback-synthesizer.d.ts +3 -3
  39. package/dist/resolution/frameworks/astro.d.ts +9 -0
  40. package/dist/resolution/frameworks/index.d.ts +1 -0
  41. package/dist/resolution/import-resolver.d.ts +10 -0
  42. package/dist/resolution/index.d.ts +80 -0
  43. package/dist/resolution/name-matcher.d.ts +61 -0
  44. package/dist/resolution/types.d.ts +27 -3
  45. package/dist/resolution/workspace-packages.d.ts +48 -0
  46. package/dist/search/query-utils.d.ts +17 -1
  47. package/dist/sync/watcher.d.ts +124 -32
  48. package/dist/telemetry/index.d.ts +146 -0
  49. package/dist/types.d.ts +17 -2
  50. package/dist/upgrade/index.d.ts +132 -0
  51. package/dist/utils.d.ts +30 -24
  52. package/package.json +7 -7
@@ -32,5 +32,20 @@ export declare class VueExtractor {
32
32
  * Process a script block by delegating to TreeSitterExtractor
33
33
  */
34
34
  private processScriptBlock;
35
+ /**
36
+ * Extract component usages from the Vue `<template>`.
37
+ *
38
+ * PascalCase tags (`<Modal>`, `<Button />`) and kebab-case tags
39
+ * (`<my-button>`) both represent component instantiations — analogous to
40
+ * function calls in imperative code. Capturing them creates parent→child
41
+ * component edges and lets `callers` / `impact` see a component that is
42
+ * only ever used in markup. Vue's extractor previously parsed only the
43
+ * `<script>` block, so these usages produced no edge at all (#629).
44
+ *
45
+ * HTML elements (lowercase, no hyphen) and Vue built-ins are skipped.
46
+ * Unmatched names create no edge during resolution, so converting
47
+ * kebab-case is safe even for native custom elements.
48
+ */
49
+ private extractTemplateComponents;
35
50
  }
36
51
  //# sourceMappingURL=vue-extractor.d.ts.map
package/dist/index.d.ts CHANGED
@@ -158,8 +158,8 @@ export declare class CodeGraph {
158
158
  */
159
159
  getPendingFiles(): PendingFile[];
160
160
  /**
161
- * Resolves once the file watcher has finished its initial chokidar scan.
162
- * Useful for tests that need a deterministic boundary before asserting on
161
+ * Resolves once the file watcher has installed its watch set. Useful for
162
+ * tests that need a deterministic boundary before asserting on
163
163
  * `getPendingFiles()`. Resolves immediately when no watcher is active.
164
164
  */
165
165
  waitUntilWatcherReady(timeoutMs?: number): Promise<void>;
@@ -171,6 +171,30 @@ export declare class CodeGraph {
171
171
  modified: string[];
172
172
  removed: string[];
173
173
  };
174
+ /**
175
+ * Most recent index timestamp (ms since epoch) across all tracked files, or
176
+ * null when nothing is indexed yet. Lets library consumers check index
177
+ * freshness without shelling out to `codegraph status --json`. (#329)
178
+ */
179
+ getLastIndexedAt(): number | null;
180
+ /**
181
+ * Which engine built the current index: the package version + extraction
182
+ * version stamped at the last full `indexAll`. Either field is null for an
183
+ * index built before stamping existed (treated as stale). See
184
+ * `extraction-version.ts` and `isIndexStale()`.
185
+ */
186
+ getIndexBuildInfo(): {
187
+ version: string | null;
188
+ extractionVersion: number | null;
189
+ };
190
+ /**
191
+ * True when the on-disk index was built by an engine whose extraction is
192
+ * older than the one now running — i.e. a re-index would add data a migration
193
+ * can't backfill. False when there's no index yet (nothing to refresh) or the
194
+ * stamp is current. This is the signal behind `codegraph status`'s re-index
195
+ * hint and `codegraph upgrade`'s reminder.
196
+ */
197
+ isIndexStale(): boolean;
174
198
  /**
175
199
  * Extract nodes and edges from source code (without storing)
176
200
  */
@@ -237,6 +261,14 @@ export declare class CodeGraph {
237
261
  * Search nodes by text
238
262
  */
239
263
  searchNodes(query: string, options?: SearchOptions): SearchResult[];
264
+ /**
265
+ * Normalized project-name tokens (go.mod / package.json / repo dir) used to
266
+ * down-weight the non-discriminative project name in search ranking (#720).
267
+ * Exposed so explore can exclude it from the PascalCase type-disambiguation
268
+ * bias, which would otherwise pull overloaded tokens toward whichever stack
269
+ * embeds the project name.
270
+ */
271
+ getProjectNameTokens(): Set<string>;
240
272
  /**
241
273
  * Find the project's "primary route file" — the file with the densest
242
274
  * concentration of framework-emitted `route` nodes (≥3 routes, ≥30%
@@ -1,18 +1,41 @@
1
1
  /**
2
- * Marker constants for the legacy agent-instructions block.
2
+ * The marker-fenced agent-instructions block the installer writes into each
3
+ * agent's instructions file (CLAUDE.md / AGENTS.md / GEMINI.md).
3
4
  *
4
- * Codegraph used to write a `## CodeGraph` usage guide into each
5
- * agent's instructions file (CLAUDE.md / AGENTS.md / GEMINI.md /
6
- * codegraph.mdc / Kiro steering doc). That duplicated the guidance the
7
- * MCP server already emits in its `initialize` response every agent
8
- * read the same playbook twice each turn (issue #529). The installer no
9
- * longer writes an instructions file; the MCP server instructions in
10
- * `mcp/server-instructions.ts` are the single source of truth.
5
+ * History: pre-#529 the installer wrote a full usage playbook here, which
6
+ * duplicated the MCP `initialize` instructions for the main agent so it
7
+ * was removed and `mcp/server-instructions.ts` became the single source of
8
+ * truth. A much smaller block returned for #704, because the MCP
9
+ * instructions cannot reach two audiences that the instructions FILE does
10
+ * reach:
11
11
  *
12
- * These markers are retained so install (self-heal on upgrade) and
13
- * uninstall can find and strip the block a previous install wrote.
12
+ * - **Task-tool subagents** they receive the project instructions file
13
+ * in their context but NOT the MCP initialize instructions. They hold
14
+ * the codegraph MCP tools only as deferred names and rarely think to
15
+ * load them: measured on a forced-delegation flow question (excalidraw,
16
+ * sonnet, high effort), subagents loaded + used codegraph in ~1 of 9
17
+ * runs without this block, and consistently with it — including runs
18
+ * with zero Read/grep fallback.
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.
22
+ *
23
+ * Keep this block SHORT. The main agent reads it every turn on top of the
24
+ * server instructions — the #529 duplication-cost argument still bounds
25
+ * its size. Command names and the two surfaces, nothing more.
14
26
  */
15
- /** Markers used by the marker-based section removal. */
27
+ /** Markers used by the marker-based section write/removal. */
16
28
  export declare const CODEGRAPH_SECTION_START = "<!-- CODEGRAPH_START -->";
17
29
  export declare const CODEGRAPH_SECTION_END = "<!-- CODEGRAPH_END -->";
30
+ /**
31
+ * The full block, markers included, exactly as written to disk.
32
+ *
33
+ * The wording is deliberately CONDITIONAL ("in repositories indexed by…"):
34
+ * a global install writes this into a user-scope file (~/.claude/CLAUDE.md,
35
+ * ~/.codex/AGENTS.md) that applies to every project the user opens —
36
+ * including unindexed ones, where an unconditional "this repository is
37
+ * indexed" claim would send subagents into failing codegraph calls (the
38
+ * noise the unindexed-session policy exists to prevent).
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 -->";
18
41
  //# sourceMappingURL=instructions-template.d.ts.map
@@ -2,10 +2,18 @@
2
2
  * opencode target.
3
3
  *
4
4
  * - MCP server entry to `~/.config/opencode/opencode.jsonc` (global,
5
- * XDG-style; `%APPDATA%/opencode/opencode.jsonc` on Windows) or
5
+ * XDG-style on EVERY platform, Windows included — see below) or
6
6
  * `./opencode.jsonc` (local). Falls back to `opencode.json` when a
7
7
  * `.json` file already exists; defaults new installs to `.jsonc`
8
8
  * because that's what opencode itself creates on first run.
9
+ *
10
+ * opencode resolves its config dir with the `xdg-basedir` package
11
+ * (sst/opencode `packages/core/src/global.ts`): `XDG_CONFIG_HOME`
12
+ * if set, else `~/.config` — unconditionally, on all platforms. It
13
+ * never reads `%APPDATA%`; that layout belonged to the discontinued
14
+ * Go fork. We previously wrote there on Windows, so opencode never
15
+ * saw the entry (#535) — install/uninstall now also sweep a stale
16
+ * codegraph entry out of the legacy `%APPDATA%/opencode` location.
9
17
  * - Instructions to `~/.config/opencode/AGENTS.md` (global) or
10
18
  * `./AGENTS.md` (local). opencode reads AGENTS.md for agent
11
19
  * instructions — same convention Codex CLI uses.
@@ -64,6 +64,20 @@ export declare function jsonDeepEqual(a: unknown, b: unknown): boolean;
64
64
  * existing block already matches `body`.
65
65
  */
66
66
  export declare function replaceOrAppendMarkedSection(filePath: string, body: string, startMarker: string, endMarker: string): 'created' | 'updated' | 'appended' | 'unchanged';
67
+ /**
68
+ * Upsert the CodeGraph instructions block into an agent instructions
69
+ * file (CLAUDE.md / AGENTS.md / GEMINI.md). The one write shared by
70
+ * every target: self-heals a stale pre-#529 long block (markers match →
71
+ * replaced by the current short one), appends after existing user
72
+ * content otherwise, and reports `unchanged` on byte-equal re-runs so
73
+ * install stays idempotent. See `instructions-template.ts` for why this
74
+ * block exists (#704: subagents + non-MCP harnesses never see the MCP
75
+ * initialize instructions).
76
+ */
77
+ export declare function upsertInstructionsEntry(file: string): {
78
+ path: string;
79
+ action: 'created' | 'updated' | 'unchanged';
80
+ };
67
81
  /**
68
82
  * Inverse of `replaceOrAppendMarkedSection`. Strips the marker
69
83
  * block from `filePath` if present. If the file becomes empty after
@@ -0,0 +1,42 @@
1
+ import type { DaemonRecord, StopResult } from './daemon-registry';
2
+ /** Sentinel option values (not real roots, so they can't collide with a project path). */
3
+ export declare const STOP_ALL = "__stop_all__";
4
+ export declare const CANCEL = "__cancel__";
5
+ export interface PickItem {
6
+ value: string;
7
+ label: string;
8
+ hint?: string;
9
+ }
10
+ /** Compact uptime: `45s`, `12m`, `3h 5m`. */
11
+ export declare function formatUptime(ms: number): string;
12
+ /**
13
+ * Build the ordered, UI-ready option list: the current project's daemon first
14
+ * (so it's the auto-selected default), the rest newest-first, then "Stop all"
15
+ * (only when there's more than one) and "Cancel".
16
+ */
17
+ export declare function buildPickItems(daemons: DaemonRecord[], cwdRoot: string | null, now: number): PickItem[];
18
+ export interface PickerDeps {
19
+ list: () => DaemonRecord[];
20
+ stop: (root: string) => Promise<StopResult>;
21
+ stopAll: () => Promise<StopResult[]>;
22
+ /** Realpath'd root of the current project's daemon, or null. */
23
+ cwdRoot: string | null;
24
+ now: () => number;
25
+ /** Render the picker; returns the chosen value or a cancel sentinel. */
26
+ select: (opts: {
27
+ message: string;
28
+ options: PickItem[];
29
+ initialValue: string;
30
+ }) => Promise<unknown>;
31
+ isCancel: (v: unknown) => boolean;
32
+ /** Per-action note (e.g. "Stopped daemon …"). */
33
+ note: (msg: string) => void;
34
+ /** Final line + teardown (clack outro). */
35
+ done: (msg: string) => void;
36
+ }
37
+ /**
38
+ * Pick a daemon → stop it → re-prompt with what's left, until the user cancels
39
+ * (Esc / Ctrl-C / "Cancel"), picks "Stop all", or nothing remains.
40
+ */
41
+ export declare function runDaemonPicker(deps: PickerDeps): Promise<void>;
42
+ //# sourceMappingURL=daemon-manager.d.ts.map
@@ -0,0 +1,47 @@
1
+ export interface DaemonRecord {
2
+ /** Realpath'd project root the daemon serves. */
3
+ root: string;
4
+ pid: number;
5
+ version: string;
6
+ socketPath: string;
7
+ /** Epoch ms when the daemon bound its socket. */
8
+ startedAt: number;
9
+ }
10
+ /**
11
+ * `~/.codegraph/daemons` — GLOBAL, keyed off the home install dir. (The
12
+ * `CODEGRAPH_DIR` env var only renames the per-project index dir, not this.)
13
+ */
14
+ export declare function getRegistryDir(): string;
15
+ /**
16
+ * Is `pid` a live process? `kill(pid, 0)` sends no signal — it just probes:
17
+ * ESRCH ⇒ dead, EPERM ⇒ alive but not ours (still alive). Same liveness check
18
+ * the PPID watchdog (#277) and daemon lock arbitration use.
19
+ */
20
+ export declare function isProcessAlive(pid: number): boolean;
21
+ /** Best-effort: record this daemon so `list`/`stop --all` can find it. */
22
+ export declare function registerDaemon(rec: DaemonRecord): void;
23
+ /** Best-effort: drop this daemon's record on graceful shutdown. */
24
+ export declare function deregisterDaemon(root: string): void;
25
+ /**
26
+ * All registered daemons whose process is still alive, newest first. Dead/garbage
27
+ * records are deleted as a side effect (self-healing) unless `prune` is false.
28
+ */
29
+ export declare function listDaemons(opts?: {
30
+ prune?: boolean;
31
+ }): DaemonRecord[];
32
+ export interface StopResult {
33
+ root: string;
34
+ pid: number | null;
35
+ /** 'term' graceful, 'kill' force, 'not-running' stale lock, 'no-daemon' none found. */
36
+ outcome: 'term' | 'kill' | 'not-running' | 'no-daemon';
37
+ }
38
+ /**
39
+ * Stop the daemon serving `root`: SIGTERM, wait, then SIGKILL if it won't go,
40
+ * then sweep its artifacts. `root` must be realpath'd (match how the daemon
41
+ * keys its socket/lockfile). Resolves the pid from the authoritative lockfile,
42
+ * falling back to the registry.
43
+ */
44
+ export declare function stopDaemonAt(root: string): Promise<StopResult>;
45
+ /** Stop every registered, live daemon. */
46
+ export declare function stopAllDaemons(): Promise<StopResult[]>;
47
+ //# sourceMappingURL=daemon-registry.d.ts.map
@@ -54,6 +54,20 @@ export interface DaemonHello {
54
54
  socketPath: string;
55
55
  protocol: 1;
56
56
  }
57
+ /**
58
+ * Optional reverse-handshake line a proxy sends right after it verifies the
59
+ * daemon hello, carrying its own pids so the daemon can reap the client if its
60
+ * process dies WITHOUT the socket ever signalling close (the Windows named-pipe
61
+ * hazard behind #692). Entirely optional and fail-safe: a connection that never
62
+ * sends it (a legacy/direct client) just falls back to the socket-close
63
+ * lifecycle. The `codegraph_client` marker is what tells it apart from the
64
+ * client's first JSON-RPC message.
65
+ */
66
+ export interface DaemonClientHello {
67
+ codegraph_client: 1;
68
+ pid: number;
69
+ hostPid: number | null;
70
+ }
57
71
  export interface DaemonStartResult {
58
72
  /** Always-non-null for a successfully-started daemon. */
59
73
  socketPath: string;
@@ -75,14 +89,21 @@ export declare class Daemon {
75
89
  private projectRoot;
76
90
  private server;
77
91
  private clients;
92
+ /** Per-client peer pids from the optional client-hello, for the liveness sweep. */
93
+ private clientPeers;
78
94
  private idleTimer;
79
95
  private idleTimeoutMs;
96
+ private maxIdleMs;
97
+ private lastActivityAt;
98
+ private maxIdleTimer;
99
+ private clientSweepTimer;
80
100
  private engine;
81
101
  private stopping;
82
102
  private socketPath;
83
103
  private pidPath;
84
104
  constructor(projectRoot: string, opts?: {
85
105
  idleTimeoutMs?: number;
106
+ maxIdleMs?: number;
86
107
  });
87
108
  /**
88
109
  * Bind the socket, kick off engine init, and register signal handlers. The
@@ -101,6 +122,26 @@ export declare class Daemon {
101
122
  private dropClient;
102
123
  private armIdleTimer;
103
124
  private disarmIdleTimer;
125
+ /**
126
+ * Defense-in-depth against a daemon that outlives its clients (#692), for the
127
+ * cases the refcount + idle timer miss because a socket close never arrives:
128
+ * - **Inactivity backstop:** exit if no inbound traffic for `maxIdleMs` while
129
+ * clients are still (nominally) connected. A phantom client sends nothing,
130
+ * so it can't pin the daemon past this window.
131
+ * - **Liveness sweep:** drop any client whose peer process has died (per the
132
+ * client-hello pids), which re-arms the idle timer once the last real
133
+ * client is gone. Catches a dead peer within one sweep instead of waiting
134
+ * out the whole backstop.
135
+ * Both timers are unref'd — the listening server keeps the loop alive, and
136
+ * neither should hold it open on its own.
137
+ */
138
+ private startLivenessTimers;
139
+ /**
140
+ * Drop every connected client whose peer process is gone. Returns the count
141
+ * reaped. `isAlive` is injected for testing. Clients with unknown pids (no
142
+ * client-hello) are skipped — they rely on the socket-close path.
143
+ */
144
+ reapDeadClients(isAlive: (pid: number) => boolean): number;
104
145
  private cleanupLockfile;
105
146
  }
106
147
  /**
@@ -128,7 +169,7 @@ export type AcquireResult = {
128
169
  * the file existed but was empty; under concurrent daemon startup a third
129
170
  * candidate could read that empty file, decode it as `null`, and `unlink` the
130
171
  * winner's lock → two daemons (two watchers, two writers). The window was
131
- * normally too small to hit, but the chokidar watcher's extra startup time made
172
+ * normally too small to hit, but the file watcher's extra startup time made
132
173
  * concurrent daemons overlap enough to reproduce it reliably.
133
174
  *
134
175
  * The fix writes the complete record to a private temp file, then hard-links it
@@ -156,6 +197,24 @@ export declare function clearStaleDaemonLock(pidPath: string, expectedDeadPid?:
156
197
  * mistake a live daemon for a dead one and clear its lock.
157
198
  */
158
199
  export declare function isProcessAlive(pid: number): boolean;
200
+ /**
201
+ * Parse one client-hello line. Returns the peer pids if `line` is a well-formed
202
+ * client-hello (carries the `codegraph_client` marker), or null otherwise — in
203
+ * which case the caller treats the bytes as ordinary JSON-RPC.
204
+ */
205
+ export declare function parseClientHelloLine(line: string): {
206
+ pid: number;
207
+ hostPid: number | null;
208
+ } | null;
209
+ /**
210
+ * A client's peer is dead when its proxy process is gone, or when its known
211
+ * host process is gone. Unknown pid (no client-hello) is never "dead" on this
212
+ * basis — those clients rely on the socket-close path. Exported for testing.
213
+ */
214
+ export declare function peerIsDead(peers: {
215
+ pid: number | null;
216
+ hostPid: number | null;
217
+ }, isAlive: (pid: number) => boolean): boolean;
159
218
  /** Exported for test stubs that need to bound the hello-line read. */
160
219
  export { MAX_HELLO_LINE_BYTES };
161
220
  //# sourceMappingURL=daemon.d.ts.map
@@ -0,0 +1,41 @@
1
+ export interface BoundaryMatch {
2
+ /** Stable form id, e.g. 'computed-call' — used for per-form dedupe. */
3
+ form: string;
4
+ /** Human label for the dispatch form, e.g. 'computed member call'. */
5
+ label: string;
6
+ /** One-line source snippet of the site (from the original, untrimmed text). */
7
+ snippet: string;
8
+ /** 1-based line within the scanned body's FILE (absolute, ready to print). */
9
+ line: number;
10
+ /**
11
+ * Statically-visible dispatch key, when one exists: the string literal in
12
+ * `handlers['save']`, the `:symbol` in ruby `send`, the type name in
13
+ * `Send(new CreateCmd(...))`. Drives candidate lookup. Undefined when the
14
+ * key is a runtime value (variable, computed expression).
15
+ */
16
+ key?: string;
17
+ /** For typed-bus matches the key is a TYPE name (candidates ~ `${key}Handler`). */
18
+ keyIsType?: boolean;
19
+ /** Additional sites of the same form+key in this body beyond the reported one. */
20
+ moreSites?: number;
21
+ }
22
+ /**
23
+ * Blank the CONTENTS of string literals (quotes preserved, offsets preserved)
24
+ * so dispatch-shaped prose — docs, error messages, template text — can't fire
25
+ * a matcher. Run AFTER comment stripping (comments are already spaces).
26
+ * Backslash escapes are honored; `'`/`"` strings end at a newline (treated as
27
+ * unterminated, matching the comment stripper); backticks span lines, and
28
+ * `${...}` interpolations inside them are blanked too — missing a dispatch
29
+ * inside a template literal is acceptable, false-firing on prose is not.
30
+ */
31
+ export declare function blankStringContents(text: string): string;
32
+ /**
33
+ * Scan one symbol's body for dynamic-dispatch sites.
34
+ *
35
+ * @param body the symbol's source text (sliced from the file)
36
+ * @param language Node.language of the symbol
37
+ * @param fileStartLine 1-based line where `body` starts in its file — returned
38
+ * line numbers are absolute file lines.
39
+ */
40
+ export declare function scanDynamicDispatch(body: string, language: string, fileStartLine: number): BoundaryMatch[];
41
+ //# sourceMappingURL=dynamic-boundaries.d.ts.map
@@ -50,6 +50,7 @@ export declare class MCPServer {
50
50
  private engine;
51
51
  private daemon;
52
52
  private ppidWatchdog;
53
+ private livenessWatchdog;
53
54
  private originalPpid;
54
55
  private hostPpid;
55
56
  private stopped;
@@ -0,0 +1,18 @@
1
+ /** Default: 60s — ~300× shorter than the 5h #850 wedge, far longer than any real main-thread block. */
2
+ export declare const DEFAULT_WATCHDOG_TIMEOUT_MS = 60000;
3
+ /** Parse the timeout env, falling back to the default for missing/invalid values. */
4
+ export declare function parseWatchdogTimeoutMs(raw: string | undefined, fallback?: number): number;
5
+ /** Derive a heartbeat cadence that emits several beats inside the timeout window. */
6
+ export declare function deriveCheckIntervalMs(timeoutMs: number): number;
7
+ export interface WatchdogHandle {
8
+ /** Stop heartbeating and shut the watchdog child down. Idempotent. */
9
+ stop(): void;
10
+ }
11
+ /**
12
+ * Install the main-thread liveness watchdog for a long-lived process. Returns a
13
+ * handle to stop it, or `null` when disabled or when the child can't be spawned
14
+ * (degraded, never throws — a missing watchdog must never keep a process from
15
+ * starting).
16
+ */
17
+ export declare function installMainThreadWatchdog(): WatchdogHandle | null;
18
+ //# sourceMappingURL=liveness-watchdog.d.ts.map
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Shared decision logic for the PPID watchdog (#277, #692).
3
+ *
4
+ * The watchdog's job: notice that the process we depend on — our parent, or the
5
+ * MCP host reached past an intermediate launcher — has died, so an orphaned
6
+ * proxy / direct server shuts itself down instead of leaking forever.
7
+ *
8
+ * Parent death surfaces differently per OS, and getting this wrong is what
9
+ * caused the unbounded daemon/proxy leak on Windows (#692, #576):
10
+ *
11
+ * - **POSIX** reparents an orphan to init (pid 1), so `process.ppid` *changes*
12
+ * the instant the parent dies. That divergence is the classic #277 signal.
13
+ * - **Windows** never reparents: `process.ppid` keeps reporting the original
14
+ * (now-dead) parent forever, so the change-check can never fire. There we
15
+ * must poll the original parent's *liveness* instead.
16
+ *
17
+ * The liveness fallback is deliberately gated to Windows. On POSIX a
18
+ * double-forked grandparent can legitimately outlive the reparent, so a dead
19
+ * `originalPpid` is not proof of orphaning there — the change-check is the
20
+ * correct and sufficient POSIX signal, and using liveness too would risk a
21
+ * false-positive shutdown.
22
+ */
23
+ export interface SupervisionState {
24
+ /** `process.ppid` captured at startup. */
25
+ originalPpid: number;
26
+ /** `process.ppid` right now. */
27
+ currentPpid: number;
28
+ /**
29
+ * The MCP host pid threaded past an intermediate launcher
30
+ * (`CODEGRAPH_HOST_PPID`), or null when unknown — e.g. the standalone bundle,
31
+ * which pre-bakes `--liftoff-only` and so never runs the relaunch that sets it.
32
+ */
33
+ hostPpid: number | null;
34
+ /** Liveness probe — `process.kill(pid, 0)` in production, stubbed in tests. */
35
+ isAlive: (pid: number) => boolean;
36
+ /** Defaults to `process.platform`. */
37
+ platform?: NodeJS.Platform;
38
+ }
39
+ /**
40
+ * Returns a human-readable reason string when the process has lost its
41
+ * supervisor and should shut down, or null while it is still supervised.
42
+ */
43
+ export declare function supervisionLostReason(state: SupervisionState): string | null;
44
+ //# sourceMappingURL=ppid-watchdog.d.ts.map
@@ -18,7 +18,13 @@
18
18
  * the direct-mode server uses; see issue #277.
19
19
  */
20
20
  import * as net from 'net';
21
+ import { DaemonHello } from './daemon';
21
22
  import type { MCPEngine } from './engine';
23
+ /**
24
+ * Log a successful daemon attach — gated behind {@link LOG_ATTACH_ENV} so it is
25
+ * silent by default (see #618). Exported for tests.
26
+ */
27
+ export declare function logAttachedDaemon(socketPath: string, hello: DaemonHello): void;
22
28
  export interface ProxyResult {
23
29
  /**
24
30
  * `proxied` — successfully attached to a same-version daemon and piped
@@ -15,5 +15,16 @@
15
15
  * burn tokens. Reference only tools that exist on `main`; gate any
16
16
  * conditional tools behind feature checks if/when they ship.
17
17
  */
18
- export declare const SERVER_INSTRUCTIONS = "# Codegraph \u2014 code intelligence over an indexed knowledge graph\n\nCodegraph is a SQLite knowledge graph of every symbol, edge, and file\nin the workspace. Reads are sub-millisecond; the index lags writes by\nabout a second through the file watcher. Consult it BEFORE writing or\nediting code, not during.\n\n## Answer directly \u2014 don't delegate exploration\n\nFor \"how does X work\", architecture, trace, or where-is-X questions,\nanswer DIRECTLY \u2014 usually with ONE `codegraph_explore` call.\n`codegraph_explore` takes either a natural-language question or a bag of\nsymbol/file names and returns the verbatim source of the relevant symbols\ngrouped by file, so it is Read-equivalent and most often the ONLY\ncodegraph call you need. Codegraph IS the pre-built search index \u2014 so\ndelegating the lookup to a separate file-reading sub-task/agent, or\nrunning your own grep + read loop, repeats work codegraph already did and\ncosts more for the same answer. Reach for raw Read/Grep only to confirm a\nspecific detail codegraph didn't cover. A direct codegraph answer is\ntypically one to a few calls; a grep/read exploration is dozens.\n\n## Tool selection by intent\n\n- **Almost any question \u2014 \"how does X work\", architecture, a bug, \"what/where is X\", or surveying an area** \u2192 `codegraph_explore` (PRIMARY \u2014 call FIRST; ONE capped call returns the verbatim source of the relevant symbols grouped by file; most often the ONLY call you need)\n- **\"How does X reach/become Y? / the flow / the path from X to Y\"** \u2192 `codegraph_explore`, naming the symbols that span the flow (e.g. `mutateElement renderScene`) \u2014 it surfaces the call path among them, including dynamic-dispatch hops (callbacks, React re-render, JSX children) grep can't follow\n- **\"What is the symbol named X?\" (just its location)** \u2192 `codegraph_search`\n- **\"What calls this?\" / \"What does this call?\" / \"What would changing this break?\"** \u2192 `codegraph_callers` / `codegraph_callees` / `codegraph_impact`\n- **One specific symbol's full source (esp. a body `codegraph_explore` trimmed), or an OVERLOADED name** \u2192 `codegraph_node` (with `includeCode`): for an ambiguous name it returns EVERY matching definition's body in one call, so you never Read a file to find the right overload\n- **\"What's in directory X?\"** \u2192 `codegraph_files`\n- **\"Is the index ready / what's its size?\"** \u2192 `codegraph_status`\n\n## Common chains\n\n- **Flow / \"how does X reach Y\"**: ONE `codegraph_explore` with the symbol names spanning the flow \u2014 it surfaces the call path among them (riding dynamic-dispatch hops) AND returns their source. No need to reconstruct the path with `codegraph_search` + `codegraph_callers`.\n- **Onboarding / understanding any area**: ONE `codegraph_explore` is usually the whole answer. Only follow up \u2014 `codegraph_node` for a specific symbol \u2014 if something is still unclear.\n- **Refactor planning**: `codegraph_search` \u2192 `codegraph_callers` \u2192 `codegraph_impact`. The blast-radius answer comes from impact, not from walking callers manually.\n- **Debugging a regression**: `codegraph_callers` of the suspected symbol; widen with `codegraph_impact` if an unexpected call appears.\n\n## Anti-patterns\n\n- **Trust codegraph's results \u2014 don't re-verify them with grep.** They come from a full AST parse; re-checking with grep is slower, less accurate, and wastes context.\n- **Don't grep first** when looking up a symbol by name \u2014 `codegraph_search` is faster and returns kind + location + signature.\n- **Don't chain `codegraph_search` + `codegraph_node`** to understand an area \u2014 ONE `codegraph_explore` returns the relevant symbols' source together in a single round-trip.\n- **Don't loop `codegraph_node` over many symbols** \u2014 one `codegraph_explore` call returns them all grouped by file, while each separate call re-reads the whole context and costs far more. Use `codegraph_node` for a single symbol.\n- **After editing, check the staleness banner.** When a tool response starts with \"\u26A0\uFE0F Some files referenced below were edited since the last index sync\u2026\", the listed files are pending re-index \u2014 Read those specific files for accurate content. Every file NOT in that banner is fresh, so still trust codegraph. `codegraph_status` also lists pending files under \"Pending sync\".\n\n## Limitations\n\n- If a tool reports the project isn't initialized, `.codegraph/` doesn't exist yet \u2014 offer to run `codegraph init -i` to build the index.\n- Index lags file writes by ~1 second.\n- Cross-file resolution is best-effort name matching; ambiguous calls may return multiple candidates.\n- No live correctness validation \u2014 that's still the TypeScript compiler / test suite / linter's job. Codegraph supplements those with structural context they don't have.\n";
18
+ export declare const SERVER_INSTRUCTIONS = "# Codegraph \u2014 code intelligence over an indexed knowledge graph\n\nCodegraph is a SQLite knowledge graph of every symbol, edge, and file in\nthe workspace \u2014 pre-computed structure you would otherwise re-derive by\nreading files (cached intelligence: thousands of parse/trace decisions you\ndon't pay to re-reason each run). Reads are sub-millisecond; the index lags\nwrites by ~1s through the file watcher. Reach for it BEFORE *and* while\nwriting or editing code \u2014 not just for questions: one call returns the\nverbatim source PLUS who calls it and what it affects, so you edit with the\nblast radius in view. More accurate context, in far fewer tokens and\nround-trips than reading files yourself.\n\n## Use codegraph instead of reading files \u2014 for questions AND edits\n\nWhether you're answering \"how does X work\" or implementing a change (fixing\na bug, adding a feature), reach for codegraph before you Read. For\nunderstanding, answer DIRECTLY \u2014 usually with ONE `codegraph_explore` call.\n`codegraph_explore` takes either a natural-language question or a bag of\nsymbol/file names and returns the verbatim source of the relevant symbols\ngrouped by file, so it is Read-equivalent and most often the ONLY\ncodegraph call you need. Codegraph IS the pre-built search index \u2014 so\ndelegating the lookup to a separate file-reading sub-task/agent, or\nrunning your own grep + read loop, repeats work codegraph already did and\ncosts more for the same answer. Reach for raw Read/Grep only to confirm a\nspecific detail codegraph didn't cover. A direct codegraph answer is\ntypically one to a few calls; a grep/read exploration is dozens.\n\n## Tool selection by intent\n\n- **Almost any question \u2014 \"how does X work\", architecture, a bug, \"what/where is X\", or surveying an area** \u2192 `codegraph_explore` (PRIMARY \u2014 call FIRST; ONE capped call returns the verbatim source of the relevant symbols grouped by file; most often the ONLY call you need)\n- **\"How does X reach/become Y? / the flow / the path from X to Y\"** \u2192 `codegraph_explore`, naming the symbols that span the flow (e.g. `mutateElement renderScene`) \u2014 it surfaces the call path among them, including dynamic-dispatch hops (callbacks, React re-render, JSX children) grep can't follow\n- **\"What is the symbol named X?\" (just its location)** \u2192 `codegraph_search`\n- **\"What calls this?\" / \"What would changing this break?\"** \u2192 `codegraph_callers` \u2014 EVERY call site with file:line, including where a function is **registered as a callback** (passed as an argument, assigned to a function pointer/field, listed in a handler table) \u2014 labeled \"via callback registration\" \u2014 so a function with no direct calls is NOT dead if it's wired up somewhere. When several UNRELATED symbols share a name (one `UserService` per monorepo app), it reports **one section per definition** (never a merged list) \u2014 pass `file` to focus the definition you mean. The wider blast radius arrives automatically on `codegraph_explore` (its \"Blast radius\" section) and `codegraph_node` (the dependents note)\n- **\"What does this call?\"** \u2192 `codegraph_node` with that symbol and `includeCode: true` \u2014 the body IS the callee list, and the caller/callee trail comes with it\n- **Reading a source FILE (any time you'd use the `Read` tool)** \u2192 `codegraph_node` with a `file` path and no `symbol`. It returns the file's **current source with line numbers \u2014 the same `<n>\\t<line>` shape `Read` gives you, safe to `Edit` from** \u2014 narrowable with `offset`/`limit` exactly like `Read`, PLUS a one-line note of which files depend on it. Same bytes as `Read`, faster (served from the index), with the blast radius attached. Use it **instead of `Read`** for indexed source files; fall back to `Read` only for what codegraph doesn't index (configs, docs). Pass `symbolsOnly: true` for just the file's structure.\n- **About to read or edit a symbol you can name** \u2192 `codegraph_node` with that `symbol` (SECONDARY \u2014 the after-explore depth tool): the verbatim source (`includeCode: true`) PLUS its caller/callee trail, so before changing it you see what calls it and what your edit would break. For an OVERLOADED name it returns EVERY matching definition's body in one call, so you never Read a file to find the right overload\n\n## Common chains\n\n- **Flow / \"how does X reach Y\"**: ONE `codegraph_explore` with the symbol names spanning the flow \u2014 it surfaces the call path among them (riding dynamic-dispatch hops) AND returns their source. No need to reconstruct the path with `codegraph_search` + `codegraph_callers`.\n- **Onboarding / understanding any area**: ONE `codegraph_explore` is usually the whole answer. Only follow up \u2014 `codegraph_node` for a specific symbol \u2014 if something is still unclear.\n- **Refactor planning**: `codegraph_callers` for the complete call-site list to update; the wider blast radius is already attached to `codegraph_explore` / `codegraph_node` output.\n- **Debugging a regression**: `codegraph_callers` of the suspected symbol; `codegraph_node` on anything unexpected that appears.\n\n## Anti-patterns\n\n- **Trust codegraph's results \u2014 don't re-verify them with grep.** They come from a full AST parse; re-checking with grep is slower, less accurate, and wastes context.\n- **Don't grep first** when looking up a symbol by name \u2014 `codegraph_search` is faster and returns kind + location + signature.\n- **Don't chain `codegraph_search` + `codegraph_node`** to understand an area \u2014 ONE `codegraph_explore` returns the relevant symbols' source together in a single round-trip.\n- **Don't loop `codegraph_node` over many symbols** \u2014 one `codegraph_explore` call returns them all grouped by file, while each separate call re-reads the whole context and costs far more. Use `codegraph_node` for a single symbol.\n- **Don't reach for the `Read` tool on an indexed source file** \u2014 `codegraph_node` with a `file` reads it for you (same `<n>\\t<line>` source, `offset`/`limit` like Read, faster, with its blast radius), and with a `symbol` it returns the source plus the caller/callee trail. Reach for raw `Read` only for what codegraph doesn't index (configs, docs) or when the staleness banner flags a file as pending re-index.\n- **After editing, check the staleness banner.** When a tool response starts with \"\u26A0\uFE0F Some files referenced below were edited since the last index sync\u2026\", the listed files are pending re-index \u2014 Read those specific files for accurate content. Every file NOT in that banner is fresh, so still trust codegraph.\n\n## Limitations\n\n- If a tool reports a project isn't indexed (no `.codegraph/`), stop calling codegraph tools for that project for the rest of the session and use your built-in tools there instead. Indexing is the user's decision \u2014 mention they can run `codegraph init` if it comes up, but don't run it yourself.\n- Index lags file writes by ~1 second.\n- Cross-file resolution is best-effort name matching; ambiguous calls may return multiple candidates.\n- No live correctness validation \u2014 that's still the TypeScript compiler / test suite / linter's job. Codegraph supplements those with structural context they don't have.\n";
19
+ /**
20
+ * Instructions variant sent when the workspace has NO codegraph index.
21
+ *
22
+ * Sending the full playbook ("lean on codegraph for everything") into a
23
+ * session where every call would fail wastes the agent's calls and — worse —
24
+ * the failures teach it codegraph is broken. The unindexed variant is a
25
+ * short, unambiguous "inactive this session" note; `tools/list` is gated to
26
+ * empty in the same state, so the agent has nothing to mis-call. Indexing is
27
+ * deliberately left to the user: the agent is told NOT to run init itself.
28
+ */
29
+ export declare const SERVER_INSTRUCTIONS_UNINDEXED = "# Codegraph \u2014 inactive (workspace not indexed)\n\nThis workspace has no codegraph index (no `.codegraph/` directory), so no\ncodegraph tools are available this session. Work with your built-in tools as\nusual.\n\nIndexing is the user's decision \u2014 do not run it yourself. If the user asks\nabout codegraph, they can enable it by running `codegraph init` in the\nproject root and starting a new session.\n";
19
30
  //# sourceMappingURL=server-instructions.d.ts.map
@@ -39,6 +39,8 @@ export declare class MCPSession {
39
39
  private transport;
40
40
  private engine;
41
41
  private clientSupportsRoots;
42
+ /** From the initialize handshake — attributes usage rollups to the agent host. */
43
+ private clientInfo;
42
44
  private rootsAttempted;
43
45
  private resolvePromise;
44
46
  private explicitProjectPath;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Treat a stdin failure as a shutdown signal — issue #799.
3
+ *
4
+ * An MCP stdio server's lifeline is its stdin: when the host/client goes away,
5
+ * stdin should end and the server should exit. The server paths listened for
6
+ * `'end'` and `'close'` — but NOT `'error'`.
7
+ *
8
+ * That gap bites with a socket-backed stdin, which is the shape VS Code /
9
+ * Claude Code use (a socketpair, not a pipe). When the client dies, the socket
10
+ * can surface as an `'error'` (ECONNRESET / hangup) rather than a clean
11
+ * `'close'`. With no `'error'` listener, Node escalates it to the process-wide
12
+ * `uncaughtException` handler, which logs and keeps running — so the server
13
+ * orphans instead of exiting. Worse, on Linux a `POLLHUP` socket fd left
14
+ * registered in epoll wakes the event loop continuously, pinning a core at
15
+ * 100% CPU (the spin reported in #799); once the main thread spins, the
16
+ * `setInterval` PPID watchdog can't even fire, so the orphan runs forever.
17
+ *
18
+ * Fix: listen for `'error'` as well, and DESTROY the stdin stream on any
19
+ * terminal event so the fd leaves epoll and can't keep churning, then run the
20
+ * caller's shutdown. Fires `onTerminal` at most once — callers' shutdowns are
21
+ * already re-entry-guarded, but the single-shot guard also keeps `destroy()`'s
22
+ * follow-on `'close'` from re-invoking it.
23
+ *
24
+ * `stream` is injectable for tests; it defaults to `process.stdin`.
25
+ */
26
+ export declare function treatStdinFailureAsShutdown(onTerminal: () => void, stream?: NodeJS.ReadableStream): void;
27
+ //# sourceMappingURL=stdin-teardown.d.ts.map