@aiready/cli 0.13.7 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +10 -10
- package/.turbo/turbo-test.log +32 -32
- package/dist/cli.js +290 -188
- package/dist/cli.mjs +239 -137
- package/package.json +12 -12
- package/packages/core/src/.aiready/aiready-report-20260314-161145.json +230 -0
- package/packages/core/src/.aiready/aiready-report-20260314-161152.json +253 -0
- package/packages/pattern-detect/src/.aiready/aiready-report-20260314-161139.json +230 -0
- package/src/.aiready/aiready-report-20260312-110843.json +28746 -0
- package/src/.aiready/aiready-report-20260312-110955.json +28746 -0
- package/src/cli.ts +15 -0
- package/src/commands/index.ts +1 -0
- package/src/commands/init.ts +97 -0
package/dist/cli.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
// src/cli.ts
|
|
9
9
|
import { Command } from "commander";
|
|
10
10
|
import { readFileSync as readFileSync4 } from "fs";
|
|
11
|
-
import { join, dirname } from "path";
|
|
11
|
+
import { join as join2, dirname } from "path";
|
|
12
12
|
import { fileURLToPath } from "url";
|
|
13
13
|
|
|
14
14
|
// src/commands/scan.ts
|
|
@@ -643,8 +643,103 @@ async function scanAction(directory, options) {
|
|
|
643
643
|
}
|
|
644
644
|
var scanHelpText = `...`;
|
|
645
645
|
|
|
646
|
-
// src/commands/
|
|
646
|
+
// src/commands/init.ts
|
|
647
|
+
import { writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
|
|
648
|
+
import { join } from "path";
|
|
647
649
|
import chalk4 from "chalk";
|
|
650
|
+
import { ToolName as ToolName2 } from "@aiready/core";
|
|
651
|
+
async function initAction(options) {
|
|
652
|
+
const fileExt = options.format === "js" ? "js" : "json";
|
|
653
|
+
const fileName = fileExt === "js" ? "aiready.config.js" : "aiready.json";
|
|
654
|
+
const filePath = join(process.cwd(), fileName);
|
|
655
|
+
if (existsSync2(filePath) && !options.force) {
|
|
656
|
+
console.error(
|
|
657
|
+
chalk4.red(`Error: ${fileName} already exists. Use --force to overwrite.`)
|
|
658
|
+
);
|
|
659
|
+
process.exit(1);
|
|
660
|
+
}
|
|
661
|
+
const defaultConfig = {
|
|
662
|
+
scan: {
|
|
663
|
+
include: [
|
|
664
|
+
"src/**/*.ts",
|
|
665
|
+
"src/**/*.js",
|
|
666
|
+
"lib/**/*.ts",
|
|
667
|
+
"packages/*/src/**/*.ts"
|
|
668
|
+
],
|
|
669
|
+
exclude: [
|
|
670
|
+
"**/node_modules/**",
|
|
671
|
+
"**/dist/**",
|
|
672
|
+
"**/build/**",
|
|
673
|
+
"**/*.test.ts",
|
|
674
|
+
"**/*.spec.ts"
|
|
675
|
+
],
|
|
676
|
+
tools: [
|
|
677
|
+
ToolName2.PatternDetect,
|
|
678
|
+
ToolName2.ContextAnalyzer,
|
|
679
|
+
ToolName2.NamingConsistency,
|
|
680
|
+
ToolName2.AiSignalClarity,
|
|
681
|
+
ToolName2.AgentGrounding,
|
|
682
|
+
ToolName2.TestabilityIndex,
|
|
683
|
+
ToolName2.DocDrift,
|
|
684
|
+
ToolName2.DependencyHealth,
|
|
685
|
+
ToolName2.ChangeAmplification
|
|
686
|
+
]
|
|
687
|
+
},
|
|
688
|
+
tools: {
|
|
689
|
+
[ToolName2.PatternDetect]: {
|
|
690
|
+
minSimilarity: 0.8,
|
|
691
|
+
minLines: 5
|
|
692
|
+
},
|
|
693
|
+
[ToolName2.ContextAnalyzer]: {
|
|
694
|
+
maxContextBudget: 128e3,
|
|
695
|
+
minCohesion: 0.6
|
|
696
|
+
},
|
|
697
|
+
[ToolName2.NamingConsistency]: {
|
|
698
|
+
shortWords: ["id", "db", "ui", "ai"]
|
|
699
|
+
},
|
|
700
|
+
[ToolName2.AiSignalClarity]: {
|
|
701
|
+
checkMagicLiterals: true,
|
|
702
|
+
checkBooleanTraps: true,
|
|
703
|
+
checkAmbiguousNames: true,
|
|
704
|
+
checkUndocumentedExports: true
|
|
705
|
+
}
|
|
706
|
+
},
|
|
707
|
+
scoring: {
|
|
708
|
+
threshold: 70,
|
|
709
|
+
showBreakdown: true
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
let content = "";
|
|
713
|
+
if (fileExt === "js") {
|
|
714
|
+
content = `/** @type {import('@aiready/core').AIReadyConfig} */
|
|
715
|
+
module.exports = ${JSON.stringify(
|
|
716
|
+
defaultConfig,
|
|
717
|
+
null,
|
|
718
|
+
2
|
|
719
|
+
)};
|
|
720
|
+
`;
|
|
721
|
+
} else {
|
|
722
|
+
content = JSON.stringify(defaultConfig, null, 2);
|
|
723
|
+
}
|
|
724
|
+
try {
|
|
725
|
+
writeFileSync2(filePath, content, "utf8");
|
|
726
|
+
console.log(
|
|
727
|
+
chalk4.green(`
|
|
728
|
+
\u2705 Created default configuration: ${chalk4.bold(fileName)}`)
|
|
729
|
+
);
|
|
730
|
+
console.log(
|
|
731
|
+
chalk4.cyan("You can now fine-tune your settings and run AIReady with:")
|
|
732
|
+
);
|
|
733
|
+
console.log(chalk4.white(` $ aiready scan
|
|
734
|
+
`));
|
|
735
|
+
} catch (error) {
|
|
736
|
+
console.error(chalk4.red(`Failed to write configuration file: ${error}`));
|
|
737
|
+
process.exit(1);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// src/commands/patterns.ts
|
|
742
|
+
import chalk5 from "chalk";
|
|
648
743
|
import { resolve as resolvePath4 } from "path";
|
|
649
744
|
import {
|
|
650
745
|
loadMergedConfig as loadMergedConfig2,
|
|
@@ -655,7 +750,7 @@ import {
|
|
|
655
750
|
formatToolScore as formatToolScore2
|
|
656
751
|
} from "@aiready/core";
|
|
657
752
|
async function patternsAction(directory, options) {
|
|
658
|
-
console.log(
|
|
753
|
+
console.log(chalk5.blue("\u{1F50D} Analyzing patterns...\n"));
|
|
659
754
|
const startTime = Date.now();
|
|
660
755
|
const resolvedDir = resolvePath4(process.cwd(), directory || ".");
|
|
661
756
|
try {
|
|
@@ -721,38 +816,38 @@ async function patternsAction(directory, options) {
|
|
|
721
816
|
const terminalWidth = process.stdout.columns || 80;
|
|
722
817
|
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
723
818
|
const divider = "\u2501".repeat(dividerWidth);
|
|
724
|
-
console.log(
|
|
725
|
-
console.log(
|
|
726
|
-
console.log(
|
|
819
|
+
console.log(chalk5.cyan(divider));
|
|
820
|
+
console.log(chalk5.bold.white(" PATTERN ANALYSIS SUMMARY"));
|
|
821
|
+
console.log(chalk5.cyan(divider) + "\n");
|
|
727
822
|
console.log(
|
|
728
|
-
|
|
823
|
+
chalk5.white(`\u{1F4C1} Files analyzed: ${chalk5.bold(results.length)}`)
|
|
729
824
|
);
|
|
730
825
|
console.log(
|
|
731
|
-
|
|
732
|
-
`\u26A0 Duplicate patterns found: ${
|
|
826
|
+
chalk5.yellow(
|
|
827
|
+
`\u26A0 Duplicate patterns found: ${chalk5.bold(summary.totalPatterns)}`
|
|
733
828
|
)
|
|
734
829
|
);
|
|
735
830
|
console.log(
|
|
736
|
-
|
|
737
|
-
`\u{1F4B0} Token cost (wasted): ${
|
|
831
|
+
chalk5.red(
|
|
832
|
+
`\u{1F4B0} Token cost (wasted): ${chalk5.bold(summary.totalTokenCost.toLocaleString())}`
|
|
738
833
|
)
|
|
739
834
|
);
|
|
740
835
|
console.log(
|
|
741
|
-
|
|
836
|
+
chalk5.gray(`\u23F1 Analysis time: ${chalk5.bold(elapsedTime + "s")}`)
|
|
742
837
|
);
|
|
743
838
|
const sortedTypes = Object.entries(summary.patternsByType || {}).filter(([, count]) => count > 0).sort(([, a], [, b]) => b - a);
|
|
744
839
|
if (sortedTypes.length > 0) {
|
|
745
|
-
console.log(
|
|
746
|
-
console.log(
|
|
747
|
-
console.log(
|
|
840
|
+
console.log(chalk5.cyan("\n" + divider));
|
|
841
|
+
console.log(chalk5.bold.white(" PATTERNS BY TYPE"));
|
|
842
|
+
console.log(chalk5.cyan(divider) + "\n");
|
|
748
843
|
sortedTypes.forEach(([type, count]) => {
|
|
749
|
-
console.log(` ${
|
|
844
|
+
console.log(` ${chalk5.white(type.padEnd(15))} ${chalk5.bold(count)}`);
|
|
750
845
|
});
|
|
751
846
|
}
|
|
752
847
|
if (summary.totalPatterns > 0 && duplicates.length > 0) {
|
|
753
|
-
console.log(
|
|
754
|
-
console.log(
|
|
755
|
-
console.log(
|
|
848
|
+
console.log(chalk5.cyan("\n" + divider));
|
|
849
|
+
console.log(chalk5.bold.white(" TOP DUPLICATE PATTERNS"));
|
|
850
|
+
console.log(chalk5.cyan(divider) + "\n");
|
|
756
851
|
const topDuplicates = [...duplicates].sort((a, b) => b.similarity - a.similarity).slice(0, 10);
|
|
757
852
|
topDuplicates.forEach((dup) => {
|
|
758
853
|
const severity = dup.similarity > 0.95 ? "CRITICAL" : dup.similarity > 0.9 ? "HIGH" : "MEDIUM";
|
|
@@ -760,25 +855,25 @@ async function patternsAction(directory, options) {
|
|
|
760
855
|
const file1Name = dup.file1.split("/").pop() || dup.file1;
|
|
761
856
|
const file2Name = dup.file2.split("/").pop() || dup.file2;
|
|
762
857
|
console.log(
|
|
763
|
-
`${severityIcon} ${severity}: ${
|
|
858
|
+
`${severityIcon} ${severity}: ${chalk5.bold(file1Name)} \u2194 ${chalk5.bold(file2Name)}`
|
|
764
859
|
);
|
|
765
860
|
console.log(
|
|
766
|
-
` Similarity: ${
|
|
861
|
+
` Similarity: ${chalk5.bold(Math.round(dup.similarity * 100) + "%")} | Wasted: ${chalk5.bold(dup.tokenCost.toLocaleString())} tokens each`
|
|
767
862
|
);
|
|
768
863
|
console.log(
|
|
769
|
-
` Lines: ${
|
|
864
|
+
` Lines: ${chalk5.cyan(dup.line1 + "-" + dup.endLine1)} \u2194 ${chalk5.cyan(dup.line2 + "-" + dup.endLine2)}
|
|
770
865
|
`
|
|
771
866
|
);
|
|
772
867
|
});
|
|
773
868
|
} else {
|
|
774
869
|
console.log(
|
|
775
|
-
|
|
870
|
+
chalk5.green("\n\u2728 Great! No duplicate patterns detected.\n")
|
|
776
871
|
);
|
|
777
872
|
}
|
|
778
873
|
if (patternScore) {
|
|
779
|
-
console.log(
|
|
780
|
-
console.log(
|
|
781
|
-
console.log(
|
|
874
|
+
console.log(chalk5.cyan(divider));
|
|
875
|
+
console.log(chalk5.bold.white(" AI READINESS SCORE (Patterns)"));
|
|
876
|
+
console.log(chalk5.cyan(divider) + "\n");
|
|
782
877
|
console.log(formatToolScore2(patternScore));
|
|
783
878
|
console.log();
|
|
784
879
|
}
|
|
@@ -795,7 +890,7 @@ EXAMPLES:
|
|
|
795
890
|
`;
|
|
796
891
|
|
|
797
892
|
// src/commands/context.ts
|
|
798
|
-
import
|
|
893
|
+
import chalk6 from "chalk";
|
|
799
894
|
import { resolve as resolvePath5 } from "path";
|
|
800
895
|
import {
|
|
801
896
|
loadMergedConfig as loadMergedConfig3,
|
|
@@ -806,7 +901,7 @@ import {
|
|
|
806
901
|
formatToolScore as formatToolScore3
|
|
807
902
|
} from "@aiready/core";
|
|
808
903
|
async function contextAction(directory, options) {
|
|
809
|
-
console.log(
|
|
904
|
+
console.log(chalk6.blue("\u{1F9E0} Analyzing context costs...\n"));
|
|
810
905
|
const startTime = Date.now();
|
|
811
906
|
const resolvedDir = resolvePath5(process.cwd(), directory || ".");
|
|
812
907
|
try {
|
|
@@ -874,85 +969,85 @@ async function contextAction(directory, options) {
|
|
|
874
969
|
const terminalWidth = process.stdout.columns || 80;
|
|
875
970
|
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
876
971
|
const divider = "\u2501".repeat(dividerWidth);
|
|
877
|
-
console.log(
|
|
878
|
-
console.log(
|
|
879
|
-
console.log(
|
|
972
|
+
console.log(chalk6.cyan(divider));
|
|
973
|
+
console.log(chalk6.bold.white(" CONTEXT ANALYSIS SUMMARY"));
|
|
974
|
+
console.log(chalk6.cyan(divider) + "\n");
|
|
880
975
|
console.log(
|
|
881
|
-
|
|
976
|
+
chalk6.white(`\u{1F4C1} Files analyzed: ${chalk6.bold(summary.totalFiles)}`)
|
|
882
977
|
);
|
|
883
978
|
console.log(
|
|
884
|
-
|
|
885
|
-
`\u{1F4CA} Total tokens: ${
|
|
979
|
+
chalk6.white(
|
|
980
|
+
`\u{1F4CA} Total tokens: ${chalk6.bold(summary.totalTokens.toLocaleString())}`
|
|
886
981
|
)
|
|
887
982
|
);
|
|
888
983
|
console.log(
|
|
889
|
-
|
|
890
|
-
`\u{1F4B0} Avg context budget: ${
|
|
984
|
+
chalk6.yellow(
|
|
985
|
+
`\u{1F4B0} Avg context budget: ${chalk6.bold(summary.avgContextBudget.toFixed(0))} tokens/file`
|
|
891
986
|
)
|
|
892
987
|
);
|
|
893
988
|
console.log(
|
|
894
|
-
|
|
989
|
+
chalk6.white(`\u23F1 Analysis time: ${chalk6.bold(elapsedTime + "s")}
|
|
895
990
|
`)
|
|
896
991
|
);
|
|
897
992
|
const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
|
|
898
993
|
if (totalIssues > 0) {
|
|
899
|
-
console.log(
|
|
994
|
+
console.log(chalk6.bold("\u26A0\uFE0F Issues Found:\n"));
|
|
900
995
|
if (summary.criticalIssues > 0) {
|
|
901
996
|
console.log(
|
|
902
|
-
|
|
997
|
+
chalk6.red(` \u{1F534} Critical: ${chalk6.bold(summary.criticalIssues)}`)
|
|
903
998
|
);
|
|
904
999
|
}
|
|
905
1000
|
if (summary.majorIssues > 0) {
|
|
906
1001
|
console.log(
|
|
907
|
-
|
|
1002
|
+
chalk6.yellow(` \u{1F7E1} Major: ${chalk6.bold(summary.majorIssues)}`)
|
|
908
1003
|
);
|
|
909
1004
|
}
|
|
910
1005
|
if (summary.minorIssues > 0) {
|
|
911
1006
|
console.log(
|
|
912
|
-
|
|
1007
|
+
chalk6.blue(` \u{1F535} Minor: ${chalk6.bold(summary.minorIssues)}`)
|
|
913
1008
|
);
|
|
914
1009
|
}
|
|
915
1010
|
console.log(
|
|
916
|
-
|
|
1011
|
+
chalk6.green(
|
|
917
1012
|
`
|
|
918
|
-
\u{1F4A1} Potential savings: ${
|
|
1013
|
+
\u{1F4A1} Potential savings: ${chalk6.bold(summary.totalPotentialSavings.toLocaleString())} tokens
|
|
919
1014
|
`
|
|
920
1015
|
)
|
|
921
1016
|
);
|
|
922
1017
|
} else {
|
|
923
|
-
console.log(
|
|
1018
|
+
console.log(chalk6.green("\u2705 No significant issues found!\n"));
|
|
924
1019
|
}
|
|
925
1020
|
if (summary.deepFiles.length > 0) {
|
|
926
|
-
console.log(
|
|
1021
|
+
console.log(chalk6.bold("\u{1F4CF} Deep Import Chains:\n"));
|
|
927
1022
|
console.log(
|
|
928
|
-
|
|
1023
|
+
chalk6.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`)
|
|
929
1024
|
);
|
|
930
1025
|
console.log(
|
|
931
|
-
|
|
1026
|
+
chalk6.gray(` Maximum depth: ${summary.maxImportDepth}
|
|
932
1027
|
`)
|
|
933
1028
|
);
|
|
934
1029
|
summary.deepFiles.slice(0, 10).forEach((item) => {
|
|
935
1030
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
936
1031
|
console.log(
|
|
937
|
-
` ${
|
|
1032
|
+
` ${chalk6.cyan("\u2192")} ${chalk6.white(fileName)} ${chalk6.dim(`(depth: ${item.depth})`)}`
|
|
938
1033
|
);
|
|
939
1034
|
});
|
|
940
1035
|
console.log();
|
|
941
1036
|
}
|
|
942
1037
|
if (summary.fragmentedModules.length > 0) {
|
|
943
|
-
console.log(
|
|
1038
|
+
console.log(chalk6.bold("\u{1F9E9} Fragmented Modules:\n"));
|
|
944
1039
|
console.log(
|
|
945
|
-
|
|
1040
|
+
chalk6.gray(
|
|
946
1041
|
` Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(0)}%
|
|
947
1042
|
`
|
|
948
1043
|
)
|
|
949
1044
|
);
|
|
950
1045
|
summary.fragmentedModules.slice(0, 10).forEach((module) => {
|
|
951
1046
|
console.log(
|
|
952
|
-
` ${
|
|
1047
|
+
` ${chalk6.yellow("\u25CF")} ${chalk6.white(module.domain)} - ${chalk6.dim(`${module.files.length} files, ${(module.fragmentationScore * 100).toFixed(0)}% scattered`)}`
|
|
953
1048
|
);
|
|
954
1049
|
console.log(
|
|
955
|
-
|
|
1050
|
+
chalk6.dim(
|
|
956
1051
|
` Token cost: ${module.totalTokens.toLocaleString()}, Cohesion: ${(module.avgCohesion * 100).toFixed(0)}%`
|
|
957
1052
|
)
|
|
958
1053
|
);
|
|
@@ -960,9 +1055,9 @@ async function contextAction(directory, options) {
|
|
|
960
1055
|
console.log();
|
|
961
1056
|
}
|
|
962
1057
|
if (summary.lowCohesionFiles.length > 0) {
|
|
963
|
-
console.log(
|
|
1058
|
+
console.log(chalk6.bold("\u{1F500} Low Cohesion Files:\n"));
|
|
964
1059
|
console.log(
|
|
965
|
-
|
|
1060
|
+
chalk6.gray(
|
|
966
1061
|
` Average cohesion: ${(summary.avgCohesion * 100).toFixed(0)}%
|
|
967
1062
|
`
|
|
968
1063
|
)
|
|
@@ -970,28 +1065,28 @@ async function contextAction(directory, options) {
|
|
|
970
1065
|
summary.lowCohesionFiles.slice(0, 10).forEach((item) => {
|
|
971
1066
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
972
1067
|
const scorePercent = (item.score * 100).toFixed(0);
|
|
973
|
-
const color = item.score < 0.4 ?
|
|
1068
|
+
const color = item.score < 0.4 ? chalk6.red : chalk6.yellow;
|
|
974
1069
|
console.log(
|
|
975
|
-
` ${color("\u25CB")} ${
|
|
1070
|
+
` ${color("\u25CB")} ${chalk6.white(fileName)} ${chalk6.dim(`(${scorePercent}% cohesion)`)}`
|
|
976
1071
|
);
|
|
977
1072
|
});
|
|
978
1073
|
console.log();
|
|
979
1074
|
}
|
|
980
1075
|
if (summary.topExpensiveFiles.length > 0) {
|
|
981
|
-
console.log(
|
|
1076
|
+
console.log(chalk6.bold("\u{1F4B8} Most Expensive Files (Context Budget):\n"));
|
|
982
1077
|
summary.topExpensiveFiles.slice(0, 10).forEach((item) => {
|
|
983
1078
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
984
|
-
const severityColor = item.severity === "critical" ?
|
|
1079
|
+
const severityColor = item.severity === "critical" ? chalk6.red : item.severity === "major" ? chalk6.yellow : chalk6.blue;
|
|
985
1080
|
console.log(
|
|
986
|
-
` ${severityColor("\u25CF")} ${
|
|
1081
|
+
` ${severityColor("\u25CF")} ${chalk6.white(fileName)} ${chalk6.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`
|
|
987
1082
|
);
|
|
988
1083
|
});
|
|
989
1084
|
console.log();
|
|
990
1085
|
}
|
|
991
1086
|
if (contextScore) {
|
|
992
|
-
console.log(
|
|
993
|
-
console.log(
|
|
994
|
-
console.log(
|
|
1087
|
+
console.log(chalk6.cyan(divider));
|
|
1088
|
+
console.log(chalk6.bold.white(" AI READINESS SCORE (Context)"));
|
|
1089
|
+
console.log(chalk6.cyan(divider) + "\n");
|
|
995
1090
|
console.log(formatToolScore3(contextScore));
|
|
996
1091
|
console.log();
|
|
997
1092
|
}
|
|
@@ -1002,8 +1097,8 @@ async function contextAction(directory, options) {
|
|
|
1002
1097
|
}
|
|
1003
1098
|
|
|
1004
1099
|
// src/commands/consistency.ts
|
|
1005
|
-
import
|
|
1006
|
-
import { writeFileSync as
|
|
1100
|
+
import chalk7 from "chalk";
|
|
1101
|
+
import { writeFileSync as writeFileSync3 } from "fs";
|
|
1007
1102
|
import { resolve as resolvePath6 } from "path";
|
|
1008
1103
|
import {
|
|
1009
1104
|
loadMergedConfig as loadMergedConfig4,
|
|
@@ -1014,7 +1109,7 @@ import {
|
|
|
1014
1109
|
formatToolScore as formatToolScore4
|
|
1015
1110
|
} from "@aiready/core";
|
|
1016
1111
|
async function consistencyAction(directory, options) {
|
|
1017
|
-
console.log(
|
|
1112
|
+
console.log(chalk7.blue("\u{1F50D} Analyzing consistency...\n"));
|
|
1018
1113
|
const startTime = Date.now();
|
|
1019
1114
|
const resolvedDir = resolvePath6(process.cwd(), directory || ".");
|
|
1020
1115
|
try {
|
|
@@ -1075,24 +1170,24 @@ async function consistencyAction(directory, options) {
|
|
|
1075
1170
|
`aiready-report-${getReportTimestamp()}.md`,
|
|
1076
1171
|
resolvedDir
|
|
1077
1172
|
);
|
|
1078
|
-
|
|
1079
|
-
console.log(
|
|
1173
|
+
writeFileSync3(outputPath, markdown);
|
|
1174
|
+
console.log(chalk7.green(`\u2705 Report saved to ${outputPath}`));
|
|
1080
1175
|
} else {
|
|
1081
|
-
console.log(
|
|
1176
|
+
console.log(chalk7.bold("\n\u{1F4CA} Summary\n"));
|
|
1082
1177
|
console.log(
|
|
1083
|
-
`Files Analyzed: ${
|
|
1178
|
+
`Files Analyzed: ${chalk7.cyan(report.summary.filesAnalyzed)}`
|
|
1084
1179
|
);
|
|
1085
|
-
console.log(`Total Issues: ${
|
|
1086
|
-
console.log(` Naming: ${
|
|
1087
|
-
console.log(` Patterns: ${
|
|
1180
|
+
console.log(`Total Issues: ${chalk7.yellow(report.summary.totalIssues)}`);
|
|
1181
|
+
console.log(` Naming: ${chalk7.yellow(report.summary.namingIssues)}`);
|
|
1182
|
+
console.log(` Patterns: ${chalk7.yellow(report.summary.patternIssues)}`);
|
|
1088
1183
|
console.log(
|
|
1089
|
-
` Architecture: ${
|
|
1184
|
+
` Architecture: ${chalk7.yellow(report.summary.architectureIssues || 0)}`
|
|
1090
1185
|
);
|
|
1091
|
-
console.log(`Analysis Time: ${
|
|
1186
|
+
console.log(`Analysis Time: ${chalk7.gray(elapsedTime + "s")}
|
|
1092
1187
|
`);
|
|
1093
1188
|
if (report.summary.totalIssues === 0) {
|
|
1094
1189
|
console.log(
|
|
1095
|
-
|
|
1190
|
+
chalk7.green(
|
|
1096
1191
|
"\u2728 No consistency issues found! Your codebase is well-maintained.\n"
|
|
1097
1192
|
)
|
|
1098
1193
|
);
|
|
@@ -1104,20 +1199,20 @@ async function consistencyAction(directory, options) {
|
|
|
1104
1199
|
(r) => r.issues.some((i) => i.category === "patterns")
|
|
1105
1200
|
);
|
|
1106
1201
|
if (namingResults.length > 0) {
|
|
1107
|
-
console.log(
|
|
1202
|
+
console.log(chalk7.bold("\u{1F3F7}\uFE0F Naming Issues\n"));
|
|
1108
1203
|
let shown = 0;
|
|
1109
1204
|
for (const result of namingResults) {
|
|
1110
1205
|
if (shown >= 5) break;
|
|
1111
1206
|
for (const issue of result.issues) {
|
|
1112
1207
|
if (shown >= 5) break;
|
|
1113
|
-
const severityColor = issue.severity === "critical" ?
|
|
1208
|
+
const severityColor = issue.severity === "critical" ? chalk7.red : issue.severity === "major" ? chalk7.yellow : issue.severity === "minor" ? chalk7.blue : chalk7.gray;
|
|
1114
1209
|
console.log(
|
|
1115
|
-
`${severityColor(issue.severity.toUpperCase())} ${
|
|
1210
|
+
`${severityColor(issue.severity.toUpperCase())} ${chalk7.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1116
1211
|
);
|
|
1117
1212
|
console.log(` ${issue.message}`);
|
|
1118
1213
|
if (issue.suggestion) {
|
|
1119
1214
|
console.log(
|
|
1120
|
-
` ${
|
|
1215
|
+
` ${chalk7.dim("\u2192")} ${chalk7.italic(issue.suggestion)}`
|
|
1121
1216
|
);
|
|
1122
1217
|
}
|
|
1123
1218
|
console.log();
|
|
@@ -1126,25 +1221,25 @@ async function consistencyAction(directory, options) {
|
|
|
1126
1221
|
}
|
|
1127
1222
|
const remaining = namingResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
1128
1223
|
if (remaining > 0) {
|
|
1129
|
-
console.log(
|
|
1224
|
+
console.log(chalk7.dim(` ... and ${remaining} more issues
|
|
1130
1225
|
`));
|
|
1131
1226
|
}
|
|
1132
1227
|
}
|
|
1133
1228
|
if (patternResults.length > 0) {
|
|
1134
|
-
console.log(
|
|
1229
|
+
console.log(chalk7.bold("\u{1F504} Pattern Issues\n"));
|
|
1135
1230
|
let shown = 0;
|
|
1136
1231
|
for (const result of patternResults) {
|
|
1137
1232
|
if (shown >= 5) break;
|
|
1138
1233
|
for (const issue of result.issues) {
|
|
1139
1234
|
if (shown >= 5) break;
|
|
1140
|
-
const severityColor = issue.severity === "critical" ?
|
|
1235
|
+
const severityColor = issue.severity === "critical" ? chalk7.red : issue.severity === "major" ? chalk7.yellow : issue.severity === "minor" ? chalk7.blue : chalk7.gray;
|
|
1141
1236
|
console.log(
|
|
1142
|
-
`${severityColor(issue.severity.toUpperCase())} ${
|
|
1237
|
+
`${severityColor(issue.severity.toUpperCase())} ${chalk7.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1143
1238
|
);
|
|
1144
1239
|
console.log(` ${issue.message}`);
|
|
1145
1240
|
if (issue.suggestion) {
|
|
1146
1241
|
console.log(
|
|
1147
|
-
` ${
|
|
1242
|
+
` ${chalk7.dim("\u2192")} ${chalk7.italic(issue.suggestion)}`
|
|
1148
1243
|
);
|
|
1149
1244
|
}
|
|
1150
1245
|
console.log();
|
|
@@ -1153,12 +1248,12 @@ async function consistencyAction(directory, options) {
|
|
|
1153
1248
|
}
|
|
1154
1249
|
const remaining = patternResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
1155
1250
|
if (remaining > 0) {
|
|
1156
|
-
console.log(
|
|
1251
|
+
console.log(chalk7.dim(` ... and ${remaining} more issues
|
|
1157
1252
|
`));
|
|
1158
1253
|
}
|
|
1159
1254
|
}
|
|
1160
1255
|
if (report.recommendations.length > 0) {
|
|
1161
|
-
console.log(
|
|
1256
|
+
console.log(chalk7.bold("\u{1F4A1} Recommendations\n"));
|
|
1162
1257
|
report.recommendations.forEach((rec, i) => {
|
|
1163
1258
|
console.log(`${i + 1}. ${rec}`);
|
|
1164
1259
|
});
|
|
@@ -1166,7 +1261,7 @@ async function consistencyAction(directory, options) {
|
|
|
1166
1261
|
}
|
|
1167
1262
|
}
|
|
1168
1263
|
if (consistencyScore) {
|
|
1169
|
-
console.log(
|
|
1264
|
+
console.log(chalk7.bold("\n\u{1F4CA} AI Readiness Score (Consistency)\n"));
|
|
1170
1265
|
console.log(formatToolScore4(consistencyScore));
|
|
1171
1266
|
console.log();
|
|
1172
1267
|
}
|
|
@@ -1177,8 +1272,8 @@ async function consistencyAction(directory, options) {
|
|
|
1177
1272
|
}
|
|
1178
1273
|
|
|
1179
1274
|
// src/commands/visualize.ts
|
|
1180
|
-
import
|
|
1181
|
-
import { writeFileSync as
|
|
1275
|
+
import chalk8 from "chalk";
|
|
1276
|
+
import { writeFileSync as writeFileSync4, readFileSync as readFileSync3, existsSync as existsSync3, copyFileSync } from "fs";
|
|
1182
1277
|
import { resolve as resolvePath7 } from "path";
|
|
1183
1278
|
import { spawn } from "child_process";
|
|
1184
1279
|
import { handleCLIError as handleCLIError6 } from "@aiready/core";
|
|
@@ -1187,17 +1282,17 @@ async function visualizeAction(directory, options) {
|
|
|
1187
1282
|
try {
|
|
1188
1283
|
const dirPath = resolvePath7(process.cwd(), directory || ".");
|
|
1189
1284
|
let reportPath = options.report ? resolvePath7(dirPath, options.report) : null;
|
|
1190
|
-
if (!reportPath || !
|
|
1285
|
+
if (!reportPath || !existsSync3(reportPath)) {
|
|
1191
1286
|
const latestScan = findLatestScanReport(dirPath);
|
|
1192
1287
|
if (latestScan) {
|
|
1193
1288
|
reportPath = latestScan;
|
|
1194
1289
|
console.log(
|
|
1195
|
-
|
|
1290
|
+
chalk8.dim(`Found latest report: ${latestScan.split("/").pop()}`)
|
|
1196
1291
|
);
|
|
1197
1292
|
} else {
|
|
1198
|
-
console.error(
|
|
1293
|
+
console.error(chalk8.red("\u274C No AI readiness report found"));
|
|
1199
1294
|
console.log(
|
|
1200
|
-
|
|
1295
|
+
chalk8.dim(
|
|
1201
1296
|
`
|
|
1202
1297
|
Generate a report with:
|
|
1203
1298
|
aiready scan --output json
|
|
@@ -1213,7 +1308,7 @@ Or specify a custom report:
|
|
|
1213
1308
|
const report = JSON.parse(raw);
|
|
1214
1309
|
const configPath = resolvePath7(dirPath, "aiready.json");
|
|
1215
1310
|
let graphConfig = { maxNodes: 400, maxEdges: 600 };
|
|
1216
|
-
if (
|
|
1311
|
+
if (existsSync3(configPath)) {
|
|
1217
1312
|
try {
|
|
1218
1313
|
const rawConfig = JSON.parse(readFileSync3(configPath, "utf8"));
|
|
1219
1314
|
if (rawConfig.visualizer?.graph) {
|
|
@@ -1238,7 +1333,7 @@ Or specify a custom report:
|
|
|
1238
1333
|
const localWebDir = resolvePath7(dirPath, "packages/visualizer");
|
|
1239
1334
|
let webDir = "";
|
|
1240
1335
|
let visualizerAvailable = false;
|
|
1241
|
-
if (
|
|
1336
|
+
if (existsSync3(localWebDir)) {
|
|
1242
1337
|
webDir = localWebDir;
|
|
1243
1338
|
visualizerAvailable = true;
|
|
1244
1339
|
} else {
|
|
@@ -1261,7 +1356,7 @@ Or specify a custom report:
|
|
|
1261
1356
|
currentDir = parent;
|
|
1262
1357
|
}
|
|
1263
1358
|
for (const location of nodemodulesLocations) {
|
|
1264
|
-
if (
|
|
1359
|
+
if (existsSync3(location) && existsSync3(resolvePath7(location, "package.json"))) {
|
|
1265
1360
|
webDir = location;
|
|
1266
1361
|
visualizerAvailable = true;
|
|
1267
1362
|
break;
|
|
@@ -1277,7 +1372,7 @@ Or specify a custom report:
|
|
|
1277
1372
|
}
|
|
1278
1373
|
}
|
|
1279
1374
|
}
|
|
1280
|
-
const webViteConfigExists = webDir &&
|
|
1375
|
+
const webViteConfigExists = webDir && existsSync3(resolvePath7(webDir, "web", "vite.config.ts"));
|
|
1281
1376
|
if (visualizerAvailable && webViteConfigExists) {
|
|
1282
1377
|
const spawnCwd = webDir;
|
|
1283
1378
|
const { watch } = await import("fs");
|
|
@@ -1327,19 +1422,19 @@ Or specify a custom report:
|
|
|
1327
1422
|
return;
|
|
1328
1423
|
} else {
|
|
1329
1424
|
console.log(
|
|
1330
|
-
|
|
1425
|
+
chalk8.yellow(
|
|
1331
1426
|
"\u26A0\uFE0F Dev server not available (requires local @aiready/visualizer with web assets)."
|
|
1332
1427
|
)
|
|
1333
1428
|
);
|
|
1334
1429
|
console.log(
|
|
1335
|
-
|
|
1430
|
+
chalk8.cyan(" Falling back to static HTML generation...\n")
|
|
1336
1431
|
);
|
|
1337
1432
|
useDevMode = false;
|
|
1338
1433
|
}
|
|
1339
1434
|
} catch (err) {
|
|
1340
1435
|
console.error("Failed to start dev server:", err);
|
|
1341
1436
|
console.log(
|
|
1342
|
-
|
|
1437
|
+
chalk8.cyan(" Falling back to static HTML generation...\n")
|
|
1343
1438
|
);
|
|
1344
1439
|
useDevMode = false;
|
|
1345
1440
|
}
|
|
@@ -1348,8 +1443,8 @@ Or specify a custom report:
|
|
|
1348
1443
|
const html = generateHTML(graph);
|
|
1349
1444
|
const defaultOutput = "visualization.html";
|
|
1350
1445
|
const outPath = resolvePath7(dirPath, options.output || defaultOutput);
|
|
1351
|
-
|
|
1352
|
-
console.log(
|
|
1446
|
+
writeFileSync4(outPath, html, "utf8");
|
|
1447
|
+
console.log(chalk8.green(`\u2705 Visualization written to: ${outPath}`));
|
|
1353
1448
|
if (options.open || options.serve) {
|
|
1354
1449
|
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
1355
1450
|
if (options.serve) {
|
|
@@ -1379,7 +1474,7 @@ Or specify a custom report:
|
|
|
1379
1474
|
server.listen(port, () => {
|
|
1380
1475
|
const addr = `http://localhost:${port}/`;
|
|
1381
1476
|
console.log(
|
|
1382
|
-
|
|
1477
|
+
chalk8.cyan(`\u{1F310} Local visualization server running at ${addr}`)
|
|
1383
1478
|
);
|
|
1384
1479
|
spawn(opener, [`"${addr}"`], { shell: true });
|
|
1385
1480
|
});
|
|
@@ -1426,15 +1521,15 @@ NOTES:
|
|
|
1426
1521
|
`;
|
|
1427
1522
|
|
|
1428
1523
|
// src/commands/ai-signal-clarity.ts
|
|
1429
|
-
import
|
|
1524
|
+
import chalk9 from "chalk";
|
|
1430
1525
|
import { loadConfig, mergeConfigWithDefaults } from "@aiready/core";
|
|
1431
1526
|
|
|
1432
1527
|
// src/commands/agent-grounding.ts
|
|
1433
|
-
import
|
|
1528
|
+
import chalk10 from "chalk";
|
|
1434
1529
|
import { loadConfig as loadConfig2, mergeConfigWithDefaults as mergeConfigWithDefaults2 } from "@aiready/core";
|
|
1435
1530
|
|
|
1436
1531
|
// src/commands/testability.ts
|
|
1437
|
-
import
|
|
1532
|
+
import chalk11 from "chalk";
|
|
1438
1533
|
import { loadConfig as loadConfig3, mergeConfigWithDefaults as mergeConfigWithDefaults3 } from "@aiready/core";
|
|
1439
1534
|
async function testabilityAction(directory, options) {
|
|
1440
1535
|
const { analyzeTestability, calculateTestabilityScore } = await import("@aiready/testability");
|
|
@@ -1459,28 +1554,28 @@ async function testabilityAction(directory, options) {
|
|
|
1459
1554
|
"blind-risk": "\u{1F480}"
|
|
1460
1555
|
};
|
|
1461
1556
|
const safetyColors = {
|
|
1462
|
-
safe:
|
|
1463
|
-
"moderate-risk":
|
|
1464
|
-
"high-risk":
|
|
1465
|
-
"blind-risk":
|
|
1557
|
+
safe: chalk11.green,
|
|
1558
|
+
"moderate-risk": chalk11.yellow,
|
|
1559
|
+
"high-risk": chalk11.red,
|
|
1560
|
+
"blind-risk": chalk11.bgRed.white
|
|
1466
1561
|
};
|
|
1467
1562
|
const safety = report.summary.aiChangeSafetyRating;
|
|
1468
1563
|
const icon = safetyIcons[safety] ?? "\u2753";
|
|
1469
|
-
const color = safetyColors[safety] ??
|
|
1564
|
+
const color = safetyColors[safety] ?? chalk11.white;
|
|
1470
1565
|
console.log(
|
|
1471
|
-
` \u{1F9EA} Testability: ${
|
|
1566
|
+
` \u{1F9EA} Testability: ${chalk11.bold(scoring.score + "/100")} (${report.summary.rating})`
|
|
1472
1567
|
);
|
|
1473
1568
|
console.log(
|
|
1474
1569
|
` AI Change Safety: ${color(`${icon} ${safety.toUpperCase()}`)}`
|
|
1475
1570
|
);
|
|
1476
1571
|
console.log(
|
|
1477
|
-
|
|
1572
|
+
chalk11.dim(
|
|
1478
1573
|
` Coverage: ${Math.round(report.summary.coverageRatio * 100)}% (${report.rawData.testFiles} test / ${report.rawData.sourceFiles} source files)`
|
|
1479
1574
|
)
|
|
1480
1575
|
);
|
|
1481
1576
|
if (safety === "blind-risk") {
|
|
1482
1577
|
console.log(
|
|
1483
|
-
|
|
1578
|
+
chalk11.red.bold(
|
|
1484
1579
|
"\n \u26A0\uFE0F NO TESTS \u2014 AI changes to this codebase are completely unverifiable!\n"
|
|
1485
1580
|
)
|
|
1486
1581
|
);
|
|
@@ -1492,7 +1587,7 @@ async function testabilityAction(directory, options) {
|
|
|
1492
1587
|
import { changeAmplificationAction } from "@aiready/change-amplification/dist/cli.js";
|
|
1493
1588
|
|
|
1494
1589
|
// src/commands/bug.ts
|
|
1495
|
-
import
|
|
1590
|
+
import chalk12 from "chalk";
|
|
1496
1591
|
import { execSync } from "child_process";
|
|
1497
1592
|
async function bugAction(message, options) {
|
|
1498
1593
|
const repoUrl = "https://github.com/caopengau/aiready-cli";
|
|
@@ -1510,35 +1605,35 @@ Generated via AIReady CLI 'bug' command.
|
|
|
1510
1605
|
Type: ${type}
|
|
1511
1606
|
`.trim();
|
|
1512
1607
|
if (options.submit) {
|
|
1513
|
-
console.log(
|
|
1608
|
+
console.log(chalk12.blue("\u{1F680} Submitting issue via GitHub CLI...\n"));
|
|
1514
1609
|
try {
|
|
1515
1610
|
execSync("gh auth status", { stdio: "ignore" });
|
|
1516
1611
|
const command = `gh issue create --repo ${repoSlug} --title ${JSON.stringify(title)} --body ${JSON.stringify(body)} --label ${label}`;
|
|
1517
1612
|
const output = execSync(command, { encoding: "utf8" }).trim();
|
|
1518
|
-
console.log(
|
|
1519
|
-
console.log(
|
|
1613
|
+
console.log(chalk12.green("\u2705 Issue Created Successfully!"));
|
|
1614
|
+
console.log(chalk12.cyan(output));
|
|
1520
1615
|
return;
|
|
1521
1616
|
} catch (error) {
|
|
1522
|
-
console.error(
|
|
1617
|
+
console.error(chalk12.red("\n\u274C Failed to submit via gh CLI."));
|
|
1523
1618
|
console.log(
|
|
1524
|
-
|
|
1619
|
+
chalk12.yellow(
|
|
1525
1620
|
' Make sure gh is installed and run "gh auth login".\n'
|
|
1526
1621
|
)
|
|
1527
1622
|
);
|
|
1528
|
-
console.log(
|
|
1623
|
+
console.log(chalk12.dim(" Falling back to URL generation..."));
|
|
1529
1624
|
}
|
|
1530
1625
|
}
|
|
1531
1626
|
const template = type === "bug" ? "bug_report.md" : type === "feature" ? "feature_request.md" : "new_metric_idea.md";
|
|
1532
1627
|
const fullUrl = `${repoUrl}/issues/new?title=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}&labels=${label}&template=${template}`;
|
|
1533
|
-
console.log(
|
|
1534
|
-
console.log(
|
|
1535
|
-
console.log(
|
|
1536
|
-
console.log(
|
|
1537
|
-
console.log(
|
|
1538
|
-
console.log(
|
|
1539
|
-
console.log(
|
|
1628
|
+
console.log(chalk12.green("\u{1F680} Issue Draft Prepared!\n"));
|
|
1629
|
+
console.log(chalk12.bold("Title: ") + title);
|
|
1630
|
+
console.log(chalk12.bold("Type: ") + type);
|
|
1631
|
+
console.log(chalk12.bold("\nClick the link below to submit this issue:"));
|
|
1632
|
+
console.log(chalk12.cyan(fullUrl));
|
|
1633
|
+
console.log(chalk12.bold("\n\u{1F916} [FOR AI AGENTS]:"));
|
|
1634
|
+
console.log(chalk12.dim(" You have successfully prepared a report."));
|
|
1540
1635
|
console.log(
|
|
1541
|
-
|
|
1636
|
+
chalk12.dim(
|
|
1542
1637
|
" Please present the URL above to the user so they can finalize the submission."
|
|
1543
1638
|
)
|
|
1544
1639
|
);
|
|
@@ -1547,14 +1642,14 @@ Type: ${type}
|
|
|
1547
1642
|
const bugUrl = `${repoUrl}/issues/new?template=bug_report.md`;
|
|
1548
1643
|
const featureUrl = `${repoUrl}/issues/new?template=feature_request.md`;
|
|
1549
1644
|
const metricUrl = `${repoUrl}/issues/new?template=new_metric_idea.md`;
|
|
1550
|
-
console.log(
|
|
1551
|
-
console.log(` Report a Bug: ${
|
|
1552
|
-
console.log(` Request a Feature: ${
|
|
1553
|
-
console.log(` Suggest a Metric: ${
|
|
1554
|
-
console.log(
|
|
1555
|
-
console.log(
|
|
1645
|
+
console.log(chalk12.blue("\u{1F4AC} Feedback & Bug Reports\n"));
|
|
1646
|
+
console.log(` Report a Bug: ${chalk12.cyan(bugUrl)}`);
|
|
1647
|
+
console.log(` Request a Feature: ${chalk12.cyan(featureUrl)}`);
|
|
1648
|
+
console.log(` Suggest a Metric: ${chalk12.cyan(metricUrl)}`);
|
|
1649
|
+
console.log(chalk12.bold("\n\u{1F916} [FOR AI AGENTS]:"));
|
|
1650
|
+
console.log(chalk12.dim(" To prepare a specific report, run:"));
|
|
1556
1651
|
console.log(
|
|
1557
|
-
|
|
1652
|
+
chalk12.cyan(
|
|
1558
1653
|
' aiready bug "your description here" --type bug|feature|metric'
|
|
1559
1654
|
)
|
|
1560
1655
|
);
|
|
@@ -1573,7 +1668,7 @@ var getDirname = () => {
|
|
|
1573
1668
|
return dirname(fileURLToPath(import.meta.url));
|
|
1574
1669
|
};
|
|
1575
1670
|
var packageJson = JSON.parse(
|
|
1576
|
-
readFileSync4(
|
|
1671
|
+
readFileSync4(join2(getDirname(), "../package.json"), "utf8")
|
|
1577
1672
|
);
|
|
1578
1673
|
var program = new Command();
|
|
1579
1674
|
program.name("aiready").description("AIReady - Assess and improve AI-readiness of codebases").version(packageJson.version).addHelpText(
|
|
@@ -1636,6 +1731,13 @@ program.command("scan").description(
|
|
|
1636
1731
|
).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", scanHelpText).action(async (directory, options) => {
|
|
1637
1732
|
await scanAction(directory, options);
|
|
1638
1733
|
});
|
|
1734
|
+
program.command("init").description("Generate a default configuration (aiready.json)").option("-f, --force", "Overwrite existing configuration file").option(
|
|
1735
|
+
"--js",
|
|
1736
|
+
"Generate configuration as a JavaScript file (aiready.config.js)"
|
|
1737
|
+
).action(async (options) => {
|
|
1738
|
+
const format = options.js ? "js" : "json";
|
|
1739
|
+
await initAction({ force: options.force, format });
|
|
1740
|
+
});
|
|
1639
1741
|
program.command("patterns").description("Detect duplicate code patterns that confuse AI models").argument("[directory]", "Directory to analyze", ".").option("-s, --similarity <number>", "Minimum similarity score (0-1)", "0.40").option("-l, --min-lines <number>", "Minimum lines to consider", "5").option(
|
|
1640
1742
|
"--max-candidates <number>",
|
|
1641
1743
|
"Maximum candidates per block (performance tuning)"
|