@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,436 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* findImplementations Tool
|
|
3
|
+
*
|
|
4
|
+
* Find classes that implement an interface or extend an abstract class.
|
|
5
|
+
* Useful for understanding interface usage and finding concrete implementations.
|
|
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 = `Find classes that implement an interface or extend an abstract class.
|
|
14
|
+
Returns information about each implementation including which methods are implemented.
|
|
15
|
+
Useful for understanding how interfaces are used across the codebase.`;
|
|
16
|
+
// Tool input schema
|
|
17
|
+
const TOOL_INPUT_SCHEMA = {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: {
|
|
20
|
+
name: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: 'Interface or abstract class name to find implementations of',
|
|
23
|
+
},
|
|
24
|
+
scope: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
description: 'Directory or file to search in (defaults to current directory)',
|
|
27
|
+
},
|
|
28
|
+
includeAbstract: {
|
|
29
|
+
type: 'boolean',
|
|
30
|
+
description: 'Include abstract classes that partially implement (default: false)',
|
|
31
|
+
default: false,
|
|
32
|
+
},
|
|
33
|
+
maxFiles: {
|
|
34
|
+
type: 'number',
|
|
35
|
+
description: 'Maximum files to search (default: 100)',
|
|
36
|
+
default: 100,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
required: ['name'],
|
|
40
|
+
};
|
|
41
|
+
// Default file patterns
|
|
42
|
+
const DEFAULT_INCLUDE = ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'];
|
|
43
|
+
const DEFAULT_EXCLUDE = ['**/node_modules/**', '**/dist/**', '**/build/**', '**/.git/**'];
|
|
44
|
+
/**
|
|
45
|
+
* findImplementations tool - Find implementations of interfaces/abstract classes
|
|
46
|
+
*/
|
|
47
|
+
export const findImplementationsTool = defineTool({
|
|
48
|
+
name: 'find_implementations',
|
|
49
|
+
description: TOOL_DESCRIPTION,
|
|
50
|
+
inputSchema: TOOL_INPUT_SCHEMA,
|
|
51
|
+
execute: executeFindImplementations,
|
|
52
|
+
});
|
|
53
|
+
/**
|
|
54
|
+
* Execute the findImplementations tool
|
|
55
|
+
*/
|
|
56
|
+
async function executeFindImplementations(input) {
|
|
57
|
+
const { name, scope = '.', includeAbstract = false, maxFiles = 100 } = input;
|
|
58
|
+
const startTime = Date.now();
|
|
59
|
+
// Validate input
|
|
60
|
+
if (!name || name.trim().length === 0) {
|
|
61
|
+
return createErrorResult('Interface or class name is required');
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
// Resolve scope path
|
|
65
|
+
const scopePath = path.resolve(scope);
|
|
66
|
+
// Check if scope exists
|
|
67
|
+
try {
|
|
68
|
+
await fs.access(scopePath);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return createErrorResult(`Scope path not found: ${scopePath}`);
|
|
72
|
+
}
|
|
73
|
+
const stats = await fs.stat(scopePath);
|
|
74
|
+
// Collect files to search
|
|
75
|
+
const files = stats.isDirectory()
|
|
76
|
+
? await collectFiles(scopePath, DEFAULT_INCLUDE, DEFAULT_EXCLUDE, 10, maxFiles)
|
|
77
|
+
: [scopePath];
|
|
78
|
+
// Find the target interface/abstract class definition
|
|
79
|
+
let targetLocation;
|
|
80
|
+
let targetMethods = [];
|
|
81
|
+
let isInterface = false;
|
|
82
|
+
let isAbstractClass = false;
|
|
83
|
+
// First pass: find the target definition
|
|
84
|
+
for (const file of files) {
|
|
85
|
+
const result = await findTargetDefinition(file, name);
|
|
86
|
+
if (result) {
|
|
87
|
+
targetLocation = { path: file, line: result.line };
|
|
88
|
+
targetMethods = result.methods;
|
|
89
|
+
isInterface = result.isInterface;
|
|
90
|
+
isAbstractClass = result.isAbstractClass;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Second pass: find implementations
|
|
95
|
+
const implementations = [];
|
|
96
|
+
for (const file of files) {
|
|
97
|
+
const fileImplementations = await findImplementationsInFile(file, name, targetMethods, isInterface, isAbstractClass, includeAbstract);
|
|
98
|
+
implementations.push(...fileImplementations);
|
|
99
|
+
}
|
|
100
|
+
const timeMs = Date.now() - startTime;
|
|
101
|
+
const result = {
|
|
102
|
+
target: name,
|
|
103
|
+
targetLocation,
|
|
104
|
+
implementations,
|
|
105
|
+
stats: {
|
|
106
|
+
filesSearched: files.length,
|
|
107
|
+
implementationsFound: implementations.length,
|
|
108
|
+
timeMs,
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
return createSuccessResult(result);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
return createErrorResult(`Failed to find implementations: ${error instanceof Error ? error.message : String(error)}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Find the target interface or abstract class definition
|
|
119
|
+
*/
|
|
120
|
+
async function findTargetDefinition(filePath, targetName) {
|
|
121
|
+
try {
|
|
122
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
123
|
+
const detection = detectLanguage(filePath);
|
|
124
|
+
if (!detection.language) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
if (!isLanguageSupported(detection.language)) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true, filePath.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
|
|
131
|
+
let result = null;
|
|
132
|
+
const visit = (node) => {
|
|
133
|
+
// Check for interface declaration
|
|
134
|
+
if (ts.isInterfaceDeclaration(node) && node.name.text === targetName) {
|
|
135
|
+
const methods = extractInterfaceMethods(node);
|
|
136
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
137
|
+
result = {
|
|
138
|
+
line: line + 1,
|
|
139
|
+
methods,
|
|
140
|
+
isInterface: true,
|
|
141
|
+
isAbstractClass: false,
|
|
142
|
+
};
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// Check for abstract class declaration
|
|
146
|
+
if (ts.isClassDeclaration(node) && node.name?.text === targetName) {
|
|
147
|
+
const isAbstract = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.AbstractKeyword) ?? false;
|
|
148
|
+
if (isAbstract) {
|
|
149
|
+
const methods = extractClassMethods(node, true);
|
|
150
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
151
|
+
result = {
|
|
152
|
+
line: line + 1,
|
|
153
|
+
methods,
|
|
154
|
+
isInterface: false,
|
|
155
|
+
isAbstractClass: true,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
ts.forEachChild(node, visit);
|
|
161
|
+
};
|
|
162
|
+
visit(sourceFile);
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Extract method names from an interface
|
|
171
|
+
*/
|
|
172
|
+
function extractInterfaceMethods(node) {
|
|
173
|
+
const methods = [];
|
|
174
|
+
for (const member of node.members) {
|
|
175
|
+
if (ts.isMethodSignature(member)) {
|
|
176
|
+
const name = member.name.getText();
|
|
177
|
+
methods.push(name);
|
|
178
|
+
}
|
|
179
|
+
if (ts.isPropertySignature(member)) {
|
|
180
|
+
// Check if it's a function type property
|
|
181
|
+
if (member.type && ts.isFunctionTypeNode(member.type)) {
|
|
182
|
+
const name = member.name.getText();
|
|
183
|
+
methods.push(name);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return methods;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Extract method names from a class
|
|
191
|
+
*/
|
|
192
|
+
function extractClassMethods(node, abstractOnly) {
|
|
193
|
+
const methods = [];
|
|
194
|
+
for (const member of node.members) {
|
|
195
|
+
if (ts.isMethodDeclaration(member)) {
|
|
196
|
+
const isAbstract = member.modifiers?.some((m) => m.kind === ts.SyntaxKind.AbstractKeyword) ?? false;
|
|
197
|
+
if (!abstractOnly || isAbstract) {
|
|
198
|
+
const name = member.name.getText();
|
|
199
|
+
methods.push(name);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return methods;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Find implementations in a single file
|
|
207
|
+
*/
|
|
208
|
+
async function findImplementationsInFile(filePath, targetName, targetMethods, isInterface, isAbstractClass, includeAbstract) {
|
|
209
|
+
try {
|
|
210
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
211
|
+
const detection = detectLanguage(filePath);
|
|
212
|
+
if (!detection.language) {
|
|
213
|
+
return [];
|
|
214
|
+
}
|
|
215
|
+
if (!isLanguageSupported(detection.language)) {
|
|
216
|
+
return [];
|
|
217
|
+
}
|
|
218
|
+
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true, filePath.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
|
|
219
|
+
const implementations = [];
|
|
220
|
+
const visit = (node) => {
|
|
221
|
+
// Check for class declaration
|
|
222
|
+
if (ts.isClassDeclaration(node) && node.name) {
|
|
223
|
+
const implementation = checkClassImplementation(node, sourceFile, filePath, targetName, targetMethods, isInterface, isAbstractClass, includeAbstract);
|
|
224
|
+
if (implementation) {
|
|
225
|
+
implementations.push(implementation);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// Check for type alias that implements the interface (for structural typing)
|
|
229
|
+
if (ts.isTypeAliasDeclaration(node) && isInterface) {
|
|
230
|
+
const typeImpl = checkTypeImplementation(node, sourceFile, filePath, targetName);
|
|
231
|
+
if (typeImpl) {
|
|
232
|
+
implementations.push(typeImpl);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// Check for object literal with type annotation
|
|
236
|
+
if (ts.isVariableStatement(node) && isInterface) {
|
|
237
|
+
for (const decl of node.declarationList.declarations) {
|
|
238
|
+
if (decl.type && ts.isTypeLiteralNode(decl.type)) {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
if (decl.type && ts.isTypeReferenceNode(decl.type)) {
|
|
242
|
+
const typeName = decl.type.typeName.getText();
|
|
243
|
+
if (typeName === targetName && ts.isIdentifier(decl.name)) {
|
|
244
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
245
|
+
const isExported = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
246
|
+
implementations.push({
|
|
247
|
+
name: decl.name.text,
|
|
248
|
+
path: filePath,
|
|
249
|
+
line: line + 1,
|
|
250
|
+
column: character + 1,
|
|
251
|
+
kind: 'object',
|
|
252
|
+
exported: isExported,
|
|
253
|
+
implementedMethods: targetMethods,
|
|
254
|
+
missingMethods: [],
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
ts.forEachChild(node, visit);
|
|
261
|
+
};
|
|
262
|
+
visit(sourceFile);
|
|
263
|
+
return implementations;
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
return [];
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Check if a class implements the target interface/abstract class
|
|
271
|
+
*/
|
|
272
|
+
function checkClassImplementation(node, sourceFile, filePath, targetName, targetMethods, isInterface, isAbstractClass, includeAbstract) {
|
|
273
|
+
if (!node.name)
|
|
274
|
+
return null;
|
|
275
|
+
const className = node.name.text;
|
|
276
|
+
// Skip the target class itself
|
|
277
|
+
if (className === targetName)
|
|
278
|
+
return null;
|
|
279
|
+
// Check if it's abstract
|
|
280
|
+
const classIsAbstract = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.AbstractKeyword) ?? false;
|
|
281
|
+
// Skip abstract classes if not including them
|
|
282
|
+
if (classIsAbstract && !includeAbstract)
|
|
283
|
+
return null;
|
|
284
|
+
// Check heritage clauses (implements and extends)
|
|
285
|
+
let implementsTarget = false;
|
|
286
|
+
let extendsTarget = false;
|
|
287
|
+
if (node.heritageClauses) {
|
|
288
|
+
for (const clause of node.heritageClauses) {
|
|
289
|
+
if (clause.token === ts.SyntaxKind.ImplementsKeyword) {
|
|
290
|
+
for (const type of clause.types) {
|
|
291
|
+
const typeName = type.expression.getText();
|
|
292
|
+
if (typeName === targetName) {
|
|
293
|
+
implementsTarget = true;
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (clause.token === ts.SyntaxKind.ExtendsKeyword) {
|
|
299
|
+
for (const type of clause.types) {
|
|
300
|
+
const typeName = type.expression.getText();
|
|
301
|
+
if (typeName === targetName) {
|
|
302
|
+
extendsTarget = true;
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// Must either implement or extend the target
|
|
310
|
+
if (!implementsTarget && !extendsTarget)
|
|
311
|
+
return null;
|
|
312
|
+
// Get implemented methods
|
|
313
|
+
const implementedMethods = [];
|
|
314
|
+
for (const member of node.members) {
|
|
315
|
+
if (ts.isMethodDeclaration(member)) {
|
|
316
|
+
const methodName = member.name.getText();
|
|
317
|
+
if (targetMethods.includes(methodName)) {
|
|
318
|
+
implementedMethods.push(methodName);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// Also check getters/setters
|
|
322
|
+
if (ts.isGetAccessor(member) || ts.isSetAccessor(member)) {
|
|
323
|
+
const methodName = member.name.getText();
|
|
324
|
+
if (targetMethods.includes(methodName)) {
|
|
325
|
+
implementedMethods.push(methodName);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
// Calculate missing methods
|
|
330
|
+
const missingMethods = targetMethods.filter((m) => !implementedMethods.includes(m));
|
|
331
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
332
|
+
const isExported = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
333
|
+
return {
|
|
334
|
+
name: className,
|
|
335
|
+
path: filePath,
|
|
336
|
+
line: line + 1,
|
|
337
|
+
column: character + 1,
|
|
338
|
+
kind: 'class',
|
|
339
|
+
isAbstract: classIsAbstract,
|
|
340
|
+
exported: isExported,
|
|
341
|
+
implementedMethods,
|
|
342
|
+
missingMethods,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Check if a type alias structurally implements an interface
|
|
347
|
+
*/
|
|
348
|
+
function checkTypeImplementation(node, sourceFile, filePath, targetName) {
|
|
349
|
+
// Check if type extends/intersects with the target
|
|
350
|
+
const typeText = node.type.getText();
|
|
351
|
+
// Simple check: does it reference the target type?
|
|
352
|
+
if (!typeText.includes(targetName))
|
|
353
|
+
return null;
|
|
354
|
+
// Check for intersection or extension patterns
|
|
355
|
+
const isIntersection = ts.isIntersectionTypeNode(node.type);
|
|
356
|
+
const extendsTarget = typeText === targetName ||
|
|
357
|
+
(isIntersection && node.type.types.some((t) => t.getText() === targetName));
|
|
358
|
+
if (!extendsTarget)
|
|
359
|
+
return null;
|
|
360
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
361
|
+
const isExported = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
362
|
+
return {
|
|
363
|
+
name: node.name.text,
|
|
364
|
+
path: filePath,
|
|
365
|
+
line: line + 1,
|
|
366
|
+
column: character + 1,
|
|
367
|
+
kind: 'type',
|
|
368
|
+
exported: isExported,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Collect files matching patterns
|
|
373
|
+
*/
|
|
374
|
+
async function collectFiles(dirPath, include, exclude, maxDepth, maxFiles, currentDepth = 0) {
|
|
375
|
+
if (currentDepth > maxDepth)
|
|
376
|
+
return [];
|
|
377
|
+
const files = [];
|
|
378
|
+
try {
|
|
379
|
+
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
380
|
+
for (const entry of entries) {
|
|
381
|
+
if (files.length >= maxFiles)
|
|
382
|
+
break;
|
|
383
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
384
|
+
const relativePath = fullPath;
|
|
385
|
+
// Check exclusions
|
|
386
|
+
const isExcluded = exclude.some((pattern) => {
|
|
387
|
+
if (pattern.includes('**')) {
|
|
388
|
+
const simplePattern = pattern.replace(/\*\*/g, '');
|
|
389
|
+
return relativePath.includes(simplePattern.replace(/\*/g, ''));
|
|
390
|
+
}
|
|
391
|
+
return entry.name === pattern || relativePath.includes(pattern);
|
|
392
|
+
});
|
|
393
|
+
if (isExcluded)
|
|
394
|
+
continue;
|
|
395
|
+
if (entry.isDirectory()) {
|
|
396
|
+
const subFiles = await collectFiles(fullPath, include, exclude, maxDepth, maxFiles - files.length, currentDepth + 1);
|
|
397
|
+
files.push(...subFiles);
|
|
398
|
+
}
|
|
399
|
+
else if (entry.isFile()) {
|
|
400
|
+
// Check if file matches include patterns
|
|
401
|
+
const isIncluded = include.some((pattern) => {
|
|
402
|
+
if (pattern.includes('*')) {
|
|
403
|
+
const ext = pattern.replace('**/', '').replace('*', '');
|
|
404
|
+
return entry.name.endsWith(ext);
|
|
405
|
+
}
|
|
406
|
+
return entry.name === pattern;
|
|
407
|
+
});
|
|
408
|
+
if (isIncluded) {
|
|
409
|
+
files.push(fullPath);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
catch {
|
|
415
|
+
// Ignore permission errors
|
|
416
|
+
}
|
|
417
|
+
return files;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Create customizable findImplementations tool
|
|
421
|
+
*/
|
|
422
|
+
export function createFindImplementationsTool(options) {
|
|
423
|
+
return defineTool({
|
|
424
|
+
name: options?.name ?? 'find_implementations',
|
|
425
|
+
description: options?.description ?? TOOL_DESCRIPTION,
|
|
426
|
+
inputSchema: TOOL_INPUT_SCHEMA,
|
|
427
|
+
execute: async (input) => {
|
|
428
|
+
const modifiedInput = {
|
|
429
|
+
...input,
|
|
430
|
+
scope: input.scope ?? options?.defaultScope,
|
|
431
|
+
maxFiles: input.maxFiles ?? options?.defaultMaxFiles,
|
|
432
|
+
};
|
|
433
|
+
return executeFindImplementations(modifiedInput);
|
|
434
|
+
},
|
|
435
|
+
});
|
|
436
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* findPatterns Tool
|
|
3
|
+
*
|
|
4
|
+
* Find code patterns, anti-patterns, and code smells in the codebase.
|
|
5
|
+
* Uses regex and AST-based pattern matching.
|
|
6
|
+
*/
|
|
7
|
+
import type { Tool } from '@compilr-dev/agents';
|
|
8
|
+
import type { FindPatternsInput, CodePattern } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* findPatterns tool
|
|
11
|
+
*/
|
|
12
|
+
export declare const findPatternsTool: Tool<FindPatternsInput>;
|
|
13
|
+
/**
|
|
14
|
+
* Create customizable findPatterns tool
|
|
15
|
+
*/
|
|
16
|
+
export declare function createFindPatternsTool(options?: {
|
|
17
|
+
name?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
defaultMaxFiles?: number;
|
|
20
|
+
additionalPatterns?: CodePattern[];
|
|
21
|
+
}): Tool<FindPatternsInput>;
|