@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/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 chalk2 from "chalk";
14
+ import chalk3 from "chalk";
15
15
  import { writeFileSync, readFileSync as readFileSync2 } from "fs";
16
- import { resolve as resolvePath2 } from "path";
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(chalk2.blue("\u{1F680} Starting AIReady unified analysis...\n"));
230
+ console.log(chalk3.blue("\u{1F680} Starting AIReady unified analysis...\n"));
158
231
  const startTime = Date.now();
159
- const resolvedDir = resolvePath2(process.cwd(), directory || ".");
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
- chalk2.yellow(
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(chalk2.cyan("\n=== AIReady Run Preview ==="));
308
+ console.log(chalk3.cyan("\n=== AIReady Run Preview ==="));
235
309
  console.log(
236
- chalk2.white("Tools to run:"),
310
+ chalk3.white("Tools to run:"),
237
311
  (finalOptions.tools || ["patterns", "context", "consistency"]).join(", ")
238
312
  );
239
- console.log(chalk2.white("Will use settings from config and defaults."));
240
- console.log(chalk2.white("\nGeneral settings:"));
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: ${chalk2.bold(String(finalOptions.rootDir))}`);
316
+ console.log(` rootDir: ${chalk3.bold(String(finalOptions.rootDir))}`);
243
317
  if (finalOptions.include)
244
318
  console.log(
245
- ` include: ${chalk2.bold(truncateArray(finalOptions.include, 6))}`
319
+ ` include: ${chalk3.bold(truncateArray(finalOptions.include, 6))}`
246
320
  );
247
321
  if (finalOptions.exclude)
248
322
  console.log(
249
- ` exclude: ${chalk2.bold(truncateArray(finalOptions.exclude, 6))}`
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(chalk2.white("\nPattern-detect settings:"));
337
+ console.log(chalk3.white("\nPattern-detect settings:"));
264
338
  console.log(
265
- ` minSimilarity: ${chalk2.bold(patternDetectConfig.minSimilarity ?? "default")}`
339
+ ` minSimilarity: ${chalk3.bold(patternDetectConfig.minSimilarity ?? "default")}`
266
340
  );
267
341
  console.log(
268
- ` minLines: ${chalk2.bold(patternDetectConfig.minLines ?? "default")}`
342
+ ` minLines: ${chalk3.bold(patternDetectConfig.minLines ?? "default")}`
269
343
  );
270
344
  if (patternDetectConfig.approx !== void 0)
271
345
  console.log(
272
- ` approx: ${chalk2.bold(String(patternDetectConfig.approx))}`
346
+ ` approx: ${chalk3.bold(String(patternDetectConfig.approx))}`
273
347
  );
274
348
  if (patternDetectConfig.minSharedTokens !== void 0)
275
349
  console.log(
276
- ` minSharedTokens: ${chalk2.bold(String(patternDetectConfig.minSharedTokens))}`
350
+ ` minSharedTokens: ${chalk3.bold(String(patternDetectConfig.minSharedTokens))}`
277
351
  );
278
352
  if (patternDetectConfig.maxCandidatesPerBlock !== void 0)
279
353
  console.log(
280
- ` maxCandidatesPerBlock: ${chalk2.bold(String(patternDetectConfig.maxCandidatesPerBlock))}`
354
+ ` maxCandidatesPerBlock: ${chalk3.bold(String(patternDetectConfig.maxCandidatesPerBlock))}`
281
355
  );
282
356
  if (patternDetectConfig.batchSize !== void 0)
283
357
  console.log(
284
- ` batchSize: ${chalk2.bold(String(patternDetectConfig.batchSize))}`
358
+ ` batchSize: ${chalk3.bold(String(patternDetectConfig.batchSize))}`
285
359
  );
286
360
  if (patternDetectConfig.streamResults !== void 0)
287
361
  console.log(
288
- ` streamResults: ${chalk2.bold(String(patternDetectConfig.streamResults))}`
362
+ ` streamResults: ${chalk3.bold(String(patternDetectConfig.streamResults))}`
289
363
  );
290
364
  if (patternDetectConfig.severity !== void 0)
291
365
  console.log(
292
- ` severity: ${chalk2.bold(String(patternDetectConfig.severity))}`
366
+ ` severity: ${chalk3.bold(String(patternDetectConfig.severity))}`
293
367
  );
294
368
  if (patternDetectConfig.includeTests !== void 0)
295
369
  console.log(
296
- ` includeTests: ${chalk2.bold(String(patternDetectConfig.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(chalk2.white("\nContext-analyzer settings:"));
308
- console.log(` maxDepth: ${chalk2.bold(ca.maxDepth ?? "default")}`);
381
+ console.log(chalk3.white("\nContext-analyzer settings:"));
382
+ console.log(` maxDepth: ${chalk3.bold(ca.maxDepth ?? "default")}`);
309
383
  console.log(
310
- ` maxContextBudget: ${chalk2.bold(ca.maxContextBudget ?? "default")}`
384
+ ` maxContextBudget: ${chalk3.bold(ca.maxContextBudget ?? "default")}`
311
385
  );
312
386
  if (ca.minCohesion !== void 0)
313
- console.log(` minCohesion: ${chalk2.bold(String(ca.minCohesion))}`);
387
+ console.log(` minCohesion: ${chalk3.bold(String(ca.minCohesion))}`);
314
388
  if (ca.maxFragmentation !== void 0)
315
389
  console.log(
316
- ` maxFragmentation: ${chalk2.bold(String(ca.maxFragmentation))}`
390
+ ` maxFragmentation: ${chalk3.bold(String(ca.maxFragmentation))}`
317
391
  );
318
392
  if (ca.includeNodeModules !== void 0)
319
393
  console.log(
320
- ` includeNodeModules: ${chalk2.bold(String(ca.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(chalk2.white("\nConsistency settings:"));
399
+ console.log(chalk3.white("\nConsistency settings:"));
326
400
  console.log(
327
- ` checkNaming: ${chalk2.bold(String(c.checkNaming ?? true))}`
401
+ ` checkNaming: ${chalk3.bold(String(c.checkNaming ?? true))}`
328
402
  );
329
403
  console.log(
330
- ` checkPatterns: ${chalk2.bold(String(c.checkPatterns ?? true))}`
404
+ ` checkPatterns: ${chalk3.bold(String(c.checkPatterns ?? true))}`
331
405
  );
332
406
  console.log(
333
- ` checkArchitecture: ${chalk2.bold(String(c.checkArchitecture ?? false))}`
407
+ ` checkArchitecture: ${chalk3.bold(String(c.checkArchitecture ?? false))}`
334
408
  );
335
409
  if (c.minSeverity)
336
- console.log(` minSeverity: ${chalk2.bold(c.minSeverity)}`);
410
+ console.log(` minSeverity: ${chalk3.bold(c.minSeverity)}`);
337
411
  if (c.acceptedAbbreviations)
338
412
  console.log(
339
- ` acceptedAbbreviations: ${chalk2.bold(truncateArray(c.acceptedAbbreviations, 8))}`
413
+ ` acceptedAbbreviations: ${chalk3.bold(truncateArray(c.acceptedAbbreviations, 8))}`
340
414
  );
341
415
  if (c.shortWords)
342
416
  console.log(
343
- ` shortWords: ${chalk2.bold(truncateArray(c.shortWords, 8))}`
417
+ ` shortWords: ${chalk3.bold(truncateArray(c.shortWords, 8))}`
344
418
  );
345
419
  }
346
- console.log(chalk2.white("\nStarting analysis..."));
420
+ console.log(chalk3.white("\nStarting analysis..."));
347
421
  const progressCallback = (event) => {
348
- console.log(chalk2.cyan(`
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: ${chalk2.bold(String(pr.duplicates?.length || 0))}`
428
+ ` Duplicate patterns: ${chalk3.bold(String(pr.duplicates?.length || 0))}`
355
429
  );
356
430
  console.log(
357
- ` Files with pattern issues: ${chalk2.bold(String(pr.results?.length || 0))}`
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 ${chalk2.bold(String(pr.duplicates?.length || 0))} duplicates into ${chalk2.bold(String(pr.groups.length))} file pairs`
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 ${chalk2.bold(String(pr.clusters.length))} refactor clusters`
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: ${chalk2.bold(String(cr.length || 0))}`
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: ${chalk2.bold(String(rep.summary?.totalIssues || 0))}`
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
- chalk2.dim(
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: ${chalk2.bold(String(dr.issues?.length || 0))}`
525
+ ` Issues found: ${chalk3.bold(String(dr.issues?.length || 0))}`
452
526
  );
453
527
  if (dr.rawData) {
454
528
  console.log(
455
- ` Signature Mismatches: ${chalk2.bold(dr.rawData.outdatedComments || 0)}`
529
+ ` Signature Mismatches: ${chalk3.bold(dr.rawData.outdatedComments || 0)}`
456
530
  );
457
531
  console.log(
458
- ` Undocumented Complexity: ${chalk2.bold(dr.rawData.undocumentedComplexity || 0)}`
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: ${chalk2.bold(String(dr.summary?.packagesAnalyzed || 0))}`
538
+ ` Packages Analyzed: ${chalk3.bold(String(dr.summary?.packagesAnalyzed || 0))}`
465
539
  );
466
540
  if (dr.rawData) {
467
541
  console.log(
468
- ` Deprecated Packages: ${chalk2.bold(dr.rawData.deprecatedPackages || 0)}`
542
+ ` Deprecated Packages: ${chalk3.bold(dr.rawData.deprecatedPackages || 0)}`
469
543
  );
470
544
  console.log(
471
- ` AI Cutoff Skew Score: ${chalk2.bold(dr.rawData.trainingCutoffSkew?.toFixed(1) || 0)}`
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: ${chalk2.bold(String(dr.issues?.length || 0))}`
551
+ ` Coupling issues: ${chalk3.bold(String(dr.issues?.length || 0))}`
478
552
  );
479
553
  if (dr.summary) {
480
554
  console.log(
481
- ` Complexity Score: ${chalk2.bold(dr.summary.score || 0)}/100`
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(chalk2.cyan("\n=== AIReady Run Summary ==="));
576
+ console.log(chalk3.cyan("\n=== AIReady Run Summary ==="));
503
577
  console.log(
504
- chalk2.white("Tools run:"),
578
+ chalk3.white("Tools run:"),
505
579
  (finalOptions.tools || ["patterns", "context", "consistency"]).join(", ")
506
580
  );
507
- console.log(chalk2.cyan("\nResults summary:"));
581
+ console.log(chalk3.cyan("\nResults summary:"));
508
582
  console.log(
509
- ` Total issues (all tools): ${chalk2.bold(String(results.summary.totalIssues || 0))}`
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: ${chalk2.bold(String(results.duplicates.length || 0))}`
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: ${chalk2.bold(String(results.patterns.length || 0))}`
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: ${chalk2.bold(String(results.context.length || 0))}`
595
+ ` Context issues: ${chalk3.bold(String(results.context.length || 0))}`
522
596
  );
523
597
  console.log(
524
- ` Consistency issues: ${chalk2.bold(String(results.consistency?.summary?.totalIssues || 0))}`
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: ${chalk2.bold(String(results.changeAmplification.summary?.score || 0))}/100`
602
+ ` Change amplification: ${chalk3.bold(String(results.changeAmplification.summary?.score || 0))}/100`
529
603
  );
530
- console.log(chalk2.cyan("===========================\n"));
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(chalk2.bold("\n\u{1F4CA} AI Readiness Overall Score"));
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
- resolvePath2(process.cwd(), options.compareTo),
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
- chalk2.green(
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
- chalk2.red(
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
- chalk2.blue(
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
- chalk2.yellow(
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
- chalk2.yellow(
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(chalk2.bold("\nTool breakdown:"));
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(chalk2.bold("Detailed tool breakdown:"));
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 = { ...results, scoring: scoringResult };
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 = { ...results, scoring: scoringResult };
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(chalk2.dim(`\u2705 Report auto-persisted to ${outputPath}`));
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(chalk2.red("\n\u{1F6AB} PR BLOCKED: AI Readiness Check Failed"));
837
- console.log(chalk2.red(` Reason: ${failReason}`));
838
- console.log(chalk2.dim("\n Remediation steps:"));
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
- chalk2.dim(" 1. Run `aiready scan` locally to see detailed issues")
988
+ chalk3.dim(" 1. Run `aiready scan` locally to see detailed issues")
841
989
  );
842
- console.log(chalk2.dim(" 2. Fix the critical issues before merging"));
990
+ console.log(chalk3.dim(" 2. Fix the critical issues before merging"));
843
991
  console.log(
844
- chalk2.dim(
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(chalk2.green("\n\u2705 PR PASSED: AI Readiness Check"));
998
+ console.log(chalk3.green("\n\u2705 PR PASSED: AI Readiness Check"));
851
999
  if (threshold) {
852
1000
  console.log(
853
- chalk2.green(
1001
+ chalk3.green(
854
1002
  ` Score: ${scoringResult.overall}/100 (threshold: ${threshold})`
855
1003
  )
856
1004
  );
857
1005
  }
858
1006
  console.log(
859
- chalk2.dim(
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
- handleCLIError(error, "Analysis");
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 chalk3 from "chalk";
900
- import { resolve as resolvePath3 } from "path";
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 handleCLIError2,
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(chalk3.blue("\u{1F50D} Analyzing patterns...\n"));
1060
+ console.log(chalk4.blue("\u{1F50D} Analyzing patterns...\n"));
911
1061
  const startTime = Date.now();
912
- const resolvedDir = resolvePath3(process.cwd(), directory || ".");
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(chalk3.cyan(divider));
977
- console.log(chalk3.bold.white(" PATTERN ANALYSIS SUMMARY"));
978
- console.log(chalk3.cyan(divider) + "\n");
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
- chalk3.white(`\u{1F4C1} Files analyzed: ${chalk3.bold(results.length)}`)
1130
+ chalk4.white(`\u{1F4C1} Files analyzed: ${chalk4.bold(results.length)}`)
981
1131
  );
982
1132
  console.log(
983
- chalk3.yellow(
984
- `\u26A0 Duplicate patterns found: ${chalk3.bold(summary.totalPatterns)}`
1133
+ chalk4.yellow(
1134
+ `\u26A0 Duplicate patterns found: ${chalk4.bold(summary.totalPatterns)}`
985
1135
  )
986
1136
  );
987
1137
  console.log(
988
- chalk3.red(
989
- `\u{1F4B0} Token cost (wasted): ${chalk3.bold(summary.totalTokenCost.toLocaleString())}`
1138
+ chalk4.red(
1139
+ `\u{1F4B0} Token cost (wasted): ${chalk4.bold(summary.totalTokenCost.toLocaleString())}`
990
1140
  )
991
1141
  );
992
1142
  console.log(
993
- chalk3.gray(`\u23F1 Analysis time: ${chalk3.bold(elapsedTime + "s")}`)
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(chalk3.cyan("\n" + divider));
998
- console.log(chalk3.bold.white(" PATTERNS BY TYPE"));
999
- console.log(chalk3.cyan(divider) + "\n");
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(` ${chalk3.white(type.padEnd(15))} ${chalk3.bold(count)}`);
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(chalk3.cyan("\n" + divider));
1006
- console.log(chalk3.bold.white(" TOP DUPLICATE PATTERNS"));
1007
- console.log(chalk3.cyan(divider) + "\n");
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}: ${chalk3.bold(file1Name)} \u2194 ${chalk3.bold(file2Name)}`
1165
+ `${severityIcon} ${severity}: ${chalk4.bold(file1Name)} \u2194 ${chalk4.bold(file2Name)}`
1016
1166
  );
1017
1167
  console.log(
1018
- ` Similarity: ${chalk3.bold(Math.round(dup.similarity * 100) + "%")} | Wasted: ${chalk3.bold(dup.tokenCost.toLocaleString())} tokens each`
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: ${chalk3.cyan(dup.line1 + "-" + dup.endLine1)} \u2194 ${chalk3.cyan(dup.line2 + "-" + dup.endLine2)}
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
- chalk3.green("\n\u2728 Great! No duplicate patterns detected.\n")
1177
+ chalk4.green("\n\u2728 Great! No duplicate patterns detected.\n")
1028
1178
  );
1029
1179
  }
1030
1180
  if (patternScore) {
1031
- console.log(chalk3.cyan(divider));
1032
- console.log(chalk3.bold.white(" AI READINESS SCORE (Patterns)"));
1033
- console.log(chalk3.cyan(divider) + "\n");
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
- handleCLIError2(error, "Pattern analysis");
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 chalk4 from "chalk";
1051
- import { resolve as resolvePath4 } from "path";
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 handleCLIError3,
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(chalk4.blue("\u{1F9E0} Analyzing context costs...\n"));
1211
+ console.log(chalk5.blue("\u{1F9E0} Analyzing context costs...\n"));
1062
1212
  const startTime = Date.now();
1063
- const resolvedDir = resolvePath4(process.cwd(), directory || ".");
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(chalk4.cyan(divider));
1130
- console.log(chalk4.bold.white(" CONTEXT ANALYSIS SUMMARY"));
1131
- console.log(chalk4.cyan(divider) + "\n");
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
- chalk4.white(`\u{1F4C1} Files analyzed: ${chalk4.bold(summary.totalFiles)}`)
1283
+ chalk5.white(`\u{1F4C1} Files analyzed: ${chalk5.bold(summary.totalFiles)}`)
1134
1284
  );
1135
1285
  console.log(
1136
- chalk4.white(
1137
- `\u{1F4CA} Total tokens: ${chalk4.bold(summary.totalTokens.toLocaleString())}`
1286
+ chalk5.white(
1287
+ `\u{1F4CA} Total tokens: ${chalk5.bold(summary.totalTokens.toLocaleString())}`
1138
1288
  )
1139
1289
  );
1140
1290
  console.log(
1141
- chalk4.yellow(
1142
- `\u{1F4B0} Avg context budget: ${chalk4.bold(summary.avgContextBudget.toFixed(0))} tokens/file`
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
- chalk4.white(`\u23F1 Analysis time: ${chalk4.bold(elapsedTime + "s")}
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(chalk4.bold("\u26A0\uFE0F Issues Found:\n"));
1301
+ console.log(chalk5.bold("\u26A0\uFE0F Issues Found:\n"));
1152
1302
  if (summary.criticalIssues > 0) {
1153
1303
  console.log(
1154
- chalk4.red(` \u{1F534} Critical: ${chalk4.bold(summary.criticalIssues)}`)
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
- chalk4.yellow(` \u{1F7E1} Major: ${chalk4.bold(summary.majorIssues)}`)
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
- chalk4.blue(` \u{1F535} Minor: ${chalk4.bold(summary.minorIssues)}`)
1314
+ chalk5.blue(` \u{1F535} Minor: ${chalk5.bold(summary.minorIssues)}`)
1165
1315
  );
1166
1316
  }
1167
1317
  console.log(
1168
- chalk4.green(
1318
+ chalk5.green(
1169
1319
  `
1170
- \u{1F4A1} Potential savings: ${chalk4.bold(summary.totalPotentialSavings.toLocaleString())} tokens
1320
+ \u{1F4A1} Potential savings: ${chalk5.bold(summary.totalPotentialSavings.toLocaleString())} tokens
1171
1321
  `
1172
1322
  )
1173
1323
  );
1174
1324
  } else {
1175
- console.log(chalk4.green("\u2705 No significant issues found!\n"));
1325
+ console.log(chalk5.green("\u2705 No significant issues found!\n"));
1176
1326
  }
1177
1327
  if (summary.deepFiles.length > 0) {
1178
- console.log(chalk4.bold("\u{1F4CF} Deep Import Chains:\n"));
1328
+ console.log(chalk5.bold("\u{1F4CF} Deep Import Chains:\n"));
1179
1329
  console.log(
1180
- chalk4.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`)
1330
+ chalk5.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`)
1181
1331
  );
1182
1332
  console.log(
1183
- chalk4.gray(` Maximum depth: ${summary.maxImportDepth}
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
- ` ${chalk4.cyan("\u2192")} ${chalk4.white(fileName)} ${chalk4.dim(`(depth: ${item.depth})`)}`
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(chalk4.bold("\u{1F9E9} Fragmented Modules:\n"));
1345
+ console.log(chalk5.bold("\u{1F9E9} Fragmented Modules:\n"));
1196
1346
  console.log(
1197
- chalk4.gray(
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
- ` ${chalk4.yellow("\u25CF")} ${chalk4.white(module.domain)} - ${chalk4.dim(`${module.files.length} files, ${(module.fragmentationScore * 100).toFixed(0)}% scattered`)}`
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
- chalk4.dim(
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(chalk4.bold("\u{1F500} Low Cohesion Files:\n"));
1365
+ console.log(chalk5.bold("\u{1F500} Low Cohesion Files:\n"));
1216
1366
  console.log(
1217
- chalk4.gray(
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 ? chalk4.red : chalk4.yellow;
1375
+ const color = item.score < 0.4 ? chalk5.red : chalk5.yellow;
1226
1376
  console.log(
1227
- ` ${color("\u25CB")} ${chalk4.white(fileName)} ${chalk4.dim(`(${scorePercent}% cohesion)`)}`
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(chalk4.bold("\u{1F4B8} Most Expensive Files (Context Budget):\n"));
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" ? chalk4.red : item.severity === "major" ? chalk4.yellow : chalk4.blue;
1386
+ const severityColor = item.severity === "critical" ? chalk5.red : item.severity === "major" ? chalk5.yellow : chalk5.blue;
1237
1387
  console.log(
1238
- ` ${severityColor("\u25CF")} ${chalk4.white(fileName)} ${chalk4.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`
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(chalk4.cyan(divider));
1245
- console.log(chalk4.bold.white(" AI READINESS SCORE (Context)"));
1246
- console.log(chalk4.cyan(divider) + "\n");
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
- handleCLIError3(error, "Context analysis");
1402
+ handleCLIError4(error, "Context analysis");
1253
1403
  }
1254
1404
  }
1255
1405
 
1256
1406
  // src/commands/consistency.ts
1257
- import chalk5 from "chalk";
1407
+ import chalk6 from "chalk";
1258
1408
  import { writeFileSync as writeFileSync2 } from "fs";
1259
- import { resolve as resolvePath5 } from "path";
1409
+ import { resolve as resolvePath6 } from "path";
1260
1410
  import {
1261
1411
  loadMergedConfig as loadMergedConfig4,
1262
1412
  handleJSONOutput as handleJSONOutput4,
1263
- handleCLIError as handleCLIError4,
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(chalk5.blue("\u{1F50D} Analyzing consistency...\n"));
1419
+ console.log(chalk6.blue("\u{1F50D} Analyzing consistency...\n"));
1270
1420
  const startTime = Date.now();
1271
- const resolvedDir = resolvePath5(process.cwd(), directory || ".");
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(chalk5.green(`\u2705 Report saved to ${outputPath}`));
1481
+ console.log(chalk6.green(`\u2705 Report saved to ${outputPath}`));
1332
1482
  } else {
1333
- console.log(chalk5.bold("\n\u{1F4CA} Summary\n"));
1483
+ console.log(chalk6.bold("\n\u{1F4CA} Summary\n"));
1334
1484
  console.log(
1335
- `Files Analyzed: ${chalk5.cyan(report.summary.filesAnalyzed)}`
1485
+ `Files Analyzed: ${chalk6.cyan(report.summary.filesAnalyzed)}`
1336
1486
  );
1337
- console.log(`Total Issues: ${chalk5.yellow(report.summary.totalIssues)}`);
1338
- console.log(` Naming: ${chalk5.yellow(report.summary.namingIssues)}`);
1339
- console.log(` Patterns: ${chalk5.yellow(report.summary.patternIssues)}`);
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: ${chalk5.yellow(report.summary.architectureIssues || 0)}`
1491
+ ` Architecture: ${chalk6.yellow(report.summary.architectureIssues || 0)}`
1342
1492
  );
1343
- console.log(`Analysis Time: ${chalk5.gray(elapsedTime + "s")}
1493
+ console.log(`Analysis Time: ${chalk6.gray(elapsedTime + "s")}
1344
1494
  `);
1345
1495
  if (report.summary.totalIssues === 0) {
1346
1496
  console.log(
1347
- chalk5.green(
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(chalk5.bold("\u{1F3F7}\uFE0F Naming Issues\n"));
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" ? chalk5.red : issue.severity === "major" ? chalk5.yellow : issue.severity === "minor" ? chalk5.blue : chalk5.gray;
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())} ${chalk5.dim(`${issue.location.file}:${issue.location.line}`)}`
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
- ` ${chalk5.dim("\u2192")} ${chalk5.italic(issue.suggestion)}`
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(chalk5.dim(` ... and ${remaining} more issues
1531
+ console.log(chalk6.dim(` ... and ${remaining} more issues
1382
1532
  `));
1383
1533
  }
1384
1534
  }
1385
1535
  if (patternResults.length > 0) {
1386
- console.log(chalk5.bold("\u{1F504} Pattern Issues\n"));
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" ? chalk5.red : issue.severity === "major" ? chalk5.yellow : issue.severity === "minor" ? chalk5.blue : chalk5.gray;
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())} ${chalk5.dim(`${issue.location.file}:${issue.location.line}`)}`
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
- ` ${chalk5.dim("\u2192")} ${chalk5.italic(issue.suggestion)}`
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(chalk5.dim(` ... and ${remaining} more issues
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(chalk5.bold("\u{1F4A1} Recommendations\n"));
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(chalk5.bold("\n\u{1F4CA} AI Readiness Score (Consistency)\n"));
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
- handleCLIError4(error, "Consistency analysis");
1577
+ handleCLIError5(error, "Consistency analysis");
1428
1578
  }
1429
1579
  }
1430
1580
 
1431
1581
  // src/commands/visualize.ts
1432
- import chalk6 from "chalk";
1582
+ import chalk7 from "chalk";
1433
1583
  import { writeFileSync as writeFileSync3, readFileSync as readFileSync3, existsSync as existsSync2, copyFileSync } from "fs";
1434
- import { resolve as resolvePath6 } from "path";
1584
+ import { resolve as resolvePath7 } from "path";
1435
1585
  import { spawn } from "child_process";
1436
- import { handleCLIError as handleCLIError5 } from "@aiready/core";
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 = resolvePath6(process.cwd(), directory || ".");
1441
- let reportPath = options.report ? resolvePath6(dirPath, options.report) : null;
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
- chalk6.dim(`Found latest report: ${latestScan.split("/").pop()}`)
1597
+ chalk7.dim(`Found latest report: ${latestScan.split("/").pop()}`)
1448
1598
  );
1449
1599
  } else {
1450
- console.error(chalk6.red("\u274C No AI readiness report found"));
1600
+ console.error(chalk7.red("\u274C No AI readiness report found"));
1451
1601
  console.log(
1452
- chalk6.dim(
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 = resolvePath6(dirPath, "aiready.json");
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 = resolvePath6(dirPath, "packages/visualizer");
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
- resolvePath6(dirPath, "node_modules", "@aiready", "visualizer"),
1499
- resolvePath6(
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
- resolvePath6(currentDir, "node_modules", "@aiready", "visualizer")
1659
+ resolvePath7(currentDir, "node_modules", "@aiready", "visualizer")
1510
1660
  );
1511
- const parent = resolvePath6(currentDir, "..");
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(resolvePath6(location, "package.json"))) {
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 = resolvePath6(vizPkgPath, "..");
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(resolvePath6(webDir, "web", "vite.config.ts"));
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 = resolvePath6(spawnCwd, "web", "report-data.json");
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
- chalk6.yellow(
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
- chalk6.cyan(" Falling back to static HTML generation...\n")
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
- chalk6.cyan(" Falling back to static HTML generation...\n")
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 = resolvePath6(dirPath, options.output || defaultOutput);
1752
+ const outPath = resolvePath7(dirPath, options.output || defaultOutput);
1603
1753
  writeFileSync3(outPath, html, "utf8");
1604
- console.log(chalk6.green(`\u2705 Visualization written to: ${outPath}`));
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
- chalk6.cyan(`\u{1F310} Local visualization server running at ${addr}`)
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
- handleCLIError5(err, "Visualization");
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 chalk7 from "chalk";
1831
+ import chalk8 from "chalk";
1682
1832
  import { loadConfig, mergeConfigWithDefaults } from "@aiready/core";
1683
1833
 
1684
1834
  // src/commands/agent-grounding.ts
1685
- import chalk8 from "chalk";
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 chalk9 from "chalk";
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();