@aiready/cli 0.15.0 → 0.15.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -8
- package/dist/cli.js +194 -276
- package/dist/cli.mjs +183 -269
- 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"));
|
|
@@ -471,7 +439,8 @@ function getProfileTools(profile) {
|
|
|
471
439
|
return [
|
|
472
440
|
import_core5.ToolName.AiSignalClarity,
|
|
473
441
|
import_core5.ToolName.AgentGrounding,
|
|
474
|
-
import_core5.ToolName.TestabilityIndex
|
|
442
|
+
import_core5.ToolName.TestabilityIndex,
|
|
443
|
+
import_core5.ToolName.ContractEnforcement
|
|
475
444
|
];
|
|
476
445
|
case "cost":
|
|
477
446
|
return [import_core5.ToolName.PatternDetect, import_core5.ToolName.ContextAnalyzer];
|
|
@@ -481,7 +450,8 @@ function getProfileTools(profile) {
|
|
|
481
450
|
import_core5.ToolName.NamingConsistency,
|
|
482
451
|
import_core5.ToolName.ContextAnalyzer,
|
|
483
452
|
import_core5.ToolName.PatternDetect,
|
|
484
|
-
import_core5.ToolName.ChangeAmplification
|
|
453
|
+
import_core5.ToolName.ChangeAmplification,
|
|
454
|
+
import_core5.ToolName.ContractEnforcement
|
|
485
455
|
];
|
|
486
456
|
case "ui":
|
|
487
457
|
return [
|
|
@@ -492,7 +462,11 @@ function getProfileTools(profile) {
|
|
|
492
462
|
import_core5.ToolName.AiSignalClarity
|
|
493
463
|
];
|
|
494
464
|
case "security":
|
|
495
|
-
return [
|
|
465
|
+
return [
|
|
466
|
+
import_core5.ToolName.NamingConsistency,
|
|
467
|
+
import_core5.ToolName.TestabilityIndex,
|
|
468
|
+
import_core5.ToolName.ContractEnforcement
|
|
469
|
+
];
|
|
496
470
|
case "onboarding":
|
|
497
471
|
return [
|
|
498
472
|
import_core5.ToolName.ContextAnalyzer,
|
|
@@ -1164,6 +1138,38 @@ ${import_chalk6.default.bold("CI/CD Integration:")}
|
|
|
1164
1138
|
Use --threshold and --fail-on to use AIReady as a quality gate in your CI pipelines.
|
|
1165
1139
|
When running in GitHub Actions, it will automatically emit annotations for found issues.
|
|
1166
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
|
+
}
|
|
1167
1173
|
|
|
1168
1174
|
// src/commands/init.ts
|
|
1169
1175
|
var import_fs5 = require("fs");
|
|
@@ -1443,33 +1449,6 @@ function renderSafetyRating(safety) {
|
|
|
1443
1449
|
` AI Change Safety: ${color(`${icon} ${safety.toUpperCase()}`)}`
|
|
1444
1450
|
);
|
|
1445
1451
|
}
|
|
1446
|
-
function renderIssueSummaryBlock(summary) {
|
|
1447
|
-
const total = (summary.criticalIssues || 0) + (summary.majorIssues || 0) + (summary.minorIssues || 0);
|
|
1448
|
-
if (total === 0) {
|
|
1449
|
-
console.log(import_chalk8.default.green("\u2705 No significant issues found!\n"));
|
|
1450
|
-
return;
|
|
1451
|
-
}
|
|
1452
|
-
console.log(import_chalk8.default.bold("\u26A0\uFE0F Issues Found:\n"));
|
|
1453
|
-
if (summary.criticalIssues > 0)
|
|
1454
|
-
console.log(
|
|
1455
|
-
import_chalk8.default.red(` \u{1F534} Critical: ${import_chalk8.default.bold(summary.criticalIssues)}`)
|
|
1456
|
-
);
|
|
1457
|
-
if (summary.majorIssues > 0)
|
|
1458
|
-
console.log(
|
|
1459
|
-
import_chalk8.default.yellow(` \u{1F7E1} Major: ${import_chalk8.default.bold(summary.majorIssues)}`)
|
|
1460
|
-
);
|
|
1461
|
-
if (summary.minorIssues > 0)
|
|
1462
|
-
console.log(import_chalk8.default.blue(` \u{1F535} Minor: ${import_chalk8.default.bold(summary.minorIssues)}`));
|
|
1463
|
-
if (summary.totalPotentialSavings) {
|
|
1464
|
-
console.log(
|
|
1465
|
-
import_chalk8.default.green(
|
|
1466
|
-
`
|
|
1467
|
-
\u{1F4A1} Potential savings: ${import_chalk8.default.bold(summary.totalPotentialSavings.toLocaleString())} tokens
|
|
1468
|
-
`
|
|
1469
|
-
)
|
|
1470
|
-
);
|
|
1471
|
-
}
|
|
1472
|
-
}
|
|
1473
1452
|
function renderSubSection(title) {
|
|
1474
1453
|
console.log("\n" + (0, import_core12.getTerminalDivider)());
|
|
1475
1454
|
console.log(import_chalk8.default.bold.white(` ${title.toUpperCase()}`));
|
|
@@ -1486,6 +1465,36 @@ function renderToolScoreFooter(score) {
|
|
|
1486
1465
|
}
|
|
1487
1466
|
|
|
1488
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
|
+
}
|
|
1489
1498
|
async function patternsAction(directory, options) {
|
|
1490
1499
|
return await executeToolAction(directory, options, {
|
|
1491
1500
|
toolName: "pattern-detect",
|
|
@@ -1565,21 +1574,30 @@ async function patternsAction(directory, options) {
|
|
|
1565
1574
|
}
|
|
1566
1575
|
});
|
|
1567
1576
|
}
|
|
1568
|
-
var PATTERNS_HELP_TEXT = `
|
|
1569
|
-
EXAMPLES:
|
|
1570
|
-
$ aiready patterns # Default analysis
|
|
1571
|
-
$ aiready patterns --similarity 0.6 # Stricter matching
|
|
1572
|
-
$ aiready patterns --min-lines 10 # Larger patterns only
|
|
1573
|
-
`;
|
|
1574
1577
|
|
|
1575
1578
|
// src/commands/context.ts
|
|
1576
1579
|
var import_chalk10 = __toESM(require("chalk"));
|
|
1577
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
|
+
}
|
|
1578
1596
|
async function contextAction(directory, options) {
|
|
1579
1597
|
return await executeToolAction(directory, options, {
|
|
1580
1598
|
toolName: "context-analyzer",
|
|
1581
1599
|
label: "Context analysis",
|
|
1582
|
-
emoji: "\u{
|
|
1600
|
+
emoji: "\u{1F9E9}",
|
|
1583
1601
|
defaults: {
|
|
1584
1602
|
maxDepth: 5,
|
|
1585
1603
|
maxContextBudget: 1e4,
|
|
@@ -1591,11 +1609,6 @@ async function contextAction(directory, options) {
|
|
|
1591
1609
|
maxDepth: opts.maxDepth ? parseInt(opts.maxDepth) : void 0,
|
|
1592
1610
|
maxContextBudget: opts.maxContext ? parseInt(opts.maxContext) : void 0
|
|
1593
1611
|
}),
|
|
1594
|
-
preAnalyze: async (resolvedDir, baseOptions) => {
|
|
1595
|
-
const { getSmartDefaults } = await import("@aiready/context-analyzer");
|
|
1596
|
-
const smartDefaults = await getSmartDefaults(resolvedDir, baseOptions);
|
|
1597
|
-
return { ...smartDefaults, ...baseOptions };
|
|
1598
|
-
},
|
|
1599
1612
|
importTool: async () => {
|
|
1600
1613
|
const { analyzeContext, generateSummary, calculateContextScore } = await import("@aiready/context-analyzer");
|
|
1601
1614
|
return {
|
|
@@ -1604,45 +1617,40 @@ async function contextAction(directory, options) {
|
|
|
1604
1617
|
calculateScore: calculateContextScore
|
|
1605
1618
|
};
|
|
1606
1619
|
},
|
|
1607
|
-
renderConsole: ({ summary, elapsedTime, score }) => {
|
|
1620
|
+
renderConsole: ({ results: _results, summary, elapsedTime, score }) => {
|
|
1608
1621
|
(0, import_core14.printTerminalHeader)("CONTEXT ANALYSIS SUMMARY");
|
|
1609
1622
|
console.log(
|
|
1610
|
-
import_chalk10.default.white(`\u{1F4C1}
|
|
1623
|
+
import_chalk10.default.white(`\u{1F4C1} Total files: ${import_chalk10.default.bold(summary.totalFiles)}`)
|
|
1611
1624
|
);
|
|
1612
1625
|
console.log(
|
|
1613
1626
|
import_chalk10.default.white(
|
|
1614
|
-
`\u{
|
|
1627
|
+
`\u{1F4B8} Total tokens (context budget): ${import_chalk10.default.bold(summary.totalTokens.toLocaleString())}`
|
|
1615
1628
|
)
|
|
1616
1629
|
);
|
|
1617
1630
|
console.log(
|
|
1618
|
-
import_chalk10.default.
|
|
1619
|
-
`\u{
|
|
1631
|
+
import_chalk10.default.cyan(
|
|
1632
|
+
`\u{1F4CA} Average context budget: ${import_chalk10.default.bold(summary.avgContextBudget.toFixed(0))} tokens`
|
|
1620
1633
|
)
|
|
1621
1634
|
);
|
|
1622
1635
|
console.log(
|
|
1623
|
-
import_chalk10.default.
|
|
1624
|
-
`)
|
|
1636
|
+
import_chalk10.default.gray(`\u23F1 Analysis time: ${import_chalk10.default.bold(elapsedTime + "s")}`)
|
|
1625
1637
|
);
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
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;
|
|
1631
1642
|
console.log(
|
|
1632
|
-
`
|
|
1643
|
+
` ${scoreColor("\u25A0")} ${import_chalk10.default.white(mod.domain.padEnd(20))} ${import_chalk10.default.bold((mod.fragmentationScore * 100).toFixed(0) + "%")} fragmentation`
|
|
1633
1644
|
);
|
|
1634
1645
|
});
|
|
1635
1646
|
}
|
|
1636
|
-
if (summary.
|
|
1637
|
-
renderSubSection("
|
|
1638
|
-
summary.
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
);
|
|
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;
|
|
1642
1652
|
console.log(
|
|
1643
|
-
import_chalk10.default.dim(
|
|
1644
|
-
` Token cost: ${module2.totalTokens.toLocaleString()}, Cohesion: ${(module2.avgCohesion * 100).toFixed(0)}%`
|
|
1645
|
-
)
|
|
1653
|
+
` ${icon} ${color(item.severity.toUpperCase())}: ${import_chalk10.default.white(item.file.split("/").pop())} ${import_chalk10.default.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`
|
|
1646
1654
|
);
|
|
1647
1655
|
});
|
|
1648
1656
|
}
|
|
@@ -1653,17 +1661,35 @@ async function contextAction(directory, options) {
|
|
|
1653
1661
|
|
|
1654
1662
|
// src/commands/consistency.ts
|
|
1655
1663
|
var import_chalk11 = __toESM(require("chalk"));
|
|
1656
|
-
var import_fs6 = require("fs");
|
|
1657
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
|
+
}
|
|
1658
1684
|
async function consistencyAction(directory, options) {
|
|
1659
1685
|
return await executeToolAction(directory, options, {
|
|
1660
1686
|
toolName: "naming-consistency",
|
|
1661
1687
|
label: "Consistency analysis",
|
|
1662
|
-
emoji: "\u{
|
|
1688
|
+
emoji: "\u{1F4CF}",
|
|
1663
1689
|
defaults: {
|
|
1664
|
-
checkNaming:
|
|
1665
|
-
checkPatterns:
|
|
1666
|
-
minSeverity: "info",
|
|
1690
|
+
checkNaming: options.naming !== false,
|
|
1691
|
+
checkPatterns: options.patterns !== false,
|
|
1692
|
+
minSeverity: options.minSeverity || "info",
|
|
1667
1693
|
include: void 0,
|
|
1668
1694
|
exclude: void 0,
|
|
1669
1695
|
output: { format: "console", file: void 0 }
|
|
@@ -1674,119 +1700,68 @@ async function consistencyAction(directory, options) {
|
|
|
1674
1700
|
minSeverity: opts.minSeverity
|
|
1675
1701
|
}),
|
|
1676
1702
|
importTool: async () => {
|
|
1677
|
-
const { analyzeConsistency, calculateConsistencyScore } = await import("@aiready/consistency");
|
|
1703
|
+
const { analyzeConsistency, generateSummary, calculateConsistencyScore } = await import("@aiready/consistency");
|
|
1678
1704
|
return {
|
|
1679
|
-
analyze:
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
);
|
|
1686
|
-
}
|
|
1705
|
+
analyze: async (opts) => {
|
|
1706
|
+
const report = await analyzeConsistency(opts);
|
|
1707
|
+
return report;
|
|
1708
|
+
},
|
|
1709
|
+
generateSummary,
|
|
1710
|
+
calculateScore: calculateConsistencyScore
|
|
1687
1711
|
};
|
|
1688
1712
|
},
|
|
1689
|
-
renderConsole: ({ results, summary, elapsedTime, score
|
|
1690
|
-
|
|
1691
|
-
const { format: outputFormat, file: userOutputFile } = (0, import_core15.resolveOutputFormat)(options, finalOptions);
|
|
1692
|
-
if (outputFormat === "markdown") {
|
|
1693
|
-
const markdown = generateMarkdownReport(report, elapsedTime);
|
|
1694
|
-
const outputPath = (0, import_core15.resolveOutputPath)(
|
|
1695
|
-
userOutputFile,
|
|
1696
|
-
`aiready-report-${(0, import_core2.getReportTimestamp)()}.md`,
|
|
1697
|
-
directory
|
|
1698
|
-
);
|
|
1699
|
-
(0, import_fs6.writeFileSync)(outputPath, markdown);
|
|
1700
|
-
console.log(import_chalk11.default.green(`\u2705 Report saved to ${outputPath}`));
|
|
1701
|
-
return;
|
|
1702
|
-
}
|
|
1703
|
-
console.log(import_chalk11.default.bold("\n\u{1F4CA} Summary\n"));
|
|
1704
|
-
console.log(`Files Analyzed: ${import_chalk11.default.cyan(summary.filesAnalyzed)}`);
|
|
1705
|
-
console.log(`Total Issues: ${import_chalk11.default.yellow(summary.totalIssues)}`);
|
|
1706
|
-
console.log(` Naming: ${import_chalk11.default.yellow(summary.namingIssues)}`);
|
|
1707
|
-
console.log(` Patterns: ${import_chalk11.default.yellow(summary.patternIssues)}`);
|
|
1713
|
+
renderConsole: ({ results: report, summary, elapsedTime, score }) => {
|
|
1714
|
+
(0, import_core15.printTerminalHeader)("CONSISTENCY ANALYSIS SUMMARY");
|
|
1708
1715
|
console.log(
|
|
1709
|
-
|
|
1716
|
+
import_chalk11.default.white(`\u{1F4C1} Files analyzed: ${import_chalk11.default.bold(summary.filesAnalyzed)}`)
|
|
1710
1717
|
);
|
|
1711
|
-
console.log(
|
|
1712
|
-
`)
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
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
|
-
console.log();
|
|
1744
|
-
shown++;
|
|
1745
|
-
}
|
|
1746
|
-
}
|
|
1747
|
-
}
|
|
1748
|
-
if (patternResults.length > 0) {
|
|
1749
|
-
console.log(import_chalk11.default.bold("\u{1F504} Pattern Issues\n"));
|
|
1750
|
-
let shown = 0;
|
|
1751
|
-
for (const patternFileResult of patternResults) {
|
|
1752
|
-
if (shown >= 5) break;
|
|
1753
|
-
for (const issue of patternFileResult.issues) {
|
|
1754
|
-
if (shown >= 5) break;
|
|
1755
|
-
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;
|
|
1756
|
-
console.log(
|
|
1757
|
-
`${severityColor(issue.severity.toUpperCase())} ${import_chalk11.default.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1758
|
-
);
|
|
1759
|
-
console.log(` ${issue.message}`);
|
|
1760
|
-
if (issue.suggestion) {
|
|
1761
|
-
console.log(
|
|
1762
|
-
` ${import_chalk11.default.dim("\u2192")} ${import_chalk11.default.italic(issue.suggestion)}`
|
|
1763
|
-
);
|
|
1764
|
-
}
|
|
1765
|
-
console.log();
|
|
1766
|
-
shown++;
|
|
1767
|
-
}
|
|
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}`));
|
|
1768
1749
|
}
|
|
1769
|
-
}
|
|
1770
|
-
if (report.recommendations?.length > 0) {
|
|
1771
|
-
console.log(import_chalk11.default.bold("\u{1F4A1} Recommendations\n"));
|
|
1772
|
-
report.recommendations.forEach((rec, i) => {
|
|
1773
|
-
console.log(`${i + 1}. ${rec}`);
|
|
1774
|
-
});
|
|
1775
1750
|
console.log();
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
console.log();
|
|
1751
|
+
});
|
|
1752
|
+
} else {
|
|
1753
|
+
console.log(
|
|
1754
|
+
import_chalk11.default.green("\n\u2728 Great! No consistency issues detected.\n")
|
|
1755
|
+
);
|
|
1782
1756
|
}
|
|
1757
|
+
renderToolScoreFooter(score);
|
|
1783
1758
|
}
|
|
1784
1759
|
});
|
|
1785
1760
|
}
|
|
1786
1761
|
|
|
1787
1762
|
// src/commands/visualize.ts
|
|
1788
1763
|
var import_chalk12 = __toESM(require("chalk"));
|
|
1789
|
-
var
|
|
1764
|
+
var import_fs6 = require("fs");
|
|
1790
1765
|
var import_path6 = require("path");
|
|
1791
1766
|
var import_child_process = require("child_process");
|
|
1792
1767
|
var import_core16 = require("@aiready/core");
|
|
@@ -1795,7 +1770,7 @@ async function visualizeAction(directory, options) {
|
|
|
1795
1770
|
try {
|
|
1796
1771
|
const dirPath = (0, import_path6.resolve)(process.cwd(), directory ?? ".");
|
|
1797
1772
|
let reportPath = options.report ? (0, import_path6.resolve)(dirPath, options.report) : null;
|
|
1798
|
-
if (!reportPath || !(0,
|
|
1773
|
+
if (!reportPath || !(0, import_fs6.existsSync)(reportPath)) {
|
|
1799
1774
|
const latestScan = (0, import_core17.findLatestReport)(dirPath);
|
|
1800
1775
|
if (latestScan) {
|
|
1801
1776
|
reportPath = latestScan;
|
|
@@ -1817,13 +1792,13 @@ Or specify a custom report:
|
|
|
1817
1792
|
return;
|
|
1818
1793
|
}
|
|
1819
1794
|
}
|
|
1820
|
-
const raw = (0,
|
|
1795
|
+
const raw = (0, import_fs6.readFileSync)(reportPath, "utf8");
|
|
1821
1796
|
const report = JSON.parse(raw);
|
|
1822
1797
|
const configPath = (0, import_path6.resolve)(dirPath, "aiready.json");
|
|
1823
1798
|
let graphConfig = { maxNodes: 400, maxEdges: 600 };
|
|
1824
|
-
if ((0,
|
|
1799
|
+
if ((0, import_fs6.existsSync)(configPath)) {
|
|
1825
1800
|
try {
|
|
1826
|
-
const rawConfig = JSON.parse((0,
|
|
1801
|
+
const rawConfig = JSON.parse((0, import_fs6.readFileSync)(configPath, "utf8"));
|
|
1827
1802
|
if (rawConfig.visualizer?.graph) {
|
|
1828
1803
|
graphConfig = {
|
|
1829
1804
|
maxNodes: rawConfig.visualizer.graph.maxNodes ?? graphConfig.maxNodes,
|
|
@@ -1846,7 +1821,7 @@ Or specify a custom report:
|
|
|
1846
1821
|
const localWebDir = (0, import_path6.resolve)(dirPath, "packages/visualizer");
|
|
1847
1822
|
let webDir = "";
|
|
1848
1823
|
let visualizerAvailable = false;
|
|
1849
|
-
if ((0,
|
|
1824
|
+
if ((0, import_fs6.existsSync)(localWebDir)) {
|
|
1850
1825
|
webDir = localWebDir;
|
|
1851
1826
|
visualizerAvailable = true;
|
|
1852
1827
|
} else {
|
|
@@ -1869,7 +1844,7 @@ Or specify a custom report:
|
|
|
1869
1844
|
currentDir = parent;
|
|
1870
1845
|
}
|
|
1871
1846
|
for (const location of nodemodulesLocations) {
|
|
1872
|
-
if ((0,
|
|
1847
|
+
if ((0, import_fs6.existsSync)(location) && (0, import_fs6.existsSync)((0, import_path6.resolve)(location, "package.json"))) {
|
|
1873
1848
|
webDir = location;
|
|
1874
1849
|
visualizerAvailable = true;
|
|
1875
1850
|
break;
|
|
@@ -1885,14 +1860,14 @@ Or specify a custom report:
|
|
|
1885
1860
|
}
|
|
1886
1861
|
}
|
|
1887
1862
|
}
|
|
1888
|
-
const webViteConfigExists = webDir && (0,
|
|
1863
|
+
const webViteConfigExists = webDir && (0, import_fs6.existsSync)((0, import_path6.resolve)(webDir, "web", "vite.config.ts"));
|
|
1889
1864
|
if (visualizerAvailable && webViteConfigExists) {
|
|
1890
1865
|
const spawnCwd = webDir;
|
|
1891
1866
|
const { watch } = await import("fs");
|
|
1892
1867
|
const copyReportToViz = () => {
|
|
1893
1868
|
try {
|
|
1894
1869
|
const destPath = (0, import_path6.resolve)(spawnCwd, "web", "report-data.json");
|
|
1895
|
-
(0,
|
|
1870
|
+
(0, import_fs6.copyFileSync)(reportPath, destPath);
|
|
1896
1871
|
console.log(`\u{1F4CB} Report synced to ${destPath}`);
|
|
1897
1872
|
} catch (e) {
|
|
1898
1873
|
console.error("Failed to sync report:", e);
|
|
@@ -1956,7 +1931,7 @@ Or specify a custom report:
|
|
|
1956
1931
|
const html = (0, import_core17.generateHTML)(graph);
|
|
1957
1932
|
const defaultOutput = "visualization.html";
|
|
1958
1933
|
const outPath = (0, import_path6.resolve)(dirPath, options.output ?? defaultOutput);
|
|
1959
|
-
(0,
|
|
1934
|
+
(0, import_fs6.writeFileSync)(outPath, html, "utf8");
|
|
1960
1935
|
console.log(import_chalk12.default.green(`\u2705 Visualization written to: ${outPath}`));
|
|
1961
1936
|
if (options.open || options.serve) {
|
|
1962
1937
|
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
@@ -2239,7 +2214,7 @@ EXAMPLES:
|
|
|
2239
2214
|
// src/commands/remediate.ts
|
|
2240
2215
|
var import_chalk18 = __toESM(require("chalk"));
|
|
2241
2216
|
var import_path7 = require("path");
|
|
2242
|
-
var
|
|
2217
|
+
var import_fs7 = require("fs");
|
|
2243
2218
|
var import_core18 = require("@aiready/core");
|
|
2244
2219
|
async function remediateAction(directory, options) {
|
|
2245
2220
|
const resolvedDir = (0, import_path7.resolve)(process.cwd(), directory || ".");
|
|
@@ -2249,15 +2224,15 @@ async function remediateAction(directory, options) {
|
|
|
2249
2224
|
let reportPath = options.report;
|
|
2250
2225
|
if (!reportPath) {
|
|
2251
2226
|
const aireadyDir = (0, import_path7.resolve)(resolvedDir, ".aiready");
|
|
2252
|
-
if ((0,
|
|
2253
|
-
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();
|
|
2254
2229
|
if (files.length > 0) {
|
|
2255
2230
|
reportPath = (0, import_path7.resolve)(aireadyDir, files[0]);
|
|
2256
2231
|
console.log(import_chalk18.default.dim(`\u{1F4C2} Using latest report: ${files[0]}`));
|
|
2257
2232
|
}
|
|
2258
2233
|
}
|
|
2259
2234
|
}
|
|
2260
|
-
if (!reportPath || !(0,
|
|
2235
|
+
if (!reportPath || !(0, import_fs7.existsSync)(reportPath)) {
|
|
2261
2236
|
console.log(import_chalk18.default.yellow("\n\u26A0\uFE0F No AIReady report found."));
|
|
2262
2237
|
console.log(
|
|
2263
2238
|
import_chalk18.default.dim(
|
|
@@ -2334,7 +2309,7 @@ var getDirname = () => {
|
|
|
2334
2309
|
return (0, import_path8.dirname)((0, import_url.fileURLToPath)(import_meta.url));
|
|
2335
2310
|
};
|
|
2336
2311
|
var packageJson = JSON.parse(
|
|
2337
|
-
(0,
|
|
2312
|
+
(0, import_fs8.readFileSync)((0, import_path8.join)(getDirname(), "../package.json"), "utf8")
|
|
2338
2313
|
);
|
|
2339
2314
|
var program = new import_commander.Command();
|
|
2340
2315
|
program.name("aiready").description("AIReady - Assess and improve AI-readiness of codebases").version(packageJson.version).addHelpText(
|
|
@@ -2363,42 +2338,15 @@ CONFIGURATION:
|
|
|
2363
2338
|
Config files (searched upward): aiready.json, .aiready.json, aiready.config.*
|
|
2364
2339
|
CLI options override config file settings
|
|
2365
2340
|
|
|
2366
|
-
Example aiready.json:
|
|
2367
|
-
{
|
|
2368
|
-
"scan": { "exclude": ["**/dist/**", "**/node_modules/**"] },
|
|
2369
|
-
"tools": {
|
|
2370
|
-
"pattern-detect": { "minSimilarity": 0.5 },
|
|
2371
|
-
"context-analyzer": { "maxContextBudget": 15000 }
|
|
2372
|
-
},
|
|
2373
|
-
"output": { "format": "json", "directory": ".aiready" }
|
|
2374
|
-
}
|
|
2375
|
-
|
|
2376
2341
|
VERSION: ${packageJson.version}
|
|
2377
2342
|
DOCUMENTATION: https://aiready.dev/docs/cli
|
|
2378
2343
|
GITHUB: https://github.com/caopengau/aiready-cli
|
|
2379
2344
|
LANDING: https://github.com/caopengau/aiready-landing`
|
|
2380
2345
|
);
|
|
2381
|
-
program
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
"Tools to run (comma-separated: patterns,context,consistency,doc-drift,deps-health,aiSignalClarity,grounding,testability,changeAmplification)"
|
|
2386
|
-
).option(
|
|
2387
|
-
"--profile <type>",
|
|
2388
|
-
"Scan profile to use (agentic, cost, logic, ui, security, onboarding)"
|
|
2389
|
-
).option(
|
|
2390
|
-
"--compare-to <path>",
|
|
2391
|
-
"Compare results against a previous AIReady report JSON"
|
|
2392
|
-
).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(
|
|
2393
|
-
"--ci",
|
|
2394
|
-
"CI mode: GitHub Actions annotations, no colors, fail on threshold"
|
|
2395
|
-
).option(
|
|
2396
|
-
"--fail-on <level>",
|
|
2397
|
-
"Fail on issues: critical, major, any",
|
|
2398
|
-
"critical"
|
|
2399
|
-
).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) => {
|
|
2400
|
-
await scanAction(directory, options);
|
|
2401
|
-
});
|
|
2346
|
+
defineScanCommand(program);
|
|
2347
|
+
definePatternsCommand(program);
|
|
2348
|
+
defineContextCommand(program);
|
|
2349
|
+
defineConsistencyCommand(program);
|
|
2402
2350
|
program.command("init").description("Generate a default configuration (aiready.json)").option("-f, --force", "Overwrite existing configuration file").option(
|
|
2403
2351
|
"--js",
|
|
2404
2352
|
"Generate configuration as a JavaScript file (aiready.config.js)"
|
|
@@ -2406,36 +2354,6 @@ program.command("init").description("Generate a default configuration (aiready.j
|
|
|
2406
2354
|
const format = options.js ? "js" : "json";
|
|
2407
2355
|
await initAction({ force: options.force, format, full: options.full });
|
|
2408
2356
|
});
|
|
2409
|
-
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(
|
|
2410
|
-
"--max-candidates <number>",
|
|
2411
|
-
"Maximum candidates per block (performance tuning)"
|
|
2412
|
-
).option(
|
|
2413
|
-
"--min-shared-tokens <number>",
|
|
2414
|
-
"Minimum shared tokens for candidates (performance tuning)"
|
|
2415
|
-
).option(
|
|
2416
|
-
"--full-scan",
|
|
2417
|
-
"Disable smart defaults for comprehensive analysis (slower)"
|
|
2418
|
-
).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) => {
|
|
2419
|
-
await patternsAction(directory, options);
|
|
2420
|
-
});
|
|
2421
|
-
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(
|
|
2422
|
-
"--max-context <number>",
|
|
2423
|
-
"Maximum acceptable context budget (tokens)",
|
|
2424
|
-
"10000"
|
|
2425
|
-
).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) => {
|
|
2426
|
-
await contextAction(directory, options);
|
|
2427
|
-
});
|
|
2428
|
-
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(
|
|
2429
|
-
"--min-severity <level>",
|
|
2430
|
-
"Minimum severity: info|minor|major|critical",
|
|
2431
|
-
"info"
|
|
2432
|
-
).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option(
|
|
2433
|
-
"-o, --output <format>",
|
|
2434
|
-
"Output format: console, json, markdown",
|
|
2435
|
-
"console"
|
|
2436
|
-
).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) => {
|
|
2437
|
-
await consistencyAction(directory, options);
|
|
2438
|
-
});
|
|
2439
2357
|
program.command("visualise").description("Alias for visualize (British spelling)").argument("[directory]", "Directory to analyze", ".").option(
|
|
2440
2358
|
"--report <path>",
|
|
2441
2359
|
"Report path (auto-detects latest .aiready/aiready-report-*.json if not provided)"
|