gitnexus 1.4.6 → 1.4.8

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 (99) hide show
  1. package/README.md +22 -1
  2. package/dist/cli/ai-context.d.ts +1 -1
  3. package/dist/cli/ai-context.js +1 -1
  4. package/dist/cli/analyze.d.ts +2 -0
  5. package/dist/cli/analyze.js +54 -21
  6. package/dist/cli/index.js +2 -1
  7. package/dist/cli/setup.js +78 -1
  8. package/dist/config/supported-languages.d.ts +30 -0
  9. package/dist/config/supported-languages.js +30 -0
  10. package/dist/core/embeddings/embedder.d.ts +6 -1
  11. package/dist/core/embeddings/embedder.js +65 -5
  12. package/dist/core/embeddings/embedding-pipeline.js +11 -9
  13. package/dist/core/embeddings/http-client.d.ts +31 -0
  14. package/dist/core/embeddings/http-client.js +179 -0
  15. package/dist/core/embeddings/index.d.ts +1 -0
  16. package/dist/core/embeddings/index.js +1 -0
  17. package/dist/core/embeddings/types.d.ts +1 -1
  18. package/dist/core/graph/types.d.ts +4 -3
  19. package/dist/core/ingestion/ast-helpers.d.ts +80 -0
  20. package/dist/core/ingestion/ast-helpers.js +738 -0
  21. package/dist/core/ingestion/call-analysis.d.ts +73 -0
  22. package/dist/core/ingestion/call-analysis.js +490 -0
  23. package/dist/core/ingestion/call-processor.d.ts +55 -2
  24. package/dist/core/ingestion/call-processor.js +673 -108
  25. package/dist/core/ingestion/call-routing.d.ts +23 -2
  26. package/dist/core/ingestion/call-routing.js +21 -0
  27. package/dist/core/ingestion/entry-point-scoring.js +36 -26
  28. package/dist/core/ingestion/framework-detection.d.ts +10 -2
  29. package/dist/core/ingestion/framework-detection.js +49 -12
  30. package/dist/core/ingestion/heritage-processor.js +47 -49
  31. package/dist/core/ingestion/import-processor.d.ts +1 -1
  32. package/dist/core/ingestion/import-processor.js +103 -194
  33. package/dist/core/ingestion/import-resolution.d.ts +101 -0
  34. package/dist/core/ingestion/import-resolution.js +251 -0
  35. package/dist/core/ingestion/language-config.d.ts +3 -0
  36. package/dist/core/ingestion/language-config.js +13 -0
  37. package/dist/core/ingestion/markdown-processor.d.ts +17 -0
  38. package/dist/core/ingestion/markdown-processor.js +124 -0
  39. package/dist/core/ingestion/mro-processor.js +8 -3
  40. package/dist/core/ingestion/named-binding-extraction.d.ts +9 -43
  41. package/dist/core/ingestion/named-binding-extraction.js +89 -79
  42. package/dist/core/ingestion/parsing-processor.d.ts +3 -2
  43. package/dist/core/ingestion/parsing-processor.js +27 -60
  44. package/dist/core/ingestion/pipeline.d.ts +10 -0
  45. package/dist/core/ingestion/pipeline.js +425 -4
  46. package/dist/core/ingestion/resolution-context.d.ts +5 -0
  47. package/dist/core/ingestion/resolution-context.js +7 -4
  48. package/dist/core/ingestion/resolvers/index.d.ts +1 -1
  49. package/dist/core/ingestion/resolvers/index.js +1 -1
  50. package/dist/core/ingestion/resolvers/jvm.d.ts +2 -1
  51. package/dist/core/ingestion/resolvers/jvm.js +25 -9
  52. package/dist/core/ingestion/resolvers/php.d.ts +14 -0
  53. package/dist/core/ingestion/resolvers/php.js +43 -3
  54. package/dist/core/ingestion/resolvers/utils.d.ts +5 -0
  55. package/dist/core/ingestion/resolvers/utils.js +16 -0
  56. package/dist/core/ingestion/symbol-table.d.ts +29 -3
  57. package/dist/core/ingestion/symbol-table.js +42 -9
  58. package/dist/core/ingestion/tree-sitter-queries.d.ts +12 -12
  59. package/dist/core/ingestion/tree-sitter-queries.js +243 -2
  60. package/dist/core/ingestion/type-env.d.ts +28 -1
  61. package/dist/core/ingestion/type-env.js +451 -72
  62. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +5 -0
  63. package/dist/core/ingestion/type-extractors/c-cpp.js +146 -2
  64. package/dist/core/ingestion/type-extractors/csharp.js +189 -16
  65. package/dist/core/ingestion/type-extractors/go.js +45 -0
  66. package/dist/core/ingestion/type-extractors/index.d.ts +1 -1
  67. package/dist/core/ingestion/type-extractors/index.js +1 -1
  68. package/dist/core/ingestion/type-extractors/jvm.js +244 -69
  69. package/dist/core/ingestion/type-extractors/php.js +31 -4
  70. package/dist/core/ingestion/type-extractors/python.js +89 -17
  71. package/dist/core/ingestion/type-extractors/ruby.js +17 -2
  72. package/dist/core/ingestion/type-extractors/rust.js +72 -4
  73. package/dist/core/ingestion/type-extractors/shared.d.ts +12 -2
  74. package/dist/core/ingestion/type-extractors/shared.js +115 -13
  75. package/dist/core/ingestion/type-extractors/swift.js +7 -6
  76. package/dist/core/ingestion/type-extractors/types.d.ts +54 -11
  77. package/dist/core/ingestion/type-extractors/typescript.js +171 -9
  78. package/dist/core/ingestion/utils.d.ts +2 -95
  79. package/dist/core/ingestion/utils.js +3 -892
  80. package/dist/core/ingestion/workers/parse-worker.d.ts +36 -11
  81. package/dist/core/ingestion/workers/parse-worker.js +116 -95
  82. package/dist/core/lbug/csv-generator.js +18 -1
  83. package/dist/core/lbug/lbug-adapter.d.ts +12 -0
  84. package/dist/core/lbug/lbug-adapter.js +71 -4
  85. package/dist/core/lbug/schema.d.ts +6 -4
  86. package/dist/core/lbug/schema.js +27 -3
  87. package/dist/mcp/core/embedder.js +11 -3
  88. package/dist/mcp/core/lbug-adapter.d.ts +22 -0
  89. package/dist/mcp/core/lbug-adapter.js +178 -23
  90. package/dist/mcp/local/local-backend.d.ts +22 -0
  91. package/dist/mcp/local/local-backend.js +136 -32
  92. package/dist/mcp/resources.js +13 -0
  93. package/dist/mcp/server.js +26 -4
  94. package/dist/mcp/tools.js +17 -7
  95. package/dist/server/api.d.ts +19 -1
  96. package/dist/server/api.js +66 -6
  97. package/dist/storage/git.d.ts +12 -0
  98. package/dist/storage/git.js +21 -0
  99. package/package.json +12 -4
@@ -1,4 +1,4 @@
1
- import { SupportedLanguages } from '../../config/supported-languages.js';
1
+ import { findChild } from './resolvers/utils.js';
2
2
  /**
3
3
  * Walk a named-binding re-export chain through NamedImportMap.
4
4
  *
@@ -41,40 +41,6 @@ export function walkBindingChain(name, currentFilePath, symbolTable, namedImport
41
41
  }
42
42
  return null;
43
43
  }
44
- /**
45
- * Extract named bindings from an import AST node.
46
- * Returns undefined if the import is not a named import (e.g., import * or default).
47
- *
48
- * TS: import { User, Repo as R } from './models'
49
- * → [{local:'User', exported:'User'}, {local:'R', exported:'Repo'}]
50
- *
51
- * Python: from models import User, Repo as R
52
- * → [{local:'User', exported:'User'}, {local:'R', exported:'Repo'}]
53
- */
54
- export function extractNamedBindings(importNode, language) {
55
- if (language === SupportedLanguages.TypeScript || language === SupportedLanguages.JavaScript) {
56
- return extractTsNamedBindings(importNode);
57
- }
58
- if (language === SupportedLanguages.Python) {
59
- return extractPythonNamedBindings(importNode);
60
- }
61
- if (language === SupportedLanguages.Kotlin) {
62
- return extractKotlinNamedBindings(importNode);
63
- }
64
- if (language === SupportedLanguages.Rust) {
65
- return extractRustNamedBindings(importNode);
66
- }
67
- if (language === SupportedLanguages.PHP) {
68
- return extractPhpNamedBindings(importNode);
69
- }
70
- if (language === SupportedLanguages.CSharp) {
71
- return extractCsharpNamedBindings(importNode);
72
- }
73
- if (language === SupportedLanguages.Java) {
74
- return extractJavaNamedBindings(importNode);
75
- }
76
- return undefined;
77
- }
78
44
  export function extractTsNamedBindings(importNode) {
79
45
  // import_statement > import_clause > named_imports > import_specifier*
80
46
  const importClause = findChild(importNode, 'import_clause');
@@ -133,34 +99,52 @@ export function extractTsNamedBindings(importNode) {
133
99
  return undefined;
134
100
  }
135
101
  export function extractPythonNamedBindings(importNode) {
136
- // Only from import_from_statement, not plain import_statement
137
- if (importNode.type !== 'import_from_statement')
138
- return undefined;
139
- const bindings = [];
140
- for (let i = 0; i < importNode.namedChildCount; i++) {
141
- const child = importNode.namedChild(i);
142
- if (!child)
143
- continue;
144
- if (child.type === 'dotted_name') {
145
- // Skip the module_name (first dotted_name is the source module)
146
- const fieldName = importNode.childForFieldName?.('module_name');
147
- if (fieldName && child.startIndex === fieldName.startIndex)
102
+ // Handle: from x import User, Repo as R
103
+ if (importNode.type === 'import_from_statement') {
104
+ const bindings = [];
105
+ for (let i = 0; i < importNode.namedChildCount; i++) {
106
+ const child = importNode.namedChild(i);
107
+ if (!child)
148
108
  continue;
149
- // This is an imported name: from x import User
150
- const name = child.text;
151
- if (name)
152
- bindings.push({ local: name, exported: name });
109
+ if (child.type === 'dotted_name') {
110
+ // Skip the module_name (first dotted_name is the source module)
111
+ const fieldName = importNode.childForFieldName?.('module_name');
112
+ if (fieldName && child.startIndex === fieldName.startIndex)
113
+ continue;
114
+ // This is an imported name: from x import User
115
+ const name = child.text;
116
+ if (name)
117
+ bindings.push({ local: name, exported: name });
118
+ }
119
+ if (child.type === 'aliased_import') {
120
+ // from x import Repo as R
121
+ const dottedName = findChild(child, 'dotted_name');
122
+ const aliasIdent = findChild(child, 'identifier');
123
+ if (dottedName && aliasIdent) {
124
+ bindings.push({ local: aliasIdent.text, exported: dottedName.text });
125
+ }
126
+ }
153
127
  }
154
- if (child.type === 'aliased_import') {
155
- // from x import Repo as R
128
+ return bindings.length > 0 ? bindings : undefined;
129
+ }
130
+ // Handle: import numpy as np (import_statement with aliased_import child)
131
+ // Tagged with isModuleAlias so applyImportResult routes these directly to
132
+ // moduleAliasMap (e.g. "np" → "numpy.py") instead of namedImportMap.
133
+ if (importNode.type === 'import_statement') {
134
+ const bindings = [];
135
+ for (let i = 0; i < importNode.namedChildCount; i++) {
136
+ const child = importNode.namedChild(i);
137
+ if (!child || child.type !== 'aliased_import')
138
+ continue;
156
139
  const dottedName = findChild(child, 'dotted_name');
157
140
  const aliasIdent = findChild(child, 'identifier');
158
141
  if (dottedName && aliasIdent) {
159
- bindings.push({ local: aliasIdent.text, exported: dottedName.text });
142
+ bindings.push({ local: aliasIdent.text, exported: dottedName.text, isModuleAlias: true });
160
143
  }
161
144
  }
145
+ return bindings.length > 0 ? bindings : undefined;
162
146
  }
163
- return bindings.length > 0 ? bindings : undefined;
147
+ return undefined;
164
148
  }
165
149
  export function extractKotlinNamedBindings(importNode) {
166
150
  // import_header > identifier + import_alias > simple_identifier
@@ -180,15 +164,21 @@ export function extractKotlinNamedBindings(importNode) {
180
164
  return [{ local: aliasIdent.text, exported: exportedName }];
181
165
  }
182
166
  // Non-aliased: import com.example.User → local="User", exported="User"
167
+ // Also handles top-level function imports: import models.getUser → local="getUser"
183
168
  // Skip wildcard imports (ending in *)
184
169
  if (fullText.endsWith('.*') || fullText.endsWith('*'))
185
170
  return undefined;
186
- // Skip lowercase last segments — those are member/function imports (e.g.,
187
- // import util.OneArg.writeAudit), not class imports. Multiple member imports
171
+ // Skip class-member imports (e.g., import util.OneArg.writeAudit) where the
172
+ // second-to-last segment is PascalCase (a class name). Multiple member imports
188
173
  // with the same function name would collide in NamedImportMap, breaking
189
- // arity-based disambiguation.
190
- if (exportedName[0] && exportedName[0] === exportedName[0].toLowerCase())
191
- return undefined;
174
+ // arity-based disambiguation. Top-level function imports (import models.getUser)
175
+ // and class imports (import models.User) have package-only prefixes.
176
+ const segments = fullText.split('.');
177
+ if (segments.length >= 3) {
178
+ const parentSegment = segments[segments.length - 2];
179
+ if (parentSegment[0] && parentSegment[0] === parentSegment[0].toUpperCase())
180
+ return undefined;
181
+ }
192
182
  return [{ local: exportedName, exported: exportedName }];
193
183
  }
194
184
  export function extractRustNamedBindings(importNode) {
@@ -260,6 +250,12 @@ export function extractPhpNamedBindings(importNode) {
260
250
  // namespace_use_declaration > namespace_use_group > namespace_use_clause* (grouped)
261
251
  if (importNode.type !== 'namespace_use_declaration')
262
252
  return undefined;
253
+ // Skip 'use function' and 'use const' declarations — these import callables/constants,
254
+ // not class types, and should not be added to namedImportMap as type bindings.
255
+ const useTypeNode = importNode.childForFieldName?.('type');
256
+ if (useTypeNode && (useTypeNode.text === 'function' || useTypeNode.text === 'const')) {
257
+ return undefined;
258
+ }
263
259
  const bindings = [];
264
260
  // Collect all clauses — from direct children AND from namespace_use_group
265
261
  const clauses = [];
@@ -311,11 +307,20 @@ export function extractPhpNamedBindings(importNode) {
311
307
  return bindings.length > 0 ? bindings : undefined;
312
308
  }
313
309
  export function extractCsharpNamedBindings(importNode) {
314
- // using_directive with identifier (alias) + qualified_name (target)
310
+ // using_directive three forms:
311
+ // using Alias = NS.Type; → aliasIdent + qualifiedName
312
+ // using static NS.Type; → static + qualifiedName (no alias)
313
+ // using NS; → qualifiedName only (namespace, not capturable)
315
314
  if (importNode.type !== 'using_directive')
316
315
  return undefined;
317
316
  let aliasIdent = null;
318
317
  let qualifiedName = null;
318
+ let isStatic = false;
319
+ for (let i = 0; i < importNode.childCount; i++) {
320
+ const child = importNode.child(i);
321
+ if (child?.text === 'static')
322
+ isStatic = true;
323
+ }
319
324
  for (let i = 0; i < importNode.namedChildCount; i++) {
320
325
  const child = importNode.namedChild(i);
321
326
  if (child?.type === 'identifier' && !aliasIdent)
@@ -323,22 +328,34 @@ export function extractCsharpNamedBindings(importNode) {
323
328
  else if (child?.type === 'qualified_name')
324
329
  qualifiedName = child;
325
330
  }
326
- if (!aliasIdent || !qualifiedName)
327
- return undefined;
328
- const fullText = qualifiedName.text;
329
- const exportedName = fullText.includes('.') ? fullText.split('.').pop() : fullText;
330
- return [{ local: aliasIdent.text, exported: exportedName }];
331
+ // Form 1: using Alias = NS.Type;
332
+ if (aliasIdent && qualifiedName) {
333
+ const fullText = qualifiedName.text;
334
+ const exportedName = fullText.includes('.') ? fullText.split('.').pop() : fullText;
335
+ return [{ local: aliasIdent.text, exported: exportedName }];
336
+ }
337
+ // Form 2: using static NS.Type; — last segment is the class name
338
+ if (isStatic && qualifiedName) {
339
+ const fullText = qualifiedName.text;
340
+ const lastSegment = fullText.includes('.') ? fullText.split('.').pop() : fullText;
341
+ return [{ local: lastSegment, exported: lastSegment }];
342
+ }
343
+ // Form 3: using NS; — namespace import, can't resolve to per-symbol bindings
344
+ return undefined;
331
345
  }
332
346
  export function extractJavaNamedBindings(importNode) {
333
347
  // import_declaration > scoped_identifier "com.example.models.User"
334
348
  // Wildcard imports (.*) don't produce named bindings
335
349
  if (importNode.type !== 'import_declaration')
336
350
  return undefined;
337
- // Check for asterisk (wildcard import) skip those
351
+ // Check for asterisk (wildcard import) and static modifier
352
+ let isStatic = false;
338
353
  for (let i = 0; i < importNode.childCount; i++) {
339
354
  const child = importNode.child(i);
340
355
  if (child?.type === 'asterisk')
341
356
  return undefined;
357
+ if (child?.text === 'static')
358
+ isStatic = true;
342
359
  }
343
360
  const scopedId = findChild(importNode, 'scoped_identifier');
344
361
  if (!scopedId)
@@ -347,17 +364,10 @@ export function extractJavaNamedBindings(importNode) {
347
364
  const lastDot = fullText.lastIndexOf('.');
348
365
  if (lastDot === -1)
349
366
  return undefined;
350
- const className = fullText.slice(lastDot + 1);
351
- // Skip lowercase names — those are package imports, not class imports
352
- if (className[0] && className[0] === className[0].toLowerCase())
367
+ const name = fullText.slice(lastDot + 1);
368
+ // Non-static: skip lowercase names — those are package imports, not class imports.
369
+ // Static: allow lowercase `import static models.UserFactory.getUser` imports a method.
370
+ if (!isStatic && name[0] && name[0] === name[0].toLowerCase())
353
371
  return undefined;
354
- return [{ local: className, exported: className }];
355
- }
356
- function findChild(node, type) {
357
- for (let i = 0; i < node.namedChildCount; i++) {
358
- const child = node.namedChild(i);
359
- if (child?.type === type)
360
- return child;
361
- }
362
- return null;
372
+ return [{ local: name, exported: name }];
363
373
  }
@@ -2,16 +2,17 @@ import { KnowledgeGraph } from '../graph/types.js';
2
2
  import { SymbolTable } from './symbol-table.js';
3
3
  import { ASTCache } from './ast-cache.js';
4
4
  import { WorkerPool } from './workers/worker-pool.js';
5
- import type { ExtractedImport, ExtractedCall, ExtractedHeritage, ExtractedRoute, FileConstructorBindings } from './workers/parse-worker.js';
5
+ import type { ExtractedImport, ExtractedCall, ExtractedAssignment, ExtractedHeritage, ExtractedRoute, FileConstructorBindings, FileTypeEnvBindings } from './workers/parse-worker.js';
6
6
  export type FileProgressCallback = (current: number, total: number, filePath: string) => void;
7
7
  export interface WorkerExtractedData {
8
8
  imports: ExtractedImport[];
9
9
  calls: ExtractedCall[];
10
+ assignments: ExtractedAssignment[];
10
11
  heritage: ExtractedHeritage[];
11
12
  routes: ExtractedRoute[];
12
13
  constructorBindings: FileConstructorBindings[];
14
+ typeEnvBindings: FileTypeEnvBindings[];
13
15
  }
14
- export { isNodeExported } from './export-detection.js';
15
16
  export declare const processParsing: (graph: KnowledgeGraph, files: {
16
17
  path: string;
17
18
  content: string;
@@ -2,14 +2,12 @@ import Parser from 'tree-sitter';
2
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 { getLanguageFromFilename, yieldToEventLoop, getDefinitionNodeFromCaptures, findEnclosingClassId, extractMethodSignature } from './utils.js';
5
+ import { getLanguageFromFilename, yieldToEventLoop, getDefinitionNodeFromCaptures, findEnclosingClassId, extractMethodSignature, getLabelFromCaptures } from './utils.js';
6
+ import { extractPropertyDeclaredType } from './type-extractors/shared.js';
6
7
  import { isNodeExported } from './export-detection.js';
7
8
  import { detectFrameworkFromAST } from './framework-detection.js';
8
9
  import { typeConfigs } from './type-extractors/index.js';
9
10
  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';
13
11
  // ============================================================================
14
12
  // Worker-based parallel parsing
15
13
  // ============================================================================
@@ -22,7 +20,7 @@ const processParsingWithWorkers = async (graph, files, symbolTable, astCache, wo
22
20
  parseableFiles.push({ path: file.path, content: file.content });
23
21
  }
24
22
  if (parseableFiles.length === 0)
25
- return { imports: [], calls: [], heritage: [], routes: [], constructorBindings: [] };
23
+ return { imports: [], calls: [], assignments: [], heritage: [], routes: [], constructorBindings: [], typeEnvBindings: [] };
26
24
  const total = files.length;
27
25
  // Dispatch to worker pool — pool handles splitting into chunks and sub-batching
28
26
  const chunkResults = await workerPool.dispatch(parseableFiles, (filesProcessed) => {
@@ -31,9 +29,11 @@ const processParsingWithWorkers = async (graph, files, symbolTable, astCache, wo
31
29
  // Merge results from all workers into graph and symbol table
32
30
  const allImports = [];
33
31
  const allCalls = [];
32
+ const allAssignments = [];
34
33
  const allHeritage = [];
35
34
  const allRoutes = [];
36
35
  const allConstructorBindings = [];
36
+ const allTypeEnvBindings = [];
37
37
  for (const result of chunkResults) {
38
38
  for (const node of result.nodes) {
39
39
  graph.addNode({
@@ -48,15 +48,20 @@ const processParsingWithWorkers = async (graph, files, symbolTable, astCache, wo
48
48
  for (const sym of result.symbols) {
49
49
  symbolTable.add(sym.filePath, sym.name, sym.nodeId, sym.type, {
50
50
  parameterCount: sym.parameterCount,
51
+ requiredParameterCount: sym.requiredParameterCount,
52
+ parameterTypes: sym.parameterTypes,
51
53
  returnType: sym.returnType,
54
+ declaredType: sym.declaredType,
52
55
  ownerId: sym.ownerId,
53
56
  });
54
57
  }
55
58
  allImports.push(...result.imports);
56
59
  allCalls.push(...result.calls);
60
+ allAssignments.push(...result.assignments);
57
61
  allHeritage.push(...result.heritage);
58
62
  allRoutes.push(...result.routes);
59
63
  allConstructorBindings.push(...result.constructorBindings);
64
+ allTypeEnvBindings.push(...result.typeEnvBindings);
60
65
  }
61
66
  // Merge and log skipped languages from workers
62
67
  const skippedLanguages = new Map();
@@ -73,7 +78,7 @@ const processParsingWithWorkers = async (graph, files, symbolTable, astCache, wo
73
78
  }
74
79
  // Final progress
75
80
  onFileProgress?.(total, total, 'done');
76
- return { imports: allImports, calls: allCalls, heritage: allHeritage, routes: allRoutes, constructorBindings: allConstructorBindings };
81
+ return { imports: allImports, calls: allCalls, assignments: allAssignments, heritage: allHeritage, routes: allRoutes, constructorBindings: allConstructorBindings, typeEnvBindings: allTypeEnvBindings };
77
82
  };
78
83
  // ============================================================================
79
84
  // Sequential fallback (original implementation)
@@ -133,62 +138,14 @@ const processParsingSequential = async (graph, files, symbolTable, astCache, onF
133
138
  match.captures.forEach(c => {
134
139
  captureMap[c.name] = c.node;
135
140
  });
136
- if (captureMap['import']) {
141
+ const nodeLabel = getLabelFromCaptures(captureMap, language);
142
+ if (!nodeLabel)
137
143
  return;
138
- }
139
- if (captureMap['call']) {
140
- return;
141
- }
142
144
  const nameNode = captureMap['name'];
143
145
  // Synthesize name for constructors without explicit @name capture (e.g. Swift init)
144
- if (!nameNode && !captureMap['definition.constructor'])
146
+ if (!nameNode && nodeLabel !== 'Constructor')
145
147
  return;
146
148
  const nodeName = nameNode ? nameNode.text : 'init';
147
- let nodeLabel = 'CodeElement';
148
- if (captureMap['definition.function'])
149
- nodeLabel = 'Function';
150
- else if (captureMap['definition.class'])
151
- nodeLabel = 'Class';
152
- else if (captureMap['definition.interface'])
153
- nodeLabel = 'Interface';
154
- else if (captureMap['definition.method'])
155
- nodeLabel = 'Method';
156
- else if (captureMap['definition.struct'])
157
- nodeLabel = 'Struct';
158
- else if (captureMap['definition.enum'])
159
- nodeLabel = 'Enum';
160
- else if (captureMap['definition.namespace'])
161
- nodeLabel = 'Namespace';
162
- else if (captureMap['definition.module'])
163
- nodeLabel = 'Module';
164
- else if (captureMap['definition.trait'])
165
- nodeLabel = 'Trait';
166
- else if (captureMap['definition.impl'])
167
- nodeLabel = 'Impl';
168
- else if (captureMap['definition.type'])
169
- nodeLabel = 'TypeAlias';
170
- else if (captureMap['definition.const'])
171
- nodeLabel = 'Const';
172
- else if (captureMap['definition.static'])
173
- nodeLabel = 'Static';
174
- else if (captureMap['definition.typedef'])
175
- nodeLabel = 'Typedef';
176
- else if (captureMap['definition.macro'])
177
- nodeLabel = 'Macro';
178
- else if (captureMap['definition.union'])
179
- nodeLabel = 'Union';
180
- else if (captureMap['definition.property'])
181
- nodeLabel = 'Property';
182
- else if (captureMap['definition.record'])
183
- nodeLabel = 'Record';
184
- else if (captureMap['definition.delegate'])
185
- nodeLabel = 'Delegate';
186
- else if (captureMap['definition.annotation'])
187
- nodeLabel = 'Annotation';
188
- else if (captureMap['definition.constructor'])
189
- nodeLabel = 'Constructor';
190
- else if (captureMap['definition.template'])
191
- nodeLabel = 'Template';
192
149
  const definitionNodeForRange = getDefinitionNodeFromCaptures(captureMap);
193
150
  const startLine = definitionNodeForRange ? definitionNodeForRange.startPosition.row : (nameNode ? nameNode.startPosition.row : 0);
194
151
  const nodeId = generateId(nodeLabel, `${file.path}:${nodeName}`);
@@ -226,6 +183,8 @@ const processParsingSequential = async (graph, files, symbolTable, astCache, onF
226
183
  } : {}),
227
184
  ...(methodSig ? {
228
185
  parameterCount: methodSig.parameterCount,
186
+ ...(methodSig.requiredParameterCount !== undefined ? { requiredParameterCount: methodSig.requiredParameterCount } : {}),
187
+ ...(methodSig.parameterTypes ? { parameterTypes: methodSig.parameterTypes } : {}),
229
188
  returnType: methodSig.returnType,
230
189
  } : {}),
231
190
  },
@@ -235,9 +194,16 @@ const processParsingSequential = async (graph, files, symbolTable, astCache, onF
235
194
  // Function is included because Kotlin/Rust/Python capture class methods as Function nodes
236
195
  const needsOwner = nodeLabel === 'Method' || nodeLabel === 'Constructor' || nodeLabel === 'Property' || nodeLabel === 'Function';
237
196
  const enclosingClassId = needsOwner ? findEnclosingClassId(nameNode || definitionNodeForRange, file.path) : null;
197
+ // Extract declared type for Property nodes (field/property type annotations)
198
+ const declaredType = (nodeLabel === 'Property' && definitionNode)
199
+ ? extractPropertyDeclaredType(definitionNode)
200
+ : undefined;
238
201
  symbolTable.add(file.path, nodeName, nodeId, nodeLabel, {
239
202
  parameterCount: methodSig?.parameterCount,
203
+ requiredParameterCount: methodSig?.requiredParameterCount,
204
+ parameterTypes: methodSig?.parameterTypes,
240
205
  returnType: methodSig?.returnType,
206
+ declaredType,
241
207
  ownerId: enclosingClassId ?? undefined,
242
208
  });
243
209
  const fileId = generateId('File', file.path);
@@ -251,13 +217,14 @@ const processParsingSequential = async (graph, files, symbolTable, astCache, onF
251
217
  reason: '',
252
218
  };
253
219
  graph.addRelationship(relationship);
254
- // ── HAS_METHOD: link method/constructor/property to enclosing class ──
220
+ // ── HAS_METHOD / HAS_PROPERTY: link member to enclosing class ──
255
221
  if (enclosingClassId) {
222
+ const memberEdgeType = nodeLabel === 'Property' ? 'HAS_PROPERTY' : 'HAS_METHOD';
256
223
  graph.addRelationship({
257
- id: generateId('HAS_METHOD', `${enclosingClassId}->${nodeId}`),
224
+ id: generateId(memberEdgeType, `${enclosingClassId}->${nodeId}`),
258
225
  sourceId: enclosingClassId,
259
226
  targetId: nodeId,
260
- type: 'HAS_METHOD',
227
+ type: memberEdgeType,
261
228
  confidence: 1.0,
262
229
  reason: '',
263
230
  });
@@ -1,6 +1,16 @@
1
1
  import { PipelineProgress, PipelineResult } from '../../types/pipeline.js';
2
+ /** A group of files with no mutual dependencies, safe to process in parallel. */
3
+ type IndependentFileGroup = readonly string[];
4
+ /** Kahn's algorithm: returns files grouped by topological level.
5
+ * Files in the same level have no mutual dependencies — safe to process in parallel.
6
+ * Files in cycles are returned as a final group (no cross-cycle propagation). */
7
+ export declare function topologicalLevelSort(importMap: ReadonlyMap<string, ReadonlySet<string>>): {
8
+ levels: readonly IndependentFileGroup[];
9
+ cycleCount: number;
10
+ };
2
11
  export interface PipelineOptions {
3
12
  /** Skip MRO, community detection, and process extraction for faster test runs. */
4
13
  skipGraphPhases?: boolean;
5
14
  }
6
15
  export declare const runPipelineFromRepo: (repoPath: string, onProgress: (progress: PipelineProgress) => void, options?: PipelineOptions) => Promise<PipelineResult>;
16
+ export {};