gitnexus 1.4.1 → 1.4.6

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 (169) hide show
  1. package/README.md +215 -194
  2. package/dist/cli/ai-context.d.ts +2 -1
  3. package/dist/cli/ai-context.js +117 -90
  4. package/dist/cli/analyze.d.ts +2 -0
  5. package/dist/cli/analyze.js +57 -30
  6. package/dist/cli/augment.js +1 -1
  7. package/dist/cli/eval-server.d.ts +1 -1
  8. package/dist/cli/eval-server.js +14 -6
  9. package/dist/cli/index.js +18 -25
  10. package/dist/cli/lazy-action.d.ts +6 -0
  11. package/dist/cli/lazy-action.js +18 -0
  12. package/dist/cli/mcp.js +1 -1
  13. package/dist/cli/setup.js +42 -32
  14. package/dist/cli/skill-gen.d.ts +26 -0
  15. package/dist/cli/skill-gen.js +549 -0
  16. package/dist/cli/status.js +13 -4
  17. package/dist/cli/tool.d.ts +3 -2
  18. package/dist/cli/tool.js +48 -13
  19. package/dist/cli/wiki.js +2 -2
  20. package/dist/config/ignore-service.d.ts +25 -0
  21. package/dist/config/ignore-service.js +76 -0
  22. package/dist/config/supported-languages.d.ts +1 -0
  23. package/dist/config/supported-languages.js +1 -1
  24. package/dist/core/augmentation/engine.js +99 -72
  25. package/dist/core/embeddings/embedder.d.ts +1 -1
  26. package/dist/core/embeddings/embedder.js +1 -1
  27. package/dist/core/embeddings/embedding-pipeline.d.ts +3 -3
  28. package/dist/core/embeddings/embedding-pipeline.js +74 -47
  29. package/dist/core/embeddings/types.d.ts +1 -1
  30. package/dist/core/graph/types.d.ts +5 -2
  31. package/dist/core/ingestion/ast-cache.js +3 -2
  32. package/dist/core/ingestion/call-processor.d.ts +5 -7
  33. package/dist/core/ingestion/call-processor.js +430 -283
  34. package/dist/core/ingestion/call-routing.d.ts +53 -0
  35. package/dist/core/ingestion/call-routing.js +108 -0
  36. package/dist/core/ingestion/cluster-enricher.js +16 -16
  37. package/dist/core/ingestion/constants.d.ts +16 -0
  38. package/dist/core/ingestion/constants.js +16 -0
  39. package/dist/core/ingestion/entry-point-scoring.d.ts +2 -1
  40. package/dist/core/ingestion/entry-point-scoring.js +94 -24
  41. package/dist/core/ingestion/export-detection.d.ts +18 -0
  42. package/dist/core/ingestion/export-detection.js +231 -0
  43. package/dist/core/ingestion/filesystem-walker.js +4 -3
  44. package/dist/core/ingestion/framework-detection.d.ts +5 -1
  45. package/dist/core/ingestion/framework-detection.js +48 -8
  46. package/dist/core/ingestion/heritage-processor.d.ts +13 -5
  47. package/dist/core/ingestion/heritage-processor.js +109 -55
  48. package/dist/core/ingestion/import-processor.d.ts +16 -20
  49. package/dist/core/ingestion/import-processor.js +202 -696
  50. package/dist/core/ingestion/language-config.d.ts +46 -0
  51. package/dist/core/ingestion/language-config.js +167 -0
  52. package/dist/core/ingestion/mro-processor.d.ts +45 -0
  53. package/dist/core/ingestion/mro-processor.js +369 -0
  54. package/dist/core/ingestion/named-binding-extraction.d.ts +61 -0
  55. package/dist/core/ingestion/named-binding-extraction.js +363 -0
  56. package/dist/core/ingestion/parsing-processor.d.ts +3 -11
  57. package/dist/core/ingestion/parsing-processor.js +85 -181
  58. package/dist/core/ingestion/pipeline.d.ts +5 -1
  59. package/dist/core/ingestion/pipeline.js +192 -116
  60. package/dist/core/ingestion/process-processor.js +2 -1
  61. package/dist/core/ingestion/resolution-context.d.ts +53 -0
  62. package/dist/core/ingestion/resolution-context.js +132 -0
  63. package/dist/core/ingestion/resolvers/csharp.d.ts +22 -0
  64. package/dist/core/ingestion/resolvers/csharp.js +109 -0
  65. package/dist/core/ingestion/resolvers/go.d.ts +19 -0
  66. package/dist/core/ingestion/resolvers/go.js +42 -0
  67. package/dist/core/ingestion/resolvers/index.d.ts +18 -0
  68. package/dist/core/ingestion/resolvers/index.js +13 -0
  69. package/dist/core/ingestion/resolvers/jvm.d.ts +23 -0
  70. package/dist/core/ingestion/resolvers/jvm.js +87 -0
  71. package/dist/core/ingestion/resolvers/php.d.ts +15 -0
  72. package/dist/core/ingestion/resolvers/php.js +35 -0
  73. package/dist/core/ingestion/resolvers/python.d.ts +19 -0
  74. package/dist/core/ingestion/resolvers/python.js +52 -0
  75. package/dist/core/ingestion/resolvers/ruby.d.ts +12 -0
  76. package/dist/core/ingestion/resolvers/ruby.js +15 -0
  77. package/dist/core/ingestion/resolvers/rust.d.ts +15 -0
  78. package/dist/core/ingestion/resolvers/rust.js +73 -0
  79. package/dist/core/ingestion/resolvers/standard.d.ts +28 -0
  80. package/dist/core/ingestion/resolvers/standard.js +123 -0
  81. package/dist/core/ingestion/resolvers/utils.d.ts +33 -0
  82. package/dist/core/ingestion/resolvers/utils.js +122 -0
  83. package/dist/core/ingestion/symbol-table.d.ts +21 -1
  84. package/dist/core/ingestion/symbol-table.js +40 -12
  85. package/dist/core/ingestion/tree-sitter-queries.d.ts +12 -11
  86. package/dist/core/ingestion/tree-sitter-queries.js +642 -485
  87. package/dist/core/ingestion/type-env.d.ts +49 -0
  88. package/dist/core/ingestion/type-env.js +611 -0
  89. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +2 -0
  90. package/dist/core/ingestion/type-extractors/c-cpp.js +385 -0
  91. package/dist/core/ingestion/type-extractors/csharp.d.ts +2 -0
  92. package/dist/core/ingestion/type-extractors/csharp.js +383 -0
  93. package/dist/core/ingestion/type-extractors/go.d.ts +2 -0
  94. package/dist/core/ingestion/type-extractors/go.js +467 -0
  95. package/dist/core/ingestion/type-extractors/index.d.ts +22 -0
  96. package/dist/core/ingestion/type-extractors/index.js +31 -0
  97. package/dist/core/ingestion/type-extractors/jvm.d.ts +3 -0
  98. package/dist/core/ingestion/type-extractors/jvm.js +681 -0
  99. package/dist/core/ingestion/type-extractors/php.d.ts +2 -0
  100. package/dist/core/ingestion/type-extractors/php.js +549 -0
  101. package/dist/core/ingestion/type-extractors/python.d.ts +2 -0
  102. package/dist/core/ingestion/type-extractors/python.js +406 -0
  103. package/dist/core/ingestion/type-extractors/ruby.d.ts +2 -0
  104. package/dist/core/ingestion/type-extractors/ruby.js +389 -0
  105. package/dist/core/ingestion/type-extractors/rust.d.ts +2 -0
  106. package/dist/core/ingestion/type-extractors/rust.js +449 -0
  107. package/dist/core/ingestion/type-extractors/shared.d.ts +133 -0
  108. package/dist/core/ingestion/type-extractors/shared.js +703 -0
  109. package/dist/core/ingestion/type-extractors/swift.d.ts +2 -0
  110. package/dist/core/ingestion/type-extractors/swift.js +137 -0
  111. package/dist/core/ingestion/type-extractors/types.d.ts +127 -0
  112. package/dist/core/ingestion/type-extractors/types.js +1 -0
  113. package/dist/core/ingestion/type-extractors/typescript.d.ts +2 -0
  114. package/dist/core/ingestion/type-extractors/typescript.js +494 -0
  115. package/dist/core/ingestion/utils.d.ts +98 -0
  116. package/dist/core/ingestion/utils.js +1064 -9
  117. package/dist/core/ingestion/workers/parse-worker.d.ts +38 -4
  118. package/dist/core/ingestion/workers/parse-worker.js +251 -359
  119. package/dist/core/ingestion/workers/worker-pool.js +8 -0
  120. package/dist/core/{kuzu → lbug}/csv-generator.d.ts +1 -1
  121. package/dist/core/{kuzu → lbug}/csv-generator.js +20 -4
  122. package/dist/core/{kuzu/kuzu-adapter.d.ts → lbug/lbug-adapter.d.ts} +19 -19
  123. package/dist/core/{kuzu/kuzu-adapter.js → lbug/lbug-adapter.js} +82 -82
  124. package/dist/core/{kuzu → lbug}/schema.d.ts +4 -4
  125. package/dist/core/{kuzu → lbug}/schema.js +304 -289
  126. package/dist/core/search/bm25-index.d.ts +4 -4
  127. package/dist/core/search/bm25-index.js +17 -16
  128. package/dist/core/search/hybrid-search.d.ts +2 -2
  129. package/dist/core/search/hybrid-search.js +9 -9
  130. package/dist/core/tree-sitter/parser-loader.js +9 -2
  131. package/dist/core/wiki/generator.d.ts +4 -52
  132. package/dist/core/wiki/generator.js +53 -552
  133. package/dist/core/wiki/graph-queries.d.ts +4 -46
  134. package/dist/core/wiki/graph-queries.js +103 -282
  135. package/dist/core/wiki/html-viewer.js +192 -192
  136. package/dist/core/wiki/llm-client.js +11 -73
  137. package/dist/core/wiki/prompts.d.ts +8 -52
  138. package/dist/core/wiki/prompts.js +86 -200
  139. package/dist/mcp/compatible-stdio-transport.d.ts +25 -0
  140. package/dist/mcp/compatible-stdio-transport.js +200 -0
  141. package/dist/mcp/core/{kuzu-adapter.d.ts → lbug-adapter.d.ts} +7 -9
  142. package/dist/mcp/core/{kuzu-adapter.js → lbug-adapter.js} +77 -79
  143. package/dist/mcp/local/local-backend.d.ts +7 -6
  144. package/dist/mcp/local/local-backend.js +176 -147
  145. package/dist/mcp/resources.js +42 -42
  146. package/dist/mcp/server.js +18 -19
  147. package/dist/mcp/tools.js +103 -104
  148. package/dist/server/api.js +12 -12
  149. package/dist/server/mcp-http.d.ts +1 -1
  150. package/dist/server/mcp-http.js +1 -1
  151. package/dist/storage/repo-manager.d.ts +20 -2
  152. package/dist/storage/repo-manager.js +55 -1
  153. package/dist/types/pipeline.d.ts +1 -1
  154. package/hooks/claude/gitnexus-hook.cjs +238 -155
  155. package/hooks/claude/pre-tool-use.sh +79 -79
  156. package/hooks/claude/session-start.sh +42 -42
  157. package/package.json +99 -96
  158. package/scripts/patch-tree-sitter-swift.cjs +74 -74
  159. package/skills/gitnexus-cli.md +82 -82
  160. package/skills/gitnexus-debugging.md +89 -89
  161. package/skills/gitnexus-exploring.md +78 -78
  162. package/skills/gitnexus-guide.md +64 -64
  163. package/skills/gitnexus-impact-analysis.md +97 -97
  164. package/skills/gitnexus-pr-review.md +163 -163
  165. package/skills/gitnexus-refactoring.md +121 -121
  166. package/vendor/leiden/index.cjs +355 -355
  167. package/vendor/leiden/utils.cjs +392 -392
  168. package/dist/core/wiki/diagrams.d.ts +0 -27
  169. package/dist/core/wiki/diagrams.js +0 -163
@@ -1,179 +1,15 @@
1
1
  import Parser from 'tree-sitter';
2
- import { loadParser, loadLanguage } from '../tree-sitter/parser-loader.js';
2
+ import { loadParser, loadLanguage, isLanguageAvailable } from '../tree-sitter/parser-loader.js';
3
3
  import { LANGUAGE_QUERIES } from './tree-sitter-queries.js';
4
4
  import { generateId } from '../../lib/utils.js';
5
- import { findSiblingChild, getLanguageFromFilename, yieldToEventLoop } from './utils.js';
5
+ import { getLanguageFromFilename, yieldToEventLoop, getDefinitionNodeFromCaptures, findEnclosingClassId, extractMethodSignature } from './utils.js';
6
+ import { isNodeExported } from './export-detection.js';
6
7
  import { detectFrameworkFromAST } from './framework-detection.js';
7
- const DEFINITION_CAPTURE_KEYS = [
8
- 'definition.function',
9
- 'definition.class',
10
- 'definition.interface',
11
- 'definition.method',
12
- 'definition.struct',
13
- 'definition.enum',
14
- 'definition.namespace',
15
- 'definition.module',
16
- 'definition.trait',
17
- 'definition.impl',
18
- 'definition.type',
19
- 'definition.const',
20
- 'definition.static',
21
- 'definition.typedef',
22
- 'definition.macro',
23
- 'definition.union',
24
- 'definition.property',
25
- 'definition.record',
26
- 'definition.delegate',
27
- 'definition.annotation',
28
- 'definition.constructor',
29
- 'definition.template',
30
- ];
31
- const getDefinitionNodeFromCaptures = (captureMap) => {
32
- for (const key of DEFINITION_CAPTURE_KEYS) {
33
- if (captureMap[key])
34
- return captureMap[key];
35
- }
36
- return null;
37
- };
38
- // ============================================================================
39
- // EXPORT DETECTION - Language-specific visibility detection
40
- // ============================================================================
41
- /**
42
- * Check if a symbol (function, class, etc.) is exported/public
43
- * Handles all 9 supported languages with explicit logic
44
- *
45
- * @param node - The AST node for the symbol name
46
- * @param name - The symbol name
47
- * @param language - The programming language
48
- * @returns true if the symbol is exported/public
49
- */
50
- export const isNodeExported = (node, name, language) => {
51
- let current = node;
52
- switch (language) {
53
- // JavaScript/TypeScript: Check for export keyword in ancestors
54
- case 'javascript':
55
- case 'typescript':
56
- while (current) {
57
- const type = current.type;
58
- if (type === 'export_statement' ||
59
- type === 'export_specifier' ||
60
- type === 'lexical_declaration' && current.parent?.type === 'export_statement') {
61
- return true;
62
- }
63
- // Also check if text starts with 'export '
64
- if (current.text?.startsWith('export ')) {
65
- return true;
66
- }
67
- current = current.parent;
68
- }
69
- return false;
70
- // Python: Public if no leading underscore (convention)
71
- case 'python':
72
- return !name.startsWith('_');
73
- // Java: Check for 'public' modifier
74
- // In tree-sitter Java, modifiers are siblings of the name node, not parents
75
- case 'java':
76
- while (current) {
77
- // Check if this node or any sibling is a 'modifiers' node containing 'public'
78
- if (current.parent) {
79
- const parent = current.parent;
80
- // Check all children of the parent for modifiers
81
- for (let i = 0; i < parent.childCount; i++) {
82
- const child = parent.child(i);
83
- if (child?.type === 'modifiers' && child.text?.includes('public')) {
84
- return true;
85
- }
86
- }
87
- // Also check if the parent's text starts with 'public' (fallback)
88
- if (parent.type === 'method_declaration' || parent.type === 'constructor_declaration') {
89
- if (parent.text?.trimStart().startsWith('public')) {
90
- return true;
91
- }
92
- }
93
- }
94
- current = current.parent;
95
- }
96
- return false;
97
- // C#: Check for 'public' modifier in ancestors
98
- case 'csharp':
99
- while (current) {
100
- if (current.type === 'modifier' || current.type === 'modifiers') {
101
- if (current.text?.includes('public'))
102
- return true;
103
- }
104
- current = current.parent;
105
- }
106
- return false;
107
- // Go: Uppercase first letter = exported
108
- case 'go':
109
- if (name.length === 0)
110
- return false;
111
- const first = name[0];
112
- // Must be uppercase letter (not a number or symbol)
113
- return first === first.toUpperCase() && first !== first.toLowerCase();
114
- // Rust: Check for 'pub' visibility modifier
115
- case 'rust':
116
- while (current) {
117
- if (current.type === 'visibility_modifier') {
118
- if (current.text?.includes('pub'))
119
- return true;
120
- }
121
- current = current.parent;
122
- }
123
- return false;
124
- // Kotlin: Default visibility is public (unlike Java)
125
- // visibility_modifier is inside modifiers, a sibling of the name node within the declaration
126
- case 'kotlin':
127
- while (current) {
128
- if (current.parent) {
129
- const visMod = findSiblingChild(current.parent, 'modifiers', 'visibility_modifier');
130
- if (visMod) {
131
- const text = visMod.text;
132
- if (text === 'private' || text === 'internal' || text === 'protected')
133
- return false;
134
- if (text === 'public')
135
- return true;
136
- }
137
- }
138
- current = current.parent;
139
- }
140
- // No visibility modifier = public (Kotlin default)
141
- return true;
142
- // C/C++: No native export concept at language level
143
- // Entry points will be detected via name patterns (main, etc.)
144
- case 'c':
145
- case 'cpp':
146
- return false;
147
- // Swift: Check for 'public' or 'open' access modifiers
148
- case 'swift':
149
- while (current) {
150
- if (current.type === 'modifiers' || current.type === 'visibility_modifier') {
151
- const text = current.text || '';
152
- if (text.includes('public') || text.includes('open'))
153
- return true;
154
- }
155
- current = current.parent;
156
- }
157
- return false;
158
- // PHP: Check for visibility modifier or top-level scope
159
- case 'php':
160
- while (current) {
161
- if (current.type === 'class_declaration' ||
162
- current.type === 'interface_declaration' ||
163
- current.type === 'trait_declaration' ||
164
- current.type === 'enum_declaration') {
165
- return true;
166
- }
167
- if (current.type === 'visibility_modifier') {
168
- return current.text === 'public';
169
- }
170
- current = current.parent;
171
- }
172
- return true; // Top-level functions are globally accessible
173
- default:
174
- return false;
175
- }
176
- };
8
+ import { typeConfigs } from './type-extractors/index.js';
9
+ import { getTreeSitterBufferSize, TREE_SITTER_MAX_BUFFER } from './constants.js';
10
+ // isNodeExported imported from ./export-detection.js (shared module)
11
+ // Re-export for backward compatibility with any external consumers
12
+ export { isNodeExported } from './export-detection.js';
177
13
  // ============================================================================
178
14
  // Worker-based parallel parsing
179
15
  // ============================================================================
@@ -186,7 +22,7 @@ const processParsingWithWorkers = async (graph, files, symbolTable, astCache, wo
186
22
  parseableFiles.push({ path: file.path, content: file.content });
187
23
  }
188
24
  if (parseableFiles.length === 0)
189
- return { imports: [], calls: [], heritage: [], routes: [] };
25
+ return { imports: [], calls: [], heritage: [], routes: [], constructorBindings: [] };
190
26
  const total = files.length;
191
27
  // Dispatch to worker pool — pool handles splitting into chunks and sub-batching
192
28
  const chunkResults = await workerPool.dispatch(parseableFiles, (filesProcessed) => {
@@ -197,6 +33,7 @@ const processParsingWithWorkers = async (graph, files, symbolTable, astCache, wo
197
33
  const allCalls = [];
198
34
  const allHeritage = [];
199
35
  const allRoutes = [];
36
+ const allConstructorBindings = [];
200
37
  for (const result of chunkResults) {
201
38
  for (const node of result.nodes) {
202
39
  graph.addNode({
@@ -209,16 +46,34 @@ const processParsingWithWorkers = async (graph, files, symbolTable, astCache, wo
209
46
  graph.addRelationship(rel);
210
47
  }
211
48
  for (const sym of result.symbols) {
212
- symbolTable.add(sym.filePath, sym.name, sym.nodeId, sym.type);
49
+ symbolTable.add(sym.filePath, sym.name, sym.nodeId, sym.type, {
50
+ parameterCount: sym.parameterCount,
51
+ returnType: sym.returnType,
52
+ ownerId: sym.ownerId,
53
+ });
213
54
  }
214
55
  allImports.push(...result.imports);
215
56
  allCalls.push(...result.calls);
216
57
  allHeritage.push(...result.heritage);
217
58
  allRoutes.push(...result.routes);
59
+ allConstructorBindings.push(...result.constructorBindings);
60
+ }
61
+ // Merge and log skipped languages from workers
62
+ const skippedLanguages = new Map();
63
+ for (const result of chunkResults) {
64
+ for (const [lang, count] of Object.entries(result.skippedLanguages)) {
65
+ skippedLanguages.set(lang, (skippedLanguages.get(lang) || 0) + count);
66
+ }
67
+ }
68
+ if (skippedLanguages.size > 0) {
69
+ const summary = Array.from(skippedLanguages.entries())
70
+ .map(([lang, count]) => `${lang}: ${count}`)
71
+ .join(', ');
72
+ console.warn(` Skipped unsupported languages: ${summary}`);
218
73
  }
219
74
  // Final progress
220
75
  onFileProgress?.(total, total, 'done');
221
- return { imports: allImports, calls: allCalls, heritage: allHeritage, routes: allRoutes };
76
+ return { imports: allImports, calls: allCalls, heritage: allHeritage, routes: allRoutes, constructorBindings: allConstructorBindings };
222
77
  };
223
78
  // ============================================================================
224
79
  // Sequential fallback (original implementation)
@@ -226,6 +81,7 @@ const processParsingWithWorkers = async (graph, files, symbolTable, astCache, wo
226
81
  const processParsingSequential = async (graph, files, symbolTable, astCache, onFileProgress) => {
227
82
  const parser = await loadParser();
228
83
  const total = files.length;
84
+ const skippedLanguages = new Map();
229
85
  for (let i = 0; i < files.length; i++) {
230
86
  const file = files[i];
231
87
  onFileProgress?.(i + 1, total, file.path);
@@ -234,18 +90,23 @@ const processParsingSequential = async (graph, files, symbolTable, astCache, onF
234
90
  const language = getLanguageFromFilename(file.path);
235
91
  if (!language)
236
92
  continue;
237
- // Skip very large files they can crash tree-sitter or cause OOM
238
- if (file.content.length > 512 * 1024)
93
+ // Skip unsupported languages (e.g. Swift when tree-sitter-swift not installed)
94
+ if (!isLanguageAvailable(language)) {
95
+ skippedLanguages.set(language, (skippedLanguages.get(language) || 0) + 1);
96
+ continue;
97
+ }
98
+ // Skip files larger than the max tree-sitter buffer (32 MB)
99
+ if (file.content.length > TREE_SITTER_MAX_BUFFER)
239
100
  continue;
240
101
  try {
241
102
  await loadLanguage(language, file.path);
242
103
  }
243
104
  catch {
244
- continue; // parser unavailable — already warned in pipeline
105
+ continue; // parser unavailable — safety net
245
106
  }
246
107
  let tree;
247
108
  try {
248
- tree = parser.parse(file.content, undefined, { bufferSize: 1024 * 256 });
109
+ tree = parser.parse(file.content, undefined, { bufferSize: getTreeSitterBufferSize(file.content.length) });
249
110
  }
250
111
  catch (parseError) {
251
112
  console.warn(`Skipping unparseable file: ${file.path}`);
@@ -330,11 +191,25 @@ const processParsingSequential = async (graph, files, symbolTable, astCache, onF
330
191
  nodeLabel = 'Template';
331
192
  const definitionNodeForRange = getDefinitionNodeFromCaptures(captureMap);
332
193
  const startLine = definitionNodeForRange ? definitionNodeForRange.startPosition.row : (nameNode ? nameNode.startPosition.row : 0);
333
- const nodeId = generateId(nodeLabel, `${file.path}:${nodeName}:${startLine}`);
194
+ const nodeId = generateId(nodeLabel, `${file.path}:${nodeName}`);
334
195
  const definitionNode = getDefinitionNodeFromCaptures(captureMap);
335
196
  const frameworkHint = definitionNode
336
197
  ? detectFrameworkFromAST(language, (definitionNode.text || '').slice(0, 300))
337
198
  : null;
199
+ // Extract method signature for Method/Constructor nodes
200
+ const methodSig = (nodeLabel === 'Function' || nodeLabel === 'Method' || nodeLabel === 'Constructor')
201
+ ? extractMethodSignature(definitionNode)
202
+ : undefined;
203
+ // Language-specific return type fallback (e.g. Ruby YARD @return [Type])
204
+ // Also upgrades uninformative AST types like PHP `array` with PHPDoc `@return User[]`
205
+ if (methodSig && (!methodSig.returnType || methodSig.returnType === 'array' || methodSig.returnType === 'iterable') && definitionNode) {
206
+ const tc = typeConfigs[language];
207
+ if (tc?.extractReturnType) {
208
+ const docReturn = tc.extractReturnType(definitionNode);
209
+ if (docReturn)
210
+ methodSig.returnType = docReturn;
211
+ }
212
+ }
338
213
  const node = {
339
214
  id: nodeId,
340
215
  label: nodeLabel,
@@ -349,10 +224,22 @@ const processParsingSequential = async (graph, files, symbolTable, astCache, onF
349
224
  astFrameworkMultiplier: frameworkHint.entryPointMultiplier,
350
225
  astFrameworkReason: frameworkHint.reason,
351
226
  } : {}),
227
+ ...(methodSig ? {
228
+ parameterCount: methodSig.parameterCount,
229
+ returnType: methodSig.returnType,
230
+ } : {}),
352
231
  },
353
232
  };
354
233
  graph.addNode(node);
355
- symbolTable.add(file.path, nodeName, nodeId, nodeLabel);
234
+ // Compute enclosing class for Method/Constructor/Property/Function — used for both ownerId and HAS_METHOD
235
+ // Function is included because Kotlin/Rust/Python capture class methods as Function nodes
236
+ const needsOwner = nodeLabel === 'Method' || nodeLabel === 'Constructor' || nodeLabel === 'Property' || nodeLabel === 'Function';
237
+ const enclosingClassId = needsOwner ? findEnclosingClassId(nameNode || definitionNodeForRange, file.path) : null;
238
+ symbolTable.add(file.path, nodeName, nodeId, nodeLabel, {
239
+ parameterCount: methodSig?.parameterCount,
240
+ returnType: methodSig?.returnType,
241
+ ownerId: enclosingClassId ?? undefined,
242
+ });
356
243
  const fileId = generateId('File', file.path);
357
244
  const relId = generateId('DEFINES', `${fileId}->${nodeId}`);
358
245
  const relationship = {
@@ -364,8 +251,25 @@ const processParsingSequential = async (graph, files, symbolTable, astCache, onF
364
251
  reason: '',
365
252
  };
366
253
  graph.addRelationship(relationship);
254
+ // ── HAS_METHOD: link method/constructor/property to enclosing class ──
255
+ if (enclosingClassId) {
256
+ graph.addRelationship({
257
+ id: generateId('HAS_METHOD', `${enclosingClassId}->${nodeId}`),
258
+ sourceId: enclosingClassId,
259
+ targetId: nodeId,
260
+ type: 'HAS_METHOD',
261
+ confidence: 1.0,
262
+ reason: '',
263
+ });
264
+ }
367
265
  });
368
266
  }
267
+ if (skippedLanguages.size > 0) {
268
+ const summary = Array.from(skippedLanguages.entries())
269
+ .map(([lang, count]) => `${lang}: ${count}`)
270
+ .join(', ');
271
+ console.warn(` Skipped unsupported languages: ${summary}`);
272
+ }
369
273
  };
370
274
  // ============================================================================
371
275
  // Public API
@@ -1,2 +1,6 @@
1
1
  import { PipelineProgress, PipelineResult } from '../../types/pipeline.js';
2
- export declare const runPipelineFromRepo: (repoPath: string, onProgress: (progress: PipelineProgress) => void) => Promise<PipelineResult>;
2
+ export interface PipelineOptions {
3
+ /** Skip MRO, community detection, and process extraction for faster test runs. */
4
+ skipGraphPhases?: boolean;
5
+ }
6
+ export declare const runPipelineFromRepo: (repoPath: string, onProgress: (progress: PipelineProgress) => void, options?: PipelineOptions) => Promise<PipelineResult>;