@aiready/cli 0.14.25 → 0.15.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/dist/chunk-HX6H3VOE.mjs +328 -0
- package/dist/chunk-L4VXALJD.mjs +280 -0
- package/dist/chunk-SK6WW6HW.mjs +325 -0
- package/dist/chunk-TKBE575H.mjs +327 -0
- package/dist/cli.js +837 -668
- package/dist/cli.mjs +578 -468
- package/dist/index.js +246 -214
- package/dist/index.mjs +7 -1
- package/package.json +13 -12
package/dist/cli.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
__require,
|
|
4
4
|
analyzeUnified,
|
|
5
5
|
scoreUnified
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-HX6H3VOE.mjs";
|
|
7
7
|
|
|
8
8
|
// src/cli.ts
|
|
9
9
|
import { Command } from "commander";
|
|
@@ -17,7 +17,7 @@ import { writeFileSync } from "fs";
|
|
|
17
17
|
import { resolve as resolvePath4 } from "path";
|
|
18
18
|
import {
|
|
19
19
|
handleJSONOutput as handleJSONOutput2,
|
|
20
|
-
handleCLIError as
|
|
20
|
+
handleCLIError as handleCLIError3,
|
|
21
21
|
resolveOutputPath,
|
|
22
22
|
getRepoMetadata,
|
|
23
23
|
emitIssuesAsAnnotations
|
|
@@ -37,8 +37,8 @@ import chalk from "chalk";
|
|
|
37
37
|
import { loadConfig, mergeConfigWithDefaults } from "@aiready/core";
|
|
38
38
|
async function warnIfGraphCapExceeded(report, dirPath) {
|
|
39
39
|
try {
|
|
40
|
-
const { loadConfig:
|
|
41
|
-
void
|
|
40
|
+
const { loadConfig: loadConfig2 } = await import("@aiready/core");
|
|
41
|
+
void loadConfig2;
|
|
42
42
|
let graphConfig = { maxNodes: 400, maxEdges: 600 };
|
|
43
43
|
const configPath = resolvePath(dirPath, "aiready.json");
|
|
44
44
|
if (existsSync(configPath)) {
|
|
@@ -405,7 +405,71 @@ import { loadMergedConfig, ToolName as ToolName2 } from "@aiready/core";
|
|
|
405
405
|
|
|
406
406
|
// src/commands/scan-helpers.ts
|
|
407
407
|
import chalk4 from "chalk";
|
|
408
|
-
import {
|
|
408
|
+
import {
|
|
409
|
+
handleCLIError as handleCLIError2,
|
|
410
|
+
getElapsedTime,
|
|
411
|
+
prepareActionConfig,
|
|
412
|
+
resolveOutputFormat,
|
|
413
|
+
formatStandardReport,
|
|
414
|
+
handleStandardJSONOutput,
|
|
415
|
+
ToolName
|
|
416
|
+
} from "@aiready/core";
|
|
417
|
+
async function executeToolAction(directory, options, config) {
|
|
418
|
+
console.log(chalk4.blue(`${config.emoji} ${config.label}...
|
|
419
|
+
`));
|
|
420
|
+
const startTime = Date.now();
|
|
421
|
+
try {
|
|
422
|
+
const { resolvedDir, finalOptions: baseOptions } = await prepareActionConfig(
|
|
423
|
+
directory,
|
|
424
|
+
config.defaults,
|
|
425
|
+
config.getCliOptions(options)
|
|
426
|
+
);
|
|
427
|
+
let finalOptions = baseOptions;
|
|
428
|
+
if (config.preAnalyze) {
|
|
429
|
+
finalOptions = await config.preAnalyze(resolvedDir, finalOptions);
|
|
430
|
+
}
|
|
431
|
+
const { analyze, generateSummary, calculateScore } = await config.importTool();
|
|
432
|
+
const results = await analyze(finalOptions);
|
|
433
|
+
const elapsedTime = getElapsedTime(startTime);
|
|
434
|
+
const summary = generateSummary(results);
|
|
435
|
+
let toolScore;
|
|
436
|
+
if (options.score && calculateScore) {
|
|
437
|
+
const resultsAny = results;
|
|
438
|
+
const scoreData = resultsAny.duplicates || resultsAny.issues || results;
|
|
439
|
+
const filesCount = resultsAny.length || resultsAny.summary?.filesAnalyzed || resultsAny.summary?.totalFiles;
|
|
440
|
+
toolScore = calculateScore(scoreData, filesCount);
|
|
441
|
+
}
|
|
442
|
+
const { format: outputFormat, file: userOutputFile } = resolveOutputFormat(
|
|
443
|
+
options,
|
|
444
|
+
finalOptions
|
|
445
|
+
);
|
|
446
|
+
const outputData = formatStandardReport({
|
|
447
|
+
results,
|
|
448
|
+
summary,
|
|
449
|
+
elapsedTime,
|
|
450
|
+
score: toolScore
|
|
451
|
+
});
|
|
452
|
+
if (outputFormat === "json") {
|
|
453
|
+
handleStandardJSONOutput({
|
|
454
|
+
outputData,
|
|
455
|
+
outputFile: userOutputFile,
|
|
456
|
+
resolvedDir
|
|
457
|
+
});
|
|
458
|
+
} else {
|
|
459
|
+
config.renderConsole({
|
|
460
|
+
results,
|
|
461
|
+
summary,
|
|
462
|
+
elapsedTime,
|
|
463
|
+
score: toolScore,
|
|
464
|
+
finalOptions
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
return outputData;
|
|
468
|
+
} catch (error) {
|
|
469
|
+
handleCLIError2(error, config.label);
|
|
470
|
+
return void 0;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
409
473
|
function getProfileTools(profile) {
|
|
410
474
|
switch (profile.toLowerCase()) {
|
|
411
475
|
case "agentic":
|
|
@@ -485,7 +549,8 @@ var SCAN_DEFAULTS = {
|
|
|
485
549
|
"testability-index",
|
|
486
550
|
"doc-drift",
|
|
487
551
|
"dependency-health",
|
|
488
|
-
"change-amplification"
|
|
552
|
+
"change-amplification",
|
|
553
|
+
"contract-enforcement"
|
|
489
554
|
],
|
|
490
555
|
include: void 0,
|
|
491
556
|
exclude: void 0,
|
|
@@ -713,15 +778,43 @@ async function scanAction(directory, options) {
|
|
|
713
778
|
});
|
|
714
779
|
}
|
|
715
780
|
await warnIfGraphCapExceeded(outputData, resolvedDir);
|
|
716
|
-
await handleGatekeeper(
|
|
781
|
+
await handleGatekeeper(
|
|
782
|
+
outputData,
|
|
783
|
+
scoringResult,
|
|
784
|
+
options,
|
|
785
|
+
finalOptions,
|
|
786
|
+
results
|
|
787
|
+
);
|
|
788
|
+
const isCI = options.ci ?? process.env.CI === "true";
|
|
789
|
+
if (!isCI) {
|
|
790
|
+
console.log(
|
|
791
|
+
chalk6.dim(
|
|
792
|
+
"\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"
|
|
793
|
+
)
|
|
794
|
+
);
|
|
795
|
+
console.log(chalk6.bold("\u{1F4C8} Want to see the full interactive report?"));
|
|
796
|
+
console.log(
|
|
797
|
+
chalk6.cyan(
|
|
798
|
+
` Upload this report to: ${chalk6.bold("https://platform.getaiready.dev")}`
|
|
799
|
+
)
|
|
800
|
+
);
|
|
801
|
+
console.log(
|
|
802
|
+
chalk6.dim(" Or run: ") + chalk6.white(`aiready upload ${outputPath}`)
|
|
803
|
+
);
|
|
804
|
+
console.log(
|
|
805
|
+
chalk6.dim(
|
|
806
|
+
"\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"
|
|
807
|
+
)
|
|
808
|
+
);
|
|
809
|
+
}
|
|
717
810
|
} catch (error) {
|
|
718
|
-
|
|
811
|
+
handleCLIError3(error, "Analysis");
|
|
719
812
|
}
|
|
720
813
|
}
|
|
721
|
-
async function handleGatekeeper(outputData, scoringResult, options, results) {
|
|
814
|
+
async function handleGatekeeper(outputData, scoringResult, options, finalOptions, results) {
|
|
722
815
|
if (!scoringResult) return;
|
|
723
|
-
const threshold = options.threshold ? parseInt(options.threshold) :
|
|
724
|
-
const failOnLevel = options.failOn ?? "critical";
|
|
816
|
+
const threshold = options.threshold ? parseInt(options.threshold) : finalOptions.threshold;
|
|
817
|
+
const failOnLevel = options.failOn ?? finalOptions.failOn ?? "critical";
|
|
725
818
|
const isCI = options.ci ?? process.env.CI === "true";
|
|
726
819
|
let shouldFail = false;
|
|
727
820
|
let failReason = "";
|
|
@@ -1026,144 +1119,160 @@ module.exports = ${JSON.stringify(defaultConfig, null, 2)};
|
|
|
1026
1119
|
}
|
|
1027
1120
|
|
|
1028
1121
|
// src/commands/patterns.ts
|
|
1122
|
+
import chalk9 from "chalk";
|
|
1123
|
+
import { printTerminalHeader } from "@aiready/core";
|
|
1124
|
+
|
|
1125
|
+
// src/utils/terminal-renderers.ts
|
|
1029
1126
|
import chalk8 from "chalk";
|
|
1030
1127
|
import {
|
|
1031
|
-
handleCLIError as handleCLIError3,
|
|
1032
|
-
getElapsedTime,
|
|
1033
1128
|
formatToolScore,
|
|
1034
|
-
|
|
1035
|
-
getTerminalDivider,
|
|
1036
|
-
prepareActionConfig,
|
|
1037
|
-
resolveOutputFormat,
|
|
1038
|
-
formatStandardReport,
|
|
1039
|
-
handleStandardJSONOutput
|
|
1129
|
+
getTerminalDivider as coreGetDivider
|
|
1040
1130
|
} from "@aiready/core";
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
cliOptions
|
|
1131
|
+
var SAFETY_ICONS = {
|
|
1132
|
+
safe: "\u2705",
|
|
1133
|
+
"moderate-risk": "\u26A0\uFE0F ",
|
|
1134
|
+
"high-risk": "\u{1F534}",
|
|
1135
|
+
"blind-risk": "\u{1F480}"
|
|
1136
|
+
};
|
|
1137
|
+
var SAFETY_COLORS = {
|
|
1138
|
+
safe: chalk8.green,
|
|
1139
|
+
"moderate-risk": chalk8.yellow,
|
|
1140
|
+
"high-risk": chalk8.red,
|
|
1141
|
+
"blind-risk": chalk8.bgRed.white
|
|
1142
|
+
};
|
|
1143
|
+
function renderToolHeader(label, emoji, score, rating) {
|
|
1144
|
+
console.log(
|
|
1145
|
+
` ${emoji} ${label}: ${chalk8.bold(score + "/100")} (${rating})`
|
|
1146
|
+
);
|
|
1147
|
+
}
|
|
1148
|
+
function renderSafetyRating(safety) {
|
|
1149
|
+
const icon = SAFETY_ICONS[safety] ?? "\u2753";
|
|
1150
|
+
const color = SAFETY_COLORS[safety] ?? chalk8.white;
|
|
1151
|
+
console.log(
|
|
1152
|
+
` AI Change Safety: ${color(`${icon} ${safety.toUpperCase()}`)}`
|
|
1153
|
+
);
|
|
1154
|
+
}
|
|
1155
|
+
function renderIssueSummaryBlock(summary) {
|
|
1156
|
+
const total = (summary.criticalIssues || 0) + (summary.majorIssues || 0) + (summary.minorIssues || 0);
|
|
1157
|
+
if (total === 0) {
|
|
1158
|
+
console.log(chalk8.green("\u2705 No significant issues found!\n"));
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
console.log(chalk8.bold("\u26A0\uFE0F Issues Found:\n"));
|
|
1162
|
+
if (summary.criticalIssues > 0)
|
|
1163
|
+
console.log(
|
|
1164
|
+
chalk8.red(` \u{1F534} Critical: ${chalk8.bold(summary.criticalIssues)}`)
|
|
1076
1165
|
);
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1166
|
+
if (summary.majorIssues > 0)
|
|
1167
|
+
console.log(
|
|
1168
|
+
chalk8.yellow(` \u{1F7E1} Major: ${chalk8.bold(summary.majorIssues)}`)
|
|
1080
1169
|
);
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1170
|
+
if (summary.minorIssues > 0)
|
|
1171
|
+
console.log(chalk8.blue(` \u{1F535} Minor: ${chalk8.bold(summary.minorIssues)}`));
|
|
1172
|
+
if (summary.totalPotentialSavings) {
|
|
1173
|
+
console.log(
|
|
1174
|
+
chalk8.green(
|
|
1175
|
+
`
|
|
1176
|
+
\u{1F4A1} Potential savings: ${chalk8.bold(summary.totalPotentialSavings.toLocaleString())} tokens
|
|
1177
|
+
`
|
|
1178
|
+
)
|
|
1090
1179
|
);
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
function renderSubSection(title) {
|
|
1183
|
+
console.log("\n" + coreGetDivider());
|
|
1184
|
+
console.log(chalk8.bold.white(` ${title.toUpperCase()}`));
|
|
1185
|
+
console.log(coreGetDivider() + "\n");
|
|
1186
|
+
}
|
|
1187
|
+
function renderToolScoreFooter(score) {
|
|
1188
|
+
if (score) {
|
|
1189
|
+
console.log(`
|
|
1190
|
+
\u{1F4CA} AI Readiness Score (${score.toolName || "Tool"})
|
|
1191
|
+
`);
|
|
1192
|
+
console.log(formatToolScore(score));
|
|
1193
|
+
console.log();
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
// src/commands/patterns.ts
|
|
1198
|
+
async function patternsAction(directory, options) {
|
|
1199
|
+
return await executeToolAction(directory, options, {
|
|
1200
|
+
toolName: "pattern-detect",
|
|
1201
|
+
label: "Pattern analysis",
|
|
1202
|
+
emoji: "\u{1F50D}",
|
|
1203
|
+
defaults: {
|
|
1204
|
+
useSmartDefaults: !options.fullScan,
|
|
1205
|
+
include: void 0,
|
|
1206
|
+
exclude: void 0,
|
|
1207
|
+
output: { format: "console", file: void 0 },
|
|
1208
|
+
minSimilarity: options.fullScan ? 0.4 : void 0,
|
|
1209
|
+
minLines: options.fullScan ? 5 : void 0
|
|
1210
|
+
},
|
|
1211
|
+
getCliOptions: (opts) => ({
|
|
1212
|
+
minSimilarity: opts.similarity ? parseFloat(opts.similarity) : void 0,
|
|
1213
|
+
minLines: opts.minLines ? parseInt(opts.minLines) : void 0,
|
|
1214
|
+
maxCandidatesPerBlock: opts.maxCandidates ? parseInt(opts.maxCandidates) : void 0,
|
|
1215
|
+
minSharedTokens: opts.minSharedTokens ? parseInt(opts.minSharedTokens) : void 0
|
|
1216
|
+
}),
|
|
1217
|
+
importTool: async () => {
|
|
1218
|
+
const { analyzePatterns, generateSummary, calculatePatternScore } = await import("@aiready/pattern-detect");
|
|
1219
|
+
return {
|
|
1220
|
+
analyze: analyzePatterns,
|
|
1221
|
+
generateSummary,
|
|
1222
|
+
calculateScore: calculatePatternScore
|
|
1223
|
+
};
|
|
1224
|
+
},
|
|
1225
|
+
renderConsole: ({ results, summary, elapsedTime, score }) => {
|
|
1226
|
+
const duplicates = results.duplicates || [];
|
|
1104
1227
|
printTerminalHeader("PATTERN ANALYSIS SUMMARY");
|
|
1105
1228
|
console.log(
|
|
1106
|
-
|
|
1229
|
+
chalk9.white(`\u{1F4C1} Files analyzed: ${chalk9.bold(results.length)}`)
|
|
1107
1230
|
);
|
|
1108
1231
|
console.log(
|
|
1109
|
-
|
|
1110
|
-
`\u26A0 Duplicate patterns found: ${
|
|
1232
|
+
chalk9.yellow(
|
|
1233
|
+
`\u26A0 Duplicate patterns found: ${chalk9.bold(summary.totalPatterns)}`
|
|
1111
1234
|
)
|
|
1112
1235
|
);
|
|
1113
1236
|
console.log(
|
|
1114
|
-
|
|
1115
|
-
`\u{1F4B0} Token cost (wasted): ${
|
|
1237
|
+
chalk9.red(
|
|
1238
|
+
`\u{1F4B0} Token cost (wasted): ${chalk9.bold(summary.totalTokenCost.toLocaleString())}`
|
|
1116
1239
|
)
|
|
1117
1240
|
);
|
|
1118
1241
|
console.log(
|
|
1119
|
-
|
|
1242
|
+
chalk9.gray(`\u23F1 Analysis time: ${chalk9.bold(elapsedTime + "s")}`)
|
|
1120
1243
|
);
|
|
1121
1244
|
const sortedTypes = Object.entries(summary.patternsByType || {}).filter(([, count]) => count > 0).sort(([, a], [, b]) => b - a);
|
|
1122
1245
|
if (sortedTypes.length > 0) {
|
|
1123
|
-
|
|
1124
|
-
console.log(chalk8.bold.white(" PATTERNS BY TYPE"));
|
|
1125
|
-
console.log(getTerminalDivider() + "\n");
|
|
1246
|
+
renderSubSection("Patterns By Type");
|
|
1126
1247
|
sortedTypes.forEach(([type, count]) => {
|
|
1127
|
-
console.log(` ${
|
|
1248
|
+
console.log(` ${chalk9.white(type.padEnd(15))} ${chalk9.bold(count)}`);
|
|
1128
1249
|
});
|
|
1129
1250
|
}
|
|
1130
1251
|
if (summary.totalPatterns > 0 && duplicates.length > 0) {
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
const severity = dup.similarity > 0.95 ? "CRITICAL" : dup.similarity > 0.9 ? "HIGH" : "MEDIUM";
|
|
1137
|
-
const severityIcon = dup.similarity > 0.95 ? "\u{1F534}" : dup.similarity > 0.9 ? "\u{1F7E1}" : "\u{1F535}";
|
|
1138
|
-
const file1Name = dup.file1.split("/").pop() || dup.file1;
|
|
1139
|
-
const file2Name = dup.file2.split("/").pop() || dup.file2;
|
|
1252
|
+
renderSubSection("Top Duplicate Patterns");
|
|
1253
|
+
[...duplicates].sort((a, b) => b.similarity - a.similarity).slice(0, 10).forEach((dup) => {
|
|
1254
|
+
const isHigh = dup.similarity > 0.9;
|
|
1255
|
+
const icon = dup.similarity > 0.95 ? "\u{1F534}" : isHigh ? "\u{1F7E1}" : "\u{1F535}";
|
|
1256
|
+
const label = dup.similarity > 0.95 ? "CRITICAL" : isHigh ? "HIGH" : "MEDIUM";
|
|
1140
1257
|
console.log(
|
|
1141
|
-
`${
|
|
1258
|
+
`${icon} ${label}: ${chalk9.bold(dup.file1.split("/").pop())} \u2194 ${chalk9.bold(dup.file2.split("/").pop())}`
|
|
1142
1259
|
);
|
|
1143
1260
|
console.log(
|
|
1144
|
-
` Similarity: ${
|
|
1261
|
+
` Similarity: ${chalk9.bold(Math.round(dup.similarity * 100) + "%")} | Wasted: ${chalk9.bold(dup.tokenCost.toLocaleString())} tokens each`
|
|
1145
1262
|
);
|
|
1146
1263
|
console.log(
|
|
1147
|
-
` Lines: ${
|
|
1264
|
+
` Lines: ${chalk9.cyan(dup.line1 + "-" + dup.endLine1)} \u2194 ${chalk9.cyan(dup.line2 + "-" + dup.endLine2)}
|
|
1148
1265
|
`
|
|
1149
1266
|
);
|
|
1150
1267
|
});
|
|
1151
1268
|
} else {
|
|
1152
1269
|
console.log(
|
|
1153
|
-
|
|
1270
|
+
chalk9.green("\n\u2728 Great! No duplicate patterns detected.\n")
|
|
1154
1271
|
);
|
|
1155
1272
|
}
|
|
1156
|
-
|
|
1157
|
-
console.log(getTerminalDivider());
|
|
1158
|
-
console.log(chalk8.bold.white(" AI READINESS SCORE (Patterns)"));
|
|
1159
|
-
console.log(getTerminalDivider() + "\n");
|
|
1160
|
-
console.log(formatToolScore(patternScore));
|
|
1161
|
-
console.log();
|
|
1162
|
-
}
|
|
1273
|
+
renderToolScoreFooter(score);
|
|
1163
1274
|
}
|
|
1164
|
-
}
|
|
1165
|
-
handleCLIError3(error, "Pattern analysis");
|
|
1166
|
-
}
|
|
1275
|
+
});
|
|
1167
1276
|
}
|
|
1168
1277
|
var PATTERNS_HELP_TEXT = `
|
|
1169
1278
|
EXAMPLES:
|
|
@@ -1173,300 +1282,150 @@ EXAMPLES:
|
|
|
1173
1282
|
`;
|
|
1174
1283
|
|
|
1175
1284
|
// src/commands/context.ts
|
|
1176
|
-
import
|
|
1177
|
-
import {
|
|
1178
|
-
handleCLIError as handleCLIError4,
|
|
1179
|
-
getElapsedTime as getElapsedTime2,
|
|
1180
|
-
formatToolScore as formatToolScore2,
|
|
1181
|
-
prepareActionConfig as prepareActionConfig2,
|
|
1182
|
-
resolveOutputFormat as resolveOutputFormat2,
|
|
1183
|
-
formatStandardReport as formatStandardReport2,
|
|
1184
|
-
handleStandardJSONOutput as handleStandardJSONOutput2
|
|
1185
|
-
} from "@aiready/core";
|
|
1285
|
+
import chalk10 from "chalk";
|
|
1286
|
+
import { printTerminalHeader as printTerminalHeader2 } from "@aiready/core";
|
|
1186
1287
|
async function contextAction(directory, options) {
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1288
|
+
return await executeToolAction(directory, options, {
|
|
1289
|
+
toolName: "context-analyzer",
|
|
1290
|
+
label: "Context analysis",
|
|
1291
|
+
emoji: "\u{1F9E0}",
|
|
1292
|
+
defaults: {
|
|
1191
1293
|
maxDepth: 5,
|
|
1192
1294
|
maxContextBudget: 1e4,
|
|
1193
1295
|
include: void 0,
|
|
1194
1296
|
exclude: void 0,
|
|
1195
|
-
output: {
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
console.log(
|
|
1217
|
-
` Min cohesion: ${(finalOptions.minCohesion * 100).toFixed(1)}%`
|
|
1218
|
-
);
|
|
1219
|
-
console.log(
|
|
1220
|
-
` Max fragmentation: ${(finalOptions.maxFragmentation * 100).toFixed(1)}%`
|
|
1221
|
-
);
|
|
1222
|
-
console.log(` Analysis focus: ${finalOptions.focus}`);
|
|
1223
|
-
console.log("");
|
|
1224
|
-
const { analyzeContext, generateSummary, calculateContextScore } = await import("@aiready/context-analyzer");
|
|
1225
|
-
const results = await analyzeContext(finalOptions);
|
|
1226
|
-
const elapsedTime = getElapsedTime2(startTime);
|
|
1227
|
-
const summary = generateSummary(results);
|
|
1228
|
-
let contextScore;
|
|
1229
|
-
if (options.score) {
|
|
1230
|
-
contextScore = calculateContextScore(summary);
|
|
1231
|
-
}
|
|
1232
|
-
const { format: outputFormat, file: userOutputFile } = resolveOutputFormat2(
|
|
1233
|
-
options,
|
|
1234
|
-
finalOptions
|
|
1235
|
-
);
|
|
1236
|
-
if (outputFormat === "json") {
|
|
1237
|
-
const outputData = formatStandardReport2({
|
|
1238
|
-
results,
|
|
1239
|
-
summary,
|
|
1240
|
-
elapsedTime,
|
|
1241
|
-
score: contextScore
|
|
1242
|
-
});
|
|
1243
|
-
handleStandardJSONOutput2({
|
|
1244
|
-
outputData,
|
|
1245
|
-
outputFile: userOutputFile,
|
|
1246
|
-
resolvedDir
|
|
1247
|
-
});
|
|
1248
|
-
} else {
|
|
1249
|
-
const terminalWidth = process.stdout.columns ?? 80;
|
|
1250
|
-
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
1251
|
-
const divider = "\u2501".repeat(dividerWidth);
|
|
1252
|
-
console.log(chalk9.cyan(divider));
|
|
1253
|
-
console.log(chalk9.bold.white(" CONTEXT ANALYSIS SUMMARY"));
|
|
1254
|
-
console.log(chalk9.cyan(divider) + "\n");
|
|
1297
|
+
output: { format: "console", file: void 0 }
|
|
1298
|
+
},
|
|
1299
|
+
getCliOptions: (opts) => ({
|
|
1300
|
+
maxDepth: opts.maxDepth ? parseInt(opts.maxDepth) : void 0,
|
|
1301
|
+
maxContextBudget: opts.maxContext ? parseInt(opts.maxContext) : void 0
|
|
1302
|
+
}),
|
|
1303
|
+
preAnalyze: async (resolvedDir, baseOptions) => {
|
|
1304
|
+
const { getSmartDefaults } = await import("@aiready/context-analyzer");
|
|
1305
|
+
const smartDefaults = await getSmartDefaults(resolvedDir, baseOptions);
|
|
1306
|
+
return { ...smartDefaults, ...baseOptions };
|
|
1307
|
+
},
|
|
1308
|
+
importTool: async () => {
|
|
1309
|
+
const { analyzeContext, generateSummary, calculateContextScore } = await import("@aiready/context-analyzer");
|
|
1310
|
+
return {
|
|
1311
|
+
analyze: analyzeContext,
|
|
1312
|
+
generateSummary,
|
|
1313
|
+
calculateScore: calculateContextScore
|
|
1314
|
+
};
|
|
1315
|
+
},
|
|
1316
|
+
renderConsole: ({ summary, elapsedTime, score }) => {
|
|
1317
|
+
printTerminalHeader2("CONTEXT ANALYSIS SUMMARY");
|
|
1255
1318
|
console.log(
|
|
1256
|
-
|
|
1319
|
+
chalk10.white(`\u{1F4C1} Files analyzed: ${chalk10.bold(summary.totalFiles)}`)
|
|
1257
1320
|
);
|
|
1258
1321
|
console.log(
|
|
1259
|
-
|
|
1260
|
-
`\u{1F4CA} Total tokens: ${
|
|
1322
|
+
chalk10.white(
|
|
1323
|
+
`\u{1F4CA} Total tokens: ${chalk10.bold(summary.totalTokens.toLocaleString())}`
|
|
1261
1324
|
)
|
|
1262
1325
|
);
|
|
1263
1326
|
console.log(
|
|
1264
|
-
|
|
1265
|
-
`\u{1F4B0} Avg context budget: ${
|
|
1327
|
+
chalk10.yellow(
|
|
1328
|
+
`\u{1F4B0} Avg context budget: ${chalk10.bold(summary.avgContextBudget.toFixed(0))} tokens/file`
|
|
1266
1329
|
)
|
|
1267
1330
|
);
|
|
1268
1331
|
console.log(
|
|
1269
|
-
|
|
1332
|
+
chalk10.white(`\u23F1 Analysis time: ${chalk10.bold(elapsedTime + "s")}
|
|
1270
1333
|
`)
|
|
1271
1334
|
);
|
|
1272
|
-
|
|
1273
|
-
if (
|
|
1274
|
-
|
|
1275
|
-
if (summary.criticalIssues > 0) {
|
|
1276
|
-
console.log(
|
|
1277
|
-
chalk9.red(` \u{1F534} Critical: ${chalk9.bold(summary.criticalIssues)}`)
|
|
1278
|
-
);
|
|
1279
|
-
}
|
|
1280
|
-
if (summary.majorIssues > 0) {
|
|
1281
|
-
console.log(
|
|
1282
|
-
chalk9.yellow(` \u{1F7E1} Major: ${chalk9.bold(summary.majorIssues)}`)
|
|
1283
|
-
);
|
|
1284
|
-
}
|
|
1285
|
-
if (summary.minorIssues > 0) {
|
|
1286
|
-
console.log(
|
|
1287
|
-
chalk9.blue(` \u{1F535} Minor: ${chalk9.bold(summary.minorIssues)}`)
|
|
1288
|
-
);
|
|
1289
|
-
}
|
|
1290
|
-
console.log(
|
|
1291
|
-
chalk9.green(
|
|
1292
|
-
`
|
|
1293
|
-
\u{1F4A1} Potential savings: ${chalk9.bold(summary.totalPotentialSavings.toLocaleString())} tokens
|
|
1294
|
-
`
|
|
1295
|
-
)
|
|
1296
|
-
);
|
|
1297
|
-
} else {
|
|
1298
|
-
console.log(chalk9.green("\u2705 No significant issues found!\n"));
|
|
1299
|
-
}
|
|
1300
|
-
if (summary.deepFiles.length > 0) {
|
|
1301
|
-
console.log(chalk9.bold("\u{1F4CF} Deep Import Chains:\n"));
|
|
1302
|
-
console.log(
|
|
1303
|
-
chalk9.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`)
|
|
1304
|
-
);
|
|
1305
|
-
console.log(
|
|
1306
|
-
chalk9.gray(` Maximum depth: ${summary.maxImportDepth}
|
|
1307
|
-
`)
|
|
1308
|
-
);
|
|
1335
|
+
renderIssueSummaryBlock(summary);
|
|
1336
|
+
if (summary.deepFiles && summary.deepFiles.length > 0) {
|
|
1337
|
+
renderSubSection("Deep Import Chains");
|
|
1309
1338
|
summary.deepFiles.slice(0, 10).forEach((item) => {
|
|
1310
1339
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1311
1340
|
console.log(
|
|
1312
|
-
` ${
|
|
1341
|
+
` ${chalk10.cyan("\u2192")} ${chalk10.white(fileName)} ${chalk10.dim(`(depth: ${item.depth})`)}`
|
|
1313
1342
|
);
|
|
1314
1343
|
});
|
|
1315
|
-
console.log();
|
|
1316
1344
|
}
|
|
1317
|
-
if (summary.fragmentedModules.length > 0) {
|
|
1318
|
-
|
|
1319
|
-
console.log(
|
|
1320
|
-
chalk9.gray(
|
|
1321
|
-
` Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(0)}%
|
|
1322
|
-
`
|
|
1323
|
-
)
|
|
1324
|
-
);
|
|
1345
|
+
if (summary.fragmentedModules && summary.fragmentedModules.length > 0) {
|
|
1346
|
+
renderSubSection("Fragmented Modules");
|
|
1325
1347
|
summary.fragmentedModules.slice(0, 10).forEach((module) => {
|
|
1326
1348
|
console.log(
|
|
1327
|
-
` ${
|
|
1349
|
+
` ${chalk10.yellow("\u25CF")} ${chalk10.white(module.domain)} - ${chalk10.dim(`${module.files.length} files, ${(module.fragmentationScore * 100).toFixed(0)}% scattered`)}`
|
|
1328
1350
|
);
|
|
1329
1351
|
console.log(
|
|
1330
|
-
|
|
1352
|
+
chalk10.dim(
|
|
1331
1353
|
` Token cost: ${module.totalTokens.toLocaleString()}, Cohesion: ${(module.avgCohesion * 100).toFixed(0)}%`
|
|
1332
1354
|
)
|
|
1333
1355
|
);
|
|
1334
1356
|
});
|
|
1335
|
-
console.log();
|
|
1336
|
-
}
|
|
1337
|
-
if (summary.lowCohesionFiles.length > 0) {
|
|
1338
|
-
console.log(chalk9.bold("\u{1F500} Low Cohesion Files:\n"));
|
|
1339
|
-
console.log(
|
|
1340
|
-
chalk9.gray(
|
|
1341
|
-
` Average cohesion: ${(summary.avgCohesion * 100).toFixed(0)}%
|
|
1342
|
-
`
|
|
1343
|
-
)
|
|
1344
|
-
);
|
|
1345
|
-
summary.lowCohesionFiles.slice(0, 10).forEach((item) => {
|
|
1346
|
-
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1347
|
-
const scorePercent = (item.score * 100).toFixed(0);
|
|
1348
|
-
const color = item.score < 0.4 ? chalk9.red : chalk9.yellow;
|
|
1349
|
-
console.log(
|
|
1350
|
-
` ${color("\u25CB")} ${chalk9.white(fileName)} ${chalk9.dim(`(${scorePercent}% cohesion)`)}`
|
|
1351
|
-
);
|
|
1352
|
-
});
|
|
1353
|
-
console.log();
|
|
1354
|
-
}
|
|
1355
|
-
if (summary.topExpensiveFiles.length > 0) {
|
|
1356
|
-
console.log(chalk9.bold("\u{1F4B8} Most Expensive Files (Context Budget):\n"));
|
|
1357
|
-
summary.topExpensiveFiles.slice(0, 10).forEach((item) => {
|
|
1358
|
-
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1359
|
-
const severityColor = item.severity === "critical" ? chalk9.red : item.severity === "major" ? chalk9.yellow : chalk9.blue;
|
|
1360
|
-
console.log(
|
|
1361
|
-
` ${severityColor("\u25CF")} ${chalk9.white(fileName)} ${chalk9.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`
|
|
1362
|
-
);
|
|
1363
|
-
});
|
|
1364
|
-
console.log();
|
|
1365
|
-
}
|
|
1366
|
-
if (contextScore) {
|
|
1367
|
-
console.log(chalk9.cyan(divider));
|
|
1368
|
-
console.log(chalk9.bold.white(" AI READINESS SCORE (Context)"));
|
|
1369
|
-
console.log(chalk9.cyan(divider) + "\n");
|
|
1370
|
-
console.log(formatToolScore2(contextScore));
|
|
1371
|
-
console.log();
|
|
1372
1357
|
}
|
|
1358
|
+
renderToolScoreFooter(score);
|
|
1373
1359
|
}
|
|
1374
|
-
}
|
|
1375
|
-
handleCLIError4(error, "Context analysis");
|
|
1376
|
-
}
|
|
1360
|
+
});
|
|
1377
1361
|
}
|
|
1378
1362
|
|
|
1379
1363
|
// src/commands/consistency.ts
|
|
1380
|
-
import
|
|
1364
|
+
import chalk11 from "chalk";
|
|
1381
1365
|
import { writeFileSync as writeFileSync3 } from "fs";
|
|
1382
1366
|
import {
|
|
1383
|
-
handleCLIError as handleCLIError5,
|
|
1384
|
-
getElapsedTime as getElapsedTime3,
|
|
1385
1367
|
resolveOutputPath as resolveOutputPath2,
|
|
1386
|
-
formatToolScore as
|
|
1387
|
-
|
|
1388
|
-
resolveOutputFormat as resolveOutputFormat3,
|
|
1389
|
-
formatStandardReport as formatStandardReport3,
|
|
1390
|
-
handleStandardJSONOutput as handleStandardJSONOutput3
|
|
1368
|
+
formatToolScore as formatToolScore2,
|
|
1369
|
+
resolveOutputFormat as resolveOutputFormat2
|
|
1391
1370
|
} from "@aiready/core";
|
|
1392
1371
|
async function consistencyAction(directory, options) {
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1372
|
+
return await executeToolAction(directory, options, {
|
|
1373
|
+
toolName: "naming-consistency",
|
|
1374
|
+
label: "Consistency analysis",
|
|
1375
|
+
emoji: "\u{1F50D}",
|
|
1376
|
+
defaults: {
|
|
1397
1377
|
checkNaming: true,
|
|
1398
1378
|
checkPatterns: true,
|
|
1399
1379
|
minSeverity: "info",
|
|
1400
1380
|
include: void 0,
|
|
1401
1381
|
exclude: void 0,
|
|
1402
|
-
output: {
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
{
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1382
|
+
output: { format: "console", file: void 0 }
|
|
1383
|
+
},
|
|
1384
|
+
getCliOptions: (opts) => ({
|
|
1385
|
+
checkNaming: opts.naming !== false,
|
|
1386
|
+
checkPatterns: opts.patterns !== false,
|
|
1387
|
+
minSeverity: opts.minSeverity
|
|
1388
|
+
}),
|
|
1389
|
+
importTool: async () => {
|
|
1390
|
+
const { analyzeConsistency, calculateConsistencyScore } = await import("@aiready/consistency");
|
|
1391
|
+
return {
|
|
1392
|
+
analyze: analyzeConsistency,
|
|
1393
|
+
generateSummary: (report) => report.summary,
|
|
1394
|
+
calculateScore: (summary, _resultsCount) => {
|
|
1395
|
+
return calculateConsistencyScore(
|
|
1396
|
+
summary.results?.flatMap((r) => r.issues) ?? [],
|
|
1397
|
+
summary.summary.filesAnalyzed
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
};
|
|
1401
|
+
},
|
|
1402
|
+
renderConsole: ({ results, summary, elapsedTime, score, finalOptions }) => {
|
|
1403
|
+
const report = results;
|
|
1404
|
+
const { format: outputFormat, file: userOutputFile } = resolveOutputFormat2(options, finalOptions);
|
|
1405
|
+
if (outputFormat === "markdown") {
|
|
1406
|
+
const markdown = generateMarkdownReport(report, elapsedTime);
|
|
1407
|
+
const outputPath = resolveOutputPath2(
|
|
1408
|
+
userOutputFile,
|
|
1409
|
+
`aiready-report-${getReportTimestamp()}.md`,
|
|
1410
|
+
directory
|
|
1411
|
+
);
|
|
1412
|
+
writeFileSync3(outputPath, markdown);
|
|
1413
|
+
console.log(chalk11.green(`\u2705 Report saved to ${outputPath}`));
|
|
1414
|
+
return;
|
|
1416
1415
|
}
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
if (options.score) {
|
|
1423
|
-
const issues = report.results?.flatMap((r) => r.issues) ?? [];
|
|
1424
|
-
consistencyScore = calculateConsistencyScore(
|
|
1425
|
-
issues,
|
|
1426
|
-
report.summary.filesAnalyzed
|
|
1427
|
-
);
|
|
1428
|
-
}
|
|
1429
|
-
const { format: outputFormat, file: userOutputFile } = resolveOutputFormat3(
|
|
1430
|
-
options,
|
|
1431
|
-
finalOptions
|
|
1432
|
-
);
|
|
1433
|
-
if (outputFormat === "json") {
|
|
1434
|
-
const outputData = formatStandardReport3({
|
|
1435
|
-
report,
|
|
1436
|
-
summary: report.summary,
|
|
1437
|
-
elapsedTime,
|
|
1438
|
-
score: consistencyScore
|
|
1439
|
-
});
|
|
1440
|
-
handleStandardJSONOutput3({
|
|
1441
|
-
outputData,
|
|
1442
|
-
outputFile: userOutputFile,
|
|
1443
|
-
resolvedDir
|
|
1444
|
-
});
|
|
1445
|
-
} else if (outputFormat === "markdown") {
|
|
1446
|
-
const markdown = generateMarkdownReport(report, elapsedTime);
|
|
1447
|
-
const outputPath = resolveOutputPath2(
|
|
1448
|
-
userOutputFile,
|
|
1449
|
-
`aiready-report-${getReportTimestamp()}.md`,
|
|
1450
|
-
resolvedDir
|
|
1451
|
-
);
|
|
1452
|
-
writeFileSync3(outputPath, markdown);
|
|
1453
|
-
console.log(chalk10.green(`\u2705 Report saved to ${outputPath}`));
|
|
1454
|
-
} else {
|
|
1455
|
-
console.log(chalk10.bold("\n\u{1F4CA} Summary\n"));
|
|
1456
|
-
console.log(
|
|
1457
|
-
`Files Analyzed: ${chalk10.cyan(report.summary.filesAnalyzed)}`
|
|
1458
|
-
);
|
|
1459
|
-
console.log(`Total Issues: ${chalk10.yellow(report.summary.totalIssues)}`);
|
|
1460
|
-
console.log(` Naming: ${chalk10.yellow(report.summary.namingIssues)}`);
|
|
1461
|
-
console.log(` Patterns: ${chalk10.yellow(report.summary.patternIssues)}`);
|
|
1416
|
+
console.log(chalk11.bold("\n\u{1F4CA} Summary\n"));
|
|
1417
|
+
console.log(`Files Analyzed: ${chalk11.cyan(summary.filesAnalyzed)}`);
|
|
1418
|
+
console.log(`Total Issues: ${chalk11.yellow(summary.totalIssues)}`);
|
|
1419
|
+
console.log(` Naming: ${chalk11.yellow(summary.namingIssues)}`);
|
|
1420
|
+
console.log(` Patterns: ${chalk11.yellow(summary.patternIssues)}`);
|
|
1462
1421
|
console.log(
|
|
1463
|
-
` Architecture: ${
|
|
1422
|
+
` Architecture: ${chalk11.yellow(summary.architectureIssues ?? 0)}`
|
|
1464
1423
|
);
|
|
1465
|
-
console.log(`Analysis Time: ${
|
|
1424
|
+
console.log(`Analysis Time: ${chalk11.gray(elapsedTime + "s")}
|
|
1466
1425
|
`);
|
|
1467
|
-
if (
|
|
1426
|
+
if (summary.totalIssues === 0) {
|
|
1468
1427
|
console.log(
|
|
1469
|
-
|
|
1428
|
+
chalk11.green(
|
|
1470
1429
|
"\u2728 No consistency issues found! Your codebase is well-maintained.\n"
|
|
1471
1430
|
)
|
|
1472
1431
|
);
|
|
@@ -1478,84 +1437,72 @@ async function consistencyAction(directory, options) {
|
|
|
1478
1437
|
(r) => r.issues.some((i) => i.category === "patterns")
|
|
1479
1438
|
);
|
|
1480
1439
|
if (namingResults.length > 0) {
|
|
1481
|
-
console.log(
|
|
1440
|
+
console.log(chalk11.bold("\u{1F3F7}\uFE0F Naming Issues\n"));
|
|
1482
1441
|
let shown = 0;
|
|
1483
1442
|
for (const namingFileResult of namingResults) {
|
|
1484
1443
|
if (shown >= 5) break;
|
|
1485
1444
|
for (const issue of namingFileResult.issues) {
|
|
1486
1445
|
if (shown >= 5) break;
|
|
1487
|
-
const severityColor = issue.severity === "critical" ?
|
|
1446
|
+
const severityColor = issue.severity === "critical" ? chalk11.red : issue.severity === "major" ? chalk11.yellow : issue.severity === "minor" ? chalk11.blue : chalk11.gray;
|
|
1488
1447
|
console.log(
|
|
1489
|
-
`${severityColor(issue.severity.toUpperCase())} ${
|
|
1448
|
+
`${severityColor(issue.severity.toUpperCase())} ${chalk11.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1490
1449
|
);
|
|
1491
1450
|
console.log(` ${issue.message}`);
|
|
1492
1451
|
if (issue.suggestion) {
|
|
1493
1452
|
console.log(
|
|
1494
|
-
` ${
|
|
1453
|
+
` ${chalk11.dim("\u2192")} ${chalk11.italic(issue.suggestion)}`
|
|
1495
1454
|
);
|
|
1496
1455
|
}
|
|
1497
1456
|
console.log();
|
|
1498
1457
|
shown++;
|
|
1499
1458
|
}
|
|
1500
1459
|
}
|
|
1501
|
-
const remaining = namingResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
1502
|
-
if (remaining > 0) {
|
|
1503
|
-
console.log(chalk10.dim(` ... and ${remaining} more issues
|
|
1504
|
-
`));
|
|
1505
|
-
}
|
|
1506
1460
|
}
|
|
1507
1461
|
if (patternResults.length > 0) {
|
|
1508
|
-
console.log(
|
|
1462
|
+
console.log(chalk11.bold("\u{1F504} Pattern Issues\n"));
|
|
1509
1463
|
let shown = 0;
|
|
1510
1464
|
for (const patternFileResult of patternResults) {
|
|
1511
1465
|
if (shown >= 5) break;
|
|
1512
1466
|
for (const issue of patternFileResult.issues) {
|
|
1513
1467
|
if (shown >= 5) break;
|
|
1514
|
-
const severityColor = issue.severity === "critical" ?
|
|
1468
|
+
const severityColor = issue.severity === "critical" ? chalk11.red : issue.severity === "major" ? chalk11.yellow : issue.severity === "minor" ? chalk11.blue : chalk11.gray;
|
|
1515
1469
|
console.log(
|
|
1516
|
-
`${severityColor(issue.severity.toUpperCase())} ${
|
|
1470
|
+
`${severityColor(issue.severity.toUpperCase())} ${chalk11.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1517
1471
|
);
|
|
1518
1472
|
console.log(` ${issue.message}`);
|
|
1519
1473
|
if (issue.suggestion) {
|
|
1520
1474
|
console.log(
|
|
1521
|
-
` ${
|
|
1475
|
+
` ${chalk11.dim("\u2192")} ${chalk11.italic(issue.suggestion)}`
|
|
1522
1476
|
);
|
|
1523
1477
|
}
|
|
1524
1478
|
console.log();
|
|
1525
1479
|
shown++;
|
|
1526
1480
|
}
|
|
1527
1481
|
}
|
|
1528
|
-
const remaining = patternResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
1529
|
-
if (remaining > 0) {
|
|
1530
|
-
console.log(chalk10.dim(` ... and ${remaining} more issues
|
|
1531
|
-
`));
|
|
1532
|
-
}
|
|
1533
1482
|
}
|
|
1534
|
-
if (report.recommendations
|
|
1535
|
-
console.log(
|
|
1483
|
+
if (report.recommendations?.length > 0) {
|
|
1484
|
+
console.log(chalk11.bold("\u{1F4A1} Recommendations\n"));
|
|
1536
1485
|
report.recommendations.forEach((rec, i) => {
|
|
1537
1486
|
console.log(`${i + 1}. ${rec}`);
|
|
1538
1487
|
});
|
|
1539
1488
|
console.log();
|
|
1540
1489
|
}
|
|
1541
1490
|
}
|
|
1542
|
-
if (
|
|
1543
|
-
console.log(
|
|
1544
|
-
console.log(
|
|
1491
|
+
if (score) {
|
|
1492
|
+
console.log(chalk11.bold("\n\u{1F4CA} AI Readiness Score (Consistency)\n"));
|
|
1493
|
+
console.log(formatToolScore2(score));
|
|
1545
1494
|
console.log();
|
|
1546
1495
|
}
|
|
1547
1496
|
}
|
|
1548
|
-
}
|
|
1549
|
-
handleCLIError5(error, "Consistency analysis");
|
|
1550
|
-
}
|
|
1497
|
+
});
|
|
1551
1498
|
}
|
|
1552
1499
|
|
|
1553
1500
|
// src/commands/visualize.ts
|
|
1554
|
-
import
|
|
1501
|
+
import chalk12 from "chalk";
|
|
1555
1502
|
import { writeFileSync as writeFileSync4, readFileSync as readFileSync3, existsSync as existsSync3, copyFileSync } from "fs";
|
|
1556
1503
|
import { resolve as resolvePath5 } from "path";
|
|
1557
1504
|
import { spawn } from "child_process";
|
|
1558
|
-
import { handleCLIError as
|
|
1505
|
+
import { handleCLIError as handleCLIError4 } from "@aiready/core";
|
|
1559
1506
|
import { generateHTML, findLatestReport as findLatestReport2 } from "@aiready/core";
|
|
1560
1507
|
async function visualizeAction(directory, options) {
|
|
1561
1508
|
try {
|
|
@@ -1566,12 +1513,12 @@ async function visualizeAction(directory, options) {
|
|
|
1566
1513
|
if (latestScan) {
|
|
1567
1514
|
reportPath = latestScan;
|
|
1568
1515
|
console.log(
|
|
1569
|
-
|
|
1516
|
+
chalk12.dim(`Found latest report: ${latestScan.split("/").pop()}`)
|
|
1570
1517
|
);
|
|
1571
1518
|
} else {
|
|
1572
|
-
console.error(
|
|
1519
|
+
console.error(chalk12.red("\u274C No AI readiness report found"));
|
|
1573
1520
|
console.log(
|
|
1574
|
-
|
|
1521
|
+
chalk12.dim(
|
|
1575
1522
|
`
|
|
1576
1523
|
Generate a report with:
|
|
1577
1524
|
aiready scan --output json
|
|
@@ -1701,19 +1648,19 @@ Or specify a custom report:
|
|
|
1701
1648
|
return;
|
|
1702
1649
|
} else {
|
|
1703
1650
|
console.log(
|
|
1704
|
-
|
|
1651
|
+
chalk12.yellow(
|
|
1705
1652
|
"\u26A0\uFE0F Dev server not available (requires local @aiready/visualizer with web assets)."
|
|
1706
1653
|
)
|
|
1707
1654
|
);
|
|
1708
1655
|
console.log(
|
|
1709
|
-
|
|
1656
|
+
chalk12.cyan(" Falling back to static HTML generation...\n")
|
|
1710
1657
|
);
|
|
1711
1658
|
useDevMode = false;
|
|
1712
1659
|
}
|
|
1713
1660
|
} catch (err) {
|
|
1714
1661
|
console.error("Failed to start dev server:", err);
|
|
1715
1662
|
console.log(
|
|
1716
|
-
|
|
1663
|
+
chalk12.cyan(" Falling back to static HTML generation...\n")
|
|
1717
1664
|
);
|
|
1718
1665
|
useDevMode = false;
|
|
1719
1666
|
}
|
|
@@ -1723,7 +1670,7 @@ Or specify a custom report:
|
|
|
1723
1670
|
const defaultOutput = "visualization.html";
|
|
1724
1671
|
const outPath = resolvePath5(dirPath, options.output ?? defaultOutput);
|
|
1725
1672
|
writeFileSync4(outPath, html, "utf8");
|
|
1726
|
-
console.log(
|
|
1673
|
+
console.log(chalk12.green(`\u2705 Visualization written to: ${outPath}`));
|
|
1727
1674
|
if (options.open || options.serve) {
|
|
1728
1675
|
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
1729
1676
|
if (options.serve) {
|
|
@@ -1753,7 +1700,7 @@ Or specify a custom report:
|
|
|
1753
1700
|
server.listen(port, () => {
|
|
1754
1701
|
const addr = `http://localhost:${port}/`;
|
|
1755
1702
|
console.log(
|
|
1756
|
-
|
|
1703
|
+
chalk12.cyan(`\u{1F310} Local visualization server running at ${addr}`)
|
|
1757
1704
|
);
|
|
1758
1705
|
spawn(opener, [`"${addr}"`], { shell: true });
|
|
1759
1706
|
});
|
|
@@ -1769,7 +1716,7 @@ Or specify a custom report:
|
|
|
1769
1716
|
}
|
|
1770
1717
|
}
|
|
1771
1718
|
} catch (err) {
|
|
1772
|
-
|
|
1719
|
+
handleCLIError4(err, "Visualization");
|
|
1773
1720
|
}
|
|
1774
1721
|
}
|
|
1775
1722
|
var VISUALIZE_HELP_TEXT = `
|
|
@@ -1800,72 +1747,134 @@ NOTES:
|
|
|
1800
1747
|
`;
|
|
1801
1748
|
|
|
1802
1749
|
// src/commands/shared/standard-tool-actions.ts
|
|
1803
|
-
import
|
|
1750
|
+
import chalk13 from "chalk";
|
|
1804
1751
|
|
|
1805
1752
|
// src/commands/agent-grounding.ts
|
|
1806
|
-
import
|
|
1807
|
-
import { loadConfig as loadConfig2, mergeConfigWithDefaults as mergeConfigWithDefaults2 } from "@aiready/core";
|
|
1753
|
+
import chalk14 from "chalk";
|
|
1808
1754
|
|
|
1809
1755
|
// src/commands/testability.ts
|
|
1810
|
-
import
|
|
1811
|
-
import { loadConfig as loadConfig3, mergeConfigWithDefaults as mergeConfigWithDefaults3 } from "@aiready/core";
|
|
1756
|
+
import chalk15 from "chalk";
|
|
1812
1757
|
async function testabilityAction(directory, options) {
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1758
|
+
return await executeToolAction(directory, options, {
|
|
1759
|
+
toolName: "testability-index",
|
|
1760
|
+
label: "Testability analysis",
|
|
1761
|
+
emoji: "\u{1F9EA}",
|
|
1762
|
+
defaults: {
|
|
1763
|
+
minCoverageRatio: 0.3,
|
|
1764
|
+
include: void 0,
|
|
1765
|
+
exclude: void 0,
|
|
1766
|
+
output: { format: "console", file: void 0 }
|
|
1767
|
+
},
|
|
1768
|
+
getCliOptions: (opts) => ({
|
|
1769
|
+
minCoverageRatio: opts.minCoverage ? parseFloat(opts.minCoverage) : void 0
|
|
1770
|
+
}),
|
|
1771
|
+
importTool: async () => {
|
|
1772
|
+
const tool = await import("@aiready/testability");
|
|
1773
|
+
return {
|
|
1774
|
+
analyze: tool.analyzeTestability,
|
|
1775
|
+
generateSummary: (report) => report.summary,
|
|
1776
|
+
calculateScore: tool.calculateTestabilityScore
|
|
1777
|
+
};
|
|
1778
|
+
},
|
|
1779
|
+
renderConsole: ({ results, summary, score }) => {
|
|
1780
|
+
renderToolHeader("Testability", "\u{1F9EA}", score?.score || 0, summary.rating);
|
|
1781
|
+
renderSafetyRating(summary.aiChangeSafetyRating);
|
|
1782
|
+
const rawData = results.rawData || results;
|
|
1783
|
+
console.log(
|
|
1784
|
+
chalk15.dim(
|
|
1785
|
+
` Coverage: ${Math.round(summary.coverageRatio * 100)}% (${rawData.testFiles} test / ${rawData.sourceFiles} source files)`
|
|
1786
|
+
)
|
|
1787
|
+
);
|
|
1788
|
+
if (summary.aiChangeSafetyRating === "blind-risk") {
|
|
1789
|
+
console.log(
|
|
1790
|
+
chalk15.red.bold(
|
|
1791
|
+
"\n \u26A0\uFE0F NO TESTS \u2014 AI changes to this codebase are completely unverifiable!\n"
|
|
1792
|
+
)
|
|
1793
|
+
);
|
|
1794
|
+
}
|
|
1795
|
+
if (score) {
|
|
1796
|
+
renderToolScoreFooter(score);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1823
1799
|
});
|
|
1824
|
-
const scoring = calculateTestabilityScore(report);
|
|
1825
|
-
if (options.output === "json") {
|
|
1826
|
-
return scoring;
|
|
1827
|
-
}
|
|
1828
|
-
const safetyIcons = {
|
|
1829
|
-
safe: "\u2705",
|
|
1830
|
-
"moderate-risk": "\u26A0\uFE0F ",
|
|
1831
|
-
"high-risk": "\u{1F534}",
|
|
1832
|
-
"blind-risk": "\u{1F480}"
|
|
1833
|
-
};
|
|
1834
|
-
const safetyColors = {
|
|
1835
|
-
safe: chalk14.green,
|
|
1836
|
-
"moderate-risk": chalk14.yellow,
|
|
1837
|
-
"high-risk": chalk14.red,
|
|
1838
|
-
"blind-risk": chalk14.bgRed.white
|
|
1839
|
-
};
|
|
1840
|
-
const safety = report.summary.aiChangeSafetyRating;
|
|
1841
|
-
const icon = safetyIcons[safety] ?? "\u2753";
|
|
1842
|
-
const color = safetyColors[safety] ?? chalk14.white;
|
|
1843
|
-
console.log(
|
|
1844
|
-
` \u{1F9EA} Testability: ${chalk14.bold(scoring.score + "/100")} (${report.summary.rating})`
|
|
1845
|
-
);
|
|
1846
|
-
console.log(
|
|
1847
|
-
` AI Change Safety: ${color(`${icon} ${safety.toUpperCase()}`)}`
|
|
1848
|
-
);
|
|
1849
|
-
console.log(
|
|
1850
|
-
chalk14.dim(
|
|
1851
|
-
` Coverage: ${Math.round(report.summary.coverageRatio * 100)}% (${report.rawData.testFiles} test / ${report.rawData.sourceFiles} source files)`
|
|
1852
|
-
)
|
|
1853
|
-
);
|
|
1854
|
-
if (safety === "blind-risk") {
|
|
1855
|
-
console.log(
|
|
1856
|
-
chalk14.red.bold(
|
|
1857
|
-
"\n \u26A0\uFE0F NO TESTS \u2014 AI changes to this codebase are completely unverifiable!\n"
|
|
1858
|
-
)
|
|
1859
|
-
);
|
|
1860
|
-
}
|
|
1861
|
-
return scoring;
|
|
1862
1800
|
}
|
|
1863
1801
|
|
|
1864
1802
|
// src/commands/change-amplification.ts
|
|
1865
1803
|
import { changeAmplificationAction } from "@aiready/change-amplification/dist/cli.js";
|
|
1866
1804
|
|
|
1805
|
+
// src/commands/contract-enforcement.ts
|
|
1806
|
+
import chalk16 from "chalk";
|
|
1807
|
+
async function contractEnforcementAction(directory, options) {
|
|
1808
|
+
return await executeToolAction(directory, options, {
|
|
1809
|
+
toolName: "contract-enforcement",
|
|
1810
|
+
label: "Contract enforcement analysis",
|
|
1811
|
+
emoji: "\u{1F6E1}\uFE0F",
|
|
1812
|
+
defaults: {
|
|
1813
|
+
minChainDepth: 3,
|
|
1814
|
+
include: void 0,
|
|
1815
|
+
exclude: void 0,
|
|
1816
|
+
output: { format: "console", file: void 0 }
|
|
1817
|
+
},
|
|
1818
|
+
getCliOptions: (opts) => ({
|
|
1819
|
+
minChainDepth: opts.minChainDepth ? parseInt(opts.minChainDepth, 10) : void 0
|
|
1820
|
+
}),
|
|
1821
|
+
importTool: async () => {
|
|
1822
|
+
const tool = await import("@aiready/contract-enforcement");
|
|
1823
|
+
return {
|
|
1824
|
+
analyze: tool.analyzeContractEnforcement,
|
|
1825
|
+
generateSummary: (report) => report.summary,
|
|
1826
|
+
calculateScore: tool.calculateContractEnforcementScore
|
|
1827
|
+
};
|
|
1828
|
+
},
|
|
1829
|
+
renderConsole: ({ results, summary, score }) => {
|
|
1830
|
+
renderToolHeader(
|
|
1831
|
+
"Contract Enforcement",
|
|
1832
|
+
"\u{1F6E1}\uFE0F",
|
|
1833
|
+
score?.score || 0,
|
|
1834
|
+
summary.rating
|
|
1835
|
+
);
|
|
1836
|
+
const rawData = results.rawData || results;
|
|
1837
|
+
console.log(
|
|
1838
|
+
chalk16.dim(
|
|
1839
|
+
` Patterns: ${summary.totalDefensivePatterns} (${summary.defensiveDensity}/kLOC) | ${summary.sourceFiles} files scanned`
|
|
1840
|
+
)
|
|
1841
|
+
);
|
|
1842
|
+
const dims = summary.dimensions;
|
|
1843
|
+
if (dims) {
|
|
1844
|
+
const entries = [
|
|
1845
|
+
["Type Escape Hatches", dims.typeEscapeHatchScore],
|
|
1846
|
+
["Fallback Cascades", dims.fallbackCascadeScore],
|
|
1847
|
+
["Error Transparency", dims.errorTransparencyScore],
|
|
1848
|
+
["Boundary Validation", dims.boundaryValidationScore]
|
|
1849
|
+
];
|
|
1850
|
+
for (const [name, val] of entries) {
|
|
1851
|
+
const color = val >= 80 ? chalk16.green : val >= 60 ? chalk16.yellow : chalk16.red;
|
|
1852
|
+
console.log(chalk16.dim(` ${name}: ${color(val + "/100")}`));
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
if (summary.totalDefensivePatterns > 0 && rawData["as-any"] !== void 0) {
|
|
1856
|
+
const breakdown = [
|
|
1857
|
+
rawData["as-any"] && `as-any: ${rawData["as-any"]}`,
|
|
1858
|
+
rawData["as-unknown"] && `as-unknown: ${rawData["as-unknown"]}`,
|
|
1859
|
+
rawData["deep-optional-chain"] && `deep-?.: ${rawData["deep-optional-chain"]}`,
|
|
1860
|
+
rawData["nullish-literal-default"] && `?? literal: ${rawData["nullish-literal-default"]}`,
|
|
1861
|
+
rawData["swallowed-error"] && `swallowed-error: ${rawData["swallowed-error"]}`,
|
|
1862
|
+
rawData["env-fallback"] && `env-fallback: ${rawData["env-fallback"]}`,
|
|
1863
|
+
rawData["unnecessary-guard"] && `guard-clause: ${rawData["unnecessary-guard"]}`,
|
|
1864
|
+
rawData["any-parameter"] && `any-param: ${rawData["any-parameter"]}`,
|
|
1865
|
+
rawData["any-return"] && `any-return: ${rawData["any-return"]}`
|
|
1866
|
+
].filter(Boolean).join(" | ");
|
|
1867
|
+
console.log(chalk16.dim(` ${breakdown}`));
|
|
1868
|
+
}
|
|
1869
|
+
if (score) {
|
|
1870
|
+
renderToolScoreFooter(score);
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
});
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1867
1876
|
// src/commands/bug.ts
|
|
1868
|
-
import
|
|
1877
|
+
import chalk17 from "chalk";
|
|
1869
1878
|
import { execSync } from "child_process";
|
|
1870
1879
|
async function bugAction(message, options) {
|
|
1871
1880
|
const repoUrl = "https://github.com/caopengau/aiready-cli";
|
|
@@ -1883,35 +1892,35 @@ Generated via AIReady CLI 'bug' command.
|
|
|
1883
1892
|
Type: ${type}
|
|
1884
1893
|
`.trim();
|
|
1885
1894
|
if (options.submit) {
|
|
1886
|
-
console.log(
|
|
1895
|
+
console.log(chalk17.blue("\u{1F680} Submitting issue via GitHub CLI...\n"));
|
|
1887
1896
|
try {
|
|
1888
1897
|
execSync("gh auth status", { stdio: "ignore" });
|
|
1889
1898
|
const command = `gh issue create --repo ${repoSlug} --title ${JSON.stringify(title)} --body ${JSON.stringify(body)} --label ${label}`;
|
|
1890
1899
|
const output = execSync(command, { encoding: "utf8" }).trim();
|
|
1891
|
-
console.log(
|
|
1892
|
-
console.log(
|
|
1900
|
+
console.log(chalk17.green("\u2705 Issue Created Successfully!"));
|
|
1901
|
+
console.log(chalk17.cyan(output));
|
|
1893
1902
|
return;
|
|
1894
1903
|
} catch {
|
|
1895
|
-
console.error(
|
|
1904
|
+
console.error(chalk17.red("\n\u274C Failed to submit via gh CLI."));
|
|
1896
1905
|
console.log(
|
|
1897
|
-
|
|
1906
|
+
chalk17.yellow(
|
|
1898
1907
|
' Make sure gh is installed and run "gh auth login".\n'
|
|
1899
1908
|
)
|
|
1900
1909
|
);
|
|
1901
|
-
console.log(
|
|
1910
|
+
console.log(chalk17.dim(" Falling back to URL generation..."));
|
|
1902
1911
|
}
|
|
1903
1912
|
}
|
|
1904
1913
|
const template = type === "bug" ? "bug_report.md" : type === "feature" ? "feature_request.md" : "new_metric_idea.md";
|
|
1905
1914
|
const fullUrl = `${repoUrl}/issues/new?title=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}&labels=${label}&template=${template}`;
|
|
1906
|
-
console.log(
|
|
1907
|
-
console.log(
|
|
1908
|
-
console.log(
|
|
1909
|
-
console.log(
|
|
1910
|
-
console.log(
|
|
1911
|
-
console.log(
|
|
1912
|
-
console.log(
|
|
1915
|
+
console.log(chalk17.green("\u{1F680} Issue Draft Prepared!\n"));
|
|
1916
|
+
console.log(chalk17.bold("Title: ") + title);
|
|
1917
|
+
console.log(chalk17.bold("Type: ") + type);
|
|
1918
|
+
console.log(chalk17.bold("\nClick the link below to submit this issue:"));
|
|
1919
|
+
console.log(chalk17.cyan(fullUrl));
|
|
1920
|
+
console.log(chalk17.bold("\n\u{1F916} [FOR AI AGENTS]:"));
|
|
1921
|
+
console.log(chalk17.dim(" You have successfully prepared a report."));
|
|
1913
1922
|
console.log(
|
|
1914
|
-
|
|
1923
|
+
chalk17.dim(
|
|
1915
1924
|
" Please present the URL above to the user so they can finalize the submission."
|
|
1916
1925
|
)
|
|
1917
1926
|
);
|
|
@@ -1920,14 +1929,14 @@ Type: ${type}
|
|
|
1920
1929
|
const bugUrl = `${repoUrl}/issues/new?template=bug_report.md`;
|
|
1921
1930
|
const featureUrl = `${repoUrl}/issues/new?template=feature_request.md`;
|
|
1922
1931
|
const metricUrl = `${repoUrl}/issues/new?template=new_metric_idea.md`;
|
|
1923
|
-
console.log(
|
|
1924
|
-
console.log(` Report a Bug: ${
|
|
1925
|
-
console.log(` Request a Feature: ${
|
|
1926
|
-
console.log(` Suggest a Metric: ${
|
|
1927
|
-
console.log(
|
|
1928
|
-
console.log(
|
|
1932
|
+
console.log(chalk17.blue("\u{1F4AC} Feedback & Bug Reports\n"));
|
|
1933
|
+
console.log(` Report a Bug: ${chalk17.cyan(bugUrl)}`);
|
|
1934
|
+
console.log(` Request a Feature: ${chalk17.cyan(featureUrl)}`);
|
|
1935
|
+
console.log(` Suggest a Metric: ${chalk17.cyan(metricUrl)}`);
|
|
1936
|
+
console.log(chalk17.bold("\n\u{1F916} [FOR AI AGENTS]:"));
|
|
1937
|
+
console.log(chalk17.dim(" To prepare a specific report, run:"));
|
|
1929
1938
|
console.log(
|
|
1930
|
-
|
|
1939
|
+
chalk17.cyan(
|
|
1931
1940
|
' aiready bug "your description here" --type bug|feature|metric'
|
|
1932
1941
|
)
|
|
1933
1942
|
);
|
|
@@ -1940,6 +1949,97 @@ EXAMPLES:
|
|
|
1940
1949
|
$ aiready bug "Fix typo in scan output" --submit # Submit directly via gh CLI
|
|
1941
1950
|
`;
|
|
1942
1951
|
|
|
1952
|
+
// src/commands/remediate.ts
|
|
1953
|
+
import chalk18 from "chalk";
|
|
1954
|
+
import { resolve as resolvePath6 } from "path";
|
|
1955
|
+
import { existsSync as existsSync4, readdirSync } from "fs";
|
|
1956
|
+
import { printTerminalHeader as printTerminalHeader3 } from "@aiready/core";
|
|
1957
|
+
async function remediateAction(directory, options) {
|
|
1958
|
+
const resolvedDir = resolvePath6(process.cwd(), directory || ".");
|
|
1959
|
+
const serverUrl = options.server || "https://platform.getaiready.dev";
|
|
1960
|
+
printTerminalHeader3("AIREADY REMEDIATION SWARM");
|
|
1961
|
+
console.log(chalk18.cyan("\u{1F916} Initializing local remediation agent..."));
|
|
1962
|
+
let reportPath = options.report;
|
|
1963
|
+
if (!reportPath) {
|
|
1964
|
+
const aireadyDir = resolvePath6(resolvedDir, ".aiready");
|
|
1965
|
+
if (existsSync4(aireadyDir)) {
|
|
1966
|
+
const files = readdirSync(aireadyDir).filter((f) => f.startsWith("aiready-report-") && f.endsWith(".json")).sort().reverse();
|
|
1967
|
+
if (files.length > 0) {
|
|
1968
|
+
reportPath = resolvePath6(aireadyDir, files[0]);
|
|
1969
|
+
console.log(chalk18.dim(`\u{1F4C2} Using latest report: ${files[0]}`));
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
if (!reportPath || !existsSync4(reportPath)) {
|
|
1974
|
+
console.log(chalk18.yellow("\n\u26A0\uFE0F No AIReady report found."));
|
|
1975
|
+
console.log(
|
|
1976
|
+
chalk18.dim(
|
|
1977
|
+
" Remediation requires a recent scan report to identify issues."
|
|
1978
|
+
)
|
|
1979
|
+
);
|
|
1980
|
+
console.log(chalk18.white(` Run ${chalk18.bold("aiready scan")} first.
|
|
1981
|
+
`));
|
|
1982
|
+
return;
|
|
1983
|
+
}
|
|
1984
|
+
console.log(chalk18.green("\n\u2705 Analysis data loaded."));
|
|
1985
|
+
console.log(chalk18.bold("\n\u{1F680} Remediation Strategy:"));
|
|
1986
|
+
if (options.tool === "patterns" || !options.tool) {
|
|
1987
|
+
console.log(
|
|
1988
|
+
chalk18.white(
|
|
1989
|
+
` \u2022 ${chalk18.bold("Pattern Consolidation")}: Suggested refactors for 95%+ similar code blocks.`
|
|
1990
|
+
)
|
|
1991
|
+
);
|
|
1992
|
+
}
|
|
1993
|
+
if (options.tool === "consistency" || !options.tool) {
|
|
1994
|
+
console.log(
|
|
1995
|
+
chalk18.white(
|
|
1996
|
+
` \u2022 ${chalk18.bold("Naming Alignment")}: Automated TSDoc generation and constant extraction.`
|
|
1997
|
+
)
|
|
1998
|
+
);
|
|
1999
|
+
}
|
|
2000
|
+
if (options.tool === "context" || !options.tool) {
|
|
2001
|
+
console.log(
|
|
2002
|
+
chalk18.white(
|
|
2003
|
+
` \u2022 ${chalk18.bold("Context Optimization")}: Barrel file cleanup and import flattening.`
|
|
2004
|
+
)
|
|
2005
|
+
);
|
|
2006
|
+
}
|
|
2007
|
+
console.log(
|
|
2008
|
+
chalk18.dim(
|
|
2009
|
+
"\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"
|
|
2010
|
+
)
|
|
2011
|
+
);
|
|
2012
|
+
console.log(chalk18.bold("\u2728 Use the Platform Swarm for Automated Fixes"));
|
|
2013
|
+
console.log(
|
|
2014
|
+
chalk18.cyan(` The high-performance Remediation Swarm is available at:`)
|
|
2015
|
+
);
|
|
2016
|
+
console.log(chalk18.white(` ${chalk18.bold(`${serverUrl}/remediate`)}`));
|
|
2017
|
+
console.log(
|
|
2018
|
+
chalk18.dim(
|
|
2019
|
+
"\n The swarm uses specialized agents to safely refactor your code,"
|
|
2020
|
+
)
|
|
2021
|
+
);
|
|
2022
|
+
console.log(
|
|
2023
|
+
chalk18.dim(" ensuring every change improves your AI-readiness score.")
|
|
2024
|
+
);
|
|
2025
|
+
console.log(
|
|
2026
|
+
chalk18.dim(
|
|
2027
|
+
"\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"
|
|
2028
|
+
)
|
|
2029
|
+
);
|
|
2030
|
+
console.log(
|
|
2031
|
+
chalk18.dim(
|
|
2032
|
+
'\n\u{1F4A1} Next Version: Local "aiready fix" command for minor documentation issues.'
|
|
2033
|
+
)
|
|
2034
|
+
);
|
|
2035
|
+
}
|
|
2036
|
+
var REMEDIATE_HELP_TEXT = `
|
|
2037
|
+
EXAMPLES:
|
|
2038
|
+
$ aiready remediate # See remediation options for latest report
|
|
2039
|
+
$ aiready remediate --tool patterns # Focus on pattern consolidation
|
|
2040
|
+
$ aiready remediate --report ./custom-report.json
|
|
2041
|
+
`;
|
|
2042
|
+
|
|
1943
2043
|
// src/cli.ts
|
|
1944
2044
|
var getDirname = () => {
|
|
1945
2045
|
if (typeof __dirname !== "undefined") return __dirname;
|
|
@@ -2090,9 +2190,19 @@ program.command("change-amplification").description("Analyze graph metrics for c
|
|
|
2090
2190
|
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) => {
|
|
2091
2191
|
await testabilityAction(directory, options);
|
|
2092
2192
|
});
|
|
2193
|
+
program.command("contract").description("Analyze structural contract enforcement and defensive coding").argument("[directory]", "Directory to analyze", ".").option(
|
|
2194
|
+
"--min-chain-depth <depth>",
|
|
2195
|
+
"Minimum optional chain depth to flag",
|
|
2196
|
+
"3"
|
|
2197
|
+
).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) => {
|
|
2198
|
+
await contractEnforcementAction(directory, options);
|
|
2199
|
+
});
|
|
2093
2200
|
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) => {
|
|
2094
2201
|
await uploadAction(file, options);
|
|
2095
2202
|
});
|
|
2203
|
+
program.command("remediate").description("Suggest AI-ready refactors based on a scan report").argument("[directory]", "Directory to remediate", ".").option("-r, --report <path>", "AIReady report JSON file").option("-t, --tool <name>", "Filter by tool: patterns, context, consistency").option("--server <url>", "Custom platform URL").addHelpText("after", REMEDIATE_HELP_TEXT).action(async (directory, options) => {
|
|
2204
|
+
await remediateAction(directory, options);
|
|
2205
|
+
});
|
|
2096
2206
|
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) => {
|
|
2097
2207
|
await bugAction(message, options);
|
|
2098
2208
|
});
|