@colbymchenry/codegraph 1.1.6 → 1.3.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 (46) hide show
  1. package/README.md +95 -2
  2. package/dist/bin/codegraph.d.ts +1 -1
  3. package/dist/db/migrations.d.ts +1 -1
  4. package/dist/db/queries.d.ts +51 -0
  5. package/dist/directory.d.ts +9 -5
  6. package/dist/extraction/cfml-extractor.d.ts +107 -0
  7. package/dist/extraction/grammars.d.ts +9 -0
  8. package/dist/extraction/index.d.ts +46 -1
  9. package/dist/extraction/languages/arkts.d.ts +3 -0
  10. package/dist/extraction/languages/c-cpp.d.ts +59 -0
  11. package/dist/extraction/languages/cfquery.d.ts +12 -0
  12. package/dist/extraction/languages/cfscript.d.ts +3 -0
  13. package/dist/extraction/languages/cobol.d.ts +33 -0
  14. package/dist/extraction/languages/erlang.d.ts +3 -0
  15. package/dist/extraction/languages/nix.d.ts +3 -0
  16. package/dist/extraction/languages/solidity.d.ts +3 -0
  17. package/dist/extraction/languages/terraform.d.ts +3 -0
  18. package/dist/extraction/languages/vbnet.d.ts +11 -0
  19. package/dist/extraction/mybatis-extractor.d.ts +30 -10
  20. package/dist/extraction/tree-sitter-types.d.ts +20 -1
  21. package/dist/extraction/tree-sitter.d.ts +38 -0
  22. package/dist/index.d.ts +63 -1
  23. package/dist/mcp/daemon.d.ts +25 -3
  24. package/dist/mcp/early-ppid.d.ts +26 -0
  25. package/dist/mcp/startup-handshake.d.ts +44 -0
  26. package/dist/mcp/tools.d.ts +22 -0
  27. package/dist/project-config.d.ts +38 -0
  28. package/dist/resolution/callback-synthesizer.d.ts +1 -1
  29. package/dist/resolution/cooperative-yield.d.ts +32 -0
  30. package/dist/resolution/frameworks/cics.d.ts +20 -0
  31. package/dist/resolution/frameworks/terraform.d.ts +38 -0
  32. package/dist/resolution/import-resolver.d.ts +7 -0
  33. package/dist/resolution/index.d.ts +44 -2
  34. package/dist/resolution/name-matcher.d.ts +22 -3
  35. package/dist/resolution/strip-comments.d.ts +1 -1
  36. package/dist/resolution/types.d.ts +20 -0
  37. package/dist/resolution/workspace-packages.d.ts +10 -0
  38. package/dist/search/identifier-segments.d.ts +60 -0
  39. package/dist/sync/watcher.d.ts +10 -5
  40. package/dist/types.d.ts +19 -1
  41. package/npm-shim.js +9 -2
  42. package/package.json +7 -7
  43. package/dist/reasoning/config.d.ts +0 -45
  44. package/dist/reasoning/credentials.d.ts +0 -5
  45. package/dist/reasoning/login.d.ts +0 -21
  46. package/dist/reasoning/reasoner.d.ts +0 -43
@@ -0,0 +1,11 @@
1
+ import type { LanguageExtractor } from '../tree-sitter-types';
2
+ /**
3
+ * The vendored VB.NET grammar has no true end-of-file token (its `_eof` rule is
4
+ * a literal-`$` placeholder that never matches real input), so a file whose
5
+ * last line lacks a trailing newline ends every parse with a MISSING-newline
6
+ * error on the final statement. Appending a newline is offset-preserving for
7
+ * all existing content.
8
+ */
9
+ export declare function ensureTrailingNewline(source: string): string;
10
+ export declare const vbnetExtractor: LanguageExtractor;
11
+ //# sourceMappingURL=vbnet.d.ts.map
@@ -12,15 +12,21 @@ import { ExtractionResult } from '../types';
12
12
  *
13
13
  * This extractor emits one method-shaped node per `<select|insert|update|
14
14
  * delete>` and per `<sql>` fragment, qualified as `<namespace>::<id>` so the
15
- * MyBatis framework synthesizer (`src/resolution/frameworks/mybatis.ts`) can
16
- * link the matching Java method → XML statement by suffix-matching qualified
17
- * names. `<include refid="...">` inside a statement yields an unresolved
18
- * reference to the SQL fragment, also keyed by `<namespace>::<refid>`.
15
+ * MyBatis framework synthesizer can link the matching Java method → XML
16
+ * statement by suffix-matching qualified names. `<include refid="...">` inside
17
+ * a statement yields an unresolved reference to the SQL fragment, also keyed
18
+ * by `<namespace>::<refid>`.
19
+ *
20
+ * Both dialects are covered: MyBatis 3 `<mapper namespace="...">` and the
21
+ * legacy iBatis 2 `<sqlMap>` (namespaced, or namespace-less with `Map.stmt`
22
+ * ids, plus its extra `<statement>`/`<procedure>` verbs). Attribute values may
23
+ * use either quote style, and statements commented out with `<!-- ... -->` are
24
+ * ignored (see the constructor's comment-stripping pre-pass).
19
25
  *
20
26
  * Non-mapper XML (Maven `pom.xml`, Spring beans XML, `web.xml`, log4j config,
21
- * etc.) is detected by the absence of a `<mapper namespace="...">` root and
22
- * returns just a file node — we still need the file row so the watcher can
23
- * track it, but we emit no symbols.
27
+ * etc.) is detected by the absence of a `<mapper namespace="...">` /
28
+ * `<sqlMap>` root and returns just a file node — we still need the file row so
29
+ * the watcher can track it, but we emit no symbols.
24
30
  */
25
31
  export declare class MyBatisExtractor {
26
32
  private filePath;
@@ -31,16 +37,30 @@ export declare class MyBatisExtractor {
31
37
  private errors;
32
38
  private lineStarts;
33
39
  constructor(filePath: string, source: string);
40
+ private static stripXmlComments;
34
41
  extract(): ExtractionResult;
35
42
  private createFileNode;
36
43
  /**
37
- * Find the `<mapper namespace="X">` opening tag. Returns the namespace and
38
- * the byte offsets of the body (between the opening and closing tag) so
39
- * statement extraction can be scoped to mapper contents.
44
+ * Find the mapper root and its dialect. Two shapes are recognized:
45
+ * - MyBatis 3: `<mapper namespace="com.foo.Bar">` namespace required.
46
+ * - iBatis 2: `<sqlMap namespace="Account">`, or a namespace-less
47
+ * `<sqlMap>` whose statement ids carry the qualifier as `Map.statement`.
48
+ * Returns the namespace, the dialect, and the byte offsets of the body
49
+ * (between the opening and closing tag) so statement extraction is scoped to
50
+ * the root's contents. Either quote style is accepted for the namespace
51
+ * (`namespace='X'` is legal XML and common in older mappers).
40
52
  */
41
53
  private findMapperRoot;
42
54
  private extractMapper;
43
55
  private buildSignature;
56
+ /**
57
+ * Build the `<namespace>::<id>` qualified name the MyBatis synthesizer
58
+ * suffix-matches against a Java `<Class>::<method>`, and the display name.
59
+ * For a namespace-less iBatis `<sqlMap>`, the statement id carries the
60
+ * qualifier as `Map.statement`, so split on the last dot to reach the same
61
+ * shape (`Account.getById` → `Account::getById`, name `getById`).
62
+ */
63
+ private qualifyStatement;
44
64
  private previewSql;
45
65
  private computeLineStarts;
46
66
  private getLineNumber;
@@ -76,8 +76,10 @@ export interface LanguageExtractor {
76
76
  * grammar mis-parses inside enum bodies). MUST preserve byte offsets (replace
77
77
  * removed text with spaces, keep newlines) so node positions and getNodeText
78
78
  * stay correct; the returned string is used for both parsing and extraction.
79
+ * `filePath` lets a transform key off the concrete file extension when one
80
+ * language id serves several dialects (C++ also parses `.metal` shaders).
79
81
  */
80
- preParse?: (source: string) => string;
82
+ preParse?: (source: string, filePath?: string) => string;
81
83
  /** Node types that represent functions */
82
84
  functionTypes: string[];
83
85
  /** Node types that represent classes */
@@ -114,6 +116,15 @@ export interface LanguageExtractor {
114
116
  returnField?: string;
115
117
  /** Override symbol name extraction (e.g. ObjC multi-part selectors). */
116
118
  resolveName?: (node: SyntaxNode, source: string) => string | undefined;
119
+ /**
120
+ * Post-process an already-extracted name to recover a real identifier from a
121
+ * name still mangled by a macro the pre-parse didn't blank (C/C++:
122
+ * `MACRO Ret name(` misparses to the name "Ret name"). Applied to every name
123
+ * this extractor produces, so it MUST be a no-op on a well-formed name — only
124
+ * C/C++ set it, because a mangled name there is unambiguous (an internal space),
125
+ * whereas e.g. Kotlin/Scala backtick identifiers legitimately contain spaces.
126
+ */
127
+ recoverMangledName?: (name: string) => string;
117
128
  /** Extract property name when the generic name walk fails (e.g. ObjC @property). */
118
129
  extractPropertyName?: (node: SyntaxNode, source: string) => string | null;
119
130
  /** Extract signature from node */
@@ -140,6 +151,14 @@ export interface LanguageExtractor {
140
151
  extraClassNodeTypes?: string[];
141
152
  /** Whether methods can be top-level without enclosing class (Go: true) */
142
153
  methodsAreTopLevel?: boolean;
154
+ /**
155
+ * Skip a bodiless class node as a forward declaration / elaborated type,
156
+ * mirroring the bodiless-struct/enum skip. Set only for languages where a
157
+ * bodiless `class` specifier is NOT a complete definition — C/C++
158
+ * (`class Foo;` is a forward decl). Leave unset for languages where a
159
+ * bodiless class IS complete (Kotlin `class Empty`, Scala `case object`). (#1093)
160
+ */
161
+ skipBodilessClass?: boolean;
143
162
  /** NodeKind to use for interface-like declarations (Rust: 'trait'). Default: 'interface' */
144
163
  interfaceKind?: NodeKind;
145
164
  /**
@@ -25,6 +25,8 @@ export declare class TreeSitterExtractor {
25
25
  private errors;
26
26
  private extractor;
27
27
  private nodeStack;
28
+ private namespacePrefix;
29
+ private cppLocalFnPtrs;
28
30
  private methodIndex;
29
31
  private fnRefSpec;
30
32
  private fnRefCandidates;
@@ -427,6 +429,25 @@ export declare class TreeSitterExtractor {
427
429
  /**
428
430
  * Extract a function call
429
431
  */
432
+ /**
433
+ * The module an Erlang gen_server target expression statically names, or
434
+ * null when it's dynamic (pid/var/tuple form). Static shapes:
435
+ * - a bare atom — either this module or another one; OTP's dominant
436
+ * registration convention (`{local, ?MODULE}`) names a server process
437
+ * after its module, so `gen_server:call(other_mod, …)` reaches
438
+ * `other_mod`'s handlers. A registered name that matches no module
439
+ * resolves to nothing downstream (the qualified ref just drops).
440
+ * - `?MODULE`, or a macro the file defines as `?MODULE`
441
+ * (`-define(SERVER, ?MODULE)` — the standard self idiom)
442
+ * - a macro the file defines as a bare atom
443
+ * (`-define(STORE, kv_store)` — the cross-module variant)
444
+ * The macro tables are memoized per file (single entry — extraction is
445
+ * file-sequential).
446
+ */
447
+ private erlangServerMacroFile;
448
+ private erlangSelfMacros;
449
+ private erlangAtomMacros;
450
+ private resolveErlangGenServerTarget;
430
451
  private extractCall;
431
452
  /**
432
453
  * `new Foo(...)` / `Foo::new(...)` / object_creation_expression —
@@ -437,6 +458,15 @@ export declare class TreeSitterExtractor {
437
458
  * Children are still walked so nested calls inside the constructor
438
459
  * arguments (`new Foo(bar())`) get their own `calls` references.
439
460
  */
461
+ /**
462
+ * VB.NET `New Invoice(1)` is syntactically ambiguous between constructing
463
+ * Invoice with an argument and allocating an Invoice array of bound 1; the
464
+ * grammar parses the parenthesized form as array_creation_expression. A
465
+ * user-defined type with no `{...}` array initializer is overwhelmingly a
466
+ * constructor call, so treat it as an instantiation. Predefined element
467
+ * types (`New Byte(1023)`) and brace-initialized forms stay arrays.
468
+ */
469
+ private isVbnetConstructorShapedArrayCreation;
440
470
  private extractInstantiation;
441
471
  /**
442
472
  * Is this C++ `declaration` a stack/direct-initialization object construction
@@ -528,6 +558,14 @@ export declare class TreeSitterExtractor {
528
558
  * heuristic — no false edges (resolution still validates each path).
529
559
  */
530
560
  private extractRustRouteMacro;
561
+ /**
562
+ * Record a C++ local function-pointer binding (`local = &fn` / `&fn<…>` /
563
+ * `&ns::fn<…>`) for the CURRENT enclosing symbol, so calls through the local
564
+ * resolve to the real target (see cppLocalFnPtrs). Only the address-of shape
565
+ * is accepted — a bare-identifier RHS (`auto x = y;`) is any value copy, and
566
+ * linking through it would guess.
567
+ */
568
+ private recordCppFnPtrBinding;
531
569
  private visitFunctionBody;
532
570
  /**
533
571
  * Extract inheritance relationships
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  * A local-first code intelligence system that builds a semantic
5
5
  * knowledge graph from any codebase.
6
6
  */
7
- import { Node, Edge, FileRecord, ExtractionResult, Subgraph, TraversalOptions, SearchOptions, SearchResult, Context, GraphStats, TaskInput, TaskContext, BuildContextOptions, FindRelevantContextOptions } from './types';
7
+ import { Node, Edge, FileRecord, ExtractionResult, Subgraph, TraversalOptions, SearchOptions, SearchResult, SegmentMatch, Context, GraphStats, TaskInput, TaskContext, BuildContextOptions, FindRelevantContextOptions } from './types';
8
8
  import { IndexProgress, IndexResult, SyncResult } from './extraction';
9
9
  import { ResolutionResult } from './resolution';
10
10
  import { WatchOptions, PendingFile } from './sync';
@@ -227,6 +227,16 @@ export declare class CodeGraph {
227
227
  * freshness without shelling out to `codegraph status --json`. (#329)
228
228
  */
229
229
  getLastIndexedAt(): number | null;
230
+ /**
231
+ * Completeness of the last full index run. `'complete'` is the only good
232
+ * state. `'indexing'` after the fact means a run was killed mid-index (OOM,
233
+ * SIGKILL, liveness watchdog) and the on-disk index is truncated;
234
+ * `'partial'` means the run finished but silently dropped files
235
+ * (discovered > indexed+skipped+errored); `'failed'` means it reported
236
+ * failure. `null` = index predates this marker. Surfaced by
237
+ * `codegraph status`.
238
+ */
239
+ getIndexState(): 'indexing' | 'complete' | 'partial' | 'failed' | null;
230
240
  /**
231
241
  * Which engine built the current index: the package version + extraction
232
242
  * version stamped at the last full `indexAll`. Either field is null for an
@@ -264,6 +274,13 @@ export declare class CodeGraph {
264
274
  * Processes chunks of unresolved refs, persisting results after each batch.
265
275
  */
266
276
  resolveReferencesBatched(onProgress?: (current: number, total: number) => void): Promise<ResolutionResult>;
277
+ /**
278
+ * References extracted but not yet resolved into edges. Zero on a healthy
279
+ * index — a completed resolution pass consumes every row. Non-zero at rest
280
+ * means a pass was interrupted mid-run (killed indexer, crash — #1187), so
281
+ * some files' call edges are missing; the next `sync` sweeps them.
282
+ */
283
+ getPendingReferenceCount(): number;
267
284
  /**
268
285
  * Get detected frameworks in the project
269
286
  */
@@ -307,10 +324,55 @@ export declare class CodeGraph {
307
324
  * definition the caller wants is never dropped below a search cut.
308
325
  */
309
326
  getNodesByName(name: string): Node[];
327
+ /** Nodes whose name starts with `prefix` (index range scan, capped). */
328
+ getNodesByNamePrefix(prefix: string, limit?: number): Node[];
310
329
  /**
311
330
  * Search nodes by text
312
331
  */
313
332
  searchNodes(query: string, options?: SearchOptions): SearchResult[];
333
+ /**
334
+ * Graph-derived prompt matching for the front-load hook's MEDIUM tier:
335
+ * which indexed symbols do these prose words name? "state machine des
336
+ * commandes" → `OrderStateMachine`, in any human language whose technical
337
+ * nouns are Latin script — no keyword list involved.
338
+ *
339
+ * Precision comes from the repo's own naming statistics, not vocabulary:
340
+ * - CO-OCCURRENCE: ≥2 words that are segments of the SAME name ("state" +
341
+ * "machine" → OrderStateMachine) is strong evidence and always qualifies.
342
+ * - RARITY: a single matched word qualifies only when its segment is
343
+ * discriminative here (≤ {@link SEGMENT_RARITY_CEILING} distinct names) —
344
+ * "checkout" in a shop backend yes, "state" in a react app no.
345
+ * Every candidate is re-verified against `nodes` before being returned
346
+ * (vocab rows are proposals; deletions leave orphans by design), so a
347
+ * returned symbol is guaranteed to exist right now.
348
+ */
349
+ getSegmentMatches(words: string[], limit?: number): SegmentMatch[];
350
+ /** A single word ("state") can match hundreds of names in a big repo — that
351
+ * is noise, not signal. Ceiling for the single-word tier; co-occurrence is
352
+ * exempt because two words on one name is already discriminative. */
353
+ private static readonly SEGMENT_RARITY_CEILING;
354
+ /** Which of the prompt's original words match `name`'s segments (via
355
+ * variants). Segments are recomputed in JS — a name-keyed vocab lookup
356
+ * would scan the (segment, name) primary key. */
357
+ private wordsMatchingName;
358
+ /**
359
+ * One-shot upgrade heal for callers that open the graph WITHOUT syncing —
360
+ * concretely the prompt hook, whose MEDIUM tier reads the segment
361
+ * vocabulary: a database migrated from before the vocab table existed
362
+ * starts with it empty, and the only other backfill lives inside `sync()`,
363
+ * which such callers never run (#1142). Returns true when the vocab is
364
+ * usable (already populated — the overwhelmingly common one-SELECT case —
365
+ * or healed here); false when it isn't (empty graph, or another process
366
+ * holds the index lock — that process's own sync heals it).
367
+ */
368
+ healSegmentVocabIfEmpty(): Promise<boolean>;
369
+ /**
370
+ * Rebuild the segment vocabulary from the current graph, batched and
371
+ * yielding — the upgrade-heal path for indexes built before the vocab table
372
+ * existed. Runs inside the index mutex/lock (sync and
373
+ * healSegmentVocabIfEmpty hold them).
374
+ */
375
+ private rebuildNameSegmentVocab;
314
376
  /**
315
377
  * Normalized project-name tokens (go.mod / package.json / repo dir) used to
316
378
  * down-weight the non-discriminative project name in search ranking (#720).
@@ -136,9 +136,11 @@ export declare class Daemon {
136
136
  /**
137
137
  * Defense-in-depth against a daemon that outlives its clients (#692), for the
138
138
  * cases the refcount + idle timer miss because a socket close never arrives:
139
- * - **Inactivity backstop:** exit if no inbound traffic for `maxIdleMs` while
140
- * clients are still (nominally) connected. A phantom client sends nothing,
141
- * so it can't pin the daemon past this window.
139
+ * - **Inactivity backstop:** after `maxIdleMs` with no inbound traffic, reap
140
+ * the daemon but ONLY if no connected client can be proven alive (see
141
+ * {@link backstopShouldExit}). This is the sole phantom class the sweep
142
+ * below can't catch: a client whose client-hello never arrived, so we have
143
+ * no pid to check.
142
144
  * - **Liveness sweep:** drop any client whose peer process has died (per the
143
145
  * client-hello pids), which re-arms the idle timer once the last real
144
146
  * client is gone. Catches a dead peer within one sweep instead of waiting
@@ -147,6 +149,26 @@ export declare class Daemon {
147
149
  * neither should hold it open on its own.
148
150
  */
149
151
  private startLivenessTimers;
152
+ /**
153
+ * Decide whether the inactivity backstop should reap the daemon right now.
154
+ * Public + `isAlive`-injected for deterministic tests; the timer calls it each
155
+ * tick with the real liveness probe.
156
+ *
157
+ * The backstop exists ONLY to catch a **phantom** client (#692) — one counted
158
+ * but actually gone, whose socket-close was never delivered. It must never
159
+ * reap a **live-but-quiet** session (connected, alive peer, just not querying):
160
+ * doing so silently severed the shared daemon and degraded that session — and
161
+ * any others sharing it — to an in-process engine. `lastActivityAt` only tracks
162
+ * inbound query bytes, and MCP has no keepalive, so a genuinely-live session
163
+ * trips the raw inactivity window after ~30 min of not being queried.
164
+ *
165
+ * So: once the inactivity window elapses, drop provably-dead peers (the same
166
+ * check the periodic sweep runs), then reap the daemon only when NOT ONE
167
+ * remaining client can be proven alive — i.e. every client left is an
168
+ * unknown-pid connection the sweep can't verify. A single provably-alive
169
+ * client keeps the daemon up. Has the sweep's side effect (drops dead peers).
170
+ */
171
+ backstopShouldExit(isAlive: (pid: number) => boolean): boolean;
150
172
  /**
151
173
  * Drop every connected client whose peer process is gone. Returns the count
152
174
  * reaped. `isAlive` is injected for testing. Clients with unknown pids (no
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Parent-pid baseline captured as early as possible in process life (#1185).
3
+ *
4
+ * The PPID watchdog's POSIX signal is "`process.ppid` CHANGED since startup" —
5
+ * but a launcher killed within the first ~100ms of our boot (an MCP host's
6
+ * config probe, an instant user cancel, an initialize-timeout teardown) can
7
+ * reparent this process to init BEFORE the serve/proxy code captured its
8
+ * baseline. The baseline then reads `1`, never diverges, and the watchdog is
9
+ * permanently blind — the orphaned-server accumulation reported in #1185.
10
+ * Reproduced on macOS: SIGKILL the launcher 50ms after spawn while the host
11
+ * holds the stdio pipes open, and the server survived indefinitely; at 150ms
12
+ * the old capture had already run and the watchdog reaped it.
13
+ *
14
+ * The CLI entry imports this module before anything else, so the capture runs
15
+ * within the first few ms of JS execution — the earliest a Node process can
16
+ * observe its parent. A kill landing in the remaining pre-JS window (process
17
+ * spawn → first require) still captures `1`; that residual case is covered by
18
+ * the startup-handshake timeout (see ./startup-handshake.ts), which reaps a
19
+ * server that never receives any MCP traffic.
20
+ *
21
+ * Library consumers don't load the CLI entry; for them the capture runs at
22
+ * first import of the MCP layer — no worse than the previous per-call-site
23
+ * capture, and identical once the module cache warms.
24
+ */
25
+ export declare const EARLY_PPID: number;
26
+ //# sourceMappingURL=early-ppid.d.ts.map
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Never-initialized backstop for `serve --mcp` (#1185).
3
+ *
4
+ * Every real MCP host sends `initialize` immediately after spawning a server.
5
+ * A server that has received NO bytes at all for many minutes is not serving
6
+ * anyone — it is the residue of an abandoned launch: the host killed the
7
+ * launcher chain during startup (config probe, instant cancel, initialize
8
+ * timeout) but kept our stdio pipe fds open, so stdin never EOFs. If the kill
9
+ * landed before {@link ../mcp/early-ppid} could observe the real parent, the
10
+ * PPID watchdog is blind too (baseline `1`), and — pre-#1185 — the orphan
11
+ * lived until the HOST process exited, accumulating one ~30MB node process
12
+ * per occurrence.
13
+ *
14
+ * This backstop closes that last hole: arm a one-shot timer at serve start
15
+ * and disarm it on the first byte of client traffic. If the timer fires, the
16
+ * caller shuts the server down. The default is deliberately generous (15
17
+ * minutes) — hosts initialize within milliseconds, so the only processes this
18
+ * ever reaps are ones nobody is talking to. It never affects a session that
19
+ * spoke even once: after the first byte the timer is gone for good (a
20
+ * quiet-but-live session is the PPID watchdog's / stdin teardown's job).
21
+ *
22
+ * IMPORTANT (callers): attaching a `'data'` listener switches the stream into
23
+ * flowing mode. Arm this AFTER the real stdin consumer is attached, in the
24
+ * same synchronous block, so no early bytes are emitted while only our
25
+ * listener exists. The detached daemon must never arm this — its stdin is
26
+ * `'ignore'` and its lifecycle is refcount/idle-based.
27
+ *
28
+ * Tune with `CODEGRAPH_STARTUP_HANDSHAKE_TIMEOUT_MS`; `0` disables.
29
+ */
30
+ /** Default wait for the first byte of MCP traffic before assuming orphaned. */
31
+ export declare const DEFAULT_STARTUP_HANDSHAKE_TIMEOUT_MS = 900000;
32
+ export declare const STARTUP_HANDSHAKE_TIMEOUT_ENV = "CODEGRAPH_STARTUP_HANDSHAKE_TIMEOUT_MS";
33
+ /**
34
+ * Parse the timeout env override. Missing/invalid → default; `<= 0` → `0`
35
+ * (disabled), the same disable convention as `CODEGRAPH_PPID_POLL_MS`.
36
+ */
37
+ export declare function parseStartupHandshakeTimeoutMs(raw: string | undefined): number;
38
+ /**
39
+ * Arm the backstop. `onAbandoned` runs at most once, only if no `'data'` event
40
+ * arrives on `stream` within the timeout. Returns a disarm function (idempotent;
41
+ * also detaches the listener). `stream`/`timeoutMs` are injectable for tests.
42
+ */
43
+ export declare function armStartupHandshakeTimeout(onAbandoned: () => void, stream?: NodeJS.ReadableStream, timeoutMs?: number): () => void;
44
+ //# sourceMappingURL=startup-handshake.d.ts.map
@@ -25,6 +25,28 @@ export declare class NotIndexedError extends Error {
25
25
  */
26
26
  export declare class PathRefusalError extends Error {
27
27
  }
28
+ /**
29
+ * Normalize Erlang-native symbol spellings in an explore query into the shapes
30
+ * the rest of the pipeline already understands. Agents working Erlang code
31
+ * name symbols the way the language spells them — `mod:fn/3`, `init/2` — and
32
+ * those tokens previously died in both consumers: the flow-builder's token
33
+ * filter rejects `:` and `/arity` outright, and the search-side field parser
34
+ * eats `mod:fn` as an unknown `field:value`. Measured on cowboy: the agent
35
+ * named `cowboy_stream_h:request_process/3` in two queries, got no body back
36
+ * either time, and fell back to Read.
37
+ *
38
+ * - `fn/3` → `fn` (arity tail after an identifier; a path segment like
39
+ * `src/2fa` doesn't match because the tail must be all digits)
40
+ * - `mod:fn` → `mod.fn` (exactly one colon between identifiers, so it rides
41
+ * the existing Class.method qualified handling; `::`, URLs, drive letters,
42
+ * and times don't match, and the query language's own field prefixes —
43
+ * kind:/lang:/language:/path:/name: — are left alone)
44
+ *
45
+ * Safe cross-language: Lua's `t:m` spelling maps to the same `t.m` its
46
+ * qualified names use, and no other supported spelling contains a bare
47
+ * single-colon identifier pair.
48
+ */
49
+ export declare function normalizeQuerySpelling(query: string): string;
28
50
  /**
29
51
  * Calculate the recommended number of codegraph_explore calls based on project size.
30
52
  * Larger codebases need more exploration calls to cover their surface area,
@@ -23,6 +23,20 @@ export interface ProjectConfig {
23
23
  * and your `.gitignore`.
24
24
  */
25
25
  exclude?: string[];
26
+ /**
27
+ * Gitignore-style patterns for first-party source to force INTO the index even
28
+ * when `.gitignore` would drop it — the general whitelist `includeIgnored`
29
+ * never was (that one only revives *embedded git repos* inside ignored dirs).
30
+ * The case this exists for: a project under a second VCS (SVN, Perforce, …)
31
+ * deliberately `.gitignore`s its own real source so it never lands in Git, yet
32
+ * that source must still be indexed. Matched against project-root-relative
33
+ * paths, so `"Tools/"`, a recursive `"Tools/**"` glob, or `"Local/typescript"`
34
+ * all work.
35
+ * Built-in default-ignored dirs (`node_modules`, `dist`, …), `.git`, and
36
+ * CodeGraph's own data dir are never resurfaced; an explicit `exclude` still
37
+ * wins. Absent/empty (the default) forces nothing in.
38
+ */
39
+ include?: string[];
26
40
  }
27
41
  /**
28
42
  * Load the validated extension overrides for a project, mtime-cached.
@@ -51,6 +65,30 @@ export declare function loadIncludeIgnoredPatterns(rootDir: string): string[];
51
65
  * the built-in defaults and the project's `.gitignore`.
52
66
  */
53
67
  export declare function loadExcludePatterns(rootDir: string): string[];
68
+ /**
69
+ * Load the validated `include` patterns for a project, mtime-cached.
70
+ *
71
+ * These name first-party source to force INTO the index even when `.gitignore`
72
+ * would drop it — the whitelist for SVN/Perforce-only source a project
73
+ * gitignores out of Git. An empty result — the zero-config default — forces
74
+ * nothing in. Built-in default-ignored dirs, `.git`, and CodeGraph's data dir
75
+ * are never resurfaced, and an explicit `exclude` still wins.
76
+ */
77
+ export declare function loadIncludePatterns(rootDir: string): string[];
54
78
  /** Test/maintenance hook: forget cached config (e.g. after rewriting it in a test). */
55
79
  export declare function clearProjectConfigCache(): void;
80
+ /**
81
+ * Add gitignore-style patterns to a project's `codegraph.json` `includeIgnored`
82
+ * list, creating the file if absent and preserving every other key. Used by the
83
+ * CLI to opt a "super-repo of gitignored child repos" (#1156) into the index on
84
+ * the user's say-so. Returns the count of patterns actually ADDED (ones already
85
+ * present are skipped, so a re-run is idempotent).
86
+ *
87
+ * A plain-JSON round-trip: a `codegraph.json` carrying comments (not valid JSON)
88
+ * already fails to load with a warning, so rather than silently clobber such a
89
+ * file this throws when an existing config won't parse — the caller falls back
90
+ * to printing the manual snippet. Invalidates the config cache so a subsequent
91
+ * index in the same process sees the new patterns.
92
+ */
93
+ export declare function addIncludeIgnoredPatterns(rootDir: string, patterns: string[]): number;
56
94
  //# sourceMappingURL=project-config.d.ts.map
@@ -11,5 +11,5 @@ import type { ResolutionContext } from './types';
11
11
  * Sidekiq Worker.perform_async → #perform + Laravel event(new X) → listener handle).
12
12
  * Returns the count added. Never throws into indexing — callers wrap in try/catch.
13
13
  */
14
- export declare function synthesizeCallbackEdges(queries: QueryBuilder, ctx: ResolutionContext): number;
14
+ export declare function synthesizeCallbackEdges(queries: QueryBuilder, ctx: ResolutionContext): Promise<number>;
15
15
  //# sourceMappingURL=callback-synthesizer.d.ts.map
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Cooperative yielding for long synchronous resolution spans.
3
+ *
4
+ * Reference resolution and callback-edge synthesis run on the indexer's MAIN
5
+ * thread — unlike parsing, which is off-thread in the parse worker. The #850
6
+ * liveness watchdog (armed on `index`/`init` since #999) SIGKILLs the process
7
+ * when that thread doesn't turn its event loop for the timeout window (default
8
+ * 60s), because its heartbeat is a `setInterval` on that same thread. On a large
9
+ * repo, resolving refs + synthesizing dynamic-dispatch edges legitimately runs
10
+ * for minutes, so a span that never yields starves the heartbeat and the
11
+ * watchdog kills a VALID, in-progress index — the exact symptom of #1091 (the
12
+ * progress bar freezes at wherever it last rendered — 88% / 100% — then the
13
+ * process is killed).
14
+ *
15
+ * `createYielder` returns a `maybeYield()` that yields (via `setImmediate`) only
16
+ * once more than `budgetMs` of wall-clock has elapsed since the last yield, so
17
+ * fast repos pay essentially nothing while slow ones give the heartbeat a
18
+ * regular window to fire. Call it at natural boundaries in a long loop (between
19
+ * batches, between synthesis passes).
20
+ *
21
+ * This does NOT weaken the watchdog. A genuinely wedged loop — an infinite or
22
+ * non-terminating span, the case the watchdog exists to catch — never reaches a
23
+ * yield point, so the heartbeat still stops and the SIGKILL still fires. We only
24
+ * stop killing work that is demonstrably making progress.
25
+ */
26
+ /** Yield when more than `budgetMs` of wall-clock has passed since the last yield. */
27
+ export type MaybeYield = () => Promise<void>;
28
+ /** Default budget: well under the watchdog's minimum heartbeat cadence (~1s), so
29
+ * a heartbeat byte always has a chance to land between yields. */
30
+ export declare const DEFAULT_YIELD_BUDGET_MS = 250;
31
+ export declare function createYielder(budgetMs?: number): MaybeYield;
32
+ //# sourceMappingURL=cooperative-yield.d.ts.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * CICS Framework Resolver (COBOL)
3
+ *
4
+ * Resolves the pseudo-conversational transaction hop: a program ends with
5
+ * `EXEC CICS RETURN TRANSID('CB00')` (or START), and CICS re-invokes the
6
+ * program that OWNS transaction CB00 on the next attention key. The
7
+ * transaction→program mapping lives in the CICS CSD, which is never in the
8
+ * repo — but by near-universal convention each program declares its own
9
+ * transaction id as a working-storage constant:
10
+ *
11
+ * 05 WS-TRANID PIC X(04) VALUE 'CB00'.
12
+ *
13
+ * The COBOL extractor emits `cics-transid:CB00` call references for literal
14
+ * (or same-file-dereferenced) TRANSID options; this resolver maps the id to
15
+ * the program module whose TRAN*-named data item declares that VALUE. No
16
+ * match (an id owned by a program outside the repo) stays unresolved.
17
+ */
18
+ import { FrameworkResolver } from '../types';
19
+ export declare const cicsResolver: FrameworkResolver;
20
+ //# sourceMappingURL=cics.d.ts.map
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Terraform Framework Resolver
3
+ *
4
+ * Terraform's scoping rule is narrow and directory-shaped: `var.X`,
5
+ * `local.X`, `module.M`, and resource/data references resolve ONLY inside
6
+ * the same module directory as the reference site. The generic name matcher
7
+ * resolves by qualified-name alone, so a reference to `var.project_id` from
8
+ * `modules/net-vpc/main.tf` could bind to a `variable "project_id"` declared
9
+ * in an unrelated module — a wrong cross-module edge that poisons impact
10
+ * analysis. This resolver enforces the real semantics:
11
+ *
12
+ * 1. Same directory as the reference site → resolve (highest confidence).
13
+ * 2. `.tfvars` files additionally walk UP to the nearest ancestor
14
+ * directory declaring the variable (`terraform apply -var-file=envs/prod.tfvars`
15
+ * sets ROOT module variables from a subdirectory).
16
+ * 3. Otherwise: no edge. Terraform cannot reference across sibling module
17
+ * directories, so a non-local candidate is never a correct target.
18
+ *
19
+ * It also bridges the module boundary through `:`-scoped references that
20
+ * only this resolver understands (see the extractor's emitModuleWiring):
21
+ *
22
+ * - `module.M:file` → the entry file of the module's local source
23
+ * directory (an `imports` edge, so a module call connects to the code
24
+ * it instantiates).
25
+ * - `module.M:var.<in>` → the child module's `variable "<in>"` node —
26
+ * the module block sets that variable, so "what depends on the child's
27
+ * var.cidr" reaches every caller.
28
+ * - `module.M:output.<o>` → the child module's `output "<o>"` node —
29
+ * `module.M.o` uses flow through to the output's definition instead of
30
+ * dead-ending at the module declaration.
31
+ *
32
+ * The module's `source` is re-read from the declaration's file (cached
33
+ * lines); only local `./`/`../` sources bridge. Registry/git sources stay
34
+ * unresolved — an out-of-repo module is a visible boundary, never a guess.
35
+ */
36
+ import type { FrameworkResolver } from '../types';
37
+ export declare const terraformResolver: FrameworkResolver;
38
+ //# sourceMappingURL=terraform.d.ts.map
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { Language } from '../types';
7
7
  import { UnresolvedRef, ResolvedRef, ResolutionContext, ImportMapping, ReExport } from './types';
8
+ export declare function isNixPathImportRef(ref: UnresolvedRef): boolean;
8
9
  /**
9
10
  * Resolve an import path to an actual file
10
11
  */
@@ -37,6 +38,12 @@ export declare function loadCppIncludeDirs(projectRoot: string): string[];
37
38
  * to a same-named symbol — so callers must not fall back to the name-matcher.
38
39
  */
39
40
  export declare function isPhpIncludePathRef(ref: UnresolvedRef): boolean;
41
+ /**
42
+ * Is this a COBOL COPY / EXEC SQL INCLUDE copybook reference? These resolve
43
+ * to files only (or stay unresolved for compiler-supplied members) — never
44
+ * to a same-named symbol via the name-matcher.
45
+ */
46
+ export declare function isCobolCopybookRef(ref: UnresolvedRef): boolean;
40
47
  /**
41
48
  * Extract import mappings from a file
42
49
  */