@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.
- package/.aiready/aiready-report-20260227-133806.json +7906 -0
- package/.aiready/aiready-report-20260227-133938.json +8052 -0
- package/.turbo/turbo-build.log +6 -6
- package/.turbo/turbo-test.log +5 -5
- package/dist/cli.js +44 -14
- package/dist/cli.mjs +45 -15
- package/package.json +12 -12
- package/src/cli.ts +4 -4
- package/src/commands/scan.ts +48 -10
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
> @aiready/cli@0.9.
|
|
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
|
[34mCLI[39m Building entry: src/cli.ts, src/index.ts
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
[34mCJS[39m Build start
|
|
11
11
|
[34mESM[39m Build start
|
|
12
12
|
|
|
13
|
-
[90m[[
|
|
13
|
+
[90m[[90m2:35:42 PM[90m][39m [43m[30m WARN [39m[49m [33m▲ [43;33m[[43;30mWARNING[43;33m][0m [1m"import.meta" is not available with the "cjs" output format and will be empty[0m [empty-import-meta]
|
|
14
14
|
|
|
15
15
|
src/cli.ts:23:31:
|
|
16
16
|
[37m 23 │ return dirname(fileURLToPath([32mimport.meta[37m.url));
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
[32mESM[39m [1mdist/cli.mjs [22m[32m65.28 KB[39m
|
|
23
24
|
[32mESM[39m [1mdist/index.mjs [22m[32m138.00 B[39m
|
|
24
|
-
[32mESM[39m [1mdist/cli.mjs [22m[32m63.68 KB[39m
|
|
25
25
|
[32mESM[39m [1mdist/chunk-PDOONNSK.mjs [22m[32m7.80 KB[39m
|
|
26
|
-
[32mESM[39m ⚡️ Build success in
|
|
27
|
-
[32mCJS[39m [1mdist/cli.js [22m[32m73.85 KB[39m
|
|
26
|
+
[32mESM[39m ⚡️ Build success in 491ms
|
|
28
27
|
[32mCJS[39m [1mdist/index.js [22m[32m9.15 KB[39m
|
|
29
|
-
[32mCJS[39m
|
|
28
|
+
[32mCJS[39m [1mdist/cli.js [22m[32m75.53 KB[39m
|
|
29
|
+
[32mCJS[39m ⚡️ Build success in 492ms
|
package/.turbo/turbo-test.log
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
> @aiready/cli@0.9.
|
|
3
|
+
> @aiready/cli@0.9.40 test /Users/pengcao/projects/aiready/packages/cli
|
|
4
4
|
> vitest run
|
|
5
5
|
|
|
6
6
|
[?25l
|
|
7
7
|
[1m[46m RUN [49m[22m [36mv4.0.18 [39m[90m/Users/pengcao/projects/aiready/packages/cli[39m
|
|
8
8
|
|
|
9
|
-
[32m✓[39m
|
|
10
|
-
[32m✓[39m
|
|
9
|
+
[32m✓[39m src/__tests__/cli.test.ts [2m([22m[2m3 tests[22m[2m)[22m[32m 5[2mms[22m[39m
|
|
10
|
+
[32m✓[39m dist/__tests__/cli.test.js [2m([22m[2m3 tests[22m[2m)[22m[32m 2[2mms[22m[39m
|
|
11
11
|
|
|
12
12
|
[2m Test Files [22m [1m[32m2 passed[39m[22m[90m (2)[39m
|
|
13
13
|
[2m Tests [22m [1m[32m6 passed[39m[22m[90m (6)[39m
|
|
14
|
-
[2m Start at [22m
|
|
15
|
-
[2m Duration [22m
|
|
14
|
+
[2m Start at [22m 14:39:26
|
|
15
|
+
[2m Duration [22m 11.33s[2m (transform 3.58s, setup 0ms, import 16.97s, tests 6ms, environment 0ms)[22m
|
|
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: "
|
|
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
|
|
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
|
-
|
|
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 {
|
|
549
|
+
const { calculateAiSignalClarityScore } = await import("@aiready/ai-signal-clarity");
|
|
540
550
|
try {
|
|
541
|
-
const hrScore =
|
|
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 #
|
|
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
|
|
1410
|
-
$ aiready scan --output 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", "
|
|
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: "
|
|
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
|
|
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
|
-
|
|
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 {
|
|
378
|
+
const { calculateAiSignalClarityScore } = await import("@aiready/ai-signal-clarity");
|
|
369
379
|
try {
|
|
370
|
-
const hrScore =
|
|
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 #
|
|
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
|
|
1259
|
-
$ aiready scan --output 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", "
|
|
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.
|
|
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.
|
|
15
|
-
"@aiready/
|
|
16
|
-
"@aiready/
|
|
17
|
-
"@aiready/
|
|
18
|
-
"@aiready/
|
|
19
|
-
"@aiready/
|
|
20
|
-
"@aiready/
|
|
21
|
-
"@aiready/
|
|
22
|
-
"@aiready/
|
|
23
|
-
"@aiready/
|
|
24
|
-
"@aiready/
|
|
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 #
|
|
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
|
|
44
|
-
$ aiready scan --output 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', '
|
|
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')
|
package/src/commands/scan.ts
CHANGED
|
@@ -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: '
|
|
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
|
|
87
|
-
tools: profileTools as any,
|
|
86
|
+
const cliOverrides: any = {
|
|
88
87
|
include: options.include?.split(','),
|
|
89
88
|
exclude: options.exclude?.split(','),
|
|
90
|
-
}
|
|
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
|
-
|
|
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 {
|
|
338
|
+
const { calculateAiSignalClarityScore } = await import('@aiready/ai-signal-clarity');
|
|
328
339
|
try {
|
|
329
|
-
const hrScore =
|
|
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
|