@aiready/cli 0.15.1 → 0.15.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +186 -274
- package/dist/cli.mjs +175 -267
- package/package.json +13 -13
package/dist/cli.js
CHANGED
|
@@ -25,7 +25,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
|
|
26
26
|
// src/cli.ts
|
|
27
27
|
var import_commander = require("commander");
|
|
28
|
-
var
|
|
28
|
+
var import_fs8 = require("fs");
|
|
29
29
|
var import_path8 = require("path");
|
|
30
30
|
var import_url = require("url");
|
|
31
31
|
|
|
@@ -96,38 +96,6 @@ async function warnIfGraphCapExceeded(report, dirPath) {
|
|
|
96
96
|
void err;
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
-
function generateMarkdownReport(report, elapsedTime) {
|
|
100
|
-
let markdown = `# Consistency Analysis Report
|
|
101
|
-
|
|
102
|
-
`;
|
|
103
|
-
markdown += `**Generated:** ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
104
|
-
`;
|
|
105
|
-
markdown += `**Analysis Time:** ${elapsedTime}s
|
|
106
|
-
|
|
107
|
-
`;
|
|
108
|
-
markdown += `## Summary
|
|
109
|
-
|
|
110
|
-
`;
|
|
111
|
-
markdown += `- **Files Analyzed:** ${report.summary.filesAnalyzed}
|
|
112
|
-
`;
|
|
113
|
-
markdown += `- **Total Issues:** ${report.summary.totalIssues}
|
|
114
|
-
`;
|
|
115
|
-
markdown += ` - Naming: ${report.summary.namingIssues}
|
|
116
|
-
`;
|
|
117
|
-
markdown += ` - Patterns: ${report.summary.patternIssues}
|
|
118
|
-
|
|
119
|
-
`;
|
|
120
|
-
if (report.recommendations.length > 0) {
|
|
121
|
-
markdown += `## Recommendations
|
|
122
|
-
|
|
123
|
-
`;
|
|
124
|
-
report.recommendations.forEach((rec, i) => {
|
|
125
|
-
markdown += `${i + 1}. ${rec}
|
|
126
|
-
`;
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
return markdown;
|
|
130
|
-
}
|
|
131
99
|
|
|
132
100
|
// src/commands/report-formatter.ts
|
|
133
101
|
var import_chalk2 = __toESM(require("chalk"));
|
|
@@ -428,7 +396,7 @@ async function executeToolAction(directory, options, config) {
|
|
|
428
396
|
const elapsedTime = (0, import_core5.getElapsedTime)(startTime);
|
|
429
397
|
const summary = generateSummary(results);
|
|
430
398
|
let toolScore;
|
|
431
|
-
if (options.score && calculateScore) {
|
|
399
|
+
if ((options.score || finalOptions.score) && calculateScore) {
|
|
432
400
|
const resultsAny = results;
|
|
433
401
|
const scoreData = resultsAny.duplicates || resultsAny.issues || results;
|
|
434
402
|
const filesCount = resultsAny.length || resultsAny.summary?.filesAnalyzed || resultsAny.summary?.totalFiles;
|
|
@@ -1170,6 +1138,38 @@ ${import_chalk6.default.bold("CI/CD Integration:")}
|
|
|
1170
1138
|
Use --threshold and --fail-on to use AIReady as a quality gate in your CI pipelines.
|
|
1171
1139
|
When running in GitHub Actions, it will automatically emit annotations for found issues.
|
|
1172
1140
|
`;
|
|
1141
|
+
function defineScanCommand(program2) {
|
|
1142
|
+
program2.command("scan").description(
|
|
1143
|
+
"Run comprehensive AI-readiness analysis (patterns + context + consistency)"
|
|
1144
|
+
).argument("[directory]", "Directory to analyze", ".").option(
|
|
1145
|
+
"-t, --tools <tools>",
|
|
1146
|
+
"Tools to run (comma-separated: patterns,context,consistency,doc-drift,deps-health,aiSignalClarity,grounding,testability,changeAmplification)"
|
|
1147
|
+
).option(
|
|
1148
|
+
"--profile <type>",
|
|
1149
|
+
"Scan profile to use (agentic, cost, logic, ui, security, onboarding)"
|
|
1150
|
+
).option(
|
|
1151
|
+
"--compare-to <path>",
|
|
1152
|
+
"Compare results against a previous AIReady report JSON"
|
|
1153
|
+
).option(
|
|
1154
|
+
"--include <patterns>",
|
|
1155
|
+
"File patterns to include (comma-separated)"
|
|
1156
|
+
).option(
|
|
1157
|
+
"--exclude <patterns>",
|
|
1158
|
+
"File patterns to exclude (comma-separated)"
|
|
1159
|
+
).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(
|
|
1160
|
+
"--threshold <score>",
|
|
1161
|
+
"Fail CI/CD if score below threshold (0-100)"
|
|
1162
|
+
).option(
|
|
1163
|
+
"--ci",
|
|
1164
|
+
"CI mode: GitHub Actions annotations, no colors, fail on threshold"
|
|
1165
|
+
).option(
|
|
1166
|
+
"--fail-on <level>",
|
|
1167
|
+
"Fail on issues: critical, major, any",
|
|
1168
|
+
"critical"
|
|
1169
|
+
).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) => {
|
|
1170
|
+
await scanAction(directory, options);
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
1173
|
|
|
1174
1174
|
// src/commands/init.ts
|
|
1175
1175
|
var import_fs5 = require("fs");
|
|
@@ -1449,33 +1449,6 @@ function renderSafetyRating(safety) {
|
|
|
1449
1449
|
` AI Change Safety: ${color(`${icon} ${safety.toUpperCase()}`)}`
|
|
1450
1450
|
);
|
|
1451
1451
|
}
|
|
1452
|
-
function renderIssueSummaryBlock(summary) {
|
|
1453
|
-
const total = (summary.criticalIssues || 0) + (summary.majorIssues || 0) + (summary.minorIssues || 0);
|
|
1454
|
-
if (total === 0) {
|
|
1455
|
-
console.log(import_chalk8.default.green("\u2705 No significant issues found!\n"));
|
|
1456
|
-
return;
|
|
1457
|
-
}
|
|
1458
|
-
console.log(import_chalk8.default.bold("\u26A0\uFE0F Issues Found:\n"));
|
|
1459
|
-
if (summary.criticalIssues > 0)
|
|
1460
|
-
console.log(
|
|
1461
|
-
import_chalk8.default.red(` \u{1F534} Critical: ${import_chalk8.default.bold(summary.criticalIssues)}`)
|
|
1462
|
-
);
|
|
1463
|
-
if (summary.majorIssues > 0)
|
|
1464
|
-
console.log(
|
|
1465
|
-
import_chalk8.default.yellow(` \u{1F7E1} Major: ${import_chalk8.default.bold(summary.majorIssues)}`)
|
|
1466
|
-
);
|
|
1467
|
-
if (summary.minorIssues > 0)
|
|
1468
|
-
console.log(import_chalk8.default.blue(` \u{1F535} Minor: ${import_chalk8.default.bold(summary.minorIssues)}`));
|
|
1469
|
-
if (summary.totalPotentialSavings) {
|
|
1470
|
-
console.log(
|
|
1471
|
-
import_chalk8.default.green(
|
|
1472
|
-
`
|
|
1473
|
-
\u{1F4A1} Potential savings: ${import_chalk8.default.bold(summary.totalPotentialSavings.toLocaleString())} tokens
|
|
1474
|
-
`
|
|
1475
|
-
)
|
|
1476
|
-
);
|
|
1477
|
-
}
|
|
1478
|
-
}
|
|
1479
1452
|
function renderSubSection(title) {
|
|
1480
1453
|
console.log("\n" + (0, import_core12.getTerminalDivider)());
|
|
1481
1454
|
console.log(import_chalk8.default.bold.white(` ${title.toUpperCase()}`));
|
|
@@ -1492,6 +1465,36 @@ function renderToolScoreFooter(score) {
|
|
|
1492
1465
|
}
|
|
1493
1466
|
|
|
1494
1467
|
// src/commands/patterns.ts
|
|
1468
|
+
var PATTERNS_HELP_TEXT = `
|
|
1469
|
+
EXAMPLES:
|
|
1470
|
+
$ aiready patterns # Default analysis
|
|
1471
|
+
$ aiready patterns --similarity 0.6 # Stricter matching
|
|
1472
|
+
$ aiready patterns --min-lines 10 # Larger patterns only
|
|
1473
|
+
`;
|
|
1474
|
+
function definePatternsCommand(program2) {
|
|
1475
|
+
program2.command("patterns").description("Detect duplicate code patterns that confuse AI models").argument("[directory]", "Directory to analyze", ".").option(
|
|
1476
|
+
"-s, --similarity <number>",
|
|
1477
|
+
"Minimum similarity score (0-1)",
|
|
1478
|
+
"0.40"
|
|
1479
|
+
).option("-l, --min-lines <number>", "Minimum lines to consider", "5").option(
|
|
1480
|
+
"--max-candidates <number>",
|
|
1481
|
+
"Maximum candidates per block (performance tuning)"
|
|
1482
|
+
).option(
|
|
1483
|
+
"--min-shared-tokens <number>",
|
|
1484
|
+
"Minimum shared tokens for candidates (performance tuning)"
|
|
1485
|
+
).option(
|
|
1486
|
+
"--full-scan",
|
|
1487
|
+
"Disable smart defaults for comprehensive analysis (slower)"
|
|
1488
|
+
).option(
|
|
1489
|
+
"--include <patterns>",
|
|
1490
|
+
"File patterns to include (comma-separated)"
|
|
1491
|
+
).option(
|
|
1492
|
+
"--exclude <patterns>",
|
|
1493
|
+
"File patterns to exclude (comma-separated)"
|
|
1494
|
+
).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) => {
|
|
1495
|
+
await patternsAction(directory, options);
|
|
1496
|
+
});
|
|
1497
|
+
}
|
|
1495
1498
|
async function patternsAction(directory, options) {
|
|
1496
1499
|
return await executeToolAction(directory, options, {
|
|
1497
1500
|
toolName: "pattern-detect",
|
|
@@ -1571,21 +1574,30 @@ async function patternsAction(directory, options) {
|
|
|
1571
1574
|
}
|
|
1572
1575
|
});
|
|
1573
1576
|
}
|
|
1574
|
-
var PATTERNS_HELP_TEXT = `
|
|
1575
|
-
EXAMPLES:
|
|
1576
|
-
$ aiready patterns # Default analysis
|
|
1577
|
-
$ aiready patterns --similarity 0.6 # Stricter matching
|
|
1578
|
-
$ aiready patterns --min-lines 10 # Larger patterns only
|
|
1579
|
-
`;
|
|
1580
1577
|
|
|
1581
1578
|
// src/commands/context.ts
|
|
1582
1579
|
var import_chalk10 = __toESM(require("chalk"));
|
|
1583
1580
|
var import_core14 = require("@aiready/core");
|
|
1581
|
+
function defineContextCommand(program2) {
|
|
1582
|
+
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(
|
|
1583
|
+
"--max-context <number>",
|
|
1584
|
+
"Maximum acceptable context budget (tokens)",
|
|
1585
|
+
"10000"
|
|
1586
|
+
).option(
|
|
1587
|
+
"--include <patterns>",
|
|
1588
|
+
"File patterns to include (comma-separated)"
|
|
1589
|
+
).option(
|
|
1590
|
+
"--exclude <patterns>",
|
|
1591
|
+
"File patterns to exclude (comma-separated)"
|
|
1592
|
+
).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) => {
|
|
1593
|
+
await contextAction(directory, options);
|
|
1594
|
+
});
|
|
1595
|
+
}
|
|
1584
1596
|
async function contextAction(directory, options) {
|
|
1585
1597
|
return await executeToolAction(directory, options, {
|
|
1586
1598
|
toolName: "context-analyzer",
|
|
1587
1599
|
label: "Context analysis",
|
|
1588
|
-
emoji: "\u{
|
|
1600
|
+
emoji: "\u{1F9E9}",
|
|
1589
1601
|
defaults: {
|
|
1590
1602
|
maxDepth: 5,
|
|
1591
1603
|
maxContextBudget: 1e4,
|
|
@@ -1597,11 +1609,6 @@ async function contextAction(directory, options) {
|
|
|
1597
1609
|
maxDepth: opts.maxDepth ? parseInt(opts.maxDepth) : void 0,
|
|
1598
1610
|
maxContextBudget: opts.maxContext ? parseInt(opts.maxContext) : void 0
|
|
1599
1611
|
}),
|
|
1600
|
-
preAnalyze: async (resolvedDir, baseOptions) => {
|
|
1601
|
-
const { getSmartDefaults } = await import("@aiready/context-analyzer");
|
|
1602
|
-
const smartDefaults = await getSmartDefaults(resolvedDir, baseOptions);
|
|
1603
|
-
return { ...smartDefaults, ...baseOptions };
|
|
1604
|
-
},
|
|
1605
1612
|
importTool: async () => {
|
|
1606
1613
|
const { analyzeContext, generateSummary, calculateContextScore } = await import("@aiready/context-analyzer");
|
|
1607
1614
|
return {
|
|
@@ -1610,45 +1617,40 @@ async function contextAction(directory, options) {
|
|
|
1610
1617
|
calculateScore: calculateContextScore
|
|
1611
1618
|
};
|
|
1612
1619
|
},
|
|
1613
|
-
renderConsole: ({ summary, elapsedTime, score }) => {
|
|
1620
|
+
renderConsole: ({ results: _results, summary, elapsedTime, score }) => {
|
|
1614
1621
|
(0, import_core14.printTerminalHeader)("CONTEXT ANALYSIS SUMMARY");
|
|
1615
1622
|
console.log(
|
|
1616
|
-
import_chalk10.default.white(`\u{1F4C1}
|
|
1623
|
+
import_chalk10.default.white(`\u{1F4C1} Total files: ${import_chalk10.default.bold(summary.totalFiles)}`)
|
|
1617
1624
|
);
|
|
1618
1625
|
console.log(
|
|
1619
1626
|
import_chalk10.default.white(
|
|
1620
|
-
`\u{
|
|
1627
|
+
`\u{1F4B8} Total tokens (context budget): ${import_chalk10.default.bold(summary.totalTokens.toLocaleString())}`
|
|
1621
1628
|
)
|
|
1622
1629
|
);
|
|
1623
1630
|
console.log(
|
|
1624
|
-
import_chalk10.default.
|
|
1625
|
-
`\u{
|
|
1631
|
+
import_chalk10.default.cyan(
|
|
1632
|
+
`\u{1F4CA} Average context budget: ${import_chalk10.default.bold(summary.avgContextBudget.toFixed(0))} tokens`
|
|
1626
1633
|
)
|
|
1627
1634
|
);
|
|
1628
1635
|
console.log(
|
|
1629
|
-
import_chalk10.default.
|
|
1630
|
-
`)
|
|
1636
|
+
import_chalk10.default.gray(`\u23F1 Analysis time: ${import_chalk10.default.bold(elapsedTime + "s")}`)
|
|
1631
1637
|
);
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1638
|
+
if (summary.fragmentedModules.length > 0) {
|
|
1639
|
+
renderSubSection("Top Fragmented Modules");
|
|
1640
|
+
summary.fragmentedModules.slice(0, 5).forEach((mod) => {
|
|
1641
|
+
const scoreColor = mod.fragmentationScore > 0.7 ? import_chalk10.default.red : mod.fragmentationScore > 0.4 ? import_chalk10.default.yellow : import_chalk10.default.green;
|
|
1637
1642
|
console.log(
|
|
1638
|
-
`
|
|
1643
|
+
` ${scoreColor("\u25A0")} ${import_chalk10.default.white(mod.domain.padEnd(20))} ${import_chalk10.default.bold((mod.fragmentationScore * 100).toFixed(0) + "%")} fragmentation`
|
|
1639
1644
|
);
|
|
1640
1645
|
});
|
|
1641
1646
|
}
|
|
1642
|
-
if (summary.
|
|
1643
|
-
renderSubSection("
|
|
1644
|
-
summary.
|
|
1647
|
+
if (summary.topExpensiveFiles.length > 0) {
|
|
1648
|
+
renderSubSection("Top Context-Expensive Files");
|
|
1649
|
+
summary.topExpensiveFiles.slice(0, 5).forEach((item) => {
|
|
1650
|
+
const icon = item.severity === "critical" ? "\u{1F534}" : item.severity === "major" ? "\u{1F7E1}" : "\u{1F535}";
|
|
1651
|
+
const color = item.severity === "critical" ? import_chalk10.default.red : item.severity === "major" ? import_chalk10.default.yellow : import_chalk10.default.blue;
|
|
1645
1652
|
console.log(
|
|
1646
|
-
`
|
|
1647
|
-
);
|
|
1648
|
-
console.log(
|
|
1649
|
-
import_chalk10.default.dim(
|
|
1650
|
-
` Token cost: ${module2.totalTokens.toLocaleString()}, Cohesion: ${(module2.avgCohesion * 100).toFixed(0)}%`
|
|
1651
|
-
)
|
|
1653
|
+
` ${icon} ${color(item.severity.toUpperCase())}: ${import_chalk10.default.white(item.file.split("/").pop())} ${import_chalk10.default.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`
|
|
1652
1654
|
);
|
|
1653
1655
|
});
|
|
1654
1656
|
}
|
|
@@ -1659,17 +1661,35 @@ async function contextAction(directory, options) {
|
|
|
1659
1661
|
|
|
1660
1662
|
// src/commands/consistency.ts
|
|
1661
1663
|
var import_chalk11 = __toESM(require("chalk"));
|
|
1662
|
-
var import_fs6 = require("fs");
|
|
1663
1664
|
var import_core15 = require("@aiready/core");
|
|
1665
|
+
function defineConsistencyCommand(program2) {
|
|
1666
|
+
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(
|
|
1667
|
+
"--min-severity <level>",
|
|
1668
|
+
"Minimum severity: info|minor|major|critical",
|
|
1669
|
+
"info"
|
|
1670
|
+
).option(
|
|
1671
|
+
"--include <patterns>",
|
|
1672
|
+
"File patterns to include (comma-separated)"
|
|
1673
|
+
).option(
|
|
1674
|
+
"--exclude <patterns>",
|
|
1675
|
+
"File patterns to exclude (comma-separated)"
|
|
1676
|
+
).option(
|
|
1677
|
+
"-o, --output <format>",
|
|
1678
|
+
"Output format: console, json, markdown",
|
|
1679
|
+
"console"
|
|
1680
|
+
).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) => {
|
|
1681
|
+
await consistencyAction(directory, options);
|
|
1682
|
+
});
|
|
1683
|
+
}
|
|
1664
1684
|
async function consistencyAction(directory, options) {
|
|
1665
1685
|
return await executeToolAction(directory, options, {
|
|
1666
1686
|
toolName: "naming-consistency",
|
|
1667
1687
|
label: "Consistency analysis",
|
|
1668
|
-
emoji: "\u{
|
|
1688
|
+
emoji: "\u{1F4CF}",
|
|
1669
1689
|
defaults: {
|
|
1670
|
-
checkNaming:
|
|
1671
|
-
checkPatterns:
|
|
1672
|
-
minSeverity: "info",
|
|
1690
|
+
checkNaming: options.naming !== false,
|
|
1691
|
+
checkPatterns: options.patterns !== false,
|
|
1692
|
+
minSeverity: options.minSeverity || "info",
|
|
1673
1693
|
include: void 0,
|
|
1674
1694
|
exclude: void 0,
|
|
1675
1695
|
output: { format: "console", file: void 0 }
|
|
@@ -1680,119 +1700,68 @@ async function consistencyAction(directory, options) {
|
|
|
1680
1700
|
minSeverity: opts.minSeverity
|
|
1681
1701
|
}),
|
|
1682
1702
|
importTool: async () => {
|
|
1683
|
-
const { analyzeConsistency, calculateConsistencyScore } = await import("@aiready/consistency");
|
|
1703
|
+
const { analyzeConsistency, generateSummary, calculateConsistencyScore } = await import("@aiready/consistency");
|
|
1684
1704
|
return {
|
|
1685
|
-
analyze:
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
);
|
|
1692
|
-
}
|
|
1705
|
+
analyze: async (opts) => {
|
|
1706
|
+
const report = await analyzeConsistency(opts);
|
|
1707
|
+
return report;
|
|
1708
|
+
},
|
|
1709
|
+
generateSummary,
|
|
1710
|
+
calculateScore: calculateConsistencyScore
|
|
1693
1711
|
};
|
|
1694
1712
|
},
|
|
1695
|
-
renderConsole: ({ results, summary, elapsedTime, score
|
|
1696
|
-
|
|
1697
|
-
const { format: outputFormat, file: userOutputFile } = (0, import_core15.resolveOutputFormat)(options, finalOptions);
|
|
1698
|
-
if (outputFormat === "markdown") {
|
|
1699
|
-
const markdown = generateMarkdownReport(report, elapsedTime);
|
|
1700
|
-
const outputPath = (0, import_core15.resolveOutputPath)(
|
|
1701
|
-
userOutputFile,
|
|
1702
|
-
`aiready-report-${(0, import_core2.getReportTimestamp)()}.md`,
|
|
1703
|
-
directory
|
|
1704
|
-
);
|
|
1705
|
-
(0, import_fs6.writeFileSync)(outputPath, markdown);
|
|
1706
|
-
console.log(import_chalk11.default.green(`\u2705 Report saved to ${outputPath}`));
|
|
1707
|
-
return;
|
|
1708
|
-
}
|
|
1709
|
-
console.log(import_chalk11.default.bold("\n\u{1F4CA} Summary\n"));
|
|
1710
|
-
console.log(`Files Analyzed: ${import_chalk11.default.cyan(summary.filesAnalyzed)}`);
|
|
1711
|
-
console.log(`Total Issues: ${import_chalk11.default.yellow(summary.totalIssues)}`);
|
|
1712
|
-
console.log(` Naming: ${import_chalk11.default.yellow(summary.namingIssues)}`);
|
|
1713
|
-
console.log(` Patterns: ${import_chalk11.default.yellow(summary.patternIssues)}`);
|
|
1713
|
+
renderConsole: ({ results: report, summary, elapsedTime, score }) => {
|
|
1714
|
+
(0, import_core15.printTerminalHeader)("CONSISTENCY ANALYSIS SUMMARY");
|
|
1714
1715
|
console.log(
|
|
1715
|
-
|
|
1716
|
+
import_chalk11.default.white(`\u{1F4C1} Files analyzed: ${import_chalk11.default.bold(summary.filesAnalyzed)}`)
|
|
1716
1717
|
);
|
|
1717
|
-
console.log(
|
|
1718
|
-
`)
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
);
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
}
|
|
1749
|
-
console.log();
|
|
1750
|
-
shown++;
|
|
1751
|
-
}
|
|
1752
|
-
}
|
|
1753
|
-
}
|
|
1754
|
-
if (patternResults.length > 0) {
|
|
1755
|
-
console.log(import_chalk11.default.bold("\u{1F504} Pattern Issues\n"));
|
|
1756
|
-
let shown = 0;
|
|
1757
|
-
for (const patternFileResult of patternResults) {
|
|
1758
|
-
if (shown >= 5) break;
|
|
1759
|
-
for (const issue of patternFileResult.issues) {
|
|
1760
|
-
if (shown >= 5) break;
|
|
1761
|
-
const severityColor = issue.severity === "critical" ? import_chalk11.default.red : issue.severity === "major" ? import_chalk11.default.yellow : issue.severity === "minor" ? import_chalk11.default.blue : import_chalk11.default.gray;
|
|
1762
|
-
console.log(
|
|
1763
|
-
`${severityColor(issue.severity.toUpperCase())} ${import_chalk11.default.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1764
|
-
);
|
|
1765
|
-
console.log(` ${issue.message}`);
|
|
1766
|
-
if (issue.suggestion) {
|
|
1767
|
-
console.log(
|
|
1768
|
-
` ${import_chalk11.default.dim("\u2192")} ${import_chalk11.default.italic(issue.suggestion)}`
|
|
1769
|
-
);
|
|
1770
|
-
}
|
|
1771
|
-
console.log();
|
|
1772
|
-
shown++;
|
|
1773
|
-
}
|
|
1718
|
+
console.log(
|
|
1719
|
+
import_chalk11.default.white(`\u26A0 Total issues: ${import_chalk11.default.bold(summary.totalIssues)}`)
|
|
1720
|
+
);
|
|
1721
|
+
console.log(
|
|
1722
|
+
import_chalk11.default.gray(`\u23F1 Analysis time: ${import_chalk11.default.bold(elapsedTime + "s")}`)
|
|
1723
|
+
);
|
|
1724
|
+
if (summary.totalIssues > 0 && report.results) {
|
|
1725
|
+
renderSubSection("Issues Breakdown");
|
|
1726
|
+
const sortedIssues = [...report.results].flatMap(
|
|
1727
|
+
(file) => (file.issues || []).map((issue) => ({
|
|
1728
|
+
...issue,
|
|
1729
|
+
file: file.fileName
|
|
1730
|
+
}))
|
|
1731
|
+
).sort((a, b) => {
|
|
1732
|
+
const levels = {
|
|
1733
|
+
critical: 4,
|
|
1734
|
+
major: 3,
|
|
1735
|
+
minor: 2,
|
|
1736
|
+
info: 1
|
|
1737
|
+
};
|
|
1738
|
+
return (levels[b.severity] || 0) - (levels[a.severity] || 0);
|
|
1739
|
+
}).slice(0, 10);
|
|
1740
|
+
sortedIssues.forEach((issue) => {
|
|
1741
|
+
const icon = issue.severity === "critical" ? "\u{1F534}" : issue.severity === "major" ? "\u{1F7E1}" : "\u{1F535}";
|
|
1742
|
+
const color = issue.severity === "critical" ? import_chalk11.default.red : issue.severity === "major" ? import_chalk11.default.yellow : import_chalk11.default.blue;
|
|
1743
|
+
console.log(
|
|
1744
|
+
` ${icon} ${color(issue.severity.toUpperCase())}: ${import_chalk11.default.white(issue.file)}${issue.line ? `:${issue.line}` : ""}`
|
|
1745
|
+
);
|
|
1746
|
+
console.log(` ${issue.message}`);
|
|
1747
|
+
if (issue.suggestion) {
|
|
1748
|
+
console.log(import_chalk11.default.dim(` \u{1F4A1} ${issue.suggestion}`));
|
|
1774
1749
|
}
|
|
1775
|
-
}
|
|
1776
|
-
if (report.recommendations?.length > 0) {
|
|
1777
|
-
console.log(import_chalk11.default.bold("\u{1F4A1} Recommendations\n"));
|
|
1778
|
-
report.recommendations.forEach((rec, i) => {
|
|
1779
|
-
console.log(`${i + 1}. ${rec}`);
|
|
1780
|
-
});
|
|
1781
1750
|
console.log();
|
|
1782
|
-
}
|
|
1783
|
-
}
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
console.log();
|
|
1751
|
+
});
|
|
1752
|
+
} else {
|
|
1753
|
+
console.log(
|
|
1754
|
+
import_chalk11.default.green("\n\u2728 Great! No consistency issues detected.\n")
|
|
1755
|
+
);
|
|
1788
1756
|
}
|
|
1757
|
+
renderToolScoreFooter(score);
|
|
1789
1758
|
}
|
|
1790
1759
|
});
|
|
1791
1760
|
}
|
|
1792
1761
|
|
|
1793
1762
|
// src/commands/visualize.ts
|
|
1794
1763
|
var import_chalk12 = __toESM(require("chalk"));
|
|
1795
|
-
var
|
|
1764
|
+
var import_fs6 = require("fs");
|
|
1796
1765
|
var import_path6 = require("path");
|
|
1797
1766
|
var import_child_process = require("child_process");
|
|
1798
1767
|
var import_core16 = require("@aiready/core");
|
|
@@ -1801,7 +1770,7 @@ async function visualizeAction(directory, options) {
|
|
|
1801
1770
|
try {
|
|
1802
1771
|
const dirPath = (0, import_path6.resolve)(process.cwd(), directory ?? ".");
|
|
1803
1772
|
let reportPath = options.report ? (0, import_path6.resolve)(dirPath, options.report) : null;
|
|
1804
|
-
if (!reportPath || !(0,
|
|
1773
|
+
if (!reportPath || !(0, import_fs6.existsSync)(reportPath)) {
|
|
1805
1774
|
const latestScan = (0, import_core17.findLatestReport)(dirPath);
|
|
1806
1775
|
if (latestScan) {
|
|
1807
1776
|
reportPath = latestScan;
|
|
@@ -1823,13 +1792,13 @@ Or specify a custom report:
|
|
|
1823
1792
|
return;
|
|
1824
1793
|
}
|
|
1825
1794
|
}
|
|
1826
|
-
const raw = (0,
|
|
1795
|
+
const raw = (0, import_fs6.readFileSync)(reportPath, "utf8");
|
|
1827
1796
|
const report = JSON.parse(raw);
|
|
1828
1797
|
const configPath = (0, import_path6.resolve)(dirPath, "aiready.json");
|
|
1829
1798
|
let graphConfig = { maxNodes: 400, maxEdges: 600 };
|
|
1830
|
-
if ((0,
|
|
1799
|
+
if ((0, import_fs6.existsSync)(configPath)) {
|
|
1831
1800
|
try {
|
|
1832
|
-
const rawConfig = JSON.parse((0,
|
|
1801
|
+
const rawConfig = JSON.parse((0, import_fs6.readFileSync)(configPath, "utf8"));
|
|
1833
1802
|
if (rawConfig.visualizer?.graph) {
|
|
1834
1803
|
graphConfig = {
|
|
1835
1804
|
maxNodes: rawConfig.visualizer.graph.maxNodes ?? graphConfig.maxNodes,
|
|
@@ -1852,7 +1821,7 @@ Or specify a custom report:
|
|
|
1852
1821
|
const localWebDir = (0, import_path6.resolve)(dirPath, "packages/visualizer");
|
|
1853
1822
|
let webDir = "";
|
|
1854
1823
|
let visualizerAvailable = false;
|
|
1855
|
-
if ((0,
|
|
1824
|
+
if ((0, import_fs6.existsSync)(localWebDir)) {
|
|
1856
1825
|
webDir = localWebDir;
|
|
1857
1826
|
visualizerAvailable = true;
|
|
1858
1827
|
} else {
|
|
@@ -1875,7 +1844,7 @@ Or specify a custom report:
|
|
|
1875
1844
|
currentDir = parent;
|
|
1876
1845
|
}
|
|
1877
1846
|
for (const location of nodemodulesLocations) {
|
|
1878
|
-
if ((0,
|
|
1847
|
+
if ((0, import_fs6.existsSync)(location) && (0, import_fs6.existsSync)((0, import_path6.resolve)(location, "package.json"))) {
|
|
1879
1848
|
webDir = location;
|
|
1880
1849
|
visualizerAvailable = true;
|
|
1881
1850
|
break;
|
|
@@ -1891,14 +1860,14 @@ Or specify a custom report:
|
|
|
1891
1860
|
}
|
|
1892
1861
|
}
|
|
1893
1862
|
}
|
|
1894
|
-
const webViteConfigExists = webDir && (0,
|
|
1863
|
+
const webViteConfigExists = webDir && (0, import_fs6.existsSync)((0, import_path6.resolve)(webDir, "web", "vite.config.ts"));
|
|
1895
1864
|
if (visualizerAvailable && webViteConfigExists) {
|
|
1896
1865
|
const spawnCwd = webDir;
|
|
1897
1866
|
const { watch } = await import("fs");
|
|
1898
1867
|
const copyReportToViz = () => {
|
|
1899
1868
|
try {
|
|
1900
1869
|
const destPath = (0, import_path6.resolve)(spawnCwd, "web", "report-data.json");
|
|
1901
|
-
(0,
|
|
1870
|
+
(0, import_fs6.copyFileSync)(reportPath, destPath);
|
|
1902
1871
|
console.log(`\u{1F4CB} Report synced to ${destPath}`);
|
|
1903
1872
|
} catch (e) {
|
|
1904
1873
|
console.error("Failed to sync report:", e);
|
|
@@ -1962,7 +1931,7 @@ Or specify a custom report:
|
|
|
1962
1931
|
const html = (0, import_core17.generateHTML)(graph);
|
|
1963
1932
|
const defaultOutput = "visualization.html";
|
|
1964
1933
|
const outPath = (0, import_path6.resolve)(dirPath, options.output ?? defaultOutput);
|
|
1965
|
-
(0,
|
|
1934
|
+
(0, import_fs6.writeFileSync)(outPath, html, "utf8");
|
|
1966
1935
|
console.log(import_chalk12.default.green(`\u2705 Visualization written to: ${outPath}`));
|
|
1967
1936
|
if (options.open || options.serve) {
|
|
1968
1937
|
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
@@ -2245,7 +2214,7 @@ EXAMPLES:
|
|
|
2245
2214
|
// src/commands/remediate.ts
|
|
2246
2215
|
var import_chalk18 = __toESM(require("chalk"));
|
|
2247
2216
|
var import_path7 = require("path");
|
|
2248
|
-
var
|
|
2217
|
+
var import_fs7 = require("fs");
|
|
2249
2218
|
var import_core18 = require("@aiready/core");
|
|
2250
2219
|
async function remediateAction(directory, options) {
|
|
2251
2220
|
const resolvedDir = (0, import_path7.resolve)(process.cwd(), directory || ".");
|
|
@@ -2255,15 +2224,15 @@ async function remediateAction(directory, options) {
|
|
|
2255
2224
|
let reportPath = options.report;
|
|
2256
2225
|
if (!reportPath) {
|
|
2257
2226
|
const aireadyDir = (0, import_path7.resolve)(resolvedDir, ".aiready");
|
|
2258
|
-
if ((0,
|
|
2259
|
-
const files = (0,
|
|
2227
|
+
if ((0, import_fs7.existsSync)(aireadyDir)) {
|
|
2228
|
+
const files = (0, import_fs7.readdirSync)(aireadyDir).filter((f) => f.startsWith("aiready-report-") && f.endsWith(".json")).sort().reverse();
|
|
2260
2229
|
if (files.length > 0) {
|
|
2261
2230
|
reportPath = (0, import_path7.resolve)(aireadyDir, files[0]);
|
|
2262
2231
|
console.log(import_chalk18.default.dim(`\u{1F4C2} Using latest report: ${files[0]}`));
|
|
2263
2232
|
}
|
|
2264
2233
|
}
|
|
2265
2234
|
}
|
|
2266
|
-
if (!reportPath || !(0,
|
|
2235
|
+
if (!reportPath || !(0, import_fs7.existsSync)(reportPath)) {
|
|
2267
2236
|
console.log(import_chalk18.default.yellow("\n\u26A0\uFE0F No AIReady report found."));
|
|
2268
2237
|
console.log(
|
|
2269
2238
|
import_chalk18.default.dim(
|
|
@@ -2340,7 +2309,7 @@ var getDirname = () => {
|
|
|
2340
2309
|
return (0, import_path8.dirname)((0, import_url.fileURLToPath)(import_meta.url));
|
|
2341
2310
|
};
|
|
2342
2311
|
var packageJson = JSON.parse(
|
|
2343
|
-
(0,
|
|
2312
|
+
(0, import_fs8.readFileSync)((0, import_path8.join)(getDirname(), "../package.json"), "utf8")
|
|
2344
2313
|
);
|
|
2345
2314
|
var program = new import_commander.Command();
|
|
2346
2315
|
program.name("aiready").description("AIReady - Assess and improve AI-readiness of codebases").version(packageJson.version).addHelpText(
|
|
@@ -2369,42 +2338,15 @@ CONFIGURATION:
|
|
|
2369
2338
|
Config files (searched upward): aiready.json, .aiready.json, aiready.config.*
|
|
2370
2339
|
CLI options override config file settings
|
|
2371
2340
|
|
|
2372
|
-
Example aiready.json:
|
|
2373
|
-
{
|
|
2374
|
-
"scan": { "exclude": ["**/dist/**", "**/node_modules/**"] },
|
|
2375
|
-
"tools": {
|
|
2376
|
-
"pattern-detect": { "minSimilarity": 0.5 },
|
|
2377
|
-
"context-analyzer": { "maxContextBudget": 15000 }
|
|
2378
|
-
},
|
|
2379
|
-
"output": { "format": "json", "directory": ".aiready" }
|
|
2380
|
-
}
|
|
2381
|
-
|
|
2382
2341
|
VERSION: ${packageJson.version}
|
|
2383
2342
|
DOCUMENTATION: https://aiready.dev/docs/cli
|
|
2384
2343
|
GITHUB: https://github.com/caopengau/aiready-cli
|
|
2385
2344
|
LANDING: https://github.com/caopengau/aiready-landing`
|
|
2386
2345
|
);
|
|
2387
|
-
program
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
"Tools to run (comma-separated: patterns,context,consistency,doc-drift,deps-health,aiSignalClarity,grounding,testability,changeAmplification)"
|
|
2392
|
-
).option(
|
|
2393
|
-
"--profile <type>",
|
|
2394
|
-
"Scan profile to use (agentic, cost, logic, ui, security, onboarding)"
|
|
2395
|
-
).option(
|
|
2396
|
-
"--compare-to <path>",
|
|
2397
|
-
"Compare results against a previous AIReady report JSON"
|
|
2398
|
-
).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(
|
|
2399
|
-
"--ci",
|
|
2400
|
-
"CI mode: GitHub Actions annotations, no colors, fail on threshold"
|
|
2401
|
-
).option(
|
|
2402
|
-
"--fail-on <level>",
|
|
2403
|
-
"Fail on issues: critical, major, any",
|
|
2404
|
-
"critical"
|
|
2405
|
-
).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) => {
|
|
2406
|
-
await scanAction(directory, options);
|
|
2407
|
-
});
|
|
2346
|
+
defineScanCommand(program);
|
|
2347
|
+
definePatternsCommand(program);
|
|
2348
|
+
defineContextCommand(program);
|
|
2349
|
+
defineConsistencyCommand(program);
|
|
2408
2350
|
program.command("init").description("Generate a default configuration (aiready.json)").option("-f, --force", "Overwrite existing configuration file").option(
|
|
2409
2351
|
"--js",
|
|
2410
2352
|
"Generate configuration as a JavaScript file (aiready.config.js)"
|
|
@@ -2412,36 +2354,6 @@ program.command("init").description("Generate a default configuration (aiready.j
|
|
|
2412
2354
|
const format = options.js ? "js" : "json";
|
|
2413
2355
|
await initAction({ force: options.force, format, full: options.full });
|
|
2414
2356
|
});
|
|
2415
|
-
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(
|
|
2416
|
-
"--max-candidates <number>",
|
|
2417
|
-
"Maximum candidates per block (performance tuning)"
|
|
2418
|
-
).option(
|
|
2419
|
-
"--min-shared-tokens <number>",
|
|
2420
|
-
"Minimum shared tokens for candidates (performance tuning)"
|
|
2421
|
-
).option(
|
|
2422
|
-
"--full-scan",
|
|
2423
|
-
"Disable smart defaults for comprehensive analysis (slower)"
|
|
2424
|
-
).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) => {
|
|
2425
|
-
await patternsAction(directory, options);
|
|
2426
|
-
});
|
|
2427
|
-
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(
|
|
2428
|
-
"--max-context <number>",
|
|
2429
|
-
"Maximum acceptable context budget (tokens)",
|
|
2430
|
-
"10000"
|
|
2431
|
-
).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) => {
|
|
2432
|
-
await contextAction(directory, options);
|
|
2433
|
-
});
|
|
2434
|
-
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(
|
|
2435
|
-
"--min-severity <level>",
|
|
2436
|
-
"Minimum severity: info|minor|major|critical",
|
|
2437
|
-
"info"
|
|
2438
|
-
).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option(
|
|
2439
|
-
"-o, --output <format>",
|
|
2440
|
-
"Output format: console, json, markdown",
|
|
2441
|
-
"console"
|
|
2442
|
-
).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) => {
|
|
2443
|
-
await consistencyAction(directory, options);
|
|
2444
|
-
});
|
|
2445
2357
|
program.command("visualise").description("Alias for visualize (British spelling)").argument("[directory]", "Directory to analyze", ".").option(
|
|
2446
2358
|
"--report <path>",
|
|
2447
2359
|
"Report path (auto-detects latest .aiready/aiready-report-*.json if not provided)"
|