gitnexus 1.3.11 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +22 -2
  2. package/dist/cli/ai-context.d.ts +2 -1
  3. package/dist/cli/ai-context.js +15 -6
  4. package/dist/cli/analyze.d.ts +2 -0
  5. package/dist/cli/analyze.js +12 -2
  6. package/dist/cli/index.js +2 -0
  7. package/dist/cli/skill-gen.d.ts +26 -0
  8. package/dist/cli/skill-gen.js +549 -0
  9. package/dist/core/graph/types.d.ts +5 -2
  10. package/dist/core/ingestion/call-processor.d.ts +5 -5
  11. package/dist/core/ingestion/call-processor.js +173 -260
  12. package/dist/core/ingestion/constants.d.ts +16 -0
  13. package/dist/core/ingestion/constants.js +16 -0
  14. package/dist/core/ingestion/entry-point-scoring.d.ts +2 -1
  15. package/dist/core/ingestion/entry-point-scoring.js +81 -22
  16. package/dist/core/ingestion/export-detection.d.ts +18 -0
  17. package/dist/core/ingestion/export-detection.js +230 -0
  18. package/dist/core/ingestion/framework-detection.d.ts +5 -1
  19. package/dist/core/ingestion/framework-detection.js +39 -8
  20. package/dist/core/ingestion/heritage-processor.d.ts +13 -4
  21. package/dist/core/ingestion/heritage-processor.js +92 -28
  22. package/dist/core/ingestion/import-processor.d.ts +17 -19
  23. package/dist/core/ingestion/import-processor.js +170 -695
  24. package/dist/core/ingestion/language-config.d.ts +46 -0
  25. package/dist/core/ingestion/language-config.js +167 -0
  26. package/dist/core/ingestion/mro-processor.d.ts +45 -0
  27. package/dist/core/ingestion/mro-processor.js +369 -0
  28. package/dist/core/ingestion/named-binding-extraction.d.ts +61 -0
  29. package/dist/core/ingestion/named-binding-extraction.js +363 -0
  30. package/dist/core/ingestion/parsing-processor.d.ts +1 -10
  31. package/dist/core/ingestion/parsing-processor.js +41 -177
  32. package/dist/core/ingestion/pipeline.js +26 -24
  33. package/dist/core/ingestion/process-processor.js +2 -1
  34. package/dist/core/ingestion/resolvers/csharp.d.ts +22 -0
  35. package/dist/core/ingestion/resolvers/csharp.js +109 -0
  36. package/dist/core/ingestion/resolvers/go.d.ts +19 -0
  37. package/dist/core/ingestion/resolvers/go.js +42 -0
  38. package/dist/core/ingestion/resolvers/index.d.ts +16 -0
  39. package/dist/core/ingestion/resolvers/index.js +11 -0
  40. package/dist/core/ingestion/resolvers/jvm.d.ts +23 -0
  41. package/dist/core/ingestion/resolvers/jvm.js +87 -0
  42. package/dist/core/ingestion/resolvers/php.d.ts +15 -0
  43. package/dist/core/ingestion/resolvers/php.js +35 -0
  44. package/dist/core/ingestion/resolvers/rust.d.ts +15 -0
  45. package/dist/core/ingestion/resolvers/rust.js +73 -0
  46. package/dist/core/ingestion/resolvers/standard.d.ts +28 -0
  47. package/dist/core/ingestion/resolvers/standard.js +145 -0
  48. package/dist/core/ingestion/resolvers/utils.d.ts +33 -0
  49. package/dist/core/ingestion/resolvers/utils.js +120 -0
  50. package/dist/core/ingestion/symbol-resolver.d.ts +32 -0
  51. package/dist/core/ingestion/symbol-resolver.js +83 -0
  52. package/dist/core/ingestion/symbol-table.d.ts +12 -1
  53. package/dist/core/ingestion/symbol-table.js +19 -12
  54. package/dist/core/ingestion/tree-sitter-queries.d.ts +11 -11
  55. package/dist/core/ingestion/tree-sitter-queries.js +114 -9
  56. package/dist/core/ingestion/type-env.d.ts +27 -0
  57. package/dist/core/ingestion/type-env.js +86 -0
  58. package/dist/core/ingestion/type-extractors/c-cpp.d.ts +2 -0
  59. package/dist/core/ingestion/type-extractors/c-cpp.js +60 -0
  60. package/dist/core/ingestion/type-extractors/csharp.d.ts +2 -0
  61. package/dist/core/ingestion/type-extractors/csharp.js +89 -0
  62. package/dist/core/ingestion/type-extractors/go.d.ts +2 -0
  63. package/dist/core/ingestion/type-extractors/go.js +105 -0
  64. package/dist/core/ingestion/type-extractors/index.d.ts +21 -0
  65. package/dist/core/ingestion/type-extractors/index.js +29 -0
  66. package/dist/core/ingestion/type-extractors/jvm.d.ts +3 -0
  67. package/dist/core/ingestion/type-extractors/jvm.js +121 -0
  68. package/dist/core/ingestion/type-extractors/php.d.ts +2 -0
  69. package/dist/core/ingestion/type-extractors/php.js +31 -0
  70. package/dist/core/ingestion/type-extractors/python.d.ts +2 -0
  71. package/dist/core/ingestion/type-extractors/python.js +41 -0
  72. package/dist/core/ingestion/type-extractors/rust.d.ts +2 -0
  73. package/dist/core/ingestion/type-extractors/rust.js +39 -0
  74. package/dist/core/ingestion/type-extractors/shared.d.ts +17 -0
  75. package/dist/core/ingestion/type-extractors/shared.js +97 -0
  76. package/dist/core/ingestion/type-extractors/swift.d.ts +2 -0
  77. package/dist/core/ingestion/type-extractors/swift.js +43 -0
  78. package/dist/core/ingestion/type-extractors/types.d.ts +14 -0
  79. package/dist/core/ingestion/type-extractors/types.js +1 -0
  80. package/dist/core/ingestion/type-extractors/typescript.d.ts +2 -0
  81. package/dist/core/ingestion/type-extractors/typescript.js +46 -0
  82. package/dist/core/ingestion/utils.d.ts +67 -0
  83. package/dist/core/ingestion/utils.js +691 -4
  84. package/dist/core/ingestion/workers/parse-worker.d.ts +20 -3
  85. package/dist/core/ingestion/workers/parse-worker.js +84 -345
  86. package/dist/core/kuzu/csv-generator.js +19 -3
  87. package/dist/core/kuzu/kuzu-adapter.js +3 -0
  88. package/dist/core/kuzu/schema.d.ts +3 -3
  89. package/dist/core/kuzu/schema.js +16 -1
  90. package/dist/mcp/tools.js +12 -3
  91. package/package.json +1 -1
@@ -2,16 +2,59 @@
2
2
  * Heritage Processor
3
3
  *
4
4
  * Extracts class inheritance relationships:
5
- * - EXTENDS: Class extends another Class (TS, JS, Python)
6
- * - IMPLEMENTS: Class implements an Interface (TS only)
5
+ * - EXTENDS: Class extends another Class (TS, JS, Python, C#, C++)
6
+ * - IMPLEMENTS: Class implements an Interface (TS, C#, Java, Kotlin, PHP)
7
+ *
8
+ * Languages like C# use a single `base_list` for both class and interface parents.
9
+ * We resolve the correct edge type by checking the symbol table: if the parent is
10
+ * registered as an Interface, we emit IMPLEMENTS; otherwise EXTENDS. For unresolved
11
+ * external symbols, the fallback heuristic is language-gated:
12
+ * - C# / Java: apply the `I[A-Z]` naming convention (e.g. IDisposable → IMPLEMENTS)
13
+ * - Swift: default to IMPLEMENTS (protocol conformance is more common than class inheritance)
14
+ * - All other languages: default to EXTENDS
7
15
  */
8
16
  import Parser from 'tree-sitter';
9
- import { loadParser, loadLanguage } from '../tree-sitter/parser-loader.js';
17
+ import { isLanguageAvailable, loadParser, loadLanguage } from '../tree-sitter/parser-loader.js';
10
18
  import { LANGUAGE_QUERIES } from './tree-sitter-queries.js';
11
19
  import { generateId } from '../../lib/utils.js';
12
- import { getLanguageFromFilename, yieldToEventLoop } from './utils.js';
13
- export const processHeritage = async (graph, files, astCache, symbolTable, onProgress) => {
20
+ import { getLanguageFromFilename, isVerboseIngestionEnabled, yieldToEventLoop } from './utils.js';
21
+ import { SupportedLanguages } from '../../config/supported-languages.js';
22
+ import { getTreeSitterBufferSize } from './constants.js';
23
+ import { resolveSymbol } from './symbol-resolver.js';
24
+ /** C#/Java convention: interfaces start with I followed by an uppercase letter */
25
+ const INTERFACE_NAME_RE = /^I[A-Z]/;
26
+ /**
27
+ * Determine whether a heritage.extends capture is actually an IMPLEMENTS relationship.
28
+ * Uses the symbol table first (authoritative — Tier 1); falls back to a language-gated
29
+ * heuristic for external symbols not present in the graph:
30
+ * - C# / Java: `I[A-Z]` naming convention
31
+ * - Swift: default IMPLEMENTS (protocol conformance is the norm)
32
+ * - All others: default EXTENDS
33
+ */
34
+ const resolveExtendsType = (parentName, currentFilePath, symbolTable, importMap, language, packageMap) => {
35
+ const resolved = resolveSymbol(parentName, currentFilePath, symbolTable, importMap, packageMap);
36
+ if (resolved) {
37
+ const isInterface = resolved.type === 'Interface';
38
+ return isInterface
39
+ ? { type: 'IMPLEMENTS', idPrefix: 'Interface' }
40
+ : { type: 'EXTENDS', idPrefix: 'Class' };
41
+ }
42
+ // Unresolved symbol — fall back to language-specific heuristic
43
+ if (language === SupportedLanguages.CSharp || language === SupportedLanguages.Java) {
44
+ if (INTERFACE_NAME_RE.test(parentName)) {
45
+ return { type: 'IMPLEMENTS', idPrefix: 'Interface' };
46
+ }
47
+ }
48
+ else if (language === SupportedLanguages.Swift) {
49
+ // Protocol conformance is far more common than class inheritance in Swift
50
+ return { type: 'IMPLEMENTS', idPrefix: 'Interface' };
51
+ }
52
+ return { type: 'EXTENDS', idPrefix: 'Class' };
53
+ };
54
+ export const processHeritage = async (graph, files, astCache, symbolTable, importMap, packageMap, onProgress) => {
14
55
  const parser = await loadParser();
56
+ const logSkipped = isVerboseIngestionEnabled();
57
+ const skippedByLang = logSkipped ? new Map() : null;
15
58
  for (let i = 0; i < files.length; i++) {
16
59
  const file = files[i];
17
60
  onProgress?.(i + 1, files.length);
@@ -21,6 +64,12 @@ export const processHeritage = async (graph, files, astCache, symbolTable, onPro
21
64
  const language = getLanguageFromFilename(file.path);
22
65
  if (!language)
23
66
  continue;
67
+ if (!isLanguageAvailable(language)) {
68
+ if (skippedByLang) {
69
+ skippedByLang.set(language, (skippedByLang.get(language) ?? 0) + 1);
70
+ }
71
+ continue;
72
+ }
24
73
  const queryStr = LANGUAGE_QUERIES[language];
25
74
  if (!queryStr)
26
75
  continue;
@@ -32,7 +81,7 @@ export const processHeritage = async (graph, files, astCache, symbolTable, onPro
32
81
  if (!tree) {
33
82
  // Use larger bufferSize for files > 32KB
34
83
  try {
35
- tree = parser.parse(file.content, undefined, { bufferSize: 1024 * 256 });
84
+ tree = parser.parse(file.content, undefined, { bufferSize: getTreeSitterBufferSize(file.content.length) });
36
85
  }
37
86
  catch (parseError) {
38
87
  // Skip files that can't be parsed
@@ -59,23 +108,29 @@ export const processHeritage = async (graph, files, astCache, symbolTable, onPro
59
108
  match.captures.forEach(c => {
60
109
  captureMap[c.name] = c.node;
61
110
  });
62
- // EXTENDS: Class extends another Class
111
+ // EXTENDS or IMPLEMENTS: resolve via symbol table for languages where
112
+ // the tree-sitter query can't distinguish classes from interfaces (C#, Java)
63
113
  if (captureMap['heritage.class'] && captureMap['heritage.extends']) {
114
+ // Go struct embedding: skip named fields (only anonymous fields are embedded)
115
+ const extendsNode = captureMap['heritage.extends'];
116
+ const fieldDecl = extendsNode.parent;
117
+ if (fieldDecl?.type === 'field_declaration' && fieldDecl.childForFieldName('name')) {
118
+ return; // Named field, not struct embedding
119
+ }
64
120
  const className = captureMap['heritage.class'].text;
65
121
  const parentClassName = captureMap['heritage.extends'].text;
66
- // Resolve both class IDs
122
+ const { type: relType, idPrefix } = resolveExtendsType(parentClassName, file.path, symbolTable, importMap, language, packageMap);
67
123
  const childId = symbolTable.lookupExact(file.path, className) ||
68
- symbolTable.lookupFuzzy(className)[0]?.nodeId ||
124
+ resolveSymbol(className, file.path, symbolTable, importMap, packageMap)?.nodeId ||
69
125
  generateId('Class', `${file.path}:${className}`);
70
- const parentId = symbolTable.lookupFuzzy(parentClassName)[0]?.nodeId ||
71
- generateId('Class', `${parentClassName}`);
126
+ const parentId = resolveSymbol(parentClassName, file.path, symbolTable, importMap, packageMap)?.nodeId ||
127
+ generateId(idPrefix, `${parentClassName}`);
72
128
  if (childId && parentId && childId !== parentId) {
73
- const relId = generateId('EXTENDS', `${childId}->${parentId}`);
74
129
  graph.addRelationship({
75
- id: relId,
130
+ id: generateId(relType, `${childId}->${parentId}`),
76
131
  sourceId: childId,
77
132
  targetId: parentId,
78
- type: 'EXTENDS',
133
+ type: relType,
79
134
  confidence: 1.0,
80
135
  reason: '',
81
136
  });
@@ -87,9 +142,9 @@ export const processHeritage = async (graph, files, astCache, symbolTable, onPro
87
142
  const interfaceName = captureMap['heritage.implements'].text;
88
143
  // Resolve class and interface IDs
89
144
  const classId = symbolTable.lookupExact(file.path, className) ||
90
- symbolTable.lookupFuzzy(className)[0]?.nodeId ||
145
+ resolveSymbol(className, file.path, symbolTable, importMap, packageMap)?.nodeId ||
91
146
  generateId('Class', `${file.path}:${className}`);
92
- const interfaceId = symbolTable.lookupFuzzy(interfaceName)[0]?.nodeId ||
147
+ const interfaceId = resolveSymbol(interfaceName, file.path, symbolTable, importMap, packageMap)?.nodeId ||
93
148
  generateId('Interface', `${interfaceName}`);
94
149
  if (classId && interfaceId) {
95
150
  const relId = generateId('IMPLEMENTS', `${classId}->${interfaceId}`);
@@ -109,9 +164,9 @@ export const processHeritage = async (graph, files, astCache, symbolTable, onPro
109
164
  const traitName = captureMap['heritage.trait'].text;
110
165
  // Resolve struct and trait IDs
111
166
  const structId = symbolTable.lookupExact(file.path, structName) ||
112
- symbolTable.lookupFuzzy(structName)[0]?.nodeId ||
167
+ resolveSymbol(structName, file.path, symbolTable, importMap, packageMap)?.nodeId ||
113
168
  generateId('Struct', `${file.path}:${structName}`);
114
- const traitId = symbolTable.lookupFuzzy(traitName)[0]?.nodeId ||
169
+ const traitId = resolveSymbol(traitName, file.path, symbolTable, importMap, packageMap)?.nodeId ||
115
170
  generateId('Trait', `${traitName}`);
116
171
  if (structId && traitId) {
117
172
  const relId = generateId('IMPLEMENTS', `${structId}->${traitId}`);
@@ -128,12 +183,17 @@ export const processHeritage = async (graph, files, astCache, symbolTable, onPro
128
183
  });
129
184
  // Tree is now owned by the LRU cache — no manual delete needed
130
185
  }
186
+ if (skippedByLang && skippedByLang.size > 0) {
187
+ for (const [lang, count] of skippedByLang.entries()) {
188
+ console.warn(`[ingestion] Skipped ${count} ${lang} file(s) in heritage processing — ${lang} parser not available.`);
189
+ }
190
+ }
131
191
  };
132
192
  /**
133
193
  * Fast path: resolve pre-extracted heritage from workers.
134
194
  * No AST parsing — workers already extracted className + parentName + kind.
135
195
  */
136
- export const processHeritageFromExtracted = async (graph, extractedHeritage, symbolTable, onProgress) => {
196
+ export const processHeritageFromExtracted = async (graph, extractedHeritage, symbolTable, importMap, packageMap, onProgress) => {
137
197
  const total = extractedHeritage.length;
138
198
  for (let i = 0; i < extractedHeritage.length; i++) {
139
199
  if (i % 500 === 0) {
@@ -142,17 +202,21 @@ export const processHeritageFromExtracted = async (graph, extractedHeritage, sym
142
202
  }
143
203
  const h = extractedHeritage[i];
144
204
  if (h.kind === 'extends') {
205
+ const fileLanguage = getLanguageFromFilename(h.filePath);
206
+ if (!fileLanguage)
207
+ continue;
208
+ const { type: relType, idPrefix } = resolveExtendsType(h.parentName, h.filePath, symbolTable, importMap, fileLanguage, packageMap);
145
209
  const childId = symbolTable.lookupExact(h.filePath, h.className) ||
146
- symbolTable.lookupFuzzy(h.className)[0]?.nodeId ||
210
+ resolveSymbol(h.className, h.filePath, symbolTable, importMap, packageMap)?.nodeId ||
147
211
  generateId('Class', `${h.filePath}:${h.className}`);
148
- const parentId = symbolTable.lookupFuzzy(h.parentName)[0]?.nodeId ||
149
- generateId('Class', `${h.parentName}`);
212
+ const parentId = resolveSymbol(h.parentName, h.filePath, symbolTable, importMap, packageMap)?.nodeId ||
213
+ generateId(idPrefix, `${h.parentName}`);
150
214
  if (childId && parentId && childId !== parentId) {
151
215
  graph.addRelationship({
152
- id: generateId('EXTENDS', `${childId}->${parentId}`),
216
+ id: generateId(relType, `${childId}->${parentId}`),
153
217
  sourceId: childId,
154
218
  targetId: parentId,
155
- type: 'EXTENDS',
219
+ type: relType,
156
220
  confidence: 1.0,
157
221
  reason: '',
158
222
  });
@@ -160,9 +224,9 @@ export const processHeritageFromExtracted = async (graph, extractedHeritage, sym
160
224
  }
161
225
  else if (h.kind === 'implements') {
162
226
  const classId = symbolTable.lookupExact(h.filePath, h.className) ||
163
- symbolTable.lookupFuzzy(h.className)[0]?.nodeId ||
227
+ resolveSymbol(h.className, h.filePath, symbolTable, importMap, packageMap)?.nodeId ||
164
228
  generateId('Class', `${h.filePath}:${h.className}`);
165
- const interfaceId = symbolTable.lookupFuzzy(h.parentName)[0]?.nodeId ||
229
+ const interfaceId = resolveSymbol(h.parentName, h.filePath, symbolTable, importMap, packageMap)?.nodeId ||
166
230
  generateId('Interface', `${h.parentName}`);
167
231
  if (classId && interfaceId) {
168
232
  graph.addRelationship({
@@ -177,9 +241,9 @@ export const processHeritageFromExtracted = async (graph, extractedHeritage, sym
177
241
  }
178
242
  else if (h.kind === 'trait-impl') {
179
243
  const structId = symbolTable.lookupExact(h.filePath, h.className) ||
180
- symbolTable.lookupFuzzy(h.className)[0]?.nodeId ||
244
+ resolveSymbol(h.className, h.filePath, symbolTable, importMap, packageMap)?.nodeId ||
181
245
  generateId('Struct', `${h.filePath}:${h.className}`);
182
- const traitId = symbolTable.lookupFuzzy(h.parentName)[0]?.nodeId ||
246
+ const traitId = resolveSymbol(h.parentName, h.filePath, symbolTable, importMap, packageMap)?.nodeId ||
183
247
  generateId('Trait', `${h.parentName}`);
184
248
  if (structId && traitId) {
185
249
  graph.addRelationship({
@@ -1,8 +1,23 @@
1
1
  import { KnowledgeGraph } from '../graph/types.js';
2
2
  import { ASTCache } from './ast-cache.js';
3
3
  import type { ExtractedImport } from './workers/parse-worker.js';
4
+ import type { SuffixIndex } from './resolvers/index.js';
5
+ export type { SuffixIndex, TsconfigPaths, GoModuleConfig, CSharpProjectConfig, ComposerConfig } from './resolvers/index.js';
4
6
  export type ImportMap = Map<string, Set<string>>;
5
7
  export declare const createImportMap: () => ImportMap;
8
+ export type PackageMap = Map<string, Set<string>>;
9
+ export declare const createPackageMap: () => PackageMap;
10
+ export interface NamedImportBinding {
11
+ sourcePath: string;
12
+ exportedName: string;
13
+ }
14
+ export type NamedImportMap = Map<string, Map<string, NamedImportBinding>>;
15
+ export declare const createNamedImportMap: () => NamedImportMap;
16
+ /**
17
+ * Check if a file path is directly inside a package directory identified by its suffix.
18
+ * Used by the symbol resolver for Go and C# directory-level import matching.
19
+ */
20
+ export declare function isFileInPackageDir(filePath: string, dirSuffix: string): boolean;
6
21
  /** Pre-built lookup structures for import resolution. Build once, reuse across chunks. */
7
22
  export interface ImportResolutionContext {
8
23
  allFilePaths: Set<string>;
@@ -12,27 +27,10 @@ export interface ImportResolutionContext {
12
27
  resolveCache: Map<string, string | null>;
13
28
  }
14
29
  export declare function buildImportResolutionContext(allPaths: string[]): ImportResolutionContext;
15
- /**
16
- * Build a suffix index for O(1) endsWith lookups.
17
- * Maps every possible path suffix to its original file path.
18
- * e.g. for "src/com/example/Foo.java":
19
- * "Foo.java" -> "src/com/example/Foo.java"
20
- * "example/Foo.java" -> "src/com/example/Foo.java"
21
- * "com/example/Foo.java" -> "src/com/example/Foo.java"
22
- * etc.
23
- */
24
- export interface SuffixIndex {
25
- /** Exact suffix lookup (case-sensitive) */
26
- get(suffix: string): string | undefined;
27
- /** Case-insensitive suffix lookup */
28
- getInsensitive(suffix: string): string | undefined;
29
- /** Get all files in a directory suffix */
30
- getFilesInDir(dirSuffix: string, extension: string): string[];
31
- }
32
30
  export declare const processImports: (graph: KnowledgeGraph, files: {
33
31
  path: string;
34
32
  content: string;
35
- }[], astCache: ASTCache, importMap: ImportMap, onProgress?: (current: number, total: number) => void, repoRoot?: string, allPaths?: string[]) => Promise<void>;
33
+ }[], astCache: ASTCache, importMap: ImportMap, onProgress?: (current: number, total: number) => void, repoRoot?: string, allPaths?: string[], packageMap?: PackageMap, namedImportMap?: NamedImportMap) => Promise<void>;
36
34
  export declare const processImportsFromExtracted: (graph: KnowledgeGraph, files: {
37
35
  path: string;
38
- }[], extractedImports: ExtractedImport[], importMap: ImportMap, onProgress?: (current: number, total: number) => void, repoRoot?: string, prebuiltCtx?: ImportResolutionContext) => Promise<void>;
36
+ }[], extractedImports: ExtractedImport[], importMap: ImportMap, onProgress?: (current: number, total: number) => void, repoRoot?: string, prebuiltCtx?: ImportResolutionContext, packageMap?: PackageMap, namedImportMap?: NamedImportMap) => Promise<void>;