@aiready/cli 0.14.25 → 0.14.26

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.js CHANGED
@@ -33,7 +33,7 @@ var import_url = require("url");
33
33
  var import_chalk6 = __toESM(require("chalk"));
34
34
  var import_fs4 = require("fs");
35
35
  var import_path4 = require("path");
36
- var import_core9 = require("@aiready/core");
36
+ var import_core10 = require("@aiready/core");
37
37
 
38
38
  // src/utils/index.ts
39
39
  var import_core2 = require("@aiready/core");
@@ -45,8 +45,8 @@ var import_chalk = __toESM(require("chalk"));
45
45
  var import_core = require("@aiready/core");
46
46
  async function warnIfGraphCapExceeded(report, dirPath) {
47
47
  try {
48
- const { loadConfig: loadConfig4 } = await import("@aiready/core");
49
- void loadConfig4;
48
+ const { loadConfig: loadConfig2 } = await import("@aiready/core");
49
+ void loadConfig2;
50
50
  let graphConfig = { maxNodes: 400, maxEdges: 600 };
51
51
  const configPath = (0, import_path.resolve)(dirPath, "aiready.json");
52
52
  if ((0, import_fs.existsSync)(configPath)) {
@@ -409,6 +409,62 @@ var import_core6 = require("@aiready/core");
409
409
  // src/commands/scan-helpers.ts
410
410
  var import_chalk4 = __toESM(require("chalk"));
411
411
  var import_core5 = require("@aiready/core");
412
+ async function executeToolAction(directory, options, config) {
413
+ console.log(import_chalk4.default.blue(`${config.emoji} ${config.label}...
414
+ `));
415
+ const startTime = Date.now();
416
+ try {
417
+ const { resolvedDir, finalOptions: baseOptions } = await (0, import_core5.prepareActionConfig)(
418
+ directory,
419
+ config.defaults,
420
+ config.getCliOptions(options)
421
+ );
422
+ let finalOptions = baseOptions;
423
+ if (config.preAnalyze) {
424
+ finalOptions = await config.preAnalyze(resolvedDir, finalOptions);
425
+ }
426
+ const { analyze, generateSummary, calculateScore } = await config.importTool();
427
+ const results = await analyze(finalOptions);
428
+ const elapsedTime = (0, import_core5.getElapsedTime)(startTime);
429
+ const summary = generateSummary(results);
430
+ let toolScore;
431
+ if (options.score && calculateScore) {
432
+ const resultsAny = results;
433
+ const scoreData = resultsAny.duplicates || resultsAny.issues || results;
434
+ const filesCount = resultsAny.length || resultsAny.summary?.filesAnalyzed || resultsAny.summary?.totalFiles;
435
+ toolScore = calculateScore(scoreData, filesCount);
436
+ }
437
+ const { format: outputFormat, file: userOutputFile } = (0, import_core5.resolveOutputFormat)(
438
+ options,
439
+ finalOptions
440
+ );
441
+ const outputData = (0, import_core5.formatStandardReport)({
442
+ results,
443
+ summary,
444
+ elapsedTime,
445
+ score: toolScore
446
+ });
447
+ if (outputFormat === "json") {
448
+ (0, import_core5.handleStandardJSONOutput)({
449
+ outputData,
450
+ outputFile: userOutputFile,
451
+ resolvedDir
452
+ });
453
+ } else {
454
+ config.renderConsole({
455
+ results,
456
+ summary,
457
+ elapsedTime,
458
+ score: toolScore,
459
+ finalOptions
460
+ });
461
+ }
462
+ return outputData;
463
+ } catch (error) {
464
+ (0, import_core5.handleCLIError)(error, config.label);
465
+ return void 0;
466
+ }
467
+ }
412
468
  function getProfileTools(profile) {
413
469
  switch (profile.toLowerCase()) {
414
470
  case "agentic":
@@ -532,9 +588,9 @@ async function resolveScanConfig(resolvedDir, options) {
532
588
  var import_chalk5 = __toESM(require("chalk"));
533
589
  var import_fs3 = require("fs");
534
590
  var import_path3 = require("path");
535
- var import_core8 = require("@aiready/core");
591
+ var import_core9 = require("@aiready/core");
536
592
 
537
- // src/index.ts
593
+ // src/orchestrator.ts
538
594
  var import_core7 = require("@aiready/core");
539
595
  var TOOL_PACKAGE_MAP = {
540
596
  [import_core7.ToolName.PatternDetect]: "@aiready/pattern-detect",
@@ -558,216 +614,257 @@ var TOOL_PACKAGE_MAP = {
558
614
  "deps-health": "@aiready/deps",
559
615
  "change-amp": "@aiready/change-amplification"
560
616
  };
561
- function sanitizeConfigRecursive(obj) {
562
- if (!obj || typeof obj !== "object" || Array.isArray(obj)) return obj;
563
- const sanitized = {};
564
- const infraToStrip = [
565
- "rootDir",
566
- "onProgress",
567
- "progressCallback",
568
- "streamResults",
569
- "batchSize",
570
- "useSmartDefaults"
571
- ];
572
- for (const [key, value] of Object.entries(obj)) {
573
- if (infraToStrip.includes(key)) continue;
574
- if (typeof value === "object" && value !== null && !Array.isArray(value)) {
575
- sanitized[key] = sanitizeConfigRecursive(value);
576
- } else {
577
- sanitized[key] = value;
578
- }
617
+ var UnifiedOrchestrator = class {
618
+ /**
619
+ * Initialize orchestrator with a tool registry.
620
+ * Injection pattern helps with testability and AI readiness score.
621
+ */
622
+ constructor(registry = import_core7.ToolRegistry) {
623
+ this.registry = registry;
579
624
  }
580
- return sanitized;
581
- }
582
- function sanitizeToolConfig(config) {
583
- return sanitizeConfigRecursive(config);
584
- }
585
- async function analyzeUnified(options) {
586
- await (0, import_core7.initializeParsers)();
587
- const startTime = Date.now();
588
- const requestedTools = options.tools ?? [
589
- "patterns",
590
- "context",
591
- "consistency"
592
- ];
593
- const result = {
594
- summary: {
595
- totalIssues: 0,
596
- criticalIssues: 0,
597
- // Added as per instruction
598
- majorIssues: 0,
599
- // Added as per instruction
600
- totalFiles: 0,
601
- toolsRun: [],
602
- executionTime: 0,
603
- config: options,
604
- toolConfigs: {}
625
+ /**
626
+ * Deeply sanitizes a configuration object.
627
+ */
628
+ sanitizeConfig(obj) {
629
+ if (!obj || typeof obj !== "object" || Array.isArray(obj)) return obj;
630
+ const sanitized = {};
631
+ const infraToStrip = [
632
+ "rootDir",
633
+ "onProgress",
634
+ "progressCallback",
635
+ "streamResults",
636
+ "batchSize",
637
+ "useSmartDefaults"
638
+ ];
639
+ for (const [key, value] of Object.entries(obj)) {
640
+ if (infraToStrip.includes(key)) continue;
641
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
642
+ sanitized[key] = this.sanitizeConfig(value);
643
+ } else {
644
+ sanitized[key] = value;
645
+ }
605
646
  }
606
- };
607
- for (const toolName of requestedTools) {
608
- let provider = import_core7.ToolRegistry.find(toolName);
609
- if (!provider) {
610
- const packageName = TOOL_PACKAGE_MAP[toolName] ?? (toolName.startsWith("@aiready/") ? toolName : `@aiready/${toolName}`);
611
- try {
612
- await import(packageName);
613
- provider = import_core7.ToolRegistry.find(toolName);
614
- if (provider) {
615
- console.log(
616
- `\u2705 Successfully loaded tool provider: ${toolName} from ${packageName}`
617
- );
618
- } else {
647
+ return sanitized;
648
+ }
649
+ /**
650
+ * Performs the unified analysis.
651
+ */
652
+ async analyze(options) {
653
+ await (0, import_core7.initializeParsers)();
654
+ const startTime = Date.now();
655
+ const requestedTools = options.tools ?? [
656
+ "patterns",
657
+ "context",
658
+ "consistency"
659
+ ];
660
+ const result = {
661
+ summary: {
662
+ totalIssues: 0,
663
+ criticalIssues: 0,
664
+ majorIssues: 0,
665
+ totalFiles: 0,
666
+ toolsRun: [],
667
+ executionTime: 0,
668
+ config: options,
669
+ toolConfigs: {}
670
+ }
671
+ };
672
+ for (const toolName of requestedTools) {
673
+ let provider = this.registry.find(toolName);
674
+ if (!provider) {
675
+ const packageName = TOOL_PACKAGE_MAP[toolName] ?? (toolName.startsWith("@aiready/") ? toolName : `@aiready/${toolName}`);
676
+ try {
677
+ await import(packageName);
678
+ provider = this.registry.find(toolName);
679
+ } catch (err) {
619
680
  console.log(
620
- `\u26A0\uFE0F Loaded ${packageName} but provider ${toolName} still not found in registry.`
681
+ `\u274C Failed to dynamically load tool ${toolName} (${packageName}):`,
682
+ err.message
621
683
  );
622
684
  }
623
- } catch (err) {
624
- console.log(
625
- `\u274C Failed to dynamically load tool ${toolName} (${packageName}):`,
626
- err.message
685
+ }
686
+ if (!provider) {
687
+ console.warn(
688
+ `\u26A0\uFE0F Warning: Tool provider for '${toolName}' not found. Skipping.`
627
689
  );
690
+ continue;
628
691
  }
629
- }
630
- if (!provider) {
631
- console.warn(
632
- `\u26A0\uFE0F Warning: Tool provider for '${toolName}' not found. Skipping.`
633
- );
634
- continue;
635
- }
636
- try {
637
- const sanitizedOptions = { ...options };
638
- delete sanitizedOptions.onProgress;
639
- delete sanitizedOptions.progressCallback;
640
- const toolOptions = {
641
- rootDir: options.rootDir
642
- // Always include rootDir
643
- };
644
- [...import_core7.GLOBAL_INFRA_OPTIONS, ...import_core7.COMMON_FINE_TUNING_OPTIONS].forEach(
645
- (key) => {
646
- if (key in options && key !== "toolConfigs" && key !== "tools") {
647
- toolOptions[key] = options[key];
692
+ try {
693
+ const toolOptions = {
694
+ rootDir: options.rootDir
695
+ };
696
+ [...import_core7.GLOBAL_INFRA_OPTIONS, ...import_core7.COMMON_FINE_TUNING_OPTIONS].forEach(
697
+ (key) => {
698
+ if (key in options && key !== "toolConfigs" && key !== "tools") {
699
+ toolOptions[key] = options[key];
700
+ }
648
701
  }
702
+ );
703
+ if (options.toolConfigs?.[provider.id]) {
704
+ Object.assign(toolOptions, options.toolConfigs[provider.id]);
705
+ } else if (options.tools && !Array.isArray(options.tools) && typeof options.tools === "object" && options.tools[provider.id]) {
706
+ Object.assign(toolOptions, options.tools[provider.id]);
707
+ }
708
+ toolOptions.onProgress = (processed, total, msg) => {
709
+ if (options.progressCallback) {
710
+ options.progressCallback({
711
+ tool: provider.id,
712
+ processed,
713
+ total,
714
+ message: msg
715
+ });
716
+ }
717
+ };
718
+ const output = await provider.analyze(toolOptions);
719
+ if (output.metadata) {
720
+ output.metadata.config = this.sanitizeConfig(toolOptions);
649
721
  }
650
- );
651
- if (options.toolConfigs?.[provider.id]) {
652
- Object.assign(toolOptions, options.toolConfigs[provider.id]);
653
- } else if (options.tools && !Array.isArray(options.tools) && typeof options.tools === "object" && options.tools[provider.id]) {
654
- Object.assign(toolOptions, options.tools[provider.id]);
655
- } else if (options[provider.id]) {
656
- Object.assign(toolOptions, options[provider.id]);
657
- }
658
- toolOptions.onProgress = (processed, total, message) => {
659
722
  if (options.progressCallback) {
660
- options.progressCallback({
661
- tool: provider.id,
662
- processed,
663
- total,
664
- message
665
- });
723
+ options.progressCallback({ tool: provider.id, data: output });
666
724
  }
667
- };
668
- const output = await provider.analyze(toolOptions);
669
- if (output.metadata) {
670
- output.metadata.config = sanitizeToolConfig(toolOptions);
671
- }
672
- if (options.progressCallback) {
673
- options.progressCallback({ tool: provider.id, data: output });
674
- }
675
- result[provider.id] = output;
676
- result.summary.toolsRun.push(provider.id);
677
- if (output.summary?.config) {
678
- result.summary.toolConfigs[provider.id] = sanitizeToolConfig(
679
- output.summary.config
680
- );
681
- } else if (output.metadata?.config) {
682
- result.summary.toolConfigs[provider.id] = sanitizeToolConfig(
683
- output.metadata.config
725
+ result[provider.id] = output;
726
+ result.summary.toolsRun.push(provider.id);
727
+ const toolConfig = output.summary?.config ?? output.metadata?.config ?? toolOptions;
728
+ result.summary.toolConfigs[provider.id] = this.sanitizeConfig(toolConfig);
729
+ const toolFiles = output.summary?.totalFiles ?? output.summary?.filesAnalyzed ?? 0;
730
+ if (toolFiles > result.summary.totalFiles) {
731
+ result.summary.totalFiles = toolFiles;
732
+ }
733
+ const issueCount = output.results.reduce(
734
+ (sum, file) => sum + (file.issues?.length ?? 0),
735
+ 0
684
736
  );
685
- } else {
686
- result.summary.toolConfigs[provider.id] = sanitizeToolConfig(toolOptions);
687
- }
688
- const toolFiles = output.summary?.totalFiles ?? output.summary?.filesAnalyzed ?? 0;
689
- if (toolFiles > result.summary.totalFiles) {
690
- result.summary.totalFiles = toolFiles;
737
+ result.summary.totalIssues += issueCount;
738
+ } catch (err) {
739
+ console.error(`\u274C Error running tool '${provider.id}':`, err);
691
740
  }
692
- const issueCount = output.results.reduce(
693
- (sum, file) => sum + (file.issues?.length ?? 0),
694
- 0
695
- );
696
- result.summary.totalIssues += issueCount;
697
- } catch (err) {
698
- console.error(`\u274C Error running tool '${provider.id}':`, err);
699
741
  }
742
+ result.summary.config = this.sanitizeConfig({
743
+ scan: {
744
+ tools: requestedTools,
745
+ include: options.include,
746
+ exclude: options.exclude
747
+ },
748
+ tools: result.summary.toolConfigs
749
+ });
750
+ result.summary.executionTime = Date.now() - startTime;
751
+ this.applyLegacyKeys(result);
752
+ return result;
700
753
  }
701
- result.summary.config = sanitizeConfigRecursive({
702
- scan: {
703
- tools: requestedTools,
704
- include: options.include,
705
- exclude: options.exclude
706
- },
707
- // Use 'tools' for tool-specific configurations to match AIReadyConfig
708
- tools: result.summary.toolConfigs
709
- });
710
- result.summary.executionTime = Date.now() - startTime;
711
- const keyMappings = {
712
- "pattern-detect": ["patternDetect", "patterns"],
713
- "context-analyzer": ["contextAnalyzer", "context"],
714
- "naming-consistency": ["namingConsistency", "consistency"],
715
- "ai-signal-clarity": ["aiSignalClarity"],
716
- "agent-grounding": ["agentGrounding"],
717
- "testability-index": ["testabilityIndex", "testability"],
718
- "doc-drift": ["docDrift"],
719
- "dependency-health": ["dependencyHealth", "deps"],
720
- "change-amplification": ["changeAmplification"]
721
- };
722
- for (const [kebabKey, aliases] of Object.entries(keyMappings)) {
723
- if (result[kebabKey]) {
724
- for (const alias of aliases) {
725
- result[alias] = result[kebabKey];
754
+ applyLegacyKeys(result) {
755
+ const keyMappings = {
756
+ "pattern-detect": ["patternDetect", "patterns"],
757
+ "context-analyzer": ["contextAnalyzer", "context"],
758
+ "naming-consistency": ["namingConsistency", "consistency"],
759
+ "ai-signal-clarity": ["aiSignalClarity"],
760
+ "agent-grounding": ["agentGrounding"],
761
+ "testability-index": ["testabilityIndex", "testability"],
762
+ "doc-drift": ["docDrift"],
763
+ "dependency-health": ["dependencyHealth", "deps"],
764
+ "change-amplification": ["changeAmplification"]
765
+ };
766
+ for (const [kebabKey, aliases] of Object.entries(keyMappings)) {
767
+ if (result[kebabKey]) {
768
+ for (const alias of aliases) {
769
+ result[alias] = result[kebabKey];
770
+ }
726
771
  }
727
772
  }
728
773
  }
729
- return result;
774
+ };
775
+ async function analyzeUnified(options) {
776
+ const orchestrator = new UnifiedOrchestrator(import_core7.ToolRegistry);
777
+ return orchestrator.analyze(options);
730
778
  }
731
- async function scoreUnified(results, options) {
732
- const toolScores = /* @__PURE__ */ new Map();
733
- for (const toolId of results.summary.toolsRun) {
734
- const provider = import_core7.ToolRegistry.get(toolId);
735
- if (!provider) continue;
736
- const output = results[toolId];
737
- if (!output) continue;
738
- try {
739
- const toolScore = provider.score(output, options);
740
- if (!toolScore.tokenBudget) {
741
- if (toolId === import_core7.ToolName.PatternDetect && output.duplicates) {
742
- const wastedTokens = output.duplicates.reduce(
743
- (sum, d) => sum + (d.tokenCost ?? 0),
744
- 0
745
- );
746
- toolScore.tokenBudget = (0, import_core7.calculateTokenBudget)({
747
- totalContextTokens: wastedTokens * 2,
748
- wastedTokens: {
749
- duplication: wastedTokens,
750
- fragmentation: 0,
751
- chattiness: 0
752
- }
753
- });
754
- } else if (toolId === import_core7.ToolName.ContextAnalyzer && output.summary) {
755
- toolScore.tokenBudget = (0, import_core7.calculateTokenBudget)({
756
- totalContextTokens: output.summary.totalTokens,
757
- wastedTokens: {
758
- duplication: 0,
759
- fragmentation: output.summary.totalPotentialSavings ?? 0,
760
- chattiness: 0
761
- }
762
- });
779
+
780
+ // src/scoring-orchestrator.ts
781
+ var import_core8 = require("@aiready/core");
782
+ var ScoringOrchestrator = class {
783
+ /**
784
+ * Initialize scoring orchestrator with a tool registry.
785
+ * Injection pattern helps with testability and AI readiness score.
786
+ */
787
+ constructor(registry = import_core8.ToolRegistry) {
788
+ this.registry = registry;
789
+ }
790
+ /**
791
+ * Calculates scores for all analyzed tools.
792
+ */
793
+ async score(results, options) {
794
+ const toolScores = /* @__PURE__ */ new Map();
795
+ for (const toolId of results.summary.toolsRun) {
796
+ const provider = this.registry.get(toolId);
797
+ if (!provider) continue;
798
+ const output = results[toolId];
799
+ if (!output) continue;
800
+ try {
801
+ const toolScore = provider.score(output, options);
802
+ if (!toolScore.tokenBudget) {
803
+ if (toolId === import_core8.ToolName.PatternDetect && output.duplicates) {
804
+ const wastedTokens = output.duplicates.reduce(
805
+ (sum, d) => sum + (d.tokenCost ?? 0),
806
+ 0
807
+ );
808
+ toolScore.tokenBudget = (0, import_core8.calculateTokenBudget)({
809
+ totalContextTokens: wastedTokens * 2,
810
+ wastedTokens: {
811
+ duplication: wastedTokens,
812
+ fragmentation: 0,
813
+ chattiness: 0
814
+ }
815
+ });
816
+ } else if (toolId === import_core8.ToolName.ContextAnalyzer && output.summary) {
817
+ toolScore.tokenBudget = (0, import_core8.calculateTokenBudget)({
818
+ totalContextTokens: output.summary.totalTokens,
819
+ wastedTokens: {
820
+ duplication: 0,
821
+ fragmentation: output.summary.totalPotentialSavings ?? 0,
822
+ chattiness: 0
823
+ }
824
+ });
825
+ }
763
826
  }
827
+ toolScores.set(toolId, toolScore);
828
+ } catch (err) {
829
+ console.error(`\u274C Error scoring tool '${toolId}':`, err);
830
+ }
831
+ }
832
+ if (toolScores.size === 0) {
833
+ return this.emptyScoringResult();
834
+ }
835
+ return (0, import_core8.calculateOverallScore)(toolScores, options, void 0);
836
+ }
837
+ /**
838
+ * Generate human-readable summary of unified results.
839
+ */
840
+ generateSummary(result) {
841
+ const { summary } = result;
842
+ let output = `\u{1F680} AIReady Analysis Complete
843
+
844
+ `;
845
+ output += `\u{1F4CA} Summary:
846
+ `;
847
+ output += ` Tools run: ${summary.toolsRun.join(", ")}
848
+ `;
849
+ output += ` Total issues found: ${summary.totalIssues}
850
+ `;
851
+ output += ` Execution time: ${(summary.executionTime / 1e3).toFixed(2)}s
852
+
853
+ `;
854
+ for (const provider of this.registry.getAll()) {
855
+ const toolResult = result[provider.id];
856
+ if (toolResult) {
857
+ const issueCount = toolResult.results.reduce(
858
+ (sum, r) => sum + (r.issues?.length ?? 0),
859
+ 0
860
+ );
861
+ output += `\u2022 ${provider.id}: ${issueCount} issues
862
+ `;
764
863
  }
765
- toolScores.set(toolId, toolScore);
766
- } catch (err) {
767
- console.error(`\u274C Error scoring tool '${toolId}':`, err);
768
864
  }
865
+ return output;
769
866
  }
770
- if (toolScores.size === 0) {
867
+ emptyScoringResult() {
771
868
  return {
772
869
  overall: 0,
773
870
  rating: "Critical",
@@ -781,7 +878,10 @@ async function scoreUnified(results, options) {
781
878
  }
782
879
  };
783
880
  }
784
- return (0, import_core7.calculateOverallScore)(toolScores, options, void 0);
881
+ };
882
+ async function scoreUnified(results, options) {
883
+ const orchestrator = new ScoringOrchestrator(import_core8.ToolRegistry);
884
+ return orchestrator.score(results, options);
785
885
  }
786
886
 
787
887
  // src/commands/scan-orchestrator.ts
@@ -870,7 +970,7 @@ async function handleBusinessImpactMetrics(results, scoringResult, modelId) {
870
970
  0
871
971
  );
872
972
  if (totalContext > 0) {
873
- const unifiedBudget = (0, import_core8.calculateTokenBudget)({
973
+ const unifiedBudget = (0, import_core9.calculateTokenBudget)({
874
974
  totalContextTokens: totalContext,
875
975
  wastedTokens: {
876
976
  duplication: totalWastedDuplication,
@@ -911,7 +1011,7 @@ async function scanAction(directory, options) {
911
1011
  console.log(import_chalk6.default.blue("\u{1F680} Starting AIReady unified analysis...\n"));
912
1012
  const startTime = Date.now();
913
1013
  const resolvedDir = (0, import_path4.resolve)(process.cwd(), directory ?? ".");
914
- const repoMetadata = (0, import_core9.getRepoMetadata)(resolvedDir);
1014
+ const repoMetadata = (0, import_core10.getRepoMetadata)(resolvedDir);
915
1015
  try {
916
1016
  const finalOptions = await resolveScanConfig(resolvedDir, options);
917
1017
  const { results, scoringResult } = await runUnifiedScan(
@@ -942,13 +1042,13 @@ async function scanAction(directory, options) {
942
1042
  repository: repoMetadata
943
1043
  };
944
1044
  const outputFormat = options.output ?? finalOptions.output?.format ?? "console";
945
- const outputPath = (0, import_core9.resolveOutputPath)(
1045
+ const outputPath = (0, import_core10.resolveOutputPath)(
946
1046
  options.outputFile ?? finalOptions.output?.file,
947
1047
  `aiready-report-${(0, import_core2.getReportTimestamp)()}.json`,
948
1048
  resolvedDir
949
1049
  );
950
1050
  if (outputFormat === "json") {
951
- (0, import_core9.handleJSONOutput)(
1051
+ (0, import_core10.handleJSONOutput)(
952
1052
  outputData,
953
1053
  outputPath,
954
1054
  `\u2705 Report saved to ${outputPath}`
@@ -968,15 +1068,21 @@ async function scanAction(directory, options) {
968
1068
  });
969
1069
  }
970
1070
  await warnIfGraphCapExceeded(outputData, resolvedDir);
971
- await handleGatekeeper(outputData, scoringResult, options, results);
1071
+ await handleGatekeeper(
1072
+ outputData,
1073
+ scoringResult,
1074
+ options,
1075
+ finalOptions,
1076
+ results
1077
+ );
972
1078
  } catch (error) {
973
- (0, import_core9.handleCLIError)(error, "Analysis");
1079
+ (0, import_core10.handleCLIError)(error, "Analysis");
974
1080
  }
975
1081
  }
976
- async function handleGatekeeper(outputData, scoringResult, options, results) {
1082
+ async function handleGatekeeper(outputData, scoringResult, options, finalOptions, results) {
977
1083
  if (!scoringResult) return;
978
- const threshold = options.threshold ? parseInt(options.threshold) : void 0;
979
- const failOnLevel = options.failOn ?? "critical";
1084
+ const threshold = options.threshold ? parseInt(options.threshold) : finalOptions.threshold;
1085
+ const failOnLevel = options.failOn ?? finalOptions.failOn ?? "critical";
980
1086
  const isCI = options.ci ?? process.env.CI === "true";
981
1087
  let shouldFail = false;
982
1088
  let failReason = "";
@@ -988,7 +1094,7 @@ async function handleGatekeeper(outputData, scoringResult, options, results) {
988
1094
  \u{1F4DD} Emitting GitHub Action annotations for ${report.results.length} issues...`
989
1095
  )
990
1096
  );
991
- (0, import_core9.emitIssuesAsAnnotations)(report.results);
1097
+ (0, import_core10.emitIssuesAsAnnotations)(report.results);
992
1098
  }
993
1099
  if (threshold && scoringResult.overall < threshold) {
994
1100
  shouldFail = true;
@@ -1037,7 +1143,7 @@ ${import_chalk6.default.bold("CI/CD Integration:")}
1037
1143
  var import_fs5 = require("fs");
1038
1144
  var import_path5 = require("path");
1039
1145
  var import_chalk7 = __toESM(require("chalk"));
1040
- var import_core10 = require("@aiready/core");
1146
+ var import_core11 = require("@aiready/core");
1041
1147
  async function initAction(options) {
1042
1148
  const fileExt = options.format === "js" ? "js" : "json";
1043
1149
  const fileName = fileExt === "js" ? "aiready.config.js" : "aiready.json";
@@ -1068,15 +1174,15 @@ async function initAction(options) {
1068
1174
  "**/*.spec.ts"
1069
1175
  ],
1070
1176
  tools: [
1071
- import_core10.ToolName.PatternDetect,
1072
- import_core10.ToolName.ContextAnalyzer,
1073
- import_core10.ToolName.NamingConsistency,
1074
- import_core10.ToolName.AiSignalClarity,
1075
- import_core10.ToolName.AgentGrounding,
1076
- import_core10.ToolName.TestabilityIndex,
1077
- import_core10.ToolName.DocDrift,
1078
- import_core10.ToolName.DependencyHealth,
1079
- import_core10.ToolName.ChangeAmplification
1177
+ import_core11.ToolName.PatternDetect,
1178
+ import_core11.ToolName.ContextAnalyzer,
1179
+ import_core11.ToolName.NamingConsistency,
1180
+ import_core11.ToolName.AiSignalClarity,
1181
+ import_core11.ToolName.AgentGrounding,
1182
+ import_core11.ToolName.TestabilityIndex,
1183
+ import_core11.ToolName.DocDrift,
1184
+ import_core11.ToolName.DependencyHealth,
1185
+ import_core11.ToolName.ChangeAmplification
1080
1186
  ]
1081
1187
  },
1082
1188
  // Output preferences
@@ -1091,7 +1197,7 @@ async function initAction(options) {
1091
1197
  },
1092
1198
  // Tool-specific configurations
1093
1199
  tools: {
1094
- [import_core10.ToolName.PatternDetect]: {
1200
+ [import_core11.ToolName.PatternDetect]: {
1095
1201
  // Core detection thresholds
1096
1202
  minSimilarity: 0.4,
1097
1203
  // Jaccard similarity threshold (0-1)
@@ -1121,7 +1227,7 @@ async function initAction(options) {
1121
1227
  // Add any additional advanced options here
1122
1228
  } : {}
1123
1229
  },
1124
- [import_core10.ToolName.ContextAnalyzer]: {
1230
+ [import_core11.ToolName.ContextAnalyzer]: {
1125
1231
  // Smart defaults are generated dynamically based on repository size
1126
1232
  // These are fallback values for when smart defaults can't be calculated
1127
1233
  maxContextBudget: 25e3,
@@ -1138,7 +1244,7 @@ async function initAction(options) {
1138
1244
  includeNodeModules: false
1139
1245
  // Whether to include node_modules in analysis
1140
1246
  },
1141
- [import_core10.ToolName.NamingConsistency]: {
1247
+ [import_core11.ToolName.NamingConsistency]: {
1142
1248
  // Core checks
1143
1249
  checkNaming: true,
1144
1250
  // Check naming conventions and quality
@@ -1185,7 +1291,7 @@ async function initAction(options) {
1185
1291
  ],
1186
1292
  ...options.full ? { disableChecks: [] } : {}
1187
1293
  },
1188
- [import_core10.ToolName.AiSignalClarity]: {
1294
+ [import_core11.ToolName.AiSignalClarity]: {
1189
1295
  // All signal clarity checks enabled by default
1190
1296
  checkMagicLiterals: true,
1191
1297
  // Detect magic numbers and strings
@@ -1204,7 +1310,7 @@ async function initAction(options) {
1204
1310
  checkLargeFiles: true
1205
1311
  // Detect files that are too large
1206
1312
  },
1207
- [import_core10.ToolName.AgentGrounding]: {
1313
+ [import_core11.ToolName.AgentGrounding]: {
1208
1314
  // Structure clarity
1209
1315
  maxRecommendedDepth: 4,
1210
1316
  // Max directory depth before flagging as "too deep"
@@ -1215,7 +1321,7 @@ async function initAction(options) {
1215
1321
  additionalVagueNames: ["stuff", "misc", "temp", "test"]
1216
1322
  // Custom vague file names
1217
1323
  },
1218
- [import_core10.ToolName.TestabilityIndex]: {
1324
+ [import_core11.ToolName.TestabilityIndex]: {
1219
1325
  // Coverage thresholds
1220
1326
  minCoverageRatio: 0.3,
1221
1327
  // Minimum acceptable test/source ratio
@@ -1225,19 +1331,19 @@ async function initAction(options) {
1225
1331
  maxDepth: 10
1226
1332
  // Maximum scan depth
1227
1333
  },
1228
- [import_core10.ToolName.DocDrift]: {
1334
+ [import_core11.ToolName.DocDrift]: {
1229
1335
  // Drift detection
1230
1336
  maxCommits: 50,
1231
1337
  // Maximum commit distance to check for drift
1232
1338
  staleMonths: 3
1233
1339
  // Consider comments older than this as outdated
1234
1340
  },
1235
- [import_core10.ToolName.DependencyHealth]: {
1341
+ [import_core11.ToolName.DependencyHealth]: {
1236
1342
  // Training cutoff for AI knowledge assessment
1237
1343
  trainingCutoffYear: 2023
1238
1344
  // Year cutoff for AI training data
1239
1345
  },
1240
- [import_core10.ToolName.ChangeAmplification]: {
1346
+ [import_core11.ToolName.ChangeAmplification]: {
1241
1347
  // Change amplification primarily relies on global scan settings
1242
1348
  // No additional tool-specific configuration required
1243
1349
  }
@@ -1281,134 +1387,157 @@ module.exports = ${JSON.stringify(defaultConfig, null, 2)};
1281
1387
  }
1282
1388
 
1283
1389
  // src/commands/patterns.ts
1390
+ var import_chalk9 = __toESM(require("chalk"));
1391
+ var import_core13 = require("@aiready/core");
1392
+
1393
+ // src/utils/terminal-renderers.ts
1284
1394
  var import_chalk8 = __toESM(require("chalk"));
1285
- var import_core11 = require("@aiready/core");
1286
- async function patternsAction(directory, options) {
1287
- console.log(import_chalk8.default.blue("\u{1F50D} Analyzing patterns...\n"));
1288
- const startTime = Date.now();
1289
- try {
1290
- const useSmartDefaults = !options.fullScan;
1291
- const defaults = {
1292
- useSmartDefaults,
1293
- include: void 0,
1294
- exclude: void 0,
1295
- output: {
1296
- format: "console",
1297
- file: void 0
1298
- }
1299
- };
1300
- if (!useSmartDefaults) {
1301
- defaults.minSimilarity = 0.4;
1302
- defaults.minLines = 5;
1303
- }
1304
- const cliOptions = {
1305
- minSimilarity: options.similarity ? parseFloat(options.similarity) : void 0,
1306
- minLines: options.minLines ? parseInt(options.minLines) : void 0,
1307
- useSmartDefaults,
1308
- include: options.include?.split(","),
1309
- exclude: options.exclude?.split(",")
1310
- };
1311
- if (options.maxCandidates) {
1312
- cliOptions.maxCandidatesPerBlock = parseInt(options.maxCandidates);
1313
- }
1314
- if (options.minSharedTokens) {
1315
- cliOptions.minSharedTokens = parseInt(options.minSharedTokens);
1316
- }
1317
- const { resolvedDir, finalOptions } = await (0, import_core11.prepareActionConfig)(
1318
- directory,
1319
- defaults,
1320
- cliOptions
1395
+ var import_core12 = require("@aiready/core");
1396
+ var SAFETY_ICONS = {
1397
+ safe: "\u2705",
1398
+ "moderate-risk": "\u26A0\uFE0F ",
1399
+ "high-risk": "\u{1F534}",
1400
+ "blind-risk": "\u{1F480}"
1401
+ };
1402
+ var SAFETY_COLORS = {
1403
+ safe: import_chalk8.default.green,
1404
+ "moderate-risk": import_chalk8.default.yellow,
1405
+ "high-risk": import_chalk8.default.red,
1406
+ "blind-risk": import_chalk8.default.bgRed.white
1407
+ };
1408
+ function renderToolHeader(label, emoji, score, rating) {
1409
+ console.log(
1410
+ ` ${emoji} ${label}: ${import_chalk8.default.bold(score + "/100")} (${rating})`
1411
+ );
1412
+ }
1413
+ function renderSafetyRating(safety) {
1414
+ const icon = SAFETY_ICONS[safety] ?? "\u2753";
1415
+ const color = SAFETY_COLORS[safety] ?? import_chalk8.default.white;
1416
+ console.log(
1417
+ ` AI Change Safety: ${color(`${icon} ${safety.toUpperCase()}`)}`
1418
+ );
1419
+ }
1420
+ function renderIssueSummaryBlock(summary) {
1421
+ const total = (summary.criticalIssues || 0) + (summary.majorIssues || 0) + (summary.minorIssues || 0);
1422
+ if (total === 0) {
1423
+ console.log(import_chalk8.default.green("\u2705 No significant issues found!\n"));
1424
+ return;
1425
+ }
1426
+ console.log(import_chalk8.default.bold("\u26A0\uFE0F Issues Found:\n"));
1427
+ if (summary.criticalIssues > 0)
1428
+ console.log(
1429
+ import_chalk8.default.red(` \u{1F534} Critical: ${import_chalk8.default.bold(summary.criticalIssues)}`)
1321
1430
  );
1322
- const { analyzePatterns, generateSummary, calculatePatternScore } = await import("@aiready/pattern-detect");
1323
- const { results, duplicates } = await analyzePatterns(
1324
- finalOptions
1431
+ if (summary.majorIssues > 0)
1432
+ console.log(
1433
+ import_chalk8.default.yellow(` \u{1F7E1} Major: ${import_chalk8.default.bold(summary.majorIssues)}`)
1325
1434
  );
1326
- const elapsedTime = (0, import_core11.getElapsedTime)(startTime);
1327
- const summary = generateSummary(results);
1328
- let patternScore;
1329
- if (options.score) {
1330
- patternScore = calculatePatternScore(duplicates, results.length);
1331
- }
1332
- const { format: outputFormat, file: userOutputFile } = (0, import_core11.resolveOutputFormat)(
1333
- options,
1334
- finalOptions
1435
+ if (summary.minorIssues > 0)
1436
+ console.log(import_chalk8.default.blue(` \u{1F535} Minor: ${import_chalk8.default.bold(summary.minorIssues)}`));
1437
+ if (summary.totalPotentialSavings) {
1438
+ console.log(
1439
+ import_chalk8.default.green(
1440
+ `
1441
+ \u{1F4A1} Potential savings: ${import_chalk8.default.bold(summary.totalPotentialSavings.toLocaleString())} tokens
1442
+ `
1443
+ )
1335
1444
  );
1336
- if (outputFormat === "json") {
1337
- const outputData = (0, import_core11.formatStandardReport)({
1338
- results,
1339
- summary,
1340
- elapsedTime,
1341
- score: patternScore
1342
- });
1343
- (0, import_core11.handleStandardJSONOutput)({
1344
- outputData,
1345
- outputFile: userOutputFile,
1346
- resolvedDir
1347
- });
1348
- } else {
1349
- (0, import_core11.printTerminalHeader)("PATTERN ANALYSIS SUMMARY");
1445
+ }
1446
+ }
1447
+ function renderSubSection(title) {
1448
+ console.log("\n" + (0, import_core12.getTerminalDivider)());
1449
+ console.log(import_chalk8.default.bold.white(` ${title.toUpperCase()}`));
1450
+ console.log((0, import_core12.getTerminalDivider)() + "\n");
1451
+ }
1452
+ function renderToolScoreFooter(score) {
1453
+ if (score) {
1454
+ console.log(`
1455
+ \u{1F4CA} AI Readiness Score (${score.toolName || "Tool"})
1456
+ `);
1457
+ console.log((0, import_core12.formatToolScore)(score));
1458
+ console.log();
1459
+ }
1460
+ }
1461
+
1462
+ // src/commands/patterns.ts
1463
+ async function patternsAction(directory, options) {
1464
+ return await executeToolAction(directory, options, {
1465
+ toolName: "pattern-detect",
1466
+ label: "Pattern analysis",
1467
+ emoji: "\u{1F50D}",
1468
+ defaults: {
1469
+ useSmartDefaults: !options.fullScan,
1470
+ include: void 0,
1471
+ exclude: void 0,
1472
+ output: { format: "console", file: void 0 },
1473
+ minSimilarity: options.fullScan ? 0.4 : void 0,
1474
+ minLines: options.fullScan ? 5 : void 0
1475
+ },
1476
+ getCliOptions: (opts) => ({
1477
+ minSimilarity: opts.similarity ? parseFloat(opts.similarity) : void 0,
1478
+ minLines: opts.minLines ? parseInt(opts.minLines) : void 0,
1479
+ maxCandidatesPerBlock: opts.maxCandidates ? parseInt(opts.maxCandidates) : void 0,
1480
+ minSharedTokens: opts.minSharedTokens ? parseInt(opts.minSharedTokens) : void 0
1481
+ }),
1482
+ importTool: async () => {
1483
+ const { analyzePatterns, generateSummary, calculatePatternScore } = await import("@aiready/pattern-detect");
1484
+ return {
1485
+ analyze: analyzePatterns,
1486
+ generateSummary,
1487
+ calculateScore: calculatePatternScore
1488
+ };
1489
+ },
1490
+ renderConsole: ({ results, summary, elapsedTime, score }) => {
1491
+ const duplicates = results.duplicates || [];
1492
+ (0, import_core13.printTerminalHeader)("PATTERN ANALYSIS SUMMARY");
1350
1493
  console.log(
1351
- import_chalk8.default.white(`\u{1F4C1} Files analyzed: ${import_chalk8.default.bold(results.length)}`)
1494
+ import_chalk9.default.white(`\u{1F4C1} Files analyzed: ${import_chalk9.default.bold(results.length)}`)
1352
1495
  );
1353
1496
  console.log(
1354
- import_chalk8.default.yellow(
1355
- `\u26A0 Duplicate patterns found: ${import_chalk8.default.bold(summary.totalPatterns)}`
1497
+ import_chalk9.default.yellow(
1498
+ `\u26A0 Duplicate patterns found: ${import_chalk9.default.bold(summary.totalPatterns)}`
1356
1499
  )
1357
1500
  );
1358
1501
  console.log(
1359
- import_chalk8.default.red(
1360
- `\u{1F4B0} Token cost (wasted): ${import_chalk8.default.bold(summary.totalTokenCost.toLocaleString())}`
1502
+ import_chalk9.default.red(
1503
+ `\u{1F4B0} Token cost (wasted): ${import_chalk9.default.bold(summary.totalTokenCost.toLocaleString())}`
1361
1504
  )
1362
1505
  );
1363
1506
  console.log(
1364
- import_chalk8.default.gray(`\u23F1 Analysis time: ${import_chalk8.default.bold(elapsedTime + "s")}`)
1507
+ import_chalk9.default.gray(`\u23F1 Analysis time: ${import_chalk9.default.bold(elapsedTime + "s")}`)
1365
1508
  );
1366
1509
  const sortedTypes = Object.entries(summary.patternsByType || {}).filter(([, count]) => count > 0).sort(([, a], [, b]) => b - a);
1367
1510
  if (sortedTypes.length > 0) {
1368
- console.log("\n" + (0, import_core11.getTerminalDivider)());
1369
- console.log(import_chalk8.default.bold.white(" PATTERNS BY TYPE"));
1370
- console.log((0, import_core11.getTerminalDivider)() + "\n");
1511
+ renderSubSection("Patterns By Type");
1371
1512
  sortedTypes.forEach(([type, count]) => {
1372
- console.log(` ${import_chalk8.default.white(type.padEnd(15))} ${import_chalk8.default.bold(count)}`);
1513
+ console.log(` ${import_chalk9.default.white(type.padEnd(15))} ${import_chalk9.default.bold(count)}`);
1373
1514
  });
1374
1515
  }
1375
1516
  if (summary.totalPatterns > 0 && duplicates.length > 0) {
1376
- console.log("\n" + (0, import_core11.getTerminalDivider)());
1377
- console.log(import_chalk8.default.bold.white(" TOP DUPLICATE PATTERNS"));
1378
- console.log((0, import_core11.getTerminalDivider)() + "\n");
1379
- const topDuplicates = [...duplicates].sort((a, b) => b.similarity - a.similarity).slice(0, 10);
1380
- topDuplicates.forEach((dup) => {
1381
- const severity = dup.similarity > 0.95 ? "CRITICAL" : dup.similarity > 0.9 ? "HIGH" : "MEDIUM";
1382
- const severityIcon = dup.similarity > 0.95 ? "\u{1F534}" : dup.similarity > 0.9 ? "\u{1F7E1}" : "\u{1F535}";
1383
- const file1Name = dup.file1.split("/").pop() || dup.file1;
1384
- const file2Name = dup.file2.split("/").pop() || dup.file2;
1517
+ renderSubSection("Top Duplicate Patterns");
1518
+ [...duplicates].sort((a, b) => b.similarity - a.similarity).slice(0, 10).forEach((dup) => {
1519
+ const isHigh = dup.similarity > 0.9;
1520
+ const icon = dup.similarity > 0.95 ? "\u{1F534}" : isHigh ? "\u{1F7E1}" : "\u{1F535}";
1521
+ const label = dup.similarity > 0.95 ? "CRITICAL" : isHigh ? "HIGH" : "MEDIUM";
1385
1522
  console.log(
1386
- `${severityIcon} ${severity}: ${import_chalk8.default.bold(file1Name)} \u2194 ${import_chalk8.default.bold(file2Name)}`
1523
+ `${icon} ${label}: ${import_chalk9.default.bold(dup.file1.split("/").pop())} \u2194 ${import_chalk9.default.bold(dup.file2.split("/").pop())}`
1387
1524
  );
1388
1525
  console.log(
1389
- ` Similarity: ${import_chalk8.default.bold(Math.round(dup.similarity * 100) + "%")} | Wasted: ${import_chalk8.default.bold(dup.tokenCost.toLocaleString())} tokens each`
1526
+ ` Similarity: ${import_chalk9.default.bold(Math.round(dup.similarity * 100) + "%")} | Wasted: ${import_chalk9.default.bold(dup.tokenCost.toLocaleString())} tokens each`
1390
1527
  );
1391
1528
  console.log(
1392
- ` Lines: ${import_chalk8.default.cyan(dup.line1 + "-" + dup.endLine1)} \u2194 ${import_chalk8.default.cyan(dup.line2 + "-" + dup.endLine2)}
1529
+ ` Lines: ${import_chalk9.default.cyan(dup.line1 + "-" + dup.endLine1)} \u2194 ${import_chalk9.default.cyan(dup.line2 + "-" + dup.endLine2)}
1393
1530
  `
1394
1531
  );
1395
1532
  });
1396
1533
  } else {
1397
1534
  console.log(
1398
- import_chalk8.default.green("\n\u2728 Great! No duplicate patterns detected.\n")
1535
+ import_chalk9.default.green("\n\u2728 Great! No duplicate patterns detected.\n")
1399
1536
  );
1400
1537
  }
1401
- if (patternScore) {
1402
- console.log((0, import_core11.getTerminalDivider)());
1403
- console.log(import_chalk8.default.bold.white(" AI READINESS SCORE (Patterns)"));
1404
- console.log((0, import_core11.getTerminalDivider)() + "\n");
1405
- console.log((0, import_core11.formatToolScore)(patternScore));
1406
- console.log();
1407
- }
1538
+ renderToolScoreFooter(score);
1408
1539
  }
1409
- } catch (error) {
1410
- (0, import_core11.handleCLIError)(error, "Pattern analysis");
1411
- }
1540
+ });
1412
1541
  }
1413
1542
  var PATTERNS_HELP_TEXT = `
1414
1543
  EXAMPLES:
@@ -1418,283 +1547,146 @@ EXAMPLES:
1418
1547
  `;
1419
1548
 
1420
1549
  // src/commands/context.ts
1421
- var import_chalk9 = __toESM(require("chalk"));
1422
- var import_core12 = require("@aiready/core");
1550
+ var import_chalk10 = __toESM(require("chalk"));
1551
+ var import_core14 = require("@aiready/core");
1423
1552
  async function contextAction(directory, options) {
1424
- console.log(import_chalk9.default.blue("\u{1F9E0} Analyzing context costs...\n"));
1425
- const startTime = Date.now();
1426
- try {
1427
- const defaults = {
1553
+ return await executeToolAction(directory, options, {
1554
+ toolName: "context-analyzer",
1555
+ label: "Context analysis",
1556
+ emoji: "\u{1F9E0}",
1557
+ defaults: {
1428
1558
  maxDepth: 5,
1429
1559
  maxContextBudget: 1e4,
1430
1560
  include: void 0,
1431
1561
  exclude: void 0,
1432
- output: {
1433
- format: "console",
1434
- file: void 0
1435
- }
1436
- };
1437
- const { resolvedDir, finalOptions: baseOptions } = await (0, import_core12.prepareActionConfig)(directory, defaults, {
1438
- maxDepth: options.maxDepth ? parseInt(options.maxDepth) : void 0,
1439
- maxContextBudget: options.maxContext ? parseInt(options.maxContext) : void 0,
1440
- include: options.include?.split(","),
1441
- exclude: options.exclude?.split(",")
1442
- });
1443
- let finalOptions = { ...baseOptions };
1444
- const { getSmartDefaults } = await import("@aiready/context-analyzer");
1445
- const contextSmartDefaults = await getSmartDefaults(
1446
- resolvedDir,
1447
- baseOptions
1448
- );
1449
- finalOptions = { ...contextSmartDefaults, ...finalOptions };
1450
- console.log("\u{1F4CB} Configuration:");
1451
- console.log(` Max depth: ${finalOptions.maxDepth}`);
1452
- console.log(` Max context budget: ${finalOptions.maxContextBudget}`);
1453
- console.log(
1454
- ` Min cohesion: ${(finalOptions.minCohesion * 100).toFixed(1)}%`
1455
- );
1456
- console.log(
1457
- ` Max fragmentation: ${(finalOptions.maxFragmentation * 100).toFixed(1)}%`
1458
- );
1459
- console.log(` Analysis focus: ${finalOptions.focus}`);
1460
- console.log("");
1461
- const { analyzeContext, generateSummary, calculateContextScore } = await import("@aiready/context-analyzer");
1462
- const results = await analyzeContext(finalOptions);
1463
- const elapsedTime = (0, import_core12.getElapsedTime)(startTime);
1464
- const summary = generateSummary(results);
1465
- let contextScore;
1466
- if (options.score) {
1467
- contextScore = calculateContextScore(summary);
1468
- }
1469
- const { format: outputFormat, file: userOutputFile } = (0, import_core12.resolveOutputFormat)(
1470
- options,
1471
- finalOptions
1472
- );
1473
- if (outputFormat === "json") {
1474
- const outputData = (0, import_core12.formatStandardReport)({
1475
- results,
1476
- summary,
1477
- elapsedTime,
1478
- score: contextScore
1479
- });
1480
- (0, import_core12.handleStandardJSONOutput)({
1481
- outputData,
1482
- outputFile: userOutputFile,
1483
- resolvedDir
1484
- });
1485
- } else {
1486
- const terminalWidth = process.stdout.columns ?? 80;
1487
- const dividerWidth = Math.min(60, terminalWidth - 2);
1488
- const divider = "\u2501".repeat(dividerWidth);
1489
- console.log(import_chalk9.default.cyan(divider));
1490
- console.log(import_chalk9.default.bold.white(" CONTEXT ANALYSIS SUMMARY"));
1491
- console.log(import_chalk9.default.cyan(divider) + "\n");
1562
+ output: { format: "console", file: void 0 }
1563
+ },
1564
+ getCliOptions: (opts) => ({
1565
+ maxDepth: opts.maxDepth ? parseInt(opts.maxDepth) : void 0,
1566
+ maxContextBudget: opts.maxContext ? parseInt(opts.maxContext) : void 0
1567
+ }),
1568
+ preAnalyze: async (resolvedDir, baseOptions) => {
1569
+ const { getSmartDefaults } = await import("@aiready/context-analyzer");
1570
+ const smartDefaults = await getSmartDefaults(resolvedDir, baseOptions);
1571
+ return { ...smartDefaults, ...baseOptions };
1572
+ },
1573
+ importTool: async () => {
1574
+ const { analyzeContext, generateSummary, calculateContextScore } = await import("@aiready/context-analyzer");
1575
+ return {
1576
+ analyze: analyzeContext,
1577
+ generateSummary,
1578
+ calculateScore: calculateContextScore
1579
+ };
1580
+ },
1581
+ renderConsole: ({ summary, elapsedTime, score }) => {
1582
+ (0, import_core14.printTerminalHeader)("CONTEXT ANALYSIS SUMMARY");
1492
1583
  console.log(
1493
- import_chalk9.default.white(`\u{1F4C1} Files analyzed: ${import_chalk9.default.bold(summary.totalFiles)}`)
1584
+ import_chalk10.default.white(`\u{1F4C1} Files analyzed: ${import_chalk10.default.bold(summary.totalFiles)}`)
1494
1585
  );
1495
1586
  console.log(
1496
- import_chalk9.default.white(
1497
- `\u{1F4CA} Total tokens: ${import_chalk9.default.bold(summary.totalTokens.toLocaleString())}`
1587
+ import_chalk10.default.white(
1588
+ `\u{1F4CA} Total tokens: ${import_chalk10.default.bold(summary.totalTokens.toLocaleString())}`
1498
1589
  )
1499
1590
  );
1500
1591
  console.log(
1501
- import_chalk9.default.yellow(
1502
- `\u{1F4B0} Avg context budget: ${import_chalk9.default.bold(summary.avgContextBudget.toFixed(0))} tokens/file`
1592
+ import_chalk10.default.yellow(
1593
+ `\u{1F4B0} Avg context budget: ${import_chalk10.default.bold(summary.avgContextBudget.toFixed(0))} tokens/file`
1503
1594
  )
1504
1595
  );
1505
1596
  console.log(
1506
- import_chalk9.default.white(`\u23F1 Analysis time: ${import_chalk9.default.bold(elapsedTime + "s")}
1597
+ import_chalk10.default.white(`\u23F1 Analysis time: ${import_chalk10.default.bold(elapsedTime + "s")}
1507
1598
  `)
1508
1599
  );
1509
- const totalIssues = summary.criticalIssues + summary.majorIssues + summary.minorIssues;
1510
- if (totalIssues > 0) {
1511
- console.log(import_chalk9.default.bold("\u26A0\uFE0F Issues Found:\n"));
1512
- if (summary.criticalIssues > 0) {
1513
- console.log(
1514
- import_chalk9.default.red(` \u{1F534} Critical: ${import_chalk9.default.bold(summary.criticalIssues)}`)
1515
- );
1516
- }
1517
- if (summary.majorIssues > 0) {
1518
- console.log(
1519
- import_chalk9.default.yellow(` \u{1F7E1} Major: ${import_chalk9.default.bold(summary.majorIssues)}`)
1520
- );
1521
- }
1522
- if (summary.minorIssues > 0) {
1523
- console.log(
1524
- import_chalk9.default.blue(` \u{1F535} Minor: ${import_chalk9.default.bold(summary.minorIssues)}`)
1525
- );
1526
- }
1527
- console.log(
1528
- import_chalk9.default.green(
1529
- `
1530
- \u{1F4A1} Potential savings: ${import_chalk9.default.bold(summary.totalPotentialSavings.toLocaleString())} tokens
1531
- `
1532
- )
1533
- );
1534
- } else {
1535
- console.log(import_chalk9.default.green("\u2705 No significant issues found!\n"));
1536
- }
1537
- if (summary.deepFiles.length > 0) {
1538
- console.log(import_chalk9.default.bold("\u{1F4CF} Deep Import Chains:\n"));
1539
- console.log(
1540
- import_chalk9.default.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`)
1541
- );
1542
- console.log(
1543
- import_chalk9.default.gray(` Maximum depth: ${summary.maxImportDepth}
1544
- `)
1545
- );
1600
+ renderIssueSummaryBlock(summary);
1601
+ if (summary.deepFiles && summary.deepFiles.length > 0) {
1602
+ renderSubSection("Deep Import Chains");
1546
1603
  summary.deepFiles.slice(0, 10).forEach((item) => {
1547
1604
  const fileName = item.file.split("/").slice(-2).join("/");
1548
1605
  console.log(
1549
- ` ${import_chalk9.default.cyan("\u2192")} ${import_chalk9.default.white(fileName)} ${import_chalk9.default.dim(`(depth: ${item.depth})`)}`
1606
+ ` ${import_chalk10.default.cyan("\u2192")} ${import_chalk10.default.white(fileName)} ${import_chalk10.default.dim(`(depth: ${item.depth})`)}`
1550
1607
  );
1551
1608
  });
1552
- console.log();
1553
1609
  }
1554
- if (summary.fragmentedModules.length > 0) {
1555
- console.log(import_chalk9.default.bold("\u{1F9E9} Fragmented Modules:\n"));
1556
- console.log(
1557
- import_chalk9.default.gray(
1558
- ` Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(0)}%
1559
- `
1560
- )
1561
- );
1610
+ if (summary.fragmentedModules && summary.fragmentedModules.length > 0) {
1611
+ renderSubSection("Fragmented Modules");
1562
1612
  summary.fragmentedModules.slice(0, 10).forEach((module2) => {
1563
1613
  console.log(
1564
- ` ${import_chalk9.default.yellow("\u25CF")} ${import_chalk9.default.white(module2.domain)} - ${import_chalk9.default.dim(`${module2.files.length} files, ${(module2.fragmentationScore * 100).toFixed(0)}% scattered`)}`
1614
+ ` ${import_chalk10.default.yellow("\u25CF")} ${import_chalk10.default.white(module2.domain)} - ${import_chalk10.default.dim(`${module2.files.length} files, ${(module2.fragmentationScore * 100).toFixed(0)}% scattered`)}`
1565
1615
  );
1566
1616
  console.log(
1567
- import_chalk9.default.dim(
1617
+ import_chalk10.default.dim(
1568
1618
  ` Token cost: ${module2.totalTokens.toLocaleString()}, Cohesion: ${(module2.avgCohesion * 100).toFixed(0)}%`
1569
1619
  )
1570
1620
  );
1571
1621
  });
1572
- console.log();
1573
- }
1574
- if (summary.lowCohesionFiles.length > 0) {
1575
- console.log(import_chalk9.default.bold("\u{1F500} Low Cohesion Files:\n"));
1576
- console.log(
1577
- import_chalk9.default.gray(
1578
- ` Average cohesion: ${(summary.avgCohesion * 100).toFixed(0)}%
1579
- `
1580
- )
1581
- );
1582
- summary.lowCohesionFiles.slice(0, 10).forEach((item) => {
1583
- const fileName = item.file.split("/").slice(-2).join("/");
1584
- const scorePercent = (item.score * 100).toFixed(0);
1585
- const color = item.score < 0.4 ? import_chalk9.default.red : import_chalk9.default.yellow;
1586
- console.log(
1587
- ` ${color("\u25CB")} ${import_chalk9.default.white(fileName)} ${import_chalk9.default.dim(`(${scorePercent}% cohesion)`)}`
1588
- );
1589
- });
1590
- console.log();
1591
- }
1592
- if (summary.topExpensiveFiles.length > 0) {
1593
- console.log(import_chalk9.default.bold("\u{1F4B8} Most Expensive Files (Context Budget):\n"));
1594
- summary.topExpensiveFiles.slice(0, 10).forEach((item) => {
1595
- const fileName = item.file.split("/").slice(-2).join("/");
1596
- const severityColor = item.severity === "critical" ? import_chalk9.default.red : item.severity === "major" ? import_chalk9.default.yellow : import_chalk9.default.blue;
1597
- console.log(
1598
- ` ${severityColor("\u25CF")} ${import_chalk9.default.white(fileName)} ${import_chalk9.default.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`
1599
- );
1600
- });
1601
- console.log();
1602
- }
1603
- if (contextScore) {
1604
- console.log(import_chalk9.default.cyan(divider));
1605
- console.log(import_chalk9.default.bold.white(" AI READINESS SCORE (Context)"));
1606
- console.log(import_chalk9.default.cyan(divider) + "\n");
1607
- console.log((0, import_core12.formatToolScore)(contextScore));
1608
- console.log();
1609
1622
  }
1623
+ renderToolScoreFooter(score);
1610
1624
  }
1611
- } catch (error) {
1612
- (0, import_core12.handleCLIError)(error, "Context analysis");
1613
- }
1625
+ });
1614
1626
  }
1615
1627
 
1616
1628
  // src/commands/consistency.ts
1617
- var import_chalk10 = __toESM(require("chalk"));
1629
+ var import_chalk11 = __toESM(require("chalk"));
1618
1630
  var import_fs6 = require("fs");
1619
- var import_core13 = require("@aiready/core");
1631
+ var import_core15 = require("@aiready/core");
1620
1632
  async function consistencyAction(directory, options) {
1621
- console.log(import_chalk10.default.blue("\u{1F50D} Analyzing consistency...\n"));
1622
- const startTime = Date.now();
1623
- try {
1624
- const defaults = {
1633
+ return await executeToolAction(directory, options, {
1634
+ toolName: "naming-consistency",
1635
+ label: "Consistency analysis",
1636
+ emoji: "\u{1F50D}",
1637
+ defaults: {
1625
1638
  checkNaming: true,
1626
1639
  checkPatterns: true,
1627
1640
  minSeverity: "info",
1628
1641
  include: void 0,
1629
1642
  exclude: void 0,
1630
- output: {
1631
- format: "console",
1632
- file: void 0
1633
- }
1634
- };
1635
- const { resolvedDir, finalOptions } = await (0, import_core13.prepareActionConfig)(
1636
- directory,
1637
- defaults,
1638
- {
1639
- checkNaming: options.naming !== false,
1640
- checkPatterns: options.patterns !== false,
1641
- minSeverity: options.minSeverity,
1642
- include: options.include?.split(","),
1643
- exclude: options.exclude?.split(",")
1643
+ output: { format: "console", file: void 0 }
1644
+ },
1645
+ getCliOptions: (opts) => ({
1646
+ checkNaming: opts.naming !== false,
1647
+ checkPatterns: opts.patterns !== false,
1648
+ minSeverity: opts.minSeverity
1649
+ }),
1650
+ importTool: async () => {
1651
+ const { analyzeConsistency, calculateConsistencyScore } = await import("@aiready/consistency");
1652
+ return {
1653
+ analyze: analyzeConsistency,
1654
+ generateSummary: (report) => report.summary,
1655
+ calculateScore: (summary, _resultsCount) => {
1656
+ return calculateConsistencyScore(
1657
+ summary.results?.flatMap((r) => r.issues) ?? [],
1658
+ summary.summary.filesAnalyzed
1659
+ );
1660
+ }
1661
+ };
1662
+ },
1663
+ renderConsole: ({ results, summary, elapsedTime, score, finalOptions }) => {
1664
+ const report = results;
1665
+ const { format: outputFormat, file: userOutputFile } = (0, import_core15.resolveOutputFormat)(options, finalOptions);
1666
+ if (outputFormat === "markdown") {
1667
+ const markdown = generateMarkdownReport(report, elapsedTime);
1668
+ const outputPath = (0, import_core15.resolveOutputPath)(
1669
+ userOutputFile,
1670
+ `aiready-report-${(0, import_core2.getReportTimestamp)()}.md`,
1671
+ directory
1672
+ );
1673
+ (0, import_fs6.writeFileSync)(outputPath, markdown);
1674
+ console.log(import_chalk11.default.green(`\u2705 Report saved to ${outputPath}`));
1675
+ return;
1644
1676
  }
1645
- );
1646
- const { analyzeConsistency, calculateConsistencyScore } = await import("@aiready/consistency");
1647
- const report = await analyzeConsistency(finalOptions);
1648
- const elapsedTime = (0, import_core13.getElapsedTime)(startTime);
1649
- let consistencyScore;
1650
- if (options.score) {
1651
- const issues = report.results?.flatMap((r) => r.issues) ?? [];
1652
- consistencyScore = calculateConsistencyScore(
1653
- issues,
1654
- report.summary.filesAnalyzed
1655
- );
1656
- }
1657
- const { format: outputFormat, file: userOutputFile } = (0, import_core13.resolveOutputFormat)(
1658
- options,
1659
- finalOptions
1660
- );
1661
- if (outputFormat === "json") {
1662
- const outputData = (0, import_core13.formatStandardReport)({
1663
- report,
1664
- summary: report.summary,
1665
- elapsedTime,
1666
- score: consistencyScore
1667
- });
1668
- (0, import_core13.handleStandardJSONOutput)({
1669
- outputData,
1670
- outputFile: userOutputFile,
1671
- resolvedDir
1672
- });
1673
- } else if (outputFormat === "markdown") {
1674
- const markdown = generateMarkdownReport(report, elapsedTime);
1675
- const outputPath = (0, import_core13.resolveOutputPath)(
1676
- userOutputFile,
1677
- `aiready-report-${(0, import_core2.getReportTimestamp)()}.md`,
1678
- resolvedDir
1679
- );
1680
- (0, import_fs6.writeFileSync)(outputPath, markdown);
1681
- console.log(import_chalk10.default.green(`\u2705 Report saved to ${outputPath}`));
1682
- } else {
1683
- console.log(import_chalk10.default.bold("\n\u{1F4CA} Summary\n"));
1684
- console.log(
1685
- `Files Analyzed: ${import_chalk10.default.cyan(report.summary.filesAnalyzed)}`
1686
- );
1687
- console.log(`Total Issues: ${import_chalk10.default.yellow(report.summary.totalIssues)}`);
1688
- console.log(` Naming: ${import_chalk10.default.yellow(report.summary.namingIssues)}`);
1689
- console.log(` Patterns: ${import_chalk10.default.yellow(report.summary.patternIssues)}`);
1677
+ console.log(import_chalk11.default.bold("\n\u{1F4CA} Summary\n"));
1678
+ console.log(`Files Analyzed: ${import_chalk11.default.cyan(summary.filesAnalyzed)}`);
1679
+ console.log(`Total Issues: ${import_chalk11.default.yellow(summary.totalIssues)}`);
1680
+ console.log(` Naming: ${import_chalk11.default.yellow(summary.namingIssues)}`);
1681
+ console.log(` Patterns: ${import_chalk11.default.yellow(summary.patternIssues)}`);
1690
1682
  console.log(
1691
- ` Architecture: ${import_chalk10.default.yellow(report.summary.architectureIssues ?? 0)}`
1683
+ ` Architecture: ${import_chalk11.default.yellow(summary.architectureIssues ?? 0)}`
1692
1684
  );
1693
- console.log(`Analysis Time: ${import_chalk10.default.gray(elapsedTime + "s")}
1685
+ console.log(`Analysis Time: ${import_chalk11.default.gray(elapsedTime + "s")}
1694
1686
  `);
1695
- if (report.summary.totalIssues === 0) {
1687
+ if (summary.totalIssues === 0) {
1696
1688
  console.log(
1697
- import_chalk10.default.green(
1689
+ import_chalk11.default.green(
1698
1690
  "\u2728 No consistency issues found! Your codebase is well-maintained.\n"
1699
1691
  )
1700
1692
  );
@@ -1706,100 +1698,88 @@ async function consistencyAction(directory, options) {
1706
1698
  (r) => r.issues.some((i) => i.category === "patterns")
1707
1699
  );
1708
1700
  if (namingResults.length > 0) {
1709
- console.log(import_chalk10.default.bold("\u{1F3F7}\uFE0F Naming Issues\n"));
1701
+ console.log(import_chalk11.default.bold("\u{1F3F7}\uFE0F Naming Issues\n"));
1710
1702
  let shown = 0;
1711
1703
  for (const namingFileResult of namingResults) {
1712
1704
  if (shown >= 5) break;
1713
1705
  for (const issue of namingFileResult.issues) {
1714
1706
  if (shown >= 5) break;
1715
- const severityColor = issue.severity === "critical" ? import_chalk10.default.red : issue.severity === "major" ? import_chalk10.default.yellow : issue.severity === "minor" ? import_chalk10.default.blue : import_chalk10.default.gray;
1707
+ const severityColor = issue.severity === "critical" ? import_chalk11.default.red : issue.severity === "major" ? import_chalk11.default.yellow : issue.severity === "minor" ? import_chalk11.default.blue : import_chalk11.default.gray;
1716
1708
  console.log(
1717
- `${severityColor(issue.severity.toUpperCase())} ${import_chalk10.default.dim(`${issue.location.file}:${issue.location.line}`)}`
1709
+ `${severityColor(issue.severity.toUpperCase())} ${import_chalk11.default.dim(`${issue.location.file}:${issue.location.line}`)}`
1718
1710
  );
1719
1711
  console.log(` ${issue.message}`);
1720
1712
  if (issue.suggestion) {
1721
1713
  console.log(
1722
- ` ${import_chalk10.default.dim("\u2192")} ${import_chalk10.default.italic(issue.suggestion)}`
1714
+ ` ${import_chalk11.default.dim("\u2192")} ${import_chalk11.default.italic(issue.suggestion)}`
1723
1715
  );
1724
1716
  }
1725
1717
  console.log();
1726
1718
  shown++;
1727
1719
  }
1728
1720
  }
1729
- const remaining = namingResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
1730
- if (remaining > 0) {
1731
- console.log(import_chalk10.default.dim(` ... and ${remaining} more issues
1732
- `));
1733
- }
1734
1721
  }
1735
1722
  if (patternResults.length > 0) {
1736
- console.log(import_chalk10.default.bold("\u{1F504} Pattern Issues\n"));
1723
+ console.log(import_chalk11.default.bold("\u{1F504} Pattern Issues\n"));
1737
1724
  let shown = 0;
1738
1725
  for (const patternFileResult of patternResults) {
1739
1726
  if (shown >= 5) break;
1740
1727
  for (const issue of patternFileResult.issues) {
1741
1728
  if (shown >= 5) break;
1742
- const severityColor = issue.severity === "critical" ? import_chalk10.default.red : issue.severity === "major" ? import_chalk10.default.yellow : issue.severity === "minor" ? import_chalk10.default.blue : import_chalk10.default.gray;
1729
+ const severityColor = issue.severity === "critical" ? import_chalk11.default.red : issue.severity === "major" ? import_chalk11.default.yellow : issue.severity === "minor" ? import_chalk11.default.blue : import_chalk11.default.gray;
1743
1730
  console.log(
1744
- `${severityColor(issue.severity.toUpperCase())} ${import_chalk10.default.dim(`${issue.location.file}:${issue.location.line}`)}`
1731
+ `${severityColor(issue.severity.toUpperCase())} ${import_chalk11.default.dim(`${issue.location.file}:${issue.location.line}`)}`
1745
1732
  );
1746
1733
  console.log(` ${issue.message}`);
1747
1734
  if (issue.suggestion) {
1748
1735
  console.log(
1749
- ` ${import_chalk10.default.dim("\u2192")} ${import_chalk10.default.italic(issue.suggestion)}`
1736
+ ` ${import_chalk11.default.dim("\u2192")} ${import_chalk11.default.italic(issue.suggestion)}`
1750
1737
  );
1751
1738
  }
1752
1739
  console.log();
1753
1740
  shown++;
1754
1741
  }
1755
1742
  }
1756
- const remaining = patternResults.reduce((sum, r) => sum + r.issues.length, 0) - shown;
1757
- if (remaining > 0) {
1758
- console.log(import_chalk10.default.dim(` ... and ${remaining} more issues
1759
- `));
1760
- }
1761
1743
  }
1762
- if (report.recommendations.length > 0) {
1763
- console.log(import_chalk10.default.bold("\u{1F4A1} Recommendations\n"));
1744
+ if (report.recommendations?.length > 0) {
1745
+ console.log(import_chalk11.default.bold("\u{1F4A1} Recommendations\n"));
1764
1746
  report.recommendations.forEach((rec, i) => {
1765
1747
  console.log(`${i + 1}. ${rec}`);
1766
1748
  });
1767
1749
  console.log();
1768
1750
  }
1769
1751
  }
1770
- if (consistencyScore) {
1771
- console.log(import_chalk10.default.bold("\n\u{1F4CA} AI Readiness Score (Consistency)\n"));
1772
- console.log((0, import_core13.formatToolScore)(consistencyScore));
1752
+ if (score) {
1753
+ console.log(import_chalk11.default.bold("\n\u{1F4CA} AI Readiness Score (Consistency)\n"));
1754
+ console.log((0, import_core15.formatToolScore)(score));
1773
1755
  console.log();
1774
1756
  }
1775
1757
  }
1776
- } catch (error) {
1777
- (0, import_core13.handleCLIError)(error, "Consistency analysis");
1778
- }
1758
+ });
1779
1759
  }
1780
1760
 
1781
1761
  // src/commands/visualize.ts
1782
- var import_chalk11 = __toESM(require("chalk"));
1762
+ var import_chalk12 = __toESM(require("chalk"));
1783
1763
  var import_fs7 = require("fs");
1784
1764
  var import_path6 = require("path");
1785
1765
  var import_child_process = require("child_process");
1786
- var import_core14 = require("@aiready/core");
1787
- var import_core15 = require("@aiready/core");
1766
+ var import_core16 = require("@aiready/core");
1767
+ var import_core17 = require("@aiready/core");
1788
1768
  async function visualizeAction(directory, options) {
1789
1769
  try {
1790
1770
  const dirPath = (0, import_path6.resolve)(process.cwd(), directory ?? ".");
1791
1771
  let reportPath = options.report ? (0, import_path6.resolve)(dirPath, options.report) : null;
1792
1772
  if (!reportPath || !(0, import_fs7.existsSync)(reportPath)) {
1793
- const latestScan = (0, import_core15.findLatestReport)(dirPath);
1773
+ const latestScan = (0, import_core17.findLatestReport)(dirPath);
1794
1774
  if (latestScan) {
1795
1775
  reportPath = latestScan;
1796
1776
  console.log(
1797
- import_chalk11.default.dim(`Found latest report: ${latestScan.split("/").pop()}`)
1777
+ import_chalk12.default.dim(`Found latest report: ${latestScan.split("/").pop()}`)
1798
1778
  );
1799
1779
  } else {
1800
- console.error(import_chalk11.default.red("\u274C No AI readiness report found"));
1780
+ console.error(import_chalk12.default.red("\u274C No AI readiness report found"));
1801
1781
  console.log(
1802
- import_chalk11.default.dim(
1782
+ import_chalk12.default.dim(
1803
1783
  `
1804
1784
  Generate a report with:
1805
1785
  aiready scan --output json
@@ -1929,29 +1909,29 @@ Or specify a custom report:
1929
1909
  return;
1930
1910
  } else {
1931
1911
  console.log(
1932
- import_chalk11.default.yellow(
1912
+ import_chalk12.default.yellow(
1933
1913
  "\u26A0\uFE0F Dev server not available (requires local @aiready/visualizer with web assets)."
1934
1914
  )
1935
1915
  );
1936
1916
  console.log(
1937
- import_chalk11.default.cyan(" Falling back to static HTML generation...\n")
1917
+ import_chalk12.default.cyan(" Falling back to static HTML generation...\n")
1938
1918
  );
1939
1919
  useDevMode = false;
1940
1920
  }
1941
1921
  } catch (err) {
1942
1922
  console.error("Failed to start dev server:", err);
1943
1923
  console.log(
1944
- import_chalk11.default.cyan(" Falling back to static HTML generation...\n")
1924
+ import_chalk12.default.cyan(" Falling back to static HTML generation...\n")
1945
1925
  );
1946
1926
  useDevMode = false;
1947
1927
  }
1948
1928
  }
1949
1929
  console.log("Generating HTML...");
1950
- const html = (0, import_core15.generateHTML)(graph);
1930
+ const html = (0, import_core17.generateHTML)(graph);
1951
1931
  const defaultOutput = "visualization.html";
1952
1932
  const outPath = (0, import_path6.resolve)(dirPath, options.output ?? defaultOutput);
1953
1933
  (0, import_fs7.writeFileSync)(outPath, html, "utf8");
1954
- console.log(import_chalk11.default.green(`\u2705 Visualization written to: ${outPath}`));
1934
+ console.log(import_chalk12.default.green(`\u2705 Visualization written to: ${outPath}`));
1955
1935
  if (options.open || options.serve) {
1956
1936
  const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
1957
1937
  if (options.serve) {
@@ -1981,7 +1961,7 @@ Or specify a custom report:
1981
1961
  server.listen(port, () => {
1982
1962
  const addr = `http://localhost:${port}/`;
1983
1963
  console.log(
1984
- import_chalk11.default.cyan(`\u{1F310} Local visualization server running at ${addr}`)
1964
+ import_chalk12.default.cyan(`\u{1F310} Local visualization server running at ${addr}`)
1985
1965
  );
1986
1966
  (0, import_child_process.spawn)(opener, [`"${addr}"`], { shell: true });
1987
1967
  });
@@ -1997,7 +1977,7 @@ Or specify a custom report:
1997
1977
  }
1998
1978
  }
1999
1979
  } catch (err) {
2000
- (0, import_core14.handleCLIError)(err, "Visualization");
1980
+ (0, import_core16.handleCLIError)(err, "Visualization");
2001
1981
  }
2002
1982
  }
2003
1983
  var VISUALIZE_HELP_TEXT = `
@@ -2028,72 +2008,63 @@ NOTES:
2028
2008
  `;
2029
2009
 
2030
2010
  // src/commands/shared/standard-tool-actions.ts
2031
- var import_chalk12 = __toESM(require("chalk"));
2011
+ var import_chalk13 = __toESM(require("chalk"));
2032
2012
 
2033
2013
  // src/commands/agent-grounding.ts
2034
- var import_chalk13 = __toESM(require("chalk"));
2035
- var import_core16 = require("@aiready/core");
2014
+ var import_chalk14 = __toESM(require("chalk"));
2036
2015
 
2037
2016
  // src/commands/testability.ts
2038
- var import_chalk14 = __toESM(require("chalk"));
2039
- var import_core17 = require("@aiready/core");
2017
+ var import_chalk15 = __toESM(require("chalk"));
2040
2018
  async function testabilityAction(directory, options) {
2041
- const { analyzeTestability, calculateTestabilityScore } = await import("@aiready/testability");
2042
- const config = await (0, import_core17.loadConfig)(directory);
2043
- const merged = (0, import_core17.mergeConfigWithDefaults)(config, {
2044
- minCoverageRatio: 0.3
2045
- });
2046
- const report = await analyzeTestability({
2047
- rootDir: directory,
2048
- minCoverageRatio: options.minCoverageRatio ?? merged.minCoverageRatio,
2049
- include: options.include,
2050
- exclude: options.exclude
2019
+ return await executeToolAction(directory, options, {
2020
+ toolName: "testability-index",
2021
+ label: "Testability analysis",
2022
+ emoji: "\u{1F9EA}",
2023
+ defaults: {
2024
+ minCoverageRatio: 0.3,
2025
+ include: void 0,
2026
+ exclude: void 0,
2027
+ output: { format: "console", file: void 0 }
2028
+ },
2029
+ getCliOptions: (opts) => ({
2030
+ minCoverageRatio: opts.minCoverage ? parseFloat(opts.minCoverage) : void 0
2031
+ }),
2032
+ importTool: async () => {
2033
+ const tool = await import("@aiready/testability");
2034
+ return {
2035
+ analyze: tool.analyzeTestability,
2036
+ generateSummary: (report) => report.summary,
2037
+ calculateScore: tool.calculateTestabilityScore
2038
+ };
2039
+ },
2040
+ renderConsole: ({ results, summary, score }) => {
2041
+ renderToolHeader("Testability", "\u{1F9EA}", score?.score || 0, summary.rating);
2042
+ renderSafetyRating(summary.aiChangeSafetyRating);
2043
+ const rawData = results.rawData || results;
2044
+ console.log(
2045
+ import_chalk15.default.dim(
2046
+ ` Coverage: ${Math.round(summary.coverageRatio * 100)}% (${rawData.testFiles} test / ${rawData.sourceFiles} source files)`
2047
+ )
2048
+ );
2049
+ if (summary.aiChangeSafetyRating === "blind-risk") {
2050
+ console.log(
2051
+ import_chalk15.default.red.bold(
2052
+ "\n \u26A0\uFE0F NO TESTS \u2014 AI changes to this codebase are completely unverifiable!\n"
2053
+ )
2054
+ );
2055
+ }
2056
+ if (score) {
2057
+ renderToolScoreFooter(score);
2058
+ }
2059
+ }
2051
2060
  });
2052
- const scoring = calculateTestabilityScore(report);
2053
- if (options.output === "json") {
2054
- return scoring;
2055
- }
2056
- const safetyIcons = {
2057
- safe: "\u2705",
2058
- "moderate-risk": "\u26A0\uFE0F ",
2059
- "high-risk": "\u{1F534}",
2060
- "blind-risk": "\u{1F480}"
2061
- };
2062
- const safetyColors = {
2063
- safe: import_chalk14.default.green,
2064
- "moderate-risk": import_chalk14.default.yellow,
2065
- "high-risk": import_chalk14.default.red,
2066
- "blind-risk": import_chalk14.default.bgRed.white
2067
- };
2068
- const safety = report.summary.aiChangeSafetyRating;
2069
- const icon = safetyIcons[safety] ?? "\u2753";
2070
- const color = safetyColors[safety] ?? import_chalk14.default.white;
2071
- console.log(
2072
- ` \u{1F9EA} Testability: ${import_chalk14.default.bold(scoring.score + "/100")} (${report.summary.rating})`
2073
- );
2074
- console.log(
2075
- ` AI Change Safety: ${color(`${icon} ${safety.toUpperCase()}`)}`
2076
- );
2077
- console.log(
2078
- import_chalk14.default.dim(
2079
- ` Coverage: ${Math.round(report.summary.coverageRatio * 100)}% (${report.rawData.testFiles} test / ${report.rawData.sourceFiles} source files)`
2080
- )
2081
- );
2082
- if (safety === "blind-risk") {
2083
- console.log(
2084
- import_chalk14.default.red.bold(
2085
- "\n \u26A0\uFE0F NO TESTS \u2014 AI changes to this codebase are completely unverifiable!\n"
2086
- )
2087
- );
2088
- }
2089
- return scoring;
2090
2061
  }
2091
2062
 
2092
2063
  // src/commands/change-amplification.ts
2093
2064
  var import_cli = require("@aiready/change-amplification/dist/cli.js");
2094
2065
 
2095
2066
  // src/commands/bug.ts
2096
- var import_chalk15 = __toESM(require("chalk"));
2067
+ var import_chalk16 = __toESM(require("chalk"));
2097
2068
  var import_child_process2 = require("child_process");
2098
2069
  async function bugAction(message, options) {
2099
2070
  const repoUrl = "https://github.com/caopengau/aiready-cli";
@@ -2111,35 +2082,35 @@ Generated via AIReady CLI 'bug' command.
2111
2082
  Type: ${type}
2112
2083
  `.trim();
2113
2084
  if (options.submit) {
2114
- console.log(import_chalk15.default.blue("\u{1F680} Submitting issue via GitHub CLI...\n"));
2085
+ console.log(import_chalk16.default.blue("\u{1F680} Submitting issue via GitHub CLI...\n"));
2115
2086
  try {
2116
2087
  (0, import_child_process2.execSync)("gh auth status", { stdio: "ignore" });
2117
2088
  const command = `gh issue create --repo ${repoSlug} --title ${JSON.stringify(title)} --body ${JSON.stringify(body)} --label ${label}`;
2118
2089
  const output = (0, import_child_process2.execSync)(command, { encoding: "utf8" }).trim();
2119
- console.log(import_chalk15.default.green("\u2705 Issue Created Successfully!"));
2120
- console.log(import_chalk15.default.cyan(output));
2090
+ console.log(import_chalk16.default.green("\u2705 Issue Created Successfully!"));
2091
+ console.log(import_chalk16.default.cyan(output));
2121
2092
  return;
2122
2093
  } catch {
2123
- console.error(import_chalk15.default.red("\n\u274C Failed to submit via gh CLI."));
2094
+ console.error(import_chalk16.default.red("\n\u274C Failed to submit via gh CLI."));
2124
2095
  console.log(
2125
- import_chalk15.default.yellow(
2096
+ import_chalk16.default.yellow(
2126
2097
  ' Make sure gh is installed and run "gh auth login".\n'
2127
2098
  )
2128
2099
  );
2129
- console.log(import_chalk15.default.dim(" Falling back to URL generation..."));
2100
+ console.log(import_chalk16.default.dim(" Falling back to URL generation..."));
2130
2101
  }
2131
2102
  }
2132
2103
  const template = type === "bug" ? "bug_report.md" : type === "feature" ? "feature_request.md" : "new_metric_idea.md";
2133
2104
  const fullUrl = `${repoUrl}/issues/new?title=${encodeURIComponent(title)}&body=${encodeURIComponent(body)}&labels=${label}&template=${template}`;
2134
- console.log(import_chalk15.default.green("\u{1F680} Issue Draft Prepared!\n"));
2135
- console.log(import_chalk15.default.bold("Title: ") + title);
2136
- console.log(import_chalk15.default.bold("Type: ") + type);
2137
- console.log(import_chalk15.default.bold("\nClick the link below to submit this issue:"));
2138
- console.log(import_chalk15.default.cyan(fullUrl));
2139
- console.log(import_chalk15.default.bold("\n\u{1F916} [FOR AI AGENTS]:"));
2140
- console.log(import_chalk15.default.dim(" You have successfully prepared a report."));
2105
+ console.log(import_chalk16.default.green("\u{1F680} Issue Draft Prepared!\n"));
2106
+ console.log(import_chalk16.default.bold("Title: ") + title);
2107
+ console.log(import_chalk16.default.bold("Type: ") + type);
2108
+ console.log(import_chalk16.default.bold("\nClick the link below to submit this issue:"));
2109
+ console.log(import_chalk16.default.cyan(fullUrl));
2110
+ console.log(import_chalk16.default.bold("\n\u{1F916} [FOR AI AGENTS]:"));
2111
+ console.log(import_chalk16.default.dim(" You have successfully prepared a report."));
2141
2112
  console.log(
2142
- import_chalk15.default.dim(
2113
+ import_chalk16.default.dim(
2143
2114
  " Please present the URL above to the user so they can finalize the submission."
2144
2115
  )
2145
2116
  );
@@ -2148,14 +2119,14 @@ Type: ${type}
2148
2119
  const bugUrl = `${repoUrl}/issues/new?template=bug_report.md`;
2149
2120
  const featureUrl = `${repoUrl}/issues/new?template=feature_request.md`;
2150
2121
  const metricUrl = `${repoUrl}/issues/new?template=new_metric_idea.md`;
2151
- console.log(import_chalk15.default.blue("\u{1F4AC} Feedback & Bug Reports\n"));
2152
- console.log(` Report a Bug: ${import_chalk15.default.cyan(bugUrl)}`);
2153
- console.log(` Request a Feature: ${import_chalk15.default.cyan(featureUrl)}`);
2154
- console.log(` Suggest a Metric: ${import_chalk15.default.cyan(metricUrl)}`);
2155
- console.log(import_chalk15.default.bold("\n\u{1F916} [FOR AI AGENTS]:"));
2156
- console.log(import_chalk15.default.dim(" To prepare a specific report, run:"));
2122
+ console.log(import_chalk16.default.blue("\u{1F4AC} Feedback & Bug Reports\n"));
2123
+ console.log(` Report a Bug: ${import_chalk16.default.cyan(bugUrl)}`);
2124
+ console.log(` Request a Feature: ${import_chalk16.default.cyan(featureUrl)}`);
2125
+ console.log(` Suggest a Metric: ${import_chalk16.default.cyan(metricUrl)}`);
2126
+ console.log(import_chalk16.default.bold("\n\u{1F916} [FOR AI AGENTS]:"));
2127
+ console.log(import_chalk16.default.dim(" To prepare a specific report, run:"));
2157
2128
  console.log(
2158
- import_chalk15.default.cyan(
2129
+ import_chalk16.default.cyan(
2159
2130
  ' aiready bug "your description here" --type bug|feature|metric'
2160
2131
  )
2161
2132
  );