@aiready/cli 0.9.41 → 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
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  __require,
4
4
  analyzeUnified
5
- } from "./chunk-HLBKROD3.mjs";
5
+ } from "./chunk-LLJMKNBI.mjs";
6
6
 
7
7
  // src/cli.ts
8
8
  import { Command } from "commander";
@@ -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
  }
@@ -489,37 +563,45 @@ async function scanAction(directory, options) {
489
563
  const results = await analyzeUnified({
490
564
  ...finalOptions,
491
565
  progressCallback,
566
+ onProgress: (processed, total, message) => {
567
+ process.stdout.write(
568
+ `\r\x1B[K [${processed}/${total}] ${message}...`
569
+ );
570
+ if (processed === total) {
571
+ process.stdout.write("\n");
572
+ }
573
+ },
492
574
  suppressToolConfig: true
493
575
  });
494
- console.log(chalk2.cyan("\n=== AIReady Run Summary ==="));
576
+ console.log(chalk3.cyan("\n=== AIReady Run Summary ==="));
495
577
  console.log(
496
- chalk2.white("Tools run:"),
578
+ chalk3.white("Tools run:"),
497
579
  (finalOptions.tools || ["patterns", "context", "consistency"]).join(", ")
498
580
  );
499
- console.log(chalk2.cyan("\nResults summary:"));
581
+ console.log(chalk3.cyan("\nResults summary:"));
500
582
  console.log(
501
- ` Total issues (all tools): ${chalk2.bold(String(results.summary.totalIssues || 0))}`
583
+ ` Total issues (all tools): ${chalk3.bold(String(results.summary.totalIssues || 0))}`
502
584
  );
503
585
  if (results.duplicates)
504
586
  console.log(
505
- ` Duplicate patterns found: ${chalk2.bold(String(results.duplicates.length || 0))}`
587
+ ` Duplicate patterns found: ${chalk3.bold(String(results.duplicates.length || 0))}`
506
588
  );
507
589
  if (results.patterns)
508
590
  console.log(
509
- ` Pattern files with issues: ${chalk2.bold(String(results.patterns.length || 0))}`
591
+ ` Pattern files with issues: ${chalk3.bold(String(results.patterns.length || 0))}`
510
592
  );
511
593
  if (results.context)
512
594
  console.log(
513
- ` Context issues: ${chalk2.bold(String(results.context.length || 0))}`
595
+ ` Context issues: ${chalk3.bold(String(results.context.length || 0))}`
514
596
  );
515
597
  console.log(
516
- ` Consistency issues: ${chalk2.bold(String(results.consistency?.summary?.totalIssues || 0))}`
598
+ ` Consistency issues: ${chalk3.bold(String(results.consistency?.summary?.totalIssues || 0))}`
517
599
  );
518
600
  if (results.changeAmplification)
519
601
  console.log(
520
- ` Change amplification: ${chalk2.bold(String(results.changeAmplification.summary?.score || 0))}/100`
602
+ ` Change amplification: ${chalk3.bold(String(results.changeAmplification.summary?.score || 0))}/100`
521
603
  );
522
- console.log(chalk2.cyan("===========================\n"));
604
+ console.log(chalk3.cyan("===========================\n"));
523
605
  const elapsedTime = getElapsedTime(startTime);
524
606
  void elapsedTime;
525
607
  let scoringResult;
@@ -532,6 +614,16 @@ async function scanAction(directory, options) {
532
614
  results.duplicates,
533
615
  results.patterns?.length || 0
534
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
+ });
535
627
  toolScores.set("pattern-detect", patternScore);
536
628
  } catch (err) {
537
629
  void err;
@@ -542,6 +634,14 @@ async function scanAction(directory, options) {
542
634
  try {
543
635
  const ctxSummary = genContextSummary(results.context);
544
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
+ });
545
645
  toolScores.set("context-analyzer", contextScore);
546
646
  } catch (err) {
547
647
  void err;
@@ -640,12 +740,12 @@ async function scanAction(directory, options) {
640
740
  finalOptions,
641
741
  cliWeights.size ? cliWeights : void 0
642
742
  );
643
- console.log(chalk2.bold("\n\u{1F4CA} AI Readiness Overall Score"));
743
+ console.log(chalk3.bold("\n\u{1F4CA} AI Readiness Overall Score"));
644
744
  console.log(` ${formatScore(scoringResult)}`);
645
745
  if (options.compareTo) {
646
746
  try {
647
747
  const prevReportStr = readFileSync2(
648
- resolvePath2(process.cwd(), options.compareTo),
748
+ resolvePath3(process.cwd(), options.compareTo),
649
749
  "utf8"
650
750
  );
651
751
  const prevReport = JSON.parse(prevReportStr);
@@ -656,19 +756,19 @@ async function scanAction(directory, options) {
656
756
  console.log();
657
757
  if (diff > 0) {
658
758
  console.log(
659
- chalk2.green(
759
+ chalk3.green(
660
760
  ` \u{1F4C8} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
661
761
  )
662
762
  );
663
763
  } else if (diff < 0) {
664
764
  console.log(
665
- chalk2.red(
765
+ chalk3.red(
666
766
  ` \u{1F4C9} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
667
767
  )
668
768
  );
669
769
  } else {
670
770
  console.log(
671
- chalk2.blue(
771
+ chalk3.blue(
672
772
  ` \u2796 Trend: No change compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
673
773
  )
674
774
  );
@@ -679,7 +779,7 @@ async function scanAction(directory, options) {
679
779
  };
680
780
  } else {
681
781
  console.log(
682
- chalk2.yellow(
782
+ chalk3.yellow(
683
783
  `
684
784
  \u26A0\uFE0F Previous report at ${options.compareTo} does not contain an overall score.`
685
785
  )
@@ -688,15 +788,49 @@ async function scanAction(directory, options) {
688
788
  } catch (e) {
689
789
  void e;
690
790
  console.log(
691
- chalk2.yellow(
791
+ chalk3.yellow(
692
792
  `
693
793
  \u26A0\uFE0F Could not read or parse previous report at ${options.compareTo}.`
694
794
  )
695
795
  );
696
796
  }
697
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
+ }
698
832
  if (scoringResult.breakdown && scoringResult.breakdown.length > 0) {
699
- console.log(chalk2.bold("\nTool breakdown:"));
833
+ console.log(chalk3.bold("\nTool breakdown:"));
700
834
  scoringResult.breakdown.forEach((tool) => {
701
835
  const rating = getRating(tool.score);
702
836
  const rd = getRatingDisplay(rating);
@@ -706,7 +840,7 @@ async function scanAction(directory, options) {
706
840
  });
707
841
  console.log();
708
842
  if (finalOptions.scoring?.showBreakdown) {
709
- console.log(chalk2.bold("Detailed tool breakdown:"));
843
+ console.log(chalk3.bold("Detailed tool breakdown:"));
710
844
  scoringResult.breakdown.forEach((tool) => {
711
845
  console.log(formatToolScore(tool));
712
846
  });
@@ -725,12 +859,23 @@ async function scanAction(directory, options) {
725
859
  defaultFilename,
726
860
  resolvedDir
727
861
  );
728
- const outputData = { ...results, scoring: scoringResult };
862
+ const outputData = {
863
+ ...results,
864
+ scoring: scoringResult,
865
+ repository: repoMetadata
866
+ };
729
867
  handleJSONOutput(
730
868
  outputData,
731
869
  outputPath,
732
870
  `\u2705 Report saved to ${outputPath}`
733
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
+ }
734
879
  await warnIfGraphCapExceeded(outputData, resolvedDir);
735
880
  } else {
736
881
  const timestamp = getReportTimestamp();
@@ -740,10 +885,21 @@ async function scanAction(directory, options) {
740
885
  defaultFilename,
741
886
  resolvedDir
742
887
  );
743
- const outputData = { ...results, scoring: scoringResult };
888
+ const outputData = {
889
+ ...results,
890
+ scoring: scoringResult,
891
+ repository: repoMetadata
892
+ };
744
893
  try {
745
894
  writeFileSync(outputPath, JSON.stringify(outputData, null, 2));
746
- 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
+ }
747
903
  await warnIfGraphCapExceeded(outputData, resolvedDir);
748
904
  } catch (err) {
749
905
  void err;
@@ -825,37 +981,37 @@ async function scanAction(directory, options) {
825
981
  }
826
982
  }
827
983
  if (shouldFail) {
828
- console.log(chalk2.red("\n\u{1F6AB} PR BLOCKED: AI Readiness Check Failed"));
829
- console.log(chalk2.red(` Reason: ${failReason}`));
830
- 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:"));
831
987
  console.log(
832
- chalk2.dim(" 1. Run `aiready scan` locally to see detailed issues")
988
+ chalk3.dim(" 1. Run `aiready scan` locally to see detailed issues")
833
989
  );
834
- console.log(chalk2.dim(" 2. Fix the critical issues before merging"));
990
+ console.log(chalk3.dim(" 2. Fix the critical issues before merging"));
835
991
  console.log(
836
- chalk2.dim(
992
+ chalk3.dim(
837
993
  " 3. Consider upgrading to Team plan for historical tracking: https://getaiready.dev/pricing"
838
994
  )
839
995
  );
840
996
  process.exit(1);
841
997
  } else {
842
- console.log(chalk2.green("\n\u2705 PR PASSED: AI Readiness Check"));
998
+ console.log(chalk3.green("\n\u2705 PR PASSED: AI Readiness Check"));
843
999
  if (threshold) {
844
1000
  console.log(
845
- chalk2.green(
1001
+ chalk3.green(
846
1002
  ` Score: ${scoringResult.overall}/100 (threshold: ${threshold})`
847
1003
  )
848
1004
  );
849
1005
  }
850
1006
  console.log(
851
- chalk2.dim(
1007
+ chalk3.dim(
852
1008
  "\n \u{1F4A1} Track historical trends: https://getaiready.dev \u2014 Team plan $99/mo"
853
1009
  )
854
1010
  );
855
1011
  }
856
1012
  }
857
1013
  } catch (error) {
858
- handleCLIError(error, "Analysis");
1014
+ handleCLIError2(error, "Analysis");
859
1015
  }
860
1016
  }
861
1017
  var scanHelpText = `
@@ -869,6 +1025,8 @@ EXAMPLES:
869
1025
  $ aiready scan --ci --threshold 70 # GitHub Actions gatekeeper
870
1026
  $ aiready scan --ci --fail-on major # Fail on major+ issues
871
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
872
1030
 
873
1031
  PROFILES:
874
1032
  agentic: aiSignalClarity, grounding, testability
@@ -888,20 +1046,20 @@ CI/CD INTEGRATION (Gatekeeper Mode):
888
1046
  `;
889
1047
 
890
1048
  // src/commands/patterns.ts
891
- import chalk3 from "chalk";
892
- import { resolve as resolvePath3 } from "path";
1049
+ import chalk4 from "chalk";
1050
+ import { resolve as resolvePath4 } from "path";
893
1051
  import {
894
1052
  loadMergedConfig as loadMergedConfig2,
895
1053
  handleJSONOutput as handleJSONOutput2,
896
- handleCLIError as handleCLIError2,
1054
+ handleCLIError as handleCLIError3,
897
1055
  getElapsedTime as getElapsedTime2,
898
1056
  resolveOutputPath as resolveOutputPath2,
899
1057
  formatToolScore as formatToolScore2
900
1058
  } from "@aiready/core";
901
1059
  async function patternsAction(directory, options) {
902
- console.log(chalk3.blue("\u{1F50D} Analyzing patterns...\n"));
1060
+ console.log(chalk4.blue("\u{1F50D} Analyzing patterns...\n"));
903
1061
  const startTime = Date.now();
904
- const resolvedDir = resolvePath3(process.cwd(), directory || ".");
1062
+ const resolvedDir = resolvePath4(process.cwd(), directory || ".");
905
1063
  try {
906
1064
  const useSmartDefaults = !options.fullScan;
907
1065
  const defaults = {
@@ -965,38 +1123,38 @@ async function patternsAction(directory, options) {
965
1123
  const terminalWidth = process.stdout.columns || 80;
966
1124
  const dividerWidth = Math.min(60, terminalWidth - 2);
967
1125
  const divider = "\u2501".repeat(dividerWidth);
968
- console.log(chalk3.cyan(divider));
969
- console.log(chalk3.bold.white(" PATTERN ANALYSIS SUMMARY"));
970
- 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");
971
1129
  console.log(
972
- chalk3.white(`\u{1F4C1} Files analyzed: ${chalk3.bold(results.length)}`)
1130
+ chalk4.white(`\u{1F4C1} Files analyzed: ${chalk4.bold(results.length)}`)
973
1131
  );
974
1132
  console.log(
975
- chalk3.yellow(
976
- `\u26A0 Duplicate patterns found: ${chalk3.bold(summary.totalPatterns)}`
1133
+ chalk4.yellow(
1134
+ `\u26A0 Duplicate patterns found: ${chalk4.bold(summary.totalPatterns)}`
977
1135
  )
978
1136
  );
979
1137
  console.log(
980
- chalk3.red(
981
- `\u{1F4B0} Token cost (wasted): ${chalk3.bold(summary.totalTokenCost.toLocaleString())}`
1138
+ chalk4.red(
1139
+ `\u{1F4B0} Token cost (wasted): ${chalk4.bold(summary.totalTokenCost.toLocaleString())}`
982
1140
  )
983
1141
  );
984
1142
  console.log(
985
- chalk3.gray(`\u23F1 Analysis time: ${chalk3.bold(elapsedTime + "s")}`)
1143
+ chalk4.gray(`\u23F1 Analysis time: ${chalk4.bold(elapsedTime + "s")}`)
986
1144
  );
987
1145
  const sortedTypes = Object.entries(summary.patternsByType || {}).filter(([, count]) => count > 0).sort(([, a], [, b]) => b - a);
988
1146
  if (sortedTypes.length > 0) {
989
- console.log(chalk3.cyan("\n" + divider));
990
- console.log(chalk3.bold.white(" PATTERNS BY TYPE"));
991
- 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");
992
1150
  sortedTypes.forEach(([type, count]) => {
993
- console.log(` ${chalk3.white(type.padEnd(15))} ${chalk3.bold(count)}`);
1151
+ console.log(` ${chalk4.white(type.padEnd(15))} ${chalk4.bold(count)}`);
994
1152
  });
995
1153
  }
996
1154
  if (summary.totalPatterns > 0 && duplicates.length > 0) {
997
- console.log(chalk3.cyan("\n" + divider));
998
- console.log(chalk3.bold.white(" TOP DUPLICATE PATTERNS"));
999
- 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");
1000
1158
  const topDuplicates = [...duplicates].sort((a, b) => b.similarity - a.similarity).slice(0, 10);
1001
1159
  topDuplicates.forEach((dup) => {
1002
1160
  const severity = dup.similarity > 0.95 ? "CRITICAL" : dup.similarity > 0.9 ? "HIGH" : "MEDIUM";
@@ -1004,31 +1162,31 @@ async function patternsAction(directory, options) {
1004
1162
  const file1Name = dup.file1.split("/").pop() || dup.file1;
1005
1163
  const file2Name = dup.file2.split("/").pop() || dup.file2;
1006
1164
  console.log(
1007
- `${severityIcon} ${severity}: ${chalk3.bold(file1Name)} \u2194 ${chalk3.bold(file2Name)}`
1165
+ `${severityIcon} ${severity}: ${chalk4.bold(file1Name)} \u2194 ${chalk4.bold(file2Name)}`
1008
1166
  );
1009
1167
  console.log(
1010
- ` 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`
1011
1169
  );
1012
1170
  console.log(
1013
- ` 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)}
1014
1172
  `
1015
1173
  );
1016
1174
  });
1017
1175
  } else {
1018
1176
  console.log(
1019
- chalk3.green("\n\u2728 Great! No duplicate patterns detected.\n")
1177
+ chalk4.green("\n\u2728 Great! No duplicate patterns detected.\n")
1020
1178
  );
1021
1179
  }
1022
1180
  if (patternScore) {
1023
- console.log(chalk3.cyan(divider));
1024
- console.log(chalk3.bold.white(" AI READINESS SCORE (Patterns)"));
1025
- 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");
1026
1184
  console.log(formatToolScore2(patternScore));
1027
1185
  console.log();
1028
1186
  }
1029
1187
  }
1030
1188
  } catch (error) {
1031
- handleCLIError2(error, "Pattern analysis");
1189
+ handleCLIError3(error, "Pattern analysis");
1032
1190
  }
1033
1191
  }
1034
1192
  var patternsHelpText = `
@@ -1039,20 +1197,20 @@ EXAMPLES:
1039
1197
  `;
1040
1198
 
1041
1199
  // src/commands/context.ts
1042
- import chalk4 from "chalk";
1043
- import { resolve as resolvePath4 } from "path";
1200
+ import chalk5 from "chalk";
1201
+ import { resolve as resolvePath5 } from "path";
1044
1202
  import {
1045
1203
  loadMergedConfig as loadMergedConfig3,
1046
1204
  handleJSONOutput as handleJSONOutput3,
1047
- handleCLIError as handleCLIError3,
1205
+ handleCLIError as handleCLIError4,
1048
1206
  getElapsedTime as getElapsedTime3,
1049
1207
  resolveOutputPath as resolveOutputPath3,
1050
1208
  formatToolScore as formatToolScore3
1051
1209
  } from "@aiready/core";
1052
1210
  async function contextAction(directory, options) {
1053
- console.log(chalk4.blue("\u{1F9E0} Analyzing context costs...\n"));
1211
+ console.log(chalk5.blue("\u{1F9E0} Analyzing context costs...\n"));
1054
1212
  const startTime = Date.now();
1055
- const resolvedDir = resolvePath4(process.cwd(), directory || ".");
1213
+ const resolvedDir = resolvePath5(process.cwd(), directory || ".");
1056
1214
  try {
1057
1215
  const defaults = {
1058
1216
  maxDepth: 5,
@@ -1118,85 +1276,85 @@ async function contextAction(directory, options) {
1118
1276
  const terminalWidth = process.stdout.columns || 80;
1119
1277
  const dividerWidth = Math.min(60, terminalWidth - 2);
1120
1278
  const divider = "\u2501".repeat(dividerWidth);
1121
- console.log(chalk4.cyan(divider));
1122
- console.log(chalk4.bold.white(" CONTEXT ANALYSIS SUMMARY"));
1123
- 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");
1124
1282
  console.log(
1125
- chalk4.white(`\u{1F4C1} Files analyzed: ${chalk4.bold(summary.totalFiles)}`)
1283
+ chalk5.white(`\u{1F4C1} Files analyzed: ${chalk5.bold(summary.totalFiles)}`)
1126
1284
  );
1127
1285
  console.log(
1128
- chalk4.white(
1129
- `\u{1F4CA} Total tokens: ${chalk4.bold(summary.totalTokens.toLocaleString())}`
1286
+ chalk5.white(
1287
+ `\u{1F4CA} Total tokens: ${chalk5.bold(summary.totalTokens.toLocaleString())}`
1130
1288
  )
1131
1289
  );
1132
1290
  console.log(
1133
- chalk4.yellow(
1134
- `\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`
1135
1293
  )
1136
1294
  );
1137
1295
  console.log(
1138
- chalk4.white(`\u23F1 Analysis time: ${chalk4.bold(elapsedTime + "s")}
1296
+ chalk5.white(`\u23F1 Analysis time: ${chalk5.bold(elapsedTime + "s")}
1139
1297
  `)
1140
1298
  );
1141
1299
  const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
1142
1300
  if (totalIssues > 0) {
1143
- console.log(chalk4.bold("\u26A0\uFE0F Issues Found:\n"));
1301
+ console.log(chalk5.bold("\u26A0\uFE0F Issues Found:\n"));
1144
1302
  if (summary.criticalIssues > 0) {
1145
1303
  console.log(
1146
- chalk4.red(` \u{1F534} Critical: ${chalk4.bold(summary.criticalIssues)}`)
1304
+ chalk5.red(` \u{1F534} Critical: ${chalk5.bold(summary.criticalIssues)}`)
1147
1305
  );
1148
1306
  }
1149
1307
  if (summary.majorIssues > 0) {
1150
1308
  console.log(
1151
- chalk4.yellow(` \u{1F7E1} Major: ${chalk4.bold(summary.majorIssues)}`)
1309
+ chalk5.yellow(` \u{1F7E1} Major: ${chalk5.bold(summary.majorIssues)}`)
1152
1310
  );
1153
1311
  }
1154
1312
  if (summary.minorIssues > 0) {
1155
1313
  console.log(
1156
- chalk4.blue(` \u{1F535} Minor: ${chalk4.bold(summary.minorIssues)}`)
1314
+ chalk5.blue(` \u{1F535} Minor: ${chalk5.bold(summary.minorIssues)}`)
1157
1315
  );
1158
1316
  }
1159
1317
  console.log(
1160
- chalk4.green(
1318
+ chalk5.green(
1161
1319
  `
1162
- \u{1F4A1} Potential savings: ${chalk4.bold(summary.totalPotentialSavings.toLocaleString())} tokens
1320
+ \u{1F4A1} Potential savings: ${chalk5.bold(summary.totalPotentialSavings.toLocaleString())} tokens
1163
1321
  `
1164
1322
  )
1165
1323
  );
1166
1324
  } else {
1167
- console.log(chalk4.green("\u2705 No significant issues found!\n"));
1325
+ console.log(chalk5.green("\u2705 No significant issues found!\n"));
1168
1326
  }
1169
1327
  if (summary.deepFiles.length > 0) {
1170
- console.log(chalk4.bold("\u{1F4CF} Deep Import Chains:\n"));
1328
+ console.log(chalk5.bold("\u{1F4CF} Deep Import Chains:\n"));
1171
1329
  console.log(
1172
- chalk4.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`)
1330
+ chalk5.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`)
1173
1331
  );
1174
1332
  console.log(
1175
- chalk4.gray(` Maximum depth: ${summary.maxImportDepth}
1333
+ chalk5.gray(` Maximum depth: ${summary.maxImportDepth}
1176
1334
  `)
1177
1335
  );
1178
1336
  summary.deepFiles.slice(0, 10).forEach((item) => {
1179
1337
  const fileName = item.file.split("/").slice(-2).join("/");
1180
1338
  console.log(
1181
- ` ${chalk4.cyan("\u2192")} ${chalk4.white(fileName)} ${chalk4.dim(`(depth: ${item.depth})`)}`
1339
+ ` ${chalk5.cyan("\u2192")} ${chalk5.white(fileName)} ${chalk5.dim(`(depth: ${item.depth})`)}`
1182
1340
  );
1183
1341
  });
1184
1342
  console.log();
1185
1343
  }
1186
1344
  if (summary.fragmentedModules.length > 0) {
1187
- console.log(chalk4.bold("\u{1F9E9} Fragmented Modules:\n"));
1345
+ console.log(chalk5.bold("\u{1F9E9} Fragmented Modules:\n"));
1188
1346
  console.log(
1189
- chalk4.gray(
1347
+ chalk5.gray(
1190
1348
  ` Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(0)}%
1191
1349
  `
1192
1350
  )
1193
1351
  );
1194
1352
  summary.fragmentedModules.slice(0, 10).forEach((module) => {
1195
1353
  console.log(
1196
- ` ${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`)}`
1197
1355
  );
1198
1356
  console.log(
1199
- chalk4.dim(
1357
+ chalk5.dim(
1200
1358
  ` Token cost: ${module.totalTokens.toLocaleString()}, Cohesion: ${(module.avgCohesion * 100).toFixed(0)}%`
1201
1359
  )
1202
1360
  );
@@ -1204,9 +1362,9 @@ async function contextAction(directory, options) {
1204
1362
  console.log();
1205
1363
  }
1206
1364
  if (summary.lowCohesionFiles.length > 0) {
1207
- console.log(chalk4.bold("\u{1F500} Low Cohesion Files:\n"));
1365
+ console.log(chalk5.bold("\u{1F500} Low Cohesion Files:\n"));
1208
1366
  console.log(
1209
- chalk4.gray(
1367
+ chalk5.gray(
1210
1368
  ` Average cohesion: ${(summary.avgCohesion * 100).toFixed(0)}%
1211
1369
  `
1212
1370
  )
@@ -1214,53 +1372,53 @@ async function contextAction(directory, options) {
1214
1372
  summary.lowCohesionFiles.slice(0, 10).forEach((item) => {
1215
1373
  const fileName = item.file.split("/").slice(-2).join("/");
1216
1374
  const scorePercent = (item.score * 100).toFixed(0);
1217
- const color = item.score < 0.4 ? chalk4.red : chalk4.yellow;
1375
+ const color = item.score < 0.4 ? chalk5.red : chalk5.yellow;
1218
1376
  console.log(
1219
- ` ${color("\u25CB")} ${chalk4.white(fileName)} ${chalk4.dim(`(${scorePercent}% cohesion)`)}`
1377
+ ` ${color("\u25CB")} ${chalk5.white(fileName)} ${chalk5.dim(`(${scorePercent}% cohesion)`)}`
1220
1378
  );
1221
1379
  });
1222
1380
  console.log();
1223
1381
  }
1224
1382
  if (summary.topExpensiveFiles.length > 0) {
1225
- 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"));
1226
1384
  summary.topExpensiveFiles.slice(0, 10).forEach((item) => {
1227
1385
  const fileName = item.file.split("/").slice(-2).join("/");
1228
- 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;
1229
1387
  console.log(
1230
- ` ${severityColor("\u25CF")} ${chalk4.white(fileName)} ${chalk4.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`
1388
+ ` ${severityColor("\u25CF")} ${chalk5.white(fileName)} ${chalk5.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`
1231
1389
  );
1232
1390
  });
1233
1391
  console.log();
1234
1392
  }
1235
1393
  if (contextScore) {
1236
- console.log(chalk4.cyan(divider));
1237
- console.log(chalk4.bold.white(" AI READINESS SCORE (Context)"));
1238
- 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");
1239
1397
  console.log(formatToolScore3(contextScore));
1240
1398
  console.log();
1241
1399
  }
1242
1400
  }
1243
1401
  } catch (error) {
1244
- handleCLIError3(error, "Context analysis");
1402
+ handleCLIError4(error, "Context analysis");
1245
1403
  }
1246
1404
  }
1247
1405
 
1248
1406
  // src/commands/consistency.ts
1249
- import chalk5 from "chalk";
1407
+ import chalk6 from "chalk";
1250
1408
  import { writeFileSync as writeFileSync2 } from "fs";
1251
- import { resolve as resolvePath5 } from "path";
1409
+ import { resolve as resolvePath6 } from "path";
1252
1410
  import {
1253
1411
  loadMergedConfig as loadMergedConfig4,
1254
1412
  handleJSONOutput as handleJSONOutput4,
1255
- handleCLIError as handleCLIError4,
1413
+ handleCLIError as handleCLIError5,
1256
1414
  getElapsedTime as getElapsedTime4,
1257
1415
  resolveOutputPath as resolveOutputPath4,
1258
1416
  formatToolScore as formatToolScore4
1259
1417
  } from "@aiready/core";
1260
1418
  async function consistencyAction(directory, options) {
1261
- console.log(chalk5.blue("\u{1F50D} Analyzing consistency...\n"));
1419
+ console.log(chalk6.blue("\u{1F50D} Analyzing consistency...\n"));
1262
1420
  const startTime = Date.now();
1263
- const resolvedDir = resolvePath5(process.cwd(), directory || ".");
1421
+ const resolvedDir = resolvePath6(process.cwd(), directory || ".");
1264
1422
  try {
1265
1423
  const defaults = {
1266
1424
  checkNaming: true,
@@ -1320,23 +1478,23 @@ async function consistencyAction(directory, options) {
1320
1478
  resolvedDir
1321
1479
  );
1322
1480
  writeFileSync2(outputPath, markdown);
1323
- console.log(chalk5.green(`\u2705 Report saved to ${outputPath}`));
1481
+ console.log(chalk6.green(`\u2705 Report saved to ${outputPath}`));
1324
1482
  } else {
1325
- console.log(chalk5.bold("\n\u{1F4CA} Summary\n"));
1483
+ console.log(chalk6.bold("\n\u{1F4CA} Summary\n"));
1326
1484
  console.log(
1327
- `Files Analyzed: ${chalk5.cyan(report.summary.filesAnalyzed)}`
1485
+ `Files Analyzed: ${chalk6.cyan(report.summary.filesAnalyzed)}`
1328
1486
  );
1329
- console.log(`Total Issues: ${chalk5.yellow(report.summary.totalIssues)}`);
1330
- console.log(` Naming: ${chalk5.yellow(report.summary.namingIssues)}`);
1331
- 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)}`);
1332
1490
  console.log(
1333
- ` Architecture: ${chalk5.yellow(report.summary.architectureIssues || 0)}`
1491
+ ` Architecture: ${chalk6.yellow(report.summary.architectureIssues || 0)}`
1334
1492
  );
1335
- console.log(`Analysis Time: ${chalk5.gray(elapsedTime + "s")}
1493
+ console.log(`Analysis Time: ${chalk6.gray(elapsedTime + "s")}
1336
1494
  `);
1337
1495
  if (report.summary.totalIssues === 0) {
1338
1496
  console.log(
1339
- chalk5.green(
1497
+ chalk6.green(
1340
1498
  "\u2728 No consistency issues found! Your codebase is well-maintained.\n"
1341
1499
  )
1342
1500
  );
@@ -1348,20 +1506,20 @@ async function consistencyAction(directory, options) {
1348
1506
  (r) => r.issues.some((i) => i.category === "patterns")
1349
1507
  );
1350
1508
  if (namingResults.length > 0) {
1351
- console.log(chalk5.bold("\u{1F3F7}\uFE0F Naming Issues\n"));
1509
+ console.log(chalk6.bold("\u{1F3F7}\uFE0F Naming Issues\n"));
1352
1510
  let shown = 0;
1353
1511
  for (const result of namingResults) {
1354
1512
  if (shown >= 5) break;
1355
1513
  for (const issue of result.issues) {
1356
1514
  if (shown >= 5) break;
1357
- 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;
1358
1516
  console.log(
1359
- `${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}`)}`
1360
1518
  );
1361
1519
  console.log(` ${issue.message}`);
1362
1520
  if (issue.suggestion) {
1363
1521
  console.log(
1364
- ` ${chalk5.dim("\u2192")} ${chalk5.italic(issue.suggestion)}`
1522
+ ` ${chalk6.dim("\u2192")} ${chalk6.italic(issue.suggestion)}`
1365
1523
  );
1366
1524
  }
1367
1525
  console.log();
@@ -1370,25 +1528,25 @@ async function consistencyAction(directory, options) {
1370
1528
  }
1371
1529
  const remaining = namingResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
1372
1530
  if (remaining > 0) {
1373
- console.log(chalk5.dim(` ... and ${remaining} more issues
1531
+ console.log(chalk6.dim(` ... and ${remaining} more issues
1374
1532
  `));
1375
1533
  }
1376
1534
  }
1377
1535
  if (patternResults.length > 0) {
1378
- console.log(chalk5.bold("\u{1F504} Pattern Issues\n"));
1536
+ console.log(chalk6.bold("\u{1F504} Pattern Issues\n"));
1379
1537
  let shown = 0;
1380
1538
  for (const result of patternResults) {
1381
1539
  if (shown >= 5) break;
1382
1540
  for (const issue of result.issues) {
1383
1541
  if (shown >= 5) break;
1384
- 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;
1385
1543
  console.log(
1386
- `${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}`)}`
1387
1545
  );
1388
1546
  console.log(` ${issue.message}`);
1389
1547
  if (issue.suggestion) {
1390
1548
  console.log(
1391
- ` ${chalk5.dim("\u2192")} ${chalk5.italic(issue.suggestion)}`
1549
+ ` ${chalk6.dim("\u2192")} ${chalk6.italic(issue.suggestion)}`
1392
1550
  );
1393
1551
  }
1394
1552
  console.log();
@@ -1397,12 +1555,12 @@ async function consistencyAction(directory, options) {
1397
1555
  }
1398
1556
  const remaining = patternResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
1399
1557
  if (remaining > 0) {
1400
- console.log(chalk5.dim(` ... and ${remaining} more issues
1558
+ console.log(chalk6.dim(` ... and ${remaining} more issues
1401
1559
  `));
1402
1560
  }
1403
1561
  }
1404
1562
  if (report.recommendations.length > 0) {
1405
- console.log(chalk5.bold("\u{1F4A1} Recommendations\n"));
1563
+ console.log(chalk6.bold("\u{1F4A1} Recommendations\n"));
1406
1564
  report.recommendations.forEach((rec, i) => {
1407
1565
  console.log(`${i + 1}. ${rec}`);
1408
1566
  });
@@ -1410,38 +1568,38 @@ async function consistencyAction(directory, options) {
1410
1568
  }
1411
1569
  }
1412
1570
  if (consistencyScore) {
1413
- 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"));
1414
1572
  console.log(formatToolScore4(consistencyScore));
1415
1573
  console.log();
1416
1574
  }
1417
1575
  }
1418
1576
  } catch (error) {
1419
- handleCLIError4(error, "Consistency analysis");
1577
+ handleCLIError5(error, "Consistency analysis");
1420
1578
  }
1421
1579
  }
1422
1580
 
1423
1581
  // src/commands/visualize.ts
1424
- import chalk6 from "chalk";
1582
+ import chalk7 from "chalk";
1425
1583
  import { writeFileSync as writeFileSync3, readFileSync as readFileSync3, existsSync as existsSync2, copyFileSync } from "fs";
1426
- import { resolve as resolvePath6 } from "path";
1584
+ import { resolve as resolvePath7 } from "path";
1427
1585
  import { spawn } from "child_process";
1428
- import { handleCLIError as handleCLIError5 } from "@aiready/core";
1586
+ import { handleCLIError as handleCLIError6 } from "@aiready/core";
1429
1587
  import { generateHTML } from "@aiready/core";
1430
1588
  async function visualizeAction(directory, options) {
1431
1589
  try {
1432
- const dirPath = resolvePath6(process.cwd(), directory || ".");
1433
- 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;
1434
1592
  if (!reportPath || !existsSync2(reportPath)) {
1435
1593
  const latestScan = findLatestScanReport(dirPath);
1436
1594
  if (latestScan) {
1437
1595
  reportPath = latestScan;
1438
1596
  console.log(
1439
- chalk6.dim(`Found latest report: ${latestScan.split("/").pop()}`)
1597
+ chalk7.dim(`Found latest report: ${latestScan.split("/").pop()}`)
1440
1598
  );
1441
1599
  } else {
1442
- console.error(chalk6.red("\u274C No AI readiness report found"));
1600
+ console.error(chalk7.red("\u274C No AI readiness report found"));
1443
1601
  console.log(
1444
- chalk6.dim(
1602
+ chalk7.dim(
1445
1603
  `
1446
1604
  Generate a report with:
1447
1605
  aiready scan --output json
@@ -1455,7 +1613,7 @@ Or specify a custom report:
1455
1613
  }
1456
1614
  const raw = readFileSync3(reportPath, "utf8");
1457
1615
  const report = JSON.parse(raw);
1458
- const configPath = resolvePath6(dirPath, "aiready.json");
1616
+ const configPath = resolvePath7(dirPath, "aiready.json");
1459
1617
  let graphConfig = { maxNodes: 400, maxEdges: 600 };
1460
1618
  if (existsSync2(configPath)) {
1461
1619
  try {
@@ -1479,7 +1637,7 @@ Or specify a custom report:
1479
1637
  let devServerStarted = false;
1480
1638
  if (useDevMode) {
1481
1639
  try {
1482
- const monorepoWebDir = resolvePath6(dirPath, "packages/visualizer");
1640
+ const monorepoWebDir = resolvePath7(dirPath, "packages/visualizer");
1483
1641
  let webDir = "";
1484
1642
  let visualizerAvailable = false;
1485
1643
  if (existsSync2(monorepoWebDir)) {
@@ -1487,8 +1645,8 @@ Or specify a custom report:
1487
1645
  visualizerAvailable = true;
1488
1646
  } else {
1489
1647
  const nodemodulesLocations = [
1490
- resolvePath6(dirPath, "node_modules", "@aiready", "visualizer"),
1491
- resolvePath6(
1648
+ resolvePath7(dirPath, "node_modules", "@aiready", "visualizer"),
1649
+ resolvePath7(
1492
1650
  process.cwd(),
1493
1651
  "node_modules",
1494
1652
  "@aiready",
@@ -1498,14 +1656,14 @@ Or specify a custom report:
1498
1656
  let currentDir = dirPath;
1499
1657
  while (currentDir !== "/" && currentDir !== ".") {
1500
1658
  nodemodulesLocations.push(
1501
- resolvePath6(currentDir, "node_modules", "@aiready", "visualizer")
1659
+ resolvePath7(currentDir, "node_modules", "@aiready", "visualizer")
1502
1660
  );
1503
- const parent = resolvePath6(currentDir, "..");
1661
+ const parent = resolvePath7(currentDir, "..");
1504
1662
  if (parent === currentDir) break;
1505
1663
  currentDir = parent;
1506
1664
  }
1507
1665
  for (const location of nodemodulesLocations) {
1508
- if (existsSync2(location) && existsSync2(resolvePath6(location, "package.json"))) {
1666
+ if (existsSync2(location) && existsSync2(resolvePath7(location, "package.json"))) {
1509
1667
  webDir = location;
1510
1668
  visualizerAvailable = true;
1511
1669
  break;
@@ -1514,20 +1672,20 @@ Or specify a custom report:
1514
1672
  if (!visualizerAvailable) {
1515
1673
  try {
1516
1674
  const vizPkgPath = __require.resolve("@aiready/visualizer/package.json");
1517
- webDir = resolvePath6(vizPkgPath, "..");
1675
+ webDir = resolvePath7(vizPkgPath, "..");
1518
1676
  visualizerAvailable = true;
1519
1677
  } catch (err) {
1520
1678
  void err;
1521
1679
  }
1522
1680
  }
1523
1681
  }
1524
- const webViteConfigExists = webDir && existsSync2(resolvePath6(webDir, "web", "vite.config.ts"));
1682
+ const webViteConfigExists = webDir && existsSync2(resolvePath7(webDir, "web", "vite.config.ts"));
1525
1683
  if (visualizerAvailable && webViteConfigExists) {
1526
1684
  const spawnCwd = webDir;
1527
1685
  const { watch } = await import("fs");
1528
1686
  const copyReportToViz = () => {
1529
1687
  try {
1530
- const destPath = resolvePath6(spawnCwd, "web", "report-data.json");
1688
+ const destPath = resolvePath7(spawnCwd, "web", "report-data.json");
1531
1689
  copyFileSync(reportPath, destPath);
1532
1690
  console.log(`\u{1F4CB} Report synced to ${destPath}`);
1533
1691
  } catch (e) {
@@ -1571,19 +1729,19 @@ Or specify a custom report:
1571
1729
  return;
1572
1730
  } else {
1573
1731
  console.log(
1574
- chalk6.yellow(
1732
+ chalk7.yellow(
1575
1733
  "\u26A0\uFE0F Dev server not available (requires local @aiready/visualizer with web assets)."
1576
1734
  )
1577
1735
  );
1578
1736
  console.log(
1579
- chalk6.cyan(" Falling back to static HTML generation...\n")
1737
+ chalk7.cyan(" Falling back to static HTML generation...\n")
1580
1738
  );
1581
1739
  useDevMode = false;
1582
1740
  }
1583
1741
  } catch (err) {
1584
1742
  console.error("Failed to start dev server:", err);
1585
1743
  console.log(
1586
- chalk6.cyan(" Falling back to static HTML generation...\n")
1744
+ chalk7.cyan(" Falling back to static HTML generation...\n")
1587
1745
  );
1588
1746
  useDevMode = false;
1589
1747
  }
@@ -1591,9 +1749,9 @@ Or specify a custom report:
1591
1749
  console.log("Generating HTML...");
1592
1750
  const html = generateHTML(graph);
1593
1751
  const defaultOutput = "visualization.html";
1594
- const outPath = resolvePath6(dirPath, options.output || defaultOutput);
1752
+ const outPath = resolvePath7(dirPath, options.output || defaultOutput);
1595
1753
  writeFileSync3(outPath, html, "utf8");
1596
- console.log(chalk6.green(`\u2705 Visualization written to: ${outPath}`));
1754
+ console.log(chalk7.green(`\u2705 Visualization written to: ${outPath}`));
1597
1755
  if (options.open || options.serve) {
1598
1756
  const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
1599
1757
  if (options.serve) {
@@ -1623,7 +1781,7 @@ Or specify a custom report:
1623
1781
  server.listen(port, () => {
1624
1782
  const addr = `http://localhost:${port}/`;
1625
1783
  console.log(
1626
- chalk6.cyan(`\u{1F310} Local visualization server running at ${addr}`)
1784
+ chalk7.cyan(`\u{1F310} Local visualization server running at ${addr}`)
1627
1785
  );
1628
1786
  spawn(opener, [`"${addr}"`], { shell: true });
1629
1787
  });
@@ -1639,7 +1797,7 @@ Or specify a custom report:
1639
1797
  }
1640
1798
  }
1641
1799
  } catch (err) {
1642
- handleCLIError5(err, "Visualization");
1800
+ handleCLIError6(err, "Visualization");
1643
1801
  }
1644
1802
  }
1645
1803
  var visualizeHelpText = `
@@ -1670,15 +1828,15 @@ NOTES:
1670
1828
  `;
1671
1829
 
1672
1830
  // src/commands/ai-signal-clarity.ts
1673
- import chalk7 from "chalk";
1831
+ import chalk8 from "chalk";
1674
1832
  import { loadConfig, mergeConfigWithDefaults } from "@aiready/core";
1675
1833
 
1676
1834
  // src/commands/agent-grounding.ts
1677
- import chalk8 from "chalk";
1835
+ import chalk9 from "chalk";
1678
1836
  import { loadConfig as loadConfig2, mergeConfigWithDefaults as mergeConfigWithDefaults2 } from "@aiready/core";
1679
1837
 
1680
1838
  // src/commands/testability.ts
1681
- import chalk9 from "chalk";
1839
+ import chalk10 from "chalk";
1682
1840
  import { loadConfig as loadConfig3, mergeConfigWithDefaults as mergeConfigWithDefaults3 } from "@aiready/core";
1683
1841
 
1684
1842
  // src/commands/change-amplification.ts
@@ -1753,7 +1911,7 @@ program.command("scan").description(
1753
1911
  "--fail-on <level>",
1754
1912
  "Fail on issues: critical, major, any",
1755
1913
  "critical"
1756
- ).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) => {
1757
1915
  await scanAction(directory, options);
1758
1916
  });
1759
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(
@@ -1834,4 +1992,7 @@ program.command("visualize").description("Generate interactive visualization fro
1834
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) => {
1835
1993
  await changeAmplificationAction(directory, options);
1836
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
+ });
1837
1998
  program.parse();