@aiready/cli 0.15.1 → 0.15.3

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.
Files changed (3) hide show
  1. package/dist/cli.js +186 -274
  2. package/dist/cli.mjs +175 -267
  3. package/package.json +13 -13
package/dist/cli.mjs CHANGED
@@ -88,38 +88,6 @@ async function warnIfGraphCapExceeded(report, dirPath) {
88
88
  void err;
89
89
  }
90
90
  }
91
- function generateMarkdownReport(report, elapsedTime) {
92
- let markdown = `# Consistency Analysis Report
93
-
94
- `;
95
- markdown += `**Generated:** ${(/* @__PURE__ */ new Date()).toISOString()}
96
- `;
97
- markdown += `**Analysis Time:** ${elapsedTime}s
98
-
99
- `;
100
- markdown += `## Summary
101
-
102
- `;
103
- markdown += `- **Files Analyzed:** ${report.summary.filesAnalyzed}
104
- `;
105
- markdown += `- **Total Issues:** ${report.summary.totalIssues}
106
- `;
107
- markdown += ` - Naming: ${report.summary.namingIssues}
108
- `;
109
- markdown += ` - Patterns: ${report.summary.patternIssues}
110
-
111
- `;
112
- if (report.recommendations.length > 0) {
113
- markdown += `## Recommendations
114
-
115
- `;
116
- report.recommendations.forEach((rec, i) => {
117
- markdown += `${i + 1}. ${rec}
118
- `;
119
- });
120
- }
121
- return markdown;
122
- }
123
91
 
124
92
  // src/commands/report-formatter.ts
125
93
  import chalk2 from "chalk";
@@ -433,7 +401,7 @@ async function executeToolAction(directory, options, config) {
433
401
  const elapsedTime = getElapsedTime(startTime);
434
402
  const summary = generateSummary(results);
435
403
  let toolScore;
436
- if (options.score && calculateScore) {
404
+ if ((options.score || finalOptions.score) && calculateScore) {
437
405
  const resultsAny = results;
438
406
  const scoreData = resultsAny.duplicates || resultsAny.issues || results;
439
407
  const filesCount = resultsAny.length || resultsAny.summary?.filesAnalyzed || resultsAny.summary?.totalFiles;
@@ -876,6 +844,38 @@ ${chalk6.bold("CI/CD Integration:")}
876
844
  Use --threshold and --fail-on to use AIReady as a quality gate in your CI pipelines.
877
845
  When running in GitHub Actions, it will automatically emit annotations for found issues.
878
846
  `;
847
+ function defineScanCommand(program2) {
848
+ program2.command("scan").description(
849
+ "Run comprehensive AI-readiness analysis (patterns + context + consistency)"
850
+ ).argument("[directory]", "Directory to analyze", ".").option(
851
+ "-t, --tools <tools>",
852
+ "Tools to run (comma-separated: patterns,context,consistency,doc-drift,deps-health,aiSignalClarity,grounding,testability,changeAmplification)"
853
+ ).option(
854
+ "--profile <type>",
855
+ "Scan profile to use (agentic, cost, logic, ui, security, onboarding)"
856
+ ).option(
857
+ "--compare-to <path>",
858
+ "Compare results against a previous AIReady report JSON"
859
+ ).option(
860
+ "--include <patterns>",
861
+ "File patterns to include (comma-separated)"
862
+ ).option(
863
+ "--exclude <patterns>",
864
+ "File patterns to exclude (comma-separated)"
865
+ ).option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score (0-100)").option("--no-score", "Disable calculating AI Readiness Score").option("--weights <weights>", "Custom scoring weights").option(
866
+ "--threshold <score>",
867
+ "Fail CI/CD if score below threshold (0-100)"
868
+ ).option(
869
+ "--ci",
870
+ "CI mode: GitHub Actions annotations, no colors, fail on threshold"
871
+ ).option(
872
+ "--fail-on <level>",
873
+ "Fail on issues: critical, major, any",
874
+ "critical"
875
+ ).option("--api-key <key>", "Platform API key for automatic upload").option("--upload", "Automatically upload results to the platform").option("--server <url>", "Custom platform URL").addHelpText("after", SCAN_HELP_TEXT).action(async (directory, options) => {
876
+ await scanAction(directory, options);
877
+ });
878
+ }
879
879
 
880
880
  // src/commands/init.ts
881
881
  import { writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
@@ -1158,33 +1158,6 @@ function renderSafetyRating(safety) {
1158
1158
  ` AI Change Safety: ${color(`${icon} ${safety.toUpperCase()}`)}`
1159
1159
  );
1160
1160
  }
1161
- function renderIssueSummaryBlock(summary) {
1162
- const total = (summary.criticalIssues || 0) + (summary.majorIssues || 0) + (summary.minorIssues || 0);
1163
- if (total === 0) {
1164
- console.log(chalk8.green("\u2705 No significant issues found!\n"));
1165
- return;
1166
- }
1167
- console.log(chalk8.bold("\u26A0\uFE0F Issues Found:\n"));
1168
- if (summary.criticalIssues > 0)
1169
- console.log(
1170
- chalk8.red(` \u{1F534} Critical: ${chalk8.bold(summary.criticalIssues)}`)
1171
- );
1172
- if (summary.majorIssues > 0)
1173
- console.log(
1174
- chalk8.yellow(` \u{1F7E1} Major: ${chalk8.bold(summary.majorIssues)}`)
1175
- );
1176
- if (summary.minorIssues > 0)
1177
- console.log(chalk8.blue(` \u{1F535} Minor: ${chalk8.bold(summary.minorIssues)}`));
1178
- if (summary.totalPotentialSavings) {
1179
- console.log(
1180
- chalk8.green(
1181
- `
1182
- \u{1F4A1} Potential savings: ${chalk8.bold(summary.totalPotentialSavings.toLocaleString())} tokens
1183
- `
1184
- )
1185
- );
1186
- }
1187
- }
1188
1161
  function renderSubSection(title) {
1189
1162
  console.log("\n" + coreGetDivider());
1190
1163
  console.log(chalk8.bold.white(` ${title.toUpperCase()}`));
@@ -1201,6 +1174,36 @@ function renderToolScoreFooter(score) {
1201
1174
  }
1202
1175
 
1203
1176
  // src/commands/patterns.ts
1177
+ var PATTERNS_HELP_TEXT = `
1178
+ EXAMPLES:
1179
+ $ aiready patterns # Default analysis
1180
+ $ aiready patterns --similarity 0.6 # Stricter matching
1181
+ $ aiready patterns --min-lines 10 # Larger patterns only
1182
+ `;
1183
+ function definePatternsCommand(program2) {
1184
+ program2.command("patterns").description("Detect duplicate code patterns that confuse AI models").argument("[directory]", "Directory to analyze", ".").option(
1185
+ "-s, --similarity <number>",
1186
+ "Minimum similarity score (0-1)",
1187
+ "0.40"
1188
+ ).option("-l, --min-lines <number>", "Minimum lines to consider", "5").option(
1189
+ "--max-candidates <number>",
1190
+ "Maximum candidates per block (performance tuning)"
1191
+ ).option(
1192
+ "--min-shared-tokens <number>",
1193
+ "Minimum shared tokens for candidates (performance tuning)"
1194
+ ).option(
1195
+ "--full-scan",
1196
+ "Disable smart defaults for comprehensive analysis (slower)"
1197
+ ).option(
1198
+ "--include <patterns>",
1199
+ "File patterns to include (comma-separated)"
1200
+ ).option(
1201
+ "--exclude <patterns>",
1202
+ "File patterns to exclude (comma-separated)"
1203
+ ).option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score (0-100)").option("--no-score", "Disable calculating AI Readiness Score").addHelpText("after", PATTERNS_HELP_TEXT).action(async (directory, options) => {
1204
+ await patternsAction(directory, options);
1205
+ });
1206
+ }
1204
1207
  async function patternsAction(directory, options) {
1205
1208
  return await executeToolAction(directory, options, {
1206
1209
  toolName: "pattern-detect",
@@ -1280,21 +1283,30 @@ async function patternsAction(directory, options) {
1280
1283
  }
1281
1284
  });
1282
1285
  }
1283
- var PATTERNS_HELP_TEXT = `
1284
- EXAMPLES:
1285
- $ aiready patterns # Default analysis
1286
- $ aiready patterns --similarity 0.6 # Stricter matching
1287
- $ aiready patterns --min-lines 10 # Larger patterns only
1288
- `;
1289
1286
 
1290
1287
  // src/commands/context.ts
1291
1288
  import chalk10 from "chalk";
1292
1289
  import { printTerminalHeader as printTerminalHeader2 } from "@aiready/core";
1290
+ function defineContextCommand(program2) {
1291
+ program2.command("context").description("Analyze context window costs and dependency fragmentation").argument("[directory]", "Directory to analyze", ".").option("--max-depth <number>", "Maximum acceptable import depth", "5").option(
1292
+ "--max-context <number>",
1293
+ "Maximum acceptable context budget (tokens)",
1294
+ "10000"
1295
+ ).option(
1296
+ "--include <patterns>",
1297
+ "File patterns to include (comma-separated)"
1298
+ ).option(
1299
+ "--exclude <patterns>",
1300
+ "File patterns to exclude (comma-separated)"
1301
+ ).option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score (0-100)", true).option("--no-score", "Disable calculating AI Readiness Score").action(async (directory, options) => {
1302
+ await contextAction(directory, options);
1303
+ });
1304
+ }
1293
1305
  async function contextAction(directory, options) {
1294
1306
  return await executeToolAction(directory, options, {
1295
1307
  toolName: "context-analyzer",
1296
1308
  label: "Context analysis",
1297
- emoji: "\u{1F9E0}",
1309
+ emoji: "\u{1F9E9}",
1298
1310
  defaults: {
1299
1311
  maxDepth: 5,
1300
1312
  maxContextBudget: 1e4,
@@ -1306,11 +1318,6 @@ async function contextAction(directory, options) {
1306
1318
  maxDepth: opts.maxDepth ? parseInt(opts.maxDepth) : void 0,
1307
1319
  maxContextBudget: opts.maxContext ? parseInt(opts.maxContext) : void 0
1308
1320
  }),
1309
- preAnalyze: async (resolvedDir, baseOptions) => {
1310
- const { getSmartDefaults } = await import("@aiready/context-analyzer");
1311
- const smartDefaults = await getSmartDefaults(resolvedDir, baseOptions);
1312
- return { ...smartDefaults, ...baseOptions };
1313
- },
1314
1321
  importTool: async () => {
1315
1322
  const { analyzeContext, generateSummary, calculateContextScore } = await import("@aiready/context-analyzer");
1316
1323
  return {
@@ -1319,45 +1326,40 @@ async function contextAction(directory, options) {
1319
1326
  calculateScore: calculateContextScore
1320
1327
  };
1321
1328
  },
1322
- renderConsole: ({ summary, elapsedTime, score }) => {
1329
+ renderConsole: ({ results: _results, summary, elapsedTime, score }) => {
1323
1330
  printTerminalHeader2("CONTEXT ANALYSIS SUMMARY");
1324
1331
  console.log(
1325
- chalk10.white(`\u{1F4C1} Files analyzed: ${chalk10.bold(summary.totalFiles)}`)
1332
+ chalk10.white(`\u{1F4C1} Total files: ${chalk10.bold(summary.totalFiles)}`)
1326
1333
  );
1327
1334
  console.log(
1328
1335
  chalk10.white(
1329
- `\u{1F4CA} Total tokens: ${chalk10.bold(summary.totalTokens.toLocaleString())}`
1336
+ `\u{1F4B8} Total tokens (context budget): ${chalk10.bold(summary.totalTokens.toLocaleString())}`
1330
1337
  )
1331
1338
  );
1332
1339
  console.log(
1333
- chalk10.yellow(
1334
- `\u{1F4B0} Avg context budget: ${chalk10.bold(summary.avgContextBudget.toFixed(0))} tokens/file`
1340
+ chalk10.cyan(
1341
+ `\u{1F4CA} Average context budget: ${chalk10.bold(summary.avgContextBudget.toFixed(0))} tokens`
1335
1342
  )
1336
1343
  );
1337
1344
  console.log(
1338
- chalk10.white(`\u23F1 Analysis time: ${chalk10.bold(elapsedTime + "s")}
1339
- `)
1345
+ chalk10.gray(`\u23F1 Analysis time: ${chalk10.bold(elapsedTime + "s")}`)
1340
1346
  );
1341
- renderIssueSummaryBlock(summary);
1342
- if (summary.deepFiles && summary.deepFiles.length > 0) {
1343
- renderSubSection("Deep Import Chains");
1344
- summary.deepFiles.slice(0, 10).forEach((item) => {
1345
- const fileName = item.file.split("/").slice(-2).join("/");
1347
+ if (summary.fragmentedModules.length > 0) {
1348
+ renderSubSection("Top Fragmented Modules");
1349
+ summary.fragmentedModules.slice(0, 5).forEach((mod) => {
1350
+ const scoreColor = mod.fragmentationScore > 0.7 ? chalk10.red : mod.fragmentationScore > 0.4 ? chalk10.yellow : chalk10.green;
1346
1351
  console.log(
1347
- ` ${chalk10.cyan("\u2192")} ${chalk10.white(fileName)} ${chalk10.dim(`(depth: ${item.depth})`)}`
1352
+ ` ${scoreColor("\u25A0")} ${chalk10.white(mod.domain.padEnd(20))} ${chalk10.bold((mod.fragmentationScore * 100).toFixed(0) + "%")} fragmentation`
1348
1353
  );
1349
1354
  });
1350
1355
  }
1351
- if (summary.fragmentedModules && summary.fragmentedModules.length > 0) {
1352
- renderSubSection("Fragmented Modules");
1353
- summary.fragmentedModules.slice(0, 10).forEach((module) => {
1354
- console.log(
1355
- ` ${chalk10.yellow("\u25CF")} ${chalk10.white(module.domain)} - ${chalk10.dim(`${module.files.length} files, ${(module.fragmentationScore * 100).toFixed(0)}% scattered`)}`
1356
- );
1356
+ if (summary.topExpensiveFiles.length > 0) {
1357
+ renderSubSection("Top Context-Expensive Files");
1358
+ summary.topExpensiveFiles.slice(0, 5).forEach((item) => {
1359
+ const icon = item.severity === "critical" ? "\u{1F534}" : item.severity === "major" ? "\u{1F7E1}" : "\u{1F535}";
1360
+ const color = item.severity === "critical" ? chalk10.red : item.severity === "major" ? chalk10.yellow : chalk10.blue;
1357
1361
  console.log(
1358
- chalk10.dim(
1359
- ` Token cost: ${module.totalTokens.toLocaleString()}, Cohesion: ${(module.avgCohesion * 100).toFixed(0)}%`
1360
- )
1362
+ ` ${icon} ${color(item.severity.toUpperCase())}: ${chalk10.white(item.file.split("/").pop())} ${chalk10.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`
1361
1363
  );
1362
1364
  });
1363
1365
  }
@@ -1368,21 +1370,35 @@ async function contextAction(directory, options) {
1368
1370
 
1369
1371
  // src/commands/consistency.ts
1370
1372
  import chalk11 from "chalk";
1371
- import { writeFileSync as writeFileSync3 } from "fs";
1372
- import {
1373
- resolveOutputPath as resolveOutputPath2,
1374
- formatToolScore as formatToolScore2,
1375
- resolveOutputFormat as resolveOutputFormat2
1376
- } from "@aiready/core";
1373
+ import { printTerminalHeader as printTerminalHeader3 } from "@aiready/core";
1374
+ function defineConsistencyCommand(program2) {
1375
+ program2.command("consistency").description("Check naming conventions and architectural consistency").argument("[directory]", "Directory to analyze", ".").option("--naming", "Check naming conventions (default: true)").option("--no-naming", "Skip naming analysis").option("--patterns", "Check code patterns (default: true)").option("--no-patterns", "Skip pattern analysis").option(
1376
+ "--min-severity <level>",
1377
+ "Minimum severity: info|minor|major|critical",
1378
+ "info"
1379
+ ).option(
1380
+ "--include <patterns>",
1381
+ "File patterns to include (comma-separated)"
1382
+ ).option(
1383
+ "--exclude <patterns>",
1384
+ "File patterns to exclude (comma-separated)"
1385
+ ).option(
1386
+ "-o, --output <format>",
1387
+ "Output format: console, json, markdown",
1388
+ "console"
1389
+ ).option("--output-file <path>", "Output file path (for json/markdown)").option("--score", "Calculate and display AI Readiness Score (0-100)", true).option("--no-score", "Disable calculating AI Readiness Score").action(async (directory, options) => {
1390
+ await consistencyAction(directory, options);
1391
+ });
1392
+ }
1377
1393
  async function consistencyAction(directory, options) {
1378
1394
  return await executeToolAction(directory, options, {
1379
1395
  toolName: "naming-consistency",
1380
1396
  label: "Consistency analysis",
1381
- emoji: "\u{1F50D}",
1397
+ emoji: "\u{1F4CF}",
1382
1398
  defaults: {
1383
- checkNaming: true,
1384
- checkPatterns: true,
1385
- minSeverity: "info",
1399
+ checkNaming: options.naming !== false,
1400
+ checkPatterns: options.patterns !== false,
1401
+ minSeverity: options.minSeverity || "info",
1386
1402
  include: void 0,
1387
1403
  exclude: void 0,
1388
1404
  output: { format: "console", file: void 0 }
@@ -1393,119 +1409,68 @@ async function consistencyAction(directory, options) {
1393
1409
  minSeverity: opts.minSeverity
1394
1410
  }),
1395
1411
  importTool: async () => {
1396
- const { analyzeConsistency, calculateConsistencyScore } = await import("@aiready/consistency");
1412
+ const { analyzeConsistency, generateSummary, calculateConsistencyScore } = await import("@aiready/consistency");
1397
1413
  return {
1398
- analyze: analyzeConsistency,
1399
- generateSummary: (report) => report.summary,
1400
- calculateScore: (summary, _resultsCount) => {
1401
- return calculateConsistencyScore(
1402
- summary.results?.flatMap((r) => r.issues) ?? [],
1403
- summary.summary.filesAnalyzed
1404
- );
1405
- }
1414
+ analyze: async (opts) => {
1415
+ const report = await analyzeConsistency(opts);
1416
+ return report;
1417
+ },
1418
+ generateSummary,
1419
+ calculateScore: calculateConsistencyScore
1406
1420
  };
1407
1421
  },
1408
- renderConsole: ({ results, summary, elapsedTime, score, finalOptions }) => {
1409
- const report = results;
1410
- const { format: outputFormat, file: userOutputFile } = resolveOutputFormat2(options, finalOptions);
1411
- if (outputFormat === "markdown") {
1412
- const markdown = generateMarkdownReport(report, elapsedTime);
1413
- const outputPath = resolveOutputPath2(
1414
- userOutputFile,
1415
- `aiready-report-${getReportTimestamp()}.md`,
1416
- directory
1417
- );
1418
- writeFileSync3(outputPath, markdown);
1419
- console.log(chalk11.green(`\u2705 Report saved to ${outputPath}`));
1420
- return;
1421
- }
1422
- console.log(chalk11.bold("\n\u{1F4CA} Summary\n"));
1423
- console.log(`Files Analyzed: ${chalk11.cyan(summary.filesAnalyzed)}`);
1424
- console.log(`Total Issues: ${chalk11.yellow(summary.totalIssues)}`);
1425
- console.log(` Naming: ${chalk11.yellow(summary.namingIssues)}`);
1426
- console.log(` Patterns: ${chalk11.yellow(summary.patternIssues)}`);
1422
+ renderConsole: ({ results: report, summary, elapsedTime, score }) => {
1423
+ printTerminalHeader3("CONSISTENCY ANALYSIS SUMMARY");
1427
1424
  console.log(
1428
- ` Architecture: ${chalk11.yellow(summary.architectureIssues ?? 0)}`
1425
+ chalk11.white(`\u{1F4C1} Files analyzed: ${chalk11.bold(summary.filesAnalyzed)}`)
1429
1426
  );
1430
- console.log(`Analysis Time: ${chalk11.gray(elapsedTime + "s")}
1431
- `);
1432
- if (summary.totalIssues === 0) {
1433
- console.log(
1434
- chalk11.green(
1435
- "\u2728 No consistency issues found! Your codebase is well-maintained.\n"
1436
- )
1437
- );
1438
- } else {
1439
- const namingResults = report.results.filter(
1440
- (r) => r.issues.some((i) => i.category === "naming")
1441
- );
1442
- const patternResults = report.results.filter(
1443
- (r) => r.issues.some((i) => i.category === "patterns")
1444
- );
1445
- if (namingResults.length > 0) {
1446
- console.log(chalk11.bold("\u{1F3F7}\uFE0F Naming Issues\n"));
1447
- let shown = 0;
1448
- for (const namingFileResult of namingResults) {
1449
- if (shown >= 5) break;
1450
- for (const issue of namingFileResult.issues) {
1451
- if (shown >= 5) break;
1452
- const severityColor = issue.severity === "critical" ? chalk11.red : issue.severity === "major" ? chalk11.yellow : issue.severity === "minor" ? chalk11.blue : chalk11.gray;
1453
- console.log(
1454
- `${severityColor(issue.severity.toUpperCase())} ${chalk11.dim(`${issue.location.file}:${issue.location.line}`)}`
1455
- );
1456
- console.log(` ${issue.message}`);
1457
- if (issue.suggestion) {
1458
- console.log(
1459
- ` ${chalk11.dim("\u2192")} ${chalk11.italic(issue.suggestion)}`
1460
- );
1461
- }
1462
- console.log();
1463
- shown++;
1464
- }
1465
- }
1466
- }
1467
- if (patternResults.length > 0) {
1468
- console.log(chalk11.bold("\u{1F504} Pattern Issues\n"));
1469
- let shown = 0;
1470
- for (const patternFileResult of patternResults) {
1471
- if (shown >= 5) break;
1472
- for (const issue of patternFileResult.issues) {
1473
- if (shown >= 5) break;
1474
- const severityColor = issue.severity === "critical" ? chalk11.red : issue.severity === "major" ? chalk11.yellow : issue.severity === "minor" ? chalk11.blue : chalk11.gray;
1475
- console.log(
1476
- `${severityColor(issue.severity.toUpperCase())} ${chalk11.dim(`${issue.location.file}:${issue.location.line}`)}`
1477
- );
1478
- console.log(` ${issue.message}`);
1479
- if (issue.suggestion) {
1480
- console.log(
1481
- ` ${chalk11.dim("\u2192")} ${chalk11.italic(issue.suggestion)}`
1482
- );
1483
- }
1484
- console.log();
1485
- shown++;
1486
- }
1427
+ console.log(
1428
+ chalk11.white(`\u26A0 Total issues: ${chalk11.bold(summary.totalIssues)}`)
1429
+ );
1430
+ console.log(
1431
+ chalk11.gray(`\u23F1 Analysis time: ${chalk11.bold(elapsedTime + "s")}`)
1432
+ );
1433
+ if (summary.totalIssues > 0 && report.results) {
1434
+ renderSubSection("Issues Breakdown");
1435
+ const sortedIssues = [...report.results].flatMap(
1436
+ (file) => (file.issues || []).map((issue) => ({
1437
+ ...issue,
1438
+ file: file.fileName
1439
+ }))
1440
+ ).sort((a, b) => {
1441
+ const levels = {
1442
+ critical: 4,
1443
+ major: 3,
1444
+ minor: 2,
1445
+ info: 1
1446
+ };
1447
+ return (levels[b.severity] || 0) - (levels[a.severity] || 0);
1448
+ }).slice(0, 10);
1449
+ sortedIssues.forEach((issue) => {
1450
+ const icon = issue.severity === "critical" ? "\u{1F534}" : issue.severity === "major" ? "\u{1F7E1}" : "\u{1F535}";
1451
+ const color = issue.severity === "critical" ? chalk11.red : issue.severity === "major" ? chalk11.yellow : chalk11.blue;
1452
+ console.log(
1453
+ ` ${icon} ${color(issue.severity.toUpperCase())}: ${chalk11.white(issue.file)}${issue.line ? `:${issue.line}` : ""}`
1454
+ );
1455
+ console.log(` ${issue.message}`);
1456
+ if (issue.suggestion) {
1457
+ console.log(chalk11.dim(` \u{1F4A1} ${issue.suggestion}`));
1487
1458
  }
1488
- }
1489
- if (report.recommendations?.length > 0) {
1490
- console.log(chalk11.bold("\u{1F4A1} Recommendations\n"));
1491
- report.recommendations.forEach((rec, i) => {
1492
- console.log(`${i + 1}. ${rec}`);
1493
- });
1494
1459
  console.log();
1495
- }
1496
- }
1497
- if (score) {
1498
- console.log(chalk11.bold("\n\u{1F4CA} AI Readiness Score (Consistency)\n"));
1499
- console.log(formatToolScore2(score));
1500
- console.log();
1460
+ });
1461
+ } else {
1462
+ console.log(
1463
+ chalk11.green("\n\u2728 Great! No consistency issues detected.\n")
1464
+ );
1501
1465
  }
1466
+ renderToolScoreFooter(score);
1502
1467
  }
1503
1468
  });
1504
1469
  }
1505
1470
 
1506
1471
  // src/commands/visualize.ts
1507
1472
  import chalk12 from "chalk";
1508
- import { writeFileSync as writeFileSync4, readFileSync as readFileSync3, existsSync as existsSync3, copyFileSync } from "fs";
1473
+ import { writeFileSync as writeFileSync3, readFileSync as readFileSync3, existsSync as existsSync3, copyFileSync } from "fs";
1509
1474
  import { resolve as resolvePath5 } from "path";
1510
1475
  import { spawn } from "child_process";
1511
1476
  import { handleCLIError as handleCLIError4 } from "@aiready/core";
@@ -1675,7 +1640,7 @@ Or specify a custom report:
1675
1640
  const html = generateHTML(graph);
1676
1641
  const defaultOutput = "visualization.html";
1677
1642
  const outPath = resolvePath5(dirPath, options.output ?? defaultOutput);
1678
- writeFileSync4(outPath, html, "utf8");
1643
+ writeFileSync3(outPath, html, "utf8");
1679
1644
  console.log(chalk12.green(`\u2705 Visualization written to: ${outPath}`));
1680
1645
  if (options.open || options.serve) {
1681
1646
  const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
@@ -1959,11 +1924,11 @@ EXAMPLES:
1959
1924
  import chalk18 from "chalk";
1960
1925
  import { resolve as resolvePath6 } from "path";
1961
1926
  import { existsSync as existsSync4, readdirSync } from "fs";
1962
- import { printTerminalHeader as printTerminalHeader3 } from "@aiready/core";
1927
+ import { printTerminalHeader as printTerminalHeader4 } from "@aiready/core";
1963
1928
  async function remediateAction(directory, options) {
1964
1929
  const resolvedDir = resolvePath6(process.cwd(), directory || ".");
1965
1930
  const serverUrl = options.server || "https://platform.getaiready.dev";
1966
- printTerminalHeader3("AIREADY REMEDIATION SWARM");
1931
+ printTerminalHeader4("AIREADY REMEDIATION SWARM");
1967
1932
  console.log(chalk18.cyan("\u{1F916} Initializing local remediation agent..."));
1968
1933
  let reportPath = options.report;
1969
1934
  if (!reportPath) {
@@ -2081,42 +2046,15 @@ CONFIGURATION:
2081
2046
  Config files (searched upward): aiready.json, .aiready.json, aiready.config.*
2082
2047
  CLI options override config file settings
2083
2048
 
2084
- Example aiready.json:
2085
- {
2086
- "scan": { "exclude": ["**/dist/**", "**/node_modules/**"] },
2087
- "tools": {
2088
- "pattern-detect": { "minSimilarity": 0.5 },
2089
- "context-analyzer": { "maxContextBudget": 15000 }
2090
- },
2091
- "output": { "format": "json", "directory": ".aiready" }
2092
- }
2093
-
2094
2049
  VERSION: ${packageJson.version}
2095
2050
  DOCUMENTATION: https://aiready.dev/docs/cli
2096
2051
  GITHUB: https://github.com/caopengau/aiready-cli
2097
2052
  LANDING: https://github.com/caopengau/aiready-landing`
2098
2053
  );
2099
- program.command("scan").description(
2100
- "Run comprehensive AI-readiness analysis (patterns + context + consistency)"
2101
- ).argument("[directory]", "Directory to analyze", ".").option(
2102
- "-t, --tools <tools>",
2103
- "Tools to run (comma-separated: patterns,context,consistency,doc-drift,deps-health,aiSignalClarity,grounding,testability,changeAmplification)"
2104
- ).option(
2105
- "--profile <type>",
2106
- "Scan profile to use (agentic, cost, logic, ui, security, onboarding)"
2107
- ).option(
2108
- "--compare-to <path>",
2109
- "Compare results against a previous AIReady report JSON"
2110
- ).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score (0-100)").option("--no-score", "Disable calculating AI Readiness Score").option("--weights <weights>", "Custom scoring weights").option("--threshold <score>", "Fail CI/CD if score below threshold (0-100)").option(
2111
- "--ci",
2112
- "CI mode: GitHub Actions annotations, no colors, fail on threshold"
2113
- ).option(
2114
- "--fail-on <level>",
2115
- "Fail on issues: critical, major, any",
2116
- "critical"
2117
- ).option("--api-key <key>", "Platform API key for automatic upload").option("--upload", "Automatically upload results to the platform").option("--server <url>", "Custom platform URL").addHelpText("after", SCAN_HELP_TEXT).action(async (directory, options) => {
2118
- await scanAction(directory, options);
2119
- });
2054
+ defineScanCommand(program);
2055
+ definePatternsCommand(program);
2056
+ defineContextCommand(program);
2057
+ defineConsistencyCommand(program);
2120
2058
  program.command("init").description("Generate a default configuration (aiready.json)").option("-f, --force", "Overwrite existing configuration file").option(
2121
2059
  "--js",
2122
2060
  "Generate configuration as a JavaScript file (aiready.config.js)"
@@ -2124,36 +2062,6 @@ program.command("init").description("Generate a default configuration (aiready.j
2124
2062
  const format = options.js ? "js" : "json";
2125
2063
  await initAction({ force: options.force, format, full: options.full });
2126
2064
  });
2127
- program.command("patterns").description("Detect duplicate code patterns that confuse AI models").argument("[directory]", "Directory to analyze", ".").option("-s, --similarity <number>", "Minimum similarity score (0-1)", "0.40").option("-l, --min-lines <number>", "Minimum lines to consider", "5").option(
2128
- "--max-candidates <number>",
2129
- "Maximum candidates per block (performance tuning)"
2130
- ).option(
2131
- "--min-shared-tokens <number>",
2132
- "Minimum shared tokens for candidates (performance tuning)"
2133
- ).option(
2134
- "--full-scan",
2135
- "Disable smart defaults for comprehensive analysis (slower)"
2136
- ).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score (0-100)").option("--no-score", "Disable calculating AI Readiness Score").addHelpText("after", PATTERNS_HELP_TEXT).action(async (directory, options) => {
2137
- await patternsAction(directory, options);
2138
- });
2139
- program.command("context").description("Analyze context window costs and dependency fragmentation").argument("[directory]", "Directory to analyze", ".").option("--max-depth <number>", "Maximum acceptable import depth", "5").option(
2140
- "--max-context <number>",
2141
- "Maximum acceptable context budget (tokens)",
2142
- "10000"
2143
- ).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score (0-100)", true).option("--no-score", "Disable calculating AI Readiness Score").action(async (directory, options) => {
2144
- await contextAction(directory, options);
2145
- });
2146
- program.command("consistency").description("Check naming conventions and architectural consistency").argument("[directory]", "Directory to analyze", ".").option("--naming", "Check naming conventions (default: true)").option("--no-naming", "Skip naming analysis").option("--patterns", "Check code patterns (default: true)").option("--no-patterns", "Skip pattern analysis").option(
2147
- "--min-severity <level>",
2148
- "Minimum severity: info|minor|major|critical",
2149
- "info"
2150
- ).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option(
2151
- "-o, --output <format>",
2152
- "Output format: console, json, markdown",
2153
- "console"
2154
- ).option("--output-file <path>", "Output file path (for json/markdown)").option("--score", "Calculate and display AI Readiness Score (0-100)", true).option("--no-score", "Disable calculating AI Readiness Score").action(async (directory, options) => {
2155
- await consistencyAction(directory, options);
2156
- });
2157
2065
  program.command("visualise").description("Alias for visualize (British spelling)").argument("[directory]", "Directory to analyze", ".").option(
2158
2066
  "--report <path>",
2159
2067
  "Report path (auto-detects latest .aiready/aiready-report-*.json if not provided)"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/cli",
3
- "version": "0.15.1",
3
+ "version": "0.15.3",
4
4
  "description": "Assess and improve your codebase's AI-readiness. Get an AI Readiness Score (0-100) and detect semantic duplicates, context fragmentation, and naming inconsistencies.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -24,18 +24,18 @@
24
24
  "dependencies": {
25
25
  "chalk": "^5.3.0",
26
26
  "commander": "^14.0.0",
27
- "@aiready/agent-grounding": "0.14.1",
28
- "@aiready/consistency": "0.21.1",
29
- "@aiready/context-analyzer": "0.22.1",
30
- "@aiready/core": "0.24.1",
31
- "@aiready/deps": "0.14.1",
32
- "@aiready/doc-drift": "0.14.1",
33
- "@aiready/ai-signal-clarity": "0.14.1",
34
- "@aiready/change-amplification": "0.14.1",
35
- "@aiready/pattern-detect": "0.17.1",
36
- "@aiready/visualizer": "0.7.1",
37
- "@aiready/testability": "0.7.1",
38
- "@aiready/contract-enforcement": "0.2.1"
27
+ "@aiready/agent-grounding": "0.14.3",
28
+ "@aiready/consistency": "0.21.3",
29
+ "@aiready/context-analyzer": "0.22.3",
30
+ "@aiready/deps": "0.14.3",
31
+ "@aiready/core": "0.24.3",
32
+ "@aiready/doc-drift": "0.14.3",
33
+ "@aiready/change-amplification": "0.14.3",
34
+ "@aiready/contract-enforcement": "0.2.3",
35
+ "@aiready/pattern-detect": "0.17.3",
36
+ "@aiready/ai-signal-clarity": "0.14.3",
37
+ "@aiready/testability": "0.7.3",
38
+ "@aiready/visualizer": "0.7.3"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "^24.0.0",