@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,482 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* getDependencyGraph Tool
|
|
3
|
+
*
|
|
4
|
+
* Analyze module-level dependencies across a directory.
|
|
5
|
+
* Builds a dependency graph and detects circular dependencies.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'node:fs/promises';
|
|
8
|
+
import * as fsSync from 'node:fs';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import * as ts from 'typescript';
|
|
11
|
+
import { defineTool, createSuccessResult, createErrorResult } from '@compilr-dev/agents';
|
|
12
|
+
import { detectLanguage, isLanguageSupported } from '../parser/typescript-parser.js';
|
|
13
|
+
// Tool description
|
|
14
|
+
const TOOL_DESCRIPTION = `Analyze module-level dependencies across a directory.
|
|
15
|
+
Returns a dependency graph showing which modules import which other modules.
|
|
16
|
+
Detects circular dependencies and provides dependency statistics.`;
|
|
17
|
+
// Tool input schema
|
|
18
|
+
const TOOL_INPUT_SCHEMA = {
|
|
19
|
+
type: 'object',
|
|
20
|
+
properties: {
|
|
21
|
+
path: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
description: 'Directory or file to analyze',
|
|
24
|
+
},
|
|
25
|
+
includeExternal: {
|
|
26
|
+
type: 'boolean',
|
|
27
|
+
description: 'Include external package dependencies (default: true)',
|
|
28
|
+
default: true,
|
|
29
|
+
},
|
|
30
|
+
includeTypeOnly: {
|
|
31
|
+
type: 'boolean',
|
|
32
|
+
description: 'Include type-only imports (default: true)',
|
|
33
|
+
default: true,
|
|
34
|
+
},
|
|
35
|
+
maxDepth: {
|
|
36
|
+
type: 'number',
|
|
37
|
+
description: 'Maximum depth for directory traversal (default: 10)',
|
|
38
|
+
default: 10,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
required: ['path'],
|
|
42
|
+
};
|
|
43
|
+
// Default file patterns
|
|
44
|
+
const DEFAULT_INCLUDE = ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.mts', '**/*.mjs'];
|
|
45
|
+
const DEFAULT_EXCLUDE = ['**/node_modules/**', '**/dist/**', '**/build/**', '**/.git/**'];
|
|
46
|
+
/**
|
|
47
|
+
* getDependencyGraph tool - Analyze module dependencies
|
|
48
|
+
*/
|
|
49
|
+
export const getDependencyGraphTool = defineTool({
|
|
50
|
+
name: 'get_dependency_graph',
|
|
51
|
+
description: TOOL_DESCRIPTION,
|
|
52
|
+
inputSchema: TOOL_INPUT_SCHEMA,
|
|
53
|
+
execute: executeGetDependencyGraph,
|
|
54
|
+
});
|
|
55
|
+
/**
|
|
56
|
+
* Execute the getDependencyGraph tool
|
|
57
|
+
*/
|
|
58
|
+
async function executeGetDependencyGraph(input) {
|
|
59
|
+
const { path: inputPath, includeExternal = true, includeTypeOnly = true, maxDepth = 10, include = DEFAULT_INCLUDE, exclude = DEFAULT_EXCLUDE, } = input;
|
|
60
|
+
// Validate input
|
|
61
|
+
if (!inputPath || inputPath.trim().length === 0) {
|
|
62
|
+
return createErrorResult('Path is required');
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
// Check if path exists
|
|
66
|
+
try {
|
|
67
|
+
await fs.access(inputPath);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return createErrorResult(`Path not found: ${inputPath}`);
|
|
71
|
+
}
|
|
72
|
+
const stats = await fs.stat(inputPath);
|
|
73
|
+
const rootPath = stats.isDirectory() ? inputPath : path.dirname(inputPath);
|
|
74
|
+
// Collect files to analyze
|
|
75
|
+
const files = stats.isDirectory()
|
|
76
|
+
? await collectFiles(inputPath, include, exclude, maxDepth)
|
|
77
|
+
: [inputPath];
|
|
78
|
+
if (files.length === 0) {
|
|
79
|
+
return createSuccessResult({
|
|
80
|
+
root: rootPath,
|
|
81
|
+
modules: [],
|
|
82
|
+
edges: [],
|
|
83
|
+
circularDependencies: [],
|
|
84
|
+
stats: {
|
|
85
|
+
totalModules: 0,
|
|
86
|
+
internalModules: 0,
|
|
87
|
+
externalPackages: 0,
|
|
88
|
+
totalEdges: 0,
|
|
89
|
+
circularDependencies: 0,
|
|
90
|
+
externalPackageList: [],
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
// Analyze dependencies
|
|
95
|
+
const result = await analyzeDependencies(files, rootPath, includeExternal, includeTypeOnly);
|
|
96
|
+
return createSuccessResult(result);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
100
|
+
return createErrorResult(`Failed to analyze dependencies: ${message}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Collect files matching patterns
|
|
105
|
+
*/
|
|
106
|
+
async function collectFiles(dir, include, exclude, maxDepth, currentDepth = 0) {
|
|
107
|
+
if (currentDepth >= maxDepth) {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
const files = [];
|
|
111
|
+
try {
|
|
112
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
113
|
+
for (const entry of entries) {
|
|
114
|
+
const fullPath = path.join(dir, entry.name);
|
|
115
|
+
const relativePath = fullPath;
|
|
116
|
+
// Check exclusions
|
|
117
|
+
if (matchesPattern(relativePath, exclude)) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (entry.isDirectory()) {
|
|
121
|
+
const subFiles = await collectFiles(fullPath, include, exclude, maxDepth, currentDepth + 1);
|
|
122
|
+
files.push(...subFiles);
|
|
123
|
+
}
|
|
124
|
+
else if (entry.isFile()) {
|
|
125
|
+
// Check if file matches include patterns
|
|
126
|
+
if (matchesPattern(relativePath, include) && isSupportedFile(fullPath)) {
|
|
127
|
+
files.push(fullPath);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// Silently skip directories we can't read
|
|
134
|
+
}
|
|
135
|
+
return files;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Check if a path matches any of the patterns
|
|
139
|
+
*/
|
|
140
|
+
function matchesPattern(filePath, patterns) {
|
|
141
|
+
const fileName = path.basename(filePath);
|
|
142
|
+
for (const pattern of patterns) {
|
|
143
|
+
// Handle simple glob patterns
|
|
144
|
+
if (pattern.includes('**')) {
|
|
145
|
+
// Check if any directory in path matches
|
|
146
|
+
const patternDir = pattern.replace('**/', '').replace('/**', '');
|
|
147
|
+
if (filePath.includes(patternDir.replace('*', ''))) {
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else if (pattern.startsWith('*.')) {
|
|
152
|
+
// Extension pattern
|
|
153
|
+
const ext = pattern.substring(1);
|
|
154
|
+
if (fileName.endsWith(ext)) {
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else if (filePath.includes(pattern) || fileName === pattern) {
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Check if file is supported
|
|
166
|
+
*/
|
|
167
|
+
function isSupportedFile(filePath) {
|
|
168
|
+
const detection = detectLanguage(filePath);
|
|
169
|
+
if (!detection.language) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
return isLanguageSupported(detection.language);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Analyze dependencies across files
|
|
176
|
+
*/
|
|
177
|
+
async function analyzeDependencies(files, rootPath, includeExternal, includeTypeOnly) {
|
|
178
|
+
const modules = new Map();
|
|
179
|
+
const edges = [];
|
|
180
|
+
const externalPackages = new Set();
|
|
181
|
+
// Track adjacency for cycle detection
|
|
182
|
+
const adjacency = new Map();
|
|
183
|
+
for (const file of files) {
|
|
184
|
+
const relativePath = path.relative(rootPath, file);
|
|
185
|
+
const imports = await extractImports(file);
|
|
186
|
+
// Add this module
|
|
187
|
+
if (!modules.has(relativePath)) {
|
|
188
|
+
modules.set(relativePath, {
|
|
189
|
+
path: relativePath,
|
|
190
|
+
absolutePath: file,
|
|
191
|
+
isExternal: false,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
const moduleDeps = [];
|
|
195
|
+
for (const imp of imports) {
|
|
196
|
+
// Skip type-only if not requested
|
|
197
|
+
if (imp.typeOnly && !includeTypeOnly) {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
// Determine if external
|
|
201
|
+
const isExternal = !imp.source.startsWith('.') && !imp.source.startsWith('/');
|
|
202
|
+
if (isExternal) {
|
|
203
|
+
if (!includeExternal) {
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
// Extract package name (handle scoped packages)
|
|
207
|
+
const packageName = imp.source.startsWith('@')
|
|
208
|
+
? imp.source.split('/').slice(0, 2).join('/')
|
|
209
|
+
: imp.source.split('/')[0];
|
|
210
|
+
externalPackages.add(packageName);
|
|
211
|
+
// Add external module node
|
|
212
|
+
if (!modules.has(packageName)) {
|
|
213
|
+
modules.set(packageName, {
|
|
214
|
+
path: packageName,
|
|
215
|
+
absolutePath: packageName,
|
|
216
|
+
isExternal: true,
|
|
217
|
+
packageName,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
edges.push({
|
|
221
|
+
from: relativePath,
|
|
222
|
+
to: packageName,
|
|
223
|
+
importType: imp.importType,
|
|
224
|
+
typeOnly: imp.typeOnly,
|
|
225
|
+
symbolCount: imp.symbols.length,
|
|
226
|
+
symbols: imp.symbols.slice(0, 10),
|
|
227
|
+
});
|
|
228
|
+
moduleDeps.push(packageName);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
// Internal import - resolve path
|
|
232
|
+
const resolvedPath = resolveImportPath(file, imp.source, rootPath);
|
|
233
|
+
if (resolvedPath) {
|
|
234
|
+
const relativeResolved = path.relative(rootPath, resolvedPath);
|
|
235
|
+
// Add target module if not exists
|
|
236
|
+
if (!modules.has(relativeResolved)) {
|
|
237
|
+
modules.set(relativeResolved, {
|
|
238
|
+
path: relativeResolved,
|
|
239
|
+
absolutePath: resolvedPath,
|
|
240
|
+
isExternal: false,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
edges.push({
|
|
244
|
+
from: relativePath,
|
|
245
|
+
to: relativeResolved,
|
|
246
|
+
importType: imp.importType,
|
|
247
|
+
typeOnly: imp.typeOnly,
|
|
248
|
+
symbolCount: imp.symbols.length,
|
|
249
|
+
symbols: imp.symbols.slice(0, 10),
|
|
250
|
+
});
|
|
251
|
+
moduleDeps.push(relativeResolved);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
adjacency.set(relativePath, moduleDeps);
|
|
256
|
+
}
|
|
257
|
+
// Detect circular dependencies
|
|
258
|
+
const circularDependencies = detectCircularDependencies(adjacency);
|
|
259
|
+
// Calculate statistics
|
|
260
|
+
const internalModules = Array.from(modules.values()).filter((m) => !m.isExternal);
|
|
261
|
+
const dependencyCounts = new Map();
|
|
262
|
+
const dependentCounts = new Map();
|
|
263
|
+
for (const edge of edges) {
|
|
264
|
+
dependencyCounts.set(edge.from, (dependencyCounts.get(edge.from) ?? 0) + 1);
|
|
265
|
+
dependentCounts.set(edge.to, (dependentCounts.get(edge.to) ?? 0) + 1);
|
|
266
|
+
}
|
|
267
|
+
let mostDependencies;
|
|
268
|
+
let mostDependents;
|
|
269
|
+
for (const [module, count] of dependencyCounts) {
|
|
270
|
+
if (!mostDependencies || count > mostDependencies.count) {
|
|
271
|
+
mostDependencies = { module, count };
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
for (const [module, count] of dependentCounts) {
|
|
275
|
+
if (!mostDependents || count > mostDependents.count) {
|
|
276
|
+
mostDependents = { module, count };
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const stats = {
|
|
280
|
+
totalModules: modules.size,
|
|
281
|
+
internalModules: internalModules.length,
|
|
282
|
+
externalPackages: externalPackages.size,
|
|
283
|
+
totalEdges: edges.length,
|
|
284
|
+
circularDependencies: circularDependencies.length,
|
|
285
|
+
externalPackageList: Array.from(externalPackages).sort(),
|
|
286
|
+
mostDependencies,
|
|
287
|
+
mostDependents,
|
|
288
|
+
};
|
|
289
|
+
return {
|
|
290
|
+
root: rootPath,
|
|
291
|
+
modules: Array.from(modules.values()),
|
|
292
|
+
edges,
|
|
293
|
+
circularDependencies,
|
|
294
|
+
stats,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Extract imports from a file
|
|
299
|
+
*/
|
|
300
|
+
async function extractImports(filePath) {
|
|
301
|
+
const imports = [];
|
|
302
|
+
try {
|
|
303
|
+
const sourceCode = await fs.readFile(filePath, 'utf-8');
|
|
304
|
+
const scriptKind = getScriptKind(filePath);
|
|
305
|
+
const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest, true, scriptKind);
|
|
306
|
+
ts.forEachChild(sourceFile, (node) => {
|
|
307
|
+
// Import declarations
|
|
308
|
+
if (ts.isImportDeclaration(node)) {
|
|
309
|
+
if (ts.isStringLiteral(node.moduleSpecifier)) {
|
|
310
|
+
const source = node.moduleSpecifier.text;
|
|
311
|
+
// Check for type-only import by looking at the import text
|
|
312
|
+
const importText = node.getText(sourceFile);
|
|
313
|
+
const typeOnly = importText.includes('import type');
|
|
314
|
+
const symbols = [];
|
|
315
|
+
let importType = 'side-effect';
|
|
316
|
+
if (node.importClause) {
|
|
317
|
+
// Default import
|
|
318
|
+
if (node.importClause.name) {
|
|
319
|
+
importType = 'default';
|
|
320
|
+
symbols.push(node.importClause.name.text);
|
|
321
|
+
}
|
|
322
|
+
// Named or namespace imports
|
|
323
|
+
if (node.importClause.namedBindings) {
|
|
324
|
+
if (ts.isNamespaceImport(node.importClause.namedBindings)) {
|
|
325
|
+
importType = 'namespace';
|
|
326
|
+
symbols.push(node.importClause.namedBindings.name.text);
|
|
327
|
+
}
|
|
328
|
+
else if (ts.isNamedImports(node.importClause.namedBindings)) {
|
|
329
|
+
importType = importType === 'default' ? 'default' : 'named';
|
|
330
|
+
for (const element of node.importClause.namedBindings.elements) {
|
|
331
|
+
symbols.push(element.name.text);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
imports.push({ source, importType, typeOnly, symbols });
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
// Export declarations with 'from' (re-exports count as dependencies)
|
|
340
|
+
if (ts.isExportDeclaration(node)) {
|
|
341
|
+
if (node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
342
|
+
const source = node.moduleSpecifier.text;
|
|
343
|
+
const typeOnly = node.isTypeOnly;
|
|
344
|
+
const symbols = [];
|
|
345
|
+
let importType = 'namespace';
|
|
346
|
+
if (node.exportClause && ts.isNamedExports(node.exportClause)) {
|
|
347
|
+
importType = 'named';
|
|
348
|
+
for (const element of node.exportClause.elements) {
|
|
349
|
+
symbols.push(element.name.text);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
imports.push({ source, importType, typeOnly, symbols });
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
catch {
|
|
358
|
+
// Silently skip files that can't be parsed
|
|
359
|
+
}
|
|
360
|
+
return imports;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Resolve import path to absolute file path
|
|
364
|
+
*/
|
|
365
|
+
function resolveImportPath(fromFile, importSource, _rootPath) {
|
|
366
|
+
const fromDir = path.dirname(fromFile);
|
|
367
|
+
const basePath = path.resolve(fromDir, importSource);
|
|
368
|
+
// Try various extensions
|
|
369
|
+
const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mts', '.mjs', ''];
|
|
370
|
+
for (const ext of extensions) {
|
|
371
|
+
const candidate = basePath + ext;
|
|
372
|
+
try {
|
|
373
|
+
// Check if file exists synchronously for simplicity
|
|
374
|
+
// (we're already in an async context and this is a quick check)
|
|
375
|
+
fsSync.accessSync(candidate);
|
|
376
|
+
return candidate;
|
|
377
|
+
}
|
|
378
|
+
catch {
|
|
379
|
+
// Try index file
|
|
380
|
+
const indexCandidate = path.join(basePath, `index${ext || '.ts'}`);
|
|
381
|
+
try {
|
|
382
|
+
fsSync.accessSync(indexCandidate);
|
|
383
|
+
return indexCandidate;
|
|
384
|
+
}
|
|
385
|
+
catch {
|
|
386
|
+
// Continue to next extension
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return undefined;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Detect circular dependencies using DFS
|
|
394
|
+
*/
|
|
395
|
+
function detectCircularDependencies(adjacency) {
|
|
396
|
+
const cycles = [];
|
|
397
|
+
const visited = new Set();
|
|
398
|
+
const recursionStack = new Set();
|
|
399
|
+
const path = [];
|
|
400
|
+
function dfs(node) {
|
|
401
|
+
if (recursionStack.has(node)) {
|
|
402
|
+
// Found a cycle
|
|
403
|
+
const cycleStart = path.indexOf(node);
|
|
404
|
+
if (cycleStart !== -1) {
|
|
405
|
+
const cycle = [...path.slice(cycleStart), node];
|
|
406
|
+
// Avoid duplicate cycles
|
|
407
|
+
const cycleKey = [...cycle].sort().join('->');
|
|
408
|
+
if (!cycles.some((c) => [...c.cycle].sort().join('->') === cycleKey)) {
|
|
409
|
+
cycles.push({
|
|
410
|
+
cycle,
|
|
411
|
+
length: cycle.length - 1,
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
if (visited.has(node)) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
visited.add(node);
|
|
421
|
+
recursionStack.add(node);
|
|
422
|
+
path.push(node);
|
|
423
|
+
const deps = adjacency.get(node) ?? [];
|
|
424
|
+
for (const dep of deps) {
|
|
425
|
+
// Only check internal dependencies
|
|
426
|
+
if (adjacency.has(dep)) {
|
|
427
|
+
dfs(dep);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
path.pop();
|
|
431
|
+
recursionStack.delete(node);
|
|
432
|
+
}
|
|
433
|
+
for (const node of adjacency.keys()) {
|
|
434
|
+
if (!visited.has(node)) {
|
|
435
|
+
dfs(node);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return cycles;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Get TypeScript script kind from file extension
|
|
442
|
+
*/
|
|
443
|
+
function getScriptKind(filePath) {
|
|
444
|
+
const ext = filePath.toLowerCase().split('.').pop();
|
|
445
|
+
switch (ext) {
|
|
446
|
+
case 'ts':
|
|
447
|
+
return ts.ScriptKind.TS;
|
|
448
|
+
case 'tsx':
|
|
449
|
+
return ts.ScriptKind.TSX;
|
|
450
|
+
case 'js':
|
|
451
|
+
return ts.ScriptKind.JS;
|
|
452
|
+
case 'jsx':
|
|
453
|
+
return ts.ScriptKind.JSX;
|
|
454
|
+
case 'mts':
|
|
455
|
+
case 'cts':
|
|
456
|
+
return ts.ScriptKind.TS;
|
|
457
|
+
case 'mjs':
|
|
458
|
+
case 'cjs':
|
|
459
|
+
return ts.ScriptKind.JS;
|
|
460
|
+
default:
|
|
461
|
+
return ts.ScriptKind.TS;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Factory function to create a customized getDependencyGraph tool
|
|
466
|
+
*/
|
|
467
|
+
export function createGetDependencyGraphTool(options) {
|
|
468
|
+
const { defaultIncludeExternal = true, defaultIncludeTypeOnly = true, defaultMaxDepth = 10, } = options ?? {};
|
|
469
|
+
return defineTool({
|
|
470
|
+
name: 'get_dependency_graph',
|
|
471
|
+
description: TOOL_DESCRIPTION,
|
|
472
|
+
inputSchema: TOOL_INPUT_SCHEMA,
|
|
473
|
+
execute: async (input) => {
|
|
474
|
+
return executeGetDependencyGraph({
|
|
475
|
+
...input,
|
|
476
|
+
includeExternal: input.includeExternal ?? defaultIncludeExternal,
|
|
477
|
+
includeTypeOnly: input.includeTypeOnly ?? defaultIncludeTypeOnly,
|
|
478
|
+
maxDepth: input.maxDepth ?? defaultMaxDepth,
|
|
479
|
+
});
|
|
480
|
+
},
|
|
481
|
+
});
|
|
482
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* getDocumentation Tool
|
|
3
|
+
*
|
|
4
|
+
* Extract JSDoc/TSDoc documentation from source files.
|
|
5
|
+
* Provides documentation coverage metrics and identifies undocumented exports.
|
|
6
|
+
*/
|
|
7
|
+
import type { Tool } from '@compilr-dev/agents';
|
|
8
|
+
import type { GetDocumentationInput } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* getDocumentation tool
|
|
11
|
+
*/
|
|
12
|
+
export declare const getDocumentationTool: Tool<GetDocumentationInput>;
|
|
13
|
+
/**
|
|
14
|
+
* Create customizable getDocumentation tool
|
|
15
|
+
*/
|
|
16
|
+
export declare function createGetDocumentationTool(options?: {
|
|
17
|
+
name?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
defaultMaxFiles?: number;
|
|
20
|
+
defaultDocumentedOnly?: boolean;
|
|
21
|
+
}): Tool<GetDocumentationInput>;
|