@mars167/git-ai 2.3.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 (122) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +364 -0
  3. package/README.zh-CN.md +361 -0
  4. package/assets/hooks/post-checkout +28 -0
  5. package/assets/hooks/post-merge +28 -0
  6. package/assets/hooks/pre-commit +17 -0
  7. package/assets/hooks/pre-push +29 -0
  8. package/dist/bin/git-ai.js +62 -0
  9. package/dist/src/commands/ai.js +30 -0
  10. package/dist/src/commands/checkIndex.js +19 -0
  11. package/dist/src/commands/dsr.js +156 -0
  12. package/dist/src/commands/graph.js +203 -0
  13. package/dist/src/commands/hooks.js +125 -0
  14. package/dist/src/commands/index.js +92 -0
  15. package/dist/src/commands/pack.js +31 -0
  16. package/dist/src/commands/query.js +139 -0
  17. package/dist/src/commands/semantic.js +134 -0
  18. package/dist/src/commands/serve.js +14 -0
  19. package/dist/src/commands/status.js +78 -0
  20. package/dist/src/commands/trae.js +75 -0
  21. package/dist/src/commands/unpack.js +28 -0
  22. package/dist/src/core/archive.js +91 -0
  23. package/dist/src/core/astGraph.js +127 -0
  24. package/dist/src/core/astGraphQuery.js +142 -0
  25. package/dist/src/core/cozo.js +266 -0
  26. package/dist/src/core/cpg/astLayer.js +56 -0
  27. package/dist/src/core/cpg/callGraph.js +483 -0
  28. package/dist/src/core/cpg/cfgLayer.js +490 -0
  29. package/dist/src/core/cpg/dfgLayer.js +237 -0
  30. package/dist/src/core/cpg/index.js +80 -0
  31. package/dist/src/core/cpg/types.js +108 -0
  32. package/dist/src/core/crypto.js +10 -0
  33. package/dist/src/core/dsr/generate.js +308 -0
  34. package/dist/src/core/dsr/gitContext.js +74 -0
  35. package/dist/src/core/dsr/indexMaterialize.js +106 -0
  36. package/dist/src/core/dsr/paths.js +26 -0
  37. package/dist/src/core/dsr/query.js +73 -0
  38. package/dist/src/core/dsr/snapshotParser.js +73 -0
  39. package/dist/src/core/dsr/state.js +27 -0
  40. package/dist/src/core/dsr/types.js +2 -0
  41. package/dist/src/core/embedding/fusion.js +52 -0
  42. package/dist/src/core/embedding/index.js +43 -0
  43. package/dist/src/core/embedding/parser.js +14 -0
  44. package/dist/src/core/embedding/semantic.js +254 -0
  45. package/dist/src/core/embedding/structural.js +97 -0
  46. package/dist/src/core/embedding/symbolic.js +117 -0
  47. package/dist/src/core/embedding/tokenizer.js +91 -0
  48. package/dist/src/core/embedding/types.js +2 -0
  49. package/dist/src/core/embedding.js +36 -0
  50. package/dist/src/core/git.js +49 -0
  51. package/dist/src/core/gitDiff.js +73 -0
  52. package/dist/src/core/indexCheck.js +131 -0
  53. package/dist/src/core/indexer.js +185 -0
  54. package/dist/src/core/indexerIncremental.js +303 -0
  55. package/dist/src/core/indexing/config.js +51 -0
  56. package/dist/src/core/indexing/hnsw.js +568 -0
  57. package/dist/src/core/indexing/index.js +17 -0
  58. package/dist/src/core/indexing/monitor.js +82 -0
  59. package/dist/src/core/indexing/parallel.js +252 -0
  60. package/dist/src/core/lancedb.js +111 -0
  61. package/dist/src/core/lfs.js +27 -0
  62. package/dist/src/core/log.js +62 -0
  63. package/dist/src/core/manifest.js +88 -0
  64. package/dist/src/core/parser/adapter.js +2 -0
  65. package/dist/src/core/parser/c.js +93 -0
  66. package/dist/src/core/parser/chunkRelations.js +178 -0
  67. package/dist/src/core/parser/chunker.js +274 -0
  68. package/dist/src/core/parser/go.js +98 -0
  69. package/dist/src/core/parser/java.js +80 -0
  70. package/dist/src/core/parser/markdown.js +76 -0
  71. package/dist/src/core/parser/python.js +81 -0
  72. package/dist/src/core/parser/rust.js +103 -0
  73. package/dist/src/core/parser/typescript.js +98 -0
  74. package/dist/src/core/parser/utils.js +62 -0
  75. package/dist/src/core/parser/yaml.js +53 -0
  76. package/dist/src/core/parser.js +75 -0
  77. package/dist/src/core/paths.js +10 -0
  78. package/dist/src/core/repoMap.js +164 -0
  79. package/dist/src/core/retrieval/cache.js +31 -0
  80. package/dist/src/core/retrieval/classifier.js +74 -0
  81. package/dist/src/core/retrieval/expander.js +80 -0
  82. package/dist/src/core/retrieval/fuser.js +40 -0
  83. package/dist/src/core/retrieval/index.js +32 -0
  84. package/dist/src/core/retrieval/reranker.js +304 -0
  85. package/dist/src/core/retrieval/types.js +2 -0
  86. package/dist/src/core/retrieval/weights.js +42 -0
  87. package/dist/src/core/search.js +41 -0
  88. package/dist/src/core/sq8.js +65 -0
  89. package/dist/src/core/symbolSearch.js +143 -0
  90. package/dist/src/core/types.js +2 -0
  91. package/dist/src/core/workspace.js +116 -0
  92. package/dist/src/mcp/server.js +794 -0
  93. package/docs/README.md +44 -0
  94. package/docs/cross-encoder.md +157 -0
  95. package/docs/embedding.md +158 -0
  96. package/docs/logo.png +0 -0
  97. package/docs/windows-setup.md +67 -0
  98. package/docs/zh-CN/DESIGN.md +102 -0
  99. package/docs/zh-CN/README.md +46 -0
  100. package/docs/zh-CN/advanced.md +26 -0
  101. package/docs/zh-CN/architecture_explained.md +116 -0
  102. package/docs/zh-CN/cli.md +109 -0
  103. package/docs/zh-CN/dsr.md +91 -0
  104. package/docs/zh-CN/graph_scenarios.md +173 -0
  105. package/docs/zh-CN/hooks.md +14 -0
  106. package/docs/zh-CN/manifests.md +136 -0
  107. package/docs/zh-CN/mcp.md +205 -0
  108. package/docs/zh-CN/quickstart.md +35 -0
  109. package/docs/zh-CN/rules.md +7 -0
  110. package/docs/zh-CN/technical-details.md +454 -0
  111. package/docs/zh-CN/troubleshooting.md +19 -0
  112. package/docs/zh-CN/windows-setup.md +67 -0
  113. package/install.sh +183 -0
  114. package/package.json +97 -0
  115. package/skills/git-ai-mcp/SKILL.md +86 -0
  116. package/skills/git-ai-mcp/references/constraints.md +143 -0
  117. package/skills/git-ai-mcp/references/tools.md +263 -0
  118. package/templates/agents/common/documents/Fix EISDIR error and enable multi-language indexing.md +14 -0
  119. package/templates/agents/common/documents/Fix git-ai index error in CodaGraph directory.md +13 -0
  120. package/templates/agents/common/skills/git-ai-mcp/SKILL.md +86 -0
  121. package/templates/agents/common/skills/git-ai-mcp/references/constraints.md +143 -0
  122. package/templates/agents/common/skills/git-ai-mcp/references/tools.md +263 -0
@@ -0,0 +1,483 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CallGraphBuilder = void 0;
7
+ exports.buildCallGraph = buildCallGraph;
8
+ exports.buildImportGraph = buildImportGraph;
9
+ const tree_sitter_1 = __importDefault(require("tree-sitter"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const tree_sitter_typescript_1 = __importDefault(require("tree-sitter-typescript"));
12
+ const types_1 = require("./types");
13
+ const paths_1 = require("../paths");
14
+ const FUNCTION_NODE_TYPES = new Set([
15
+ 'function_declaration',
16
+ 'function',
17
+ 'arrow_function',
18
+ 'method_definition',
19
+ ]);
20
+ const EXPORT_TYPES = new Set([
21
+ 'export_statement',
22
+ 'export_clause',
23
+ 'export_specifier',
24
+ 'export_default_declaration',
25
+ 'export_assignment',
26
+ ]);
27
+ const IMPORT_TYPES = new Set([
28
+ 'import_statement',
29
+ 'import_clause',
30
+ 'import_specifier',
31
+ 'namespace_import',
32
+ ]);
33
+ function resolveModulePath(fromFile, specifier) {
34
+ if (!specifier)
35
+ return specifier;
36
+ if (specifier.startsWith('.')) {
37
+ const resolved = path_1.default.normalize(path_1.default.join(path_1.default.dirname(fromFile), specifier));
38
+ return (0, paths_1.toPosixPath)(resolved);
39
+ }
40
+ return specifier;
41
+ }
42
+ function collectSymbolTable(contexts) {
43
+ const table = new Map();
44
+ for (const ctx of contexts) {
45
+ const filePosix = (0, paths_1.toPosixPath)(ctx.filePath);
46
+ const visit = (node) => {
47
+ if (node.type === 'function_declaration' || node.type === 'method_definition') {
48
+ const nameNode = node.childForFieldName('name');
49
+ if (nameNode) {
50
+ const symbol = {
51
+ name: nameNode.text,
52
+ kind: node.type === 'method_definition' ? 'method' : 'function',
53
+ startLine: node.startPosition.row + 1,
54
+ endLine: node.endPosition.row + 1,
55
+ signature: node.text.split('{')[0].trim(),
56
+ };
57
+ const id = (0, types_1.symbolNodeId)(filePosix, symbol);
58
+ table.set(`${filePosix}:${symbol.name}`, {
59
+ id,
60
+ name: symbol.name,
61
+ file: filePosix,
62
+ kind: symbol.kind,
63
+ startLine: symbol.startLine,
64
+ endLine: symbol.endLine,
65
+ });
66
+ }
67
+ }
68
+ if (node.type === 'class_declaration') {
69
+ const nameNode = node.childForFieldName('name');
70
+ if (nameNode) {
71
+ const symbol = {
72
+ name: nameNode.text,
73
+ kind: 'class',
74
+ startLine: node.startPosition.row + 1,
75
+ endLine: node.endPosition.row + 1,
76
+ signature: `class ${nameNode.text}`,
77
+ };
78
+ const id = (0, types_1.symbolNodeId)(filePosix, symbol);
79
+ table.set(`${filePosix}:${symbol.name}`, {
80
+ id,
81
+ name: symbol.name,
82
+ file: filePosix,
83
+ kind: symbol.kind,
84
+ startLine: symbol.startLine,
85
+ endLine: symbol.endLine,
86
+ });
87
+ }
88
+ }
89
+ for (let i = 0; i < node.childCount; i++) {
90
+ const child = node.child(i);
91
+ if (child)
92
+ visit(child);
93
+ }
94
+ };
95
+ visit(ctx.root);
96
+ }
97
+ return table;
98
+ }
99
+ function collectImports(context) {
100
+ const bindings = [];
101
+ const visit = (node) => {
102
+ if (node.type === 'import_statement') {
103
+ const source = node.childForFieldName('source');
104
+ const moduleName = source ? source.text.replace(/['"]/g, '') : '';
105
+ let clause = node.childForFieldName('clause') ?? node.childForFieldName('declaration');
106
+ if (!clause) {
107
+ for (let k = 0; k < node.namedChildCount; k++) {
108
+ const c = node.namedChild(k);
109
+ if (c?.type === 'import_clause') {
110
+ clause = c;
111
+ break;
112
+ }
113
+ }
114
+ }
115
+ if (moduleName && clause) {
116
+ for (let i = 0; i < clause.namedChildCount; i++) {
117
+ const child = clause.namedChild(i);
118
+ if (!child)
119
+ continue;
120
+ if (child.type === 'identifier') {
121
+ bindings.push({ modulePath: moduleName, importedName: 'default', localName: child.text });
122
+ }
123
+ else if (child.type === 'named_imports') {
124
+ for (let j = 0; j < child.namedChildCount; j++) {
125
+ const spec = child.namedChild(j);
126
+ if (spec?.type === 'import_specifier') {
127
+ const nameNode = spec.childForFieldName('name');
128
+ const aliasNode = spec.childForFieldName('alias');
129
+ const importedName = nameNode?.text ?? '';
130
+ const localName = aliasNode?.text ?? importedName;
131
+ if (localName)
132
+ bindings.push({ modulePath: moduleName, importedName, localName });
133
+ }
134
+ }
135
+ }
136
+ }
137
+ }
138
+ }
139
+ for (let i = 0; i < node.childCount; i++) {
140
+ const child = node.child(i);
141
+ if (child)
142
+ visit(child);
143
+ }
144
+ };
145
+ visit(context.root);
146
+ return bindings;
147
+ }
148
+ function collectCommonJsImports(context) {
149
+ const bindings = [];
150
+ const visit = (node) => {
151
+ if (node.type === 'call_expression') {
152
+ const callee = node.childForFieldName('function') ?? node.namedChild(0);
153
+ if (callee?.type === 'identifier' && callee.text === 'require') {
154
+ const args = node.childForFieldName('arguments');
155
+ const arg = args?.namedChild(0);
156
+ if (arg?.type === 'string') {
157
+ const moduleName = arg.text.replace(/['"]/g, '');
158
+ const parent = node.parent;
159
+ if (parent?.type === 'variable_declarator') {
160
+ const nameNode = parent.childForFieldName('name');
161
+ if (nameNode?.type === 'identifier') {
162
+ bindings.push({ modulePath: moduleName, importedName: 'default', localName: nameNode.text });
163
+ }
164
+ }
165
+ }
166
+ }
167
+ }
168
+ for (let i = 0; i < node.childCount; i++) {
169
+ const child = node.child(i);
170
+ if (child)
171
+ visit(child);
172
+ }
173
+ };
174
+ visit(context.root);
175
+ return bindings;
176
+ }
177
+ function collectFunctionScopes(context, symbolTable) {
178
+ const funcs = [];
179
+ const visit = (node) => {
180
+ if (FUNCTION_NODE_TYPES.has(node.type)) {
181
+ const nameNode = node.childForFieldName('name');
182
+ if (nameNode) {
183
+ const symbol = symbolTable.get(`${(0, paths_1.toPosixPath)(context.filePath)}:${nameNode.text}`);
184
+ const id = symbol?.id ?? (0, types_1.symbolNodeId)((0, paths_1.toPosixPath)(context.filePath), {
185
+ name: nameNode.text,
186
+ kind: node.type === 'method_definition' ? 'method' : 'function',
187
+ signature: node.text.split('{')[0].trim(),
188
+ startLine: node.startPosition.row + 1,
189
+ endLine: node.endPosition.row + 1,
190
+ });
191
+ funcs.push({
192
+ id,
193
+ name: nameNode.text,
194
+ filePath: (0, paths_1.toPosixPath)(context.filePath),
195
+ startLine: node.startPosition.row + 1,
196
+ endLine: node.endPosition.row + 1,
197
+ });
198
+ }
199
+ }
200
+ if (node.type === 'class_declaration') {
201
+ const nameNode = node.childForFieldName('name');
202
+ if (nameNode) {
203
+ const symbol = symbolTable.get(`${(0, paths_1.toPosixPath)(context.filePath)}:${nameNode.text}`);
204
+ const id = symbol?.id ?? (0, types_1.symbolNodeId)((0, paths_1.toPosixPath)(context.filePath), {
205
+ name: nameNode.text,
206
+ kind: 'class',
207
+ signature: `class ${nameNode.text}`,
208
+ startLine: node.startPosition.row + 1,
209
+ endLine: node.endPosition.row + 1,
210
+ });
211
+ funcs.push({
212
+ id,
213
+ name: nameNode.text,
214
+ filePath: (0, paths_1.toPosixPath)(context.filePath),
215
+ startLine: node.startPosition.row + 1,
216
+ endLine: node.endPosition.row + 1,
217
+ });
218
+ }
219
+ }
220
+ for (let i = 0; i < node.childCount; i++) {
221
+ const child = node.child(i);
222
+ if (child)
223
+ visit(child);
224
+ }
225
+ };
226
+ visit(context.root);
227
+ return funcs;
228
+ }
229
+ function findNearestFunction(node, symbolTable, filePath) {
230
+ let current = node;
231
+ while (current) {
232
+ if (FUNCTION_NODE_TYPES.has(current.type)) {
233
+ const nameNode = current.childForFieldName('name');
234
+ if (nameNode) {
235
+ const symbol = symbolTable.get(`${(0, paths_1.toPosixPath)(filePath)}:${nameNode.text}`);
236
+ const id = symbol?.id ?? (0, types_1.symbolNodeId)((0, paths_1.toPosixPath)(filePath), {
237
+ name: nameNode.text,
238
+ kind: current.type === 'method_definition' ? 'method' : 'function',
239
+ signature: current.text.split('{')[0].trim(),
240
+ startLine: current.startPosition.row + 1,
241
+ endLine: current.endPosition.row + 1,
242
+ });
243
+ return { id, name: nameNode.text };
244
+ }
245
+ }
246
+ current = current.parent;
247
+ }
248
+ return null;
249
+ }
250
+ function extractCalleeName(node) {
251
+ if (node.type === 'identifier')
252
+ return node.text;
253
+ if (node.type === 'member_expression' || node.type === 'optional_chain') {
254
+ const prop = node.childForFieldName('property');
255
+ if (prop)
256
+ return prop.text;
257
+ const last = node.namedChild(node.namedChildCount - 1);
258
+ if (last)
259
+ return last.text;
260
+ }
261
+ return null;
262
+ }
263
+ function resolveCallTarget(calleeNode, importBindings, symbolTable, currentFile) {
264
+ const filePosix = (0, paths_1.toPosixPath)(currentFile);
265
+ const lookup = (name, file) => {
266
+ if (file) {
267
+ let qualified = `${file}:${name}`;
268
+ if (symbolTable.has(qualified))
269
+ return symbolTable.get(qualified);
270
+ const extensions = ['.ts', '.tsx', '.js', '.jsx', '.d.ts'];
271
+ for (const ext of extensions) {
272
+ qualified = `${file}${ext}:${name}`;
273
+ if (symbolTable.has(qualified))
274
+ return symbolTable.get(qualified);
275
+ }
276
+ for (const ext of extensions) {
277
+ qualified = `${file}/index${ext}:${name}`;
278
+ if (symbolTable.has(qualified))
279
+ return symbolTable.get(qualified);
280
+ }
281
+ }
282
+ return undefined;
283
+ };
284
+ if (calleeNode.type === 'identifier') {
285
+ const direct = lookup(calleeNode.text, filePosix);
286
+ if (direct)
287
+ return direct;
288
+ const imported = importBindings.find((binding) => binding.localName === calleeNode.text);
289
+ if (imported) {
290
+ const resolvedModule = resolveModulePath(filePosix, imported.modulePath);
291
+ const resolvedName = imported.importedName === 'default' ? 'default' : (imported.importedName || imported.localName);
292
+ if (imported.importedName === '*')
293
+ return null;
294
+ return lookup(resolvedName, resolvedModule) ?? null;
295
+ }
296
+ }
297
+ if (calleeNode.type === 'member_expression' || calleeNode.type === 'optional_chain') {
298
+ const objectNode = calleeNode.childForFieldName('object');
299
+ const propNode = calleeNode.childForFieldName('property') ?? calleeNode.namedChild(calleeNode.namedChildCount - 1);
300
+ if (objectNode?.type === 'identifier') {
301
+ const binding = importBindings.find((entry) => entry.localName === objectNode.text);
302
+ if (binding) {
303
+ const resolvedModule = resolveModulePath(filePosix, binding.modulePath);
304
+ const resolved = propNode ? lookup(propNode.text, resolvedModule) : null;
305
+ return resolved ?? null;
306
+ }
307
+ }
308
+ }
309
+ const fallback = extractCalleeName(calleeNode);
310
+ if (fallback)
311
+ return lookup(fallback, filePosix) ?? null;
312
+ return null;
313
+ }
314
+ function buildCallGraphLayer(contexts) {
315
+ const nodes = [];
316
+ const edges = [];
317
+ const edgeTypes = [types_1.EdgeType.CALLS, types_1.EdgeType.DEFINES];
318
+ const functions = new Map();
319
+ const calls = [];
320
+ const imports = [];
321
+ const symbolTable = collectSymbolTable(contexts);
322
+ for (const ctx of contexts) {
323
+ const filePosix = (0, paths_1.toPosixPath)(ctx.filePath);
324
+ const moduleId = (0, types_1.moduleNodeId)(filePosix);
325
+ const importBindings = [...collectImports(ctx), ...collectCommonJsImports(ctx)];
326
+ const importsByModule = new Map();
327
+ for (const binding of importBindings) {
328
+ const resolved = resolveModulePath(filePosix, binding.modulePath);
329
+ const set = importsByModule.get(resolved) ?? new Set();
330
+ set.add(binding.importedName || binding.localName);
331
+ importsByModule.set(resolved, set);
332
+ }
333
+ for (const [toFile, symbols] of importsByModule) {
334
+ imports.push({ fromFile: filePosix, toFile, importedSymbols: Array.from(symbols.values()) });
335
+ }
336
+ const fileFunctions = collectFunctionScopes(ctx, symbolTable);
337
+ for (const fn of fileFunctions) {
338
+ functions.set(fn.id, fn);
339
+ nodes.push({ id: fn.id, kind: 'symbol', label: fn.name, file: fn.filePath, startLine: fn.startLine, endLine: fn.endLine });
340
+ }
341
+ const visit = (node) => {
342
+ if (node.type === 'call_expression') {
343
+ const fnNode = node.childForFieldName('function') ?? node.namedChild(0);
344
+ if (fnNode) {
345
+ const resolved = resolveCallTarget(fnNode, importBindings, symbolTable, ctx.filePath);
346
+ if (resolved) {
347
+ const caller = findNearestFunction(node, symbolTable, ctx.filePath) ?? { id: moduleId, name: filePosix };
348
+ edges.push({ from: caller.id, to: resolved.id, type: types_1.EdgeType.CALLS });
349
+ calls.push({ from: caller.id, to: resolved.id, line: node.startPosition.row + 1 });
350
+ }
351
+ }
352
+ }
353
+ if (node.type === 'new_expression') {
354
+ const ctor = node.childForFieldName('constructor') ?? node.namedChild(0);
355
+ if (ctor) {
356
+ const resolved = resolveCallTarget(ctor, importBindings, symbolTable, ctx.filePath);
357
+ if (resolved) {
358
+ const caller = findNearestFunction(node, symbolTable, ctx.filePath) ?? { id: moduleId, name: filePosix };
359
+ edges.push({ from: caller.id, to: resolved.id, type: types_1.EdgeType.CALLS });
360
+ calls.push({ from: caller.id, to: resolved.id, line: node.startPosition.row + 1 });
361
+ }
362
+ }
363
+ }
364
+ if (EXPORT_TYPES.has(node.type)) {
365
+ const decl = node.childForFieldName('declaration');
366
+ const nameNode = decl?.childForFieldName('name');
367
+ if (nameNode) {
368
+ const symbol = symbolTable.get(nameNode.text);
369
+ if (symbol)
370
+ edges.push({ from: moduleId, to: symbol.id, type: types_1.EdgeType.DEFINES });
371
+ }
372
+ }
373
+ if (node.type === 'class_declaration') {
374
+ const nameNode = node.childForFieldName('name');
375
+ if (nameNode) {
376
+ const symbol = symbolTable.get(nameNode.text);
377
+ if (symbol)
378
+ edges.push({ from: moduleId, to: symbol.id, type: types_1.EdgeType.DEFINES });
379
+ }
380
+ }
381
+ for (let i = 0; i < node.childCount; i++) {
382
+ const child = node.child(i);
383
+ if (child)
384
+ visit(child);
385
+ }
386
+ };
387
+ visit(ctx.root);
388
+ nodes.push((0, types_1.createModuleNode)(filePosix));
389
+ }
390
+ const graph = { functions, calls, imports };
391
+ const layer = { nodes, edges, edgeTypes };
392
+ return { graph, layer };
393
+ }
394
+ function buildCallGraph(contexts) {
395
+ return buildCallGraphLayer(contexts).layer;
396
+ }
397
+ function buildImportGraph(contexts) {
398
+ const nodes = [];
399
+ const edges = [];
400
+ const edgeTypes = [types_1.EdgeType.IMPORTS, types_1.EdgeType.INHERITS, types_1.EdgeType.IMPLEMENTS];
401
+ const symbolTable = collectSymbolTable(contexts);
402
+ for (const ctx of contexts) {
403
+ const filePosix = (0, paths_1.toPosixPath)(ctx.filePath);
404
+ const fileNode = (0, types_1.createModuleNode)(filePosix);
405
+ nodes.push(fileNode);
406
+ const visit = (node) => {
407
+ if (IMPORT_TYPES.has(node.type) && node.type === 'import_statement') {
408
+ const source = node.childForFieldName('source');
409
+ const moduleName = source ? source.text.replace(/['"]/g, '') : '';
410
+ if (moduleName) {
411
+ const resolved = resolveModulePath(ctx.filePath, moduleName);
412
+ nodes.push((0, types_1.createModuleNode)(resolved));
413
+ edges.push({ from: fileNode.id, to: (0, types_1.moduleNodeId)(resolved), type: types_1.EdgeType.IMPORTS });
414
+ }
415
+ }
416
+ if (node.type === 'class_declaration') {
417
+ const extendsNode = node.childForFieldName('superclass');
418
+ if (extendsNode) {
419
+ const target = symbolTable.get(extendsNode.text);
420
+ if (target)
421
+ edges.push({ from: fileNode.id, to: target.id, type: types_1.EdgeType.INHERITS });
422
+ }
423
+ const implNode = node.childForFieldName('interfaces');
424
+ if (implNode) {
425
+ for (let i = 0; i < implNode.namedChildCount; i++) {
426
+ const iface = implNode.namedChild(i);
427
+ if (!iface)
428
+ continue;
429
+ const target = symbolTable.get(iface.text);
430
+ if (target)
431
+ edges.push({ from: fileNode.id, to: target.id, type: types_1.EdgeType.IMPLEMENTS });
432
+ }
433
+ }
434
+ }
435
+ for (let i = 0; i < node.childCount; i++) {
436
+ const child = node.child(i);
437
+ if (child)
438
+ visit(child);
439
+ }
440
+ };
441
+ visit(ctx.root);
442
+ }
443
+ return { nodes, edges, edgeTypes };
444
+ }
445
+ class CallGraphBuilder {
446
+ constructor(repoRoot) {
447
+ this.repoRoot = repoRoot;
448
+ this.contexts = [];
449
+ this.graph = null;
450
+ }
451
+ addFile(filePath, content) {
452
+ const parser = new tree_sitter_1.default();
453
+ parser.setLanguage(tree_sitter_typescript_1.default.typescript);
454
+ const tree = parser.parse(content);
455
+ const filePosix = (0, paths_1.toPosixPath)(path_1.default.isAbsolute(filePath) ? filePath : path_1.default.join(this.repoRoot, filePath));
456
+ this.contexts.push({ filePath: filePosix, lang: 'typescript', root: tree.rootNode });
457
+ }
458
+ build() {
459
+ if (!this.graph) {
460
+ this.graph = buildCallGraphLayer(this.contexts).graph;
461
+ }
462
+ return this.graph;
463
+ }
464
+ getCallees(functionId) {
465
+ const graph = this.build();
466
+ const callees = new Set();
467
+ for (const call of graph.calls) {
468
+ if (call.from === functionId)
469
+ callees.add(call.to);
470
+ }
471
+ return Array.from(callees);
472
+ }
473
+ getCallers(functionId) {
474
+ const graph = this.build();
475
+ const callers = new Set();
476
+ for (const call of graph.calls) {
477
+ if (call.to === functionId)
478
+ callers.add(call.from);
479
+ }
480
+ return Array.from(callers);
481
+ }
482
+ }
483
+ exports.CallGraphBuilder = CallGraphBuilder;