@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,458 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* findSymbol Tool
|
|
3
|
+
*
|
|
4
|
+
* Find the definition location of a symbol across the codebase.
|
|
5
|
+
* Uses TypeScript Compiler API for accurate AST analysis.
|
|
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
|
+
// Supported file extensions for searching
|
|
13
|
+
const SUPPORTED_EXTENSIONS = new Set([
|
|
14
|
+
'.ts',
|
|
15
|
+
'.tsx',
|
|
16
|
+
'.js',
|
|
17
|
+
'.jsx',
|
|
18
|
+
'.mts',
|
|
19
|
+
'.mjs',
|
|
20
|
+
'.cts',
|
|
21
|
+
'.cjs',
|
|
22
|
+
]);
|
|
23
|
+
// Helper to check for a specific modifier kind
|
|
24
|
+
function hasModifierKind(modifiers, kind) {
|
|
25
|
+
return modifiers?.some((m) => m.kind === kind) ?? false;
|
|
26
|
+
}
|
|
27
|
+
// Tool description
|
|
28
|
+
const TOOL_DESCRIPTION = `Find the definition location of a symbol by name across the codebase.
|
|
29
|
+
Returns structured JSON with file path, line number, symbol kind, and other metadata.
|
|
30
|
+
Use this to locate where functions, classes, variables, types, etc. are defined.`;
|
|
31
|
+
// Tool input schema
|
|
32
|
+
const TOOL_INPUT_SCHEMA = {
|
|
33
|
+
type: 'object',
|
|
34
|
+
properties: {
|
|
35
|
+
symbol: {
|
|
36
|
+
type: 'string',
|
|
37
|
+
description: 'Symbol name to find',
|
|
38
|
+
},
|
|
39
|
+
scope: {
|
|
40
|
+
type: 'string',
|
|
41
|
+
description: 'Scope the search to a specific file or directory',
|
|
42
|
+
},
|
|
43
|
+
kind: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
enum: [
|
|
46
|
+
'function',
|
|
47
|
+
'class',
|
|
48
|
+
'variable',
|
|
49
|
+
'type',
|
|
50
|
+
'interface',
|
|
51
|
+
'enum',
|
|
52
|
+
'method',
|
|
53
|
+
'property',
|
|
54
|
+
'any',
|
|
55
|
+
],
|
|
56
|
+
description: 'Filter by symbol kind (default: any)',
|
|
57
|
+
default: 'any',
|
|
58
|
+
},
|
|
59
|
+
limit: {
|
|
60
|
+
type: 'number',
|
|
61
|
+
description: 'Maximum results to return (default: 10)',
|
|
62
|
+
default: 10,
|
|
63
|
+
},
|
|
64
|
+
includeNodeModules: {
|
|
65
|
+
type: 'boolean',
|
|
66
|
+
description: 'Include symbols from node_modules (default: false)',
|
|
67
|
+
default: false,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
required: ['symbol'],
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* findSymbol tool - Find symbol definitions
|
|
74
|
+
*/
|
|
75
|
+
export const findSymbolTool = defineTool({
|
|
76
|
+
name: 'find_symbol',
|
|
77
|
+
description: TOOL_DESCRIPTION,
|
|
78
|
+
inputSchema: TOOL_INPUT_SCHEMA,
|
|
79
|
+
execute: executeFindSymbol,
|
|
80
|
+
});
|
|
81
|
+
/**
|
|
82
|
+
* Execute the findSymbol tool
|
|
83
|
+
*/
|
|
84
|
+
async function executeFindSymbol(input) {
|
|
85
|
+
const { symbol, scope, kind = 'any', limit = 10, includeNodeModules = false } = input;
|
|
86
|
+
// Validate input
|
|
87
|
+
if (!symbol || symbol.trim().length === 0) {
|
|
88
|
+
return createErrorResult('Symbol name is required');
|
|
89
|
+
}
|
|
90
|
+
const startTime = Date.now();
|
|
91
|
+
const definitions = [];
|
|
92
|
+
let filesSearched = 0;
|
|
93
|
+
try {
|
|
94
|
+
// Determine search path
|
|
95
|
+
const searchPath = scope || process.cwd();
|
|
96
|
+
// Check if path exists
|
|
97
|
+
try {
|
|
98
|
+
await fs.access(searchPath);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return createErrorResult(`Path not found: ${searchPath}`);
|
|
102
|
+
}
|
|
103
|
+
const stats = await fs.stat(searchPath);
|
|
104
|
+
if (stats.isFile()) {
|
|
105
|
+
// Search single file
|
|
106
|
+
const detection = detectLanguage(searchPath);
|
|
107
|
+
if (detection.language && isLanguageSupported(detection.language)) {
|
|
108
|
+
filesSearched = 1;
|
|
109
|
+
const fileDefinitions = await searchFileForSymbol(searchPath, symbol, kind);
|
|
110
|
+
definitions.push(...fileDefinitions);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else if (stats.isDirectory()) {
|
|
114
|
+
// Search directory recursively
|
|
115
|
+
const files = await collectFiles(searchPath, includeNodeModules);
|
|
116
|
+
filesSearched = files.length;
|
|
117
|
+
for (const filePath of files) {
|
|
118
|
+
if (definitions.length >= limit)
|
|
119
|
+
break;
|
|
120
|
+
const fileDefinitions = await searchFileForSymbol(filePath, symbol, kind);
|
|
121
|
+
const remaining = limit - definitions.length;
|
|
122
|
+
definitions.push(...fileDefinitions.slice(0, remaining));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
return createErrorResult(`Path is neither a file nor directory: ${searchPath}`);
|
|
127
|
+
}
|
|
128
|
+
const endTime = Date.now();
|
|
129
|
+
const result = {
|
|
130
|
+
query: symbol,
|
|
131
|
+
definitions: definitions.slice(0, limit),
|
|
132
|
+
stats: {
|
|
133
|
+
filesSearched,
|
|
134
|
+
timeMs: endTime - startTime,
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
return createSuccessResult(result);
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
141
|
+
return createErrorResult(`Failed to search for symbol: ${message}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Collect all supported files in a directory recursively
|
|
146
|
+
*/
|
|
147
|
+
async function collectFiles(dir, includeNodeModules) {
|
|
148
|
+
const files = [];
|
|
149
|
+
async function walk(currentDir) {
|
|
150
|
+
const entries = await fs.readdir(currentDir, { withFileTypes: true });
|
|
151
|
+
for (const entry of entries) {
|
|
152
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
153
|
+
if (entry.isDirectory()) {
|
|
154
|
+
// Skip node_modules unless explicitly included
|
|
155
|
+
if (entry.name === 'node_modules' && !includeNodeModules) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
// Skip common non-source directories
|
|
159
|
+
if (entry.name.startsWith('.') ||
|
|
160
|
+
entry.name === 'dist' ||
|
|
161
|
+
entry.name === 'build' ||
|
|
162
|
+
entry.name === 'coverage') {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
await walk(fullPath);
|
|
166
|
+
}
|
|
167
|
+
else if (entry.isFile()) {
|
|
168
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
169
|
+
if (SUPPORTED_EXTENSIONS.has(ext)) {
|
|
170
|
+
files.push(fullPath);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
await walk(dir);
|
|
176
|
+
return files;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Search a single file for symbol definitions
|
|
180
|
+
*/
|
|
181
|
+
async function searchFileForSymbol(filePath, symbolName, kindFilter) {
|
|
182
|
+
const definitions = [];
|
|
183
|
+
try {
|
|
184
|
+
const sourceCode = await fs.readFile(filePath, 'utf-8');
|
|
185
|
+
const scriptKind = getScriptKind(filePath);
|
|
186
|
+
const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest, true, scriptKind);
|
|
187
|
+
// Search for symbols
|
|
188
|
+
findSymbolsInNode(sourceFile, sourceFile, symbolName, kindFilter, filePath, definitions);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
// Silently skip files that can't be parsed
|
|
192
|
+
}
|
|
193
|
+
return definitions;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Get TypeScript script kind from file extension
|
|
197
|
+
*/
|
|
198
|
+
function getScriptKind(filePath) {
|
|
199
|
+
const ext = filePath.toLowerCase().split('.').pop();
|
|
200
|
+
switch (ext) {
|
|
201
|
+
case 'ts':
|
|
202
|
+
return ts.ScriptKind.TS;
|
|
203
|
+
case 'tsx':
|
|
204
|
+
return ts.ScriptKind.TSX;
|
|
205
|
+
case 'js':
|
|
206
|
+
return ts.ScriptKind.JS;
|
|
207
|
+
case 'jsx':
|
|
208
|
+
return ts.ScriptKind.JSX;
|
|
209
|
+
case 'mts':
|
|
210
|
+
case 'cts':
|
|
211
|
+
return ts.ScriptKind.TS;
|
|
212
|
+
case 'mjs':
|
|
213
|
+
case 'cjs':
|
|
214
|
+
return ts.ScriptKind.JS;
|
|
215
|
+
default:
|
|
216
|
+
return ts.ScriptKind.TS;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Recursively find symbols in AST nodes
|
|
221
|
+
*/
|
|
222
|
+
function findSymbolsInNode(node, sourceFile, symbolName, kindFilter, filePath, definitions, container) {
|
|
223
|
+
// Check current node for symbol definition
|
|
224
|
+
const def = extractSymbolDefinition(node, sourceFile, symbolName, kindFilter, filePath, container);
|
|
225
|
+
if (def) {
|
|
226
|
+
definitions.push(def);
|
|
227
|
+
}
|
|
228
|
+
// Recurse into children
|
|
229
|
+
ts.forEachChild(node, (child) => {
|
|
230
|
+
// Track container context for methods/properties
|
|
231
|
+
let newContainer = container;
|
|
232
|
+
if (ts.isClassDeclaration(node) && node.name) {
|
|
233
|
+
newContainer = node.name.text;
|
|
234
|
+
}
|
|
235
|
+
findSymbolsInNode(child, sourceFile, symbolName, kindFilter, filePath, definitions, newContainer);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Extract symbol definition from a node if it matches
|
|
240
|
+
*/
|
|
241
|
+
function extractSymbolDefinition(node, sourceFile, symbolName, kindFilter, filePath, container) {
|
|
242
|
+
let name;
|
|
243
|
+
let kind;
|
|
244
|
+
let exported = false;
|
|
245
|
+
let signature;
|
|
246
|
+
let docSummary;
|
|
247
|
+
const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
|
|
248
|
+
exported = hasModifierKind(modifiers, ts.SyntaxKind.ExportKeyword);
|
|
249
|
+
// Function declarations
|
|
250
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
251
|
+
name = node.name.text;
|
|
252
|
+
kind = 'function';
|
|
253
|
+
signature = buildFunctionSignature(node);
|
|
254
|
+
docSummary = getJsDocSummary(node, sourceFile);
|
|
255
|
+
}
|
|
256
|
+
// Class declarations
|
|
257
|
+
else if (ts.isClassDeclaration(node) && node.name) {
|
|
258
|
+
name = node.name.text;
|
|
259
|
+
kind = 'class';
|
|
260
|
+
signature = `class ${name}`;
|
|
261
|
+
docSummary = getJsDocSummary(node, sourceFile);
|
|
262
|
+
}
|
|
263
|
+
// Interface declarations
|
|
264
|
+
else if (ts.isInterfaceDeclaration(node)) {
|
|
265
|
+
name = node.name.text;
|
|
266
|
+
kind = 'interface';
|
|
267
|
+
signature = `interface ${name}`;
|
|
268
|
+
docSummary = getJsDocSummary(node, sourceFile);
|
|
269
|
+
}
|
|
270
|
+
// Type alias declarations
|
|
271
|
+
else if (ts.isTypeAliasDeclaration(node)) {
|
|
272
|
+
name = node.name.text;
|
|
273
|
+
kind = 'type';
|
|
274
|
+
signature = `type ${name}`;
|
|
275
|
+
docSummary = getJsDocSummary(node, sourceFile);
|
|
276
|
+
}
|
|
277
|
+
// Enum declarations
|
|
278
|
+
else if (ts.isEnumDeclaration(node)) {
|
|
279
|
+
name = node.name.text;
|
|
280
|
+
kind = 'enum';
|
|
281
|
+
signature = `enum ${name}`;
|
|
282
|
+
docSummary = getJsDocSummary(node, sourceFile);
|
|
283
|
+
}
|
|
284
|
+
// Variable declarations
|
|
285
|
+
else if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {
|
|
286
|
+
// Skip if this is a function (arrow function or function expression)
|
|
287
|
+
if (node.initializer &&
|
|
288
|
+
(ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer))) {
|
|
289
|
+
name = node.name.text;
|
|
290
|
+
kind = 'function';
|
|
291
|
+
signature = buildArrowFunctionSignature(node);
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
name = node.name.text;
|
|
295
|
+
kind = 'variable';
|
|
296
|
+
signature = `${getVariableKind(node)} ${name}`;
|
|
297
|
+
}
|
|
298
|
+
// Check parent for export status
|
|
299
|
+
const parent = node.parent.parent;
|
|
300
|
+
if (ts.isVariableStatement(parent)) {
|
|
301
|
+
const parentMods = ts.canHaveModifiers(parent) ? ts.getModifiers(parent) : undefined;
|
|
302
|
+
exported = hasModifierKind(parentMods, ts.SyntaxKind.ExportKeyword);
|
|
303
|
+
docSummary = getJsDocSummary(parent, sourceFile);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// Method declarations (inside classes)
|
|
307
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- node.name can be undefined for computed properties
|
|
308
|
+
else if (ts.isMethodDeclaration(node) && node.name && ts.isIdentifier(node.name)) {
|
|
309
|
+
name = node.name.text;
|
|
310
|
+
kind = 'method';
|
|
311
|
+
signature = buildMethodSignature(node);
|
|
312
|
+
docSummary = getJsDocSummary(node, sourceFile);
|
|
313
|
+
// Methods are exported if their containing class is exported
|
|
314
|
+
}
|
|
315
|
+
// Property declarations (inside classes)
|
|
316
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- node.name can be undefined for computed properties
|
|
317
|
+
else if (ts.isPropertyDeclaration(node) && node.name && ts.isIdentifier(node.name)) {
|
|
318
|
+
name = node.name.text;
|
|
319
|
+
kind = 'property';
|
|
320
|
+
const typeAnnotation = node.type ? `: ${node.type.getText(sourceFile)}` : '';
|
|
321
|
+
signature = `${name}${typeAnnotation}`;
|
|
322
|
+
docSummary = getJsDocSummary(node, sourceFile);
|
|
323
|
+
}
|
|
324
|
+
// Check if this matches our search
|
|
325
|
+
if (!name || name !== symbolName) {
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
if (!kind) {
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
// Apply kind filter
|
|
332
|
+
if (kindFilter !== 'any' && kind !== kindFilter) {
|
|
333
|
+
// Handle type filter matching both 'type' and 'interface'
|
|
334
|
+
if (kindFilter === 'type' && kind === 'interface') {
|
|
335
|
+
// Allow interface to match type filter
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
// Get position information
|
|
342
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
343
|
+
return {
|
|
344
|
+
name,
|
|
345
|
+
path: filePath,
|
|
346
|
+
line: line + 1, // Convert to 1-based
|
|
347
|
+
column: character + 1, // Convert to 1-based
|
|
348
|
+
kind,
|
|
349
|
+
container,
|
|
350
|
+
exported,
|
|
351
|
+
signature,
|
|
352
|
+
docSummary,
|
|
353
|
+
confidence: 1.0, // Exact match
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Get variable declaration kind (const, let, var)
|
|
358
|
+
*/
|
|
359
|
+
function getVariableKind(node) {
|
|
360
|
+
const parent = node.parent;
|
|
361
|
+
if (ts.isVariableDeclarationList(parent)) {
|
|
362
|
+
if (parent.flags & ts.NodeFlags.Const)
|
|
363
|
+
return 'const';
|
|
364
|
+
if (parent.flags & ts.NodeFlags.Let)
|
|
365
|
+
return 'let';
|
|
366
|
+
}
|
|
367
|
+
return 'var';
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Build a function signature string
|
|
371
|
+
*/
|
|
372
|
+
function buildFunctionSignature(node) {
|
|
373
|
+
const name = node.name?.text ?? 'anonymous';
|
|
374
|
+
const params = node.parameters
|
|
375
|
+
.map((p) => {
|
|
376
|
+
const paramName = ts.isIdentifier(p.name) ? p.name.text : 'param';
|
|
377
|
+
const paramType = p.type ? p.type.getText() : '';
|
|
378
|
+
return paramType ? `${paramName}: ${paramType}` : paramName;
|
|
379
|
+
})
|
|
380
|
+
.join(', ');
|
|
381
|
+
const returnType = node.type ? `: ${node.type.getText()}` : '';
|
|
382
|
+
const asyncPrefix = hasModifierKind(ts.getModifiers(node), ts.SyntaxKind.AsyncKeyword)
|
|
383
|
+
? 'async '
|
|
384
|
+
: '';
|
|
385
|
+
return `${asyncPrefix}function ${name}(${params})${returnType}`;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Build a method signature string
|
|
389
|
+
*/
|
|
390
|
+
function buildMethodSignature(node) {
|
|
391
|
+
const name = ts.isIdentifier(node.name) ? node.name.text : 'method';
|
|
392
|
+
const params = node.parameters
|
|
393
|
+
.map((p) => {
|
|
394
|
+
const paramName = ts.isIdentifier(p.name) ? p.name.text : 'param';
|
|
395
|
+
const paramType = p.type ? p.type.getText() : '';
|
|
396
|
+
return paramType ? `${paramName}: ${paramType}` : paramName;
|
|
397
|
+
})
|
|
398
|
+
.join(', ');
|
|
399
|
+
const returnType = node.type ? `: ${node.type.getText()}` : '';
|
|
400
|
+
const modifiers = ts.getModifiers(node);
|
|
401
|
+
const asyncPrefix = hasModifierKind(modifiers, ts.SyntaxKind.AsyncKeyword) ? 'async ' : '';
|
|
402
|
+
const staticPrefix = hasModifierKind(modifiers, ts.SyntaxKind.StaticKeyword) ? 'static ' : '';
|
|
403
|
+
return `${staticPrefix}${asyncPrefix}${name}(${params})${returnType}`;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Build an arrow function signature string from variable declaration
|
|
407
|
+
*/
|
|
408
|
+
function buildArrowFunctionSignature(node) {
|
|
409
|
+
const name = ts.isIdentifier(node.name) ? node.name.text : 'anonymous';
|
|
410
|
+
if (node.initializer &&
|
|
411
|
+
(ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer))) {
|
|
412
|
+
const func = node.initializer;
|
|
413
|
+
const params = func.parameters
|
|
414
|
+
.map((p) => {
|
|
415
|
+
const paramName = ts.isIdentifier(p.name) ? p.name.text : 'param';
|
|
416
|
+
const paramType = p.type ? p.type.getText() : '';
|
|
417
|
+
return paramType ? `${paramName}: ${paramType}` : paramName;
|
|
418
|
+
})
|
|
419
|
+
.join(', ');
|
|
420
|
+
const returnType = func.type ? `: ${func.type.getText()}` : '';
|
|
421
|
+
return `const ${name} = (${params})${returnType}`;
|
|
422
|
+
}
|
|
423
|
+
return `const ${name}`;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Extract JSDoc summary (first line) from a node
|
|
427
|
+
*/
|
|
428
|
+
function getJsDocSummary(node, sourceFile) {
|
|
429
|
+
// Get leading trivia (comments before the node)
|
|
430
|
+
const fullText = sourceFile.getFullText();
|
|
431
|
+
const nodeStart = node.getFullStart();
|
|
432
|
+
const nodeTextStart = node.getStart(sourceFile);
|
|
433
|
+
const trivia = fullText.substring(nodeStart, nodeTextStart);
|
|
434
|
+
// Look for JSDoc comment
|
|
435
|
+
const jsDocMatch = trivia.match(/\/\*\*\s*\n?\s*\*?\s*([^\n*]+)/);
|
|
436
|
+
if (jsDocMatch) {
|
|
437
|
+
return jsDocMatch[1].trim();
|
|
438
|
+
}
|
|
439
|
+
return undefined;
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Factory function to create a customized findSymbol tool
|
|
443
|
+
*/
|
|
444
|
+
export function createFindSymbolTool(options) {
|
|
445
|
+
const { defaultLimit = 10, defaultIncludeNodeModules = false } = options ?? {};
|
|
446
|
+
return defineTool({
|
|
447
|
+
name: 'find_symbol',
|
|
448
|
+
description: TOOL_DESCRIPTION,
|
|
449
|
+
inputSchema: TOOL_INPUT_SCHEMA,
|
|
450
|
+
execute: async (input) => {
|
|
451
|
+
return executeFindSymbol({
|
|
452
|
+
...input,
|
|
453
|
+
limit: input.limit ?? defaultLimit,
|
|
454
|
+
includeNodeModules: input.includeNodeModules ?? defaultIncludeNodeModules,
|
|
455
|
+
});
|
|
456
|
+
},
|
|
457
|
+
});
|
|
458
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
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 type { Tool } from '@compilr-dev/agents';
|
|
8
|
+
import type { GetCallGraphInput, CallGraphDirection } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* getCallGraph tool - Analyze function call relationships
|
|
11
|
+
*/
|
|
12
|
+
export declare const getCallGraphTool: Tool<GetCallGraphInput>;
|
|
13
|
+
/**
|
|
14
|
+
* Factory function to create a customized getCallGraph tool
|
|
15
|
+
*/
|
|
16
|
+
export declare function createGetCallGraphTool(options?: {
|
|
17
|
+
/** Default direction */
|
|
18
|
+
defaultDirection?: CallGraphDirection;
|
|
19
|
+
/** Default max depth */
|
|
20
|
+
defaultMaxDepth?: number;
|
|
21
|
+
/** Default includeExternal */
|
|
22
|
+
defaultIncludeExternal?: boolean;
|
|
23
|
+
}): Tool<GetCallGraphInput>;
|