gitnexus 1.4.0 → 1.4.1

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 (131) hide show
  1. package/README.md +194 -214
  2. package/dist/cli/ai-context.d.ts +1 -2
  3. package/dist/cli/ai-context.js +90 -117
  4. package/dist/cli/analyze.d.ts +0 -2
  5. package/dist/cli/analyze.js +2 -20
  6. package/dist/cli/index.js +25 -17
  7. package/dist/cli/setup.js +19 -17
  8. package/dist/core/augmentation/engine.js +20 -20
  9. package/dist/core/embeddings/embedding-pipeline.js +26 -26
  10. package/dist/core/graph/types.d.ts +2 -5
  11. package/dist/core/ingestion/ast-cache.js +2 -3
  12. package/dist/core/ingestion/call-processor.d.ts +5 -5
  13. package/dist/core/ingestion/call-processor.js +258 -173
  14. package/dist/core/ingestion/cluster-enricher.js +16 -16
  15. package/dist/core/ingestion/entry-point-scoring.d.ts +1 -2
  16. package/dist/core/ingestion/entry-point-scoring.js +22 -81
  17. package/dist/core/ingestion/framework-detection.d.ts +1 -5
  18. package/dist/core/ingestion/framework-detection.js +8 -39
  19. package/dist/core/ingestion/heritage-processor.d.ts +4 -13
  20. package/dist/core/ingestion/heritage-processor.js +28 -92
  21. package/dist/core/ingestion/import-processor.d.ts +19 -17
  22. package/dist/core/ingestion/import-processor.js +695 -170
  23. package/dist/core/ingestion/parsing-processor.d.ts +10 -1
  24. package/dist/core/ingestion/parsing-processor.js +177 -41
  25. package/dist/core/ingestion/pipeline.js +26 -49
  26. package/dist/core/ingestion/process-processor.js +1 -2
  27. package/dist/core/ingestion/symbol-table.d.ts +1 -12
  28. package/dist/core/ingestion/symbol-table.js +12 -19
  29. package/dist/core/ingestion/tree-sitter-queries.d.ts +11 -11
  30. package/dist/core/ingestion/tree-sitter-queries.js +485 -590
  31. package/dist/core/ingestion/utils.d.ts +0 -67
  32. package/dist/core/ingestion/utils.js +9 -692
  33. package/dist/core/ingestion/workers/parse-worker.d.ts +3 -20
  34. package/dist/core/ingestion/workers/parse-worker.js +345 -84
  35. package/dist/core/ingestion/workers/worker-pool.js +0 -8
  36. package/dist/core/kuzu/csv-generator.js +3 -19
  37. package/dist/core/kuzu/kuzu-adapter.js +19 -14
  38. package/dist/core/kuzu/schema.d.ts +3 -3
  39. package/dist/core/kuzu/schema.js +288 -303
  40. package/dist/core/search/bm25-index.js +6 -7
  41. package/dist/core/search/hybrid-search.js +3 -3
  42. package/dist/core/wiki/diagrams.d.ts +27 -0
  43. package/dist/core/wiki/diagrams.js +163 -0
  44. package/dist/core/wiki/generator.d.ts +50 -2
  45. package/dist/core/wiki/generator.js +548 -49
  46. package/dist/core/wiki/graph-queries.d.ts +42 -0
  47. package/dist/core/wiki/graph-queries.js +276 -97
  48. package/dist/core/wiki/html-viewer.js +192 -192
  49. package/dist/core/wiki/llm-client.js +73 -11
  50. package/dist/core/wiki/prompts.d.ts +52 -8
  51. package/dist/core/wiki/prompts.js +200 -86
  52. package/dist/mcp/core/kuzu-adapter.d.ts +3 -1
  53. package/dist/mcp/core/kuzu-adapter.js +44 -13
  54. package/dist/mcp/local/local-backend.js +128 -128
  55. package/dist/mcp/resources.js +42 -42
  56. package/dist/mcp/server.js +19 -18
  57. package/dist/mcp/tools.js +104 -103
  58. package/hooks/claude/gitnexus-hook.cjs +155 -238
  59. package/hooks/claude/pre-tool-use.sh +79 -79
  60. package/hooks/claude/session-start.sh +42 -42
  61. package/package.json +96 -96
  62. package/scripts/patch-tree-sitter-swift.cjs +74 -74
  63. package/skills/gitnexus-cli.md +82 -82
  64. package/skills/gitnexus-debugging.md +89 -89
  65. package/skills/gitnexus-exploring.md +78 -78
  66. package/skills/gitnexus-guide.md +64 -64
  67. package/skills/gitnexus-impact-analysis.md +97 -97
  68. package/skills/gitnexus-pr-review.md +163 -163
  69. package/skills/gitnexus-refactoring.md +121 -121
  70. package/vendor/leiden/index.cjs +355 -355
  71. package/vendor/leiden/utils.cjs +392 -392
  72. package/dist/cli/lazy-action.d.ts +0 -6
  73. package/dist/cli/lazy-action.js +0 -18
  74. package/dist/cli/skill-gen.d.ts +0 -26
  75. package/dist/cli/skill-gen.js +0 -549
  76. package/dist/core/ingestion/constants.d.ts +0 -16
  77. package/dist/core/ingestion/constants.js +0 -16
  78. package/dist/core/ingestion/export-detection.d.ts +0 -18
  79. package/dist/core/ingestion/export-detection.js +0 -230
  80. package/dist/core/ingestion/language-config.d.ts +0 -46
  81. package/dist/core/ingestion/language-config.js +0 -167
  82. package/dist/core/ingestion/mro-processor.d.ts +0 -45
  83. package/dist/core/ingestion/mro-processor.js +0 -369
  84. package/dist/core/ingestion/named-binding-extraction.d.ts +0 -61
  85. package/dist/core/ingestion/named-binding-extraction.js +0 -363
  86. package/dist/core/ingestion/resolvers/csharp.d.ts +0 -22
  87. package/dist/core/ingestion/resolvers/csharp.js +0 -109
  88. package/dist/core/ingestion/resolvers/go.d.ts +0 -19
  89. package/dist/core/ingestion/resolvers/go.js +0 -42
  90. package/dist/core/ingestion/resolvers/index.d.ts +0 -16
  91. package/dist/core/ingestion/resolvers/index.js +0 -11
  92. package/dist/core/ingestion/resolvers/jvm.d.ts +0 -23
  93. package/dist/core/ingestion/resolvers/jvm.js +0 -87
  94. package/dist/core/ingestion/resolvers/php.d.ts +0 -15
  95. package/dist/core/ingestion/resolvers/php.js +0 -35
  96. package/dist/core/ingestion/resolvers/rust.d.ts +0 -15
  97. package/dist/core/ingestion/resolvers/rust.js +0 -73
  98. package/dist/core/ingestion/resolvers/standard.d.ts +0 -28
  99. package/dist/core/ingestion/resolvers/standard.js +0 -145
  100. package/dist/core/ingestion/resolvers/utils.d.ts +0 -33
  101. package/dist/core/ingestion/resolvers/utils.js +0 -120
  102. package/dist/core/ingestion/symbol-resolver.d.ts +0 -32
  103. package/dist/core/ingestion/symbol-resolver.js +0 -83
  104. package/dist/core/ingestion/type-env.d.ts +0 -27
  105. package/dist/core/ingestion/type-env.js +0 -86
  106. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +0 -2
  107. package/dist/core/ingestion/type-extractors/c-cpp.js +0 -60
  108. package/dist/core/ingestion/type-extractors/csharp.d.ts +0 -2
  109. package/dist/core/ingestion/type-extractors/csharp.js +0 -89
  110. package/dist/core/ingestion/type-extractors/go.d.ts +0 -2
  111. package/dist/core/ingestion/type-extractors/go.js +0 -105
  112. package/dist/core/ingestion/type-extractors/index.d.ts +0 -21
  113. package/dist/core/ingestion/type-extractors/index.js +0 -29
  114. package/dist/core/ingestion/type-extractors/jvm.d.ts +0 -3
  115. package/dist/core/ingestion/type-extractors/jvm.js +0 -121
  116. package/dist/core/ingestion/type-extractors/php.d.ts +0 -2
  117. package/dist/core/ingestion/type-extractors/php.js +0 -31
  118. package/dist/core/ingestion/type-extractors/python.d.ts +0 -2
  119. package/dist/core/ingestion/type-extractors/python.js +0 -41
  120. package/dist/core/ingestion/type-extractors/rust.d.ts +0 -2
  121. package/dist/core/ingestion/type-extractors/rust.js +0 -39
  122. package/dist/core/ingestion/type-extractors/shared.d.ts +0 -17
  123. package/dist/core/ingestion/type-extractors/shared.js +0 -97
  124. package/dist/core/ingestion/type-extractors/swift.d.ts +0 -2
  125. package/dist/core/ingestion/type-extractors/swift.js +0 -43
  126. package/dist/core/ingestion/type-extractors/types.d.ts +0 -14
  127. package/dist/core/ingestion/type-extractors/types.js +0 -1
  128. package/dist/core/ingestion/type-extractors/typescript.d.ts +0 -2
  129. package/dist/core/ingestion/type-extractors/typescript.js +0 -46
  130. package/dist/mcp/compatible-stdio-transport.d.ts +0 -25
  131. package/dist/mcp/compatible-stdio-transport.js +0 -200
@@ -10,7 +10,16 @@ export interface WorkerExtractedData {
10
10
  heritage: ExtractedHeritage[];
11
11
  routes: ExtractedRoute[];
12
12
  }
13
- export { isNodeExported } from './export-detection.js';
13
+ /**
14
+ * Check if a symbol (function, class, etc.) is exported/public
15
+ * Handles all 9 supported languages with explicit logic
16
+ *
17
+ * @param node - The AST node for the symbol name
18
+ * @param name - The symbol name
19
+ * @param language - The programming language
20
+ * @returns true if the symbol is exported/public
21
+ */
22
+ export declare const isNodeExported: (node: any, name: string, language: string) => boolean;
14
23
  export declare const processParsing: (graph: KnowledgeGraph, files: {
15
24
  path: string;
16
25
  content: string;
@@ -2,13 +2,178 @@ import Parser from 'tree-sitter';
2
2
  import { loadParser, loadLanguage } from '../tree-sitter/parser-loader.js';
3
3
  import { LANGUAGE_QUERIES } from './tree-sitter-queries.js';
4
4
  import { generateId } from '../../lib/utils.js';
5
- import { getLanguageFromFilename, yieldToEventLoop, getDefinitionNodeFromCaptures, findEnclosingClassId, extractMethodSignature } from './utils.js';
6
- import { isNodeExported } from './export-detection.js';
5
+ import { findSiblingChild, getLanguageFromFilename, yieldToEventLoop } from './utils.js';
7
6
  import { detectFrameworkFromAST } from './framework-detection.js';
8
- import { getTreeSitterBufferSize, TREE_SITTER_MAX_BUFFER } from './constants.js';
9
- // isNodeExported imported from ./export-detection.js (shared module)
10
- // Re-export for backward compatibility with any external consumers
11
- export { isNodeExported } from './export-detection.js';
7
+ const DEFINITION_CAPTURE_KEYS = [
8
+ 'definition.function',
9
+ 'definition.class',
10
+ 'definition.interface',
11
+ 'definition.method',
12
+ 'definition.struct',
13
+ 'definition.enum',
14
+ 'definition.namespace',
15
+ 'definition.module',
16
+ 'definition.trait',
17
+ 'definition.impl',
18
+ 'definition.type',
19
+ 'definition.const',
20
+ 'definition.static',
21
+ 'definition.typedef',
22
+ 'definition.macro',
23
+ 'definition.union',
24
+ 'definition.property',
25
+ 'definition.record',
26
+ 'definition.delegate',
27
+ 'definition.annotation',
28
+ 'definition.constructor',
29
+ 'definition.template',
30
+ ];
31
+ const getDefinitionNodeFromCaptures = (captureMap) => {
32
+ for (const key of DEFINITION_CAPTURE_KEYS) {
33
+ if (captureMap[key])
34
+ return captureMap[key];
35
+ }
36
+ return null;
37
+ };
38
+ // ============================================================================
39
+ // EXPORT DETECTION - Language-specific visibility detection
40
+ // ============================================================================
41
+ /**
42
+ * Check if a symbol (function, class, etc.) is exported/public
43
+ * Handles all 9 supported languages with explicit logic
44
+ *
45
+ * @param node - The AST node for the symbol name
46
+ * @param name - The symbol name
47
+ * @param language - The programming language
48
+ * @returns true if the symbol is exported/public
49
+ */
50
+ export const isNodeExported = (node, name, language) => {
51
+ let current = node;
52
+ switch (language) {
53
+ // JavaScript/TypeScript: Check for export keyword in ancestors
54
+ case 'javascript':
55
+ case 'typescript':
56
+ while (current) {
57
+ const type = current.type;
58
+ if (type === 'export_statement' ||
59
+ type === 'export_specifier' ||
60
+ type === 'lexical_declaration' && current.parent?.type === 'export_statement') {
61
+ return true;
62
+ }
63
+ // Also check if text starts with 'export '
64
+ if (current.text?.startsWith('export ')) {
65
+ return true;
66
+ }
67
+ current = current.parent;
68
+ }
69
+ return false;
70
+ // Python: Public if no leading underscore (convention)
71
+ case 'python':
72
+ return !name.startsWith('_');
73
+ // Java: Check for 'public' modifier
74
+ // In tree-sitter Java, modifiers are siblings of the name node, not parents
75
+ case 'java':
76
+ while (current) {
77
+ // Check if this node or any sibling is a 'modifiers' node containing 'public'
78
+ if (current.parent) {
79
+ const parent = current.parent;
80
+ // Check all children of the parent for modifiers
81
+ for (let i = 0; i < parent.childCount; i++) {
82
+ const child = parent.child(i);
83
+ if (child?.type === 'modifiers' && child.text?.includes('public')) {
84
+ return true;
85
+ }
86
+ }
87
+ // Also check if the parent's text starts with 'public' (fallback)
88
+ if (parent.type === 'method_declaration' || parent.type === 'constructor_declaration') {
89
+ if (parent.text?.trimStart().startsWith('public')) {
90
+ return true;
91
+ }
92
+ }
93
+ }
94
+ current = current.parent;
95
+ }
96
+ return false;
97
+ // C#: Check for 'public' modifier in ancestors
98
+ case 'csharp':
99
+ while (current) {
100
+ if (current.type === 'modifier' || current.type === 'modifiers') {
101
+ if (current.text?.includes('public'))
102
+ return true;
103
+ }
104
+ current = current.parent;
105
+ }
106
+ return false;
107
+ // Go: Uppercase first letter = exported
108
+ case 'go':
109
+ if (name.length === 0)
110
+ return false;
111
+ const first = name[0];
112
+ // Must be uppercase letter (not a number or symbol)
113
+ return first === first.toUpperCase() && first !== first.toLowerCase();
114
+ // Rust: Check for 'pub' visibility modifier
115
+ case 'rust':
116
+ while (current) {
117
+ if (current.type === 'visibility_modifier') {
118
+ if (current.text?.includes('pub'))
119
+ return true;
120
+ }
121
+ current = current.parent;
122
+ }
123
+ return false;
124
+ // Kotlin: Default visibility is public (unlike Java)
125
+ // visibility_modifier is inside modifiers, a sibling of the name node within the declaration
126
+ case 'kotlin':
127
+ while (current) {
128
+ if (current.parent) {
129
+ const visMod = findSiblingChild(current.parent, 'modifiers', 'visibility_modifier');
130
+ if (visMod) {
131
+ const text = visMod.text;
132
+ if (text === 'private' || text === 'internal' || text === 'protected')
133
+ return false;
134
+ if (text === 'public')
135
+ return true;
136
+ }
137
+ }
138
+ current = current.parent;
139
+ }
140
+ // No visibility modifier = public (Kotlin default)
141
+ return true;
142
+ // C/C++: No native export concept at language level
143
+ // Entry points will be detected via name patterns (main, etc.)
144
+ case 'c':
145
+ case 'cpp':
146
+ return false;
147
+ // Swift: Check for 'public' or 'open' access modifiers
148
+ case 'swift':
149
+ while (current) {
150
+ if (current.type === 'modifiers' || current.type === 'visibility_modifier') {
151
+ const text = current.text || '';
152
+ if (text.includes('public') || text.includes('open'))
153
+ return true;
154
+ }
155
+ current = current.parent;
156
+ }
157
+ return false;
158
+ // PHP: Check for visibility modifier or top-level scope
159
+ case 'php':
160
+ while (current) {
161
+ if (current.type === 'class_declaration' ||
162
+ current.type === 'interface_declaration' ||
163
+ current.type === 'trait_declaration' ||
164
+ current.type === 'enum_declaration') {
165
+ return true;
166
+ }
167
+ if (current.type === 'visibility_modifier') {
168
+ return current.text === 'public';
169
+ }
170
+ current = current.parent;
171
+ }
172
+ return true; // Top-level functions are globally accessible
173
+ default:
174
+ return false;
175
+ }
176
+ };
12
177
  // ============================================================================
13
178
  // Worker-based parallel parsing
14
179
  // ============================================================================
@@ -44,10 +209,7 @@ const processParsingWithWorkers = async (graph, files, symbolTable, astCache, wo
44
209
  graph.addRelationship(rel);
45
210
  }
46
211
  for (const sym of result.symbols) {
47
- symbolTable.add(sym.filePath, sym.name, sym.nodeId, sym.type, {
48
- parameterCount: sym.parameterCount,
49
- ownerId: sym.ownerId,
50
- });
212
+ symbolTable.add(sym.filePath, sym.name, sym.nodeId, sym.type);
51
213
  }
52
214
  allImports.push(...result.imports);
53
215
  allCalls.push(...result.calls);
@@ -72,8 +234,8 @@ const processParsingSequential = async (graph, files, symbolTable, astCache, onF
72
234
  const language = getLanguageFromFilename(file.path);
73
235
  if (!language)
74
236
  continue;
75
- // Skip files larger than the max tree-sitter buffer (32 MB)
76
- if (file.content.length > TREE_SITTER_MAX_BUFFER)
237
+ // Skip very large files they can crash tree-sitter or cause OOM
238
+ if (file.content.length > 512 * 1024)
77
239
  continue;
78
240
  try {
79
241
  await loadLanguage(language, file.path);
@@ -83,7 +245,7 @@ const processParsingSequential = async (graph, files, symbolTable, astCache, onF
83
245
  }
84
246
  let tree;
85
247
  try {
86
- tree = parser.parse(file.content, undefined, { bufferSize: getTreeSitterBufferSize(file.content.length) });
248
+ tree = parser.parse(file.content, undefined, { bufferSize: 1024 * 256 });
87
249
  }
88
250
  catch (parseError) {
89
251
  console.warn(`Skipping unparseable file: ${file.path}`);
@@ -168,15 +330,11 @@ const processParsingSequential = async (graph, files, symbolTable, astCache, onF
168
330
  nodeLabel = 'Template';
169
331
  const definitionNodeForRange = getDefinitionNodeFromCaptures(captureMap);
170
332
  const startLine = definitionNodeForRange ? definitionNodeForRange.startPosition.row : (nameNode ? nameNode.startPosition.row : 0);
171
- const nodeId = generateId(nodeLabel, `${file.path}:${nodeName}`);
333
+ const nodeId = generateId(nodeLabel, `${file.path}:${nodeName}:${startLine}`);
172
334
  const definitionNode = getDefinitionNodeFromCaptures(captureMap);
173
335
  const frameworkHint = definitionNode
174
336
  ? detectFrameworkFromAST(language, (definitionNode.text || '').slice(0, 300))
175
337
  : null;
176
- // Extract method signature for Method/Constructor nodes
177
- const methodSig = (nodeLabel === 'Function' || nodeLabel === 'Method' || nodeLabel === 'Constructor')
178
- ? extractMethodSignature(definitionNode)
179
- : undefined;
180
338
  const node = {
181
339
  id: nodeId,
182
340
  label: nodeLabel,
@@ -191,21 +349,10 @@ const processParsingSequential = async (graph, files, symbolTable, astCache, onF
191
349
  astFrameworkMultiplier: frameworkHint.entryPointMultiplier,
192
350
  astFrameworkReason: frameworkHint.reason,
193
351
  } : {}),
194
- ...(methodSig ? {
195
- parameterCount: methodSig.parameterCount,
196
- returnType: methodSig.returnType,
197
- } : {}),
198
352
  },
199
353
  };
200
354
  graph.addNode(node);
201
- // Compute enclosing class for Method/Constructor/Property/Function — used for both ownerId and HAS_METHOD
202
- // Function is included because Kotlin/Rust/Python capture class methods as Function nodes
203
- const needsOwner = nodeLabel === 'Method' || nodeLabel === 'Constructor' || nodeLabel === 'Property' || nodeLabel === 'Function';
204
- const enclosingClassId = needsOwner ? findEnclosingClassId(nameNode || definitionNodeForRange, file.path) : null;
205
- symbolTable.add(file.path, nodeName, nodeId, nodeLabel, {
206
- parameterCount: methodSig?.parameterCount,
207
- ownerId: enclosingClassId ?? undefined,
208
- });
355
+ symbolTable.add(file.path, nodeName, nodeId, nodeLabel);
209
356
  const fileId = generateId('File', file.path);
210
357
  const relId = generateId('DEFINES', `${fileId}->${nodeId}`);
211
358
  const relationship = {
@@ -217,17 +364,6 @@ const processParsingSequential = async (graph, files, symbolTable, astCache, onF
217
364
  reason: '',
218
365
  };
219
366
  graph.addRelationship(relationship);
220
- // ── HAS_METHOD: link method/constructor/property to enclosing class ──
221
- if (enclosingClassId) {
222
- graph.addRelationship({
223
- id: generateId('HAS_METHOD', `${enclosingClassId}->${nodeId}`),
224
- sourceId: enclosingClassId,
225
- targetId: nodeId,
226
- type: 'HAS_METHOD',
227
- confidence: 1.0,
228
- reason: '',
229
- });
230
- }
231
367
  });
232
368
  }
233
369
  };
@@ -1,10 +1,9 @@
1
1
  import { createKnowledgeGraph } from '../graph/graph.js';
2
2
  import { processStructure } from './structure-processor.js';
3
3
  import { processParsing } from './parsing-processor.js';
4
- import { processImports, processImportsFromExtracted, createImportMap, createPackageMap, createNamedImportMap, buildImportResolutionContext } from './import-processor.js';
4
+ import { processImports, processImportsFromExtracted, createImportMap, buildImportResolutionContext } from './import-processor.js';
5
5
  import { processCalls, processCallsFromExtracted, processRoutesFromExtracted } from './call-processor.js';
6
6
  import { processHeritage, processHeritageFromExtracted } from './heritage-processor.js';
7
- import { computeMRO } from './mro-processor.js';
8
7
  import { processCommunities } from './community-processor.js';
9
8
  import { processProcesses } from './process-processor.js';
10
9
  import { createSymbolTable } from './symbol-table.js';
@@ -13,9 +12,6 @@ import { walkRepositoryPaths, readFileContents } from './filesystem-walker.js';
13
12
  import { getLanguageFromFilename } from './utils.js';
14
13
  import { isLanguageAvailable } from '../tree-sitter/parser-loader.js';
15
14
  import { createWorkerPool } from './workers/worker-pool.js';
16
- import fs from 'node:fs';
17
- import path from 'node:path';
18
- import { fileURLToPath, pathToFileURL } from 'node:url';
19
15
  const isDev = process.env.NODE_ENV === 'development';
20
16
  /** Max bytes of source content to load per parse chunk. Each chunk's source +
21
17
  * parsed ASTs + extracted records + worker serialization overhead all live in
@@ -29,8 +25,6 @@ export const runPipelineFromRepo = async (repoPath, onProgress) => {
29
25
  const symbolTable = createSymbolTable();
30
26
  let astCache = createASTCache(AST_CACHE_CAP);
31
27
  const importMap = createImportMap();
32
- const packageMap = createPackageMap();
33
- const namedImportMap = createNamedImportMap();
34
28
  const cleanup = () => {
35
29
  astCache.clear();
36
30
  symbolTable.clear();
@@ -93,14 +87,6 @@ export const runPipelineFromRepo = async (repoPath, onProgress) => {
93
87
  console.warn(`Skipping ${count} ${lang} file(s) — ${lang} parser not available (native binding may not have built). Try: npm rebuild tree-sitter-${lang}`);
94
88
  }
95
89
  const totalParseable = parseableScanned.length;
96
- if (totalParseable === 0) {
97
- onProgress({
98
- phase: 'parsing',
99
- percent: 82,
100
- message: 'No parseable files found — skipping parsing phase',
101
- stats: { filesProcessed: 0, totalFiles: 0, nodesCreated: graph.nodeCount },
102
- });
103
- }
104
90
  // Build byte-budget chunks
105
91
  const chunks = [];
106
92
  let currentChunk = [];
@@ -130,21 +116,11 @@ export const runPipelineFromRepo = async (repoPath, onProgress) => {
130
116
  // Create worker pool once, reuse across chunks
131
117
  let workerPool;
132
118
  try {
133
- let workerUrl = new URL('./workers/parse-worker.js', import.meta.url);
134
- // When running under vitest, import.meta.url points to src/ where no .js exists.
135
- // Fall back to the compiled dist/ worker so the pool can spawn real worker threads.
136
- const thisDir = fileURLToPath(new URL('.', import.meta.url));
137
- if (!fs.existsSync(fileURLToPath(workerUrl))) {
138
- const distWorker = path.resolve(thisDir, '..', '..', '..', 'dist', 'core', 'ingestion', 'workers', 'parse-worker.js');
139
- if (fs.existsSync(distWorker)) {
140
- workerUrl = pathToFileURL(distWorker);
141
- }
142
- }
119
+ const workerUrl = new URL('./workers/parse-worker.js', import.meta.url);
143
120
  workerPool = createWorkerPool(workerUrl);
144
121
  }
145
122
  catch (err) {
146
- if (isDev)
147
- console.warn('Worker pool creation failed, using sequential fallback:', err.message);
123
+ // Worker pool creation failed — sequential fallback
148
124
  }
149
125
  let filesParsedSoFar = 0;
150
126
  // AST cache sized for one chunk (sequential fallback uses it for import/call/heritage)
@@ -181,18 +157,22 @@ export const runPipelineFromRepo = async (repoPath, onProgress) => {
181
157
  }, workerPool);
182
158
  if (chunkWorkerData) {
183
159
  // Imports
184
- await processImportsFromExtracted(graph, allPathObjects, chunkWorkerData.imports, importMap, undefined, repoPath, importCtx, packageMap, namedImportMap);
185
- // Calls + Heritage + Routes — resolve in parallel (no shared mutable state between them)
186
- // This is safe because each writes disjoint relationship types into idempotent id-keyed Maps,
187
- // and the single-threaded event loop prevents races between synchronous addRelationship calls.
188
- await Promise.all([
189
- processCallsFromExtracted(graph, chunkWorkerData.calls, symbolTable, importMap, packageMap, undefined, namedImportMap),
190
- processHeritageFromExtracted(graph, chunkWorkerData.heritage, symbolTable, importMap, packageMap),
191
- processRoutesFromExtracted(graph, chunkWorkerData.routes ?? [], symbolTable, importMap, packageMap),
192
- ]);
160
+ await processImportsFromExtracted(graph, allPathObjects, chunkWorkerData.imports, importMap, undefined, repoPath, importCtx);
161
+ // Calls — resolve immediately, then free the array
162
+ if (chunkWorkerData.calls.length > 0) {
163
+ await processCallsFromExtracted(graph, chunkWorkerData.calls, symbolTable, importMap);
164
+ }
165
+ // Heritage resolve immediately, then free
166
+ if (chunkWorkerData.heritage.length > 0) {
167
+ await processHeritageFromExtracted(graph, chunkWorkerData.heritage, symbolTable);
168
+ }
169
+ // Routes — resolve immediately (Laravel route→controller CALLS edges)
170
+ if (chunkWorkerData.routes && chunkWorkerData.routes.length > 0) {
171
+ await processRoutesFromExtracted(graph, chunkWorkerData.routes, symbolTable, importMap);
172
+ }
193
173
  }
194
174
  else {
195
- await processImports(graph, chunkFiles, astCache, importMap, undefined, repoPath, allPaths, packageMap, namedImportMap);
175
+ await processImports(graph, chunkFiles, astCache, importMap, undefined, repoPath, allPaths);
196
176
  sequentialChunkPaths.push(chunkPaths);
197
177
  }
198
178
  filesParsedSoFar += chunkFiles.length;
@@ -211,8 +191,8 @@ export const runPipelineFromRepo = async (repoPath, onProgress) => {
211
191
  .filter(p => chunkContents.has(p))
212
192
  .map(p => ({ path: p, content: chunkContents.get(p) }));
213
193
  astCache = createASTCache(chunkFiles.length);
214
- await processCalls(graph, chunkFiles, astCache, symbolTable, importMap, packageMap, undefined, namedImportMap);
215
- await processHeritage(graph, chunkFiles, astCache, symbolTable, importMap, packageMap);
194
+ await processCalls(graph, chunkFiles, astCache, symbolTable, importMap);
195
+ await processHeritage(graph, chunkFiles, astCache, symbolTable);
216
196
  astCache.clear();
217
197
  }
218
198
  // Free import resolution context — suffix index + resolve cache no longer needed
@@ -221,16 +201,13 @@ export const runPipelineFromRepo = async (repoPath, onProgress) => {
221
201
  importCtx.resolveCache.clear();
222
202
  importCtx.suffixIndex = null;
223
203
  importCtx.normalizedFileList = null;
224
- // ── Phase 4.5: Method Resolution Order ──────────────────────────────
225
- onProgress({
226
- phase: 'parsing',
227
- percent: 81,
228
- message: 'Computing method resolution order...',
229
- stats: { filesProcessed: totalFiles, totalFiles, nodesCreated: graph.nodeCount },
230
- });
231
- const mroResult = computeMRO(graph);
232
- if (isDev && mroResult.entries.length > 0) {
233
- console.log(`🔀 MRO: ${mroResult.entries.length} classes analyzed, ${mroResult.ambiguityCount} ambiguities found, ${mroResult.overrideEdges} OVERRIDES edges`);
204
+ if (isDev) {
205
+ let importsCount = 0;
206
+ for (const r of graph.iterRelationships()) {
207
+ if (r.type === 'IMPORTS')
208
+ importsCount++;
209
+ }
210
+ console.log(`📊 Pipeline: graph has ${importsCount} IMPORTS, ${graph.relationshipCount} total relationships`);
234
211
  }
235
212
  // ── Phase 5: Communities ───────────────────────────────────────────
236
213
  onProgress({
@@ -10,7 +10,6 @@
10
10
  * Processes help agents understand how features work through the codebase.
11
11
  */
12
12
  import { calculateEntryPointScore, isTestFile } from './entry-point-scoring.js';
13
- import { SupportedLanguages } from '../../config/supported-languages.js';
14
13
  const isDev = process.env.NODE_ENV === 'development';
15
14
  const DEFAULT_CONFIG = {
16
15
  maxTraceDepth: 10,
@@ -179,7 +178,7 @@ const findEntryPoints = (graph, reverseCallsEdges, callsEdges) => {
179
178
  if (callees.length === 0)
180
179
  continue;
181
180
  // Calculate entry point score using new scoring system
182
- const { score: baseScore, reasons } = calculateEntryPointScore(node.properties.name, node.properties.language ?? SupportedLanguages.JavaScript, node.properties.isExported ?? false, callers.length, callees.length, filePath // Pass filePath for framework detection
181
+ const { score: baseScore, reasons } = calculateEntryPointScore(node.properties.name, node.properties.language || 'javascript', node.properties.isExported ?? false, callers.length, callees.length, filePath // Pass filePath for framework detection
183
182
  );
184
183
  let score = baseScore;
185
184
  const astFrameworkMultiplier = node.properties.astFrameworkMultiplier ?? 1.0;
@@ -2,28 +2,17 @@ export interface SymbolDefinition {
2
2
  nodeId: string;
3
3
  filePath: string;
4
4
  type: string;
5
- parameterCount?: number;
6
- /** Links Method/Constructor to owning Class/Struct/Trait nodeId */
7
- ownerId?: string;
8
5
  }
9
6
  export interface SymbolTable {
10
7
  /**
11
8
  * Register a new symbol definition
12
9
  */
13
- add: (filePath: string, name: string, nodeId: string, type: string, metadata?: {
14
- parameterCount?: number;
15
- ownerId?: string;
16
- }) => void;
10
+ add: (filePath: string, name: string, nodeId: string, type: string) => void;
17
11
  /**
18
12
  * High Confidence: Look for a symbol specifically inside a file
19
13
  * Returns the Node ID if found
20
14
  */
21
15
  lookupExact: (filePath: string, name: string) => string | undefined;
22
- /**
23
- * High Confidence: Look for a symbol in a specific file, returning full definition.
24
- * Includes type information needed for heritage resolution (Class vs Interface).
25
- */
26
- lookupExactFull: (filePath: string, name: string) => SymbolDefinition | undefined;
27
16
  /**
28
17
  * Low Confidence: Look for a symbol anywhere in the project
29
18
  * Used when imports are missing or for framework magic
@@ -1,34 +1,27 @@
1
1
  export const createSymbolTable = () => {
2
- // 1. File-Specific Index stores full SymbolDefinition for O(1) lookupExactFull
3
- // Structure: FilePath -> (SymbolName -> SymbolDefinition)
2
+ // 1. File-Specific Index (The "Good" one)
3
+ // Structure: FilePath -> (SymbolName -> NodeID)
4
4
  const fileIndex = new Map();
5
5
  // 2. Global Reverse Index (The "Backup")
6
6
  // Structure: SymbolName -> [List of Definitions]
7
7
  const globalIndex = new Map();
8
- const add = (filePath, name, nodeId, type, metadata) => {
9
- const def = {
10
- nodeId,
11
- filePath,
12
- type,
13
- ...(metadata?.parameterCount !== undefined ? { parameterCount: metadata.parameterCount } : {}),
14
- ...(metadata?.ownerId !== undefined ? { ownerId: metadata.ownerId } : {}),
15
- };
16
- // A. Add to File Index (shared reference — zero additional memory)
8
+ const add = (filePath, name, nodeId, type) => {
9
+ // A. Add to File Index
17
10
  if (!fileIndex.has(filePath)) {
18
11
  fileIndex.set(filePath, new Map());
19
12
  }
20
- fileIndex.get(filePath).set(name, def);
21
- // B. Add to Global Index (same object reference)
13
+ fileIndex.get(filePath).set(name, nodeId);
14
+ // B. Add to Global Index
22
15
  if (!globalIndex.has(name)) {
23
16
  globalIndex.set(name, []);
24
17
  }
25
- globalIndex.get(name).push(def);
18
+ globalIndex.get(name).push({ nodeId, filePath, type });
26
19
  };
27
20
  const lookupExact = (filePath, name) => {
28
- return fileIndex.get(filePath)?.get(name)?.nodeId;
29
- };
30
- const lookupExactFull = (filePath, name) => {
31
- return fileIndex.get(filePath)?.get(name);
21
+ const fileSymbols = fileIndex.get(filePath);
22
+ if (!fileSymbols)
23
+ return undefined;
24
+ return fileSymbols.get(name);
32
25
  };
33
26
  const lookupFuzzy = (name) => {
34
27
  return globalIndex.get(name) || [];
@@ -41,5 +34,5 @@ export const createSymbolTable = () => {
41
34
  fileIndex.clear();
42
35
  globalIndex.clear();
43
36
  };
44
- return { add, lookupExact, lookupExactFull, lookupFuzzy, getStats, clear };
37
+ return { add, lookupExact, lookupFuzzy, getStats, clear };
45
38
  };