gitnexus 1.6.4-rc.8 → 1.6.4-rc.9

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.
@@ -44,7 +44,7 @@ export { CLASS_KINDS, METHOD_KINDS, FIELD_KINDS } from './scope-resolution/regis
44
44
  export type { RegistryContext, RegistryProviders, OwnerScopedContributor, ArityVerdict, } from './scope-resolution/registries/context.js';
45
45
  export { makeScopeId, clearScopeIdInternPool } from './scope-resolution/scope-id.js';
46
46
  export type { ScopeIdInput } from './scope-resolution/scope-id.js';
47
- export { buildScopeTree, ScopeTreeInvariantError } from './scope-resolution/scope-tree.js';
47
+ export { buildScopeTree, canParentScope, ScopeTreeInvariantError, } from './scope-resolution/scope-tree.js';
48
48
  export type { ScopeTree } from './scope-resolution/scope-tree.js';
49
49
  export { buildPositionIndex } from './scope-resolution/position-index.js';
50
50
  export type { PositionIndex } from './scope-resolution/position-index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -31,7 +31,7 @@ export { compareByConfidenceWithTiebreaks, CONFIDENCE_EPSILON, } from './scope-r
31
31
  export { CLASS_KINDS, METHOD_KINDS, FIELD_KINDS } from './scope-resolution/registries/context.js';
32
32
  // Scope tree spine + position lookup (RFC §2.2 + §3.1; Ring 2 SHARED #912)
33
33
  export { makeScopeId, clearScopeIdInternPool } from './scope-resolution/scope-id.js';
34
- export { buildScopeTree, ScopeTreeInvariantError } from './scope-resolution/scope-tree.js';
34
+ export { buildScopeTree, canParentScope, ScopeTreeInvariantError, } from './scope-resolution/scope-tree.js';
35
35
  export { buildPositionIndex } from './scope-resolution/position-index.js';
36
36
  // Shadow-mode diff + aggregation (RFC §6.3; Ring 2 SHARED #918)
37
37
  export { diffResolutions } from './scope-resolution/shadow/diff.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,mBAAmB;AACnB,OAAO,EACL,WAAW,EACX,cAAc,EACd,SAAS,EACT,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AAGpC,mBAAmB;AACnB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAC;AAiCjG,8DAA8D;AAC9D,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AACnG,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAGxE,yDAAyD;AACzD,OAAO,EACL,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,+CAA+C,CAAC;AAGvD,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAEhE,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AAEjF,OAAO,EAAE,uBAAuB,EAAE,MAAM,4CAA4C,CAAC;AAGrF,gEAAgE;AAChE,yEAAyE;AACzE,2DAA2D;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAOxE,oFAAoF;AACpF,OAAO,EAAE,wBAAwB,EAAE,MAAM,6CAA6C,CAAC;AAMvF,uEAAuE;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,0CAA0C,CAAC;AAUpE,sEAAsE;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAErF,OAAO,EAAE,mBAAmB,EAAE,MAAM,kDAAkD,CAAC;AAKvF,OAAO,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAKrF,OAAO,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAC;AAE1E,OAAO,EAAE,eAAe,EAAE,MAAM,mDAAmD,CAAC;AAEpF,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AAEpG,OAAO,EACL,gCAAgC,EAChC,kBAAkB,GACnB,MAAM,6CAA6C,CAAC;AAErD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,0CAA0C,CAAC;AAQlG,2EAA2E;AAC3E,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAErF,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAE3F,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAG1E,gEAAgE;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAMpE,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,mBAAmB;AACnB,OAAO,EACL,WAAW,EACX,cAAc,EACd,SAAS,EACT,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AAGpC,mBAAmB;AACnB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAC;AAiCjG,8DAA8D;AAC9D,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAC;AACnG,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAGxE,yDAAyD;AACzD,OAAO,EACL,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,+CAA+C,CAAC;AAGvD,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAEhE,OAAO,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AAEjF,OAAO,EAAE,uBAAuB,EAAE,MAAM,4CAA4C,CAAC;AAGrF,gEAAgE;AAChE,yEAAyE;AACzE,2DAA2D;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAOxE,oFAAoF;AACpF,OAAO,EAAE,wBAAwB,EAAE,MAAM,6CAA6C,CAAC;AAMvF,uEAAuE;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,0CAA0C,CAAC;AAUpE,sEAAsE;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAErF,OAAO,EAAE,mBAAmB,EAAE,MAAM,kDAAkD,CAAC;AAKvF,OAAO,EAAE,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAKrF,OAAO,EAAE,UAAU,EAAE,MAAM,8CAA8C,CAAC;AAE1E,OAAO,EAAE,eAAe,EAAE,MAAM,mDAAmD,CAAC;AAEpF,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AAEpG,OAAO,EACL,gCAAgC,EAChC,kBAAkB,GACnB,MAAM,6CAA6C,CAAC;AAErD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,0CAA0C,CAAC;AAQlG,2EAA2E;AAC3E,OAAO,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAErF,OAAO,EACL,cAAc,EACd,cAAc,EACd,uBAAuB,GACxB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAG1E,gEAAgE;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAMpE,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC"}
@@ -25,7 +25,7 @@
25
25
  * Immutable surface: `byId` is a `ReadonlyMap`; children arrays are
26
26
  * `Object.freeze`d; miss lookups return a shared frozen empty array.
27
27
  */
28
- import type { Scope, ScopeId, ScopeLookup } from './types.js';
28
+ import type { Scope, ScopeId, ScopeLookup, Range } from './types.js';
29
29
  export interface ScopeTree extends ScopeLookup {
30
30
  readonly size: number;
31
31
  readonly byId: ReadonlyMap<ScopeId, Scope>;
@@ -58,4 +58,26 @@ export declare class ScopeTreeInvariantError extends Error {
58
58
  * consumers to handle, so fail-fast is the correct posture.
59
59
  */
60
60
  export declare function buildScopeTree(scopes: readonly Scope[]): ScopeTree;
61
+ /**
62
+ * Whether `outer` (kind `outerKind`) is a valid parent for `inner` (kind
63
+ * `innerKind`).
64
+ *
65
+ * Strict containment is the general rule. The single carve-out is the
66
+ * `Module`/non-`Module` pair whose ranges are exactly equal — this happens
67
+ * naturally when tree-sitter reports identical byte spans for the
68
+ * `compilation_unit` (or equivalent file-root construct) and the file's
69
+ * single top-level scope. Common shape: a C# file consisting of nothing
70
+ * but `namespace X { ... }` with no leading or trailing trivia outside the
71
+ * namespace's `{}` body — `compilation_unit` and `namespace_declaration`
72
+ * both span exactly the same byte range. The `Module` is the universal
73
+ * outer of any file-level scope by language semantics, so coincident
74
+ * ranges should not break the parent chain.
75
+ *
76
+ * The carve-out is direction-asymmetric: only `Module`-as-outer parents a
77
+ * same-range non-`Module`, never the reverse. This preserves the
78
+ * acyclicity buildScopeTree relies on, and matches the corresponding
79
+ * helper in `scope-extractor.ts` so `pass1BuildScopes` and the validator
80
+ * agree on what a well-formed parent edge looks like.
81
+ */
82
+ export declare function canParentScope(outer: Range, inner: Range, outerKind: Scope['kind'], innerKind: Scope['kind']): boolean;
61
83
  //# sourceMappingURL=scope-tree.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scope-tree.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/scope-tree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAS,MAAM,YAAY,CAAC;AAIrE,MAAM,WAAW,SAAU,SAAQ,WAAW;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAE3C,QAAQ,CAAC,EAAE,EAAE,OAAO,GAAG,KAAK,GAAG,SAAS,CAAC;IACzC,SAAS,CAAC,EAAE,EAAE,OAAO,GAAG,KAAK,GAAG,SAAS,CAAC;IAC1C,4EAA4E;IAC5E,WAAW,CAAC,EAAE,EAAE,OAAO,GAAG,SAAS,OAAO,EAAE,CAAC;IAC7C;;;;OAIG;IACH,YAAY,CAAC,EAAE,EAAE,OAAO,GAAG,SAAS,OAAO,EAAE,CAAC;IAC9C,GAAG,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC;CAC3B;AAID;;;;GAIG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;IAE9C,QAAQ,CAAC,SAAS,EACd,4BAA4B,GAC5B,kBAAkB,GAClB,2BAA2B,GAC3B,wBAAwB,GACxB,4BAA4B,GAC5B,oBAAoB;gBANf,SAAS,EACd,4BAA4B,GAC5B,kBAAkB,GAClB,2BAA2B,GAC3B,wBAAwB,GACxB,4BAA4B,GAC5B,oBAAoB,EACxB,OAAO,EAAE,MAAM;CAKlB;AAID;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,SAAS,CAiFlE"}
1
+ {"version":3,"file":"scope-tree.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/scope-tree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAIrE,MAAM,WAAW,SAAU,SAAQ,WAAW;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAE3C,QAAQ,CAAC,EAAE,EAAE,OAAO,GAAG,KAAK,GAAG,SAAS,CAAC;IACzC,SAAS,CAAC,EAAE,EAAE,OAAO,GAAG,KAAK,GAAG,SAAS,CAAC;IAC1C,4EAA4E;IAC5E,WAAW,CAAC,EAAE,EAAE,OAAO,GAAG,SAAS,OAAO,EAAE,CAAC;IAC7C;;;;OAIG;IACH,YAAY,CAAC,EAAE,EAAE,OAAO,GAAG,SAAS,OAAO,EAAE,CAAC;IAC9C,GAAG,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC;CAC3B;AAID;;;;GAIG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;IAE9C,QAAQ,CAAC,SAAS,EACd,4BAA4B,GAC5B,kBAAkB,GAClB,2BAA2B,GAC3B,wBAAwB,GACxB,4BAA4B,GAC5B,oBAAoB;gBANf,SAAS,EACd,4BAA4B,GAC5B,kBAAkB,GAClB,2BAA2B,GAC3B,wBAAwB,GACxB,4BAA4B,GAC5B,oBAAoB,EACxB,OAAO,EAAE,MAAM;CAKlB;AAID;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,SAAS,CAiFlE;AA+ED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,EACxB,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,GACvB,OAAO,CAIT"}
@@ -72,8 +72,8 @@ export function buildScopeTree(scopes) {
72
72
  if (parent.filePath !== scope.filePath) {
73
73
  throw new ScopeTreeInvariantError('parent-must-share-filepath', `Scope '${scope.id}' (${scope.filePath}) has parent '${parent.id}' in a different file (${parent.filePath}). Parent/child scopes must share filePath.`);
74
74
  }
75
- if (!rangeStrictlyContains(parent.range, scope.range)) {
76
- throw new ScopeTreeInvariantError('parent-must-contain-child', `Parent scope '${parent.id}' at ${formatRange(parent.range)} does not strictly contain child '${scope.id}' at ${formatRange(scope.range)}.`);
75
+ if (!canParentScope(parent.range, scope.range, parent.kind, scope.kind)) {
76
+ throw new ScopeTreeInvariantError('parent-must-contain-child', `Parent scope '${parent.id}' at ${formatRange(parent.range)} does not contain child '${scope.id}' at ${formatRange(scope.range)} (allowed: strict containment, or equal-range Module-as-parent).`);
77
77
  }
78
78
  let bucket = childrenById.get(parent.id);
79
79
  if (bucket === undefined) {
@@ -164,6 +164,40 @@ function rangeStrictlyContains(outer, inner) {
164
164
  (outer.endLine === inner.endLine && outer.endCol >= inner.endCol);
165
165
  return outerStartsAtOrBefore && outerEndsAtOrAfter;
166
166
  }
167
+ function rangesEqual(a, b) {
168
+ return (a.startLine === b.startLine &&
169
+ a.startCol === b.startCol &&
170
+ a.endLine === b.endLine &&
171
+ a.endCol === b.endCol);
172
+ }
173
+ /**
174
+ * Whether `outer` (kind `outerKind`) is a valid parent for `inner` (kind
175
+ * `innerKind`).
176
+ *
177
+ * Strict containment is the general rule. The single carve-out is the
178
+ * `Module`/non-`Module` pair whose ranges are exactly equal — this happens
179
+ * naturally when tree-sitter reports identical byte spans for the
180
+ * `compilation_unit` (or equivalent file-root construct) and the file's
181
+ * single top-level scope. Common shape: a C# file consisting of nothing
182
+ * but `namespace X { ... }` with no leading or trailing trivia outside the
183
+ * namespace's `{}` body — `compilation_unit` and `namespace_declaration`
184
+ * both span exactly the same byte range. The `Module` is the universal
185
+ * outer of any file-level scope by language semantics, so coincident
186
+ * ranges should not break the parent chain.
187
+ *
188
+ * The carve-out is direction-asymmetric: only `Module`-as-outer parents a
189
+ * same-range non-`Module`, never the reverse. This preserves the
190
+ * acyclicity buildScopeTree relies on, and matches the corresponding
191
+ * helper in `scope-extractor.ts` so `pass1BuildScopes` and the validator
192
+ * agree on what a well-formed parent edge looks like.
193
+ */
194
+ export function canParentScope(outer, inner, outerKind, innerKind) {
195
+ if (rangeStrictlyContains(outer, inner))
196
+ return true;
197
+ if (outerKind === 'Module' && innerKind !== 'Module' && rangesEqual(outer, inner))
198
+ return true;
199
+ return false;
200
+ }
167
201
  /**
168
202
  * Two ranges overlap when neither finishes before the other begins. Ranges
169
203
  * that merely touch at a single boundary point (`a.end === b.start`) do
@@ -1 +1 @@
1
- {"version":3,"file":"scope-tree.js","sourceRoot":"","sources":["../../src/scope-resolution/scope-tree.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scope-tree.js","sourceRoot":"","sources":["../../src/scope-resolution/scope-tree.ts"],"names":[],"mappings":"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"}
@@ -58,7 +58,7 @@
58
58
  * - `ParsedFile.localDefs` — flattened union of `Scope.ownedDefs`.
59
59
  * - `ParsedFile.referenceSites` — pre-resolution usage facts.
60
60
  */
61
- import { buildPositionIndex, buildScopeTree, makeScopeId } from '../../_shared/index.js';
61
+ import { buildPositionIndex, buildScopeTree, canParentScope, makeScopeId } from '../../_shared/index.js';
62
62
  // ─── Public entry point ─────────────────────────────────────────────────────
63
63
  /**
64
64
  * Drive the five extraction passes and return a `ParsedFile`.
@@ -210,7 +210,11 @@ function pass1BuildScopes(matches, filePath, provider) {
210
210
  candidates.push({ match, range: anchor.range, kind, id });
211
211
  }
212
212
  // Sort by (startLine, startCol) ASC, (endLine, endCol) DESC so outer
213
- // scopes appear before their children for parent-resolution.
213
+ // scopes appear before their children for parent-resolution. When two
214
+ // candidates have exactly equal ranges (e.g. a `compilation_unit` and
215
+ // the only top-level scope in the file — see `canParentScope`), Module
216
+ // sorts first so it lands on the stack ahead of the candidate that will
217
+ // claim it as parent.
214
218
  candidates.sort((a, b) => {
215
219
  if (a.range.startLine !== b.range.startLine)
216
220
  return a.range.startLine - b.range.startLine;
@@ -218,13 +222,23 @@ function pass1BuildScopes(matches, filePath, provider) {
218
222
  return a.range.startCol - b.range.startCol;
219
223
  if (a.range.endLine !== b.range.endLine)
220
224
  return b.range.endLine - a.range.endLine;
221
- return b.range.endCol - a.range.endCol;
225
+ if (a.range.endCol !== b.range.endCol)
226
+ return b.range.endCol - a.range.endCol;
227
+ if (a.kind === b.kind)
228
+ return 0;
229
+ if (a.kind === 'Module')
230
+ return -1;
231
+ if (b.kind === 'Module')
232
+ return 1;
233
+ return 0;
222
234
  });
223
235
  const drafts = [];
224
236
  const stack = []; // enclosing real scopes, outermost at [0]
225
237
  for (const cand of candidates) {
226
- // Pop the stack until the top strictly contains this candidate.
227
- while (stack.length > 0 && !rangeStrictlyContains(stack[stack.length - 1].range, cand.range)) {
238
+ // Pop the stack until the top can parent this candidate (strict
239
+ // containment, plus the equal-range Module carve-out).
240
+ while (stack.length > 0 &&
241
+ !canParentScope(stack[stack.length - 1].range, cand.range, stack[stack.length - 1].kind, cand.kind)) {
228
242
  stack.pop();
229
243
  }
230
244
  const parent = stack.length > 0 ? stack[stack.length - 1].id : null;
@@ -692,19 +706,6 @@ function rangesEqual(a, b) {
692
706
  a.endLine === b.endLine &&
693
707
  a.endCol === b.endCol);
694
708
  }
695
- function rangeStrictlyContains(outer, inner) {
696
- if (outer.startLine === inner.startLine &&
697
- outer.startCol === inner.startCol &&
698
- outer.endLine === inner.endLine &&
699
- outer.endCol === inner.endCol) {
700
- return false;
701
- }
702
- const startsBefore = outer.startLine < inner.startLine ||
703
- (outer.startLine === inner.startLine && outer.startCol <= inner.startCol);
704
- const endsAfter = outer.endLine > inner.endLine ||
705
- (outer.endLine === inner.endLine && outer.endCol >= inner.endCol);
706
- return startsBefore && endsAfter;
707
- }
708
709
  /**
709
710
  * Capture names that are never anchors — they are sub-tags nested inside a
710
711
  * larger anchor (e.g., the receiver expression inside a `@reference.call`
@@ -150,10 +150,9 @@ export declare const createFTSIndex: (tableName: string, indexName: string, prop
150
150
  /**
151
151
  * Lazy-create an FTS index, caching the fact in-process.
152
152
  *
153
- * Used by `queryFTS` so that `analyze` doesn't pay the ~440 ms × 5 fixed
154
- * LadybugDB cost up-front (it dominates analyze on small repos). Instead,
155
- * the cost is moved to the first `query`/`context` call in a session,
156
- * where it's amortised across many lookups.
153
+ * Kept for writable maintenance paths that need to lazily materialize an
154
+ * index. Read-only query paths must not call this; production analysis owns
155
+ * creating the configured search indexes before the database is served.
157
156
  *
158
157
  * Safe to call repeatedly — the in-process Set guarantees only the first
159
158
  * call hits LadybugDB. `closeLbug` clears the cache so re-init starts fresh.
@@ -112,11 +112,9 @@ let ftsLoaded = false;
112
112
  let vectorExtensionLoaded = false;
113
113
  /**
114
114
  * In-process cache of FTS indexes that have been ensured against the current
115
- * connection. Prevents repeated `CALL CREATE_FTS_INDEX` round-trips inside a
116
- * single CLI/MCP session the first call to `ensureFTSIndex` for a given
117
- * `(tableName, indexName)` pays the LadybugDB cost (~440 ms even when the
118
- * index already exists on disk), subsequent calls are a Set lookup. Cleared
119
- * by `closeLbug` so a re-init starts fresh.
115
+ * writable connection. Prevents repeated `CALL CREATE_FTS_INDEX` round-trips
116
+ * for callers that explicitly opt into `ensureFTSIndex`. Cleared by
117
+ * `closeLbug` so a re-init starts fresh.
120
118
  *
121
119
  * Key format: `${tableName}:${indexName}`.
122
120
  */
@@ -1126,10 +1124,9 @@ export const createFTSIndex = async (tableName, indexName, properties, stemmer =
1126
1124
  /**
1127
1125
  * Lazy-create an FTS index, caching the fact in-process.
1128
1126
  *
1129
- * Used by `queryFTS` so that `analyze` doesn't pay the ~440 ms × 5 fixed
1130
- * LadybugDB cost up-front (it dominates analyze on small repos). Instead,
1131
- * the cost is moved to the first `query`/`context` call in a session,
1132
- * where it's amortised across many lookups.
1127
+ * Kept for writable maintenance paths that need to lazily materialize an
1128
+ * index. Read-only query paths must not call this; production analysis owns
1129
+ * creating the configured search indexes before the database is served.
1133
1130
  *
1134
1131
  * Safe to call repeatedly — the in-process Set guarantees only the first
1135
1132
  * call hits LadybugDB. `closeLbug` clears the cache so re-init starts fresh.
@@ -12,6 +12,7 @@ import path from 'path';
12
12
  import fs from 'fs/promises';
13
13
  import { runPipelineFromRepo } from './ingestion/pipeline.js';
14
14
  import { initLbug, loadGraphToLbug, getLbugStats, executeQuery, executeWithReusedStatement, closeLbug, loadCachedEmbeddings, } from './lbug/lbug-adapter.js';
15
+ import { createSearchFTSIndexes } from './search/fts-indexes.js';
15
16
  import { getStoragePaths, saveMeta, loadMeta, addToGitignore, registerRepo, cleanupOldKuzuFiles, } from '../storage/repo-manager.js';
16
17
  import { getCurrentCommit, getRemoteUrl, hasGitDir, getInferredRepoName } from '../storage/git.js';
17
18
  import { generateAIContextFiles } from '../cli/ai-context.js';
@@ -165,12 +166,9 @@ export async function runFullAnalysis(repoPath, options, callbacks) {
165
166
  progress('lbug', pct, msg);
166
167
  });
167
168
  // ── Phase 3: FTS (85–90%) ─────────────────────────────────────────
168
- // FTS indexes are created lazily on first `query`/`context` call instead
169
- // of eagerly here. On small repos / CI runners the LadybugDB
170
- // CREATE_FTS_INDEX cost is ~440 ms × 5 (≈2 s) regardless of table size,
171
- // which dominated `analyze` runtime and pushed Windows CI past its
172
- // 30 s test budget. Lazy creation is implemented in
173
- // `core/search/bm25-index.ts` via `ensureFTSIndex`.
169
+ progress('fts', 85, 'Creating search indexes...');
170
+ await createSearchFTSIndexes();
171
+ progress('fts', 90, 'Search indexes ready');
174
172
  // ── Phase 3.5: Re-insert cached embeddings ────────────────────────
175
173
  if (cachedEmbeddings.length > 0) {
176
174
  const cachedDims = cachedEmbeddings[0].embedding.length;
@@ -3,12 +3,6 @@
3
3
  *
4
4
  * Uses LadybugDB's built-in full-text search indexes for keyword-based search.
5
5
  * Always reads from the database (no cached state to drift).
6
- *
7
- * FTS indexes are created lazily on first query (via `ensureFTSIndex`) — see
8
- * `lbug-adapter.ts` for the rationale. This keeps `analyze` fast (the
9
- * ~440 ms × 5 LadybugDB CREATE_FTS_INDEX cost dominates pipeline time on
10
- * small repos / CI runners) at the cost of paying that overhead on the
11
- * first `query`/`context` call in a session.
12
6
  */
13
7
  export interface BM25SearchResult {
14
8
  filePath: string;
@@ -16,17 +10,6 @@ export interface BM25SearchResult {
16
10
  rank: number;
17
11
  nodeIds?: string[];
18
12
  }
19
- /**
20
- * Drop all ensured-FTS cache entries for a given repoId.
21
- *
22
- * Called from the pool-close listener so that a pool teardown / recreation
23
- * forces the next `searchFTSFromLbug` call to re-issue `CREATE_FTS_INDEX`
24
- * against the fresh connection rather than trust stale ensure-state from a
25
- * previous pool lifetime.
26
- *
27
- * Exported for tests; the listener wiring is internal.
28
- */
29
- export declare function invalidateEnsuredFTSForRepo(repoId: string): void;
30
13
  /**
31
14
  * Search using LadybugDB's built-in FTS (always fresh, reads from disk)
32
15
  *
@@ -3,96 +3,9 @@
3
3
  *
4
4
  * Uses LadybugDB's built-in full-text search indexes for keyword-based search.
5
5
  * Always reads from the database (no cached state to drift).
6
- *
7
- * FTS indexes are created lazily on first query (via `ensureFTSIndex`) — see
8
- * `lbug-adapter.ts` for the rationale. This keeps `analyze` fast (the
9
- * ~440 ms × 5 LadybugDB CREATE_FTS_INDEX cost dominates pipeline time on
10
- * small repos / CI runners) at the cost of paying that overhead on the
11
- * first `query`/`context` call in a session.
12
- */
13
- import { queryFTS, ensureFTSIndex } from '../lbug/lbug-adapter.js';
14
- /**
15
- * FTS schema served by `searchFTSFromLbug`. Centralised so that both the
16
- * CLI/pipeline path and the MCP pool path use identical (table, index,
17
- * properties) tuples and the lazy-create logic stays in one place.
18
- */
19
- const FTS_INDEXES = [
20
- { table: 'File', indexName: 'file_fts', properties: ['name', 'content'] },
21
- { table: 'Function', indexName: 'function_fts', properties: ['name', 'content'] },
22
- { table: 'Class', indexName: 'class_fts', properties: ['name', 'content'] },
23
- { table: 'Method', indexName: 'method_fts', properties: ['name', 'content'] },
24
- { table: 'Interface', indexName: 'interface_fts', properties: ['name', 'content'] },
25
- ];
26
- /**
27
- * Per-process cache for the MCP pool path: tracks which `(repoId, table)`
28
- * pairs have been ensured. The CLI/pipeline path gets its own cache inside
29
- * `lbug-adapter.ts` keyed by table/index, scoped to the singleton connection.
30
- *
31
- * IMPORTANT: an entry is added ONLY when the index was confirmed to exist
32
- * (CREATE_FTS_INDEX succeeded, or failed with `'already exists'`). Other
33
- * failures (transient lock errors, missing extension, etc.) leave the key
34
- * unset so the next query retries instead of silently caching the failure.
35
- *
36
- * Entries for a given repoId are invalidated when its pool is closed —
37
- * see the `addPoolCloseListener` registration in `searchFTSFromLbug`.
38
- */
39
- const ensuredPoolFTS = new Set();
40
- /**
41
- * Drop all ensured-FTS cache entries for a given repoId.
42
- *
43
- * Called from the pool-close listener so that a pool teardown / recreation
44
- * forces the next `searchFTSFromLbug` call to re-issue `CREATE_FTS_INDEX`
45
- * against the fresh connection rather than trust stale ensure-state from a
46
- * previous pool lifetime.
47
- *
48
- * Exported for tests; the listener wiring is internal.
49
6
  */
50
- export function invalidateEnsuredFTSForRepo(repoId) {
51
- const prefix = `${repoId}:`;
52
- for (const key of ensuredPoolFTS) {
53
- if (key.startsWith(prefix))
54
- ensuredPoolFTS.delete(key);
55
- }
56
- }
57
- /**
58
- * Tracks whether we've already wired the pool-close listener for this
59
- * process. The pool adapter is dynamically imported, so registration
60
- * happens lazily on the first MCP-pool-backed FTS query.
61
- */
62
- let poolCloseListenerRegistered = false;
63
- function registerPoolCloseListenerOnce(addPoolCloseListener) {
64
- if (poolCloseListenerRegistered)
65
- return;
66
- poolCloseListenerRegistered = true;
67
- addPoolCloseListener((repoId) => invalidateEnsuredFTSForRepo(repoId));
68
- }
69
- async function ensureFTSIndexViaExecutor(executor, repoId, table, indexName, properties) {
70
- const key = `${repoId}:${table}:${indexName}`;
71
- if (ensuredPoolFTS.has(key))
72
- return;
73
- const propList = properties.map((p) => `'${p}'`).join(', ');
74
- try {
75
- await executor(`CALL CREATE_FTS_INDEX('${table}', '${indexName}', [${propList}], stemmer := 'porter')`);
76
- // Index was created successfully — safe to cache.
77
- ensuredPoolFTS.add(key);
78
- }
79
- catch (e) {
80
- // 'already exists' is the happy path (index persists on disk between
81
- // process invocations) — cache it. Anything else is treated as a
82
- // transient failure: surface a one-time warning and leave the key
83
- // unset so the NEXT query retries rather than silently using a
84
- // cached failure (which previously disabled BM25 for the whole
85
- // process for that repo).
86
- const msg = String(e?.message ?? '');
87
- if (msg.includes('already exists')) {
88
- ensuredPoolFTS.add(key);
89
- }
90
- else {
91
- console.warn(`[gitnexus] FTS index ensure failed for repo "${repoId}" table "${table}" ` +
92
- `(index "${indexName}"): ${msg || e}. Will retry on next query.`);
93
- }
94
- }
95
- }
7
+ import { queryFTS } from '../lbug/lbug-adapter.js';
8
+ import { FTS_INDEXES } from './fts-schema.js';
96
9
  /**
97
10
  * Execute a single FTS query via a custom executor (for MCP connection pool).
98
11
  * Returns the same shape as core queryFTS (from LadybugDB adapter).
@@ -134,41 +47,23 @@ async function queryFTSViaExecutor(executor, tableName, indexName, query, limit)
134
47
  * @returns Ranked search results from FTS indexes
135
48
  */
136
49
  export const searchFTSFromLbug = async (query, limit = 20, repoId) => {
137
- let fileResults, functionResults, classResults, methodResults, interfaceResults;
50
+ const resultsByIndex = [];
138
51
  if (repoId) {
139
52
  // Use MCP connection pool via dynamic import
140
53
  // IMPORTANT: FTS queries run sequentially to avoid connection contention.
141
54
  // The MCP pool supports multiple connections, but FTS is best run serially.
142
55
  const poolMod = await import('../lbug/pool-adapter.js');
143
- const { executeQuery, addPoolCloseListener } = poolMod;
144
- // Register the pool-close listener lazily on first use so a teardown of
145
- // the pool entry (LRU eviction, idle timeout, explicit close) drops the
146
- // matching `ensuredPoolFTS` entries. Without this, stale ensure-state
147
- // can outlive the pool that produced it.
148
- registerPoolCloseListenerOnce(addPoolCloseListener);
56
+ const { executeQuery } = poolMod;
149
57
  const executor = (cypher) => executeQuery(repoId, cypher);
150
- // Lazy-create FTS indexes on first query for this repo (analyze no longer
151
- // creates them up-front, so we ensure them here). Cached per-process.
152
- for (const { table, indexName, properties } of FTS_INDEXES) {
153
- await ensureFTSIndexViaExecutor(executor, repoId, table, indexName, properties);
58
+ for (const { table, indexName } of FTS_INDEXES) {
59
+ resultsByIndex.push(await queryFTSViaExecutor(executor, table, indexName, query, limit));
154
60
  }
155
- fileResults = await queryFTSViaExecutor(executor, 'File', 'file_fts', query, limit);
156
- functionResults = await queryFTSViaExecutor(executor, 'Function', 'function_fts', query, limit);
157
- classResults = await queryFTSViaExecutor(executor, 'Class', 'class_fts', query, limit);
158
- methodResults = await queryFTSViaExecutor(executor, 'Method', 'method_fts', query, limit);
159
- interfaceResults = await queryFTSViaExecutor(executor, 'Interface', 'interface_fts', query, limit);
160
61
  }
161
62
  else {
162
63
  // Use core lbug adapter (CLI / pipeline context) — also sequential for safety.
163
- // Lazy-create FTS indexes on first query (analyze no longer does it).
164
- for (const { table, indexName, properties } of FTS_INDEXES) {
165
- await ensureFTSIndex(table, indexName, [...properties]).catch(() => { });
64
+ for (const { table, indexName } of FTS_INDEXES) {
65
+ resultsByIndex.push(await queryFTS(table, indexName, query, limit, false).catch(() => []));
166
66
  }
167
- fileResults = await queryFTS('File', 'file_fts', query, limit, false).catch(() => []);
168
- functionResults = await queryFTS('Function', 'function_fts', query, limit, false).catch(() => []);
169
- classResults = await queryFTS('Class', 'class_fts', query, limit, false).catch(() => []);
170
- methodResults = await queryFTS('Method', 'method_fts', query, limit, false).catch(() => []);
171
- interfaceResults = await queryFTS('Interface', 'interface_fts', query, limit, false).catch(() => []);
172
67
  }
173
68
  // Collect all node scores per filePath to track which nodes actually matched
174
69
  const fileNodeScores = new Map();
@@ -179,11 +74,8 @@ export const searchFTSFromLbug = async (query, limit = 20, repoId) => {
179
74
  fileNodeScores.get(r.filePath).push({ score: r.score, nodeId: r.nodeId });
180
75
  }
181
76
  };
182
- addResults(fileResults);
183
- addResults(functionResults);
184
- addResults(classResults);
185
- addResults(methodResults);
186
- addResults(interfaceResults);
77
+ for (const results of resultsByIndex)
78
+ addResults(results);
187
79
  // Sum the top-3 highest-scoring nodes per file and collect their nodeIds.
188
80
  // Summing all nodes naively inflates scores for files with many mediocre
189
81
  // matches (e.g. test files) over files with a single highly-relevant symbol.
@@ -0,0 +1 @@
1
+ export declare function createSearchFTSIndexes(): Promise<void>;
@@ -0,0 +1,7 @@
1
+ import { createFTSIndex } from '../lbug/lbug-adapter.js';
2
+ import { FTS_INDEXES } from './fts-schema.js';
3
+ export async function createSearchFTSIndexes() {
4
+ for (const { table, indexName, properties } of FTS_INDEXES) {
5
+ await createFTSIndex(table, indexName, [...properties]);
6
+ }
7
+ }
@@ -0,0 +1,6 @@
1
+ export interface FTSIndexDefinition {
2
+ readonly table: string;
3
+ readonly indexName: string;
4
+ readonly properties: readonly string[];
5
+ }
6
+ export declare const FTS_INDEXES: readonly FTSIndexDefinition[];
@@ -0,0 +1,7 @@
1
+ export const FTS_INDEXES = [
2
+ { table: 'File', indexName: 'file_fts', properties: ['name', 'content'] },
3
+ { table: 'Function', indexName: 'function_fts', properties: ['name', 'content'] },
4
+ { table: 'Class', indexName: 'class_fts', properties: ['name', 'content'] },
5
+ { table: 'Method', indexName: 'method_fts', properties: ['name', 'content'] },
6
+ { table: 'Interface', indexName: 'interface_fts', properties: ['name', 'content'] },
7
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.4-rc.8",
3
+ "version": "1.6.4-rc.9",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",