gitnexus 1.4.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +194 -214
  2. package/dist/cli/ai-context.d.ts +1 -2
  3. package/dist/cli/ai-context.js +90 -117
  4. package/dist/cli/analyze.d.ts +0 -2
  5. package/dist/cli/analyze.js +2 -20
  6. package/dist/cli/index.js +25 -17
  7. package/dist/cli/setup.js +19 -17
  8. package/dist/core/augmentation/engine.js +20 -20
  9. package/dist/core/embeddings/embedding-pipeline.js +26 -26
  10. package/dist/core/graph/types.d.ts +2 -5
  11. package/dist/core/ingestion/ast-cache.js +2 -3
  12. package/dist/core/ingestion/call-processor.d.ts +5 -5
  13. package/dist/core/ingestion/call-processor.js +258 -173
  14. package/dist/core/ingestion/cluster-enricher.js +16 -16
  15. package/dist/core/ingestion/entry-point-scoring.d.ts +1 -2
  16. package/dist/core/ingestion/entry-point-scoring.js +22 -81
  17. package/dist/core/ingestion/framework-detection.d.ts +1 -5
  18. package/dist/core/ingestion/framework-detection.js +8 -39
  19. package/dist/core/ingestion/heritage-processor.d.ts +4 -13
  20. package/dist/core/ingestion/heritage-processor.js +28 -92
  21. package/dist/core/ingestion/import-processor.d.ts +19 -17
  22. package/dist/core/ingestion/import-processor.js +695 -170
  23. package/dist/core/ingestion/parsing-processor.d.ts +10 -1
  24. package/dist/core/ingestion/parsing-processor.js +177 -41
  25. package/dist/core/ingestion/pipeline.js +26 -49
  26. package/dist/core/ingestion/process-processor.js +1 -2
  27. package/dist/core/ingestion/symbol-table.d.ts +1 -12
  28. package/dist/core/ingestion/symbol-table.js +12 -19
  29. package/dist/core/ingestion/tree-sitter-queries.d.ts +11 -11
  30. package/dist/core/ingestion/tree-sitter-queries.js +485 -590
  31. package/dist/core/ingestion/utils.d.ts +0 -67
  32. package/dist/core/ingestion/utils.js +9 -692
  33. package/dist/core/ingestion/workers/parse-worker.d.ts +3 -20
  34. package/dist/core/ingestion/workers/parse-worker.js +345 -84
  35. package/dist/core/ingestion/workers/worker-pool.js +0 -8
  36. package/dist/core/kuzu/csv-generator.js +3 -19
  37. package/dist/core/kuzu/kuzu-adapter.js +19 -14
  38. package/dist/core/kuzu/schema.d.ts +3 -3
  39. package/dist/core/kuzu/schema.js +288 -303
  40. package/dist/core/search/bm25-index.js +6 -7
  41. package/dist/core/search/hybrid-search.js +3 -3
  42. package/dist/core/wiki/diagrams.d.ts +27 -0
  43. package/dist/core/wiki/diagrams.js +163 -0
  44. package/dist/core/wiki/generator.d.ts +50 -2
  45. package/dist/core/wiki/generator.js +548 -49
  46. package/dist/core/wiki/graph-queries.d.ts +42 -0
  47. package/dist/core/wiki/graph-queries.js +276 -97
  48. package/dist/core/wiki/html-viewer.js +192 -192
  49. package/dist/core/wiki/llm-client.js +73 -11
  50. package/dist/core/wiki/prompts.d.ts +52 -8
  51. package/dist/core/wiki/prompts.js +200 -86
  52. package/dist/mcp/core/kuzu-adapter.d.ts +3 -1
  53. package/dist/mcp/core/kuzu-adapter.js +44 -13
  54. package/dist/mcp/local/local-backend.js +128 -128
  55. package/dist/mcp/resources.js +42 -42
  56. package/dist/mcp/server.js +19 -18
  57. package/dist/mcp/tools.js +104 -103
  58. package/hooks/claude/gitnexus-hook.cjs +155 -238
  59. package/hooks/claude/pre-tool-use.sh +79 -79
  60. package/hooks/claude/session-start.sh +42 -42
  61. package/package.json +96 -96
  62. package/scripts/patch-tree-sitter-swift.cjs +74 -74
  63. package/skills/gitnexus-cli.md +82 -82
  64. package/skills/gitnexus-debugging.md +89 -89
  65. package/skills/gitnexus-exploring.md +78 -78
  66. package/skills/gitnexus-guide.md +64 -64
  67. package/skills/gitnexus-impact-analysis.md +97 -97
  68. package/skills/gitnexus-pr-review.md +163 -163
  69. package/skills/gitnexus-refactoring.md +121 -121
  70. package/vendor/leiden/index.cjs +355 -355
  71. package/vendor/leiden/utils.cjs +392 -392
  72. package/dist/cli/lazy-action.d.ts +0 -6
  73. package/dist/cli/lazy-action.js +0 -18
  74. package/dist/cli/skill-gen.d.ts +0 -26
  75. package/dist/cli/skill-gen.js +0 -549
  76. package/dist/core/ingestion/constants.d.ts +0 -16
  77. package/dist/core/ingestion/constants.js +0 -16
  78. package/dist/core/ingestion/export-detection.d.ts +0 -18
  79. package/dist/core/ingestion/export-detection.js +0 -230
  80. package/dist/core/ingestion/language-config.d.ts +0 -46
  81. package/dist/core/ingestion/language-config.js +0 -167
  82. package/dist/core/ingestion/mro-processor.d.ts +0 -45
  83. package/dist/core/ingestion/mro-processor.js +0 -369
  84. package/dist/core/ingestion/named-binding-extraction.d.ts +0 -61
  85. package/dist/core/ingestion/named-binding-extraction.js +0 -363
  86. package/dist/core/ingestion/resolvers/csharp.d.ts +0 -22
  87. package/dist/core/ingestion/resolvers/csharp.js +0 -109
  88. package/dist/core/ingestion/resolvers/go.d.ts +0 -19
  89. package/dist/core/ingestion/resolvers/go.js +0 -42
  90. package/dist/core/ingestion/resolvers/index.d.ts +0 -16
  91. package/dist/core/ingestion/resolvers/index.js +0 -11
  92. package/dist/core/ingestion/resolvers/jvm.d.ts +0 -23
  93. package/dist/core/ingestion/resolvers/jvm.js +0 -87
  94. package/dist/core/ingestion/resolvers/php.d.ts +0 -15
  95. package/dist/core/ingestion/resolvers/php.js +0 -35
  96. package/dist/core/ingestion/resolvers/rust.d.ts +0 -15
  97. package/dist/core/ingestion/resolvers/rust.js +0 -73
  98. package/dist/core/ingestion/resolvers/standard.d.ts +0 -28
  99. package/dist/core/ingestion/resolvers/standard.js +0 -145
  100. package/dist/core/ingestion/resolvers/utils.d.ts +0 -33
  101. package/dist/core/ingestion/resolvers/utils.js +0 -120
  102. package/dist/core/ingestion/symbol-resolver.d.ts +0 -32
  103. package/dist/core/ingestion/symbol-resolver.js +0 -83
  104. package/dist/core/ingestion/type-env.d.ts +0 -27
  105. package/dist/core/ingestion/type-env.js +0 -86
  106. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +0 -2
  107. package/dist/core/ingestion/type-extractors/c-cpp.js +0 -60
  108. package/dist/core/ingestion/type-extractors/csharp.d.ts +0 -2
  109. package/dist/core/ingestion/type-extractors/csharp.js +0 -89
  110. package/dist/core/ingestion/type-extractors/go.d.ts +0 -2
  111. package/dist/core/ingestion/type-extractors/go.js +0 -105
  112. package/dist/core/ingestion/type-extractors/index.d.ts +0 -21
  113. package/dist/core/ingestion/type-extractors/index.js +0 -29
  114. package/dist/core/ingestion/type-extractors/jvm.d.ts +0 -3
  115. package/dist/core/ingestion/type-extractors/jvm.js +0 -121
  116. package/dist/core/ingestion/type-extractors/php.d.ts +0 -2
  117. package/dist/core/ingestion/type-extractors/php.js +0 -31
  118. package/dist/core/ingestion/type-extractors/python.d.ts +0 -2
  119. package/dist/core/ingestion/type-extractors/python.js +0 -41
  120. package/dist/core/ingestion/type-extractors/rust.d.ts +0 -2
  121. package/dist/core/ingestion/type-extractors/rust.js +0 -39
  122. package/dist/core/ingestion/type-extractors/shared.d.ts +0 -17
  123. package/dist/core/ingestion/type-extractors/shared.js +0 -97
  124. package/dist/core/ingestion/type-extractors/swift.d.ts +0 -2
  125. package/dist/core/ingestion/type-extractors/swift.js +0 -43
  126. package/dist/core/ingestion/type-extractors/types.d.ts +0 -14
  127. package/dist/core/ingestion/type-extractors/types.js +0 -1
  128. package/dist/core/ingestion/type-extractors/typescript.d.ts +0 -2
  129. package/dist/core/ingestion/type-extractors/typescript.js +0 -46
  130. package/dist/mcp/compatible-stdio-transport.d.ts +0 -25
  131. package/dist/mcp/compatible-stdio-transport.js +0 -200
@@ -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: SupportedLanguages;
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' | 'HAS_METHOD';
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: 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
- }[];
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 { 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';
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
- // isNodeExported imported from ../export-detection.js (shared module)
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
- const { funcName, label } = extractFunctionName(current);
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
- return generateId(label, `${filePath}:${funcName}`);
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
- // DEFINITION_CAPTURE_KEYS and getDefinitionNodeFromCaptures imported from ../utils.js
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 (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
- }
991
+ catch {
679
992
  return;
680
993
  }
681
994
  for (const file of files) {
682
- // Skip files larger than the max tree-sitter buffer (32 MB)
683
- if (file.content.length > TREE_SITTER_MAX_BUFFER)
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: getTreeSitterBufferSize(file.content.length) });
1000
+ tree = parser.parse(file.content, undefined, { bufferSize: 1024 * 256 });
688
1001
  }
689
- catch (err) {
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 (err) {
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 (!isBuiltInOrNoise(calledName)) {
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
- 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
- });
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
- // 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
- }
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++) {