@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.mjs
CHANGED
|
@@ -88,38 +88,6 @@ async function warnIfGraphCapExceeded(report, dirPath) {
|
|
|
88
88
|
void err;
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
-
function generateMarkdownReport(report, elapsedTime) {
|
|
92
|
-
let markdown = `# Consistency Analysis Report
|
|
93
|
-
|
|
94
|
-
`;
|
|
95
|
-
markdown += `**Generated:** ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
96
|
-
`;
|
|
97
|
-
markdown += `**Analysis Time:** ${elapsedTime}s
|
|
98
|
-
|
|
99
|
-
`;
|
|
100
|
-
markdown += `## Summary
|
|
101
|
-
|
|
102
|
-
`;
|
|
103
|
-
markdown += `- **Files Analyzed:** ${report.summary.filesAnalyzed}
|
|
104
|
-
`;
|
|
105
|
-
markdown += `- **Total Issues:** ${report.summary.totalIssues}
|
|
106
|
-
`;
|
|
107
|
-
markdown += ` - Naming: ${report.summary.namingIssues}
|
|
108
|
-
`;
|
|
109
|
-
markdown += ` - Patterns: ${report.summary.patternIssues}
|
|
110
|
-
|
|
111
|
-
`;
|
|
112
|
-
if (report.recommendations.length > 0) {
|
|
113
|
-
markdown += `## Recommendations
|
|
114
|
-
|
|
115
|
-
`;
|
|
116
|
-
report.recommendations.forEach((rec, i) => {
|
|
117
|
-
markdown += `${i + 1}. ${rec}
|
|
118
|
-
`;
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
return markdown;
|
|
122
|
-
}
|
|
123
91
|
|
|
124
92
|
// src/commands/report-formatter.ts
|
|
125
93
|
import chalk2 from "chalk";
|
|
@@ -433,7 +401,7 @@ async function executeToolAction(directory, options, config) {
|
|
|
433
401
|
const elapsedTime = getElapsedTime(startTime);
|
|
434
402
|
const summary = generateSummary(results);
|
|
435
403
|
let toolScore;
|
|
436
|
-
if (options.score && calculateScore) {
|
|
404
|
+
if ((options.score || finalOptions.score) && calculateScore) {
|
|
437
405
|
const resultsAny = results;
|
|
438
406
|
const scoreData = resultsAny.duplicates || resultsAny.issues || results;
|
|
439
407
|
const filesCount = resultsAny.length || resultsAny.summary?.filesAnalyzed || resultsAny.summary?.totalFiles;
|
|
@@ -876,6 +844,38 @@ ${chalk6.bold("CI/CD Integration:")}
|
|
|
876
844
|
Use --threshold and --fail-on to use AIReady as a quality gate in your CI pipelines.
|
|
877
845
|
When running in GitHub Actions, it will automatically emit annotations for found issues.
|
|
878
846
|
`;
|
|
847
|
+
function defineScanCommand(program2) {
|
|
848
|
+
program2.command("scan").description(
|
|
849
|
+
"Run comprehensive AI-readiness analysis (patterns + context + consistency)"
|
|
850
|
+
).argument("[directory]", "Directory to analyze", ".").option(
|
|
851
|
+
"-t, --tools <tools>",
|
|
852
|
+
"Tools to run (comma-separated: patterns,context,consistency,doc-drift,deps-health,aiSignalClarity,grounding,testability,changeAmplification)"
|
|
853
|
+
).option(
|
|
854
|
+
"--profile <type>",
|
|
855
|
+
"Scan profile to use (agentic, cost, logic, ui, security, onboarding)"
|
|
856
|
+
).option(
|
|
857
|
+
"--compare-to <path>",
|
|
858
|
+
"Compare results against a previous AIReady report JSON"
|
|
859
|
+
).option(
|
|
860
|
+
"--include <patterns>",
|
|
861
|
+
"File patterns to include (comma-separated)"
|
|
862
|
+
).option(
|
|
863
|
+
"--exclude <patterns>",
|
|
864
|
+
"File patterns to exclude (comma-separated)"
|
|
865
|
+
).option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score (0-100)").option("--no-score", "Disable calculating AI Readiness Score").option("--weights <weights>", "Custom scoring weights").option(
|
|
866
|
+
"--threshold <score>",
|
|
867
|
+
"Fail CI/CD if score below threshold (0-100)"
|
|
868
|
+
).option(
|
|
869
|
+
"--ci",
|
|
870
|
+
"CI mode: GitHub Actions annotations, no colors, fail on threshold"
|
|
871
|
+
).option(
|
|
872
|
+
"--fail-on <level>",
|
|
873
|
+
"Fail on issues: critical, major, any",
|
|
874
|
+
"critical"
|
|
875
|
+
).option("--api-key <key>", "Platform API key for automatic upload").option("--upload", "Automatically upload results to the platform").option("--server <url>", "Custom platform URL").addHelpText("after", SCAN_HELP_TEXT).action(async (directory, options) => {
|
|
876
|
+
await scanAction(directory, options);
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
879
|
|
|
880
880
|
// src/commands/init.ts
|
|
881
881
|
import { writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
|
|
@@ -1158,33 +1158,6 @@ function renderSafetyRating(safety) {
|
|
|
1158
1158
|
` AI Change Safety: ${color(`${icon} ${safety.toUpperCase()}`)}`
|
|
1159
1159
|
);
|
|
1160
1160
|
}
|
|
1161
|
-
function renderIssueSummaryBlock(summary) {
|
|
1162
|
-
const total = (summary.criticalIssues || 0) + (summary.majorIssues || 0) + (summary.minorIssues || 0);
|
|
1163
|
-
if (total === 0) {
|
|
1164
|
-
console.log(chalk8.green("\u2705 No significant issues found!\n"));
|
|
1165
|
-
return;
|
|
1166
|
-
}
|
|
1167
|
-
console.log(chalk8.bold("\u26A0\uFE0F Issues Found:\n"));
|
|
1168
|
-
if (summary.criticalIssues > 0)
|
|
1169
|
-
console.log(
|
|
1170
|
-
chalk8.red(` \u{1F534} Critical: ${chalk8.bold(summary.criticalIssues)}`)
|
|
1171
|
-
);
|
|
1172
|
-
if (summary.majorIssues > 0)
|
|
1173
|
-
console.log(
|
|
1174
|
-
chalk8.yellow(` \u{1F7E1} Major: ${chalk8.bold(summary.majorIssues)}`)
|
|
1175
|
-
);
|
|
1176
|
-
if (summary.minorIssues > 0)
|
|
1177
|
-
console.log(chalk8.blue(` \u{1F535} Minor: ${chalk8.bold(summary.minorIssues)}`));
|
|
1178
|
-
if (summary.totalPotentialSavings) {
|
|
1179
|
-
console.log(
|
|
1180
|
-
chalk8.green(
|
|
1181
|
-
`
|
|
1182
|
-
\u{1F4A1} Potential savings: ${chalk8.bold(summary.totalPotentialSavings.toLocaleString())} tokens
|
|
1183
|
-
`
|
|
1184
|
-
)
|
|
1185
|
-
);
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
1161
|
function renderSubSection(title) {
|
|
1189
1162
|
console.log("\n" + coreGetDivider());
|
|
1190
1163
|
console.log(chalk8.bold.white(` ${title.toUpperCase()}`));
|
|
@@ -1201,6 +1174,36 @@ function renderToolScoreFooter(score) {
|
|
|
1201
1174
|
}
|
|
1202
1175
|
|
|
1203
1176
|
// src/commands/patterns.ts
|
|
1177
|
+
var PATTERNS_HELP_TEXT = `
|
|
1178
|
+
EXAMPLES:
|
|
1179
|
+
$ aiready patterns # Default analysis
|
|
1180
|
+
$ aiready patterns --similarity 0.6 # Stricter matching
|
|
1181
|
+
$ aiready patterns --min-lines 10 # Larger patterns only
|
|
1182
|
+
`;
|
|
1183
|
+
function definePatternsCommand(program2) {
|
|
1184
|
+
program2.command("patterns").description("Detect duplicate code patterns that confuse AI models").argument("[directory]", "Directory to analyze", ".").option(
|
|
1185
|
+
"-s, --similarity <number>",
|
|
1186
|
+
"Minimum similarity score (0-1)",
|
|
1187
|
+
"0.40"
|
|
1188
|
+
).option("-l, --min-lines <number>", "Minimum lines to consider", "5").option(
|
|
1189
|
+
"--max-candidates <number>",
|
|
1190
|
+
"Maximum candidates per block (performance tuning)"
|
|
1191
|
+
).option(
|
|
1192
|
+
"--min-shared-tokens <number>",
|
|
1193
|
+
"Minimum shared tokens for candidates (performance tuning)"
|
|
1194
|
+
).option(
|
|
1195
|
+
"--full-scan",
|
|
1196
|
+
"Disable smart defaults for comprehensive analysis (slower)"
|
|
1197
|
+
).option(
|
|
1198
|
+
"--include <patterns>",
|
|
1199
|
+
"File patterns to include (comma-separated)"
|
|
1200
|
+
).option(
|
|
1201
|
+
"--exclude <patterns>",
|
|
1202
|
+
"File patterns to exclude (comma-separated)"
|
|
1203
|
+
).option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score (0-100)").option("--no-score", "Disable calculating AI Readiness Score").addHelpText("after", PATTERNS_HELP_TEXT).action(async (directory, options) => {
|
|
1204
|
+
await patternsAction(directory, options);
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1204
1207
|
async function patternsAction(directory, options) {
|
|
1205
1208
|
return await executeToolAction(directory, options, {
|
|
1206
1209
|
toolName: "pattern-detect",
|
|
@@ -1280,21 +1283,30 @@ async function patternsAction(directory, options) {
|
|
|
1280
1283
|
}
|
|
1281
1284
|
});
|
|
1282
1285
|
}
|
|
1283
|
-
var PATTERNS_HELP_TEXT = `
|
|
1284
|
-
EXAMPLES:
|
|
1285
|
-
$ aiready patterns # Default analysis
|
|
1286
|
-
$ aiready patterns --similarity 0.6 # Stricter matching
|
|
1287
|
-
$ aiready patterns --min-lines 10 # Larger patterns only
|
|
1288
|
-
`;
|
|
1289
1286
|
|
|
1290
1287
|
// src/commands/context.ts
|
|
1291
1288
|
import chalk10 from "chalk";
|
|
1292
1289
|
import { printTerminalHeader as printTerminalHeader2 } from "@aiready/core";
|
|
1290
|
+
function defineContextCommand(program2) {
|
|
1291
|
+
program2.command("context").description("Analyze context window costs and dependency fragmentation").argument("[directory]", "Directory to analyze", ".").option("--max-depth <number>", "Maximum acceptable import depth", "5").option(
|
|
1292
|
+
"--max-context <number>",
|
|
1293
|
+
"Maximum acceptable context budget (tokens)",
|
|
1294
|
+
"10000"
|
|
1295
|
+
).option(
|
|
1296
|
+
"--include <patterns>",
|
|
1297
|
+
"File patterns to include (comma-separated)"
|
|
1298
|
+
).option(
|
|
1299
|
+
"--exclude <patterns>",
|
|
1300
|
+
"File patterns to exclude (comma-separated)"
|
|
1301
|
+
).option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score (0-100)", true).option("--no-score", "Disable calculating AI Readiness Score").action(async (directory, options) => {
|
|
1302
|
+
await contextAction(directory, options);
|
|
1303
|
+
});
|
|
1304
|
+
}
|
|
1293
1305
|
async function contextAction(directory, options) {
|
|
1294
1306
|
return await executeToolAction(directory, options, {
|
|
1295
1307
|
toolName: "context-analyzer",
|
|
1296
1308
|
label: "Context analysis",
|
|
1297
|
-
emoji: "\u{
|
|
1309
|
+
emoji: "\u{1F9E9}",
|
|
1298
1310
|
defaults: {
|
|
1299
1311
|
maxDepth: 5,
|
|
1300
1312
|
maxContextBudget: 1e4,
|
|
@@ -1306,11 +1318,6 @@ async function contextAction(directory, options) {
|
|
|
1306
1318
|
maxDepth: opts.maxDepth ? parseInt(opts.maxDepth) : void 0,
|
|
1307
1319
|
maxContextBudget: opts.maxContext ? parseInt(opts.maxContext) : void 0
|
|
1308
1320
|
}),
|
|
1309
|
-
preAnalyze: async (resolvedDir, baseOptions) => {
|
|
1310
|
-
const { getSmartDefaults } = await import("@aiready/context-analyzer");
|
|
1311
|
-
const smartDefaults = await getSmartDefaults(resolvedDir, baseOptions);
|
|
1312
|
-
return { ...smartDefaults, ...baseOptions };
|
|
1313
|
-
},
|
|
1314
1321
|
importTool: async () => {
|
|
1315
1322
|
const { analyzeContext, generateSummary, calculateContextScore } = await import("@aiready/context-analyzer");
|
|
1316
1323
|
return {
|
|
@@ -1319,45 +1326,40 @@ async function contextAction(directory, options) {
|
|
|
1319
1326
|
calculateScore: calculateContextScore
|
|
1320
1327
|
};
|
|
1321
1328
|
},
|
|
1322
|
-
renderConsole: ({ summary, elapsedTime, score }) => {
|
|
1329
|
+
renderConsole: ({ results: _results, summary, elapsedTime, score }) => {
|
|
1323
1330
|
printTerminalHeader2("CONTEXT ANALYSIS SUMMARY");
|
|
1324
1331
|
console.log(
|
|
1325
|
-
chalk10.white(`\u{1F4C1}
|
|
1332
|
+
chalk10.white(`\u{1F4C1} Total files: ${chalk10.bold(summary.totalFiles)}`)
|
|
1326
1333
|
);
|
|
1327
1334
|
console.log(
|
|
1328
1335
|
chalk10.white(
|
|
1329
|
-
`\u{
|
|
1336
|
+
`\u{1F4B8} Total tokens (context budget): ${chalk10.bold(summary.totalTokens.toLocaleString())}`
|
|
1330
1337
|
)
|
|
1331
1338
|
);
|
|
1332
1339
|
console.log(
|
|
1333
|
-
chalk10.
|
|
1334
|
-
`\u{
|
|
1340
|
+
chalk10.cyan(
|
|
1341
|
+
`\u{1F4CA} Average context budget: ${chalk10.bold(summary.avgContextBudget.toFixed(0))} tokens`
|
|
1335
1342
|
)
|
|
1336
1343
|
);
|
|
1337
1344
|
console.log(
|
|
1338
|
-
chalk10.
|
|
1339
|
-
`)
|
|
1345
|
+
chalk10.gray(`\u23F1 Analysis time: ${chalk10.bold(elapsedTime + "s")}`)
|
|
1340
1346
|
);
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1347
|
+
if (summary.fragmentedModules.length > 0) {
|
|
1348
|
+
renderSubSection("Top Fragmented Modules");
|
|
1349
|
+
summary.fragmentedModules.slice(0, 5).forEach((mod) => {
|
|
1350
|
+
const scoreColor = mod.fragmentationScore > 0.7 ? chalk10.red : mod.fragmentationScore > 0.4 ? chalk10.yellow : chalk10.green;
|
|
1346
1351
|
console.log(
|
|
1347
|
-
`
|
|
1352
|
+
` ${scoreColor("\u25A0")} ${chalk10.white(mod.domain.padEnd(20))} ${chalk10.bold((mod.fragmentationScore * 100).toFixed(0) + "%")} fragmentation`
|
|
1348
1353
|
);
|
|
1349
1354
|
});
|
|
1350
1355
|
}
|
|
1351
|
-
if (summary.
|
|
1352
|
-
renderSubSection("
|
|
1353
|
-
summary.
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
);
|
|
1356
|
+
if (summary.topExpensiveFiles.length > 0) {
|
|
1357
|
+
renderSubSection("Top Context-Expensive Files");
|
|
1358
|
+
summary.topExpensiveFiles.slice(0, 5).forEach((item) => {
|
|
1359
|
+
const icon = item.severity === "critical" ? "\u{1F534}" : item.severity === "major" ? "\u{1F7E1}" : "\u{1F535}";
|
|
1360
|
+
const color = item.severity === "critical" ? chalk10.red : item.severity === "major" ? chalk10.yellow : chalk10.blue;
|
|
1357
1361
|
console.log(
|
|
1358
|
-
chalk10.dim(
|
|
1359
|
-
` Token cost: ${module.totalTokens.toLocaleString()}, Cohesion: ${(module.avgCohesion * 100).toFixed(0)}%`
|
|
1360
|
-
)
|
|
1362
|
+
` ${icon} ${color(item.severity.toUpperCase())}: ${chalk10.white(item.file.split("/").pop())} ${chalk10.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`
|
|
1361
1363
|
);
|
|
1362
1364
|
});
|
|
1363
1365
|
}
|
|
@@ -1368,21 +1370,35 @@ async function contextAction(directory, options) {
|
|
|
1368
1370
|
|
|
1369
1371
|
// src/commands/consistency.ts
|
|
1370
1372
|
import chalk11 from "chalk";
|
|
1371
|
-
import {
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1373
|
+
import { printTerminalHeader as printTerminalHeader3 } from "@aiready/core";
|
|
1374
|
+
function defineConsistencyCommand(program2) {
|
|
1375
|
+
program2.command("consistency").description("Check naming conventions and architectural consistency").argument("[directory]", "Directory to analyze", ".").option("--naming", "Check naming conventions (default: true)").option("--no-naming", "Skip naming analysis").option("--patterns", "Check code patterns (default: true)").option("--no-patterns", "Skip pattern analysis").option(
|
|
1376
|
+
"--min-severity <level>",
|
|
1377
|
+
"Minimum severity: info|minor|major|critical",
|
|
1378
|
+
"info"
|
|
1379
|
+
).option(
|
|
1380
|
+
"--include <patterns>",
|
|
1381
|
+
"File patterns to include (comma-separated)"
|
|
1382
|
+
).option(
|
|
1383
|
+
"--exclude <patterns>",
|
|
1384
|
+
"File patterns to exclude (comma-separated)"
|
|
1385
|
+
).option(
|
|
1386
|
+
"-o, --output <format>",
|
|
1387
|
+
"Output format: console, json, markdown",
|
|
1388
|
+
"console"
|
|
1389
|
+
).option("--output-file <path>", "Output file path (for json/markdown)").option("--score", "Calculate and display AI Readiness Score (0-100)", true).option("--no-score", "Disable calculating AI Readiness Score").action(async (directory, options) => {
|
|
1390
|
+
await consistencyAction(directory, options);
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1377
1393
|
async function consistencyAction(directory, options) {
|
|
1378
1394
|
return await executeToolAction(directory, options, {
|
|
1379
1395
|
toolName: "naming-consistency",
|
|
1380
1396
|
label: "Consistency analysis",
|
|
1381
|
-
emoji: "\u{
|
|
1397
|
+
emoji: "\u{1F4CF}",
|
|
1382
1398
|
defaults: {
|
|
1383
|
-
checkNaming:
|
|
1384
|
-
checkPatterns:
|
|
1385
|
-
minSeverity: "info",
|
|
1399
|
+
checkNaming: options.naming !== false,
|
|
1400
|
+
checkPatterns: options.patterns !== false,
|
|
1401
|
+
minSeverity: options.minSeverity || "info",
|
|
1386
1402
|
include: void 0,
|
|
1387
1403
|
exclude: void 0,
|
|
1388
1404
|
output: { format: "console", file: void 0 }
|
|
@@ -1393,119 +1409,68 @@ async function consistencyAction(directory, options) {
|
|
|
1393
1409
|
minSeverity: opts.minSeverity
|
|
1394
1410
|
}),
|
|
1395
1411
|
importTool: async () => {
|
|
1396
|
-
const { analyzeConsistency, calculateConsistencyScore } = await import("@aiready/consistency");
|
|
1412
|
+
const { analyzeConsistency, generateSummary, calculateConsistencyScore } = await import("@aiready/consistency");
|
|
1397
1413
|
return {
|
|
1398
|
-
analyze:
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
);
|
|
1405
|
-
}
|
|
1414
|
+
analyze: async (opts) => {
|
|
1415
|
+
const report = await analyzeConsistency(opts);
|
|
1416
|
+
return report;
|
|
1417
|
+
},
|
|
1418
|
+
generateSummary,
|
|
1419
|
+
calculateScore: calculateConsistencyScore
|
|
1406
1420
|
};
|
|
1407
1421
|
},
|
|
1408
|
-
renderConsole: ({ results, summary, elapsedTime, score
|
|
1409
|
-
|
|
1410
|
-
const { format: outputFormat, file: userOutputFile } = resolveOutputFormat2(options, finalOptions);
|
|
1411
|
-
if (outputFormat === "markdown") {
|
|
1412
|
-
const markdown = generateMarkdownReport(report, elapsedTime);
|
|
1413
|
-
const outputPath = resolveOutputPath2(
|
|
1414
|
-
userOutputFile,
|
|
1415
|
-
`aiready-report-${getReportTimestamp()}.md`,
|
|
1416
|
-
directory
|
|
1417
|
-
);
|
|
1418
|
-
writeFileSync3(outputPath, markdown);
|
|
1419
|
-
console.log(chalk11.green(`\u2705 Report saved to ${outputPath}`));
|
|
1420
|
-
return;
|
|
1421
|
-
}
|
|
1422
|
-
console.log(chalk11.bold("\n\u{1F4CA} Summary\n"));
|
|
1423
|
-
console.log(`Files Analyzed: ${chalk11.cyan(summary.filesAnalyzed)}`);
|
|
1424
|
-
console.log(`Total Issues: ${chalk11.yellow(summary.totalIssues)}`);
|
|
1425
|
-
console.log(` Naming: ${chalk11.yellow(summary.namingIssues)}`);
|
|
1426
|
-
console.log(` Patterns: ${chalk11.yellow(summary.patternIssues)}`);
|
|
1422
|
+
renderConsole: ({ results: report, summary, elapsedTime, score }) => {
|
|
1423
|
+
printTerminalHeader3("CONSISTENCY ANALYSIS SUMMARY");
|
|
1427
1424
|
console.log(
|
|
1428
|
-
|
|
1425
|
+
chalk11.white(`\u{1F4C1} Files analyzed: ${chalk11.bold(summary.filesAnalyzed)}`)
|
|
1429
1426
|
);
|
|
1430
|
-
console.log(
|
|
1431
|
-
`)
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
);
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
}
|
|
1462
|
-
console.log();
|
|
1463
|
-
shown++;
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
if (patternResults.length > 0) {
|
|
1468
|
-
console.log(chalk11.bold("\u{1F504} Pattern Issues\n"));
|
|
1469
|
-
let shown = 0;
|
|
1470
|
-
for (const patternFileResult of patternResults) {
|
|
1471
|
-
if (shown >= 5) break;
|
|
1472
|
-
for (const issue of patternFileResult.issues) {
|
|
1473
|
-
if (shown >= 5) break;
|
|
1474
|
-
const severityColor = issue.severity === "critical" ? chalk11.red : issue.severity === "major" ? chalk11.yellow : issue.severity === "minor" ? chalk11.blue : chalk11.gray;
|
|
1475
|
-
console.log(
|
|
1476
|
-
`${severityColor(issue.severity.toUpperCase())} ${chalk11.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1477
|
-
);
|
|
1478
|
-
console.log(` ${issue.message}`);
|
|
1479
|
-
if (issue.suggestion) {
|
|
1480
|
-
console.log(
|
|
1481
|
-
` ${chalk11.dim("\u2192")} ${chalk11.italic(issue.suggestion)}`
|
|
1482
|
-
);
|
|
1483
|
-
}
|
|
1484
|
-
console.log();
|
|
1485
|
-
shown++;
|
|
1486
|
-
}
|
|
1427
|
+
console.log(
|
|
1428
|
+
chalk11.white(`\u26A0 Total issues: ${chalk11.bold(summary.totalIssues)}`)
|
|
1429
|
+
);
|
|
1430
|
+
console.log(
|
|
1431
|
+
chalk11.gray(`\u23F1 Analysis time: ${chalk11.bold(elapsedTime + "s")}`)
|
|
1432
|
+
);
|
|
1433
|
+
if (summary.totalIssues > 0 && report.results) {
|
|
1434
|
+
renderSubSection("Issues Breakdown");
|
|
1435
|
+
const sortedIssues = [...report.results].flatMap(
|
|
1436
|
+
(file) => (file.issues || []).map((issue) => ({
|
|
1437
|
+
...issue,
|
|
1438
|
+
file: file.fileName
|
|
1439
|
+
}))
|
|
1440
|
+
).sort((a, b) => {
|
|
1441
|
+
const levels = {
|
|
1442
|
+
critical: 4,
|
|
1443
|
+
major: 3,
|
|
1444
|
+
minor: 2,
|
|
1445
|
+
info: 1
|
|
1446
|
+
};
|
|
1447
|
+
return (levels[b.severity] || 0) - (levels[a.severity] || 0);
|
|
1448
|
+
}).slice(0, 10);
|
|
1449
|
+
sortedIssues.forEach((issue) => {
|
|
1450
|
+
const icon = issue.severity === "critical" ? "\u{1F534}" : issue.severity === "major" ? "\u{1F7E1}" : "\u{1F535}";
|
|
1451
|
+
const color = issue.severity === "critical" ? chalk11.red : issue.severity === "major" ? chalk11.yellow : chalk11.blue;
|
|
1452
|
+
console.log(
|
|
1453
|
+
` ${icon} ${color(issue.severity.toUpperCase())}: ${chalk11.white(issue.file)}${issue.line ? `:${issue.line}` : ""}`
|
|
1454
|
+
);
|
|
1455
|
+
console.log(` ${issue.message}`);
|
|
1456
|
+
if (issue.suggestion) {
|
|
1457
|
+
console.log(chalk11.dim(` \u{1F4A1} ${issue.suggestion}`));
|
|
1487
1458
|
}
|
|
1488
|
-
}
|
|
1489
|
-
if (report.recommendations?.length > 0) {
|
|
1490
|
-
console.log(chalk11.bold("\u{1F4A1} Recommendations\n"));
|
|
1491
|
-
report.recommendations.forEach((rec, i) => {
|
|
1492
|
-
console.log(`${i + 1}. ${rec}`);
|
|
1493
|
-
});
|
|
1494
1459
|
console.log();
|
|
1495
|
-
}
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
console.log();
|
|
1460
|
+
});
|
|
1461
|
+
} else {
|
|
1462
|
+
console.log(
|
|
1463
|
+
chalk11.green("\n\u2728 Great! No consistency issues detected.\n")
|
|
1464
|
+
);
|
|
1501
1465
|
}
|
|
1466
|
+
renderToolScoreFooter(score);
|
|
1502
1467
|
}
|
|
1503
1468
|
});
|
|
1504
1469
|
}
|
|
1505
1470
|
|
|
1506
1471
|
// src/commands/visualize.ts
|
|
1507
1472
|
import chalk12 from "chalk";
|
|
1508
|
-
import { writeFileSync as
|
|
1473
|
+
import { writeFileSync as writeFileSync3, readFileSync as readFileSync3, existsSync as existsSync3, copyFileSync } from "fs";
|
|
1509
1474
|
import { resolve as resolvePath5 } from "path";
|
|
1510
1475
|
import { spawn } from "child_process";
|
|
1511
1476
|
import { handleCLIError as handleCLIError4 } from "@aiready/core";
|
|
@@ -1675,7 +1640,7 @@ Or specify a custom report:
|
|
|
1675
1640
|
const html = generateHTML(graph);
|
|
1676
1641
|
const defaultOutput = "visualization.html";
|
|
1677
1642
|
const outPath = resolvePath5(dirPath, options.output ?? defaultOutput);
|
|
1678
|
-
|
|
1643
|
+
writeFileSync3(outPath, html, "utf8");
|
|
1679
1644
|
console.log(chalk12.green(`\u2705 Visualization written to: ${outPath}`));
|
|
1680
1645
|
if (options.open || options.serve) {
|
|
1681
1646
|
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
@@ -1959,11 +1924,11 @@ EXAMPLES:
|
|
|
1959
1924
|
import chalk18 from "chalk";
|
|
1960
1925
|
import { resolve as resolvePath6 } from "path";
|
|
1961
1926
|
import { existsSync as existsSync4, readdirSync } from "fs";
|
|
1962
|
-
import { printTerminalHeader as
|
|
1927
|
+
import { printTerminalHeader as printTerminalHeader4 } from "@aiready/core";
|
|
1963
1928
|
async function remediateAction(directory, options) {
|
|
1964
1929
|
const resolvedDir = resolvePath6(process.cwd(), directory || ".");
|
|
1965
1930
|
const serverUrl = options.server || "https://platform.getaiready.dev";
|
|
1966
|
-
|
|
1931
|
+
printTerminalHeader4("AIREADY REMEDIATION SWARM");
|
|
1967
1932
|
console.log(chalk18.cyan("\u{1F916} Initializing local remediation agent..."));
|
|
1968
1933
|
let reportPath = options.report;
|
|
1969
1934
|
if (!reportPath) {
|
|
@@ -2081,42 +2046,15 @@ CONFIGURATION:
|
|
|
2081
2046
|
Config files (searched upward): aiready.json, .aiready.json, aiready.config.*
|
|
2082
2047
|
CLI options override config file settings
|
|
2083
2048
|
|
|
2084
|
-
Example aiready.json:
|
|
2085
|
-
{
|
|
2086
|
-
"scan": { "exclude": ["**/dist/**", "**/node_modules/**"] },
|
|
2087
|
-
"tools": {
|
|
2088
|
-
"pattern-detect": { "minSimilarity": 0.5 },
|
|
2089
|
-
"context-analyzer": { "maxContextBudget": 15000 }
|
|
2090
|
-
},
|
|
2091
|
-
"output": { "format": "json", "directory": ".aiready" }
|
|
2092
|
-
}
|
|
2093
|
-
|
|
2094
2049
|
VERSION: ${packageJson.version}
|
|
2095
2050
|
DOCUMENTATION: https://aiready.dev/docs/cli
|
|
2096
2051
|
GITHUB: https://github.com/caopengau/aiready-cli
|
|
2097
2052
|
LANDING: https://github.com/caopengau/aiready-landing`
|
|
2098
2053
|
);
|
|
2099
|
-
program
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
"Tools to run (comma-separated: patterns,context,consistency,doc-drift,deps-health,aiSignalClarity,grounding,testability,changeAmplification)"
|
|
2104
|
-
).option(
|
|
2105
|
-
"--profile <type>",
|
|
2106
|
-
"Scan profile to use (agentic, cost, logic, ui, security, onboarding)"
|
|
2107
|
-
).option(
|
|
2108
|
-
"--compare-to <path>",
|
|
2109
|
-
"Compare results against a previous AIReady report JSON"
|
|
2110
|
-
).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(
|
|
2111
|
-
"--ci",
|
|
2112
|
-
"CI mode: GitHub Actions annotations, no colors, fail on threshold"
|
|
2113
|
-
).option(
|
|
2114
|
-
"--fail-on <level>",
|
|
2115
|
-
"Fail on issues: critical, major, any",
|
|
2116
|
-
"critical"
|
|
2117
|
-
).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) => {
|
|
2118
|
-
await scanAction(directory, options);
|
|
2119
|
-
});
|
|
2054
|
+
defineScanCommand(program);
|
|
2055
|
+
definePatternsCommand(program);
|
|
2056
|
+
defineContextCommand(program);
|
|
2057
|
+
defineConsistencyCommand(program);
|
|
2120
2058
|
program.command("init").description("Generate a default configuration (aiready.json)").option("-f, --force", "Overwrite existing configuration file").option(
|
|
2121
2059
|
"--js",
|
|
2122
2060
|
"Generate configuration as a JavaScript file (aiready.config.js)"
|
|
@@ -2124,36 +2062,6 @@ program.command("init").description("Generate a default configuration (aiready.j
|
|
|
2124
2062
|
const format = options.js ? "js" : "json";
|
|
2125
2063
|
await initAction({ force: options.force, format, full: options.full });
|
|
2126
2064
|
});
|
|
2127
|
-
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(
|
|
2128
|
-
"--max-candidates <number>",
|
|
2129
|
-
"Maximum candidates per block (performance tuning)"
|
|
2130
|
-
).option(
|
|
2131
|
-
"--min-shared-tokens <number>",
|
|
2132
|
-
"Minimum shared tokens for candidates (performance tuning)"
|
|
2133
|
-
).option(
|
|
2134
|
-
"--full-scan",
|
|
2135
|
-
"Disable smart defaults for comprehensive analysis (slower)"
|
|
2136
|
-
).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) => {
|
|
2137
|
-
await patternsAction(directory, options);
|
|
2138
|
-
});
|
|
2139
|
-
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(
|
|
2140
|
-
"--max-context <number>",
|
|
2141
|
-
"Maximum acceptable context budget (tokens)",
|
|
2142
|
-
"10000"
|
|
2143
|
-
).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) => {
|
|
2144
|
-
await contextAction(directory, options);
|
|
2145
|
-
});
|
|
2146
|
-
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(
|
|
2147
|
-
"--min-severity <level>",
|
|
2148
|
-
"Minimum severity: info|minor|major|critical",
|
|
2149
|
-
"info"
|
|
2150
|
-
).option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option(
|
|
2151
|
-
"-o, --output <format>",
|
|
2152
|
-
"Output format: console, json, markdown",
|
|
2153
|
-
"console"
|
|
2154
|
-
).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) => {
|
|
2155
|
-
await consistencyAction(directory, options);
|
|
2156
|
-
});
|
|
2157
2065
|
program.command("visualise").description("Alias for visualize (British spelling)").argument("[directory]", "Directory to analyze", ".").option(
|
|
2158
2066
|
"--report <path>",
|
|
2159
2067
|
"Report path (auto-detects latest .aiready/aiready-report-*.json if not provided)"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiready/cli",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.3",
|
|
4
4
|
"description": "Assess and improve your codebase's AI-readiness. Get an AI Readiness Score (0-100) and detect semantic duplicates, context fragmentation, and naming inconsistencies.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -24,18 +24,18 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"chalk": "^5.3.0",
|
|
26
26
|
"commander": "^14.0.0",
|
|
27
|
-
"@aiready/agent-grounding": "0.14.
|
|
28
|
-
"@aiready/consistency": "0.21.
|
|
29
|
-
"@aiready/context-analyzer": "0.22.
|
|
30
|
-
"@aiready/
|
|
31
|
-
"@aiready/
|
|
32
|
-
"@aiready/doc-drift": "0.14.
|
|
33
|
-
"@aiready/
|
|
34
|
-
"@aiready/
|
|
35
|
-
"@aiready/pattern-detect": "0.17.
|
|
36
|
-
"@aiready/
|
|
37
|
-
"@aiready/testability": "0.7.
|
|
38
|
-
"@aiready/
|
|
27
|
+
"@aiready/agent-grounding": "0.14.3",
|
|
28
|
+
"@aiready/consistency": "0.21.3",
|
|
29
|
+
"@aiready/context-analyzer": "0.22.3",
|
|
30
|
+
"@aiready/deps": "0.14.3",
|
|
31
|
+
"@aiready/core": "0.24.3",
|
|
32
|
+
"@aiready/doc-drift": "0.14.3",
|
|
33
|
+
"@aiready/change-amplification": "0.14.3",
|
|
34
|
+
"@aiready/contract-enforcement": "0.2.3",
|
|
35
|
+
"@aiready/pattern-detect": "0.17.3",
|
|
36
|
+
"@aiready/ai-signal-clarity": "0.14.3",
|
|
37
|
+
"@aiready/testability": "0.7.3",
|
|
38
|
+
"@aiready/visualizer": "0.7.3"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^24.0.0",
|