gitnexus 1.5.2 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/README.md +10 -0
  2. package/dist/_shared/graph/types.d.ts +1 -1
  3. package/dist/_shared/graph/types.d.ts.map +1 -1
  4. package/dist/_shared/index.d.ts +1 -0
  5. package/dist/_shared/index.d.ts.map +1 -1
  6. package/dist/_shared/language-detection.d.ts.map +1 -1
  7. package/dist/_shared/language-detection.js +2 -0
  8. package/dist/_shared/language-detection.js.map +1 -1
  9. package/dist/_shared/languages.d.ts +1 -0
  10. package/dist/_shared/languages.d.ts.map +1 -1
  11. package/dist/_shared/languages.js +1 -0
  12. package/dist/_shared/languages.js.map +1 -1
  13. package/dist/_shared/lbug/schema-constants.d.ts +1 -1
  14. package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
  15. package/dist/_shared/lbug/schema-constants.js +3 -1
  16. package/dist/_shared/lbug/schema-constants.js.map +1 -1
  17. package/dist/_shared/mro-strategy.d.ts +19 -0
  18. package/dist/_shared/mro-strategy.d.ts.map +1 -0
  19. package/dist/_shared/mro-strategy.js +2 -0
  20. package/dist/_shared/mro-strategy.js.map +1 -0
  21. package/dist/cli/ai-context.d.ts +1 -0
  22. package/dist/cli/ai-context.js +28 -4
  23. package/dist/cli/analyze.d.ts +2 -0
  24. package/dist/cli/analyze.js +2 -1
  25. package/dist/cli/group.d.ts +2 -0
  26. package/dist/cli/group.js +233 -0
  27. package/dist/cli/index.js +3 -0
  28. package/dist/cli/serve.js +4 -1
  29. package/dist/cli/setup.js +34 -3
  30. package/dist/cli/wiki.js +15 -44
  31. package/dist/config/ignore-service.js +8 -3
  32. package/dist/core/augmentation/engine.js +1 -1
  33. package/dist/core/git-staleness.d.ts +13 -0
  34. package/dist/core/git-staleness.js +29 -0
  35. package/dist/core/group/bridge-db.d.ts +82 -0
  36. package/dist/core/group/bridge-db.js +460 -0
  37. package/dist/core/group/bridge-schema.d.ts +27 -0
  38. package/dist/core/group/bridge-schema.js +55 -0
  39. package/dist/core/group/config-parser.d.ts +3 -0
  40. package/dist/core/group/config-parser.js +83 -0
  41. package/dist/core/group/contract-extractor.d.ts +7 -0
  42. package/dist/core/group/contract-extractor.js +1 -0
  43. package/dist/core/group/extractors/grpc-extractor.d.ts +16 -0
  44. package/dist/core/group/extractors/grpc-extractor.js +264 -0
  45. package/dist/core/group/extractors/http-route-extractor.d.ts +24 -0
  46. package/dist/core/group/extractors/http-route-extractor.js +428 -0
  47. package/dist/core/group/extractors/topic-extractor.d.ts +9 -0
  48. package/dist/core/group/extractors/topic-extractor.js +234 -0
  49. package/dist/core/group/matching.d.ts +13 -0
  50. package/dist/core/group/matching.js +198 -0
  51. package/dist/core/group/normalization.d.ts +3 -0
  52. package/dist/core/group/normalization.js +115 -0
  53. package/dist/core/group/service-boundary-detector.d.ts +8 -0
  54. package/dist/core/group/service-boundary-detector.js +155 -0
  55. package/dist/core/group/service.d.ts +46 -0
  56. package/dist/core/group/service.js +160 -0
  57. package/dist/core/group/storage.d.ts +9 -0
  58. package/dist/core/group/storage.js +91 -0
  59. package/dist/core/group/sync.d.ts +21 -0
  60. package/dist/core/group/sync.js +148 -0
  61. package/dist/core/group/types.d.ts +130 -0
  62. package/dist/core/group/types.js +1 -0
  63. package/dist/core/ingestion/binding-accumulator.d.ts +207 -0
  64. package/dist/core/ingestion/binding-accumulator.js +332 -0
  65. package/dist/core/ingestion/call-processor.d.ts +155 -24
  66. package/dist/core/ingestion/call-processor.js +1129 -247
  67. package/dist/core/ingestion/class-extractors/generic.d.ts +2 -0
  68. package/dist/core/ingestion/class-extractors/generic.js +135 -0
  69. package/dist/core/ingestion/class-types.d.ts +34 -0
  70. package/dist/core/ingestion/class-types.js +1 -0
  71. package/dist/core/ingestion/entry-point-scoring.d.ts +1 -0
  72. package/dist/core/ingestion/entry-point-scoring.js +1 -0
  73. package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +5 -1
  74. package/dist/core/ingestion/field-extractors/configs/helpers.js +13 -3
  75. package/dist/core/ingestion/field-types.d.ts +2 -2
  76. package/dist/core/ingestion/filesystem-walker.js +8 -0
  77. package/dist/core/ingestion/framework-detection.d.ts +1 -0
  78. package/dist/core/ingestion/framework-detection.js +1 -0
  79. package/dist/core/ingestion/heritage-processor.d.ts +8 -15
  80. package/dist/core/ingestion/heritage-processor.js +15 -28
  81. package/dist/core/ingestion/import-processor.d.ts +1 -11
  82. package/dist/core/ingestion/import-processor.js +0 -12
  83. package/dist/core/ingestion/import-resolvers/utils.js +1 -0
  84. package/dist/core/ingestion/import-resolvers/vue.d.ts +8 -0
  85. package/dist/core/ingestion/import-resolvers/vue.js +9 -0
  86. package/dist/core/ingestion/language-provider.d.ts +6 -3
  87. package/dist/core/ingestion/languages/c-cpp.js +168 -1
  88. package/dist/core/ingestion/languages/csharp.js +20 -0
  89. package/dist/core/ingestion/languages/dart.js +26 -4
  90. package/dist/core/ingestion/languages/go.js +22 -0
  91. package/dist/core/ingestion/languages/index.d.ts +1 -0
  92. package/dist/core/ingestion/languages/index.js +2 -0
  93. package/dist/core/ingestion/languages/java.js +17 -0
  94. package/dist/core/ingestion/languages/kotlin.js +24 -1
  95. package/dist/core/ingestion/languages/php.js +23 -11
  96. package/dist/core/ingestion/languages/python.js +9 -0
  97. package/dist/core/ingestion/languages/ruby.js +28 -0
  98. package/dist/core/ingestion/languages/rust.js +38 -0
  99. package/dist/core/ingestion/languages/swift.js +31 -0
  100. package/dist/core/ingestion/languages/typescript.d.ts +1 -0
  101. package/dist/core/ingestion/languages/typescript.js +54 -1
  102. package/dist/core/ingestion/languages/vue.d.ts +13 -0
  103. package/dist/core/ingestion/languages/vue.js +81 -0
  104. package/dist/core/ingestion/method-extractors/configs/c-cpp.d.ts +3 -0
  105. package/dist/core/ingestion/method-extractors/configs/c-cpp.js +387 -0
  106. package/dist/core/ingestion/method-extractors/configs/csharp.js +5 -1
  107. package/dist/core/ingestion/method-extractors/configs/dart.d.ts +2 -0
  108. package/dist/core/ingestion/method-extractors/configs/dart.js +376 -0
  109. package/dist/core/ingestion/method-extractors/configs/go.d.ts +2 -0
  110. package/dist/core/ingestion/method-extractors/configs/go.js +176 -0
  111. package/dist/core/ingestion/method-extractors/configs/jvm.js +13 -4
  112. package/dist/core/ingestion/method-extractors/configs/php.d.ts +2 -0
  113. package/dist/core/ingestion/method-extractors/configs/php.js +304 -0
  114. package/dist/core/ingestion/method-extractors/configs/python.d.ts +2 -0
  115. package/dist/core/ingestion/method-extractors/configs/python.js +309 -0
  116. package/dist/core/ingestion/method-extractors/configs/ruby.d.ts +2 -0
  117. package/dist/core/ingestion/method-extractors/configs/ruby.js +285 -0
  118. package/dist/core/ingestion/method-extractors/configs/rust.d.ts +2 -0
  119. package/dist/core/ingestion/method-extractors/configs/rust.js +195 -0
  120. package/dist/core/ingestion/method-extractors/configs/swift.d.ts +2 -0
  121. package/dist/core/ingestion/method-extractors/configs/swift.js +277 -0
  122. package/dist/core/ingestion/method-extractors/configs/typescript-javascript.d.ts +3 -0
  123. package/dist/core/ingestion/method-extractors/configs/typescript-javascript.js +338 -0
  124. package/dist/core/ingestion/method-extractors/generic.js +38 -15
  125. package/dist/core/ingestion/method-types.d.ts +25 -0
  126. package/dist/core/ingestion/model/field-registry.d.ts +18 -0
  127. package/dist/core/ingestion/model/field-registry.js +22 -0
  128. package/dist/core/ingestion/model/heritage-map.d.ts +70 -0
  129. package/dist/core/ingestion/model/heritage-map.js +159 -0
  130. package/dist/core/ingestion/model/index.d.ts +20 -0
  131. package/dist/core/ingestion/model/index.js +41 -0
  132. package/dist/core/ingestion/model/method-registry.d.ts +62 -0
  133. package/dist/core/ingestion/model/method-registry.js +130 -0
  134. package/dist/core/ingestion/model/registration-table.d.ts +139 -0
  135. package/dist/core/ingestion/model/registration-table.js +224 -0
  136. package/dist/core/ingestion/model/resolution-context.d.ts +93 -0
  137. package/dist/core/ingestion/model/resolution-context.js +337 -0
  138. package/dist/core/ingestion/model/resolve.d.ts +56 -0
  139. package/dist/core/ingestion/model/resolve.js +242 -0
  140. package/dist/core/ingestion/model/semantic-model.d.ts +86 -0
  141. package/dist/core/ingestion/model/semantic-model.js +120 -0
  142. package/dist/core/ingestion/model/symbol-table.d.ts +222 -0
  143. package/dist/core/ingestion/model/symbol-table.js +206 -0
  144. package/dist/core/ingestion/model/type-registry.d.ts +39 -0
  145. package/dist/core/ingestion/model/type-registry.js +62 -0
  146. package/dist/core/ingestion/mro-processor.d.ts +4 -3
  147. package/dist/core/ingestion/mro-processor.js +310 -106
  148. package/dist/core/ingestion/parsing-processor.d.ts +5 -4
  149. package/dist/core/ingestion/parsing-processor.js +210 -85
  150. package/dist/core/ingestion/pipeline.d.ts +2 -0
  151. package/dist/core/ingestion/pipeline.js +192 -68
  152. package/dist/core/ingestion/tree-sitter-queries.d.ts +6 -6
  153. package/dist/core/ingestion/tree-sitter-queries.js +37 -0
  154. package/dist/core/ingestion/type-env.d.ts +15 -2
  155. package/dist/core/ingestion/type-env.js +163 -102
  156. package/dist/core/ingestion/type-extractors/csharp.js +17 -0
  157. package/dist/core/ingestion/type-extractors/jvm.js +11 -0
  158. package/dist/core/ingestion/type-extractors/php.js +0 -55
  159. package/dist/core/ingestion/type-extractors/ruby.js +0 -32
  160. package/dist/core/ingestion/type-extractors/swift.js +13 -0
  161. package/dist/core/ingestion/type-extractors/types.d.ts +8 -8
  162. package/dist/core/ingestion/type-extractors/typescript.js +66 -69
  163. package/dist/core/ingestion/utils/ast-helpers.d.ts +33 -43
  164. package/dist/core/ingestion/utils/ast-helpers.js +129 -565
  165. package/dist/core/ingestion/utils/method-props.d.ts +32 -0
  166. package/dist/core/ingestion/utils/method-props.js +147 -0
  167. package/dist/core/ingestion/vue-sfc-extractor.d.ts +44 -0
  168. package/dist/core/ingestion/vue-sfc-extractor.js +94 -0
  169. package/dist/core/ingestion/workers/parse-worker.d.ts +31 -19
  170. package/dist/core/ingestion/workers/parse-worker.js +463 -198
  171. package/dist/core/lbug/lbug-adapter.d.ts +6 -0
  172. package/dist/core/lbug/lbug-adapter.js +68 -3
  173. package/dist/core/lbug/pool-adapter.d.ts +76 -0
  174. package/dist/core/lbug/pool-adapter.js +522 -0
  175. package/dist/core/run-analyze.d.ts +2 -0
  176. package/dist/core/run-analyze.js +1 -1
  177. package/dist/core/search/bm25-index.js +1 -1
  178. package/dist/core/tree-sitter/parser-loader.js +1 -0
  179. package/dist/core/wiki/graph-queries.js +1 -1
  180. package/dist/core/wiki/html-viewer.js +6 -4
  181. package/dist/core/wiki/llm-client.js +4 -6
  182. package/dist/mcp/core/embedder.js +6 -5
  183. package/dist/mcp/core/lbug-adapter.d.ts +3 -63
  184. package/dist/mcp/core/lbug-adapter.js +3 -484
  185. package/dist/mcp/local/local-backend.d.ts +31 -2
  186. package/dist/mcp/local/local-backend.js +255 -46
  187. package/dist/mcp/resources.js +5 -4
  188. package/dist/mcp/staleness.d.ts +3 -13
  189. package/dist/mcp/staleness.js +2 -31
  190. package/dist/mcp/tools.js +80 -4
  191. package/dist/server/analyze-job.d.ts +2 -0
  192. package/dist/server/analyze-job.js +4 -0
  193. package/dist/server/api.d.ts +20 -1
  194. package/dist/server/api.js +306 -71
  195. package/dist/server/git-clone.d.ts +2 -1
  196. package/dist/server/git-clone.js +98 -5
  197. package/dist/storage/git.d.ts +13 -0
  198. package/dist/storage/git.js +25 -0
  199. package/dist/storage/repo-manager.js +1 -1
  200. package/package.json +8 -2
  201. package/scripts/patch-tree-sitter-swift.cjs +78 -0
  202. package/dist/core/ingestion/named-binding-processor.d.ts +0 -18
  203. package/dist/core/ingestion/named-binding-processor.js +0 -42
  204. package/dist/core/ingestion/resolution-context.d.ts +0 -58
  205. package/dist/core/ingestion/resolution-context.js +0 -135
  206. package/dist/core/ingestion/symbol-table.d.ts +0 -79
  207. package/dist/core/ingestion/symbol-table.js +0 -115
@@ -52,3 +52,28 @@ export const hasGitDir = (dirPath) => {
52
52
  return false;
53
53
  }
54
54
  };
55
+ /**
56
+ * Parse unified diff output (with -U0) into per-file hunk ranges.
57
+ * Extracts the new-file line ranges from @@ hunk headers.
58
+ */
59
+ export function parseDiffHunks(diffOutput) {
60
+ const files = [];
61
+ let current = null;
62
+ for (const line of diffOutput.split('\n')) {
63
+ if (line.startsWith('+++ b/')) {
64
+ current = { filePath: line.slice(6), hunks: [] };
65
+ files.push(current);
66
+ }
67
+ else if (line.startsWith('@@') && current) {
68
+ const match = line.match(/@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/);
69
+ if (match) {
70
+ const start = parseInt(match[1], 10);
71
+ const count = match[2] !== undefined ? parseInt(match[2], 10) : 1;
72
+ if (count > 0) {
73
+ current.hunks.push({ startLine: start, endLine: start + count - 1 });
74
+ }
75
+ }
76
+ }
77
+ }
78
+ return files;
79
+ }
@@ -167,7 +167,7 @@ export const addToGitignore = async (repoPath) => {
167
167
  * Get the path to the global GitNexus directory
168
168
  */
169
169
  export const getGlobalDir = () => {
170
- return path.join(os.homedir(), '.gitnexus');
170
+ return process.env.GITNEXUS_HOME || path.join(os.homedir(), '.gitnexus');
171
171
  };
172
172
  /**
173
173
  * Get the path to the global registry file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.5.2",
3
+ "version": "1.6.0",
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",
@@ -46,6 +46,7 @@
46
46
  "test:integration": "vitest run test/integration",
47
47
  "test:watch": "vitest",
48
48
  "test:coverage": "vitest run --coverage",
49
+ "postinstall": "node scripts/patch-tree-sitter-swift.cjs",
49
50
  "prepare": "node scripts/build.js",
50
51
  "prepack": "node scripts/build.js"
51
52
  },
@@ -53,15 +54,18 @@
53
54
  "@huggingface/transformers": "^3.0.0",
54
55
  "@ladybugdb/core": "^0.15.2",
55
56
  "@modelcontextprotocol/sdk": "^1.0.0",
57
+ "@scarf/scarf": "^1.4.0",
56
58
  "cli-progress": "^3.12.0",
57
59
  "commander": "^12.0.0",
58
60
  "cors": "^2.8.5",
59
61
  "express": "^4.19.2",
62
+ "gitnexus-shared": "file:../gitnexus-shared",
60
63
  "glob": "^11.0.0",
61
64
  "graphology": "^0.25.4",
62
65
  "graphology-indices": "^0.17.0",
63
66
  "graphology-utils": "^2.3.0",
64
67
  "ignore": "^7.0.5",
68
+ "js-yaml": "^4.1.1",
65
69
  "lru-cache": "^11.0.0",
66
70
  "mnemonist": "^0.39.0",
67
71
  "onnxruntime-node": "^1.24.0",
@@ -90,6 +94,7 @@
90
94
  "@types/cli-progress": "^3.11.6",
91
95
  "@types/cors": "^2.8.17",
92
96
  "@types/express": "^4.17.21",
97
+ "@types/js-yaml": "^4.0.9",
93
98
  "@types/node": "^20.0.0",
94
99
  "@types/uuid": "^10.0.0",
95
100
  "@vitest/coverage-v8": "^4.0.18",
@@ -100,7 +105,8 @@
100
105
  "overrides": {
101
106
  "@huggingface/transformers": {
102
107
  "onnxruntime-node": "$onnxruntime-node"
103
- }
108
+ },
109
+ "tree-sitter-c": "0.23.2"
104
110
  },
105
111
  "engines": {
106
112
  "node": ">=20.0.0"
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * WORKAROUND: tree-sitter-swift@0.6.0 binding.gyp build failure
4
+ *
5
+ * Background:
6
+ * tree-sitter-swift@0.6.0's binding.gyp contains an "actions" array that
7
+ * invokes `tree-sitter generate` to regenerate parser.c from grammar.js.
8
+ * This is intended for grammar developers, but the published npm package
9
+ * already ships pre-generated parser files (parser.c, scanner.c), so the
10
+ * actions are unnecessary for consumers. Since consumers don't have
11
+ * tree-sitter-cli installed, the actions always fail during `npm install`.
12
+ *
13
+ * Why we can't just upgrade:
14
+ * tree-sitter-swift@0.7.1 fixes this (removes postinstall, ships prebuilds),
15
+ * but it requires tree-sitter@^0.22.1. The upstream project pins tree-sitter
16
+ * to ^0.21.0 and all other grammar packages depend on that version.
17
+ * Upgrading tree-sitter would be a separate breaking change.
18
+ *
19
+ * How this workaround works:
20
+ * 1. tree-sitter-swift's own postinstall fails (npm warns but continues)
21
+ * 2. This script runs as gitnexus's postinstall
22
+ * 3. It removes the "actions" array from binding.gyp
23
+ * 4. It rebuilds the native binding with the cleaned binding.gyp
24
+ *
25
+ * TODO: Remove this script when tree-sitter is upgraded to ^0.22.x,
26
+ * which allows using tree-sitter-swift@0.7.1+ directly.
27
+ */
28
+ const fs = require('fs');
29
+ const path = require('path');
30
+ const { execSync } = require('child_process');
31
+
32
+ const swiftDir = path.join(__dirname, '..', 'node_modules', 'tree-sitter-swift');
33
+ const bindingPath = path.join(swiftDir, 'binding.gyp');
34
+
35
+ try {
36
+ if (!fs.existsSync(bindingPath)) {
37
+ process.exit(0);
38
+ }
39
+
40
+ const content = fs.readFileSync(bindingPath, 'utf8');
41
+ let needsRebuild = false;
42
+
43
+ if (content.includes('"actions"')) {
44
+ // Strip Python-style comments (#) and trailing commas before JSON parsing
45
+ const cleaned = content
46
+ .replace(/#[^\n]*/g, '') // Remove # comments
47
+ .replace(/,(\s*[\]}])/g, '$1'); // Remove trailing commas before ] or }
48
+ const gyp = JSON.parse(cleaned);
49
+
50
+ if (gyp.targets && gyp.targets[0] && gyp.targets[0].actions) {
51
+ delete gyp.targets[0].actions;
52
+ fs.writeFileSync(bindingPath, JSON.stringify(gyp, null, 2) + '\n');
53
+ console.log('[tree-sitter-swift] Patched binding.gyp (removed actions array)');
54
+ needsRebuild = true;
55
+ }
56
+ }
57
+
58
+ // Check if native binding exists
59
+ const bindingNode = path.join(swiftDir, 'build', 'Release', 'tree_sitter_swift_binding.node');
60
+ if (!fs.existsSync(bindingNode)) {
61
+ needsRebuild = true;
62
+ }
63
+
64
+ if (needsRebuild) {
65
+ console.log('[tree-sitter-swift] Rebuilding native binding...');
66
+ execSync('npx node-gyp rebuild', {
67
+ cwd: swiftDir,
68
+ stdio: 'pipe',
69
+ timeout: 120000,
70
+ });
71
+ console.log('[tree-sitter-swift] Native binding built successfully');
72
+ }
73
+ } catch (err) {
74
+ console.warn('[tree-sitter-swift] Could not build native binding:', err.message);
75
+ console.warn(
76
+ '[tree-sitter-swift] You may need to manually run: cd node_modules/tree-sitter-swift && npx node-gyp rebuild',
77
+ );
78
+ }
@@ -1,18 +0,0 @@
1
- import type { SymbolTable, SymbolDefinition } from './symbol-table.js';
2
- import type { NamedImportMap } from './import-processor.js';
3
- /**
4
- * Walk a named-binding re-export chain through NamedImportMap.
5
- *
6
- * When file A imports { User } from B, and B re-exports { User } from C,
7
- * the NamedImportMap for A points to B, but B has no User definition.
8
- * This function follows the chain: A→B→C until a definition is found.
9
- *
10
- * Returns the definitions found at the end of the chain, or null if the
11
- * chain breaks (missing binding, circular reference, or depth exceeded).
12
- * Max depth 5 to prevent infinite loops.
13
- *
14
- * @param allDefs Pre-computed `symbolTable.lookupFuzzy(name)` result — must be the
15
- * complete unfiltered result. Passing a file-filtered subset will cause
16
- * silent misses at depth=0 for non-aliased bindings.
17
- */
18
- export declare function walkBindingChain(name: string, currentFilePath: string, symbolTable: SymbolTable, namedImportMap: NamedImportMap, allDefs: SymbolDefinition[]): SymbolDefinition[] | null;
@@ -1,42 +0,0 @@
1
- /**
2
- * Walk a named-binding re-export chain through NamedImportMap.
3
- *
4
- * When file A imports { User } from B, and B re-exports { User } from C,
5
- * the NamedImportMap for A points to B, but B has no User definition.
6
- * This function follows the chain: A→B→C until a definition is found.
7
- *
8
- * Returns the definitions found at the end of the chain, or null if the
9
- * chain breaks (missing binding, circular reference, or depth exceeded).
10
- * Max depth 5 to prevent infinite loops.
11
- *
12
- * @param allDefs Pre-computed `symbolTable.lookupFuzzy(name)` result — must be the
13
- * complete unfiltered result. Passing a file-filtered subset will cause
14
- * silent misses at depth=0 for non-aliased bindings.
15
- */
16
- export function walkBindingChain(name, currentFilePath, symbolTable, namedImportMap, allDefs) {
17
- let lookupFile = currentFilePath;
18
- let lookupName = name;
19
- const visited = new Set();
20
- for (let depth = 0; depth < 5; depth++) {
21
- const bindings = namedImportMap.get(lookupFile);
22
- if (!bindings)
23
- return null;
24
- const binding = bindings.get(lookupName);
25
- if (!binding)
26
- return null;
27
- const key = `${binding.sourcePath}:${binding.exportedName}`;
28
- if (visited.has(key))
29
- return null; // circular
30
- visited.add(key);
31
- const targetName = binding.exportedName;
32
- const resolvedDefs = targetName !== lookupName || depth > 0
33
- ? symbolTable.lookupFuzzy(targetName).filter((def) => def.filePath === binding.sourcePath)
34
- : allDefs.filter((def) => def.filePath === binding.sourcePath);
35
- if (resolvedDefs.length > 0)
36
- return resolvedDefs;
37
- // No definition in source file → follow re-export chain
38
- lookupFile = binding.sourcePath;
39
- lookupName = targetName;
40
- }
41
- return null;
42
- }
@@ -1,58 +0,0 @@
1
- /**
2
- * Resolution Context
3
- *
4
- * Single implementation of tiered name resolution. Replaces the duplicated
5
- * tier-selection logic previously split between symbol-resolver.ts and
6
- * call-processor.ts.
7
- *
8
- * Resolution tiers (highest confidence first):
9
- * 1. Same file (lookupExactFull — authoritative)
10
- * 2a-named. Named binding chain (walkBindingChain via NamedImportMap)
11
- * 2a. Import-scoped (lookupFuzzy filtered by ImportMap)
12
- * 2b. Package-scoped (lookupFuzzy filtered by PackageMap)
13
- * 3. Global (all candidates — consumers must check candidate count)
14
- */
15
- import type { SymbolTable, SymbolDefinition } from './symbol-table.js';
16
- import type { NamedImportBinding } from './import-processor.js';
17
- /** Resolution tier for tracking, logging, and test assertions. */
18
- export type ResolutionTier = 'same-file' | 'import-scoped' | 'global';
19
- /** Tier-selected candidates with metadata. */
20
- export interface TieredCandidates {
21
- readonly candidates: readonly SymbolDefinition[];
22
- readonly tier: ResolutionTier;
23
- }
24
- /** Confidence scores per resolution tier. */
25
- export declare const TIER_CONFIDENCE: Record<ResolutionTier, number>;
26
- export type ImportMap = Map<string, Set<string>>;
27
- export type PackageMap = Map<string, Set<string>>;
28
- export type NamedImportMap = Map<string, Map<string, NamedImportBinding>>;
29
- /** Maps callerFile → (moduleAlias → sourceFilePath) for Python namespace imports.
30
- * e.g. `import models` in app.py → moduleAliasMap.get('app.py')?.get('models') === 'models.py' */
31
- export type ModuleAliasMap = Map<string, Map<string, string>>;
32
- export interface ResolutionContext {
33
- /**
34
- * The only resolution API. Returns all candidates at the winning tier.
35
- *
36
- * Tier 3 ('global') returns ALL candidates regardless of count —
37
- * consumers must check candidates.length and refuse ambiguous matches.
38
- */
39
- resolve(name: string, fromFile: string): TieredCandidates | null;
40
- /** Symbol table — used by parsing-processor to populate symbols. */
41
- readonly symbols: SymbolTable;
42
- /** Raw maps — used by import-processor to populate import data. */
43
- readonly importMap: ImportMap;
44
- readonly packageMap: PackageMap;
45
- readonly namedImportMap: NamedImportMap;
46
- /** Module-alias map for Python namespace imports: callerFile → (alias → sourceFile). */
47
- readonly moduleAliasMap: ModuleAliasMap;
48
- enableCache(filePath: string): void;
49
- clearCache(): void;
50
- getStats(): {
51
- fileCount: number;
52
- globalSymbolCount: number;
53
- cacheHits: number;
54
- cacheMisses: number;
55
- };
56
- clear(): void;
57
- }
58
- export declare const createResolutionContext: () => ResolutionContext;
@@ -1,135 +0,0 @@
1
- /**
2
- * Resolution Context
3
- *
4
- * Single implementation of tiered name resolution. Replaces the duplicated
5
- * tier-selection logic previously split between symbol-resolver.ts and
6
- * call-processor.ts.
7
- *
8
- * Resolution tiers (highest confidence first):
9
- * 1. Same file (lookupExactFull — authoritative)
10
- * 2a-named. Named binding chain (walkBindingChain via NamedImportMap)
11
- * 2a. Import-scoped (lookupFuzzy filtered by ImportMap)
12
- * 2b. Package-scoped (lookupFuzzy filtered by PackageMap)
13
- * 3. Global (all candidates — consumers must check candidate count)
14
- */
15
- import { createSymbolTable } from './symbol-table.js';
16
- import { isFileInPackageDir } from './import-processor.js';
17
- import { walkBindingChain } from './named-binding-processor.js';
18
- /** Confidence scores per resolution tier. */
19
- export const TIER_CONFIDENCE = {
20
- 'same-file': 0.95,
21
- 'import-scoped': 0.9,
22
- global: 0.5,
23
- };
24
- export const createResolutionContext = () => {
25
- const symbols = createSymbolTable();
26
- const importMap = new Map();
27
- const packageMap = new Map();
28
- const namedImportMap = new Map();
29
- const moduleAliasMap = new Map();
30
- // Per-file cache state
31
- let cacheFile = null;
32
- let cache = null;
33
- let cacheHits = 0;
34
- let cacheMisses = 0;
35
- // --- Core resolution (single implementation of tier logic) ---
36
- const resolveUncached = (name, fromFile) => {
37
- // Tier 1: Same file — authoritative match (returns all overloads)
38
- const localDefs = symbols.lookupExactAll(fromFile, name);
39
- if (localDefs.length > 0) {
40
- return { candidates: localDefs, tier: 'same-file' };
41
- }
42
- // Get all global definitions for subsequent tiers
43
- const allDefs = symbols.lookupFuzzy(name);
44
- // Tier 2a-named: Check named bindings BEFORE empty-allDefs early return
45
- // because aliased imports mean lookupFuzzy('U') returns empty but we
46
- // can resolve via the exported name.
47
- const chainResult = walkBindingChain(name, fromFile, symbols, namedImportMap, allDefs);
48
- if (chainResult && chainResult.length > 0) {
49
- return { candidates: chainResult, tier: 'import-scoped' };
50
- }
51
- if (allDefs.length === 0)
52
- return null;
53
- // Tier 2a: Import-scoped — definition in a file imported by fromFile
54
- const importedFiles = importMap.get(fromFile);
55
- if (importedFiles) {
56
- const importedDefs = allDefs.filter((def) => importedFiles.has(def.filePath));
57
- if (importedDefs.length > 0) {
58
- return { candidates: importedDefs, tier: 'import-scoped' };
59
- }
60
- }
61
- // Tier 2b: Package-scoped — definition in a package dir imported by fromFile
62
- const importedPackages = packageMap.get(fromFile);
63
- if (importedPackages) {
64
- const packageDefs = allDefs.filter((def) => {
65
- for (const dirSuffix of importedPackages) {
66
- if (isFileInPackageDir(def.filePath, dirSuffix))
67
- return true;
68
- }
69
- return false;
70
- });
71
- if (packageDefs.length > 0) {
72
- return { candidates: packageDefs, tier: 'import-scoped' };
73
- }
74
- }
75
- // Tier 3: Global — pass all candidates through.
76
- // Consumers must check candidate count and refuse ambiguous matches.
77
- return { candidates: allDefs, tier: 'global' };
78
- };
79
- const resolve = (name, fromFile) => {
80
- // Check cache (only when enabled AND fromFile matches cached file)
81
- if (cache && cacheFile === fromFile) {
82
- if (cache.has(name)) {
83
- cacheHits++;
84
- return cache.get(name);
85
- }
86
- cacheMisses++;
87
- }
88
- const result = resolveUncached(name, fromFile);
89
- // Store in cache if active and file matches
90
- if (cache && cacheFile === fromFile) {
91
- cache.set(name, result);
92
- }
93
- return result;
94
- };
95
- // --- Cache lifecycle ---
96
- const enableCache = (filePath) => {
97
- cacheFile = filePath;
98
- if (!cache)
99
- cache = new Map();
100
- else
101
- cache.clear();
102
- };
103
- const clearCache = () => {
104
- cacheFile = null;
105
- // Reuse the Map instance — just clear entries to reduce GC pressure at scale.
106
- cache?.clear();
107
- };
108
- const getStats = () => ({
109
- ...symbols.getStats(),
110
- cacheHits,
111
- cacheMisses,
112
- });
113
- const clear = () => {
114
- symbols.clear();
115
- importMap.clear();
116
- packageMap.clear();
117
- namedImportMap.clear();
118
- moduleAliasMap.clear();
119
- clearCache();
120
- cacheHits = 0;
121
- cacheMisses = 0;
122
- };
123
- return {
124
- resolve,
125
- symbols,
126
- importMap,
127
- packageMap,
128
- namedImportMap,
129
- moduleAliasMap,
130
- enableCache,
131
- clearCache,
132
- getStats,
133
- clear,
134
- };
135
- };
@@ -1,79 +0,0 @@
1
- import type { NodeLabel } from '../../_shared/index.js';
2
- export interface SymbolDefinition {
3
- nodeId: string;
4
- filePath: string;
5
- type: NodeLabel;
6
- parameterCount?: number;
7
- /** Number of required (non-optional, non-default) parameters.
8
- * Enables range-based arity filtering: argCount >= requiredParameterCount && argCount <= parameterCount. */
9
- requiredParameterCount?: number;
10
- /** Per-parameter type names for overload disambiguation (e.g. ['int', 'String']).
11
- * Populated when parameter types are resolvable from AST (any typed language).
12
- * Used for disambiguation in overloading languages (Java, Kotlin, C#, C++). */
13
- parameterTypes?: string[];
14
- /** Raw return type text extracted from AST (e.g. 'User', 'Promise<User>') */
15
- returnType?: string;
16
- /** Declared type for non-callable symbols — fields/properties (e.g. 'Address', 'List<User>') */
17
- declaredType?: string;
18
- /** Links Method/Constructor/Property to owning Class/Struct/Trait nodeId */
19
- ownerId?: string;
20
- }
21
- export interface SymbolTable {
22
- /**
23
- * Register a new symbol definition
24
- */
25
- add: (filePath: string, name: string, nodeId: string, type: NodeLabel, metadata?: {
26
- parameterCount?: number;
27
- requiredParameterCount?: number;
28
- parameterTypes?: string[];
29
- returnType?: string;
30
- declaredType?: string;
31
- ownerId?: string;
32
- }) => void;
33
- /**
34
- * High Confidence: Look for a symbol specifically inside a file
35
- * Returns the Node ID if found
36
- */
37
- lookupExact: (filePath: string, name: string) => string | undefined;
38
- /**
39
- * High Confidence: Look for a symbol in a specific file, returning full definition.
40
- * Includes type information needed for heritage resolution (Class vs Interface).
41
- * Returns first matching definition — use lookupExactAll for overloaded methods.
42
- */
43
- lookupExactFull: (filePath: string, name: string) => SymbolDefinition | undefined;
44
- /**
45
- * High Confidence: Look for ALL symbols with this name in a specific file.
46
- * Returns all definitions, including overloaded methods with the same name.
47
- * Used by resolution-context to pass all same-file overloads to candidate filtering.
48
- */
49
- lookupExactAll: (filePath: string, name: string) => SymbolDefinition[];
50
- /**
51
- * Low Confidence: Look for a symbol anywhere in the project
52
- * Used when imports are missing or for framework magic
53
- */
54
- lookupFuzzy: (name: string) => SymbolDefinition[];
55
- /**
56
- * Low Confidence: Look for callable symbols (Function/Method/Constructor) by name.
57
- * Faster than `lookupFuzzy` + filter — backed by a lazy callable-only index.
58
- * Used by ReturnTypeLookup to resolve callee → return type.
59
- */
60
- lookupFuzzyCallable: (name: string) => SymbolDefinition[];
61
- /**
62
- * Look up a field/property by its owning class nodeId and field name.
63
- * O(1) via dedicated eagerly-populated index keyed by `ownerNodeId\0fieldName`.
64
- * Returns undefined when no matching property exists or the owner is ambiguous.
65
- */
66
- lookupFieldByOwner: (ownerNodeId: string, fieldName: string) => SymbolDefinition | undefined;
67
- /**
68
- * Debugging: See how many symbols are tracked
69
- */
70
- getStats: () => {
71
- fileCount: number;
72
- globalSymbolCount: number;
73
- };
74
- /**
75
- * Cleanup memory
76
- */
77
- clear: () => void;
78
- }
79
- export declare const createSymbolTable: () => SymbolTable;
@@ -1,115 +0,0 @@
1
- export const createSymbolTable = () => {
2
- // 1. File-Specific Index — stores full SymbolDefinition(s) for O(1) lookup.
3
- // Structure: FilePath -> (SymbolName -> SymbolDefinition[])
4
- // Array allows overloaded methods (same name, different signatures) to coexist.
5
- const fileIndex = new Map();
6
- // 2. Global Reverse Index (The "Backup")
7
- // Structure: SymbolName -> [List of Definitions]
8
- const globalIndex = new Map();
9
- // 3. Lazy Callable Index — populated on first lookupFuzzyCallable call.
10
- // Structure: SymbolName -> [Callable Definitions]
11
- // Only Function, Method, Constructor symbols are indexed.
12
- let callableIndex = null;
13
- // 4. Eagerly-populated Field/Property Index — keyed by "ownerNodeId\0fieldName".
14
- // Only Property symbols with ownerId and declaredType are indexed.
15
- const fieldByOwner = new Map();
16
- const CALLABLE_TYPES = new Set(['Function', 'Method', 'Constructor']);
17
- const add = (filePath, name, nodeId, type, metadata) => {
18
- const def = {
19
- nodeId,
20
- filePath,
21
- type,
22
- ...(metadata?.parameterCount !== undefined
23
- ? { parameterCount: metadata.parameterCount }
24
- : {}),
25
- ...(metadata?.requiredParameterCount !== undefined
26
- ? { requiredParameterCount: metadata.requiredParameterCount }
27
- : {}),
28
- ...(metadata?.parameterTypes !== undefined
29
- ? { parameterTypes: metadata.parameterTypes }
30
- : {}),
31
- ...(metadata?.returnType !== undefined ? { returnType: metadata.returnType } : {}),
32
- ...(metadata?.declaredType !== undefined ? { declaredType: metadata.declaredType } : {}),
33
- ...(metadata?.ownerId !== undefined ? { ownerId: metadata.ownerId } : {}),
34
- };
35
- // A. Add to File Index (shared reference — zero additional memory)
36
- if (!fileIndex.has(filePath)) {
37
- fileIndex.set(filePath, new Map());
38
- }
39
- const fileMap = fileIndex.get(filePath);
40
- if (!fileMap.has(name)) {
41
- fileMap.set(name, [def]);
42
- }
43
- else {
44
- fileMap.get(name).push(def);
45
- }
46
- // B. Properties go to fieldByOwner index only — skip globalIndex to prevent
47
- // namespace pollution for common names like 'id', 'name', 'type'.
48
- // Index ALL properties (even without declaredType) so write-access tracking
49
- // can resolve field ownership for dynamically-typed languages (Ruby, JS).
50
- if (type === 'Property' && metadata?.ownerId) {
51
- fieldByOwner.set(`${metadata.ownerId}\0${name}`, def);
52
- // Still add to fileIndex above (for lookupExact), but skip globalIndex
53
- return;
54
- }
55
- // C. Add to Global Index (same object reference)
56
- if (!globalIndex.has(name)) {
57
- globalIndex.set(name, []);
58
- }
59
- globalIndex.get(name).push(def);
60
- // D. Invalidate the lazy callable index only when adding callable types
61
- if (CALLABLE_TYPES.has(type)) {
62
- callableIndex = null;
63
- }
64
- };
65
- const lookupExact = (filePath, name) => {
66
- const defs = fileIndex.get(filePath)?.get(name);
67
- return defs?.[0]?.nodeId;
68
- };
69
- const lookupExactFull = (filePath, name) => {
70
- const defs = fileIndex.get(filePath)?.get(name);
71
- return defs?.[0];
72
- };
73
- const lookupExactAll = (filePath, name) => {
74
- return fileIndex.get(filePath)?.get(name) ?? [];
75
- };
76
- const lookupFuzzy = (name) => {
77
- return globalIndex.get(name) || [];
78
- };
79
- const lookupFuzzyCallable = (name) => {
80
- if (!callableIndex) {
81
- // Build the callable index lazily on first use
82
- callableIndex = new Map();
83
- for (const [symName, defs] of globalIndex) {
84
- const callables = defs.filter((d) => CALLABLE_TYPES.has(d.type));
85
- if (callables.length > 0)
86
- callableIndex.set(symName, callables);
87
- }
88
- }
89
- return callableIndex.get(name) ?? [];
90
- };
91
- const lookupFieldByOwner = (ownerNodeId, fieldName) => {
92
- return fieldByOwner.get(`${ownerNodeId}\0${fieldName}`);
93
- };
94
- const getStats = () => ({
95
- fileCount: fileIndex.size,
96
- globalSymbolCount: globalIndex.size,
97
- });
98
- const clear = () => {
99
- fileIndex.clear();
100
- globalIndex.clear();
101
- callableIndex = null;
102
- fieldByOwner.clear();
103
- };
104
- return {
105
- add,
106
- lookupExact,
107
- lookupExactFull,
108
- lookupExactAll,
109
- lookupFuzzy,
110
- lookupFuzzyCallable,
111
- lookupFieldByOwner,
112
- getStats,
113
- clear,
114
- };
115
- };