@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/cli.mjs CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  __require,
4
4
  analyzeUnified,
5
5
  scoreUnified
6
- } from "./chunk-HUSUJEQJ.mjs";
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 handleCLIError2,
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: loadConfig4 } = await import("@aiready/core");
41
- void loadConfig4;
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 { ToolName } from "@aiready/core";
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(outputData, scoringResult, options, results);
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
- handleCLIError2(error, "Analysis");
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) : void 0;
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
- printTerminalHeader,
1035
- getTerminalDivider,
1036
- prepareActionConfig,
1037
- resolveOutputFormat,
1038
- formatStandardReport,
1039
- handleStandardJSONOutput
1129
+ getTerminalDivider as coreGetDivider
1040
1130
  } from "@aiready/core";
1041
- async function patternsAction(directory, options) {
1042
- console.log(chalk8.blue("\u{1F50D} Analyzing patterns...\n"));
1043
- const startTime = Date.now();
1044
- try {
1045
- const useSmartDefaults = !options.fullScan;
1046
- const defaults = {
1047
- useSmartDefaults,
1048
- include: void 0,
1049
- exclude: void 0,
1050
- output: {
1051
- format: "console",
1052
- file: void 0
1053
- }
1054
- };
1055
- if (!useSmartDefaults) {
1056
- defaults.minSimilarity = 0.4;
1057
- defaults.minLines = 5;
1058
- }
1059
- const cliOptions = {
1060
- minSimilarity: options.similarity ? parseFloat(options.similarity) : void 0,
1061
- minLines: options.minLines ? parseInt(options.minLines) : void 0,
1062
- useSmartDefaults,
1063
- include: options.include?.split(","),
1064
- exclude: options.exclude?.split(",")
1065
- };
1066
- if (options.maxCandidates) {
1067
- cliOptions.maxCandidatesPerBlock = parseInt(options.maxCandidates);
1068
- }
1069
- if (options.minSharedTokens) {
1070
- cliOptions.minSharedTokens = parseInt(options.minSharedTokens);
1071
- }
1072
- const { resolvedDir, finalOptions } = await prepareActionConfig(
1073
- directory,
1074
- defaults,
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
- const { analyzePatterns, generateSummary, calculatePatternScore } = await import("@aiready/pattern-detect");
1078
- const { results, duplicates } = await analyzePatterns(
1079
- finalOptions
1166
+ if (summary.majorIssues > 0)
1167
+ console.log(
1168
+ chalk8.yellow(` \u{1F7E1} Major: ${chalk8.bold(summary.majorIssues)}`)
1080
1169
  );
1081
- const elapsedTime = getElapsedTime(startTime);
1082
- const summary = generateSummary(results);
1083
- let patternScore;
1084
- if (options.score) {
1085
- patternScore = calculatePatternScore(duplicates, results.length);
1086
- }
1087
- const { format: outputFormat, file: userOutputFile } = resolveOutputFormat(
1088
- options,
1089
- finalOptions
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
- if (outputFormat === "json") {
1092
- const outputData = formatStandardReport({
1093
- results,
1094
- summary,
1095
- elapsedTime,
1096
- score: patternScore
1097
- });
1098
- handleStandardJSONOutput({
1099
- outputData,
1100
- outputFile: userOutputFile,
1101
- resolvedDir
1102
- });
1103
- } else {
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
- chalk8.white(`\u{1F4C1} Files analyzed: ${chalk8.bold(results.length)}`)
1229
+ chalk9.white(`\u{1F4C1} Files analyzed: ${chalk9.bold(results.length)}`)
1107
1230
  );
1108
1231
  console.log(
1109
- chalk8.yellow(
1110
- `\u26A0 Duplicate patterns found: ${chalk8.bold(summary.totalPatterns)}`
1232
+ chalk9.yellow(
1233
+ `\u26A0 Duplicate patterns found: ${chalk9.bold(summary.totalPatterns)}`
1111
1234
  )
1112
1235
  );
1113
1236
  console.log(
1114
- chalk8.red(
1115
- `\u{1F4B0} Token cost (wasted): ${chalk8.bold(summary.totalTokenCost.toLocaleString())}`
1237
+ chalk9.red(
1238
+ `\u{1F4B0} Token cost (wasted): ${chalk9.bold(summary.totalTokenCost.toLocaleString())}`
1116
1239
  )
1117
1240
  );
1118
1241
  console.log(
1119
- chalk8.gray(`\u23F1 Analysis time: ${chalk8.bold(elapsedTime + "s")}`)
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
- console.log("\n" + getTerminalDivider());
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(` ${chalk8.white(type.padEnd(15))} ${chalk8.bold(count)}`);
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
- console.log("\n" + getTerminalDivider());
1132
- console.log(chalk8.bold.white(" TOP DUPLICATE PATTERNS"));
1133
- console.log(getTerminalDivider() + "\n");
1134
- const topDuplicates = [...duplicates].sort((a, b) => b.similarity - a.similarity).slice(0, 10);
1135
- topDuplicates.forEach((dup) => {
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
- `${severityIcon} ${severity}: ${chalk8.bold(file1Name)} \u2194 ${chalk8.bold(file2Name)}`
1258
+ `${icon} ${label}: ${chalk9.bold(dup.file1.split("/").pop())} \u2194 ${chalk9.bold(dup.file2.split("/").pop())}`
1142
1259
  );
1143
1260
  console.log(
1144
- ` Similarity: ${chalk8.bold(Math.round(dup.similarity * 100) + "%")} | Wasted: ${chalk8.bold(dup.tokenCost.toLocaleString())} tokens each`
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: ${chalk8.cyan(dup.line1 + "-" + dup.endLine1)} \u2194 ${chalk8.cyan(dup.line2 + "-" + dup.endLine2)}
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
- chalk8.green("\n\u2728 Great! No duplicate patterns detected.\n")
1270
+ chalk9.green("\n\u2728 Great! No duplicate patterns detected.\n")
1154
1271
  );
1155
1272
  }
1156
- if (patternScore) {
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
- } catch (error) {
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 chalk9 from "chalk";
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
- console.log(chalk9.blue("\u{1F9E0} Analyzing context costs...\n"));
1188
- const startTime = Date.now();
1189
- try {
1190
- const defaults = {
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
- format: "console",
1197
- file: void 0
1198
- }
1199
- };
1200
- const { resolvedDir, finalOptions: baseOptions } = await prepareActionConfig2(directory, defaults, {
1201
- maxDepth: options.maxDepth ? parseInt(options.maxDepth) : void 0,
1202
- maxContextBudget: options.maxContext ? parseInt(options.maxContext) : void 0,
1203
- include: options.include?.split(","),
1204
- exclude: options.exclude?.split(",")
1205
- });
1206
- let finalOptions = { ...baseOptions };
1207
- const { getSmartDefaults } = await import("@aiready/context-analyzer");
1208
- const contextSmartDefaults = await getSmartDefaults(
1209
- resolvedDir,
1210
- baseOptions
1211
- );
1212
- finalOptions = { ...contextSmartDefaults, ...finalOptions };
1213
- console.log("\u{1F4CB} Configuration:");
1214
- console.log(` Max depth: ${finalOptions.maxDepth}`);
1215
- console.log(` Max context budget: ${finalOptions.maxContextBudget}`);
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
- chalk9.white(`\u{1F4C1} Files analyzed: ${chalk9.bold(summary.totalFiles)}`)
1319
+ chalk10.white(`\u{1F4C1} Files analyzed: ${chalk10.bold(summary.totalFiles)}`)
1257
1320
  );
1258
1321
  console.log(
1259
- chalk9.white(
1260
- `\u{1F4CA} Total tokens: ${chalk9.bold(summary.totalTokens.toLocaleString())}`
1322
+ chalk10.white(
1323
+ `\u{1F4CA} Total tokens: ${chalk10.bold(summary.totalTokens.toLocaleString())}`
1261
1324
  )
1262
1325
  );
1263
1326
  console.log(
1264
- chalk9.yellow(
1265
- `\u{1F4B0} Avg context budget: ${chalk9.bold(summary.avgContextBudget.toFixed(0))} tokens/file`
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
- chalk9.white(`\u23F1 Analysis time: ${chalk9.bold(elapsedTime + "s")}
1332
+ chalk10.white(`\u23F1 Analysis time: ${chalk10.bold(elapsedTime + "s")}
1270
1333
  `)
1271
1334
  );
1272
- const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
1273
- if (totalIssues > 0) {
1274
- console.log(chalk9.bold("\u26A0\uFE0F Issues Found:\n"));
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
- ` ${chalk9.cyan("\u2192")} ${chalk9.white(fileName)} ${chalk9.dim(`(depth: ${item.depth})`)}`
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
- console.log(chalk9.bold("\u{1F9E9} Fragmented Modules:\n"));
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
- ` ${chalk9.yellow("\u25CF")} ${chalk9.white(module.domain)} - ${chalk9.dim(`${module.files.length} files, ${(module.fragmentationScore * 100).toFixed(0)}% scattered`)}`
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
- chalk9.dim(
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
- } catch (error) {
1375
- handleCLIError4(error, "Context analysis");
1376
- }
1360
+ });
1377
1361
  }
1378
1362
 
1379
1363
  // src/commands/consistency.ts
1380
- import chalk10 from "chalk";
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 formatToolScore3,
1387
- prepareActionConfig as prepareActionConfig3,
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
- console.log(chalk10.blue("\u{1F50D} Analyzing consistency...\n"));
1394
- const startTime = Date.now();
1395
- try {
1396
- const defaults = {
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
- format: "console",
1404
- file: void 0
1405
- }
1406
- };
1407
- const { resolvedDir, finalOptions } = await prepareActionConfig3(
1408
- directory,
1409
- defaults,
1410
- {
1411
- checkNaming: options.naming !== false,
1412
- checkPatterns: options.patterns !== false,
1413
- minSeverity: options.minSeverity,
1414
- include: options.include?.split(","),
1415
- exclude: options.exclude?.split(",")
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
- const { analyzeConsistency, calculateConsistencyScore } = await import("@aiready/consistency");
1419
- const report = await analyzeConsistency(finalOptions);
1420
- const elapsedTime = getElapsedTime3(startTime);
1421
- let consistencyScore;
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: ${chalk10.yellow(report.summary.architectureIssues ?? 0)}`
1422
+ ` Architecture: ${chalk11.yellow(summary.architectureIssues ?? 0)}`
1464
1423
  );
1465
- console.log(`Analysis Time: ${chalk10.gray(elapsedTime + "s")}
1424
+ console.log(`Analysis Time: ${chalk11.gray(elapsedTime + "s")}
1466
1425
  `);
1467
- if (report.summary.totalIssues === 0) {
1426
+ if (summary.totalIssues === 0) {
1468
1427
  console.log(
1469
- chalk10.green(
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(chalk10.bold("\u{1F3F7}\uFE0F Naming Issues\n"));
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" ? chalk10.red : issue.severity === "major" ? chalk10.yellow : issue.severity === "minor" ? chalk10.blue : chalk10.gray;
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())} ${chalk10.dim(`${issue.location.file}:${issue.location.line}`)}`
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
- ` ${chalk10.dim("\u2192")} ${chalk10.italic(issue.suggestion)}`
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(chalk10.bold("\u{1F504} Pattern Issues\n"));
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" ? chalk10.red : issue.severity === "major" ? chalk10.yellow : issue.severity === "minor" ? chalk10.blue : chalk10.gray;
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())} ${chalk10.dim(`${issue.location.file}:${issue.location.line}`)}`
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
- ` ${chalk10.dim("\u2192")} ${chalk10.italic(issue.suggestion)}`
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.length > 0) {
1535
- console.log(chalk10.bold("\u{1F4A1} Recommendations\n"));
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 (consistencyScore) {
1543
- console.log(chalk10.bold("\n\u{1F4CA} AI Readiness Score (Consistency)\n"));
1544
- console.log(formatToolScore3(consistencyScore));
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
- } catch (error) {
1549
- handleCLIError5(error, "Consistency analysis");
1550
- }
1497
+ });
1551
1498
  }
1552
1499
 
1553
1500
  // src/commands/visualize.ts
1554
- import chalk11 from "chalk";
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 handleCLIError6 } from "@aiready/core";
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
- chalk11.dim(`Found latest report: ${latestScan.split("/").pop()}`)
1516
+ chalk12.dim(`Found latest report: ${latestScan.split("/").pop()}`)
1570
1517
  );
1571
1518
  } else {
1572
- console.error(chalk11.red("\u274C No AI readiness report found"));
1519
+ console.error(chalk12.red("\u274C No AI readiness report found"));
1573
1520
  console.log(
1574
- chalk11.dim(
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
- chalk11.yellow(
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
- chalk11.cyan(" Falling back to static HTML generation...\n")
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
- chalk11.cyan(" Falling back to static HTML generation...\n")
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(chalk11.green(`\u2705 Visualization written to: ${outPath}`));
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
- chalk11.cyan(`\u{1F310} Local visualization server running at ${addr}`)
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
- handleCLIError6(err, "Visualization");
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 chalk12 from "chalk";
1750
+ import chalk13 from "chalk";
1804
1751
 
1805
1752
  // src/commands/agent-grounding.ts
1806
- import chalk13 from "chalk";
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 chalk14 from "chalk";
1811
- import { loadConfig as loadConfig3, mergeConfigWithDefaults as mergeConfigWithDefaults3 } from "@aiready/core";
1756
+ import chalk15 from "chalk";
1812
1757
  async function testabilityAction(directory, options) {
1813
- const { analyzeTestability, calculateTestabilityScore } = await import("@aiready/testability");
1814
- const config = await loadConfig3(directory);
1815
- const merged = mergeConfigWithDefaults3(config, {
1816
- minCoverageRatio: 0.3
1817
- });
1818
- const report = await analyzeTestability({
1819
- rootDir: directory,
1820
- minCoverageRatio: options.minCoverageRatio ?? merged.minCoverageRatio,
1821
- include: options.include,
1822
- exclude: options.exclude
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 chalk15 from "chalk";
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(chalk15.blue("\u{1F680} Submitting issue via GitHub CLI...\n"));
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(chalk15.green("\u2705 Issue Created Successfully!"));
1892
- console.log(chalk15.cyan(output));
1900
+ console.log(chalk17.green("\u2705 Issue Created Successfully!"));
1901
+ console.log(chalk17.cyan(output));
1893
1902
  return;
1894
1903
  } catch {
1895
- console.error(chalk15.red("\n\u274C Failed to submit via gh CLI."));
1904
+ console.error(chalk17.red("\n\u274C Failed to submit via gh CLI."));
1896
1905
  console.log(
1897
- chalk15.yellow(
1906
+ chalk17.yellow(
1898
1907
  ' Make sure gh is installed and run "gh auth login".\n'
1899
1908
  )
1900
1909
  );
1901
- console.log(chalk15.dim(" Falling back to URL generation..."));
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(chalk15.green("\u{1F680} Issue Draft Prepared!\n"));
1907
- console.log(chalk15.bold("Title: ") + title);
1908
- console.log(chalk15.bold("Type: ") + type);
1909
- console.log(chalk15.bold("\nClick the link below to submit this issue:"));
1910
- console.log(chalk15.cyan(fullUrl));
1911
- console.log(chalk15.bold("\n\u{1F916} [FOR AI AGENTS]:"));
1912
- console.log(chalk15.dim(" You have successfully prepared a report."));
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
- chalk15.dim(
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(chalk15.blue("\u{1F4AC} Feedback & Bug Reports\n"));
1924
- console.log(` Report a Bug: ${chalk15.cyan(bugUrl)}`);
1925
- console.log(` Request a Feature: ${chalk15.cyan(featureUrl)}`);
1926
- console.log(` Suggest a Metric: ${chalk15.cyan(metricUrl)}`);
1927
- console.log(chalk15.bold("\n\u{1F916} [FOR AI AGENTS]:"));
1928
- console.log(chalk15.dim(" To prepare a specific report, run:"));
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
- chalk15.cyan(
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
  });