@aiready/cli 0.9.39 → 0.9.40

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.
@@ -1,6 +1,6 @@
1
1
 
2
2
  
3
- > @aiready/cli@0.9.39 build /Users/pengcao/projects/aiready/packages/cli
3
+ > @aiready/cli@0.9.40 build /Users/pengcao/projects/aiready/packages/cli
4
4
  > tsup src/index.ts src/cli.ts --format cjs,esm
5
5
 
6
6
  CLI Building entry: src/cli.ts, src/index.ts
@@ -10,7 +10,7 @@
10
10
  CJS Build start
11
11
  ESM Build start
12
12
 
13
- [1:04:00 PM]  WARN  ▲ [WARNING] "import.meta" is not available with the "cjs" output format and will be empty [empty-import-meta]
13
+ [2:35:42 PM]  WARN  ▲ [WARNING] "import.meta" is not available with the "cjs" output format and will be empty [empty-import-meta]
14
14
 
15
15
  src/cli.ts:23:31:
16
16
   23 │ return dirname(fileURLToPath(import.meta.url));
@@ -20,10 +20,10 @@
20
20
 
21
21
 
22
22
 
23
+ ESM dist/cli.mjs 65.28 KB
23
24
  ESM dist/index.mjs 138.00 B
24
- ESM dist/cli.mjs 63.68 KB
25
25
  ESM dist/chunk-PDOONNSK.mjs 7.80 KB
26
- ESM ⚡️ Build success in 34ms
27
- CJS dist/cli.js 73.85 KB
26
+ ESM ⚡️ Build success in 491ms
28
27
  CJS dist/index.js 9.15 KB
29
- CJS ⚡️ Build success in 35ms
28
+ CJS dist/cli.js 75.53 KB
29
+ CJS ⚡️ Build success in 492ms
@@ -1,17 +1,17 @@
1
1
 
2
2
  
3
- > @aiready/cli@0.9.39 test /Users/pengcao/projects/aiready/packages/cli
3
+ > @aiready/cli@0.9.40 test /Users/pengcao/projects/aiready/packages/cli
4
4
  > vitest run
5
5
 
6
6
  [?25l
7
7
   RUN  v4.0.18 /Users/pengcao/projects/aiready/packages/cli
8
8
 
9
- ✓ dist/__tests__/cli.test.js (3 tests) 4ms
10
- ✓ src/__tests__/cli.test.ts (3 tests) 6ms
9
+ ✓ src/__tests__/cli.test.ts (3 tests) 5ms
10
+ ✓ dist/__tests__/cli.test.js (3 tests) 2ms
11
11
 
12
12
   Test Files  2 passed (2)
13
13
   Tests  6 passed (6)
14
-  Start at  13:04:30
15
-  Duration  2.67s (transform 1.17s, setup 0ms, import 4.60s, tests 10ms, environment 0ms)
14
+  Start at  14:39:26
15
+  Duration  11.33s (transform 3.58s, setup 0ms, import 16.97s, tests 6ms, environment 0ms)
16
16
 
17
17
  [?25h
package/dist/cli.js CHANGED
@@ -311,11 +311,11 @@ async function scanAction(directory, options) {
311
311
  const resolvedDir = (0, import_path2.resolve)(process.cwd(), directory || ".");
312
312
  try {
313
313
  const defaults = {
314
- tools: ["patterns", "context", "consistency", "aiSignalClarity", "grounding", "testability", "doc-drift", "deps-health"],
314
+ tools: ["patterns", "context", "consistency", "aiSignalClarity", "grounding", "testability", "doc-drift", "deps-health", "changeAmplification"],
315
315
  include: void 0,
316
316
  exclude: void 0,
317
317
  output: {
318
- format: "json",
318
+ format: "console",
319
319
  file: void 0
320
320
  }
321
321
  };
@@ -343,11 +343,14 @@ async function scanAction(directory, options) {
343
343
  \u26A0\uFE0F Unknown profile '${options.profile}'. Using specified tools or defaults.`));
344
344
  }
345
345
  }
346
- const baseOptions = await (0, import_core.loadMergedConfig)(resolvedDir, defaults, {
347
- tools: profileTools,
346
+ const cliOverrides = {
348
347
  include: options.include?.split(","),
349
348
  exclude: options.exclude?.split(",")
350
- });
349
+ };
350
+ if (profileTools) {
351
+ cliOverrides.tools = profileTools;
352
+ }
353
+ const baseOptions = await (0, import_core.loadMergedConfig)(resolvedDir, defaults, cliOverrides);
351
354
  let finalOptions = { ...baseOptions };
352
355
  if (baseOptions.tools.includes("patterns")) {
353
356
  const { getSmartDefaults } = await import("@aiready/pattern-detect");
@@ -490,6 +493,12 @@ async function scanAction(directory, options) {
490
493
  console.log(` Deprecated Packages: ${import_chalk2.default.bold(dr.rawData.deprecatedPackages || 0)}`);
491
494
  console.log(` AI Cutoff Skew Score: ${import_chalk2.default.bold(dr.rawData.trainingCutoffSkew?.toFixed(1) || 0)}`);
492
495
  }
496
+ } else if (event.tool === "change-amplification" || event.tool === "changeAmplification") {
497
+ const dr = event.data;
498
+ console.log(` Coupling issues: ${import_chalk2.default.bold(String(dr.issues?.length || 0))}`);
499
+ if (dr.summary) {
500
+ console.log(` Complexity Score: ${import_chalk2.default.bold(dr.summary.score || 0)}/100`);
501
+ }
493
502
  }
494
503
  } catch (err) {
495
504
  }
@@ -502,7 +511,8 @@ async function scanAction(directory, options) {
502
511
  if (results.duplicates) console.log(` Duplicate patterns found: ${import_chalk2.default.bold(String(results.duplicates.length || 0))}`);
503
512
  if (results.patterns) console.log(` Pattern files with issues: ${import_chalk2.default.bold(String(results.patterns.length || 0))}`);
504
513
  if (results.context) console.log(` Context issues: ${import_chalk2.default.bold(String(results.context.length || 0))}`);
505
- if (results.consistency) console.log(` Consistency issues: ${import_chalk2.default.bold(String(results.consistency.summary.totalIssues || 0))}`);
514
+ console.log(` Consistency issues: ${import_chalk2.default.bold(String(results.consistency?.summary?.totalIssues || 0))}`);
515
+ if (results.changeAmplification) console.log(` Change amplification: ${import_chalk2.default.bold(String(results.changeAmplification.summary?.score || 0))}/100`);
506
516
  console.log(import_chalk2.default.cyan("===========================\n"));
507
517
  const elapsedTime = (0, import_core.getElapsedTime)(startTime);
508
518
  let scoringResult;
@@ -536,9 +546,9 @@ async function scanAction(directory, options) {
536
546
  }
537
547
  }
538
548
  if (results.aiSignalClarity) {
539
- const { calculateHallucinationScore } = await import("@aiready/ai-signal-clarity");
549
+ const { calculateAiSignalClarityScore } = await import("@aiready/ai-signal-clarity");
540
550
  try {
541
- const hrScore = calculateHallucinationScore(results.aiSignalClarity);
551
+ const hrScore = calculateAiSignalClarityScore(results.aiSignalClarity);
542
552
  toolScores.set("ai-signal-clarity", hrScore);
543
553
  } catch (err) {
544
554
  }
@@ -565,7 +575,7 @@ async function scanAction(directory, options) {
565
575
  score: results.docDrift.summary.score,
566
576
  rawMetrics: results.docDrift.rawData,
567
577
  factors: [],
568
- recommendations: results.docDrift.recommendations.map((action) => ({ action, estimatedImpact: 5, priority: "medium" }))
578
+ recommendations: (results.docDrift.recommendations || []).map((action) => ({ action, estimatedImpact: 5, priority: "medium" }))
569
579
  });
570
580
  }
571
581
  if (results.deps) {
@@ -574,7 +584,16 @@ async function scanAction(directory, options) {
574
584
  score: results.deps.summary.score,
575
585
  rawMetrics: results.deps.rawData,
576
586
  factors: [],
577
- recommendations: results.deps.recommendations.map((action) => ({ action, estimatedImpact: 5, priority: "medium" }))
587
+ recommendations: (results.deps.recommendations || []).map((action) => ({ action, estimatedImpact: 5, priority: "medium" }))
588
+ });
589
+ }
590
+ if (results.changeAmplification) {
591
+ toolScores.set("change-amplification", {
592
+ toolName: "change-amplification",
593
+ score: results.changeAmplification.summary.score,
594
+ rawMetrics: results.changeAmplification.rawData,
595
+ factors: [],
596
+ recommendations: (results.changeAmplification.recommendations || []).map((action) => ({ action, estimatedImpact: 5, priority: "medium" }))
578
597
  });
579
598
  }
580
599
  const cliWeights = (0, import_core.parseWeightString)(options.weights);
@@ -638,6 +657,17 @@ async function scanAction(directory, options) {
638
657
  const outputData = { ...results, scoring: scoringResult };
639
658
  (0, import_core.handleJSONOutput)(outputData, outputPath, `\u2705 Report saved to ${outputPath}`);
640
659
  warnIfGraphCapExceeded(outputData, resolvedDir);
660
+ } else {
661
+ const timestamp = getReportTimestamp();
662
+ const defaultFilename = `aiready-report-${timestamp}.json`;
663
+ const outputPath = (0, import_core.resolveOutputPath)(userOutputFile, defaultFilename, resolvedDir);
664
+ const outputData = { ...results, scoring: scoringResult };
665
+ try {
666
+ (0, import_fs2.writeFileSync)(outputPath, JSON.stringify(outputData, null, 2));
667
+ console.log(import_chalk2.default.dim(`\u2705 Report auto-persisted to ${outputPath}`));
668
+ warnIfGraphCapExceeded(outputData, resolvedDir);
669
+ } catch (err) {
670
+ }
641
671
  }
642
672
  const isCI = options.ci || process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true";
643
673
  if (isCI && scoringResult) {
@@ -1403,11 +1433,11 @@ AI READINESS SCORING:
1403
1433
  Use --score flag with any analysis command for detailed breakdown.
1404
1434
 
1405
1435
  EXAMPLES:
1406
- $ aiready scan # Quick analysis of current directory
1436
+ $ aiready scan # Comprehensive analysis of current directory
1407
1437
  $ aiready scan --score # Get AI Readiness Score (0-100)
1408
1438
  $ aiready scan --tools patterns # Run only pattern detection
1409
- $ aiready patterns --similarity 0.6 # Custom similarity threshold
1410
- $ aiready scan --output json --output-file results.json
1439
+ $ npx @aiready/cli scan # Industry standard way to run standard scan
1440
+ $ aiready scan --output json # Output raw JSON for piping
1411
1441
 
1412
1442
  GETTING STARTED:
1413
1443
  1. Run 'aiready scan' to analyze your codebase
@@ -1433,7 +1463,7 @@ VERSION: ${packageJson.version}
1433
1463
  DOCUMENTATION: https://aiready.dev/docs/cli
1434
1464
  GITHUB: https://github.com/caopengau/aiready-cli
1435
1465
  LANDING: https://github.com/caopengau/aiready-landing`);
1436
- program.command("scan").description("Run comprehensive AI-readiness analysis (patterns + context + consistency)").argument("[directory]", "Directory to analyze", ".").option("-t, --tools <tools>", "Tools to run (comma-separated: patterns,context,consistency,doc-drift,deps-health,aiSignalClarity,grounding,testability,changeAmplification)").option("--profile <type>", "Scan profile to use (agentic, cost, security, onboarding)").option("--compare-to <path>", "Compare results against a previous AIReady report JSON").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", "json").option("--output-file <path>", "Output file path (for json)").option("--no-score", "Disable calculating AI Readiness Score (enabled by default)").option("--weights <weights>", "Custom scoring weights").option("--threshold <score>", "Fail CI/CD if score below threshold (0-100)").option("--ci", "CI mode: GitHub Actions annotations, no colors, fail on threshold").option("--fail-on <level>", "Fail on issues: critical, major, any", "critical").addHelpText("after", scanHelpText).action(async (directory, options) => {
1466
+ program.command("scan").description("Run comprehensive AI-readiness analysis (patterns + context + consistency)").argument("[directory]", "Directory to analyze", ".").option("-t, --tools <tools>", "Tools to run (comma-separated: patterns,context,consistency,doc-drift,deps-health,aiSignalClarity,grounding,testability,changeAmplification)").option("--profile <type>", "Scan profile to use (agentic, cost, security, onboarding)").option("--compare-to <path>", "Compare results against a previous AIReady report JSON").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("--no-score", "Disable calculating AI Readiness Score (enabled by default)").option("--weights <weights>", "Custom scoring weights").option("--threshold <score>", "Fail CI/CD if score below threshold (0-100)").option("--ci", "CI mode: GitHub Actions annotations, no colors, fail on threshold").option("--fail-on <level>", "Fail on issues: critical, major, any", "critical").addHelpText("after", scanHelpText).action(async (directory, options) => {
1437
1467
  await scanAction(directory, options);
1438
1468
  });
1439
1469
  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("--max-candidates <number>", "Maximum candidates per block (performance tuning)").option("--min-shared-tokens <number>", "Minimum shared tokens for candidates (performance tuning)").option("--full-scan", "Disable smart defaults for comprehensive analysis (slower)").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 for patterns (0-100)").addHelpText("after", patternsHelpText).action(async (directory, options) => {
package/dist/cli.mjs CHANGED
@@ -12,7 +12,7 @@ import { fileURLToPath } from "url";
12
12
 
13
13
  // src/commands/scan.ts
14
14
  import chalk2 from "chalk";
15
- import { readFileSync as readFileSync2 } from "fs";
15
+ import { writeFileSync, readFileSync as readFileSync2 } from "fs";
16
16
  import { resolve as resolvePath2 } from "path";
17
17
  import {
18
18
  loadMergedConfig,
@@ -140,11 +140,11 @@ async function scanAction(directory, options) {
140
140
  const resolvedDir = resolvePath2(process.cwd(), directory || ".");
141
141
  try {
142
142
  const defaults = {
143
- tools: ["patterns", "context", "consistency", "aiSignalClarity", "grounding", "testability", "doc-drift", "deps-health"],
143
+ tools: ["patterns", "context", "consistency", "aiSignalClarity", "grounding", "testability", "doc-drift", "deps-health", "changeAmplification"],
144
144
  include: void 0,
145
145
  exclude: void 0,
146
146
  output: {
147
- format: "json",
147
+ format: "console",
148
148
  file: void 0
149
149
  }
150
150
  };
@@ -172,11 +172,14 @@ async function scanAction(directory, options) {
172
172
  \u26A0\uFE0F Unknown profile '${options.profile}'. Using specified tools or defaults.`));
173
173
  }
174
174
  }
175
- const baseOptions = await loadMergedConfig(resolvedDir, defaults, {
176
- tools: profileTools,
175
+ const cliOverrides = {
177
176
  include: options.include?.split(","),
178
177
  exclude: options.exclude?.split(",")
179
- });
178
+ };
179
+ if (profileTools) {
180
+ cliOverrides.tools = profileTools;
181
+ }
182
+ const baseOptions = await loadMergedConfig(resolvedDir, defaults, cliOverrides);
180
183
  let finalOptions = { ...baseOptions };
181
184
  if (baseOptions.tools.includes("patterns")) {
182
185
  const { getSmartDefaults } = await import("@aiready/pattern-detect");
@@ -319,6 +322,12 @@ async function scanAction(directory, options) {
319
322
  console.log(` Deprecated Packages: ${chalk2.bold(dr.rawData.deprecatedPackages || 0)}`);
320
323
  console.log(` AI Cutoff Skew Score: ${chalk2.bold(dr.rawData.trainingCutoffSkew?.toFixed(1) || 0)}`);
321
324
  }
325
+ } else if (event.tool === "change-amplification" || event.tool === "changeAmplification") {
326
+ const dr = event.data;
327
+ console.log(` Coupling issues: ${chalk2.bold(String(dr.issues?.length || 0))}`);
328
+ if (dr.summary) {
329
+ console.log(` Complexity Score: ${chalk2.bold(dr.summary.score || 0)}/100`);
330
+ }
322
331
  }
323
332
  } catch (err) {
324
333
  }
@@ -331,7 +340,8 @@ async function scanAction(directory, options) {
331
340
  if (results.duplicates) console.log(` Duplicate patterns found: ${chalk2.bold(String(results.duplicates.length || 0))}`);
332
341
  if (results.patterns) console.log(` Pattern files with issues: ${chalk2.bold(String(results.patterns.length || 0))}`);
333
342
  if (results.context) console.log(` Context issues: ${chalk2.bold(String(results.context.length || 0))}`);
334
- if (results.consistency) console.log(` Consistency issues: ${chalk2.bold(String(results.consistency.summary.totalIssues || 0))}`);
343
+ console.log(` Consistency issues: ${chalk2.bold(String(results.consistency?.summary?.totalIssues || 0))}`);
344
+ if (results.changeAmplification) console.log(` Change amplification: ${chalk2.bold(String(results.changeAmplification.summary?.score || 0))}/100`);
335
345
  console.log(chalk2.cyan("===========================\n"));
336
346
  const elapsedTime = getElapsedTime(startTime);
337
347
  let scoringResult;
@@ -365,9 +375,9 @@ async function scanAction(directory, options) {
365
375
  }
366
376
  }
367
377
  if (results.aiSignalClarity) {
368
- const { calculateHallucinationScore } = await import("@aiready/ai-signal-clarity");
378
+ const { calculateAiSignalClarityScore } = await import("@aiready/ai-signal-clarity");
369
379
  try {
370
- const hrScore = calculateHallucinationScore(results.aiSignalClarity);
380
+ const hrScore = calculateAiSignalClarityScore(results.aiSignalClarity);
371
381
  toolScores.set("ai-signal-clarity", hrScore);
372
382
  } catch (err) {
373
383
  }
@@ -394,7 +404,7 @@ async function scanAction(directory, options) {
394
404
  score: results.docDrift.summary.score,
395
405
  rawMetrics: results.docDrift.rawData,
396
406
  factors: [],
397
- recommendations: results.docDrift.recommendations.map((action) => ({ action, estimatedImpact: 5, priority: "medium" }))
407
+ recommendations: (results.docDrift.recommendations || []).map((action) => ({ action, estimatedImpact: 5, priority: "medium" }))
398
408
  });
399
409
  }
400
410
  if (results.deps) {
@@ -403,7 +413,16 @@ async function scanAction(directory, options) {
403
413
  score: results.deps.summary.score,
404
414
  rawMetrics: results.deps.rawData,
405
415
  factors: [],
406
- recommendations: results.deps.recommendations.map((action) => ({ action, estimatedImpact: 5, priority: "medium" }))
416
+ recommendations: (results.deps.recommendations || []).map((action) => ({ action, estimatedImpact: 5, priority: "medium" }))
417
+ });
418
+ }
419
+ if (results.changeAmplification) {
420
+ toolScores.set("change-amplification", {
421
+ toolName: "change-amplification",
422
+ score: results.changeAmplification.summary.score,
423
+ rawMetrics: results.changeAmplification.rawData,
424
+ factors: [],
425
+ recommendations: (results.changeAmplification.recommendations || []).map((action) => ({ action, estimatedImpact: 5, priority: "medium" }))
407
426
  });
408
427
  }
409
428
  const cliWeights = parseWeightString(options.weights);
@@ -467,6 +486,17 @@ async function scanAction(directory, options) {
467
486
  const outputData = { ...results, scoring: scoringResult };
468
487
  handleJSONOutput(outputData, outputPath, `\u2705 Report saved to ${outputPath}`);
469
488
  warnIfGraphCapExceeded(outputData, resolvedDir);
489
+ } else {
490
+ const timestamp = getReportTimestamp();
491
+ const defaultFilename = `aiready-report-${timestamp}.json`;
492
+ const outputPath = resolveOutputPath(userOutputFile, defaultFilename, resolvedDir);
493
+ const outputData = { ...results, scoring: scoringResult };
494
+ try {
495
+ writeFileSync(outputPath, JSON.stringify(outputData, null, 2));
496
+ console.log(chalk2.dim(`\u2705 Report auto-persisted to ${outputPath}`));
497
+ warnIfGraphCapExceeded(outputData, resolvedDir);
498
+ } catch (err) {
499
+ }
470
500
  }
471
501
  const isCI = options.ci || process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true";
472
502
  if (isCI && scoringResult) {
@@ -1252,11 +1282,11 @@ AI READINESS SCORING:
1252
1282
  Use --score flag with any analysis command for detailed breakdown.
1253
1283
 
1254
1284
  EXAMPLES:
1255
- $ aiready scan # Quick analysis of current directory
1285
+ $ aiready scan # Comprehensive analysis of current directory
1256
1286
  $ aiready scan --score # Get AI Readiness Score (0-100)
1257
1287
  $ aiready scan --tools patterns # Run only pattern detection
1258
- $ aiready patterns --similarity 0.6 # Custom similarity threshold
1259
- $ aiready scan --output json --output-file results.json
1288
+ $ npx @aiready/cli scan # Industry standard way to run standard scan
1289
+ $ aiready scan --output json # Output raw JSON for piping
1260
1290
 
1261
1291
  GETTING STARTED:
1262
1292
  1. Run 'aiready scan' to analyze your codebase
@@ -1282,7 +1312,7 @@ VERSION: ${packageJson.version}
1282
1312
  DOCUMENTATION: https://aiready.dev/docs/cli
1283
1313
  GITHUB: https://github.com/caopengau/aiready-cli
1284
1314
  LANDING: https://github.com/caopengau/aiready-landing`);
1285
- program.command("scan").description("Run comprehensive AI-readiness analysis (patterns + context + consistency)").argument("[directory]", "Directory to analyze", ".").option("-t, --tools <tools>", "Tools to run (comma-separated: patterns,context,consistency,doc-drift,deps-health,aiSignalClarity,grounding,testability,changeAmplification)").option("--profile <type>", "Scan profile to use (agentic, cost, security, onboarding)").option("--compare-to <path>", "Compare results against a previous AIReady report JSON").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", "json").option("--output-file <path>", "Output file path (for json)").option("--no-score", "Disable calculating AI Readiness Score (enabled by default)").option("--weights <weights>", "Custom scoring weights").option("--threshold <score>", "Fail CI/CD if score below threshold (0-100)").option("--ci", "CI mode: GitHub Actions annotations, no colors, fail on threshold").option("--fail-on <level>", "Fail on issues: critical, major, any", "critical").addHelpText("after", scanHelpText).action(async (directory, options) => {
1315
+ program.command("scan").description("Run comprehensive AI-readiness analysis (patterns + context + consistency)").argument("[directory]", "Directory to analyze", ".").option("-t, --tools <tools>", "Tools to run (comma-separated: patterns,context,consistency,doc-drift,deps-health,aiSignalClarity,grounding,testability,changeAmplification)").option("--profile <type>", "Scan profile to use (agentic, cost, security, onboarding)").option("--compare-to <path>", "Compare results against a previous AIReady report JSON").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("--no-score", "Disable calculating AI Readiness Score (enabled by default)").option("--weights <weights>", "Custom scoring weights").option("--threshold <score>", "Fail CI/CD if score below threshold (0-100)").option("--ci", "CI mode: GitHub Actions annotations, no colors, fail on threshold").option("--fail-on <level>", "Fail on issues: critical, major, any", "critical").addHelpText("after", scanHelpText).action(async (directory, options) => {
1286
1316
  await scanAction(directory, options);
1287
1317
  });
1288
1318
  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("--max-candidates <number>", "Maximum candidates per block (performance tuning)").option("--min-shared-tokens <number>", "Minimum shared tokens for candidates (performance tuning)").option("--full-scan", "Disable smart defaults for comprehensive analysis (slower)").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 for patterns (0-100)").addHelpText("after", patternsHelpText).action(async (directory, options) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/cli",
3
- "version": "0.9.39",
3
+ "version": "0.9.40",
4
4
  "description": "Unified CLI for AIReady analysis tools",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -11,17 +11,17 @@
11
11
  "dependencies": {
12
12
  "chalk": "^5.3.0",
13
13
  "commander": "^14.0.0",
14
- "@aiready/agent-grounding": "0.1.4",
15
- "@aiready/core": "0.9.31",
16
- "@aiready/context-analyzer": "0.9.34",
17
- "@aiready/consistency": "0.8.30",
18
- "@aiready/visualizer": "0.1.36",
19
- "@aiready/doc-drift": "0.1.4",
20
- "@aiready/pattern-detect": "0.11.30",
21
- "@aiready/deps": "0.1.4",
22
- "@aiready/ai-signal-clarity": "0.1.4",
23
- "@aiready/change-amplification": "0.1.4",
24
- "@aiready/testability": "0.1.4"
14
+ "@aiready/agent-grounding": "0.1.5",
15
+ "@aiready/consistency": "0.8.31",
16
+ "@aiready/core": "0.9.32",
17
+ "@aiready/context-analyzer": "0.9.35",
18
+ "@aiready/pattern-detect": "0.11.31",
19
+ "@aiready/change-amplification": "0.1.5",
20
+ "@aiready/testability": "0.1.5",
21
+ "@aiready/visualizer": "0.1.37",
22
+ "@aiready/deps": "0.1.5",
23
+ "@aiready/doc-drift": "0.1.5",
24
+ "@aiready/ai-signal-clarity": "0.1.5"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^24.0.0",
package/src/cli.ts CHANGED
@@ -37,11 +37,11 @@ AI READINESS SCORING:
37
37
  Use --score flag with any analysis command for detailed breakdown.
38
38
 
39
39
  EXAMPLES:
40
- $ aiready scan # Quick analysis of current directory
40
+ $ aiready scan # Comprehensive analysis of current directory
41
41
  $ aiready scan --score # Get AI Readiness Score (0-100)
42
42
  $ aiready scan --tools patterns # Run only pattern detection
43
- $ aiready patterns --similarity 0.6 # Custom similarity threshold
44
- $ aiready scan --output json --output-file results.json
43
+ $ npx @aiready/cli scan # Industry standard way to run standard scan
44
+ $ aiready scan --output json # Output raw JSON for piping
45
45
 
46
46
  GETTING STARTED:
47
47
  1. Run 'aiready scan' to analyze your codebase
@@ -78,7 +78,7 @@ program
78
78
  .option('--compare-to <path>', 'Compare results against a previous AIReady report JSON')
79
79
  .option('--include <patterns>', 'File patterns to include (comma-separated)')
80
80
  .option('--exclude <patterns>', 'File patterns to exclude (comma-separated)')
81
- .option('-o, --output <format>', 'Output format: console, json', 'json')
81
+ .option('-o, --output <format>', 'Output format: console, json', 'console')
82
82
  .option('--output-file <path>', 'Output file path (for json)')
83
83
  .option('--no-score', 'Disable calculating AI Readiness Score (enabled by default)')
84
84
  .option('--weights <weights>', 'Custom scoring weights')
@@ -49,11 +49,11 @@ export async function scanAction(directory: string, options: ScanOptions) {
49
49
  try {
50
50
  // Define defaults
51
51
  const defaults = {
52
- tools: ['patterns', 'context', 'consistency', 'aiSignalClarity', 'grounding', 'testability', 'doc-drift', 'deps-health'],
52
+ tools: ['patterns', 'context', 'consistency', 'aiSignalClarity', 'grounding', 'testability', 'doc-drift', 'deps-health', 'changeAmplification'],
53
53
  include: undefined,
54
54
  exclude: undefined,
55
55
  output: {
56
- format: 'json',
56
+ format: 'console',
57
57
  file: undefined,
58
58
  },
59
59
  };
@@ -83,11 +83,15 @@ export async function scanAction(directory: string, options: ScanOptions) {
83
83
  }
84
84
 
85
85
  // Load and merge config with CLI options
86
- const baseOptions = await loadMergedConfig(resolvedDir, defaults, {
87
- tools: profileTools as any,
86
+ const cliOverrides: any = {
88
87
  include: options.include?.split(','),
89
88
  exclude: options.exclude?.split(','),
90
- }) as any;
89
+ };
90
+ if (profileTools) {
91
+ cliOverrides.tools = profileTools;
92
+ }
93
+
94
+ const baseOptions = await loadMergedConfig(resolvedDir, defaults, cliOverrides) as any;
91
95
 
92
96
 
93
97
  // Apply smart defaults for pattern detection if patterns tool is enabled
@@ -258,6 +262,12 @@ export async function scanAction(directory: string, options: ScanOptions) {
258
262
  console.log(` Deprecated Packages: ${chalk.bold(dr.rawData.deprecatedPackages || 0)}`);
259
263
  console.log(` AI Cutoff Skew Score: ${chalk.bold(dr.rawData.trainingCutoffSkew?.toFixed(1) || 0)}`);
260
264
  }
265
+ } else if (event.tool === 'change-amplification' || event.tool === 'changeAmplification') {
266
+ const dr = event.data as any;
267
+ console.log(` Coupling issues: ${chalk.bold(String(dr.issues?.length || 0))}`);
268
+ if (dr.summary) {
269
+ console.log(` Complexity Score: ${chalk.bold(dr.summary.score || 0)}/100`);
270
+ }
261
271
  }
262
272
  } catch (err) {
263
273
  // don't crash the run for progress printing errors
@@ -276,7 +286,8 @@ export async function scanAction(directory: string, options: ScanOptions) {
276
286
  if (results.duplicates) console.log(` Duplicate patterns found: ${chalk.bold(String(results.duplicates.length || 0))}`);
277
287
  if (results.patterns) console.log(` Pattern files with issues: ${chalk.bold(String(results.patterns.length || 0))}`);
278
288
  if (results.context) console.log(` Context issues: ${chalk.bold(String(results.context.length || 0))}`);
279
- if (results.consistency) console.log(` Consistency issues: ${chalk.bold(String(results.consistency.summary.totalIssues || 0))}`);
289
+ console.log(` Consistency issues: ${chalk.bold(String(results.consistency?.summary?.totalIssues || 0))}`);
290
+ if (results.changeAmplification) console.log(` Change amplification: ${chalk.bold(String(results.changeAmplification.summary?.score || 0))}/100`);
280
291
  console.log(chalk.cyan('===========================\n'));
281
292
 
282
293
  const elapsedTime = getElapsedTime(startTime);
@@ -324,9 +335,9 @@ export async function scanAction(directory: string, options: ScanOptions) {
324
335
 
325
336
  // AI signal clarity score
326
337
  if (results.aiSignalClarity) {
327
- const { calculateHallucinationScore } = await import('@aiready/ai-signal-clarity');
338
+ const { calculateAiSignalClarityScore } = await import('@aiready/ai-signal-clarity');
328
339
  try {
329
- const hrScore = calculateHallucinationScore(results.aiSignalClarity);
340
+ const hrScore = calculateAiSignalClarityScore(results.aiSignalClarity);
330
341
  toolScores.set('ai-signal-clarity', hrScore);
331
342
  } catch (err) {
332
343
  // ignore
@@ -362,7 +373,7 @@ export async function scanAction(directory: string, options: ScanOptions) {
362
373
  score: results.docDrift.summary.score,
363
374
  rawMetrics: results.docDrift.rawData,
364
375
  factors: [],
365
- recommendations: results.docDrift.recommendations.map((action: string) => ({ action, estimatedImpact: 5, priority: 'medium' }))
376
+ recommendations: (results.docDrift.recommendations || []).map((action: string) => ({ action, estimatedImpact: 5, priority: 'medium' }))
366
377
  });
367
378
  }
368
379
 
@@ -373,10 +384,22 @@ export async function scanAction(directory: string, options: ScanOptions) {
373
384
  score: results.deps.summary.score,
374
385
  rawMetrics: results.deps.rawData,
375
386
  factors: [],
376
- recommendations: results.deps.recommendations.map((action: string) => ({ action, estimatedImpact: 5, priority: 'medium' }))
387
+ recommendations: (results.deps.recommendations || []).map((action: string) => ({ action, estimatedImpact: 5, priority: 'medium' }))
388
+ });
389
+ }
390
+
391
+ // Change Amplification score
392
+ if (results.changeAmplification) {
393
+ toolScores.set('change-amplification', {
394
+ toolName: 'change-amplification',
395
+ score: results.changeAmplification.summary.score,
396
+ rawMetrics: results.changeAmplification.rawData,
397
+ factors: [],
398
+ recommendations: (results.changeAmplification.recommendations || []).map((action: string) => ({ action, estimatedImpact: 5, priority: 'medium' }))
377
399
  });
378
400
  }
379
401
 
402
+
380
403
  // Parse CLI weight overrides (if any)
381
404
  const cliWeights = parseWeightString((options as any).weights);
382
405
 
@@ -454,6 +477,21 @@ export async function scanAction(directory: string, options: ScanOptions) {
454
477
 
455
478
  // Warn if graph caps may be exceeded
456
479
  warnIfGraphCapExceeded(outputData, resolvedDir);
480
+ } else {
481
+ // Auto-persist report even in console mode for downstream tools
482
+ const timestamp = getReportTimestamp();
483
+ const defaultFilename = `aiready-report-${timestamp}.json`;
484
+ const outputPath = resolveOutputPath(userOutputFile, defaultFilename, resolvedDir);
485
+ const outputData = { ...results, scoring: scoringResult };
486
+
487
+ try {
488
+ writeFileSync(outputPath, JSON.stringify(outputData, null, 2));
489
+ console.log(chalk.dim(`✅ Report auto-persisted to ${outputPath}`));
490
+ // Warn if graph caps may be exceeded
491
+ warnIfGraphCapExceeded(outputData, resolvedDir);
492
+ } catch (err) {
493
+ // failed to save report, but don't fail the command
494
+ }
457
495
  }
458
496
 
459
497
  // CI/CD Gatekeeper Mode