@compilr-dev/agents-coding-ts 0.1.2 → 0.1.4
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/dist/index.d.ts +8 -5
- package/dist/index.js +14 -4
- package/dist/parser/index.d.ts +2 -2
- package/dist/parser/index.js +1 -1
- package/dist/parser/typescript-parser.d.ts +2 -2
- package/dist/parser/typescript-parser.js +77 -54
- package/dist/skills/code-health.js +18 -5
- package/dist/skills/code-structure.js +15 -5
- package/dist/skills/dependency-audit.js +16 -5
- package/dist/skills/index.d.ts +7 -7
- package/dist/skills/index.js +11 -11
- package/dist/skills/refactor-impact.js +16 -5
- package/dist/skills/type-analysis.js +16 -5
- package/dist/tools/find-dead-code.d.ts +2 -2
- package/dist/tools/find-dead-code.js +82 -58
- package/dist/tools/find-duplicates.d.ts +2 -2
- package/dist/tools/find-duplicates.js +41 -38
- package/dist/tools/find-implementations.d.ts +2 -2
- package/dist/tools/find-implementations.js +44 -36
- package/dist/tools/find-patterns.d.ts +2 -2
- package/dist/tools/find-patterns.js +154 -148
- package/dist/tools/find-references.d.ts +2 -2
- package/dist/tools/find-references.js +76 -72
- package/dist/tools/find-symbol.d.ts +2 -2
- package/dist/tools/find-symbol.js +106 -96
- package/dist/tools/get-call-graph.d.ts +2 -2
- package/dist/tools/get-call-graph.js +52 -47
- package/dist/tools/get-complexity.d.ts +2 -2
- package/dist/tools/get-complexity.js +94 -46
- package/dist/tools/get-dependency-graph.d.ts +2 -2
- package/dist/tools/get-dependency-graph.js +66 -52
- package/dist/tools/get-documentation.d.ts +2 -2
- package/dist/tools/get-documentation.js +154 -122
- package/dist/tools/get-exports.d.ts +2 -2
- package/dist/tools/get-exports.js +73 -61
- package/dist/tools/get-file-structure.d.ts +2 -2
- package/dist/tools/get-file-structure.js +16 -16
- package/dist/tools/get-imports.d.ts +2 -2
- package/dist/tools/get-imports.js +46 -46
- package/dist/tools/get-signature.d.ts +2 -2
- package/dist/tools/get-signature.js +168 -124
- package/dist/tools/get-type-hierarchy.d.ts +2 -2
- package/dist/tools/get-type-hierarchy.js +53 -44
- package/dist/tools/index.d.ts +18 -16
- package/dist/tools/index.js +17 -15
- package/dist/tools/read-symbol.d.ts +62 -0
- package/dist/tools/read-symbol.js +464 -0
- package/dist/tools/types.d.ts +27 -27
- package/package.json +3 -3
|
@@ -4,53 +4,53 @@
|
|
|
4
4
|
* Calculate code complexity metrics including cyclomatic complexity,
|
|
5
5
|
* cognitive complexity, nesting depth, and identify complexity hotspots.
|
|
6
6
|
*/
|
|
7
|
-
import * as fs from
|
|
8
|
-
import * as path from
|
|
9
|
-
import * as ts from
|
|
10
|
-
import { defineTool, createSuccessResult, createErrorResult } from
|
|
11
|
-
import { detectLanguage, isLanguageSupported } from
|
|
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
12
|
// Tool description
|
|
13
13
|
const TOOL_DESCRIPTION = `Calculate code complexity metrics for files or directories.
|
|
14
14
|
Returns cyclomatic complexity, cognitive complexity, nesting depth, and identifies hotspots.
|
|
15
15
|
Useful for finding functions that may need refactoring.`;
|
|
16
16
|
// Tool input schema
|
|
17
17
|
const TOOL_INPUT_SCHEMA = {
|
|
18
|
-
type:
|
|
18
|
+
type: "object",
|
|
19
19
|
properties: {
|
|
20
20
|
path: {
|
|
21
|
-
type:
|
|
22
|
-
description:
|
|
21
|
+
type: "string",
|
|
22
|
+
description: "File or directory to analyze",
|
|
23
23
|
},
|
|
24
24
|
recursive: {
|
|
25
|
-
type:
|
|
26
|
-
description:
|
|
25
|
+
type: "boolean",
|
|
26
|
+
description: "Recursive analysis for directories (default: false)",
|
|
27
27
|
default: false,
|
|
28
28
|
},
|
|
29
29
|
threshold: {
|
|
30
|
-
type:
|
|
31
|
-
description:
|
|
30
|
+
type: "number",
|
|
31
|
+
description: "Complexity threshold for warnings (default: 10)",
|
|
32
32
|
default: 10,
|
|
33
33
|
},
|
|
34
34
|
onlyAboveThreshold: {
|
|
35
|
-
type:
|
|
36
|
-
description:
|
|
35
|
+
type: "boolean",
|
|
36
|
+
description: "Only return items above threshold (default: false)",
|
|
37
37
|
default: false,
|
|
38
38
|
},
|
|
39
39
|
maxFiles: {
|
|
40
|
-
type:
|
|
41
|
-
description:
|
|
40
|
+
type: "number",
|
|
41
|
+
description: "Maximum files to analyze (default: 50)",
|
|
42
42
|
default: 50,
|
|
43
43
|
},
|
|
44
44
|
},
|
|
45
|
-
required: [
|
|
45
|
+
required: ["path"],
|
|
46
46
|
};
|
|
47
47
|
// Default exclusions
|
|
48
|
-
const DEFAULT_EXCLUDE = [
|
|
48
|
+
const DEFAULT_EXCLUDE = ["node_modules", "dist", "build", ".git", "coverage"];
|
|
49
49
|
/**
|
|
50
50
|
* getComplexity tool
|
|
51
51
|
*/
|
|
52
52
|
export const getComplexityTool = defineTool({
|
|
53
|
-
name:
|
|
53
|
+
name: "get_complexity",
|
|
54
54
|
description: TOOL_DESCRIPTION,
|
|
55
55
|
inputSchema: TOOL_INPUT_SCHEMA,
|
|
56
56
|
execute: executeGetComplexity,
|
|
@@ -124,7 +124,9 @@ async function executeGetComplexity(input) {
|
|
|
124
124
|
summary: {
|
|
125
125
|
totalFiles: fileResults.length,
|
|
126
126
|
totalFunctions,
|
|
127
|
-
averageComplexity: totalFunctions > 0
|
|
127
|
+
averageComplexity: totalFunctions > 0
|
|
128
|
+
? Math.round((totalComplexity / totalFunctions) * 100) / 100
|
|
129
|
+
: 0,
|
|
128
130
|
maxComplexity,
|
|
129
131
|
aboveThreshold: aboveThresholdCount,
|
|
130
132
|
threshold,
|
|
@@ -157,7 +159,8 @@ async function collectFiles(dirPath, files, maxDepth, maxFiles, currentDepth = 0
|
|
|
157
159
|
}
|
|
158
160
|
else if (entry.isFile()) {
|
|
159
161
|
// Only include TypeScript/JavaScript files
|
|
160
|
-
if (/\.(ts|tsx|js|jsx)$/.test(entry.name) &&
|
|
162
|
+
if (/\.(ts|tsx|js|jsx)$/.test(entry.name) &&
|
|
163
|
+
!entry.name.endsWith(".d.ts")) {
|
|
161
164
|
files.push(fullPath);
|
|
162
165
|
}
|
|
163
166
|
}
|
|
@@ -172,12 +175,12 @@ async function collectFiles(dirPath, files, maxDepth, maxFiles, currentDepth = 0
|
|
|
172
175
|
*/
|
|
173
176
|
async function analyzeFile(filePath, threshold) {
|
|
174
177
|
try {
|
|
175
|
-
const content = await fs.readFile(filePath,
|
|
178
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
176
179
|
const detection = detectLanguage(filePath);
|
|
177
180
|
if (!detection.language || !isLanguageSupported(detection.language)) {
|
|
178
181
|
return null;
|
|
179
182
|
}
|
|
180
|
-
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true, filePath.endsWith(
|
|
183
|
+
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true, filePath.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
|
|
181
184
|
const functions = [];
|
|
182
185
|
let classCount = 0;
|
|
183
186
|
// Visit all nodes
|
|
@@ -200,14 +203,18 @@ async function analyzeFile(filePath, threshold) {
|
|
|
200
203
|
};
|
|
201
204
|
visit(sourceFile);
|
|
202
205
|
// Calculate file metrics
|
|
203
|
-
const lines = content.split(
|
|
206
|
+
const lines = content.split("\n");
|
|
204
207
|
const totalLines = lines.length;
|
|
205
|
-
const blankLines = lines.filter((l) => l.trim() ===
|
|
208
|
+
const blankLines = lines.filter((l) => l.trim() === "").length;
|
|
206
209
|
const linesOfCode = totalLines - blankLines;
|
|
207
210
|
const avgComplexity = functions.length > 0
|
|
208
|
-
? Math.round((functions.reduce((sum, f) => sum + f.cyclomatic, 0) /
|
|
211
|
+
? Math.round((functions.reduce((sum, f) => sum + f.cyclomatic, 0) /
|
|
212
|
+
functions.length) *
|
|
213
|
+
100) / 100
|
|
214
|
+
: 0;
|
|
215
|
+
const maxComplexity = functions.length > 0
|
|
216
|
+
? Math.max(...functions.map((f) => f.cyclomatic))
|
|
209
217
|
: 0;
|
|
210
|
-
const maxComplexity = functions.length > 0 ? Math.max(...functions.map((f) => f.cyclomatic)) : 0;
|
|
211
218
|
return {
|
|
212
219
|
path: filePath,
|
|
213
220
|
metrics: {
|
|
@@ -230,12 +237,12 @@ async function analyzeFile(filePath, threshold) {
|
|
|
230
237
|
*/
|
|
231
238
|
function analyzeFunctionComplexity(node, sourceFile, threshold) {
|
|
232
239
|
// Get function name
|
|
233
|
-
let name =
|
|
240
|
+
let name = "<anonymous>";
|
|
234
241
|
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
235
242
|
name = node.name.text;
|
|
236
243
|
}
|
|
237
244
|
else if (ts.isMethodDeclaration(node)) {
|
|
238
|
-
name = ts.isIdentifier(node.name) ? node.name.text :
|
|
245
|
+
name = ts.isIdentifier(node.name) ? node.name.text : "<computed>";
|
|
239
246
|
}
|
|
240
247
|
else if (ts.isFunctionExpression(node) && node.name) {
|
|
241
248
|
name = node.name.text;
|
|
@@ -246,19 +253,22 @@ function analyzeFunctionComplexity(node, sourceFile, threshold) {
|
|
|
246
253
|
if (ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name)) {
|
|
247
254
|
name = parent.name.text;
|
|
248
255
|
}
|
|
249
|
-
else if (ts.isPropertyAssignment(parent) &&
|
|
256
|
+
else if (ts.isPropertyAssignment(parent) &&
|
|
257
|
+
ts.isIdentifier(parent.name)) {
|
|
250
258
|
name = parent.name.text;
|
|
251
259
|
}
|
|
252
260
|
}
|
|
253
261
|
// Skip anonymous functions in certain contexts
|
|
254
|
-
if (name ===
|
|
262
|
+
if (name === "<anonymous>") {
|
|
255
263
|
// Only include top-level anonymous functions or named expressions
|
|
256
264
|
const parent = node.parent;
|
|
257
265
|
if (!ts.isVariableDeclaration(parent) && !ts.isPropertyAssignment(parent)) {
|
|
258
266
|
return null;
|
|
259
267
|
}
|
|
260
268
|
}
|
|
261
|
-
const body = ts.isFunctionDeclaration(node) ||
|
|
269
|
+
const body = ts.isFunctionDeclaration(node) ||
|
|
270
|
+
ts.isMethodDeclaration(node) ||
|
|
271
|
+
ts.isFunctionExpression(node)
|
|
262
272
|
? node.body
|
|
263
273
|
: node.body;
|
|
264
274
|
if (!body)
|
|
@@ -281,57 +291,95 @@ function analyzeFunctionComplexity(node, sourceFile, threshold) {
|
|
|
281
291
|
if (ts.isIfStatement(n)) {
|
|
282
292
|
cyclomatic++;
|
|
283
293
|
cognitive += 1 + nestingLevel; // Cognitive adds nesting penalty
|
|
284
|
-
contributors.push({
|
|
294
|
+
contributors.push({
|
|
295
|
+
type: "if",
|
|
296
|
+
line: line + 1,
|
|
297
|
+
contribution: 1 + nestingLevel,
|
|
298
|
+
});
|
|
285
299
|
}
|
|
286
300
|
else if (ts.isConditionalExpression(n)) {
|
|
287
301
|
cyclomatic++;
|
|
288
302
|
cognitive += 1 + nestingLevel;
|
|
289
|
-
contributors.push({
|
|
303
|
+
contributors.push({
|
|
304
|
+
type: "ternary",
|
|
305
|
+
line: line + 1,
|
|
306
|
+
contribution: 1 + nestingLevel,
|
|
307
|
+
});
|
|
290
308
|
}
|
|
291
|
-
else if (ts.isForStatement(n) ||
|
|
309
|
+
else if (ts.isForStatement(n) ||
|
|
310
|
+
ts.isForInStatement(n) ||
|
|
311
|
+
ts.isForOfStatement(n)) {
|
|
292
312
|
cyclomatic++;
|
|
293
313
|
cognitive += 1 + nestingLevel;
|
|
294
|
-
contributors.push({
|
|
314
|
+
contributors.push({
|
|
315
|
+
type: "for",
|
|
316
|
+
line: line + 1,
|
|
317
|
+
contribution: 1 + nestingLevel,
|
|
318
|
+
});
|
|
295
319
|
}
|
|
296
320
|
else if (ts.isWhileStatement(n)) {
|
|
297
321
|
cyclomatic++;
|
|
298
322
|
cognitive += 1 + nestingLevel;
|
|
299
|
-
contributors.push({
|
|
323
|
+
contributors.push({
|
|
324
|
+
type: "while",
|
|
325
|
+
line: line + 1,
|
|
326
|
+
contribution: 1 + nestingLevel,
|
|
327
|
+
});
|
|
300
328
|
}
|
|
301
329
|
else if (ts.isDoStatement(n)) {
|
|
302
330
|
cyclomatic++;
|
|
303
331
|
cognitive += 1 + nestingLevel;
|
|
304
|
-
contributors.push({
|
|
332
|
+
contributors.push({
|
|
333
|
+
type: "do",
|
|
334
|
+
line: line + 1,
|
|
335
|
+
contribution: 1 + nestingLevel,
|
|
336
|
+
});
|
|
305
337
|
}
|
|
306
338
|
else if (ts.isSwitchStatement(n)) {
|
|
307
339
|
cyclomatic++;
|
|
308
340
|
cognitive += 1 + nestingLevel;
|
|
309
|
-
contributors.push({
|
|
341
|
+
contributors.push({
|
|
342
|
+
type: "switch",
|
|
343
|
+
line: line + 1,
|
|
344
|
+
contribution: 1 + nestingLevel,
|
|
345
|
+
});
|
|
310
346
|
}
|
|
311
347
|
else if (ts.isCaseClause(n)) {
|
|
312
348
|
cyclomatic++;
|
|
313
|
-
contributors.push({ type:
|
|
349
|
+
contributors.push({ type: "case", line: line + 1, contribution: 1 });
|
|
314
350
|
}
|
|
315
351
|
else if (ts.isCatchClause(n)) {
|
|
316
352
|
cyclomatic++;
|
|
317
353
|
cognitive += 1 + nestingLevel;
|
|
318
|
-
contributors.push({
|
|
354
|
+
contributors.push({
|
|
355
|
+
type: "catch",
|
|
356
|
+
line: line + 1,
|
|
357
|
+
contribution: 1 + nestingLevel,
|
|
358
|
+
});
|
|
319
359
|
}
|
|
320
360
|
else if (ts.isBinaryExpression(n)) {
|
|
321
361
|
if (n.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken) {
|
|
322
362
|
cyclomatic++;
|
|
323
363
|
cognitive++;
|
|
324
|
-
contributors.push({
|
|
364
|
+
contributors.push({
|
|
365
|
+
type: "logical_and",
|
|
366
|
+
line: line + 1,
|
|
367
|
+
contribution: 1,
|
|
368
|
+
});
|
|
325
369
|
}
|
|
326
370
|
else if (n.operatorToken.kind === ts.SyntaxKind.BarBarToken) {
|
|
327
371
|
cyclomatic++;
|
|
328
372
|
cognitive++;
|
|
329
|
-
contributors.push({
|
|
373
|
+
contributors.push({
|
|
374
|
+
type: "logical_or",
|
|
375
|
+
line: line + 1,
|
|
376
|
+
contribution: 1,
|
|
377
|
+
});
|
|
330
378
|
}
|
|
331
379
|
else if (n.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
332
380
|
cyclomatic++;
|
|
333
381
|
cognitive++;
|
|
334
|
-
contributors.push({ type:
|
|
382
|
+
contributors.push({ type: "nullish", line: line + 1, contribution: 1 });
|
|
335
383
|
}
|
|
336
384
|
}
|
|
337
385
|
// Increase nesting for blocks
|
|
@@ -343,7 +391,7 @@ function analyzeFunctionComplexity(node, sourceFile, threshold) {
|
|
|
343
391
|
analyzeNode(body, 0);
|
|
344
392
|
// Calculate LOC
|
|
345
393
|
const bodyText = body.getText(sourceFile);
|
|
346
|
-
const bodyLines = bodyText.split(
|
|
394
|
+
const bodyLines = bodyText.split("\n").filter((l) => l.trim() !== "").length;
|
|
347
395
|
// Parameter count
|
|
348
396
|
const parameterCount = node.parameters.length;
|
|
349
397
|
const aboveThreshold = cyclomatic >= threshold;
|
|
@@ -379,7 +427,7 @@ function isNestingNode(node) {
|
|
|
379
427
|
*/
|
|
380
428
|
export function createGetComplexityTool(options) {
|
|
381
429
|
return defineTool({
|
|
382
|
-
name: options?.name ??
|
|
430
|
+
name: options?.name ?? "get_complexity",
|
|
383
431
|
description: options?.description ?? TOOL_DESCRIPTION,
|
|
384
432
|
inputSchema: TOOL_INPUT_SCHEMA,
|
|
385
433
|
execute: async (input) => {
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Analyze module-level dependencies across a directory.
|
|
5
5
|
* Builds a dependency graph and detects circular dependencies.
|
|
6
6
|
*/
|
|
7
|
-
import type { Tool } from
|
|
8
|
-
import type { GetDependencyGraphInput } from
|
|
7
|
+
import type { Tool } from "@compilr-dev/agents";
|
|
8
|
+
import type { GetDependencyGraphInput } from "./types.js";
|
|
9
9
|
/**
|
|
10
10
|
* getDependencyGraph tool - Analyze module dependencies
|
|
11
11
|
*/
|
|
@@ -4,50 +4,62 @@
|
|
|
4
4
|
* Analyze module-level dependencies across a directory.
|
|
5
5
|
* Builds a dependency graph and detects circular dependencies.
|
|
6
6
|
*/
|
|
7
|
-
import * as fs from
|
|
8
|
-
import * as fsSync from
|
|
9
|
-
import * as path from
|
|
10
|
-
import * as ts from
|
|
11
|
-
import { defineTool, createSuccessResult, createErrorResult } from
|
|
12
|
-
import { detectLanguage, isLanguageSupported } from
|
|
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
13
|
// Tool description
|
|
14
14
|
const TOOL_DESCRIPTION = `Analyze module-level dependencies across a directory.
|
|
15
15
|
Returns a dependency graph showing which modules import which other modules.
|
|
16
16
|
Detects circular dependencies and provides dependency statistics.`;
|
|
17
17
|
// Tool input schema
|
|
18
18
|
const TOOL_INPUT_SCHEMA = {
|
|
19
|
-
type:
|
|
19
|
+
type: "object",
|
|
20
20
|
properties: {
|
|
21
21
|
path: {
|
|
22
|
-
type:
|
|
23
|
-
description:
|
|
22
|
+
type: "string",
|
|
23
|
+
description: "Directory or file to analyze",
|
|
24
24
|
},
|
|
25
25
|
includeExternal: {
|
|
26
|
-
type:
|
|
27
|
-
description:
|
|
26
|
+
type: "boolean",
|
|
27
|
+
description: "Include external package dependencies (default: true)",
|
|
28
28
|
default: true,
|
|
29
29
|
},
|
|
30
30
|
includeTypeOnly: {
|
|
31
|
-
type:
|
|
32
|
-
description:
|
|
31
|
+
type: "boolean",
|
|
32
|
+
description: "Include type-only imports (default: true)",
|
|
33
33
|
default: true,
|
|
34
34
|
},
|
|
35
35
|
maxDepth: {
|
|
36
|
-
type:
|
|
37
|
-
description:
|
|
36
|
+
type: "number",
|
|
37
|
+
description: "Maximum depth for directory traversal (default: 10)",
|
|
38
38
|
default: 10,
|
|
39
39
|
},
|
|
40
40
|
},
|
|
41
|
-
required: [
|
|
41
|
+
required: ["path"],
|
|
42
42
|
};
|
|
43
43
|
// Default file patterns
|
|
44
|
-
const DEFAULT_INCLUDE = [
|
|
45
|
-
|
|
44
|
+
const DEFAULT_INCLUDE = [
|
|
45
|
+
"**/*.ts",
|
|
46
|
+
"**/*.tsx",
|
|
47
|
+
"**/*.js",
|
|
48
|
+
"**/*.jsx",
|
|
49
|
+
"**/*.mts",
|
|
50
|
+
"**/*.mjs",
|
|
51
|
+
];
|
|
52
|
+
const DEFAULT_EXCLUDE = [
|
|
53
|
+
"**/node_modules/**",
|
|
54
|
+
"**/dist/**",
|
|
55
|
+
"**/build/**",
|
|
56
|
+
"**/.git/**",
|
|
57
|
+
];
|
|
46
58
|
/**
|
|
47
59
|
* getDependencyGraph tool - Analyze module dependencies
|
|
48
60
|
*/
|
|
49
61
|
export const getDependencyGraphTool = defineTool({
|
|
50
|
-
name:
|
|
62
|
+
name: "get_dependency_graph",
|
|
51
63
|
description: TOOL_DESCRIPTION,
|
|
52
64
|
inputSchema: TOOL_INPUT_SCHEMA,
|
|
53
65
|
execute: executeGetDependencyGraph,
|
|
@@ -59,7 +71,7 @@ async function executeGetDependencyGraph(input) {
|
|
|
59
71
|
const { path: inputPath, includeExternal = true, includeTypeOnly = true, maxDepth = 10, include = DEFAULT_INCLUDE, exclude = DEFAULT_EXCLUDE, } = input;
|
|
60
72
|
// Validate input
|
|
61
73
|
if (!inputPath || inputPath.trim().length === 0) {
|
|
62
|
-
return createErrorResult(
|
|
74
|
+
return createErrorResult("Path is required");
|
|
63
75
|
}
|
|
64
76
|
try {
|
|
65
77
|
// Check if path exists
|
|
@@ -123,7 +135,8 @@ async function collectFiles(dir, include, exclude, maxDepth, currentDepth = 0) {
|
|
|
123
135
|
}
|
|
124
136
|
else if (entry.isFile()) {
|
|
125
137
|
// Check if file matches include patterns
|
|
126
|
-
if (matchesPattern(relativePath, include) &&
|
|
138
|
+
if (matchesPattern(relativePath, include) &&
|
|
139
|
+
isSupportedFile(fullPath)) {
|
|
127
140
|
files.push(fullPath);
|
|
128
141
|
}
|
|
129
142
|
}
|
|
@@ -141,14 +154,14 @@ function matchesPattern(filePath, patterns) {
|
|
|
141
154
|
const fileName = path.basename(filePath);
|
|
142
155
|
for (const pattern of patterns) {
|
|
143
156
|
// Handle simple glob patterns
|
|
144
|
-
if (pattern.includes(
|
|
157
|
+
if (pattern.includes("**")) {
|
|
145
158
|
// Check if any directory in path matches
|
|
146
|
-
const patternDir = pattern.replace(
|
|
147
|
-
if (filePath.includes(patternDir.replace(
|
|
159
|
+
const patternDir = pattern.replace("**/", "").replace("/**", "");
|
|
160
|
+
if (filePath.includes(patternDir.replace("*", ""))) {
|
|
148
161
|
return true;
|
|
149
162
|
}
|
|
150
163
|
}
|
|
151
|
-
else if (pattern.startsWith(
|
|
164
|
+
else if (pattern.startsWith("*.")) {
|
|
152
165
|
// Extension pattern
|
|
153
166
|
const ext = pattern.substring(1);
|
|
154
167
|
if (fileName.endsWith(ext)) {
|
|
@@ -198,15 +211,15 @@ async function analyzeDependencies(files, rootPath, includeExternal, includeType
|
|
|
198
211
|
continue;
|
|
199
212
|
}
|
|
200
213
|
// Determine if external
|
|
201
|
-
const isExternal = !imp.source.startsWith(
|
|
214
|
+
const isExternal = !imp.source.startsWith(".") && !imp.source.startsWith("/");
|
|
202
215
|
if (isExternal) {
|
|
203
216
|
if (!includeExternal) {
|
|
204
217
|
continue;
|
|
205
218
|
}
|
|
206
219
|
// Extract package name (handle scoped packages)
|
|
207
|
-
const packageName = imp.source.startsWith(
|
|
208
|
-
? imp.source.split(
|
|
209
|
-
: imp.source.split(
|
|
220
|
+
const packageName = imp.source.startsWith("@")
|
|
221
|
+
? imp.source.split("/").slice(0, 2).join("/")
|
|
222
|
+
: imp.source.split("/")[0];
|
|
210
223
|
externalPackages.add(packageName);
|
|
211
224
|
// Add external module node
|
|
212
225
|
if (!modules.has(packageName)) {
|
|
@@ -300,7 +313,7 @@ async function analyzeDependencies(files, rootPath, includeExternal, includeType
|
|
|
300
313
|
async function extractImports(filePath) {
|
|
301
314
|
const imports = [];
|
|
302
315
|
try {
|
|
303
|
-
const sourceCode = await fs.readFile(filePath,
|
|
316
|
+
const sourceCode = await fs.readFile(filePath, "utf-8");
|
|
304
317
|
const scriptKind = getScriptKind(filePath);
|
|
305
318
|
const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest, true, scriptKind);
|
|
306
319
|
ts.forEachChild(sourceFile, (node) => {
|
|
@@ -310,24 +323,25 @@ async function extractImports(filePath) {
|
|
|
310
323
|
const source = node.moduleSpecifier.text;
|
|
311
324
|
// Check for type-only import by looking at the import text
|
|
312
325
|
const importText = node.getText(sourceFile);
|
|
313
|
-
const typeOnly = importText.includes(
|
|
326
|
+
const typeOnly = importText.includes("import type");
|
|
314
327
|
const symbols = [];
|
|
315
|
-
let importType =
|
|
328
|
+
let importType = "side-effect";
|
|
316
329
|
if (node.importClause) {
|
|
317
330
|
// Default import
|
|
318
331
|
if (node.importClause.name) {
|
|
319
|
-
importType =
|
|
332
|
+
importType = "default";
|
|
320
333
|
symbols.push(node.importClause.name.text);
|
|
321
334
|
}
|
|
322
335
|
// Named or namespace imports
|
|
323
336
|
if (node.importClause.namedBindings) {
|
|
324
337
|
if (ts.isNamespaceImport(node.importClause.namedBindings)) {
|
|
325
|
-
importType =
|
|
338
|
+
importType = "namespace";
|
|
326
339
|
symbols.push(node.importClause.namedBindings.name.text);
|
|
327
340
|
}
|
|
328
341
|
else if (ts.isNamedImports(node.importClause.namedBindings)) {
|
|
329
|
-
importType = importType ===
|
|
330
|
-
for (const element of node.importClause.namedBindings
|
|
342
|
+
importType = importType === "default" ? "default" : "named";
|
|
343
|
+
for (const element of node.importClause.namedBindings
|
|
344
|
+
.elements) {
|
|
331
345
|
symbols.push(element.name.text);
|
|
332
346
|
}
|
|
333
347
|
}
|
|
@@ -342,9 +356,9 @@ async function extractImports(filePath) {
|
|
|
342
356
|
const source = node.moduleSpecifier.text;
|
|
343
357
|
const typeOnly = node.isTypeOnly;
|
|
344
358
|
const symbols = [];
|
|
345
|
-
let importType =
|
|
359
|
+
let importType = "namespace";
|
|
346
360
|
if (node.exportClause && ts.isNamedExports(node.exportClause)) {
|
|
347
|
-
importType =
|
|
361
|
+
importType = "named";
|
|
348
362
|
for (const element of node.exportClause.elements) {
|
|
349
363
|
symbols.push(element.name.text);
|
|
350
364
|
}
|
|
@@ -366,7 +380,7 @@ function resolveImportPath(fromFile, importSource, _rootPath) {
|
|
|
366
380
|
const fromDir = path.dirname(fromFile);
|
|
367
381
|
const basePath = path.resolve(fromDir, importSource);
|
|
368
382
|
// Try various extensions
|
|
369
|
-
const extensions = [
|
|
383
|
+
const extensions = [".ts", ".tsx", ".js", ".jsx", ".mts", ".mjs", ""];
|
|
370
384
|
for (const ext of extensions) {
|
|
371
385
|
const candidate = basePath + ext;
|
|
372
386
|
try {
|
|
@@ -377,7 +391,7 @@ function resolveImportPath(fromFile, importSource, _rootPath) {
|
|
|
377
391
|
}
|
|
378
392
|
catch {
|
|
379
393
|
// Try index file
|
|
380
|
-
const indexCandidate = path.join(basePath, `index${ext ||
|
|
394
|
+
const indexCandidate = path.join(basePath, `index${ext || ".ts"}`);
|
|
381
395
|
try {
|
|
382
396
|
fsSync.accessSync(indexCandidate);
|
|
383
397
|
return indexCandidate;
|
|
@@ -404,8 +418,8 @@ function detectCircularDependencies(adjacency) {
|
|
|
404
418
|
if (cycleStart !== -1) {
|
|
405
419
|
const cycle = [...path.slice(cycleStart), node];
|
|
406
420
|
// Avoid duplicate cycles
|
|
407
|
-
const cycleKey = [...cycle].sort().join(
|
|
408
|
-
if (!cycles.some((c) => [...c.cycle].sort().join(
|
|
421
|
+
const cycleKey = [...cycle].sort().join("->");
|
|
422
|
+
if (!cycles.some((c) => [...c.cycle].sort().join("->") === cycleKey)) {
|
|
409
423
|
cycles.push({
|
|
410
424
|
cycle,
|
|
411
425
|
length: cycle.length - 1,
|
|
@@ -441,21 +455,21 @@ function detectCircularDependencies(adjacency) {
|
|
|
441
455
|
* Get TypeScript script kind from file extension
|
|
442
456
|
*/
|
|
443
457
|
function getScriptKind(filePath) {
|
|
444
|
-
const ext = filePath.toLowerCase().split(
|
|
458
|
+
const ext = filePath.toLowerCase().split(".").pop();
|
|
445
459
|
switch (ext) {
|
|
446
|
-
case
|
|
460
|
+
case "ts":
|
|
447
461
|
return ts.ScriptKind.TS;
|
|
448
|
-
case
|
|
462
|
+
case "tsx":
|
|
449
463
|
return ts.ScriptKind.TSX;
|
|
450
|
-
case
|
|
464
|
+
case "js":
|
|
451
465
|
return ts.ScriptKind.JS;
|
|
452
|
-
case
|
|
466
|
+
case "jsx":
|
|
453
467
|
return ts.ScriptKind.JSX;
|
|
454
|
-
case
|
|
455
|
-
case
|
|
468
|
+
case "mts":
|
|
469
|
+
case "cts":
|
|
456
470
|
return ts.ScriptKind.TS;
|
|
457
|
-
case
|
|
458
|
-
case
|
|
471
|
+
case "mjs":
|
|
472
|
+
case "cjs":
|
|
459
473
|
return ts.ScriptKind.JS;
|
|
460
474
|
default:
|
|
461
475
|
return ts.ScriptKind.TS;
|
|
@@ -467,7 +481,7 @@ function getScriptKind(filePath) {
|
|
|
467
481
|
export function createGetDependencyGraphTool(options) {
|
|
468
482
|
const { defaultIncludeExternal = true, defaultIncludeTypeOnly = true, defaultMaxDepth = 10, } = options ?? {};
|
|
469
483
|
return defineTool({
|
|
470
|
-
name:
|
|
484
|
+
name: "get_dependency_graph",
|
|
471
485
|
description: TOOL_DESCRIPTION,
|
|
472
486
|
inputSchema: TOOL_INPUT_SCHEMA,
|
|
473
487
|
execute: async (input) => {
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Extract JSDoc/TSDoc documentation from source files.
|
|
5
5
|
* Provides documentation coverage metrics and identifies undocumented exports.
|
|
6
6
|
*/
|
|
7
|
-
import type { Tool } from
|
|
8
|
-
import type { GetDocumentationInput } from
|
|
7
|
+
import type { Tool } from "@compilr-dev/agents";
|
|
8
|
+
import type { GetDocumentationInput } from "./types.js";
|
|
9
9
|
/**
|
|
10
10
|
* getDocumentation tool
|
|
11
11
|
*/
|