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