@colbymchenry/codegraph 1.0.0 → 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.
Files changed (34) hide show
  1. package/README.md +87 -51
  2. package/dist/bin/fatal-handler.d.ts +20 -0
  3. package/dist/db/index.d.ts +22 -1
  4. package/dist/db/queries.d.ts +14 -0
  5. package/dist/directory.d.ts +58 -0
  6. package/dist/extraction/grammars.d.ts +11 -3
  7. package/dist/extraction/tree-sitter-types.d.ts +13 -0
  8. package/dist/extraction/tree-sitter.d.ts +119 -0
  9. package/dist/index.d.ts +33 -0
  10. package/dist/installer/instructions-template.d.ts +3 -3
  11. package/dist/installer/targets/claude.d.ts +18 -12
  12. package/dist/installer/targets/shared.d.ts +12 -2
  13. package/dist/installer/targets/types.d.ts +7 -0
  14. package/dist/mcp/daemon-manager.d.ts +42 -0
  15. package/dist/mcp/daemon-registry.d.ts +47 -0
  16. package/dist/mcp/index.d.ts +1 -0
  17. package/dist/mcp/liveness-watchdog.d.ts +18 -0
  18. package/dist/mcp/server-instructions.d.ts +18 -14
  19. package/dist/mcp/tools.d.ts +51 -1
  20. package/dist/project-config.d.ts +19 -0
  21. package/dist/reasoning/config.d.ts +45 -0
  22. package/dist/reasoning/credentials.d.ts +5 -0
  23. package/dist/reasoning/login.d.ts +21 -0
  24. package/dist/reasoning/reasoner.d.ts +43 -0
  25. package/dist/resolution/c-fnptr-synthesizer.d.ts +33 -0
  26. package/dist/resolution/callback-synthesizer.d.ts +6 -1
  27. package/dist/resolution/frameworks/goframe.d.ts +41 -0
  28. package/dist/resolution/frameworks/index.d.ts +1 -0
  29. package/dist/resolution/goframe-synthesizer.d.ts +28 -0
  30. package/dist/resolution/strip-comments.d.ts +1 -1
  31. package/dist/sync/watcher.d.ts +68 -1
  32. package/dist/telemetry/index.d.ts +0 -3
  33. package/dist/utils.d.ts +14 -1
  34. package/package.json +7 -7
@@ -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
@@ -26,22 +26,28 @@ import { AgentTarget, Location, WriteResult } from './types';
26
26
  */
27
27
  export declare function writeMcpEntry(loc: Location): WriteResult['files'][number];
28
28
  /**
29
- * Remove stale codegraph auto-sync hooks from Claude `settings.json`.
30
- *
31
- * Surgical at the individual-command level: only entries matching
32
- * `isLegacyCodegraphHookCommand` are dropped, so a sibling hook sharing
33
- * a matcher group (or the Stop event) with ours survives. We prune a
34
- * matcher group only once its `hooks` array is empty, an event only
35
- * once it has no groups left, and `hooks` itself only once every event
36
- * is gone — and none of that runs unless we actually removed a
37
- * codegraph command, so a settings.json with no legacy hooks is left
38
- * byte-for-byte untouched and reported `unchanged`.
39
- *
40
- * Exported so it can be unit-tested directly and reused by both
29
+ * Remove stale codegraph auto-sync hooks (`mark-dirty` / `sync-if-dirty`) that a
30
+ * pre-0.8 install wrote. Exported for direct unit-testing; reused by both
41
31
  * `install` (an upgrade self-heals) and `uninstall`.
42
32
  */
43
33
  export declare function cleanupLegacyHooks(loc: Location): WriteResult['files'][number];
34
+ /**
35
+ * Remove the front-load `UserPromptSubmit` hook this installer writes (see
36
+ * writePromptHookEntry). Used by `uninstall`, and by `install` when the user
37
+ * opts out, so the choice round-trips.
38
+ */
39
+ export declare function removePromptHookEntry(loc: Location): WriteResult['files'][number];
44
40
  export declare function writePermissionsEntry(loc: Location): WriteResult['files'][number];
41
+ /**
42
+ * Write the front-load `UserPromptSubmit` hook into Claude `settings.json` —
43
+ * a `command` hook that runs `codegraph prompt-hook`, which injects
44
+ * codegraph_explore context for structural prompts so the agent reliably uses
45
+ * the graph. Idempotent: if our command is already wired under UserPromptSubmit
46
+ * the file is left byte-for-byte untouched and reported `unchanged`. Sibling
47
+ * hooks (the user's own, or other events) are preserved. Opt-in — the installer
48
+ * only calls this when the user accepts the prompt (default-yes).
49
+ */
50
+ export declare function writePromptHookEntry(loc: Location): WriteResult['files'][number];
45
51
  /**
46
52
  * Strip the marker-delimited CodeGraph block from CLAUDE.md if a prior
47
53
  * install wrote one. Codegraph no longer maintains an instructions file
@@ -19,8 +19,18 @@ export declare function getMcpServerConfig(): {
19
19
  };
20
20
  /**
21
21
  * Permissions list for Claude `settings.json`. Other targets that
22
- * have a permissions concept can compose this list directly. The
23
- * permission strings follow Claude's `mcp__<server>__<tool>` format.
22
+ * have a permissions concept can compose this list directly.
23
+ *
24
+ * One server-scoped wildcard rather than a per-tool list. By default only
25
+ * `codegraph_explore` is even LISTED to the agent (see DEFAULT_MCP_TOOLS in
26
+ * mcp/tools.ts), so in practice explore is the only tool this auto-approves —
27
+ * but the wildcard means that if a user re-enables another tool via
28
+ * CODEGRAPH_MCP_TOOLS, it's already pre-approved (no permission prompt, no
29
+ * hand-editing settings.json), and future tools are covered too. Claude only
30
+ * honors globs after a literal `mcp__<server>__` prefix, so this exact string
31
+ * is the way to allow-all for one server; a bare `mcp__codegraph` or `*` is
32
+ * ignored. The allowlist gates PROMPTING, not visibility, so a superset here
33
+ * never makes a hidden tool appear.
24
34
  */
25
35
  export declare function getCodeGraphPermissions(): string[];
26
36
  /**
@@ -63,6 +63,13 @@ export interface InstallOptions {
63
63
  * target has no permissions concept this option is a no-op.
64
64
  */
65
65
  autoAllow: boolean;
66
+ /**
67
+ * Front-load prompt hook (Claude `UserPromptSubmit`) that injects
68
+ * codegraph_explore context for structural prompts. `true` installs it,
69
+ * `false` removes any prior install (so opt-out round-trips), `undefined`
70
+ * leaves it untouched. Targets without a prompt-hook concept ignore it.
71
+ */
72
+ promptHook?: boolean;
66
73
  }
67
74
  export interface AgentTarget {
68
75
  /** Stable id; matches the `TargetId` union. */
@@ -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
@@ -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
@@ -7,24 +7,28 @@
7
7
  * before it sees individual tool descriptions.
8
8
  *
9
9
  * Goals when editing this:
10
- * - Tool selection by intent (which tool for which question)
11
- * - Common chains (refactor planning = X then Y)
12
- * - Anti-patterns (don't grep when codegraph_search is faster)
10
+ * - Lead the agent to codegraph_explore for any structural/flow question
11
+ * - Reinforce "explore instead of Read/Grep" for indexed code
12
+ * - Anti-patterns (don't re-verify with grep; don't hand-reconstruct flows)
13
13
  *
14
14
  * Keep it tight. The agent reads this every session — long instructions
15
- * burn tokens. Reference only tools that exist on `main`; gate any
16
- * conditional tools behind feature checks if/when they ship.
15
+ * burn tokens. The DEFAULT MCP surface is `codegraph_explore` ALONE (see
16
+ * DEFAULT_MCP_TOOLS in tools.ts) reference only that tool here. The other
17
+ * tools (node/search/callers/…) stay defined and are re-enablable via
18
+ * CODEGRAPH_MCP_TOOLS, but they are NOT listed to agents, so don't name them.
17
19
  */
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";
20
+ 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## One tool: codegraph_explore \u2014 use it instead of reading files\n\nThere is a single tool, `codegraph_explore`, and it is Read-equivalent. It\ntakes either a natural-language question or a bag of symbol/file names and\nreturns the **verbatim, line-numbered source** of the relevant symbols\ngrouped by file \u2014 the same `<n>\\t<line>` shape `Read` gives you, safe to\n`Edit` from \u2014 PLUS the call path among them (including dynamic-dispatch hops\nlike callbacks, React re-render, and JSX children that grep can't follow) and\na blast-radius summary of what depends on them.\n\nWhether you're answering \"how does X work\" or implementing a change (fixing a\nbug, adding a feature), call `codegraph_explore` before you Read. ONE call\nusually answers the whole question. Codegraph IS the pre-built search index \u2014\nso running your own grep + read loop, or delegating the lookup to a separate\nfile-reading sub-task/agent, repeats work codegraph already did and costs more\nfor the same answer. A direct codegraph answer is typically one to a few\ncalls; a grep/read exploration is dozens.\n\n## How to query\n\n- **Almost any question \u2014 \"how does X work\", architecture, a bug, \"what/where is X\", or surveying an area** \u2192 `codegraph_explore` with a natural-language question or the relevant names. ONE capped call returns the verbatim source 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, riding dynamic-dispatch hops, and returns their source.\n- **Reading or editing a file/symbol you can name** \u2192 put its name or file path in the `codegraph_explore` query \u2014 it returns that current line-numbered source (safe to `Edit` from) with the call path and blast radius attached, so you don't Read it separately. For an overloaded name it returns every matching definition's body in one call.\n- **Need more?** Call `codegraph_explore` again with more specific names \u2014 treat the source it returns as already Read.\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 or Read first** to find or understand indexed code \u2014 ONE `codegraph_explore` returns the relevant symbols' source together in a single round-trip. Reach for raw `Read`/`Grep` only to confirm a specific detail codegraph didn't cover, or for what codegraph doesn't index (configs, docs).\n- **Don't reconstruct a flow by hand** \u2014 name the endpoints in one `codegraph_explore` and it surfaces the path between them, dynamic-dispatch hops included.\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. A different, rarer banner \u2014 \"\u26A0\uFE0F CodeGraph auto-sync is DISABLED\u2026\" \u2014 means live watching stopped entirely (the whole index is frozen, not just a few files); until it's resolved, Read files directly to confirm anything that may have changed.\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
21
  /**
20
- * Instructions variant sent when the workspace has NO codegraph index.
22
+ * Instructions variant sent when the server's own root has NO codegraph index.
21
23
  *
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.
24
+ * The tools are still exposed (gating tool availability on whether `./` has an
25
+ * index is the bug behind #964: it breaks monorepos where only sub-projects are
26
+ * indexed, and a server that started before `codegraph init` never surfaces the
27
+ * tools afterward). Instead of an "inactive" note, this variant tells the agent
28
+ * codegraph works **per project**: there's no default project to query, so pass
29
+ * a `projectPath` to any project that HAS a `.codegraph/`. The full single-
30
+ * project playbook ({@link SERVER_INSTRUCTIONS}) is sent instead when the root
31
+ * IS indexed, so the common case stays tight.
28
32
  */
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";
33
+ export declare const SERVER_INSTRUCTIONS_NO_ROOT_INDEX = "# Codegraph \u2014 available (per-project; pass projectPath)\n\nCodegraph is a SQLite knowledge graph of a codebase's symbols, edges, and\nfiles: one `codegraph_explore` call returns the verbatim, line-numbered source\nof the relevant symbols PLUS the call paths between them and a blast-radius\nsummary \u2014 replacing a grep + Read loop with one round-trip.\n\nThis server started somewhere with no `.codegraph/` of its own, so there is no\ndefault project \u2014 but the tools are available and work **per project**:\n\n- To query a project that HAS a `.codegraph/` index (e.g. a service inside a\n monorepo, or a second repo), pass its path as `projectPath` to\n `codegraph_explore` (and any other codegraph tool). Codegraph resolves the\n nearest `.codegraph/` at or above that path and answers from it \u2014 for as many\n projects as you like in one session.\n- For a project with no `.codegraph/`, use your built-in tools (Read/Grep/Glob)\n for that project. Indexing is the user's decision \u2014 don't run it yourself, but\n if it comes up they can run `codegraph init` in a project to enable codegraph\n there (a new index is picked up live, no restart).\n";
30
34
  //# sourceMappingURL=server-instructions.d.ts.map
@@ -57,7 +57,7 @@ export interface ExploreOutputBudget {
57
57
  maxCharsPerFile: number;
58
58
  /** Cluster gap threshold in lines — tighter clustering on small projects. */
59
59
  gapThreshold: number;
60
- /** Max symbols listed in the per-file header (`#### path — sym(kind), ...`). */
60
+ /** Max symbols listed in the per-file header (``**`path`** — sym(kind), ...``). */
61
61
  maxSymbolsInFileHeader: number;
62
62
  /** Max edges shown per relationship kind in the Relationships section. */
63
63
  maxEdgesPerRelationshipKind: number;
@@ -93,6 +93,15 @@ export declare function formatStaleBanner(stale: PendingFile[]): string;
93
93
  * without bloating the main banner.
94
94
  */
95
95
  export declare function formatStaleFooter(stale: PendingFile[]): string;
96
+ /**
97
+ * Whole-index degradation banner (issue #876). Emitted at the top of a read
98
+ * tool response when live watching has permanently stopped — at which point
99
+ * `getPendingFiles()` is empty, so the per-file banner above can't fire even
100
+ * though the index is now FROZEN and silently drifting stale. Leads with the
101
+ * agent-actionable instruction (Read directly) and carries the reason, which
102
+ * already names the operator remedy (`codegraph sync` / git hooks).
103
+ */
104
+ export declare function formatDegradedBanner(reason: string | null): string;
96
105
  /**
97
106
  * MCP Tool definition
98
107
  */
@@ -163,6 +172,16 @@ export declare class ToolHandler {
163
172
  * stale data, which is what would have happened without the gate.
164
173
  */
165
174
  setCatchUpGate(p: Promise<void> | null): void;
175
+ /**
176
+ * Await the catch-up gate, but no longer than the configured timeout (#905).
177
+ * If the reconcile settles first, we got the fully-reconciled answer. If the
178
+ * timeout wins, we serve the call now and let the reconcile finish in the
179
+ * background — it yields to the event loop (see SYNC_RECONCILE_YIELD_INTERVAL),
180
+ * so a concurrent read still runs against the same connection. Never throws:
181
+ * a failed reconcile is logged by the engine, and we serve best-effort over
182
+ * the same potentially-stale data the un-gated path would have.
183
+ */
184
+ private awaitCatchUpGate;
166
185
  /**
167
186
  * Record the directory the server tried to resolve the default project from.
168
187
  * Used only to make the "no default project" error actionable.
@@ -200,6 +219,17 @@ export declare class ToolHandler {
200
219
  * similar to how git finds .git/ directories.
201
220
  */
202
221
  private getCodeGraph;
222
+ /**
223
+ * Heal a long-lived connection whose `.codegraph/` was removed and recreated
224
+ * at the same path (a worktree recreated, or `rm -rf .codegraph` + re-init)
225
+ * before handing it to a tool. Otherwise the daemon keeps serving the
226
+ * pre-removal snapshot from its now-unlinked file handle until restart — and
227
+ * because the daemon registry is keyed by path, a same-path recreate routes
228
+ * new clients straight back to this same stale daemon (#925). The check is one
229
+ * stat() and a no-op unless the inode actually changed; it never throws into a
230
+ * tool call.
231
+ */
232
+ private freshen;
203
233
  /**
204
234
  * Close all cached project connections
205
235
  */
@@ -316,6 +346,26 @@ export declare class ToolHandler {
316
346
  * connected flow never reaches this method.
317
347
  */
318
348
  private buildDynamicBoundaries;
349
+ /**
350
+ * Interface/registry-dispatch announcement — #687 extended to GRAPH-visible
351
+ * polymorphism (the body-scan can't see it: `nodeType.execute()` is textually
352
+ * an ordinary call; the polymorphism lives in the `implements`/`extends` edges).
353
+ *
354
+ * A method the agent named that resolves to a large same-name family whose
355
+ * definers overwhelmingly implement/extend ONE supertype is a runtime dispatch:
356
+ * the concrete target is chosen at runtime from N implementations, so no single
357
+ * static edge is "the answer" — the implementations ARE the continuations. We
358
+ * announce the supertype, its TRUE implementer count, and a few concrete targets,
359
+ * then steer to codegraph_explore. Graph-only, query-time, zero mutation; the
360
+ * caller fires it ONLY for an UNCOVERED named token, so a connected flow is silent.
361
+ *
362
+ * Robust to FTS sampling bias: the same-name family is a capped FTS sample that
363
+ * over-represents whatever FTS ranks first (n8n: DB `TableOperation.execute`
364
+ * outnumbered `INodeType.execute` in the sample 7:6 even though INodeType has
365
+ * 611 implementers vs a handful). So candidate supertypes are ranked by their
366
+ * TRUE graph-wide implementer count, NOT their frequency in the sample.
367
+ */
368
+ private buildPolymorphicBoundaries;
319
369
  /**
320
370
  * Shortlist candidate runtime targets for a dispatch key surfaced by
321
371
  * {@link buildDynamicBoundaries}. Exact conventional names first (`save` →
@@ -0,0 +1,19 @@
1
+ import { Language } from './types';
2
+ /** Filename of the project-scoped config, resolved relative to the project root. */
3
+ export declare const PROJECT_CONFIG_FILENAME = "codegraph.json";
4
+ export interface ProjectConfig {
5
+ /** Map of custom file extension (`.foo`) to a supported language id. */
6
+ extensions?: Record<string, string>;
7
+ }
8
+ /**
9
+ * Load the validated extension overrides for a project, mtime-cached.
10
+ *
11
+ * Returns a map of `.ext` → supported language id. The result merges on top of
12
+ * the built-in extension map at the point of use (see `detectLanguage` /
13
+ * `isSourceFile`), with these user mappings taking precedence. Returns an empty
14
+ * map when there is no `codegraph.json` (the zero-config default).
15
+ */
16
+ export declare function loadExtensionOverrides(rootDir: string): Record<string, Language>;
17
+ /** Test/maintenance hook: forget cached config (e.g. after rewriting it in a test). */
18
+ export declare function clearProjectConfigCache(): void;
19
+ //# sourceMappingURL=project-config.d.ts.map