@aiready/cli 0.14.17 → 0.14.22
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/cli.js +299 -295
- package/dist/cli.mjs +262 -258
- package/package.json +12 -12
package/dist/cli.mjs
CHANGED
|
@@ -12,7 +12,7 @@ import { join as join2, dirname } from "path";
|
|
|
12
12
|
import { fileURLToPath } from "url";
|
|
13
13
|
|
|
14
14
|
// src/commands/scan.ts
|
|
15
|
-
import
|
|
15
|
+
import chalk5 from "chalk";
|
|
16
16
|
import { writeFileSync, readFileSync as readFileSync2 } from "fs";
|
|
17
17
|
import { resolve as resolvePath3 } from "path";
|
|
18
18
|
import {
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
resolveOutputPath,
|
|
23
23
|
getRepoMetadata,
|
|
24
24
|
calculateTokenBudget,
|
|
25
|
-
ToolName,
|
|
25
|
+
ToolName as ToolName2,
|
|
26
26
|
emitIssuesAsAnnotations
|
|
27
27
|
} from "@aiready/core";
|
|
28
28
|
|
|
@@ -388,7 +388,7 @@ async function uploadAction(file, options) {
|
|
|
388
388
|
handleCLIError(error, "Upload");
|
|
389
389
|
}
|
|
390
390
|
}
|
|
391
|
-
var
|
|
391
|
+
var UPLOAD_HELP_TEXT = `
|
|
392
392
|
EXAMPLES:
|
|
393
393
|
$ aiready upload report.json --api-key ar_...
|
|
394
394
|
$ aiready upload .aiready/latest.json
|
|
@@ -399,9 +399,80 @@ ENVIRONMENT VARIABLES:
|
|
|
399
399
|
AIREADY_SERVER Custom platform URL (default: https://dev.platform.getaiready.dev)
|
|
400
400
|
`;
|
|
401
401
|
|
|
402
|
+
// src/commands/scan-helpers.ts
|
|
403
|
+
import chalk4 from "chalk";
|
|
404
|
+
import { ToolName } from "@aiready/core";
|
|
405
|
+
function getProfileTools(profile) {
|
|
406
|
+
switch (profile.toLowerCase()) {
|
|
407
|
+
case "agentic":
|
|
408
|
+
return [
|
|
409
|
+
ToolName.AiSignalClarity,
|
|
410
|
+
ToolName.AgentGrounding,
|
|
411
|
+
ToolName.TestabilityIndex
|
|
412
|
+
];
|
|
413
|
+
case "cost":
|
|
414
|
+
return [ToolName.PatternDetect, ToolName.ContextAnalyzer];
|
|
415
|
+
case "logic":
|
|
416
|
+
return [
|
|
417
|
+
ToolName.TestabilityIndex,
|
|
418
|
+
ToolName.NamingConsistency,
|
|
419
|
+
ToolName.ContextAnalyzer,
|
|
420
|
+
ToolName.PatternDetect,
|
|
421
|
+
ToolName.ChangeAmplification
|
|
422
|
+
];
|
|
423
|
+
case "ui":
|
|
424
|
+
return [
|
|
425
|
+
ToolName.NamingConsistency,
|
|
426
|
+
ToolName.ContextAnalyzer,
|
|
427
|
+
ToolName.PatternDetect,
|
|
428
|
+
ToolName.DocDrift,
|
|
429
|
+
ToolName.AiSignalClarity
|
|
430
|
+
];
|
|
431
|
+
case "security":
|
|
432
|
+
return [ToolName.NamingConsistency, ToolName.TestabilityIndex];
|
|
433
|
+
case "onboarding":
|
|
434
|
+
return [
|
|
435
|
+
ToolName.ContextAnalyzer,
|
|
436
|
+
ToolName.NamingConsistency,
|
|
437
|
+
ToolName.AgentGrounding
|
|
438
|
+
];
|
|
439
|
+
default:
|
|
440
|
+
console.log(
|
|
441
|
+
chalk4.yellow(`
|
|
442
|
+
\u26A0\uFE0F Unknown profile '${profile}'. Using defaults.`)
|
|
443
|
+
);
|
|
444
|
+
return void 0;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
function createProgressCallback() {
|
|
448
|
+
return (event) => {
|
|
449
|
+
if (event.message) {
|
|
450
|
+
process.stdout.write(`\r\x1B[K [${event.tool}] ${event.message}`);
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
process.stdout.write("\r\x1B[K");
|
|
454
|
+
console.log(chalk4.cyan(`--- ${event.tool.toUpperCase()} RESULTS ---`));
|
|
455
|
+
const toolResult = event.data;
|
|
456
|
+
if (toolResult && toolResult.summary) {
|
|
457
|
+
if (toolResult.summary.totalIssues !== void 0)
|
|
458
|
+
console.log(
|
|
459
|
+
` Issues found: ${chalk4.bold(toolResult.summary.totalIssues)}`
|
|
460
|
+
);
|
|
461
|
+
if (toolResult.summary.score !== void 0)
|
|
462
|
+
console.log(
|
|
463
|
+
` Tool Score: ${chalk4.bold(toolResult.summary.score)}/100`
|
|
464
|
+
);
|
|
465
|
+
if (toolResult.summary.totalFiles !== void 0)
|
|
466
|
+
console.log(
|
|
467
|
+
` Files analyzed: ${chalk4.bold(toolResult.summary.totalFiles)}`
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
|
|
402
473
|
// src/commands/scan.ts
|
|
403
474
|
async function scanAction(directory, options) {
|
|
404
|
-
console.log(
|
|
475
|
+
console.log(chalk5.blue("\u{1F680} Starting AIReady unified analysis...\n"));
|
|
405
476
|
const startTime = Date.now();
|
|
406
477
|
const resolvedDir = resolvePath3(process.cwd(), directory ?? ".");
|
|
407
478
|
const repoMetadata = getRepoMetadata(resolvedDir);
|
|
@@ -427,56 +498,7 @@ async function scanAction(directory, options) {
|
|
|
427
498
|
};
|
|
428
499
|
let profileTools = options.tools ? options.tools.split(",").map((t) => t.trim()) : void 0;
|
|
429
500
|
if (options.profile) {
|
|
430
|
-
|
|
431
|
-
case "agentic":
|
|
432
|
-
profileTools = [
|
|
433
|
-
ToolName.AiSignalClarity,
|
|
434
|
-
ToolName.AgentGrounding,
|
|
435
|
-
ToolName.TestabilityIndex
|
|
436
|
-
];
|
|
437
|
-
break;
|
|
438
|
-
case "cost":
|
|
439
|
-
profileTools = [ToolName.PatternDetect, ToolName.ContextAnalyzer];
|
|
440
|
-
break;
|
|
441
|
-
case "logic":
|
|
442
|
-
profileTools = [
|
|
443
|
-
ToolName.TestabilityIndex,
|
|
444
|
-
ToolName.NamingConsistency,
|
|
445
|
-
ToolName.ContextAnalyzer,
|
|
446
|
-
ToolName.PatternDetect,
|
|
447
|
-
ToolName.ChangeAmplification
|
|
448
|
-
];
|
|
449
|
-
break;
|
|
450
|
-
case "ui":
|
|
451
|
-
profileTools = [
|
|
452
|
-
ToolName.NamingConsistency,
|
|
453
|
-
ToolName.ContextAnalyzer,
|
|
454
|
-
ToolName.PatternDetect,
|
|
455
|
-
ToolName.DocDrift,
|
|
456
|
-
ToolName.AiSignalClarity
|
|
457
|
-
];
|
|
458
|
-
break;
|
|
459
|
-
case "security":
|
|
460
|
-
profileTools = [
|
|
461
|
-
ToolName.NamingConsistency,
|
|
462
|
-
ToolName.TestabilityIndex
|
|
463
|
-
];
|
|
464
|
-
break;
|
|
465
|
-
case "onboarding":
|
|
466
|
-
profileTools = [
|
|
467
|
-
ToolName.ContextAnalyzer,
|
|
468
|
-
ToolName.NamingConsistency,
|
|
469
|
-
ToolName.AgentGrounding
|
|
470
|
-
];
|
|
471
|
-
break;
|
|
472
|
-
default:
|
|
473
|
-
console.log(
|
|
474
|
-
chalk4.yellow(
|
|
475
|
-
`
|
|
476
|
-
\u26A0\uFE0F Unknown profile '${options.profile}'. Using defaults.`
|
|
477
|
-
)
|
|
478
|
-
);
|
|
479
|
-
}
|
|
501
|
+
profileTools = getProfileTools(options.profile);
|
|
480
502
|
}
|
|
481
503
|
const cliOverrides = {
|
|
482
504
|
include: options.include?.split(","),
|
|
@@ -489,42 +511,24 @@ async function scanAction(directory, options) {
|
|
|
489
511
|
cliOverrides
|
|
490
512
|
);
|
|
491
513
|
const finalOptions = { ...baseOptions };
|
|
492
|
-
if (baseOptions.tools.includes(
|
|
514
|
+
if (baseOptions.tools.includes(ToolName2.PatternDetect) || baseOptions.tools.includes("patterns")) {
|
|
493
515
|
const { getSmartDefaults } = await import("@aiready/pattern-detect");
|
|
494
516
|
const patternSmartDefaults = await getSmartDefaults(
|
|
495
517
|
resolvedDir,
|
|
496
|
-
finalOptions.toolConfigs?.[
|
|
518
|
+
finalOptions.toolConfigs?.[ToolName2.PatternDetect] ?? {}
|
|
497
519
|
);
|
|
498
520
|
if (!finalOptions.toolConfigs) finalOptions.toolConfigs = {};
|
|
499
|
-
finalOptions.toolConfigs[
|
|
521
|
+
finalOptions.toolConfigs[ToolName2.PatternDetect] = {
|
|
500
522
|
...patternSmartDefaults,
|
|
501
|
-
...finalOptions.toolConfigs[
|
|
523
|
+
...finalOptions.toolConfigs[ToolName2.PatternDetect]
|
|
502
524
|
};
|
|
503
525
|
}
|
|
504
|
-
console.log(
|
|
526
|
+
console.log(chalk5.cyan("\n=== AIReady Run Preview ==="));
|
|
505
527
|
console.log(
|
|
506
|
-
|
|
528
|
+
chalk5.white("Tools to run:"),
|
|
507
529
|
(finalOptions.tools ?? []).join(", ")
|
|
508
530
|
);
|
|
509
|
-
const progressCallback = (
|
|
510
|
-
if (event.message) {
|
|
511
|
-
process.stdout.write(`\r\x1B[K [${event.tool}] ${event.message}`);
|
|
512
|
-
return;
|
|
513
|
-
}
|
|
514
|
-
process.stdout.write("\r\x1B[K");
|
|
515
|
-
console.log(chalk4.cyan(`--- ${event.tool.toUpperCase()} RESULTS ---`));
|
|
516
|
-
const res = event.data;
|
|
517
|
-
if (res && res.summary) {
|
|
518
|
-
if (res.summary.totalIssues !== void 0)
|
|
519
|
-
console.log(` Issues found: ${chalk4.bold(res.summary.totalIssues)}`);
|
|
520
|
-
if (res.summary.score !== void 0)
|
|
521
|
-
console.log(` Tool Score: ${chalk4.bold(res.summary.score)}/100`);
|
|
522
|
-
if (res.summary.totalFiles !== void 0)
|
|
523
|
-
console.log(
|
|
524
|
-
` Files analyzed: ${chalk4.bold(res.summary.totalFiles)}`
|
|
525
|
-
);
|
|
526
|
-
}
|
|
527
|
-
};
|
|
531
|
+
const progressCallback = createProgressCallback();
|
|
528
532
|
const scoringProfile = options.profile ?? baseOptions.scoring?.profile ?? "default";
|
|
529
533
|
const results = await analyzeUnified({
|
|
530
534
|
...finalOptions,
|
|
@@ -555,19 +559,19 @@ async function scanAction(directory, options) {
|
|
|
555
559
|
const diffStr = diff > 0 ? `+${diff}` : String(diff);
|
|
556
560
|
if (diff > 0)
|
|
557
561
|
console.log(
|
|
558
|
-
|
|
562
|
+
chalk5.green(
|
|
559
563
|
` \u{1F4C8} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
|
|
560
564
|
)
|
|
561
565
|
);
|
|
562
566
|
else if (diff < 0)
|
|
563
567
|
console.log(
|
|
564
|
-
|
|
568
|
+
chalk5.red(
|
|
565
569
|
` \u{1F4C9} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
|
|
566
570
|
)
|
|
567
571
|
);
|
|
568
572
|
else
|
|
569
573
|
console.log(
|
|
570
|
-
|
|
574
|
+
chalk5.blue(
|
|
571
575
|
` \u2796 Trend: No change (${prevScore} \u2192 ${scoringResult.overall})`
|
|
572
576
|
)
|
|
573
577
|
);
|
|
@@ -627,19 +631,19 @@ async function scanAction(directory, options) {
|
|
|
627
631
|
}
|
|
628
632
|
}
|
|
629
633
|
console.log(
|
|
630
|
-
|
|
634
|
+
chalk5.dim(
|
|
631
635
|
"\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
|
|
632
636
|
)
|
|
633
637
|
);
|
|
634
|
-
console.log(
|
|
635
|
-
console.log(
|
|
638
|
+
console.log(chalk5.dim("\u{1F4AC} Found a bug or have a metric idea?"));
|
|
639
|
+
console.log(chalk5.dim("\u{1F449} Copy/paste this to your AI agent:"));
|
|
636
640
|
console.log(
|
|
637
|
-
|
|
641
|
+
chalk5.cyan(
|
|
638
642
|
` "Any feedback for the tools? Please use 'aiready bug' to report \u2764\uFE0F"`
|
|
639
643
|
)
|
|
640
644
|
);
|
|
641
645
|
console.log(
|
|
642
|
-
|
|
646
|
+
chalk5.dim(
|
|
643
647
|
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
|
|
644
648
|
)
|
|
645
649
|
);
|
|
@@ -662,7 +666,7 @@ async function scanAction(directory, options) {
|
|
|
662
666
|
} else {
|
|
663
667
|
try {
|
|
664
668
|
writeFileSync(outputPath, JSON.stringify(outputData, null, 2));
|
|
665
|
-
console.log(
|
|
669
|
+
console.log(chalk5.dim(`\u2705 Report auto-persisted to ${outputPath}`));
|
|
666
670
|
} catch (err) {
|
|
667
671
|
void err;
|
|
668
672
|
}
|
|
@@ -683,7 +687,7 @@ async function scanAction(directory, options) {
|
|
|
683
687
|
const report = mapToUnifiedReport(results, scoringResult);
|
|
684
688
|
if (isCI && report.results && report.results.length > 0) {
|
|
685
689
|
console.log(
|
|
686
|
-
|
|
690
|
+
chalk5.cyan(
|
|
687
691
|
`
|
|
688
692
|
\u{1F4DD} Emitting GitHub Action annotations for ${report.results.length} issues...`
|
|
689
693
|
)
|
|
@@ -704,31 +708,31 @@ async function scanAction(directory, options) {
|
|
|
704
708
|
}
|
|
705
709
|
}
|
|
706
710
|
if (shouldFail) {
|
|
707
|
-
console.log(
|
|
711
|
+
console.log(chalk5.red(`
|
|
708
712
|
\u{1F6AB} SCAN FAILED: ${failReason}`));
|
|
709
713
|
process.exit(1);
|
|
710
714
|
} else {
|
|
711
|
-
console.log(
|
|
715
|
+
console.log(chalk5.green("\n\u2705 SCAN PASSED"));
|
|
712
716
|
}
|
|
713
717
|
}
|
|
714
718
|
} catch (error) {
|
|
715
719
|
handleCLIError2(error, "Analysis");
|
|
716
720
|
}
|
|
717
721
|
}
|
|
718
|
-
var
|
|
722
|
+
var SCAN_HELP_TEXT = `...`;
|
|
719
723
|
|
|
720
724
|
// src/commands/init.ts
|
|
721
725
|
import { writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
|
|
722
726
|
import { join } from "path";
|
|
723
|
-
import
|
|
724
|
-
import { ToolName as
|
|
727
|
+
import chalk6 from "chalk";
|
|
728
|
+
import { ToolName as ToolName3 } from "@aiready/core";
|
|
725
729
|
async function initAction(options) {
|
|
726
730
|
const fileExt = options.format === "js" ? "js" : "json";
|
|
727
731
|
const fileName = fileExt === "js" ? "aiready.config.js" : "aiready.json";
|
|
728
732
|
const filePath = join(process.cwd(), fileName);
|
|
729
733
|
if (existsSync2(filePath) && !options.force) {
|
|
730
734
|
console.error(
|
|
731
|
-
|
|
735
|
+
chalk6.red(`Error: ${fileName} already exists. Use --force to overwrite.`)
|
|
732
736
|
);
|
|
733
737
|
process.exit(1);
|
|
734
738
|
}
|
|
@@ -752,15 +756,15 @@ async function initAction(options) {
|
|
|
752
756
|
"**/*.spec.ts"
|
|
753
757
|
],
|
|
754
758
|
tools: [
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
759
|
+
ToolName3.PatternDetect,
|
|
760
|
+
ToolName3.ContextAnalyzer,
|
|
761
|
+
ToolName3.NamingConsistency,
|
|
762
|
+
ToolName3.AiSignalClarity,
|
|
763
|
+
ToolName3.AgentGrounding,
|
|
764
|
+
ToolName3.TestabilityIndex,
|
|
765
|
+
ToolName3.DocDrift,
|
|
766
|
+
ToolName3.DependencyHealth,
|
|
767
|
+
ToolName3.ChangeAmplification
|
|
764
768
|
]
|
|
765
769
|
},
|
|
766
770
|
// Output preferences
|
|
@@ -775,7 +779,7 @@ async function initAction(options) {
|
|
|
775
779
|
},
|
|
776
780
|
// Tool-specific configurations
|
|
777
781
|
tools: {
|
|
778
|
-
[
|
|
782
|
+
[ToolName3.PatternDetect]: {
|
|
779
783
|
// Core detection thresholds
|
|
780
784
|
minSimilarity: 0.4,
|
|
781
785
|
// Jaccard similarity threshold (0-1)
|
|
@@ -805,7 +809,7 @@ async function initAction(options) {
|
|
|
805
809
|
// Add any additional advanced options here
|
|
806
810
|
} : {}
|
|
807
811
|
},
|
|
808
|
-
[
|
|
812
|
+
[ToolName3.ContextAnalyzer]: {
|
|
809
813
|
// Smart defaults are generated dynamically based on repository size
|
|
810
814
|
// These are fallback values for when smart defaults can't be calculated
|
|
811
815
|
maxContextBudget: 25e3,
|
|
@@ -822,7 +826,7 @@ async function initAction(options) {
|
|
|
822
826
|
includeNodeModules: false
|
|
823
827
|
// Whether to include node_modules in analysis
|
|
824
828
|
},
|
|
825
|
-
[
|
|
829
|
+
[ToolName3.NamingConsistency]: {
|
|
826
830
|
// Core checks
|
|
827
831
|
checkNaming: true,
|
|
828
832
|
// Check naming conventions and quality
|
|
@@ -869,7 +873,7 @@ async function initAction(options) {
|
|
|
869
873
|
],
|
|
870
874
|
...options.full ? { disableChecks: [] } : {}
|
|
871
875
|
},
|
|
872
|
-
[
|
|
876
|
+
[ToolName3.AiSignalClarity]: {
|
|
873
877
|
// All signal clarity checks enabled by default
|
|
874
878
|
checkMagicLiterals: true,
|
|
875
879
|
// Detect magic numbers and strings
|
|
@@ -888,7 +892,7 @@ async function initAction(options) {
|
|
|
888
892
|
checkLargeFiles: true
|
|
889
893
|
// Detect files that are too large
|
|
890
894
|
},
|
|
891
|
-
[
|
|
895
|
+
[ToolName3.AgentGrounding]: {
|
|
892
896
|
// Structure clarity
|
|
893
897
|
maxRecommendedDepth: 4,
|
|
894
898
|
// Max directory depth before flagging as "too deep"
|
|
@@ -899,7 +903,7 @@ async function initAction(options) {
|
|
|
899
903
|
additionalVagueNames: ["stuff", "misc", "temp", "test"]
|
|
900
904
|
// Custom vague file names
|
|
901
905
|
},
|
|
902
|
-
[
|
|
906
|
+
[ToolName3.TestabilityIndex]: {
|
|
903
907
|
// Coverage thresholds
|
|
904
908
|
minCoverageRatio: 0.3,
|
|
905
909
|
// Minimum acceptable test/source ratio
|
|
@@ -909,19 +913,19 @@ async function initAction(options) {
|
|
|
909
913
|
maxDepth: 10
|
|
910
914
|
// Maximum scan depth
|
|
911
915
|
},
|
|
912
|
-
[
|
|
916
|
+
[ToolName3.DocDrift]: {
|
|
913
917
|
// Drift detection
|
|
914
918
|
maxCommits: 50,
|
|
915
919
|
// Maximum commit distance to check for drift
|
|
916
920
|
staleMonths: 3
|
|
917
921
|
// Consider comments older than this as outdated
|
|
918
922
|
},
|
|
919
|
-
[
|
|
923
|
+
[ToolName3.DependencyHealth]: {
|
|
920
924
|
// Training cutoff for AI knowledge assessment
|
|
921
925
|
trainingCutoffYear: 2023
|
|
922
926
|
// Year cutoff for AI training data
|
|
923
927
|
},
|
|
924
|
-
[
|
|
928
|
+
[ToolName3.ChangeAmplification]: {
|
|
925
929
|
// Change amplification primarily relies on global scan settings
|
|
926
930
|
// No additional tool-specific configuration required
|
|
927
931
|
}
|
|
@@ -950,22 +954,22 @@ module.exports = ${JSON.stringify(defaultConfig, null, 2)};
|
|
|
950
954
|
try {
|
|
951
955
|
writeFileSync2(filePath, content, "utf8");
|
|
952
956
|
console.log(
|
|
953
|
-
|
|
954
|
-
\u2705 Created default configuration: ${
|
|
957
|
+
chalk6.green(`
|
|
958
|
+
\u2705 Created default configuration: ${chalk6.bold(fileName)}`)
|
|
955
959
|
);
|
|
956
960
|
console.log(
|
|
957
|
-
|
|
961
|
+
chalk6.cyan("You can now fine-tune your settings and run AIReady with:")
|
|
958
962
|
);
|
|
959
|
-
console.log(
|
|
963
|
+
console.log(chalk6.white(` $ aiready scan
|
|
960
964
|
`));
|
|
961
965
|
} catch (error) {
|
|
962
|
-
console.error(
|
|
966
|
+
console.error(chalk6.red(`Failed to write configuration file: ${error}`));
|
|
963
967
|
process.exit(1);
|
|
964
968
|
}
|
|
965
969
|
}
|
|
966
970
|
|
|
967
971
|
// src/commands/patterns.ts
|
|
968
|
-
import
|
|
972
|
+
import chalk7 from "chalk";
|
|
969
973
|
import { resolve as resolvePath4 } from "path";
|
|
970
974
|
import {
|
|
971
975
|
loadMergedConfig as loadMergedConfig2,
|
|
@@ -976,7 +980,7 @@ import {
|
|
|
976
980
|
formatToolScore
|
|
977
981
|
} from "@aiready/core";
|
|
978
982
|
async function patternsAction(directory, options) {
|
|
979
|
-
console.log(
|
|
983
|
+
console.log(chalk7.blue("\u{1F50D} Analyzing patterns...\n"));
|
|
980
984
|
const startTime = Date.now();
|
|
981
985
|
const resolvedDir = resolvePath4(process.cwd(), directory ?? ".");
|
|
982
986
|
try {
|
|
@@ -1044,38 +1048,38 @@ async function patternsAction(directory, options) {
|
|
|
1044
1048
|
const terminalWidth = process.stdout.columns || 80;
|
|
1045
1049
|
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
1046
1050
|
const divider = "\u2501".repeat(dividerWidth);
|
|
1047
|
-
console.log(
|
|
1048
|
-
console.log(
|
|
1049
|
-
console.log(
|
|
1051
|
+
console.log(chalk7.cyan(divider));
|
|
1052
|
+
console.log(chalk7.bold.white(" PATTERN ANALYSIS SUMMARY"));
|
|
1053
|
+
console.log(chalk7.cyan(divider) + "\n");
|
|
1050
1054
|
console.log(
|
|
1051
|
-
|
|
1055
|
+
chalk7.white(`\u{1F4C1} Files analyzed: ${chalk7.bold(results.length)}`)
|
|
1052
1056
|
);
|
|
1053
1057
|
console.log(
|
|
1054
|
-
|
|
1055
|
-
`\u26A0 Duplicate patterns found: ${
|
|
1058
|
+
chalk7.yellow(
|
|
1059
|
+
`\u26A0 Duplicate patterns found: ${chalk7.bold(summary.totalPatterns)}`
|
|
1056
1060
|
)
|
|
1057
1061
|
);
|
|
1058
1062
|
console.log(
|
|
1059
|
-
|
|
1060
|
-
`\u{1F4B0} Token cost (wasted): ${
|
|
1063
|
+
chalk7.red(
|
|
1064
|
+
`\u{1F4B0} Token cost (wasted): ${chalk7.bold(summary.totalTokenCost.toLocaleString())}`
|
|
1061
1065
|
)
|
|
1062
1066
|
);
|
|
1063
1067
|
console.log(
|
|
1064
|
-
|
|
1068
|
+
chalk7.gray(`\u23F1 Analysis time: ${chalk7.bold(elapsedTime + "s")}`)
|
|
1065
1069
|
);
|
|
1066
1070
|
const sortedTypes = Object.entries(summary.patternsByType || {}).filter(([, count]) => count > 0).sort(([, a], [, b]) => b - a);
|
|
1067
1071
|
if (sortedTypes.length > 0) {
|
|
1068
|
-
console.log(
|
|
1069
|
-
console.log(
|
|
1070
|
-
console.log(
|
|
1072
|
+
console.log(chalk7.cyan("\n" + divider));
|
|
1073
|
+
console.log(chalk7.bold.white(" PATTERNS BY TYPE"));
|
|
1074
|
+
console.log(chalk7.cyan(divider) + "\n");
|
|
1071
1075
|
sortedTypes.forEach(([type, count]) => {
|
|
1072
|
-
console.log(` ${
|
|
1076
|
+
console.log(` ${chalk7.white(type.padEnd(15))} ${chalk7.bold(count)}`);
|
|
1073
1077
|
});
|
|
1074
1078
|
}
|
|
1075
1079
|
if (summary.totalPatterns > 0 && duplicates.length > 0) {
|
|
1076
|
-
console.log(
|
|
1077
|
-
console.log(
|
|
1078
|
-
console.log(
|
|
1080
|
+
console.log(chalk7.cyan("\n" + divider));
|
|
1081
|
+
console.log(chalk7.bold.white(" TOP DUPLICATE PATTERNS"));
|
|
1082
|
+
console.log(chalk7.cyan(divider) + "\n");
|
|
1079
1083
|
const topDuplicates = [...duplicates].sort((a, b) => b.similarity - a.similarity).slice(0, 10);
|
|
1080
1084
|
topDuplicates.forEach((dup) => {
|
|
1081
1085
|
const severity = dup.similarity > 0.95 ? "CRITICAL" : dup.similarity > 0.9 ? "HIGH" : "MEDIUM";
|
|
@@ -1083,25 +1087,25 @@ async function patternsAction(directory, options) {
|
|
|
1083
1087
|
const file1Name = dup.file1.split("/").pop() || dup.file1;
|
|
1084
1088
|
const file2Name = dup.file2.split("/").pop() || dup.file2;
|
|
1085
1089
|
console.log(
|
|
1086
|
-
`${severityIcon} ${severity}: ${
|
|
1090
|
+
`${severityIcon} ${severity}: ${chalk7.bold(file1Name)} \u2194 ${chalk7.bold(file2Name)}`
|
|
1087
1091
|
);
|
|
1088
1092
|
console.log(
|
|
1089
|
-
` Similarity: ${
|
|
1093
|
+
` Similarity: ${chalk7.bold(Math.round(dup.similarity * 100) + "%")} | Wasted: ${chalk7.bold(dup.tokenCost.toLocaleString())} tokens each`
|
|
1090
1094
|
);
|
|
1091
1095
|
console.log(
|
|
1092
|
-
` Lines: ${
|
|
1096
|
+
` Lines: ${chalk7.cyan(dup.line1 + "-" + dup.endLine1)} \u2194 ${chalk7.cyan(dup.line2 + "-" + dup.endLine2)}
|
|
1093
1097
|
`
|
|
1094
1098
|
);
|
|
1095
1099
|
});
|
|
1096
1100
|
} else {
|
|
1097
1101
|
console.log(
|
|
1098
|
-
|
|
1102
|
+
chalk7.green("\n\u2728 Great! No duplicate patterns detected.\n")
|
|
1099
1103
|
);
|
|
1100
1104
|
}
|
|
1101
1105
|
if (patternScore) {
|
|
1102
|
-
console.log(
|
|
1103
|
-
console.log(
|
|
1104
|
-
console.log(
|
|
1106
|
+
console.log(chalk7.cyan(divider));
|
|
1107
|
+
console.log(chalk7.bold.white(" AI READINESS SCORE (Patterns)"));
|
|
1108
|
+
console.log(chalk7.cyan(divider) + "\n");
|
|
1105
1109
|
console.log(formatToolScore(patternScore));
|
|
1106
1110
|
console.log();
|
|
1107
1111
|
}
|
|
@@ -1110,7 +1114,7 @@ async function patternsAction(directory, options) {
|
|
|
1110
1114
|
handleCLIError3(error, "Pattern analysis");
|
|
1111
1115
|
}
|
|
1112
1116
|
}
|
|
1113
|
-
var
|
|
1117
|
+
var PATTERNS_HELP_TEXT = `
|
|
1114
1118
|
EXAMPLES:
|
|
1115
1119
|
$ aiready patterns # Default analysis
|
|
1116
1120
|
$ aiready patterns --similarity 0.6 # Stricter matching
|
|
@@ -1118,7 +1122,7 @@ EXAMPLES:
|
|
|
1118
1122
|
`;
|
|
1119
1123
|
|
|
1120
1124
|
// src/commands/context.ts
|
|
1121
|
-
import
|
|
1125
|
+
import chalk8 from "chalk";
|
|
1122
1126
|
import { resolve as resolvePath5 } from "path";
|
|
1123
1127
|
import {
|
|
1124
1128
|
loadMergedConfig as loadMergedConfig3,
|
|
@@ -1129,7 +1133,7 @@ import {
|
|
|
1129
1133
|
formatToolScore as formatToolScore2
|
|
1130
1134
|
} from "@aiready/core";
|
|
1131
1135
|
async function contextAction(directory, options) {
|
|
1132
|
-
console.log(
|
|
1136
|
+
console.log(chalk8.blue("\u{1F9E0} Analyzing context costs...\n"));
|
|
1133
1137
|
const startTime = Date.now();
|
|
1134
1138
|
const resolvedDir = resolvePath5(process.cwd(), directory ?? ".");
|
|
1135
1139
|
try {
|
|
@@ -1197,85 +1201,85 @@ async function contextAction(directory, options) {
|
|
|
1197
1201
|
const terminalWidth = process.stdout.columns ?? 80;
|
|
1198
1202
|
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
1199
1203
|
const divider = "\u2501".repeat(dividerWidth);
|
|
1200
|
-
console.log(
|
|
1201
|
-
console.log(
|
|
1202
|
-
console.log(
|
|
1204
|
+
console.log(chalk8.cyan(divider));
|
|
1205
|
+
console.log(chalk8.bold.white(" CONTEXT ANALYSIS SUMMARY"));
|
|
1206
|
+
console.log(chalk8.cyan(divider) + "\n");
|
|
1203
1207
|
console.log(
|
|
1204
|
-
|
|
1208
|
+
chalk8.white(`\u{1F4C1} Files analyzed: ${chalk8.bold(summary.totalFiles)}`)
|
|
1205
1209
|
);
|
|
1206
1210
|
console.log(
|
|
1207
|
-
|
|
1208
|
-
`\u{1F4CA} Total tokens: ${
|
|
1211
|
+
chalk8.white(
|
|
1212
|
+
`\u{1F4CA} Total tokens: ${chalk8.bold(summary.totalTokens.toLocaleString())}`
|
|
1209
1213
|
)
|
|
1210
1214
|
);
|
|
1211
1215
|
console.log(
|
|
1212
|
-
|
|
1213
|
-
`\u{1F4B0} Avg context budget: ${
|
|
1216
|
+
chalk8.yellow(
|
|
1217
|
+
`\u{1F4B0} Avg context budget: ${chalk8.bold(summary.avgContextBudget.toFixed(0))} tokens/file`
|
|
1214
1218
|
)
|
|
1215
1219
|
);
|
|
1216
1220
|
console.log(
|
|
1217
|
-
|
|
1221
|
+
chalk8.white(`\u23F1 Analysis time: ${chalk8.bold(elapsedTime + "s")}
|
|
1218
1222
|
`)
|
|
1219
1223
|
);
|
|
1220
1224
|
const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
|
|
1221
1225
|
if (totalIssues > 0) {
|
|
1222
|
-
console.log(
|
|
1226
|
+
console.log(chalk8.bold("\u26A0\uFE0F Issues Found:\n"));
|
|
1223
1227
|
if (summary.criticalIssues > 0) {
|
|
1224
1228
|
console.log(
|
|
1225
|
-
|
|
1229
|
+
chalk8.red(` \u{1F534} Critical: ${chalk8.bold(summary.criticalIssues)}`)
|
|
1226
1230
|
);
|
|
1227
1231
|
}
|
|
1228
1232
|
if (summary.majorIssues > 0) {
|
|
1229
1233
|
console.log(
|
|
1230
|
-
|
|
1234
|
+
chalk8.yellow(` \u{1F7E1} Major: ${chalk8.bold(summary.majorIssues)}`)
|
|
1231
1235
|
);
|
|
1232
1236
|
}
|
|
1233
1237
|
if (summary.minorIssues > 0) {
|
|
1234
1238
|
console.log(
|
|
1235
|
-
|
|
1239
|
+
chalk8.blue(` \u{1F535} Minor: ${chalk8.bold(summary.minorIssues)}`)
|
|
1236
1240
|
);
|
|
1237
1241
|
}
|
|
1238
1242
|
console.log(
|
|
1239
|
-
|
|
1243
|
+
chalk8.green(
|
|
1240
1244
|
`
|
|
1241
|
-
\u{1F4A1} Potential savings: ${
|
|
1245
|
+
\u{1F4A1} Potential savings: ${chalk8.bold(summary.totalPotentialSavings.toLocaleString())} tokens
|
|
1242
1246
|
`
|
|
1243
1247
|
)
|
|
1244
1248
|
);
|
|
1245
1249
|
} else {
|
|
1246
|
-
console.log(
|
|
1250
|
+
console.log(chalk8.green("\u2705 No significant issues found!\n"));
|
|
1247
1251
|
}
|
|
1248
1252
|
if (summary.deepFiles.length > 0) {
|
|
1249
|
-
console.log(
|
|
1253
|
+
console.log(chalk8.bold("\u{1F4CF} Deep Import Chains:\n"));
|
|
1250
1254
|
console.log(
|
|
1251
|
-
|
|
1255
|
+
chalk8.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`)
|
|
1252
1256
|
);
|
|
1253
1257
|
console.log(
|
|
1254
|
-
|
|
1258
|
+
chalk8.gray(` Maximum depth: ${summary.maxImportDepth}
|
|
1255
1259
|
`)
|
|
1256
1260
|
);
|
|
1257
1261
|
summary.deepFiles.slice(0, 10).forEach((item) => {
|
|
1258
1262
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1259
1263
|
console.log(
|
|
1260
|
-
` ${
|
|
1264
|
+
` ${chalk8.cyan("\u2192")} ${chalk8.white(fileName)} ${chalk8.dim(`(depth: ${item.depth})`)}`
|
|
1261
1265
|
);
|
|
1262
1266
|
});
|
|
1263
1267
|
console.log();
|
|
1264
1268
|
}
|
|
1265
1269
|
if (summary.fragmentedModules.length > 0) {
|
|
1266
|
-
console.log(
|
|
1270
|
+
console.log(chalk8.bold("\u{1F9E9} Fragmented Modules:\n"));
|
|
1267
1271
|
console.log(
|
|
1268
|
-
|
|
1272
|
+
chalk8.gray(
|
|
1269
1273
|
` Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(0)}%
|
|
1270
1274
|
`
|
|
1271
1275
|
)
|
|
1272
1276
|
);
|
|
1273
1277
|
summary.fragmentedModules.slice(0, 10).forEach((module) => {
|
|
1274
1278
|
console.log(
|
|
1275
|
-
` ${
|
|
1279
|
+
` ${chalk8.yellow("\u25CF")} ${chalk8.white(module.domain)} - ${chalk8.dim(`${module.files.length} files, ${(module.fragmentationScore * 100).toFixed(0)}% scattered`)}`
|
|
1276
1280
|
);
|
|
1277
1281
|
console.log(
|
|
1278
|
-
|
|
1282
|
+
chalk8.dim(
|
|
1279
1283
|
` Token cost: ${module.totalTokens.toLocaleString()}, Cohesion: ${(module.avgCohesion * 100).toFixed(0)}%`
|
|
1280
1284
|
)
|
|
1281
1285
|
);
|
|
@@ -1283,9 +1287,9 @@ async function contextAction(directory, options) {
|
|
|
1283
1287
|
console.log();
|
|
1284
1288
|
}
|
|
1285
1289
|
if (summary.lowCohesionFiles.length > 0) {
|
|
1286
|
-
console.log(
|
|
1290
|
+
console.log(chalk8.bold("\u{1F500} Low Cohesion Files:\n"));
|
|
1287
1291
|
console.log(
|
|
1288
|
-
|
|
1292
|
+
chalk8.gray(
|
|
1289
1293
|
` Average cohesion: ${(summary.avgCohesion * 100).toFixed(0)}%
|
|
1290
1294
|
`
|
|
1291
1295
|
)
|
|
@@ -1293,28 +1297,28 @@ async function contextAction(directory, options) {
|
|
|
1293
1297
|
summary.lowCohesionFiles.slice(0, 10).forEach((item) => {
|
|
1294
1298
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1295
1299
|
const scorePercent = (item.score * 100).toFixed(0);
|
|
1296
|
-
const color = item.score < 0.4 ?
|
|
1300
|
+
const color = item.score < 0.4 ? chalk8.red : chalk8.yellow;
|
|
1297
1301
|
console.log(
|
|
1298
|
-
` ${color("\u25CB")} ${
|
|
1302
|
+
` ${color("\u25CB")} ${chalk8.white(fileName)} ${chalk8.dim(`(${scorePercent}% cohesion)`)}`
|
|
1299
1303
|
);
|
|
1300
1304
|
});
|
|
1301
1305
|
console.log();
|
|
1302
1306
|
}
|
|
1303
1307
|
if (summary.topExpensiveFiles.length > 0) {
|
|
1304
|
-
console.log(
|
|
1308
|
+
console.log(chalk8.bold("\u{1F4B8} Most Expensive Files (Context Budget):\n"));
|
|
1305
1309
|
summary.topExpensiveFiles.slice(0, 10).forEach((item) => {
|
|
1306
1310
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1307
|
-
const severityColor = item.severity === "critical" ?
|
|
1311
|
+
const severityColor = item.severity === "critical" ? chalk8.red : item.severity === "major" ? chalk8.yellow : chalk8.blue;
|
|
1308
1312
|
console.log(
|
|
1309
|
-
` ${severityColor("\u25CF")} ${
|
|
1313
|
+
` ${severityColor("\u25CF")} ${chalk8.white(fileName)} ${chalk8.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`
|
|
1310
1314
|
);
|
|
1311
1315
|
});
|
|
1312
1316
|
console.log();
|
|
1313
1317
|
}
|
|
1314
1318
|
if (contextScore) {
|
|
1315
|
-
console.log(
|
|
1316
|
-
console.log(
|
|
1317
|
-
console.log(
|
|
1319
|
+
console.log(chalk8.cyan(divider));
|
|
1320
|
+
console.log(chalk8.bold.white(" AI READINESS SCORE (Context)"));
|
|
1321
|
+
console.log(chalk8.cyan(divider) + "\n");
|
|
1318
1322
|
console.log(formatToolScore2(contextScore));
|
|
1319
1323
|
console.log();
|
|
1320
1324
|
}
|
|
@@ -1325,7 +1329,7 @@ async function contextAction(directory, options) {
|
|
|
1325
1329
|
}
|
|
1326
1330
|
|
|
1327
1331
|
// src/commands/consistency.ts
|
|
1328
|
-
import
|
|
1332
|
+
import chalk9 from "chalk";
|
|
1329
1333
|
import { writeFileSync as writeFileSync3 } from "fs";
|
|
1330
1334
|
import { resolve as resolvePath6 } from "path";
|
|
1331
1335
|
import {
|
|
@@ -1337,7 +1341,7 @@ import {
|
|
|
1337
1341
|
formatToolScore as formatToolScore3
|
|
1338
1342
|
} from "@aiready/core";
|
|
1339
1343
|
async function consistencyAction(directory, options) {
|
|
1340
|
-
console.log(
|
|
1344
|
+
console.log(chalk9.blue("\u{1F50D} Analyzing consistency...\n"));
|
|
1341
1345
|
const startTime = Date.now();
|
|
1342
1346
|
const resolvedDir = resolvePath6(process.cwd(), directory ?? ".");
|
|
1343
1347
|
try {
|
|
@@ -1399,23 +1403,23 @@ async function consistencyAction(directory, options) {
|
|
|
1399
1403
|
resolvedDir
|
|
1400
1404
|
);
|
|
1401
1405
|
writeFileSync3(outputPath, markdown);
|
|
1402
|
-
console.log(
|
|
1406
|
+
console.log(chalk9.green(`\u2705 Report saved to ${outputPath}`));
|
|
1403
1407
|
} else {
|
|
1404
|
-
console.log(
|
|
1408
|
+
console.log(chalk9.bold("\n\u{1F4CA} Summary\n"));
|
|
1405
1409
|
console.log(
|
|
1406
|
-
`Files Analyzed: ${
|
|
1410
|
+
`Files Analyzed: ${chalk9.cyan(report.summary.filesAnalyzed)}`
|
|
1407
1411
|
);
|
|
1408
|
-
console.log(`Total Issues: ${
|
|
1409
|
-
console.log(` Naming: ${
|
|
1410
|
-
console.log(` Patterns: ${
|
|
1412
|
+
console.log(`Total Issues: ${chalk9.yellow(report.summary.totalIssues)}`);
|
|
1413
|
+
console.log(` Naming: ${chalk9.yellow(report.summary.namingIssues)}`);
|
|
1414
|
+
console.log(` Patterns: ${chalk9.yellow(report.summary.patternIssues)}`);
|
|
1411
1415
|
console.log(
|
|
1412
|
-
` Architecture: ${
|
|
1416
|
+
` Architecture: ${chalk9.yellow(report.summary.architectureIssues ?? 0)}`
|
|
1413
1417
|
);
|
|
1414
|
-
console.log(`Analysis Time: ${
|
|
1418
|
+
console.log(`Analysis Time: ${chalk9.gray(elapsedTime + "s")}
|
|
1415
1419
|
`);
|
|
1416
1420
|
if (report.summary.totalIssues === 0) {
|
|
1417
1421
|
console.log(
|
|
1418
|
-
|
|
1422
|
+
chalk9.green(
|
|
1419
1423
|
"\u2728 No consistency issues found! Your codebase is well-maintained.\n"
|
|
1420
1424
|
)
|
|
1421
1425
|
);
|
|
@@ -1427,20 +1431,20 @@ async function consistencyAction(directory, options) {
|
|
|
1427
1431
|
(r) => r.issues.some((i) => i.category === "patterns")
|
|
1428
1432
|
);
|
|
1429
1433
|
if (namingResults.length > 0) {
|
|
1430
|
-
console.log(
|
|
1434
|
+
console.log(chalk9.bold("\u{1F3F7}\uFE0F Naming Issues\n"));
|
|
1431
1435
|
let shown = 0;
|
|
1432
|
-
for (const
|
|
1436
|
+
for (const namingFileResult of namingResults) {
|
|
1433
1437
|
if (shown >= 5) break;
|
|
1434
|
-
for (const issue of
|
|
1438
|
+
for (const issue of namingFileResult.issues) {
|
|
1435
1439
|
if (shown >= 5) break;
|
|
1436
|
-
const severityColor = issue.severity === "critical" ?
|
|
1440
|
+
const severityColor = issue.severity === "critical" ? chalk9.red : issue.severity === "major" ? chalk9.yellow : issue.severity === "minor" ? chalk9.blue : chalk9.gray;
|
|
1437
1441
|
console.log(
|
|
1438
|
-
`${severityColor(issue.severity.toUpperCase())} ${
|
|
1442
|
+
`${severityColor(issue.severity.toUpperCase())} ${chalk9.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1439
1443
|
);
|
|
1440
1444
|
console.log(` ${issue.message}`);
|
|
1441
1445
|
if (issue.suggestion) {
|
|
1442
1446
|
console.log(
|
|
1443
|
-
` ${
|
|
1447
|
+
` ${chalk9.dim("\u2192")} ${chalk9.italic(issue.suggestion)}`
|
|
1444
1448
|
);
|
|
1445
1449
|
}
|
|
1446
1450
|
console.log();
|
|
@@ -1449,25 +1453,25 @@ async function consistencyAction(directory, options) {
|
|
|
1449
1453
|
}
|
|
1450
1454
|
const remaining = namingResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
1451
1455
|
if (remaining > 0) {
|
|
1452
|
-
console.log(
|
|
1456
|
+
console.log(chalk9.dim(` ... and ${remaining} more issues
|
|
1453
1457
|
`));
|
|
1454
1458
|
}
|
|
1455
1459
|
}
|
|
1456
1460
|
if (patternResults.length > 0) {
|
|
1457
|
-
console.log(
|
|
1461
|
+
console.log(chalk9.bold("\u{1F504} Pattern Issues\n"));
|
|
1458
1462
|
let shown = 0;
|
|
1459
|
-
for (const
|
|
1463
|
+
for (const patternFileResult of patternResults) {
|
|
1460
1464
|
if (shown >= 5) break;
|
|
1461
|
-
for (const issue of
|
|
1465
|
+
for (const issue of patternFileResult.issues) {
|
|
1462
1466
|
if (shown >= 5) break;
|
|
1463
|
-
const severityColor = issue.severity === "critical" ?
|
|
1467
|
+
const severityColor = issue.severity === "critical" ? chalk9.red : issue.severity === "major" ? chalk9.yellow : issue.severity === "minor" ? chalk9.blue : chalk9.gray;
|
|
1464
1468
|
console.log(
|
|
1465
|
-
`${severityColor(issue.severity.toUpperCase())} ${
|
|
1469
|
+
`${severityColor(issue.severity.toUpperCase())} ${chalk9.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1466
1470
|
);
|
|
1467
1471
|
console.log(` ${issue.message}`);
|
|
1468
1472
|
if (issue.suggestion) {
|
|
1469
1473
|
console.log(
|
|
1470
|
-
` ${
|
|
1474
|
+
` ${chalk9.dim("\u2192")} ${chalk9.italic(issue.suggestion)}`
|
|
1471
1475
|
);
|
|
1472
1476
|
}
|
|
1473
1477
|
console.log();
|
|
@@ -1476,12 +1480,12 @@ async function consistencyAction(directory, options) {
|
|
|
1476
1480
|
}
|
|
1477
1481
|
const remaining = patternResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
1478
1482
|
if (remaining > 0) {
|
|
1479
|
-
console.log(
|
|
1483
|
+
console.log(chalk9.dim(` ... and ${remaining} more issues
|
|
1480
1484
|
`));
|
|
1481
1485
|
}
|
|
1482
1486
|
}
|
|
1483
1487
|
if (report.recommendations.length > 0) {
|
|
1484
|
-
console.log(
|
|
1488
|
+
console.log(chalk9.bold("\u{1F4A1} Recommendations\n"));
|
|
1485
1489
|
report.recommendations.forEach((rec, i) => {
|
|
1486
1490
|
console.log(`${i + 1}. ${rec}`);
|
|
1487
1491
|
});
|
|
@@ -1489,7 +1493,7 @@ async function consistencyAction(directory, options) {
|
|
|
1489
1493
|
}
|
|
1490
1494
|
}
|
|
1491
1495
|
if (consistencyScore) {
|
|
1492
|
-
console.log(
|
|
1496
|
+
console.log(chalk9.bold("\n\u{1F4CA} AI Readiness Score (Consistency)\n"));
|
|
1493
1497
|
console.log(formatToolScore3(consistencyScore));
|
|
1494
1498
|
console.log();
|
|
1495
1499
|
}
|
|
@@ -1500,7 +1504,7 @@ async function consistencyAction(directory, options) {
|
|
|
1500
1504
|
}
|
|
1501
1505
|
|
|
1502
1506
|
// src/commands/visualize.ts
|
|
1503
|
-
import
|
|
1507
|
+
import chalk10 from "chalk";
|
|
1504
1508
|
import { writeFileSync as writeFileSync4, readFileSync as readFileSync3, existsSync as existsSync3, copyFileSync } from "fs";
|
|
1505
1509
|
import { resolve as resolvePath7 } from "path";
|
|
1506
1510
|
import { spawn } from "child_process";
|
|
@@ -1515,12 +1519,12 @@ async function visualizeAction(directory, options) {
|
|
|
1515
1519
|
if (latestScan) {
|
|
1516
1520
|
reportPath = latestScan;
|
|
1517
1521
|
console.log(
|
|
1518
|
-
|
|
1522
|
+
chalk10.dim(`Found latest report: ${latestScan.split("/").pop()}`)
|
|
1519
1523
|
);
|
|
1520
1524
|
} else {
|
|
1521
|
-
console.error(
|
|
1525
|
+
console.error(chalk10.red("\u274C No AI readiness report found"));
|
|
1522
1526
|
console.log(
|
|
1523
|
-
|
|
1527
|
+
chalk10.dim(
|
|
1524
1528
|
`
|
|
1525
1529
|
Generate a report with:
|
|
1526
1530
|
aiready scan --output json
|
|
@@ -1650,19 +1654,19 @@ Or specify a custom report:
|
|
|
1650
1654
|
return;
|
|
1651
1655
|
} else {
|
|
1652
1656
|
console.log(
|
|
1653
|
-
|
|
1657
|
+
chalk10.yellow(
|
|
1654
1658
|
"\u26A0\uFE0F Dev server not available (requires local @aiready/visualizer with web assets)."
|
|
1655
1659
|
)
|
|
1656
1660
|
);
|
|
1657
1661
|
console.log(
|
|
1658
|
-
|
|
1662
|
+
chalk10.cyan(" Falling back to static HTML generation...\n")
|
|
1659
1663
|
);
|
|
1660
1664
|
useDevMode = false;
|
|
1661
1665
|
}
|
|
1662
1666
|
} catch (err) {
|
|
1663
1667
|
console.error("Failed to start dev server:", err);
|
|
1664
1668
|
console.log(
|
|
1665
|
-
|
|
1669
|
+
chalk10.cyan(" Falling back to static HTML generation...\n")
|
|
1666
1670
|
);
|
|
1667
1671
|
useDevMode = false;
|
|
1668
1672
|
}
|
|
@@ -1672,7 +1676,7 @@ Or specify a custom report:
|
|
|
1672
1676
|
const defaultOutput = "visualization.html";
|
|
1673
1677
|
const outPath = resolvePath7(dirPath, options.output ?? defaultOutput);
|
|
1674
1678
|
writeFileSync4(outPath, html, "utf8");
|
|
1675
|
-
console.log(
|
|
1679
|
+
console.log(chalk10.green(`\u2705 Visualization written to: ${outPath}`));
|
|
1676
1680
|
if (options.open || options.serve) {
|
|
1677
1681
|
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
1678
1682
|
if (options.serve) {
|
|
@@ -1702,7 +1706,7 @@ Or specify a custom report:
|
|
|
1702
1706
|
server.listen(port, () => {
|
|
1703
1707
|
const addr = `http://localhost:${port}/`;
|
|
1704
1708
|
console.log(
|
|
1705
|
-
|
|
1709
|
+
chalk10.cyan(`\u{1F310} Local visualization server running at ${addr}`)
|
|
1706
1710
|
);
|
|
1707
1711
|
spawn(opener, [`"${addr}"`], { shell: true });
|
|
1708
1712
|
});
|
|
@@ -1721,7 +1725,7 @@ Or specify a custom report:
|
|
|
1721
1725
|
handleCLIError6(err, "Visualization");
|
|
1722
1726
|
}
|
|
1723
1727
|
}
|
|
1724
|
-
var
|
|
1728
|
+
var VISUALIZE_HELP_TEXT = `
|
|
1725
1729
|
EXAMPLES:
|
|
1726
1730
|
$ aiready visualize . # Auto-detects latest report, generates HTML
|
|
1727
1731
|
$ aiready visualize . --report .aiready/aiready-report-20260217-143022.json
|
|
@@ -1738,7 +1742,7 @@ NOTES:
|
|
|
1738
1742
|
- --dev starts a Vite dev server with live reload (requires local @aiready/visualizer installation).
|
|
1739
1743
|
When --dev is not available, it falls back to static HTML generation.
|
|
1740
1744
|
`;
|
|
1741
|
-
var
|
|
1745
|
+
var VISUALISE_HELP_TEXT = `
|
|
1742
1746
|
EXAMPLES:
|
|
1743
1747
|
$ aiready visualise . # Auto-detects latest report
|
|
1744
1748
|
$ aiready visualise . --report .aiready/aiready-report-20260217-143022.json
|
|
@@ -1749,14 +1753,14 @@ NOTES:
|
|
|
1749
1753
|
`;
|
|
1750
1754
|
|
|
1751
1755
|
// src/commands/shared/standard-tool-actions.ts
|
|
1752
|
-
import
|
|
1756
|
+
import chalk11 from "chalk";
|
|
1753
1757
|
|
|
1754
1758
|
// src/commands/agent-grounding.ts
|
|
1755
|
-
import
|
|
1759
|
+
import chalk12 from "chalk";
|
|
1756
1760
|
import { loadConfig as loadConfig2, mergeConfigWithDefaults as mergeConfigWithDefaults2 } from "@aiready/core";
|
|
1757
1761
|
|
|
1758
1762
|
// src/commands/testability.ts
|
|
1759
|
-
import
|
|
1763
|
+
import chalk13 from "chalk";
|
|
1760
1764
|
import { loadConfig as loadConfig3, mergeConfigWithDefaults as mergeConfigWithDefaults3 } from "@aiready/core";
|
|
1761
1765
|
async function testabilityAction(directory, options) {
|
|
1762
1766
|
const { analyzeTestability, calculateTestabilityScore } = await import("@aiready/testability");
|
|
@@ -1781,28 +1785,28 @@ async function testabilityAction(directory, options) {
|
|
|
1781
1785
|
"blind-risk": "\u{1F480}"
|
|
1782
1786
|
};
|
|
1783
1787
|
const safetyColors = {
|
|
1784
|
-
safe:
|
|
1785
|
-
"moderate-risk":
|
|
1786
|
-
"high-risk":
|
|
1787
|
-
"blind-risk":
|
|
1788
|
+
safe: chalk13.green,
|
|
1789
|
+
"moderate-risk": chalk13.yellow,
|
|
1790
|
+
"high-risk": chalk13.red,
|
|
1791
|
+
"blind-risk": chalk13.bgRed.white
|
|
1788
1792
|
};
|
|
1789
1793
|
const safety = report.summary.aiChangeSafetyRating;
|
|
1790
1794
|
const icon = safetyIcons[safety] ?? "\u2753";
|
|
1791
|
-
const color = safetyColors[safety] ??
|
|
1795
|
+
const color = safetyColors[safety] ?? chalk13.white;
|
|
1792
1796
|
console.log(
|
|
1793
|
-
` \u{1F9EA} Testability: ${
|
|
1797
|
+
` \u{1F9EA} Testability: ${chalk13.bold(scoring.score + "/100")} (${report.summary.rating})`
|
|
1794
1798
|
);
|
|
1795
1799
|
console.log(
|
|
1796
1800
|
` AI Change Safety: ${color(`${icon} ${safety.toUpperCase()}`)}`
|
|
1797
1801
|
);
|
|
1798
1802
|
console.log(
|
|
1799
|
-
|
|
1803
|
+
chalk13.dim(
|
|
1800
1804
|
` Coverage: ${Math.round(report.summary.coverageRatio * 100)}% (${report.rawData.testFiles} test / ${report.rawData.sourceFiles} source files)`
|
|
1801
1805
|
)
|
|
1802
1806
|
);
|
|
1803
1807
|
if (safety === "blind-risk") {
|
|
1804
1808
|
console.log(
|
|
1805
|
-
|
|
1809
|
+
chalk13.red.bold(
|
|
1806
1810
|
"\n \u26A0\uFE0F NO TESTS \u2014 AI changes to this codebase are completely unverifiable!\n"
|
|
1807
1811
|
)
|
|
1808
1812
|
);
|
|
@@ -1814,7 +1818,7 @@ async function testabilityAction(directory, options) {
|
|
|
1814
1818
|
import { changeAmplificationAction } from "@aiready/change-amplification/dist/cli.js";
|
|
1815
1819
|
|
|
1816
1820
|
// src/commands/bug.ts
|
|
1817
|
-
import
|
|
1821
|
+
import chalk14 from "chalk";
|
|
1818
1822
|
import { execSync } from "child_process";
|
|
1819
1823
|
async function bugAction(message, options) {
|
|
1820
1824
|
const repoUrl = "https://github.com/caopengau/aiready-cli";
|
|
@@ -1832,35 +1836,35 @@ Generated via AIReady CLI 'bug' command.
|
|
|
1832
1836
|
Type: ${type}
|
|
1833
1837
|
`.trim();
|
|
1834
1838
|
if (options.submit) {
|
|
1835
|
-
console.log(
|
|
1839
|
+
console.log(chalk14.blue("\u{1F680} Submitting issue via GitHub CLI...\n"));
|
|
1836
1840
|
try {
|
|
1837
1841
|
execSync("gh auth status", { stdio: "ignore" });
|
|
1838
1842
|
const command = `gh issue create --repo ${repoSlug} --title ${JSON.stringify(title)} --body ${JSON.stringify(body)} --label ${label}`;
|
|
1839
1843
|
const output = execSync(command, { encoding: "utf8" }).trim();
|
|
1840
|
-
console.log(
|
|
1841
|
-
console.log(
|
|
1844
|
+
console.log(chalk14.green("\u2705 Issue Created Successfully!"));
|
|
1845
|
+
console.log(chalk14.cyan(output));
|
|
1842
1846
|
return;
|
|
1843
1847
|
} catch {
|
|
1844
|
-
console.error(
|
|
1848
|
+
console.error(chalk14.red("\n\u274C Failed to submit via gh CLI."));
|
|
1845
1849
|
console.log(
|
|
1846
|
-
|
|
1850
|
+
chalk14.yellow(
|
|
1847
1851
|
' Make sure gh is installed and run "gh auth login".\n'
|
|
1848
1852
|
)
|
|
1849
1853
|
);
|
|
1850
|
-
console.log(
|
|
1854
|
+
console.log(chalk14.dim(" Falling back to URL generation..."));
|
|
1851
1855
|
}
|
|
1852
1856
|
}
|
|
1853
1857
|
const template = type === "bug" ? "bug_report.md" : type === "feature" ? "feature_request.md" : "new_metric_idea.md";
|
|
1854
1858
|
const fullUrl = `${repoUrl}/issues/new?title=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}&labels=${label}&template=${template}`;
|
|
1855
|
-
console.log(
|
|
1856
|
-
console.log(
|
|
1857
|
-
console.log(
|
|
1858
|
-
console.log(
|
|
1859
|
-
console.log(
|
|
1860
|
-
console.log(
|
|
1861
|
-
console.log(
|
|
1859
|
+
console.log(chalk14.green("\u{1F680} Issue Draft Prepared!\n"));
|
|
1860
|
+
console.log(chalk14.bold("Title: ") + title);
|
|
1861
|
+
console.log(chalk14.bold("Type: ") + type);
|
|
1862
|
+
console.log(chalk14.bold("\nClick the link below to submit this issue:"));
|
|
1863
|
+
console.log(chalk14.cyan(fullUrl));
|
|
1864
|
+
console.log(chalk14.bold("\n\u{1F916} [FOR AI AGENTS]:"));
|
|
1865
|
+
console.log(chalk14.dim(" You have successfully prepared a report."));
|
|
1862
1866
|
console.log(
|
|
1863
|
-
|
|
1867
|
+
chalk14.dim(
|
|
1864
1868
|
" Please present the URL above to the user so they can finalize the submission."
|
|
1865
1869
|
)
|
|
1866
1870
|
);
|
|
@@ -1869,19 +1873,19 @@ Type: ${type}
|
|
|
1869
1873
|
const bugUrl = `${repoUrl}/issues/new?template=bug_report.md`;
|
|
1870
1874
|
const featureUrl = `${repoUrl}/issues/new?template=feature_request.md`;
|
|
1871
1875
|
const metricUrl = `${repoUrl}/issues/new?template=new_metric_idea.md`;
|
|
1872
|
-
console.log(
|
|
1873
|
-
console.log(` Report a Bug: ${
|
|
1874
|
-
console.log(` Request a Feature: ${
|
|
1875
|
-
console.log(` Suggest a Metric: ${
|
|
1876
|
-
console.log(
|
|
1877
|
-
console.log(
|
|
1876
|
+
console.log(chalk14.blue("\u{1F4AC} Feedback & Bug Reports\n"));
|
|
1877
|
+
console.log(` Report a Bug: ${chalk14.cyan(bugUrl)}`);
|
|
1878
|
+
console.log(` Request a Feature: ${chalk14.cyan(featureUrl)}`);
|
|
1879
|
+
console.log(` Suggest a Metric: ${chalk14.cyan(metricUrl)}`);
|
|
1880
|
+
console.log(chalk14.bold("\n\u{1F916} [FOR AI AGENTS]:"));
|
|
1881
|
+
console.log(chalk14.dim(" To prepare a specific report, run:"));
|
|
1878
1882
|
console.log(
|
|
1879
|
-
|
|
1883
|
+
chalk14.cyan(
|
|
1880
1884
|
' aiready bug "your description here" --type bug|feature|metric'
|
|
1881
1885
|
)
|
|
1882
1886
|
);
|
|
1883
1887
|
}
|
|
1884
|
-
var
|
|
1888
|
+
var BUG_HELP_TEXT = `
|
|
1885
1889
|
EXAMPLES:
|
|
1886
1890
|
$ aiready bug # Show general links
|
|
1887
1891
|
$ aiready bug "Naming check is too slow" # Prepare a pre-filled bug report
|
|
@@ -1957,7 +1961,7 @@ program.command("scan").description(
|
|
|
1957
1961
|
"--fail-on <level>",
|
|
1958
1962
|
"Fail on issues: critical, major, any",
|
|
1959
1963
|
"critical"
|
|
1960
|
-
).option("--api-key <key>", "Platform API key for automatic upload").option("--upload", "Automatically upload results to the platform").option("--server <url>", "Custom platform URL").addHelpText("after",
|
|
1964
|
+
).option("--api-key <key>", "Platform API key for automatic upload").option("--upload", "Automatically upload results to the platform").option("--server <url>", "Custom platform URL").addHelpText("after", SCAN_HELP_TEXT).action(async (directory, options) => {
|
|
1961
1965
|
await scanAction(directory, options);
|
|
1962
1966
|
});
|
|
1963
1967
|
program.command("init").description("Generate a default configuration (aiready.json)").option("-f, --force", "Overwrite existing configuration file").option(
|
|
@@ -1976,7 +1980,7 @@ program.command("patterns").description("Detect duplicate code patterns that con
|
|
|
1976
1980
|
).option(
|
|
1977
1981
|
"--full-scan",
|
|
1978
1982
|
"Disable smart defaults for comprehensive analysis (slower)"
|
|
1979
|
-
).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score (0-100)", true).option("--no-score", "Disable calculating AI Readiness Score").addHelpText("after",
|
|
1983
|
+
).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score (0-100)", true).option("--no-score", "Disable calculating AI Readiness Score").addHelpText("after", PATTERNS_HELP_TEXT).action(async (directory, options) => {
|
|
1980
1984
|
await patternsAction(directory, options);
|
|
1981
1985
|
});
|
|
1982
1986
|
program.command("context").description("Analyze context window costs and dependency fragmentation").argument("[directory]", "Directory to analyze", ".").option("--max-depth <number>", "Maximum acceptable import depth", "5").option(
|
|
@@ -2012,7 +2016,7 @@ program.command("visualise").description("Alias for visualize (British spelling)
|
|
|
2012
2016
|
"--dev",
|
|
2013
2017
|
"Start Vite dev server (live reload) for interactive development",
|
|
2014
2018
|
true
|
|
2015
|
-
).addHelpText("after",
|
|
2019
|
+
).addHelpText("after", VISUALISE_HELP_TEXT).action(async (directory, options) => {
|
|
2016
2020
|
await visualizeAction(directory, options);
|
|
2017
2021
|
});
|
|
2018
2022
|
program.command("visualize").description("Generate interactive visualization from an AIReady report").argument("[directory]", "Directory to analyze", ".").option(
|
|
@@ -2030,7 +2034,7 @@ program.command("visualize").description("Generate interactive visualization fro
|
|
|
2030
2034
|
"--dev",
|
|
2031
2035
|
"Start Vite dev server (live reload) for interactive development",
|
|
2032
2036
|
false
|
|
2033
|
-
).addHelpText("after",
|
|
2037
|
+
).addHelpText("after", VISUALIZE_HELP_TEXT).action(async (directory, options) => {
|
|
2034
2038
|
await visualizeAction(directory, options);
|
|
2035
2039
|
});
|
|
2036
2040
|
program.command("change-amplification").description("Analyze graph metrics for change amplification").argument("[directory]", "Directory to analyze", ".").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").action(async (directory, options) => {
|
|
@@ -2039,10 +2043,10 @@ program.command("change-amplification").description("Analyze graph metrics for c
|
|
|
2039
2043
|
program.command("testability").description("Analyze test coverage and AI readiness").argument("[directory]", "Directory to analyze", ".").option("--min-coverage <ratio>", "Minimum acceptable coverage ratio", "0.3").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").action(async (directory, options) => {
|
|
2040
2044
|
await testabilityAction(directory, options);
|
|
2041
2045
|
});
|
|
2042
|
-
program.command("upload").description("Upload an AIReady report JSON to the platform").argument("<file>", "Report JSON file to upload").option("--api-key <key>", "Platform API key").option("--repo-id <id>", "Platform repository ID (optional)").option("--server <url>", "Custom platform URL").addHelpText("after",
|
|
2046
|
+
program.command("upload").description("Upload an AIReady report JSON to the platform").argument("<file>", "Report JSON file to upload").option("--api-key <key>", "Platform API key").option("--repo-id <id>", "Platform repository ID (optional)").option("--server <url>", "Custom platform URL").addHelpText("after", UPLOAD_HELP_TEXT).action(async (file, options) => {
|
|
2043
2047
|
await uploadAction(file, options);
|
|
2044
2048
|
});
|
|
2045
|
-
program.command("bug").description("Report a bug or provide feedback (Agent-friendly)").argument("[message]", "Short description of the issue").option("-t, --type <type>", "Issue type: bug, feature, metric", "bug").option("--submit", "Submit the issue directly using the GitHub CLI (gh)").addHelpText("after",
|
|
2049
|
+
program.command("bug").description("Report a bug or provide feedback (Agent-friendly)").argument("[message]", "Short description of the issue").option("-t, --type <type>", "Issue type: bug, feature, metric", "bug").option("--submit", "Submit the issue directly using the GitHub CLI (gh)").addHelpText("after", BUG_HELP_TEXT).action(async (message, options) => {
|
|
2046
2050
|
await bugAction(message, options);
|
|
2047
2051
|
});
|
|
2048
2052
|
program.parse();
|