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
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { SupportedLanguages } from '../../../config/supported-languages.js';
|
|
2
1
|
interface ParsedNode {
|
|
3
2
|
id: string;
|
|
4
3
|
label: string;
|
|
@@ -7,20 +6,18 @@ interface ParsedNode {
|
|
|
7
6
|
filePath: string;
|
|
8
7
|
startLine: number;
|
|
9
8
|
endLine: number;
|
|
10
|
-
language:
|
|
9
|
+
language: string;
|
|
11
10
|
isExported: boolean;
|
|
12
11
|
astFrameworkMultiplier?: number;
|
|
13
12
|
astFrameworkReason?: string;
|
|
14
13
|
description?: string;
|
|
15
|
-
parameterCount?: number;
|
|
16
|
-
returnType?: string;
|
|
17
14
|
};
|
|
18
15
|
}
|
|
19
16
|
interface ParsedRelationship {
|
|
20
17
|
id: string;
|
|
21
18
|
sourceId: string;
|
|
22
19
|
targetId: string;
|
|
23
|
-
type: 'DEFINES'
|
|
20
|
+
type: 'DEFINES';
|
|
24
21
|
confidence: number;
|
|
25
22
|
reason: string;
|
|
26
23
|
}
|
|
@@ -29,31 +26,17 @@ interface ParsedSymbol {
|
|
|
29
26
|
name: string;
|
|
30
27
|
nodeId: string;
|
|
31
28
|
type: string;
|
|
32
|
-
parameterCount?: number;
|
|
33
|
-
ownerId?: string;
|
|
34
29
|
}
|
|
35
30
|
export interface ExtractedImport {
|
|
36
31
|
filePath: string;
|
|
37
32
|
rawImportPath: string;
|
|
38
|
-
language:
|
|
39
|
-
/** Named bindings from the import (e.g., import {User as U} → [{local:'U', exported:'User'}]) */
|
|
40
|
-
namedBindings?: {
|
|
41
|
-
local: string;
|
|
42
|
-
exported: string;
|
|
43
|
-
}[];
|
|
33
|
+
language: string;
|
|
44
34
|
}
|
|
45
35
|
export interface ExtractedCall {
|
|
46
36
|
filePath: string;
|
|
47
37
|
calledName: string;
|
|
48
38
|
/** generateId of enclosing function, or generateId('File', filePath) for top-level */
|
|
49
39
|
sourceId: string;
|
|
50
|
-
argCount?: number;
|
|
51
|
-
/** Discriminates free function calls from member/constructor calls */
|
|
52
|
-
callForm?: 'free' | 'member' | 'constructor';
|
|
53
|
-
/** Simple identifier of the receiver for member calls (e.g., 'user' in user.save()) */
|
|
54
|
-
receiverName?: string;
|
|
55
|
-
/** Resolved type name of the receiver (e.g., 'User' for user.save() when user: User) */
|
|
56
|
-
receiverTypeName?: string;
|
|
57
40
|
}
|
|
58
41
|
export interface ExtractedHeritage {
|
|
59
42
|
filePath: string;
|
|
@@ -14,7 +14,6 @@ import PHP from 'tree-sitter-php';
|
|
|
14
14
|
import { createRequire } from 'node:module';
|
|
15
15
|
import { SupportedLanguages } from '../../../config/supported-languages.js';
|
|
16
16
|
import { LANGUAGE_QUERIES } from '../tree-sitter-queries.js';
|
|
17
|
-
import { getTreeSitterBufferSize, TREE_SITTER_MAX_BUFFER } from '../constants.js';
|
|
18
17
|
// tree-sitter-swift is an optionalDependency — may not be installed
|
|
19
18
|
const _require = createRequire(import.meta.url);
|
|
20
19
|
let Swift = null;
|
|
@@ -22,13 +21,9 @@ try {
|
|
|
22
21
|
Swift = _require('tree-sitter-swift');
|
|
23
22
|
}
|
|
24
23
|
catch { }
|
|
25
|
-
import {
|
|
26
|
-
import { buildTypeEnv, lookupTypeEnv } from '../type-env.js';
|
|
27
|
-
import { isNodeExported } from '../export-detection.js';
|
|
24
|
+
import { findSiblingChild, getLanguageFromFilename } from '../utils.js';
|
|
28
25
|
import { detectFrameworkFromAST } from '../framework-detection.js';
|
|
29
26
|
import { generateId } from '../../../lib/utils.js';
|
|
30
|
-
import { extractNamedBindings } from '../named-binding-extraction.js';
|
|
31
|
-
import { appendKotlinWildcard } from '../resolvers/index.js';
|
|
32
27
|
// ============================================================================
|
|
33
28
|
// Worker-local parser + language map
|
|
34
29
|
// ============================================================================
|
|
@@ -57,24 +52,307 @@ const setLanguage = (language, filePath) => {
|
|
|
57
52
|
throw new Error(`Unsupported language: ${language}`);
|
|
58
53
|
parser.setLanguage(lang);
|
|
59
54
|
};
|
|
60
|
-
//
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// Export detection (copied — needs AST parent traversal, can't cross threads)
|
|
57
|
+
// ============================================================================
|
|
58
|
+
const isNodeExported = (node, name, language) => {
|
|
59
|
+
let current = node;
|
|
60
|
+
switch (language) {
|
|
61
|
+
case 'javascript':
|
|
62
|
+
case 'typescript':
|
|
63
|
+
while (current) {
|
|
64
|
+
const type = current.type;
|
|
65
|
+
if (type === 'export_statement' ||
|
|
66
|
+
type === 'export_specifier' ||
|
|
67
|
+
type === 'lexical_declaration' && current.parent?.type === 'export_statement') {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
if (current.text?.startsWith('export ')) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
current = current.parent;
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
case 'python':
|
|
77
|
+
return !name.startsWith('_');
|
|
78
|
+
case 'java':
|
|
79
|
+
while (current) {
|
|
80
|
+
if (current.parent) {
|
|
81
|
+
const parent = current.parent;
|
|
82
|
+
for (let i = 0; i < parent.childCount; i++) {
|
|
83
|
+
const child = parent.child(i);
|
|
84
|
+
if (child?.type === 'modifiers' && child.text?.includes('public')) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
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
|
+
case 'csharp':
|
|
98
|
+
while (current) {
|
|
99
|
+
if (current.type === 'modifier' || current.type === 'modifiers') {
|
|
100
|
+
if (current.text?.includes('public'))
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
current = current.parent;
|
|
104
|
+
}
|
|
105
|
+
return false;
|
|
106
|
+
case 'go':
|
|
107
|
+
if (name.length === 0)
|
|
108
|
+
return false;
|
|
109
|
+
const first = name[0];
|
|
110
|
+
return first === first.toUpperCase() && first !== first.toLowerCase();
|
|
111
|
+
case 'rust':
|
|
112
|
+
while (current) {
|
|
113
|
+
if (current.type === 'visibility_modifier') {
|
|
114
|
+
if (current.text?.includes('pub'))
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
current = current.parent;
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
// Kotlin: Default visibility is public (unlike Java)
|
|
121
|
+
// visibility_modifier is inside modifiers, a sibling of the name node within the declaration
|
|
122
|
+
case 'kotlin':
|
|
123
|
+
while (current) {
|
|
124
|
+
if (current.parent) {
|
|
125
|
+
const visMod = findSiblingChild(current.parent, 'modifiers', 'visibility_modifier');
|
|
126
|
+
if (visMod) {
|
|
127
|
+
const text = visMod.text;
|
|
128
|
+
if (text === 'private' || text === 'internal' || text === 'protected')
|
|
129
|
+
return false;
|
|
130
|
+
if (text === 'public')
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
current = current.parent;
|
|
135
|
+
}
|
|
136
|
+
// No visibility modifier = public (Kotlin default)
|
|
137
|
+
return true;
|
|
138
|
+
case 'c':
|
|
139
|
+
case 'cpp':
|
|
140
|
+
return false;
|
|
141
|
+
case 'php':
|
|
142
|
+
// Top-level classes/interfaces/traits are always accessible
|
|
143
|
+
// Methods/properties are exported only if they have 'public' modifier
|
|
144
|
+
while (current) {
|
|
145
|
+
if (current.type === 'class_declaration' ||
|
|
146
|
+
current.type === 'interface_declaration' ||
|
|
147
|
+
current.type === 'trait_declaration' ||
|
|
148
|
+
current.type === 'enum_declaration') {
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
if (current.type === 'visibility_modifier') {
|
|
152
|
+
return current.text === 'public';
|
|
153
|
+
}
|
|
154
|
+
current = current.parent;
|
|
155
|
+
}
|
|
156
|
+
// Top-level functions (no parent class) are globally accessible
|
|
157
|
+
return true;
|
|
158
|
+
case 'swift':
|
|
159
|
+
while (current) {
|
|
160
|
+
if (current.type === 'modifiers' || current.type === 'visibility_modifier') {
|
|
161
|
+
const text = current.text || '';
|
|
162
|
+
if (text.includes('public') || text.includes('open'))
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
current = current.parent;
|
|
166
|
+
}
|
|
167
|
+
return false;
|
|
168
|
+
default:
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
61
172
|
// ============================================================================
|
|
62
173
|
// Enclosing function detection (for call extraction)
|
|
63
174
|
// ============================================================================
|
|
175
|
+
const FUNCTION_NODE_TYPES = new Set([
|
|
176
|
+
'function_declaration', 'arrow_function', 'function_expression',
|
|
177
|
+
'method_definition', 'generator_function_declaration',
|
|
178
|
+
'function_definition', 'async_function_declaration', 'async_arrow_function',
|
|
179
|
+
'method_declaration', 'constructor_declaration',
|
|
180
|
+
'local_function_statement', 'function_item', 'impl_item',
|
|
181
|
+
// Kotlin
|
|
182
|
+
'lambda_literal',
|
|
183
|
+
// PHP
|
|
184
|
+
'anonymous_function',
|
|
185
|
+
// Swift initializers/deinitializers
|
|
186
|
+
'init_declaration', 'deinit_declaration',
|
|
187
|
+
]);
|
|
64
188
|
/** Walk up AST to find enclosing function, return its generateId or null for top-level */
|
|
65
189
|
const findEnclosingFunctionId = (node, filePath) => {
|
|
66
190
|
let current = node.parent;
|
|
67
191
|
while (current) {
|
|
68
192
|
if (FUNCTION_NODE_TYPES.has(current.type)) {
|
|
69
|
-
|
|
193
|
+
let funcName = null;
|
|
194
|
+
let label = 'Function';
|
|
195
|
+
if (current.type === 'init_declaration' || current.type === 'deinit_declaration') {
|
|
196
|
+
const funcName = current.type === 'init_declaration' ? 'init' : 'deinit';
|
|
197
|
+
const label = 'Constructor';
|
|
198
|
+
const startLine = current.startPosition?.row ?? 0;
|
|
199
|
+
return generateId(label, `${filePath}:${funcName}:${startLine}`);
|
|
200
|
+
}
|
|
201
|
+
if (['function_declaration', 'function_definition', 'async_function_declaration',
|
|
202
|
+
'generator_function_declaration', 'function_item'].includes(current.type)) {
|
|
203
|
+
const nameNode = current.childForFieldName?.('name') ||
|
|
204
|
+
current.children?.find((c) => c.type === 'identifier' || c.type === 'property_identifier');
|
|
205
|
+
funcName = nameNode?.text;
|
|
206
|
+
}
|
|
207
|
+
else if (current.type === 'impl_item') {
|
|
208
|
+
const funcItem = current.children?.find((c) => c.type === 'function_item');
|
|
209
|
+
if (funcItem) {
|
|
210
|
+
const nameNode = funcItem.childForFieldName?.('name') ||
|
|
211
|
+
funcItem.children?.find((c) => c.type === 'identifier');
|
|
212
|
+
funcName = nameNode?.text;
|
|
213
|
+
label = 'Method';
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
else if (current.type === 'method_definition') {
|
|
217
|
+
const nameNode = current.childForFieldName?.('name') ||
|
|
218
|
+
current.children?.find((c) => c.type === 'property_identifier');
|
|
219
|
+
funcName = nameNode?.text;
|
|
220
|
+
label = 'Method';
|
|
221
|
+
}
|
|
222
|
+
else if (current.type === 'method_declaration' || current.type === 'constructor_declaration') {
|
|
223
|
+
const nameNode = current.childForFieldName?.('name') ||
|
|
224
|
+
current.children?.find((c) => c.type === 'identifier');
|
|
225
|
+
funcName = nameNode?.text;
|
|
226
|
+
label = 'Method';
|
|
227
|
+
}
|
|
228
|
+
else if (current.type === 'arrow_function' || current.type === 'function_expression') {
|
|
229
|
+
const parent = current.parent;
|
|
230
|
+
if (parent?.type === 'variable_declarator') {
|
|
231
|
+
const nameNode = parent.childForFieldName?.('name') ||
|
|
232
|
+
parent.children?.find((c) => c.type === 'identifier');
|
|
233
|
+
funcName = nameNode?.text;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
70
236
|
if (funcName) {
|
|
71
|
-
|
|
237
|
+
const startLine = current.startPosition?.row ?? 0;
|
|
238
|
+
return generateId(label, `${filePath}:${funcName}:${startLine}`);
|
|
72
239
|
}
|
|
73
240
|
}
|
|
74
241
|
current = current.parent;
|
|
75
242
|
}
|
|
76
243
|
return null;
|
|
77
244
|
};
|
|
245
|
+
const BUILT_INS = new Set([
|
|
246
|
+
// JavaScript/TypeScript
|
|
247
|
+
'console', 'log', 'warn', 'error', 'info', 'debug',
|
|
248
|
+
'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval',
|
|
249
|
+
'parseInt', 'parseFloat', 'isNaN', 'isFinite',
|
|
250
|
+
'encodeURI', 'decodeURI', 'encodeURIComponent', 'decodeURIComponent',
|
|
251
|
+
'JSON', 'parse', 'stringify',
|
|
252
|
+
'Object', 'Array', 'String', 'Number', 'Boolean', 'Symbol', 'BigInt',
|
|
253
|
+
'Map', 'Set', 'WeakMap', 'WeakSet',
|
|
254
|
+
'Promise', 'resolve', 'reject', 'then', 'catch', 'finally',
|
|
255
|
+
'Math', 'Date', 'RegExp', 'Error',
|
|
256
|
+
'require', 'import', 'export', 'fetch', 'Response', 'Request',
|
|
257
|
+
'useState', 'useEffect', 'useCallback', 'useMemo', 'useRef', 'useContext',
|
|
258
|
+
'useReducer', 'useLayoutEffect', 'useImperativeHandle', 'useDebugValue',
|
|
259
|
+
'createElement', 'createContext', 'createRef', 'forwardRef', 'memo', 'lazy',
|
|
260
|
+
'map', 'filter', 'reduce', 'forEach', 'find', 'findIndex', 'some', 'every',
|
|
261
|
+
'includes', 'indexOf', 'slice', 'splice', 'concat', 'join', 'split',
|
|
262
|
+
'push', 'pop', 'shift', 'unshift', 'sort', 'reverse',
|
|
263
|
+
'keys', 'values', 'entries', 'assign', 'freeze', 'seal',
|
|
264
|
+
'hasOwnProperty', 'toString', 'valueOf',
|
|
265
|
+
// Python
|
|
266
|
+
'print', 'len', 'range', 'str', 'int', 'float', 'list', 'dict', 'set', 'tuple',
|
|
267
|
+
'open', 'read', 'write', 'close', 'append', 'extend', 'update',
|
|
268
|
+
'super', 'type', 'isinstance', 'issubclass', 'getattr', 'setattr', 'hasattr',
|
|
269
|
+
'enumerate', 'zip', 'sorted', 'reversed', 'min', 'max', 'sum', 'abs',
|
|
270
|
+
// Kotlin stdlib (IMPORTANT: keep in sync with call-processor.ts BUILT_IN_NAMES)
|
|
271
|
+
'println', 'print', 'readLine', 'require', 'requireNotNull', 'check', 'assert', 'lazy', 'error',
|
|
272
|
+
'listOf', 'mapOf', 'setOf', 'mutableListOf', 'mutableMapOf', 'mutableSetOf',
|
|
273
|
+
'arrayOf', 'sequenceOf', 'also', 'apply', 'run', 'with', 'takeIf', 'takeUnless',
|
|
274
|
+
'TODO', 'buildString', 'buildList', 'buildMap', 'buildSet',
|
|
275
|
+
'repeat', 'synchronized',
|
|
276
|
+
// Kotlin coroutine builders & scope functions
|
|
277
|
+
'launch', 'async', 'runBlocking', 'withContext', 'coroutineScope',
|
|
278
|
+
'supervisorScope', 'delay',
|
|
279
|
+
// Kotlin Flow operators
|
|
280
|
+
'flow', 'flowOf', 'collect', 'emit', 'onEach', 'catch',
|
|
281
|
+
'buffer', 'conflate', 'distinctUntilChanged',
|
|
282
|
+
'flatMapLatest', 'flatMapMerge', 'combine',
|
|
283
|
+
'stateIn', 'shareIn', 'launchIn',
|
|
284
|
+
// Kotlin infix stdlib functions
|
|
285
|
+
'to', 'until', 'downTo', 'step',
|
|
286
|
+
// C/C++ standard library
|
|
287
|
+
'printf', 'fprintf', 'sprintf', 'snprintf', 'vprintf', 'vfprintf', 'vsprintf', 'vsnprintf',
|
|
288
|
+
'scanf', 'fscanf', 'sscanf',
|
|
289
|
+
'malloc', 'calloc', 'realloc', 'free', 'memcpy', 'memmove', 'memset', 'memcmp',
|
|
290
|
+
'strlen', 'strcpy', 'strncpy', 'strcat', 'strncat', 'strcmp', 'strncmp', 'strstr', 'strchr', 'strrchr',
|
|
291
|
+
'atoi', 'atol', 'atof', 'strtol', 'strtoul', 'strtoll', 'strtoull', 'strtod',
|
|
292
|
+
'sizeof', 'offsetof', 'typeof',
|
|
293
|
+
'assert', 'abort', 'exit', '_exit',
|
|
294
|
+
'fopen', 'fclose', 'fread', 'fwrite', 'fseek', 'ftell', 'rewind', 'fflush', 'fgets', 'fputs',
|
|
295
|
+
// Linux kernel common macros/helpers (not real call targets)
|
|
296
|
+
'likely', 'unlikely', 'BUG', 'BUG_ON', 'WARN', 'WARN_ON', 'WARN_ONCE',
|
|
297
|
+
'IS_ERR', 'PTR_ERR', 'ERR_PTR', 'IS_ERR_OR_NULL',
|
|
298
|
+
'ARRAY_SIZE', 'container_of', 'list_for_each_entry', 'list_for_each_entry_safe',
|
|
299
|
+
'min', 'max', 'clamp', 'abs', 'swap',
|
|
300
|
+
'pr_info', 'pr_warn', 'pr_err', 'pr_debug', 'pr_notice', 'pr_crit', 'pr_emerg',
|
|
301
|
+
'printk', 'dev_info', 'dev_warn', 'dev_err', 'dev_dbg',
|
|
302
|
+
'GFP_KERNEL', 'GFP_ATOMIC',
|
|
303
|
+
'spin_lock', 'spin_unlock', 'spin_lock_irqsave', 'spin_unlock_irqrestore',
|
|
304
|
+
'mutex_lock', 'mutex_unlock', 'mutex_init',
|
|
305
|
+
'kfree', 'kmalloc', 'kzalloc', 'kcalloc', 'krealloc', 'kvmalloc', 'kvfree',
|
|
306
|
+
'get', 'put',
|
|
307
|
+
// PHP built-ins
|
|
308
|
+
'echo', 'isset', 'empty', 'unset', 'list', 'array', 'compact', 'extract',
|
|
309
|
+
'count', 'strlen', 'strpos', 'strrpos', 'substr', 'strtolower', 'strtoupper', 'trim',
|
|
310
|
+
'ltrim', 'rtrim', 'str_replace', 'str_contains', 'str_starts_with', 'str_ends_with',
|
|
311
|
+
'sprintf', 'vsprintf', 'printf', 'number_format',
|
|
312
|
+
'array_map', 'array_filter', 'array_reduce', 'array_push', 'array_pop', 'array_shift',
|
|
313
|
+
'array_unshift', 'array_slice', 'array_splice', 'array_merge', 'array_keys', 'array_values',
|
|
314
|
+
'array_key_exists', 'in_array', 'array_search', 'array_unique', 'usort', 'rsort',
|
|
315
|
+
'json_encode', 'json_decode', 'serialize', 'unserialize',
|
|
316
|
+
'intval', 'floatval', 'strval', 'boolval', 'is_null', 'is_string', 'is_int', 'is_array',
|
|
317
|
+
'is_object', 'is_numeric', 'is_bool', 'is_float',
|
|
318
|
+
'var_dump', 'print_r', 'var_export',
|
|
319
|
+
'date', 'time', 'strtotime', 'mktime', 'microtime',
|
|
320
|
+
'file_exists', 'file_get_contents', 'file_put_contents', 'is_file', 'is_dir',
|
|
321
|
+
'preg_match', 'preg_match_all', 'preg_replace', 'preg_split',
|
|
322
|
+
'header', 'session_start', 'session_destroy', 'ob_start', 'ob_end_clean', 'ob_get_clean',
|
|
323
|
+
'dd', 'dump',
|
|
324
|
+
// Swift/iOS built-ins and standard library
|
|
325
|
+
'print', 'debugPrint', 'dump', 'fatalError', 'precondition', 'preconditionFailure',
|
|
326
|
+
'assert', 'assertionFailure', 'NSLog',
|
|
327
|
+
'abs', 'min', 'max', 'zip', 'stride', 'sequence', 'repeatElement',
|
|
328
|
+
'swap', 'withUnsafePointer', 'withUnsafeMutablePointer', 'withUnsafeBytes',
|
|
329
|
+
'autoreleasepool', 'unsafeBitCast', 'unsafeDowncast', 'numericCast',
|
|
330
|
+
'type', 'MemoryLayout',
|
|
331
|
+
// Swift collection/string methods (common noise)
|
|
332
|
+
'map', 'flatMap', 'compactMap', 'filter', 'reduce', 'forEach', 'contains',
|
|
333
|
+
'first', 'last', 'prefix', 'suffix', 'dropFirst', 'dropLast',
|
|
334
|
+
'sorted', 'reversed', 'enumerated', 'joined', 'split',
|
|
335
|
+
'append', 'insert', 'remove', 'removeAll', 'removeFirst', 'removeLast',
|
|
336
|
+
'isEmpty', 'count', 'index', 'startIndex', 'endIndex',
|
|
337
|
+
// UIKit/Foundation common methods (noise in call graph)
|
|
338
|
+
'addSubview', 'removeFromSuperview', 'layoutSubviews', 'setNeedsLayout',
|
|
339
|
+
'layoutIfNeeded', 'setNeedsDisplay', 'invalidateIntrinsicContentSize',
|
|
340
|
+
'addTarget', 'removeTarget', 'addGestureRecognizer',
|
|
341
|
+
'addConstraint', 'addConstraints', 'removeConstraint', 'removeConstraints',
|
|
342
|
+
'NSLocalizedString', 'Bundle',
|
|
343
|
+
'reloadData', 'reloadSections', 'reloadRows', 'performBatchUpdates',
|
|
344
|
+
'register', 'dequeueReusableCell', 'dequeueReusableSupplementaryView',
|
|
345
|
+
'beginUpdates', 'endUpdates', 'insertRows', 'deleteRows', 'insertSections', 'deleteSections',
|
|
346
|
+
'present', 'dismiss', 'pushViewController', 'popViewController', 'popToRootViewController',
|
|
347
|
+
'performSegue', 'prepare',
|
|
348
|
+
// GCD / async
|
|
349
|
+
'DispatchQueue', 'async', 'sync', 'asyncAfter',
|
|
350
|
+
'Task', 'withCheckedContinuation', 'withCheckedThrowingContinuation',
|
|
351
|
+
// Combine
|
|
352
|
+
'sink', 'store', 'assign', 'receive', 'subscribe',
|
|
353
|
+
// Notification / KVO
|
|
354
|
+
'addObserver', 'removeObserver', 'post', 'NotificationCenter',
|
|
355
|
+
]);
|
|
78
356
|
// ============================================================================
|
|
79
357
|
// Label detection from capture map
|
|
80
358
|
// ============================================================================
|
|
@@ -130,7 +408,49 @@ const getLabelFromCaptures = (captureMap) => {
|
|
|
130
408
|
return 'Template';
|
|
131
409
|
return 'CodeElement';
|
|
132
410
|
};
|
|
133
|
-
|
|
411
|
+
const DEFINITION_CAPTURE_KEYS = [
|
|
412
|
+
'definition.function',
|
|
413
|
+
'definition.class',
|
|
414
|
+
'definition.interface',
|
|
415
|
+
'definition.method',
|
|
416
|
+
'definition.struct',
|
|
417
|
+
'definition.enum',
|
|
418
|
+
'definition.namespace',
|
|
419
|
+
'definition.module',
|
|
420
|
+
'definition.trait',
|
|
421
|
+
'definition.impl',
|
|
422
|
+
'definition.type',
|
|
423
|
+
'definition.const',
|
|
424
|
+
'definition.static',
|
|
425
|
+
'definition.typedef',
|
|
426
|
+
'definition.macro',
|
|
427
|
+
'definition.union',
|
|
428
|
+
'definition.property',
|
|
429
|
+
'definition.record',
|
|
430
|
+
'definition.delegate',
|
|
431
|
+
'definition.annotation',
|
|
432
|
+
'definition.constructor',
|
|
433
|
+
'definition.template',
|
|
434
|
+
];
|
|
435
|
+
const getDefinitionNodeFromCaptures = (captureMap) => {
|
|
436
|
+
for (const key of DEFINITION_CAPTURE_KEYS) {
|
|
437
|
+
if (captureMap[key])
|
|
438
|
+
return captureMap[key];
|
|
439
|
+
}
|
|
440
|
+
return null;
|
|
441
|
+
};
|
|
442
|
+
/**
|
|
443
|
+
* Append .* to a Kotlin import path if the AST has a wildcard_import sibling node.
|
|
444
|
+
* Pure function — returns a new string without mutating the input.
|
|
445
|
+
*/
|
|
446
|
+
const appendKotlinWildcard = (importPath, importNode) => {
|
|
447
|
+
for (let i = 0; i < importNode.childCount; i++) {
|
|
448
|
+
if (importNode.child(i)?.type === 'wildcard_import') {
|
|
449
|
+
return importPath.endsWith('.*') ? importPath : `${importPath}.*`;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return importPath;
|
|
453
|
+
};
|
|
134
454
|
// ============================================================================
|
|
135
455
|
// Process a batch of files
|
|
136
456
|
// ============================================================================
|
|
@@ -668,38 +988,27 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
668
988
|
const lang = parser.getLanguage();
|
|
669
989
|
query = new Parser.Query(lang, queryString);
|
|
670
990
|
}
|
|
671
|
-
catch
|
|
672
|
-
const message = `Query compilation failed for ${language}: ${err instanceof Error ? err.message : String(err)}`;
|
|
673
|
-
if (parentPort) {
|
|
674
|
-
parentPort.postMessage({ type: 'warning', message });
|
|
675
|
-
}
|
|
676
|
-
else {
|
|
677
|
-
console.warn(message);
|
|
678
|
-
}
|
|
991
|
+
catch {
|
|
679
992
|
return;
|
|
680
993
|
}
|
|
681
994
|
for (const file of files) {
|
|
682
|
-
// Skip files
|
|
683
|
-
if (file.content.length >
|
|
995
|
+
// Skip very large files — they can crash tree-sitter or cause OOM
|
|
996
|
+
if (file.content.length > 512 * 1024)
|
|
684
997
|
continue;
|
|
685
998
|
let tree;
|
|
686
999
|
try {
|
|
687
|
-
tree = parser.parse(file.content, undefined, { bufferSize:
|
|
1000
|
+
tree = parser.parse(file.content, undefined, { bufferSize: 1024 * 256 });
|
|
688
1001
|
}
|
|
689
|
-
catch
|
|
690
|
-
console.warn(`Failed to parse file ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1002
|
+
catch {
|
|
691
1003
|
continue;
|
|
692
1004
|
}
|
|
693
1005
|
result.fileCount++;
|
|
694
1006
|
onFileProcessed?.();
|
|
695
|
-
// Build per-file TypeEnv from explicit type annotations (for receiver resolution)
|
|
696
|
-
const typeEnv = buildTypeEnv(tree, language);
|
|
697
1007
|
let matches;
|
|
698
1008
|
try {
|
|
699
1009
|
matches = query.matches(tree.rootNode);
|
|
700
1010
|
}
|
|
701
|
-
catch
|
|
702
|
-
console.warn(`Query execution failed for ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1011
|
+
catch {
|
|
703
1012
|
continue;
|
|
704
1013
|
}
|
|
705
1014
|
for (const match of matches) {
|
|
@@ -712,12 +1021,10 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
712
1021
|
const rawImportPath = language === SupportedLanguages.Kotlin
|
|
713
1022
|
? appendKotlinWildcard(captureMap['import.source'].text.replace(/['"<>]/g, ''), captureMap['import'])
|
|
714
1023
|
: captureMap['import.source'].text.replace(/['"<>]/g, '');
|
|
715
|
-
const namedBindings = extractNamedBindings(captureMap['import'], language);
|
|
716
1024
|
result.imports.push({
|
|
717
1025
|
filePath: file.path,
|
|
718
1026
|
rawImportPath,
|
|
719
1027
|
language: language,
|
|
720
|
-
...(namedBindings ? { namedBindings } : {}),
|
|
721
1028
|
});
|
|
722
1029
|
continue;
|
|
723
1030
|
}
|
|
@@ -726,22 +1033,11 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
726
1033
|
const callNameNode = captureMap['call.name'];
|
|
727
1034
|
if (callNameNode) {
|
|
728
1035
|
const calledName = callNameNode.text;
|
|
729
|
-
if (!
|
|
1036
|
+
if (!BUILT_INS.has(calledName)) {
|
|
730
1037
|
const callNode = captureMap['call'];
|
|
731
1038
|
const sourceId = findEnclosingFunctionId(callNode, file.path)
|
|
732
1039
|
|| generateId('File', file.path);
|
|
733
|
-
|
|
734
|
-
const receiverName = callForm === 'member' ? extractReceiverName(callNameNode) : undefined;
|
|
735
|
-
const receiverTypeName = receiverName ? lookupTypeEnv(typeEnv, receiverName, callNode) : undefined;
|
|
736
|
-
result.calls.push({
|
|
737
|
-
filePath: file.path,
|
|
738
|
-
calledName,
|
|
739
|
-
sourceId,
|
|
740
|
-
argCount: countCallArguments(callNode),
|
|
741
|
-
...(callForm !== undefined ? { callForm } : {}),
|
|
742
|
-
...(receiverName !== undefined ? { receiverName } : {}),
|
|
743
|
-
...(receiverTypeName !== undefined ? { receiverTypeName } : {}),
|
|
744
|
-
});
|
|
1040
|
+
result.calls.push({ filePath: file.path, calledName, sourceId });
|
|
745
1041
|
}
|
|
746
1042
|
}
|
|
747
1043
|
continue;
|
|
@@ -749,21 +1045,12 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
749
1045
|
// Extract heritage (extends/implements)
|
|
750
1046
|
if (captureMap['heritage.class']) {
|
|
751
1047
|
if (captureMap['heritage.extends']) {
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
&& fieldDecl.childForFieldName('name');
|
|
759
|
-
if (!isNamedField) {
|
|
760
|
-
result.heritage.push({
|
|
761
|
-
filePath: file.path,
|
|
762
|
-
className: captureMap['heritage.class'].text,
|
|
763
|
-
parentName: captureMap['heritage.extends'].text,
|
|
764
|
-
kind: 'extends',
|
|
765
|
-
});
|
|
766
|
-
}
|
|
1048
|
+
result.heritage.push({
|
|
1049
|
+
filePath: file.path,
|
|
1050
|
+
className: captureMap['heritage.class'].text,
|
|
1051
|
+
parentName: captureMap['heritage.extends'].text,
|
|
1052
|
+
kind: 'extends',
|
|
1053
|
+
});
|
|
767
1054
|
}
|
|
768
1055
|
if (captureMap['heritage.implements']) {
|
|
769
1056
|
result.heritage.push({
|
|
@@ -795,7 +1082,7 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
795
1082
|
const nodeName = nameNode ? nameNode.text : 'init';
|
|
796
1083
|
const definitionNode = getDefinitionNodeFromCaptures(captureMap);
|
|
797
1084
|
const startLine = definitionNode ? definitionNode.startPosition.row : (nameNode ? nameNode.startPosition.row : 0);
|
|
798
|
-
const nodeId = generateId(nodeLabel, `${file.path}:${nodeName}`);
|
|
1085
|
+
const nodeId = generateId(nodeLabel, `${file.path}:${nodeName}:${startLine}`);
|
|
799
1086
|
let description;
|
|
800
1087
|
if (language === SupportedLanguages.PHP) {
|
|
801
1088
|
if (nodeLabel === 'Property' && captureMap['definition.property']) {
|
|
@@ -808,13 +1095,6 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
808
1095
|
const frameworkHint = definitionNode
|
|
809
1096
|
? detectFrameworkFromAST(language, (definitionNode.text || '').slice(0, 300))
|
|
810
1097
|
: null;
|
|
811
|
-
let parameterCount;
|
|
812
|
-
let returnType;
|
|
813
|
-
if (nodeLabel === 'Function' || nodeLabel === 'Method' || nodeLabel === 'Constructor') {
|
|
814
|
-
const sig = extractMethodSignature(definitionNode);
|
|
815
|
-
parameterCount = sig.parameterCount;
|
|
816
|
-
returnType = sig.returnType;
|
|
817
|
-
}
|
|
818
1098
|
result.nodes.push({
|
|
819
1099
|
id: nodeId,
|
|
820
1100
|
label: nodeLabel,
|
|
@@ -830,21 +1110,13 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
830
1110
|
astFrameworkReason: frameworkHint.reason,
|
|
831
1111
|
} : {}),
|
|
832
1112
|
...(description !== undefined ? { description } : {}),
|
|
833
|
-
...(parameterCount !== undefined ? { parameterCount } : {}),
|
|
834
|
-
...(returnType !== undefined ? { returnType } : {}),
|
|
835
1113
|
},
|
|
836
1114
|
});
|
|
837
|
-
// Compute enclosing class for Method/Constructor/Property/Function — used for both ownerId and HAS_METHOD
|
|
838
|
-
// Function is included because Kotlin/Rust/Python capture class methods as Function nodes
|
|
839
|
-
const needsOwner = nodeLabel === 'Method' || nodeLabel === 'Constructor' || nodeLabel === 'Property' || nodeLabel === 'Function';
|
|
840
|
-
const enclosingClassId = needsOwner ? findEnclosingClassId(nameNode || definitionNode, file.path) : null;
|
|
841
1115
|
result.symbols.push({
|
|
842
1116
|
filePath: file.path,
|
|
843
1117
|
name: nodeName,
|
|
844
1118
|
nodeId,
|
|
845
1119
|
type: nodeLabel,
|
|
846
|
-
...(parameterCount !== undefined ? { parameterCount } : {}),
|
|
847
|
-
...(enclosingClassId ? { ownerId: enclosingClassId } : {}),
|
|
848
1120
|
});
|
|
849
1121
|
const fileId = generateId('File', file.path);
|
|
850
1122
|
const relId = generateId('DEFINES', `${fileId}->${nodeId}`);
|
|
@@ -856,17 +1128,6 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
856
1128
|
confidence: 1.0,
|
|
857
1129
|
reason: '',
|
|
858
1130
|
});
|
|
859
|
-
// ── HAS_METHOD: link method/constructor/property to enclosing class ──
|
|
860
|
-
if (enclosingClassId) {
|
|
861
|
-
result.relationships.push({
|
|
862
|
-
id: generateId('HAS_METHOD', `${enclosingClassId}->${nodeId}`),
|
|
863
|
-
sourceId: enclosingClassId,
|
|
864
|
-
targetId: nodeId,
|
|
865
|
-
type: 'HAS_METHOD',
|
|
866
|
-
confidence: 1.0,
|
|
867
|
-
reason: '',
|
|
868
|
-
});
|
|
869
|
-
}
|
|
870
1131
|
}
|
|
871
1132
|
// Extract Laravel routes from route files via procedural AST walk
|
|
872
1133
|
if (language === SupportedLanguages.PHP && (file.path.includes('/routes/') || file.path.startsWith('routes/')) && file.path.endsWith('.php')) {
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { Worker } from 'node:worker_threads';
|
|
2
2
|
import os from 'node:os';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
3
|
/**
|
|
6
4
|
* Max files to send to a worker in a single postMessage.
|
|
7
5
|
* Keeps structured-clone memory bounded per sub-batch.
|
|
@@ -14,12 +12,6 @@ const SUB_BATCH_TIMEOUT_MS = 30_000;
|
|
|
14
12
|
* Create a pool of worker threads.
|
|
15
13
|
*/
|
|
16
14
|
export const createWorkerPool = (workerUrl, poolSize) => {
|
|
17
|
-
// Validate worker script exists before spawning to prevent uncaught
|
|
18
|
-
// MODULE_NOT_FOUND crashes in worker threads (e.g. when running from src/ via vitest)
|
|
19
|
-
const workerPath = fileURLToPath(workerUrl);
|
|
20
|
-
if (!fs.existsSync(workerPath)) {
|
|
21
|
-
throw new Error(`Worker script not found: ${workerPath}`);
|
|
22
|
-
}
|
|
23
15
|
const size = poolSize ?? Math.min(8, Math.max(1, os.cpus().length - 1));
|
|
24
16
|
const workers = [];
|
|
25
17
|
for (let i = 0; i < size; i++) {
|