@aiready/cli 0.4.5 → 0.5.1
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/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-test.log +4 -4
- package/dist/cli.js +179 -19
- package/dist/cli.mjs +179 -19
- package/package.json +4 -4
- package/src/cli.ts +207 -20
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
> @aiready/cli@0.
|
|
3
|
+
> @aiready/cli@0.5.1 build /Users/pengcao/projects/aiready/packages/cli
|
|
4
4
|
> tsup src/index.ts src/cli.ts --format cjs,esm --dts
|
|
5
5
|
|
|
6
6
|
[34mCLI[39m Building entry: src/cli.ts, src/index.ts
|
|
@@ -9,15 +9,15 @@
|
|
|
9
9
|
[34mCLI[39m Target: es2020
|
|
10
10
|
[34mCJS[39m Build start
|
|
11
11
|
[34mESM[39m Build start
|
|
12
|
-
[32mCJS[39m [1mdist/cli.js [22m[32m16.87 KB[39m
|
|
13
|
-
[32mCJS[39m [1mdist/index.js [22m[32m3.15 KB[39m
|
|
14
|
-
[32mCJS[39m ⚡️ Build success in 53ms
|
|
15
|
-
[32mESM[39m [1mdist/chunk-VOB7SA3E.mjs [22m[32m2.02 KB[39m
|
|
16
|
-
[32mESM[39m [1mdist/cli.mjs [22m[32m13.23 KB[39m
|
|
17
12
|
[32mESM[39m [1mdist/index.mjs [22m[32m138.00 B[39m
|
|
18
|
-
[32mESM[39m
|
|
13
|
+
[32mESM[39m [1mdist/cli.mjs [22m[32m21.95 KB[39m
|
|
14
|
+
[32mESM[39m [1mdist/chunk-VOB7SA3E.mjs [22m[32m2.02 KB[39m
|
|
15
|
+
[32mESM[39m ⚡️ Build success in 56ms
|
|
16
|
+
[32mCJS[39m [1mdist/cli.js [22m[32m27.02 KB[39m
|
|
17
|
+
[32mCJS[39m [1mdist/index.js [22m[32m3.15 KB[39m
|
|
18
|
+
[32mCJS[39m ⚡️ Build success in 56ms
|
|
19
19
|
DTS Build start
|
|
20
|
-
DTS ⚡️ Build success in
|
|
20
|
+
DTS ⚡️ Build success in 518ms
|
|
21
21
|
DTS dist/cli.d.ts 20.00 B
|
|
22
22
|
DTS dist/index.d.ts 991.00 B
|
|
23
23
|
DTS dist/cli.d.mts 20.00 B
|
package/.turbo/turbo-test.log
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
> @aiready/cli@0.
|
|
3
|
+
> @aiready/cli@0.5.1 test /Users/pengcao/projects/aiready/packages/cli
|
|
4
4
|
> vitest run
|
|
5
5
|
|
|
6
6
|
[?25l
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
[2m Test Files [22m[1m[32m0 passed[39m[22m[90m (1)[39m
|
|
13
13
|
[2m Tests [22m[1m[32m0 passed[39m[22m[90m (0)[39m
|
|
14
|
-
[2m Start at [
|
|
14
|
+
[2m Start at [22m08:28:00
|
|
15
15
|
[2m Duration [22m0ms
|
|
16
16
|
[?2026l[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K [32m✓[39m src/__tests__/cli.test.ts [2m([22m[2m3 tests[22m[2m)[22m[32m 2[2mms[22m[39m
|
|
17
17
|
[32m✓[39m CLI Unified Analysis [2m(3)[22m
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
[2m Test Files [22m [1m[32m1 passed[39m[22m[90m (1)[39m
|
|
23
23
|
[2m Tests [22m [1m[32m3 passed[39m[22m[90m (3)[39m
|
|
24
|
-
[2m Start at [22m
|
|
25
|
-
[2m Duration [22m
|
|
24
|
+
[2m Start at [22m 08:28:00
|
|
25
|
+
[2m Duration [22m 144ms[2m (transform 44ms, setup 0ms, import 66ms, tests 2ms, environment 0ms)[22m
|
|
26
26
|
|
|
27
27
|
[?25h
|
package/dist/cli.js
CHANGED
|
@@ -184,7 +184,7 @@ program.command("patterns").description("Run pattern detection analysis").argume
|
|
|
184
184
|
}
|
|
185
185
|
const finalOptions = (0, import_core.loadMergedConfig)(directory, defaults, cliOptions);
|
|
186
186
|
const { analyzePatterns: analyzePatterns2, generateSummary } = await import("@aiready/pattern-detect");
|
|
187
|
-
const { results } = await analyzePatterns2(finalOptions);
|
|
187
|
+
const { results, duplicates } = await analyzePatterns2(finalOptions);
|
|
188
188
|
const elapsedTime = (0, import_core.getElapsedTime)(startTime);
|
|
189
189
|
const summary = generateSummary(results);
|
|
190
190
|
const outputFormat = options.output || finalOptions.output?.format || "console";
|
|
@@ -201,9 +201,43 @@ program.command("patterns").description("Run pattern detection analysis").argume
|
|
|
201
201
|
);
|
|
202
202
|
(0, import_core.handleJSONOutput)(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
|
|
203
203
|
} else {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
204
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
205
|
+
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
206
|
+
const divider = "\u2501".repeat(dividerWidth);
|
|
207
|
+
console.log(import_chalk.default.cyan(divider));
|
|
208
|
+
console.log(import_chalk.default.bold.white(" PATTERN ANALYSIS SUMMARY"));
|
|
209
|
+
console.log(import_chalk.default.cyan(divider) + "\n");
|
|
210
|
+
console.log(import_chalk.default.white(`\u{1F4C1} Files analyzed: ${import_chalk.default.bold(results.length)}`));
|
|
211
|
+
console.log(import_chalk.default.yellow(`\u26A0 Duplicate patterns found: ${import_chalk.default.bold(summary.totalPatterns)}`));
|
|
212
|
+
console.log(import_chalk.default.red(`\u{1F4B0} Token cost (wasted): ${import_chalk.default.bold(summary.totalTokenCost.toLocaleString())}`));
|
|
213
|
+
console.log(import_chalk.default.gray(`\u23F1 Analysis time: ${import_chalk.default.bold(elapsedTime + "s")}`));
|
|
214
|
+
const sortedTypes = Object.entries(summary.patternsByType || {}).filter(([, count]) => count > 0).sort(([, a], [, b]) => b - a);
|
|
215
|
+
if (sortedTypes.length > 0) {
|
|
216
|
+
console.log(import_chalk.default.cyan("\n" + divider));
|
|
217
|
+
console.log(import_chalk.default.bold.white(" PATTERNS BY TYPE"));
|
|
218
|
+
console.log(import_chalk.default.cyan(divider) + "\n");
|
|
219
|
+
sortedTypes.forEach(([type, count]) => {
|
|
220
|
+
console.log(` ${import_chalk.default.white(type.padEnd(15))} ${import_chalk.default.bold(count)}`);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
if (summary.totalPatterns > 0 && duplicates.length > 0) {
|
|
224
|
+
console.log(import_chalk.default.cyan("\n" + divider));
|
|
225
|
+
console.log(import_chalk.default.bold.white(" TOP DUPLICATE PATTERNS"));
|
|
226
|
+
console.log(import_chalk.default.cyan(divider) + "\n");
|
|
227
|
+
const topDuplicates = [...duplicates].sort((a, b) => b.similarity - a.similarity).slice(0, 10);
|
|
228
|
+
topDuplicates.forEach((dup) => {
|
|
229
|
+
const severity = dup.similarity > 0.95 ? "CRITICAL" : dup.similarity > 0.9 ? "HIGH" : "MEDIUM";
|
|
230
|
+
const severityIcon = dup.similarity > 0.95 ? "\u{1F534}" : dup.similarity > 0.9 ? "\u{1F7E1}" : "\u{1F535}";
|
|
231
|
+
const file1Name = dup.file1.split("/").pop() || dup.file1;
|
|
232
|
+
const file2Name = dup.file2.split("/").pop() || dup.file2;
|
|
233
|
+
console.log(`${severityIcon} ${severity}: ${import_chalk.default.bold(file1Name)} \u2194 ${import_chalk.default.bold(file2Name)}`);
|
|
234
|
+
console.log(` Similarity: ${import_chalk.default.bold(Math.round(dup.similarity * 100) + "%")} | Wasted: ${import_chalk.default.bold(dup.tokenCost.toLocaleString())} tokens each`);
|
|
235
|
+
console.log(` Lines: ${import_chalk.default.cyan(dup.line1 + "-" + dup.endLine1)} \u2194 ${import_chalk.default.cyan(dup.line2 + "-" + dup.endLine2)}
|
|
236
|
+
`);
|
|
237
|
+
});
|
|
238
|
+
} else {
|
|
239
|
+
console.log(import_chalk.default.green("\n\u2728 Great! No duplicate patterns detected.\n"));
|
|
240
|
+
}
|
|
207
241
|
}
|
|
208
242
|
} catch (error) {
|
|
209
243
|
(0, import_core.handleCLIError)(error, "Pattern analysis");
|
|
@@ -258,11 +292,77 @@ program.command("context").description("Run context window cost analysis").argum
|
|
|
258
292
|
);
|
|
259
293
|
(0, import_core.handleJSONOutput)(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
|
|
260
294
|
} else {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
console.log(
|
|
265
|
-
console.log(
|
|
295
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
296
|
+
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
297
|
+
const divider = "\u2501".repeat(dividerWidth);
|
|
298
|
+
console.log(import_chalk.default.cyan(divider));
|
|
299
|
+
console.log(import_chalk.default.bold.white(" CONTEXT ANALYSIS SUMMARY"));
|
|
300
|
+
console.log(import_chalk.default.cyan(divider) + "\n");
|
|
301
|
+
console.log(import_chalk.default.white(`\u{1F4C1} Files analyzed: ${import_chalk.default.bold(summary.totalFiles)}`));
|
|
302
|
+
console.log(import_chalk.default.white(`\u{1F4CA} Total tokens: ${import_chalk.default.bold(summary.totalTokens.toLocaleString())}`));
|
|
303
|
+
console.log(import_chalk.default.yellow(`\u{1F4B0} Avg context budget: ${import_chalk.default.bold(summary.avgContextBudget.toFixed(0))} tokens/file`));
|
|
304
|
+
console.log(import_chalk.default.white(`\u23F1 Analysis time: ${import_chalk.default.bold(elapsedTime + "s")}
|
|
305
|
+
`));
|
|
306
|
+
const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
|
|
307
|
+
if (totalIssues > 0) {
|
|
308
|
+
console.log(import_chalk.default.bold("\u26A0\uFE0F Issues Found:\n"));
|
|
309
|
+
if (summary.criticalIssues > 0) {
|
|
310
|
+
console.log(import_chalk.default.red(` \u{1F534} Critical: ${import_chalk.default.bold(summary.criticalIssues)}`));
|
|
311
|
+
}
|
|
312
|
+
if (summary.majorIssues > 0) {
|
|
313
|
+
console.log(import_chalk.default.yellow(` \u{1F7E1} Major: ${import_chalk.default.bold(summary.majorIssues)}`));
|
|
314
|
+
}
|
|
315
|
+
if (summary.minorIssues > 0) {
|
|
316
|
+
console.log(import_chalk.default.blue(` \u{1F535} Minor: ${import_chalk.default.bold(summary.minorIssues)}`));
|
|
317
|
+
}
|
|
318
|
+
console.log(import_chalk.default.green(`
|
|
319
|
+
\u{1F4A1} Potential savings: ${import_chalk.default.bold(summary.totalPotentialSavings.toLocaleString())} tokens
|
|
320
|
+
`));
|
|
321
|
+
} else {
|
|
322
|
+
console.log(import_chalk.default.green("\u2705 No significant issues found!\n"));
|
|
323
|
+
}
|
|
324
|
+
if (summary.deepFiles.length > 0) {
|
|
325
|
+
console.log(import_chalk.default.bold("\u{1F4CF} Deep Import Chains:\n"));
|
|
326
|
+
console.log(import_chalk.default.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`));
|
|
327
|
+
console.log(import_chalk.default.gray(` Maximum depth: ${summary.maxImportDepth}
|
|
328
|
+
`));
|
|
329
|
+
summary.deepFiles.slice(0, 10).forEach((item) => {
|
|
330
|
+
const fileName = item.file.split("/").slice(-2).join("/");
|
|
331
|
+
console.log(` ${import_chalk.default.cyan("\u2192")} ${import_chalk.default.white(fileName)} ${import_chalk.default.dim(`(depth: ${item.depth})`)}`);
|
|
332
|
+
});
|
|
333
|
+
console.log();
|
|
334
|
+
}
|
|
335
|
+
if (summary.fragmentedModules.length > 0) {
|
|
336
|
+
console.log(import_chalk.default.bold("\u{1F9E9} Fragmented Modules:\n"));
|
|
337
|
+
console.log(import_chalk.default.gray(` Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(0)}%
|
|
338
|
+
`));
|
|
339
|
+
summary.fragmentedModules.slice(0, 10).forEach((module2) => {
|
|
340
|
+
console.log(` ${import_chalk.default.yellow("\u25CF")} ${import_chalk.default.white(module2.domain)} - ${import_chalk.default.dim(`${module2.files.length} files, ${(module2.fragmentationScore * 100).toFixed(0)}% scattered`)}`);
|
|
341
|
+
console.log(import_chalk.default.dim(` Token cost: ${module2.totalTokens.toLocaleString()}, Cohesion: ${(module2.avgCohesion * 100).toFixed(0)}%`));
|
|
342
|
+
});
|
|
343
|
+
console.log();
|
|
344
|
+
}
|
|
345
|
+
if (summary.lowCohesionFiles.length > 0) {
|
|
346
|
+
console.log(import_chalk.default.bold("\u{1F500} Low Cohesion Files:\n"));
|
|
347
|
+
console.log(import_chalk.default.gray(` Average cohesion: ${(summary.avgCohesion * 100).toFixed(0)}%
|
|
348
|
+
`));
|
|
349
|
+
summary.lowCohesionFiles.slice(0, 10).forEach((item) => {
|
|
350
|
+
const fileName = item.file.split("/").slice(-2).join("/");
|
|
351
|
+
const scorePercent = (item.score * 100).toFixed(0);
|
|
352
|
+
const color = item.score < 0.4 ? import_chalk.default.red : import_chalk.default.yellow;
|
|
353
|
+
console.log(` ${color("\u25CB")} ${import_chalk.default.white(fileName)} ${import_chalk.default.dim(`(${scorePercent}% cohesion)`)}`);
|
|
354
|
+
});
|
|
355
|
+
console.log();
|
|
356
|
+
}
|
|
357
|
+
if (summary.topExpensiveFiles.length > 0) {
|
|
358
|
+
console.log(import_chalk.default.bold("\u{1F4B8} Most Expensive Files (Context Budget):\n"));
|
|
359
|
+
summary.topExpensiveFiles.slice(0, 10).forEach((item) => {
|
|
360
|
+
const fileName = item.file.split("/").slice(-2).join("/");
|
|
361
|
+
const severityColor = item.severity === "critical" ? import_chalk.default.red : item.severity === "major" ? import_chalk.default.yellow : import_chalk.default.blue;
|
|
362
|
+
console.log(` ${severityColor("\u25CF")} ${import_chalk.default.white(fileName)} ${import_chalk.default.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`);
|
|
363
|
+
});
|
|
364
|
+
console.log();
|
|
365
|
+
}
|
|
266
366
|
}
|
|
267
367
|
} catch (error) {
|
|
268
368
|
(0, import_core.handleCLIError)(error, "Context analysis");
|
|
@@ -319,16 +419,76 @@ program.command("consistency").description("Check naming, patterns, and architec
|
|
|
319
419
|
(0, import_fs.writeFileSync)(outputPath, markdown);
|
|
320
420
|
console.log(import_chalk.default.green(`\u2705 Report saved to ${outputPath}`));
|
|
321
421
|
} else {
|
|
322
|
-
console.log(
|
|
323
|
-
console.log(`Files
|
|
324
|
-
console.log(`Total
|
|
325
|
-
console.log(` Naming: ${report.summary.namingIssues}`);
|
|
326
|
-
console.log(` Patterns: ${report.summary.patternIssues}`);
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
422
|
+
console.log(import_chalk.default.bold("\n\u{1F4CA} Summary\n"));
|
|
423
|
+
console.log(`Files Analyzed: ${import_chalk.default.cyan(report.summary.filesAnalyzed)}`);
|
|
424
|
+
console.log(`Total Issues: ${import_chalk.default.yellow(report.summary.totalIssues)}`);
|
|
425
|
+
console.log(` Naming: ${import_chalk.default.yellow(report.summary.namingIssues)}`);
|
|
426
|
+
console.log(` Patterns: ${import_chalk.default.yellow(report.summary.patternIssues)}`);
|
|
427
|
+
console.log(` Architecture: ${import_chalk.default.yellow(report.summary.architectureIssues || 0)}`);
|
|
428
|
+
console.log(`Analysis Time: ${import_chalk.default.gray(elapsedTime + "s")}
|
|
429
|
+
`);
|
|
430
|
+
if (report.summary.totalIssues === 0) {
|
|
431
|
+
console.log(import_chalk.default.green("\u2728 No consistency issues found! Your codebase is well-maintained.\n"));
|
|
432
|
+
} else {
|
|
433
|
+
const namingResults = report.results.filter(
|
|
434
|
+
(r) => r.issues.some((i) => i.category === "naming")
|
|
435
|
+
);
|
|
436
|
+
const patternResults = report.results.filter(
|
|
437
|
+
(r) => r.issues.some((i) => i.category === "patterns")
|
|
438
|
+
);
|
|
439
|
+
if (namingResults.length > 0) {
|
|
440
|
+
console.log(import_chalk.default.bold("\u{1F3F7}\uFE0F Naming Issues\n"));
|
|
441
|
+
let shown = 0;
|
|
442
|
+
for (const result of namingResults) {
|
|
443
|
+
if (shown >= 5) break;
|
|
444
|
+
for (const issue of result.issues) {
|
|
445
|
+
if (shown >= 5) break;
|
|
446
|
+
const severityColor = issue.severity === "critical" ? import_chalk.default.red : issue.severity === "major" ? import_chalk.default.yellow : issue.severity === "minor" ? import_chalk.default.blue : import_chalk.default.gray;
|
|
447
|
+
console.log(`${severityColor(issue.severity.toUpperCase())} ${import_chalk.default.dim(`${issue.location.file}:${issue.location.line}`)}`);
|
|
448
|
+
console.log(` ${issue.message}`);
|
|
449
|
+
if (issue.suggestion) {
|
|
450
|
+
console.log(` ${import_chalk.default.dim("\u2192")} ${import_chalk.default.italic(issue.suggestion)}`);
|
|
451
|
+
}
|
|
452
|
+
console.log();
|
|
453
|
+
shown++;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
const remaining = namingResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
457
|
+
if (remaining > 0) {
|
|
458
|
+
console.log(import_chalk.default.dim(` ... and ${remaining} more issues
|
|
459
|
+
`));
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
if (patternResults.length > 0) {
|
|
463
|
+
console.log(import_chalk.default.bold("\u{1F504} Pattern Issues\n"));
|
|
464
|
+
let shown = 0;
|
|
465
|
+
for (const result of patternResults) {
|
|
466
|
+
if (shown >= 5) break;
|
|
467
|
+
for (const issue of result.issues) {
|
|
468
|
+
if (shown >= 5) break;
|
|
469
|
+
const severityColor = issue.severity === "critical" ? import_chalk.default.red : issue.severity === "major" ? import_chalk.default.yellow : issue.severity === "minor" ? import_chalk.default.blue : import_chalk.default.gray;
|
|
470
|
+
console.log(`${severityColor(issue.severity.toUpperCase())} ${import_chalk.default.dim(`${issue.location.file}:${issue.location.line}`)}`);
|
|
471
|
+
console.log(` ${issue.message}`);
|
|
472
|
+
if (issue.suggestion) {
|
|
473
|
+
console.log(` ${import_chalk.default.dim("\u2192")} ${import_chalk.default.italic(issue.suggestion)}`);
|
|
474
|
+
}
|
|
475
|
+
console.log();
|
|
476
|
+
shown++;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
const remaining = patternResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
480
|
+
if (remaining > 0) {
|
|
481
|
+
console.log(import_chalk.default.dim(` ... and ${remaining} more issues
|
|
482
|
+
`));
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
if (report.recommendations.length > 0) {
|
|
486
|
+
console.log(import_chalk.default.bold("\u{1F4A1} Recommendations\n"));
|
|
487
|
+
report.recommendations.forEach((rec, i) => {
|
|
488
|
+
console.log(`${i + 1}. ${rec}`);
|
|
489
|
+
});
|
|
490
|
+
console.log();
|
|
491
|
+
}
|
|
332
492
|
}
|
|
333
493
|
}
|
|
334
494
|
} catch (error) {
|
package/dist/cli.mjs
CHANGED
|
@@ -96,7 +96,7 @@ program.command("patterns").description("Run pattern detection analysis").argume
|
|
|
96
96
|
}
|
|
97
97
|
const finalOptions = loadMergedConfig(directory, defaults, cliOptions);
|
|
98
98
|
const { analyzePatterns, generateSummary } = await import("@aiready/pattern-detect");
|
|
99
|
-
const { results } = await analyzePatterns(finalOptions);
|
|
99
|
+
const { results, duplicates } = await analyzePatterns(finalOptions);
|
|
100
100
|
const elapsedTime = getElapsedTime(startTime);
|
|
101
101
|
const summary = generateSummary(results);
|
|
102
102
|
const outputFormat = options.output || finalOptions.output?.format || "console";
|
|
@@ -113,9 +113,43 @@ program.command("patterns").description("Run pattern detection analysis").argume
|
|
|
113
113
|
);
|
|
114
114
|
handleJSONOutput(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
|
|
115
115
|
} else {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
117
|
+
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
118
|
+
const divider = "\u2501".repeat(dividerWidth);
|
|
119
|
+
console.log(chalk.cyan(divider));
|
|
120
|
+
console.log(chalk.bold.white(" PATTERN ANALYSIS SUMMARY"));
|
|
121
|
+
console.log(chalk.cyan(divider) + "\n");
|
|
122
|
+
console.log(chalk.white(`\u{1F4C1} Files analyzed: ${chalk.bold(results.length)}`));
|
|
123
|
+
console.log(chalk.yellow(`\u26A0 Duplicate patterns found: ${chalk.bold(summary.totalPatterns)}`));
|
|
124
|
+
console.log(chalk.red(`\u{1F4B0} Token cost (wasted): ${chalk.bold(summary.totalTokenCost.toLocaleString())}`));
|
|
125
|
+
console.log(chalk.gray(`\u23F1 Analysis time: ${chalk.bold(elapsedTime + "s")}`));
|
|
126
|
+
const sortedTypes = Object.entries(summary.patternsByType || {}).filter(([, count]) => count > 0).sort(([, a], [, b]) => b - a);
|
|
127
|
+
if (sortedTypes.length > 0) {
|
|
128
|
+
console.log(chalk.cyan("\n" + divider));
|
|
129
|
+
console.log(chalk.bold.white(" PATTERNS BY TYPE"));
|
|
130
|
+
console.log(chalk.cyan(divider) + "\n");
|
|
131
|
+
sortedTypes.forEach(([type, count]) => {
|
|
132
|
+
console.log(` ${chalk.white(type.padEnd(15))} ${chalk.bold(count)}`);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
if (summary.totalPatterns > 0 && duplicates.length > 0) {
|
|
136
|
+
console.log(chalk.cyan("\n" + divider));
|
|
137
|
+
console.log(chalk.bold.white(" TOP DUPLICATE PATTERNS"));
|
|
138
|
+
console.log(chalk.cyan(divider) + "\n");
|
|
139
|
+
const topDuplicates = [...duplicates].sort((a, b) => b.similarity - a.similarity).slice(0, 10);
|
|
140
|
+
topDuplicates.forEach((dup) => {
|
|
141
|
+
const severity = dup.similarity > 0.95 ? "CRITICAL" : dup.similarity > 0.9 ? "HIGH" : "MEDIUM";
|
|
142
|
+
const severityIcon = dup.similarity > 0.95 ? "\u{1F534}" : dup.similarity > 0.9 ? "\u{1F7E1}" : "\u{1F535}";
|
|
143
|
+
const file1Name = dup.file1.split("/").pop() || dup.file1;
|
|
144
|
+
const file2Name = dup.file2.split("/").pop() || dup.file2;
|
|
145
|
+
console.log(`${severityIcon} ${severity}: ${chalk.bold(file1Name)} \u2194 ${chalk.bold(file2Name)}`);
|
|
146
|
+
console.log(` Similarity: ${chalk.bold(Math.round(dup.similarity * 100) + "%")} | Wasted: ${chalk.bold(dup.tokenCost.toLocaleString())} tokens each`);
|
|
147
|
+
console.log(` Lines: ${chalk.cyan(dup.line1 + "-" + dup.endLine1)} \u2194 ${chalk.cyan(dup.line2 + "-" + dup.endLine2)}
|
|
148
|
+
`);
|
|
149
|
+
});
|
|
150
|
+
} else {
|
|
151
|
+
console.log(chalk.green("\n\u2728 Great! No duplicate patterns detected.\n"));
|
|
152
|
+
}
|
|
119
153
|
}
|
|
120
154
|
} catch (error) {
|
|
121
155
|
handleCLIError(error, "Pattern analysis");
|
|
@@ -170,11 +204,77 @@ program.command("context").description("Run context window cost analysis").argum
|
|
|
170
204
|
);
|
|
171
205
|
handleJSONOutput(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
|
|
172
206
|
} else {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
console.log(
|
|
177
|
-
console.log(
|
|
207
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
208
|
+
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
209
|
+
const divider = "\u2501".repeat(dividerWidth);
|
|
210
|
+
console.log(chalk.cyan(divider));
|
|
211
|
+
console.log(chalk.bold.white(" CONTEXT ANALYSIS SUMMARY"));
|
|
212
|
+
console.log(chalk.cyan(divider) + "\n");
|
|
213
|
+
console.log(chalk.white(`\u{1F4C1} Files analyzed: ${chalk.bold(summary.totalFiles)}`));
|
|
214
|
+
console.log(chalk.white(`\u{1F4CA} Total tokens: ${chalk.bold(summary.totalTokens.toLocaleString())}`));
|
|
215
|
+
console.log(chalk.yellow(`\u{1F4B0} Avg context budget: ${chalk.bold(summary.avgContextBudget.toFixed(0))} tokens/file`));
|
|
216
|
+
console.log(chalk.white(`\u23F1 Analysis time: ${chalk.bold(elapsedTime + "s")}
|
|
217
|
+
`));
|
|
218
|
+
const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
|
|
219
|
+
if (totalIssues > 0) {
|
|
220
|
+
console.log(chalk.bold("\u26A0\uFE0F Issues Found:\n"));
|
|
221
|
+
if (summary.criticalIssues > 0) {
|
|
222
|
+
console.log(chalk.red(` \u{1F534} Critical: ${chalk.bold(summary.criticalIssues)}`));
|
|
223
|
+
}
|
|
224
|
+
if (summary.majorIssues > 0) {
|
|
225
|
+
console.log(chalk.yellow(` \u{1F7E1} Major: ${chalk.bold(summary.majorIssues)}`));
|
|
226
|
+
}
|
|
227
|
+
if (summary.minorIssues > 0) {
|
|
228
|
+
console.log(chalk.blue(` \u{1F535} Minor: ${chalk.bold(summary.minorIssues)}`));
|
|
229
|
+
}
|
|
230
|
+
console.log(chalk.green(`
|
|
231
|
+
\u{1F4A1} Potential savings: ${chalk.bold(summary.totalPotentialSavings.toLocaleString())} tokens
|
|
232
|
+
`));
|
|
233
|
+
} else {
|
|
234
|
+
console.log(chalk.green("\u2705 No significant issues found!\n"));
|
|
235
|
+
}
|
|
236
|
+
if (summary.deepFiles.length > 0) {
|
|
237
|
+
console.log(chalk.bold("\u{1F4CF} Deep Import Chains:\n"));
|
|
238
|
+
console.log(chalk.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`));
|
|
239
|
+
console.log(chalk.gray(` Maximum depth: ${summary.maxImportDepth}
|
|
240
|
+
`));
|
|
241
|
+
summary.deepFiles.slice(0, 10).forEach((item) => {
|
|
242
|
+
const fileName = item.file.split("/").slice(-2).join("/");
|
|
243
|
+
console.log(` ${chalk.cyan("\u2192")} ${chalk.white(fileName)} ${chalk.dim(`(depth: ${item.depth})`)}`);
|
|
244
|
+
});
|
|
245
|
+
console.log();
|
|
246
|
+
}
|
|
247
|
+
if (summary.fragmentedModules.length > 0) {
|
|
248
|
+
console.log(chalk.bold("\u{1F9E9} Fragmented Modules:\n"));
|
|
249
|
+
console.log(chalk.gray(` Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(0)}%
|
|
250
|
+
`));
|
|
251
|
+
summary.fragmentedModules.slice(0, 10).forEach((module) => {
|
|
252
|
+
console.log(` ${chalk.yellow("\u25CF")} ${chalk.white(module.domain)} - ${chalk.dim(`${module.files.length} files, ${(module.fragmentationScore * 100).toFixed(0)}% scattered`)}`);
|
|
253
|
+
console.log(chalk.dim(` Token cost: ${module.totalTokens.toLocaleString()}, Cohesion: ${(module.avgCohesion * 100).toFixed(0)}%`));
|
|
254
|
+
});
|
|
255
|
+
console.log();
|
|
256
|
+
}
|
|
257
|
+
if (summary.lowCohesionFiles.length > 0) {
|
|
258
|
+
console.log(chalk.bold("\u{1F500} Low Cohesion Files:\n"));
|
|
259
|
+
console.log(chalk.gray(` Average cohesion: ${(summary.avgCohesion * 100).toFixed(0)}%
|
|
260
|
+
`));
|
|
261
|
+
summary.lowCohesionFiles.slice(0, 10).forEach((item) => {
|
|
262
|
+
const fileName = item.file.split("/").slice(-2).join("/");
|
|
263
|
+
const scorePercent = (item.score * 100).toFixed(0);
|
|
264
|
+
const color = item.score < 0.4 ? chalk.red : chalk.yellow;
|
|
265
|
+
console.log(` ${color("\u25CB")} ${chalk.white(fileName)} ${chalk.dim(`(${scorePercent}% cohesion)`)}`);
|
|
266
|
+
});
|
|
267
|
+
console.log();
|
|
268
|
+
}
|
|
269
|
+
if (summary.topExpensiveFiles.length > 0) {
|
|
270
|
+
console.log(chalk.bold("\u{1F4B8} Most Expensive Files (Context Budget):\n"));
|
|
271
|
+
summary.topExpensiveFiles.slice(0, 10).forEach((item) => {
|
|
272
|
+
const fileName = item.file.split("/").slice(-2).join("/");
|
|
273
|
+
const severityColor = item.severity === "critical" ? chalk.red : item.severity === "major" ? chalk.yellow : chalk.blue;
|
|
274
|
+
console.log(` ${severityColor("\u25CF")} ${chalk.white(fileName)} ${chalk.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`);
|
|
275
|
+
});
|
|
276
|
+
console.log();
|
|
277
|
+
}
|
|
178
278
|
}
|
|
179
279
|
} catch (error) {
|
|
180
280
|
handleCLIError(error, "Context analysis");
|
|
@@ -231,16 +331,76 @@ program.command("consistency").description("Check naming, patterns, and architec
|
|
|
231
331
|
writeFileSync(outputPath, markdown);
|
|
232
332
|
console.log(chalk.green(`\u2705 Report saved to ${outputPath}`));
|
|
233
333
|
} else {
|
|
234
|
-
console.log(
|
|
235
|
-
console.log(`Files
|
|
236
|
-
console.log(`Total
|
|
237
|
-
console.log(` Naming: ${report.summary.namingIssues}`);
|
|
238
|
-
console.log(` Patterns: ${report.summary.patternIssues}`);
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
334
|
+
console.log(chalk.bold("\n\u{1F4CA} Summary\n"));
|
|
335
|
+
console.log(`Files Analyzed: ${chalk.cyan(report.summary.filesAnalyzed)}`);
|
|
336
|
+
console.log(`Total Issues: ${chalk.yellow(report.summary.totalIssues)}`);
|
|
337
|
+
console.log(` Naming: ${chalk.yellow(report.summary.namingIssues)}`);
|
|
338
|
+
console.log(` Patterns: ${chalk.yellow(report.summary.patternIssues)}`);
|
|
339
|
+
console.log(` Architecture: ${chalk.yellow(report.summary.architectureIssues || 0)}`);
|
|
340
|
+
console.log(`Analysis Time: ${chalk.gray(elapsedTime + "s")}
|
|
341
|
+
`);
|
|
342
|
+
if (report.summary.totalIssues === 0) {
|
|
343
|
+
console.log(chalk.green("\u2728 No consistency issues found! Your codebase is well-maintained.\n"));
|
|
344
|
+
} else {
|
|
345
|
+
const namingResults = report.results.filter(
|
|
346
|
+
(r) => r.issues.some((i) => i.category === "naming")
|
|
347
|
+
);
|
|
348
|
+
const patternResults = report.results.filter(
|
|
349
|
+
(r) => r.issues.some((i) => i.category === "patterns")
|
|
350
|
+
);
|
|
351
|
+
if (namingResults.length > 0) {
|
|
352
|
+
console.log(chalk.bold("\u{1F3F7}\uFE0F Naming Issues\n"));
|
|
353
|
+
let shown = 0;
|
|
354
|
+
for (const result of namingResults) {
|
|
355
|
+
if (shown >= 5) break;
|
|
356
|
+
for (const issue of result.issues) {
|
|
357
|
+
if (shown >= 5) break;
|
|
358
|
+
const severityColor = issue.severity === "critical" ? chalk.red : issue.severity === "major" ? chalk.yellow : issue.severity === "minor" ? chalk.blue : chalk.gray;
|
|
359
|
+
console.log(`${severityColor(issue.severity.toUpperCase())} ${chalk.dim(`${issue.location.file}:${issue.location.line}`)}`);
|
|
360
|
+
console.log(` ${issue.message}`);
|
|
361
|
+
if (issue.suggestion) {
|
|
362
|
+
console.log(` ${chalk.dim("\u2192")} ${chalk.italic(issue.suggestion)}`);
|
|
363
|
+
}
|
|
364
|
+
console.log();
|
|
365
|
+
shown++;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
const remaining = namingResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
369
|
+
if (remaining > 0) {
|
|
370
|
+
console.log(chalk.dim(` ... and ${remaining} more issues
|
|
371
|
+
`));
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (patternResults.length > 0) {
|
|
375
|
+
console.log(chalk.bold("\u{1F504} Pattern Issues\n"));
|
|
376
|
+
let shown = 0;
|
|
377
|
+
for (const result of patternResults) {
|
|
378
|
+
if (shown >= 5) break;
|
|
379
|
+
for (const issue of result.issues) {
|
|
380
|
+
if (shown >= 5) break;
|
|
381
|
+
const severityColor = issue.severity === "critical" ? chalk.red : issue.severity === "major" ? chalk.yellow : issue.severity === "minor" ? chalk.blue : chalk.gray;
|
|
382
|
+
console.log(`${severityColor(issue.severity.toUpperCase())} ${chalk.dim(`${issue.location.file}:${issue.location.line}`)}`);
|
|
383
|
+
console.log(` ${issue.message}`);
|
|
384
|
+
if (issue.suggestion) {
|
|
385
|
+
console.log(` ${chalk.dim("\u2192")} ${chalk.italic(issue.suggestion)}`);
|
|
386
|
+
}
|
|
387
|
+
console.log();
|
|
388
|
+
shown++;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
const remaining = patternResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
392
|
+
if (remaining > 0) {
|
|
393
|
+
console.log(chalk.dim(` ... and ${remaining} more issues
|
|
394
|
+
`));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
if (report.recommendations.length > 0) {
|
|
398
|
+
console.log(chalk.bold("\u{1F4A1} Recommendations\n"));
|
|
399
|
+
report.recommendations.forEach((rec, i) => {
|
|
400
|
+
console.log(`${i + 1}. ${rec}`);
|
|
401
|
+
});
|
|
402
|
+
console.log();
|
|
403
|
+
}
|
|
244
404
|
}
|
|
245
405
|
}
|
|
246
406
|
} catch (error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiready/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Unified CLI for AIReady analysis tools",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
"commander": "^12.1.0",
|
|
13
13
|
"chalk": "^5.3.0",
|
|
14
14
|
"@aiready/core": "0.5.6",
|
|
15
|
-
"@aiready/
|
|
16
|
-
"@aiready/
|
|
17
|
-
"@aiready/
|
|
15
|
+
"@aiready/context-analyzer": "0.5.3",
|
|
16
|
+
"@aiready/pattern-detect": "0.9.0",
|
|
17
|
+
"@aiready/consistency": "0.3.0"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"tsup": "^8.3.5",
|
package/src/cli.ts
CHANGED
|
@@ -152,7 +152,7 @@ program
|
|
|
152
152
|
|
|
153
153
|
const { analyzePatterns, generateSummary } = await import('@aiready/pattern-detect');
|
|
154
154
|
|
|
155
|
-
const { results } = await analyzePatterns(finalOptions);
|
|
155
|
+
const { results, duplicates } = await analyzePatterns(finalOptions);
|
|
156
156
|
|
|
157
157
|
const elapsedTime = getElapsedTime(startTime);
|
|
158
158
|
const summary = generateSummary(results);
|
|
@@ -174,9 +174,57 @@ program
|
|
|
174
174
|
|
|
175
175
|
handleJSONOutput(outputData, outputPath, `✅ Results saved to ${outputPath}`);
|
|
176
176
|
} else {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
177
|
+
// Console output - format to match standalone CLI
|
|
178
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
179
|
+
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
180
|
+
const divider = '━'.repeat(dividerWidth);
|
|
181
|
+
|
|
182
|
+
console.log(chalk.cyan(divider));
|
|
183
|
+
console.log(chalk.bold.white(' PATTERN ANALYSIS SUMMARY'));
|
|
184
|
+
console.log(chalk.cyan(divider) + '\n');
|
|
185
|
+
|
|
186
|
+
console.log(chalk.white(`📁 Files analyzed: ${chalk.bold(results.length)}`));
|
|
187
|
+
console.log(chalk.yellow(`⚠ Duplicate patterns found: ${chalk.bold(summary.totalPatterns)}`));
|
|
188
|
+
console.log(chalk.red(`💰 Token cost (wasted): ${chalk.bold(summary.totalTokenCost.toLocaleString())}`));
|
|
189
|
+
console.log(chalk.gray(`⏱ Analysis time: ${chalk.bold(elapsedTime + 's')}`));
|
|
190
|
+
|
|
191
|
+
// Show breakdown by pattern type
|
|
192
|
+
const sortedTypes = Object.entries(summary.patternsByType || {})
|
|
193
|
+
.filter(([, count]) => count > 0)
|
|
194
|
+
.sort(([, a], [, b]) => (b as number) - (a as number));
|
|
195
|
+
|
|
196
|
+
if (sortedTypes.length > 0) {
|
|
197
|
+
console.log(chalk.cyan('\n' + divider));
|
|
198
|
+
console.log(chalk.bold.white(' PATTERNS BY TYPE'));
|
|
199
|
+
console.log(chalk.cyan(divider) + '\n');
|
|
200
|
+
sortedTypes.forEach(([type, count]) => {
|
|
201
|
+
console.log(` ${chalk.white(type.padEnd(15))} ${chalk.bold(count)}`);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Show top duplicates
|
|
206
|
+
if (summary.totalPatterns > 0 && duplicates.length > 0) {
|
|
207
|
+
console.log(chalk.cyan('\n' + divider));
|
|
208
|
+
console.log(chalk.bold.white(' TOP DUPLICATE PATTERNS'));
|
|
209
|
+
console.log(chalk.cyan(divider) + '\n');
|
|
210
|
+
|
|
211
|
+
// Sort by similarity and take top 10
|
|
212
|
+
const topDuplicates = [...duplicates]
|
|
213
|
+
.sort((a, b) => b.similarity - a.similarity)
|
|
214
|
+
.slice(0, 10);
|
|
215
|
+
|
|
216
|
+
topDuplicates.forEach((dup) => {
|
|
217
|
+
const severity = dup.similarity > 0.95 ? 'CRITICAL' : dup.similarity > 0.9 ? 'HIGH' : 'MEDIUM';
|
|
218
|
+
const severityIcon = dup.similarity > 0.95 ? '🔴' : dup.similarity > 0.9 ? '🟡' : '🔵';
|
|
219
|
+
const file1Name = dup.file1.split('/').pop() || dup.file1;
|
|
220
|
+
const file2Name = dup.file2.split('/').pop() || dup.file2;
|
|
221
|
+
console.log(`${severityIcon} ${severity}: ${chalk.bold(file1Name)} ↔ ${chalk.bold(file2Name)}`);
|
|
222
|
+
console.log(` Similarity: ${chalk.bold(Math.round(dup.similarity * 100) + '%')} | Wasted: ${chalk.bold(dup.tokenCost.toLocaleString())} tokens each`);
|
|
223
|
+
console.log(` Lines: ${chalk.cyan(dup.line1 + '-' + dup.endLine1)} ↔ ${chalk.cyan(dup.line2 + '-' + dup.endLine2)}\n`);
|
|
224
|
+
});
|
|
225
|
+
} else {
|
|
226
|
+
console.log(chalk.green('\n✨ Great! No duplicate patterns detected.\n'));
|
|
227
|
+
}
|
|
180
228
|
}
|
|
181
229
|
} catch (error) {
|
|
182
230
|
handleCLIError(error, 'Pattern analysis');
|
|
@@ -258,11 +306,84 @@ program
|
|
|
258
306
|
|
|
259
307
|
handleJSONOutput(outputData, outputPath, `✅ Results saved to ${outputPath}`);
|
|
260
308
|
} else {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
309
|
+
// Console output - format the results nicely
|
|
310
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
311
|
+
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
312
|
+
const divider = '━'.repeat(dividerWidth);
|
|
313
|
+
|
|
314
|
+
console.log(chalk.cyan(divider));
|
|
315
|
+
console.log(chalk.bold.white(' CONTEXT ANALYSIS SUMMARY'));
|
|
316
|
+
console.log(chalk.cyan(divider) + '\n');
|
|
317
|
+
|
|
318
|
+
console.log(chalk.white(`📁 Files analyzed: ${chalk.bold(summary.totalFiles)}`));
|
|
319
|
+
console.log(chalk.white(`📊 Total tokens: ${chalk.bold(summary.totalTokens.toLocaleString())}`));
|
|
320
|
+
console.log(chalk.yellow(`💰 Avg context budget: ${chalk.bold(summary.avgContextBudget.toFixed(0))} tokens/file`));
|
|
321
|
+
console.log(chalk.white(`⏱ Analysis time: ${chalk.bold(elapsedTime + 's')}\n`));
|
|
322
|
+
|
|
323
|
+
// Issues summary
|
|
324
|
+
const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
|
|
325
|
+
if (totalIssues > 0) {
|
|
326
|
+
console.log(chalk.bold('⚠️ Issues Found:\n'));
|
|
327
|
+
if (summary.criticalIssues > 0) {
|
|
328
|
+
console.log(chalk.red(` 🔴 Critical: ${chalk.bold(summary.criticalIssues)}`));
|
|
329
|
+
}
|
|
330
|
+
if (summary.majorIssues > 0) {
|
|
331
|
+
console.log(chalk.yellow(` 🟡 Major: ${chalk.bold(summary.majorIssues)}`));
|
|
332
|
+
}
|
|
333
|
+
if (summary.minorIssues > 0) {
|
|
334
|
+
console.log(chalk.blue(` 🔵 Minor: ${chalk.bold(summary.minorIssues)}`));
|
|
335
|
+
}
|
|
336
|
+
console.log(chalk.green(`\n 💡 Potential savings: ${chalk.bold(summary.totalPotentialSavings.toLocaleString())} tokens\n`));
|
|
337
|
+
} else {
|
|
338
|
+
console.log(chalk.green('✅ No significant issues found!\n'));
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Deep import chains
|
|
342
|
+
if (summary.deepFiles.length > 0) {
|
|
343
|
+
console.log(chalk.bold('📏 Deep Import Chains:\n'));
|
|
344
|
+
console.log(chalk.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`));
|
|
345
|
+
console.log(chalk.gray(` Maximum depth: ${summary.maxImportDepth}\n`));
|
|
346
|
+
summary.deepFiles.slice(0, 10).forEach((item) => {
|
|
347
|
+
const fileName = item.file.split('/').slice(-2).join('/');
|
|
348
|
+
console.log(` ${chalk.cyan('→')} ${chalk.white(fileName)} ${chalk.dim(`(depth: ${item.depth})`)}`);
|
|
349
|
+
});
|
|
350
|
+
console.log();
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Fragmented modules
|
|
354
|
+
if (summary.fragmentedModules.length > 0) {
|
|
355
|
+
console.log(chalk.bold('🧩 Fragmented Modules:\n'));
|
|
356
|
+
console.log(chalk.gray(` Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(0)}%\n`));
|
|
357
|
+
summary.fragmentedModules.slice(0, 10).forEach((module) => {
|
|
358
|
+
console.log(` ${chalk.yellow('●')} ${chalk.white(module.domain)} - ${chalk.dim(`${module.files.length} files, ${(module.fragmentationScore * 100).toFixed(0)}% scattered`)}`);
|
|
359
|
+
console.log(chalk.dim(` Token cost: ${module.totalTokens.toLocaleString()}, Cohesion: ${(module.avgCohesion * 100).toFixed(0)}%`));
|
|
360
|
+
});
|
|
361
|
+
console.log();
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Low cohesion files
|
|
365
|
+
if (summary.lowCohesionFiles.length > 0) {
|
|
366
|
+
console.log(chalk.bold('🔀 Low Cohesion Files:\n'));
|
|
367
|
+
console.log(chalk.gray(` Average cohesion: ${(summary.avgCohesion * 100).toFixed(0)}%\n`));
|
|
368
|
+
summary.lowCohesionFiles.slice(0, 10).forEach((item) => {
|
|
369
|
+
const fileName = item.file.split('/').slice(-2).join('/');
|
|
370
|
+
const scorePercent = (item.score * 100).toFixed(0);
|
|
371
|
+
const color = item.score < 0.4 ? chalk.red : chalk.yellow;
|
|
372
|
+
console.log(` ${color('○')} ${chalk.white(fileName)} ${chalk.dim(`(${scorePercent}% cohesion)`)}`);
|
|
373
|
+
});
|
|
374
|
+
console.log();
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Top expensive files
|
|
378
|
+
if (summary.topExpensiveFiles.length > 0) {
|
|
379
|
+
console.log(chalk.bold('💸 Most Expensive Files (Context Budget):\n'));
|
|
380
|
+
summary.topExpensiveFiles.slice(0, 10).forEach((item) => {
|
|
381
|
+
const fileName = item.file.split('/').slice(-2).join('/');
|
|
382
|
+
const severityColor = item.severity === 'critical' ? chalk.red : item.severity === 'major' ? chalk.yellow : chalk.blue;
|
|
383
|
+
console.log(` ${severityColor('●')} ${chalk.white(fileName)} ${chalk.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`);
|
|
384
|
+
});
|
|
385
|
+
console.log();
|
|
386
|
+
}
|
|
266
387
|
}
|
|
267
388
|
} catch (error) {
|
|
268
389
|
handleCLIError(error, 'Context analysis');
|
|
@@ -346,17 +467,83 @@ program
|
|
|
346
467
|
writeFileSync(outputPath, markdown);
|
|
347
468
|
console.log(chalk.green(`✅ Report saved to ${outputPath}`));
|
|
348
469
|
} else {
|
|
349
|
-
|
|
350
|
-
console.log(
|
|
351
|
-
console.log(`
|
|
352
|
-
console.log(`
|
|
353
|
-
console.log(`
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
470
|
+
// Console output - format to match standalone CLI
|
|
471
|
+
console.log(chalk.bold('\n📊 Summary\n'));
|
|
472
|
+
console.log(`Files Analyzed: ${chalk.cyan(report.summary.filesAnalyzed)}`);
|
|
473
|
+
console.log(`Total Issues: ${chalk.yellow(report.summary.totalIssues)}`);
|
|
474
|
+
console.log(` Naming: ${chalk.yellow(report.summary.namingIssues)}`);
|
|
475
|
+
console.log(` Patterns: ${chalk.yellow(report.summary.patternIssues)}`);
|
|
476
|
+
console.log(` Architecture: ${chalk.yellow(report.summary.architectureIssues || 0)}`);
|
|
477
|
+
console.log(`Analysis Time: ${chalk.gray(elapsedTime + 's')}\n`);
|
|
478
|
+
|
|
479
|
+
if (report.summary.totalIssues === 0) {
|
|
480
|
+
console.log(chalk.green('✨ No consistency issues found! Your codebase is well-maintained.\n'));
|
|
481
|
+
} else {
|
|
482
|
+
// Group and display issues by category
|
|
483
|
+
const namingResults = report.results.filter((r: any) =>
|
|
484
|
+
r.issues.some((i: any) => i.category === 'naming')
|
|
485
|
+
);
|
|
486
|
+
const patternResults = report.results.filter((r: any) =>
|
|
487
|
+
r.issues.some((i: any) => i.category === 'patterns')
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
if (namingResults.length > 0) {
|
|
491
|
+
console.log(chalk.bold('🏷️ Naming Issues\n'));
|
|
492
|
+
let shown = 0;
|
|
493
|
+
for (const result of namingResults) {
|
|
494
|
+
if (shown >= 5) break;
|
|
495
|
+
for (const issue of result.issues) {
|
|
496
|
+
if (shown >= 5) break;
|
|
497
|
+
const severityColor = issue.severity === 'critical' ? chalk.red :
|
|
498
|
+
issue.severity === 'major' ? chalk.yellow :
|
|
499
|
+
issue.severity === 'minor' ? chalk.blue : chalk.gray;
|
|
500
|
+
console.log(`${severityColor(issue.severity.toUpperCase())} ${chalk.dim(`${issue.location.file}:${issue.location.line}`)}`);
|
|
501
|
+
console.log(` ${issue.message}`);
|
|
502
|
+
if (issue.suggestion) {
|
|
503
|
+
console.log(` ${chalk.dim('→')} ${chalk.italic(issue.suggestion)}`);
|
|
504
|
+
}
|
|
505
|
+
console.log();
|
|
506
|
+
shown++;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
const remaining = namingResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
510
|
+
if (remaining > 0) {
|
|
511
|
+
console.log(chalk.dim(` ... and ${remaining} more issues\n`));
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (patternResults.length > 0) {
|
|
516
|
+
console.log(chalk.bold('🔄 Pattern Issues\n'));
|
|
517
|
+
let shown = 0;
|
|
518
|
+
for (const result of patternResults) {
|
|
519
|
+
if (shown >= 5) break;
|
|
520
|
+
for (const issue of result.issues) {
|
|
521
|
+
if (shown >= 5) break;
|
|
522
|
+
const severityColor = issue.severity === 'critical' ? chalk.red :
|
|
523
|
+
issue.severity === 'major' ? chalk.yellow :
|
|
524
|
+
issue.severity === 'minor' ? chalk.blue : chalk.gray;
|
|
525
|
+
console.log(`${severityColor(issue.severity.toUpperCase())} ${chalk.dim(`${issue.location.file}:${issue.location.line}`)}`);
|
|
526
|
+
console.log(` ${issue.message}`);
|
|
527
|
+
if (issue.suggestion) {
|
|
528
|
+
console.log(` ${chalk.dim('→')} ${chalk.italic(issue.suggestion)}`);
|
|
529
|
+
}
|
|
530
|
+
console.log();
|
|
531
|
+
shown++;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
const remaining = patternResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
535
|
+
if (remaining > 0) {
|
|
536
|
+
console.log(chalk.dim(` ... and ${remaining} more issues\n`));
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (report.recommendations.length > 0) {
|
|
541
|
+
console.log(chalk.bold('💡 Recommendations\n'));
|
|
542
|
+
report.recommendations.forEach((rec: string, i: number) => {
|
|
543
|
+
console.log(`${i + 1}. ${rec}`);
|
|
544
|
+
});
|
|
545
|
+
console.log();
|
|
546
|
+
}
|
|
360
547
|
}
|
|
361
548
|
}
|
|
362
549
|
} catch (error) {
|