@aiready/cli 0.10.6 → 0.12.1

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
@@ -3,7 +3,7 @@ import {
3
3
  __require,
4
4
  analyzeUnified,
5
5
  scoreUnified
6
- } from "./chunk-VQCWYJYJ.mjs";
6
+ } from "./chunk-LTUQDJPO.mjs";
7
7
 
8
8
  // src/cli.ts
9
9
  import { Command } from "commander";
@@ -19,18 +19,14 @@ import {
19
19
  loadMergedConfig,
20
20
  handleJSONOutput,
21
21
  handleCLIError as handleCLIError2,
22
- getElapsedTime,
23
22
  resolveOutputPath,
24
23
  formatScore,
25
- formatToolScore,
26
24
  calculateTokenBudget,
27
25
  estimateCostFromBudget,
28
26
  getModelPreset,
29
27
  getRating,
30
- getRatingDisplay,
31
28
  getRepoMetadata,
32
29
  Severity,
33
- IssueType,
34
30
  ToolName
35
31
  } from "@aiready/core";
36
32
 
@@ -151,12 +147,6 @@ function generateMarkdownReport(report, elapsedTime) {
151
147
  }
152
148
  return markdown;
153
149
  }
154
- function truncateArray(arr, cap = 8) {
155
- if (!Array.isArray(arr)) return "";
156
- const shown = arr.slice(0, cap).map((v) => String(v));
157
- const more = arr.length - shown.length;
158
- return shown.join(", ") + (more > 0 ? `, ... (+${more} more)` : "");
159
- }
160
150
 
161
151
  // src/commands/upload.ts
162
152
  import fs from "fs";
@@ -270,14 +260,14 @@ async function scanAction(directory, options) {
270
260
  try {
271
261
  const defaults = {
272
262
  tools: [
273
- "patterns",
274
- "context",
275
- "consistency",
263
+ "pattern-detect",
264
+ "context-analyzer",
265
+ "naming-consistency",
276
266
  "ai-signal-clarity",
277
267
  "agent-grounding",
278
- "testability",
268
+ "testability-index",
279
269
  "doc-drift",
280
- "deps-health",
270
+ "dependency-health",
281
271
  "change-amplification"
282
272
  ],
283
273
  include: void 0,
@@ -287,35 +277,37 @@ async function scanAction(directory, options) {
287
277
  file: void 0
288
278
  }
289
279
  };
290
- let profileTools = options.tools ? options.tools.split(",").map((t) => {
291
- const tool = t.trim();
292
- if (tool === "hallucination" || tool === "hallucination-risk")
293
- return "aiSignalClarity";
294
- return tool;
295
- }) : void 0;
280
+ let profileTools = options.tools ? options.tools.split(",").map((t) => t.trim()) : void 0;
296
281
  if (options.profile) {
297
282
  switch (options.profile.toLowerCase()) {
298
283
  case "agentic":
299
284
  profileTools = [
300
- "ai-signal-clarity",
301
- "agent-grounding",
302
- "testability"
285
+ ToolName.AiSignalClarity,
286
+ ToolName.AgentGrounding,
287
+ ToolName.TestabilityIndex
303
288
  ];
304
289
  break;
305
290
  case "cost":
306
- profileTools = ["patterns", "context"];
291
+ profileTools = [ToolName.PatternDetect, ToolName.ContextAnalyzer];
307
292
  break;
308
293
  case "security":
309
- profileTools = ["consistency", "testability"];
294
+ profileTools = [
295
+ ToolName.NamingConsistency,
296
+ ToolName.TestabilityIndex
297
+ ];
310
298
  break;
311
299
  case "onboarding":
312
- profileTools = ["context", "consistency", "agent-grounding"];
300
+ profileTools = [
301
+ ToolName.ContextAnalyzer,
302
+ ToolName.NamingConsistency,
303
+ ToolName.AgentGrounding
304
+ ];
313
305
  break;
314
306
  default:
315
307
  console.log(
316
308
  chalk3.yellow(
317
309
  `
318
- \u26A0\uFE0F Unknown profile '${options.profile}'. Using specified tools or defaults.`
310
+ \u26A0\uFE0F Unknown profile '${options.profile}'. Using defaults.`
319
311
  )
320
312
  );
321
313
  }
@@ -324,16 +316,14 @@ async function scanAction(directory, options) {
324
316
  include: options.include?.split(","),
325
317
  exclude: options.exclude?.split(",")
326
318
  };
327
- if (profileTools) {
328
- cliOverrides.tools = profileTools;
329
- }
319
+ if (profileTools) cliOverrides.tools = profileTools;
330
320
  const baseOptions = await loadMergedConfig(
331
321
  resolvedDir,
332
322
  defaults,
333
323
  cliOverrides
334
324
  );
335
325
  let finalOptions = { ...baseOptions };
336
- if (baseOptions.tools.includes("patterns")) {
326
+ if (baseOptions.tools.includes(ToolName.PatternDetect) || baseOptions.tools.includes("patterns")) {
337
327
  const { getSmartDefaults } = await import("@aiready/pattern-detect");
338
328
  const patternSmartDefaults = await getSmartDefaults(
339
329
  resolvedDir,
@@ -348,256 +338,25 @@ async function scanAction(directory, options) {
348
338
  console.log(chalk3.cyan("\n=== AIReady Run Preview ==="));
349
339
  console.log(
350
340
  chalk3.white("Tools to run:"),
351
- (finalOptions.tools || ["patterns", "context", "consistency"]).join(", ")
341
+ (finalOptions.tools || []).join(", ")
352
342
  );
353
- console.log(chalk3.white("Will use settings from config and defaults."));
354
- console.log(chalk3.white("\nGeneral settings:"));
355
- if (finalOptions.rootDir)
356
- console.log(` rootDir: ${chalk3.bold(String(finalOptions.rootDir))}`);
357
- if (finalOptions.include)
358
- console.log(
359
- ` include: ${chalk3.bold(truncateArray(finalOptions.include, 6))}`
360
- );
361
- if (finalOptions.exclude)
362
- console.log(
363
- ` exclude: ${chalk3.bold(truncateArray(finalOptions.exclude, 6))}`
364
- );
365
- if (finalOptions["pattern-detect"] || finalOptions.minSimilarity) {
366
- const patternDetectConfig = finalOptions["pattern-detect"] || {
367
- minSimilarity: finalOptions.minSimilarity,
368
- minLines: finalOptions.minLines,
369
- approx: finalOptions.approx,
370
- minSharedTokens: finalOptions.minSharedTokens,
371
- maxCandidatesPerBlock: finalOptions.maxCandidatesPerBlock,
372
- batchSize: finalOptions.batchSize,
373
- streamResults: finalOptions.streamResults,
374
- severity: finalOptions.severity,
375
- includeTests: finalOptions.includeTests
376
- };
377
- console.log(chalk3.white("\nPattern-detect settings:"));
378
- console.log(
379
- ` minSimilarity: ${chalk3.bold(patternDetectConfig.minSimilarity ?? "default")}`
380
- );
381
- console.log(
382
- ` minLines: ${chalk3.bold(patternDetectConfig.minLines ?? "default")}`
383
- );
384
- if (patternDetectConfig.approx !== void 0)
385
- console.log(
386
- ` approx: ${chalk3.bold(String(patternDetectConfig.approx))}`
387
- );
388
- if (patternDetectConfig.minSharedTokens !== void 0)
389
- console.log(
390
- ` minSharedTokens: ${chalk3.bold(String(patternDetectConfig.minSharedTokens))}`
391
- );
392
- if (patternDetectConfig.maxCandidatesPerBlock !== void 0)
393
- console.log(
394
- ` maxCandidatesPerBlock: ${chalk3.bold(String(patternDetectConfig.maxCandidatesPerBlock))}`
395
- );
396
- if (patternDetectConfig.batchSize !== void 0)
397
- console.log(
398
- ` batchSize: ${chalk3.bold(String(patternDetectConfig.batchSize))}`
399
- );
400
- if (patternDetectConfig.streamResults !== void 0)
401
- console.log(
402
- ` streamResults: ${chalk3.bold(String(patternDetectConfig.streamResults))}`
403
- );
404
- if (patternDetectConfig.severity !== void 0)
405
- console.log(
406
- ` severity: ${chalk3.bold(String(patternDetectConfig.severity))}`
407
- );
408
- if (patternDetectConfig.includeTests !== void 0)
409
- console.log(
410
- ` includeTests: ${chalk3.bold(String(patternDetectConfig.includeTests))}`
411
- );
412
- }
413
- if (finalOptions["context-analyzer"] || finalOptions.maxDepth) {
414
- const ca = finalOptions["context-analyzer"] || {
415
- maxDepth: finalOptions.maxDepth,
416
- maxContextBudget: finalOptions.maxContextBudget,
417
- minCohesion: finalOptions.minCohesion,
418
- maxFragmentation: finalOptions.maxFragmentation,
419
- includeNodeModules: finalOptions.includeNodeModules
420
- };
421
- console.log(chalk3.white("\nContext-analyzer settings:"));
422
- console.log(` maxDepth: ${chalk3.bold(ca.maxDepth ?? "default")}`);
423
- console.log(
424
- ` maxContextBudget: ${chalk3.bold(ca.maxContextBudget ?? "default")}`
425
- );
426
- if (ca.minCohesion !== void 0)
427
- console.log(` minCohesion: ${chalk3.bold(String(ca.minCohesion))}`);
428
- if (ca.maxFragmentation !== void 0)
429
- console.log(
430
- ` maxFragmentation: ${chalk3.bold(String(ca.maxFragmentation))}`
431
- );
432
- if (ca.includeNodeModules !== void 0)
433
- console.log(
434
- ` includeNodeModules: ${chalk3.bold(String(ca.includeNodeModules))}`
435
- );
436
- }
437
- if (finalOptions.consistency) {
438
- const c = finalOptions.consistency;
439
- console.log(chalk3.white("\nConsistency settings:"));
440
- console.log(
441
- ` checkNaming: ${chalk3.bold(String(c.checkNaming ?? true))}`
442
- );
443
- console.log(
444
- ` checkPatterns: ${chalk3.bold(String(c.checkPatterns ?? true))}`
445
- );
446
- console.log(
447
- ` checkArchitecture: ${chalk3.bold(String(c.checkArchitecture ?? false))}`
448
- );
449
- if (c.minSeverity)
450
- console.log(` minSeverity: ${chalk3.bold(c.minSeverity)}`);
451
- if (c.acceptedAbbreviations)
452
- console.log(
453
- ` acceptedAbbreviations: ${chalk3.bold(truncateArray(c.acceptedAbbreviations, 8))}`
454
- );
455
- if (c.shortWords)
456
- console.log(
457
- ` shortWords: ${chalk3.bold(truncateArray(c.shortWords, 8))}`
458
- );
459
- }
460
- console.log(chalk3.white("\nStarting analysis..."));
461
343
  const progressCallback = (event) => {
462
- console.log(chalk3.cyan(`
463
- --- ${event.tool.toUpperCase()} RESULTS ---`));
464
- try {
465
- if (event.tool === "patterns") {
466
- const pr = event.data;
467
- console.log(
468
- ` Duplicate patterns: ${chalk3.bold(String(pr.duplicates?.length || 0))}`
469
- );
470
- console.log(
471
- ` Files with pattern issues: ${chalk3.bold(String(pr.results?.length || 0))}`
472
- );
473
- if (pr.duplicates && pr.duplicates.length > 0) {
474
- pr.duplicates.slice(0, 5).forEach((d, i) => {
475
- console.log(
476
- ` ${i + 1}. ${d.file1.split("/").pop()} \u2194 ${d.file2.split("/").pop()} (sim=${(d.similarity * 100).toFixed(1)}%)`
477
- );
478
- });
479
- }
480
- if (pr.results && pr.results.length > 0) {
481
- console.log(` Top files with pattern issues:`);
482
- const sortedByIssues = [...pr.results].sort(
483
- (a, b) => (b.issues?.length || 0) - (a.issues?.length || 0)
484
- );
485
- sortedByIssues.slice(0, 5).forEach((r, i) => {
486
- console.log(
487
- ` ${i + 1}. ${r.fileName.split("/").pop()} - ${r.issues.length} issue(s)`
488
- );
489
- });
490
- }
491
- if (pr.groups && pr.groups.length >= 0) {
492
- console.log(
493
- ` \u2705 Grouped ${chalk3.bold(String(pr.duplicates?.length || 0))} duplicates into ${chalk3.bold(String(pr.groups.length))} file pairs`
494
- );
495
- }
496
- if (pr.clusters && pr.clusters.length >= 0) {
497
- console.log(
498
- ` \u2705 Created ${chalk3.bold(String(pr.clusters.length))} refactor clusters`
499
- );
500
- pr.clusters.slice(0, 3).forEach((cl, idx) => {
501
- const files = (cl.files || []).map((f) => f.path.split("/").pop()).join(", ");
502
- console.log(
503
- ` ${idx + 1}. ${files} (${cl.tokenCost || "n/a"} tokens)`
504
- );
505
- });
506
- }
507
- } else if (event.tool === "context") {
508
- const cr = event.data;
509
- console.log(
510
- ` Context issues found: ${chalk3.bold(String(cr.length || 0))}`
511
- );
512
- cr.slice(0, 5).forEach((c, i) => {
513
- const msg = c.message ? ` - ${c.message}` : "";
514
- console.log(
515
- ` ${i + 1}. ${c.file} (${c.severity || "n/a"})${msg}`
516
- );
517
- });
518
- } else if (event.tool === "consistency") {
519
- const rep = event.data;
520
- console.log(
521
- ` Consistency totalIssues: ${chalk3.bold(String(rep.summary?.totalIssues || 0))}`
522
- );
523
- if (rep.results && rep.results.length > 0) {
524
- const fileMap = /* @__PURE__ */ new Map();
525
- rep.results.forEach((r) => {
526
- (r.issues || []).forEach((issue) => {
527
- const file = issue.location?.file || r.file || "unknown";
528
- if (!fileMap.has(file)) fileMap.set(file, []);
529
- fileMap.get(file).push(issue);
530
- });
531
- });
532
- const files = Array.from(fileMap.entries()).sort(
533
- (a, b) => b[1].length - a[1].length
534
- );
535
- const topFiles = files.slice(0, 10);
536
- topFiles.forEach(([file, issues], idx) => {
537
- const counts = issues.reduce(
538
- (acc, it) => {
539
- const s = (it.severity || Severity.Info).toLowerCase();
540
- acc[s] = (acc[s] || 0) + 1;
541
- return acc;
542
- },
543
- {}
544
- );
545
- const sample = issues.find(
546
- (it) => it.severity === Severity.Critical || it.severity === Severity.Major
547
- ) || issues[0];
548
- const sampleMsg = sample ? ` \u2014 ${sample.message}` : "";
549
- console.log(
550
- ` ${idx + 1}. ${file} \u2014 ${issues.length} issue(s) (critical:${counts[Severity.Critical] || 0} major:${counts[Severity.Major] || 0} minor:${counts[Severity.Minor] || 0} info:${counts[Severity.Info] || 0})${sampleMsg}`
551
- );
552
- });
553
- const remaining = files.length - topFiles.length;
554
- if (remaining > 0) {
555
- console.log(
556
- chalk3.dim(
557
- ` ... and ${remaining} more files with issues (use --output json for full details)`
558
- )
559
- );
560
- }
561
- }
562
- } else if (event.tool === "doc-drift") {
563
- const dr = event.data;
564
- console.log(
565
- ` Issues found: ${chalk3.bold(String(dr.issues?.length || 0))}`
566
- );
567
- if (dr.rawData) {
568
- console.log(
569
- ` Signature Mismatches: ${chalk3.bold(dr.rawData.outdatedComments || 0)}`
570
- );
571
- console.log(
572
- ` Undocumented Complexity: ${chalk3.bold(dr.rawData.undocumentedComplexity || 0)}`
573
- );
574
- }
575
- } else if (event.tool === "deps-health") {
576
- const dr = event.data;
577
- console.log(
578
- ` Packages Analyzed: ${chalk3.bold(String(dr.summary?.packagesAnalyzed || 0))}`
579
- );
580
- if (dr.rawData) {
581
- console.log(
582
- ` Deprecated Packages: ${chalk3.bold(dr.rawData.deprecatedPackages || 0)}`
583
- );
584
- console.log(
585
- ` AI Cutoff Skew Score: ${chalk3.bold(dr.rawData.trainingCutoffSkew?.toFixed(1) || 0)}`
586
- );
587
- }
588
- } else if (event.tool === "change-amplification" || event.tool === "changeAmplification") {
589
- const dr = event.data;
344
+ if (event.message) {
345
+ process.stdout.write(`\r\x1B[K [${event.tool}] ${event.message}`);
346
+ return;
347
+ }
348
+ process.stdout.write("\n");
349
+ console.log(chalk3.cyan(`--- ${event.tool.toUpperCase()} RESULTS ---`));
350
+ const res = event.data;
351
+ if (res && res.summary) {
352
+ if (res.summary.totalIssues !== void 0)
353
+ console.log(` Issues found: ${chalk3.bold(res.summary.totalIssues)}`);
354
+ if (res.summary.score !== void 0)
355
+ console.log(` Tool Score: ${chalk3.bold(res.summary.score)}/100`);
356
+ if (res.summary.totalFiles !== void 0)
590
357
  console.log(
591
- ` Coupling issues: ${chalk3.bold(String(dr.issues?.length || 0))}`
358
+ ` Files analyzed: ${chalk3.bold(res.summary.totalFiles)}`
592
359
  );
593
- if (dr.summary) {
594
- console.log(
595
- ` Complexity Score: ${chalk3.bold(dr.summary.score || 0)}/100`
596
- );
597
- }
598
- }
599
- } catch (err) {
600
- void err;
601
360
  }
602
361
  };
603
362
  const results = await analyzeUnified({
@@ -607,44 +366,14 @@ async function scanAction(directory, options) {
607
366
  process.stdout.write(
608
367
  `\r\x1B[K [${processed}/${total}] ${message}...`
609
368
  );
610
- if (processed === total) {
611
- process.stdout.write("\n");
612
- }
369
+ if (processed === total) process.stdout.write("\n");
613
370
  },
614
371
  suppressToolConfig: true
615
372
  });
616
373
  console.log(chalk3.cyan("\n=== AIReady Run Summary ==="));
617
- console.log(
618
- chalk3.white("Tools run:"),
619
- (finalOptions.tools || ["patterns", "context", "consistency"]).join(", ")
620
- );
621
- console.log(chalk3.cyan("\nResults summary:"));
622
374
  console.log(
623
375
  ` Total issues (all tools): ${chalk3.bold(String(results.summary.totalIssues || 0))}`
624
376
  );
625
- if (results[ToolName.PatternDetect]) {
626
- console.log(
627
- ` Duplicate patterns found: ${chalk3.bold(String(results[ToolName.PatternDetect].duplicates?.length || 0))}`
628
- );
629
- console.log(
630
- ` Pattern files with issues: ${chalk3.bold(String(results[ToolName.PatternDetect].results.length || 0))}`
631
- );
632
- }
633
- if (results[ToolName.ContextAnalyzer])
634
- console.log(
635
- ` Context issues: ${chalk3.bold(String(results[ToolName.ContextAnalyzer].results.length || 0))}`
636
- );
637
- if (results[ToolName.NamingConsistency])
638
- console.log(
639
- ` Consistency issues: ${chalk3.bold(String(results[ToolName.NamingConsistency].summary?.totalIssues || 0))}`
640
- );
641
- if (results[ToolName.ChangeAmplification])
642
- console.log(
643
- ` Change amplification: ${chalk3.bold(String(results[ToolName.ChangeAmplification].summary?.score || 0))}/100`
644
- );
645
- console.log(chalk3.cyan("===========================\n"));
646
- const elapsedTime = getElapsedTime(startTime);
647
- void elapsedTime;
648
377
  let scoringResult;
649
378
  if (options.score || finalOptions.scoring?.showBreakdown) {
650
379
  scoringResult = await scoreUnified(results, finalOptions);
@@ -652,55 +381,34 @@ async function scanAction(directory, options) {
652
381
  console.log(` ${formatScore(scoringResult)}`);
653
382
  if (options.compareTo) {
654
383
  try {
655
- const prevReportStr = readFileSync2(
656
- resolvePath3(process.cwd(), options.compareTo),
657
- "utf8"
384
+ const prevReport = JSON.parse(
385
+ readFileSync2(resolvePath3(process.cwd(), options.compareTo), "utf8")
658
386
  );
659
- const prevReport = JSON.parse(prevReportStr);
660
- const prevScore = prevReport.scoring?.score || prevReport.scoring?.overallScore;
387
+ const prevScore = prevReport.scoring?.overall || prevReport.scoring?.score;
661
388
  if (typeof prevScore === "number") {
662
389
  const diff = scoringResult.overall - prevScore;
663
390
  const diffStr = diff > 0 ? `+${diff}` : String(diff);
664
- console.log();
665
- if (diff > 0) {
391
+ if (diff > 0)
666
392
  console.log(
667
393
  chalk3.green(
668
394
  ` \u{1F4C8} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
669
395
  )
670
396
  );
671
- } else if (diff < 0) {
397
+ else if (diff < 0)
672
398
  console.log(
673
399
  chalk3.red(
674
400
  ` \u{1F4C9} Trend: ${diffStr} compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
675
401
  )
676
402
  );
677
- } else {
403
+ else
678
404
  console.log(
679
405
  chalk3.blue(
680
- ` \u2796 Trend: No change compared to ${options.compareTo} (${prevScore} \u2192 ${scoringResult.overall})`
406
+ ` \u2796 Trend: No change (${prevScore} \u2192 ${scoringResult.overall})`
681
407
  )
682
408
  );
683
- }
684
- scoringResult.trend = {
685
- previousScore: prevScore,
686
- difference: diff
687
- };
688
- } else {
689
- console.log(
690
- chalk3.yellow(
691
- `
692
- \u26A0\uFE0F Previous report at ${options.compareTo} does not contain an overall score.`
693
- )
694
- );
695
409
  }
696
410
  } catch (e) {
697
411
  void e;
698
- console.log(
699
- chalk3.yellow(
700
- `
701
- \u26A0\uFE0F Could not read or parse previous report at ${options.compareTo}.`
702
- )
703
- );
704
412
  }
705
413
  }
706
414
  const totalWastedDuplication = (scoringResult.breakdown || []).reduce(
@@ -714,7 +422,8 @@ async function scanAction(directory, options) {
714
422
  const totalContext = Math.max(
715
423
  ...(scoringResult.breakdown || []).map(
716
424
  (s) => s.tokenBudget?.totalContextTokens || 0
717
- )
425
+ ),
426
+ 0
718
427
  );
719
428
  if (totalContext > 0) {
720
429
  const unifiedBudget = calculateTokenBudget({
@@ -725,35 +434,17 @@ async function scanAction(directory, options) {
725
434
  chattiness: 0
726
435
  }
727
436
  });
728
- const targetModel = options.model || "claude-4.6";
729
- const modelPreset = getModelPreset(targetModel);
437
+ const modelPreset = getModelPreset(options.model || "claude-4.6");
730
438
  const costEstimate = estimateCostFromBudget(unifiedBudget, modelPreset);
731
- const barWidth = 20;
732
- const filled = Math.round(unifiedBudget.efficiencyRatio * barWidth);
733
- const bar = chalk3.green("\u2588".repeat(filled)) + chalk3.dim("\u2591".repeat(barWidth - filled));
734
- console.log(chalk3.bold("\n\u{1F4CA} AI Token Budget Analysis (v0.13)"));
735
- console.log(
736
- ` Efficiency: [${bar}] ${(unifiedBudget.efficiencyRatio * 100).toFixed(0)}%`
737
- );
738
- console.log(
739
- ` Total Context: ${chalk3.bold(unifiedBudget.totalContextTokens.toLocaleString())} tokens`
740
- );
439
+ console.log(chalk3.bold("\n\u{1F4CA} AI Token Budget Analysis"));
741
440
  console.log(
742
- ` Wasted Tokens: ${chalk3.red(unifiedBudget.wastedTokens.total.toLocaleString())} (${(unifiedBudget.wastedTokens.total / unifiedBudget.totalContextTokens * 100).toFixed(1)}%)`
441
+ ` Efficiency: ${(unifiedBudget.efficiencyRatio * 100).toFixed(0)}%`
743
442
  );
744
- console.log(` Waste Breakdown:`);
745
443
  console.log(
746
- ` \u2022 Duplication: ${unifiedBudget.wastedTokens.bySource.duplication.toLocaleString()} tokens`
444
+ ` Wasted Tokens: ${chalk3.red(unifiedBudget.wastedTokens.total.toLocaleString())}`
747
445
  );
748
446
  console.log(
749
- ` \u2022 Fragmentation: ${unifiedBudget.wastedTokens.bySource.fragmentation.toLocaleString()} tokens`
750
- );
751
- console.log(
752
- ` Potential Savings: ${chalk3.green(unifiedBudget.potentialRetrievableTokens.toLocaleString())} tokens retrievable`
753
- );
754
- console.log(
755
- `
756
- Est. Monthly Cost (${modelPreset.name}): ${chalk3.bold("$" + costEstimate.total)} [range: $${costEstimate.range[0]}-$${costEstimate.range[1]}]`
447
+ ` Est. Monthly Cost (${modelPreset.name}): ${chalk3.bold("$" + costEstimate.total)}`
757
448
  );
758
449
  scoringResult.tokenBudget = unifiedBudget;
759
450
  scoringResult.costEstimate = {
@@ -761,85 +452,31 @@ async function scanAction(directory, options) {
761
452
  ...costEstimate
762
453
  };
763
454
  }
764
- if (scoringResult.breakdown && scoringResult.breakdown.length > 0) {
455
+ if (scoringResult.breakdown) {
765
456
  console.log(chalk3.bold("\nTool breakdown:"));
766
457
  scoringResult.breakdown.forEach((tool) => {
767
458
  const rating = getRating(tool.score);
768
- const rd = getRatingDisplay(rating);
769
- console.log(
770
- ` - ${tool.toolName}: ${tool.score}/100 (${rating}) ${rd.emoji}`
771
- );
459
+ console.log(` - ${tool.toolName}: ${tool.score}/100 (${rating})`);
772
460
  });
773
- console.log();
774
- if (finalOptions.scoring?.showBreakdown) {
775
- console.log(chalk3.bold("Detailed tool breakdown:"));
776
- scoringResult.breakdown.forEach((tool) => {
777
- console.log(formatToolScore(tool));
778
- });
779
- console.log();
780
- }
781
461
  }
782
462
  }
783
463
  const mapToUnifiedReport = (res, scoring) => {
784
464
  const allResults = [];
785
- let totalFilesSet = /* @__PURE__ */ new Set();
465
+ const totalFilesSet = /* @__PURE__ */ new Set();
786
466
  let criticalCount = 0;
787
467
  let majorCount = 0;
788
- const collect = (spokeRes, defaultType = IssueType.AiSignalClarity) => {
468
+ res.summary.toolsRun.forEach((toolId) => {
469
+ const spokeRes = res[toolId];
789
470
  if (!spokeRes || !spokeRes.results) return;
790
471
  spokeRes.results.forEach((r) => {
791
- const fileName = r.fileName || r.file || "unknown";
792
- totalFilesSet.add(fileName);
793
- const normalizedResult = {
794
- fileName,
795
- issues: [],
796
- metrics: r.metrics || { tokenCost: r.tokenCost || 0 }
797
- };
798
- if (r.issues && Array.isArray(r.issues)) {
799
- r.issues.forEach((i) => {
800
- const normalizedIssue = typeof i === "string" ? {
801
- type: defaultType,
802
- severity: r.severity || Severity.Info,
803
- message: i,
804
- location: { file: fileName, line: 1 }
805
- } : {
806
- type: i.type || defaultType,
807
- severity: i.severity || r.severity || Severity.Info,
808
- message: i.message || String(i),
809
- location: i.location || { file: fileName, line: 1 },
810
- suggestion: i.suggestion
811
- };
812
- if (normalizedIssue.severity === Severity.Critical || normalizedIssue.severity === "critical")
813
- criticalCount++;
814
- if (normalizedIssue.severity === Severity.Major || normalizedIssue.severity === "major")
815
- majorCount++;
816
- normalizedResult.issues.push(normalizedIssue);
817
- });
818
- } else if (r.severity) {
819
- const normalizedIssue = {
820
- type: defaultType,
821
- severity: r.severity,
822
- message: r.message || "General issue",
823
- location: { file: fileName, line: 1 }
824
- };
825
- if (normalizedIssue.severity === Severity.Critical || normalizedIssue.severity === "critical")
826
- criticalCount++;
827
- if (normalizedIssue.severity === Severity.Major || normalizedIssue.severity === "major")
828
- majorCount++;
829
- normalizedResult.issues.push(normalizedIssue);
830
- }
831
- allResults.push(normalizedResult);
472
+ totalFilesSet.add(r.fileName);
473
+ allResults.push(r);
474
+ r.issues?.forEach((i) => {
475
+ if (i.severity === Severity.Critical) criticalCount++;
476
+ if (i.severity === Severity.Major) majorCount++;
477
+ });
832
478
  });
833
- };
834
- collect(res[ToolName.PatternDetect], IssueType.DuplicatePattern);
835
- collect(res[ToolName.ContextAnalyzer], IssueType.ContextFragmentation);
836
- collect(res[ToolName.NamingConsistency], IssueType.NamingInconsistency);
837
- collect(res[ToolName.DocDrift], IssueType.DocDrift);
838
- collect(res[ToolName.DependencyHealth], IssueType.DependencyHealth);
839
- collect(res[ToolName.AiSignalClarity], IssueType.AiSignalClarity);
840
- collect(res[ToolName.AgentGrounding], IssueType.AgentNavigationFailure);
841
- collect(res[ToolName.TestabilityIndex], IssueType.LowTestability);
842
- collect(res[ToolName.ChangeAmplification], IssueType.ChangeAmplification);
479
+ });
843
480
  return {
844
481
  ...res,
845
482
  results: allResults,
@@ -852,199 +489,70 @@ async function scanAction(directory, options) {
852
489
  scoring
853
490
  };
854
491
  };
492
+ const outputData = {
493
+ ...mapToUnifiedReport(results, scoringResult),
494
+ repository: repoMetadata
495
+ };
855
496
  const outputFormat = options.output || finalOptions.output?.format || "console";
856
- const userOutputFile = options.outputFile || finalOptions.output?.file;
497
+ const outputPath = resolveOutputPath(
498
+ options.outputFile || finalOptions.output?.file,
499
+ `aiready-report-${getReportTimestamp()}.json`,
500
+ resolvedDir
501
+ );
857
502
  if (outputFormat === "json") {
858
- const timestamp = getReportTimestamp();
859
- const defaultFilename = `aiready-report-${timestamp}.json`;
860
- const outputPath = resolveOutputPath(
861
- userOutputFile,
862
- defaultFilename,
863
- resolvedDir
864
- );
865
- const outputData = {
866
- ...mapToUnifiedReport(results, scoringResult),
867
- repository: repoMetadata
868
- };
869
503
  handleJSONOutput(
870
504
  outputData,
871
505
  outputPath,
872
506
  `\u2705 Report saved to ${outputPath}`
873
507
  );
874
- if (options.upload) {
875
- console.log(chalk3.blue("\n\u{1F4E4} Automatic upload triggered..."));
876
- await uploadAction(outputPath, {
877
- apiKey: options.apiKey,
878
- server: options.server
879
- });
880
- }
881
- await warnIfGraphCapExceeded(outputData, resolvedDir);
882
508
  } else {
883
- const timestamp = getReportTimestamp();
884
- const defaultFilename = `aiready-report-${timestamp}.json`;
885
- const outputPath = resolveOutputPath(
886
- userOutputFile,
887
- defaultFilename,
888
- resolvedDir
889
- );
890
- const outputData = {
891
- ...mapToUnifiedReport(results, scoringResult),
892
- repository: repoMetadata
893
- };
894
509
  try {
895
510
  writeFileSync(outputPath, JSON.stringify(outputData, null, 2));
896
511
  console.log(chalk3.dim(`\u2705 Report auto-persisted to ${outputPath}`));
897
- if (options.upload) {
898
- console.log(chalk3.blue("\n\u{1F4E4} Automatic upload triggered..."));
899
- await uploadAction(outputPath, {
900
- apiKey: options.apiKey,
901
- server: options.server
902
- });
903
- }
904
- await warnIfGraphCapExceeded(outputData, resolvedDir);
905
512
  } catch (err) {
906
513
  void err;
907
514
  }
908
515
  }
909
- const isCI = options.ci || process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true";
516
+ if (options.upload) {
517
+ await uploadAction(outputPath, {
518
+ apiKey: options.apiKey,
519
+ server: options.server
520
+ });
521
+ }
522
+ await warnIfGraphCapExceeded(outputData, resolvedDir);
523
+ const isCI = options.ci || process.env.CI === "true";
910
524
  if (isCI && scoringResult) {
911
525
  const threshold = options.threshold ? parseInt(options.threshold) : void 0;
912
526
  const failOnLevel = options.failOn || "critical";
913
- if (process.env.GITHUB_ACTIONS === "true") {
914
- console.log(`
915
- ::group::AI Readiness Score`);
916
- console.log(`score=${scoringResult.overall}`);
917
- if (scoringResult.breakdown) {
918
- scoringResult.breakdown.forEach((tool) => {
919
- console.log(`${tool.toolName}=${tool.score}`);
920
- });
921
- }
922
- console.log("::endgroup::");
923
- if (threshold && scoringResult.overall < threshold) {
924
- console.log(
925
- `::error::AI Readiness Score ${scoringResult.overall} is below threshold ${threshold}`
926
- );
927
- } else if (threshold) {
928
- console.log(
929
- `::notice::AI Readiness Score: ${scoringResult.overall}/100 (threshold: ${threshold})`
930
- );
931
- }
932
- if (results[ToolName.PatternDetect]) {
933
- const criticalPatterns = results[ToolName.PatternDetect].results.flatMap(
934
- (p) => p.issues.filter((i) => i.severity === Severity.Critical)
935
- );
936
- criticalPatterns.slice(0, 10).forEach((issue) => {
937
- console.log(
938
- `::warning file=${issue.location?.file || "unknown"},line=${issue.location?.line || 1}::${issue.message}`
939
- );
940
- });
941
- }
942
- }
943
527
  let shouldFail = false;
944
528
  let failReason = "";
945
529
  if (threshold && scoringResult.overall < threshold) {
946
530
  shouldFail = true;
947
- failReason = `AI Readiness Score ${scoringResult.overall} is below threshold ${threshold}`;
531
+ failReason = `Score ${scoringResult.overall} < threshold ${threshold}`;
948
532
  }
533
+ const report = mapToUnifiedReport(results, scoringResult);
949
534
  if (failOnLevel !== "none") {
950
- const severityLevels = { critical: 4, major: 3, minor: 2, any: 1 };
951
- const minSeverity = severityLevels[failOnLevel] || 4;
952
- let criticalCount = 0;
953
- let majorCount = 0;
954
- if (results[ToolName.PatternDetect]) {
955
- results[ToolName.PatternDetect].results.forEach((p) => {
956
- p.issues.forEach((i) => {
957
- if (i.severity === Severity.Critical) criticalCount++;
958
- if (i.severity === Severity.Major) majorCount++;
959
- });
960
- });
961
- }
962
- if (results[ToolName.ContextAnalyzer]) {
963
- results[ToolName.ContextAnalyzer].results.forEach((c) => {
964
- if (c.severity === Severity.Critical) criticalCount++;
965
- if (c.severity === Severity.Major) majorCount++;
966
- });
967
- }
968
- if (results[ToolName.NamingConsistency]) {
969
- results[ToolName.NamingConsistency].results.forEach((r) => {
970
- r.issues?.forEach((i) => {
971
- if (i.severity === Severity.Critical) criticalCount++;
972
- if (i.severity === Severity.Major) majorCount++;
973
- });
974
- });
975
- }
976
- if (minSeverity >= 4 && criticalCount > 0) {
535
+ if (failOnLevel === "critical" && report.summary.criticalIssues > 0) {
977
536
  shouldFail = true;
978
- failReason = `Found ${criticalCount} critical issues`;
979
- } else if (minSeverity >= 3 && criticalCount + majorCount > 0) {
537
+ failReason = `Found ${report.summary.criticalIssues} critical issues`;
538
+ } else if (failOnLevel === "major" && report.summary.criticalIssues + report.summary.majorIssues > 0) {
980
539
  shouldFail = true;
981
- failReason = `Found ${criticalCount} critical and ${majorCount} major issues`;
540
+ failReason = `Found ${report.summary.criticalIssues} critical and ${report.summary.majorIssues} major issues`;
982
541
  }
983
542
  }
984
543
  if (shouldFail) {
985
- console.log(chalk3.red("\n\u{1F6AB} PR BLOCKED: AI Readiness Check Failed"));
986
- console.log(chalk3.red(` Reason: ${failReason}`));
987
- console.log(chalk3.dim("\n Remediation steps:"));
988
- console.log(
989
- chalk3.dim(" 1. Run `aiready scan` locally to see detailed issues")
990
- );
991
- console.log(chalk3.dim(" 2. Fix the critical issues before merging"));
992
- console.log(
993
- chalk3.dim(
994
- " 3. Consider upgrading to Team plan for historical tracking: https://getaiready.dev/pricing"
995
- )
996
- );
544
+ console.log(chalk3.red(`
545
+ \u{1F6AB} PR BLOCKED: ${failReason}`));
997
546
  process.exit(1);
998
547
  } else {
999
- console.log(chalk3.green("\n\u2705 PR PASSED: AI Readiness Check"));
1000
- if (threshold) {
1001
- console.log(
1002
- chalk3.green(
1003
- ` Score: ${scoringResult.overall}/100 (threshold: ${threshold})`
1004
- )
1005
- );
1006
- }
1007
- console.log(
1008
- chalk3.dim(
1009
- "\n \u{1F4A1} Track historical trends: https://getaiready.dev \u2014 Team plan $99/mo"
1010
- )
1011
- );
548
+ console.log(chalk3.green("\n\u2705 PR PASSED"));
1012
549
  }
1013
550
  }
1014
551
  } catch (error) {
1015
552
  handleCLIError2(error, "Analysis");
1016
553
  }
1017
554
  }
1018
- var scanHelpText = `
1019
- EXAMPLES:
1020
- $ aiready scan # Analyze all tools
1021
- $ aiready scan --tools patterns,context # Skip consistency
1022
- $ aiready scan --profile agentic # Optimize for AI agent execution
1023
- $ aiready scan --profile security # Optimize for secure coding (testability)
1024
- $ aiready scan --compare-to prev-report.json # Compare trends against previous run
1025
- $ aiready scan --score --threshold 75 # CI/CD with threshold
1026
- $ aiready scan --ci --threshold 70 # GitHub Actions gatekeeper
1027
- $ aiready scan --ci --fail-on major # Fail on major+ issues
1028
- $ aiready scan --output json --output-file report.json
1029
- $ aiready scan --upload --api-key ar_... # Automatic platform upload
1030
- $ aiready scan --upload --server custom-url.com # Upload to custom platform
1031
-
1032
- PROFILES:
1033
- agentic: aiSignalClarity, grounding, testability
1034
- cost: patterns, context
1035
- security: consistency, testability
1036
- onboarding: context, consistency, grounding
1037
-
1038
- CI/CD INTEGRATION (Gatekeeper Mode):
1039
- Use --ci for GitHub Actions integration:
1040
- - Outputs GitHub Actions annotations for PR checks
1041
- - Fails with exit code 1 if threshold not met
1042
- - Shows clear "blocked" message with remediation steps
1043
-
1044
- Example GitHub Actions workflow:
1045
- - name: AI Readiness Check
1046
- run: aiready scan --ci --threshold 70
1047
- `;
555
+ var scanHelpText = `...`;
1048
556
 
1049
557
  // src/commands/patterns.ts
1050
558
  import chalk4 from "chalk";