@aiready/cli 0.10.4 → 0.12.0

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