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.
- package/README.md +194 -214
- package/dist/cli/ai-context.d.ts +1 -2
- package/dist/cli/ai-context.js +90 -117
- package/dist/cli/analyze.d.ts +0 -2
- package/dist/cli/analyze.js +2 -20
- package/dist/cli/index.js +25 -17
- package/dist/cli/setup.js +19 -17
- package/dist/core/augmentation/engine.js +20 -20
- package/dist/core/embeddings/embedding-pipeline.js +26 -26
- package/dist/core/graph/types.d.ts +2 -5
- package/dist/core/ingestion/ast-cache.js +2 -3
- package/dist/core/ingestion/call-processor.d.ts +5 -5
- package/dist/core/ingestion/call-processor.js +258 -173
- package/dist/core/ingestion/cluster-enricher.js +16 -16
- package/dist/core/ingestion/entry-point-scoring.d.ts +1 -2
- package/dist/core/ingestion/entry-point-scoring.js +22 -81
- package/dist/core/ingestion/framework-detection.d.ts +1 -5
- package/dist/core/ingestion/framework-detection.js +8 -39
- package/dist/core/ingestion/heritage-processor.d.ts +4 -13
- package/dist/core/ingestion/heritage-processor.js +28 -92
- package/dist/core/ingestion/import-processor.d.ts +19 -17
- package/dist/core/ingestion/import-processor.js +695 -170
- package/dist/core/ingestion/parsing-processor.d.ts +10 -1
- package/dist/core/ingestion/parsing-processor.js +177 -41
- package/dist/core/ingestion/pipeline.js +26 -49
- package/dist/core/ingestion/process-processor.js +1 -2
- package/dist/core/ingestion/symbol-table.d.ts +1 -12
- package/dist/core/ingestion/symbol-table.js +12 -19
- package/dist/core/ingestion/tree-sitter-queries.d.ts +11 -11
- package/dist/core/ingestion/tree-sitter-queries.js +485 -590
- package/dist/core/ingestion/utils.d.ts +0 -67
- package/dist/core/ingestion/utils.js +9 -692
- package/dist/core/ingestion/workers/parse-worker.d.ts +3 -20
- package/dist/core/ingestion/workers/parse-worker.js +345 -84
- package/dist/core/ingestion/workers/worker-pool.js +0 -8
- package/dist/core/kuzu/csv-generator.js +3 -19
- package/dist/core/kuzu/kuzu-adapter.js +19 -14
- package/dist/core/kuzu/schema.d.ts +3 -3
- package/dist/core/kuzu/schema.js +288 -303
- package/dist/core/search/bm25-index.js +6 -7
- package/dist/core/search/hybrid-search.js +3 -3
- package/dist/core/wiki/diagrams.d.ts +27 -0
- package/dist/core/wiki/diagrams.js +163 -0
- package/dist/core/wiki/generator.d.ts +50 -2
- package/dist/core/wiki/generator.js +548 -49
- package/dist/core/wiki/graph-queries.d.ts +42 -0
- package/dist/core/wiki/graph-queries.js +276 -97
- package/dist/core/wiki/html-viewer.js +192 -192
- package/dist/core/wiki/llm-client.js +73 -11
- package/dist/core/wiki/prompts.d.ts +52 -8
- package/dist/core/wiki/prompts.js +200 -86
- package/dist/mcp/core/kuzu-adapter.d.ts +3 -1
- package/dist/mcp/core/kuzu-adapter.js +44 -13
- package/dist/mcp/local/local-backend.js +128 -128
- package/dist/mcp/resources.js +42 -42
- package/dist/mcp/server.js +19 -18
- package/dist/mcp/tools.js +104 -103
- package/hooks/claude/gitnexus-hook.cjs +155 -238
- package/hooks/claude/pre-tool-use.sh +79 -79
- package/hooks/claude/session-start.sh +42 -42
- package/package.json +96 -96
- package/scripts/patch-tree-sitter-swift.cjs +74 -74
- package/skills/gitnexus-cli.md +82 -82
- package/skills/gitnexus-debugging.md +89 -89
- package/skills/gitnexus-exploring.md +78 -78
- package/skills/gitnexus-guide.md +64 -64
- package/skills/gitnexus-impact-analysis.md +97 -97
- package/skills/gitnexus-pr-review.md +163 -163
- package/skills/gitnexus-refactoring.md +121 -121
- package/vendor/leiden/index.cjs +355 -355
- package/vendor/leiden/utils.cjs +392 -392
- package/dist/cli/lazy-action.d.ts +0 -6
- package/dist/cli/lazy-action.js +0 -18
- package/dist/cli/skill-gen.d.ts +0 -26
- package/dist/cli/skill-gen.js +0 -549
- package/dist/core/ingestion/constants.d.ts +0 -16
- package/dist/core/ingestion/constants.js +0 -16
- package/dist/core/ingestion/export-detection.d.ts +0 -18
- package/dist/core/ingestion/export-detection.js +0 -230
- package/dist/core/ingestion/language-config.d.ts +0 -46
- package/dist/core/ingestion/language-config.js +0 -167
- package/dist/core/ingestion/mro-processor.d.ts +0 -45
- package/dist/core/ingestion/mro-processor.js +0 -369
- package/dist/core/ingestion/named-binding-extraction.d.ts +0 -61
- package/dist/core/ingestion/named-binding-extraction.js +0 -363
- package/dist/core/ingestion/resolvers/csharp.d.ts +0 -22
- package/dist/core/ingestion/resolvers/csharp.js +0 -109
- package/dist/core/ingestion/resolvers/go.d.ts +0 -19
- package/dist/core/ingestion/resolvers/go.js +0 -42
- package/dist/core/ingestion/resolvers/index.d.ts +0 -16
- package/dist/core/ingestion/resolvers/index.js +0 -11
- package/dist/core/ingestion/resolvers/jvm.d.ts +0 -23
- package/dist/core/ingestion/resolvers/jvm.js +0 -87
- package/dist/core/ingestion/resolvers/php.d.ts +0 -15
- package/dist/core/ingestion/resolvers/php.js +0 -35
- package/dist/core/ingestion/resolvers/rust.d.ts +0 -15
- package/dist/core/ingestion/resolvers/rust.js +0 -73
- package/dist/core/ingestion/resolvers/standard.d.ts +0 -28
- package/dist/core/ingestion/resolvers/standard.js +0 -145
- package/dist/core/ingestion/resolvers/utils.d.ts +0 -33
- package/dist/core/ingestion/resolvers/utils.js +0 -120
- package/dist/core/ingestion/symbol-resolver.d.ts +0 -32
- package/dist/core/ingestion/symbol-resolver.js +0 -83
- package/dist/core/ingestion/type-env.d.ts +0 -27
- package/dist/core/ingestion/type-env.js +0 -86
- package/dist/core/ingestion/type-extractors/c-cpp.d.ts +0 -2
- package/dist/core/ingestion/type-extractors/c-cpp.js +0 -60
- package/dist/core/ingestion/type-extractors/csharp.d.ts +0 -2
- package/dist/core/ingestion/type-extractors/csharp.js +0 -89
- package/dist/core/ingestion/type-extractors/go.d.ts +0 -2
- package/dist/core/ingestion/type-extractors/go.js +0 -105
- package/dist/core/ingestion/type-extractors/index.d.ts +0 -21
- package/dist/core/ingestion/type-extractors/index.js +0 -29
- package/dist/core/ingestion/type-extractors/jvm.d.ts +0 -3
- package/dist/core/ingestion/type-extractors/jvm.js +0 -121
- package/dist/core/ingestion/type-extractors/php.d.ts +0 -2
- package/dist/core/ingestion/type-extractors/php.js +0 -31
- package/dist/core/ingestion/type-extractors/python.d.ts +0 -2
- package/dist/core/ingestion/type-extractors/python.js +0 -41
- package/dist/core/ingestion/type-extractors/rust.d.ts +0 -2
- package/dist/core/ingestion/type-extractors/rust.js +0 -39
- package/dist/core/ingestion/type-extractors/shared.d.ts +0 -17
- package/dist/core/ingestion/type-extractors/shared.js +0 -97
- package/dist/core/ingestion/type-extractors/swift.d.ts +0 -2
- package/dist/core/ingestion/type-extractors/swift.js +0 -43
- package/dist/core/ingestion/type-extractors/types.d.ts +0 -14
- package/dist/core/ingestion/type-extractors/types.js +0 -1
- package/dist/core/ingestion/type-extractors/typescript.d.ts +0 -2
- package/dist/core/ingestion/type-extractors/typescript.js +0 -46
- package/dist/mcp/compatible-stdio-transport.d.ts +0 -25
- 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
|
-
|
|
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
|
|
6
|
-
import { isNodeExported } from './export-detection.js';
|
|
5
|
+
import { findSiblingChild, getLanguageFromFilename, yieldToEventLoop } from './utils.js';
|
|
7
6
|
import { detectFrameworkFromAST } from './framework-detection.js';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
|
76
|
-
if (file.content.length >
|
|
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:
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
185
|
-
// Calls
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
|
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
|
|
215
|
-
await processHeritage(graph, chunkFiles, astCache, symbolTable
|
|
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
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
|
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
|
|
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
|
|
3
|
-
// Structure: FilePath -> (SymbolName ->
|
|
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
|
|
9
|
-
|
|
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,
|
|
21
|
-
// B. Add to Global Index
|
|
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(
|
|
18
|
+
globalIndex.get(name).push({ nodeId, filePath, type });
|
|
26
19
|
};
|
|
27
20
|
const lookupExact = (filePath, name) => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return
|
|
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,
|
|
37
|
+
return { add, lookupExact, lookupFuzzy, getStats, clear };
|
|
45
38
|
};
|