gitnexus 1.4.10 → 1.5.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 (186) hide show
  1. package/README.md +6 -5
  2. package/dist/cli/ai-context.d.ts +4 -1
  3. package/dist/cli/ai-context.js +19 -11
  4. package/dist/cli/analyze.d.ts +6 -0
  5. package/dist/cli/analyze.js +105 -251
  6. package/dist/cli/eval-server.js +20 -11
  7. package/dist/cli/index-repo.js +20 -22
  8. package/dist/cli/index.js +8 -7
  9. package/dist/cli/mcp.js +1 -1
  10. package/dist/cli/serve.js +29 -1
  11. package/dist/cli/setup.js +9 -9
  12. package/dist/cli/skill-gen.js +15 -9
  13. package/dist/cli/wiki.d.ts +2 -0
  14. package/dist/cli/wiki.js +141 -26
  15. package/dist/config/ignore-service.js +102 -22
  16. package/dist/config/supported-languages.d.ts +8 -42
  17. package/dist/config/supported-languages.js +8 -43
  18. package/dist/core/augmentation/engine.js +19 -7
  19. package/dist/core/embeddings/embedder.js +19 -15
  20. package/dist/core/embeddings/embedding-pipeline.js +6 -6
  21. package/dist/core/embeddings/http-client.js +3 -3
  22. package/dist/core/embeddings/text-generator.js +9 -24
  23. package/dist/core/embeddings/types.d.ts +1 -1
  24. package/dist/core/embeddings/types.js +1 -7
  25. package/dist/core/graph/graph.js +6 -2
  26. package/dist/core/graph/types.d.ts +9 -59
  27. package/dist/core/ingestion/ast-cache.js +3 -3
  28. package/dist/core/ingestion/call-processor.d.ts +20 -2
  29. package/dist/core/ingestion/call-processor.js +347 -144
  30. package/dist/core/ingestion/call-routing.js +10 -4
  31. package/dist/core/ingestion/call-sites/extract-language-call-site.d.ts +10 -0
  32. package/dist/core/ingestion/call-sites/extract-language-call-site.js +22 -0
  33. package/dist/core/ingestion/call-sites/java.d.ts +9 -0
  34. package/dist/core/ingestion/call-sites/java.js +30 -0
  35. package/dist/core/ingestion/cluster-enricher.js +6 -8
  36. package/dist/core/ingestion/cobol/cobol-copy-expander.js +10 -3
  37. package/dist/core/ingestion/cobol/cobol-preprocessor.js +287 -81
  38. package/dist/core/ingestion/cobol/jcl-parser.js +1 -1
  39. package/dist/core/ingestion/cobol/jcl-processor.js +1 -1
  40. package/dist/core/ingestion/cobol-processor.js +102 -56
  41. package/dist/core/ingestion/community-processor.js +21 -15
  42. package/dist/core/ingestion/entry-point-scoring.d.ts +1 -1
  43. package/dist/core/ingestion/entry-point-scoring.js +5 -6
  44. package/dist/core/ingestion/export-detection.js +32 -9
  45. package/dist/core/ingestion/field-extractor.d.ts +1 -1
  46. package/dist/core/ingestion/field-extractors/configs/c-cpp.js +8 -12
  47. package/dist/core/ingestion/field-extractors/configs/csharp.js +45 -2
  48. package/dist/core/ingestion/field-extractors/configs/dart.js +5 -3
  49. package/dist/core/ingestion/field-extractors/configs/go.js +3 -7
  50. package/dist/core/ingestion/field-extractors/configs/helpers.d.ts +5 -0
  51. package/dist/core/ingestion/field-extractors/configs/helpers.js +14 -0
  52. package/dist/core/ingestion/field-extractors/configs/jvm.js +7 -7
  53. package/dist/core/ingestion/field-extractors/configs/php.js +9 -11
  54. package/dist/core/ingestion/field-extractors/configs/python.js +1 -1
  55. package/dist/core/ingestion/field-extractors/configs/ruby.js +4 -3
  56. package/dist/core/ingestion/field-extractors/configs/rust.js +2 -5
  57. package/dist/core/ingestion/field-extractors/configs/swift.js +9 -7
  58. package/dist/core/ingestion/field-extractors/configs/typescript-javascript.js +2 -6
  59. package/dist/core/ingestion/field-extractors/generic.d.ts +5 -2
  60. package/dist/core/ingestion/field-extractors/generic.js +6 -0
  61. package/dist/core/ingestion/field-extractors/typescript.d.ts +1 -1
  62. package/dist/core/ingestion/field-extractors/typescript.js +1 -1
  63. package/dist/core/ingestion/field-types.d.ts +4 -2
  64. package/dist/core/ingestion/filesystem-walker.js +3 -3
  65. package/dist/core/ingestion/framework-detection.d.ts +1 -1
  66. package/dist/core/ingestion/framework-detection.js +355 -85
  67. package/dist/core/ingestion/heritage-processor.d.ts +24 -0
  68. package/dist/core/ingestion/heritage-processor.js +99 -8
  69. package/dist/core/ingestion/import-processor.js +44 -15
  70. package/dist/core/ingestion/import-resolvers/csharp.js +7 -3
  71. package/dist/core/ingestion/import-resolvers/dart.js +1 -1
  72. package/dist/core/ingestion/import-resolvers/go.js +4 -2
  73. package/dist/core/ingestion/import-resolvers/jvm.js +4 -4
  74. package/dist/core/ingestion/import-resolvers/php.js +4 -4
  75. package/dist/core/ingestion/import-resolvers/python.js +1 -1
  76. package/dist/core/ingestion/import-resolvers/rust.js +9 -3
  77. package/dist/core/ingestion/import-resolvers/standard.d.ts +1 -1
  78. package/dist/core/ingestion/import-resolvers/standard.js +6 -5
  79. package/dist/core/ingestion/import-resolvers/swift.js +2 -1
  80. package/dist/core/ingestion/import-resolvers/utils.js +26 -7
  81. package/dist/core/ingestion/language-config.js +5 -4
  82. package/dist/core/ingestion/language-provider.d.ts +7 -2
  83. package/dist/core/ingestion/languages/c-cpp.js +106 -21
  84. package/dist/core/ingestion/languages/cobol.js +1 -1
  85. package/dist/core/ingestion/languages/csharp.js +96 -19
  86. package/dist/core/ingestion/languages/dart.js +23 -7
  87. package/dist/core/ingestion/languages/go.js +1 -1
  88. package/dist/core/ingestion/languages/index.d.ts +1 -1
  89. package/dist/core/ingestion/languages/index.js +2 -3
  90. package/dist/core/ingestion/languages/java.js +4 -1
  91. package/dist/core/ingestion/languages/kotlin.js +60 -13
  92. package/dist/core/ingestion/languages/php.js +102 -25
  93. package/dist/core/ingestion/languages/python.js +28 -5
  94. package/dist/core/ingestion/languages/ruby.js +56 -14
  95. package/dist/core/ingestion/languages/rust.js +55 -11
  96. package/dist/core/ingestion/languages/swift.js +112 -27
  97. package/dist/core/ingestion/languages/typescript.js +95 -19
  98. package/dist/core/ingestion/markdown-processor.js +5 -5
  99. package/dist/core/ingestion/method-extractors/configs/csharp.d.ts +2 -0
  100. package/dist/core/ingestion/method-extractors/configs/csharp.js +283 -0
  101. package/dist/core/ingestion/method-extractors/configs/jvm.d.ts +3 -0
  102. package/dist/core/ingestion/method-extractors/configs/jvm.js +326 -0
  103. package/dist/core/ingestion/method-extractors/generic.d.ts +5 -0
  104. package/dist/core/ingestion/method-extractors/generic.js +137 -0
  105. package/dist/core/ingestion/method-types.d.ts +61 -0
  106. package/dist/core/ingestion/method-types.js +2 -0
  107. package/dist/core/ingestion/mro-processor.d.ts +1 -1
  108. package/dist/core/ingestion/mro-processor.js +12 -8
  109. package/dist/core/ingestion/named-binding-processor.js +2 -2
  110. package/dist/core/ingestion/named-bindings/rust.js +3 -1
  111. package/dist/core/ingestion/parsing-processor.js +74 -24
  112. package/dist/core/ingestion/pipeline.d.ts +2 -1
  113. package/dist/core/ingestion/pipeline.js +208 -102
  114. package/dist/core/ingestion/process-processor.js +12 -10
  115. package/dist/core/ingestion/resolution-context.js +3 -3
  116. package/dist/core/ingestion/route-extractors/middleware.js +31 -7
  117. package/dist/core/ingestion/route-extractors/php.js +2 -1
  118. package/dist/core/ingestion/route-extractors/response-shapes.js +8 -4
  119. package/dist/core/ingestion/structure-processor.d.ts +1 -1
  120. package/dist/core/ingestion/structure-processor.js +4 -4
  121. package/dist/core/ingestion/symbol-table.d.ts +1 -1
  122. package/dist/core/ingestion/symbol-table.js +22 -6
  123. package/dist/core/ingestion/tree-sitter-queries.d.ts +1 -1
  124. package/dist/core/ingestion/tree-sitter-queries.js +1 -1
  125. package/dist/core/ingestion/type-env.d.ts +2 -2
  126. package/dist/core/ingestion/type-env.js +75 -50
  127. package/dist/core/ingestion/type-extractors/c-cpp.js +33 -30
  128. package/dist/core/ingestion/type-extractors/csharp.js +24 -14
  129. package/dist/core/ingestion/type-extractors/dart.js +6 -8
  130. package/dist/core/ingestion/type-extractors/go.js +7 -6
  131. package/dist/core/ingestion/type-extractors/jvm.js +10 -21
  132. package/dist/core/ingestion/type-extractors/php.js +26 -13
  133. package/dist/core/ingestion/type-extractors/python.js +11 -15
  134. package/dist/core/ingestion/type-extractors/ruby.js +8 -3
  135. package/dist/core/ingestion/type-extractors/rust.js +6 -8
  136. package/dist/core/ingestion/type-extractors/shared.js +134 -50
  137. package/dist/core/ingestion/type-extractors/swift.js +16 -13
  138. package/dist/core/ingestion/type-extractors/typescript.js +23 -15
  139. package/dist/core/ingestion/utils/ast-helpers.d.ts +8 -8
  140. package/dist/core/ingestion/utils/ast-helpers.js +72 -35
  141. package/dist/core/ingestion/utils/call-analysis.d.ts +2 -0
  142. package/dist/core/ingestion/utils/call-analysis.js +96 -49
  143. package/dist/core/ingestion/utils/event-loop.js +1 -1
  144. package/dist/core/ingestion/workers/parse-worker.d.ts +7 -2
  145. package/dist/core/ingestion/workers/parse-worker.js +364 -84
  146. package/dist/core/ingestion/workers/worker-pool.js +5 -10
  147. package/dist/core/lbug/csv-generator.js +54 -15
  148. package/dist/core/lbug/lbug-adapter.d.ts +5 -0
  149. package/dist/core/lbug/lbug-adapter.js +86 -23
  150. package/dist/core/lbug/schema.d.ts +3 -6
  151. package/dist/core/lbug/schema.js +6 -30
  152. package/dist/core/run-analyze.d.ts +49 -0
  153. package/dist/core/run-analyze.js +257 -0
  154. package/dist/core/tree-sitter/parser-loader.d.ts +1 -1
  155. package/dist/core/tree-sitter/parser-loader.js +1 -1
  156. package/dist/core/wiki/cursor-client.js +2 -7
  157. package/dist/core/wiki/generator.js +38 -23
  158. package/dist/core/wiki/graph-queries.js +10 -10
  159. package/dist/core/wiki/html-viewer.js +7 -3
  160. package/dist/core/wiki/llm-client.d.ts +23 -2
  161. package/dist/core/wiki/llm-client.js +96 -26
  162. package/dist/core/wiki/prompts.js +7 -6
  163. package/dist/mcp/core/embedder.js +1 -1
  164. package/dist/mcp/core/lbug-adapter.d.ts +4 -1
  165. package/dist/mcp/core/lbug-adapter.js +17 -7
  166. package/dist/mcp/local/local-backend.js +247 -95
  167. package/dist/mcp/resources.js +14 -6
  168. package/dist/mcp/server.js +13 -5
  169. package/dist/mcp/staleness.js +5 -1
  170. package/dist/mcp/tools.js +100 -23
  171. package/dist/server/analyze-job.d.ts +53 -0
  172. package/dist/server/analyze-job.js +146 -0
  173. package/dist/server/analyze-worker.d.ts +13 -0
  174. package/dist/server/analyze-worker.js +59 -0
  175. package/dist/server/api.js +795 -44
  176. package/dist/server/git-clone.d.ts +25 -0
  177. package/dist/server/git-clone.js +91 -0
  178. package/dist/storage/git.js +1 -3
  179. package/dist/storage/repo-manager.d.ts +5 -2
  180. package/dist/storage/repo-manager.js +4 -4
  181. package/dist/types/pipeline.d.ts +1 -21
  182. package/dist/types/pipeline.js +1 -18
  183. package/hooks/claude/gitnexus-hook.cjs +52 -22
  184. package/package.json +3 -2
  185. package/dist/core/ingestion/utils/language-detection.d.ts +0 -9
  186. package/dist/core/ingestion/utils/language-detection.js +0 -70
@@ -16,7 +16,7 @@
16
16
  import Parser from 'tree-sitter';
17
17
  import { isLanguageAvailable, loadParser, loadLanguage } from '../tree-sitter/parser-loader.js';
18
18
  import { generateId } from '../../lib/utils.js';
19
- import { getLanguageFromFilename } from './utils/language-detection.js';
19
+ import { getLanguageFromFilename } from 'gitnexus-shared';
20
20
  import { isVerboseIngestionEnabled } from './utils/verbose.js';
21
21
  import { yieldToEventLoop } from './utils/event-loop.js';
22
22
  import { getProvider } from './languages/index.js';
@@ -30,7 +30,8 @@ import { TIER_CONFIDENCE } from './resolution-context.js';
30
30
  * - heritageDefaultEdge: 'IMPLEMENTS' causes all unresolved parents to map to IMPLEMENTS
31
31
  * - All others: default EXTENDS
32
32
  */
33
- const resolveExtendsType = (parentName, currentFilePath, ctx, language) => {
33
+ /** Exported for implementor-map construction (C#/Java: `extends` rows in base_list may be interfaces). */
34
+ export const resolveExtendsType = (parentName, currentFilePath, ctx, language) => {
34
35
  const resolved = ctx.resolve(parentName, currentFilePath);
35
36
  if (resolved && resolved.candidates.length > 0) {
36
37
  const isInterface = resolved.candidates[0].type === 'Interface';
@@ -53,12 +54,18 @@ const resolveHeritageId = (name, filePath, ctx, fallbackLabel, fallbackKey) => {
53
54
  if (resolved && resolved.candidates.length > 0) {
54
55
  // For global with multiple candidates, refuse (a wrong edge is worse than no edge)
55
56
  if (resolved.tier === 'global' && resolved.candidates.length > 1) {
56
- return { id: generateId(fallbackLabel, fallbackKey ?? name), confidence: TIER_CONFIDENCE['global'] };
57
+ return {
58
+ id: generateId(fallbackLabel, fallbackKey ?? name),
59
+ confidence: TIER_CONFIDENCE['global'],
60
+ };
57
61
  }
58
62
  return { id: resolved.candidates[0].nodeId, confidence: TIER_CONFIDENCE[resolved.tier] };
59
63
  }
60
64
  // Unresolved: use global-tier confidence as fallback
61
- return { id: generateId(fallbackLabel, fallbackKey ?? name), confidence: TIER_CONFIDENCE['global'] };
65
+ return {
66
+ id: generateId(fallbackLabel, fallbackKey ?? name),
67
+ confidence: TIER_CONFIDENCE['global'],
68
+ };
62
69
  };
63
70
  export const processHeritage = async (graph, files, astCache, ctx, onProgress) => {
64
71
  const parser = await loadParser();
@@ -90,7 +97,9 @@ export const processHeritage = async (graph, files, astCache, ctx, onProgress) =
90
97
  if (!tree) {
91
98
  // Use larger bufferSize for files > 32KB
92
99
  try {
93
- tree = parser.parse(file.content, undefined, { bufferSize: getTreeSitterBufferSize(file.content.length) });
100
+ tree = parser.parse(file.content, undefined, {
101
+ bufferSize: getTreeSitterBufferSize(file.content.length),
102
+ });
94
103
  }
95
104
  catch (parseError) {
96
105
  // Skip files that can't be parsed
@@ -111,9 +120,9 @@ export const processHeritage = async (graph, files, astCache, ctx, onProgress) =
111
120
  continue;
112
121
  }
113
122
  // 4. Process heritage matches
114
- matches.forEach(match => {
123
+ matches.forEach((match) => {
115
124
  const captureMap = {};
116
- match.captures.forEach(c => {
125
+ match.captures.forEach((c) => {
117
126
  captureMap[c.name] = c.node;
118
127
  });
119
128
  // EXTENDS or IMPLEMENTS: resolve via symbol table for languages where
@@ -228,7 +237,10 @@ export const processHeritageFromExtracted = async (graph, extractedHeritage, ctx
228
237
  });
229
238
  }
230
239
  }
231
- else if (h.kind === 'trait-impl' || h.kind === 'include' || h.kind === 'extend' || h.kind === 'prepend') {
240
+ else if (h.kind === 'trait-impl' ||
241
+ h.kind === 'include' ||
242
+ h.kind === 'extend' ||
243
+ h.kind === 'prepend') {
232
244
  const strct = resolveHeritageId(h.className, h.filePath, ctx, 'Struct', `${h.filePath}:${h.className}`);
233
245
  const trait = resolveHeritageId(h.parentName, h.filePath, ctx, 'Trait');
234
246
  if (strct.id && trait.id) {
@@ -245,3 +257,82 @@ export const processHeritageFromExtracted = async (graph, extractedHeritage, ctx
245
257
  }
246
258
  onProgress?.(total, total);
247
259
  };
260
+ /**
261
+ * Walk source files with the same heritage captures as parse-worker, producing
262
+ * {@link ExtractedHeritage} rows without mutating the graph. Used on the
263
+ * sequential pipeline path so `buildImplementorMap(..., ctx)` can run before
264
+ * `processCalls` (worker path defers calls until heritage from all chunks exists).
265
+ */
266
+ export async function extractExtractedHeritageFromFiles(files, astCache) {
267
+ const parser = await loadParser();
268
+ const out = [];
269
+ for (const file of files) {
270
+ const language = getLanguageFromFilename(file.path);
271
+ if (!language || !isLanguageAvailable(language))
272
+ continue;
273
+ const provider = getProvider(language);
274
+ const queryStr = provider.treeSitterQueries;
275
+ if (!queryStr)
276
+ continue;
277
+ await loadLanguage(language, file.path);
278
+ let tree = astCache.get(file.path);
279
+ if (!tree) {
280
+ try {
281
+ tree = parser.parse(file.content, undefined, {
282
+ bufferSize: getTreeSitterBufferSize(file.content.length),
283
+ });
284
+ }
285
+ catch {
286
+ continue;
287
+ }
288
+ astCache.set(file.path, tree);
289
+ }
290
+ let matches;
291
+ try {
292
+ const lang = parser.getLanguage();
293
+ const query = new Parser.Query(lang, queryStr);
294
+ matches = query.matches(tree.rootNode);
295
+ }
296
+ catch {
297
+ continue;
298
+ }
299
+ for (const match of matches) {
300
+ const captureMap = {};
301
+ match.captures.forEach((c) => {
302
+ captureMap[c.name] = c.node;
303
+ });
304
+ if (captureMap['heritage.class']) {
305
+ if (captureMap['heritage.extends']) {
306
+ const extendsNode = captureMap['heritage.extends'];
307
+ const fieldDecl = extendsNode.parent;
308
+ const isNamedField = fieldDecl?.type === 'field_declaration' && fieldDecl.childForFieldName('name');
309
+ if (!isNamedField) {
310
+ out.push({
311
+ filePath: file.path,
312
+ className: captureMap['heritage.class'].text,
313
+ parentName: captureMap['heritage.extends'].text,
314
+ kind: 'extends',
315
+ });
316
+ }
317
+ }
318
+ if (captureMap['heritage.implements']) {
319
+ out.push({
320
+ filePath: file.path,
321
+ className: captureMap['heritage.class'].text,
322
+ parentName: captureMap['heritage.implements'].text,
323
+ kind: 'implements',
324
+ });
325
+ }
326
+ if (captureMap['heritage.trait']) {
327
+ out.push({
328
+ filePath: file.path,
329
+ className: captureMap['heritage.class'].text,
330
+ parentName: captureMap['heritage.trait'].text,
331
+ kind: 'trait-impl',
332
+ });
333
+ }
334
+ }
335
+ }
336
+ }
337
+ return out;
338
+ }
@@ -2,7 +2,7 @@ import Parser from 'tree-sitter';
2
2
  import { isLanguageAvailable, loadParser, loadLanguage } from '../tree-sitter/parser-loader.js';
3
3
  import { getProvider, getProviderForFile, providersWithImplicitWiring } from './languages/index.js';
4
4
  import { generateId } from '../../lib/utils.js';
5
- import { getLanguageFromFilename } from './utils/language-detection.js';
5
+ import { getLanguageFromFilename } from 'gitnexus-shared';
6
6
  import { isVerboseIngestionEnabled } from './utils/verbose.js';
7
7
  import { yieldToEventLoop } from './utils/event-loop.js';
8
8
  import { getTreeSitterBufferSize } from './constants.js';
@@ -47,7 +47,7 @@ export function isFileInPackageDir(filePath, dirSuffix) {
47
47
  // ImportResolutionContext is defined in ./import-resolvers/types.ts — re-exported here for consumers.
48
48
  export function buildImportResolutionContext(allPaths) {
49
49
  const allFileList = allPaths;
50
- const normalizedFileList = allFileList.map(p => p.replace(/\\/g, '/'));
50
+ const normalizedFileList = allFileList.map((p) => p.replace(/\\/g, '/'));
51
51
  const allFilePaths = new Set(allFileList);
52
52
  const index = buildSuffixIndex(normalizedFileList, allFileList);
53
53
  return { allFilePaths, allFileList, normalizedFileList, index, resolveCache: new Map() };
@@ -80,7 +80,14 @@ function createImportEdgeHelpers(graph, importMap) {
80
80
  const targetId = generateId('File', resolvedPath);
81
81
  const relId = generateId('IMPORTS', `${filePath}->${resolvedPath}`);
82
82
  totalImportsResolved++;
83
- graph.addRelationship({ id: relId, sourceId, targetId, type: 'IMPORTS', confidence: 1.0, reason: '' });
83
+ graph.addRelationship({
84
+ id: relId,
85
+ sourceId,
86
+ targetId,
87
+ type: 'IMPORTS',
88
+ confidence: 1.0,
89
+ reason: '',
90
+ });
84
91
  };
85
92
  const addImportEdge = (filePath, resolvedPath) => {
86
93
  addImportGraphEdge(filePath, resolvedPath);
@@ -147,7 +154,10 @@ function applyImportResult(result, filePath, importMap, packageMap, addImportEdg
147
154
  fileBindings.delete(binding.local);
148
155
  }
149
156
  else {
150
- fileBindings.set(binding.local, { sourcePath: resolvedFile, exportedName: binding.exported });
157
+ fileBindings.set(binding.local, {
158
+ sourcePath: resolvedFile,
159
+ exportedName: binding.exported,
160
+ });
151
161
  }
152
162
  }
153
163
  }
@@ -159,7 +169,7 @@ function applyImportResult(result, filePath, importMap, packageMap, addImportEdg
159
169
  if (binding.isModuleAlias)
160
170
  continue;
161
171
  const lowerName = binding.exported.toLowerCase();
162
- const matchedFile = files.find(f => {
172
+ const matchedFile = files.find((f) => {
163
173
  const base = f.replace(/\\/g, '/').split('/').pop() ?? '';
164
174
  const nameWithoutExt = base.substring(0, base.lastIndexOf('.')).toLowerCase();
165
175
  return nameWithoutExt === lowerName;
@@ -170,7 +180,10 @@ function applyImportResult(result, filePath, importMap, packageMap, addImportEdg
170
180
  fileBindings.delete(binding.local);
171
181
  }
172
182
  else {
173
- fileBindings.set(binding.local, { sourcePath: matchedFile, exportedName: binding.exported });
183
+ fileBindings.set(binding.local, {
184
+ sourcePath: matchedFile,
185
+ exportedName: binding.exported,
186
+ });
174
187
  }
175
188
  }
176
189
  }
@@ -187,21 +200,28 @@ export const processImports = async (graph, files, astCache, ctx, onProgress, re
187
200
  const namedImportMap = ctx.namedImportMap;
188
201
  const moduleAliasMap = ctx.moduleAliasMap;
189
202
  // Use allPaths (full repo) when available for cross-chunk resolution, else fall back to chunk files
190
- const allFileList = allPaths ?? files.map(f => f.path);
203
+ const allFileList = allPaths ?? files.map((f) => f.path);
191
204
  const allFilePaths = new Set(allFileList);
192
205
  const parser = await loadParser();
193
206
  const logSkipped = isVerboseIngestionEnabled();
194
207
  const skippedByLang = logSkipped ? new Map() : null;
195
208
  const resolveCache = new Map();
196
209
  // Pre-compute normalized file list once (forward slashes)
197
- const normalizedFileList = allFileList.map(p => p.replace(/\\/g, '/'));
210
+ const normalizedFileList = allFileList.map((p) => p.replace(/\\/g, '/'));
198
211
  // Build suffix index for O(1) lookups
199
212
  const index = buildSuffixIndex(normalizedFileList, allFileList);
200
213
  // Track import statistics
201
214
  let totalImportsFound = 0;
202
215
  // Load language-specific configs once before the file loop
203
216
  const configs = await loadImportConfigs(repoRoot || '');
204
- const resolveCtx = { allFilePaths, allFileList, normalizedFileList, index, resolveCache, configs };
217
+ const resolveCtx = {
218
+ allFilePaths,
219
+ allFileList,
220
+ normalizedFileList,
221
+ index,
222
+ resolveCache,
223
+ configs,
224
+ };
205
225
  const { addImportEdge, addImportGraphEdge, getResolvedCount } = createImportEdgeHelpers(graph, importMap);
206
226
  for (let i = 0; i < files.length; i++) {
207
227
  const file = files[i];
@@ -229,7 +249,9 @@ export const processImports = async (graph, files, astCache, ctx, onProgress, re
229
249
  let wasReparsed = false;
230
250
  if (!tree) {
231
251
  try {
232
- tree = parser.parse(file.content, undefined, { bufferSize: getTreeSitterBufferSize(file.content.length) });
252
+ tree = parser.parse(file.content, undefined, {
253
+ bufferSize: getTreeSitterBufferSize(file.content.length),
254
+ });
233
255
  }
234
256
  catch (parseError) {
235
257
  continue;
@@ -260,9 +282,9 @@ export const processImports = async (graph, files, astCache, ctx, onProgress, re
260
282
  tree.delete?.();
261
283
  continue;
262
284
  }
263
- matches.forEach(match => {
285
+ matches.forEach((match) => {
264
286
  const captureMap = {};
265
- match.captures.forEach(c => captureMap[c.name] = c.node);
287
+ match.captures.forEach((c) => (captureMap[c.name] = c.node));
266
288
  if (captureMap['import']) {
267
289
  const sourceNode = captureMap['import.source'];
268
290
  if (!sourceNode) {
@@ -313,11 +335,18 @@ export const processImportsFromExtracted = async (graph, files, extractedImports
313
335
  const packageMap = ctx.packageMap;
314
336
  const namedImportMap = ctx.namedImportMap;
315
337
  const moduleAliasMap = ctx.moduleAliasMap;
316
- const importCtx = prebuiltCtx ?? buildImportResolutionContext(files.map(f => f.path));
338
+ const importCtx = prebuiltCtx ?? buildImportResolutionContext(files.map((f) => f.path));
317
339
  const { allFilePaths, allFileList, normalizedFileList, index, resolveCache } = importCtx;
318
340
  let totalImportsFound = 0;
319
341
  const configs = await loadImportConfigs(repoRoot || '');
320
- const resolveCtx = { allFilePaths, allFileList, normalizedFileList, index, resolveCache, configs };
342
+ const resolveCtx = {
343
+ allFilePaths,
344
+ allFileList,
345
+ normalizedFileList,
346
+ index,
347
+ resolveCache,
348
+ configs,
349
+ };
321
350
  const { addImportEdge, addImportGraphEdge, getResolvedCount } = createImportEdgeHelpers(graph, importMap);
322
351
  // Group by file for progress reporting (users see file count, not import count)
323
352
  const importsByFile = new Map();
@@ -345,7 +374,7 @@ export const processImportsFromExtracted = async (graph, files, extractedImports
345
374
  }
346
375
  }
347
376
  onProgress?.(totalFiles, totalFiles);
348
- wireImplicitImports(files.map(f => f.path), importMap, addImportEdge, configs);
377
+ wireImplicitImports(files.map((f) => f.path), importMap, addImportEdge, configs);
349
378
  if (isDev) {
350
379
  console.log(`📊 Import processing (fast path): ${getResolvedCount()}/${totalImportsFound} imports resolved to graph edges`);
351
380
  }
@@ -3,7 +3,7 @@
3
3
  * Handles using-directive resolution via .csproj root namespace stripping.
4
4
  */
5
5
  import { suffixResolve } from './utils.js';
6
- import { SupportedLanguages } from '../../../config/supported-languages.js';
6
+ import { SupportedLanguages } from 'gitnexus-shared';
7
7
  import { resolveStandard } from './standard.js';
8
8
  /**
9
9
  * Resolve a C# using-directive import path to matching .cs files (low-level helper).
@@ -26,7 +26,9 @@ export function resolveCSharpImportInternal(importPath, csharpConfigs, normalize
26
26
  continue;
27
27
  }
28
28
  const dirPrefix = config.projectDir
29
- ? (relative ? config.projectDir + '/' + relative : config.projectDir)
29
+ ? relative
30
+ ? config.projectDir + '/' + relative
31
+ : config.projectDir
30
32
  : relative;
31
33
  // 1. Try as single file: relative.cs (e.g., "Models/DlqMessage.cs")
32
34
  if (relative) {
@@ -101,7 +103,9 @@ export function resolveCSharpNamespaceDir(importPath, csharpConfigs) {
101
103
  continue;
102
104
  }
103
105
  const dirPrefix = config.projectDir
104
- ? (relative ? config.projectDir + '/' + relative : config.projectDir)
106
+ ? relative
107
+ ? config.projectDir + '/' + relative
108
+ : config.projectDir
105
109
  : relative;
106
110
  if (!dirPrefix)
107
111
  continue;
@@ -4,7 +4,7 @@
4
4
  * SDK imports (dart:*) and external packages are skipped.
5
5
  */
6
6
  import { resolveStandard } from './standard.js';
7
- import { SupportedLanguages } from '../../../config/supported-languages.js';
7
+ import { SupportedLanguages } from 'gitnexus-shared';
8
8
  export function resolveDartImport(rawImportPath, filePath, ctx) {
9
9
  // Strip surrounding quotes from configurable_uri capture
10
10
  const stripped = rawImportPath.replace(/^['"]|['"]$/g, '');
@@ -2,7 +2,7 @@
2
2
  * Go package import resolution.
3
3
  * Handles Go module path-based package imports.
4
4
  */
5
- import { SupportedLanguages } from '../../../config/supported-languages.js';
5
+ import { SupportedLanguages } from 'gitnexus-shared';
6
6
  import { resolveStandard } from './standard.js';
7
7
  /**
8
8
  * Extract the package directory suffix from a Go import path.
@@ -33,7 +33,9 @@ export function resolveGoPackage(importPath, goModule, normalizedFileList, allFi
33
33
  // Prepend '/' so paths like "internal/auth/service.go" match suffix "/internal/auth/"
34
34
  const normalized = '/' + normalizedFileList[i];
35
35
  // File must be directly in the package directory (not a subdirectory)
36
- if (normalized.includes(pkgSuffix) && normalized.endsWith('.go') && !normalized.endsWith('_test.go')) {
36
+ if (normalized.includes(pkgSuffix) &&
37
+ normalized.endsWith('.go') &&
38
+ !normalized.endsWith('_test.go')) {
37
39
  const afterPkg = normalized.substring(normalized.indexOf(pkgSuffix) + pkgSuffix.length);
38
40
  if (!afterPkg.includes('/')) {
39
41
  matches.push(allFileList[i]);
@@ -2,7 +2,7 @@
2
2
  * JVM import resolution (Java + Kotlin).
3
3
  * Handles wildcard imports, member/static imports, and Kotlin-specific patterns.
4
4
  */
5
- import { SupportedLanguages } from '../../../config/supported-languages.js';
5
+ import { SupportedLanguages } from 'gitnexus-shared';
6
6
  import { resolveStandard } from './standard.js';
7
7
  /** Kotlin file extensions for JVM resolver reuse */
8
8
  export const KOTLIN_EXTENSIONS = ['.kt', '.kts'];
@@ -26,11 +26,11 @@ export function resolveJvmWildcard(importPath, normalizedFileList, allFileList,
26
26
  // "com.example.util.*" -> "com/example/util"
27
27
  const packagePath = importPath.slice(0, -2).replace(/\./g, '/');
28
28
  if (index) {
29
- const candidates = extensions.flatMap(ext => index.getFilesInDir(packagePath, ext));
29
+ const candidates = extensions.flatMap((ext) => index.getFilesInDir(packagePath, ext));
30
30
  // Filter to only direct children (no subdirectories)
31
31
  const packageSuffix = '/' + packagePath + '/';
32
32
  const packagePrefix = packagePath + '/';
33
- return candidates.filter(f => {
33
+ return candidates.filter((f) => {
34
34
  const normalized = f.replace(/\\/g, '/');
35
35
  // Match both nested (src/models/User.kt) and root-level (models/User.kt) packages
36
36
  let afterPkg;
@@ -53,7 +53,7 @@ export function resolveJvmWildcard(importPath, normalizedFileList, allFileList,
53
53
  const matches = [];
54
54
  for (let i = 0; i < normalizedFileList.length; i++) {
55
55
  const normalized = normalizedFileList[i];
56
- if (!extensions.some(ext => normalized.endsWith(ext)))
56
+ if (!extensions.some((ext) => normalized.endsWith(ext)))
57
57
  continue;
58
58
  // Match both nested (src/models/User.kt) and root-level (models/User.kt) packages
59
59
  let afterPackage = null;
@@ -50,9 +50,7 @@ export function resolvePhpImportInternal(importPath, composerConfig, allFiles, n
50
50
  // 2. Function/constant fallback: strip last segment (symbol name), scan namespace directory.
51
51
  // e.g. App\Models\getUser → directory app/Models/, find first .php file in that dir.
52
52
  const lastSlash = remainder.lastIndexOf('/');
53
- const nsDir = lastSlash >= 0
54
- ? dirPrefix + '/' + remainder.slice(0, lastSlash)
55
- : dirPrefix;
53
+ const nsDir = lastSlash >= 0 ? dirPrefix + '/' + remainder.slice(0, lastSlash) : dirPrefix;
56
54
  // Prefer SuffixIndex directory lookup (O(log n + matches)) over linear scan
57
55
  if (index) {
58
56
  const candidates = index.getFilesInDir(nsDir, '.php');
@@ -62,7 +60,9 @@ export function resolvePhpImportInternal(importPath, composerConfig, allFiles, n
62
60
  // Fallback: linear scan (only when SuffixIndex unavailable)
63
61
  const nsDirPrefix = nsDir.endsWith('/') ? nsDir : nsDir + '/';
64
62
  for (const f of allFiles) {
65
- if (f.startsWith(nsDirPrefix) && f.endsWith('.php') && !f.slice(nsDirPrefix.length).includes('/')) {
63
+ if (f.startsWith(nsDirPrefix) &&
64
+ f.endsWith('.php') &&
65
+ !f.slice(nsDirPrefix.length).includes('/')) {
66
66
  return f;
67
67
  }
68
68
  }
@@ -3,7 +3,7 @@
3
3
  * Import system spec: PEP 302 (original), PEP 451 (current).
4
4
  */
5
5
  import { tryResolveWithExtensions } from './utils.js';
6
- import { SupportedLanguages } from '../../../config/supported-languages.js';
6
+ import { SupportedLanguages } from 'gitnexus-shared';
7
7
  import { resolveStandard } from './standard.js';
8
8
  /**
9
9
  * Resolve a Python import to a file path (low-level helper).
@@ -2,7 +2,7 @@
2
2
  * Rust module import resolution.
3
3
  * Handles crate::, super::, self:: prefix paths and :: separators.
4
4
  */
5
- import { SupportedLanguages } from '../../../config/supported-languages.js';
5
+ import { SupportedLanguages } from 'gitnexus-shared';
6
6
  import { resolveStandard } from './standard.js';
7
7
  /**
8
8
  * Resolve Rust use-path to a file (low-level helper).
@@ -78,7 +78,10 @@ export function resolveRustImport(rawImportPath, filePath, ctx) {
78
78
  // Top-level grouped: use {crate::a, crate::b}
79
79
  if (rawImportPath.startsWith('{') && rawImportPath.endsWith('}')) {
80
80
  const inner = rawImportPath.slice(1, -1);
81
- const parts = inner.split(',').map(p => p.trim()).filter(Boolean);
81
+ const parts = inner
82
+ .split(',')
83
+ .map((p) => p.trim())
84
+ .filter(Boolean);
82
85
  const resolved = [];
83
86
  for (const part of parts) {
84
87
  const r = resolveRustImportInternal(filePath, part, ctx.allFilePaths);
@@ -92,7 +95,10 @@ export function resolveRustImport(rawImportPath, filePath, ctx) {
92
95
  if (braceIdx !== -1 && rawImportPath.endsWith('}')) {
93
96
  const pathPrefix = rawImportPath.substring(0, braceIdx);
94
97
  const braceContent = rawImportPath.substring(braceIdx + 3, rawImportPath.length - 1);
95
- const items = braceContent.split(',').map(s => s.trim()).filter(Boolean);
98
+ const items = braceContent
99
+ .split(',')
100
+ .map((s) => s.trim())
101
+ .filter(Boolean);
96
102
  const resolved = [];
97
103
  for (const item of items) {
98
104
  // Handle `use crate::models::{User, Repo as R}` — strip alias for resolution
@@ -4,7 +4,7 @@
4
4
  * Used as the fallback when language-specific resolvers don't match.
5
5
  */
6
6
  import type { SuffixIndex } from './utils.js';
7
- import { SupportedLanguages } from '../../../config/supported-languages.js';
7
+ import { SupportedLanguages } from 'gitnexus-shared';
8
8
  import type { ImportResult, ImportResolverFn, ResolveCtx } from './types.js';
9
9
  import type { TsconfigPaths } from '../language-config.js';
10
10
  /** Max entries in the resolve cache. Beyond this, entries are evicted.
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { tryResolveWithExtensions, suffixResolve } from './utils.js';
7
7
  import { resolveRustImportInternal } from './rust.js';
8
- import { SupportedLanguages } from '../../../config/supported-languages.js';
8
+ import { SupportedLanguages } from 'gitnexus-shared';
9
9
  /** Max entries in the resolve cache. Beyond this, entries are evicted.
10
10
  * 100K entries ≈ 15MB — covers the most common import patterns. */
11
11
  export const RESOLVE_CACHE_CAP = 100_000;
@@ -76,7 +76,10 @@ export const resolveImportPath = (currentFile, importPath, allFiles, allFileList
76
76
  // Rust grouped-import blocks in processImports / processImportsBatch). This fallback
77
77
  // handles any path that reaches resolveImportPath directly.
78
78
  const inner = importPath.slice(1, -1);
79
- const parts = inner.split(',').map(p => p.trim()).filter(Boolean);
79
+ const parts = inner
80
+ .split(',')
81
+ .map((p) => p.trim())
82
+ .filter(Boolean);
80
83
  for (const part of parts) {
81
84
  const partResult = resolveRustImportInternal(currentFile, part, allFiles);
82
85
  if (partResult)
@@ -114,9 +117,7 @@ export const resolveImportPath = (currentFile, importPath, allFiles, allFileList
114
117
  }
115
118
  // C/C++ includes use actual file paths (e.g. "animal.h") — don't convert dots to slashes
116
119
  const isCpp = language === SupportedLanguages.C || language === SupportedLanguages.CPlusPlus;
117
- const pathLike = importPath.includes('/') || isCpp
118
- ? importPath
119
- : importPath.replace(/\./g, '/');
120
+ const pathLike = importPath.includes('/') || isCpp ? importPath : importPath.replace(/\./g, '/');
120
121
  const pathParts = pathLike.split('/').filter(Boolean);
121
122
  const resolved = suffixResolve(pathParts, normalizedFileList, allFileList, index);
122
123
  return cache(resolved);
@@ -11,7 +11,8 @@ export function resolveSwiftImport(rawImportPath, _filePath, ctx) {
11
11
  const dirPrefix = targetDir + '/';
12
12
  const files = [];
13
13
  for (let i = 0; i < ctx.normalizedFileList.length; i++) {
14
- if (ctx.normalizedFileList[i].startsWith(dirPrefix) && ctx.normalizedFileList[i].endsWith('.swift')) {
14
+ if (ctx.normalizedFileList[i].startsWith(dirPrefix) &&
15
+ ctx.normalizedFileList[i].endsWith('.swift')) {
15
16
  files.push(ctx.allFileList[i]);
16
17
  }
17
18
  }
@@ -6,23 +6,41 @@
6
6
  export const EXTENSIONS = [
7
7
  '',
8
8
  // TypeScript/JavaScript
9
- '.tsx', '.ts', '.jsx', '.js', '/index.tsx', '/index.ts', '/index.jsx', '/index.js',
9
+ '.tsx',
10
+ '.ts',
11
+ '.jsx',
12
+ '.js',
13
+ '/index.tsx',
14
+ '/index.ts',
15
+ '/index.jsx',
16
+ '/index.js',
10
17
  // Python
11
- '.py', '/__init__.py',
18
+ '.py',
19
+ '/__init__.py',
12
20
  // Java
13
21
  '.java',
14
22
  // Kotlin
15
- '.kt', '.kts',
23
+ '.kt',
24
+ '.kts',
16
25
  // C/C++
17
- '.c', '.h', '.cpp', '.hpp', '.cc', '.cxx', '.hxx', '.hh',
26
+ '.c',
27
+ '.h',
28
+ '.cpp',
29
+ '.hpp',
30
+ '.cc',
31
+ '.cxx',
32
+ '.hxx',
33
+ '.hh',
18
34
  // C#
19
35
  '.cs',
20
36
  // Go
21
37
  '.go',
22
38
  // Rust
23
- '.rs', '/mod.rs',
39
+ '.rs',
40
+ '/mod.rs',
24
41
  // PHP
25
- '.php', '.phtml',
42
+ '.php',
43
+ '.phtml',
26
44
  // Swift
27
45
  '.swift',
28
46
  // Ruby
@@ -119,7 +137,8 @@ export function suffixResolve(pathParts, normalizedFileList, allFileList, index)
119
137
  for (const ext of EXTENSIONS) {
120
138
  const suffixWithExt = suffix + ext;
121
139
  const suffixPattern = '/' + suffixWithExt;
122
- const matchIdx = normalizedFileList.findIndex(filePath => filePath.endsWith(suffixPattern) || filePath.toLowerCase().endsWith(suffixPattern.toLowerCase()));
140
+ const matchIdx = normalizedFileList.findIndex((filePath) => filePath.endsWith(suffixPattern) ||
141
+ filePath.toLowerCase().endsWith(suffixPattern.toLowerCase()));
123
142
  if (matchIdx !== -1) {
124
143
  return allFileList[matchIdx];
125
144
  }
@@ -107,7 +107,10 @@ export async function loadCSharpProjectConfig(repoRoot) {
107
107
  for (const entry of entries) {
108
108
  if (entry.isDirectory() && depth < maxDepth) {
109
109
  // Skip common non-project directories
110
- if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === 'bin' || entry.name === 'obj')
110
+ if (entry.name === 'node_modules' ||
111
+ entry.name === '.git' ||
112
+ entry.name === 'bin' ||
113
+ entry.name === 'obj')
111
114
  continue;
112
115
  scanQueue.push({ dir: path.join(dir, entry.name), depth: depth + 1 });
113
116
  }
@@ -116,9 +119,7 @@ export async function loadCSharpProjectConfig(repoRoot) {
116
119
  const csprojPath = path.join(dir, entry.name);
117
120
  const content = await fs.readFile(csprojPath, 'utf-8');
118
121
  const nsMatch = content.match(/<RootNamespace>\s*([^<]+)\s*<\/RootNamespace>/);
119
- const rootNamespace = nsMatch
120
- ? nsMatch[1].trim()
121
- : entry.name.replace(/\.csproj$/, '');
122
+ const rootNamespace = nsMatch ? nsMatch[1].trim() : entry.name.replace(/\.csproj$/, '');
122
123
  const projectDir = path.relative(repoRoot, dir).replace(/\\/g, '/');
123
124
  configs.push({ rootNamespace, projectDir });
124
125
  if (isDev) {
@@ -8,15 +8,16 @@
8
8
  * The providers table in `languages/index.ts` uses `satisfies Record<SupportedLanguages, LanguageProvider>`
9
9
  * so adding a language to the enum without creating a provider is a compiler error.
10
10
  */
11
- import type { SupportedLanguages } from '../../config/supported-languages.js';
11
+ import type { SupportedLanguages } from 'gitnexus-shared';
12
12
  import type { LanguageTypeConfig } from './type-extractors/types.js';
13
13
  import type { CallRouter } from './call-routing.js';
14
14
  import type { ExportChecker } from './export-detection.js';
15
15
  import type { FieldExtractor } from './field-extractor.js';
16
+ import type { MethodExtractor } from './method-types.js';
16
17
  import type { ImportResolverFn } from './import-resolvers/types.js';
17
18
  import type { NamedBindingExtractorFn } from './named-bindings/types.js';
18
19
  import type { SyntaxNode } from './utils/ast-helpers.js';
19
- import type { NodeLabel } from '../graph/types.js';
20
+ import type { NodeLabel } from 'gitnexus-shared';
20
21
  /** Tree-sitter query captures: capture name → AST node (or undefined if not captured). */
21
22
  export type CaptureMap = Record<string, SyntaxNode | undefined>;
22
23
  /** MRO strategy for multiple inheritance resolution. */
@@ -96,6 +97,10 @@ interface LanguageProviderConfig {
96
97
  * declarations. Produces FieldInfo[] with name, type, visibility, static,
97
98
  * readonly metadata. Default: undefined (no field extraction). */
98
99
  readonly fieldExtractor?: FieldExtractor;
100
+ /** Method extractor for extracting method/function definitions from class/struct/interface
101
+ * declarations. Produces MethodInfo[] with name, parameters, visibility, isAbstract,
102
+ * isFinal, annotations metadata. Default: undefined (no method extraction). */
103
+ readonly methodExtractor?: MethodExtractor;
99
104
  /** Extract a semantic description for a definition node (e.g., PHP Eloquent
100
105
  * property arrays, relation method descriptions).
101
106
  * Default: undefined (no description extraction). */