gitnexus 1.6.10-rc.46 → 1.6.10-rc.48

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 (28) hide show
  1. package/README.md +2 -0
  2. package/dist/_shared/scope-resolution/types.d.ts +14 -2
  3. package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
  4. package/dist/core/ingestion/class-extractors/configs/jvm.js +24 -0
  5. package/dist/core/ingestion/languages/java/captures.js +78 -1
  6. package/dist/core/ingestion/languages/java/query.js +8 -0
  7. package/dist/core/ingestion/languages/java/scope-resolver.js +6 -0
  8. package/dist/core/ingestion/languages/javascript/query.js +7 -0
  9. package/dist/core/ingestion/languages/kotlin/query.js +6 -0
  10. package/dist/core/ingestion/languages/typescript/query.js +17 -0
  11. package/dist/core/ingestion/scope-extractor.js +2 -0
  12. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +17 -0
  13. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +9 -0
  14. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +95 -1
  15. package/dist/core/ingestion/scope-resolution/pipeline/run.js +2 -0
  16. package/dist/core/ingestion/scope-resolution/resolution-outcome.d.ts +1 -1
  17. package/dist/core/ingestion/scope-resolution/scope/walkers.js +63 -44
  18. package/dist/core/ingestion/tree-sitter-queries.d.ts +1 -1
  19. package/dist/core/ingestion/tree-sitter-queries.js +4 -0
  20. package/dist/core/ingestion/type-extractors/jvm.js +17 -2
  21. package/dist/core/ingestion/utils/ast-helpers.d.ts +1 -0
  22. package/dist/core/ingestion/utils/ast-helpers.js +99 -1
  23. package/dist/core/lbug/lbug-config.d.ts +9 -2
  24. package/dist/core/lbug/lbug-config.js +57 -3
  25. package/dist/storage/parse-cache.js +1 -1
  26. package/dist/storage/repo-manager.d.ts +8 -2
  27. package/dist/storage/repo-manager.js +7 -1
  28. package/package.json +1 -1
package/README.md CHANGED
@@ -471,6 +471,8 @@ Configure the behavior with these environment variables:
471
471
  | `GITNEXUS_FTS_CJK_SEGMENTATION` | `none`, `bigram` | `none` | `bigram` inserts overlapping character-bigram boundaries into Chinese/Japanese Han-ideograph spans in `content`/`description` before FTS indexing, so LadybugDB's space-only tokenizer can see sub-phrase word boundaries. Scoped to CJK Unified Ideographs only — Japanese Hiragana/Katakana and Korean Hangul are not currently segmented. Unlike `GITNEXUS_FTS_STEMMER`, this rewrites stored text — enabling it on an already-indexed repo requires a full `gitnexus analyze --force`; neither `--repair-fts` nor a plain incremental `analyze` applies it to previously-indexed files. Set the same value wherever `analyze` and search-serving processes (CLI query, MCP server, web server) run. |
472
472
  | `GITNEXUS_COMMUNITY_ENGINE` | `graphology`, `icebug`, `auto` | `graphology` | Community-detection engine used during analyze. `graphology` uses the bundled default path. `icebug` and `auto` currently behave identically: both try the experimental Icebug CSR path and fall back to Graphology if the optional native module is unavailable or incompatible. |
473
473
  | `GITNEXUS_WAL_CHECKPOINT_THRESHOLD` | integer `>= -1` | `67108864` (64 MiB) | LadybugDB WAL auto-checkpoint threshold during analyze (bytes). Auto-checkpoint remains enabled; `-1` keeps Ladybug's stock ~16 MiB. Larger thresholds reduce checkpoint frequency but increase the WAL size at rotation time — choose a smaller value on disk-constrained environments. |
474
+ | `GITNEXUS_LBUG_BUFFER_POOL_SIZE` | integer `>= 0` (bytes) | min(2 GiB, 80% RAM) | LadybugDB buffer-pool ceiling for every GitNexus database (analyze, MCP server, serve, group bridges). Bounded so a long-lived `gitnexus mcp` process or a large incremental `analyze` cannot grow toward LadybugDB's native 80%-of-RAM default and OOM the host (#2557). `0` restores that native unbounded default; invalid values warn and fall back to the default. |
475
+ | `GITNEXUS_LBUG_MAX_DB_SIZE` | positive integer (bytes) | `17179869184` (16 GiB) | Upper bound for a single LadybugDB database file. This is an mmap/disk-address-space ceiling, not a memory limit — it does not constrain the buffer pool (use `GITNEXUS_LBUG_BUFFER_POOL_SIZE` for that). Raise it when indexing genuinely huge monorepos; invalid values silently fall back to the default. |
474
476
 
475
477
  ```bash
476
478
  # Offline/airgapped: never reach the network for extensions
@@ -27,8 +27,20 @@ import type { SymbolDefinition } from './symbol-definition.js';
27
27
  export type ScopeId = string;
28
28
  /** Stable symbol-definition identifier (graph nodeId). */
29
29
  export type DefId = string;
30
- /** Kinds of lexical scope a `Scope` node can represent. */
31
- export type ScopeKind = 'Module' | 'Namespace' | 'Class' | 'Function' | 'Block' | 'Expression';
30
+ /**
31
+ * Kinds of lexical scope a `Scope` node can represent.
32
+ *
33
+ * `Object` is a hoist boundary ONLY: an object/record literal body
34
+ * (TS/JS `{...}`, Kotlin anonymous `object {...}`). Members are
35
+ * reachable via property access, never as bare identifiers, so
36
+ * scope-chain walkers (`scope/walkers.ts`) must skip an `Object`
37
+ * scope's own bindings while still traversing past it to the parent
38
+ * (#2545/#2551) -- unlike `Block`, where a nested closure legitimately
39
+ * DOES see a sibling `let`/`const` from an enclosing `if`/`for`/`while`,
40
+ * a nested closure inside an object literal must NOT see a sibling
41
+ * property's name as a free identifier.
42
+ */
43
+ export type ScopeKind = 'Module' | 'Namespace' | 'Class' | 'Function' | 'Block' | 'Expression' | 'Object';
32
44
  /** Source-text range. 1-based `startLine`/`endLine`; 0-based `startCol`/`endCol`. */
33
45
  export interface Range {
34
46
  readonly startLine: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/types.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  // gitnexus/src/core/ingestion/class-extractors/configs/jvm.ts
2
2
  import { SupportedLanguages } from '../../../../_shared/index.js';
3
+ import { synthesizeJavaAnonymousClassName } from '../../utils/ast-helpers.js';
3
4
  // ---------------------------------------------------------------------------
4
5
  // Java
5
6
  // ---------------------------------------------------------------------------
@@ -10,6 +11,12 @@ export const javaClassConfig = {
10
11
  'interface_declaration',
11
12
  'enum_declaration',
12
13
  'record_declaration',
14
+ // Anonymous class bodies (`new Runnable() { ... }`) — the matching
15
+ // JAVA_QUERIES pattern only captures `object_creation_expression`
16
+ // WITH a `class_body`, and `extractName` below returns undefined for
17
+ // any other shape, so plain `new Foo()` constructor calls never
18
+ // produce a Class node (#2550).
19
+ 'object_creation_expression',
13
20
  ],
14
21
  fileScopeNodeTypes: ['package_declaration'],
15
22
  ancestorScopeNodeTypes: [
@@ -18,6 +25,23 @@ export const javaClassConfig = {
18
25
  'enum_declaration',
19
26
  'record_declaration',
20
27
  ],
28
+ extractName(node) {
29
+ if (node.type === 'object_creation_expression') {
30
+ return synthesizeJavaAnonymousClassName(node);
31
+ }
32
+ return undefined;
33
+ },
34
+ // An anonymous body whose name CANNOT be synthesized (no supported host
35
+ // type declaration) must not become a Class node at all. Without this
36
+ // skip, `extract()`'s `extractTypeNameFromNode` fallback names the node
37
+ // after the CONSTRUCTED type — emitting a phantom `Class:...:Runnable`
38
+ // for `new Runnable() { ... }` (empirically caught in review).
39
+ shouldSkipClassCapture({ definitionNode }) {
40
+ return (definitionNode !== null &&
41
+ definitionNode !== undefined &&
42
+ definitionNode.type === 'object_creation_expression' &&
43
+ synthesizeJavaAnonymousClassName(definitionNode) === undefined);
44
+ },
21
45
  };
22
46
  // ---------------------------------------------------------------------------
23
47
  // Kotlin
@@ -13,7 +13,7 @@
13
13
  *
14
14
  * Pure given the input source text. No I/O, no globals consulted.
15
15
  */
16
- import { nodeIfType, nodeToCapture, syntheticCapture } from '../../utils/ast-helpers.js';
16
+ import { nodeIfType, nodeToCapture, synthesizeJavaAnonymousClassName, syntheticCapture, } from '../../utils/ast-helpers.js';
17
17
  import { splitImportDeclaration } from './import-decomposer.js';
18
18
  import { computeJavaArityMetadata } from './arity-metadata.js';
19
19
  import { synthesizeJavaReceiverBinding } from './receiver-binding.js';
@@ -184,9 +184,79 @@ export function emitJavaScopeCaptures(sourceText, _filePath, cachedTree) {
184
184
  ...resolveVarTypeBindings(out),
185
185
  ...synthesizeJavaInheritanceReferences(tree.rootNode),
186
186
  ...synthesizeJavaExplicitConstructorReferences(tree.rootNode),
187
+ ...synthesizeJavaAnonymousClassDeclarations(tree.rootNode),
187
188
  ...synthesizeCallableFlowCaptures(tree.rootNode, JAVA_CALLABLE_CAPTURE_OPTIONS),
188
189
  ];
189
190
  }
191
+ /**
192
+ * Synthesize `@declaration.class` matches for anonymous class bodies
193
+ * (`new Runnable() { ... }`), named by the same javac-style authority
194
+ * (`synthesizeJavaAnonymousClassName` → `Worker$N`) the structure phase
195
+ * uses — the two layers agree by construction (#2550).
196
+ *
197
+ * The anchor is the `class_body` node: it shares its range with the
198
+ * `(object_creation_expression (class_body) @scope.class)` scope rule in
199
+ * query.ts, so the def is owned by that Class scope's `ownedDefs`
200
+ * (making `populateClassOwnedMembers` stamp `ownerId` on the anonymous
201
+ * class's methods) and the name auto-hoists to the enclosing scope —
202
+ * exactly the binding shape a named class declaration produces.
203
+ */
204
+ function synthesizeJavaAnonymousClassDeclarations(rootNode) {
205
+ const out = [];
206
+ for (const oce of rootNode.descendantsOfType('object_creation_expression')) {
207
+ const name = synthesizeJavaAnonymousClassName(oce);
208
+ if (name === undefined)
209
+ continue;
210
+ const body = oce.namedChildren.find((c) => c.type === 'class_body');
211
+ if (body === undefined)
212
+ continue;
213
+ out.push({
214
+ '@declaration.class': nodeToCapture('@declaration.class', body),
215
+ '@declaration.name': syntheticCapture('@declaration.name', body, name),
216
+ });
217
+ // Inheritance: the anonymous class extends/implements its constructed
218
+ // type. Anchor the `@reference.inherits` on the `class_body` — its
219
+ // range equals the anonymous Class scope, so the reference site's
220
+ // enclosing class resolves to the SYNTHESIZED `Worker$N` def (anchoring
221
+ // on the constructed-type node would sit OUTSIDE the anonymous scope
222
+ // and mis-attribute the edge to the lexically enclosing class). The
223
+ // synthetic `@reference.name` carries the base's simple name; a JDK
224
+ // type with no repo def simply resolves to nothing (no edge). Without
225
+ // this edge `mroFor(Worker$N)` is empty and the #2550 instance-
226
+ // ownership gate suppressed TRUE bare calls to inherited methods
227
+ // inside the anonymous body (empirically caught in review).
228
+ const constructedType = oce.childForFieldName?.('type');
229
+ const baseSimpleName = constructedType !== null && constructedType !== undefined
230
+ ? javaBaseSimpleNameOf(constructedType)
231
+ : undefined;
232
+ if (baseSimpleName !== undefined) {
233
+ out.push({
234
+ '@reference.inherits': nodeToCapture('@reference.inherits', body),
235
+ '@reference.name': syntheticCapture('@reference.name', body, baseSimpleName),
236
+ });
237
+ }
238
+ // Receiver typeBinding: `Runnable handler = new Runnable() { ... }`
239
+ // binds `handler` to the ANONYMOUS class (`Worker$1`), not the declared
240
+ // interface — the instance is what `handler.run()` dispatches into, and
241
+ // the declared type is frequently a JDK interface with no repo def.
242
+ // Appended after the raw matches, so it overwrites the declared-type
243
+ // binding the `@type-binding.annotation` query rule produced for the
244
+ // same variable (pass-4 applies bindings in match order; last wins).
245
+ const declarator = oce.parent;
246
+ if (declarator !== null && declarator.type === 'variable_declarator') {
247
+ const varName = declarator.childForFieldName?.('name');
248
+ const declNode = declarator.parent ?? declarator;
249
+ if (varName !== null && varName !== undefined) {
250
+ out.push({
251
+ '@type-binding.annotation': nodeToCapture('@type-binding.annotation', declNode),
252
+ '@type-binding.name': nodeToCapture('@type-binding.name', varName),
253
+ '@type-binding.type': syntheticCapture('@type-binding.type', oce, name),
254
+ });
255
+ }
256
+ }
257
+ }
258
+ return out;
259
+ }
190
260
  /**
191
261
  * Synthesize `@reference.call.constructor` captures for explicit constructor
192
262
  * invocations — `super(...)` and `this(...)` (F38 #1928). tree-sitter-java
@@ -377,6 +447,13 @@ function emitJavaInheritanceBase(out, base) {
377
447
  });
378
448
  }
379
449
  /** Resolve a Java base-type node to its bare simple-name identifier node. */
450
+ /** Simple name of a constructed/base type node, reusing the same node
451
+ * shapes `javaBaseLookupNameNode` handles (`Runnable`, `a.b.Base`,
452
+ * `Box<T>`). Returns undefined when the node is none of those. */
453
+ function javaBaseSimpleNameOf(typeNode) {
454
+ const nameNode = javaBaseLookupNameNode(typeNode);
455
+ return nameNode === null ? undefined : nameNode.text;
456
+ }
380
457
  function javaBaseLookupNameNode(node) {
381
458
  switch (node.type) {
382
459
  case 'type_identifier':
@@ -37,6 +37,14 @@ const JAVA_SCOPE_QUERY = `
37
37
  (record_declaration) @scope.class
38
38
  (annotation_type_declaration) @scope.class
39
39
 
40
+ ;; Anonymous class body: \`new Runnable() { public void run() {} }\`.
41
+ ;; Without its own scope, a method's auto-hoist (scope-extractor.ts) has
42
+ ;; nowhere to stop and leaks the name past the anonymous class into the
43
+ ;; enclosing scope -- the same failure mode fixed for TS/JS object
44
+ ;; literals (#2545).
45
+ (object_creation_expression
46
+ (class_body) @scope.class)
47
+
40
48
  (method_declaration) @scope.function
41
49
  (constructor_declaration) @scope.function
42
50
 
@@ -31,6 +31,12 @@ const javaScopeResolver = {
31
31
  collapseMemberCallsByCallerTarget: true,
32
32
  hoistTypeBindingsToModule: true,
33
33
  stripReceiverCastExpressions: true,
34
+ // #2550: every Java method belongs to a class instance — a free call may
35
+ // resolve to a Method only when the caller's enclosing class chain
36
+ // (self + MRO) contains the method's owner. Closes the finalize-bucket
37
+ // leak (unqualified `run()` matching an unrelated same-file anonymous
38
+ // class's method). C# is the intended next adopter.
39
+ freeCallsRequireInstanceOwnership: true,
34
40
  populateNamespaceSiblings: populateJavaPackageSiblings,
35
41
  populateRangeBindings: populateJavaCrossFileReturnTypes,
36
42
  };
@@ -66,6 +66,13 @@ const JAVASCRIPT_SCOPE_QUERY = `
66
66
  (arrow_function) @scope.function
67
67
  (method_definition) @scope.function
68
68
 
69
+ ;; Object literals get their own scope boundary -- see the matching
70
+ ;; comment in typescript/query.ts (#2545/#2551). Prevents a
71
+ ;; method_definition/property-arrow's auto-hoist from leaking its name
72
+ ;; past the literal into the enclosing scope, and (unlike Block) keeps
73
+ ;; sibling properties from seeing each other as bare identifiers.
74
+ (object) @scope.object
75
+
69
76
  ;; Declarations — classes
70
77
  (class_declaration
71
78
  name: (identifier) @declaration.name) @declaration.class
@@ -15,6 +15,12 @@ const KOTLIN_SCOPE_QUERY = `
15
15
  (class_declaration) @scope.class
16
16
  (object_declaration) @scope.class
17
17
  (companion_object) @scope.class
18
+ ;; Anonymous object expression: \`val h = object { fun fetch() {} }\`
19
+ ;; (distinct from the named \`object_declaration\`/\`companion_object\` above).
20
+ ;; Without its own scope, a method's auto-hoist (scope-extractor.ts) has
21
+ ;; nowhere to stop and leaks the name past the literal into the enclosing
22
+ ;; scope -- the same failure mode fixed for TS/JS object literals (#2545).
23
+ (object_literal) @scope.class
18
24
  (function_declaration) @scope.function
19
25
 
20
26
  ;; Secondary-constructor body scope (issue #1919 review CF1). A
@@ -96,6 +96,23 @@ const TYPESCRIPT_SCOPE_QUERY = `
96
96
  (arrow_function) @scope.function
97
97
  (function_expression) @scope.function
98
98
 
99
+ ;; Object literals (the { ... } value expression, NOT object_type or
100
+ ;; object_pattern) get their own scope boundary. Without it, a
101
+ ;; method_definition/property-arrow's auto-hoist (scope-extractor.ts)
102
+ ;; has nowhere to stop and leaks the name past the literal into whatever
103
+ ;; lexically encloses it -- e.g. 'export default { async fetch(req) {} }'
104
+ ;; would bind fetch at Module scope, letting an unrelated same-file
105
+ ;; fetch(...) call (the platform global) incorrectly resolve to it
106
+ ;; (#2545). Object (not Block or Class): object-literal members are
107
+ ;; reachable only via property access, never as bare identifiers -- not
108
+ ;; even by a SIBLING property's function body, unlike a real Block
109
+ ;; (if/for/while, where a nested closure legitimately sees a sibling
110
+ ;; let/const) or a Class (implicit-this sibling dispatch). Scope-chain
111
+ ;; walkers (scope-resolution/scope/walkers.ts) skip an Object scope's
112
+ ;; own bindings entirely while still treating it as a hoist boundary
113
+ ;; (#2551).
114
+ (object) @scope.object
115
+
99
116
  ;; Type aliases that contain an object_type are structurally class-like —
100
117
  ;; they define a shape with named members. Emit @scope.class so the
101
118
  ;; field-extractor's type-alias-with-object-type handling (in
@@ -308,6 +308,8 @@ function resolveKindForScopeMatch(match, anchor, provider) {
308
308
  return 'Block';
309
309
  case 'expression':
310
310
  return 'Expression';
311
+ case 'object':
312
+ return 'Object';
311
313
  default:
312
314
  return null;
313
315
  }
@@ -626,6 +626,23 @@ export interface ScopeResolver {
626
626
  * but is too loose as a default for strict module systems.
627
627
  */
628
628
  readonly allowGlobalFreeCallFallback?: boolean;
629
+ /**
630
+ * In this language every `Method` belongs to a class instance, so a
631
+ * FREE (receiver-less) call may resolve to a `Method` only when the
632
+ * caller's enclosing class chain — the class itself plus its MRO —
633
+ * contains the method's owner (#2550). Suppresses the finalize-bucket
634
+ * leak where an unqualified call matched any same-file method by bare
635
+ * name (`materializeBindings` flattens every declaration onto module
636
+ * scope). Java opts in; C# is the intended next adopter.
637
+ *
638
+ * NOT implemented via `LanguageProvider.builtInNames`: that mechanism
639
+ * has unrelated consumers (`parse-worker`'s call-site extraction gate
640
+ * suppresses member calls too; `type-env`'s return-type lookup) which
641
+ * assume a flagged name is never a real repository declaration —
642
+ * false for common method names like `run`/`get`/`compare` (verified
643
+ * regression).
644
+ */
645
+ readonly freeCallsRequireInstanceOwnership?: boolean;
629
646
  /**
630
647
  * When true, a constructor-form call `Type(...)` links to the Class def
631
648
  * itself rather than its explicit Constructor def. Default
@@ -57,6 +57,15 @@ export declare function emitFreeCallFallback(graph: KnowledgeGraph, scopes: Scop
57
57
  * fail at the call site. Three-valued; `'unknown'` keeps the
58
58
  * candidate (monotonicity). */
59
59
  readonly constraintCompatibility?: ScopeResolver['constraintCompatibility'];
60
+ /** Platform/language built-in names (e.g. `fetch`, `setTimeout`) that
61
+ * are never real repository declarations. Gates the finalize-bucket
62
+ * guard below (#2545) -- see `hasGenuineLexicalBinding`. */
63
+ readonly isBuiltInName?: (name: string) => boolean;
64
+ /** Instance-ownership gate (#2550): a free call may resolve to a
65
+ * `Method` only when the caller's enclosing class chain (self + MRO)
66
+ * contains the method's owner. See
67
+ * `ScopeResolver.freeCallsRequireInstanceOwnership`. */
68
+ readonly freeCallsRequireInstanceOwnership?: boolean;
60
69
  readonly recordResolutionOutcome?: ResolutionOutcomeRecorder;
61
70
  /** Call sites owned by a later precise pass (for example callable-value-flow). */
62
71
  readonly skipSites?: ReadonlySet<string>;
@@ -17,7 +17,7 @@
17
17
  * generalization plan.
18
18
  */
19
19
  import { resolveCallerGraphId, resolveDefGraphId } from '../graph-bridge/ids.js';
20
- import { findAllCallableBindingsInScope, findCallableBindingInScope, findCallableBindingsAndAdlBlocker, resolveInheritanceBaseInScope, } from '../scope/walkers.js';
20
+ import { findAllCallableBindingsInScope, findCallableBindingInScope, findCallableBindingsAndAdlBlocker, findEnclosingClassDef, resolveInheritanceBaseInScope, } from '../scope/walkers.js';
21
21
  import { isOverloadAmbiguousAfterNormalization, narrowOverloadCandidates, } from './overload-narrowing.js';
22
22
  export function emitFreeCallFallback(graph, scopes, parsedFiles, nodeLookup, _referenceIndex, handledSites, model, workspaceIndex, options = {}) {
23
23
  let emitted = 0;
@@ -109,6 +109,72 @@ export function emitFreeCallFallback(graph, scopes, parsedFiles, nodeLookup, _re
109
109
  // available AND the binding scope contains multiple overloads,
110
110
  // refine with narrowOverloadCandidates (#1578).
111
111
  fnDef = findCallableBindingInScope(site.inScope, site.name, scopes);
112
+ if (fnDef !== undefined &&
113
+ options.isBuiltInName?.(site.name) === true &&
114
+ fnDef.filePath === parsed.filePath &&
115
+ !hasGenuineLexicalBinding(site.inScope, site.name, scopes)) {
116
+ // A platform/language built-in (e.g. `fetch`, `setTimeout`)
117
+ // with no binding reachable via the TRUE lexical scope chain
118
+ // (Scope.bindings only) -- the match came solely from
119
+ // finalize's per-file "local + imports + wildcards" bucket,
120
+ // which flattens every declaration in the file onto the
121
+ // module scope regardless of true nesting depth
122
+ // (gitnexus-shared's `materializeBindings`). That flattening
123
+ // is correct for its purpose (cross-file import targets) but
124
+ // over-matches same-file built-in-shadowing declarations that
125
+ // were never really module-scope-visible -- e.g. a Cloudflare
126
+ // Worker's `export default { fetch(req) {} }` handler (#2545).
127
+ // Leave the call unresolved rather than emit a false edge.
128
+ //
129
+ // `fnDef.filePath === parsed.filePath` is the load-bearing
130
+ // guard against a real regression: `materializeBindings`'s
131
+ // flat bucket is per-file, so the leak this guard targets is
132
+ // ALWAYS same-file. A cross-file match at this point can only
133
+ // come from a genuine import/namespace/workspace-FQN channel
134
+ // (the separate, gated `pickUniqueGlobalCallable` global
135
+ // fallback runs later and isn't what populated `fnDef` here)
136
+ // -- e.g. `import { fetch } from './fetch-polyfill'` must
137
+ // keep resolving. Without this check, that import silently
138
+ // stopped resolving (verified via a scratch probe fixture).
139
+ fnDef = undefined;
140
+ }
141
+ // Instance-ownership gate (#2550). Placement matters: after the
142
+ // scope-chain lookup, BEFORE overload narrowing -- a suppressed
143
+ // candidate must not participate in overload selection. The
144
+ // legitimate same-class bare call already resolved earlier via
145
+ // `pickImplicitThisOverload`; an inherited bare call passes the
146
+ // MRO arm here; what remains is the finalize-bucket leak (an
147
+ // unrelated same-file method matched by bare name).
148
+ //
149
+ // Same-file only (mirrors the #2545 guard's load-bearing
150
+ // condition): the `materializeBindings` bucket is per-file, so
151
+ // the leak is ALWAYS same-file. A cross-file Method match here
152
+ // came through a genuine import channel -- e.g. the arity-
153
+ // narrowing parity fixtures resolve a bare `writeAudit(u)` to
154
+ // an imported class's method, which must keep working
155
+ // (suppressing it broke `java.test.ts`'s arity-filtering suite,
156
+ // verified empirically).
157
+ if (fnDef !== undefined &&
158
+ options.freeCallsRequireInstanceOwnership === true &&
159
+ fnDef.type === 'Method' &&
160
+ fnDef.ownerId !== undefined &&
161
+ fnDef.filePath === parsed.filePath) {
162
+ const enclosing = findEnclosingClassDef(site.inScope, scopes);
163
+ const ownerReachable = enclosing !== undefined &&
164
+ (enclosing.nodeId === fnDef.ownerId ||
165
+ scopes.methodDispatch.mroFor(enclosing.nodeId).includes(fnDef.ownerId));
166
+ if (!ownerReachable) {
167
+ recordSuppressedOutcome(options.recordResolutionOutcome, {
168
+ phase: 'free-call-fallback',
169
+ filePath: parsed.filePath,
170
+ name: site.name,
171
+ range: site.atRange,
172
+ reason: 'free-call-instance-ownership',
173
+ candidates: [fnDef],
174
+ });
175
+ fnDef = undefined;
176
+ }
177
+ }
112
178
  if (fnDef !== undefined && options.conversionRankFn !== undefined) {
113
179
  const allCallables = findAllCallableBindingsInScope(site.inScope, site.name, scopes);
114
180
  if (allCallables.length > 1) {
@@ -709,3 +775,31 @@ export function pickImplicitThisOverload(site, scopes, workspaceIndex, model, ho
709
775
  return undefined;
710
776
  return candidates[0];
711
777
  }
778
+ /**
779
+ * True when `name` is bound somewhere along the TRUE lexical scope
780
+ * chain from `startScope` -- i.e. via `Scope.bindings` (the raw,
781
+ * nesting-aware per-scope map built during extraction), NOT via
782
+ * finalize's `indexes.bindings` module-scope bucket (which flattens
783
+ * every declaration in the file onto the module scope regardless of
784
+ * true nesting -- see `hasGenuineLexicalBinding`'s caller for why that
785
+ * distinction matters, #2545).
786
+ */
787
+ function hasGenuineLexicalBinding(startScope, name, scopes) {
788
+ let currentId = startScope;
789
+ const visited = new Set();
790
+ while (currentId !== null) {
791
+ if (visited.has(currentId))
792
+ return false;
793
+ visited.add(currentId);
794
+ const scope = scopes.scopeTree.getScope(currentId);
795
+ if (scope === undefined)
796
+ return false;
797
+ // `Object` scopes (object/record literal bodies) are a hoist
798
+ // boundary only -- never a genuine lexical binding, not even to
799
+ // their own nested children (#2551, mirrors scope/walkers.ts).
800
+ if (scope.kind !== 'Object' && scope.bindings.get(name) !== undefined)
801
+ return true;
802
+ currentId = scope.parent;
803
+ }
804
+ return false;
805
+ }
@@ -508,6 +508,8 @@ export function runScopeResolution(input, provider) {
508
508
  allowGlobalFallback: provider.allowGlobalFreeCallFallback === true,
509
509
  constructorCallTargetsClass: provider.constructorCallTargetsClass === true,
510
510
  isFileLocalDef: provider.isFileLocalDef,
511
+ isBuiltInName: provider.languageProvider.isBuiltInName,
512
+ freeCallsRequireInstanceOwnership: provider.freeCallsRequireInstanceOwnership === true,
511
513
  isCallableVisibleFromCaller: provider.isCallableVisibleFromCaller,
512
514
  resolveAdlCandidates: provider.resolveAdlCandidates,
513
515
  conversionRankFn: provider.conversionRankFn,
@@ -1,5 +1,5 @@
1
1
  import type { Range } from '../../../_shared/index.js';
2
- export type ResolutionSuppressionReason = 'adl-ordinary-lookup-blocked' | 'conversion-rank-tied' | 'inline-ns-ambiguous' | 'member-lookup-ambiguous' | 'selected-callable-deleted' | 'overload-ambiguous' | 'overload-ambiguous-normalization';
2
+ export type ResolutionSuppressionReason = 'adl-ordinary-lookup-blocked' | 'conversion-rank-tied' | 'inline-ns-ambiguous' | 'member-lookup-ambiguous' | 'selected-callable-deleted' | 'overload-ambiguous' | 'overload-ambiguous-normalization' | 'free-call-instance-ownership';
3
3
  export type ResolutionOutcome = {
4
4
  readonly kind: 'resolved';
5
5
  readonly targetId: string;
@@ -577,20 +577,26 @@ function walkScopeChain(startScope, name, scopes, predicate) {
577
577
  const scope = scopes.scopeTree.getScope(currentId);
578
578
  if (scope === undefined)
579
579
  return undefined;
580
- // Local first: a `const x` in this scope shadows any imported `x`.
581
- const localBindings = scope.bindings.get(name);
582
- if (localBindings !== undefined) {
583
- for (const b of localBindings) {
580
+ // `Object` scopes (object/record literal bodies) are a hoist
581
+ // boundary only -- their members are reachable via property access,
582
+ // never bare identifiers, so they contribute nothing to lookup
583
+ // (#2545/#2551). Still traverse past to the parent.
584
+ if (scope.kind !== 'Object') {
585
+ // Local first: a `const x` in this scope shadows any imported `x`.
586
+ const localBindings = scope.bindings.get(name);
587
+ if (localBindings !== undefined) {
588
+ for (const b of localBindings) {
589
+ if (predicate(b.def))
590
+ return b.def;
591
+ }
592
+ }
593
+ // Then imported/augmented bindings — only consulted when no local match.
594
+ const importedBindings = lookupBindingsAt(currentId, name, scopes);
595
+ for (const b of importedBindings) {
584
596
  if (predicate(b.def))
585
597
  return b.def;
586
598
  }
587
599
  }
588
- // Then imported/augmented bindings — only consulted when no local match.
589
- const importedBindings = lookupBindingsAt(currentId, name, scopes);
590
- for (const b of importedBindings) {
591
- if (predicate(b.def))
592
- return b.def;
593
- }
594
600
  currentId = scope.parent;
595
601
  }
596
602
  return undefined;
@@ -626,28 +632,32 @@ export function findAllCallableBindingsInScope(startScope, callableName, scopes)
626
632
  const scope = scopes.scopeTree.getScope(currentId);
627
633
  if (scope === undefined)
628
634
  return [];
629
- const out = [];
630
- const seen = new Set();
631
- const pushCallable = (def) => {
632
- if (def.type !== 'Function' && def.type !== 'Method' && def.type !== 'Constructor')
633
- return;
634
- if (seen.has(def.nodeId))
635
- return;
636
- seen.add(def.nodeId);
637
- out.push(def);
638
- };
639
- const localBindings = scope.bindings.get(callableName);
640
- if (localBindings !== undefined) {
641
- for (const b of localBindings) {
635
+ // `Object` scopes are a hoist boundary only -- see walkScopeChain's
636
+ // comment (#2545/#2551). Skip lookup here, still traverse to parent.
637
+ if (scope.kind !== 'Object') {
638
+ const out = [];
639
+ const seen = new Set();
640
+ const pushCallable = (def) => {
641
+ if (def.type !== 'Function' && def.type !== 'Method' && def.type !== 'Constructor')
642
+ return;
643
+ if (seen.has(def.nodeId))
644
+ return;
645
+ seen.add(def.nodeId);
646
+ out.push(def);
647
+ };
648
+ const localBindings = scope.bindings.get(callableName);
649
+ if (localBindings !== undefined) {
650
+ for (const b of localBindings) {
651
+ pushCallable(b.def);
652
+ }
653
+ }
654
+ const importedBindings = lookupBindingsAt(currentId, callableName, scopes);
655
+ for (const b of importedBindings) {
642
656
  pushCallable(b.def);
643
657
  }
658
+ if (out.length > 0)
659
+ return out;
644
660
  }
645
- const importedBindings = lookupBindingsAt(currentId, callableName, scopes);
646
- for (const b of importedBindings) {
647
- pushCallable(b.def);
648
- }
649
- if (out.length > 0)
650
- return out;
651
661
  currentId = scope.parent;
652
662
  }
653
663
  return [];
@@ -694,16 +704,22 @@ export function findCallableBindingsAndAdlBlocker(startScope, name, scopes) {
694
704
  nonCallableFound = true;
695
705
  }
696
706
  };
697
- const localBindings = scope.bindings.get(name);
698
- if (localBindings !== undefined) {
699
- for (const b of localBindings) {
707
+ // `Object` scopes are a hoist boundary only (#2545/#2551) -- never
708
+ // reached by C++'s ADL path in practice (no language reusing this
709
+ // function emits `@scope.object`), guarded for consistency with the
710
+ // other scope-chain walkers in this file.
711
+ if (scope.kind !== 'Object') {
712
+ const localBindings = scope.bindings.get(name);
713
+ if (localBindings !== undefined) {
714
+ for (const b of localBindings) {
715
+ process(b.def);
716
+ }
717
+ }
718
+ const importedBindings = lookupBindingsAt(currentId, name, scopes);
719
+ for (const b of importedBindings) {
700
720
  process(b.def);
701
721
  }
702
722
  }
703
- const importedBindings = lookupBindingsAt(currentId, name, scopes);
704
- for (const b of importedBindings) {
705
- process(b.def);
706
- }
707
723
  if (anyBinding) {
708
724
  // ISO C++: a block-scope function declaration (Function or Block scope)
709
725
  // that is NOT a using-declaration blocks ADL. If we found callables at
@@ -930,18 +946,21 @@ export function findExportedDefByName(name, inScope, scopes, index) {
930
946
  const scope = scopes.scopeTree.getScope(currentId);
931
947
  if (scope === undefined)
932
948
  break;
933
- const local = scope.bindings.get(name);
934
- if (local !== undefined) {
935
- for (const b of local) {
949
+ // `Object` scopes are a hoist boundary only (#2545/#2551).
950
+ if (scope.kind !== 'Object') {
951
+ const local = scope.bindings.get(name);
952
+ if (local !== undefined) {
953
+ for (const b of local) {
954
+ if (b.def.type === 'Function' || b.def.type === 'Method')
955
+ return b.def;
956
+ }
957
+ }
958
+ const finalized = lookupBindingsAt(currentId, name, scopes);
959
+ for (const b of finalized) {
936
960
  if (b.def.type === 'Function' || b.def.type === 'Method')
937
961
  return b.def;
938
962
  }
939
963
  }
940
- const finalized = lookupBindingsAt(currentId, name, scopes);
941
- for (const b of finalized) {
942
- if (b.def.type === 'Function' || b.def.type === 'Method')
943
- return b.def;
944
- }
945
964
  currentId = scope.parent;
946
965
  }
947
966
  // Workspace-wide fallback: the first locally-declared callable binding
@@ -1,7 +1,7 @@
1
1
  export declare const TYPESCRIPT_QUERIES: string;
2
2
  export declare const JAVASCRIPT_QUERIES: string;
3
3
  export declare const PYTHON_QUERIES = "\n(class_definition\n name: (identifier) @name) @definition.class\n\n(function_definition\n name: (identifier) @name) @definition.function\n\n(import_statement\n name: (dotted_name) @import.source) @import\n\n; import numpy as np \u2192 aliased_import captures the module name so the\n; import path is resolved and named-binding extraction stores \"np\" \u2192 \"numpy\".\n(import_statement\n name: (aliased_import\n name: (dotted_name) @import.source)) @import\n\n(import_from_statement\n module_name: (dotted_name) @import.source) @import\n\n(import_from_statement\n module_name: (relative_import) @import.source) @import\n\n(call\n function: (identifier) @call.name) @call\n\n(call\n function: (attribute\n attribute: (identifier) @call.name)) @call\n\n; Class attribute type annotations \u2014 PEP 526: address: Address or address: Address = Address()\n; Both bare annotations (address: Address) and annotated assignments (name: str = \"test\")\n; are parsed as (assignment left: ... type: ...) in tree-sitter-python.\n(expression_statement\n (assignment\n left: (identifier) @name\n type: (type)) @definition.property)\n\n; Plain variable assignments without type annotation: x = 5, MAX_SIZE = 100\n; Overlap with @definition.property (typed) is handled by parse-worker dedup.\n(expression_statement\n (assignment\n left: (identifier) @name)) @definition.variable\n\n; Write access: obj.field = value\n(assignment\n left: (attribute\n object: (_) @assignment.receiver\n attribute: (identifier) @assignment.property)\n right: (_)) @assignment\n\n; Write access: obj.field += value (compound assignment)\n(augmented_assignment\n left: (attribute\n object: (_) @assignment.receiver\n attribute: (identifier) @assignment.property)\n right: (_)) @assignment\n\n; Python HTTP clients: requests.get('/path'), httpx.post('/path'), session.get('/path')\n(call\n function: (attribute\n attribute: (identifier) @http_client.method)\n arguments: (argument_list\n (string (string_content) @http_client.url))) @http_client\n\n; Python decorators: @app.route, @router.get, etc.\n; The first positional argument is captured three ways (#2391): a string literal\n; path via @decorator.arg (quote-free, the fast path); a bare constant name or a\n; plus-concatenation via @decorator.arg_expr (resolved cross-file by the constant\n; resolver). The anchored optional alternation pins to the FIRST arg and stays\n; optional, so no-arg decorators (@app.tool(), etc.) and non-path first args still\n; match.\n(decorator\n (call\n function: (attribute\n object: (identifier) @decorator.receiver\n attribute: (identifier) @decorator.name)\n arguments: (argument_list\n .\n [\n (string (string_content)? @decorator.arg) @decorator.arg_str\n (identifier) @decorator.arg_expr\n (binary_operator) @decorator.arg_expr\n ]?))) @decorator\n";
4
- export declare const JAVA_QUERIES = "\n; Classes, Interfaces, Enums, Annotations\n(class_declaration name: (identifier) @name) @definition.class\n(interface_declaration name: (identifier) @name) @definition.interface\n(enum_declaration name: (identifier) @name) @definition.enum\n(annotation_type_declaration name: (identifier) @name) @definition.annotation\n\n; Methods & Constructors\n(method_declaration name: (identifier) @name) @definition.method\n(constructor_declaration name: (identifier) @name) @definition.constructor\n\n; Fields \u2014 typed field declarations inside class bodies\n(field_declaration\n declarator: (variable_declarator\n name: (identifier) @name)) @definition.property\n\n; Imports - capture any import declaration child as source\n(import_declaration (_) @import.source) @import\n\n; Calls\n(method_invocation name: (identifier) @call.name) @call\n(method_invocation object: (_) name: (identifier) @call.name) @call\n(method_reference) @call\n\n; Constructor calls: new Foo()\n(object_creation_expression type: (type_identifier) @call.name) @call\n\n; Local variable declarations inside method bodies\n(local_variable_declaration\n declarator: (variable_declarator\n name: (identifier) @name)) @definition.variable\n\n; Write access: obj.field = value\n(assignment_expression\n left: (field_access\n object: (_) @assignment.receiver\n field: (identifier) @assignment.property)\n right: (_)) @assignment\n";
4
+ export declare const JAVA_QUERIES = "\n; Classes, Interfaces, Enums, Annotations\n(class_declaration name: (identifier) @name) @definition.class\n(interface_declaration name: (identifier) @name) @definition.interface\n(enum_declaration name: (identifier) @name) @definition.enum\n(annotation_type_declaration name: (identifier) @name) @definition.annotation\n\n; Anonymous class bodies: new Runnable() { ... } \u2014 no @name capture; the\n; class extractor synthesizes the javac-style Worker$N name (#2550)\n(object_creation_expression (class_body)) @definition.class\n\n; Methods & Constructors\n(method_declaration name: (identifier) @name) @definition.method\n(constructor_declaration name: (identifier) @name) @definition.constructor\n\n; Fields \u2014 typed field declarations inside class bodies\n(field_declaration\n declarator: (variable_declarator\n name: (identifier) @name)) @definition.property\n\n; Imports - capture any import declaration child as source\n(import_declaration (_) @import.source) @import\n\n; Calls\n(method_invocation name: (identifier) @call.name) @call\n(method_invocation object: (_) name: (identifier) @call.name) @call\n(method_reference) @call\n\n; Constructor calls: new Foo()\n(object_creation_expression type: (type_identifier) @call.name) @call\n\n; Local variable declarations inside method bodies\n(local_variable_declaration\n declarator: (variable_declarator\n name: (identifier) @name)) @definition.variable\n\n; Write access: obj.field = value\n(assignment_expression\n left: (field_access\n object: (_) @assignment.receiver\n field: (identifier) @assignment.property)\n right: (_)) @assignment\n";
5
5
  export declare const C_QUERIES = "\n; Functions (direct declarator)\n(function_definition declarator: (function_declarator declarator: (identifier) @name)) @definition.function\n(declaration declarator: (function_declarator declarator: (identifier) @name)) @definition.function\n\n; Functions returning pointers (pointer_declarator wraps function_declarator)\n(function_definition declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @name))) @definition.function\n(declaration declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @name))) @definition.function\n\n; Functions returning double pointers (nested pointer_declarator)\n(function_definition declarator: (pointer_declarator declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @name)))) @definition.function\n\n; Structs, Unions, Enums, Typedefs\n(struct_specifier name: (type_identifier) @name) @definition.struct\n(type_definition\n type: (struct_specifier\n body: (field_declaration_list))\n declarator: (type_identifier) @name) @definition.struct\n(union_specifier name: (type_identifier) @name) @definition.union\n(enum_specifier name: (type_identifier) @name) @definition.enum\n(type_definition\n type: (enum_specifier\n body: (enumerator_list))\n declarator: (type_identifier) @name) @definition.enum\n(enumerator name: (identifier) @name) @definition.const\n(type_definition declarator: (type_identifier) @name) @definition.typedef\n\n; Macros\n(preproc_function_def name: (identifier) @name) @definition.macro\n(preproc_def name: (identifier) @name) @definition.macro\n\n; Includes\n(preproc_include path: (_) @import.source) @import\n\n; Calls\n(call_expression function: (identifier) @call.name) @call\n(call_expression function: (field_expression field: (field_identifier) @call.name)) @call\n\n; Variable declarations: int x = 5; or int x;\n(declaration\n declarator: (init_declarator\n declarator: (identifier) @name)) @definition.variable\n";
6
6
  export declare const GO_QUERIES = "\n; Functions & Methods\n(function_declaration name: (identifier) @name) @definition.function\n(method_declaration name: (field_identifier) @name) @definition.method\n(method_elem name: (field_identifier) @name) @definition.method\n\n; Types\n(type_declaration (type_spec name: (type_identifier) @name type: (struct_type))) @definition.struct\n(type_declaration (type_spec name: (type_identifier) @name type: (interface_type))) @definition.interface\n\n; Imports\n(import_declaration (import_spec path: (interpreted_string_literal) @import.source)) @import\n(import_declaration (import_spec_list (import_spec path: (interpreted_string_literal) @import.source))) @import\n\n; Struct fields \u2014 named field declarations inside struct types\n(field_declaration_list\n (field_declaration\n name: (field_identifier) @name) @definition.property)\n\n; Calls\n(call_expression function: (identifier) @call.name) @call\n(call_expression function: (selector_expression field: (field_identifier) @call.name)) @call\n\n; Const/var declarations\n(const_declaration (const_spec (identifier) @name)) @definition.const\n(var_declaration (var_spec (identifier) @name)) @definition.variable\n(var_declaration (var_spec_list (var_spec (identifier) @name))) @definition.variable\n\n; Short variable declaration: x := 5\n(short_var_declaration left: (expression_list (identifier) @name)) @definition.variable\n\n; Struct literal construction: User{Name: \"Alice\"}\n(composite_literal type: (type_identifier) @call.name) @call\n\n; Write access: obj.field = value\n(assignment_statement\n left: (expression_list\n (selector_expression\n operand: (_) @assignment.receiver\n field: (field_identifier) @assignment.property))\n right: (_)) @assignment\n\n; Write access: obj.field++ / obj.field--\n(inc_statement\n (selector_expression\n operand: (_) @assignment.receiver\n field: (field_identifier) @assignment.property)) @assignment\n(dec_statement\n (selector_expression\n operand: (_) @assignment.receiver\n field: (field_identifier) @assignment.property)) @assignment\n";
7
7
  export declare const CPP_QUERIES = "\n; Classes, Structs, Namespaces\n(class_specifier name: (type_identifier) @name) @definition.class\n(class_specifier\n name: (template_type\n (type_identifier) @name\n (template_argument_list) @template-arguments)) @definition.class\n; Out-of-line nested definition: class Outer::Inner { ... } / struct Outer::Inner { ... }.\n; Key the node by the full qualified_identifier text so the def materializes a\n; node that matches the HAS_METHOD owner id (also the full qualified text) and\n; stays distinct from a same-tail type in another scope (#1975, #1978).\n(class_specifier name: (qualified_identifier) @name) @definition.class\n(struct_specifier name: (qualified_identifier) @name) @definition.struct\n(struct_specifier name: (type_identifier) @name) @definition.struct\n(struct_specifier\n name: (template_type\n (type_identifier) @name\n (template_argument_list) @template-arguments)) @definition.struct\n(namespace_definition name: (namespace_identifier) @name) @definition.namespace\n(enum_specifier name: (type_identifier) @name) @definition.enum\n\n; Typedefs and unions (common in C-style headers and mixed C/C++ code)\n(type_definition\n type: (struct_specifier\n body: (field_declaration_list))\n declarator: (type_identifier) @name) @definition.struct\n(type_definition\n type: (enum_specifier\n body: (enumerator_list))\n declarator: (type_identifier) @name) @definition.enum\n(enumerator name: (identifier) @name) @definition.const\n(type_definition declarator: (type_identifier) @name) @definition.typedef\n(union_specifier name: (type_identifier) @name) @definition.union\n\n; Macros\n(preproc_function_def name: (identifier) @name) @definition.macro\n(preproc_def name: (identifier) @name) @definition.macro\n\n; Functions & Methods (direct declarator)\n(function_definition declarator: (function_declarator declarator: (identifier) @name)) @definition.function\n(function_definition declarator: (function_declarator declarator: (operator_name) @name)) @definition.function\n(function_definition declarator: (function_declarator declarator: (qualified_identifier name: (identifier) @name))) @definition.method\n(function_definition declarator: (function_declarator declarator: (qualified_identifier name: (operator_name) @name))) @definition.method\n\n; Functions/methods returning pointers (pointer_declarator wraps function_declarator)\n(function_definition declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @name))) @definition.function\n(function_definition declarator: (pointer_declarator declarator: (function_declarator declarator: (qualified_identifier name: (identifier) @name)))) @definition.method\n\n; Functions/methods returning double pointers (nested pointer_declarator)\n(function_definition declarator: (pointer_declarator declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @name)))) @definition.function\n(function_definition declarator: (pointer_declarator declarator: (pointer_declarator declarator: (function_declarator declarator: (qualified_identifier name: (identifier) @name))))) @definition.method\n\n; Functions/methods returning references (reference_declarator wraps function_declarator)\n(function_definition declarator: (reference_declarator (function_declarator declarator: (identifier) @name))) @definition.function\n(function_definition declarator: (reference_declarator (function_declarator declarator: (operator_name) @name))) @definition.function\n(function_definition declarator: (reference_declarator (function_declarator declarator: (qualified_identifier name: (identifier) @name)))) @definition.method\n(function_definition declarator: (reference_declarator (function_declarator declarator: (qualified_identifier name: (operator_name) @name)))) @definition.method\n\n; Destructors (destructor_name is distinct from identifier in tree-sitter-cpp)\n(function_definition declarator: (function_declarator declarator: (qualified_identifier name: (destructor_name) @name))) @definition.method\n\n; Function declarations / prototypes (common in headers)\n(declaration declarator: (function_declarator declarator: (identifier) @name)) @definition.function\n(declaration declarator: (function_declarator declarator: (operator_name) @name)) @definition.function\n(declaration declarator: (pointer_declarator declarator: (function_declarator declarator: (identifier) @name))) @definition.function\n(declaration declarator: (reference_declarator (function_declarator declarator: (operator_name) @name))) @definition.function\n\n; Class/struct data member fields (Address address; int count;)\n; Uses field_identifier to exclude method declarations (which use function_declarator)\n(field_declaration\n declarator: (field_identifier) @name) @definition.property\n\n; Pointer member fields (Address* address;)\n(field_declaration\n declarator: (pointer_declarator\n declarator: (field_identifier) @name)) @definition.property\n\n; Reference member fields (Address& address;)\n(field_declaration\n declarator: (reference_declarator\n (field_identifier) @name)) @definition.property\n\n; Inline class method declarations (inside class body, no body: void save();)\n; tree-sitter-cpp uses field_identifier (not identifier) for names inside class bodies\n(field_declaration declarator: (function_declarator declarator: [(field_identifier) (identifier) (operator_name)] @name)) @definition.method\n\n; Inline class method declarations returning a pointer (User* lookup();)\n(field_declaration declarator: (pointer_declarator declarator: (function_declarator declarator: [(field_identifier) (identifier)] @name))) @definition.method\n\n; Inline class method declarations returning a reference (User& lookup();)\n(field_declaration declarator: (reference_declarator (function_declarator declarator: [(field_identifier) (identifier) (operator_name)] @name))) @definition.method\n\n; Inline class method definitions (inside class body, with body: void Foo() { ... })\n(field_declaration_list\n (function_definition\n declarator: (function_declarator\n declarator: [(field_identifier) (identifier) (operator_name) (destructor_name)] @name)) @definition.method)\n\n; Inline class methods returning a pointer type (User* lookup(int id) { ... })\n(field_declaration_list\n (function_definition\n declarator: (pointer_declarator\n declarator: (function_declarator\n declarator: [(field_identifier) (identifier) (operator_name)] @name))) @definition.method)\n\n; Inline class methods returning a reference type (User& lookup(int id) { ... })\n(field_declaration_list\n (function_definition\n declarator: (reference_declarator\n (function_declarator\n declarator: [(field_identifier) (identifier) (operator_name)] @name))) @definition.method)\n\n; Templates\n(template_declaration (class_specifier name: (type_identifier) @name)) @definition.template\n(template_declaration\n (class_specifier\n name: (template_type\n (type_identifier) @name\n (template_argument_list) @template-arguments))) @definition.template\n(template_declaration (function_definition declarator: (function_declarator declarator: (identifier) @name))) @definition.template\n\n; Includes\n(preproc_include path: (_) @import.source) @import\n\n; Calls\n(call_expression function: (identifier) @call.name) @call\n(call_expression function: (field_expression field: (field_identifier) @call.name)) @call\n(call_expression function: (qualified_identifier name: (identifier) @call.name)) @call\n(call_expression function: (template_function name: (identifier) @call.name)) @call\n(binary_expression operator: \"+\" @call.name) @call\n(binary_expression operator: \"<<\" @call.name) @call\n\n; Constructor calls: new User()\n(new_expression type: (type_identifier) @call.name) @call\n\n; Variable declarations: int x = 5; or auto x = 5;\n(declaration\n declarator: (init_declarator\n declarator: (identifier) @name)) @definition.variable\n\n; Structured bindings: auto [a, b] = makePair(); (one @name per bound identifier)\n(declaration\n declarator: (init_declarator\n declarator: (structured_binding_declarator\n (identifier) @name))) @definition.variable\n\n; Structured bindings, reference form: auto& [x, y] = tup;\n(declaration\n declarator: (init_declarator\n declarator: (reference_declarator\n (structured_binding_declarator\n (identifier) @name)))) @definition.variable\n\n; Write access: obj.field = value\n(assignment_expression\n left: (field_expression\n argument: (_) @assignment.receiver\n field: (field_identifier) @assignment.property)\n right: (_)) @assignment\n\n";
@@ -744,6 +744,10 @@ export const JAVA_QUERIES = `
744
744
  (enum_declaration name: (identifier) @name) @definition.enum
745
745
  (annotation_type_declaration name: (identifier) @name) @definition.annotation
746
746
 
747
+ ; Anonymous class bodies: new Runnable() { ... } — no @name capture; the
748
+ ; class extractor synthesizes the javac-style Worker$N name (#2550)
749
+ (object_creation_expression (class_body)) @definition.class
750
+
747
751
  ; Methods & Constructors
748
752
  (method_declaration name: (identifier) @name) @definition.method
749
753
  (constructor_declaration name: (identifier) @name) @definition.constructor
@@ -1,10 +1,20 @@
1
- import { findChild } from '../utils/ast-helpers.js';
1
+ import { findChild, synthesizeJavaAnonymousClassName, } from '../utils/ast-helpers.js';
2
2
  import { extractSimpleTypeName, extractVarName, extractGenericTypeArgs, resolveIterableElementType, methodToTypeArgPosition, extractElementTypeFromString, } from './shared.js';
3
3
  // ── Java ──────────────────────────────────────────────────────────────────
4
4
  const JAVA_DECLARATION_NODE_TYPES = new Set([
5
5
  'local_variable_declaration',
6
6
  'field_declaration',
7
7
  ]);
8
+ /** `Runnable handler = new Runnable() { ... }` — the variable's effective
9
+ * type is the ANONYMOUS class (`Worker$1`), not the declared interface;
10
+ * that is the instance `handler.run()` dispatches into (#2550). Returns
11
+ * undefined for declarators without an anonymous-body initializer. */
12
+ const anonymousInitializerTypeName = (declarator) => {
13
+ const valueNode = declarator.childForFieldName('value');
14
+ if (!valueNode || valueNode.type !== 'object_creation_expression')
15
+ return undefined;
16
+ return synthesizeJavaAnonymousClassName(valueNode);
17
+ };
8
18
  /** Java: Type x = ...; Type x; */
9
19
  const extractJavaDeclaration = (node, env) => {
10
20
  const typeNode = node.childForFieldName('type');
@@ -22,7 +32,7 @@ const extractJavaDeclaration = (node, env) => {
22
32
  if (nameNode) {
23
33
  const varName = extractVarName(nameNode);
24
34
  if (varName)
25
- env.set(varName, typeName);
35
+ env.set(varName, anonymousInitializerTypeName(child) ?? typeName);
26
36
  }
27
37
  }
28
38
  };
@@ -42,6 +52,11 @@ const extractJavaInitializer = (node, env, _classNames) => {
42
52
  continue;
43
53
  if (valueNode.type !== 'object_creation_expression')
44
54
  continue;
55
+ const anonName = anonymousInitializerTypeName(child);
56
+ if (anonName) {
57
+ env.set(varName, anonName);
58
+ continue;
59
+ }
45
60
  const ctorType = valueNode.childForFieldName('type');
46
61
  if (!ctorType)
47
62
  continue;
@@ -102,6 +102,7 @@ export interface EnclosingClassInfo {
102
102
  */
103
103
  qualifiedClassId?: string;
104
104
  }
105
+ export declare const synthesizeJavaAnonymousClassName: (node: SyntaxNode) => string | undefined;
105
106
  export declare const findEnclosingClassInfo: (node: SyntaxNode, filePath: string, resolveEnclosingOwner?: (node: SyntaxNode) => SyntaxNode | null,
106
107
  /**
107
108
  * Optional (#1978): returns the enclosing type's fully-qualified name
@@ -270,7 +270,14 @@ export function getLabelFromCaptures(captureMap, provider) {
270
270
  return null;
271
271
  const hasDefaultExportHocNameSeed = captureMap['definition.function'] !== undefined &&
272
272
  (captureMap['hoc'] !== undefined || captureMap['callee'] !== undefined);
273
- if (!captureMap['name'] && !captureMap['definition.constructor'] && !hasDefaultExportHocNameSeed)
273
+ // Nameless `definition.class` passes through: a class extractor may
274
+ // synthesize the name (Java anonymous class bodies → `Worker$N`, #2550).
275
+ // Downstream stays safe — parse-worker skips any nameless definition the
276
+ // extractor could not name (its `!nameNode && !extractedClassSymbol` gate).
277
+ if (!captureMap['name'] &&
278
+ !captureMap['definition.constructor'] &&
279
+ !captureMap['definition.class'] &&
280
+ !hasDefaultExportHocNameSeed)
274
281
  return null;
275
282
  if (captureMap['definition.function']) {
276
283
  if (provider.labelOverride) {
@@ -353,6 +360,82 @@ export function getLabelFromCaptures(captureMap, provider) {
353
360
  * `MAX_ENCLOSING_WALK_ITERATIONS` defense-in-depth guard below prevents
354
361
  * pathological hooks from creating an infinite loop. */
355
362
  const MAX_ENCLOSING_WALK_ITERATIONS = 4096;
363
+ /**
364
+ * Synthesize a javac-style name for a Java anonymous class body:
365
+ * `new Runnable() { ... }` inside top-level class `Worker` becomes
366
+ * `Worker$1` (`$N` = 1-based source order of anonymous bodies within the
367
+ * top-level class). Returns undefined when the node is not an
368
+ * `object_creation_expression` carrying a `class_body` child — which also
369
+ * keeps this a no-op for C#, whose `object_creation_expression` uses
370
+ * `initializer_expression`, never `class_body` (#2550).
371
+ *
372
+ * The SAME name must be produced by every layer that keys the anonymous
373
+ * class (structure-phase node id, enclosing-owner walk, scope-side
374
+ * declaration synthesis, receiver typeBinding) — they agree by all calling
375
+ * this one helper.
376
+ */
377
+ /** Type-declaration node types that can host (and name) a Java anonymous
378
+ * class body — javac numbers `$N` per top-level type of any of these
379
+ * kinds. Enum constant bodies (`A { ... }`) are a different node shape
380
+ * and remain unmodeled. */
381
+ const JAVA_ANON_HOST_TYPES = new Set([
382
+ 'class_declaration',
383
+ 'enum_declaration',
384
+ 'interface_declaration',
385
+ 'record_declaration',
386
+ ]);
387
+ /** Per-parse-tree memo of anonymous-body numbering: tree → (startIndex →
388
+ * synthesized name). Keyed by the tree OBJECT via WeakMap so entries die
389
+ * with the parse; without it every call re-scans the host subtree
390
+ * (`descendantsOfType`), and the helper is called from four independent
391
+ * layers per anonymous body — quadratic on anon-heavy files (old-style
392
+ * listener-per-widget Java). */
393
+ const javaAnonNameMemo = new WeakMap();
394
+ export const synthesizeJavaAnonymousClassName = (node) => {
395
+ if (node.type !== 'object_creation_expression')
396
+ return undefined;
397
+ const hasClassBody = node.namedChildren?.some((c) => c.type === 'class_body');
398
+ if (hasClassBody !== true)
399
+ return undefined;
400
+ const tree = node.tree;
401
+ if (tree !== undefined) {
402
+ const cached = javaAnonNameMemo.get(tree)?.get(node.startIndex);
403
+ if (cached !== undefined)
404
+ return cached;
405
+ }
406
+ // Topmost enclosing host type declaration — javac numbers per top-level type.
407
+ let topHost = null;
408
+ let cursor = node.parent;
409
+ let iterations = 0;
410
+ while (cursor) {
411
+ if (++iterations > MAX_ENCLOSING_WALK_ITERATIONS)
412
+ return undefined;
413
+ if (JAVA_ANON_HOST_TYPES.has(cursor.type))
414
+ topHost = cursor;
415
+ cursor = cursor.parent;
416
+ }
417
+ if (topHost === null)
418
+ return undefined;
419
+ const topName = topHost.childForFieldName?.('name')?.text;
420
+ if (topName === undefined || topName.length === 0)
421
+ return undefined;
422
+ const anonBodies = (topHost.descendantsOfType?.('object_creation_expression') ?? []).filter((c) => c.namedChildren?.some((n) => n.type === 'class_body'));
423
+ if (tree !== undefined) {
424
+ let byStart = javaAnonNameMemo.get(tree);
425
+ if (byStart === undefined) {
426
+ byStart = new Map();
427
+ javaAnonNameMemo.set(tree, byStart);
428
+ }
429
+ for (let i = 0; i < anonBodies.length; i++) {
430
+ byStart.set(anonBodies[i].startIndex, `${topName}$${i + 1}`);
431
+ }
432
+ return byStart.get(node.startIndex);
433
+ }
434
+ const index = anonBodies.findIndex((c) => c.startIndex === node.startIndex);
435
+ if (index === -1)
436
+ return undefined;
437
+ return `${topName}$${index + 1}`;
438
+ };
356
439
  export const findEnclosingClassInfo = (node, filePath, resolveEnclosingOwner,
357
440
  /**
358
441
  * Optional (#1978): returns the enclosing type's fully-qualified name
@@ -411,6 +494,21 @@ getQualifiedOwnerName) => {
411
494
  }
412
495
  }
413
496
  }
497
+ // Java: an anonymous class body (`new Runnable() { ... }`) owns its
498
+ // members — attribute to the synthesized `Worker$N` class, not the
499
+ // lexically enclosing named class (#2550). `synthesizeJavaAnonymousClassName`
500
+ // returns undefined for `object_creation_expression` without a
501
+ // `class_body` (plain `new Foo()`, and every C# shape), so the walk
502
+ // continues unchanged for those.
503
+ if (current.type === 'object_creation_expression') {
504
+ const anonName = synthesizeJavaAnonymousClassName(current);
505
+ if (anonName !== undefined) {
506
+ return {
507
+ classId: generateId('Class', `${filePath}:${anonName}`),
508
+ className: anonName,
509
+ };
510
+ }
511
+ }
414
512
  if (CLASS_CONTAINER_TYPES.has(current.type)) {
415
513
  // Delegate language-specific container remapping to the provider hook.
416
514
  if (resolveEnclosingOwner) {
@@ -39,8 +39,15 @@ export declare function resolveNativeSafeStorageDir(storagePath: string, subdir:
39
39
  * `true` (0.16.0). Existing call sites that relied on the positional
40
40
  * default must now pass `false` explicitly to preserve behaviour.
41
41
  *
42
- * Putting both in one shared module guarantees every `new lbug.Database(...)`
43
- * call site agrees on the same ceiling and behaviour.
42
+ * 3. `bufferManagerSize` (not a 0.16.0 change, same pin-explicitly
43
+ * principle): `0` means "native default", and the native default buffer
44
+ * pool is 80% of physical RAM. A long-lived `gitnexus mcp` process or a
45
+ * large incremental analyze can balloon to that ceiling and OOM-kill the
46
+ * host session (#2557), so GitNexus pins an explicit bounded pool — see
47
+ * `resolveBufferManagerSize`.
48
+ *
49
+ * Putting these in one shared module guarantees every `new lbug.Database(...)`
50
+ * call site agrees on the same ceilings and behaviour.
44
51
  */
45
52
  /**
46
53
  * Upper bound for any single GitNexus LadybugDB file (graph index, group
@@ -227,8 +227,15 @@ export function resolveNativeSafeStorageDir(storagePath, subdir) {
227
227
  * `true` (0.16.0). Existing call sites that relied on the positional
228
228
  * default must now pass `false` explicitly to preserve behaviour.
229
229
  *
230
- * Putting both in one shared module guarantees every `new lbug.Database(...)`
231
- * call site agrees on the same ceiling and behaviour.
230
+ * 3. `bufferManagerSize` (not a 0.16.0 change, same pin-explicitly
231
+ * principle): `0` means "native default", and the native default buffer
232
+ * pool is 80% of physical RAM. A long-lived `gitnexus mcp` process or a
233
+ * large incremental analyze can balloon to that ceiling and OOM-kill the
234
+ * host session (#2557), so GitNexus pins an explicit bounded pool — see
235
+ * `resolveBufferManagerSize`.
236
+ *
237
+ * Putting these in one shared module guarantees every `new lbug.Database(...)`
238
+ * call site agrees on the same ceilings and behaviour.
232
239
  */
233
240
  /**
234
241
  * Upper bound for any single GitNexus LadybugDB file (graph index, group
@@ -286,6 +293,53 @@ const resolveCheckpointThreshold = () => {
286
293
  }
287
294
  return DEFAULT_WAL_CHECKPOINT_THRESHOLD;
288
295
  };
296
+ /**
297
+ * Default ceiling for the LadybugDB buffer pool in bytes (#2557).
298
+ *
299
+ * The pool is a page cache with eviction, so the ceiling trades throughput
300
+ * on very large working sets for a machine that stays alive: 2 GiB is
301
+ * ~40× the GitNexus self-index, while the native 80%-of-RAM default let a
302
+ * `detect_changes` call grow a 105 MiB on-disk index to 19.5 GiB RSS and
303
+ * OOM-kill the reporter's session. The `min` with 80% of `os.totalmem()`
304
+ * keeps sub-2.5-GiB machines at the native-equivalent bound (no regression
305
+ * there); the 64 MiB floor keeps tiny containers above any plausible
306
+ * native minimum pool size.
307
+ */
308
+ const DEFAULT_BUFFER_POOL_CAP = 2 * 1024 * 1024 * 1024;
309
+ const BUFFER_POOL_FLOOR = 64 * 1024 * 1024;
310
+ const parseBufferPoolSize = (raw) => {
311
+ if (raw === undefined)
312
+ return undefined;
313
+ const normalized = raw.trim();
314
+ if (normalized.length === 0)
315
+ return undefined;
316
+ const parsed = Number(normalized);
317
+ if (!Number.isFinite(parsed) || parsed < 0)
318
+ return undefined;
319
+ return Math.floor(parsed);
320
+ };
321
+ const defaultBufferPoolSize = () => Math.min(DEFAULT_BUFFER_POOL_CAP, Math.max(BUFFER_POOL_FLOOR, Math.floor(os.totalmem() * 0.8)));
322
+ /**
323
+ * Resolve the `bufferManagerSize` passed to every `new lbug.Database(...)`.
324
+ * `GITNEXUS_LBUG_BUFFER_POOL_SIZE` (bytes) overrides the default; `0` is a
325
+ * deliberate escape hatch that restores LadybugDB's native unbounded
326
+ * 80%-of-RAM default. Resolved at call time (not module load) so tests can
327
+ * stub the env var and `os.totalmem`.
328
+ */
329
+ const resolveBufferManagerSize = () => {
330
+ const raw = process.env.GITNEXUS_LBUG_BUFFER_POOL_SIZE;
331
+ if (raw === undefined)
332
+ return defaultBufferPoolSize();
333
+ const parsed = parseBufferPoolSize(raw);
334
+ if (parsed !== undefined)
335
+ return parsed;
336
+ // Non-empty but unparseable input: warn the operator and fall back —
337
+ // mirrors the GITNEXUS_WAL_CHECKPOINT_THRESHOLD env path above.
338
+ if (raw.trim().length > 0) {
339
+ logger.warn({ rawValue: raw, fallback: defaultBufferPoolSize() }, `Ignoring invalid GITNEXUS_LBUG_BUFFER_POOL_SIZE=${raw}; expected integer >= 0 (bytes; 0 restores the native 80%-of-RAM default); falling back to min(2 GiB, 80% of RAM).`);
340
+ }
341
+ return defaultBufferPoolSize();
342
+ };
289
343
  /** Matches WAL corruption errors from the LadybugDB engine. */
290
344
  const WAL_CORRUPTION_RE = /corrupt(ed)?\s+wal|invalid\s+wal\s+record|wal.*corrupt|checksum.*wal/i;
291
345
  export const WAL_RECOVERY_SUGGESTION = 'WAL corruption detected. Run `gitnexus analyze --force` to rebuild the index.';
@@ -496,7 +550,7 @@ export const classifyDeleteAllError = (err) => {
496
550
  };
497
551
  export function createLbugDatabase(lbugModule, databasePath, options = {}) {
498
552
  // .d.ts declares fewer args than the native constructor accepts.
499
- return new lbugModule.Database(databasePath, 0, // bufferManagerSize
553
+ return new lbugModule.Database(databasePath, resolveBufferManagerSize(), // bufferManagerSize (#2557: default min(2 GiB, 80% RAM); GITNEXUS_LBUG_BUFFER_POOL_SIZE overrides; 0 restores the native 80%-of-RAM default)
500
554
  false, // enableCompression (pinned for v0.16.0)
501
555
  options.readOnly ?? false, LBUG_MAX_DB_SIZE, true, // autoCheckpoint (always on)
502
556
  resolveCheckpointThreshold(), // checkpointThreshold (default 64 MiB; override with GITNEXUS_WAL_CHECKPOINT_THRESHOLD; -1 keeps Ladybug stock ~16 MiB)
@@ -52,7 +52,7 @@ import { fileURLToPath } from 'url';
52
52
  // the main thread (the #1983 OOM). Because the two stores share this version,
53
53
  // any future change to the `ParsedFile` serialization shape MUST bump
54
54
  // SCHEMA_BUMP so both invalidate in lockstep.
55
- const SCHEMA_BUMP = 17; // Callable-value-flow operands now retain expression kind/qualified identity and formal signature metadata. (16 = direct callee identity; 15 = always-on callableFlowSites; 14 = #2437 value refs.)
55
+ const SCHEMA_BUMP = 18; // Java anonymous class bodies emit synthesized Worker$N Class nodes and re-keyed methods (#2550). (17 = callable-value-flow operand identity; 16 = direct callee identity; 15 = always-on callableFlowSites.)
56
56
  const GITNEXUS_PKG_VERSION = (() => {
57
57
  try {
58
58
  // package.json sits at gitnexus/package.json — two levels up from
@@ -335,8 +335,14 @@ export interface RepoMeta {
335
335
  * only covers changed files (`computeEffectiveWriteSet`), so a top-up against a
336
336
  * pre-v7 index would silently omit the new edges for every unchanged file pair;
337
337
  * force a full re-analyze instead (same contract as v2–v6).
338
- */
339
- export declare const INCREMENTAL_SCHEMA_VERSION = 7;
338
+ * v8: Java anonymous class bodies became first-class Class nodes (#2550):
339
+ * `new Runnable() { run(){} }` now emits `Class:...:Worker$1` and its methods
340
+ * re-keyed from `Worker.run` to `Worker$1.run`. Node identities move on
341
+ * unchanged files — a top-up against a pre-v8 index would strand the old
342
+ * `Worker.run`-keyed Method nodes alongside the new ones (the v5 Route
343
+ * precedent); force a full re-analyze instead.
344
+ */
345
+ export declare const INCREMENTAL_SCHEMA_VERSION = 8;
340
346
  export interface IndexedRepo {
341
347
  repoPath: string;
342
348
  storagePath: string;
@@ -112,8 +112,14 @@ export const cloneDirBelongsToEntry = (cloneDir, entryPath) => registryPathEqual
112
112
  * only covers changed files (`computeEffectiveWriteSet`), so a top-up against a
113
113
  * pre-v7 index would silently omit the new edges for every unchanged file pair;
114
114
  * force a full re-analyze instead (same contract as v2–v6).
115
+ * v8: Java anonymous class bodies became first-class Class nodes (#2550):
116
+ * `new Runnable() { run(){} }` now emits `Class:...:Worker$1` and its methods
117
+ * re-keyed from `Worker.run` to `Worker$1.run`. Node identities move on
118
+ * unchanged files — a top-up against a pre-v8 index would strand the old
119
+ * `Worker.run`-keyed Method nodes alongside the new ones (the v5 Route
120
+ * precedent); force a full re-analyze instead.
115
121
  */
116
- export const INCREMENTAL_SCHEMA_VERSION = 7;
122
+ export const INCREMENTAL_SCHEMA_VERSION = 8;
117
123
  const GITNEXUS_DIR = '.gitnexus';
118
124
  const GITNEXUS_EXCLUDE_ENTRY = `${GITNEXUS_DIR}/`;
119
125
  export const INDEX_METADATA_FILE = 'gitnexus.json';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.10-rc.46",
3
+ "version": "1.6.10-rc.48",
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",