@compilr-dev/agents-coding-ts 0.1.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.
- package/LICENSE +21 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +66 -0
- package/dist/parser/index.d.ts +7 -0
- package/dist/parser/index.js +6 -0
- package/dist/parser/typescript-parser.d.ts +22 -0
- package/dist/parser/typescript-parser.js +423 -0
- package/dist/skills/code-health.d.ts +9 -0
- package/dist/skills/code-health.js +167 -0
- package/dist/skills/code-structure.d.ts +9 -0
- package/dist/skills/code-structure.js +97 -0
- package/dist/skills/dependency-audit.d.ts +9 -0
- package/dist/skills/dependency-audit.js +110 -0
- package/dist/skills/index.d.ts +16 -0
- package/dist/skills/index.js +27 -0
- package/dist/skills/refactor-impact.d.ts +9 -0
- package/dist/skills/refactor-impact.js +135 -0
- package/dist/skills/type-analysis.d.ts +9 -0
- package/dist/skills/type-analysis.js +150 -0
- package/dist/tools/find-dead-code.d.ts +20 -0
- package/dist/tools/find-dead-code.js +375 -0
- package/dist/tools/find-duplicates.d.ts +21 -0
- package/dist/tools/find-duplicates.js +274 -0
- package/dist/tools/find-implementations.d.ts +21 -0
- package/dist/tools/find-implementations.js +436 -0
- package/dist/tools/find-patterns.d.ts +21 -0
- package/dist/tools/find-patterns.js +457 -0
- package/dist/tools/find-references.d.ts +23 -0
- package/dist/tools/find-references.js +488 -0
- package/dist/tools/find-symbol.d.ts +21 -0
- package/dist/tools/find-symbol.js +458 -0
- package/dist/tools/get-call-graph.d.ts +23 -0
- package/dist/tools/get-call-graph.js +469 -0
- package/dist/tools/get-complexity.d.ts +21 -0
- package/dist/tools/get-complexity.js +394 -0
- package/dist/tools/get-dependency-graph.d.ts +23 -0
- package/dist/tools/get-dependency-graph.js +482 -0
- package/dist/tools/get-documentation.d.ts +21 -0
- package/dist/tools/get-documentation.js +613 -0
- package/dist/tools/get-exports.d.ts +21 -0
- package/dist/tools/get-exports.js +427 -0
- package/dist/tools/get-file-structure.d.ts +27 -0
- package/dist/tools/get-file-structure.js +120 -0
- package/dist/tools/get-imports.d.ts +23 -0
- package/dist/tools/get-imports.js +350 -0
- package/dist/tools/get-signature.d.ts +20 -0
- package/dist/tools/get-signature.js +758 -0
- package/dist/tools/get-type-hierarchy.d.ts +22 -0
- package/dist/tools/get-type-hierarchy.js +485 -0
- package/dist/tools/index.d.ts +23 -0
- package/dist/tools/index.js +25 -0
- package/dist/tools/types.d.ts +1302 -0
- package/dist/tools/types.js +7 -0
- package/package.json +84 -0
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* getCallGraph Tool
|
|
3
|
+
*
|
|
4
|
+
* Analyze function call relationships using TypeScript Compiler API.
|
|
5
|
+
* Returns a call graph showing what functions call what.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'node:fs/promises';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import * as ts from 'typescript';
|
|
10
|
+
import { defineTool, createSuccessResult, createErrorResult } from '@compilr-dev/agents';
|
|
11
|
+
import { detectLanguage, isLanguageSupported } from '../parser/typescript-parser.js';
|
|
12
|
+
// Tool description
|
|
13
|
+
const TOOL_DESCRIPTION = `Analyze function call relationships in source code.
|
|
14
|
+
Returns a call graph showing which functions call which other functions.
|
|
15
|
+
Use this to understand code flow, dependencies, and refactoring impact.`;
|
|
16
|
+
// Tool input schema
|
|
17
|
+
const TOOL_INPUT_SCHEMA = {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: {
|
|
20
|
+
path: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: 'File to analyze',
|
|
23
|
+
},
|
|
24
|
+
functionName: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
description: 'Specific function to analyze (optional)',
|
|
27
|
+
},
|
|
28
|
+
direction: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
enum: ['callers', 'callees', 'both'],
|
|
31
|
+
description: "Direction: 'callers' | 'callees' | 'both' (default: 'both')",
|
|
32
|
+
},
|
|
33
|
+
maxDepth: {
|
|
34
|
+
type: 'number',
|
|
35
|
+
description: 'Maximum depth to traverse (default: 2)',
|
|
36
|
+
default: 2,
|
|
37
|
+
},
|
|
38
|
+
includeExternal: {
|
|
39
|
+
type: 'boolean',
|
|
40
|
+
description: 'Include external package calls (default: false)',
|
|
41
|
+
default: false,
|
|
42
|
+
},
|
|
43
|
+
scope: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: "Scope directory for finding callers (default: file's directory)",
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
required: ['path'],
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* getCallGraph tool - Analyze function call relationships
|
|
52
|
+
*/
|
|
53
|
+
export const getCallGraphTool = defineTool({
|
|
54
|
+
name: 'get_call_graph',
|
|
55
|
+
description: TOOL_DESCRIPTION,
|
|
56
|
+
inputSchema: TOOL_INPUT_SCHEMA,
|
|
57
|
+
execute: executeGetCallGraph,
|
|
58
|
+
});
|
|
59
|
+
/**
|
|
60
|
+
* Execute the getCallGraph tool
|
|
61
|
+
*/
|
|
62
|
+
async function executeGetCallGraph(input) {
|
|
63
|
+
const { path: inputPath, functionName, direction = 'both', maxDepth = 2, includeExternal = false, scope, } = input;
|
|
64
|
+
// Validate input
|
|
65
|
+
if (!inputPath || inputPath.trim().length === 0) {
|
|
66
|
+
return createErrorResult('Path is required');
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
// Check if path exists
|
|
70
|
+
try {
|
|
71
|
+
await fs.access(inputPath);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return createErrorResult(`Path not found: ${inputPath}`);
|
|
75
|
+
}
|
|
76
|
+
const stats = await fs.stat(inputPath);
|
|
77
|
+
if (!stats.isFile()) {
|
|
78
|
+
return createErrorResult(`Path must be a file: ${inputPath}`);
|
|
79
|
+
}
|
|
80
|
+
// Check for supported language
|
|
81
|
+
const detection = detectLanguage(inputPath);
|
|
82
|
+
if (!detection.language || !isLanguageSupported(detection.language)) {
|
|
83
|
+
return createErrorResult(`Unsupported file type: ${path.extname(inputPath)}`);
|
|
84
|
+
}
|
|
85
|
+
// Analyze the file
|
|
86
|
+
const scopeDir = scope ?? path.dirname(inputPath);
|
|
87
|
+
const result = await analyzeCallGraph(inputPath, functionName, direction, maxDepth, includeExternal, scopeDir);
|
|
88
|
+
return createSuccessResult(result);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
92
|
+
return createErrorResult(`Failed to analyze call graph: ${message}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Analyze call graph for a file
|
|
97
|
+
*/
|
|
98
|
+
async function analyzeCallGraph(filePath, functionName, direction, maxDepth, includeExternal, _scopeDir) {
|
|
99
|
+
const sourceCode = await fs.readFile(filePath, 'utf-8');
|
|
100
|
+
const scriptKind = getScriptKind(filePath);
|
|
101
|
+
const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest, true, scriptKind);
|
|
102
|
+
// Find all functions in the file
|
|
103
|
+
const functions = extractFunctions(sourceFile, filePath);
|
|
104
|
+
// Build the call graph
|
|
105
|
+
const graphNodes = new Map();
|
|
106
|
+
let rootNode;
|
|
107
|
+
let externalCallCount = 0;
|
|
108
|
+
// If functionName is specified, find it
|
|
109
|
+
if (functionName) {
|
|
110
|
+
const targetFunc = functions.find((f) => f.node.name === functionName);
|
|
111
|
+
if (!targetFunc) {
|
|
112
|
+
// Return empty result if function not found
|
|
113
|
+
return {
|
|
114
|
+
graph: [],
|
|
115
|
+
stats: {
|
|
116
|
+
totalFunctions: 0,
|
|
117
|
+
totalCalls: 0,
|
|
118
|
+
maxDepthReached: 0,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
rootNode = targetFunc.node;
|
|
123
|
+
}
|
|
124
|
+
// Analyze callees (what each function calls)
|
|
125
|
+
if (direction === 'callees' || direction === 'both') {
|
|
126
|
+
const functionsToAnalyze = functionName
|
|
127
|
+
? functions.filter((f) => f.node.name === functionName)
|
|
128
|
+
: functions;
|
|
129
|
+
for (const func of functionsToAnalyze) {
|
|
130
|
+
const callees = extractCallees(sourceFile, func, functions, includeExternal);
|
|
131
|
+
let graphNode = graphNodes.get(func.node.name);
|
|
132
|
+
if (!graphNode) {
|
|
133
|
+
graphNode = {
|
|
134
|
+
function: func.node,
|
|
135
|
+
calls: [],
|
|
136
|
+
calledBy: [],
|
|
137
|
+
};
|
|
138
|
+
graphNodes.set(func.node.name, graphNode);
|
|
139
|
+
}
|
|
140
|
+
graphNode.calls = callees;
|
|
141
|
+
// Count external calls
|
|
142
|
+
if (includeExternal) {
|
|
143
|
+
externalCallCount += callees.filter((c) => c.function.path === 'external').length;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Analyze callers (what calls each function)
|
|
148
|
+
if (direction === 'callers' || direction === 'both') {
|
|
149
|
+
const functionsToAnalyze = functionName
|
|
150
|
+
? functions.filter((f) => f.node.name === functionName)
|
|
151
|
+
: functions;
|
|
152
|
+
for (const func of functionsToAnalyze) {
|
|
153
|
+
// Find callers within the same file
|
|
154
|
+
const callers = findCallersInFile(sourceFile, func.node.name, functions, filePath);
|
|
155
|
+
let graphNode = graphNodes.get(func.node.name);
|
|
156
|
+
if (!graphNode) {
|
|
157
|
+
graphNode = {
|
|
158
|
+
function: func.node,
|
|
159
|
+
calls: [],
|
|
160
|
+
calledBy: [],
|
|
161
|
+
};
|
|
162
|
+
graphNodes.set(func.node.name, graphNode);
|
|
163
|
+
}
|
|
164
|
+
graphNode.calledBy = callers;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// Calculate statistics
|
|
168
|
+
const graph = Array.from(graphNodes.values());
|
|
169
|
+
const totalCalls = graph.reduce((sum, node) => sum + node.calls.length, 0);
|
|
170
|
+
const callGraphStats = {
|
|
171
|
+
totalFunctions: graph.length,
|
|
172
|
+
totalCalls,
|
|
173
|
+
maxDepthReached: Math.min(maxDepth, 1), // Currently we only go 1 level deep
|
|
174
|
+
externalCalls: includeExternal ? externalCallCount : undefined,
|
|
175
|
+
};
|
|
176
|
+
return {
|
|
177
|
+
root: rootNode,
|
|
178
|
+
graph,
|
|
179
|
+
stats: callGraphStats,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Extract all functions from a source file
|
|
184
|
+
*/
|
|
185
|
+
function extractFunctions(sourceFile, filePath) {
|
|
186
|
+
const functions = [];
|
|
187
|
+
function visit(node, className) {
|
|
188
|
+
// Function declarations
|
|
189
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
190
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
191
|
+
const isExported = hasExportModifier(node);
|
|
192
|
+
const isAsync = hasAsyncModifier(node);
|
|
193
|
+
functions.push({
|
|
194
|
+
node: {
|
|
195
|
+
name: node.name.text,
|
|
196
|
+
path: filePath,
|
|
197
|
+
line: line + 1,
|
|
198
|
+
column: character + 1,
|
|
199
|
+
async: isAsync,
|
|
200
|
+
exported: isExported,
|
|
201
|
+
},
|
|
202
|
+
bodyStart: node.body?.getStart(sourceFile) ?? node.getStart(sourceFile),
|
|
203
|
+
bodyEnd: node.body?.getEnd() ?? node.getEnd(),
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
// Arrow functions assigned to variables
|
|
207
|
+
if (ts.isVariableStatement(node)) {
|
|
208
|
+
const decls = node.declarationList.declarations;
|
|
209
|
+
for (const decl of decls) {
|
|
210
|
+
if (ts.isIdentifier(decl.name) && decl.initializer) {
|
|
211
|
+
if (ts.isArrowFunction(decl.initializer) || ts.isFunctionExpression(decl.initializer)) {
|
|
212
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(decl.getStart(sourceFile));
|
|
213
|
+
const isExported = hasExportModifier(node);
|
|
214
|
+
const isAsync = hasAsyncModifier(decl.initializer);
|
|
215
|
+
functions.push({
|
|
216
|
+
node: {
|
|
217
|
+
name: decl.name.text,
|
|
218
|
+
path: filePath,
|
|
219
|
+
line: line + 1,
|
|
220
|
+
column: character + 1,
|
|
221
|
+
async: isAsync,
|
|
222
|
+
exported: isExported,
|
|
223
|
+
},
|
|
224
|
+
bodyStart: decl.initializer.body.getStart(sourceFile),
|
|
225
|
+
bodyEnd: decl.initializer.body.getEnd(),
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Class methods
|
|
232
|
+
if (ts.isClassDeclaration(node) && node.name) {
|
|
233
|
+
const currentClassName = node.name.text;
|
|
234
|
+
for (const member of node.members) {
|
|
235
|
+
if (ts.isMethodDeclaration(member) && ts.isIdentifier(member.name)) {
|
|
236
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(member.getStart(sourceFile));
|
|
237
|
+
const isAsync = hasAsyncModifier(member);
|
|
238
|
+
functions.push({
|
|
239
|
+
node: {
|
|
240
|
+
name: member.name.text,
|
|
241
|
+
path: filePath,
|
|
242
|
+
line: line + 1,
|
|
243
|
+
column: character + 1,
|
|
244
|
+
className: currentClassName,
|
|
245
|
+
async: isAsync,
|
|
246
|
+
exported: hasExportModifier(node), // Class export implies method access
|
|
247
|
+
},
|
|
248
|
+
bodyStart: member.body?.getStart(sourceFile) ?? member.getStart(sourceFile),
|
|
249
|
+
bodyEnd: member.body?.getEnd() ?? member.getEnd(),
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
ts.forEachChild(node, (child) => {
|
|
255
|
+
visit(child, className);
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
visit(sourceFile);
|
|
259
|
+
return functions;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Extract callees (functions called by a function)
|
|
263
|
+
*/
|
|
264
|
+
function extractCallees(sourceFile, func, allFunctions, includeExternal) {
|
|
265
|
+
const callCounts = new Map();
|
|
266
|
+
function visit(node) {
|
|
267
|
+
// Skip nodes outside the function body
|
|
268
|
+
const nodeStart = node.getStart(sourceFile);
|
|
269
|
+
const nodeEnd = node.getEnd();
|
|
270
|
+
if (nodeStart < func.bodyStart || nodeEnd > func.bodyEnd) {
|
|
271
|
+
ts.forEachChild(node, visit);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (ts.isCallExpression(node)) {
|
|
275
|
+
const callInfo = extractCallInfo(node, sourceFile, allFunctions, includeExternal);
|
|
276
|
+
if (callInfo) {
|
|
277
|
+
const key = `${callInfo.function.name}:${callInfo.function.path}`;
|
|
278
|
+
const existing = callCounts.get(key);
|
|
279
|
+
if (existing) {
|
|
280
|
+
existing.count++;
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
callCounts.set(key, callInfo);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
ts.forEachChild(node, visit);
|
|
288
|
+
}
|
|
289
|
+
visit(sourceFile);
|
|
290
|
+
return Array.from(callCounts.values());
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Extract call info from a call expression
|
|
294
|
+
*/
|
|
295
|
+
function extractCallInfo(node, sourceFile, allFunctions, includeExternal) {
|
|
296
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
297
|
+
let funcName;
|
|
298
|
+
let callType = 'direct';
|
|
299
|
+
let className;
|
|
300
|
+
// Simple function call: funcName()
|
|
301
|
+
if (ts.isIdentifier(node.expression)) {
|
|
302
|
+
funcName = node.expression.text;
|
|
303
|
+
}
|
|
304
|
+
// Method call: obj.method() or this.method() or promise chain
|
|
305
|
+
else if (ts.isPropertyAccessExpression(node.expression)) {
|
|
306
|
+
funcName = node.expression.name.text;
|
|
307
|
+
const propName = node.expression.name.text;
|
|
308
|
+
// Check for promise chain
|
|
309
|
+
if (propName === 'then' || propName === 'catch' || propName === 'finally') {
|
|
310
|
+
callType = 'promise';
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
callType = 'method';
|
|
314
|
+
if (ts.isIdentifier(node.expression.expression)) {
|
|
315
|
+
const objName = node.expression.expression.text;
|
|
316
|
+
if (objName !== 'this') {
|
|
317
|
+
// Could be instance.method or ClassName.staticMethod
|
|
318
|
+
className = objName;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (!funcName) {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
// Check if it's a known function in the file
|
|
327
|
+
const knownFunc = allFunctions.find((f) => f.node.name === funcName);
|
|
328
|
+
if (knownFunc) {
|
|
329
|
+
return {
|
|
330
|
+
function: knownFunc.node,
|
|
331
|
+
callLine: line + 1,
|
|
332
|
+
callColumn: character + 1,
|
|
333
|
+
type: callType,
|
|
334
|
+
count: 1,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
// External call (not in this file)
|
|
338
|
+
if (includeExternal) {
|
|
339
|
+
return {
|
|
340
|
+
function: {
|
|
341
|
+
name: funcName,
|
|
342
|
+
path: 'external',
|
|
343
|
+
line: 0,
|
|
344
|
+
column: 0,
|
|
345
|
+
className,
|
|
346
|
+
},
|
|
347
|
+
callLine: line + 1,
|
|
348
|
+
callColumn: character + 1,
|
|
349
|
+
type: callType,
|
|
350
|
+
count: 1,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Find callers of a function within the same file
|
|
357
|
+
*/
|
|
358
|
+
function findCallersInFile(sourceFile, targetFuncName, allFunctions, _filePath) {
|
|
359
|
+
const callerCounts = new Map();
|
|
360
|
+
for (const func of allFunctions) {
|
|
361
|
+
// Skip the function itself
|
|
362
|
+
if (func.node.name === targetFuncName) {
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
// Check if this function calls the target
|
|
366
|
+
let callsTarget = false;
|
|
367
|
+
let callLine = 0;
|
|
368
|
+
let callColumn = 0;
|
|
369
|
+
let callCount = 0;
|
|
370
|
+
function visit(node) {
|
|
371
|
+
// Skip nodes outside the function body
|
|
372
|
+
const nodeStart = node.getStart(sourceFile);
|
|
373
|
+
const nodeEnd = node.getEnd();
|
|
374
|
+
if (nodeStart < func.bodyStart || nodeEnd > func.bodyEnd) {
|
|
375
|
+
ts.forEachChild(node, visit);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
if (ts.isCallExpression(node)) {
|
|
379
|
+
let calledName;
|
|
380
|
+
if (ts.isIdentifier(node.expression)) {
|
|
381
|
+
calledName = node.expression.text;
|
|
382
|
+
}
|
|
383
|
+
else if (ts.isPropertyAccessExpression(node.expression)) {
|
|
384
|
+
calledName = node.expression.name.text;
|
|
385
|
+
}
|
|
386
|
+
if (calledName === targetFuncName) {
|
|
387
|
+
callsTarget = true;
|
|
388
|
+
callCount++;
|
|
389
|
+
if (callLine === 0) {
|
|
390
|
+
const pos = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
391
|
+
callLine = pos.line + 1;
|
|
392
|
+
callColumn = pos.character + 1;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
ts.forEachChild(node, visit);
|
|
397
|
+
}
|
|
398
|
+
visit(sourceFile);
|
|
399
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Modified in nested visit function
|
|
400
|
+
if (callsTarget) {
|
|
401
|
+
const key = func.node.name;
|
|
402
|
+
callerCounts.set(key, {
|
|
403
|
+
function: func.node,
|
|
404
|
+
callLine,
|
|
405
|
+
callColumn,
|
|
406
|
+
type: 'direct',
|
|
407
|
+
count: callCount,
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return Array.from(callerCounts.values());
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Check if a node has export modifier
|
|
415
|
+
*/
|
|
416
|
+
function hasExportModifier(node) {
|
|
417
|
+
const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
|
|
418
|
+
return modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Check if a node has async modifier
|
|
422
|
+
*/
|
|
423
|
+
function hasAsyncModifier(node) {
|
|
424
|
+
const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
|
|
425
|
+
return modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) ?? false;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Get TypeScript script kind from file extension
|
|
429
|
+
*/
|
|
430
|
+
function getScriptKind(filePath) {
|
|
431
|
+
const ext = filePath.toLowerCase().split('.').pop();
|
|
432
|
+
switch (ext) {
|
|
433
|
+
case 'ts':
|
|
434
|
+
return ts.ScriptKind.TS;
|
|
435
|
+
case 'tsx':
|
|
436
|
+
return ts.ScriptKind.TSX;
|
|
437
|
+
case 'js':
|
|
438
|
+
return ts.ScriptKind.JS;
|
|
439
|
+
case 'jsx':
|
|
440
|
+
return ts.ScriptKind.JSX;
|
|
441
|
+
case 'mts':
|
|
442
|
+
case 'cts':
|
|
443
|
+
return ts.ScriptKind.TS;
|
|
444
|
+
case 'mjs':
|
|
445
|
+
case 'cjs':
|
|
446
|
+
return ts.ScriptKind.JS;
|
|
447
|
+
default:
|
|
448
|
+
return ts.ScriptKind.TS;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Factory function to create a customized getCallGraph tool
|
|
453
|
+
*/
|
|
454
|
+
export function createGetCallGraphTool(options) {
|
|
455
|
+
const { defaultDirection = 'both', defaultMaxDepth = 2, defaultIncludeExternal = false, } = options ?? {};
|
|
456
|
+
return defineTool({
|
|
457
|
+
name: 'get_call_graph',
|
|
458
|
+
description: TOOL_DESCRIPTION,
|
|
459
|
+
inputSchema: TOOL_INPUT_SCHEMA,
|
|
460
|
+
execute: async (input) => {
|
|
461
|
+
return executeGetCallGraph({
|
|
462
|
+
...input,
|
|
463
|
+
direction: input.direction ?? defaultDirection,
|
|
464
|
+
maxDepth: input.maxDepth ?? defaultMaxDepth,
|
|
465
|
+
includeExternal: input.includeExternal ?? defaultIncludeExternal,
|
|
466
|
+
});
|
|
467
|
+
},
|
|
468
|
+
});
|
|
469
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* getComplexity Tool
|
|
3
|
+
*
|
|
4
|
+
* Calculate code complexity metrics including cyclomatic complexity,
|
|
5
|
+
* cognitive complexity, nesting depth, and identify complexity hotspots.
|
|
6
|
+
*/
|
|
7
|
+
import type { Tool } from '@compilr-dev/agents';
|
|
8
|
+
import type { GetComplexityInput } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* getComplexity tool
|
|
11
|
+
*/
|
|
12
|
+
export declare const getComplexityTool: Tool<GetComplexityInput>;
|
|
13
|
+
/**
|
|
14
|
+
* Create customizable getComplexity tool
|
|
15
|
+
*/
|
|
16
|
+
export declare function createGetComplexityTool(options?: {
|
|
17
|
+
name?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
defaultThreshold?: number;
|
|
20
|
+
defaultMaxFiles?: number;
|
|
21
|
+
}): Tool<GetComplexityInput>;
|