@aiready/cli 0.14.3 → 0.14.5
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 +7 -7
- package/.turbo/turbo-lint.log +0 -32
- package/.turbo/turbo-test.log +35 -34
- package/aiready-report.json +30703 -0
- package/dist/cli.js +357 -344
- package/dist/cli.mjs +294 -280
- package/package.json +12 -12
- package/src/__tests__/cli.test.ts +1 -1
- package/src/__tests__/config-shape.test.ts +0 -1
- package/src/__tests__/unified.test.ts +1 -1
- package/src/commands/__tests__/extra-commands.test.ts +0 -1
- package/src/commands/__tests__/init.test.ts +56 -0
- package/src/commands/__tests__/scan.test.ts +1 -1
- package/src/commands/__tests__/upload.test.ts +0 -1
- package/src/commands/bug.ts +1 -2
- package/src/commands/init.ts +0 -4
- package/src/commands/patterns.ts +3 -1
- package/src/commands/report-formatter.ts +128 -0
- package/src/commands/scan.ts +18 -112
- package/src/commands/upload.ts +15 -13
- package/src/commands/visualize.ts +9 -1
- package/src/index.ts +18 -3
- package/src/utils/helpers.ts +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -12,7 +12,7 @@ import { join as join2, dirname } from "path";
|
|
|
12
12
|
import { fileURLToPath } from "url";
|
|
13
13
|
|
|
14
14
|
// src/commands/scan.ts
|
|
15
|
-
import
|
|
15
|
+
import chalk4 from "chalk";
|
|
16
16
|
import { writeFileSync, readFileSync as readFileSync2 } from "fs";
|
|
17
17
|
import { resolve as resolvePath3 } from "path";
|
|
18
18
|
import {
|
|
@@ -20,12 +20,8 @@ import {
|
|
|
20
20
|
handleJSONOutput,
|
|
21
21
|
handleCLIError as handleCLIError2,
|
|
22
22
|
resolveOutputPath,
|
|
23
|
-
formatScore,
|
|
24
|
-
calculateTokenBudget,
|
|
25
|
-
getRating,
|
|
26
|
-
getRatingDisplay,
|
|
27
23
|
getRepoMetadata,
|
|
28
|
-
|
|
24
|
+
calculateTokenBudget,
|
|
29
25
|
ToolName,
|
|
30
26
|
emitIssuesAsAnnotations
|
|
31
27
|
} from "@aiready/core";
|
|
@@ -127,10 +123,102 @@ function generateMarkdownReport(report, elapsedTime) {
|
|
|
127
123
|
return markdown;
|
|
128
124
|
}
|
|
129
125
|
|
|
126
|
+
// src/commands/report-formatter.ts
|
|
127
|
+
import chalk2 from "chalk";
|
|
128
|
+
import {
|
|
129
|
+
Severity,
|
|
130
|
+
formatScore,
|
|
131
|
+
getRating,
|
|
132
|
+
getRatingDisplay
|
|
133
|
+
} from "@aiready/core";
|
|
134
|
+
function printScanSummary(results, startTime) {
|
|
135
|
+
console.log(chalk2.cyan("\n=== AIReady Run Summary ==="));
|
|
136
|
+
console.log(
|
|
137
|
+
` Total issues (all tools): ${chalk2.bold(String(results.summary.totalIssues || 0))}`
|
|
138
|
+
);
|
|
139
|
+
console.log(
|
|
140
|
+
` Execution time: ${chalk2.bold(((Date.now() - startTime) / 1e3).toFixed(2) + "s")}`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
function printBusinessImpact(roi, unifiedBudget) {
|
|
144
|
+
console.log(chalk2.bold("\n\u{1F4B0} Business Impact Analysis (Monthly)"));
|
|
145
|
+
console.log(
|
|
146
|
+
` Potential Savings: ${chalk2.green(chalk2.bold("$" + roi.monthlySavings.toLocaleString()))}`
|
|
147
|
+
);
|
|
148
|
+
console.log(
|
|
149
|
+
` Productivity Gain: ${chalk2.cyan(chalk2.bold(roi.productivityGainHours + "h"))} (est. dev time)`
|
|
150
|
+
);
|
|
151
|
+
console.log(
|
|
152
|
+
` Context Efficiency: ${chalk2.yellow((unifiedBudget.efficiencyRatio * 100).toFixed(0) + "%")}`
|
|
153
|
+
);
|
|
154
|
+
console.log(
|
|
155
|
+
` Annual Value: ${chalk2.bold("$" + roi.annualValue.toLocaleString())} (ROI Prediction)`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
function printScoring(scoringResult, scoringProfile) {
|
|
159
|
+
console.log(chalk2.bold("\n\u{1F4CA} AI Readiness Overall Score"));
|
|
160
|
+
console.log(` ${formatScore(scoringResult)}`);
|
|
161
|
+
console.log(chalk2.dim(` (Scoring Profile: ${scoringProfile})`));
|
|
162
|
+
if (scoringResult.breakdown) {
|
|
163
|
+
console.log(chalk2.bold("\nTool breakdown:"));
|
|
164
|
+
scoringResult.breakdown.forEach((tool) => {
|
|
165
|
+
const rating = getRating(tool.score);
|
|
166
|
+
const emoji = getRatingDisplay(rating).emoji;
|
|
167
|
+
console.log(
|
|
168
|
+
` - ${tool.toolName}: ${tool.score}/100 (${rating}) ${emoji}`
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
const allRecs = scoringResult.breakdown.flatMap(
|
|
172
|
+
(t) => (t.recommendations || []).map((r) => ({ ...r, tool: t.toolName }))
|
|
173
|
+
).sort((a, b) => b.estimatedImpact - a.estimatedImpact).slice(0, 3);
|
|
174
|
+
if (allRecs.length > 0) {
|
|
175
|
+
console.log(chalk2.bold("\n\u{1F3AF} Top Actionable Recommendations:"));
|
|
176
|
+
allRecs.forEach((rec, i) => {
|
|
177
|
+
const priorityIcon = rec.priority === "high" ? "\u{1F534}" : rec.priority === "medium" ? "\u{1F7E1}" : "\u{1F535}";
|
|
178
|
+
console.log(` ${i + 1}. ${priorityIcon} ${chalk2.bold(rec.action)}`);
|
|
179
|
+
console.log(
|
|
180
|
+
` Impact: ${chalk2.green(`+${rec.estimatedImpact} points`)} to ${rec.tool}`
|
|
181
|
+
);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function mapToUnifiedReport(res, scoring) {
|
|
187
|
+
const allResults = [];
|
|
188
|
+
const totalFilesSet = /* @__PURE__ */ new Set();
|
|
189
|
+
let criticalCount = 0;
|
|
190
|
+
let majorCount = 0;
|
|
191
|
+
res.summary.toolsRun.forEach((toolId) => {
|
|
192
|
+
const spokeRes = res[toolId];
|
|
193
|
+
if (!spokeRes || !spokeRes.results) return;
|
|
194
|
+
spokeRes.results.forEach((r) => {
|
|
195
|
+
totalFilesSet.add(r.fileName);
|
|
196
|
+
allResults.push(r);
|
|
197
|
+
r.issues?.forEach((i) => {
|
|
198
|
+
if (i.severity === Severity.Critical || i.severity === "critical")
|
|
199
|
+
criticalCount++;
|
|
200
|
+
if (i.severity === Severity.Major || i.severity === "major")
|
|
201
|
+
majorCount++;
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
return {
|
|
206
|
+
...res,
|
|
207
|
+
results: allResults,
|
|
208
|
+
summary: {
|
|
209
|
+
...res.summary,
|
|
210
|
+
totalFiles: totalFilesSet.size,
|
|
211
|
+
criticalIssues: criticalCount,
|
|
212
|
+
majorIssues: majorCount
|
|
213
|
+
},
|
|
214
|
+
scoring
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
130
218
|
// src/commands/upload.ts
|
|
131
219
|
import fs from "fs";
|
|
132
220
|
import { resolve as resolvePath2 } from "path";
|
|
133
|
-
import
|
|
221
|
+
import chalk3 from "chalk";
|
|
134
222
|
import { handleCLIError } from "@aiready/core";
|
|
135
223
|
async function uploadAction(file, options) {
|
|
136
224
|
const startTime = Date.now();
|
|
@@ -138,31 +226,31 @@ async function uploadAction(file, options) {
|
|
|
138
226
|
const serverUrl = options.server || process.env.AIREADY_SERVER || "https://dev.platform.getaiready.dev";
|
|
139
227
|
const apiKey = options.apiKey || process.env.AIREADY_API_KEY;
|
|
140
228
|
if (!apiKey) {
|
|
141
|
-
console.error(
|
|
229
|
+
console.error(chalk3.red("\u274C API Key is required for upload."));
|
|
142
230
|
console.log(
|
|
143
|
-
|
|
231
|
+
chalk3.dim(
|
|
144
232
|
" Set AIREADY_API_KEY environment variable or use --api-key flag."
|
|
145
233
|
)
|
|
146
234
|
);
|
|
147
235
|
console.log(
|
|
148
|
-
|
|
236
|
+
chalk3.dim(
|
|
149
237
|
" Get an API key from https://platform.getaiready.dev/dashboard"
|
|
150
238
|
)
|
|
151
239
|
);
|
|
152
240
|
process.exit(1);
|
|
153
241
|
}
|
|
154
242
|
if (!fs.existsSync(filePath)) {
|
|
155
|
-
console.error(
|
|
243
|
+
console.error(chalk3.red(`\u274C File not found: ${filePath}`));
|
|
156
244
|
process.exit(1);
|
|
157
245
|
}
|
|
158
246
|
try {
|
|
159
|
-
console.log(
|
|
160
|
-
console.log(
|
|
247
|
+
console.log(chalk3.blue(`\u{1F680} Uploading report to ${serverUrl}...`));
|
|
248
|
+
console.log(chalk3.dim(` Reading report from ${filePath}...`));
|
|
161
249
|
const reportContent = fs.readFileSync(filePath, "utf-8");
|
|
162
250
|
const reportData = JSON.parse(reportContent);
|
|
163
|
-
console.log(
|
|
251
|
+
console.log(chalk3.dim(` Successfully parsed report JSON.`));
|
|
164
252
|
const repoId = options.repoId || reportData.repository?.repoId;
|
|
165
|
-
const
|
|
253
|
+
const response = await fetch(`${serverUrl}/api/analysis/upload`, {
|
|
166
254
|
method: "POST",
|
|
167
255
|
headers: {
|
|
168
256
|
"Content-Type": "application/json",
|
|
@@ -174,46 +262,48 @@ async function uploadAction(file, options) {
|
|
|
174
262
|
// Might be null, server will handle mapping
|
|
175
263
|
})
|
|
176
264
|
});
|
|
177
|
-
const contentType =
|
|
178
|
-
let
|
|
265
|
+
const contentType = response.headers.get("content-type");
|
|
266
|
+
let uploadResult = {};
|
|
179
267
|
if (contentType?.includes("application/json")) {
|
|
180
|
-
|
|
268
|
+
uploadResult = await response.json();
|
|
181
269
|
} else {
|
|
182
|
-
const text = await
|
|
183
|
-
|
|
270
|
+
const text = await response.text();
|
|
271
|
+
uploadResult = { error: text || response.statusText };
|
|
184
272
|
}
|
|
185
|
-
if (!
|
|
273
|
+
if (!response.ok) {
|
|
186
274
|
console.error(
|
|
187
|
-
|
|
275
|
+
chalk3.red(
|
|
276
|
+
`\u274C Upload failed: ${uploadResult.error || response.statusText}`
|
|
277
|
+
)
|
|
188
278
|
);
|
|
189
279
|
if (contentType?.includes("text/html")) {
|
|
190
280
|
console.log(
|
|
191
|
-
|
|
281
|
+
chalk3.yellow(
|
|
192
282
|
" Note: Received an HTML response. This often indicates a redirect (e.g., to a login page) or a server error."
|
|
193
283
|
)
|
|
194
284
|
);
|
|
195
|
-
if (
|
|
285
|
+
if (uploadResult.error?.includes("Redirecting")) {
|
|
196
286
|
console.log(
|
|
197
|
-
|
|
287
|
+
chalk3.dim(
|
|
198
288
|
" Detected redirect. Check if the API endpoint requires authentication or has changed."
|
|
199
289
|
)
|
|
200
290
|
);
|
|
201
291
|
}
|
|
202
292
|
}
|
|
203
|
-
if (
|
|
293
|
+
if (response.status === 401) {
|
|
204
294
|
console.log(
|
|
205
|
-
|
|
295
|
+
chalk3.dim(" Hint: Your API key may be invalid or expired.")
|
|
206
296
|
);
|
|
207
297
|
}
|
|
208
298
|
process.exit(1);
|
|
209
299
|
}
|
|
210
300
|
const duration = ((Date.now() - startTime) / 1e3).toFixed(2);
|
|
211
|
-
console.log(
|
|
301
|
+
console.log(chalk3.green(`
|
|
212
302
|
\u2705 Upload successful! (${duration}s)`));
|
|
213
|
-
console.log(
|
|
214
|
-
if (
|
|
215
|
-
console.log(
|
|
216
|
-
console.log(
|
|
303
|
+
console.log(chalk3.cyan(` View results: ${serverUrl}/dashboard`));
|
|
304
|
+
if (uploadResult.analysis) {
|
|
305
|
+
console.log(chalk3.dim(` Analysis ID: ${uploadResult.analysis.id}`));
|
|
306
|
+
console.log(chalk3.dim(` Score: ${uploadResult.analysis.aiScore}/100`));
|
|
217
307
|
}
|
|
218
308
|
} catch (error) {
|
|
219
309
|
handleCLIError(error, "Upload");
|
|
@@ -232,7 +322,7 @@ ENVIRONMENT VARIABLES:
|
|
|
232
322
|
|
|
233
323
|
// src/commands/scan.ts
|
|
234
324
|
async function scanAction(directory, options) {
|
|
235
|
-
console.log(
|
|
325
|
+
console.log(chalk4.blue("\u{1F680} Starting AIReady unified analysis...\n"));
|
|
236
326
|
const startTime = Date.now();
|
|
237
327
|
const resolvedDir = resolvePath3(process.cwd(), directory || ".");
|
|
238
328
|
const repoMetadata = getRepoMetadata(resolvedDir);
|
|
@@ -302,7 +392,7 @@ async function scanAction(directory, options) {
|
|
|
302
392
|
break;
|
|
303
393
|
default:
|
|
304
394
|
console.log(
|
|
305
|
-
|
|
395
|
+
chalk4.yellow(
|
|
306
396
|
`
|
|
307
397
|
\u26A0\uFE0F Unknown profile '${options.profile}'. Using defaults.`
|
|
308
398
|
)
|
|
@@ -332,9 +422,9 @@ async function scanAction(directory, options) {
|
|
|
332
422
|
...finalOptions.toolConfigs[ToolName.PatternDetect]
|
|
333
423
|
};
|
|
334
424
|
}
|
|
335
|
-
console.log(
|
|
425
|
+
console.log(chalk4.cyan("\n=== AIReady Run Preview ==="));
|
|
336
426
|
console.log(
|
|
337
|
-
|
|
427
|
+
chalk4.white("Tools to run:"),
|
|
338
428
|
(finalOptions.tools || []).join(", ")
|
|
339
429
|
);
|
|
340
430
|
const progressCallback = (event) => {
|
|
@@ -343,16 +433,16 @@ async function scanAction(directory, options) {
|
|
|
343
433
|
return;
|
|
344
434
|
}
|
|
345
435
|
process.stdout.write("\r\x1B[K");
|
|
346
|
-
console.log(
|
|
436
|
+
console.log(chalk4.cyan(`--- ${event.tool.toUpperCase()} RESULTS ---`));
|
|
347
437
|
const res = event.data;
|
|
348
438
|
if (res && res.summary) {
|
|
349
439
|
if (res.summary.totalIssues !== void 0)
|
|
350
|
-
console.log(` Issues found: ${
|
|
440
|
+
console.log(` Issues found: ${chalk4.bold(res.summary.totalIssues)}`);
|
|
351
441
|
if (res.summary.score !== void 0)
|
|
352
|
-
console.log(` Tool Score: ${
|
|
442
|
+
console.log(` Tool Score: ${chalk4.bold(res.summary.score)}/100`);
|
|
353
443
|
if (res.summary.totalFiles !== void 0)
|
|
354
444
|
console.log(
|
|
355
|
-
` Files analyzed: ${
|
|
445
|
+
` Files analyzed: ${chalk4.bold(res.summary.totalFiles)}`
|
|
356
446
|
);
|
|
357
447
|
}
|
|
358
448
|
};
|
|
@@ -364,13 +454,7 @@ async function scanAction(directory, options) {
|
|
|
364
454
|
},
|
|
365
455
|
suppressToolConfig: true
|
|
366
456
|
});
|
|
367
|
-
|
|
368
|
-
console.log(
|
|
369
|
-
` Total issues (all tools): ${chalk3.bold(String(results.summary.totalIssues || 0))}`
|
|
370
|
-
);
|
|
371
|
-
console.log(
|
|
372
|
-
` Execution time: ${chalk3.bold(((Date.now() - startTime) / 1e3).toFixed(2) + "s")}`
|
|
373
|
-
);
|
|
457
|
+
printScanSummary(results, startTime);
|
|
374
458
|
let scoringResult;
|
|
375
459
|
if (options.score || finalOptions.scoring?.showBreakdown) {
|
|
376
460
|
scoringResult = await scoreUnified(results, {
|
|
@@ -380,9 +464,7 @@ async function scanAction(directory, options) {
|
|
|
380
464
|
profile: scoringProfile
|
|
381
465
|
}
|
|
382
466
|
});
|
|
383
|
-
|
|
384
|
-
console.log(` ${formatScore(scoringResult)}`);
|
|
385
|
-
console.log(chalk3.dim(` (Scoring Profile: ${scoringProfile})`));
|
|
467
|
+
printScoring(scoringResult, scoringProfile);
|
|
386
468
|
if (options.compareTo) {
|
|
387
469
|
try {
|
|
388
470
|
const prevReport = JSON.parse(
|
|
@@ -394,19 +476,19 @@ async function scanAction(directory, options) {
|
|
|
394
476
|
const diffStr = diff > 0 ? `+${diff}` : String(diff);
|
|
395
477
|
if (diff > 0)
|
|
396
478
|
console.log(
|
|
397
|
-
|
|
479
|
+
chalk4.green(
|
|
398
480
|
` \u{1F4C8} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
|
|
399
481
|
)
|
|
400
482
|
);
|
|
401
483
|
else if (diff < 0)
|
|
402
484
|
console.log(
|
|
403
|
-
|
|
485
|
+
chalk4.red(
|
|
404
486
|
` \u{1F4C9} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
|
|
405
487
|
)
|
|
406
488
|
);
|
|
407
489
|
else
|
|
408
490
|
console.log(
|
|
409
|
-
|
|
491
|
+
chalk4.blue(
|
|
410
492
|
` \u2796 Trend: No change (${prevScore} \u2192 ${scoringResult.overall})`
|
|
411
493
|
)
|
|
412
494
|
);
|
|
@@ -455,19 +537,7 @@ async function scanAction(directory, options) {
|
|
|
455
537
|
issues: allIssues,
|
|
456
538
|
modelId
|
|
457
539
|
});
|
|
458
|
-
|
|
459
|
-
console.log(
|
|
460
|
-
` Potential Savings: ${chalk3.green(chalk3.bold("$" + roi.monthlySavings.toLocaleString()))}`
|
|
461
|
-
);
|
|
462
|
-
console.log(
|
|
463
|
-
` Productivity Gain: ${chalk3.cyan(chalk3.bold(roi.productivityGainHours + "h"))} (est. dev time)`
|
|
464
|
-
);
|
|
465
|
-
console.log(
|
|
466
|
-
` Context Efficiency: ${chalk3.yellow((unifiedBudget.efficiencyRatio * 100).toFixed(0) + "%")}`
|
|
467
|
-
);
|
|
468
|
-
console.log(
|
|
469
|
-
` Annual Value: ${chalk3.bold("$" + roi.annualValue.toLocaleString())} (ROI Prediction)`
|
|
470
|
-
);
|
|
540
|
+
printBusinessImpact(roi, unifiedBudget);
|
|
471
541
|
results.summary.businessImpact = {
|
|
472
542
|
estimatedMonthlyWaste: roi.monthlySavings,
|
|
473
543
|
potentialSavings: roi.monthlySavings,
|
|
@@ -476,78 +546,24 @@ async function scanAction(directory, options) {
|
|
|
476
546
|
scoringResult.tokenBudget = unifiedBudget;
|
|
477
547
|
scoringResult.businessROI = roi;
|
|
478
548
|
}
|
|
479
|
-
if (scoringResult.breakdown) {
|
|
480
|
-
console.log(chalk3.bold("\nTool breakdown:"));
|
|
481
|
-
scoringResult.breakdown.forEach((tool) => {
|
|
482
|
-
const rating = getRating(tool.score);
|
|
483
|
-
const emoji = getRatingDisplay(rating).emoji;
|
|
484
|
-
console.log(
|
|
485
|
-
` - ${tool.toolName}: ${tool.score}/100 (${rating}) ${emoji}`
|
|
486
|
-
);
|
|
487
|
-
});
|
|
488
|
-
const allRecs = scoringResult.breakdown.flatMap(
|
|
489
|
-
(t) => (t.recommendations || []).map((r) => ({ ...r, tool: t.toolName }))
|
|
490
|
-
).sort((a, b) => b.estimatedImpact - a.estimatedImpact).slice(0, 3);
|
|
491
|
-
if (allRecs.length > 0) {
|
|
492
|
-
console.log(chalk3.bold("\n\u{1F3AF} Top Actionable Recommendations:"));
|
|
493
|
-
allRecs.forEach((rec, i) => {
|
|
494
|
-
const priorityIcon = rec.priority === "high" ? "\u{1F534}" : rec.priority === "medium" ? "\u{1F7E1}" : "\u{1F535}";
|
|
495
|
-
console.log(
|
|
496
|
-
` ${i + 1}. ${priorityIcon} ${chalk3.bold(rec.action)}`
|
|
497
|
-
);
|
|
498
|
-
console.log(
|
|
499
|
-
` Impact: ${chalk3.green(`+${rec.estimatedImpact} points`)} to ${rec.tool}`
|
|
500
|
-
);
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
549
|
}
|
|
505
550
|
console.log(
|
|
506
|
-
|
|
551
|
+
chalk4.dim(
|
|
507
552
|
"\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
|
|
508
553
|
)
|
|
509
554
|
);
|
|
510
|
-
console.log(
|
|
511
|
-
console.log(
|
|
555
|
+
console.log(chalk4.dim("\u{1F4AC} Found a bug or have a metric idea?"));
|
|
556
|
+
console.log(chalk4.dim("\u{1F449} Copy/paste this to your AI agent:"));
|
|
512
557
|
console.log(
|
|
513
|
-
|
|
558
|
+
chalk4.cyan(
|
|
514
559
|
` "Any feedback for the tools? Please use 'aiready bug' to report \u2764\uFE0F"`
|
|
515
560
|
)
|
|
516
561
|
);
|
|
517
562
|
console.log(
|
|
518
|
-
|
|
563
|
+
chalk4.dim(
|
|
519
564
|
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
|
|
520
565
|
)
|
|
521
566
|
);
|
|
522
|
-
const mapToUnifiedReport = (res, scoring) => {
|
|
523
|
-
const allResults = [];
|
|
524
|
-
const totalFilesSet = /* @__PURE__ */ new Set();
|
|
525
|
-
let criticalCount = 0;
|
|
526
|
-
let majorCount = 0;
|
|
527
|
-
res.summary.toolsRun.forEach((toolId) => {
|
|
528
|
-
const spokeRes = res[toolId];
|
|
529
|
-
if (!spokeRes || !spokeRes.results) return;
|
|
530
|
-
spokeRes.results.forEach((r) => {
|
|
531
|
-
totalFilesSet.add(r.fileName);
|
|
532
|
-
allResults.push(r);
|
|
533
|
-
r.issues?.forEach((i) => {
|
|
534
|
-
if (i.severity === Severity.Critical) criticalCount++;
|
|
535
|
-
if (i.severity === Severity.Major) majorCount++;
|
|
536
|
-
});
|
|
537
|
-
});
|
|
538
|
-
});
|
|
539
|
-
return {
|
|
540
|
-
...res,
|
|
541
|
-
results: allResults,
|
|
542
|
-
summary: {
|
|
543
|
-
...res.summary,
|
|
544
|
-
totalFiles: totalFilesSet.size,
|
|
545
|
-
criticalIssues: criticalCount,
|
|
546
|
-
majorIssues: majorCount
|
|
547
|
-
},
|
|
548
|
-
scoring
|
|
549
|
-
};
|
|
550
|
-
};
|
|
551
567
|
const outputData = {
|
|
552
568
|
...mapToUnifiedReport(results, scoringResult),
|
|
553
569
|
repository: repoMetadata
|
|
@@ -567,7 +583,7 @@ async function scanAction(directory, options) {
|
|
|
567
583
|
} else {
|
|
568
584
|
try {
|
|
569
585
|
writeFileSync(outputPath, JSON.stringify(outputData, null, 2));
|
|
570
|
-
console.log(
|
|
586
|
+
console.log(chalk4.dim(`\u2705 Report auto-persisted to ${outputPath}`));
|
|
571
587
|
} catch (err) {
|
|
572
588
|
void err;
|
|
573
589
|
}
|
|
@@ -588,7 +604,7 @@ async function scanAction(directory, options) {
|
|
|
588
604
|
const report = mapToUnifiedReport(results, scoringResult);
|
|
589
605
|
if (isCI && report.results && report.results.length > 0) {
|
|
590
606
|
console.log(
|
|
591
|
-
|
|
607
|
+
chalk4.cyan(
|
|
592
608
|
`
|
|
593
609
|
\u{1F4DD} Emitting GitHub Action annotations for ${report.results.length} issues...`
|
|
594
610
|
)
|
|
@@ -609,11 +625,11 @@ async function scanAction(directory, options) {
|
|
|
609
625
|
}
|
|
610
626
|
}
|
|
611
627
|
if (shouldFail) {
|
|
612
|
-
console.log(
|
|
628
|
+
console.log(chalk4.red(`
|
|
613
629
|
\u{1F6AB} SCAN FAILED: ${failReason}`));
|
|
614
630
|
process.exit(1);
|
|
615
631
|
} else {
|
|
616
|
-
console.log(
|
|
632
|
+
console.log(chalk4.green("\n\u2705 SCAN PASSED"));
|
|
617
633
|
}
|
|
618
634
|
}
|
|
619
635
|
} catch (error) {
|
|
@@ -625,7 +641,7 @@ var scanHelpText = `...`;
|
|
|
625
641
|
// src/commands/init.ts
|
|
626
642
|
import { writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
|
|
627
643
|
import { join } from "path";
|
|
628
|
-
import
|
|
644
|
+
import chalk5 from "chalk";
|
|
629
645
|
import { ToolName as ToolName2 } from "@aiready/core";
|
|
630
646
|
async function initAction(options) {
|
|
631
647
|
const fileExt = options.format === "js" ? "js" : "json";
|
|
@@ -633,7 +649,7 @@ async function initAction(options) {
|
|
|
633
649
|
const filePath = join(process.cwd(), fileName);
|
|
634
650
|
if (existsSync2(filePath) && !options.force) {
|
|
635
651
|
console.error(
|
|
636
|
-
|
|
652
|
+
chalk5.red(`Error: ${fileName} already exists. Use --force to overwrite.`)
|
|
637
653
|
);
|
|
638
654
|
process.exit(1);
|
|
639
655
|
}
|
|
@@ -720,10 +736,6 @@ async function initAction(options) {
|
|
|
720
736
|
...options.full ? { profile: "default" } : {}
|
|
721
737
|
},
|
|
722
738
|
...options.full ? {
|
|
723
|
-
output: {
|
|
724
|
-
format: fileExt,
|
|
725
|
-
file: "aiready-report.json"
|
|
726
|
-
},
|
|
727
739
|
visualizer: {
|
|
728
740
|
groupingDirs: ["packages", "src", "lib"],
|
|
729
741
|
graph: {
|
|
@@ -749,33 +761,33 @@ module.exports = ${JSON.stringify(
|
|
|
749
761
|
try {
|
|
750
762
|
writeFileSync2(filePath, content, "utf8");
|
|
751
763
|
console.log(
|
|
752
|
-
|
|
753
|
-
\u2705 Created default configuration: ${
|
|
764
|
+
chalk5.green(`
|
|
765
|
+
\u2705 Created default configuration: ${chalk5.bold(fileName)}`)
|
|
754
766
|
);
|
|
755
767
|
console.log(
|
|
756
|
-
|
|
768
|
+
chalk5.cyan("You can now fine-tune your settings and run AIReady with:")
|
|
757
769
|
);
|
|
758
|
-
console.log(
|
|
770
|
+
console.log(chalk5.white(` $ aiready scan
|
|
759
771
|
`));
|
|
760
772
|
} catch (error) {
|
|
761
|
-
console.error(
|
|
773
|
+
console.error(chalk5.red(`Failed to write configuration file: ${error}`));
|
|
762
774
|
process.exit(1);
|
|
763
775
|
}
|
|
764
776
|
}
|
|
765
777
|
|
|
766
778
|
// src/commands/patterns.ts
|
|
767
|
-
import
|
|
779
|
+
import chalk6 from "chalk";
|
|
768
780
|
import { resolve as resolvePath4 } from "path";
|
|
769
781
|
import {
|
|
770
782
|
loadMergedConfig as loadMergedConfig2,
|
|
771
783
|
handleJSONOutput as handleJSONOutput2,
|
|
772
784
|
handleCLIError as handleCLIError3,
|
|
773
|
-
getElapsedTime
|
|
785
|
+
getElapsedTime,
|
|
774
786
|
resolveOutputPath as resolveOutputPath2,
|
|
775
|
-
formatToolScore
|
|
787
|
+
formatToolScore
|
|
776
788
|
} from "@aiready/core";
|
|
777
789
|
async function patternsAction(directory, options) {
|
|
778
|
-
console.log(
|
|
790
|
+
console.log(chalk6.blue("\u{1F50D} Analyzing patterns...\n"));
|
|
779
791
|
const startTime = Date.now();
|
|
780
792
|
const resolvedDir = resolvePath4(process.cwd(), directory || ".");
|
|
781
793
|
try {
|
|
@@ -812,8 +824,10 @@ async function patternsAction(directory, options) {
|
|
|
812
824
|
cliOptions
|
|
813
825
|
);
|
|
814
826
|
const { analyzePatterns, generateSummary, calculatePatternScore } = await import("@aiready/pattern-detect");
|
|
815
|
-
const { results, duplicates } = await analyzePatterns(
|
|
816
|
-
|
|
827
|
+
const { results, duplicates } = await analyzePatterns(
|
|
828
|
+
finalOptions
|
|
829
|
+
);
|
|
830
|
+
const elapsedTime = getElapsedTime(startTime);
|
|
817
831
|
const summary = generateSummary(results);
|
|
818
832
|
let patternScore;
|
|
819
833
|
if (options.score) {
|
|
@@ -841,38 +855,38 @@ async function patternsAction(directory, options) {
|
|
|
841
855
|
const terminalWidth = process.stdout.columns || 80;
|
|
842
856
|
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
843
857
|
const divider = "\u2501".repeat(dividerWidth);
|
|
844
|
-
console.log(
|
|
845
|
-
console.log(
|
|
846
|
-
console.log(
|
|
858
|
+
console.log(chalk6.cyan(divider));
|
|
859
|
+
console.log(chalk6.bold.white(" PATTERN ANALYSIS SUMMARY"));
|
|
860
|
+
console.log(chalk6.cyan(divider) + "\n");
|
|
847
861
|
console.log(
|
|
848
|
-
|
|
862
|
+
chalk6.white(`\u{1F4C1} Files analyzed: ${chalk6.bold(results.length)}`)
|
|
849
863
|
);
|
|
850
864
|
console.log(
|
|
851
|
-
|
|
852
|
-
`\u26A0 Duplicate patterns found: ${
|
|
865
|
+
chalk6.yellow(
|
|
866
|
+
`\u26A0 Duplicate patterns found: ${chalk6.bold(summary.totalPatterns)}`
|
|
853
867
|
)
|
|
854
868
|
);
|
|
855
869
|
console.log(
|
|
856
|
-
|
|
857
|
-
`\u{1F4B0} Token cost (wasted): ${
|
|
870
|
+
chalk6.red(
|
|
871
|
+
`\u{1F4B0} Token cost (wasted): ${chalk6.bold(summary.totalTokenCost.toLocaleString())}`
|
|
858
872
|
)
|
|
859
873
|
);
|
|
860
874
|
console.log(
|
|
861
|
-
|
|
875
|
+
chalk6.gray(`\u23F1 Analysis time: ${chalk6.bold(elapsedTime + "s")}`)
|
|
862
876
|
);
|
|
863
877
|
const sortedTypes = Object.entries(summary.patternsByType || {}).filter(([, count]) => count > 0).sort(([, a], [, b]) => b - a);
|
|
864
878
|
if (sortedTypes.length > 0) {
|
|
865
|
-
console.log(
|
|
866
|
-
console.log(
|
|
867
|
-
console.log(
|
|
879
|
+
console.log(chalk6.cyan("\n" + divider));
|
|
880
|
+
console.log(chalk6.bold.white(" PATTERNS BY TYPE"));
|
|
881
|
+
console.log(chalk6.cyan(divider) + "\n");
|
|
868
882
|
sortedTypes.forEach(([type, count]) => {
|
|
869
|
-
console.log(` ${
|
|
883
|
+
console.log(` ${chalk6.white(type.padEnd(15))} ${chalk6.bold(count)}`);
|
|
870
884
|
});
|
|
871
885
|
}
|
|
872
886
|
if (summary.totalPatterns > 0 && duplicates.length > 0) {
|
|
873
|
-
console.log(
|
|
874
|
-
console.log(
|
|
875
|
-
console.log(
|
|
887
|
+
console.log(chalk6.cyan("\n" + divider));
|
|
888
|
+
console.log(chalk6.bold.white(" TOP DUPLICATE PATTERNS"));
|
|
889
|
+
console.log(chalk6.cyan(divider) + "\n");
|
|
876
890
|
const topDuplicates = [...duplicates].sort((a, b) => b.similarity - a.similarity).slice(0, 10);
|
|
877
891
|
topDuplicates.forEach((dup) => {
|
|
878
892
|
const severity = dup.similarity > 0.95 ? "CRITICAL" : dup.similarity > 0.9 ? "HIGH" : "MEDIUM";
|
|
@@ -880,26 +894,26 @@ async function patternsAction(directory, options) {
|
|
|
880
894
|
const file1Name = dup.file1.split("/").pop() || dup.file1;
|
|
881
895
|
const file2Name = dup.file2.split("/").pop() || dup.file2;
|
|
882
896
|
console.log(
|
|
883
|
-
`${severityIcon} ${severity}: ${
|
|
897
|
+
`${severityIcon} ${severity}: ${chalk6.bold(file1Name)} \u2194 ${chalk6.bold(file2Name)}`
|
|
884
898
|
);
|
|
885
899
|
console.log(
|
|
886
|
-
` Similarity: ${
|
|
900
|
+
` Similarity: ${chalk6.bold(Math.round(dup.similarity * 100) + "%")} | Wasted: ${chalk6.bold(dup.tokenCost.toLocaleString())} tokens each`
|
|
887
901
|
);
|
|
888
902
|
console.log(
|
|
889
|
-
` Lines: ${
|
|
903
|
+
` Lines: ${chalk6.cyan(dup.line1 + "-" + dup.endLine1)} \u2194 ${chalk6.cyan(dup.line2 + "-" + dup.endLine2)}
|
|
890
904
|
`
|
|
891
905
|
);
|
|
892
906
|
});
|
|
893
907
|
} else {
|
|
894
908
|
console.log(
|
|
895
|
-
|
|
909
|
+
chalk6.green("\n\u2728 Great! No duplicate patterns detected.\n")
|
|
896
910
|
);
|
|
897
911
|
}
|
|
898
912
|
if (patternScore) {
|
|
899
|
-
console.log(
|
|
900
|
-
console.log(
|
|
901
|
-
console.log(
|
|
902
|
-
console.log(
|
|
913
|
+
console.log(chalk6.cyan(divider));
|
|
914
|
+
console.log(chalk6.bold.white(" AI READINESS SCORE (Patterns)"));
|
|
915
|
+
console.log(chalk6.cyan(divider) + "\n");
|
|
916
|
+
console.log(formatToolScore(patternScore));
|
|
903
917
|
console.log();
|
|
904
918
|
}
|
|
905
919
|
}
|
|
@@ -915,18 +929,18 @@ EXAMPLES:
|
|
|
915
929
|
`;
|
|
916
930
|
|
|
917
931
|
// src/commands/context.ts
|
|
918
|
-
import
|
|
932
|
+
import chalk7 from "chalk";
|
|
919
933
|
import { resolve as resolvePath5 } from "path";
|
|
920
934
|
import {
|
|
921
935
|
loadMergedConfig as loadMergedConfig3,
|
|
922
936
|
handleJSONOutput as handleJSONOutput3,
|
|
923
937
|
handleCLIError as handleCLIError4,
|
|
924
|
-
getElapsedTime as
|
|
938
|
+
getElapsedTime as getElapsedTime2,
|
|
925
939
|
resolveOutputPath as resolveOutputPath3,
|
|
926
|
-
formatToolScore as
|
|
940
|
+
formatToolScore as formatToolScore2
|
|
927
941
|
} from "@aiready/core";
|
|
928
942
|
async function contextAction(directory, options) {
|
|
929
|
-
console.log(
|
|
943
|
+
console.log(chalk7.blue("\u{1F9E0} Analyzing context costs...\n"));
|
|
930
944
|
const startTime = Date.now();
|
|
931
945
|
const resolvedDir = resolvePath5(process.cwd(), directory || ".");
|
|
932
946
|
try {
|
|
@@ -966,7 +980,7 @@ async function contextAction(directory, options) {
|
|
|
966
980
|
console.log("");
|
|
967
981
|
const { analyzeContext, generateSummary, calculateContextScore } = await import("@aiready/context-analyzer");
|
|
968
982
|
const results = await analyzeContext(finalOptions);
|
|
969
|
-
const elapsedTime =
|
|
983
|
+
const elapsedTime = getElapsedTime2(startTime);
|
|
970
984
|
const summary = generateSummary(results);
|
|
971
985
|
let contextScore;
|
|
972
986
|
if (options.score) {
|
|
@@ -994,85 +1008,85 @@ async function contextAction(directory, options) {
|
|
|
994
1008
|
const terminalWidth = process.stdout.columns || 80;
|
|
995
1009
|
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
996
1010
|
const divider = "\u2501".repeat(dividerWidth);
|
|
997
|
-
console.log(
|
|
998
|
-
console.log(
|
|
999
|
-
console.log(
|
|
1011
|
+
console.log(chalk7.cyan(divider));
|
|
1012
|
+
console.log(chalk7.bold.white(" CONTEXT ANALYSIS SUMMARY"));
|
|
1013
|
+
console.log(chalk7.cyan(divider) + "\n");
|
|
1000
1014
|
console.log(
|
|
1001
|
-
|
|
1015
|
+
chalk7.white(`\u{1F4C1} Files analyzed: ${chalk7.bold(summary.totalFiles)}`)
|
|
1002
1016
|
);
|
|
1003
1017
|
console.log(
|
|
1004
|
-
|
|
1005
|
-
`\u{1F4CA} Total tokens: ${
|
|
1018
|
+
chalk7.white(
|
|
1019
|
+
`\u{1F4CA} Total tokens: ${chalk7.bold(summary.totalTokens.toLocaleString())}`
|
|
1006
1020
|
)
|
|
1007
1021
|
);
|
|
1008
1022
|
console.log(
|
|
1009
|
-
|
|
1010
|
-
`\u{1F4B0} Avg context budget: ${
|
|
1023
|
+
chalk7.yellow(
|
|
1024
|
+
`\u{1F4B0} Avg context budget: ${chalk7.bold(summary.avgContextBudget.toFixed(0))} tokens/file`
|
|
1011
1025
|
)
|
|
1012
1026
|
);
|
|
1013
1027
|
console.log(
|
|
1014
|
-
|
|
1028
|
+
chalk7.white(`\u23F1 Analysis time: ${chalk7.bold(elapsedTime + "s")}
|
|
1015
1029
|
`)
|
|
1016
1030
|
);
|
|
1017
1031
|
const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
|
|
1018
1032
|
if (totalIssues > 0) {
|
|
1019
|
-
console.log(
|
|
1033
|
+
console.log(chalk7.bold("\u26A0\uFE0F Issues Found:\n"));
|
|
1020
1034
|
if (summary.criticalIssues > 0) {
|
|
1021
1035
|
console.log(
|
|
1022
|
-
|
|
1036
|
+
chalk7.red(` \u{1F534} Critical: ${chalk7.bold(summary.criticalIssues)}`)
|
|
1023
1037
|
);
|
|
1024
1038
|
}
|
|
1025
1039
|
if (summary.majorIssues > 0) {
|
|
1026
1040
|
console.log(
|
|
1027
|
-
|
|
1041
|
+
chalk7.yellow(` \u{1F7E1} Major: ${chalk7.bold(summary.majorIssues)}`)
|
|
1028
1042
|
);
|
|
1029
1043
|
}
|
|
1030
1044
|
if (summary.minorIssues > 0) {
|
|
1031
1045
|
console.log(
|
|
1032
|
-
|
|
1046
|
+
chalk7.blue(` \u{1F535} Minor: ${chalk7.bold(summary.minorIssues)}`)
|
|
1033
1047
|
);
|
|
1034
1048
|
}
|
|
1035
1049
|
console.log(
|
|
1036
|
-
|
|
1050
|
+
chalk7.green(
|
|
1037
1051
|
`
|
|
1038
|
-
\u{1F4A1} Potential savings: ${
|
|
1052
|
+
\u{1F4A1} Potential savings: ${chalk7.bold(summary.totalPotentialSavings.toLocaleString())} tokens
|
|
1039
1053
|
`
|
|
1040
1054
|
)
|
|
1041
1055
|
);
|
|
1042
1056
|
} else {
|
|
1043
|
-
console.log(
|
|
1057
|
+
console.log(chalk7.green("\u2705 No significant issues found!\n"));
|
|
1044
1058
|
}
|
|
1045
1059
|
if (summary.deepFiles.length > 0) {
|
|
1046
|
-
console.log(
|
|
1060
|
+
console.log(chalk7.bold("\u{1F4CF} Deep Import Chains:\n"));
|
|
1047
1061
|
console.log(
|
|
1048
|
-
|
|
1062
|
+
chalk7.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`)
|
|
1049
1063
|
);
|
|
1050
1064
|
console.log(
|
|
1051
|
-
|
|
1065
|
+
chalk7.gray(` Maximum depth: ${summary.maxImportDepth}
|
|
1052
1066
|
`)
|
|
1053
1067
|
);
|
|
1054
1068
|
summary.deepFiles.slice(0, 10).forEach((item) => {
|
|
1055
1069
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1056
1070
|
console.log(
|
|
1057
|
-
` ${
|
|
1071
|
+
` ${chalk7.cyan("\u2192")} ${chalk7.white(fileName)} ${chalk7.dim(`(depth: ${item.depth})`)}`
|
|
1058
1072
|
);
|
|
1059
1073
|
});
|
|
1060
1074
|
console.log();
|
|
1061
1075
|
}
|
|
1062
1076
|
if (summary.fragmentedModules.length > 0) {
|
|
1063
|
-
console.log(
|
|
1077
|
+
console.log(chalk7.bold("\u{1F9E9} Fragmented Modules:\n"));
|
|
1064
1078
|
console.log(
|
|
1065
|
-
|
|
1079
|
+
chalk7.gray(
|
|
1066
1080
|
` Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(0)}%
|
|
1067
1081
|
`
|
|
1068
1082
|
)
|
|
1069
1083
|
);
|
|
1070
1084
|
summary.fragmentedModules.slice(0, 10).forEach((module) => {
|
|
1071
1085
|
console.log(
|
|
1072
|
-
` ${
|
|
1086
|
+
` ${chalk7.yellow("\u25CF")} ${chalk7.white(module.domain)} - ${chalk7.dim(`${module.files.length} files, ${(module.fragmentationScore * 100).toFixed(0)}% scattered`)}`
|
|
1073
1087
|
);
|
|
1074
1088
|
console.log(
|
|
1075
|
-
|
|
1089
|
+
chalk7.dim(
|
|
1076
1090
|
` Token cost: ${module.totalTokens.toLocaleString()}, Cohesion: ${(module.avgCohesion * 100).toFixed(0)}%`
|
|
1077
1091
|
)
|
|
1078
1092
|
);
|
|
@@ -1080,9 +1094,9 @@ async function contextAction(directory, options) {
|
|
|
1080
1094
|
console.log();
|
|
1081
1095
|
}
|
|
1082
1096
|
if (summary.lowCohesionFiles.length > 0) {
|
|
1083
|
-
console.log(
|
|
1097
|
+
console.log(chalk7.bold("\u{1F500} Low Cohesion Files:\n"));
|
|
1084
1098
|
console.log(
|
|
1085
|
-
|
|
1099
|
+
chalk7.gray(
|
|
1086
1100
|
` Average cohesion: ${(summary.avgCohesion * 100).toFixed(0)}%
|
|
1087
1101
|
`
|
|
1088
1102
|
)
|
|
@@ -1090,29 +1104,29 @@ async function contextAction(directory, options) {
|
|
|
1090
1104
|
summary.lowCohesionFiles.slice(0, 10).forEach((item) => {
|
|
1091
1105
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1092
1106
|
const scorePercent = (item.score * 100).toFixed(0);
|
|
1093
|
-
const color = item.score < 0.4 ?
|
|
1107
|
+
const color = item.score < 0.4 ? chalk7.red : chalk7.yellow;
|
|
1094
1108
|
console.log(
|
|
1095
|
-
` ${color("\u25CB")} ${
|
|
1109
|
+
` ${color("\u25CB")} ${chalk7.white(fileName)} ${chalk7.dim(`(${scorePercent}% cohesion)`)}`
|
|
1096
1110
|
);
|
|
1097
1111
|
});
|
|
1098
1112
|
console.log();
|
|
1099
1113
|
}
|
|
1100
1114
|
if (summary.topExpensiveFiles.length > 0) {
|
|
1101
|
-
console.log(
|
|
1115
|
+
console.log(chalk7.bold("\u{1F4B8} Most Expensive Files (Context Budget):\n"));
|
|
1102
1116
|
summary.topExpensiveFiles.slice(0, 10).forEach((item) => {
|
|
1103
1117
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1104
|
-
const severityColor = item.severity === "critical" ?
|
|
1118
|
+
const severityColor = item.severity === "critical" ? chalk7.red : item.severity === "major" ? chalk7.yellow : chalk7.blue;
|
|
1105
1119
|
console.log(
|
|
1106
|
-
` ${severityColor("\u25CF")} ${
|
|
1120
|
+
` ${severityColor("\u25CF")} ${chalk7.white(fileName)} ${chalk7.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`
|
|
1107
1121
|
);
|
|
1108
1122
|
});
|
|
1109
1123
|
console.log();
|
|
1110
1124
|
}
|
|
1111
1125
|
if (contextScore) {
|
|
1112
|
-
console.log(
|
|
1113
|
-
console.log(
|
|
1114
|
-
console.log(
|
|
1115
|
-
console.log(
|
|
1126
|
+
console.log(chalk7.cyan(divider));
|
|
1127
|
+
console.log(chalk7.bold.white(" AI READINESS SCORE (Context)"));
|
|
1128
|
+
console.log(chalk7.cyan(divider) + "\n");
|
|
1129
|
+
console.log(formatToolScore2(contextScore));
|
|
1116
1130
|
console.log();
|
|
1117
1131
|
}
|
|
1118
1132
|
}
|
|
@@ -1122,19 +1136,19 @@ async function contextAction(directory, options) {
|
|
|
1122
1136
|
}
|
|
1123
1137
|
|
|
1124
1138
|
// src/commands/consistency.ts
|
|
1125
|
-
import
|
|
1139
|
+
import chalk8 from "chalk";
|
|
1126
1140
|
import { writeFileSync as writeFileSync3 } from "fs";
|
|
1127
1141
|
import { resolve as resolvePath6 } from "path";
|
|
1128
1142
|
import {
|
|
1129
1143
|
loadMergedConfig as loadMergedConfig4,
|
|
1130
1144
|
handleJSONOutput as handleJSONOutput4,
|
|
1131
1145
|
handleCLIError as handleCLIError5,
|
|
1132
|
-
getElapsedTime as
|
|
1146
|
+
getElapsedTime as getElapsedTime3,
|
|
1133
1147
|
resolveOutputPath as resolveOutputPath4,
|
|
1134
|
-
formatToolScore as
|
|
1148
|
+
formatToolScore as formatToolScore3
|
|
1135
1149
|
} from "@aiready/core";
|
|
1136
1150
|
async function consistencyAction(directory, options) {
|
|
1137
|
-
console.log(
|
|
1151
|
+
console.log(chalk8.blue("\u{1F50D} Analyzing consistency...\n"));
|
|
1138
1152
|
const startTime = Date.now();
|
|
1139
1153
|
const resolvedDir = resolvePath6(process.cwd(), directory || ".");
|
|
1140
1154
|
try {
|
|
@@ -1158,7 +1172,7 @@ async function consistencyAction(directory, options) {
|
|
|
1158
1172
|
});
|
|
1159
1173
|
const { analyzeConsistency, calculateConsistencyScore } = await import("@aiready/consistency");
|
|
1160
1174
|
const report = await analyzeConsistency(finalOptions);
|
|
1161
|
-
const elapsedTime =
|
|
1175
|
+
const elapsedTime = getElapsedTime3(startTime);
|
|
1162
1176
|
let consistencyScore;
|
|
1163
1177
|
if (options.score) {
|
|
1164
1178
|
const issues = report.results?.flatMap((r) => r.issues) || [];
|
|
@@ -1196,23 +1210,23 @@ async function consistencyAction(directory, options) {
|
|
|
1196
1210
|
resolvedDir
|
|
1197
1211
|
);
|
|
1198
1212
|
writeFileSync3(outputPath, markdown);
|
|
1199
|
-
console.log(
|
|
1213
|
+
console.log(chalk8.green(`\u2705 Report saved to ${outputPath}`));
|
|
1200
1214
|
} else {
|
|
1201
|
-
console.log(
|
|
1215
|
+
console.log(chalk8.bold("\n\u{1F4CA} Summary\n"));
|
|
1202
1216
|
console.log(
|
|
1203
|
-
`Files Analyzed: ${
|
|
1217
|
+
`Files Analyzed: ${chalk8.cyan(report.summary.filesAnalyzed)}`
|
|
1204
1218
|
);
|
|
1205
|
-
console.log(`Total Issues: ${
|
|
1206
|
-
console.log(` Naming: ${
|
|
1207
|
-
console.log(` Patterns: ${
|
|
1219
|
+
console.log(`Total Issues: ${chalk8.yellow(report.summary.totalIssues)}`);
|
|
1220
|
+
console.log(` Naming: ${chalk8.yellow(report.summary.namingIssues)}`);
|
|
1221
|
+
console.log(` Patterns: ${chalk8.yellow(report.summary.patternIssues)}`);
|
|
1208
1222
|
console.log(
|
|
1209
|
-
` Architecture: ${
|
|
1223
|
+
` Architecture: ${chalk8.yellow(report.summary.architectureIssues || 0)}`
|
|
1210
1224
|
);
|
|
1211
|
-
console.log(`Analysis Time: ${
|
|
1225
|
+
console.log(`Analysis Time: ${chalk8.gray(elapsedTime + "s")}
|
|
1212
1226
|
`);
|
|
1213
1227
|
if (report.summary.totalIssues === 0) {
|
|
1214
1228
|
console.log(
|
|
1215
|
-
|
|
1229
|
+
chalk8.green(
|
|
1216
1230
|
"\u2728 No consistency issues found! Your codebase is well-maintained.\n"
|
|
1217
1231
|
)
|
|
1218
1232
|
);
|
|
@@ -1224,20 +1238,20 @@ async function consistencyAction(directory, options) {
|
|
|
1224
1238
|
(r) => r.issues.some((i) => i.category === "patterns")
|
|
1225
1239
|
);
|
|
1226
1240
|
if (namingResults.length > 0) {
|
|
1227
|
-
console.log(
|
|
1241
|
+
console.log(chalk8.bold("\u{1F3F7}\uFE0F Naming Issues\n"));
|
|
1228
1242
|
let shown = 0;
|
|
1229
1243
|
for (const result of namingResults) {
|
|
1230
1244
|
if (shown >= 5) break;
|
|
1231
1245
|
for (const issue of result.issues) {
|
|
1232
1246
|
if (shown >= 5) break;
|
|
1233
|
-
const severityColor = issue.severity === "critical" ?
|
|
1247
|
+
const severityColor = issue.severity === "critical" ? chalk8.red : issue.severity === "major" ? chalk8.yellow : issue.severity === "minor" ? chalk8.blue : chalk8.gray;
|
|
1234
1248
|
console.log(
|
|
1235
|
-
`${severityColor(issue.severity.toUpperCase())} ${
|
|
1249
|
+
`${severityColor(issue.severity.toUpperCase())} ${chalk8.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1236
1250
|
);
|
|
1237
1251
|
console.log(` ${issue.message}`);
|
|
1238
1252
|
if (issue.suggestion) {
|
|
1239
1253
|
console.log(
|
|
1240
|
-
` ${
|
|
1254
|
+
` ${chalk8.dim("\u2192")} ${chalk8.italic(issue.suggestion)}`
|
|
1241
1255
|
);
|
|
1242
1256
|
}
|
|
1243
1257
|
console.log();
|
|
@@ -1246,25 +1260,25 @@ async function consistencyAction(directory, options) {
|
|
|
1246
1260
|
}
|
|
1247
1261
|
const remaining = namingResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
1248
1262
|
if (remaining > 0) {
|
|
1249
|
-
console.log(
|
|
1263
|
+
console.log(chalk8.dim(` ... and ${remaining} more issues
|
|
1250
1264
|
`));
|
|
1251
1265
|
}
|
|
1252
1266
|
}
|
|
1253
1267
|
if (patternResults.length > 0) {
|
|
1254
|
-
console.log(
|
|
1268
|
+
console.log(chalk8.bold("\u{1F504} Pattern Issues\n"));
|
|
1255
1269
|
let shown = 0;
|
|
1256
1270
|
for (const result of patternResults) {
|
|
1257
1271
|
if (shown >= 5) break;
|
|
1258
1272
|
for (const issue of result.issues) {
|
|
1259
1273
|
if (shown >= 5) break;
|
|
1260
|
-
const severityColor = issue.severity === "critical" ?
|
|
1274
|
+
const severityColor = issue.severity === "critical" ? chalk8.red : issue.severity === "major" ? chalk8.yellow : issue.severity === "minor" ? chalk8.blue : chalk8.gray;
|
|
1261
1275
|
console.log(
|
|
1262
|
-
`${severityColor(issue.severity.toUpperCase())} ${
|
|
1276
|
+
`${severityColor(issue.severity.toUpperCase())} ${chalk8.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1263
1277
|
);
|
|
1264
1278
|
console.log(` ${issue.message}`);
|
|
1265
1279
|
if (issue.suggestion) {
|
|
1266
1280
|
console.log(
|
|
1267
|
-
` ${
|
|
1281
|
+
` ${chalk8.dim("\u2192")} ${chalk8.italic(issue.suggestion)}`
|
|
1268
1282
|
);
|
|
1269
1283
|
}
|
|
1270
1284
|
console.log();
|
|
@@ -1273,12 +1287,12 @@ async function consistencyAction(directory, options) {
|
|
|
1273
1287
|
}
|
|
1274
1288
|
const remaining = patternResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
1275
1289
|
if (remaining > 0) {
|
|
1276
|
-
console.log(
|
|
1290
|
+
console.log(chalk8.dim(` ... and ${remaining} more issues
|
|
1277
1291
|
`));
|
|
1278
1292
|
}
|
|
1279
1293
|
}
|
|
1280
1294
|
if (report.recommendations.length > 0) {
|
|
1281
|
-
console.log(
|
|
1295
|
+
console.log(chalk8.bold("\u{1F4A1} Recommendations\n"));
|
|
1282
1296
|
report.recommendations.forEach((rec, i) => {
|
|
1283
1297
|
console.log(`${i + 1}. ${rec}`);
|
|
1284
1298
|
});
|
|
@@ -1286,8 +1300,8 @@ async function consistencyAction(directory, options) {
|
|
|
1286
1300
|
}
|
|
1287
1301
|
}
|
|
1288
1302
|
if (consistencyScore) {
|
|
1289
|
-
console.log(
|
|
1290
|
-
console.log(
|
|
1303
|
+
console.log(chalk8.bold("\n\u{1F4CA} AI Readiness Score (Consistency)\n"));
|
|
1304
|
+
console.log(formatToolScore3(consistencyScore));
|
|
1291
1305
|
console.log();
|
|
1292
1306
|
}
|
|
1293
1307
|
}
|
|
@@ -1297,7 +1311,7 @@ async function consistencyAction(directory, options) {
|
|
|
1297
1311
|
}
|
|
1298
1312
|
|
|
1299
1313
|
// src/commands/visualize.ts
|
|
1300
|
-
import
|
|
1314
|
+
import chalk9 from "chalk";
|
|
1301
1315
|
import { writeFileSync as writeFileSync4, readFileSync as readFileSync3, existsSync as existsSync3, copyFileSync } from "fs";
|
|
1302
1316
|
import { resolve as resolvePath7 } from "path";
|
|
1303
1317
|
import { spawn } from "child_process";
|
|
@@ -1312,12 +1326,12 @@ async function visualizeAction(directory, options) {
|
|
|
1312
1326
|
if (latestScan) {
|
|
1313
1327
|
reportPath = latestScan;
|
|
1314
1328
|
console.log(
|
|
1315
|
-
|
|
1329
|
+
chalk9.dim(`Found latest report: ${latestScan.split("/").pop()}`)
|
|
1316
1330
|
);
|
|
1317
1331
|
} else {
|
|
1318
|
-
console.error(
|
|
1332
|
+
console.error(chalk9.red("\u274C No AI readiness report found"));
|
|
1319
1333
|
console.log(
|
|
1320
|
-
|
|
1334
|
+
chalk9.dim(
|
|
1321
1335
|
`
|
|
1322
1336
|
Generate a report with:
|
|
1323
1337
|
aiready scan --output json
|
|
@@ -1447,19 +1461,19 @@ Or specify a custom report:
|
|
|
1447
1461
|
return;
|
|
1448
1462
|
} else {
|
|
1449
1463
|
console.log(
|
|
1450
|
-
|
|
1464
|
+
chalk9.yellow(
|
|
1451
1465
|
"\u26A0\uFE0F Dev server not available (requires local @aiready/visualizer with web assets)."
|
|
1452
1466
|
)
|
|
1453
1467
|
);
|
|
1454
1468
|
console.log(
|
|
1455
|
-
|
|
1469
|
+
chalk9.cyan(" Falling back to static HTML generation...\n")
|
|
1456
1470
|
);
|
|
1457
1471
|
useDevMode = false;
|
|
1458
1472
|
}
|
|
1459
1473
|
} catch (err) {
|
|
1460
1474
|
console.error("Failed to start dev server:", err);
|
|
1461
1475
|
console.log(
|
|
1462
|
-
|
|
1476
|
+
chalk9.cyan(" Falling back to static HTML generation...\n")
|
|
1463
1477
|
);
|
|
1464
1478
|
useDevMode = false;
|
|
1465
1479
|
}
|
|
@@ -1469,7 +1483,7 @@ Or specify a custom report:
|
|
|
1469
1483
|
const defaultOutput = "visualization.html";
|
|
1470
1484
|
const outPath = resolvePath7(dirPath, options.output || defaultOutput);
|
|
1471
1485
|
writeFileSync4(outPath, html, "utf8");
|
|
1472
|
-
console.log(
|
|
1486
|
+
console.log(chalk9.green(`\u2705 Visualization written to: ${outPath}`));
|
|
1473
1487
|
if (options.open || options.serve) {
|
|
1474
1488
|
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
1475
1489
|
if (options.serve) {
|
|
@@ -1499,7 +1513,7 @@ Or specify a custom report:
|
|
|
1499
1513
|
server.listen(port, () => {
|
|
1500
1514
|
const addr = `http://localhost:${port}/`;
|
|
1501
1515
|
console.log(
|
|
1502
|
-
|
|
1516
|
+
chalk9.cyan(`\u{1F310} Local visualization server running at ${addr}`)
|
|
1503
1517
|
);
|
|
1504
1518
|
spawn(opener, [`"${addr}"`], { shell: true });
|
|
1505
1519
|
});
|
|
@@ -1546,14 +1560,14 @@ NOTES:
|
|
|
1546
1560
|
`;
|
|
1547
1561
|
|
|
1548
1562
|
// src/commands/shared/standard-tool-actions.ts
|
|
1549
|
-
import
|
|
1563
|
+
import chalk10 from "chalk";
|
|
1550
1564
|
|
|
1551
1565
|
// src/commands/agent-grounding.ts
|
|
1552
|
-
import
|
|
1566
|
+
import chalk11 from "chalk";
|
|
1553
1567
|
import { loadConfig as loadConfig2, mergeConfigWithDefaults as mergeConfigWithDefaults2 } from "@aiready/core";
|
|
1554
1568
|
|
|
1555
1569
|
// src/commands/testability.ts
|
|
1556
|
-
import
|
|
1570
|
+
import chalk12 from "chalk";
|
|
1557
1571
|
import { loadConfig as loadConfig3, mergeConfigWithDefaults as mergeConfigWithDefaults3 } from "@aiready/core";
|
|
1558
1572
|
async function testabilityAction(directory, options) {
|
|
1559
1573
|
const { analyzeTestability, calculateTestabilityScore } = await import("@aiready/testability");
|
|
@@ -1578,28 +1592,28 @@ async function testabilityAction(directory, options) {
|
|
|
1578
1592
|
"blind-risk": "\u{1F480}"
|
|
1579
1593
|
};
|
|
1580
1594
|
const safetyColors = {
|
|
1581
|
-
safe:
|
|
1582
|
-
"moderate-risk":
|
|
1583
|
-
"high-risk":
|
|
1584
|
-
"blind-risk":
|
|
1595
|
+
safe: chalk12.green,
|
|
1596
|
+
"moderate-risk": chalk12.yellow,
|
|
1597
|
+
"high-risk": chalk12.red,
|
|
1598
|
+
"blind-risk": chalk12.bgRed.white
|
|
1585
1599
|
};
|
|
1586
1600
|
const safety = report.summary.aiChangeSafetyRating;
|
|
1587
1601
|
const icon = safetyIcons[safety] ?? "\u2753";
|
|
1588
|
-
const color = safetyColors[safety] ??
|
|
1602
|
+
const color = safetyColors[safety] ?? chalk12.white;
|
|
1589
1603
|
console.log(
|
|
1590
|
-
` \u{1F9EA} Testability: ${
|
|
1604
|
+
` \u{1F9EA} Testability: ${chalk12.bold(scoring.score + "/100")} (${report.summary.rating})`
|
|
1591
1605
|
);
|
|
1592
1606
|
console.log(
|
|
1593
1607
|
` AI Change Safety: ${color(`${icon} ${safety.toUpperCase()}`)}`
|
|
1594
1608
|
);
|
|
1595
1609
|
console.log(
|
|
1596
|
-
|
|
1610
|
+
chalk12.dim(
|
|
1597
1611
|
` Coverage: ${Math.round(report.summary.coverageRatio * 100)}% (${report.rawData.testFiles} test / ${report.rawData.sourceFiles} source files)`
|
|
1598
1612
|
)
|
|
1599
1613
|
);
|
|
1600
1614
|
if (safety === "blind-risk") {
|
|
1601
1615
|
console.log(
|
|
1602
|
-
|
|
1616
|
+
chalk12.red.bold(
|
|
1603
1617
|
"\n \u26A0\uFE0F NO TESTS \u2014 AI changes to this codebase are completely unverifiable!\n"
|
|
1604
1618
|
)
|
|
1605
1619
|
);
|
|
@@ -1611,7 +1625,7 @@ async function testabilityAction(directory, options) {
|
|
|
1611
1625
|
import { changeAmplificationAction } from "@aiready/change-amplification/dist/cli.js";
|
|
1612
1626
|
|
|
1613
1627
|
// src/commands/bug.ts
|
|
1614
|
-
import
|
|
1628
|
+
import chalk13 from "chalk";
|
|
1615
1629
|
import { execSync } from "child_process";
|
|
1616
1630
|
async function bugAction(message, options) {
|
|
1617
1631
|
const repoUrl = "https://github.com/caopengau/aiready-cli";
|
|
@@ -1629,35 +1643,35 @@ Generated via AIReady CLI 'bug' command.
|
|
|
1629
1643
|
Type: ${type}
|
|
1630
1644
|
`.trim();
|
|
1631
1645
|
if (options.submit) {
|
|
1632
|
-
console.log(
|
|
1646
|
+
console.log(chalk13.blue("\u{1F680} Submitting issue via GitHub CLI...\n"));
|
|
1633
1647
|
try {
|
|
1634
1648
|
execSync("gh auth status", { stdio: "ignore" });
|
|
1635
1649
|
const command = `gh issue create --repo ${repoSlug} --title ${JSON.stringify(title)} --body ${JSON.stringify(body)} --label ${label}`;
|
|
1636
1650
|
const output = execSync(command, { encoding: "utf8" }).trim();
|
|
1637
|
-
console.log(
|
|
1638
|
-
console.log(
|
|
1651
|
+
console.log(chalk13.green("\u2705 Issue Created Successfully!"));
|
|
1652
|
+
console.log(chalk13.cyan(output));
|
|
1639
1653
|
return;
|
|
1640
|
-
} catch
|
|
1641
|
-
console.error(
|
|
1654
|
+
} catch {
|
|
1655
|
+
console.error(chalk13.red("\n\u274C Failed to submit via gh CLI."));
|
|
1642
1656
|
console.log(
|
|
1643
|
-
|
|
1657
|
+
chalk13.yellow(
|
|
1644
1658
|
' Make sure gh is installed and run "gh auth login".\n'
|
|
1645
1659
|
)
|
|
1646
1660
|
);
|
|
1647
|
-
console.log(
|
|
1661
|
+
console.log(chalk13.dim(" Falling back to URL generation..."));
|
|
1648
1662
|
}
|
|
1649
1663
|
}
|
|
1650
1664
|
const template = type === "bug" ? "bug_report.md" : type === "feature" ? "feature_request.md" : "new_metric_idea.md";
|
|
1651
1665
|
const fullUrl = `${repoUrl}/issues/new?title=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}&labels=${label}&template=${template}`;
|
|
1652
|
-
console.log(
|
|
1653
|
-
console.log(
|
|
1654
|
-
console.log(
|
|
1655
|
-
console.log(
|
|
1656
|
-
console.log(
|
|
1657
|
-
console.log(
|
|
1658
|
-
console.log(
|
|
1666
|
+
console.log(chalk13.green("\u{1F680} Issue Draft Prepared!\n"));
|
|
1667
|
+
console.log(chalk13.bold("Title: ") + title);
|
|
1668
|
+
console.log(chalk13.bold("Type: ") + type);
|
|
1669
|
+
console.log(chalk13.bold("\nClick the link below to submit this issue:"));
|
|
1670
|
+
console.log(chalk13.cyan(fullUrl));
|
|
1671
|
+
console.log(chalk13.bold("\n\u{1F916} [FOR AI AGENTS]:"));
|
|
1672
|
+
console.log(chalk13.dim(" You have successfully prepared a report."));
|
|
1659
1673
|
console.log(
|
|
1660
|
-
|
|
1674
|
+
chalk13.dim(
|
|
1661
1675
|
" Please present the URL above to the user so they can finalize the submission."
|
|
1662
1676
|
)
|
|
1663
1677
|
);
|
|
@@ -1666,14 +1680,14 @@ Type: ${type}
|
|
|
1666
1680
|
const bugUrl = `${repoUrl}/issues/new?template=bug_report.md`;
|
|
1667
1681
|
const featureUrl = `${repoUrl}/issues/new?template=feature_request.md`;
|
|
1668
1682
|
const metricUrl = `${repoUrl}/issues/new?template=new_metric_idea.md`;
|
|
1669
|
-
console.log(
|
|
1670
|
-
console.log(` Report a Bug: ${
|
|
1671
|
-
console.log(` Request a Feature: ${
|
|
1672
|
-
console.log(` Suggest a Metric: ${
|
|
1673
|
-
console.log(
|
|
1674
|
-
console.log(
|
|
1683
|
+
console.log(chalk13.blue("\u{1F4AC} Feedback & Bug Reports\n"));
|
|
1684
|
+
console.log(` Report a Bug: ${chalk13.cyan(bugUrl)}`);
|
|
1685
|
+
console.log(` Request a Feature: ${chalk13.cyan(featureUrl)}`);
|
|
1686
|
+
console.log(` Suggest a Metric: ${chalk13.cyan(metricUrl)}`);
|
|
1687
|
+
console.log(chalk13.bold("\n\u{1F916} [FOR AI AGENTS]:"));
|
|
1688
|
+
console.log(chalk13.dim(" To prepare a specific report, run:"));
|
|
1675
1689
|
console.log(
|
|
1676
|
-
|
|
1690
|
+
chalk13.cyan(
|
|
1677
1691
|
' aiready bug "your description here" --type bug|feature|metric'
|
|
1678
1692
|
)
|
|
1679
1693
|
);
|