gitnexus 1.3.10 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -2
- package/dist/cli/ai-context.d.ts +2 -1
- package/dist/cli/ai-context.js +33 -6
- package/dist/cli/analyze.d.ts +2 -0
- package/dist/cli/analyze.js +20 -2
- package/dist/cli/index.js +2 -0
- package/dist/cli/setup.js +17 -19
- package/dist/cli/skill-gen.d.ts +26 -0
- package/dist/cli/skill-gen.js +549 -0
- package/dist/core/graph/types.d.ts +5 -2
- package/dist/core/ingestion/call-processor.d.ts +5 -5
- package/dist/core/ingestion/call-processor.js +173 -260
- package/dist/core/ingestion/constants.d.ts +16 -0
- package/dist/core/ingestion/constants.js +16 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +2 -1
- package/dist/core/ingestion/entry-point-scoring.js +81 -22
- package/dist/core/ingestion/export-detection.d.ts +18 -0
- package/dist/core/ingestion/export-detection.js +230 -0
- package/dist/core/ingestion/framework-detection.d.ts +5 -1
- package/dist/core/ingestion/framework-detection.js +39 -8
- package/dist/core/ingestion/heritage-processor.d.ts +13 -4
- package/dist/core/ingestion/heritage-processor.js +92 -28
- package/dist/core/ingestion/import-processor.d.ts +17 -19
- package/dist/core/ingestion/import-processor.js +170 -695
- package/dist/core/ingestion/language-config.d.ts +46 -0
- package/dist/core/ingestion/language-config.js +167 -0
- package/dist/core/ingestion/mro-processor.d.ts +45 -0
- package/dist/core/ingestion/mro-processor.js +369 -0
- package/dist/core/ingestion/named-binding-extraction.d.ts +61 -0
- package/dist/core/ingestion/named-binding-extraction.js +363 -0
- package/dist/core/ingestion/parsing-processor.d.ts +1 -10
- package/dist/core/ingestion/parsing-processor.js +41 -177
- package/dist/core/ingestion/pipeline.js +41 -26
- package/dist/core/ingestion/process-processor.js +2 -1
- package/dist/core/ingestion/resolvers/csharp.d.ts +22 -0
- package/dist/core/ingestion/resolvers/csharp.js +109 -0
- package/dist/core/ingestion/resolvers/go.d.ts +19 -0
- package/dist/core/ingestion/resolvers/go.js +42 -0
- package/dist/core/ingestion/resolvers/index.d.ts +16 -0
- package/dist/core/ingestion/resolvers/index.js +11 -0
- package/dist/core/ingestion/resolvers/jvm.d.ts +23 -0
- package/dist/core/ingestion/resolvers/jvm.js +87 -0
- package/dist/core/ingestion/resolvers/php.d.ts +15 -0
- package/dist/core/ingestion/resolvers/php.js +35 -0
- package/dist/core/ingestion/resolvers/rust.d.ts +15 -0
- package/dist/core/ingestion/resolvers/rust.js +73 -0
- package/dist/core/ingestion/resolvers/standard.d.ts +28 -0
- package/dist/core/ingestion/resolvers/standard.js +145 -0
- package/dist/core/ingestion/resolvers/utils.d.ts +33 -0
- package/dist/core/ingestion/resolvers/utils.js +120 -0
- package/dist/core/ingestion/symbol-resolver.d.ts +32 -0
- package/dist/core/ingestion/symbol-resolver.js +83 -0
- package/dist/core/ingestion/symbol-table.d.ts +12 -1
- package/dist/core/ingestion/symbol-table.js +19 -12
- package/dist/core/ingestion/tree-sitter-queries.d.ts +11 -11
- package/dist/core/ingestion/tree-sitter-queries.js +114 -9
- package/dist/core/ingestion/type-env.d.ts +27 -0
- package/dist/core/ingestion/type-env.js +86 -0
- package/dist/core/ingestion/type-extractors/c-cpp.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +60 -0
- package/dist/core/ingestion/type-extractors/csharp.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/csharp.js +89 -0
- package/dist/core/ingestion/type-extractors/go.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/go.js +105 -0
- package/dist/core/ingestion/type-extractors/index.d.ts +21 -0
- package/dist/core/ingestion/type-extractors/index.js +29 -0
- package/dist/core/ingestion/type-extractors/jvm.d.ts +3 -0
- package/dist/core/ingestion/type-extractors/jvm.js +121 -0
- package/dist/core/ingestion/type-extractors/php.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/php.js +31 -0
- package/dist/core/ingestion/type-extractors/python.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/python.js +41 -0
- package/dist/core/ingestion/type-extractors/rust.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/rust.js +39 -0
- package/dist/core/ingestion/type-extractors/shared.d.ts +17 -0
- package/dist/core/ingestion/type-extractors/shared.js +97 -0
- package/dist/core/ingestion/type-extractors/swift.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/swift.js +43 -0
- package/dist/core/ingestion/type-extractors/types.d.ts +14 -0
- package/dist/core/ingestion/type-extractors/types.js +1 -0
- package/dist/core/ingestion/type-extractors/typescript.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/typescript.js +46 -0
- package/dist/core/ingestion/utils.d.ts +67 -0
- package/dist/core/ingestion/utils.js +691 -4
- package/dist/core/ingestion/workers/parse-worker.d.ts +20 -3
- package/dist/core/ingestion/workers/parse-worker.js +84 -345
- package/dist/core/ingestion/workers/worker-pool.js +8 -0
- package/dist/core/kuzu/csv-generator.js +19 -3
- package/dist/core/kuzu/kuzu-adapter.js +5 -2
- package/dist/core/kuzu/schema.d.ts +3 -3
- package/dist/core/kuzu/schema.js +16 -1
- package/dist/core/search/bm25-index.js +2 -1
- package/dist/mcp/core/kuzu-adapter.js +6 -18
- package/dist/mcp/tools.js +12 -3
- package/hooks/claude/gitnexus-hook.cjs +149 -66
- package/package.json +1 -1
- package/skills/gitnexus-cli.md +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SupportedLanguages } from '../../../config/supported-languages.js';
|
|
1
2
|
interface ParsedNode {
|
|
2
3
|
id: string;
|
|
3
4
|
label: string;
|
|
@@ -6,18 +7,20 @@ interface ParsedNode {
|
|
|
6
7
|
filePath: string;
|
|
7
8
|
startLine: number;
|
|
8
9
|
endLine: number;
|
|
9
|
-
language:
|
|
10
|
+
language: SupportedLanguages;
|
|
10
11
|
isExported: boolean;
|
|
11
12
|
astFrameworkMultiplier?: number;
|
|
12
13
|
astFrameworkReason?: string;
|
|
13
14
|
description?: string;
|
|
15
|
+
parameterCount?: number;
|
|
16
|
+
returnType?: string;
|
|
14
17
|
};
|
|
15
18
|
}
|
|
16
19
|
interface ParsedRelationship {
|
|
17
20
|
id: string;
|
|
18
21
|
sourceId: string;
|
|
19
22
|
targetId: string;
|
|
20
|
-
type: 'DEFINES';
|
|
23
|
+
type: 'DEFINES' | 'HAS_METHOD';
|
|
21
24
|
confidence: number;
|
|
22
25
|
reason: string;
|
|
23
26
|
}
|
|
@@ -26,17 +29,31 @@ interface ParsedSymbol {
|
|
|
26
29
|
name: string;
|
|
27
30
|
nodeId: string;
|
|
28
31
|
type: string;
|
|
32
|
+
parameterCount?: number;
|
|
33
|
+
ownerId?: string;
|
|
29
34
|
}
|
|
30
35
|
export interface ExtractedImport {
|
|
31
36
|
filePath: string;
|
|
32
37
|
rawImportPath: string;
|
|
33
|
-
language:
|
|
38
|
+
language: SupportedLanguages;
|
|
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
|
+
}[];
|
|
34
44
|
}
|
|
35
45
|
export interface ExtractedCall {
|
|
36
46
|
filePath: string;
|
|
37
47
|
calledName: string;
|
|
38
48
|
/** generateId of enclosing function, or generateId('File', filePath) for top-level */
|
|
39
49
|
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;
|
|
40
57
|
}
|
|
41
58
|
export interface ExtractedHeritage {
|
|
42
59
|
filePath: string;
|
|
@@ -14,6 +14,7 @@ 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';
|
|
17
18
|
// tree-sitter-swift is an optionalDependency — may not be installed
|
|
18
19
|
const _require = createRequire(import.meta.url);
|
|
19
20
|
let Swift = null;
|
|
@@ -21,9 +22,13 @@ try {
|
|
|
21
22
|
Swift = _require('tree-sitter-swift');
|
|
22
23
|
}
|
|
23
24
|
catch { }
|
|
24
|
-
import {
|
|
25
|
+
import { getLanguageFromFilename, FUNCTION_NODE_TYPES, extractFunctionName, isBuiltInOrNoise, getDefinitionNodeFromCaptures, findEnclosingClassId, extractMethodSignature, countCallArguments, inferCallForm, extractReceiverName } from '../utils.js';
|
|
26
|
+
import { buildTypeEnv, lookupTypeEnv } from '../type-env.js';
|
|
27
|
+
import { isNodeExported } from '../export-detection.js';
|
|
25
28
|
import { detectFrameworkFromAST } from '../framework-detection.js';
|
|
26
29
|
import { generateId } from '../../../lib/utils.js';
|
|
30
|
+
import { extractNamedBindings } from '../named-binding-extraction.js';
|
|
31
|
+
import { appendKotlinWildcard } from '../resolvers/index.js';
|
|
27
32
|
// ============================================================================
|
|
28
33
|
// Worker-local parser + language map
|
|
29
34
|
// ============================================================================
|
|
@@ -52,307 +57,24 @@ const setLanguage = (language, filePath) => {
|
|
|
52
57
|
throw new Error(`Unsupported language: ${language}`);
|
|
53
58
|
parser.setLanguage(lang);
|
|
54
59
|
};
|
|
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
|
-
};
|
|
60
|
+
// isNodeExported imported from ../export-detection.js (shared module)
|
|
172
61
|
// ============================================================================
|
|
173
62
|
// Enclosing function detection (for call extraction)
|
|
174
63
|
// ============================================================================
|
|
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
|
-
]);
|
|
188
64
|
/** Walk up AST to find enclosing function, return its generateId or null for top-level */
|
|
189
65
|
const findEnclosingFunctionId = (node, filePath) => {
|
|
190
66
|
let current = node.parent;
|
|
191
67
|
while (current) {
|
|
192
68
|
if (FUNCTION_NODE_TYPES.has(current.type)) {
|
|
193
|
-
|
|
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
|
-
}
|
|
69
|
+
const { funcName, label } = extractFunctionName(current);
|
|
236
70
|
if (funcName) {
|
|
237
|
-
|
|
238
|
-
return generateId(label, `${filePath}:${funcName}:${startLine}`);
|
|
71
|
+
return generateId(label, `${filePath}:${funcName}`);
|
|
239
72
|
}
|
|
240
73
|
}
|
|
241
74
|
current = current.parent;
|
|
242
75
|
}
|
|
243
76
|
return null;
|
|
244
77
|
};
|
|
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
|
-
]);
|
|
356
78
|
// ============================================================================
|
|
357
79
|
// Label detection from capture map
|
|
358
80
|
// ============================================================================
|
|
@@ -408,49 +130,7 @@ const getLabelFromCaptures = (captureMap) => {
|
|
|
408
130
|
return 'Template';
|
|
409
131
|
return 'CodeElement';
|
|
410
132
|
};
|
|
411
|
-
|
|
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
|
-
};
|
|
133
|
+
// DEFINITION_CAPTURE_KEYS and getDefinitionNodeFromCaptures imported from ../utils.js
|
|
454
134
|
// ============================================================================
|
|
455
135
|
// Process a batch of files
|
|
456
136
|
// ============================================================================
|
|
@@ -988,27 +668,38 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
988
668
|
const lang = parser.getLanguage();
|
|
989
669
|
query = new Parser.Query(lang, queryString);
|
|
990
670
|
}
|
|
991
|
-
catch {
|
|
671
|
+
catch (err) {
|
|
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
|
+
}
|
|
992
679
|
return;
|
|
993
680
|
}
|
|
994
681
|
for (const file of files) {
|
|
995
|
-
// Skip
|
|
996
|
-
if (file.content.length >
|
|
682
|
+
// Skip files larger than the max tree-sitter buffer (32 MB)
|
|
683
|
+
if (file.content.length > TREE_SITTER_MAX_BUFFER)
|
|
997
684
|
continue;
|
|
998
685
|
let tree;
|
|
999
686
|
try {
|
|
1000
|
-
tree = parser.parse(file.content, undefined, { bufferSize:
|
|
687
|
+
tree = parser.parse(file.content, undefined, { bufferSize: getTreeSitterBufferSize(file.content.length) });
|
|
1001
688
|
}
|
|
1002
|
-
catch {
|
|
689
|
+
catch (err) {
|
|
690
|
+
console.warn(`Failed to parse file ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1003
691
|
continue;
|
|
1004
692
|
}
|
|
1005
693
|
result.fileCount++;
|
|
1006
694
|
onFileProcessed?.();
|
|
695
|
+
// Build per-file TypeEnv from explicit type annotations (for receiver resolution)
|
|
696
|
+
const typeEnv = buildTypeEnv(tree, language);
|
|
1007
697
|
let matches;
|
|
1008
698
|
try {
|
|
1009
699
|
matches = query.matches(tree.rootNode);
|
|
1010
700
|
}
|
|
1011
|
-
catch {
|
|
701
|
+
catch (err) {
|
|
702
|
+
console.warn(`Query execution failed for ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1012
703
|
continue;
|
|
1013
704
|
}
|
|
1014
705
|
for (const match of matches) {
|
|
@@ -1021,10 +712,12 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1021
712
|
const rawImportPath = language === SupportedLanguages.Kotlin
|
|
1022
713
|
? appendKotlinWildcard(captureMap['import.source'].text.replace(/['"<>]/g, ''), captureMap['import'])
|
|
1023
714
|
: captureMap['import.source'].text.replace(/['"<>]/g, '');
|
|
715
|
+
const namedBindings = extractNamedBindings(captureMap['import'], language);
|
|
1024
716
|
result.imports.push({
|
|
1025
717
|
filePath: file.path,
|
|
1026
718
|
rawImportPath,
|
|
1027
719
|
language: language,
|
|
720
|
+
...(namedBindings ? { namedBindings } : {}),
|
|
1028
721
|
});
|
|
1029
722
|
continue;
|
|
1030
723
|
}
|
|
@@ -1033,11 +726,22 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1033
726
|
const callNameNode = captureMap['call.name'];
|
|
1034
727
|
if (callNameNode) {
|
|
1035
728
|
const calledName = callNameNode.text;
|
|
1036
|
-
if (!
|
|
729
|
+
if (!isBuiltInOrNoise(calledName)) {
|
|
1037
730
|
const callNode = captureMap['call'];
|
|
1038
731
|
const sourceId = findEnclosingFunctionId(callNode, file.path)
|
|
1039
732
|
|| generateId('File', file.path);
|
|
1040
|
-
|
|
733
|
+
const callForm = inferCallForm(callNode, callNameNode);
|
|
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
|
+
});
|
|
1041
745
|
}
|
|
1042
746
|
}
|
|
1043
747
|
continue;
|
|
@@ -1045,12 +749,21 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1045
749
|
// Extract heritage (extends/implements)
|
|
1046
750
|
if (captureMap['heritage.class']) {
|
|
1047
751
|
if (captureMap['heritage.extends']) {
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
752
|
+
// Go struct embedding: the query matches ALL field_declarations with
|
|
753
|
+
// type_identifier, but only anonymous fields (no name) are embedded.
|
|
754
|
+
// Named fields like `Breed string` also match — skip them.
|
|
755
|
+
const extendsNode = captureMap['heritage.extends'];
|
|
756
|
+
const fieldDecl = extendsNode.parent;
|
|
757
|
+
const isNamedField = fieldDecl?.type === 'field_declaration'
|
|
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
|
+
}
|
|
1054
767
|
}
|
|
1055
768
|
if (captureMap['heritage.implements']) {
|
|
1056
769
|
result.heritage.push({
|
|
@@ -1082,7 +795,7 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1082
795
|
const nodeName = nameNode ? nameNode.text : 'init';
|
|
1083
796
|
const definitionNode = getDefinitionNodeFromCaptures(captureMap);
|
|
1084
797
|
const startLine = definitionNode ? definitionNode.startPosition.row : (nameNode ? nameNode.startPosition.row : 0);
|
|
1085
|
-
const nodeId = generateId(nodeLabel, `${file.path}:${nodeName}
|
|
798
|
+
const nodeId = generateId(nodeLabel, `${file.path}:${nodeName}`);
|
|
1086
799
|
let description;
|
|
1087
800
|
if (language === SupportedLanguages.PHP) {
|
|
1088
801
|
if (nodeLabel === 'Property' && captureMap['definition.property']) {
|
|
@@ -1095,6 +808,13 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1095
808
|
const frameworkHint = definitionNode
|
|
1096
809
|
? detectFrameworkFromAST(language, (definitionNode.text || '').slice(0, 300))
|
|
1097
810
|
: 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
|
+
}
|
|
1098
818
|
result.nodes.push({
|
|
1099
819
|
id: nodeId,
|
|
1100
820
|
label: nodeLabel,
|
|
@@ -1110,13 +830,21 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1110
830
|
astFrameworkReason: frameworkHint.reason,
|
|
1111
831
|
} : {}),
|
|
1112
832
|
...(description !== undefined ? { description } : {}),
|
|
833
|
+
...(parameterCount !== undefined ? { parameterCount } : {}),
|
|
834
|
+
...(returnType !== undefined ? { returnType } : {}),
|
|
1113
835
|
},
|
|
1114
836
|
});
|
|
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;
|
|
1115
841
|
result.symbols.push({
|
|
1116
842
|
filePath: file.path,
|
|
1117
843
|
name: nodeName,
|
|
1118
844
|
nodeId,
|
|
1119
845
|
type: nodeLabel,
|
|
846
|
+
...(parameterCount !== undefined ? { parameterCount } : {}),
|
|
847
|
+
...(enclosingClassId ? { ownerId: enclosingClassId } : {}),
|
|
1120
848
|
});
|
|
1121
849
|
const fileId = generateId('File', file.path);
|
|
1122
850
|
const relId = generateId('DEFINES', `${fileId}->${nodeId}`);
|
|
@@ -1128,6 +856,17 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1128
856
|
confidence: 1.0,
|
|
1129
857
|
reason: '',
|
|
1130
858
|
});
|
|
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
|
+
}
|
|
1131
870
|
}
|
|
1132
871
|
// Extract Laravel routes from route files via procedural AST walk
|
|
1133
872
|
if (language === SupportedLanguages.PHP && (file.path.includes('/routes/') || file.path.startsWith('routes/')) && file.path.endsWith('.php')) {
|
|
@@ -1,5 +1,7 @@
|
|
|
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';
|
|
3
5
|
/**
|
|
4
6
|
* Max files to send to a worker in a single postMessage.
|
|
5
7
|
* Keeps structured-clone memory bounded per sub-batch.
|
|
@@ -12,6 +14,12 @@ const SUB_BATCH_TIMEOUT_MS = 30_000;
|
|
|
12
14
|
* Create a pool of worker threads.
|
|
13
15
|
*/
|
|
14
16
|
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
|
+
}
|
|
15
23
|
const size = poolSize ?? Math.min(8, Math.max(1, os.cpus().length - 1));
|
|
16
24
|
const workers = [];
|
|
17
25
|
for (let i = 0; i < size; i++) {
|