@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,457 @@
|
|
|
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 * 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 code patterns, anti-patterns, and code smells.
|
|
14
|
+
Searches for common issues like security vulnerabilities, performance problems, and maintainability concerns.
|
|
15
|
+
Supports built-in patterns or custom pattern definitions.`;
|
|
16
|
+
// Tool input schema
|
|
17
|
+
const TOOL_INPUT_SCHEMA = {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: {
|
|
20
|
+
path: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: 'Directory to analyze',
|
|
23
|
+
},
|
|
24
|
+
patterns: {
|
|
25
|
+
type: 'array',
|
|
26
|
+
description: 'Custom patterns to search for (uses built-in if not specified)',
|
|
27
|
+
items: {
|
|
28
|
+
type: 'object',
|
|
29
|
+
properties: {
|
|
30
|
+
name: { type: 'string' },
|
|
31
|
+
description: { type: 'string' },
|
|
32
|
+
type: { type: 'string' },
|
|
33
|
+
matcher: { type: 'string' },
|
|
34
|
+
severity: { type: 'string' },
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
categories: {
|
|
39
|
+
type: 'array',
|
|
40
|
+
description: 'Pattern categories to include (default: all)',
|
|
41
|
+
items: {
|
|
42
|
+
type: 'string',
|
|
43
|
+
enum: ['security', 'performance', 'maintainability', 'all'],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
maxFiles: {
|
|
47
|
+
type: 'number',
|
|
48
|
+
description: 'Maximum files to analyze (default: 100)',
|
|
49
|
+
default: 100,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
required: ['path'],
|
|
53
|
+
};
|
|
54
|
+
// Default exclusions
|
|
55
|
+
const DEFAULT_EXCLUDE = ['node_modules', 'dist', 'build', '.git', 'coverage'];
|
|
56
|
+
// Built-in patterns
|
|
57
|
+
const BUILTIN_PATTERNS = [
|
|
58
|
+
// Security patterns
|
|
59
|
+
{
|
|
60
|
+
name: 'eval-usage',
|
|
61
|
+
description: 'Use of eval() can lead to code injection vulnerabilities',
|
|
62
|
+
type: 'anti-pattern',
|
|
63
|
+
matcher: '\\beval\\s*\\(',
|
|
64
|
+
severity: 'error',
|
|
65
|
+
category: 'security',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'innerHTML-usage',
|
|
69
|
+
description: 'innerHTML can lead to XSS vulnerabilities',
|
|
70
|
+
type: 'anti-pattern',
|
|
71
|
+
matcher: '\\.innerHTML\\s*=',
|
|
72
|
+
severity: 'warning',
|
|
73
|
+
category: 'security',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'hardcoded-secret',
|
|
77
|
+
description: 'Potential hardcoded secret or API key',
|
|
78
|
+
type: 'anti-pattern',
|
|
79
|
+
matcher: '(password|secret|api_key|apikey|token)\\s*[=:]\\s*["\'][^"\']+["\']',
|
|
80
|
+
severity: 'error',
|
|
81
|
+
category: 'security',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: 'sql-injection-risk',
|
|
85
|
+
description: 'String concatenation in SQL query (potential injection)',
|
|
86
|
+
type: 'anti-pattern',
|
|
87
|
+
matcher: '(query|execute)\\s*\\(\\s*[`"\'].*\\$\\{',
|
|
88
|
+
severity: 'error',
|
|
89
|
+
category: 'security',
|
|
90
|
+
},
|
|
91
|
+
// Performance patterns
|
|
92
|
+
{
|
|
93
|
+
name: 'console-log',
|
|
94
|
+
description: 'console.log left in code (should be removed in production)',
|
|
95
|
+
type: 'smell',
|
|
96
|
+
matcher: '\\bconsole\\.(log|debug|info)\\s*\\(',
|
|
97
|
+
severity: 'info',
|
|
98
|
+
category: 'performance',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'sync-fs-operations',
|
|
102
|
+
description: 'Synchronous file system operations block the event loop',
|
|
103
|
+
type: 'anti-pattern',
|
|
104
|
+
matcher: '\\b(readFileSync|writeFileSync|existsSync|statSync|readdirSync)\\s*\\(',
|
|
105
|
+
severity: 'warning',
|
|
106
|
+
category: 'performance',
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'nested-loops',
|
|
110
|
+
description: 'Deeply nested loops may indicate O(n²) or worse complexity',
|
|
111
|
+
type: 'smell',
|
|
112
|
+
matcher: 'for\\s*\\([^)]+\\)\\s*\\{[^}]*for\\s*\\([^)]+\\)\\s*\\{[^}]*for\\s*\\(',
|
|
113
|
+
severity: 'warning',
|
|
114
|
+
category: 'performance',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'await-in-loop',
|
|
118
|
+
description: 'await inside loop may cause sequential execution instead of parallel',
|
|
119
|
+
type: 'smell',
|
|
120
|
+
matcher: '(for|while)\\s*\\([^)]*\\)\\s*\\{[^}]*await\\b',
|
|
121
|
+
severity: 'info',
|
|
122
|
+
category: 'performance',
|
|
123
|
+
},
|
|
124
|
+
// Maintainability patterns
|
|
125
|
+
{
|
|
126
|
+
name: 'magic-number',
|
|
127
|
+
description: 'Magic numbers should be named constants',
|
|
128
|
+
type: 'smell',
|
|
129
|
+
matcher: '(?<!\\.)\\b(?!(0|1|2|10|100|1000)\\b)\\d{2,}\\b(?!\\.)',
|
|
130
|
+
severity: 'info',
|
|
131
|
+
category: 'maintainability',
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: 'todo-fixme',
|
|
135
|
+
description: 'TODO or FIXME comment indicating incomplete work',
|
|
136
|
+
type: 'smell',
|
|
137
|
+
matcher: '(TODO|FIXME|HACK|XXX)\\s*:?',
|
|
138
|
+
severity: 'info',
|
|
139
|
+
category: 'maintainability',
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'any-type',
|
|
143
|
+
description: 'Use of "any" type defeats TypeScript type checking',
|
|
144
|
+
type: 'smell',
|
|
145
|
+
matcher: ':\\s*any\\b',
|
|
146
|
+
severity: 'warning',
|
|
147
|
+
category: 'maintainability',
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
name: 'ts-ignore',
|
|
151
|
+
description: '@ts-ignore suppresses TypeScript errors',
|
|
152
|
+
type: 'smell',
|
|
153
|
+
matcher: '@ts-ignore',
|
|
154
|
+
severity: 'warning',
|
|
155
|
+
category: 'maintainability',
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: 'eslint-disable',
|
|
159
|
+
description: 'eslint-disable suppresses linting rules',
|
|
160
|
+
type: 'smell',
|
|
161
|
+
matcher: 'eslint-disable(?!-next-line)',
|
|
162
|
+
severity: 'info',
|
|
163
|
+
category: 'maintainability',
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: 'long-function',
|
|
167
|
+
description: 'Function body exceeds recommended length',
|
|
168
|
+
type: 'smell',
|
|
169
|
+
matcher: 'AST:long-function',
|
|
170
|
+
severity: 'warning',
|
|
171
|
+
category: 'maintainability',
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: 'many-parameters',
|
|
175
|
+
description: 'Function has too many parameters (consider using options object)',
|
|
176
|
+
type: 'smell',
|
|
177
|
+
matcher: 'AST:many-parameters',
|
|
178
|
+
severity: 'info',
|
|
179
|
+
category: 'maintainability',
|
|
180
|
+
},
|
|
181
|
+
];
|
|
182
|
+
/**
|
|
183
|
+
* findPatterns tool
|
|
184
|
+
*/
|
|
185
|
+
export const findPatternsTool = defineTool({
|
|
186
|
+
name: 'find_patterns',
|
|
187
|
+
description: TOOL_DESCRIPTION,
|
|
188
|
+
inputSchema: TOOL_INPUT_SCHEMA,
|
|
189
|
+
execute: executeFindPatterns,
|
|
190
|
+
});
|
|
191
|
+
/**
|
|
192
|
+
* Execute the findPatterns tool
|
|
193
|
+
*/
|
|
194
|
+
async function executeFindPatterns(input) {
|
|
195
|
+
const { path: inputPath, patterns, categories = ['all'], maxFiles = 100 } = input;
|
|
196
|
+
try {
|
|
197
|
+
const resolvedPath = path.resolve(inputPath);
|
|
198
|
+
// Check if path exists
|
|
199
|
+
try {
|
|
200
|
+
await fs.access(resolvedPath);
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return createErrorResult(`Path not found: ${resolvedPath}`);
|
|
204
|
+
}
|
|
205
|
+
const stats = await fs.stat(resolvedPath);
|
|
206
|
+
if (!stats.isDirectory()) {
|
|
207
|
+
return createErrorResult('findPatterns requires a directory path');
|
|
208
|
+
}
|
|
209
|
+
// Determine which patterns to use
|
|
210
|
+
const patternsToUse = patterns ?? getBuiltinPatterns(categories);
|
|
211
|
+
// Collect files
|
|
212
|
+
const files = [];
|
|
213
|
+
await collectFiles(resolvedPath, files, maxFiles);
|
|
214
|
+
// Find matches
|
|
215
|
+
const allMatches = [];
|
|
216
|
+
const byPattern = {};
|
|
217
|
+
let errorCount = 0;
|
|
218
|
+
let warningCount = 0;
|
|
219
|
+
let infoCount = 0;
|
|
220
|
+
for (const file of files) {
|
|
221
|
+
const fileMatches = await analyzeFileForPatterns(file, patternsToUse);
|
|
222
|
+
allMatches.push(...fileMatches);
|
|
223
|
+
}
|
|
224
|
+
// Count by pattern and severity
|
|
225
|
+
for (const match of allMatches) {
|
|
226
|
+
byPattern[match.pattern] = (byPattern[match.pattern] ?? 0) + 1;
|
|
227
|
+
if (match.severity === 'error')
|
|
228
|
+
errorCount++;
|
|
229
|
+
else if (match.severity === 'warning')
|
|
230
|
+
warningCount++;
|
|
231
|
+
else
|
|
232
|
+
infoCount++;
|
|
233
|
+
}
|
|
234
|
+
// Sort by severity (errors first)
|
|
235
|
+
allMatches.sort((a, b) => {
|
|
236
|
+
const severityOrder = { error: 0, warning: 1, info: 2 };
|
|
237
|
+
return severityOrder[a.severity] - severityOrder[b.severity];
|
|
238
|
+
});
|
|
239
|
+
// Limit results
|
|
240
|
+
const limitedMatches = allMatches.slice(0, 100);
|
|
241
|
+
const result = {
|
|
242
|
+
path: resolvedPath,
|
|
243
|
+
matches: limitedMatches,
|
|
244
|
+
stats: {
|
|
245
|
+
filesAnalyzed: files.length,
|
|
246
|
+
totalMatches: allMatches.length,
|
|
247
|
+
byPattern,
|
|
248
|
+
bySeverity: {
|
|
249
|
+
error: errorCount,
|
|
250
|
+
warning: warningCount,
|
|
251
|
+
info: infoCount,
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
return createSuccessResult(result);
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
return createErrorResult(`Failed to find patterns: ${error instanceof Error ? error.message : String(error)}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Get built-in patterns filtered by category
|
|
263
|
+
*/
|
|
264
|
+
function getBuiltinPatterns(categories) {
|
|
265
|
+
if (categories.includes('all')) {
|
|
266
|
+
return BUILTIN_PATTERNS;
|
|
267
|
+
}
|
|
268
|
+
return BUILTIN_PATTERNS.filter((p) => categories.includes(p.category));
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Collect files to analyze
|
|
272
|
+
*/
|
|
273
|
+
async function collectFiles(dirPath, files, maxFiles, currentDepth = 0) {
|
|
274
|
+
if (currentDepth > 10 || files.length >= maxFiles)
|
|
275
|
+
return;
|
|
276
|
+
try {
|
|
277
|
+
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
278
|
+
for (const entry of entries) {
|
|
279
|
+
if (files.length >= maxFiles)
|
|
280
|
+
break;
|
|
281
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
282
|
+
if (entry.isDirectory()) {
|
|
283
|
+
if (DEFAULT_EXCLUDE.includes(entry.name))
|
|
284
|
+
continue;
|
|
285
|
+
await collectFiles(fullPath, files, maxFiles, currentDepth + 1);
|
|
286
|
+
}
|
|
287
|
+
else if (entry.isFile()) {
|
|
288
|
+
// Only include TypeScript/JavaScript files
|
|
289
|
+
if (/\.(ts|tsx|js|jsx)$/.test(entry.name) && !entry.name.endsWith('.d.ts')) {
|
|
290
|
+
files.push(fullPath);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
// Ignore permission errors
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Analyze a file for patterns
|
|
301
|
+
*/
|
|
302
|
+
async function analyzeFileForPatterns(filePath, patterns) {
|
|
303
|
+
try {
|
|
304
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
305
|
+
const lines = content.split('\n');
|
|
306
|
+
const matches = [];
|
|
307
|
+
// Separate regex patterns from AST patterns
|
|
308
|
+
const regexPatterns = patterns.filter((p) => !p.matcher.startsWith('AST:'));
|
|
309
|
+
const astPatterns = patterns.filter((p) => p.matcher.startsWith('AST:'));
|
|
310
|
+
// Check regex patterns line by line
|
|
311
|
+
for (let i = 0; i < lines.length; i++) {
|
|
312
|
+
const line = lines[i];
|
|
313
|
+
for (const pattern of regexPatterns) {
|
|
314
|
+
try {
|
|
315
|
+
const regex = new RegExp(pattern.matcher, 'gi');
|
|
316
|
+
if (regex.test(line)) {
|
|
317
|
+
matches.push({
|
|
318
|
+
pattern: pattern.name,
|
|
319
|
+
description: pattern.description,
|
|
320
|
+
severity: pattern.severity,
|
|
321
|
+
path: filePath,
|
|
322
|
+
line: i + 1,
|
|
323
|
+
snippet: line.trim().slice(0, 100),
|
|
324
|
+
suggestion: getSuggestion(pattern.name),
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
catch {
|
|
329
|
+
// Invalid regex, skip
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// Check AST patterns
|
|
334
|
+
if (astPatterns.length > 0) {
|
|
335
|
+
const astMatches = analyzeFileForAstPatterns(filePath, content, astPatterns);
|
|
336
|
+
matches.push(...astMatches);
|
|
337
|
+
}
|
|
338
|
+
return matches;
|
|
339
|
+
}
|
|
340
|
+
catch {
|
|
341
|
+
return [];
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Analyze file for AST-based patterns
|
|
346
|
+
*/
|
|
347
|
+
function analyzeFileForAstPatterns(filePath, content, patterns) {
|
|
348
|
+
const detection = detectLanguage(filePath);
|
|
349
|
+
if (!detection.language || !isLanguageSupported(detection.language)) {
|
|
350
|
+
return [];
|
|
351
|
+
}
|
|
352
|
+
const matches = [];
|
|
353
|
+
try {
|
|
354
|
+
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true, filePath.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
|
|
355
|
+
const checkLongFunction = patterns.some((p) => p.matcher === 'AST:long-function');
|
|
356
|
+
const checkManyParameters = patterns.some((p) => p.matcher === 'AST:many-parameters');
|
|
357
|
+
const visit = (node) => {
|
|
358
|
+
// Check long functions (>50 lines)
|
|
359
|
+
if (checkLongFunction) {
|
|
360
|
+
if (ts.isFunctionDeclaration(node) ||
|
|
361
|
+
ts.isMethodDeclaration(node) ||
|
|
362
|
+
ts.isArrowFunction(node) ||
|
|
363
|
+
ts.isFunctionExpression(node)) {
|
|
364
|
+
const { line: startLine } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
365
|
+
const { line: endLine } = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
|
|
366
|
+
const lines = endLine - startLine;
|
|
367
|
+
if (lines > 50) {
|
|
368
|
+
let name = '<anonymous>';
|
|
369
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
370
|
+
name = node.name.text;
|
|
371
|
+
}
|
|
372
|
+
else if (ts.isMethodDeclaration(node) && ts.isIdentifier(node.name)) {
|
|
373
|
+
name = node.name.text;
|
|
374
|
+
}
|
|
375
|
+
matches.push({
|
|
376
|
+
pattern: 'long-function',
|
|
377
|
+
description: `Function "${name}" has ${String(lines)} lines (recommended: <50)`,
|
|
378
|
+
severity: 'warning',
|
|
379
|
+
path: filePath,
|
|
380
|
+
line: startLine + 1,
|
|
381
|
+
snippet: `function ${name}(...) { /* ${String(lines)} lines */ }`,
|
|
382
|
+
suggestion: 'Consider breaking this function into smaller functions',
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
// Check many parameters (>5)
|
|
388
|
+
if (checkManyParameters) {
|
|
389
|
+
if (ts.isFunctionDeclaration(node) ||
|
|
390
|
+
ts.isMethodDeclaration(node) ||
|
|
391
|
+
ts.isArrowFunction(node) ||
|
|
392
|
+
ts.isFunctionExpression(node)) {
|
|
393
|
+
const paramCount = node.parameters.length;
|
|
394
|
+
if (paramCount > 5) {
|
|
395
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
396
|
+
let name = '<anonymous>';
|
|
397
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
398
|
+
name = node.name.text;
|
|
399
|
+
}
|
|
400
|
+
else if (ts.isMethodDeclaration(node) && ts.isIdentifier(node.name)) {
|
|
401
|
+
name = node.name.text;
|
|
402
|
+
}
|
|
403
|
+
matches.push({
|
|
404
|
+
pattern: 'many-parameters',
|
|
405
|
+
description: `Function "${name}" has ${String(paramCount)} parameters (recommended: <5)`,
|
|
406
|
+
severity: 'info',
|
|
407
|
+
path: filePath,
|
|
408
|
+
line: line + 1,
|
|
409
|
+
snippet: `function ${name}(${String(paramCount)} params...)`,
|
|
410
|
+
suggestion: 'Consider using an options object instead of many parameters',
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
ts.forEachChild(node, visit);
|
|
416
|
+
};
|
|
417
|
+
visit(sourceFile);
|
|
418
|
+
}
|
|
419
|
+
catch {
|
|
420
|
+
// Ignore parse errors
|
|
421
|
+
}
|
|
422
|
+
return matches;
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Get suggestion for a pattern
|
|
426
|
+
*/
|
|
427
|
+
function getSuggestion(patternName) {
|
|
428
|
+
const suggestions = {
|
|
429
|
+
'eval-usage': 'Use safer alternatives like JSON.parse() or Function constructor',
|
|
430
|
+
'innerHTML-usage': 'Use textContent or sanitize HTML before insertion',
|
|
431
|
+
'hardcoded-secret': 'Use environment variables or a secrets manager',
|
|
432
|
+
'sql-injection-risk': 'Use parameterized queries or an ORM',
|
|
433
|
+
'console-log': 'Remove or replace with proper logging',
|
|
434
|
+
'sync-fs-operations': 'Use async versions like readFile, writeFile',
|
|
435
|
+
'any-type': 'Use a specific type or unknown if type is truly unknown',
|
|
436
|
+
'ts-ignore': 'Fix the underlying type issue instead of suppressing',
|
|
437
|
+
};
|
|
438
|
+
return suggestions[patternName];
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Create customizable findPatterns tool
|
|
442
|
+
*/
|
|
443
|
+
export function createFindPatternsTool(options) {
|
|
444
|
+
return defineTool({
|
|
445
|
+
name: options?.name ?? 'find_patterns',
|
|
446
|
+
description: options?.description ?? TOOL_DESCRIPTION,
|
|
447
|
+
inputSchema: TOOL_INPUT_SCHEMA,
|
|
448
|
+
execute: async (input) => {
|
|
449
|
+
const modifiedInput = {
|
|
450
|
+
...input,
|
|
451
|
+
maxFiles: input.maxFiles ?? options?.defaultMaxFiles,
|
|
452
|
+
patterns: input.patterns ?? [...BUILTIN_PATTERNS, ...(options?.additionalPatterns ?? [])],
|
|
453
|
+
};
|
|
454
|
+
return executeFindPatterns(modifiedInput);
|
|
455
|
+
},
|
|
456
|
+
});
|
|
457
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
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 type { Tool } from '@compilr-dev/agents';
|
|
8
|
+
import type { FindReferencesInput } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* findReferences tool - Find all usages of a symbol
|
|
11
|
+
*/
|
|
12
|
+
export declare const findReferencesTool: Tool<FindReferencesInput>;
|
|
13
|
+
/**
|
|
14
|
+
* Factory function to create a customized findReferences tool
|
|
15
|
+
*/
|
|
16
|
+
export declare function createFindReferencesTool(options?: {
|
|
17
|
+
/** Default limit */
|
|
18
|
+
defaultLimit?: number;
|
|
19
|
+
/** Default groupByFile */
|
|
20
|
+
defaultGroupByFile?: boolean;
|
|
21
|
+
/** Default includeDefinition */
|
|
22
|
+
defaultIncludeDefinition?: boolean;
|
|
23
|
+
}): Tool<FindReferencesInput>;
|