@aiready/cli 0.15.0 → 0.15.2

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 (4) hide show
  1. package/README.md +10 -8
  2. package/dist/cli.js +194 -276
  3. package/dist/cli.mjs +183 -269
  4. 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";
@@ -476,7 +444,8 @@ function getProfileTools(profile) {
476
444
  return [
477
445
  ToolName.AiSignalClarity,
478
446
  ToolName.AgentGrounding,
479
- ToolName.TestabilityIndex
447
+ ToolName.TestabilityIndex,
448
+ ToolName.ContractEnforcement
480
449
  ];
481
450
  case "cost":
482
451
  return [ToolName.PatternDetect, ToolName.ContextAnalyzer];
@@ -486,7 +455,8 @@ function getProfileTools(profile) {
486
455
  ToolName.NamingConsistency,
487
456
  ToolName.ContextAnalyzer,
488
457
  ToolName.PatternDetect,
489
- ToolName.ChangeAmplification
458
+ ToolName.ChangeAmplification,
459
+ ToolName.ContractEnforcement
490
460
  ];
491
461
  case "ui":
492
462
  return [
@@ -497,7 +467,11 @@ function getProfileTools(profile) {
497
467
  ToolName.AiSignalClarity
498
468
  ];
499
469
  case "security":
500
- return [ToolName.NamingConsistency, ToolName.TestabilityIndex];
470
+ return [
471
+ ToolName.NamingConsistency,
472
+ ToolName.TestabilityIndex,
473
+ ToolName.ContractEnforcement
474
+ ];
501
475
  case "onboarding":
502
476
  return [
503
477
  ToolName.ContextAnalyzer,
@@ -870,6 +844,38 @@ ${chalk6.bold("CI/CD Integration:")}
870
844
  Use --threshold and --fail-on to use AIReady as a quality gate in your CI pipelines.
871
845
  When running in GitHub Actions, it will automatically emit annotations for found issues.
872
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
+ }
873
879
 
874
880
  // src/commands/init.ts
875
881
  import { writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
@@ -1152,33 +1158,6 @@ function renderSafetyRating(safety) {
1152
1158
  ` AI Change Safety: ${color(`${icon} ${safety.toUpperCase()}`)}`
1153
1159
  );
1154
1160
  }
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)}`)
1165
- );
1166
- if (summary.majorIssues > 0)
1167
- console.log(
1168
- chalk8.yellow(` \u{1F7E1} Major: ${chalk8.bold(summary.majorIssues)}`)
1169
- );
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
- )
1179
- );
1180
- }
1181
- }
1182
1161
  function renderSubSection(title) {
1183
1162
  console.log("\n" + coreGetDivider());
1184
1163
  console.log(chalk8.bold.white(` ${title.toUpperCase()}`));
@@ -1195,6 +1174,36 @@ function renderToolScoreFooter(score) {
1195
1174
  }
1196
1175
 
1197
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
+ }
1198
1207
  async function patternsAction(directory, options) {
1199
1208
  return await executeToolAction(directory, options, {
1200
1209
  toolName: "pattern-detect",
@@ -1274,21 +1283,30 @@ async function patternsAction(directory, options) {
1274
1283
  }
1275
1284
  });
1276
1285
  }
1277
- var PATTERNS_HELP_TEXT = `
1278
- EXAMPLES:
1279
- $ aiready patterns # Default analysis
1280
- $ aiready patterns --similarity 0.6 # Stricter matching
1281
- $ aiready patterns --min-lines 10 # Larger patterns only
1282
- `;
1283
1286
 
1284
1287
  // src/commands/context.ts
1285
1288
  import chalk10 from "chalk";
1286
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
+ }
1287
1305
  async function contextAction(directory, options) {
1288
1306
  return await executeToolAction(directory, options, {
1289
1307
  toolName: "context-analyzer",
1290
1308
  label: "Context analysis",
1291
- emoji: "\u{1F9E0}",
1309
+ emoji: "\u{1F9E9}",
1292
1310
  defaults: {
1293
1311
  maxDepth: 5,
1294
1312
  maxContextBudget: 1e4,
@@ -1300,11 +1318,6 @@ async function contextAction(directory, options) {
1300
1318
  maxDepth: opts.maxDepth ? parseInt(opts.maxDepth) : void 0,
1301
1319
  maxContextBudget: opts.maxContext ? parseInt(opts.maxContext) : void 0
1302
1320
  }),
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
1321
  importTool: async () => {
1309
1322
  const { analyzeContext, generateSummary, calculateContextScore } = await import("@aiready/context-analyzer");
1310
1323
  return {
@@ -1313,45 +1326,40 @@ async function contextAction(directory, options) {
1313
1326
  calculateScore: calculateContextScore
1314
1327
  };
1315
1328
  },
1316
- renderConsole: ({ summary, elapsedTime, score }) => {
1329
+ renderConsole: ({ results: _results, summary, elapsedTime, score }) => {
1317
1330
  printTerminalHeader2("CONTEXT ANALYSIS SUMMARY");
1318
1331
  console.log(
1319
- chalk10.white(`\u{1F4C1} Files analyzed: ${chalk10.bold(summary.totalFiles)}`)
1332
+ chalk10.white(`\u{1F4C1} Total files: ${chalk10.bold(summary.totalFiles)}`)
1320
1333
  );
1321
1334
  console.log(
1322
1335
  chalk10.white(
1323
- `\u{1F4CA} Total tokens: ${chalk10.bold(summary.totalTokens.toLocaleString())}`
1336
+ `\u{1F4B8} Total tokens (context budget): ${chalk10.bold(summary.totalTokens.toLocaleString())}`
1324
1337
  )
1325
1338
  );
1326
1339
  console.log(
1327
- chalk10.yellow(
1328
- `\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`
1329
1342
  )
1330
1343
  );
1331
1344
  console.log(
1332
- chalk10.white(`\u23F1 Analysis time: ${chalk10.bold(elapsedTime + "s")}
1333
- `)
1345
+ chalk10.gray(`\u23F1 Analysis time: ${chalk10.bold(elapsedTime + "s")}`)
1334
1346
  );
1335
- renderIssueSummaryBlock(summary);
1336
- if (summary.deepFiles && summary.deepFiles.length > 0) {
1337
- renderSubSection("Deep Import Chains");
1338
- summary.deepFiles.slice(0, 10).forEach((item) => {
1339
- 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;
1340
1351
  console.log(
1341
- ` ${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`
1342
1353
  );
1343
1354
  });
1344
1355
  }
1345
- if (summary.fragmentedModules && summary.fragmentedModules.length > 0) {
1346
- renderSubSection("Fragmented Modules");
1347
- summary.fragmentedModules.slice(0, 10).forEach((module) => {
1348
- console.log(
1349
- ` ${chalk10.yellow("\u25CF")} ${chalk10.white(module.domain)} - ${chalk10.dim(`${module.files.length} files, ${(module.fragmentationScore * 100).toFixed(0)}% scattered`)}`
1350
- );
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;
1351
1361
  console.log(
1352
- chalk10.dim(
1353
- ` Token cost: ${module.totalTokens.toLocaleString()}, Cohesion: ${(module.avgCohesion * 100).toFixed(0)}%`
1354
- )
1362
+ ` ${icon} ${color(item.severity.toUpperCase())}: ${chalk10.white(item.file.split("/").pop())} ${chalk10.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`
1355
1363
  );
1356
1364
  });
1357
1365
  }
@@ -1362,21 +1370,35 @@ async function contextAction(directory, options) {
1362
1370
 
1363
1371
  // src/commands/consistency.ts
1364
1372
  import chalk11 from "chalk";
1365
- import { writeFileSync as writeFileSync3 } from "fs";
1366
- import {
1367
- resolveOutputPath as resolveOutputPath2,
1368
- formatToolScore as formatToolScore2,
1369
- resolveOutputFormat as resolveOutputFormat2
1370
- } 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
+ }
1371
1393
  async function consistencyAction(directory, options) {
1372
1394
  return await executeToolAction(directory, options, {
1373
1395
  toolName: "naming-consistency",
1374
1396
  label: "Consistency analysis",
1375
- emoji: "\u{1F50D}",
1397
+ emoji: "\u{1F4CF}",
1376
1398
  defaults: {
1377
- checkNaming: true,
1378
- checkPatterns: true,
1379
- minSeverity: "info",
1399
+ checkNaming: options.naming !== false,
1400
+ checkPatterns: options.patterns !== false,
1401
+ minSeverity: options.minSeverity || "info",
1380
1402
  include: void 0,
1381
1403
  exclude: void 0,
1382
1404
  output: { format: "console", file: void 0 }
@@ -1387,119 +1409,68 @@ async function consistencyAction(directory, options) {
1387
1409
  minSeverity: opts.minSeverity
1388
1410
  }),
1389
1411
  importTool: async () => {
1390
- const { analyzeConsistency, calculateConsistencyScore } = await import("@aiready/consistency");
1412
+ const { analyzeConsistency, generateSummary, calculateConsistencyScore } = await import("@aiready/consistency");
1391
1413
  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
- }
1414
+ analyze: async (opts) => {
1415
+ const report = await analyzeConsistency(opts);
1416
+ return report;
1417
+ },
1418
+ generateSummary,
1419
+ calculateScore: calculateConsistencyScore
1400
1420
  };
1401
1421
  },
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;
1415
- }
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)}`);
1422
+ renderConsole: ({ results: report, summary, elapsedTime, score }) => {
1423
+ printTerminalHeader3("CONSISTENCY ANALYSIS SUMMARY");
1421
1424
  console.log(
1422
- ` Architecture: ${chalk11.yellow(summary.architectureIssues ?? 0)}`
1425
+ chalk11.white(`\u{1F4C1} Files analyzed: ${chalk11.bold(summary.filesAnalyzed)}`)
1423
1426
  );
1424
- console.log(`Analysis Time: ${chalk11.gray(elapsedTime + "s")}
1425
- `);
1426
- if (summary.totalIssues === 0) {
1427
- console.log(
1428
- chalk11.green(
1429
- "\u2728 No consistency issues found! Your codebase is well-maintained.\n"
1430
- )
1431
- );
1432
- } else {
1433
- const namingResults = report.results.filter(
1434
- (r) => r.issues.some((i) => i.category === "naming")
1435
- );
1436
- const patternResults = report.results.filter(
1437
- (r) => r.issues.some((i) => i.category === "patterns")
1438
- );
1439
- if (namingResults.length > 0) {
1440
- console.log(chalk11.bold("\u{1F3F7}\uFE0F Naming Issues\n"));
1441
- let shown = 0;
1442
- for (const namingFileResult of namingResults) {
1443
- if (shown >= 5) break;
1444
- for (const issue of namingFileResult.issues) {
1445
- if (shown >= 5) break;
1446
- const severityColor = issue.severity === "critical" ? chalk11.red : issue.severity === "major" ? chalk11.yellow : issue.severity === "minor" ? chalk11.blue : chalk11.gray;
1447
- console.log(
1448
- `${severityColor(issue.severity.toUpperCase())} ${chalk11.dim(`${issue.location.file}:${issue.location.line}`)}`
1449
- );
1450
- console.log(` ${issue.message}`);
1451
- if (issue.suggestion) {
1452
- console.log(
1453
- ` ${chalk11.dim("\u2192")} ${chalk11.italic(issue.suggestion)}`
1454
- );
1455
- }
1456
- console.log();
1457
- shown++;
1458
- }
1459
- }
1460
- }
1461
- if (patternResults.length > 0) {
1462
- console.log(chalk11.bold("\u{1F504} Pattern Issues\n"));
1463
- let shown = 0;
1464
- for (const patternFileResult of patternResults) {
1465
- if (shown >= 5) break;
1466
- for (const issue of patternFileResult.issues) {
1467
- if (shown >= 5) break;
1468
- const severityColor = issue.severity === "critical" ? chalk11.red : issue.severity === "major" ? chalk11.yellow : issue.severity === "minor" ? chalk11.blue : chalk11.gray;
1469
- console.log(
1470
- `${severityColor(issue.severity.toUpperCase())} ${chalk11.dim(`${issue.location.file}:${issue.location.line}`)}`
1471
- );
1472
- console.log(` ${issue.message}`);
1473
- if (issue.suggestion) {
1474
- console.log(
1475
- ` ${chalk11.dim("\u2192")} ${chalk11.italic(issue.suggestion)}`
1476
- );
1477
- }
1478
- console.log();
1479
- shown++;
1480
- }
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}`));
1481
1458
  }
1482
- }
1483
- if (report.recommendations?.length > 0) {
1484
- console.log(chalk11.bold("\u{1F4A1} Recommendations\n"));
1485
- report.recommendations.forEach((rec, i) => {
1486
- console.log(`${i + 1}. ${rec}`);
1487
- });
1488
1459
  console.log();
1489
- }
1490
- }
1491
- if (score) {
1492
- console.log(chalk11.bold("\n\u{1F4CA} AI Readiness Score (Consistency)\n"));
1493
- console.log(formatToolScore2(score));
1494
- console.log();
1460
+ });
1461
+ } else {
1462
+ console.log(
1463
+ chalk11.green("\n\u2728 Great! No consistency issues detected.\n")
1464
+ );
1495
1465
  }
1466
+ renderToolScoreFooter(score);
1496
1467
  }
1497
1468
  });
1498
1469
  }
1499
1470
 
1500
1471
  // src/commands/visualize.ts
1501
1472
  import chalk12 from "chalk";
1502
- 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";
1503
1474
  import { resolve as resolvePath5 } from "path";
1504
1475
  import { spawn } from "child_process";
1505
1476
  import { handleCLIError as handleCLIError4 } from "@aiready/core";
@@ -1669,7 +1640,7 @@ Or specify a custom report:
1669
1640
  const html = generateHTML(graph);
1670
1641
  const defaultOutput = "visualization.html";
1671
1642
  const outPath = resolvePath5(dirPath, options.output ?? defaultOutput);
1672
- writeFileSync4(outPath, html, "utf8");
1643
+ writeFileSync3(outPath, html, "utf8");
1673
1644
  console.log(chalk12.green(`\u2705 Visualization written to: ${outPath}`));
1674
1645
  if (options.open || options.serve) {
1675
1646
  const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
@@ -1953,11 +1924,11 @@ EXAMPLES:
1953
1924
  import chalk18 from "chalk";
1954
1925
  import { resolve as resolvePath6 } from "path";
1955
1926
  import { existsSync as existsSync4, readdirSync } from "fs";
1956
- import { printTerminalHeader as printTerminalHeader3 } from "@aiready/core";
1927
+ import { printTerminalHeader as printTerminalHeader4 } from "@aiready/core";
1957
1928
  async function remediateAction(directory, options) {
1958
1929
  const resolvedDir = resolvePath6(process.cwd(), directory || ".");
1959
1930
  const serverUrl = options.server || "https://platform.getaiready.dev";
1960
- printTerminalHeader3("AIREADY REMEDIATION SWARM");
1931
+ printTerminalHeader4("AIREADY REMEDIATION SWARM");
1961
1932
  console.log(chalk18.cyan("\u{1F916} Initializing local remediation agent..."));
1962
1933
  let reportPath = options.report;
1963
1934
  if (!reportPath) {
@@ -2075,42 +2046,15 @@ CONFIGURATION:
2075
2046
  Config files (searched upward): aiready.json, .aiready.json, aiready.config.*
2076
2047
  CLI options override config file settings
2077
2048
 
2078
- Example aiready.json:
2079
- {
2080
- "scan": { "exclude": ["**/dist/**", "**/node_modules/**"] },
2081
- "tools": {
2082
- "pattern-detect": { "minSimilarity": 0.5 },
2083
- "context-analyzer": { "maxContextBudget": 15000 }
2084
- },
2085
- "output": { "format": "json", "directory": ".aiready" }
2086
- }
2087
-
2088
2049
  VERSION: ${packageJson.version}
2089
2050
  DOCUMENTATION: https://aiready.dev/docs/cli
2090
2051
  GITHUB: https://github.com/caopengau/aiready-cli
2091
2052
  LANDING: https://github.com/caopengau/aiready-landing`
2092
2053
  );
2093
- program.command("scan").description(
2094
- "Run comprehensive AI-readiness analysis (patterns + context + consistency)"
2095
- ).argument("[directory]", "Directory to analyze", ".").option(
2096
- "-t, --tools <tools>",
2097
- "Tools to run (comma-separated: patterns,context,consistency,doc-drift,deps-health,aiSignalClarity,grounding,testability,changeAmplification)"
2098
- ).option(
2099
- "--profile <type>",
2100
- "Scan profile to use (agentic, cost, logic, ui, security, onboarding)"
2101
- ).option(
2102
- "--compare-to <path>",
2103
- "Compare results against a previous AIReady report JSON"
2104
- ).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(
2105
- "--ci",
2106
- "CI mode: GitHub Actions annotations, no colors, fail on threshold"
2107
- ).option(
2108
- "--fail-on <level>",
2109
- "Fail on issues: critical, major, any",
2110
- "critical"
2111
- ).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) => {
2112
- await scanAction(directory, options);
2113
- });
2054
+ defineScanCommand(program);
2055
+ definePatternsCommand(program);
2056
+ defineContextCommand(program);
2057
+ defineConsistencyCommand(program);
2114
2058
  program.command("init").description("Generate a default configuration (aiready.json)").option("-f, --force", "Overwrite existing configuration file").option(
2115
2059
  "--js",
2116
2060
  "Generate configuration as a JavaScript file (aiready.config.js)"
@@ -2118,36 +2062,6 @@ program.command("init").description("Generate a default configuration (aiready.j
2118
2062
  const format = options.js ? "js" : "json";
2119
2063
  await initAction({ force: options.force, format, full: options.full });
2120
2064
  });
2121
- 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(
2122
- "--max-candidates <number>",
2123
- "Maximum candidates per block (performance tuning)"
2124
- ).option(
2125
- "--min-shared-tokens <number>",
2126
- "Minimum shared tokens for candidates (performance tuning)"
2127
- ).option(
2128
- "--full-scan",
2129
- "Disable smart defaults for comprehensive analysis (slower)"
2130
- ).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) => {
2131
- await patternsAction(directory, options);
2132
- });
2133
- 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(
2134
- "--max-context <number>",
2135
- "Maximum acceptable context budget (tokens)",
2136
- "10000"
2137
- ).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) => {
2138
- await contextAction(directory, options);
2139
- });
2140
- 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(
2141
- "--min-severity <level>",
2142
- "Minimum severity: info|minor|major|critical",
2143
- "info"
2144
- ).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option(
2145
- "-o, --output <format>",
2146
- "Output format: console, json, markdown",
2147
- "console"
2148
- ).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) => {
2149
- await consistencyAction(directory, options);
2150
- });
2151
2065
  program.command("visualise").description("Alias for visualize (British spelling)").argument("[directory]", "Directory to analyze", ".").option(
2152
2066
  "--report <path>",
2153
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.0",
3
+ "version": "0.15.2",
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.0",
28
- "@aiready/consistency": "0.21.0",
29
- "@aiready/deps": "0.14.0",
30
- "@aiready/context-analyzer": "0.22.0",
31
- "@aiready/core": "0.24.0",
32
- "@aiready/doc-drift": "0.14.0",
33
- "@aiready/change-amplification": "0.14.0",
34
- "@aiready/contract-enforcement": "0.2.0",
35
- "@aiready/ai-signal-clarity": "0.14.0",
36
- "@aiready/pattern-detect": "0.17.0",
37
- "@aiready/testability": "0.7.0",
38
- "@aiready/visualizer": "0.7.0"
27
+ "@aiready/agent-grounding": "0.14.2",
28
+ "@aiready/consistency": "0.21.2",
29
+ "@aiready/context-analyzer": "0.22.2",
30
+ "@aiready/core": "0.24.2",
31
+ "@aiready/deps": "0.14.2",
32
+ "@aiready/doc-drift": "0.14.2",
33
+ "@aiready/change-amplification": "0.14.2",
34
+ "@aiready/contract-enforcement": "0.2.2",
35
+ "@aiready/ai-signal-clarity": "0.14.2",
36
+ "@aiready/pattern-detect": "0.17.2",
37
+ "@aiready/testability": "0.7.2",
38
+ "@aiready/visualizer": "0.7.2"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "^24.0.0",