@aiready/cli 0.7.16 → 0.7.18
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/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-test.log +5 -5
- package/dist/chunk-P3XAXCTK.mjs +110 -0
- package/dist/cli.js +124 -11
- package/dist/cli.mjs +135 -13
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.mjs +1 -1
- package/package.json +7 -7
- package/src/cli.ts +174 -4
- package/src/index.ts +4 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
> @aiready/cli@0.7.
|
|
3
|
+
> @aiready/cli@0.7.18 build /Users/pengcao/projects/aiready/packages/cli
|
|
4
4
|
> tsup src/index.ts src/cli.ts --format cjs,esm --dts
|
|
5
5
|
|
|
6
6
|
[34mCLI[39m Building entry: src/cli.ts, src/index.ts
|
|
@@ -9,16 +9,16 @@
|
|
|
9
9
|
[34mCLI[39m Target: es2020
|
|
10
10
|
[34mCJS[39m Build start
|
|
11
11
|
[34mESM[39m Build start
|
|
12
|
-
[32mCJS[39m [1mdist/
|
|
13
|
-
[32mCJS[39m [1mdist/
|
|
12
|
+
[32mCJS[39m [1mdist/cli.js [22m[32m35.44 KB[39m
|
|
13
|
+
[32mCJS[39m [1mdist/index.js [22m[32m4.60 KB[39m
|
|
14
14
|
[32mCJS[39m ⚡️ Build success in 13ms
|
|
15
|
-
[32mESM[39m [1mdist/cli.mjs [22m[32m22.00 KB[39m
|
|
16
15
|
[32mESM[39m [1mdist/index.mjs [22m[32m138.00 B[39m
|
|
17
|
-
[32mESM[39m [1mdist/
|
|
16
|
+
[32mESM[39m [1mdist/cli.mjs [22m[32m28.57 KB[39m
|
|
17
|
+
[32mESM[39m [1mdist/chunk-P3XAXCTK.mjs [22m[32m3.46 KB[39m
|
|
18
18
|
[32mESM[39m ⚡️ Build success in 13ms
|
|
19
19
|
DTS Build start
|
|
20
|
-
DTS ⚡️ Build success in
|
|
20
|
+
DTS ⚡️ Build success in 501ms
|
|
21
21
|
DTS dist/cli.d.ts 20.00 B
|
|
22
|
-
DTS dist/index.d.ts
|
|
22
|
+
DTS dist/index.d.ts 1.06 KB
|
|
23
23
|
DTS dist/cli.d.mts 20.00 B
|
|
24
|
-
DTS dist/index.d.mts
|
|
24
|
+
DTS dist/index.d.mts 1.06 KB
|
package/.turbo/turbo-test.log
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
> @aiready/cli@0.7.
|
|
3
|
+
> @aiready/cli@0.7.18 test /Users/pengcao/projects/aiready/packages/cli
|
|
4
4
|
> vitest run
|
|
5
5
|
|
|
6
6
|
[?25l
|
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
|
|
12
12
|
[2m Test Files [22m[1m[32m0 passed[39m[22m[90m (1)[39m
|
|
13
13
|
[2m Tests [22m[1m[32m0 passed[39m[22m[90m (0)[39m
|
|
14
|
-
[2m Start at [
|
|
14
|
+
[2m Start at [22m23:10:25
|
|
15
15
|
[2m Duration [22m0ms
|
|
16
16
|
[?2026l[?2026h[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K
|
|
17
17
|
[1m[33m ❯ [39m[22msrc/__tests__/cli.test.ts[2m 0/3[22m
|
|
18
18
|
|
|
19
19
|
[2m Test Files [22m[1m[32m0 passed[39m[22m[90m (1)[39m
|
|
20
20
|
[2m Tests [22m[1m[32m0 passed[39m[22m[90m (3)[39m
|
|
21
|
-
[2m Start at [
|
|
21
|
+
[2m Start at [22m23:10:25
|
|
22
22
|
[2m Duration [22m201ms
|
|
23
23
|
[?2026l[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K[1A[K [32m✓[39m src/__tests__/cli.test.ts [2m([22m[2m3 tests[22m[2m)[22m[32m 2[2mms[22m[39m
|
|
24
24
|
[32m✓[39m CLI Unified Analysis [2m(3)[22m
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
[2m Test Files [22m [1m[32m1 passed[39m[22m[90m (1)[39m
|
|
30
30
|
[2m Tests [22m [1m[32m3 passed[39m[22m[90m (3)[39m
|
|
31
|
-
[2m Start at [22m
|
|
32
|
-
[2m Duration [22m
|
|
31
|
+
[2m Start at [22m 23:10:25
|
|
32
|
+
[2m Duration [22m 283ms[2m (transform 53ms, setup 0ms, import 211ms, tests 2ms, environment 0ms)[22m
|
|
33
33
|
|
|
34
34
|
[?25h
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { analyzePatterns } from "@aiready/pattern-detect";
|
|
3
|
+
import { analyzeContext } from "@aiready/context-analyzer";
|
|
4
|
+
import { analyzeConsistency } from "@aiready/consistency";
|
|
5
|
+
var severityOrder = {
|
|
6
|
+
critical: 4,
|
|
7
|
+
major: 3,
|
|
8
|
+
minor: 2,
|
|
9
|
+
info: 1
|
|
10
|
+
};
|
|
11
|
+
function sortBySeverity(results) {
|
|
12
|
+
return results.map((file) => {
|
|
13
|
+
const sortedIssues = [...file.issues].sort((a, b) => {
|
|
14
|
+
const severityDiff = (severityOrder[b.severity] || 0) - (severityOrder[a.severity] || 0);
|
|
15
|
+
if (severityDiff !== 0) return severityDiff;
|
|
16
|
+
return (a.location?.line || 0) - (b.location?.line || 0);
|
|
17
|
+
});
|
|
18
|
+
return { ...file, issues: sortedIssues };
|
|
19
|
+
}).sort((a, b) => {
|
|
20
|
+
const aMaxSeverity = Math.max(...a.issues.map((i) => severityOrder[i.severity] || 0), 0);
|
|
21
|
+
const bMaxSeverity = Math.max(...b.issues.map((i) => severityOrder[i.severity] || 0), 0);
|
|
22
|
+
if (aMaxSeverity !== bMaxSeverity) {
|
|
23
|
+
return bMaxSeverity - aMaxSeverity;
|
|
24
|
+
}
|
|
25
|
+
if (a.issues.length !== b.issues.length) {
|
|
26
|
+
return b.issues.length - a.issues.length;
|
|
27
|
+
}
|
|
28
|
+
return a.fileName.localeCompare(b.fileName);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async function analyzeUnified(options) {
|
|
32
|
+
const startTime = Date.now();
|
|
33
|
+
const tools = options.tools || ["patterns", "context", "consistency"];
|
|
34
|
+
const result = {
|
|
35
|
+
summary: {
|
|
36
|
+
totalIssues: 0,
|
|
37
|
+
toolsRun: tools,
|
|
38
|
+
executionTime: 0
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
if (tools.includes("patterns")) {
|
|
42
|
+
const patternResult = await analyzePatterns(options);
|
|
43
|
+
result.patterns = sortBySeverity(patternResult.results);
|
|
44
|
+
result.duplicates = patternResult.duplicates;
|
|
45
|
+
result.summary.totalIssues += patternResult.results.reduce(
|
|
46
|
+
(sum, file) => sum + file.issues.length,
|
|
47
|
+
0
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
if (tools.includes("context")) {
|
|
51
|
+
const contextResults = await analyzeContext(options);
|
|
52
|
+
result.context = contextResults.sort((a, b) => {
|
|
53
|
+
const severityDiff = (severityOrder[b.severity] || 0) - (severityOrder[a.severity] || 0);
|
|
54
|
+
if (severityDiff !== 0) return severityDiff;
|
|
55
|
+
if (a.tokenCost !== b.tokenCost) return b.tokenCost - a.tokenCost;
|
|
56
|
+
return b.fragmentationScore - a.fragmentationScore;
|
|
57
|
+
});
|
|
58
|
+
result.summary.totalIssues += result.context?.length || 0;
|
|
59
|
+
}
|
|
60
|
+
if (tools.includes("consistency")) {
|
|
61
|
+
const report = await analyzeConsistency({
|
|
62
|
+
rootDir: options.rootDir,
|
|
63
|
+
include: options.include,
|
|
64
|
+
exclude: options.exclude,
|
|
65
|
+
checkNaming: true,
|
|
66
|
+
checkPatterns: true,
|
|
67
|
+
minSeverity: "info"
|
|
68
|
+
});
|
|
69
|
+
if (report.results) {
|
|
70
|
+
report.results = sortBySeverity(report.results);
|
|
71
|
+
}
|
|
72
|
+
result.consistency = report;
|
|
73
|
+
result.summary.totalIssues += report.summary.totalIssues;
|
|
74
|
+
}
|
|
75
|
+
result.summary.executionTime = Date.now() - startTime;
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
function generateUnifiedSummary(result) {
|
|
79
|
+
const { summary } = result;
|
|
80
|
+
let output = `\u{1F680} AIReady Analysis Complete
|
|
81
|
+
|
|
82
|
+
`;
|
|
83
|
+
output += `\u{1F4CA} Summary:
|
|
84
|
+
`;
|
|
85
|
+
output += ` Tools run: ${summary.toolsRun.join(", ")}
|
|
86
|
+
`;
|
|
87
|
+
output += ` Total issues found: ${summary.totalIssues}
|
|
88
|
+
`;
|
|
89
|
+
output += ` Execution time: ${(summary.executionTime / 1e3).toFixed(2)}s
|
|
90
|
+
|
|
91
|
+
`;
|
|
92
|
+
if (result.patterns?.length) {
|
|
93
|
+
output += `\u{1F50D} Pattern Analysis: ${result.patterns.length} issues
|
|
94
|
+
`;
|
|
95
|
+
}
|
|
96
|
+
if (result.context?.length) {
|
|
97
|
+
output += `\u{1F9E0} Context Analysis: ${result.context.length} issues
|
|
98
|
+
`;
|
|
99
|
+
}
|
|
100
|
+
if (result.consistency) {
|
|
101
|
+
output += `\u{1F3F7}\uFE0F Consistency Analysis: ${result.consistency.summary.totalIssues} issues
|
|
102
|
+
`;
|
|
103
|
+
}
|
|
104
|
+
return output;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export {
|
|
108
|
+
analyzeUnified,
|
|
109
|
+
generateUnifiedSummary
|
|
110
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -69,6 +69,7 @@ async function analyzeUnified(options) {
|
|
|
69
69
|
if (tools.includes("patterns")) {
|
|
70
70
|
const patternResult = await (0, import_pattern_detect.analyzePatterns)(options);
|
|
71
71
|
result.patterns = sortBySeverity(patternResult.results);
|
|
72
|
+
result.duplicates = patternResult.duplicates;
|
|
72
73
|
result.summary.totalIssues += patternResult.results.reduce(
|
|
73
74
|
(sum, file) => sum + file.issues.length,
|
|
74
75
|
0
|
|
@@ -140,7 +141,7 @@ var import_fs2 = require("fs");
|
|
|
140
141
|
var packageJson = JSON.parse((0, import_fs2.readFileSync)((0, import_path.join)(__dirname, "../package.json"), "utf8"));
|
|
141
142
|
var program = new import_commander.Command();
|
|
142
143
|
program.name("aiready").description("AIReady - Unified AI-readiness analysis tools").version(packageJson.version).addHelpText("after", "\nCONFIGURATION:\n Supports config files: aiready.json, aiready.config.json, .aiready.json, .aireadyrc.json, aiready.config.js, .aireadyrc.js\n CLI options override config file settings");
|
|
143
|
-
program.command("scan").description("Run unified analysis on a codebase").argument("[directory]", "Directory to analyze", ".").option("-t, --tools <tools>", "Tools to run (comma-separated: patterns,context,consistency)", "patterns,context,consistency").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)").action(async (directory, options) => {
|
|
144
|
+
program.command("scan").description("Run unified analysis on a codebase").argument("[directory]", "Directory to analyze", ".").option("-t, --tools <tools>", "Tools to run (comma-separated: patterns,context,consistency)", "patterns,context,consistency").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("--weights <weights>", 'Override tool weights for scoring (e.g., "patterns:50,context:30,consistency:20")').option("--threshold <score>", "Minimum passing score for CI/CD (exits with code 1 if below)").action(async (directory, options) => {
|
|
144
145
|
console.log(import_chalk.default.blue("\u{1F680} Starting AIReady unified analysis...\n"));
|
|
145
146
|
const startTime = Date.now();
|
|
146
147
|
try {
|
|
@@ -166,6 +167,47 @@ program.command("scan").description("Run unified analysis on a codebase").argume
|
|
|
166
167
|
}
|
|
167
168
|
const results = await analyzeUnified(finalOptions);
|
|
168
169
|
const elapsedTime = (0, import_core.getElapsedTime)(startTime);
|
|
170
|
+
let scoringResult;
|
|
171
|
+
if (options.score || finalOptions.scoring?.showBreakdown) {
|
|
172
|
+
const toolScores = /* @__PURE__ */ new Map();
|
|
173
|
+
if (results.patterns && baseOptions.tools.includes("patterns")) {
|
|
174
|
+
const { calculatePatternScore } = await import("@aiready/pattern-detect");
|
|
175
|
+
const duplicates = results.duplicates || [];
|
|
176
|
+
const score = calculatePatternScore(duplicates, results.patterns.length);
|
|
177
|
+
toolScores.set("pattern-detect", score);
|
|
178
|
+
}
|
|
179
|
+
if (results.context && baseOptions.tools.includes("context")) {
|
|
180
|
+
const { calculateContextScore } = await import("@aiready/context-analyzer");
|
|
181
|
+
const summary = {
|
|
182
|
+
avgContextBudget: results.context.reduce((sum, r) => sum + r.contextBudget, 0) / results.context.length,
|
|
183
|
+
maxContextBudget: Math.max(...results.context.map((r) => r.contextBudget)),
|
|
184
|
+
avgImportDepth: results.context.reduce((sum, r) => sum + r.importDepth, 0) / results.context.length,
|
|
185
|
+
maxImportDepth: Math.max(...results.context.map((r) => r.importDepth)),
|
|
186
|
+
avgFragmentation: results.context.reduce((sum, r) => sum + r.fragmentationScore, 0) / results.context.length,
|
|
187
|
+
criticalIssues: results.context.filter((r) => r.severity === "critical").length,
|
|
188
|
+
majorIssues: results.context.filter((r) => r.severity === "major").length
|
|
189
|
+
};
|
|
190
|
+
const score = calculateContextScore(summary);
|
|
191
|
+
toolScores.set("context-analyzer", score);
|
|
192
|
+
}
|
|
193
|
+
if (results.consistency && baseOptions.tools.includes("consistency")) {
|
|
194
|
+
const { calculateConsistencyScore } = await import("@aiready/consistency");
|
|
195
|
+
const issues = results.consistency.results?.flatMap((r) => r.issues) || [];
|
|
196
|
+
const score = calculateConsistencyScore(issues, results.consistency.summary.filesAnalyzed);
|
|
197
|
+
toolScores.set("consistency", score);
|
|
198
|
+
}
|
|
199
|
+
const cliWeights = options.weights ? (0, import_core.parseWeightString)(options.weights) : void 0;
|
|
200
|
+
scoringResult = (0, import_core.calculateOverallScore)(toolScores, finalOptions, cliWeights);
|
|
201
|
+
if (options.threshold) {
|
|
202
|
+
const threshold = parseFloat(options.threshold);
|
|
203
|
+
if (scoringResult.overall < threshold) {
|
|
204
|
+
console.error(import_chalk.default.red(`
|
|
205
|
+
\u274C Score ${scoringResult.overall} is below threshold ${threshold}
|
|
206
|
+
`));
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
169
211
|
const outputFormat = options.output || finalOptions.output?.format || "console";
|
|
170
212
|
const userOutputFile = options.outputFile || finalOptions.output?.file;
|
|
171
213
|
if (outputFormat === "json") {
|
|
@@ -174,7 +216,8 @@ program.command("scan").description("Run unified analysis on a codebase").argume
|
|
|
174
216
|
summary: {
|
|
175
217
|
...results.summary,
|
|
176
218
|
executionTime: parseFloat(elapsedTime)
|
|
177
|
-
}
|
|
219
|
+
},
|
|
220
|
+
...scoringResult && { scoring: scoringResult }
|
|
178
221
|
};
|
|
179
222
|
const outputPath = (0, import_core.resolveOutputPath)(
|
|
180
223
|
userOutputFile,
|
|
@@ -184,12 +227,47 @@ program.command("scan").description("Run unified analysis on a codebase").argume
|
|
|
184
227
|
(0, import_core.handleJSONOutput)(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
|
|
185
228
|
} else {
|
|
186
229
|
console.log(generateUnifiedSummary(results));
|
|
230
|
+
if (scoringResult) {
|
|
231
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
232
|
+
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
233
|
+
const divider = "\u2501".repeat(dividerWidth);
|
|
234
|
+
console.log(import_chalk.default.cyan("\n" + divider));
|
|
235
|
+
console.log(import_chalk.default.bold.white(" AI READINESS SCORE"));
|
|
236
|
+
console.log(import_chalk.default.cyan(divider) + "\n");
|
|
237
|
+
const { emoji, color } = (0, import_core.getRatingDisplay)(scoringResult.rating);
|
|
238
|
+
const scoreColor = color === "green" ? import_chalk.default.green : color === "blue" ? import_chalk.default.blue : color === "yellow" ? import_chalk.default.yellow : import_chalk.default.red;
|
|
239
|
+
console.log(` ${emoji} Overall Score: ${scoreColor.bold(scoringResult.overall + "/100")} (${import_chalk.default.bold(scoringResult.rating)})`);
|
|
240
|
+
console.log(` ${import_chalk.default.dim("Timestamp:")} ${new Date(scoringResult.timestamp).toLocaleString()}
|
|
241
|
+
`);
|
|
242
|
+
if (scoringResult.breakdown.length > 0) {
|
|
243
|
+
console.log(import_chalk.default.bold(" Component Scores:\n"));
|
|
244
|
+
scoringResult.breakdown.forEach((tool) => {
|
|
245
|
+
const toolEmoji = tool.toolName === "pattern-detect" ? "\u{1F50D}" : tool.toolName === "context-analyzer" ? "\u{1F9E0}" : "\u{1F3F7}\uFE0F";
|
|
246
|
+
const weight = scoringResult.calculation.weights[tool.toolName];
|
|
247
|
+
console.log(` ${toolEmoji} ${import_chalk.default.white(tool.toolName.padEnd(20))} ${scoreColor(tool.score + "/100")} ${import_chalk.default.dim(`(weight: ${weight})`)}`);
|
|
248
|
+
});
|
|
249
|
+
console.log();
|
|
250
|
+
}
|
|
251
|
+
console.log(import_chalk.default.dim(` Weighted Formula: ${scoringResult.calculation.formula}`));
|
|
252
|
+
console.log(import_chalk.default.dim(` Normalized Score: ${scoringResult.calculation.normalized}
|
|
253
|
+
`));
|
|
254
|
+
const allRecommendations = scoringResult.breakdown.flatMap((tool) => tool.recommendations || []).sort((a, b) => b.estimatedImpact - a.estimatedImpact).slice(0, 5);
|
|
255
|
+
if (allRecommendations.length > 0) {
|
|
256
|
+
console.log(import_chalk.default.bold(" Top Recommendations:\n"));
|
|
257
|
+
allRecommendations.forEach((rec, i) => {
|
|
258
|
+
const priorityIcon = rec.priority === "high" ? "\u{1F534}" : rec.priority === "medium" ? "\u{1F7E1}" : "\u{1F535}";
|
|
259
|
+
console.log(` ${i + 1}. ${priorityIcon} ${rec.action}`);
|
|
260
|
+
console.log(` ${import_chalk.default.dim(`Impact: +${rec.estimatedImpact} points`)}
|
|
261
|
+
`);
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
187
265
|
}
|
|
188
266
|
} catch (error) {
|
|
189
267
|
(0, import_core.handleCLIError)(error, "Analysis");
|
|
190
268
|
}
|
|
191
269
|
});
|
|
192
|
-
program.command("patterns").description("Run pattern detection analysis").argument("[directory]", "Directory to analyze", ".").option("-s, --similarity <number>", "Minimum similarity score (0-1)", "0.40").option("-l, --min-lines <number>", "Minimum lines to consider", "5").option("--max-candidates <number>", "Maximum candidates per block (performance tuning)").option("--min-shared-tokens <number>", "Minimum shared tokens for candidates (performance tuning)").option("--full-scan", "Disable smart defaults for comprehensive analysis (slower)").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").action(async (directory, options) => {
|
|
270
|
+
program.command("patterns").description("Run pattern detection analysis").argument("[directory]", "Directory to analyze", ".").option("-s, --similarity <number>", "Minimum similarity score (0-1)", "0.40").option("-l, --min-lines <number>", "Minimum lines to consider", "5").option("--max-candidates <number>", "Maximum candidates per block (performance tuning)").option("--min-shared-tokens <number>", "Minimum shared tokens for candidates (performance tuning)").option("--full-scan", "Disable smart defaults for comprehensive analysis (slower)").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score for patterns (0-100)").action(async (directory, options) => {
|
|
193
271
|
console.log(import_chalk.default.blue("\u{1F50D} Analyzing patterns...\n"));
|
|
194
272
|
const startTime = Date.now();
|
|
195
273
|
try {
|
|
@@ -221,16 +299,21 @@ program.command("patterns").description("Run pattern detection analysis").argume
|
|
|
221
299
|
cliOptions.minSharedTokens = parseInt(options.minSharedTokens);
|
|
222
300
|
}
|
|
223
301
|
const finalOptions = await (0, import_core.loadMergedConfig)(directory, defaults, cliOptions);
|
|
224
|
-
const { analyzePatterns: analyzePatterns2, generateSummary } = await import("@aiready/pattern-detect");
|
|
302
|
+
const { analyzePatterns: analyzePatterns2, generateSummary, calculatePatternScore } = await import("@aiready/pattern-detect");
|
|
225
303
|
const { results, duplicates } = await analyzePatterns2(finalOptions);
|
|
226
304
|
const elapsedTime = (0, import_core.getElapsedTime)(startTime);
|
|
227
305
|
const summary = generateSummary(results);
|
|
306
|
+
let patternScore;
|
|
307
|
+
if (options.score) {
|
|
308
|
+
patternScore = calculatePatternScore(duplicates, results.length);
|
|
309
|
+
}
|
|
228
310
|
const outputFormat = options.output || finalOptions.output?.format || "console";
|
|
229
311
|
const userOutputFile = options.outputFile || finalOptions.output?.file;
|
|
230
312
|
if (outputFormat === "json") {
|
|
231
313
|
const outputData = {
|
|
232
314
|
results,
|
|
233
|
-
summary: { ...summary, executionTime: parseFloat(elapsedTime) }
|
|
315
|
+
summary: { ...summary, executionTime: parseFloat(elapsedTime) },
|
|
316
|
+
...patternScore && { scoring: patternScore }
|
|
234
317
|
};
|
|
235
318
|
const outputPath = (0, import_core.resolveOutputPath)(
|
|
236
319
|
userOutputFile,
|
|
@@ -276,12 +359,19 @@ program.command("patterns").description("Run pattern detection analysis").argume
|
|
|
276
359
|
} else {
|
|
277
360
|
console.log(import_chalk.default.green("\n\u2728 Great! No duplicate patterns detected.\n"));
|
|
278
361
|
}
|
|
362
|
+
if (patternScore) {
|
|
363
|
+
console.log(import_chalk.default.cyan(divider));
|
|
364
|
+
console.log(import_chalk.default.bold.white(" AI READINESS SCORE (Patterns)"));
|
|
365
|
+
console.log(import_chalk.default.cyan(divider) + "\n");
|
|
366
|
+
console.log((0, import_core.formatToolScore)(patternScore));
|
|
367
|
+
console.log();
|
|
368
|
+
}
|
|
279
369
|
}
|
|
280
370
|
} catch (error) {
|
|
281
371
|
(0, import_core.handleCLIError)(error, "Pattern analysis");
|
|
282
372
|
}
|
|
283
373
|
});
|
|
284
|
-
program.command("context").description("Run context window cost analysis").argument("[directory]", "Directory to analyze", ".").option("--max-depth <number>", "Maximum acceptable import depth", "5").option("--max-context <number>", "Maximum acceptable context budget (tokens)", "10000").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)").action(async (directory, options) => {
|
|
374
|
+
program.command("context").description("Run context window cost analysis").argument("[directory]", "Directory to analyze", ".").option("--max-depth <number>", "Maximum acceptable import depth", "5").option("--max-context <number>", "Maximum acceptable context budget (tokens)", "10000").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score for context (0-100)").action(async (directory, options) => {
|
|
285
375
|
console.log(import_chalk.default.blue("\u{1F9E0} Analyzing context costs...\n"));
|
|
286
376
|
const startTime = Date.now();
|
|
287
377
|
try {
|
|
@@ -312,16 +402,21 @@ program.command("context").description("Run context window cost analysis").argum
|
|
|
312
402
|
console.log(` Max fragmentation: ${(finalOptions.maxFragmentation * 100).toFixed(1)}%`);
|
|
313
403
|
console.log(` Analysis focus: ${finalOptions.focus}`);
|
|
314
404
|
console.log("");
|
|
315
|
-
const { analyzeContext: analyzeContext2, generateSummary } = await import("@aiready/context-analyzer");
|
|
405
|
+
const { analyzeContext: analyzeContext2, generateSummary, calculateContextScore } = await import("@aiready/context-analyzer");
|
|
316
406
|
const results = await analyzeContext2(finalOptions);
|
|
317
407
|
const elapsedTime = (0, import_core.getElapsedTime)(startTime);
|
|
318
408
|
const summary = generateSummary(results);
|
|
409
|
+
let contextScore;
|
|
410
|
+
if (options.score) {
|
|
411
|
+
contextScore = calculateContextScore(summary);
|
|
412
|
+
}
|
|
319
413
|
const outputFormat = options.output || finalOptions.output?.format || "console";
|
|
320
414
|
const userOutputFile = options.outputFile || finalOptions.output?.file;
|
|
321
415
|
if (outputFormat === "json") {
|
|
322
416
|
const outputData = {
|
|
323
417
|
results,
|
|
324
|
-
summary: { ...summary, executionTime: parseFloat(elapsedTime) }
|
|
418
|
+
summary: { ...summary, executionTime: parseFloat(elapsedTime) },
|
|
419
|
+
...contextScore && { scoring: contextScore }
|
|
325
420
|
};
|
|
326
421
|
const outputPath = (0, import_core.resolveOutputPath)(
|
|
327
422
|
userOutputFile,
|
|
@@ -401,12 +496,19 @@ program.command("context").description("Run context window cost analysis").argum
|
|
|
401
496
|
});
|
|
402
497
|
console.log();
|
|
403
498
|
}
|
|
499
|
+
if (contextScore) {
|
|
500
|
+
console.log(import_chalk.default.cyan(divider));
|
|
501
|
+
console.log(import_chalk.default.bold.white(" AI READINESS SCORE (Context)"));
|
|
502
|
+
console.log(import_chalk.default.cyan(divider) + "\n");
|
|
503
|
+
console.log((0, import_core.formatToolScore)(contextScore));
|
|
504
|
+
console.log();
|
|
505
|
+
}
|
|
404
506
|
}
|
|
405
507
|
} catch (error) {
|
|
406
508
|
(0, import_core.handleCLIError)(error, "Context analysis");
|
|
407
509
|
}
|
|
408
510
|
});
|
|
409
|
-
program.command("consistency").description("Check naming, patterns, and architecture 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("--min-severity <level>", "Minimum severity: info|minor|major|critical", "info").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, markdown", "console").option("--output-file <path>", "Output file path (for json/markdown)").action(async (directory, options) => {
|
|
511
|
+
program.command("consistency").description("Check naming, patterns, and architecture 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("--min-severity <level>", "Minimum severity: info|minor|major|critical", "info").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, markdown", "console").option("--output-file <path>", "Output file path (for json/markdown)").option("--score", "Calculate and display AI Readiness Score for consistency (0-100)").action(async (directory, options) => {
|
|
410
512
|
console.log(import_chalk.default.blue("\u{1F50D} Analyzing consistency...\n"));
|
|
411
513
|
const startTime = Date.now();
|
|
412
514
|
try {
|
|
@@ -428,9 +530,14 @@ program.command("consistency").description("Check naming, patterns, and architec
|
|
|
428
530
|
include: options.include?.split(","),
|
|
429
531
|
exclude: options.exclude?.split(",")
|
|
430
532
|
});
|
|
431
|
-
const { analyzeConsistency: analyzeConsistency2 } = await import("@aiready/consistency");
|
|
533
|
+
const { analyzeConsistency: analyzeConsistency2, calculateConsistencyScore } = await import("@aiready/consistency");
|
|
432
534
|
const report = await analyzeConsistency2(finalOptions);
|
|
433
535
|
const elapsedTime = (0, import_core.getElapsedTime)(startTime);
|
|
536
|
+
let consistencyScore;
|
|
537
|
+
if (options.score) {
|
|
538
|
+
const issues = report.results?.flatMap((r) => r.issues) || [];
|
|
539
|
+
consistencyScore = calculateConsistencyScore(issues, report.summary.filesAnalyzed);
|
|
540
|
+
}
|
|
434
541
|
const outputFormat = options.output || finalOptions.output?.format || "console";
|
|
435
542
|
const userOutputFile = options.outputFile || finalOptions.output?.file;
|
|
436
543
|
if (outputFormat === "json") {
|
|
@@ -439,7 +546,8 @@ program.command("consistency").description("Check naming, patterns, and architec
|
|
|
439
546
|
summary: {
|
|
440
547
|
...report.summary,
|
|
441
548
|
executionTime: parseFloat(elapsedTime)
|
|
442
|
-
}
|
|
549
|
+
},
|
|
550
|
+
...consistencyScore && { scoring: consistencyScore }
|
|
443
551
|
};
|
|
444
552
|
const outputPath = (0, import_core.resolveOutputPath)(
|
|
445
553
|
userOutputFile,
|
|
@@ -528,6 +636,11 @@ program.command("consistency").description("Check naming, patterns, and architec
|
|
|
528
636
|
console.log();
|
|
529
637
|
}
|
|
530
638
|
}
|
|
639
|
+
if (consistencyScore) {
|
|
640
|
+
console.log(import_chalk.default.bold("\n\u{1F4CA} AI Readiness Score (Consistency)\n"));
|
|
641
|
+
console.log((0, import_core.formatToolScore)(consistencyScore));
|
|
642
|
+
console.log();
|
|
643
|
+
}
|
|
531
644
|
}
|
|
532
645
|
} catch (error) {
|
|
533
646
|
(0, import_core.handleCLIError)(error, "Consistency analysis");
|
package/dist/cli.mjs
CHANGED
|
@@ -2,19 +2,29 @@
|
|
|
2
2
|
import {
|
|
3
3
|
analyzeUnified,
|
|
4
4
|
generateUnifiedSummary
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-P3XAXCTK.mjs";
|
|
6
6
|
|
|
7
7
|
// src/cli.ts
|
|
8
8
|
import { Command } from "commander";
|
|
9
9
|
import chalk from "chalk";
|
|
10
10
|
import { writeFileSync } from "fs";
|
|
11
11
|
import { join } from "path";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
loadMergedConfig,
|
|
14
|
+
handleJSONOutput,
|
|
15
|
+
handleCLIError,
|
|
16
|
+
getElapsedTime,
|
|
17
|
+
resolveOutputPath,
|
|
18
|
+
calculateOverallScore,
|
|
19
|
+
formatToolScore,
|
|
20
|
+
getRatingDisplay,
|
|
21
|
+
parseWeightString
|
|
22
|
+
} from "@aiready/core";
|
|
13
23
|
import { readFileSync } from "fs";
|
|
14
24
|
var packageJson = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf8"));
|
|
15
25
|
var program = new Command();
|
|
16
26
|
program.name("aiready").description("AIReady - Unified AI-readiness analysis tools").version(packageJson.version).addHelpText("after", "\nCONFIGURATION:\n Supports config files: aiready.json, aiready.config.json, .aiready.json, .aireadyrc.json, aiready.config.js, .aireadyrc.js\n CLI options override config file settings");
|
|
17
|
-
program.command("scan").description("Run unified analysis on a codebase").argument("[directory]", "Directory to analyze", ".").option("-t, --tools <tools>", "Tools to run (comma-separated: patterns,context,consistency)", "patterns,context,consistency").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)").action(async (directory, options) => {
|
|
27
|
+
program.command("scan").description("Run unified analysis on a codebase").argument("[directory]", "Directory to analyze", ".").option("-t, --tools <tools>", "Tools to run (comma-separated: patterns,context,consistency)", "patterns,context,consistency").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("--weights <weights>", 'Override tool weights for scoring (e.g., "patterns:50,context:30,consistency:20")').option("--threshold <score>", "Minimum passing score for CI/CD (exits with code 1 if below)").action(async (directory, options) => {
|
|
18
28
|
console.log(chalk.blue("\u{1F680} Starting AIReady unified analysis...\n"));
|
|
19
29
|
const startTime = Date.now();
|
|
20
30
|
try {
|
|
@@ -40,6 +50,47 @@ program.command("scan").description("Run unified analysis on a codebase").argume
|
|
|
40
50
|
}
|
|
41
51
|
const results = await analyzeUnified(finalOptions);
|
|
42
52
|
const elapsedTime = getElapsedTime(startTime);
|
|
53
|
+
let scoringResult;
|
|
54
|
+
if (options.score || finalOptions.scoring?.showBreakdown) {
|
|
55
|
+
const toolScores = /* @__PURE__ */ new Map();
|
|
56
|
+
if (results.patterns && baseOptions.tools.includes("patterns")) {
|
|
57
|
+
const { calculatePatternScore } = await import("@aiready/pattern-detect");
|
|
58
|
+
const duplicates = results.duplicates || [];
|
|
59
|
+
const score = calculatePatternScore(duplicates, results.patterns.length);
|
|
60
|
+
toolScores.set("pattern-detect", score);
|
|
61
|
+
}
|
|
62
|
+
if (results.context && baseOptions.tools.includes("context")) {
|
|
63
|
+
const { calculateContextScore } = await import("@aiready/context-analyzer");
|
|
64
|
+
const summary = {
|
|
65
|
+
avgContextBudget: results.context.reduce((sum, r) => sum + r.contextBudget, 0) / results.context.length,
|
|
66
|
+
maxContextBudget: Math.max(...results.context.map((r) => r.contextBudget)),
|
|
67
|
+
avgImportDepth: results.context.reduce((sum, r) => sum + r.importDepth, 0) / results.context.length,
|
|
68
|
+
maxImportDepth: Math.max(...results.context.map((r) => r.importDepth)),
|
|
69
|
+
avgFragmentation: results.context.reduce((sum, r) => sum + r.fragmentationScore, 0) / results.context.length,
|
|
70
|
+
criticalIssues: results.context.filter((r) => r.severity === "critical").length,
|
|
71
|
+
majorIssues: results.context.filter((r) => r.severity === "major").length
|
|
72
|
+
};
|
|
73
|
+
const score = calculateContextScore(summary);
|
|
74
|
+
toolScores.set("context-analyzer", score);
|
|
75
|
+
}
|
|
76
|
+
if (results.consistency && baseOptions.tools.includes("consistency")) {
|
|
77
|
+
const { calculateConsistencyScore } = await import("@aiready/consistency");
|
|
78
|
+
const issues = results.consistency.results?.flatMap((r) => r.issues) || [];
|
|
79
|
+
const score = calculateConsistencyScore(issues, results.consistency.summary.filesAnalyzed);
|
|
80
|
+
toolScores.set("consistency", score);
|
|
81
|
+
}
|
|
82
|
+
const cliWeights = options.weights ? parseWeightString(options.weights) : void 0;
|
|
83
|
+
scoringResult = calculateOverallScore(toolScores, finalOptions, cliWeights);
|
|
84
|
+
if (options.threshold) {
|
|
85
|
+
const threshold = parseFloat(options.threshold);
|
|
86
|
+
if (scoringResult.overall < threshold) {
|
|
87
|
+
console.error(chalk.red(`
|
|
88
|
+
\u274C Score ${scoringResult.overall} is below threshold ${threshold}
|
|
89
|
+
`));
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
43
94
|
const outputFormat = options.output || finalOptions.output?.format || "console";
|
|
44
95
|
const userOutputFile = options.outputFile || finalOptions.output?.file;
|
|
45
96
|
if (outputFormat === "json") {
|
|
@@ -48,7 +99,8 @@ program.command("scan").description("Run unified analysis on a codebase").argume
|
|
|
48
99
|
summary: {
|
|
49
100
|
...results.summary,
|
|
50
101
|
executionTime: parseFloat(elapsedTime)
|
|
51
|
-
}
|
|
102
|
+
},
|
|
103
|
+
...scoringResult && { scoring: scoringResult }
|
|
52
104
|
};
|
|
53
105
|
const outputPath = resolveOutputPath(
|
|
54
106
|
userOutputFile,
|
|
@@ -58,12 +110,47 @@ program.command("scan").description("Run unified analysis on a codebase").argume
|
|
|
58
110
|
handleJSONOutput(outputData, outputPath, `\u2705 Results saved to ${outputPath}`);
|
|
59
111
|
} else {
|
|
60
112
|
console.log(generateUnifiedSummary(results));
|
|
113
|
+
if (scoringResult) {
|
|
114
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
115
|
+
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
116
|
+
const divider = "\u2501".repeat(dividerWidth);
|
|
117
|
+
console.log(chalk.cyan("\n" + divider));
|
|
118
|
+
console.log(chalk.bold.white(" AI READINESS SCORE"));
|
|
119
|
+
console.log(chalk.cyan(divider) + "\n");
|
|
120
|
+
const { emoji, color } = getRatingDisplay(scoringResult.rating);
|
|
121
|
+
const scoreColor = color === "green" ? chalk.green : color === "blue" ? chalk.blue : color === "yellow" ? chalk.yellow : chalk.red;
|
|
122
|
+
console.log(` ${emoji} Overall Score: ${scoreColor.bold(scoringResult.overall + "/100")} (${chalk.bold(scoringResult.rating)})`);
|
|
123
|
+
console.log(` ${chalk.dim("Timestamp:")} ${new Date(scoringResult.timestamp).toLocaleString()}
|
|
124
|
+
`);
|
|
125
|
+
if (scoringResult.breakdown.length > 0) {
|
|
126
|
+
console.log(chalk.bold(" Component Scores:\n"));
|
|
127
|
+
scoringResult.breakdown.forEach((tool) => {
|
|
128
|
+
const toolEmoji = tool.toolName === "pattern-detect" ? "\u{1F50D}" : tool.toolName === "context-analyzer" ? "\u{1F9E0}" : "\u{1F3F7}\uFE0F";
|
|
129
|
+
const weight = scoringResult.calculation.weights[tool.toolName];
|
|
130
|
+
console.log(` ${toolEmoji} ${chalk.white(tool.toolName.padEnd(20))} ${scoreColor(tool.score + "/100")} ${chalk.dim(`(weight: ${weight})`)}`);
|
|
131
|
+
});
|
|
132
|
+
console.log();
|
|
133
|
+
}
|
|
134
|
+
console.log(chalk.dim(` Weighted Formula: ${scoringResult.calculation.formula}`));
|
|
135
|
+
console.log(chalk.dim(` Normalized Score: ${scoringResult.calculation.normalized}
|
|
136
|
+
`));
|
|
137
|
+
const allRecommendations = scoringResult.breakdown.flatMap((tool) => tool.recommendations || []).sort((a, b) => b.estimatedImpact - a.estimatedImpact).slice(0, 5);
|
|
138
|
+
if (allRecommendations.length > 0) {
|
|
139
|
+
console.log(chalk.bold(" Top Recommendations:\n"));
|
|
140
|
+
allRecommendations.forEach((rec, i) => {
|
|
141
|
+
const priorityIcon = rec.priority === "high" ? "\u{1F534}" : rec.priority === "medium" ? "\u{1F7E1}" : "\u{1F535}";
|
|
142
|
+
console.log(` ${i + 1}. ${priorityIcon} ${rec.action}`);
|
|
143
|
+
console.log(` ${chalk.dim(`Impact: +${rec.estimatedImpact} points`)}
|
|
144
|
+
`);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
61
148
|
}
|
|
62
149
|
} catch (error) {
|
|
63
150
|
handleCLIError(error, "Analysis");
|
|
64
151
|
}
|
|
65
152
|
});
|
|
66
|
-
program.command("patterns").description("Run pattern detection analysis").argument("[directory]", "Directory to analyze", ".").option("-s, --similarity <number>", "Minimum similarity score (0-1)", "0.40").option("-l, --min-lines <number>", "Minimum lines to consider", "5").option("--max-candidates <number>", "Maximum candidates per block (performance tuning)").option("--min-shared-tokens <number>", "Minimum shared tokens for candidates (performance tuning)").option("--full-scan", "Disable smart defaults for comprehensive analysis (slower)").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").action(async (directory, options) => {
|
|
153
|
+
program.command("patterns").description("Run pattern detection analysis").argument("[directory]", "Directory to analyze", ".").option("-s, --similarity <number>", "Minimum similarity score (0-1)", "0.40").option("-l, --min-lines <number>", "Minimum lines to consider", "5").option("--max-candidates <number>", "Maximum candidates per block (performance tuning)").option("--min-shared-tokens <number>", "Minimum shared tokens for candidates (performance tuning)").option("--full-scan", "Disable smart defaults for comprehensive analysis (slower)").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score for patterns (0-100)").action(async (directory, options) => {
|
|
67
154
|
console.log(chalk.blue("\u{1F50D} Analyzing patterns...\n"));
|
|
68
155
|
const startTime = Date.now();
|
|
69
156
|
try {
|
|
@@ -95,16 +182,21 @@ program.command("patterns").description("Run pattern detection analysis").argume
|
|
|
95
182
|
cliOptions.minSharedTokens = parseInt(options.minSharedTokens);
|
|
96
183
|
}
|
|
97
184
|
const finalOptions = await loadMergedConfig(directory, defaults, cliOptions);
|
|
98
|
-
const { analyzePatterns, generateSummary } = await import("@aiready/pattern-detect");
|
|
185
|
+
const { analyzePatterns, generateSummary, calculatePatternScore } = await import("@aiready/pattern-detect");
|
|
99
186
|
const { results, duplicates } = await analyzePatterns(finalOptions);
|
|
100
187
|
const elapsedTime = getElapsedTime(startTime);
|
|
101
188
|
const summary = generateSummary(results);
|
|
189
|
+
let patternScore;
|
|
190
|
+
if (options.score) {
|
|
191
|
+
patternScore = calculatePatternScore(duplicates, results.length);
|
|
192
|
+
}
|
|
102
193
|
const outputFormat = options.output || finalOptions.output?.format || "console";
|
|
103
194
|
const userOutputFile = options.outputFile || finalOptions.output?.file;
|
|
104
195
|
if (outputFormat === "json") {
|
|
105
196
|
const outputData = {
|
|
106
197
|
results,
|
|
107
|
-
summary: { ...summary, executionTime: parseFloat(elapsedTime) }
|
|
198
|
+
summary: { ...summary, executionTime: parseFloat(elapsedTime) },
|
|
199
|
+
...patternScore && { scoring: patternScore }
|
|
108
200
|
};
|
|
109
201
|
const outputPath = resolveOutputPath(
|
|
110
202
|
userOutputFile,
|
|
@@ -150,12 +242,19 @@ program.command("patterns").description("Run pattern detection analysis").argume
|
|
|
150
242
|
} else {
|
|
151
243
|
console.log(chalk.green("\n\u2728 Great! No duplicate patterns detected.\n"));
|
|
152
244
|
}
|
|
245
|
+
if (patternScore) {
|
|
246
|
+
console.log(chalk.cyan(divider));
|
|
247
|
+
console.log(chalk.bold.white(" AI READINESS SCORE (Patterns)"));
|
|
248
|
+
console.log(chalk.cyan(divider) + "\n");
|
|
249
|
+
console.log(formatToolScore(patternScore));
|
|
250
|
+
console.log();
|
|
251
|
+
}
|
|
153
252
|
}
|
|
154
253
|
} catch (error) {
|
|
155
254
|
handleCLIError(error, "Pattern analysis");
|
|
156
255
|
}
|
|
157
256
|
});
|
|
158
|
-
program.command("context").description("Run context window cost analysis").argument("[directory]", "Directory to analyze", ".").option("--max-depth <number>", "Maximum acceptable import depth", "5").option("--max-context <number>", "Maximum acceptable context budget (tokens)", "10000").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)").action(async (directory, options) => {
|
|
257
|
+
program.command("context").description("Run context window cost analysis").argument("[directory]", "Directory to analyze", ".").option("--max-depth <number>", "Maximum acceptable import depth", "5").option("--max-context <number>", "Maximum acceptable context budget (tokens)", "10000").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").option("--score", "Calculate and display AI Readiness Score for context (0-100)").action(async (directory, options) => {
|
|
159
258
|
console.log(chalk.blue("\u{1F9E0} Analyzing context costs...\n"));
|
|
160
259
|
const startTime = Date.now();
|
|
161
260
|
try {
|
|
@@ -186,16 +285,21 @@ program.command("context").description("Run context window cost analysis").argum
|
|
|
186
285
|
console.log(` Max fragmentation: ${(finalOptions.maxFragmentation * 100).toFixed(1)}%`);
|
|
187
286
|
console.log(` Analysis focus: ${finalOptions.focus}`);
|
|
188
287
|
console.log("");
|
|
189
|
-
const { analyzeContext, generateSummary } = await import("@aiready/context-analyzer");
|
|
288
|
+
const { analyzeContext, generateSummary, calculateContextScore } = await import("@aiready/context-analyzer");
|
|
190
289
|
const results = await analyzeContext(finalOptions);
|
|
191
290
|
const elapsedTime = getElapsedTime(startTime);
|
|
192
291
|
const summary = generateSummary(results);
|
|
292
|
+
let contextScore;
|
|
293
|
+
if (options.score) {
|
|
294
|
+
contextScore = calculateContextScore(summary);
|
|
295
|
+
}
|
|
193
296
|
const outputFormat = options.output || finalOptions.output?.format || "console";
|
|
194
297
|
const userOutputFile = options.outputFile || finalOptions.output?.file;
|
|
195
298
|
if (outputFormat === "json") {
|
|
196
299
|
const outputData = {
|
|
197
300
|
results,
|
|
198
|
-
summary: { ...summary, executionTime: parseFloat(elapsedTime) }
|
|
301
|
+
summary: { ...summary, executionTime: parseFloat(elapsedTime) },
|
|
302
|
+
...contextScore && { scoring: contextScore }
|
|
199
303
|
};
|
|
200
304
|
const outputPath = resolveOutputPath(
|
|
201
305
|
userOutputFile,
|
|
@@ -275,12 +379,19 @@ program.command("context").description("Run context window cost analysis").argum
|
|
|
275
379
|
});
|
|
276
380
|
console.log();
|
|
277
381
|
}
|
|
382
|
+
if (contextScore) {
|
|
383
|
+
console.log(chalk.cyan(divider));
|
|
384
|
+
console.log(chalk.bold.white(" AI READINESS SCORE (Context)"));
|
|
385
|
+
console.log(chalk.cyan(divider) + "\n");
|
|
386
|
+
console.log(formatToolScore(contextScore));
|
|
387
|
+
console.log();
|
|
388
|
+
}
|
|
278
389
|
}
|
|
279
390
|
} catch (error) {
|
|
280
391
|
handleCLIError(error, "Context analysis");
|
|
281
392
|
}
|
|
282
393
|
});
|
|
283
|
-
program.command("consistency").description("Check naming, patterns, and architecture 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("--min-severity <level>", "Minimum severity: info|minor|major|critical", "info").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, markdown", "console").option("--output-file <path>", "Output file path (for json/markdown)").action(async (directory, options) => {
|
|
394
|
+
program.command("consistency").description("Check naming, patterns, and architecture 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("--min-severity <level>", "Minimum severity: info|minor|major|critical", "info").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, markdown", "console").option("--output-file <path>", "Output file path (for json/markdown)").option("--score", "Calculate and display AI Readiness Score for consistency (0-100)").action(async (directory, options) => {
|
|
284
395
|
console.log(chalk.blue("\u{1F50D} Analyzing consistency...\n"));
|
|
285
396
|
const startTime = Date.now();
|
|
286
397
|
try {
|
|
@@ -302,9 +413,14 @@ program.command("consistency").description("Check naming, patterns, and architec
|
|
|
302
413
|
include: options.include?.split(","),
|
|
303
414
|
exclude: options.exclude?.split(",")
|
|
304
415
|
});
|
|
305
|
-
const { analyzeConsistency } = await import("@aiready/consistency");
|
|
416
|
+
const { analyzeConsistency, calculateConsistencyScore } = await import("@aiready/consistency");
|
|
306
417
|
const report = await analyzeConsistency(finalOptions);
|
|
307
418
|
const elapsedTime = getElapsedTime(startTime);
|
|
419
|
+
let consistencyScore;
|
|
420
|
+
if (options.score) {
|
|
421
|
+
const issues = report.results?.flatMap((r) => r.issues) || [];
|
|
422
|
+
consistencyScore = calculateConsistencyScore(issues, report.summary.filesAnalyzed);
|
|
423
|
+
}
|
|
308
424
|
const outputFormat = options.output || finalOptions.output?.format || "console";
|
|
309
425
|
const userOutputFile = options.outputFile || finalOptions.output?.file;
|
|
310
426
|
if (outputFormat === "json") {
|
|
@@ -313,7 +429,8 @@ program.command("consistency").description("Check naming, patterns, and architec
|
|
|
313
429
|
summary: {
|
|
314
430
|
...report.summary,
|
|
315
431
|
executionTime: parseFloat(elapsedTime)
|
|
316
|
-
}
|
|
432
|
+
},
|
|
433
|
+
...consistencyScore && { scoring: consistencyScore }
|
|
317
434
|
};
|
|
318
435
|
const outputPath = resolveOutputPath(
|
|
319
436
|
userOutputFile,
|
|
@@ -402,6 +519,11 @@ program.command("consistency").description("Check naming, patterns, and architec
|
|
|
402
519
|
console.log();
|
|
403
520
|
}
|
|
404
521
|
}
|
|
522
|
+
if (consistencyScore) {
|
|
523
|
+
console.log(chalk.bold("\n\u{1F4CA} AI Readiness Score (Consistency)\n"));
|
|
524
|
+
console.log(formatToolScore(consistencyScore));
|
|
525
|
+
console.log();
|
|
526
|
+
}
|
|
405
527
|
}
|
|
406
528
|
} catch (error) {
|
|
407
529
|
handleCLIError(error, "Consistency analysis");
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ScanOptions, AnalysisResult } from '@aiready/core';
|
|
2
2
|
import { ContextAnalysisResult } from '@aiready/context-analyzer';
|
|
3
|
+
import { DuplicatePattern } from '@aiready/pattern-detect';
|
|
3
4
|
import { ConsistencyReport } from '@aiready/consistency';
|
|
4
5
|
|
|
5
6
|
interface UnifiedAnalysisOptions extends ScanOptions {
|
|
@@ -12,6 +13,7 @@ interface UnifiedAnalysisOptions extends ScanOptions {
|
|
|
12
13
|
}
|
|
13
14
|
interface UnifiedAnalysisResult {
|
|
14
15
|
patterns?: AnalysisResult[];
|
|
16
|
+
duplicates?: DuplicatePattern[];
|
|
15
17
|
context?: ContextAnalysisResult[];
|
|
16
18
|
consistency?: ConsistencyReport;
|
|
17
19
|
summary: {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ScanOptions, AnalysisResult } from '@aiready/core';
|
|
2
2
|
import { ContextAnalysisResult } from '@aiready/context-analyzer';
|
|
3
|
+
import { DuplicatePattern } from '@aiready/pattern-detect';
|
|
3
4
|
import { ConsistencyReport } from '@aiready/consistency';
|
|
4
5
|
|
|
5
6
|
interface UnifiedAnalysisOptions extends ScanOptions {
|
|
@@ -12,6 +13,7 @@ interface UnifiedAnalysisOptions extends ScanOptions {
|
|
|
12
13
|
}
|
|
13
14
|
interface UnifiedAnalysisResult {
|
|
14
15
|
patterns?: AnalysisResult[];
|
|
16
|
+
duplicates?: DuplicatePattern[];
|
|
15
17
|
context?: ContextAnalysisResult[];
|
|
16
18
|
consistency?: ConsistencyReport;
|
|
17
19
|
summary: {
|
package/dist/index.js
CHANGED
|
@@ -66,6 +66,7 @@ async function analyzeUnified(options) {
|
|
|
66
66
|
if (tools.includes("patterns")) {
|
|
67
67
|
const patternResult = await (0, import_pattern_detect.analyzePatterns)(options);
|
|
68
68
|
result.patterns = sortBySeverity(patternResult.results);
|
|
69
|
+
result.duplicates = patternResult.duplicates;
|
|
69
70
|
result.summary.totalIssues += patternResult.results.reduce(
|
|
70
71
|
(sum, file) => sum + file.issues.length,
|
|
71
72
|
0
|
package/dist/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiready/cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.18",
|
|
4
4
|
"description": "Unified CLI for AIReady analysis tools",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -9,16 +9,16 @@
|
|
|
9
9
|
"aiready": "./dist/cli.js"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"commander": "^
|
|
12
|
+
"commander": "^14.0.0",
|
|
13
13
|
"chalk": "^5.3.0",
|
|
14
|
-
"@aiready/
|
|
15
|
-
"@aiready/
|
|
16
|
-
"@aiready/
|
|
17
|
-
"@aiready/
|
|
14
|
+
"@aiready/context-analyzer": "0.7.16",
|
|
15
|
+
"@aiready/consistency": "0.6.14",
|
|
16
|
+
"@aiready/core": "0.7.11",
|
|
17
|
+
"@aiready/pattern-detect": "0.9.20"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"tsup": "^8.3.5",
|
|
21
|
-
"@types/node": "^
|
|
21
|
+
"@types/node": "^24.0.0"
|
|
22
22
|
},
|
|
23
23
|
"keywords": [
|
|
24
24
|
"aiready",
|
package/src/cli.ts
CHANGED
|
@@ -5,7 +5,20 @@ import { analyzeUnified, generateUnifiedSummary } from './index';
|
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import { writeFileSync } from 'fs';
|
|
7
7
|
import { join } from 'path';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
loadMergedConfig,
|
|
10
|
+
handleJSONOutput,
|
|
11
|
+
handleCLIError,
|
|
12
|
+
getElapsedTime,
|
|
13
|
+
resolveOutputPath,
|
|
14
|
+
calculateOverallScore,
|
|
15
|
+
formatScore,
|
|
16
|
+
formatToolScore,
|
|
17
|
+
getRatingDisplay,
|
|
18
|
+
parseWeightString,
|
|
19
|
+
type AIReadyConfig,
|
|
20
|
+
type ToolScoringOutput,
|
|
21
|
+
} from '@aiready/core';
|
|
9
22
|
import { readFileSync } from 'fs';
|
|
10
23
|
|
|
11
24
|
const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf8'));
|
|
@@ -27,6 +40,9 @@ program
|
|
|
27
40
|
.option('--exclude <patterns>', 'File patterns to exclude (comma-separated)')
|
|
28
41
|
.option('-o, --output <format>', 'Output format: console, json', 'console')
|
|
29
42
|
.option('--output-file <path>', 'Output file path (for json)')
|
|
43
|
+
.option('--score', 'Calculate and display AI Readiness Score (0-100)')
|
|
44
|
+
.option('--weights <weights>', 'Override tool weights for scoring (e.g., "patterns:50,context:30,consistency:20")')
|
|
45
|
+
.option('--threshold <score>', 'Minimum passing score for CI/CD (exits with code 1 if below)')
|
|
30
46
|
.action(async (directory, options) => {
|
|
31
47
|
console.log(chalk.blue('🚀 Starting AIReady unified analysis...\n'));
|
|
32
48
|
|
|
@@ -63,6 +79,59 @@ program
|
|
|
63
79
|
|
|
64
80
|
const elapsedTime = getElapsedTime(startTime);
|
|
65
81
|
|
|
82
|
+
// Calculate score if requested
|
|
83
|
+
let scoringResult: ReturnType<typeof calculateOverallScore> | undefined;
|
|
84
|
+
if (options.score || finalOptions.scoring?.showBreakdown) {
|
|
85
|
+
const toolScores: Map<string, ToolScoringOutput> = new Map();
|
|
86
|
+
|
|
87
|
+
// Collect scores from each tool that was run
|
|
88
|
+
if (results.patterns && baseOptions.tools.includes('patterns')) {
|
|
89
|
+
const { calculatePatternScore } = await import('@aiready/pattern-detect');
|
|
90
|
+
// Use the actual duplicates array which has tokenCost field
|
|
91
|
+
const duplicates = results.duplicates || [];
|
|
92
|
+
const score = calculatePatternScore(duplicates, results.patterns.length);
|
|
93
|
+
toolScores.set('pattern-detect', score);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (results.context && baseOptions.tools.includes('context')) {
|
|
97
|
+
const { calculateContextScore } = await import('@aiready/context-analyzer');
|
|
98
|
+
// Calculate summary from context results
|
|
99
|
+
const summary = {
|
|
100
|
+
avgContextBudget: results.context.reduce((sum, r) => sum + r.contextBudget, 0) / results.context.length,
|
|
101
|
+
maxContextBudget: Math.max(...results.context.map(r => r.contextBudget)),
|
|
102
|
+
avgImportDepth: results.context.reduce((sum, r) => sum + r.importDepth, 0) / results.context.length,
|
|
103
|
+
maxImportDepth: Math.max(...results.context.map(r => r.importDepth)),
|
|
104
|
+
avgFragmentation: results.context.reduce((sum, r) => sum + r.fragmentationScore, 0) / results.context.length,
|
|
105
|
+
criticalIssues: results.context.filter(r => r.severity === 'critical').length,
|
|
106
|
+
majorIssues: results.context.filter(r => r.severity === 'major').length,
|
|
107
|
+
};
|
|
108
|
+
const score = calculateContextScore(summary as any);
|
|
109
|
+
toolScores.set('context-analyzer', score);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (results.consistency && baseOptions.tools.includes('consistency')) {
|
|
113
|
+
const { calculateConsistencyScore } = await import('@aiready/consistency');
|
|
114
|
+
const issues = results.consistency.results?.flatMap((r: any) => r.issues) || [];
|
|
115
|
+
const score = calculateConsistencyScore(issues, results.consistency.summary.filesAnalyzed);
|
|
116
|
+
toolScores.set('consistency', score);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Parse weight overrides from CLI
|
|
120
|
+
const cliWeights = options.weights ? parseWeightString(options.weights) : undefined;
|
|
121
|
+
|
|
122
|
+
// Calculate overall score
|
|
123
|
+
scoringResult = calculateOverallScore(toolScores, finalOptions as AIReadyConfig, cliWeights);
|
|
124
|
+
|
|
125
|
+
// Check threshold
|
|
126
|
+
if (options.threshold) {
|
|
127
|
+
const threshold = parseFloat(options.threshold);
|
|
128
|
+
if (scoringResult.overall < threshold) {
|
|
129
|
+
console.error(chalk.red(`\n❌ Score ${scoringResult.overall} is below threshold ${threshold}\n`));
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
66
135
|
const outputFormat = options.output || finalOptions.output?.format || 'console';
|
|
67
136
|
const userOutputFile = options.outputFile || finalOptions.output?.file;
|
|
68
137
|
|
|
@@ -73,6 +142,7 @@ program
|
|
|
73
142
|
...results.summary,
|
|
74
143
|
executionTime: parseFloat(elapsedTime),
|
|
75
144
|
},
|
|
145
|
+
...(scoringResult && { scoring: scoringResult }),
|
|
76
146
|
};
|
|
77
147
|
|
|
78
148
|
const outputPath = resolveOutputPath(
|
|
@@ -85,6 +155,56 @@ program
|
|
|
85
155
|
} else {
|
|
86
156
|
// Console output
|
|
87
157
|
console.log(generateUnifiedSummary(results));
|
|
158
|
+
|
|
159
|
+
// Display score if calculated
|
|
160
|
+
if (scoringResult) {
|
|
161
|
+
const terminalWidth = process.stdout.columns || 80;
|
|
162
|
+
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
163
|
+
const divider = '━'.repeat(dividerWidth);
|
|
164
|
+
|
|
165
|
+
console.log(chalk.cyan('\n' + divider));
|
|
166
|
+
console.log(chalk.bold.white(' AI READINESS SCORE'));
|
|
167
|
+
console.log(chalk.cyan(divider) + '\n');
|
|
168
|
+
|
|
169
|
+
const { emoji, color } = getRatingDisplay(scoringResult.rating);
|
|
170
|
+
const scoreColor = color === 'green' ? chalk.green :
|
|
171
|
+
color === 'blue' ? chalk.blue :
|
|
172
|
+
color === 'yellow' ? chalk.yellow : chalk.red;
|
|
173
|
+
|
|
174
|
+
console.log(` ${emoji} Overall Score: ${scoreColor.bold(scoringResult.overall + '/100')} (${chalk.bold(scoringResult.rating)})`); console.log(` ${chalk.dim('Timestamp:')} ${new Date(scoringResult.timestamp).toLocaleString()}\n`);
|
|
175
|
+
|
|
176
|
+
// Show breakdown by tool
|
|
177
|
+
if (scoringResult.breakdown.length > 0) {
|
|
178
|
+
console.log(chalk.bold(' Component Scores:\n'));
|
|
179
|
+
scoringResult.breakdown.forEach(tool => {
|
|
180
|
+
const toolEmoji = tool.toolName === 'pattern-detect' ? '🔍' :
|
|
181
|
+
tool.toolName === 'context-analyzer' ? '🧠' : '🏷️';
|
|
182
|
+
const weight = scoringResult.calculation.weights[tool.toolName];
|
|
183
|
+
console.log(` ${toolEmoji} ${chalk.white(tool.toolName.padEnd(20))} ${scoreColor(tool.score + '/100')} ${chalk.dim(`(weight: ${weight})`)}`);
|
|
184
|
+
});
|
|
185
|
+
console.log();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Show calculation
|
|
189
|
+
console.log(chalk.dim(` Weighted Formula: ${scoringResult.calculation.formula}`));
|
|
190
|
+
console.log(chalk.dim(` Normalized Score: ${scoringResult.calculation.normalized}\n`));
|
|
191
|
+
|
|
192
|
+
// Show top recommendations across all tools
|
|
193
|
+
const allRecommendations = scoringResult.breakdown
|
|
194
|
+
.flatMap(tool => tool.recommendations || [])
|
|
195
|
+
.sort((a, b) => b.estimatedImpact - a.estimatedImpact)
|
|
196
|
+
.slice(0, 5);
|
|
197
|
+
|
|
198
|
+
if (allRecommendations.length > 0) {
|
|
199
|
+
console.log(chalk.bold(' Top Recommendations:\n'));
|
|
200
|
+
allRecommendations.forEach((rec, i) => {
|
|
201
|
+
const priorityIcon = rec.priority === 'high' ? '🔴' :
|
|
202
|
+
rec.priority === 'medium' ? '🟡' : '🔵';
|
|
203
|
+
console.log(` ${i + 1}. ${priorityIcon} ${rec.action}`);
|
|
204
|
+
console.log(` ${chalk.dim(`Impact: +${rec.estimatedImpact} points`)}\n`);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
88
208
|
}
|
|
89
209
|
} catch (error) {
|
|
90
210
|
handleCLIError(error, 'Analysis');
|
|
@@ -105,6 +225,7 @@ program
|
|
|
105
225
|
.option('--exclude <patterns>', 'File patterns to exclude (comma-separated)')
|
|
106
226
|
.option('-o, --output <format>', 'Output format: console, json', 'console')
|
|
107
227
|
.option('--output-file <path>', 'Output file path (for json)')
|
|
228
|
+
.option('--score', 'Calculate and display AI Readiness Score for patterns (0-100)')
|
|
108
229
|
.action(async (directory, options) => {
|
|
109
230
|
console.log(chalk.blue('🔍 Analyzing patterns...\n'));
|
|
110
231
|
|
|
@@ -150,12 +271,18 @@ program
|
|
|
150
271
|
|
|
151
272
|
const finalOptions = await loadMergedConfig(directory, defaults, cliOptions);
|
|
152
273
|
|
|
153
|
-
const { analyzePatterns, generateSummary } = await import('@aiready/pattern-detect');
|
|
274
|
+
const { analyzePatterns, generateSummary, calculatePatternScore } = await import('@aiready/pattern-detect');
|
|
154
275
|
|
|
155
276
|
const { results, duplicates } = await analyzePatterns(finalOptions);
|
|
156
277
|
|
|
157
278
|
const elapsedTime = getElapsedTime(startTime);
|
|
158
279
|
const summary = generateSummary(results);
|
|
280
|
+
|
|
281
|
+
// Calculate score if requested
|
|
282
|
+
let patternScore: ToolScoringOutput | undefined;
|
|
283
|
+
if (options.score) {
|
|
284
|
+
patternScore = calculatePatternScore(duplicates, results.length);
|
|
285
|
+
}
|
|
159
286
|
|
|
160
287
|
const outputFormat = options.output || finalOptions.output?.format || 'console';
|
|
161
288
|
const userOutputFile = options.outputFile || finalOptions.output?.file;
|
|
@@ -164,6 +291,7 @@ program
|
|
|
164
291
|
const outputData = {
|
|
165
292
|
results,
|
|
166
293
|
summary: { ...summary, executionTime: parseFloat(elapsedTime) },
|
|
294
|
+
...(patternScore && { scoring: patternScore }),
|
|
167
295
|
};
|
|
168
296
|
|
|
169
297
|
const outputPath = resolveOutputPath(
|
|
@@ -225,6 +353,15 @@ program
|
|
|
225
353
|
} else {
|
|
226
354
|
console.log(chalk.green('\n✨ Great! No duplicate patterns detected.\n'));
|
|
227
355
|
}
|
|
356
|
+
|
|
357
|
+
// Display score if calculated
|
|
358
|
+
if (patternScore) {
|
|
359
|
+
console.log(chalk.cyan(divider));
|
|
360
|
+
console.log(chalk.bold.white(' AI READINESS SCORE (Patterns)'));
|
|
361
|
+
console.log(chalk.cyan(divider) + '\n');
|
|
362
|
+
console.log(formatToolScore(patternScore));
|
|
363
|
+
console.log();
|
|
364
|
+
}
|
|
228
365
|
}
|
|
229
366
|
} catch (error) {
|
|
230
367
|
handleCLIError(error, 'Pattern analysis');
|
|
@@ -241,6 +378,7 @@ program
|
|
|
241
378
|
.option('--exclude <patterns>', 'File patterns to exclude (comma-separated)')
|
|
242
379
|
.option('-o, --output <format>', 'Output format: console, json', 'console')
|
|
243
380
|
.option('--output-file <path>', 'Output file path (for json)')
|
|
381
|
+
.option('--score', 'Calculate and display AI Readiness Score for context (0-100)')
|
|
244
382
|
.action(async (directory, options) => {
|
|
245
383
|
console.log(chalk.blue('🧠 Analyzing context costs...\n'));
|
|
246
384
|
|
|
@@ -282,12 +420,18 @@ program
|
|
|
282
420
|
console.log(` Analysis focus: ${finalOptions.focus}`);
|
|
283
421
|
console.log('');
|
|
284
422
|
|
|
285
|
-
const { analyzeContext, generateSummary } = await import('@aiready/context-analyzer');
|
|
423
|
+
const { analyzeContext, generateSummary, calculateContextScore } = await import('@aiready/context-analyzer');
|
|
286
424
|
|
|
287
425
|
const results = await analyzeContext(finalOptions);
|
|
288
426
|
|
|
289
427
|
const elapsedTime = getElapsedTime(startTime);
|
|
290
428
|
const summary = generateSummary(results);
|
|
429
|
+
|
|
430
|
+
// Calculate score if requested
|
|
431
|
+
let contextScore: ToolScoringOutput | undefined;
|
|
432
|
+
if (options.score) {
|
|
433
|
+
contextScore = calculateContextScore(summary as any);
|
|
434
|
+
}
|
|
291
435
|
|
|
292
436
|
const outputFormat = options.output || finalOptions.output?.format || 'console';
|
|
293
437
|
const userOutputFile = options.outputFile || finalOptions.output?.file;
|
|
@@ -296,6 +440,7 @@ program
|
|
|
296
440
|
const outputData = {
|
|
297
441
|
results,
|
|
298
442
|
summary: { ...summary, executionTime: parseFloat(elapsedTime) },
|
|
443
|
+
...(contextScore && { scoring: contextScore }),
|
|
299
444
|
};
|
|
300
445
|
|
|
301
446
|
const outputPath = resolveOutputPath(
|
|
@@ -384,6 +529,15 @@ program
|
|
|
384
529
|
});
|
|
385
530
|
console.log();
|
|
386
531
|
}
|
|
532
|
+
|
|
533
|
+
// Display score if calculated
|
|
534
|
+
if (contextScore) {
|
|
535
|
+
console.log(chalk.cyan(divider));
|
|
536
|
+
console.log(chalk.bold.white(' AI READINESS SCORE (Context)'));
|
|
537
|
+
console.log(chalk.cyan(divider) + '\n');
|
|
538
|
+
console.log(formatToolScore(contextScore));
|
|
539
|
+
console.log();
|
|
540
|
+
}
|
|
387
541
|
}
|
|
388
542
|
} catch (error) {
|
|
389
543
|
handleCLIError(error, 'Context analysis');
|
|
@@ -403,6 +557,7 @@ program
|
|
|
403
557
|
.option('--exclude <patterns>', 'File patterns to exclude (comma-separated)')
|
|
404
558
|
.option('-o, --output <format>', 'Output format: console, json, markdown', 'console')
|
|
405
559
|
.option('--output-file <path>', 'Output file path (for json/markdown)')
|
|
560
|
+
.option('--score', 'Calculate and display AI Readiness Score for consistency (0-100)')
|
|
406
561
|
.action(async (directory, options) => {
|
|
407
562
|
console.log(chalk.blue('🔍 Analyzing consistency...\n'));
|
|
408
563
|
|
|
@@ -431,11 +586,18 @@ program
|
|
|
431
586
|
exclude: options.exclude?.split(','),
|
|
432
587
|
});
|
|
433
588
|
|
|
434
|
-
const { analyzeConsistency } = await import('@aiready/consistency');
|
|
589
|
+
const { analyzeConsistency, calculateConsistencyScore } = await import('@aiready/consistency');
|
|
435
590
|
|
|
436
591
|
const report = await analyzeConsistency(finalOptions);
|
|
437
592
|
|
|
438
593
|
const elapsedTime = getElapsedTime(startTime);
|
|
594
|
+
|
|
595
|
+
// Calculate score if requested
|
|
596
|
+
let consistencyScore: ToolScoringOutput | undefined;
|
|
597
|
+
if (options.score) {
|
|
598
|
+
const issues = report.results?.flatMap((r: any) => r.issues) || [];
|
|
599
|
+
consistencyScore = calculateConsistencyScore(issues, report.summary.filesAnalyzed);
|
|
600
|
+
}
|
|
439
601
|
|
|
440
602
|
const outputFormat = options.output || finalOptions.output?.format || 'console';
|
|
441
603
|
const userOutputFile = options.outputFile || finalOptions.output?.file;
|
|
@@ -447,6 +609,7 @@ program
|
|
|
447
609
|
...report.summary,
|
|
448
610
|
executionTime: parseFloat(elapsedTime),
|
|
449
611
|
},
|
|
612
|
+
...(consistencyScore && { scoring: consistencyScore }),
|
|
450
613
|
};
|
|
451
614
|
|
|
452
615
|
const outputPath = resolveOutputPath(
|
|
@@ -545,6 +708,13 @@ program
|
|
|
545
708
|
console.log();
|
|
546
709
|
}
|
|
547
710
|
}
|
|
711
|
+
|
|
712
|
+
// Display score if calculated
|
|
713
|
+
if (consistencyScore) {
|
|
714
|
+
console.log(chalk.bold('\n📊 AI Readiness Score (Consistency)\n'));
|
|
715
|
+
console.log(formatToolScore(consistencyScore));
|
|
716
|
+
console.log();
|
|
717
|
+
}
|
|
548
718
|
}
|
|
549
719
|
} catch (error) {
|
|
550
720
|
handleCLIError(error, 'Consistency analysis');
|
package/src/index.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { analyzeContext } from '@aiready/context-analyzer';
|
|
|
3
3
|
import { analyzeConsistency } from '@aiready/consistency';
|
|
4
4
|
import type { AnalysisResult, ScanOptions } from '@aiready/core';
|
|
5
5
|
import type { ContextAnalysisResult } from '@aiready/context-analyzer';
|
|
6
|
-
import type { PatternDetectOptions } from '@aiready/pattern-detect';
|
|
6
|
+
import type { PatternDetectOptions, DuplicatePattern } from '@aiready/pattern-detect';
|
|
7
7
|
import type { ConsistencyReport } from '@aiready/consistency';
|
|
8
8
|
|
|
9
9
|
export interface UnifiedAnalysisOptions extends ScanOptions {
|
|
@@ -17,6 +17,7 @@ export interface UnifiedAnalysisOptions extends ScanOptions {
|
|
|
17
17
|
|
|
18
18
|
export interface UnifiedAnalysisResult {
|
|
19
19
|
patterns?: AnalysisResult[];
|
|
20
|
+
duplicates?: DuplicatePattern[]; // Store actual duplicates for scoring
|
|
20
21
|
context?: ContextAnalysisResult[];
|
|
21
22
|
consistency?: ConsistencyReport;
|
|
22
23
|
summary: {
|
|
@@ -80,6 +81,8 @@ export async function analyzeUnified(
|
|
|
80
81
|
const patternResult = await analyzePatterns(options);
|
|
81
82
|
// Sort results by severity
|
|
82
83
|
result.patterns = sortBySeverity(patternResult.results);
|
|
84
|
+
// Store duplicates for scoring
|
|
85
|
+
result.duplicates = patternResult.duplicates;
|
|
83
86
|
// Count actual issues, not file count
|
|
84
87
|
result.summary.totalIssues += patternResult.results.reduce(
|
|
85
88
|
(sum, file) => sum + file.issues.length,
|