@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,488 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* findReferences Tool
|
|
3
|
+
*
|
|
4
|
+
* Find all usages 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 all usages of a symbol across the codebase.
|
|
29
|
+
Returns structured JSON with file paths, line numbers, and reference types.
|
|
30
|
+
Use this to understand how a function, class, or variable is used throughout the project.`;
|
|
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 references for',
|
|
38
|
+
},
|
|
39
|
+
definitionFile: {
|
|
40
|
+
type: 'string',
|
|
41
|
+
description: 'File where the symbol is defined (improves accuracy)',
|
|
42
|
+
},
|
|
43
|
+
definitionLine: {
|
|
44
|
+
type: 'number',
|
|
45
|
+
description: 'Line where symbol is defined (improves accuracy)',
|
|
46
|
+
},
|
|
47
|
+
scope: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
description: 'Scope the search to a specific file or directory',
|
|
50
|
+
},
|
|
51
|
+
limit: {
|
|
52
|
+
type: 'number',
|
|
53
|
+
description: 'Maximum results to return (default: 50)',
|
|
54
|
+
default: 50,
|
|
55
|
+
},
|
|
56
|
+
includeDefinition: {
|
|
57
|
+
type: 'boolean',
|
|
58
|
+
description: 'Include the definition itself (default: false)',
|
|
59
|
+
default: false,
|
|
60
|
+
},
|
|
61
|
+
groupByFile: {
|
|
62
|
+
type: 'boolean',
|
|
63
|
+
description: 'Group results by file (default: true)',
|
|
64
|
+
default: true,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
required: ['symbol'],
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* findReferences tool - Find all usages of a symbol
|
|
71
|
+
*/
|
|
72
|
+
export const findReferencesTool = defineTool({
|
|
73
|
+
name: 'find_references',
|
|
74
|
+
description: TOOL_DESCRIPTION,
|
|
75
|
+
inputSchema: TOOL_INPUT_SCHEMA,
|
|
76
|
+
execute: executeFindReferences,
|
|
77
|
+
});
|
|
78
|
+
/**
|
|
79
|
+
* Execute the findReferences tool
|
|
80
|
+
*/
|
|
81
|
+
async function executeFindReferences(input) {
|
|
82
|
+
const { symbol, definitionFile, definitionLine, scope, limit = 50, includeDefinition = false, groupByFile = true, } = input;
|
|
83
|
+
// Validate input
|
|
84
|
+
if (!symbol || symbol.trim().length === 0) {
|
|
85
|
+
return createErrorResult('Symbol name is required');
|
|
86
|
+
}
|
|
87
|
+
const startTime = Date.now();
|
|
88
|
+
const allReferences = [];
|
|
89
|
+
let filesSearched = 0;
|
|
90
|
+
let definition;
|
|
91
|
+
try {
|
|
92
|
+
// Determine search path
|
|
93
|
+
const searchPath = scope || process.cwd();
|
|
94
|
+
// Check if path exists
|
|
95
|
+
try {
|
|
96
|
+
await fs.access(searchPath);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return createErrorResult(`Path not found: ${searchPath}`);
|
|
100
|
+
}
|
|
101
|
+
const stats = await fs.stat(searchPath);
|
|
102
|
+
// First, try to find the definition if definitionFile is provided
|
|
103
|
+
if (definitionFile) {
|
|
104
|
+
try {
|
|
105
|
+
await fs.access(definitionFile);
|
|
106
|
+
const detection = detectLanguage(definitionFile);
|
|
107
|
+
if (detection.language && isLanguageSupported(detection.language)) {
|
|
108
|
+
definition = await findDefinitionInFile(definitionFile, symbol, definitionLine);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// Definition file not found, continue without it
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Collect files to search
|
|
116
|
+
let filesToSearch = [];
|
|
117
|
+
if (stats.isFile()) {
|
|
118
|
+
const detection = detectLanguage(searchPath);
|
|
119
|
+
if (detection.language && isLanguageSupported(detection.language)) {
|
|
120
|
+
filesToSearch = [searchPath];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else if (stats.isDirectory()) {
|
|
124
|
+
filesToSearch = await collectFiles(searchPath);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
return createErrorResult(`Path is neither a file nor directory: ${searchPath}`);
|
|
128
|
+
}
|
|
129
|
+
filesSearched = filesToSearch.length;
|
|
130
|
+
// Search each file for references
|
|
131
|
+
for (const filePath of filesToSearch) {
|
|
132
|
+
if (allReferences.length >= limit)
|
|
133
|
+
break;
|
|
134
|
+
const fileRefs = await searchFileForReferences(filePath, symbol, definition, includeDefinition);
|
|
135
|
+
const remaining = limit - allReferences.length;
|
|
136
|
+
allReferences.push(...fileRefs.slice(0, remaining));
|
|
137
|
+
}
|
|
138
|
+
const endTime = Date.now();
|
|
139
|
+
const truncated = allReferences.length >= limit;
|
|
140
|
+
// Build result
|
|
141
|
+
const result = {
|
|
142
|
+
symbol,
|
|
143
|
+
definition,
|
|
144
|
+
totalCount: allReferences.length,
|
|
145
|
+
truncated,
|
|
146
|
+
stats: {
|
|
147
|
+
filesSearched,
|
|
148
|
+
timeMs: endTime - startTime,
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
if (groupByFile) {
|
|
152
|
+
result.byFile = groupReferencesByFile(allReferences);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
result.references = allReferences;
|
|
156
|
+
}
|
|
157
|
+
return createSuccessResult(result);
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
161
|
+
return createErrorResult(`Failed to search for references: ${message}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Collect all supported files in a directory recursively
|
|
166
|
+
*/
|
|
167
|
+
async function collectFiles(dir) {
|
|
168
|
+
const files = [];
|
|
169
|
+
async function walk(currentDir) {
|
|
170
|
+
const entries = await fs.readdir(currentDir, { withFileTypes: true });
|
|
171
|
+
for (const entry of entries) {
|
|
172
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
173
|
+
if (entry.isDirectory()) {
|
|
174
|
+
// Skip node_modules and common non-source directories
|
|
175
|
+
if (entry.name === 'node_modules' ||
|
|
176
|
+
entry.name.startsWith('.') ||
|
|
177
|
+
entry.name === 'dist' ||
|
|
178
|
+
entry.name === 'build' ||
|
|
179
|
+
entry.name === 'coverage') {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
await walk(fullPath);
|
|
183
|
+
}
|
|
184
|
+
else if (entry.isFile()) {
|
|
185
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
186
|
+
if (SUPPORTED_EXTENSIONS.has(ext)) {
|
|
187
|
+
files.push(fullPath);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
await walk(dir);
|
|
193
|
+
return files;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Find the definition of a symbol in a file
|
|
197
|
+
*/
|
|
198
|
+
async function findDefinitionInFile(filePath, symbolName, targetLine) {
|
|
199
|
+
try {
|
|
200
|
+
const sourceCode = await fs.readFile(filePath, 'utf-8');
|
|
201
|
+
const scriptKind = getScriptKind(filePath);
|
|
202
|
+
const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest, true, scriptKind);
|
|
203
|
+
let foundDef;
|
|
204
|
+
function visit(node) {
|
|
205
|
+
if (foundDef)
|
|
206
|
+
return;
|
|
207
|
+
const def = tryExtractDefinition(node, sourceFile, symbolName, filePath);
|
|
208
|
+
if (def) {
|
|
209
|
+
// If targetLine is specified, only match if on that line
|
|
210
|
+
if (targetLine === undefined || def.line === targetLine) {
|
|
211
|
+
foundDef = def;
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
ts.forEachChild(node, visit);
|
|
216
|
+
}
|
|
217
|
+
visit(sourceFile);
|
|
218
|
+
return foundDef;
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Try to extract a definition from a node
|
|
226
|
+
*/
|
|
227
|
+
function tryExtractDefinition(node, sourceFile, symbolName, filePath) {
|
|
228
|
+
let name;
|
|
229
|
+
let kind;
|
|
230
|
+
const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
|
|
231
|
+
const exported = hasModifierKind(modifiers, ts.SyntaxKind.ExportKeyword);
|
|
232
|
+
if (ts.isFunctionDeclaration(node) && node.name?.text === symbolName) {
|
|
233
|
+
name = node.name.text;
|
|
234
|
+
kind = 'function';
|
|
235
|
+
}
|
|
236
|
+
else if (ts.isClassDeclaration(node) && node.name?.text === symbolName) {
|
|
237
|
+
name = node.name.text;
|
|
238
|
+
kind = 'class';
|
|
239
|
+
}
|
|
240
|
+
else if (ts.isInterfaceDeclaration(node) && node.name.text === symbolName) {
|
|
241
|
+
name = node.name.text;
|
|
242
|
+
kind = 'interface';
|
|
243
|
+
}
|
|
244
|
+
else if (ts.isTypeAliasDeclaration(node) && node.name.text === symbolName) {
|
|
245
|
+
name = node.name.text;
|
|
246
|
+
kind = 'type';
|
|
247
|
+
}
|
|
248
|
+
else if (ts.isEnumDeclaration(node) && node.name.text === symbolName) {
|
|
249
|
+
name = node.name.text;
|
|
250
|
+
kind = 'enum';
|
|
251
|
+
}
|
|
252
|
+
else if (ts.isVariableDeclaration(node) &&
|
|
253
|
+
ts.isIdentifier(node.name) &&
|
|
254
|
+
node.name.text === symbolName) {
|
|
255
|
+
name = node.name.text;
|
|
256
|
+
kind = 'variable';
|
|
257
|
+
}
|
|
258
|
+
if (name && kind) {
|
|
259
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
260
|
+
return {
|
|
261
|
+
name,
|
|
262
|
+
path: filePath,
|
|
263
|
+
line: line + 1,
|
|
264
|
+
column: character + 1,
|
|
265
|
+
kind,
|
|
266
|
+
exported,
|
|
267
|
+
confidence: 1.0,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
return undefined;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Search a single file for references to a symbol
|
|
274
|
+
*/
|
|
275
|
+
async function searchFileForReferences(filePath, symbolName, definition, includeDefinition) {
|
|
276
|
+
const references = [];
|
|
277
|
+
try {
|
|
278
|
+
const sourceCode = await fs.readFile(filePath, 'utf-8');
|
|
279
|
+
const lines = sourceCode.split('\n');
|
|
280
|
+
const scriptKind = getScriptKind(filePath);
|
|
281
|
+
const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest, true, scriptKind);
|
|
282
|
+
// Track current containing function for context
|
|
283
|
+
let containingFunction;
|
|
284
|
+
function visit(node) {
|
|
285
|
+
// Track containing function
|
|
286
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
287
|
+
containingFunction = node.name.text;
|
|
288
|
+
}
|
|
289
|
+
else if (ts.isMethodDeclaration(node) && ts.isIdentifier(node.name)) {
|
|
290
|
+
containingFunction = node.name.text;
|
|
291
|
+
}
|
|
292
|
+
else if (ts.isArrowFunction(node)) {
|
|
293
|
+
const parent = node.parent;
|
|
294
|
+
if (ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name)) {
|
|
295
|
+
containingFunction = parent.name.text;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
// Check for references
|
|
299
|
+
const ref = tryExtractReference(node, sourceFile, symbolName, filePath, lines, containingFunction);
|
|
300
|
+
if (ref) {
|
|
301
|
+
// Skip if this is the definition and we don't want to include it
|
|
302
|
+
if (!includeDefinition &&
|
|
303
|
+
definition &&
|
|
304
|
+
ref.path === definition.path &&
|
|
305
|
+
ref.line === definition.line) {
|
|
306
|
+
// Skip definition
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
references.push(ref);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
ts.forEachChild(node, visit);
|
|
313
|
+
// Reset containing function when leaving
|
|
314
|
+
if (ts.isFunctionDeclaration(node) ||
|
|
315
|
+
ts.isMethodDeclaration(node) ||
|
|
316
|
+
ts.isArrowFunction(node)) {
|
|
317
|
+
containingFunction = undefined;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
visit(sourceFile);
|
|
321
|
+
}
|
|
322
|
+
catch {
|
|
323
|
+
// Silently skip files that can't be parsed
|
|
324
|
+
}
|
|
325
|
+
return references;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Try to extract a reference from a node
|
|
329
|
+
*/
|
|
330
|
+
function tryExtractReference(node, sourceFile, symbolName, filePath, lines, containingFunction) {
|
|
331
|
+
// Check if this is an identifier matching our symbol
|
|
332
|
+
if (!ts.isIdentifier(node) || node.text !== symbolName) {
|
|
333
|
+
return undefined;
|
|
334
|
+
}
|
|
335
|
+
// Determine reference type based on context
|
|
336
|
+
const refType = determineReferenceType(node);
|
|
337
|
+
if (!refType) {
|
|
338
|
+
return undefined;
|
|
339
|
+
}
|
|
340
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
341
|
+
const lineIndex = line;
|
|
342
|
+
const context = lineIndex < lines.length ? lines[lineIndex].trim() : '';
|
|
343
|
+
return {
|
|
344
|
+
path: filePath,
|
|
345
|
+
line: line + 1,
|
|
346
|
+
column: character + 1,
|
|
347
|
+
context,
|
|
348
|
+
type: refType,
|
|
349
|
+
containingFunction,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Determine the type of reference based on AST context
|
|
354
|
+
*/
|
|
355
|
+
function determineReferenceType(node) {
|
|
356
|
+
const parent = node.parent;
|
|
357
|
+
// Import statement
|
|
358
|
+
if (ts.isImportSpecifier(parent) || ts.isImportClause(parent) || ts.isNamespaceImport(parent)) {
|
|
359
|
+
return 'import';
|
|
360
|
+
}
|
|
361
|
+
// Export statement
|
|
362
|
+
if (ts.isExportSpecifier(parent) || ts.isExportAssignment(parent)) {
|
|
363
|
+
return 'export';
|
|
364
|
+
}
|
|
365
|
+
// Type reference (in type annotations)
|
|
366
|
+
if (ts.isTypeReferenceNode(parent)) {
|
|
367
|
+
return 'type';
|
|
368
|
+
}
|
|
369
|
+
// Extends clause
|
|
370
|
+
if (ts.isExpressionWithTypeArguments(parent)) {
|
|
371
|
+
const grandparent = parent.parent;
|
|
372
|
+
if (ts.isHeritageClause(grandparent)) {
|
|
373
|
+
if (grandparent.token === ts.SyntaxKind.ExtendsKeyword) {
|
|
374
|
+
return 'extend';
|
|
375
|
+
}
|
|
376
|
+
// Use else for implements to avoid the "always true" comparison error
|
|
377
|
+
return 'implement';
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
// Call expression
|
|
381
|
+
if (ts.isCallExpression(parent) && parent.expression === node) {
|
|
382
|
+
return 'call';
|
|
383
|
+
}
|
|
384
|
+
// New expression
|
|
385
|
+
if (ts.isNewExpression(parent) && parent.expression === node) {
|
|
386
|
+
return 'call';
|
|
387
|
+
}
|
|
388
|
+
// Property access (reading)
|
|
389
|
+
if (ts.isPropertyAccessExpression(parent) && parent.name === node) {
|
|
390
|
+
// Check if this is a write (assignment target)
|
|
391
|
+
const grandparent = parent.parent;
|
|
392
|
+
if (ts.isBinaryExpression(grandparent) &&
|
|
393
|
+
grandparent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
394
|
+
grandparent.left === parent) {
|
|
395
|
+
return 'write';
|
|
396
|
+
}
|
|
397
|
+
return 'read';
|
|
398
|
+
}
|
|
399
|
+
// Variable declaration (definition, but also a "write")
|
|
400
|
+
if (ts.isVariableDeclaration(parent) && parent.name === node) {
|
|
401
|
+
return 'write';
|
|
402
|
+
}
|
|
403
|
+
// Assignment expression
|
|
404
|
+
if (ts.isBinaryExpression(parent) &&
|
|
405
|
+
parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
406
|
+
parent.left === node) {
|
|
407
|
+
return 'write';
|
|
408
|
+
}
|
|
409
|
+
// Parameter declaration
|
|
410
|
+
if (ts.isParameter(parent)) {
|
|
411
|
+
return 'write';
|
|
412
|
+
}
|
|
413
|
+
// Function/class/interface declaration (this is a definition)
|
|
414
|
+
if (ts.isFunctionDeclaration(parent) ||
|
|
415
|
+
ts.isClassDeclaration(parent) ||
|
|
416
|
+
ts.isInterfaceDeclaration(parent) ||
|
|
417
|
+
ts.isTypeAliasDeclaration(parent) ||
|
|
418
|
+
ts.isEnumDeclaration(parent) ||
|
|
419
|
+
ts.isMethodDeclaration(parent) ||
|
|
420
|
+
ts.isPropertyDeclaration(parent)) {
|
|
421
|
+
// This is a definition, we'll include it based on includeDefinition flag
|
|
422
|
+
return 'write';
|
|
423
|
+
}
|
|
424
|
+
// Default to read for other usages
|
|
425
|
+
return 'read';
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Group references by file
|
|
429
|
+
*/
|
|
430
|
+
function groupReferencesByFile(references) {
|
|
431
|
+
const byFile = new Map();
|
|
432
|
+
for (const ref of references) {
|
|
433
|
+
const existing = byFile.get(ref.path);
|
|
434
|
+
if (existing) {
|
|
435
|
+
existing.push(ref);
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
byFile.set(ref.path, [ref]);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return Array.from(byFile.entries()).map(([path, refs]) => ({
|
|
442
|
+
path,
|
|
443
|
+
references: refs,
|
|
444
|
+
}));
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Get TypeScript script kind from file extension
|
|
448
|
+
*/
|
|
449
|
+
function getScriptKind(filePath) {
|
|
450
|
+
const ext = filePath.toLowerCase().split('.').pop();
|
|
451
|
+
switch (ext) {
|
|
452
|
+
case 'ts':
|
|
453
|
+
return ts.ScriptKind.TS;
|
|
454
|
+
case 'tsx':
|
|
455
|
+
return ts.ScriptKind.TSX;
|
|
456
|
+
case 'js':
|
|
457
|
+
return ts.ScriptKind.JS;
|
|
458
|
+
case 'jsx':
|
|
459
|
+
return ts.ScriptKind.JSX;
|
|
460
|
+
case 'mts':
|
|
461
|
+
case 'cts':
|
|
462
|
+
return ts.ScriptKind.TS;
|
|
463
|
+
case 'mjs':
|
|
464
|
+
case 'cjs':
|
|
465
|
+
return ts.ScriptKind.JS;
|
|
466
|
+
default:
|
|
467
|
+
return ts.ScriptKind.TS;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Factory function to create a customized findReferences tool
|
|
472
|
+
*/
|
|
473
|
+
export function createFindReferencesTool(options) {
|
|
474
|
+
const { defaultLimit = 50, defaultGroupByFile = true, defaultIncludeDefinition = false, } = options ?? {};
|
|
475
|
+
return defineTool({
|
|
476
|
+
name: 'find_references',
|
|
477
|
+
description: TOOL_DESCRIPTION,
|
|
478
|
+
inputSchema: TOOL_INPUT_SCHEMA,
|
|
479
|
+
execute: async (input) => {
|
|
480
|
+
return executeFindReferences({
|
|
481
|
+
...input,
|
|
482
|
+
limit: input.limit ?? defaultLimit,
|
|
483
|
+
groupByFile: input.groupByFile ?? defaultGroupByFile,
|
|
484
|
+
includeDefinition: input.includeDefinition ?? defaultIncludeDefinition,
|
|
485
|
+
});
|
|
486
|
+
},
|
|
487
|
+
});
|
|
488
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
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 type { Tool } from '@compilr-dev/agents';
|
|
8
|
+
import type { FindSymbolInput } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* findSymbol tool - Find symbol definitions
|
|
11
|
+
*/
|
|
12
|
+
export declare const findSymbolTool: Tool<FindSymbolInput>;
|
|
13
|
+
/**
|
|
14
|
+
* Factory function to create a customized findSymbol tool
|
|
15
|
+
*/
|
|
16
|
+
export declare function createFindSymbolTool(options?: {
|
|
17
|
+
/** Default limit */
|
|
18
|
+
defaultLimit?: number;
|
|
19
|
+
/** Default includeNodeModules */
|
|
20
|
+
defaultIncludeNodeModules?: boolean;
|
|
21
|
+
}): Tool<FindSymbolInput>;
|