@aiready/cli 0.14.2 → 0.14.4
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/.aiready/aiready-report-20260314-164626.json +2 -5
- package/.aiready/aiready-report-20260314-164741.json +2 -5
- package/.turbo/turbo-build.log +29 -28
- package/.turbo/turbo-lint.log +0 -32
- package/.turbo/turbo-test.log +35 -125
- package/aiready-report.json +30703 -0
- package/dist/cli.js +415 -378
- package/dist/cli.mjs +358 -320
- package/package.json +12 -12
- package/packages/core/src/.aiready/aiready-report-20260314-161145.json +4 -10
- package/packages/core/src/.aiready/aiready-report-20260314-161152.json +10 -28
- package/packages/pattern-detect/src/.aiready/aiready-report-20260314-161139.json +4 -10
- package/src/.aiready/aiready-report-20260312-103623.json +3 -9
- package/src/.aiready/aiready-report-20260312-110843.json +3 -9
- package/src/.aiready/aiready-report-20260312-110955.json +3 -9
- package/src/.aiready/aiready-report-20260314-203209.json +3 -9
- package/src/.aiready/aiready-report-20260314-203736.json +3 -9
- package/src/.aiready/aiready-report-20260314-203857.json +3 -9
- package/src/.aiready/aiready-report-20260314-204047.json +3 -9
- 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/cli.ts +2 -1
- package/src/commands/__tests__/consistency.test.ts +3 -0
- package/src/commands/__tests__/extra-commands.test.ts +29 -38
- package/src/commands/__tests__/init.test.ts +56 -0
- package/src/commands/__tests__/scan.test.ts +4 -2
- package/src/commands/__tests__/upload.test.ts +0 -1
- package/src/commands/__tests__/visualize.test.ts +3 -7
- package/src/commands/ai-signal-clarity.ts +1 -56
- package/src/commands/bug.ts +1 -2
- package/src/commands/deps-health.ts +1 -65
- package/src/commands/doc-drift.ts +1 -62
- package/src/commands/init.ts +58 -2
- package/src/commands/patterns.ts +3 -1
- package/src/commands/report-formatter.ts +128 -0
- package/src/commands/scan.ts +29 -120
- package/src/commands/shared/configured-tool-action.ts +35 -0
- package/src/commands/shared/standard-tool-actions.ts +126 -0
- package/src/commands/upload.ts +15 -13
- package/src/commands/visualize.ts +11 -4
- package/src/index.ts +18 -3
- package/src/utils/helpers.ts +86 -37
- package/vitest.config.ts +5 -12
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,48 +20,23 @@ 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";
|
|
32
28
|
|
|
33
29
|
// src/utils/helpers.ts
|
|
34
30
|
import { resolve as resolvePath } from "path";
|
|
35
|
-
import { existsSync,
|
|
31
|
+
import { existsSync, readFileSync } from "fs";
|
|
36
32
|
import chalk from "chalk";
|
|
33
|
+
import { loadConfig, mergeConfigWithDefaults } from "@aiready/core";
|
|
34
|
+
import { findLatestReport } from "@aiready/core";
|
|
37
35
|
function getReportTimestamp() {
|
|
38
36
|
const now = /* @__PURE__ */ new Date();
|
|
39
37
|
const pad = (n) => String(n).padStart(2, "0");
|
|
40
38
|
return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
|
|
41
39
|
}
|
|
42
|
-
function findLatestScanReport(dirPath) {
|
|
43
|
-
const aireadyDir = resolvePath(dirPath, ".aiready");
|
|
44
|
-
if (!existsSync(aireadyDir)) {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
let files = readdirSync(aireadyDir).filter(
|
|
48
|
-
(f) => f.startsWith("aiready-report-") && f.endsWith(".json")
|
|
49
|
-
);
|
|
50
|
-
if (files.length === 0) {
|
|
51
|
-
files = readdirSync(aireadyDir).filter(
|
|
52
|
-
(f) => f.startsWith("aiready-scan-") && f.endsWith(".json")
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
if (files.length === 0) {
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
const sortedFiles = files.map((f) => ({
|
|
59
|
-
name: f,
|
|
60
|
-
path: resolvePath(aireadyDir, f),
|
|
61
|
-
mtime: statSync(resolvePath(aireadyDir, f)).mtime
|
|
62
|
-
})).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
63
|
-
return sortedFiles[0].path;
|
|
64
|
-
}
|
|
65
40
|
async function warnIfGraphCapExceeded(report, dirPath) {
|
|
66
41
|
try {
|
|
67
42
|
const { loadConfig: loadConfig4 } = await import("@aiready/core");
|
|
@@ -148,10 +123,102 @@ function generateMarkdownReport(report, elapsedTime) {
|
|
|
148
123
|
return markdown;
|
|
149
124
|
}
|
|
150
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
|
+
|
|
151
218
|
// src/commands/upload.ts
|
|
152
219
|
import fs from "fs";
|
|
153
220
|
import { resolve as resolvePath2 } from "path";
|
|
154
|
-
import
|
|
221
|
+
import chalk3 from "chalk";
|
|
155
222
|
import { handleCLIError } from "@aiready/core";
|
|
156
223
|
async function uploadAction(file, options) {
|
|
157
224
|
const startTime = Date.now();
|
|
@@ -159,31 +226,31 @@ async function uploadAction(file, options) {
|
|
|
159
226
|
const serverUrl = options.server || process.env.AIREADY_SERVER || "https://dev.platform.getaiready.dev";
|
|
160
227
|
const apiKey = options.apiKey || process.env.AIREADY_API_KEY;
|
|
161
228
|
if (!apiKey) {
|
|
162
|
-
console.error(
|
|
229
|
+
console.error(chalk3.red("\u274C API Key is required for upload."));
|
|
163
230
|
console.log(
|
|
164
|
-
|
|
231
|
+
chalk3.dim(
|
|
165
232
|
" Set AIREADY_API_KEY environment variable or use --api-key flag."
|
|
166
233
|
)
|
|
167
234
|
);
|
|
168
235
|
console.log(
|
|
169
|
-
|
|
236
|
+
chalk3.dim(
|
|
170
237
|
" Get an API key from https://platform.getaiready.dev/dashboard"
|
|
171
238
|
)
|
|
172
239
|
);
|
|
173
240
|
process.exit(1);
|
|
174
241
|
}
|
|
175
242
|
if (!fs.existsSync(filePath)) {
|
|
176
|
-
console.error(
|
|
243
|
+
console.error(chalk3.red(`\u274C File not found: ${filePath}`));
|
|
177
244
|
process.exit(1);
|
|
178
245
|
}
|
|
179
246
|
try {
|
|
180
|
-
console.log(
|
|
181
|
-
console.log(
|
|
247
|
+
console.log(chalk3.blue(`\u{1F680} Uploading report to ${serverUrl}...`));
|
|
248
|
+
console.log(chalk3.dim(` Reading report from ${filePath}...`));
|
|
182
249
|
const reportContent = fs.readFileSync(filePath, "utf-8");
|
|
183
250
|
const reportData = JSON.parse(reportContent);
|
|
184
|
-
console.log(
|
|
251
|
+
console.log(chalk3.dim(` Successfully parsed report JSON.`));
|
|
185
252
|
const repoId = options.repoId || reportData.repository?.repoId;
|
|
186
|
-
const
|
|
253
|
+
const response = await fetch(`${serverUrl}/api/analysis/upload`, {
|
|
187
254
|
method: "POST",
|
|
188
255
|
headers: {
|
|
189
256
|
"Content-Type": "application/json",
|
|
@@ -195,46 +262,48 @@ async function uploadAction(file, options) {
|
|
|
195
262
|
// Might be null, server will handle mapping
|
|
196
263
|
})
|
|
197
264
|
});
|
|
198
|
-
const contentType =
|
|
199
|
-
let
|
|
265
|
+
const contentType = response.headers.get("content-type");
|
|
266
|
+
let uploadResult = {};
|
|
200
267
|
if (contentType?.includes("application/json")) {
|
|
201
|
-
|
|
268
|
+
uploadResult = await response.json();
|
|
202
269
|
} else {
|
|
203
|
-
const text = await
|
|
204
|
-
|
|
270
|
+
const text = await response.text();
|
|
271
|
+
uploadResult = { error: text || response.statusText };
|
|
205
272
|
}
|
|
206
|
-
if (!
|
|
273
|
+
if (!response.ok) {
|
|
207
274
|
console.error(
|
|
208
|
-
|
|
275
|
+
chalk3.red(
|
|
276
|
+
`\u274C Upload failed: ${uploadResult.error || response.statusText}`
|
|
277
|
+
)
|
|
209
278
|
);
|
|
210
279
|
if (contentType?.includes("text/html")) {
|
|
211
280
|
console.log(
|
|
212
|
-
|
|
281
|
+
chalk3.yellow(
|
|
213
282
|
" Note: Received an HTML response. This often indicates a redirect (e.g., to a login page) or a server error."
|
|
214
283
|
)
|
|
215
284
|
);
|
|
216
|
-
if (
|
|
285
|
+
if (uploadResult.error?.includes("Redirecting")) {
|
|
217
286
|
console.log(
|
|
218
|
-
|
|
287
|
+
chalk3.dim(
|
|
219
288
|
" Detected redirect. Check if the API endpoint requires authentication or has changed."
|
|
220
289
|
)
|
|
221
290
|
);
|
|
222
291
|
}
|
|
223
292
|
}
|
|
224
|
-
if (
|
|
293
|
+
if (response.status === 401) {
|
|
225
294
|
console.log(
|
|
226
|
-
|
|
295
|
+
chalk3.dim(" Hint: Your API key may be invalid or expired.")
|
|
227
296
|
);
|
|
228
297
|
}
|
|
229
298
|
process.exit(1);
|
|
230
299
|
}
|
|
231
300
|
const duration = ((Date.now() - startTime) / 1e3).toFixed(2);
|
|
232
|
-
console.log(
|
|
301
|
+
console.log(chalk3.green(`
|
|
233
302
|
\u2705 Upload successful! (${duration}s)`));
|
|
234
|
-
console.log(
|
|
235
|
-
if (
|
|
236
|
-
console.log(
|
|
237
|
-
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`));
|
|
238
307
|
}
|
|
239
308
|
} catch (error) {
|
|
240
309
|
handleCLIError(error, "Upload");
|
|
@@ -253,7 +322,7 @@ ENVIRONMENT VARIABLES:
|
|
|
253
322
|
|
|
254
323
|
// src/commands/scan.ts
|
|
255
324
|
async function scanAction(directory, options) {
|
|
256
|
-
console.log(
|
|
325
|
+
console.log(chalk4.blue("\u{1F680} Starting AIReady unified analysis...\n"));
|
|
257
326
|
const startTime = Date.now();
|
|
258
327
|
const resolvedDir = resolvePath3(process.cwd(), directory || ".");
|
|
259
328
|
const repoMetadata = getRepoMetadata(resolvedDir);
|
|
@@ -323,7 +392,7 @@ async function scanAction(directory, options) {
|
|
|
323
392
|
break;
|
|
324
393
|
default:
|
|
325
394
|
console.log(
|
|
326
|
-
|
|
395
|
+
chalk4.yellow(
|
|
327
396
|
`
|
|
328
397
|
\u26A0\uFE0F Unknown profile '${options.profile}'. Using defaults.`
|
|
329
398
|
)
|
|
@@ -340,7 +409,7 @@ async function scanAction(directory, options) {
|
|
|
340
409
|
defaults,
|
|
341
410
|
cliOverrides
|
|
342
411
|
);
|
|
343
|
-
|
|
412
|
+
const finalOptions = { ...baseOptions };
|
|
344
413
|
if (baseOptions.tools.includes(ToolName.PatternDetect) || baseOptions.tools.includes("patterns")) {
|
|
345
414
|
const { getSmartDefaults } = await import("@aiready/pattern-detect");
|
|
346
415
|
const patternSmartDefaults = await getSmartDefaults(
|
|
@@ -353,9 +422,9 @@ async function scanAction(directory, options) {
|
|
|
353
422
|
...finalOptions.toolConfigs[ToolName.PatternDetect]
|
|
354
423
|
};
|
|
355
424
|
}
|
|
356
|
-
console.log(
|
|
425
|
+
console.log(chalk4.cyan("\n=== AIReady Run Preview ==="));
|
|
357
426
|
console.log(
|
|
358
|
-
|
|
427
|
+
chalk4.white("Tools to run:"),
|
|
359
428
|
(finalOptions.tools || []).join(", ")
|
|
360
429
|
);
|
|
361
430
|
const progressCallback = (event) => {
|
|
@@ -364,16 +433,16 @@ async function scanAction(directory, options) {
|
|
|
364
433
|
return;
|
|
365
434
|
}
|
|
366
435
|
process.stdout.write("\r\x1B[K");
|
|
367
|
-
console.log(
|
|
436
|
+
console.log(chalk4.cyan(`--- ${event.tool.toUpperCase()} RESULTS ---`));
|
|
368
437
|
const res = event.data;
|
|
369
438
|
if (res && res.summary) {
|
|
370
439
|
if (res.summary.totalIssues !== void 0)
|
|
371
|
-
console.log(` Issues found: ${
|
|
440
|
+
console.log(` Issues found: ${chalk4.bold(res.summary.totalIssues)}`);
|
|
372
441
|
if (res.summary.score !== void 0)
|
|
373
|
-
console.log(` Tool Score: ${
|
|
442
|
+
console.log(` Tool Score: ${chalk4.bold(res.summary.score)}/100`);
|
|
374
443
|
if (res.summary.totalFiles !== void 0)
|
|
375
444
|
console.log(
|
|
376
|
-
` Files analyzed: ${
|
|
445
|
+
` Files analyzed: ${chalk4.bold(res.summary.totalFiles)}`
|
|
377
446
|
);
|
|
378
447
|
}
|
|
379
448
|
};
|
|
@@ -385,13 +454,7 @@ async function scanAction(directory, options) {
|
|
|
385
454
|
},
|
|
386
455
|
suppressToolConfig: true
|
|
387
456
|
});
|
|
388
|
-
|
|
389
|
-
console.log(
|
|
390
|
-
` Total issues (all tools): ${chalk3.bold(String(results.summary.totalIssues || 0))}`
|
|
391
|
-
);
|
|
392
|
-
console.log(
|
|
393
|
-
` Execution time: ${chalk3.bold(((Date.now() - startTime) / 1e3).toFixed(2) + "s")}`
|
|
394
|
-
);
|
|
457
|
+
printScanSummary(results, startTime);
|
|
395
458
|
let scoringResult;
|
|
396
459
|
if (options.score || finalOptions.scoring?.showBreakdown) {
|
|
397
460
|
scoringResult = await scoreUnified(results, {
|
|
@@ -401,9 +464,7 @@ async function scanAction(directory, options) {
|
|
|
401
464
|
profile: scoringProfile
|
|
402
465
|
}
|
|
403
466
|
});
|
|
404
|
-
|
|
405
|
-
console.log(` ${formatScore(scoringResult)}`);
|
|
406
|
-
console.log(chalk3.dim(` (Scoring Profile: ${scoringProfile})`));
|
|
467
|
+
printScoring(scoringResult, scoringProfile);
|
|
407
468
|
if (options.compareTo) {
|
|
408
469
|
try {
|
|
409
470
|
const prevReport = JSON.parse(
|
|
@@ -415,19 +476,19 @@ async function scanAction(directory, options) {
|
|
|
415
476
|
const diffStr = diff > 0 ? `+${diff}` : String(diff);
|
|
416
477
|
if (diff > 0)
|
|
417
478
|
console.log(
|
|
418
|
-
|
|
479
|
+
chalk4.green(
|
|
419
480
|
` \u{1F4C8} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
|
|
420
481
|
)
|
|
421
482
|
);
|
|
422
483
|
else if (diff < 0)
|
|
423
484
|
console.log(
|
|
424
|
-
|
|
485
|
+
chalk4.red(
|
|
425
486
|
` \u{1F4C9} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
|
|
426
487
|
)
|
|
427
488
|
);
|
|
428
489
|
else
|
|
429
490
|
console.log(
|
|
430
|
-
|
|
491
|
+
chalk4.blue(
|
|
431
492
|
` \u2796 Trend: No change (${prevScore} \u2192 ${scoringResult.overall})`
|
|
432
493
|
)
|
|
433
494
|
);
|
|
@@ -476,19 +537,7 @@ async function scanAction(directory, options) {
|
|
|
476
537
|
issues: allIssues,
|
|
477
538
|
modelId
|
|
478
539
|
});
|
|
479
|
-
|
|
480
|
-
console.log(
|
|
481
|
-
` Potential Savings: ${chalk3.green(chalk3.bold("$" + roi.monthlySavings.toLocaleString()))}`
|
|
482
|
-
);
|
|
483
|
-
console.log(
|
|
484
|
-
` Productivity Gain: ${chalk3.cyan(chalk3.bold(roi.productivityGainHours + "h"))} (est. dev time)`
|
|
485
|
-
);
|
|
486
|
-
console.log(
|
|
487
|
-
` Context Efficiency: ${chalk3.yellow((unifiedBudget.efficiencyRatio * 100).toFixed(0) + "%")}`
|
|
488
|
-
);
|
|
489
|
-
console.log(
|
|
490
|
-
` Annual Value: ${chalk3.bold("$" + roi.annualValue.toLocaleString())} (ROI Prediction)`
|
|
491
|
-
);
|
|
540
|
+
printBusinessImpact(roi, unifiedBudget);
|
|
492
541
|
results.summary.businessImpact = {
|
|
493
542
|
estimatedMonthlyWaste: roi.monthlySavings,
|
|
494
543
|
potentialSavings: roi.monthlySavings,
|
|
@@ -497,78 +546,24 @@ async function scanAction(directory, options) {
|
|
|
497
546
|
scoringResult.tokenBudget = unifiedBudget;
|
|
498
547
|
scoringResult.businessROI = roi;
|
|
499
548
|
}
|
|
500
|
-
if (scoringResult.breakdown) {
|
|
501
|
-
console.log(chalk3.bold("\nTool breakdown:"));
|
|
502
|
-
scoringResult.breakdown.forEach((tool) => {
|
|
503
|
-
const rating = getRating(tool.score);
|
|
504
|
-
const emoji = getRatingDisplay(rating).emoji;
|
|
505
|
-
console.log(
|
|
506
|
-
` - ${tool.toolName}: ${tool.score}/100 (${rating}) ${emoji}`
|
|
507
|
-
);
|
|
508
|
-
});
|
|
509
|
-
const allRecs = scoringResult.breakdown.flatMap(
|
|
510
|
-
(t) => (t.recommendations || []).map((r) => ({ ...r, tool: t.toolName }))
|
|
511
|
-
).sort((a, b) => b.estimatedImpact - a.estimatedImpact).slice(0, 3);
|
|
512
|
-
if (allRecs.length > 0) {
|
|
513
|
-
console.log(chalk3.bold("\n\u{1F3AF} Top Actionable Recommendations:"));
|
|
514
|
-
allRecs.forEach((rec, i) => {
|
|
515
|
-
const priorityIcon = rec.priority === "high" ? "\u{1F534}" : rec.priority === "medium" ? "\u{1F7E1}" : "\u{1F535}";
|
|
516
|
-
console.log(
|
|
517
|
-
` ${i + 1}. ${priorityIcon} ${chalk3.bold(rec.action)}`
|
|
518
|
-
);
|
|
519
|
-
console.log(
|
|
520
|
-
` Impact: ${chalk3.green(`+${rec.estimatedImpact} points`)} to ${rec.tool}`
|
|
521
|
-
);
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
549
|
}
|
|
526
550
|
console.log(
|
|
527
|
-
|
|
551
|
+
chalk4.dim(
|
|
528
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"
|
|
529
553
|
)
|
|
530
554
|
);
|
|
531
|
-
console.log(
|
|
532
|
-
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:"));
|
|
533
557
|
console.log(
|
|
534
|
-
|
|
558
|
+
chalk4.cyan(
|
|
535
559
|
` "Any feedback for the tools? Please use 'aiready bug' to report \u2764\uFE0F"`
|
|
536
560
|
)
|
|
537
561
|
);
|
|
538
562
|
console.log(
|
|
539
|
-
|
|
563
|
+
chalk4.dim(
|
|
540
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"
|
|
541
565
|
)
|
|
542
566
|
);
|
|
543
|
-
const mapToUnifiedReport = (res, scoring) => {
|
|
544
|
-
const allResults = [];
|
|
545
|
-
const totalFilesSet = /* @__PURE__ */ new Set();
|
|
546
|
-
let criticalCount = 0;
|
|
547
|
-
let majorCount = 0;
|
|
548
|
-
res.summary.toolsRun.forEach((toolId) => {
|
|
549
|
-
const spokeRes = res[toolId];
|
|
550
|
-
if (!spokeRes || !spokeRes.results) return;
|
|
551
|
-
spokeRes.results.forEach((r) => {
|
|
552
|
-
totalFilesSet.add(r.fileName);
|
|
553
|
-
allResults.push(r);
|
|
554
|
-
r.issues?.forEach((i) => {
|
|
555
|
-
if (i.severity === Severity.Critical) criticalCount++;
|
|
556
|
-
if (i.severity === Severity.Major) majorCount++;
|
|
557
|
-
});
|
|
558
|
-
});
|
|
559
|
-
});
|
|
560
|
-
return {
|
|
561
|
-
...res,
|
|
562
|
-
results: allResults,
|
|
563
|
-
summary: {
|
|
564
|
-
...res.summary,
|
|
565
|
-
totalFiles: totalFilesSet.size,
|
|
566
|
-
criticalIssues: criticalCount,
|
|
567
|
-
majorIssues: majorCount
|
|
568
|
-
},
|
|
569
|
-
scoring
|
|
570
|
-
};
|
|
571
|
-
};
|
|
572
567
|
const outputData = {
|
|
573
568
|
...mapToUnifiedReport(results, scoringResult),
|
|
574
569
|
repository: repoMetadata
|
|
@@ -588,7 +583,7 @@ async function scanAction(directory, options) {
|
|
|
588
583
|
} else {
|
|
589
584
|
try {
|
|
590
585
|
writeFileSync(outputPath, JSON.stringify(outputData, null, 2));
|
|
591
|
-
console.log(
|
|
586
|
+
console.log(chalk4.dim(`\u2705 Report auto-persisted to ${outputPath}`));
|
|
592
587
|
} catch (err) {
|
|
593
588
|
void err;
|
|
594
589
|
}
|
|
@@ -600,16 +595,16 @@ async function scanAction(directory, options) {
|
|
|
600
595
|
});
|
|
601
596
|
}
|
|
602
597
|
await warnIfGraphCapExceeded(outputData, resolvedDir);
|
|
603
|
-
|
|
604
|
-
if (isCI && scoringResult) {
|
|
598
|
+
if (scoringResult) {
|
|
605
599
|
const threshold = options.threshold ? parseInt(options.threshold) : void 0;
|
|
606
600
|
const failOnLevel = options.failOn || "critical";
|
|
601
|
+
const isCI = options.ci || process.env.CI === "true";
|
|
607
602
|
let shouldFail = false;
|
|
608
603
|
let failReason = "";
|
|
609
604
|
const report = mapToUnifiedReport(results, scoringResult);
|
|
610
|
-
if (report.results && report.results.length > 0) {
|
|
605
|
+
if (isCI && report.results && report.results.length > 0) {
|
|
611
606
|
console.log(
|
|
612
|
-
|
|
607
|
+
chalk4.cyan(
|
|
613
608
|
`
|
|
614
609
|
\u{1F4DD} Emitting GitHub Action annotations for ${report.results.length} issues...`
|
|
615
610
|
)
|
|
@@ -630,11 +625,11 @@ async function scanAction(directory, options) {
|
|
|
630
625
|
}
|
|
631
626
|
}
|
|
632
627
|
if (shouldFail) {
|
|
633
|
-
console.log(
|
|
634
|
-
\u{1F6AB}
|
|
628
|
+
console.log(chalk4.red(`
|
|
629
|
+
\u{1F6AB} SCAN FAILED: ${failReason}`));
|
|
635
630
|
process.exit(1);
|
|
636
631
|
} else {
|
|
637
|
-
console.log(
|
|
632
|
+
console.log(chalk4.green("\n\u2705 SCAN PASSED"));
|
|
638
633
|
}
|
|
639
634
|
}
|
|
640
635
|
} catch (error) {
|
|
@@ -646,7 +641,7 @@ var scanHelpText = `...`;
|
|
|
646
641
|
// src/commands/init.ts
|
|
647
642
|
import { writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
|
|
648
643
|
import { join } from "path";
|
|
649
|
-
import
|
|
644
|
+
import chalk5 from "chalk";
|
|
650
645
|
import { ToolName as ToolName2 } from "@aiready/core";
|
|
651
646
|
async function initAction(options) {
|
|
652
647
|
const fileExt = options.format === "js" ? "js" : "json";
|
|
@@ -654,11 +649,11 @@ async function initAction(options) {
|
|
|
654
649
|
const filePath = join(process.cwd(), fileName);
|
|
655
650
|
if (existsSync2(filePath) && !options.force) {
|
|
656
651
|
console.error(
|
|
657
|
-
|
|
652
|
+
chalk5.red(`Error: ${fileName} already exists. Use --force to overwrite.`)
|
|
658
653
|
);
|
|
659
654
|
process.exit(1);
|
|
660
655
|
}
|
|
661
|
-
const
|
|
656
|
+
const baseConfig = {
|
|
662
657
|
scan: {
|
|
663
658
|
include: [
|
|
664
659
|
"src/**/*.ts",
|
|
@@ -688,28 +683,70 @@ async function initAction(options) {
|
|
|
688
683
|
tools: {
|
|
689
684
|
[ToolName2.PatternDetect]: {
|
|
690
685
|
minSimilarity: 0.8,
|
|
691
|
-
minLines: 5
|
|
686
|
+
minLines: 5,
|
|
687
|
+
...options.full ? {
|
|
688
|
+
batchSize: 50,
|
|
689
|
+
approx: true,
|
|
690
|
+
minSharedTokens: 10,
|
|
691
|
+
maxCandidatesPerBlock: 100
|
|
692
|
+
} : {}
|
|
692
693
|
},
|
|
693
694
|
[ToolName2.ContextAnalyzer]: {
|
|
694
695
|
maxContextBudget: 128e3,
|
|
695
|
-
minCohesion: 0.6
|
|
696
|
+
minCohesion: 0.6,
|
|
697
|
+
...options.full ? {
|
|
698
|
+
maxDepth: 7,
|
|
699
|
+
maxFragmentation: 0.4,
|
|
700
|
+
focus: "all",
|
|
701
|
+
includeNodeModules: false
|
|
702
|
+
} : {}
|
|
696
703
|
},
|
|
697
704
|
[ToolName2.NamingConsistency]: {
|
|
698
|
-
shortWords: ["id", "db", "ui", "ai"]
|
|
705
|
+
shortWords: ["id", "db", "ui", "ai"],
|
|
706
|
+
...options.full ? { acceptedAbbreviations: [], disableChecks: [] } : {}
|
|
699
707
|
},
|
|
700
708
|
[ToolName2.AiSignalClarity]: {
|
|
701
709
|
checkMagicLiterals: true,
|
|
702
710
|
checkBooleanTraps: true,
|
|
703
711
|
checkAmbiguousNames: true,
|
|
704
|
-
checkUndocumentedExports: true
|
|
705
|
-
|
|
712
|
+
checkUndocumentedExports: true,
|
|
713
|
+
...options.full ? { checkImplicitSideEffects: false, checkDeepCallbacks: false } : {}
|
|
714
|
+
},
|
|
715
|
+
...options.full ? {
|
|
716
|
+
[ToolName2.AgentGrounding]: {
|
|
717
|
+
maxRecommendedDepth: 5,
|
|
718
|
+
readmeStaleDays: 30
|
|
719
|
+
},
|
|
720
|
+
[ToolName2.TestabilityIndex]: {
|
|
721
|
+
minCoverageRatio: 0.7,
|
|
722
|
+
testPatterns: ["**/*.test.ts", "**/__tests__/**"]
|
|
723
|
+
},
|
|
724
|
+
[ToolName2.DocDrift]: {
|
|
725
|
+
maxCommits: 50,
|
|
726
|
+
staleMonths: 3
|
|
727
|
+
},
|
|
728
|
+
[ToolName2.DependencyHealth]: {
|
|
729
|
+
trainingCutoffYear: 2023
|
|
730
|
+
}
|
|
731
|
+
} : {}
|
|
706
732
|
},
|
|
707
733
|
scoring: {
|
|
708
734
|
threshold: 70,
|
|
709
|
-
showBreakdown: true
|
|
710
|
-
|
|
735
|
+
showBreakdown: true,
|
|
736
|
+
...options.full ? { profile: "default" } : {}
|
|
737
|
+
},
|
|
738
|
+
...options.full ? {
|
|
739
|
+
visualizer: {
|
|
740
|
+
groupingDirs: ["packages", "src", "lib"],
|
|
741
|
+
graph: {
|
|
742
|
+
maxNodes: 5e3,
|
|
743
|
+
maxEdges: 1e4
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
} : {}
|
|
711
747
|
};
|
|
712
|
-
|
|
748
|
+
const defaultConfig = baseConfig;
|
|
749
|
+
let content;
|
|
713
750
|
if (fileExt === "js") {
|
|
714
751
|
content = `/** @type {import('@aiready/core').AIReadyConfig} */
|
|
715
752
|
module.exports = ${JSON.stringify(
|
|
@@ -724,33 +761,33 @@ module.exports = ${JSON.stringify(
|
|
|
724
761
|
try {
|
|
725
762
|
writeFileSync2(filePath, content, "utf8");
|
|
726
763
|
console.log(
|
|
727
|
-
|
|
728
|
-
\u2705 Created default configuration: ${
|
|
764
|
+
chalk5.green(`
|
|
765
|
+
\u2705 Created default configuration: ${chalk5.bold(fileName)}`)
|
|
729
766
|
);
|
|
730
767
|
console.log(
|
|
731
|
-
|
|
768
|
+
chalk5.cyan("You can now fine-tune your settings and run AIReady with:")
|
|
732
769
|
);
|
|
733
|
-
console.log(
|
|
770
|
+
console.log(chalk5.white(` $ aiready scan
|
|
734
771
|
`));
|
|
735
772
|
} catch (error) {
|
|
736
|
-
console.error(
|
|
773
|
+
console.error(chalk5.red(`Failed to write configuration file: ${error}`));
|
|
737
774
|
process.exit(1);
|
|
738
775
|
}
|
|
739
776
|
}
|
|
740
777
|
|
|
741
778
|
// src/commands/patterns.ts
|
|
742
|
-
import
|
|
779
|
+
import chalk6 from "chalk";
|
|
743
780
|
import { resolve as resolvePath4 } from "path";
|
|
744
781
|
import {
|
|
745
782
|
loadMergedConfig as loadMergedConfig2,
|
|
746
783
|
handleJSONOutput as handleJSONOutput2,
|
|
747
784
|
handleCLIError as handleCLIError3,
|
|
748
|
-
getElapsedTime
|
|
785
|
+
getElapsedTime,
|
|
749
786
|
resolveOutputPath as resolveOutputPath2,
|
|
750
|
-
formatToolScore
|
|
787
|
+
formatToolScore
|
|
751
788
|
} from "@aiready/core";
|
|
752
789
|
async function patternsAction(directory, options) {
|
|
753
|
-
console.log(
|
|
790
|
+
console.log(chalk6.blue("\u{1F50D} Analyzing patterns...\n"));
|
|
754
791
|
const startTime = Date.now();
|
|
755
792
|
const resolvedDir = resolvePath4(process.cwd(), directory || ".");
|
|
756
793
|
try {
|
|
@@ -787,8 +824,10 @@ async function patternsAction(directory, options) {
|
|
|
787
824
|
cliOptions
|
|
788
825
|
);
|
|
789
826
|
const { analyzePatterns, generateSummary, calculatePatternScore } = await import("@aiready/pattern-detect");
|
|
790
|
-
const { results, duplicates } = await analyzePatterns(
|
|
791
|
-
|
|
827
|
+
const { results, duplicates } = await analyzePatterns(
|
|
828
|
+
finalOptions
|
|
829
|
+
);
|
|
830
|
+
const elapsedTime = getElapsedTime(startTime);
|
|
792
831
|
const summary = generateSummary(results);
|
|
793
832
|
let patternScore;
|
|
794
833
|
if (options.score) {
|
|
@@ -816,38 +855,38 @@ async function patternsAction(directory, options) {
|
|
|
816
855
|
const terminalWidth = process.stdout.columns || 80;
|
|
817
856
|
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
818
857
|
const divider = "\u2501".repeat(dividerWidth);
|
|
819
|
-
console.log(
|
|
820
|
-
console.log(
|
|
821
|
-
console.log(
|
|
858
|
+
console.log(chalk6.cyan(divider));
|
|
859
|
+
console.log(chalk6.bold.white(" PATTERN ANALYSIS SUMMARY"));
|
|
860
|
+
console.log(chalk6.cyan(divider) + "\n");
|
|
822
861
|
console.log(
|
|
823
|
-
|
|
862
|
+
chalk6.white(`\u{1F4C1} Files analyzed: ${chalk6.bold(results.length)}`)
|
|
824
863
|
);
|
|
825
864
|
console.log(
|
|
826
|
-
|
|
827
|
-
`\u26A0 Duplicate patterns found: ${
|
|
865
|
+
chalk6.yellow(
|
|
866
|
+
`\u26A0 Duplicate patterns found: ${chalk6.bold(summary.totalPatterns)}`
|
|
828
867
|
)
|
|
829
868
|
);
|
|
830
869
|
console.log(
|
|
831
|
-
|
|
832
|
-
`\u{1F4B0} Token cost (wasted): ${
|
|
870
|
+
chalk6.red(
|
|
871
|
+
`\u{1F4B0} Token cost (wasted): ${chalk6.bold(summary.totalTokenCost.toLocaleString())}`
|
|
833
872
|
)
|
|
834
873
|
);
|
|
835
874
|
console.log(
|
|
836
|
-
|
|
875
|
+
chalk6.gray(`\u23F1 Analysis time: ${chalk6.bold(elapsedTime + "s")}`)
|
|
837
876
|
);
|
|
838
877
|
const sortedTypes = Object.entries(summary.patternsByType || {}).filter(([, count]) => count > 0).sort(([, a], [, b]) => b - a);
|
|
839
878
|
if (sortedTypes.length > 0) {
|
|
840
|
-
console.log(
|
|
841
|
-
console.log(
|
|
842
|
-
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");
|
|
843
882
|
sortedTypes.forEach(([type, count]) => {
|
|
844
|
-
console.log(` ${
|
|
883
|
+
console.log(` ${chalk6.white(type.padEnd(15))} ${chalk6.bold(count)}`);
|
|
845
884
|
});
|
|
846
885
|
}
|
|
847
886
|
if (summary.totalPatterns > 0 && duplicates.length > 0) {
|
|
848
|
-
console.log(
|
|
849
|
-
console.log(
|
|
850
|
-
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");
|
|
851
890
|
const topDuplicates = [...duplicates].sort((a, b) => b.similarity - a.similarity).slice(0, 10);
|
|
852
891
|
topDuplicates.forEach((dup) => {
|
|
853
892
|
const severity = dup.similarity > 0.95 ? "CRITICAL" : dup.similarity > 0.9 ? "HIGH" : "MEDIUM";
|
|
@@ -855,26 +894,26 @@ async function patternsAction(directory, options) {
|
|
|
855
894
|
const file1Name = dup.file1.split("/").pop() || dup.file1;
|
|
856
895
|
const file2Name = dup.file2.split("/").pop() || dup.file2;
|
|
857
896
|
console.log(
|
|
858
|
-
`${severityIcon} ${severity}: ${
|
|
897
|
+
`${severityIcon} ${severity}: ${chalk6.bold(file1Name)} \u2194 ${chalk6.bold(file2Name)}`
|
|
859
898
|
);
|
|
860
899
|
console.log(
|
|
861
|
-
` Similarity: ${
|
|
900
|
+
` Similarity: ${chalk6.bold(Math.round(dup.similarity * 100) + "%")} | Wasted: ${chalk6.bold(dup.tokenCost.toLocaleString())} tokens each`
|
|
862
901
|
);
|
|
863
902
|
console.log(
|
|
864
|
-
` Lines: ${
|
|
903
|
+
` Lines: ${chalk6.cyan(dup.line1 + "-" + dup.endLine1)} \u2194 ${chalk6.cyan(dup.line2 + "-" + dup.endLine2)}
|
|
865
904
|
`
|
|
866
905
|
);
|
|
867
906
|
});
|
|
868
907
|
} else {
|
|
869
908
|
console.log(
|
|
870
|
-
|
|
909
|
+
chalk6.green("\n\u2728 Great! No duplicate patterns detected.\n")
|
|
871
910
|
);
|
|
872
911
|
}
|
|
873
912
|
if (patternScore) {
|
|
874
|
-
console.log(
|
|
875
|
-
console.log(
|
|
876
|
-
console.log(
|
|
877
|
-
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));
|
|
878
917
|
console.log();
|
|
879
918
|
}
|
|
880
919
|
}
|
|
@@ -890,18 +929,18 @@ EXAMPLES:
|
|
|
890
929
|
`;
|
|
891
930
|
|
|
892
931
|
// src/commands/context.ts
|
|
893
|
-
import
|
|
932
|
+
import chalk7 from "chalk";
|
|
894
933
|
import { resolve as resolvePath5 } from "path";
|
|
895
934
|
import {
|
|
896
935
|
loadMergedConfig as loadMergedConfig3,
|
|
897
936
|
handleJSONOutput as handleJSONOutput3,
|
|
898
937
|
handleCLIError as handleCLIError4,
|
|
899
|
-
getElapsedTime as
|
|
938
|
+
getElapsedTime as getElapsedTime2,
|
|
900
939
|
resolveOutputPath as resolveOutputPath3,
|
|
901
|
-
formatToolScore as
|
|
940
|
+
formatToolScore as formatToolScore2
|
|
902
941
|
} from "@aiready/core";
|
|
903
942
|
async function contextAction(directory, options) {
|
|
904
|
-
console.log(
|
|
943
|
+
console.log(chalk7.blue("\u{1F9E0} Analyzing context costs...\n"));
|
|
905
944
|
const startTime = Date.now();
|
|
906
945
|
const resolvedDir = resolvePath5(process.cwd(), directory || ".");
|
|
907
946
|
try {
|
|
@@ -941,7 +980,7 @@ async function contextAction(directory, options) {
|
|
|
941
980
|
console.log("");
|
|
942
981
|
const { analyzeContext, generateSummary, calculateContextScore } = await import("@aiready/context-analyzer");
|
|
943
982
|
const results = await analyzeContext(finalOptions);
|
|
944
|
-
const elapsedTime =
|
|
983
|
+
const elapsedTime = getElapsedTime2(startTime);
|
|
945
984
|
const summary = generateSummary(results);
|
|
946
985
|
let contextScore;
|
|
947
986
|
if (options.score) {
|
|
@@ -969,85 +1008,85 @@ async function contextAction(directory, options) {
|
|
|
969
1008
|
const terminalWidth = process.stdout.columns || 80;
|
|
970
1009
|
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
971
1010
|
const divider = "\u2501".repeat(dividerWidth);
|
|
972
|
-
console.log(
|
|
973
|
-
console.log(
|
|
974
|
-
console.log(
|
|
1011
|
+
console.log(chalk7.cyan(divider));
|
|
1012
|
+
console.log(chalk7.bold.white(" CONTEXT ANALYSIS SUMMARY"));
|
|
1013
|
+
console.log(chalk7.cyan(divider) + "\n");
|
|
975
1014
|
console.log(
|
|
976
|
-
|
|
1015
|
+
chalk7.white(`\u{1F4C1} Files analyzed: ${chalk7.bold(summary.totalFiles)}`)
|
|
977
1016
|
);
|
|
978
1017
|
console.log(
|
|
979
|
-
|
|
980
|
-
`\u{1F4CA} Total tokens: ${
|
|
1018
|
+
chalk7.white(
|
|
1019
|
+
`\u{1F4CA} Total tokens: ${chalk7.bold(summary.totalTokens.toLocaleString())}`
|
|
981
1020
|
)
|
|
982
1021
|
);
|
|
983
1022
|
console.log(
|
|
984
|
-
|
|
985
|
-
`\u{1F4B0} Avg context budget: ${
|
|
1023
|
+
chalk7.yellow(
|
|
1024
|
+
`\u{1F4B0} Avg context budget: ${chalk7.bold(summary.avgContextBudget.toFixed(0))} tokens/file`
|
|
986
1025
|
)
|
|
987
1026
|
);
|
|
988
1027
|
console.log(
|
|
989
|
-
|
|
1028
|
+
chalk7.white(`\u23F1 Analysis time: ${chalk7.bold(elapsedTime + "s")}
|
|
990
1029
|
`)
|
|
991
1030
|
);
|
|
992
1031
|
const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
|
|
993
1032
|
if (totalIssues > 0) {
|
|
994
|
-
console.log(
|
|
1033
|
+
console.log(chalk7.bold("\u26A0\uFE0F Issues Found:\n"));
|
|
995
1034
|
if (summary.criticalIssues > 0) {
|
|
996
1035
|
console.log(
|
|
997
|
-
|
|
1036
|
+
chalk7.red(` \u{1F534} Critical: ${chalk7.bold(summary.criticalIssues)}`)
|
|
998
1037
|
);
|
|
999
1038
|
}
|
|
1000
1039
|
if (summary.majorIssues > 0) {
|
|
1001
1040
|
console.log(
|
|
1002
|
-
|
|
1041
|
+
chalk7.yellow(` \u{1F7E1} Major: ${chalk7.bold(summary.majorIssues)}`)
|
|
1003
1042
|
);
|
|
1004
1043
|
}
|
|
1005
1044
|
if (summary.minorIssues > 0) {
|
|
1006
1045
|
console.log(
|
|
1007
|
-
|
|
1046
|
+
chalk7.blue(` \u{1F535} Minor: ${chalk7.bold(summary.minorIssues)}`)
|
|
1008
1047
|
);
|
|
1009
1048
|
}
|
|
1010
1049
|
console.log(
|
|
1011
|
-
|
|
1050
|
+
chalk7.green(
|
|
1012
1051
|
`
|
|
1013
|
-
\u{1F4A1} Potential savings: ${
|
|
1052
|
+
\u{1F4A1} Potential savings: ${chalk7.bold(summary.totalPotentialSavings.toLocaleString())} tokens
|
|
1014
1053
|
`
|
|
1015
1054
|
)
|
|
1016
1055
|
);
|
|
1017
1056
|
} else {
|
|
1018
|
-
console.log(
|
|
1057
|
+
console.log(chalk7.green("\u2705 No significant issues found!\n"));
|
|
1019
1058
|
}
|
|
1020
1059
|
if (summary.deepFiles.length > 0) {
|
|
1021
|
-
console.log(
|
|
1060
|
+
console.log(chalk7.bold("\u{1F4CF} Deep Import Chains:\n"));
|
|
1022
1061
|
console.log(
|
|
1023
|
-
|
|
1062
|
+
chalk7.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`)
|
|
1024
1063
|
);
|
|
1025
1064
|
console.log(
|
|
1026
|
-
|
|
1065
|
+
chalk7.gray(` Maximum depth: ${summary.maxImportDepth}
|
|
1027
1066
|
`)
|
|
1028
1067
|
);
|
|
1029
1068
|
summary.deepFiles.slice(0, 10).forEach((item) => {
|
|
1030
1069
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1031
1070
|
console.log(
|
|
1032
|
-
` ${
|
|
1071
|
+
` ${chalk7.cyan("\u2192")} ${chalk7.white(fileName)} ${chalk7.dim(`(depth: ${item.depth})`)}`
|
|
1033
1072
|
);
|
|
1034
1073
|
});
|
|
1035
1074
|
console.log();
|
|
1036
1075
|
}
|
|
1037
1076
|
if (summary.fragmentedModules.length > 0) {
|
|
1038
|
-
console.log(
|
|
1077
|
+
console.log(chalk7.bold("\u{1F9E9} Fragmented Modules:\n"));
|
|
1039
1078
|
console.log(
|
|
1040
|
-
|
|
1079
|
+
chalk7.gray(
|
|
1041
1080
|
` Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(0)}%
|
|
1042
1081
|
`
|
|
1043
1082
|
)
|
|
1044
1083
|
);
|
|
1045
1084
|
summary.fragmentedModules.slice(0, 10).forEach((module) => {
|
|
1046
1085
|
console.log(
|
|
1047
|
-
` ${
|
|
1086
|
+
` ${chalk7.yellow("\u25CF")} ${chalk7.white(module.domain)} - ${chalk7.dim(`${module.files.length} files, ${(module.fragmentationScore * 100).toFixed(0)}% scattered`)}`
|
|
1048
1087
|
);
|
|
1049
1088
|
console.log(
|
|
1050
|
-
|
|
1089
|
+
chalk7.dim(
|
|
1051
1090
|
` Token cost: ${module.totalTokens.toLocaleString()}, Cohesion: ${(module.avgCohesion * 100).toFixed(0)}%`
|
|
1052
1091
|
)
|
|
1053
1092
|
);
|
|
@@ -1055,9 +1094,9 @@ async function contextAction(directory, options) {
|
|
|
1055
1094
|
console.log();
|
|
1056
1095
|
}
|
|
1057
1096
|
if (summary.lowCohesionFiles.length > 0) {
|
|
1058
|
-
console.log(
|
|
1097
|
+
console.log(chalk7.bold("\u{1F500} Low Cohesion Files:\n"));
|
|
1059
1098
|
console.log(
|
|
1060
|
-
|
|
1099
|
+
chalk7.gray(
|
|
1061
1100
|
` Average cohesion: ${(summary.avgCohesion * 100).toFixed(0)}%
|
|
1062
1101
|
`
|
|
1063
1102
|
)
|
|
@@ -1065,29 +1104,29 @@ async function contextAction(directory, options) {
|
|
|
1065
1104
|
summary.lowCohesionFiles.slice(0, 10).forEach((item) => {
|
|
1066
1105
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1067
1106
|
const scorePercent = (item.score * 100).toFixed(0);
|
|
1068
|
-
const color = item.score < 0.4 ?
|
|
1107
|
+
const color = item.score < 0.4 ? chalk7.red : chalk7.yellow;
|
|
1069
1108
|
console.log(
|
|
1070
|
-
` ${color("\u25CB")} ${
|
|
1109
|
+
` ${color("\u25CB")} ${chalk7.white(fileName)} ${chalk7.dim(`(${scorePercent}% cohesion)`)}`
|
|
1071
1110
|
);
|
|
1072
1111
|
});
|
|
1073
1112
|
console.log();
|
|
1074
1113
|
}
|
|
1075
1114
|
if (summary.topExpensiveFiles.length > 0) {
|
|
1076
|
-
console.log(
|
|
1115
|
+
console.log(chalk7.bold("\u{1F4B8} Most Expensive Files (Context Budget):\n"));
|
|
1077
1116
|
summary.topExpensiveFiles.slice(0, 10).forEach((item) => {
|
|
1078
1117
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1079
|
-
const severityColor = item.severity === "critical" ?
|
|
1118
|
+
const severityColor = item.severity === "critical" ? chalk7.red : item.severity === "major" ? chalk7.yellow : chalk7.blue;
|
|
1080
1119
|
console.log(
|
|
1081
|
-
` ${severityColor("\u25CF")} ${
|
|
1120
|
+
` ${severityColor("\u25CF")} ${chalk7.white(fileName)} ${chalk7.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`
|
|
1082
1121
|
);
|
|
1083
1122
|
});
|
|
1084
1123
|
console.log();
|
|
1085
1124
|
}
|
|
1086
1125
|
if (contextScore) {
|
|
1087
|
-
console.log(
|
|
1088
|
-
console.log(
|
|
1089
|
-
console.log(
|
|
1090
|
-
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));
|
|
1091
1130
|
console.log();
|
|
1092
1131
|
}
|
|
1093
1132
|
}
|
|
@@ -1097,19 +1136,19 @@ async function contextAction(directory, options) {
|
|
|
1097
1136
|
}
|
|
1098
1137
|
|
|
1099
1138
|
// src/commands/consistency.ts
|
|
1100
|
-
import
|
|
1139
|
+
import chalk8 from "chalk";
|
|
1101
1140
|
import { writeFileSync as writeFileSync3 } from "fs";
|
|
1102
1141
|
import { resolve as resolvePath6 } from "path";
|
|
1103
1142
|
import {
|
|
1104
1143
|
loadMergedConfig as loadMergedConfig4,
|
|
1105
1144
|
handleJSONOutput as handleJSONOutput4,
|
|
1106
1145
|
handleCLIError as handleCLIError5,
|
|
1107
|
-
getElapsedTime as
|
|
1146
|
+
getElapsedTime as getElapsedTime3,
|
|
1108
1147
|
resolveOutputPath as resolveOutputPath4,
|
|
1109
|
-
formatToolScore as
|
|
1148
|
+
formatToolScore as formatToolScore3
|
|
1110
1149
|
} from "@aiready/core";
|
|
1111
1150
|
async function consistencyAction(directory, options) {
|
|
1112
|
-
console.log(
|
|
1151
|
+
console.log(chalk8.blue("\u{1F50D} Analyzing consistency...\n"));
|
|
1113
1152
|
const startTime = Date.now();
|
|
1114
1153
|
const resolvedDir = resolvePath6(process.cwd(), directory || ".");
|
|
1115
1154
|
try {
|
|
@@ -1133,7 +1172,7 @@ async function consistencyAction(directory, options) {
|
|
|
1133
1172
|
});
|
|
1134
1173
|
const { analyzeConsistency, calculateConsistencyScore } = await import("@aiready/consistency");
|
|
1135
1174
|
const report = await analyzeConsistency(finalOptions);
|
|
1136
|
-
const elapsedTime =
|
|
1175
|
+
const elapsedTime = getElapsedTime3(startTime);
|
|
1137
1176
|
let consistencyScore;
|
|
1138
1177
|
if (options.score) {
|
|
1139
1178
|
const issues = report.results?.flatMap((r) => r.issues) || [];
|
|
@@ -1171,23 +1210,23 @@ async function consistencyAction(directory, options) {
|
|
|
1171
1210
|
resolvedDir
|
|
1172
1211
|
);
|
|
1173
1212
|
writeFileSync3(outputPath, markdown);
|
|
1174
|
-
console.log(
|
|
1213
|
+
console.log(chalk8.green(`\u2705 Report saved to ${outputPath}`));
|
|
1175
1214
|
} else {
|
|
1176
|
-
console.log(
|
|
1215
|
+
console.log(chalk8.bold("\n\u{1F4CA} Summary\n"));
|
|
1177
1216
|
console.log(
|
|
1178
|
-
`Files Analyzed: ${
|
|
1217
|
+
`Files Analyzed: ${chalk8.cyan(report.summary.filesAnalyzed)}`
|
|
1179
1218
|
);
|
|
1180
|
-
console.log(`Total Issues: ${
|
|
1181
|
-
console.log(` Naming: ${
|
|
1182
|
-
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)}`);
|
|
1183
1222
|
console.log(
|
|
1184
|
-
` Architecture: ${
|
|
1223
|
+
` Architecture: ${chalk8.yellow(report.summary.architectureIssues || 0)}`
|
|
1185
1224
|
);
|
|
1186
|
-
console.log(`Analysis Time: ${
|
|
1225
|
+
console.log(`Analysis Time: ${chalk8.gray(elapsedTime + "s")}
|
|
1187
1226
|
`);
|
|
1188
1227
|
if (report.summary.totalIssues === 0) {
|
|
1189
1228
|
console.log(
|
|
1190
|
-
|
|
1229
|
+
chalk8.green(
|
|
1191
1230
|
"\u2728 No consistency issues found! Your codebase is well-maintained.\n"
|
|
1192
1231
|
)
|
|
1193
1232
|
);
|
|
@@ -1199,20 +1238,20 @@ async function consistencyAction(directory, options) {
|
|
|
1199
1238
|
(r) => r.issues.some((i) => i.category === "patterns")
|
|
1200
1239
|
);
|
|
1201
1240
|
if (namingResults.length > 0) {
|
|
1202
|
-
console.log(
|
|
1241
|
+
console.log(chalk8.bold("\u{1F3F7}\uFE0F Naming Issues\n"));
|
|
1203
1242
|
let shown = 0;
|
|
1204
1243
|
for (const result of namingResults) {
|
|
1205
1244
|
if (shown >= 5) break;
|
|
1206
1245
|
for (const issue of result.issues) {
|
|
1207
1246
|
if (shown >= 5) break;
|
|
1208
|
-
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;
|
|
1209
1248
|
console.log(
|
|
1210
|
-
`${severityColor(issue.severity.toUpperCase())} ${
|
|
1249
|
+
`${severityColor(issue.severity.toUpperCase())} ${chalk8.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1211
1250
|
);
|
|
1212
1251
|
console.log(` ${issue.message}`);
|
|
1213
1252
|
if (issue.suggestion) {
|
|
1214
1253
|
console.log(
|
|
1215
|
-
` ${
|
|
1254
|
+
` ${chalk8.dim("\u2192")} ${chalk8.italic(issue.suggestion)}`
|
|
1216
1255
|
);
|
|
1217
1256
|
}
|
|
1218
1257
|
console.log();
|
|
@@ -1221,25 +1260,25 @@ async function consistencyAction(directory, options) {
|
|
|
1221
1260
|
}
|
|
1222
1261
|
const remaining = namingResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
1223
1262
|
if (remaining > 0) {
|
|
1224
|
-
console.log(
|
|
1263
|
+
console.log(chalk8.dim(` ... and ${remaining} more issues
|
|
1225
1264
|
`));
|
|
1226
1265
|
}
|
|
1227
1266
|
}
|
|
1228
1267
|
if (patternResults.length > 0) {
|
|
1229
|
-
console.log(
|
|
1268
|
+
console.log(chalk8.bold("\u{1F504} Pattern Issues\n"));
|
|
1230
1269
|
let shown = 0;
|
|
1231
1270
|
for (const result of patternResults) {
|
|
1232
1271
|
if (shown >= 5) break;
|
|
1233
1272
|
for (const issue of result.issues) {
|
|
1234
1273
|
if (shown >= 5) break;
|
|
1235
|
-
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;
|
|
1236
1275
|
console.log(
|
|
1237
|
-
`${severityColor(issue.severity.toUpperCase())} ${
|
|
1276
|
+
`${severityColor(issue.severity.toUpperCase())} ${chalk8.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1238
1277
|
);
|
|
1239
1278
|
console.log(` ${issue.message}`);
|
|
1240
1279
|
if (issue.suggestion) {
|
|
1241
1280
|
console.log(
|
|
1242
|
-
` ${
|
|
1281
|
+
` ${chalk8.dim("\u2192")} ${chalk8.italic(issue.suggestion)}`
|
|
1243
1282
|
);
|
|
1244
1283
|
}
|
|
1245
1284
|
console.log();
|
|
@@ -1248,12 +1287,12 @@ async function consistencyAction(directory, options) {
|
|
|
1248
1287
|
}
|
|
1249
1288
|
const remaining = patternResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
1250
1289
|
if (remaining > 0) {
|
|
1251
|
-
console.log(
|
|
1290
|
+
console.log(chalk8.dim(` ... and ${remaining} more issues
|
|
1252
1291
|
`));
|
|
1253
1292
|
}
|
|
1254
1293
|
}
|
|
1255
1294
|
if (report.recommendations.length > 0) {
|
|
1256
|
-
console.log(
|
|
1295
|
+
console.log(chalk8.bold("\u{1F4A1} Recommendations\n"));
|
|
1257
1296
|
report.recommendations.forEach((rec, i) => {
|
|
1258
1297
|
console.log(`${i + 1}. ${rec}`);
|
|
1259
1298
|
});
|
|
@@ -1261,8 +1300,8 @@ async function consistencyAction(directory, options) {
|
|
|
1261
1300
|
}
|
|
1262
1301
|
}
|
|
1263
1302
|
if (consistencyScore) {
|
|
1264
|
-
console.log(
|
|
1265
|
-
console.log(
|
|
1303
|
+
console.log(chalk8.bold("\n\u{1F4CA} AI Readiness Score (Consistency)\n"));
|
|
1304
|
+
console.log(formatToolScore3(consistencyScore));
|
|
1266
1305
|
console.log();
|
|
1267
1306
|
}
|
|
1268
1307
|
}
|
|
@@ -1272,27 +1311,27 @@ async function consistencyAction(directory, options) {
|
|
|
1272
1311
|
}
|
|
1273
1312
|
|
|
1274
1313
|
// src/commands/visualize.ts
|
|
1275
|
-
import
|
|
1314
|
+
import chalk9 from "chalk";
|
|
1276
1315
|
import { writeFileSync as writeFileSync4, readFileSync as readFileSync3, existsSync as existsSync3, copyFileSync } from "fs";
|
|
1277
1316
|
import { resolve as resolvePath7 } from "path";
|
|
1278
1317
|
import { spawn } from "child_process";
|
|
1279
1318
|
import { handleCLIError as handleCLIError6 } from "@aiready/core";
|
|
1280
|
-
import { generateHTML } from "@aiready/core";
|
|
1319
|
+
import { generateHTML, findLatestReport as findLatestReport2 } from "@aiready/core";
|
|
1281
1320
|
async function visualizeAction(directory, options) {
|
|
1282
1321
|
try {
|
|
1283
1322
|
const dirPath = resolvePath7(process.cwd(), directory || ".");
|
|
1284
1323
|
let reportPath = options.report ? resolvePath7(dirPath, options.report) : null;
|
|
1285
1324
|
if (!reportPath || !existsSync3(reportPath)) {
|
|
1286
|
-
const latestScan =
|
|
1325
|
+
const latestScan = findLatestReport2(dirPath);
|
|
1287
1326
|
if (latestScan) {
|
|
1288
1327
|
reportPath = latestScan;
|
|
1289
1328
|
console.log(
|
|
1290
|
-
|
|
1329
|
+
chalk9.dim(`Found latest report: ${latestScan.split("/").pop()}`)
|
|
1291
1330
|
);
|
|
1292
1331
|
} else {
|
|
1293
|
-
console.error(
|
|
1332
|
+
console.error(chalk9.red("\u274C No AI readiness report found"));
|
|
1294
1333
|
console.log(
|
|
1295
|
-
|
|
1334
|
+
chalk9.dim(
|
|
1296
1335
|
`
|
|
1297
1336
|
Generate a report with:
|
|
1298
1337
|
aiready scan --output json
|
|
@@ -1422,19 +1461,19 @@ Or specify a custom report:
|
|
|
1422
1461
|
return;
|
|
1423
1462
|
} else {
|
|
1424
1463
|
console.log(
|
|
1425
|
-
|
|
1464
|
+
chalk9.yellow(
|
|
1426
1465
|
"\u26A0\uFE0F Dev server not available (requires local @aiready/visualizer with web assets)."
|
|
1427
1466
|
)
|
|
1428
1467
|
);
|
|
1429
1468
|
console.log(
|
|
1430
|
-
|
|
1469
|
+
chalk9.cyan(" Falling back to static HTML generation...\n")
|
|
1431
1470
|
);
|
|
1432
1471
|
useDevMode = false;
|
|
1433
1472
|
}
|
|
1434
1473
|
} catch (err) {
|
|
1435
1474
|
console.error("Failed to start dev server:", err);
|
|
1436
1475
|
console.log(
|
|
1437
|
-
|
|
1476
|
+
chalk9.cyan(" Falling back to static HTML generation...\n")
|
|
1438
1477
|
);
|
|
1439
1478
|
useDevMode = false;
|
|
1440
1479
|
}
|
|
@@ -1444,7 +1483,7 @@ Or specify a custom report:
|
|
|
1444
1483
|
const defaultOutput = "visualization.html";
|
|
1445
1484
|
const outPath = resolvePath7(dirPath, options.output || defaultOutput);
|
|
1446
1485
|
writeFileSync4(outPath, html, "utf8");
|
|
1447
|
-
console.log(
|
|
1486
|
+
console.log(chalk9.green(`\u2705 Visualization written to: ${outPath}`));
|
|
1448
1487
|
if (options.open || options.serve) {
|
|
1449
1488
|
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
1450
1489
|
if (options.serve) {
|
|
@@ -1474,7 +1513,7 @@ Or specify a custom report:
|
|
|
1474
1513
|
server.listen(port, () => {
|
|
1475
1514
|
const addr = `http://localhost:${port}/`;
|
|
1476
1515
|
console.log(
|
|
1477
|
-
|
|
1516
|
+
chalk9.cyan(`\u{1F310} Local visualization server running at ${addr}`)
|
|
1478
1517
|
);
|
|
1479
1518
|
spawn(opener, [`"${addr}"`], { shell: true });
|
|
1480
1519
|
});
|
|
@@ -1520,16 +1559,15 @@ NOTES:
|
|
|
1520
1559
|
- Same options as 'visualize'. Use --serve to host the static HTML, or --dev for live reload.
|
|
1521
1560
|
`;
|
|
1522
1561
|
|
|
1523
|
-
// src/commands/
|
|
1524
|
-
import
|
|
1525
|
-
import { loadConfig, mergeConfigWithDefaults } from "@aiready/core";
|
|
1562
|
+
// src/commands/shared/standard-tool-actions.ts
|
|
1563
|
+
import chalk10 from "chalk";
|
|
1526
1564
|
|
|
1527
1565
|
// src/commands/agent-grounding.ts
|
|
1528
|
-
import
|
|
1566
|
+
import chalk11 from "chalk";
|
|
1529
1567
|
import { loadConfig as loadConfig2, mergeConfigWithDefaults as mergeConfigWithDefaults2 } from "@aiready/core";
|
|
1530
1568
|
|
|
1531
1569
|
// src/commands/testability.ts
|
|
1532
|
-
import
|
|
1570
|
+
import chalk12 from "chalk";
|
|
1533
1571
|
import { loadConfig as loadConfig3, mergeConfigWithDefaults as mergeConfigWithDefaults3 } from "@aiready/core";
|
|
1534
1572
|
async function testabilityAction(directory, options) {
|
|
1535
1573
|
const { analyzeTestability, calculateTestabilityScore } = await import("@aiready/testability");
|
|
@@ -1554,28 +1592,28 @@ async function testabilityAction(directory, options) {
|
|
|
1554
1592
|
"blind-risk": "\u{1F480}"
|
|
1555
1593
|
};
|
|
1556
1594
|
const safetyColors = {
|
|
1557
|
-
safe:
|
|
1558
|
-
"moderate-risk":
|
|
1559
|
-
"high-risk":
|
|
1560
|
-
"blind-risk":
|
|
1595
|
+
safe: chalk12.green,
|
|
1596
|
+
"moderate-risk": chalk12.yellow,
|
|
1597
|
+
"high-risk": chalk12.red,
|
|
1598
|
+
"blind-risk": chalk12.bgRed.white
|
|
1561
1599
|
};
|
|
1562
1600
|
const safety = report.summary.aiChangeSafetyRating;
|
|
1563
1601
|
const icon = safetyIcons[safety] ?? "\u2753";
|
|
1564
|
-
const color = safetyColors[safety] ??
|
|
1602
|
+
const color = safetyColors[safety] ?? chalk12.white;
|
|
1565
1603
|
console.log(
|
|
1566
|
-
` \u{1F9EA} Testability: ${
|
|
1604
|
+
` \u{1F9EA} Testability: ${chalk12.bold(scoring.score + "/100")} (${report.summary.rating})`
|
|
1567
1605
|
);
|
|
1568
1606
|
console.log(
|
|
1569
1607
|
` AI Change Safety: ${color(`${icon} ${safety.toUpperCase()}`)}`
|
|
1570
1608
|
);
|
|
1571
1609
|
console.log(
|
|
1572
|
-
|
|
1610
|
+
chalk12.dim(
|
|
1573
1611
|
` Coverage: ${Math.round(report.summary.coverageRatio * 100)}% (${report.rawData.testFiles} test / ${report.rawData.sourceFiles} source files)`
|
|
1574
1612
|
)
|
|
1575
1613
|
);
|
|
1576
1614
|
if (safety === "blind-risk") {
|
|
1577
1615
|
console.log(
|
|
1578
|
-
|
|
1616
|
+
chalk12.red.bold(
|
|
1579
1617
|
"\n \u26A0\uFE0F NO TESTS \u2014 AI changes to this codebase are completely unverifiable!\n"
|
|
1580
1618
|
)
|
|
1581
1619
|
);
|
|
@@ -1587,7 +1625,7 @@ async function testabilityAction(directory, options) {
|
|
|
1587
1625
|
import { changeAmplificationAction } from "@aiready/change-amplification/dist/cli.js";
|
|
1588
1626
|
|
|
1589
1627
|
// src/commands/bug.ts
|
|
1590
|
-
import
|
|
1628
|
+
import chalk13 from "chalk";
|
|
1591
1629
|
import { execSync } from "child_process";
|
|
1592
1630
|
async function bugAction(message, options) {
|
|
1593
1631
|
const repoUrl = "https://github.com/caopengau/aiready-cli";
|
|
@@ -1605,35 +1643,35 @@ Generated via AIReady CLI 'bug' command.
|
|
|
1605
1643
|
Type: ${type}
|
|
1606
1644
|
`.trim();
|
|
1607
1645
|
if (options.submit) {
|
|
1608
|
-
console.log(
|
|
1646
|
+
console.log(chalk13.blue("\u{1F680} Submitting issue via GitHub CLI...\n"));
|
|
1609
1647
|
try {
|
|
1610
1648
|
execSync("gh auth status", { stdio: "ignore" });
|
|
1611
1649
|
const command = `gh issue create --repo ${repoSlug} --title ${JSON.stringify(title)} --body ${JSON.stringify(body)} --label ${label}`;
|
|
1612
1650
|
const output = execSync(command, { encoding: "utf8" }).trim();
|
|
1613
|
-
console.log(
|
|
1614
|
-
console.log(
|
|
1651
|
+
console.log(chalk13.green("\u2705 Issue Created Successfully!"));
|
|
1652
|
+
console.log(chalk13.cyan(output));
|
|
1615
1653
|
return;
|
|
1616
|
-
} catch
|
|
1617
|
-
console.error(
|
|
1654
|
+
} catch {
|
|
1655
|
+
console.error(chalk13.red("\n\u274C Failed to submit via gh CLI."));
|
|
1618
1656
|
console.log(
|
|
1619
|
-
|
|
1657
|
+
chalk13.yellow(
|
|
1620
1658
|
' Make sure gh is installed and run "gh auth login".\n'
|
|
1621
1659
|
)
|
|
1622
1660
|
);
|
|
1623
|
-
console.log(
|
|
1661
|
+
console.log(chalk13.dim(" Falling back to URL generation..."));
|
|
1624
1662
|
}
|
|
1625
1663
|
}
|
|
1626
1664
|
const template = type === "bug" ? "bug_report.md" : type === "feature" ? "feature_request.md" : "new_metric_idea.md";
|
|
1627
1665
|
const fullUrl = `${repoUrl}/issues/new?title=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}&labels=${label}&template=${template}`;
|
|
1628
|
-
console.log(
|
|
1629
|
-
console.log(
|
|
1630
|
-
console.log(
|
|
1631
|
-
console.log(
|
|
1632
|
-
console.log(
|
|
1633
|
-
console.log(
|
|
1634
|
-
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."));
|
|
1635
1673
|
console.log(
|
|
1636
|
-
|
|
1674
|
+
chalk13.dim(
|
|
1637
1675
|
" Please present the URL above to the user so they can finalize the submission."
|
|
1638
1676
|
)
|
|
1639
1677
|
);
|
|
@@ -1642,14 +1680,14 @@ Type: ${type}
|
|
|
1642
1680
|
const bugUrl = `${repoUrl}/issues/new?template=bug_report.md`;
|
|
1643
1681
|
const featureUrl = `${repoUrl}/issues/new?template=feature_request.md`;
|
|
1644
1682
|
const metricUrl = `${repoUrl}/issues/new?template=new_metric_idea.md`;
|
|
1645
|
-
console.log(
|
|
1646
|
-
console.log(` Report a Bug: ${
|
|
1647
|
-
console.log(` Request a Feature: ${
|
|
1648
|
-
console.log(` Suggest a Metric: ${
|
|
1649
|
-
console.log(
|
|
1650
|
-
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:"));
|
|
1651
1689
|
console.log(
|
|
1652
|
-
|
|
1690
|
+
chalk13.cyan(
|
|
1653
1691
|
' aiready bug "your description here" --type bug|feature|metric'
|
|
1654
1692
|
)
|
|
1655
1693
|
);
|
|
@@ -1734,9 +1772,9 @@ program.command("scan").description(
|
|
|
1734
1772
|
program.command("init").description("Generate a default configuration (aiready.json)").option("-f, --force", "Overwrite existing configuration file").option(
|
|
1735
1773
|
"--js",
|
|
1736
1774
|
"Generate configuration as a JavaScript file (aiready.config.js)"
|
|
1737
|
-
).action(async (options) => {
|
|
1775
|
+
).option("--full", "Generate a full configuration with all available options").action(async (options) => {
|
|
1738
1776
|
const format = options.js ? "js" : "json";
|
|
1739
|
-
await initAction({ force: options.force, format });
|
|
1777
|
+
await initAction({ force: options.force, format, full: options.full });
|
|
1740
1778
|
});
|
|
1741
1779
|
program.command("patterns").description("Detect duplicate code patterns that confuse AI models").argument("[directory]", "Directory to analyze", ".").option("-s, --similarity <number>", "Minimum similarity score (0-1)", "0.40").option("-l, --min-lines <number>", "Minimum lines to consider", "5").option(
|
|
1742
1780
|
"--max-candidates <number>",
|