@aiready/cli 0.9.43 → 0.9.46
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 +11 -11
- package/.turbo/turbo-test.log +5 -5
- package/README.md +45 -7
- package/dist/chunk-6FOVC2OE.mjs +392 -0
- package/dist/chunk-MEXEG3IJ.mjs +389 -0
- package/dist/cli.js +631 -415
- package/dist/cli.mjs +451 -377
- package/dist/index.js +149 -2
- package/dist/index.mjs +5 -3
- package/package.json +12 -12
- package/src/.aiready/aiready-report-20260301-141543.json +8261 -0
- package/src/.aiready/aiready-report-20260301-141556.json +8261 -0
- package/src/.aiready/aiready-report-20260301-141611.json +8261 -0
- package/src/.aiready/aiready-report-20260304-125348.json +8324 -0
- package/src/cli.ts +18 -0
- package/src/commands/index.ts +1 -0
- package/src/commands/scan.ts +176 -211
- package/src/commands/upload.ts +125 -0
- package/src/index.ts +190 -3
package/dist/cli.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
__require,
|
|
4
|
-
analyzeUnified
|
|
5
|
-
|
|
4
|
+
analyzeUnified,
|
|
5
|
+
scoreUnified
|
|
6
|
+
} from "./chunk-6FOVC2OE.mjs";
|
|
6
7
|
|
|
7
8
|
// src/cli.ts
|
|
8
9
|
import { Command } from "commander";
|
|
@@ -11,21 +12,23 @@ import { join, dirname } from "path";
|
|
|
11
12
|
import { fileURLToPath } from "url";
|
|
12
13
|
|
|
13
14
|
// src/commands/scan.ts
|
|
14
|
-
import
|
|
15
|
+
import chalk3 from "chalk";
|
|
15
16
|
import { writeFileSync, readFileSync as readFileSync2 } from "fs";
|
|
16
|
-
import { resolve as
|
|
17
|
+
import { resolve as resolvePath3 } from "path";
|
|
17
18
|
import {
|
|
18
19
|
loadMergedConfig,
|
|
19
20
|
handleJSONOutput,
|
|
20
|
-
handleCLIError,
|
|
21
|
+
handleCLIError as handleCLIError2,
|
|
21
22
|
getElapsedTime,
|
|
22
23
|
resolveOutputPath,
|
|
23
|
-
calculateOverallScore,
|
|
24
24
|
formatScore,
|
|
25
25
|
formatToolScore,
|
|
26
|
+
calculateTokenBudget,
|
|
27
|
+
estimateCostFromBudget,
|
|
28
|
+
getModelPreset,
|
|
26
29
|
getRating,
|
|
27
30
|
getRatingDisplay,
|
|
28
|
-
|
|
31
|
+
getRepoMetadata
|
|
29
32
|
} from "@aiready/core";
|
|
30
33
|
|
|
31
34
|
// src/utils/helpers.ts
|
|
@@ -152,11 +155,113 @@ function truncateArray(arr, cap = 8) {
|
|
|
152
155
|
return shown.join(", ") + (more > 0 ? `, ... (+${more} more)` : "");
|
|
153
156
|
}
|
|
154
157
|
|
|
158
|
+
// src/commands/upload.ts
|
|
159
|
+
import fs from "fs";
|
|
160
|
+
import { resolve as resolvePath2 } from "path";
|
|
161
|
+
import chalk2 from "chalk";
|
|
162
|
+
import { handleCLIError } from "@aiready/core";
|
|
163
|
+
async function uploadAction(file, options) {
|
|
164
|
+
const startTime = Date.now();
|
|
165
|
+
const filePath = resolvePath2(process.cwd(), file);
|
|
166
|
+
const serverUrl = options.server || process.env.AIREADY_SERVER || "https://dev.platform.getaiready.dev";
|
|
167
|
+
const apiKey = options.apiKey || process.env.AIREADY_API_KEY;
|
|
168
|
+
if (!apiKey) {
|
|
169
|
+
console.error(chalk2.red("\u274C API Key is required for upload."));
|
|
170
|
+
console.log(
|
|
171
|
+
chalk2.dim(
|
|
172
|
+
" Set AIREADY_API_KEY environment variable or use --api-key flag."
|
|
173
|
+
)
|
|
174
|
+
);
|
|
175
|
+
console.log(
|
|
176
|
+
chalk2.dim(" Get an API key from https://getaiready.dev/dashboard")
|
|
177
|
+
);
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
if (!fs.existsSync(filePath)) {
|
|
181
|
+
console.error(chalk2.red(`\u274C File not found: ${filePath}`));
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
console.log(chalk2.blue(`\u{1F680} Uploading report to ${serverUrl}...`));
|
|
186
|
+
console.log(chalk2.dim(` Reading report from ${filePath}...`));
|
|
187
|
+
const reportContent = fs.readFileSync(filePath, "utf-8");
|
|
188
|
+
const reportData = JSON.parse(reportContent);
|
|
189
|
+
console.log(chalk2.dim(` Successfully parsed report JSON.`));
|
|
190
|
+
const repoId = options.repoId || reportData.repository?.repoId;
|
|
191
|
+
const res = await fetch(`${serverUrl}/api/analysis/upload`, {
|
|
192
|
+
method: "POST",
|
|
193
|
+
headers: {
|
|
194
|
+
"Content-Type": "application/json",
|
|
195
|
+
Authorization: `Bearer ${apiKey}`
|
|
196
|
+
},
|
|
197
|
+
body: JSON.stringify({
|
|
198
|
+
data: reportData,
|
|
199
|
+
repoId
|
|
200
|
+
// Might be null, server will handle mapping
|
|
201
|
+
})
|
|
202
|
+
});
|
|
203
|
+
const contentType = res.headers.get("content-type");
|
|
204
|
+
let result = {};
|
|
205
|
+
if (contentType?.includes("application/json")) {
|
|
206
|
+
result = await res.json();
|
|
207
|
+
} else {
|
|
208
|
+
const text = await res.text();
|
|
209
|
+
result = { error: text || res.statusText };
|
|
210
|
+
}
|
|
211
|
+
if (!res.ok) {
|
|
212
|
+
console.error(
|
|
213
|
+
chalk2.red(`\u274C Upload failed: ${result.error || res.statusText}`)
|
|
214
|
+
);
|
|
215
|
+
if (contentType?.includes("text/html")) {
|
|
216
|
+
console.log(
|
|
217
|
+
chalk2.yellow(
|
|
218
|
+
" Note: Received an HTML response. This often indicates a redirect (e.g., to a login page) or a server error."
|
|
219
|
+
)
|
|
220
|
+
);
|
|
221
|
+
if (result.error?.includes("Redirecting")) {
|
|
222
|
+
console.log(
|
|
223
|
+
chalk2.dim(
|
|
224
|
+
" Detected redirect. Check if the API endpoint requires authentication or has changed."
|
|
225
|
+
)
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (res.status === 401) {
|
|
230
|
+
console.log(
|
|
231
|
+
chalk2.dim(" Hint: Your API key may be invalid or expired.")
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
const duration = ((Date.now() - startTime) / 1e3).toFixed(2);
|
|
237
|
+
console.log(chalk2.green(`
|
|
238
|
+
\u2705 Upload successful! (${duration}s)`));
|
|
239
|
+
console.log(chalk2.cyan(` View results: ${serverUrl}/dashboard`));
|
|
240
|
+
if (result.analysis) {
|
|
241
|
+
console.log(chalk2.dim(` Analysis ID: ${result.analysis.id}`));
|
|
242
|
+
console.log(chalk2.dim(` Score: ${result.analysis.aiScore}/100`));
|
|
243
|
+
}
|
|
244
|
+
} catch (error) {
|
|
245
|
+
handleCLIError(error, "Upload");
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
var uploadHelpText = `
|
|
249
|
+
EXAMPLES:
|
|
250
|
+
$ aiready upload report.json --api-key ar_...
|
|
251
|
+
$ aiready upload .aiready/latest.json
|
|
252
|
+
$ AIREADY_API_KEY=ar_... aiready upload report.json
|
|
253
|
+
|
|
254
|
+
ENVIRONMENT VARIABLES:
|
|
255
|
+
AIREADY_API_KEY Your platform API key
|
|
256
|
+
AIREADY_SERVER Custom platform URL (default: https://dev.platform.getaiready.dev)
|
|
257
|
+
`;
|
|
258
|
+
|
|
155
259
|
// src/commands/scan.ts
|
|
156
260
|
async function scanAction(directory, options) {
|
|
157
|
-
console.log(
|
|
261
|
+
console.log(chalk3.blue("\u{1F680} Starting AIReady unified analysis...\n"));
|
|
158
262
|
const startTime = Date.now();
|
|
159
|
-
const resolvedDir =
|
|
263
|
+
const resolvedDir = resolvePath3(process.cwd(), directory || ".");
|
|
264
|
+
const repoMetadata = getRepoMetadata(resolvedDir);
|
|
160
265
|
try {
|
|
161
266
|
const defaults = {
|
|
162
267
|
tools: [
|
|
@@ -199,7 +304,7 @@ async function scanAction(directory, options) {
|
|
|
199
304
|
break;
|
|
200
305
|
default:
|
|
201
306
|
console.log(
|
|
202
|
-
|
|
307
|
+
chalk3.yellow(
|
|
203
308
|
`
|
|
204
309
|
\u26A0\uFE0F Unknown profile '${options.profile}'. Using specified tools or defaults.`
|
|
205
310
|
)
|
|
@@ -231,22 +336,22 @@ async function scanAction(directory, options) {
|
|
|
231
336
|
...baseOptions
|
|
232
337
|
};
|
|
233
338
|
}
|
|
234
|
-
console.log(
|
|
339
|
+
console.log(chalk3.cyan("\n=== AIReady Run Preview ==="));
|
|
235
340
|
console.log(
|
|
236
|
-
|
|
341
|
+
chalk3.white("Tools to run:"),
|
|
237
342
|
(finalOptions.tools || ["patterns", "context", "consistency"]).join(", ")
|
|
238
343
|
);
|
|
239
|
-
console.log(
|
|
240
|
-
console.log(
|
|
344
|
+
console.log(chalk3.white("Will use settings from config and defaults."));
|
|
345
|
+
console.log(chalk3.white("\nGeneral settings:"));
|
|
241
346
|
if (finalOptions.rootDir)
|
|
242
|
-
console.log(` rootDir: ${
|
|
347
|
+
console.log(` rootDir: ${chalk3.bold(String(finalOptions.rootDir))}`);
|
|
243
348
|
if (finalOptions.include)
|
|
244
349
|
console.log(
|
|
245
|
-
` include: ${
|
|
350
|
+
` include: ${chalk3.bold(truncateArray(finalOptions.include, 6))}`
|
|
246
351
|
);
|
|
247
352
|
if (finalOptions.exclude)
|
|
248
353
|
console.log(
|
|
249
|
-
` exclude: ${
|
|
354
|
+
` exclude: ${chalk3.bold(truncateArray(finalOptions.exclude, 6))}`
|
|
250
355
|
);
|
|
251
356
|
if (finalOptions["pattern-detect"] || finalOptions.minSimilarity) {
|
|
252
357
|
const patternDetectConfig = finalOptions["pattern-detect"] || {
|
|
@@ -260,40 +365,40 @@ async function scanAction(directory, options) {
|
|
|
260
365
|
severity: finalOptions.severity,
|
|
261
366
|
includeTests: finalOptions.includeTests
|
|
262
367
|
};
|
|
263
|
-
console.log(
|
|
368
|
+
console.log(chalk3.white("\nPattern-detect settings:"));
|
|
264
369
|
console.log(
|
|
265
|
-
` minSimilarity: ${
|
|
370
|
+
` minSimilarity: ${chalk3.bold(patternDetectConfig.minSimilarity ?? "default")}`
|
|
266
371
|
);
|
|
267
372
|
console.log(
|
|
268
|
-
` minLines: ${
|
|
373
|
+
` minLines: ${chalk3.bold(patternDetectConfig.minLines ?? "default")}`
|
|
269
374
|
);
|
|
270
375
|
if (patternDetectConfig.approx !== void 0)
|
|
271
376
|
console.log(
|
|
272
|
-
` approx: ${
|
|
377
|
+
` approx: ${chalk3.bold(String(patternDetectConfig.approx))}`
|
|
273
378
|
);
|
|
274
379
|
if (patternDetectConfig.minSharedTokens !== void 0)
|
|
275
380
|
console.log(
|
|
276
|
-
` minSharedTokens: ${
|
|
381
|
+
` minSharedTokens: ${chalk3.bold(String(patternDetectConfig.minSharedTokens))}`
|
|
277
382
|
);
|
|
278
383
|
if (patternDetectConfig.maxCandidatesPerBlock !== void 0)
|
|
279
384
|
console.log(
|
|
280
|
-
` maxCandidatesPerBlock: ${
|
|
385
|
+
` maxCandidatesPerBlock: ${chalk3.bold(String(patternDetectConfig.maxCandidatesPerBlock))}`
|
|
281
386
|
);
|
|
282
387
|
if (patternDetectConfig.batchSize !== void 0)
|
|
283
388
|
console.log(
|
|
284
|
-
` batchSize: ${
|
|
389
|
+
` batchSize: ${chalk3.bold(String(patternDetectConfig.batchSize))}`
|
|
285
390
|
);
|
|
286
391
|
if (patternDetectConfig.streamResults !== void 0)
|
|
287
392
|
console.log(
|
|
288
|
-
` streamResults: ${
|
|
393
|
+
` streamResults: ${chalk3.bold(String(patternDetectConfig.streamResults))}`
|
|
289
394
|
);
|
|
290
395
|
if (patternDetectConfig.severity !== void 0)
|
|
291
396
|
console.log(
|
|
292
|
-
` severity: ${
|
|
397
|
+
` severity: ${chalk3.bold(String(patternDetectConfig.severity))}`
|
|
293
398
|
);
|
|
294
399
|
if (patternDetectConfig.includeTests !== void 0)
|
|
295
400
|
console.log(
|
|
296
|
-
` includeTests: ${
|
|
401
|
+
` includeTests: ${chalk3.bold(String(patternDetectConfig.includeTests))}`
|
|
297
402
|
);
|
|
298
403
|
}
|
|
299
404
|
if (finalOptions["context-analyzer"] || finalOptions.maxDepth) {
|
|
@@ -304,57 +409,57 @@ async function scanAction(directory, options) {
|
|
|
304
409
|
maxFragmentation: finalOptions.maxFragmentation,
|
|
305
410
|
includeNodeModules: finalOptions.includeNodeModules
|
|
306
411
|
};
|
|
307
|
-
console.log(
|
|
308
|
-
console.log(` maxDepth: ${
|
|
412
|
+
console.log(chalk3.white("\nContext-analyzer settings:"));
|
|
413
|
+
console.log(` maxDepth: ${chalk3.bold(ca.maxDepth ?? "default")}`);
|
|
309
414
|
console.log(
|
|
310
|
-
` maxContextBudget: ${
|
|
415
|
+
` maxContextBudget: ${chalk3.bold(ca.maxContextBudget ?? "default")}`
|
|
311
416
|
);
|
|
312
417
|
if (ca.minCohesion !== void 0)
|
|
313
|
-
console.log(` minCohesion: ${
|
|
418
|
+
console.log(` minCohesion: ${chalk3.bold(String(ca.minCohesion))}`);
|
|
314
419
|
if (ca.maxFragmentation !== void 0)
|
|
315
420
|
console.log(
|
|
316
|
-
` maxFragmentation: ${
|
|
421
|
+
` maxFragmentation: ${chalk3.bold(String(ca.maxFragmentation))}`
|
|
317
422
|
);
|
|
318
423
|
if (ca.includeNodeModules !== void 0)
|
|
319
424
|
console.log(
|
|
320
|
-
` includeNodeModules: ${
|
|
425
|
+
` includeNodeModules: ${chalk3.bold(String(ca.includeNodeModules))}`
|
|
321
426
|
);
|
|
322
427
|
}
|
|
323
428
|
if (finalOptions.consistency) {
|
|
324
429
|
const c = finalOptions.consistency;
|
|
325
|
-
console.log(
|
|
430
|
+
console.log(chalk3.white("\nConsistency settings:"));
|
|
326
431
|
console.log(
|
|
327
|
-
` checkNaming: ${
|
|
432
|
+
` checkNaming: ${chalk3.bold(String(c.checkNaming ?? true))}`
|
|
328
433
|
);
|
|
329
434
|
console.log(
|
|
330
|
-
` checkPatterns: ${
|
|
435
|
+
` checkPatterns: ${chalk3.bold(String(c.checkPatterns ?? true))}`
|
|
331
436
|
);
|
|
332
437
|
console.log(
|
|
333
|
-
` checkArchitecture: ${
|
|
438
|
+
` checkArchitecture: ${chalk3.bold(String(c.checkArchitecture ?? false))}`
|
|
334
439
|
);
|
|
335
440
|
if (c.minSeverity)
|
|
336
|
-
console.log(` minSeverity: ${
|
|
441
|
+
console.log(` minSeverity: ${chalk3.bold(c.minSeverity)}`);
|
|
337
442
|
if (c.acceptedAbbreviations)
|
|
338
443
|
console.log(
|
|
339
|
-
` acceptedAbbreviations: ${
|
|
444
|
+
` acceptedAbbreviations: ${chalk3.bold(truncateArray(c.acceptedAbbreviations, 8))}`
|
|
340
445
|
);
|
|
341
446
|
if (c.shortWords)
|
|
342
447
|
console.log(
|
|
343
|
-
` shortWords: ${
|
|
448
|
+
` shortWords: ${chalk3.bold(truncateArray(c.shortWords, 8))}`
|
|
344
449
|
);
|
|
345
450
|
}
|
|
346
|
-
console.log(
|
|
451
|
+
console.log(chalk3.white("\nStarting analysis..."));
|
|
347
452
|
const progressCallback = (event) => {
|
|
348
|
-
console.log(
|
|
453
|
+
console.log(chalk3.cyan(`
|
|
349
454
|
--- ${event.tool.toUpperCase()} RESULTS ---`));
|
|
350
455
|
try {
|
|
351
456
|
if (event.tool === "patterns") {
|
|
352
457
|
const pr = event.data;
|
|
353
458
|
console.log(
|
|
354
|
-
` Duplicate patterns: ${
|
|
459
|
+
` Duplicate patterns: ${chalk3.bold(String(pr.duplicates?.length || 0))}`
|
|
355
460
|
);
|
|
356
461
|
console.log(
|
|
357
|
-
` Files with pattern issues: ${
|
|
462
|
+
` Files with pattern issues: ${chalk3.bold(String(pr.results?.length || 0))}`
|
|
358
463
|
);
|
|
359
464
|
if (pr.duplicates && pr.duplicates.length > 0) {
|
|
360
465
|
pr.duplicates.slice(0, 5).forEach((d, i) => {
|
|
@@ -376,12 +481,12 @@ async function scanAction(directory, options) {
|
|
|
376
481
|
}
|
|
377
482
|
if (pr.groups && pr.groups.length >= 0) {
|
|
378
483
|
console.log(
|
|
379
|
-
` \u2705 Grouped ${
|
|
484
|
+
` \u2705 Grouped ${chalk3.bold(String(pr.duplicates?.length || 0))} duplicates into ${chalk3.bold(String(pr.groups.length))} file pairs`
|
|
380
485
|
);
|
|
381
486
|
}
|
|
382
487
|
if (pr.clusters && pr.clusters.length >= 0) {
|
|
383
488
|
console.log(
|
|
384
|
-
` \u2705 Created ${
|
|
489
|
+
` \u2705 Created ${chalk3.bold(String(pr.clusters.length))} refactor clusters`
|
|
385
490
|
);
|
|
386
491
|
pr.clusters.slice(0, 3).forEach((cl, idx) => {
|
|
387
492
|
const files = (cl.files || []).map((f) => f.path.split("/").pop()).join(", ");
|
|
@@ -393,7 +498,7 @@ async function scanAction(directory, options) {
|
|
|
393
498
|
} else if (event.tool === "context") {
|
|
394
499
|
const cr = event.data;
|
|
395
500
|
console.log(
|
|
396
|
-
` Context issues found: ${
|
|
501
|
+
` Context issues found: ${chalk3.bold(String(cr.length || 0))}`
|
|
397
502
|
);
|
|
398
503
|
cr.slice(0, 5).forEach((c, i) => {
|
|
399
504
|
const msg = c.message ? ` - ${c.message}` : "";
|
|
@@ -404,7 +509,7 @@ async function scanAction(directory, options) {
|
|
|
404
509
|
} else if (event.tool === "consistency") {
|
|
405
510
|
const rep = event.data;
|
|
406
511
|
console.log(
|
|
407
|
-
` Consistency totalIssues: ${
|
|
512
|
+
` Consistency totalIssues: ${chalk3.bold(String(rep.summary?.totalIssues || 0))}`
|
|
408
513
|
);
|
|
409
514
|
if (rep.results && rep.results.length > 0) {
|
|
410
515
|
const fileMap = /* @__PURE__ */ new Map();
|
|
@@ -439,7 +544,7 @@ async function scanAction(directory, options) {
|
|
|
439
544
|
const remaining = files.length - topFiles.length;
|
|
440
545
|
if (remaining > 0) {
|
|
441
546
|
console.log(
|
|
442
|
-
|
|
547
|
+
chalk3.dim(
|
|
443
548
|
` ... and ${remaining} more files with issues (use --output json for full details)`
|
|
444
549
|
)
|
|
445
550
|
);
|
|
@@ -448,37 +553,37 @@ async function scanAction(directory, options) {
|
|
|
448
553
|
} else if (event.tool === "doc-drift") {
|
|
449
554
|
const dr = event.data;
|
|
450
555
|
console.log(
|
|
451
|
-
` Issues found: ${
|
|
556
|
+
` Issues found: ${chalk3.bold(String(dr.issues?.length || 0))}`
|
|
452
557
|
);
|
|
453
558
|
if (dr.rawData) {
|
|
454
559
|
console.log(
|
|
455
|
-
` Signature Mismatches: ${
|
|
560
|
+
` Signature Mismatches: ${chalk3.bold(dr.rawData.outdatedComments || 0)}`
|
|
456
561
|
);
|
|
457
562
|
console.log(
|
|
458
|
-
` Undocumented Complexity: ${
|
|
563
|
+
` Undocumented Complexity: ${chalk3.bold(dr.rawData.undocumentedComplexity || 0)}`
|
|
459
564
|
);
|
|
460
565
|
}
|
|
461
566
|
} else if (event.tool === "deps-health") {
|
|
462
567
|
const dr = event.data;
|
|
463
568
|
console.log(
|
|
464
|
-
` Packages Analyzed: ${
|
|
569
|
+
` Packages Analyzed: ${chalk3.bold(String(dr.summary?.packagesAnalyzed || 0))}`
|
|
465
570
|
);
|
|
466
571
|
if (dr.rawData) {
|
|
467
572
|
console.log(
|
|
468
|
-
` Deprecated Packages: ${
|
|
573
|
+
` Deprecated Packages: ${chalk3.bold(dr.rawData.deprecatedPackages || 0)}`
|
|
469
574
|
);
|
|
470
575
|
console.log(
|
|
471
|
-
` AI Cutoff Skew Score: ${
|
|
576
|
+
` AI Cutoff Skew Score: ${chalk3.bold(dr.rawData.trainingCutoffSkew?.toFixed(1) || 0)}`
|
|
472
577
|
);
|
|
473
578
|
}
|
|
474
579
|
} else if (event.tool === "change-amplification" || event.tool === "changeAmplification") {
|
|
475
580
|
const dr = event.data;
|
|
476
581
|
console.log(
|
|
477
|
-
` Coupling issues: ${
|
|
582
|
+
` Coupling issues: ${chalk3.bold(String(dr.issues?.length || 0))}`
|
|
478
583
|
);
|
|
479
584
|
if (dr.summary) {
|
|
480
585
|
console.log(
|
|
481
|
-
` Complexity Score: ${
|
|
586
|
+
` Complexity Score: ${chalk3.bold(dr.summary.score || 0)}/100`
|
|
482
587
|
);
|
|
483
588
|
}
|
|
484
589
|
}
|
|
@@ -499,227 +604,169 @@ async function scanAction(directory, options) {
|
|
|
499
604
|
},
|
|
500
605
|
suppressToolConfig: true
|
|
501
606
|
});
|
|
502
|
-
console.log(
|
|
607
|
+
console.log(chalk3.cyan("\n=== AIReady Run Summary ==="));
|
|
503
608
|
console.log(
|
|
504
|
-
|
|
609
|
+
chalk3.white("Tools run:"),
|
|
505
610
|
(finalOptions.tools || ["patterns", "context", "consistency"]).join(", ")
|
|
506
611
|
);
|
|
507
|
-
console.log(
|
|
612
|
+
console.log(chalk3.cyan("\nResults summary:"));
|
|
508
613
|
console.log(
|
|
509
|
-
` Total issues (all tools): ${
|
|
614
|
+
` Total issues (all tools): ${chalk3.bold(String(results.summary.totalIssues || 0))}`
|
|
510
615
|
);
|
|
511
616
|
if (results.duplicates)
|
|
512
617
|
console.log(
|
|
513
|
-
` Duplicate patterns found: ${
|
|
618
|
+
` Duplicate patterns found: ${chalk3.bold(String(results.duplicates.length || 0))}`
|
|
514
619
|
);
|
|
515
620
|
if (results.patterns)
|
|
516
621
|
console.log(
|
|
517
|
-
` Pattern files with issues: ${
|
|
622
|
+
` Pattern files with issues: ${chalk3.bold(String(results.patterns.length || 0))}`
|
|
518
623
|
);
|
|
519
624
|
if (results.context)
|
|
520
625
|
console.log(
|
|
521
|
-
` Context issues: ${
|
|
626
|
+
` Context issues: ${chalk3.bold(String(results.context.length || 0))}`
|
|
522
627
|
);
|
|
523
628
|
console.log(
|
|
524
|
-
` Consistency issues: ${
|
|
629
|
+
` Consistency issues: ${chalk3.bold(String(results.consistency?.summary?.totalIssues || 0))}`
|
|
525
630
|
);
|
|
526
631
|
if (results.changeAmplification)
|
|
527
632
|
console.log(
|
|
528
|
-
` Change amplification: ${
|
|
633
|
+
` Change amplification: ${chalk3.bold(String(results.changeAmplification.summary?.score || 0))}/100`
|
|
529
634
|
);
|
|
530
|
-
console.log(
|
|
635
|
+
console.log(chalk3.cyan("===========================\n"));
|
|
531
636
|
const elapsedTime = getElapsedTime(startTime);
|
|
532
637
|
void elapsedTime;
|
|
533
638
|
let scoringResult;
|
|
534
639
|
if (options.score || finalOptions.scoring?.showBreakdown) {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
const patternScore = calculatePatternScore(
|
|
540
|
-
results.duplicates,
|
|
541
|
-
results.patterns?.length || 0
|
|
542
|
-
);
|
|
543
|
-
toolScores.set("pattern-detect", patternScore);
|
|
544
|
-
} catch (err) {
|
|
545
|
-
void err;
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
if (results.context) {
|
|
549
|
-
const { generateSummary: genContextSummary, calculateContextScore } = await import("@aiready/context-analyzer");
|
|
640
|
+
scoringResult = await scoreUnified(results, finalOptions);
|
|
641
|
+
console.log(chalk3.bold("\n\u{1F4CA} AI Readiness Overall Score"));
|
|
642
|
+
console.log(` ${formatScore(scoringResult)}`);
|
|
643
|
+
if (options.compareTo) {
|
|
550
644
|
try {
|
|
551
|
-
const
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
} catch (err) {
|
|
555
|
-
void err;
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
if (results.consistency) {
|
|
559
|
-
const { calculateConsistencyScore } = await import("@aiready/consistency");
|
|
560
|
-
try {
|
|
561
|
-
const issues = results.consistency.results?.flatMap((r) => r.issues) || [];
|
|
562
|
-
const totalFiles = results.consistency.summary?.filesAnalyzed || 0;
|
|
563
|
-
const consistencyScore = calculateConsistencyScore(
|
|
564
|
-
issues,
|
|
565
|
-
totalFiles
|
|
566
|
-
);
|
|
567
|
-
toolScores.set("consistency", consistencyScore);
|
|
568
|
-
} catch (err) {
|
|
569
|
-
void err;
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
if (results.aiSignalClarity) {
|
|
573
|
-
const { calculateAiSignalClarityScore } = await import("@aiready/ai-signal-clarity");
|
|
574
|
-
try {
|
|
575
|
-
const hrScore = calculateAiSignalClarityScore(
|
|
576
|
-
results.aiSignalClarity
|
|
645
|
+
const prevReportStr = readFileSync2(
|
|
646
|
+
resolvePath3(process.cwd(), options.compareTo),
|
|
647
|
+
"utf8"
|
|
577
648
|
);
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
toolScores.set("testability", tbScore);
|
|
597
|
-
} catch (err) {
|
|
598
|
-
void err;
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
if (results.docDrift) {
|
|
602
|
-
toolScores.set("doc-drift", {
|
|
603
|
-
toolName: "doc-drift",
|
|
604
|
-
score: results.docDrift.summary.score,
|
|
605
|
-
rawMetrics: results.docDrift.rawData,
|
|
606
|
-
factors: [],
|
|
607
|
-
recommendations: (results.docDrift.recommendations || []).map(
|
|
608
|
-
(action) => ({
|
|
609
|
-
action,
|
|
610
|
-
estimatedImpact: 5,
|
|
611
|
-
priority: "medium"
|
|
612
|
-
})
|
|
613
|
-
)
|
|
614
|
-
});
|
|
615
|
-
}
|
|
616
|
-
if (results.deps) {
|
|
617
|
-
toolScores.set("dependency-health", {
|
|
618
|
-
toolName: "dependency-health",
|
|
619
|
-
score: results.deps.summary.score,
|
|
620
|
-
rawMetrics: results.deps.rawData,
|
|
621
|
-
factors: [],
|
|
622
|
-
recommendations: (results.deps.recommendations || []).map(
|
|
623
|
-
(action) => ({
|
|
624
|
-
action,
|
|
625
|
-
estimatedImpact: 5,
|
|
626
|
-
priority: "medium"
|
|
627
|
-
})
|
|
628
|
-
)
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
if (results.changeAmplification) {
|
|
632
|
-
toolScores.set("change-amplification", {
|
|
633
|
-
toolName: "change-amplification",
|
|
634
|
-
score: results.changeAmplification.summary.score,
|
|
635
|
-
rawMetrics: results.changeAmplification.rawData,
|
|
636
|
-
factors: [],
|
|
637
|
-
recommendations: (results.changeAmplification.recommendations || []).map((action) => ({
|
|
638
|
-
action,
|
|
639
|
-
estimatedImpact: 5,
|
|
640
|
-
priority: "medium"
|
|
641
|
-
}))
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
const cliWeights = parseWeightString(options.weights);
|
|
645
|
-
if (toolScores.size > 0) {
|
|
646
|
-
scoringResult = calculateOverallScore(
|
|
647
|
-
toolScores,
|
|
648
|
-
finalOptions,
|
|
649
|
-
cliWeights.size ? cliWeights : void 0
|
|
650
|
-
);
|
|
651
|
-
console.log(chalk2.bold("\n\u{1F4CA} AI Readiness Overall Score"));
|
|
652
|
-
console.log(` ${formatScore(scoringResult)}`);
|
|
653
|
-
if (options.compareTo) {
|
|
654
|
-
try {
|
|
655
|
-
const prevReportStr = readFileSync2(
|
|
656
|
-
resolvePath2(process.cwd(), options.compareTo),
|
|
657
|
-
"utf8"
|
|
658
|
-
);
|
|
659
|
-
const prevReport = JSON.parse(prevReportStr);
|
|
660
|
-
const prevScore = prevReport.scoring?.score || prevReport.scoring?.overallScore;
|
|
661
|
-
if (typeof prevScore === "number") {
|
|
662
|
-
const diff = scoringResult.overall - prevScore;
|
|
663
|
-
const diffStr = diff > 0 ? `+${diff}` : String(diff);
|
|
664
|
-
console.log();
|
|
665
|
-
if (diff > 0) {
|
|
666
|
-
console.log(
|
|
667
|
-
chalk2.green(
|
|
668
|
-
` \u{1F4C8} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
|
|
669
|
-
)
|
|
670
|
-
);
|
|
671
|
-
} else if (diff < 0) {
|
|
672
|
-
console.log(
|
|
673
|
-
chalk2.red(
|
|
674
|
-
` \u{1F4C9} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
|
|
675
|
-
)
|
|
676
|
-
);
|
|
677
|
-
} else {
|
|
678
|
-
console.log(
|
|
679
|
-
chalk2.blue(
|
|
680
|
-
` \u2796 Trend: No change compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
|
|
681
|
-
)
|
|
682
|
-
);
|
|
683
|
-
}
|
|
684
|
-
scoringResult.trend = {
|
|
685
|
-
previousScore: prevScore,
|
|
686
|
-
difference: diff
|
|
687
|
-
};
|
|
649
|
+
const prevReport = JSON.parse(prevReportStr);
|
|
650
|
+
const prevScore = prevReport.scoring?.score || prevReport.scoring?.overallScore;
|
|
651
|
+
if (typeof prevScore === "number") {
|
|
652
|
+
const diff = scoringResult.overall - prevScore;
|
|
653
|
+
const diffStr = diff > 0 ? `+${diff}` : String(diff);
|
|
654
|
+
console.log();
|
|
655
|
+
if (diff > 0) {
|
|
656
|
+
console.log(
|
|
657
|
+
chalk3.green(
|
|
658
|
+
` \u{1F4C8} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
|
|
659
|
+
)
|
|
660
|
+
);
|
|
661
|
+
} else if (diff < 0) {
|
|
662
|
+
console.log(
|
|
663
|
+
chalk3.red(
|
|
664
|
+
` \u{1F4C9} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
|
|
665
|
+
)
|
|
666
|
+
);
|
|
688
667
|
} else {
|
|
689
668
|
console.log(
|
|
690
|
-
|
|
691
|
-
`
|
|
692
|
-
\u26A0\uFE0F Previous report at ${options.compareTo} does not contain an overall score.`
|
|
669
|
+
chalk3.blue(
|
|
670
|
+
` \u2796 Trend: No change compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
|
|
693
671
|
)
|
|
694
672
|
);
|
|
695
673
|
}
|
|
696
|
-
|
|
697
|
-
|
|
674
|
+
scoringResult.trend = {
|
|
675
|
+
previousScore: prevScore,
|
|
676
|
+
difference: diff
|
|
677
|
+
};
|
|
678
|
+
} else {
|
|
698
679
|
console.log(
|
|
699
|
-
|
|
680
|
+
chalk3.yellow(
|
|
700
681
|
`
|
|
701
|
-
\u26A0\uFE0F
|
|
682
|
+
\u26A0\uFE0F Previous report at ${options.compareTo} does not contain an overall score.`
|
|
702
683
|
)
|
|
703
684
|
);
|
|
704
685
|
}
|
|
686
|
+
} catch (e) {
|
|
687
|
+
void e;
|
|
688
|
+
console.log(
|
|
689
|
+
chalk3.yellow(
|
|
690
|
+
`
|
|
691
|
+
\u26A0\uFE0F Could not read or parse previous report at ${options.compareTo}.`
|
|
692
|
+
)
|
|
693
|
+
);
|
|
705
694
|
}
|
|
706
|
-
|
|
707
|
-
|
|
695
|
+
}
|
|
696
|
+
const totalWastedDuplication = (scoringResult.breakdown || []).reduce(
|
|
697
|
+
(sum, s) => sum + (s.tokenBudget?.wastedTokens.bySource.duplication || 0),
|
|
698
|
+
0
|
|
699
|
+
);
|
|
700
|
+
const totalWastedFragmentation = (scoringResult.breakdown || []).reduce(
|
|
701
|
+
(sum, s) => sum + (s.tokenBudget?.wastedTokens.bySource.fragmentation || 0),
|
|
702
|
+
0
|
|
703
|
+
);
|
|
704
|
+
const totalContext = Math.max(
|
|
705
|
+
...(scoringResult.breakdown || []).map(
|
|
706
|
+
(s) => s.tokenBudget?.totalContextTokens || 0
|
|
707
|
+
)
|
|
708
|
+
);
|
|
709
|
+
if (totalContext > 0) {
|
|
710
|
+
const unifiedBudget = calculateTokenBudget({
|
|
711
|
+
totalContextTokens: totalContext,
|
|
712
|
+
wastedTokens: {
|
|
713
|
+
duplication: totalWastedDuplication,
|
|
714
|
+
fragmentation: totalWastedFragmentation,
|
|
715
|
+
chattiness: 0
|
|
716
|
+
}
|
|
717
|
+
});
|
|
718
|
+
const targetModel = options.model || "claude-4.6";
|
|
719
|
+
const modelPreset = getModelPreset(targetModel);
|
|
720
|
+
const costEstimate = estimateCostFromBudget(unifiedBudget, modelPreset);
|
|
721
|
+
const barWidth = 20;
|
|
722
|
+
const filled = Math.round(unifiedBudget.efficiencyRatio * barWidth);
|
|
723
|
+
const bar = chalk3.green("\u2588".repeat(filled)) + chalk3.dim("\u2591".repeat(barWidth - filled));
|
|
724
|
+
console.log(chalk3.bold("\n\u{1F4CA} AI Token Budget Analysis (v0.13)"));
|
|
725
|
+
console.log(
|
|
726
|
+
` Efficiency: [${bar}] ${(unifiedBudget.efficiencyRatio * 100).toFixed(0)}%`
|
|
727
|
+
);
|
|
728
|
+
console.log(
|
|
729
|
+
` Total Context: ${chalk3.bold(unifiedBudget.totalContextTokens.toLocaleString())} tokens`
|
|
730
|
+
);
|
|
731
|
+
console.log(
|
|
732
|
+
` Wasted Tokens: ${chalk3.red(unifiedBudget.wastedTokens.total.toLocaleString())} (${(unifiedBudget.wastedTokens.total / unifiedBudget.totalContextTokens * 100).toFixed(1)}%)`
|
|
733
|
+
);
|
|
734
|
+
console.log(` Waste Breakdown:`);
|
|
735
|
+
console.log(
|
|
736
|
+
` \u2022 Duplication: ${unifiedBudget.wastedTokens.bySource.duplication.toLocaleString()} tokens`
|
|
737
|
+
);
|
|
738
|
+
console.log(
|
|
739
|
+
` \u2022 Fragmentation: ${unifiedBudget.wastedTokens.bySource.fragmentation.toLocaleString()} tokens`
|
|
740
|
+
);
|
|
741
|
+
console.log(
|
|
742
|
+
` Potential Savings: ${chalk3.green(unifiedBudget.potentialRetrievableTokens.toLocaleString())} tokens retrievable`
|
|
743
|
+
);
|
|
744
|
+
console.log(
|
|
745
|
+
`
|
|
746
|
+
Est. Monthly Cost (${modelPreset.name}): ${chalk3.bold("$" + costEstimate.total)} [range: $${costEstimate.range[0]}-$${costEstimate.range[1]}]`
|
|
747
|
+
);
|
|
748
|
+
scoringResult.tokenBudget = unifiedBudget;
|
|
749
|
+
scoringResult.costEstimate = {
|
|
750
|
+
model: modelPreset.name,
|
|
751
|
+
...costEstimate
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
if (scoringResult.breakdown && scoringResult.breakdown.length > 0) {
|
|
755
|
+
console.log(chalk3.bold("\nTool breakdown:"));
|
|
756
|
+
scoringResult.breakdown.forEach((tool) => {
|
|
757
|
+
const rating = getRating(tool.score);
|
|
758
|
+
const rd = getRatingDisplay(rating);
|
|
759
|
+
console.log(
|
|
760
|
+
` - ${tool.toolName}: ${tool.score}/100 (${rating}) ${rd.emoji}`
|
|
761
|
+
);
|
|
762
|
+
});
|
|
763
|
+
console.log();
|
|
764
|
+
if (finalOptions.scoring?.showBreakdown) {
|
|
765
|
+
console.log(chalk3.bold("Detailed tool breakdown:"));
|
|
708
766
|
scoringResult.breakdown.forEach((tool) => {
|
|
709
|
-
|
|
710
|
-
const rd = getRatingDisplay(rating);
|
|
711
|
-
console.log(
|
|
712
|
-
` - ${tool.toolName}: ${tool.score}/100 (${rating}) ${rd.emoji}`
|
|
713
|
-
);
|
|
767
|
+
console.log(formatToolScore(tool));
|
|
714
768
|
});
|
|
715
769
|
console.log();
|
|
716
|
-
if (finalOptions.scoring?.showBreakdown) {
|
|
717
|
-
console.log(chalk2.bold("Detailed tool breakdown:"));
|
|
718
|
-
scoringResult.breakdown.forEach((tool) => {
|
|
719
|
-
console.log(formatToolScore(tool));
|
|
720
|
-
});
|
|
721
|
-
console.log();
|
|
722
|
-
}
|
|
723
770
|
}
|
|
724
771
|
}
|
|
725
772
|
}
|
|
@@ -733,12 +780,23 @@ async function scanAction(directory, options) {
|
|
|
733
780
|
defaultFilename,
|
|
734
781
|
resolvedDir
|
|
735
782
|
);
|
|
736
|
-
const outputData = {
|
|
783
|
+
const outputData = {
|
|
784
|
+
...results,
|
|
785
|
+
scoring: scoringResult,
|
|
786
|
+
repository: repoMetadata
|
|
787
|
+
};
|
|
737
788
|
handleJSONOutput(
|
|
738
789
|
outputData,
|
|
739
790
|
outputPath,
|
|
740
791
|
`\u2705 Report saved to ${outputPath}`
|
|
741
792
|
);
|
|
793
|
+
if (options.upload) {
|
|
794
|
+
console.log(chalk3.blue("\n\u{1F4E4} Automatic upload triggered..."));
|
|
795
|
+
await uploadAction(outputPath, {
|
|
796
|
+
apiKey: options.apiKey,
|
|
797
|
+
server: options.server
|
|
798
|
+
});
|
|
799
|
+
}
|
|
742
800
|
await warnIfGraphCapExceeded(outputData, resolvedDir);
|
|
743
801
|
} else {
|
|
744
802
|
const timestamp = getReportTimestamp();
|
|
@@ -748,10 +806,21 @@ async function scanAction(directory, options) {
|
|
|
748
806
|
defaultFilename,
|
|
749
807
|
resolvedDir
|
|
750
808
|
);
|
|
751
|
-
const outputData = {
|
|
809
|
+
const outputData = {
|
|
810
|
+
...results,
|
|
811
|
+
scoring: scoringResult,
|
|
812
|
+
repository: repoMetadata
|
|
813
|
+
};
|
|
752
814
|
try {
|
|
753
815
|
writeFileSync(outputPath, JSON.stringify(outputData, null, 2));
|
|
754
|
-
console.log(
|
|
816
|
+
console.log(chalk3.dim(`\u2705 Report auto-persisted to ${outputPath}`));
|
|
817
|
+
if (options.upload) {
|
|
818
|
+
console.log(chalk3.blue("\n\u{1F4E4} Automatic upload triggered..."));
|
|
819
|
+
await uploadAction(outputPath, {
|
|
820
|
+
apiKey: options.apiKey,
|
|
821
|
+
server: options.server
|
|
822
|
+
});
|
|
823
|
+
}
|
|
755
824
|
await warnIfGraphCapExceeded(outputData, resolvedDir);
|
|
756
825
|
} catch (err) {
|
|
757
826
|
void err;
|
|
@@ -833,37 +902,37 @@ async function scanAction(directory, options) {
|
|
|
833
902
|
}
|
|
834
903
|
}
|
|
835
904
|
if (shouldFail) {
|
|
836
|
-
console.log(
|
|
837
|
-
console.log(
|
|
838
|
-
console.log(
|
|
905
|
+
console.log(chalk3.red("\n\u{1F6AB} PR BLOCKED: AI Readiness Check Failed"));
|
|
906
|
+
console.log(chalk3.red(` Reason: ${failReason}`));
|
|
907
|
+
console.log(chalk3.dim("\n Remediation steps:"));
|
|
839
908
|
console.log(
|
|
840
|
-
|
|
909
|
+
chalk3.dim(" 1. Run `aiready scan` locally to see detailed issues")
|
|
841
910
|
);
|
|
842
|
-
console.log(
|
|
911
|
+
console.log(chalk3.dim(" 2. Fix the critical issues before merging"));
|
|
843
912
|
console.log(
|
|
844
|
-
|
|
913
|
+
chalk3.dim(
|
|
845
914
|
" 3. Consider upgrading to Team plan for historical tracking: https://getaiready.dev/pricing"
|
|
846
915
|
)
|
|
847
916
|
);
|
|
848
917
|
process.exit(1);
|
|
849
918
|
} else {
|
|
850
|
-
console.log(
|
|
919
|
+
console.log(chalk3.green("\n\u2705 PR PASSED: AI Readiness Check"));
|
|
851
920
|
if (threshold) {
|
|
852
921
|
console.log(
|
|
853
|
-
|
|
922
|
+
chalk3.green(
|
|
854
923
|
` Score: ${scoringResult.overall}/100 (threshold: ${threshold})`
|
|
855
924
|
)
|
|
856
925
|
);
|
|
857
926
|
}
|
|
858
927
|
console.log(
|
|
859
|
-
|
|
928
|
+
chalk3.dim(
|
|
860
929
|
"\n \u{1F4A1} Track historical trends: https://getaiready.dev \u2014 Team plan $99/mo"
|
|
861
930
|
)
|
|
862
931
|
);
|
|
863
932
|
}
|
|
864
933
|
}
|
|
865
934
|
} catch (error) {
|
|
866
|
-
|
|
935
|
+
handleCLIError2(error, "Analysis");
|
|
867
936
|
}
|
|
868
937
|
}
|
|
869
938
|
var scanHelpText = `
|
|
@@ -877,6 +946,8 @@ EXAMPLES:
|
|
|
877
946
|
$ aiready scan --ci --threshold 70 # GitHub Actions gatekeeper
|
|
878
947
|
$ aiready scan --ci --fail-on major # Fail on major+ issues
|
|
879
948
|
$ aiready scan --output json --output-file report.json
|
|
949
|
+
$ aiready scan --upload --api-key ar_... # Automatic platform upload
|
|
950
|
+
$ aiready scan --upload --server custom-url.com # Upload to custom platform
|
|
880
951
|
|
|
881
952
|
PROFILES:
|
|
882
953
|
agentic: aiSignalClarity, grounding, testability
|
|
@@ -896,20 +967,20 @@ CI/CD INTEGRATION (Gatekeeper Mode):
|
|
|
896
967
|
`;
|
|
897
968
|
|
|
898
969
|
// src/commands/patterns.ts
|
|
899
|
-
import
|
|
900
|
-
import { resolve as
|
|
970
|
+
import chalk4 from "chalk";
|
|
971
|
+
import { resolve as resolvePath4 } from "path";
|
|
901
972
|
import {
|
|
902
973
|
loadMergedConfig as loadMergedConfig2,
|
|
903
974
|
handleJSONOutput as handleJSONOutput2,
|
|
904
|
-
handleCLIError as
|
|
975
|
+
handleCLIError as handleCLIError3,
|
|
905
976
|
getElapsedTime as getElapsedTime2,
|
|
906
977
|
resolveOutputPath as resolveOutputPath2,
|
|
907
978
|
formatToolScore as formatToolScore2
|
|
908
979
|
} from "@aiready/core";
|
|
909
980
|
async function patternsAction(directory, options) {
|
|
910
|
-
console.log(
|
|
981
|
+
console.log(chalk4.blue("\u{1F50D} Analyzing patterns...\n"));
|
|
911
982
|
const startTime = Date.now();
|
|
912
|
-
const resolvedDir =
|
|
983
|
+
const resolvedDir = resolvePath4(process.cwd(), directory || ".");
|
|
913
984
|
try {
|
|
914
985
|
const useSmartDefaults = !options.fullScan;
|
|
915
986
|
const defaults = {
|
|
@@ -973,38 +1044,38 @@ async function patternsAction(directory, options) {
|
|
|
973
1044
|
const terminalWidth = process.stdout.columns || 80;
|
|
974
1045
|
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
975
1046
|
const divider = "\u2501".repeat(dividerWidth);
|
|
976
|
-
console.log(
|
|
977
|
-
console.log(
|
|
978
|
-
console.log(
|
|
1047
|
+
console.log(chalk4.cyan(divider));
|
|
1048
|
+
console.log(chalk4.bold.white(" PATTERN ANALYSIS SUMMARY"));
|
|
1049
|
+
console.log(chalk4.cyan(divider) + "\n");
|
|
979
1050
|
console.log(
|
|
980
|
-
|
|
1051
|
+
chalk4.white(`\u{1F4C1} Files analyzed: ${chalk4.bold(results.length)}`)
|
|
981
1052
|
);
|
|
982
1053
|
console.log(
|
|
983
|
-
|
|
984
|
-
`\u26A0 Duplicate patterns found: ${
|
|
1054
|
+
chalk4.yellow(
|
|
1055
|
+
`\u26A0 Duplicate patterns found: ${chalk4.bold(summary.totalPatterns)}`
|
|
985
1056
|
)
|
|
986
1057
|
);
|
|
987
1058
|
console.log(
|
|
988
|
-
|
|
989
|
-
`\u{1F4B0} Token cost (wasted): ${
|
|
1059
|
+
chalk4.red(
|
|
1060
|
+
`\u{1F4B0} Token cost (wasted): ${chalk4.bold(summary.totalTokenCost.toLocaleString())}`
|
|
990
1061
|
)
|
|
991
1062
|
);
|
|
992
1063
|
console.log(
|
|
993
|
-
|
|
1064
|
+
chalk4.gray(`\u23F1 Analysis time: ${chalk4.bold(elapsedTime + "s")}`)
|
|
994
1065
|
);
|
|
995
1066
|
const sortedTypes = Object.entries(summary.patternsByType || {}).filter(([, count]) => count > 0).sort(([, a], [, b]) => b - a);
|
|
996
1067
|
if (sortedTypes.length > 0) {
|
|
997
|
-
console.log(
|
|
998
|
-
console.log(
|
|
999
|
-
console.log(
|
|
1068
|
+
console.log(chalk4.cyan("\n" + divider));
|
|
1069
|
+
console.log(chalk4.bold.white(" PATTERNS BY TYPE"));
|
|
1070
|
+
console.log(chalk4.cyan(divider) + "\n");
|
|
1000
1071
|
sortedTypes.forEach(([type, count]) => {
|
|
1001
|
-
console.log(` ${
|
|
1072
|
+
console.log(` ${chalk4.white(type.padEnd(15))} ${chalk4.bold(count)}`);
|
|
1002
1073
|
});
|
|
1003
1074
|
}
|
|
1004
1075
|
if (summary.totalPatterns > 0 && duplicates.length > 0) {
|
|
1005
|
-
console.log(
|
|
1006
|
-
console.log(
|
|
1007
|
-
console.log(
|
|
1076
|
+
console.log(chalk4.cyan("\n" + divider));
|
|
1077
|
+
console.log(chalk4.bold.white(" TOP DUPLICATE PATTERNS"));
|
|
1078
|
+
console.log(chalk4.cyan(divider) + "\n");
|
|
1008
1079
|
const topDuplicates = [...duplicates].sort((a, b) => b.similarity - a.similarity).slice(0, 10);
|
|
1009
1080
|
topDuplicates.forEach((dup) => {
|
|
1010
1081
|
const severity = dup.similarity > 0.95 ? "CRITICAL" : dup.similarity > 0.9 ? "HIGH" : "MEDIUM";
|
|
@@ -1012,31 +1083,31 @@ async function patternsAction(directory, options) {
|
|
|
1012
1083
|
const file1Name = dup.file1.split("/").pop() || dup.file1;
|
|
1013
1084
|
const file2Name = dup.file2.split("/").pop() || dup.file2;
|
|
1014
1085
|
console.log(
|
|
1015
|
-
`${severityIcon} ${severity}: ${
|
|
1086
|
+
`${severityIcon} ${severity}: ${chalk4.bold(file1Name)} \u2194 ${chalk4.bold(file2Name)}`
|
|
1016
1087
|
);
|
|
1017
1088
|
console.log(
|
|
1018
|
-
` Similarity: ${
|
|
1089
|
+
` Similarity: ${chalk4.bold(Math.round(dup.similarity * 100) + "%")} | Wasted: ${chalk4.bold(dup.tokenCost.toLocaleString())} tokens each`
|
|
1019
1090
|
);
|
|
1020
1091
|
console.log(
|
|
1021
|
-
` Lines: ${
|
|
1092
|
+
` Lines: ${chalk4.cyan(dup.line1 + "-" + dup.endLine1)} \u2194 ${chalk4.cyan(dup.line2 + "-" + dup.endLine2)}
|
|
1022
1093
|
`
|
|
1023
1094
|
);
|
|
1024
1095
|
});
|
|
1025
1096
|
} else {
|
|
1026
1097
|
console.log(
|
|
1027
|
-
|
|
1098
|
+
chalk4.green("\n\u2728 Great! No duplicate patterns detected.\n")
|
|
1028
1099
|
);
|
|
1029
1100
|
}
|
|
1030
1101
|
if (patternScore) {
|
|
1031
|
-
console.log(
|
|
1032
|
-
console.log(
|
|
1033
|
-
console.log(
|
|
1102
|
+
console.log(chalk4.cyan(divider));
|
|
1103
|
+
console.log(chalk4.bold.white(" AI READINESS SCORE (Patterns)"));
|
|
1104
|
+
console.log(chalk4.cyan(divider) + "\n");
|
|
1034
1105
|
console.log(formatToolScore2(patternScore));
|
|
1035
1106
|
console.log();
|
|
1036
1107
|
}
|
|
1037
1108
|
}
|
|
1038
1109
|
} catch (error) {
|
|
1039
|
-
|
|
1110
|
+
handleCLIError3(error, "Pattern analysis");
|
|
1040
1111
|
}
|
|
1041
1112
|
}
|
|
1042
1113
|
var patternsHelpText = `
|
|
@@ -1047,20 +1118,20 @@ EXAMPLES:
|
|
|
1047
1118
|
`;
|
|
1048
1119
|
|
|
1049
1120
|
// src/commands/context.ts
|
|
1050
|
-
import
|
|
1051
|
-
import { resolve as
|
|
1121
|
+
import chalk5 from "chalk";
|
|
1122
|
+
import { resolve as resolvePath5 } from "path";
|
|
1052
1123
|
import {
|
|
1053
1124
|
loadMergedConfig as loadMergedConfig3,
|
|
1054
1125
|
handleJSONOutput as handleJSONOutput3,
|
|
1055
|
-
handleCLIError as
|
|
1126
|
+
handleCLIError as handleCLIError4,
|
|
1056
1127
|
getElapsedTime as getElapsedTime3,
|
|
1057
1128
|
resolveOutputPath as resolveOutputPath3,
|
|
1058
1129
|
formatToolScore as formatToolScore3
|
|
1059
1130
|
} from "@aiready/core";
|
|
1060
1131
|
async function contextAction(directory, options) {
|
|
1061
|
-
console.log(
|
|
1132
|
+
console.log(chalk5.blue("\u{1F9E0} Analyzing context costs...\n"));
|
|
1062
1133
|
const startTime = Date.now();
|
|
1063
|
-
const resolvedDir =
|
|
1134
|
+
const resolvedDir = resolvePath5(process.cwd(), directory || ".");
|
|
1064
1135
|
try {
|
|
1065
1136
|
const defaults = {
|
|
1066
1137
|
maxDepth: 5,
|
|
@@ -1126,85 +1197,85 @@ async function contextAction(directory, options) {
|
|
|
1126
1197
|
const terminalWidth = process.stdout.columns || 80;
|
|
1127
1198
|
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
1128
1199
|
const divider = "\u2501".repeat(dividerWidth);
|
|
1129
|
-
console.log(
|
|
1130
|
-
console.log(
|
|
1131
|
-
console.log(
|
|
1200
|
+
console.log(chalk5.cyan(divider));
|
|
1201
|
+
console.log(chalk5.bold.white(" CONTEXT ANALYSIS SUMMARY"));
|
|
1202
|
+
console.log(chalk5.cyan(divider) + "\n");
|
|
1132
1203
|
console.log(
|
|
1133
|
-
|
|
1204
|
+
chalk5.white(`\u{1F4C1} Files analyzed: ${chalk5.bold(summary.totalFiles)}`)
|
|
1134
1205
|
);
|
|
1135
1206
|
console.log(
|
|
1136
|
-
|
|
1137
|
-
`\u{1F4CA} Total tokens: ${
|
|
1207
|
+
chalk5.white(
|
|
1208
|
+
`\u{1F4CA} Total tokens: ${chalk5.bold(summary.totalTokens.toLocaleString())}`
|
|
1138
1209
|
)
|
|
1139
1210
|
);
|
|
1140
1211
|
console.log(
|
|
1141
|
-
|
|
1142
|
-
`\u{1F4B0} Avg context budget: ${
|
|
1212
|
+
chalk5.yellow(
|
|
1213
|
+
`\u{1F4B0} Avg context budget: ${chalk5.bold(summary.avgContextBudget.toFixed(0))} tokens/file`
|
|
1143
1214
|
)
|
|
1144
1215
|
);
|
|
1145
1216
|
console.log(
|
|
1146
|
-
|
|
1217
|
+
chalk5.white(`\u23F1 Analysis time: ${chalk5.bold(elapsedTime + "s")}
|
|
1147
1218
|
`)
|
|
1148
1219
|
);
|
|
1149
1220
|
const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
|
|
1150
1221
|
if (totalIssues > 0) {
|
|
1151
|
-
console.log(
|
|
1222
|
+
console.log(chalk5.bold("\u26A0\uFE0F Issues Found:\n"));
|
|
1152
1223
|
if (summary.criticalIssues > 0) {
|
|
1153
1224
|
console.log(
|
|
1154
|
-
|
|
1225
|
+
chalk5.red(` \u{1F534} Critical: ${chalk5.bold(summary.criticalIssues)}`)
|
|
1155
1226
|
);
|
|
1156
1227
|
}
|
|
1157
1228
|
if (summary.majorIssues > 0) {
|
|
1158
1229
|
console.log(
|
|
1159
|
-
|
|
1230
|
+
chalk5.yellow(` \u{1F7E1} Major: ${chalk5.bold(summary.majorIssues)}`)
|
|
1160
1231
|
);
|
|
1161
1232
|
}
|
|
1162
1233
|
if (summary.minorIssues > 0) {
|
|
1163
1234
|
console.log(
|
|
1164
|
-
|
|
1235
|
+
chalk5.blue(` \u{1F535} Minor: ${chalk5.bold(summary.minorIssues)}`)
|
|
1165
1236
|
);
|
|
1166
1237
|
}
|
|
1167
1238
|
console.log(
|
|
1168
|
-
|
|
1239
|
+
chalk5.green(
|
|
1169
1240
|
`
|
|
1170
|
-
\u{1F4A1} Potential savings: ${
|
|
1241
|
+
\u{1F4A1} Potential savings: ${chalk5.bold(summary.totalPotentialSavings.toLocaleString())} tokens
|
|
1171
1242
|
`
|
|
1172
1243
|
)
|
|
1173
1244
|
);
|
|
1174
1245
|
} else {
|
|
1175
|
-
console.log(
|
|
1246
|
+
console.log(chalk5.green("\u2705 No significant issues found!\n"));
|
|
1176
1247
|
}
|
|
1177
1248
|
if (summary.deepFiles.length > 0) {
|
|
1178
|
-
console.log(
|
|
1249
|
+
console.log(chalk5.bold("\u{1F4CF} Deep Import Chains:\n"));
|
|
1179
1250
|
console.log(
|
|
1180
|
-
|
|
1251
|
+
chalk5.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`)
|
|
1181
1252
|
);
|
|
1182
1253
|
console.log(
|
|
1183
|
-
|
|
1254
|
+
chalk5.gray(` Maximum depth: ${summary.maxImportDepth}
|
|
1184
1255
|
`)
|
|
1185
1256
|
);
|
|
1186
1257
|
summary.deepFiles.slice(0, 10).forEach((item) => {
|
|
1187
1258
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1188
1259
|
console.log(
|
|
1189
|
-
` ${
|
|
1260
|
+
` ${chalk5.cyan("\u2192")} ${chalk5.white(fileName)} ${chalk5.dim(`(depth: ${item.depth})`)}`
|
|
1190
1261
|
);
|
|
1191
1262
|
});
|
|
1192
1263
|
console.log();
|
|
1193
1264
|
}
|
|
1194
1265
|
if (summary.fragmentedModules.length > 0) {
|
|
1195
|
-
console.log(
|
|
1266
|
+
console.log(chalk5.bold("\u{1F9E9} Fragmented Modules:\n"));
|
|
1196
1267
|
console.log(
|
|
1197
|
-
|
|
1268
|
+
chalk5.gray(
|
|
1198
1269
|
` Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(0)}%
|
|
1199
1270
|
`
|
|
1200
1271
|
)
|
|
1201
1272
|
);
|
|
1202
1273
|
summary.fragmentedModules.slice(0, 10).forEach((module) => {
|
|
1203
1274
|
console.log(
|
|
1204
|
-
` ${
|
|
1275
|
+
` ${chalk5.yellow("\u25CF")} ${chalk5.white(module.domain)} - ${chalk5.dim(`${module.files.length} files, ${(module.fragmentationScore * 100).toFixed(0)}% scattered`)}`
|
|
1205
1276
|
);
|
|
1206
1277
|
console.log(
|
|
1207
|
-
|
|
1278
|
+
chalk5.dim(
|
|
1208
1279
|
` Token cost: ${module.totalTokens.toLocaleString()}, Cohesion: ${(module.avgCohesion * 100).toFixed(0)}%`
|
|
1209
1280
|
)
|
|
1210
1281
|
);
|
|
@@ -1212,9 +1283,9 @@ async function contextAction(directory, options) {
|
|
|
1212
1283
|
console.log();
|
|
1213
1284
|
}
|
|
1214
1285
|
if (summary.lowCohesionFiles.length > 0) {
|
|
1215
|
-
console.log(
|
|
1286
|
+
console.log(chalk5.bold("\u{1F500} Low Cohesion Files:\n"));
|
|
1216
1287
|
console.log(
|
|
1217
|
-
|
|
1288
|
+
chalk5.gray(
|
|
1218
1289
|
` Average cohesion: ${(summary.avgCohesion * 100).toFixed(0)}%
|
|
1219
1290
|
`
|
|
1220
1291
|
)
|
|
@@ -1222,53 +1293,53 @@ async function contextAction(directory, options) {
|
|
|
1222
1293
|
summary.lowCohesionFiles.slice(0, 10).forEach((item) => {
|
|
1223
1294
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1224
1295
|
const scorePercent = (item.score * 100).toFixed(0);
|
|
1225
|
-
const color = item.score < 0.4 ?
|
|
1296
|
+
const color = item.score < 0.4 ? chalk5.red : chalk5.yellow;
|
|
1226
1297
|
console.log(
|
|
1227
|
-
` ${color("\u25CB")} ${
|
|
1298
|
+
` ${color("\u25CB")} ${chalk5.white(fileName)} ${chalk5.dim(`(${scorePercent}% cohesion)`)}`
|
|
1228
1299
|
);
|
|
1229
1300
|
});
|
|
1230
1301
|
console.log();
|
|
1231
1302
|
}
|
|
1232
1303
|
if (summary.topExpensiveFiles.length > 0) {
|
|
1233
|
-
console.log(
|
|
1304
|
+
console.log(chalk5.bold("\u{1F4B8} Most Expensive Files (Context Budget):\n"));
|
|
1234
1305
|
summary.topExpensiveFiles.slice(0, 10).forEach((item) => {
|
|
1235
1306
|
const fileName = item.file.split("/").slice(-2).join("/");
|
|
1236
|
-
const severityColor = item.severity === "critical" ?
|
|
1307
|
+
const severityColor = item.severity === "critical" ? chalk5.red : item.severity === "major" ? chalk5.yellow : chalk5.blue;
|
|
1237
1308
|
console.log(
|
|
1238
|
-
` ${severityColor("\u25CF")} ${
|
|
1309
|
+
` ${severityColor("\u25CF")} ${chalk5.white(fileName)} ${chalk5.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`
|
|
1239
1310
|
);
|
|
1240
1311
|
});
|
|
1241
1312
|
console.log();
|
|
1242
1313
|
}
|
|
1243
1314
|
if (contextScore) {
|
|
1244
|
-
console.log(
|
|
1245
|
-
console.log(
|
|
1246
|
-
console.log(
|
|
1315
|
+
console.log(chalk5.cyan(divider));
|
|
1316
|
+
console.log(chalk5.bold.white(" AI READINESS SCORE (Context)"));
|
|
1317
|
+
console.log(chalk5.cyan(divider) + "\n");
|
|
1247
1318
|
console.log(formatToolScore3(contextScore));
|
|
1248
1319
|
console.log();
|
|
1249
1320
|
}
|
|
1250
1321
|
}
|
|
1251
1322
|
} catch (error) {
|
|
1252
|
-
|
|
1323
|
+
handleCLIError4(error, "Context analysis");
|
|
1253
1324
|
}
|
|
1254
1325
|
}
|
|
1255
1326
|
|
|
1256
1327
|
// src/commands/consistency.ts
|
|
1257
|
-
import
|
|
1328
|
+
import chalk6 from "chalk";
|
|
1258
1329
|
import { writeFileSync as writeFileSync2 } from "fs";
|
|
1259
|
-
import { resolve as
|
|
1330
|
+
import { resolve as resolvePath6 } from "path";
|
|
1260
1331
|
import {
|
|
1261
1332
|
loadMergedConfig as loadMergedConfig4,
|
|
1262
1333
|
handleJSONOutput as handleJSONOutput4,
|
|
1263
|
-
handleCLIError as
|
|
1334
|
+
handleCLIError as handleCLIError5,
|
|
1264
1335
|
getElapsedTime as getElapsedTime4,
|
|
1265
1336
|
resolveOutputPath as resolveOutputPath4,
|
|
1266
1337
|
formatToolScore as formatToolScore4
|
|
1267
1338
|
} from "@aiready/core";
|
|
1268
1339
|
async function consistencyAction(directory, options) {
|
|
1269
|
-
console.log(
|
|
1340
|
+
console.log(chalk6.blue("\u{1F50D} Analyzing consistency...\n"));
|
|
1270
1341
|
const startTime = Date.now();
|
|
1271
|
-
const resolvedDir =
|
|
1342
|
+
const resolvedDir = resolvePath6(process.cwd(), directory || ".");
|
|
1272
1343
|
try {
|
|
1273
1344
|
const defaults = {
|
|
1274
1345
|
checkNaming: true,
|
|
@@ -1328,23 +1399,23 @@ async function consistencyAction(directory, options) {
|
|
|
1328
1399
|
resolvedDir
|
|
1329
1400
|
);
|
|
1330
1401
|
writeFileSync2(outputPath, markdown);
|
|
1331
|
-
console.log(
|
|
1402
|
+
console.log(chalk6.green(`\u2705 Report saved to ${outputPath}`));
|
|
1332
1403
|
} else {
|
|
1333
|
-
console.log(
|
|
1404
|
+
console.log(chalk6.bold("\n\u{1F4CA} Summary\n"));
|
|
1334
1405
|
console.log(
|
|
1335
|
-
`Files Analyzed: ${
|
|
1406
|
+
`Files Analyzed: ${chalk6.cyan(report.summary.filesAnalyzed)}`
|
|
1336
1407
|
);
|
|
1337
|
-
console.log(`Total Issues: ${
|
|
1338
|
-
console.log(` Naming: ${
|
|
1339
|
-
console.log(` Patterns: ${
|
|
1408
|
+
console.log(`Total Issues: ${chalk6.yellow(report.summary.totalIssues)}`);
|
|
1409
|
+
console.log(` Naming: ${chalk6.yellow(report.summary.namingIssues)}`);
|
|
1410
|
+
console.log(` Patterns: ${chalk6.yellow(report.summary.patternIssues)}`);
|
|
1340
1411
|
console.log(
|
|
1341
|
-
` Architecture: ${
|
|
1412
|
+
` Architecture: ${chalk6.yellow(report.summary.architectureIssues || 0)}`
|
|
1342
1413
|
);
|
|
1343
|
-
console.log(`Analysis Time: ${
|
|
1414
|
+
console.log(`Analysis Time: ${chalk6.gray(elapsedTime + "s")}
|
|
1344
1415
|
`);
|
|
1345
1416
|
if (report.summary.totalIssues === 0) {
|
|
1346
1417
|
console.log(
|
|
1347
|
-
|
|
1418
|
+
chalk6.green(
|
|
1348
1419
|
"\u2728 No consistency issues found! Your codebase is well-maintained.\n"
|
|
1349
1420
|
)
|
|
1350
1421
|
);
|
|
@@ -1356,20 +1427,20 @@ async function consistencyAction(directory, options) {
|
|
|
1356
1427
|
(r) => r.issues.some((i) => i.category === "patterns")
|
|
1357
1428
|
);
|
|
1358
1429
|
if (namingResults.length > 0) {
|
|
1359
|
-
console.log(
|
|
1430
|
+
console.log(chalk6.bold("\u{1F3F7}\uFE0F Naming Issues\n"));
|
|
1360
1431
|
let shown = 0;
|
|
1361
1432
|
for (const result of namingResults) {
|
|
1362
1433
|
if (shown >= 5) break;
|
|
1363
1434
|
for (const issue of result.issues) {
|
|
1364
1435
|
if (shown >= 5) break;
|
|
1365
|
-
const severityColor = issue.severity === "critical" ?
|
|
1436
|
+
const severityColor = issue.severity === "critical" ? chalk6.red : issue.severity === "major" ? chalk6.yellow : issue.severity === "minor" ? chalk6.blue : chalk6.gray;
|
|
1366
1437
|
console.log(
|
|
1367
|
-
`${severityColor(issue.severity.toUpperCase())} ${
|
|
1438
|
+
`${severityColor(issue.severity.toUpperCase())} ${chalk6.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1368
1439
|
);
|
|
1369
1440
|
console.log(` ${issue.message}`);
|
|
1370
1441
|
if (issue.suggestion) {
|
|
1371
1442
|
console.log(
|
|
1372
|
-
` ${
|
|
1443
|
+
` ${chalk6.dim("\u2192")} ${chalk6.italic(issue.suggestion)}`
|
|
1373
1444
|
);
|
|
1374
1445
|
}
|
|
1375
1446
|
console.log();
|
|
@@ -1378,25 +1449,25 @@ async function consistencyAction(directory, options) {
|
|
|
1378
1449
|
}
|
|
1379
1450
|
const remaining = namingResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
1380
1451
|
if (remaining > 0) {
|
|
1381
|
-
console.log(
|
|
1452
|
+
console.log(chalk6.dim(` ... and ${remaining} more issues
|
|
1382
1453
|
`));
|
|
1383
1454
|
}
|
|
1384
1455
|
}
|
|
1385
1456
|
if (patternResults.length > 0) {
|
|
1386
|
-
console.log(
|
|
1457
|
+
console.log(chalk6.bold("\u{1F504} Pattern Issues\n"));
|
|
1387
1458
|
let shown = 0;
|
|
1388
1459
|
for (const result of patternResults) {
|
|
1389
1460
|
if (shown >= 5) break;
|
|
1390
1461
|
for (const issue of result.issues) {
|
|
1391
1462
|
if (shown >= 5) break;
|
|
1392
|
-
const severityColor = issue.severity === "critical" ?
|
|
1463
|
+
const severityColor = issue.severity === "critical" ? chalk6.red : issue.severity === "major" ? chalk6.yellow : issue.severity === "minor" ? chalk6.blue : chalk6.gray;
|
|
1393
1464
|
console.log(
|
|
1394
|
-
`${severityColor(issue.severity.toUpperCase())} ${
|
|
1465
|
+
`${severityColor(issue.severity.toUpperCase())} ${chalk6.dim(`${issue.location.file}:${issue.location.line}`)}`
|
|
1395
1466
|
);
|
|
1396
1467
|
console.log(` ${issue.message}`);
|
|
1397
1468
|
if (issue.suggestion) {
|
|
1398
1469
|
console.log(
|
|
1399
|
-
` ${
|
|
1470
|
+
` ${chalk6.dim("\u2192")} ${chalk6.italic(issue.suggestion)}`
|
|
1400
1471
|
);
|
|
1401
1472
|
}
|
|
1402
1473
|
console.log();
|
|
@@ -1405,12 +1476,12 @@ async function consistencyAction(directory, options) {
|
|
|
1405
1476
|
}
|
|
1406
1477
|
const remaining = patternResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
|
|
1407
1478
|
if (remaining > 0) {
|
|
1408
|
-
console.log(
|
|
1479
|
+
console.log(chalk6.dim(` ... and ${remaining} more issues
|
|
1409
1480
|
`));
|
|
1410
1481
|
}
|
|
1411
1482
|
}
|
|
1412
1483
|
if (report.recommendations.length > 0) {
|
|
1413
|
-
console.log(
|
|
1484
|
+
console.log(chalk6.bold("\u{1F4A1} Recommendations\n"));
|
|
1414
1485
|
report.recommendations.forEach((rec, i) => {
|
|
1415
1486
|
console.log(`${i + 1}. ${rec}`);
|
|
1416
1487
|
});
|
|
@@ -1418,38 +1489,38 @@ async function consistencyAction(directory, options) {
|
|
|
1418
1489
|
}
|
|
1419
1490
|
}
|
|
1420
1491
|
if (consistencyScore) {
|
|
1421
|
-
console.log(
|
|
1492
|
+
console.log(chalk6.bold("\n\u{1F4CA} AI Readiness Score (Consistency)\n"));
|
|
1422
1493
|
console.log(formatToolScore4(consistencyScore));
|
|
1423
1494
|
console.log();
|
|
1424
1495
|
}
|
|
1425
1496
|
}
|
|
1426
1497
|
} catch (error) {
|
|
1427
|
-
|
|
1498
|
+
handleCLIError5(error, "Consistency analysis");
|
|
1428
1499
|
}
|
|
1429
1500
|
}
|
|
1430
1501
|
|
|
1431
1502
|
// src/commands/visualize.ts
|
|
1432
|
-
import
|
|
1503
|
+
import chalk7 from "chalk";
|
|
1433
1504
|
import { writeFileSync as writeFileSync3, readFileSync as readFileSync3, existsSync as existsSync2, copyFileSync } from "fs";
|
|
1434
|
-
import { resolve as
|
|
1505
|
+
import { resolve as resolvePath7 } from "path";
|
|
1435
1506
|
import { spawn } from "child_process";
|
|
1436
|
-
import { handleCLIError as
|
|
1507
|
+
import { handleCLIError as handleCLIError6 } from "@aiready/core";
|
|
1437
1508
|
import { generateHTML } from "@aiready/core";
|
|
1438
1509
|
async function visualizeAction(directory, options) {
|
|
1439
1510
|
try {
|
|
1440
|
-
const dirPath =
|
|
1441
|
-
let reportPath = options.report ?
|
|
1511
|
+
const dirPath = resolvePath7(process.cwd(), directory || ".");
|
|
1512
|
+
let reportPath = options.report ? resolvePath7(dirPath, options.report) : null;
|
|
1442
1513
|
if (!reportPath || !existsSync2(reportPath)) {
|
|
1443
1514
|
const latestScan = findLatestScanReport(dirPath);
|
|
1444
1515
|
if (latestScan) {
|
|
1445
1516
|
reportPath = latestScan;
|
|
1446
1517
|
console.log(
|
|
1447
|
-
|
|
1518
|
+
chalk7.dim(`Found latest report: ${latestScan.split("/").pop()}`)
|
|
1448
1519
|
);
|
|
1449
1520
|
} else {
|
|
1450
|
-
console.error(
|
|
1521
|
+
console.error(chalk7.red("\u274C No AI readiness report found"));
|
|
1451
1522
|
console.log(
|
|
1452
|
-
|
|
1523
|
+
chalk7.dim(
|
|
1453
1524
|
`
|
|
1454
1525
|
Generate a report with:
|
|
1455
1526
|
aiready scan --output json
|
|
@@ -1463,7 +1534,7 @@ Or specify a custom report:
|
|
|
1463
1534
|
}
|
|
1464
1535
|
const raw = readFileSync3(reportPath, "utf8");
|
|
1465
1536
|
const report = JSON.parse(raw);
|
|
1466
|
-
const configPath =
|
|
1537
|
+
const configPath = resolvePath7(dirPath, "aiready.json");
|
|
1467
1538
|
let graphConfig = { maxNodes: 400, maxEdges: 600 };
|
|
1468
1539
|
if (existsSync2(configPath)) {
|
|
1469
1540
|
try {
|
|
@@ -1487,7 +1558,7 @@ Or specify a custom report:
|
|
|
1487
1558
|
let devServerStarted = false;
|
|
1488
1559
|
if (useDevMode) {
|
|
1489
1560
|
try {
|
|
1490
|
-
const monorepoWebDir =
|
|
1561
|
+
const monorepoWebDir = resolvePath7(dirPath, "packages/visualizer");
|
|
1491
1562
|
let webDir = "";
|
|
1492
1563
|
let visualizerAvailable = false;
|
|
1493
1564
|
if (existsSync2(monorepoWebDir)) {
|
|
@@ -1495,8 +1566,8 @@ Or specify a custom report:
|
|
|
1495
1566
|
visualizerAvailable = true;
|
|
1496
1567
|
} else {
|
|
1497
1568
|
const nodemodulesLocations = [
|
|
1498
|
-
|
|
1499
|
-
|
|
1569
|
+
resolvePath7(dirPath, "node_modules", "@aiready", "visualizer"),
|
|
1570
|
+
resolvePath7(
|
|
1500
1571
|
process.cwd(),
|
|
1501
1572
|
"node_modules",
|
|
1502
1573
|
"@aiready",
|
|
@@ -1506,14 +1577,14 @@ Or specify a custom report:
|
|
|
1506
1577
|
let currentDir = dirPath;
|
|
1507
1578
|
while (currentDir !== "/" && currentDir !== ".") {
|
|
1508
1579
|
nodemodulesLocations.push(
|
|
1509
|
-
|
|
1580
|
+
resolvePath7(currentDir, "node_modules", "@aiready", "visualizer")
|
|
1510
1581
|
);
|
|
1511
|
-
const parent =
|
|
1582
|
+
const parent = resolvePath7(currentDir, "..");
|
|
1512
1583
|
if (parent === currentDir) break;
|
|
1513
1584
|
currentDir = parent;
|
|
1514
1585
|
}
|
|
1515
1586
|
for (const location of nodemodulesLocations) {
|
|
1516
|
-
if (existsSync2(location) && existsSync2(
|
|
1587
|
+
if (existsSync2(location) && existsSync2(resolvePath7(location, "package.json"))) {
|
|
1517
1588
|
webDir = location;
|
|
1518
1589
|
visualizerAvailable = true;
|
|
1519
1590
|
break;
|
|
@@ -1522,20 +1593,20 @@ Or specify a custom report:
|
|
|
1522
1593
|
if (!visualizerAvailable) {
|
|
1523
1594
|
try {
|
|
1524
1595
|
const vizPkgPath = __require.resolve("@aiready/visualizer/package.json");
|
|
1525
|
-
webDir =
|
|
1596
|
+
webDir = resolvePath7(vizPkgPath, "..");
|
|
1526
1597
|
visualizerAvailable = true;
|
|
1527
1598
|
} catch (err) {
|
|
1528
1599
|
void err;
|
|
1529
1600
|
}
|
|
1530
1601
|
}
|
|
1531
1602
|
}
|
|
1532
|
-
const webViteConfigExists = webDir && existsSync2(
|
|
1603
|
+
const webViteConfigExists = webDir && existsSync2(resolvePath7(webDir, "web", "vite.config.ts"));
|
|
1533
1604
|
if (visualizerAvailable && webViteConfigExists) {
|
|
1534
1605
|
const spawnCwd = webDir;
|
|
1535
1606
|
const { watch } = await import("fs");
|
|
1536
1607
|
const copyReportToViz = () => {
|
|
1537
1608
|
try {
|
|
1538
|
-
const destPath =
|
|
1609
|
+
const destPath = resolvePath7(spawnCwd, "web", "report-data.json");
|
|
1539
1610
|
copyFileSync(reportPath, destPath);
|
|
1540
1611
|
console.log(`\u{1F4CB} Report synced to ${destPath}`);
|
|
1541
1612
|
} catch (e) {
|
|
@@ -1579,19 +1650,19 @@ Or specify a custom report:
|
|
|
1579
1650
|
return;
|
|
1580
1651
|
} else {
|
|
1581
1652
|
console.log(
|
|
1582
|
-
|
|
1653
|
+
chalk7.yellow(
|
|
1583
1654
|
"\u26A0\uFE0F Dev server not available (requires local @aiready/visualizer with web assets)."
|
|
1584
1655
|
)
|
|
1585
1656
|
);
|
|
1586
1657
|
console.log(
|
|
1587
|
-
|
|
1658
|
+
chalk7.cyan(" Falling back to static HTML generation...\n")
|
|
1588
1659
|
);
|
|
1589
1660
|
useDevMode = false;
|
|
1590
1661
|
}
|
|
1591
1662
|
} catch (err) {
|
|
1592
1663
|
console.error("Failed to start dev server:", err);
|
|
1593
1664
|
console.log(
|
|
1594
|
-
|
|
1665
|
+
chalk7.cyan(" Falling back to static HTML generation...\n")
|
|
1595
1666
|
);
|
|
1596
1667
|
useDevMode = false;
|
|
1597
1668
|
}
|
|
@@ -1599,9 +1670,9 @@ Or specify a custom report:
|
|
|
1599
1670
|
console.log("Generating HTML...");
|
|
1600
1671
|
const html = generateHTML(graph);
|
|
1601
1672
|
const defaultOutput = "visualization.html";
|
|
1602
|
-
const outPath =
|
|
1673
|
+
const outPath = resolvePath7(dirPath, options.output || defaultOutput);
|
|
1603
1674
|
writeFileSync3(outPath, html, "utf8");
|
|
1604
|
-
console.log(
|
|
1675
|
+
console.log(chalk7.green(`\u2705 Visualization written to: ${outPath}`));
|
|
1605
1676
|
if (options.open || options.serve) {
|
|
1606
1677
|
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
1607
1678
|
if (options.serve) {
|
|
@@ -1631,7 +1702,7 @@ Or specify a custom report:
|
|
|
1631
1702
|
server.listen(port, () => {
|
|
1632
1703
|
const addr = `http://localhost:${port}/`;
|
|
1633
1704
|
console.log(
|
|
1634
|
-
|
|
1705
|
+
chalk7.cyan(`\u{1F310} Local visualization server running at ${addr}`)
|
|
1635
1706
|
);
|
|
1636
1707
|
spawn(opener, [`"${addr}"`], { shell: true });
|
|
1637
1708
|
});
|
|
@@ -1647,7 +1718,7 @@ Or specify a custom report:
|
|
|
1647
1718
|
}
|
|
1648
1719
|
}
|
|
1649
1720
|
} catch (err) {
|
|
1650
|
-
|
|
1721
|
+
handleCLIError6(err, "Visualization");
|
|
1651
1722
|
}
|
|
1652
1723
|
}
|
|
1653
1724
|
var visualizeHelpText = `
|
|
@@ -1678,15 +1749,15 @@ NOTES:
|
|
|
1678
1749
|
`;
|
|
1679
1750
|
|
|
1680
1751
|
// src/commands/ai-signal-clarity.ts
|
|
1681
|
-
import
|
|
1752
|
+
import chalk8 from "chalk";
|
|
1682
1753
|
import { loadConfig, mergeConfigWithDefaults } from "@aiready/core";
|
|
1683
1754
|
|
|
1684
1755
|
// src/commands/agent-grounding.ts
|
|
1685
|
-
import
|
|
1756
|
+
import chalk9 from "chalk";
|
|
1686
1757
|
import { loadConfig as loadConfig2, mergeConfigWithDefaults as mergeConfigWithDefaults2 } from "@aiready/core";
|
|
1687
1758
|
|
|
1688
1759
|
// src/commands/testability.ts
|
|
1689
|
-
import
|
|
1760
|
+
import chalk10 from "chalk";
|
|
1690
1761
|
import { loadConfig as loadConfig3, mergeConfigWithDefaults as mergeConfigWithDefaults3 } from "@aiready/core";
|
|
1691
1762
|
|
|
1692
1763
|
// src/commands/change-amplification.ts
|
|
@@ -1761,7 +1832,7 @@ program.command("scan").description(
|
|
|
1761
1832
|
"--fail-on <level>",
|
|
1762
1833
|
"Fail on issues: critical, major, any",
|
|
1763
1834
|
"critical"
|
|
1764
|
-
).addHelpText("after", scanHelpText).action(async (directory, options) => {
|
|
1835
|
+
).option("--api-key <key>", "Platform API key for automatic upload").option("--upload", "Automatically upload results to the platform").option("--server <url>", "Custom platform URL").addHelpText("after", scanHelpText).action(async (directory, options) => {
|
|
1765
1836
|
await scanAction(directory, options);
|
|
1766
1837
|
});
|
|
1767
1838
|
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(
|
|
@@ -1842,4 +1913,7 @@ program.command("visualize").description("Generate interactive visualization fro
|
|
|
1842
1913
|
program.command("change-amplification").description("Analyze graph metrics for change amplification").argument("[directory]", "Directory to analyze", ".").option("--include <patterns>", "File patterns to include (comma-separated)").option("--exclude <patterns>", "File patterns to exclude (comma-separated)").option("-o, --output <format>", "Output format: console, json", "console").option("--output-file <path>", "Output file path (for json)").action(async (directory, options) => {
|
|
1843
1914
|
await changeAmplificationAction(directory, options);
|
|
1844
1915
|
});
|
|
1916
|
+
program.command("upload").description("Upload an AIReady report JSON to the platform").argument("<file>", "Report JSON file to upload").option("--api-key <key>", "Platform API key").option("--repo-id <id>", "Platform repository ID (optional)").option("--server <url>", "Custom platform URL").addHelpText("after", uploadHelpText).action(async (file, options) => {
|
|
1917
|
+
await uploadAction(file, options);
|
|
1918
|
+
});
|
|
1845
1919
|
program.parse();
|