@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,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type Analysis Skill
|
|
3
|
+
*
|
|
4
|
+
* Analyze type hierarchies, interfaces, and their implementations.
|
|
5
|
+
*/
|
|
6
|
+
import { defineSkill } from '@compilr-dev/agents';
|
|
7
|
+
/**
|
|
8
|
+
* Type analysis skill - Analyze type hierarchies and implementations
|
|
9
|
+
*/
|
|
10
|
+
export const typeAnalysisSkill = defineSkill({
|
|
11
|
+
name: 'type-analysis',
|
|
12
|
+
description: 'Analyze type hierarchies, interfaces, and their implementations',
|
|
13
|
+
prompt: `You are in TYPE ANALYSIS mode. Analyze type relationships in the codebase.
|
|
14
|
+
|
|
15
|
+
## TOOLS TO USE
|
|
16
|
+
|
|
17
|
+
1. **get_type_hierarchy**: Analyze inheritance relationships
|
|
18
|
+
- Find what a class/interface extends
|
|
19
|
+
- Find what extends/implements a class/interface
|
|
20
|
+
- Trace the full inheritance chain
|
|
21
|
+
- Identify abstract vs concrete types
|
|
22
|
+
|
|
23
|
+
2. **find_implementations**: Find concrete implementations
|
|
24
|
+
- Find all classes implementing an interface
|
|
25
|
+
- Find all classes extending an abstract class
|
|
26
|
+
- See which methods are implemented
|
|
27
|
+
- Identify partial implementations
|
|
28
|
+
|
|
29
|
+
## ANALYSIS WORKFLOW
|
|
30
|
+
|
|
31
|
+
### For Interfaces
|
|
32
|
+
\`\`\`
|
|
33
|
+
// Step 1: Get the interface hierarchy
|
|
34
|
+
get_type_hierarchy({
|
|
35
|
+
path: "/path/to/file.ts",
|
|
36
|
+
typeName: "IMyInterface",
|
|
37
|
+
direction: "descendants" // Find implementors
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
// Step 2: Find all implementations
|
|
41
|
+
find_implementations({
|
|
42
|
+
name: "IMyInterface",
|
|
43
|
+
scope: "/path/to/project",
|
|
44
|
+
includeAbstract: false // Only concrete implementations
|
|
45
|
+
})
|
|
46
|
+
\`\`\`
|
|
47
|
+
|
|
48
|
+
### For Abstract Classes
|
|
49
|
+
\`\`\`
|
|
50
|
+
// Step 1: Get class hierarchy
|
|
51
|
+
get_type_hierarchy({
|
|
52
|
+
path: "/path/to/file.ts",
|
|
53
|
+
typeName: "AbstractBase",
|
|
54
|
+
direction: "both" // See parents and children
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
// Step 2: Find concrete implementations
|
|
58
|
+
find_implementations({
|
|
59
|
+
name: "AbstractBase",
|
|
60
|
+
scope: "/path/to/project",
|
|
61
|
+
includeAbstract: true // Include abstract subclasses too
|
|
62
|
+
})
|
|
63
|
+
\`\`\`
|
|
64
|
+
|
|
65
|
+
### For Concrete Classes
|
|
66
|
+
\`\`\`
|
|
67
|
+
// Understand what it inherits from
|
|
68
|
+
get_type_hierarchy({
|
|
69
|
+
path: "/path/to/file.ts",
|
|
70
|
+
typeName: "MyClass",
|
|
71
|
+
direction: "ancestors" // See parent chain
|
|
72
|
+
})
|
|
73
|
+
\`\`\`
|
|
74
|
+
|
|
75
|
+
## OUTPUT FORMAT
|
|
76
|
+
|
|
77
|
+
### Type Overview
|
|
78
|
+
| Property | Value |
|
|
79
|
+
|----------|-------|
|
|
80
|
+
| Name | \`TypeName\` |
|
|
81
|
+
| Kind | interface / abstract class / class |
|
|
82
|
+
| Defined in | path/to/file.ts:42 |
|
|
83
|
+
| Exported | Yes / No |
|
|
84
|
+
| Generic | Yes (T, K) / No |
|
|
85
|
+
|
|
86
|
+
### Inheritance Chain (ancestors)
|
|
87
|
+
\`\`\`
|
|
88
|
+
TypeName
|
|
89
|
+
└── extends ParentClass
|
|
90
|
+
└── extends GrandparentClass
|
|
91
|
+
└── implements IBaseInterface
|
|
92
|
+
\`\`\`
|
|
93
|
+
|
|
94
|
+
### Implementations (descendants)
|
|
95
|
+
| Class | File | Status | Missing Methods |
|
|
96
|
+
|-------|------|--------|-----------------|
|
|
97
|
+
| ConcreteA | src/a.ts:10 | ✅ Complete | - |
|
|
98
|
+
| ConcreteB | src/b.ts:25 | ⚠️ Partial | method1, method2 |
|
|
99
|
+
| AbstractC | src/c.ts:5 | 🔷 Abstract | method3 |
|
|
100
|
+
|
|
101
|
+
### Interface Contract
|
|
102
|
+
| Method | Implementations | Notes |
|
|
103
|
+
|--------|-----------------|-------|
|
|
104
|
+
| \`doSomething()\` | 5/5 | All implement |
|
|
105
|
+
| \`optionalMethod?()\` | 2/5 | Optional, partially implemented |
|
|
106
|
+
|
|
107
|
+
### Type Relationships Diagram
|
|
108
|
+
\`\`\`
|
|
109
|
+
ISerializable
|
|
110
|
+
│
|
|
111
|
+
┌────────────┼────────────┐
|
|
112
|
+
│ │ │
|
|
113
|
+
IEntity IDocument IMessage
|
|
114
|
+
│ │
|
|
115
|
+
┌────┴────┐ │
|
|
116
|
+
│ │ │
|
|
117
|
+
User Product Report
|
|
118
|
+
\`\`\`
|
|
119
|
+
|
|
120
|
+
### Analysis Summary
|
|
121
|
+
- **Total implementations**: N classes implement this interface
|
|
122
|
+
- **Complete implementations**: X (Y%)
|
|
123
|
+
- **Partial implementations**: Z (list missing methods)
|
|
124
|
+
- **Abstract subclasses**: W
|
|
125
|
+
|
|
126
|
+
### Recommendations
|
|
127
|
+
1. Classes with missing methods that need attention
|
|
128
|
+
2. Potential interface segregation opportunities
|
|
129
|
+
3. Deep inheritance chains that could be simplified
|
|
130
|
+
|
|
131
|
+
## COMMON QUESTIONS THIS SKILL ANSWERS
|
|
132
|
+
|
|
133
|
+
- "What implements IRepository?" → find_implementations
|
|
134
|
+
- "What does UserService extend?" → get_type_hierarchy (ancestors)
|
|
135
|
+
- "What are all the event handlers?" → find_implementations for IEventHandler
|
|
136
|
+
- "Show me the class hierarchy" → get_type_hierarchy (both)
|
|
137
|
+
- "Which classes are missing method X?" → find_implementations (check missingMethods)
|
|
138
|
+
|
|
139
|
+
## RULES
|
|
140
|
+
|
|
141
|
+
- Always identify the type kind first (interface, abstract, concrete)
|
|
142
|
+
- For interfaces: focus on implementations
|
|
143
|
+
- For abstract classes: show both hierarchy and implementations
|
|
144
|
+
- For concrete classes: focus on what they inherit
|
|
145
|
+
- Highlight incomplete implementations
|
|
146
|
+
- Note generic type parameters when present
|
|
147
|
+
- Keep diagrams simple - expand on request`,
|
|
148
|
+
tags: ['types', 'analysis', 'inheritance', 'interfaces', 'architecture'],
|
|
149
|
+
version: '1.0.0',
|
|
150
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* findDeadCode Tool
|
|
3
|
+
*
|
|
4
|
+
* Find potentially unused exports, functions, and variables across a codebase.
|
|
5
|
+
* Uses static analysis to identify code that may be dead.
|
|
6
|
+
*/
|
|
7
|
+
import type { Tool } from '@compilr-dev/agents';
|
|
8
|
+
import type { FindDeadCodeInput } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* findDeadCode tool
|
|
11
|
+
*/
|
|
12
|
+
export declare const findDeadCodeTool: Tool<FindDeadCodeInput>;
|
|
13
|
+
/**
|
|
14
|
+
* Create customizable findDeadCode tool
|
|
15
|
+
*/
|
|
16
|
+
export declare function createFindDeadCodeTool(options?: {
|
|
17
|
+
name?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
defaultMaxFiles?: number;
|
|
20
|
+
}): Tool<FindDeadCodeInput>;
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* findDeadCode Tool
|
|
3
|
+
*
|
|
4
|
+
* Find potentially unused exports, functions, and variables across a codebase.
|
|
5
|
+
* Uses static analysis to identify code that may be dead.
|
|
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 potentially unused exports, functions, and variables.
|
|
14
|
+
Analyzes the codebase to identify code that may be dead (never used).
|
|
15
|
+
Useful for cleanup and reducing bundle size.`;
|
|
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
|
+
includeTests: {
|
|
25
|
+
type: 'boolean',
|
|
26
|
+
description: 'Include test files in analysis (default: false)',
|
|
27
|
+
default: false,
|
|
28
|
+
},
|
|
29
|
+
checkExports: {
|
|
30
|
+
type: 'boolean',
|
|
31
|
+
description: 'Check for unused exports (default: true)',
|
|
32
|
+
default: true,
|
|
33
|
+
},
|
|
34
|
+
checkFunctions: {
|
|
35
|
+
type: 'boolean',
|
|
36
|
+
description: 'Check for unused functions (default: true)',
|
|
37
|
+
default: true,
|
|
38
|
+
},
|
|
39
|
+
checkVariables: {
|
|
40
|
+
type: 'boolean',
|
|
41
|
+
description: 'Check for unused variables (default: false)',
|
|
42
|
+
default: false,
|
|
43
|
+
},
|
|
44
|
+
maxFiles: {
|
|
45
|
+
type: 'number',
|
|
46
|
+
description: 'Maximum files to analyze (default: 100)',
|
|
47
|
+
default: 100,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
required: ['path'],
|
|
51
|
+
};
|
|
52
|
+
// Default exclusions
|
|
53
|
+
const DEFAULT_EXCLUDE = ['node_modules', 'dist', 'build', '.git', 'coverage'];
|
|
54
|
+
const TEST_PATTERNS = ['.test.', '.spec.', '__tests__', '__mocks__'];
|
|
55
|
+
/**
|
|
56
|
+
* findDeadCode tool
|
|
57
|
+
*/
|
|
58
|
+
export const findDeadCodeTool = defineTool({
|
|
59
|
+
name: 'find_dead_code',
|
|
60
|
+
description: TOOL_DESCRIPTION,
|
|
61
|
+
inputSchema: TOOL_INPUT_SCHEMA,
|
|
62
|
+
execute: executeFindDeadCode,
|
|
63
|
+
});
|
|
64
|
+
/**
|
|
65
|
+
* Execute the findDeadCode tool
|
|
66
|
+
*/
|
|
67
|
+
async function executeFindDeadCode(input) {
|
|
68
|
+
const { path: inputPath, includeTests = false, checkExports = true, checkFunctions = true, checkVariables = false, maxFiles = 100, } = input;
|
|
69
|
+
try {
|
|
70
|
+
const resolvedPath = path.resolve(inputPath);
|
|
71
|
+
// Check if path exists
|
|
72
|
+
try {
|
|
73
|
+
await fs.access(resolvedPath);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return createErrorResult(`Path not found: ${resolvedPath}`);
|
|
77
|
+
}
|
|
78
|
+
const stats = await fs.stat(resolvedPath);
|
|
79
|
+
if (!stats.isDirectory()) {
|
|
80
|
+
return createErrorResult('findDeadCode requires a directory path');
|
|
81
|
+
}
|
|
82
|
+
// Collect files
|
|
83
|
+
const files = [];
|
|
84
|
+
await collectFiles(resolvedPath, files, includeTests, maxFiles);
|
|
85
|
+
// Build symbol index
|
|
86
|
+
const definitions = [];
|
|
87
|
+
const usages = new Set();
|
|
88
|
+
const importedSymbols = new Set();
|
|
89
|
+
// First pass: collect all definitions and usages
|
|
90
|
+
for (const file of files) {
|
|
91
|
+
await analyzeFile(file, definitions, usages, importedSymbols);
|
|
92
|
+
}
|
|
93
|
+
// Find dead code
|
|
94
|
+
const deadCode = [];
|
|
95
|
+
// Stats
|
|
96
|
+
let totalExports = 0;
|
|
97
|
+
let unusedExports = 0;
|
|
98
|
+
let totalFunctions = 0;
|
|
99
|
+
let unusedFunctions = 0;
|
|
100
|
+
let totalVariables = 0;
|
|
101
|
+
let unusedVariables = 0;
|
|
102
|
+
for (const def of definitions) {
|
|
103
|
+
// Skip index files (re-exports)
|
|
104
|
+
if (def.path.endsWith('index.ts') || def.path.endsWith('index.js')) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const isUsed = usages.has(def.name) || importedSymbols.has(def.name);
|
|
108
|
+
// Check exports
|
|
109
|
+
if (def.exported && checkExports) {
|
|
110
|
+
totalExports++;
|
|
111
|
+
if (!importedSymbols.has(def.name)) {
|
|
112
|
+
// Check if it's a main entry point
|
|
113
|
+
const isEntryPoint = isLikelyEntryPoint(def.path, def.name);
|
|
114
|
+
if (!isEntryPoint) {
|
|
115
|
+
unusedExports++;
|
|
116
|
+
deadCode.push({
|
|
117
|
+
name: def.name,
|
|
118
|
+
path: def.path,
|
|
119
|
+
line: def.line,
|
|
120
|
+
kind: 'export',
|
|
121
|
+
exported: true,
|
|
122
|
+
confidence: 'medium',
|
|
123
|
+
reason: 'Export is not imported anywhere in the codebase',
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Check functions
|
|
129
|
+
if (def.kind === 'function' && checkFunctions) {
|
|
130
|
+
totalFunctions++;
|
|
131
|
+
if (!isUsed && !def.exported) {
|
|
132
|
+
unusedFunctions++;
|
|
133
|
+
deadCode.push({
|
|
134
|
+
name: def.name,
|
|
135
|
+
path: def.path,
|
|
136
|
+
line: def.line,
|
|
137
|
+
kind: 'function',
|
|
138
|
+
exported: false,
|
|
139
|
+
confidence: 'high',
|
|
140
|
+
reason: 'Function is not called anywhere in the file',
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Check variables
|
|
145
|
+
if (def.kind === 'variable' && checkVariables) {
|
|
146
|
+
totalVariables++;
|
|
147
|
+
if (!isUsed && !def.exported) {
|
|
148
|
+
unusedVariables++;
|
|
149
|
+
deadCode.push({
|
|
150
|
+
name: def.name,
|
|
151
|
+
path: def.path,
|
|
152
|
+
line: def.line,
|
|
153
|
+
kind: 'variable',
|
|
154
|
+
exported: false,
|
|
155
|
+
confidence: 'medium',
|
|
156
|
+
reason: 'Variable is not referenced after declaration',
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const result = {
|
|
162
|
+
path: resolvedPath,
|
|
163
|
+
deadCode,
|
|
164
|
+
stats: {
|
|
165
|
+
filesAnalyzed: files.length,
|
|
166
|
+
totalExports,
|
|
167
|
+
unusedExports,
|
|
168
|
+
totalFunctions,
|
|
169
|
+
unusedFunctions,
|
|
170
|
+
totalVariables,
|
|
171
|
+
unusedVariables,
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
return createSuccessResult(result);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
return createErrorResult(`Failed to find dead code: ${error instanceof Error ? error.message : String(error)}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Collect files to analyze
|
|
182
|
+
*/
|
|
183
|
+
async function collectFiles(dirPath, files, includeTests, maxFiles, currentDepth = 0) {
|
|
184
|
+
if (currentDepth > 10 || files.length >= maxFiles)
|
|
185
|
+
return;
|
|
186
|
+
try {
|
|
187
|
+
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
188
|
+
for (const entry of entries) {
|
|
189
|
+
if (files.length >= maxFiles)
|
|
190
|
+
break;
|
|
191
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
192
|
+
// Skip excluded directories
|
|
193
|
+
if (entry.isDirectory()) {
|
|
194
|
+
if (DEFAULT_EXCLUDE.includes(entry.name))
|
|
195
|
+
continue;
|
|
196
|
+
if (!includeTests && entry.name === '__tests__')
|
|
197
|
+
continue;
|
|
198
|
+
await collectFiles(fullPath, files, includeTests, maxFiles, currentDepth + 1);
|
|
199
|
+
}
|
|
200
|
+
else if (entry.isFile()) {
|
|
201
|
+
// Only include TypeScript/JavaScript files
|
|
202
|
+
if (/\.(ts|tsx|js|jsx)$/.test(entry.name) && !entry.name.endsWith('.d.ts')) {
|
|
203
|
+
// Skip test files if not including tests
|
|
204
|
+
if (!includeTests && TEST_PATTERNS.some((p) => fullPath.includes(p))) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
files.push(fullPath);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
// Ignore permission errors
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Analyze a file for definitions and usages
|
|
218
|
+
*/
|
|
219
|
+
async function analyzeFile(filePath, definitions, usages, importedSymbols) {
|
|
220
|
+
try {
|
|
221
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
222
|
+
const detection = detectLanguage(filePath);
|
|
223
|
+
if (!detection.language || !isLanguageSupported(detection.language)) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true, filePath.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
|
|
227
|
+
// Collect definitions
|
|
228
|
+
const visit = (node) => {
|
|
229
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
230
|
+
// Function declarations
|
|
231
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
232
|
+
const isExported = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
233
|
+
definitions.push({
|
|
234
|
+
name: node.name.text,
|
|
235
|
+
path: filePath,
|
|
236
|
+
line: line + 1,
|
|
237
|
+
kind: 'function',
|
|
238
|
+
exported: isExported,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
// Class declarations
|
|
242
|
+
if (ts.isClassDeclaration(node) && node.name) {
|
|
243
|
+
const isExported = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
244
|
+
definitions.push({
|
|
245
|
+
name: node.name.text,
|
|
246
|
+
path: filePath,
|
|
247
|
+
line: line + 1,
|
|
248
|
+
kind: 'class',
|
|
249
|
+
exported: isExported,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
// Variable declarations (const, let, var)
|
|
253
|
+
if (ts.isVariableStatement(node)) {
|
|
254
|
+
const isExported = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
255
|
+
for (const decl of node.declarationList.declarations) {
|
|
256
|
+
if (ts.isIdentifier(decl.name)) {
|
|
257
|
+
// Check if it's a function expression
|
|
258
|
+
const kind = decl.initializer &&
|
|
259
|
+
(ts.isFunctionExpression(decl.initializer) || ts.isArrowFunction(decl.initializer))
|
|
260
|
+
? 'function'
|
|
261
|
+
: 'variable';
|
|
262
|
+
definitions.push({
|
|
263
|
+
name: decl.name.text,
|
|
264
|
+
path: filePath,
|
|
265
|
+
line: line + 1,
|
|
266
|
+
kind,
|
|
267
|
+
exported: isExported,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// Interface declarations
|
|
273
|
+
if (ts.isInterfaceDeclaration(node)) {
|
|
274
|
+
const isExported = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
275
|
+
definitions.push({
|
|
276
|
+
name: node.name.text,
|
|
277
|
+
path: filePath,
|
|
278
|
+
line: line + 1,
|
|
279
|
+
kind: 'interface',
|
|
280
|
+
exported: isExported,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
// Type alias declarations
|
|
284
|
+
if (ts.isTypeAliasDeclaration(node)) {
|
|
285
|
+
const isExported = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
286
|
+
definitions.push({
|
|
287
|
+
name: node.name.text,
|
|
288
|
+
path: filePath,
|
|
289
|
+
line: line + 1,
|
|
290
|
+
kind: 'type',
|
|
291
|
+
exported: isExported,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
// Enum declarations
|
|
295
|
+
if (ts.isEnumDeclaration(node)) {
|
|
296
|
+
const isExported = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
|
|
297
|
+
definitions.push({
|
|
298
|
+
name: node.name.text,
|
|
299
|
+
path: filePath,
|
|
300
|
+
line: line + 1,
|
|
301
|
+
kind: 'enum',
|
|
302
|
+
exported: isExported,
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
// Import declarations - track what's imported
|
|
306
|
+
if (ts.isImportDeclaration(node) && node.importClause) {
|
|
307
|
+
// Named imports
|
|
308
|
+
if (node.importClause.namedBindings && ts.isNamedImports(node.importClause.namedBindings)) {
|
|
309
|
+
for (const specifier of node.importClause.namedBindings.elements) {
|
|
310
|
+
importedSymbols.add(specifier.name.text);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
// Default import
|
|
314
|
+
if (node.importClause.name) {
|
|
315
|
+
importedSymbols.add(node.importClause.name.text);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
// Track usages (identifiers)
|
|
319
|
+
if (ts.isIdentifier(node)) {
|
|
320
|
+
// Skip if it's a declaration name
|
|
321
|
+
const parent = node.parent;
|
|
322
|
+
if (!((ts.isFunctionDeclaration(parent) && parent.name === node) ||
|
|
323
|
+
(ts.isClassDeclaration(parent) && parent.name === node) ||
|
|
324
|
+
(ts.isVariableDeclaration(parent) && parent.name === node) ||
|
|
325
|
+
(ts.isInterfaceDeclaration(parent) && parent.name === node) ||
|
|
326
|
+
(ts.isTypeAliasDeclaration(parent) && parent.name === node) ||
|
|
327
|
+
(ts.isEnumDeclaration(parent) && parent.name === node) ||
|
|
328
|
+
(ts.isParameter(parent) && parent.name === node) ||
|
|
329
|
+
ts.isImportSpecifier(parent) ||
|
|
330
|
+
ts.isExportSpecifier(parent))) {
|
|
331
|
+
usages.add(node.text);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
ts.forEachChild(node, visit);
|
|
335
|
+
};
|
|
336
|
+
visit(sourceFile);
|
|
337
|
+
}
|
|
338
|
+
catch {
|
|
339
|
+
// Ignore errors
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Check if a symbol is likely an entry point
|
|
344
|
+
*/
|
|
345
|
+
function isLikelyEntryPoint(filePath, symbolName) {
|
|
346
|
+
// Main entry files
|
|
347
|
+
const entryPatterns = ['index.ts', 'index.js', 'main.ts', 'main.js', 'app.ts', 'app.js'];
|
|
348
|
+
const fileName = path.basename(filePath);
|
|
349
|
+
if (entryPatterns.includes(fileName)) {
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
// Common exported names that are likely entry points
|
|
353
|
+
const entrySymbols = ['default', 'main', 'app', 'handler', 'server', 'client'];
|
|
354
|
+
if (entrySymbols.includes(symbolName.toLowerCase())) {
|
|
355
|
+
return true;
|
|
356
|
+
}
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Create customizable findDeadCode tool
|
|
361
|
+
*/
|
|
362
|
+
export function createFindDeadCodeTool(options) {
|
|
363
|
+
return defineTool({
|
|
364
|
+
name: options?.name ?? 'find_dead_code',
|
|
365
|
+
description: options?.description ?? TOOL_DESCRIPTION,
|
|
366
|
+
inputSchema: TOOL_INPUT_SCHEMA,
|
|
367
|
+
execute: async (input) => {
|
|
368
|
+
const modifiedInput = {
|
|
369
|
+
...input,
|
|
370
|
+
maxFiles: input.maxFiles ?? options?.defaultMaxFiles,
|
|
371
|
+
};
|
|
372
|
+
return executeFindDeadCode(modifiedInput);
|
|
373
|
+
},
|
|
374
|
+
});
|
|
375
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* findDuplicates Tool
|
|
3
|
+
*
|
|
4
|
+
* Detect duplicate code blocks across the codebase using content hashing.
|
|
5
|
+
* Helps identify opportunities for refactoring and code reuse.
|
|
6
|
+
*/
|
|
7
|
+
import type { Tool } from '@compilr-dev/agents';
|
|
8
|
+
import type { FindDuplicatesInput } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* findDuplicates tool
|
|
11
|
+
*/
|
|
12
|
+
export declare const findDuplicatesTool: Tool<FindDuplicatesInput>;
|
|
13
|
+
/**
|
|
14
|
+
* Create customizable findDuplicates tool
|
|
15
|
+
*/
|
|
16
|
+
export declare function createFindDuplicatesTool(options?: {
|
|
17
|
+
name?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
defaultMinLines?: number;
|
|
20
|
+
defaultMaxFiles?: number;
|
|
21
|
+
}): Tool<FindDuplicatesInput>;
|