@aiready/core 0.9.37 → 0.9.39

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/index.mjs CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  getToolWeight,
19
19
  normalizeToolName,
20
20
  parseWeightString
21
- } from "./chunk-HFLFBA6F.mjs";
21
+ } from "./chunk-UQGI67WR.mjs";
22
22
 
23
23
  // src/utils/file-scanner.ts
24
24
  import { glob } from "glob";
@@ -513,8 +513,43 @@ function handleCLIError(error, commandName) {
513
513
  function getElapsedTime(startTime) {
514
514
  return ((Date.now() - startTime) / 1e3).toFixed(2);
515
515
  }
516
+ function getScoreBar(val) {
517
+ return "\u2588".repeat(Math.round(val / 10)).padEnd(10, "\u2591");
518
+ }
519
+ function getSafetyIcon(rating) {
520
+ switch (rating) {
521
+ case "safe":
522
+ return "\u2705";
523
+ case "moderate-risk":
524
+ return "\u26A0\uFE0F ";
525
+ case "high-risk":
526
+ return "\u{1F534}";
527
+ case "blind-risk":
528
+ return "\u{1F480}";
529
+ default:
530
+ return "\u2753";
531
+ }
532
+ }
533
+ function getSeverityColor(severity, chalk) {
534
+ switch (severity.toLowerCase()) {
535
+ case "critical":
536
+ case "high-risk":
537
+ case "blind-risk":
538
+ return chalk.red;
539
+ case "major":
540
+ case "moderate-risk":
541
+ return chalk.yellow;
542
+ case "minor":
543
+ case "safe":
544
+ return chalk.green;
545
+ case "info":
546
+ return chalk.blue;
547
+ default:
548
+ return chalk.white;
549
+ }
550
+ }
516
551
 
517
- // src/business-metrics.ts
552
+ // src/business/pricing-models.ts
518
553
  var MODEL_PRICING_PRESETS = {
519
554
  "gpt-5.3": {
520
555
  name: "GPT-5.3",
@@ -576,30 +611,17 @@ var MODEL_PRICING_PRESETS = {
576
611
  function getModelPreset(modelId) {
577
612
  return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["claude-4.6"];
578
613
  }
614
+
615
+ // src/business/cost-metrics.ts
579
616
  var DEFAULT_COST_CONFIG = {
580
617
  pricePer1KTokens: 5e-3,
581
- // GPT-4o input price (updated from GPT-4 era 0.01)
582
618
  queriesPerDevPerDay: 60,
583
- // Average AI queries per developer (updated: 40→60 as of 2026)
584
619
  developerCount: 5,
585
- // Default team size
586
620
  daysPerMonth: 30
587
621
  };
588
- var SEVERITY_TIME_ESTIMATES = {
589
- critical: 4,
590
- // Complex architectural issues (industry avg: 6.8h)
591
- major: 2,
592
- // Significant refactoring needed (avg: 3.4h)
593
- minor: 0.5,
594
- // Simple naming/style fixes (avg: 0.8h)
595
- info: 0.25
596
- // Documentation improvements
597
- };
598
- var DEFAULT_HOURLY_RATE = 75;
599
622
  function calculateMonthlyCost(tokenWaste, config = {}) {
600
623
  const budget = calculateTokenBudget({
601
624
  totalContextTokens: tokenWaste * 2.5,
602
- // Heuristic: context is larger than waste
603
625
  wastedTokens: {
604
626
  duplication: tokenWaste * 0.7,
605
627
  fragmentation: tokenWaste * 0.3,
@@ -615,7 +637,10 @@ function calculateTokenBudget(params) {
615
637
  const totalWaste = wastedTokens.duplication + wastedTokens.fragmentation + wastedTokens.chattiness;
616
638
  const efficiencyRatio = Math.max(
617
639
  0,
618
- Math.min(1, (totalContextTokens - totalWaste) / Math.max(1, totalContextTokens))
640
+ Math.min(
641
+ 1,
642
+ (totalContextTokens - totalWaste) / Math.max(1, totalContextTokens)
643
+ )
619
644
  );
620
645
  return {
621
646
  totalContextTokens: Math.round(totalContextTokens),
@@ -630,7 +655,6 @@ function calculateTokenBudget(params) {
630
655
  },
631
656
  efficiencyRatio: Math.round(efficiencyRatio * 100) / 100,
632
657
  potentialRetrievableTokens: Math.round(totalWaste * 0.8)
633
- // Heuristic: 80% is fixable
634
658
  };
635
659
  }
636
660
  function estimateCostFromBudget(budget, model, config = {}) {
@@ -639,7 +663,8 @@ function estimateCostFromBudget(budget, model, config = {}) {
639
663
  const tokensPerDay = wastePerQuery * cfg.queriesPerDevPerDay;
640
664
  const tokensPerMonth = tokensPerDay * cfg.daysPerMonth;
641
665
  const totalWeight = cfg.developerCount;
642
- const baseCost = tokensPerMonth / 1e3 * model.pricePer1KInputTokens * totalWeight;
666
+ const price = config.pricePer1KTokens ?? model.pricePer1KInputTokens;
667
+ const baseCost = tokensPerMonth / 1e3 * price * totalWeight;
643
668
  let confidence = 0.85;
644
669
  if (model.contextTier === "frontier") confidence = 0.7;
645
670
  const variance = 0.25;
@@ -653,6 +678,15 @@ function estimateCostFromBudget(budget, model, config = {}) {
653
678
  confidence
654
679
  };
655
680
  }
681
+
682
+ // src/business/productivity-metrics.ts
683
+ var SEVERITY_TIME_ESTIMATES = {
684
+ critical: 4,
685
+ major: 2,
686
+ minor: 0.5,
687
+ info: 0.25
688
+ };
689
+ var DEFAULT_HOURLY_RATE = 75;
656
690
  function calculateProductivityImpact(issues, hourlyRate = DEFAULT_HOURLY_RATE) {
657
691
  const counts = {
658
692
  critical: issues.filter((i) => i.severity === "critical").length,
@@ -693,108 +727,158 @@ function predictAcceptanceRate(toolOutputs) {
693
727
  const baseRate = 0.3;
694
728
  const patterns = toolOutputs.get("pattern-detect");
695
729
  if (patterns) {
696
- const patternImpact = (patterns.score - 50) * 3e-3;
697
730
  factors.push({
698
731
  name: "Semantic Duplication",
699
- impact: Math.round(patternImpact * 100)
732
+ impact: Math.round((patterns.score - 50) * 3e-3 * 100)
700
733
  });
701
734
  }
702
735
  const context = toolOutputs.get("context-analyzer");
703
736
  if (context) {
704
- const contextImpact = (context.score - 50) * 4e-3;
705
737
  factors.push({
706
738
  name: "Context Efficiency",
707
- impact: Math.round(contextImpact * 100)
739
+ impact: Math.round((context.score - 50) * 4e-3 * 100)
708
740
  });
709
741
  }
710
742
  const consistency = toolOutputs.get("consistency");
711
743
  if (consistency) {
712
- const consistencyImpact = (consistency.score - 50) * 2e-3;
713
744
  factors.push({
714
745
  name: "Code Consistency",
715
- impact: Math.round(consistencyImpact * 100)
746
+ impact: Math.round((consistency.score - 50) * 2e-3 * 100)
716
747
  });
717
748
  }
718
749
  const aiSignalClarity = toolOutputs.get("ai-signal-clarity");
719
750
  if (aiSignalClarity) {
720
- const hrImpact = (50 - aiSignalClarity.score) * 2e-3;
721
751
  factors.push({
722
752
  name: "AI Signal Clarity",
723
- impact: Math.round(hrImpact * 100)
753
+ impact: Math.round((50 - aiSignalClarity.score) * 2e-3 * 100)
724
754
  });
725
755
  }
726
756
  const totalImpact = factors.reduce((sum, f) => sum + f.impact / 100, 0);
727
757
  const rate = Math.max(0.05, Math.min(0.8, baseRate + totalImpact));
728
- let confidence;
758
+ let confidence = 0.35;
729
759
  if (toolOutputs.size >= 4) confidence = 0.75;
730
760
  else if (toolOutputs.size >= 3) confidence = 0.65;
731
761
  else if (toolOutputs.size >= 2) confidence = 0.5;
732
- else confidence = 0.35;
733
- return {
734
- rate: Math.round(rate * 100) / 100,
735
- confidence,
736
- factors
737
- };
762
+ return { rate: Math.round(rate * 100) / 100, confidence, factors };
738
763
  }
739
- function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentation, consistencyScore, totalFiles, modelTier = "standard") {
740
- const tierThresholds = CONTEXT_TIER_THRESHOLDS[modelTier];
741
- const idealBudget = tierThresholds.idealTokens;
742
- const criticalBudget = tierThresholds.criticalTokens;
743
- const idealDepth = tierThresholds.idealDepth;
744
- const budgetRange = criticalBudget - idealBudget;
745
- const budgetFactor = Math.min(
746
- 100,
747
- Math.max(0, (contextBudget - idealBudget) / budgetRange * 100)
748
- );
749
- const depthFactor = Math.min(
750
- 100,
751
- Math.max(0, (importDepth - idealDepth) * 10)
752
- );
753
- const fragmentationFactor = Math.min(
754
- 100,
755
- Math.max(0, (fragmentation - 0.3) * 250)
756
- );
757
- const consistencyFactor = Math.min(100, Math.max(0, 100 - consistencyScore));
758
- const fileFactor = Math.min(100, Math.max(0, (totalFiles - 50) / 5));
764
+
765
+ // src/business/risk-metrics.ts
766
+ function calculateKnowledgeConcentration(params) {
767
+ const { uniqueConceptFiles, totalFiles, singleAuthorFiles, orphanFiles } = params;
768
+ const concentrationRatio = totalFiles > 0 ? (uniqueConceptFiles + singleAuthorFiles) / (totalFiles * 2) : 0;
759
769
  const score = Math.round(
760
- budgetFactor * 0.35 + depthFactor * 0.2 + fragmentationFactor * 0.2 + consistencyFactor * 0.15 + fileFactor * 0.1
770
+ Math.min(
771
+ 100,
772
+ concentrationRatio * 100 + orphanFiles / Math.max(1, totalFiles) * 20
773
+ )
761
774
  );
762
775
  let rating;
763
- if (score < 20) rating = "trivial";
764
- else if (score < 40) rating = "easy";
765
- else if (score < 60) rating = "moderate";
766
- else if (score < 80) rating = "difficult";
767
- else rating = "expert";
776
+ if (score < 30) rating = "low";
777
+ else if (score < 50) rating = "moderate";
778
+ else if (score < 75) rating = "high";
779
+ else rating = "critical";
780
+ const recommendations = [];
781
+ if (singleAuthorFiles > 0)
782
+ recommendations.push(
783
+ `Distribute knowledge for ${singleAuthorFiles} single-author files.`
784
+ );
785
+ if (orphanFiles > 0)
786
+ recommendations.push(
787
+ `Link ${orphanFiles} orphan files to the rest of the codebase.`
788
+ );
768
789
  return {
769
790
  score,
770
791
  rating,
771
- factors: [
772
- {
773
- name: "Context Budget",
774
- contribution: Math.round(budgetFactor * 0.35),
775
- description: `${Math.round(contextBudget)} tokens required (${modelTier} model tier: ideal <${idealBudget.toLocaleString()})`
776
- },
777
- {
778
- name: "Import Depth",
779
- contribution: Math.round(depthFactor * 0.2),
780
- description: `${importDepth.toFixed(1)} average levels (ideal <${idealDepth} for ${modelTier})`
781
- },
782
- {
783
- name: "Code Fragmentation",
784
- contribution: Math.round(fragmentationFactor * 0.2),
785
- description: `${(fragmentation * 100).toFixed(0)}% fragmentation`
786
- },
787
- {
788
- name: "Consistency",
789
- contribution: Math.round(consistencyFactor * 0.15),
790
- description: `${consistencyScore}/100 consistency score`
791
- },
792
- {
793
- name: "Project Scale",
794
- contribution: Math.round(fileFactor * 0.1),
795
- description: `${totalFiles} files analyzed`
796
- }
797
- ]
792
+ recommendations,
793
+ analysis: {
794
+ uniqueConceptFiles,
795
+ totalFiles,
796
+ concentrationRatio,
797
+ singleAuthorFiles,
798
+ orphanFiles
799
+ }
800
+ };
801
+ }
802
+ function calculateDebtInterest(principal, monthlyGrowthRate) {
803
+ const monthlyRate = monthlyGrowthRate;
804
+ const annualRate = Math.pow(1 + monthlyRate, 12) - 1;
805
+ const monthlyCost = principal * monthlyRate;
806
+ return {
807
+ monthlyRate,
808
+ annualRate,
809
+ principal,
810
+ monthlyCost,
811
+ projections: {
812
+ months6: principal * Math.pow(1 + monthlyRate, 6),
813
+ months12: principal * Math.pow(1 + monthlyRate, 12),
814
+ months24: principal * Math.pow(1 + monthlyRate, 24)
815
+ }
816
+ };
817
+ }
818
+
819
+ // src/business/comprehension-metrics.ts
820
+ function calculateTechnicalValueChain(params) {
821
+ const { businessLogicDensity, dataAccessComplexity, apiSurfaceArea } = params;
822
+ const score = (businessLogicDensity * 0.5 + (1 - dataAccessComplexity / 10) * 0.3 + (1 - apiSurfaceArea / 20) * 0.2) * 100;
823
+ return {
824
+ score: Math.round(Math.max(0, Math.min(100, score))),
825
+ density: businessLogicDensity,
826
+ complexity: dataAccessComplexity,
827
+ surface: apiSurfaceArea
828
+ };
829
+ }
830
+ function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentation, modelTier = "frontier") {
831
+ const tierMap = {
832
+ compact: "compact",
833
+ standard: "standard",
834
+ extended: "extended",
835
+ frontier: "frontier",
836
+ easy: "frontier",
837
+ // Map legacy 'easy' to 'frontier'
838
+ moderate: "standard",
839
+ difficult: "compact"
840
+ };
841
+ const tier = tierMap[modelTier] || "frontier";
842
+ const threshold = CONTEXT_TIER_THRESHOLDS[tier];
843
+ const budgetRatio = contextBudget / threshold.idealTokens;
844
+ const score = (budgetRatio * 0.6 + importDepth / 10 * 0.2 + fragmentation * 0.2) * 100;
845
+ const finalScore = Math.round(Math.max(0, Math.min(100, score)));
846
+ let rating;
847
+ if (finalScore < 20) rating = "trivial";
848
+ else if (finalScore < 40) rating = "easy";
849
+ else if (finalScore < 60) rating = "moderate";
850
+ else if (finalScore < 85) rating = "difficult";
851
+ else rating = "expert";
852
+ return {
853
+ score: finalScore,
854
+ rating,
855
+ factors: { budgetRatio, depthRatio: importDepth / 10, fragmentation }
856
+ };
857
+ }
858
+
859
+ // src/business-metrics.ts
860
+ function calculateBusinessROI(params) {
861
+ const model = getModelPreset(params.modelId || "claude-4.6");
862
+ const devCount = params.developerCount || 5;
863
+ const budget = calculateTokenBudget({
864
+ totalContextTokens: params.tokenWaste * 2.5,
865
+ wastedTokens: {
866
+ duplication: params.tokenWaste * 0.7,
867
+ fragmentation: params.tokenWaste * 0.3,
868
+ chattiness: 0
869
+ }
870
+ });
871
+ const cost = estimateCostFromBudget(budget, model, {
872
+ developerCount: devCount
873
+ });
874
+ const productivity = calculateProductivityImpact(params.issues);
875
+ const monthlySavings = cost.total;
876
+ const productivityGainHours = productivity.totalHours;
877
+ const annualValue = (monthlySavings + productivityGainHours * 75) * 12;
878
+ return {
879
+ monthlySavings: Math.round(monthlySavings),
880
+ productivityGainHours: Math.round(productivityGainHours),
881
+ annualValue: Math.round(annualValue)
798
882
  };
799
883
  }
800
884
  function formatCost(cost) {
@@ -820,220 +904,6 @@ function formatHours(hours) {
820
904
  function formatAcceptanceRate(rate) {
821
905
  return `${Math.round(rate * 100)}%`;
822
906
  }
823
- function calculateScoreTrend(history) {
824
- if (history.length < 2) {
825
- return {
826
- direction: "stable",
827
- change30Days: 0,
828
- change90Days: 0,
829
- velocity: 0,
830
- projectedScore: history[0]?.overallScore || 100
831
- };
832
- }
833
- const now = /* @__PURE__ */ new Date();
834
- const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1e3);
835
- const ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1e3);
836
- const last30Days = history.filter(
837
- (e) => new Date(e.timestamp) >= thirtyDaysAgo
838
- );
839
- const last90Days = history.filter(
840
- (e) => new Date(e.timestamp) >= ninetyDaysAgo
841
- );
842
- const currentScore = history[history.length - 1].overallScore;
843
- const thirtyDaysAgoScore = last30Days[0]?.overallScore || currentScore;
844
- const ninetyDaysAgoScore = last90Days[0]?.overallScore || thirtyDaysAgoScore;
845
- const change30Days = currentScore - thirtyDaysAgoScore;
846
- const change90Days = currentScore - ninetyDaysAgoScore;
847
- const weeksOfData = Math.max(1, history.length / 7);
848
- const totalChange = currentScore - history[0].overallScore;
849
- const velocity = totalChange / weeksOfData;
850
- let direction;
851
- if (change30Days > 3) direction = "improving";
852
- else if (change30Days < -3) direction = "degrading";
853
- else direction = "stable";
854
- const projectedScore = Math.max(
855
- 0,
856
- Math.min(100, currentScore + velocity * 4)
857
- );
858
- return {
859
- direction,
860
- change30Days,
861
- change90Days,
862
- velocity: Math.round(velocity * 10) / 10,
863
- projectedScore: Math.round(projectedScore)
864
- };
865
- }
866
- function calculateRemediationVelocity(history, currentIssues) {
867
- if (history.length < 2) {
868
- return {
869
- issuesFixedThisWeek: 0,
870
- avgIssuesPerWeek: 0,
871
- trend: "stable",
872
- estimatedCompletionWeeks: currentIssues > 0 ? Infinity : 0
873
- };
874
- }
875
- const now = /* @__PURE__ */ new Date();
876
- const oneWeekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3);
877
- const twoWeeksAgo = new Date(now.getTime() - 14 * 24 * 60 * 60 * 1e3);
878
- const thisWeek = history.filter((e) => new Date(e.timestamp) >= oneWeekAgo);
879
- const lastWeek = history.filter(
880
- (e) => new Date(e.timestamp) >= twoWeeksAgo && new Date(e.timestamp) < oneWeekAgo
881
- );
882
- const issuesFixedThisWeek = thisWeek.length > 1 ? thisWeek[0].totalIssues - thisWeek[thisWeek.length - 1].totalIssues : 0;
883
- const totalIssuesFixed = history[0].totalIssues - history[history.length - 1].totalIssues;
884
- const weeksOfData = Math.max(1, history.length / 7);
885
- const avgIssuesPerWeek = totalIssuesFixed / weeksOfData;
886
- let trend;
887
- if (lastWeek.length > 1) {
888
- const lastWeekFixed = lastWeek[0].totalIssues - lastWeek[lastWeek.length - 1].totalIssues;
889
- if (issuesFixedThisWeek > lastWeekFixed * 1.2) trend = "accelerating";
890
- else if (issuesFixedThisWeek < lastWeekFixed * 0.8) trend = "decelerating";
891
- else trend = "stable";
892
- } else {
893
- trend = "stable";
894
- }
895
- const estimatedCompletionWeeks = avgIssuesPerWeek > 0 ? Math.ceil(currentIssues / avgIssuesPerWeek) : Infinity;
896
- return {
897
- issuesFixedThisWeek: Math.max(0, issuesFixedThisWeek),
898
- avgIssuesPerWeek: Math.round(avgIssuesPerWeek * 10) / 10,
899
- trend,
900
- estimatedCompletionWeeks
901
- };
902
- }
903
- function calculateKnowledgeConcentration(files, authorData) {
904
- if (files.length === 0) {
905
- return {
906
- score: 0,
907
- rating: "low",
908
- analysis: {
909
- uniqueConceptFiles: 0,
910
- totalFiles: 0,
911
- concentrationRatio: 0,
912
- singleAuthorFiles: 0,
913
- orphanFiles: 0
914
- },
915
- recommendations: ["No files to analyze"]
916
- };
917
- }
918
- const orphanFiles = files.filter(
919
- (f) => f.exports < 2 && f.imports < 2
920
- ).length;
921
- const avgExports = files.reduce((sum, f) => sum + f.exports, 0) / files.length;
922
- const uniqueConceptFiles = files.filter(
923
- (f) => f.exports > avgExports * 2
924
- ).length;
925
- const totalExports = files.reduce((sum, f) => sum + f.exports, 0);
926
- const concentrationRatio = totalExports > 0 ? uniqueConceptFiles / files.length : 0;
927
- let singleAuthorFiles = 0;
928
- if (authorData) {
929
- for (const files2 of authorData.values()) {
930
- if (files2.length === 1) singleAuthorFiles++;
931
- }
932
- }
933
- const orphanRisk = orphanFiles / files.length * 30;
934
- const uniqueRisk = concentrationRatio * 40;
935
- const singleAuthorRisk = authorData ? singleAuthorFiles / files.length * 30 : 0;
936
- const score = Math.min(
937
- 100,
938
- Math.round(orphanRisk + uniqueRisk + singleAuthorRisk)
939
- );
940
- let rating;
941
- if (score < 20) rating = "low";
942
- else if (score < 40) rating = "moderate";
943
- else if (score < 70) rating = "high";
944
- else rating = "critical";
945
- const recommendations = [];
946
- if (orphanFiles > files.length * 0.2) {
947
- recommendations.push(
948
- `Reduce ${orphanFiles} orphan files by connecting them to main modules`
949
- );
950
- }
951
- if (uniqueConceptFiles > files.length * 0.1) {
952
- recommendations.push(
953
- "Distribute high-export files into more focused modules"
954
- );
955
- }
956
- if (authorData && singleAuthorFiles > files.length * 0.3) {
957
- recommendations.push(
958
- "Increase knowledge sharing to reduce single-author dependencies"
959
- );
960
- }
961
- return {
962
- score,
963
- rating,
964
- analysis: {
965
- uniqueConceptFiles,
966
- totalFiles: files.length,
967
- concentrationRatio: Math.round(concentrationRatio * 100) / 100,
968
- singleAuthorFiles,
969
- orphanFiles
970
- },
971
- recommendations
972
- };
973
- }
974
- function calculateTechnicalDebtInterest(params) {
975
- const { currentMonthlyCost, issues, monthsOpen } = params;
976
- const criticalCount = issues.filter((i) => i.severity === "critical").length;
977
- const majorCount = issues.filter((i) => i.severity === "major").length;
978
- const minorCount = issues.filter((i) => i.severity === "minor").length;
979
- const severityWeight = (criticalCount * 3 + majorCount * 2 + minorCount * 1) / Math.max(1, issues.length);
980
- const baseRate = 0.02 + severityWeight * 0.01;
981
- const timeMultiplier = Math.max(1, 1 + monthsOpen * 0.1);
982
- const monthlyRate = baseRate * timeMultiplier;
983
- const projectDebt = (principal2, months) => {
984
- let debt = principal2;
985
- for (let i = 0; i < months; i++) {
986
- debt = debt * (1 + monthlyRate);
987
- }
988
- return Math.round(debt);
989
- };
990
- const principal = currentMonthlyCost * 12;
991
- const projections = {
992
- months6: projectDebt(principal, 6),
993
- months12: projectDebt(principal, 12),
994
- months24: projectDebt(principal, 24)
995
- };
996
- return {
997
- monthlyRate: Math.round(monthlyRate * 1e4) / 100,
998
- annualRate: Math.round((Math.pow(1 + monthlyRate, 12) - 1) * 1e4) / 100,
999
- principal,
1000
- projections,
1001
- monthlyCost: Math.round(currentMonthlyCost * (1 + monthlyRate) * 100) / 100
1002
- };
1003
- }
1004
- function getDebtBreakdown(patternCost, contextCost, consistencyCost) {
1005
- const breakdowns = [
1006
- {
1007
- category: "Semantic Duplication",
1008
- currentCost: patternCost,
1009
- monthlyGrowthRate: 5,
1010
- // Grows as devs copy-paste
1011
- priority: patternCost > 1e3 ? "high" : "medium",
1012
- fixCost: patternCost * 3
1013
- // Fixing costs 3x current waste
1014
- },
1015
- {
1016
- category: "Context Fragmentation",
1017
- currentCost: contextCost,
1018
- monthlyGrowthRate: 3,
1019
- // Grows with new features
1020
- priority: contextCost > 500 ? "high" : "medium",
1021
- fixCost: contextCost * 2.5
1022
- },
1023
- {
1024
- category: "Consistency Issues",
1025
- currentCost: consistencyCost,
1026
- monthlyGrowthRate: 2,
1027
- // Grows with new devs
1028
- priority: consistencyCost > 200 ? "medium" : "low",
1029
- fixCost: consistencyCost * 1.5
1030
- }
1031
- ];
1032
- return breakdowns.sort((a, b) => {
1033
- const priorityOrder = { high: 0, medium: 1, low: 2 };
1034
- return priorityOrder[a.priority] - priorityOrder[b.priority];
1035
- });
1036
- }
1037
907
  function generateValueChain(params) {
1038
908
  const { issueType, count, severity } = params;
1039
909
  const impacts = {
@@ -1073,9 +943,7 @@ function generateValueChain(params) {
1073
943
  },
1074
944
  businessOutcome: {
1075
945
  directCost: count * 12,
1076
- // Placeholder linear cost
1077
946
  opportunityCost: productivityLoss * 15e3,
1078
- // Monthly avg dev cost $15k
1079
947
  riskLevel: impact.risk
1080
948
  }
1081
949
  };
@@ -1519,7 +1387,9 @@ var ParserFactory = class _ParserFactory {
1519
1387
  registerParser(parser) {
1520
1388
  this.parsers.set(parser.language, parser);
1521
1389
  parser.extensions.forEach((ext) => {
1522
- this.extensionMap.set(ext, parser.language);
1390
+ const lang = LANGUAGE_EXTENSIONS[ext] || parser.language;
1391
+ this.extensionMap.set(ext, lang);
1392
+ this.parsers.set(lang, parser);
1523
1393
  });
1524
1394
  }
1525
1395
  /**
@@ -1588,7 +1458,55 @@ function getSupportedLanguages() {
1588
1458
  return ParserFactory.getInstance().getSupportedLanguages();
1589
1459
  }
1590
1460
 
1591
- // src/future-proof-metrics.ts
1461
+ // src/metrics/remediation-utils.ts
1462
+ function collectFutureProofRecommendations(params) {
1463
+ const recommendations = [];
1464
+ for (const rec of params.aiSignalClarity.recommendations) {
1465
+ recommendations.push({ action: rec, estimatedImpact: 8, priority: "high" });
1466
+ }
1467
+ for (const rec of params.agentGrounding.recommendations) {
1468
+ recommendations.push({
1469
+ action: rec,
1470
+ estimatedImpact: 6,
1471
+ priority: "medium"
1472
+ });
1473
+ }
1474
+ for (const rec of params.testability.recommendations) {
1475
+ const priority = params.testability.aiChangeSafetyRating === "blind-risk" ? "high" : "medium";
1476
+ recommendations.push({ action: rec, estimatedImpact: 10, priority });
1477
+ }
1478
+ for (const rec of params.patternEntropy.recommendations) {
1479
+ recommendations.push({ action: rec, estimatedImpact: 5, priority: "low" });
1480
+ }
1481
+ if (params.conceptCohesion.rating === "poor") {
1482
+ recommendations.push({
1483
+ action: "Improve concept cohesion by grouping related exports",
1484
+ estimatedImpact: 8,
1485
+ priority: "high"
1486
+ });
1487
+ }
1488
+ if (params.docDrift) {
1489
+ for (const rec of params.docDrift.recommendations) {
1490
+ recommendations.push({
1491
+ action: rec,
1492
+ estimatedImpact: 8,
1493
+ priority: "high"
1494
+ });
1495
+ }
1496
+ }
1497
+ if (params.dependencyHealth) {
1498
+ for (const rec of params.dependencyHealth.recommendations) {
1499
+ recommendations.push({
1500
+ action: rec,
1501
+ estimatedImpact: 7,
1502
+ priority: "medium"
1503
+ });
1504
+ }
1505
+ }
1506
+ return recommendations;
1507
+ }
1508
+
1509
+ // src/metrics/cognitive-load.ts
1592
1510
  function calculateCognitiveLoad(params) {
1593
1511
  const {
1594
1512
  linesOfCode,
@@ -1647,13 +1565,20 @@ function calculateCognitiveLoad(params) {
1647
1565
  }
1648
1566
  };
1649
1567
  }
1568
+
1569
+ // src/metrics/semantic-distance.ts
1650
1570
  function calculateSemanticDistance(params) {
1651
- const { file1, file2, file1Domain, file2Domain, sharedDependencies } = params;
1571
+ const {
1572
+ file1,
1573
+ file2,
1574
+ file1Domain,
1575
+ file2Domain,
1576
+ sharedDependencies,
1577
+ file1Imports,
1578
+ file2Imports
1579
+ } = params;
1652
1580
  const domainDistance = file1Domain === file2Domain ? 0 : file1Domain && file2Domain ? 0.5 : 0.8;
1653
- const importOverlap = sharedDependencies.length / Math.max(
1654
- 1,
1655
- Math.min(params.file1Imports.length, params.file2Imports.length)
1656
- );
1581
+ const importOverlap = sharedDependencies.length / Math.max(1, Math.min(file1Imports.length, file2Imports.length));
1657
1582
  const importDistance = 1 - importOverlap;
1658
1583
  const distance = domainDistance * 0.4 + importDistance * 0.3 + (sharedDependencies.length > 0 ? 0 : 0.3);
1659
1584
  let relationship;
@@ -1672,6 +1597,8 @@ function calculateSemanticDistance(params) {
1672
1597
  reason: relationship === "same-domain" ? `Both in "${file1Domain}" domain` : relationship === "cross-domain" ? `Share ${sharedDependencies.length} dependency(ies)` : "No strong semantic relationship detected"
1673
1598
  };
1674
1599
  }
1600
+
1601
+ // src/metrics/structural-metrics.ts
1675
1602
  function calculatePatternEntropy(files) {
1676
1603
  if (files.length === 0) {
1677
1604
  return {
@@ -1721,23 +1648,18 @@ function calculatePatternEntropy(files) {
1721
1648
  else if (normalizedEntropy < 0.8) rating = "fragmented";
1722
1649
  else rating = "chaotic";
1723
1650
  const recommendations = [];
1724
- if (normalizedEntropy > 0.5) {
1651
+ if (normalizedEntropy > 0.5)
1725
1652
  recommendations.push(
1726
1653
  `Consolidate ${files.length} files into fewer directories by domain`
1727
1654
  );
1728
- }
1729
- if (dirGroups.size > 5) {
1655
+ if (dirGroups.size > 5)
1730
1656
  recommendations.push(
1731
1657
  "Consider barrel exports to reduce directory navigation"
1732
1658
  );
1733
- }
1734
- if (gini > 0.5) {
1659
+ if (gini > 0.5)
1735
1660
  recommendations.push("Redistribute files more evenly across directories");
1736
- }
1737
- const firstFile = files.length > 0 ? files[0] : null;
1738
- const domainValue = firstFile ? firstFile.domain : "mixed";
1739
1661
  return {
1740
- domain: domainValue,
1662
+ domain: files[0]?.domain || "mixed",
1741
1663
  entropy: Math.round(normalizedEntropy * 100) / 100,
1742
1664
  rating,
1743
1665
  distribution: {
@@ -1768,9 +1690,8 @@ function calculateConceptCohesion(params) {
1768
1690
  }
1769
1691
  const uniqueDomains = new Set(allDomains);
1770
1692
  const domainCounts = /* @__PURE__ */ new Map();
1771
- for (const d of allDomains) {
1693
+ for (const d of allDomains)
1772
1694
  domainCounts.set(d, (domainCounts.get(d) || 0) + 1);
1773
- }
1774
1695
  const maxCount = Math.max(...Array.from(domainCounts.values()), 1);
1775
1696
  const domainConcentration = maxCount / allDomains.length;
1776
1697
  const exportPurposeClarity = 1 - (uniqueDomains.size - 1) / Math.max(1, exports.length);
@@ -1790,59 +1711,8 @@ function calculateConceptCohesion(params) {
1790
1711
  }
1791
1712
  };
1792
1713
  }
1793
- function calculateFutureProofScore(params) {
1794
- const loadScore = 100 - params.cognitiveLoad.score;
1795
- const entropyScore = 100 - params.patternEntropy.entropy * 100;
1796
- const cohesionScore = params.conceptCohesion.score * 100;
1797
- const overall = Math.round(
1798
- loadScore * 0.4 + entropyScore * 0.3 + cohesionScore * 0.3
1799
- );
1800
- const factors = [
1801
- {
1802
- name: "Cognitive Load",
1803
- impact: Math.round(loadScore - 50),
1804
- description: params.cognitiveLoad.rating
1805
- },
1806
- {
1807
- name: "Pattern Entropy",
1808
- impact: Math.round(entropyScore - 50),
1809
- description: params.patternEntropy.rating
1810
- },
1811
- {
1812
- name: "Concept Cohesion",
1813
- impact: Math.round(cohesionScore - 50),
1814
- description: params.conceptCohesion.rating
1815
- }
1816
- ];
1817
- const recommendations = [];
1818
- for (const rec of params.patternEntropy.recommendations) {
1819
- recommendations.push({
1820
- action: rec,
1821
- estimatedImpact: 5,
1822
- priority: "medium"
1823
- });
1824
- }
1825
- if (params.conceptCohesion.rating === "poor") {
1826
- recommendations.push({
1827
- action: "Improve concept cohesion by grouping related exports",
1828
- estimatedImpact: 8,
1829
- priority: "high"
1830
- });
1831
- }
1832
- const semanticDistanceAvg = params.semanticDistances && params.semanticDistances.length > 0 ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
1833
- return {
1834
- toolName: "future-proof",
1835
- score: overall,
1836
- rawMetrics: {
1837
- cognitiveLoadScore: params.cognitiveLoad.score,
1838
- entropyScore: params.patternEntropy.entropy,
1839
- cohesionScore: params.conceptCohesion.score,
1840
- semanticDistanceAvg
1841
- },
1842
- factors,
1843
- recommendations
1844
- };
1845
- }
1714
+
1715
+ // src/metrics/ai-signal-clarity.ts
1846
1716
  function calculateAiSignalClarity(params) {
1847
1717
  const {
1848
1718
  overloadedSymbols,
@@ -1864,75 +1734,53 @@ function calculateAiSignalClarity(params) {
1864
1734
  recommendations: []
1865
1735
  };
1866
1736
  }
1867
- const overloadRatio = Math.min(
1868
- 1,
1869
- overloadedSymbols / Math.max(1, totalSymbols)
1870
- );
1737
+ const overloadRatio = overloadedSymbols / Math.max(1, totalSymbols);
1871
1738
  const overloadSignal = {
1872
1739
  name: "Symbol Overloading",
1873
1740
  count: overloadedSymbols,
1874
- riskContribution: Math.round(overloadRatio * 100 * 0.25),
1875
- // 25% weight
1741
+ riskContribution: Math.round(Math.min(1, overloadRatio) * 100 * 0.25),
1876
1742
  description: `${overloadedSymbols} overloaded symbols \u2014 AI picks wrong signature`
1877
1743
  };
1878
- const magicRatio = Math.min(1, magicLiterals / Math.max(1, totalSymbols * 2));
1744
+ const magicRatio = magicLiterals / Math.max(1, totalSymbols * 2);
1879
1745
  const magicSignal = {
1880
1746
  name: "Magic Literals",
1881
1747
  count: magicLiterals,
1882
- riskContribution: Math.round(magicRatio * 100 * 0.2),
1883
- // 20% weight
1748
+ riskContribution: Math.round(Math.min(1, magicRatio) * 100 * 0.2),
1884
1749
  description: `${magicLiterals} unnamed constants \u2014 AI invents wrong values`
1885
1750
  };
1886
- const trapRatio = Math.min(1, booleanTraps / Math.max(1, totalSymbols));
1751
+ const trapRatio = booleanTraps / Math.max(1, totalSymbols);
1887
1752
  const trapSignal = {
1888
1753
  name: "Boolean Traps",
1889
1754
  count: booleanTraps,
1890
- riskContribution: Math.round(trapRatio * 100 * 0.2),
1891
- // 20% weight
1755
+ riskContribution: Math.round(Math.min(1, trapRatio) * 100 * 0.2),
1892
1756
  description: `${booleanTraps} boolean trap parameters \u2014 AI inverts intent`
1893
1757
  };
1894
- const sideEffectRatio = Math.min(
1895
- 1,
1896
- implicitSideEffects / Math.max(1, totalExports)
1897
- );
1758
+ const sideEffectRatio = implicitSideEffects / Math.max(1, totalExports);
1898
1759
  const sideEffectSignal = {
1899
1760
  name: "Implicit Side Effects",
1900
1761
  count: implicitSideEffects,
1901
- riskContribution: Math.round(sideEffectRatio * 100 * 0.15),
1902
- // 15% weight
1762
+ riskContribution: Math.round(Math.min(1, sideEffectRatio) * 100 * 0.15),
1903
1763
  description: `${implicitSideEffects} functions with implicit side effects \u2014 AI misses contracts`
1904
1764
  };
1905
- const callbackRatio = Math.min(
1906
- 1,
1907
- deepCallbacks / Math.max(1, totalSymbols * 0.1)
1908
- );
1765
+ const callbackRatio = deepCallbacks / Math.max(1, totalSymbols * 0.1);
1909
1766
  const callbackSignal = {
1910
1767
  name: "Callback Nesting",
1911
1768
  count: deepCallbacks,
1912
- riskContribution: Math.round(callbackRatio * 100 * 0.1),
1913
- // 10% weight
1769
+ riskContribution: Math.round(Math.min(1, callbackRatio) * 100 * 0.1),
1914
1770
  description: `${deepCallbacks} deep callback chains \u2014 AI loses control flow context`
1915
1771
  };
1916
- const ambiguousRatio = Math.min(
1917
- 1,
1918
- ambiguousNames / Math.max(1, totalSymbols)
1919
- );
1772
+ const ambiguousRatio = ambiguousNames / Math.max(1, totalSymbols);
1920
1773
  const ambiguousSignal = {
1921
1774
  name: "Ambiguous Names",
1922
1775
  count: ambiguousNames,
1923
- riskContribution: Math.round(ambiguousRatio * 100 * 0.1),
1924
- // 10% weight
1776
+ riskContribution: Math.round(Math.min(1, ambiguousRatio) * 100 * 0.1),
1925
1777
  description: `${ambiguousNames} non-descriptive identifiers \u2014 AI guesses wrong intent`
1926
1778
  };
1927
- const undocRatio = Math.min(
1928
- 1,
1929
- undocumentedExports / Math.max(1, totalExports)
1930
- );
1779
+ const undocRatio = undocumentedExports / Math.max(1, totalExports);
1931
1780
  const undocSignal = {
1932
1781
  name: "Undocumented Exports",
1933
1782
  count: undocumentedExports,
1934
- riskContribution: Math.round(undocRatio * 100 * 0.1),
1935
- // 10% weight
1783
+ riskContribution: Math.round(Math.min(1, undocRatio) * 100 * 0.1),
1936
1784
  description: `${undocumentedExports} public functions without docs \u2014 AI fabricates behavior`
1937
1785
  };
1938
1786
  const signals = [
@@ -1957,33 +1805,28 @@ function calculateAiSignalClarity(params) {
1957
1805
  const topSignal = signals.reduce(
1958
1806
  (a, b) => a.riskContribution > b.riskContribution ? a : b
1959
1807
  );
1960
- const topRisk = topSignal.riskContribution > 0 ? topSignal.description : "No significant AI signal claritys detected";
1808
+ const topRisk = topSignal.riskContribution > 0 ? topSignal.description : "No significant issues detected";
1961
1809
  const recommendations = [];
1962
- if (overloadSignal.riskContribution > 5) {
1810
+ if (overloadSignal.riskContribution > 5)
1963
1811
  recommendations.push(
1964
1812
  `Rename ${overloadedSymbols} overloaded symbols to unique, intent-revealing names`
1965
1813
  );
1966
- }
1967
- if (magicSignal.riskContribution > 5) {
1814
+ if (magicSignal.riskContribution > 5)
1968
1815
  recommendations.push(
1969
1816
  `Extract ${magicLiterals} magic literals into named constants`
1970
1817
  );
1971
- }
1972
- if (trapSignal.riskContribution > 5) {
1818
+ if (trapSignal.riskContribution > 5)
1973
1819
  recommendations.push(
1974
1820
  `Replace ${booleanTraps} boolean traps with named options objects`
1975
1821
  );
1976
- }
1977
- if (undocSignal.riskContribution > 5) {
1822
+ if (undocSignal.riskContribution > 5)
1978
1823
  recommendations.push(
1979
1824
  `Add JSDoc/docstrings to ${undocumentedExports} undocumented public functions`
1980
1825
  );
1981
- }
1982
- if (sideEffectSignal.riskContribution > 5) {
1826
+ if (sideEffectSignal.riskContribution > 5)
1983
1827
  recommendations.push(
1984
1828
  "Mark functions with side effects explicitly in their names or docs"
1985
1829
  );
1986
- }
1987
1830
  return {
1988
1831
  score: Math.round(score),
1989
1832
  rating,
@@ -1992,6 +1835,8 @@ function calculateAiSignalClarity(params) {
1992
1835
  recommendations
1993
1836
  };
1994
1837
  }
1838
+
1839
+ // src/metrics/agent-grounding.ts
1995
1840
  function calculateAgentGrounding(params) {
1996
1841
  const {
1997
1842
  deepDirectories,
@@ -2006,25 +1851,33 @@ function calculateAgentGrounding(params) {
2006
1851
  inconsistentDomainTerms,
2007
1852
  domainVocabularySize
2008
1853
  } = params;
2009
- const deepDirRatio = totalDirectories > 0 ? deepDirectories / totalDirectories : 0;
2010
1854
  const structureClarityScore = Math.max(
2011
1855
  0,
2012
- Math.round(100 - deepDirRatio * 80)
1856
+ Math.round(
1857
+ 100 - (totalDirectories > 0 ? deepDirectories / totalDirectories * 80 : 0)
1858
+ )
1859
+ );
1860
+ const selfDocumentationScore = Math.max(
1861
+ 0,
1862
+ Math.round(100 - (totalFiles > 0 ? vagueFileNames / totalFiles * 90 : 0))
2013
1863
  );
2014
- const vagueRatio = totalFiles > 0 ? vagueFileNames / totalFiles : 0;
2015
- const selfDocumentationScore = Math.max(0, Math.round(100 - vagueRatio * 90));
2016
1864
  let entryPointScore = 60;
2017
1865
  if (hasRootReadme) entryPointScore += 25;
2018
1866
  if (readmeIsFresh) entryPointScore += 10;
2019
1867
  const barrelRatio = totalFiles > 0 ? barrelExports / (totalFiles * 0.1) : 0;
2020
1868
  entryPointScore += Math.round(Math.min(5, barrelRatio * 5));
2021
1869
  entryPointScore = Math.min(100, entryPointScore);
2022
- const untypedRatio = totalExports > 0 ? untypedExports / totalExports : 0;
2023
- const apiClarityScore = Math.max(0, Math.round(100 - untypedRatio * 70));
2024
- const inconsistencyRatio = domainVocabularySize > 0 ? inconsistentDomainTerms / domainVocabularySize : 0;
1870
+ const apiClarityScore = Math.max(
1871
+ 0,
1872
+ Math.round(
1873
+ 100 - (totalExports > 0 ? untypedExports / totalExports * 70 : 0)
1874
+ )
1875
+ );
2025
1876
  const domainConsistencyScore = Math.max(
2026
1877
  0,
2027
- Math.round(100 - inconsistencyRatio * 80)
1878
+ Math.round(
1879
+ 100 - (domainVocabularySize > 0 ? inconsistentDomainTerms / domainVocabularySize * 80 : 0)
1880
+ )
2028
1881
  );
2029
1882
  const score = Math.round(
2030
1883
  structureClarityScore * 0.2 + selfDocumentationScore * 0.25 + entryPointScore * 0.2 + apiClarityScore * 0.15 + domainConsistencyScore * 0.2
@@ -2036,35 +1889,30 @@ function calculateAgentGrounding(params) {
2036
1889
  else if (score >= 30) rating = "poor";
2037
1890
  else rating = "disorienting";
2038
1891
  const recommendations = [];
2039
- if (structureClarityScore < 70) {
1892
+ if (structureClarityScore < 70)
2040
1893
  recommendations.push(
2041
1894
  `Flatten ${deepDirectories} overly-deep directories to improve agent navigation`
2042
1895
  );
2043
- }
2044
- if (selfDocumentationScore < 70) {
1896
+ if (selfDocumentationScore < 70)
2045
1897
  recommendations.push(
2046
1898
  `Rename ${vagueFileNames} vague files (utils, helpers, misc) to domain-specific names`
2047
1899
  );
2048
- }
2049
- if (!hasRootReadme) {
1900
+ if (!hasRootReadme)
2050
1901
  recommendations.push(
2051
1902
  "Add a root README.md so agents understand the project context immediately"
2052
1903
  );
2053
- } else if (!readmeIsFresh) {
1904
+ else if (!readmeIsFresh)
2054
1905
  recommendations.push(
2055
1906
  "Update README.md \u2014 stale entry-point documentation disorients agents"
2056
1907
  );
2057
- }
2058
- if (apiClarityScore < 70) {
1908
+ if (apiClarityScore < 70)
2059
1909
  recommendations.push(
2060
1910
  `Add TypeScript types to ${untypedExports} untyped exports to improve API discoverability`
2061
1911
  );
2062
- }
2063
- if (domainConsistencyScore < 70) {
1912
+ if (domainConsistencyScore < 70)
2064
1913
  recommendations.push(
2065
1914
  `Unify ${inconsistentDomainTerms} inconsistent domain terms \u2014 agents need one word per concept`
2066
1915
  );
2067
- }
2068
1916
  return {
2069
1917
  score,
2070
1918
  rating,
@@ -2078,6 +1926,8 @@ function calculateAgentGrounding(params) {
2078
1926
  recommendations
2079
1927
  };
2080
1928
  }
1929
+
1930
+ // src/metrics/testability-index.ts
2081
1931
  function calculateTestabilityIndex(params) {
2082
1932
  const {
2083
1933
  testFiles,
@@ -2093,16 +1943,27 @@ function calculateTestabilityIndex(params) {
2093
1943
  } = params;
2094
1944
  const rawCoverageRatio = sourceFiles > 0 ? testFiles / sourceFiles : 0;
2095
1945
  const testCoverageRatio = Math.min(100, Math.round(rawCoverageRatio * 100));
2096
- const purityRatio = totalFunctions > 0 ? pureFunctions / totalFunctions : 0.5;
2097
- const purityScore = Math.round(purityRatio * 100);
2098
- const injectionRatio = totalClasses > 0 ? injectionPatterns / totalClasses : 0.5;
1946
+ const purityScore = Math.round(
1947
+ (totalFunctions > 0 ? pureFunctions / totalFunctions : 0.5) * 100
1948
+ );
2099
1949
  const dependencyInjectionScore = Math.round(
2100
- Math.min(100, injectionRatio * 100)
1950
+ Math.min(
1951
+ 100,
1952
+ (totalClasses > 0 ? injectionPatterns / totalClasses : 0.5) * 100
1953
+ )
1954
+ );
1955
+ const interfaceFocusScore = Math.max(
1956
+ 0,
1957
+ Math.round(
1958
+ 100 - (totalInterfaces > 0 ? bloatedInterfaces / totalInterfaces * 80 : 0)
1959
+ )
1960
+ );
1961
+ const observabilityScore = Math.max(
1962
+ 0,
1963
+ Math.round(
1964
+ 100 - (totalFunctions > 0 ? externalStateMutations / totalFunctions * 100 : 0)
1965
+ )
2101
1966
  );
2102
- const bloatedRatio = totalInterfaces > 0 ? bloatedInterfaces / totalInterfaces : 0;
2103
- const interfaceFocusScore = Math.max(0, Math.round(100 - bloatedRatio * 80));
2104
- const mutationRatio = totalFunctions > 0 ? externalStateMutations / totalFunctions : 0;
2105
- const observabilityScore = Math.max(0, Math.round(100 - mutationRatio * 100));
2106
1967
  const frameworkWeight = hasTestFramework ? 1 : 0.8;
2107
1968
  const rawScore = (testCoverageRatio * 0.3 + purityScore * 0.25 + dependencyInjectionScore * 0.2 + interfaceFocusScore * 0.1 + observabilityScore * 0.15) * frameworkWeight;
2108
1969
  const score = Math.max(0, Math.min(100, Math.round(rawScore)));
@@ -2119,32 +1980,28 @@ function calculateTestabilityIndex(params) {
2119
1980
  else if (rawCoverageRatio > 0) aiChangeSafetyRating = "high-risk";
2120
1981
  else aiChangeSafetyRating = "blind-risk";
2121
1982
  const recommendations = [];
2122
- if (!hasTestFramework) {
1983
+ if (!hasTestFramework)
2123
1984
  recommendations.push(
2124
1985
  "Add a testing framework (Jest, Vitest, pytest) \u2014 AI changes cannot be verified without tests"
2125
1986
  );
2126
- }
2127
1987
  if (rawCoverageRatio < 0.3) {
2128
1988
  const neededTests = Math.round(sourceFiles * 0.3 - testFiles);
2129
1989
  recommendations.push(
2130
1990
  `Add ~${neededTests} test files to reach 30% coverage ratio \u2014 minimum for safe AI assistance`
2131
1991
  );
2132
1992
  }
2133
- if (purityScore < 50) {
1993
+ if (purityScore < 50)
2134
1994
  recommendations.push(
2135
1995
  "Extract pure functions from side-effectful code \u2014 pure functions are trivially AI-testable"
2136
1996
  );
2137
- }
2138
- if (dependencyInjectionScore < 50 && totalClasses > 0) {
1997
+ if (dependencyInjectionScore < 50 && totalClasses > 0)
2139
1998
  recommendations.push(
2140
1999
  "Adopt dependency injection \u2014 makes classes mockable and AI-generated code verifiable"
2141
2000
  );
2142
- }
2143
- if (externalStateMutations > totalFunctions * 0.3) {
2001
+ if (externalStateMutations > totalFunctions * 0.3)
2144
2002
  recommendations.push(
2145
2003
  "Reduce direct state mutations \u2014 return values instead to improve observability"
2146
2004
  );
2147
- }
2148
2005
  return {
2149
2006
  score,
2150
2007
  rating,
@@ -2159,6 +2016,8 @@ function calculateTestabilityIndex(params) {
2159
2016
  recommendations
2160
2017
  };
2161
2018
  }
2019
+
2020
+ // src/metrics/doc-drift.ts
2162
2021
  function calculateDocDrift(params) {
2163
2022
  const {
2164
2023
  uncommentedExports,
@@ -2181,21 +2040,18 @@ function calculateDocDrift(params) {
2181
2040
  else if (finalScore < 85) rating = "high";
2182
2041
  else rating = "severe";
2183
2042
  const recommendations = [];
2184
- if (outdatedComments > 0) {
2043
+ if (outdatedComments > 0)
2185
2044
  recommendations.push(
2186
2045
  `Update or remove ${outdatedComments} outdated comments that contradict the code.`
2187
2046
  );
2188
- }
2189
- if (uncommentedRatio > 0.3) {
2047
+ if (uncommentedRatio > 0.3)
2190
2048
  recommendations.push(
2191
2049
  `Add JSDoc to ${uncommentedExports} uncommented exports.`
2192
2050
  );
2193
- }
2194
- if (undocumentedComplexity > 0) {
2051
+ if (undocumentedComplexity > 0)
2195
2052
  recommendations.push(
2196
2053
  `Explain the business logic for ${undocumentedComplexity} highly complex functions.`
2197
2054
  );
2198
- }
2199
2055
  return {
2200
2056
  score: finalScore,
2201
2057
  rating,
@@ -2207,6 +2063,8 @@ function calculateDocDrift(params) {
2207
2063
  recommendations
2208
2064
  };
2209
2065
  }
2066
+
2067
+ // src/metrics/dependency-health.ts
2210
2068
  function calculateDependencyHealth(params) {
2211
2069
  const {
2212
2070
  totalPackages,
@@ -2219,8 +2077,12 @@ function calculateDependencyHealth(params) {
2219
2077
  const outdatedScore = Math.max(0, 100 - outdatedRatio * 200);
2220
2078
  const deprecatedScore = Math.max(0, 100 - deprecatedRatio * 500);
2221
2079
  const skewScore = Math.max(0, 100 - trainingCutoffSkew * 100);
2222
- const rawScore = outdatedScore * 0.3 + deprecatedScore * 0.4 + skewScore * 0.3;
2223
- const score = Math.round(Math.min(100, Math.max(0, rawScore)));
2080
+ const score = Math.round(
2081
+ Math.min(
2082
+ 100,
2083
+ Math.max(0, outdatedScore * 0.3 + deprecatedScore * 0.4 + skewScore * 0.3)
2084
+ )
2085
+ );
2224
2086
  let rating;
2225
2087
  if (score >= 85) rating = "excellent";
2226
2088
  else if (score >= 70) rating = "good";
@@ -2235,33 +2097,22 @@ function calculateDependencyHealth(params) {
2235
2097
  else if (trainingCutoffSkew < 0.8) aiKnowledgeConfidence = "low";
2236
2098
  else aiKnowledgeConfidence = "blind";
2237
2099
  const recommendations = [];
2238
- if (deprecatedPackages > 0) {
2239
- recommendations.push(
2240
- `Replace ${deprecatedPackages} deprecated packages, as AI will struggle to find modern solutions.`
2241
- );
2242
- }
2243
- if (outdatedRatio > 0.2) {
2244
- recommendations.push(
2245
- `Update ${outdatedPackages} outdated packages to keep APIs aligned with AI training data.`
2246
- );
2247
- }
2248
- if (trainingCutoffSkew > 0.5) {
2249
- recommendations.push(
2250
- "High training cutoff skew detected. AI may hallucinate APIs that were introduced recently."
2251
- );
2252
- }
2100
+ if (deprecatedPackages > 0)
2101
+ recommendations.push(`Replace ${deprecatedPackages} deprecated packages.`);
2102
+ if (outdatedRatio > 0.2)
2103
+ recommendations.push(`Update ${outdatedPackages} outdated packages.`);
2104
+ if (trainingCutoffSkew > 0.5)
2105
+ recommendations.push("High training cutoff skew detected.");
2253
2106
  return {
2254
2107
  score,
2255
2108
  rating,
2256
- dimensions: {
2257
- outdatedPackages,
2258
- deprecatedPackages,
2259
- trainingCutoffSkew
2260
- },
2109
+ dimensions: { outdatedPackages, deprecatedPackages, trainingCutoffSkew },
2261
2110
  aiKnowledgeConfidence,
2262
2111
  recommendations
2263
2112
  };
2264
2113
  }
2114
+
2115
+ // src/metrics/change-amplification.ts
2265
2116
  function calculateChangeAmplification(params) {
2266
2117
  const { files } = params;
2267
2118
  if (files.length === 0) {
@@ -2274,15 +2125,16 @@ function calculateChangeAmplification(params) {
2274
2125
  recommendations: []
2275
2126
  };
2276
2127
  }
2277
- const hotspots = files.map((f) => {
2278
- const amplificationFactor = f.fanOut + f.fanIn * 0.5;
2279
- return { ...f, amplificationFactor };
2280
- }).sort((a, b) => b.amplificationFactor - a.amplificationFactor);
2128
+ const hotspots = files.map((f) => ({ ...f, amplificationFactor: f.fanOut + f.fanIn * 0.5 })).sort((a, b) => b.amplificationFactor - a.amplificationFactor);
2281
2129
  const maxAmplification = hotspots[0].amplificationFactor;
2282
2130
  const avgAmplification = hotspots.reduce((sum, h) => sum + h.amplificationFactor, 0) / hotspots.length;
2283
- let score = 100 - avgAmplification * 5;
2284
- if (maxAmplification > 20) score -= maxAmplification - 20;
2285
- score = Math.max(0, Math.min(100, score));
2131
+ let score = Math.max(
2132
+ 0,
2133
+ Math.min(
2134
+ 100,
2135
+ 100 - avgAmplification * 5 - (maxAmplification > 20 ? maxAmplification - 20 : 0)
2136
+ )
2137
+ );
2286
2138
  let rating = "isolated";
2287
2139
  if (score < 40) rating = "explosive";
2288
2140
  else if (score < 70) rating = "amplified";
@@ -2290,12 +2142,12 @@ function calculateChangeAmplification(params) {
2290
2142
  const recommendations = [];
2291
2143
  if (score < 70 && hotspots.length > 0) {
2292
2144
  recommendations.push(
2293
- `Refactor top hotspot '${hotspots[0].file}' to reduce coupling (fan-out: ${hotspots[0].fanOut}, fan-in: ${hotspots[0].fanIn}).`
2145
+ `Refactor top hotspot '${hotspots[0].file}' to reduce coupling.`
2294
2146
  );
2295
2147
  }
2296
2148
  if (maxAmplification > 30) {
2297
2149
  recommendations.push(
2298
- `Break down key bottlenecks with amplification factor > 30.`
2150
+ "Break down key bottlenecks with amplification factor > 30."
2299
2151
  );
2300
2152
  }
2301
2153
  return {
@@ -2307,6 +2159,61 @@ function calculateChangeAmplification(params) {
2307
2159
  recommendations
2308
2160
  };
2309
2161
  }
2162
+
2163
+ // src/future-proof-metrics.ts
2164
+ function calculateFutureProofScore(params) {
2165
+ const loadScore = 100 - params.cognitiveLoad.score;
2166
+ const entropyScore = 100 - params.patternEntropy.entropy * 100;
2167
+ const cohesionScore = params.conceptCohesion.score * 100;
2168
+ const overall = Math.round(
2169
+ loadScore * 0.4 + entropyScore * 0.3 + cohesionScore * 0.3
2170
+ );
2171
+ const factors = [
2172
+ {
2173
+ name: "Cognitive Load",
2174
+ impact: Math.round(loadScore - 50),
2175
+ description: params.cognitiveLoad.rating
2176
+ },
2177
+ {
2178
+ name: "Pattern Entropy",
2179
+ impact: Math.round(entropyScore - 50),
2180
+ description: params.patternEntropy.rating
2181
+ },
2182
+ {
2183
+ name: "Concept Cohesion",
2184
+ impact: Math.round(cohesionScore - 50),
2185
+ description: params.conceptCohesion.rating
2186
+ }
2187
+ ];
2188
+ const recommendations = [];
2189
+ for (const rec of params.patternEntropy.recommendations) {
2190
+ recommendations.push({
2191
+ action: rec,
2192
+ estimatedImpact: 5,
2193
+ priority: "medium"
2194
+ });
2195
+ }
2196
+ if (params.conceptCohesion.rating === "poor") {
2197
+ recommendations.push({
2198
+ action: "Improve concept cohesion by grouping related exports",
2199
+ estimatedImpact: 8,
2200
+ priority: "high"
2201
+ });
2202
+ }
2203
+ const semanticDistanceAvg = params.semanticDistances?.length ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
2204
+ return {
2205
+ toolName: "future-proof",
2206
+ score: overall,
2207
+ rawMetrics: {
2208
+ cognitiveLoadScore: params.cognitiveLoad.score,
2209
+ entropyScore: params.patternEntropy.entropy,
2210
+ cohesionScore: params.conceptCohesion.score,
2211
+ semanticDistanceAvg
2212
+ },
2213
+ factors,
2214
+ recommendations
2215
+ };
2216
+ }
2310
2217
  function calculateExtendedFutureProofScore(params) {
2311
2218
  const loadScore = 100 - params.cognitiveLoad.score;
2312
2219
  const entropyScore = 100 - params.patternEntropy.entropy * 100;
@@ -2315,7 +2222,7 @@ function calculateExtendedFutureProofScore(params) {
2315
2222
  const groundingScore = params.agentGrounding.score;
2316
2223
  const testabilityScore = params.testability.score;
2317
2224
  const docDriftScore = params.docDrift ? 100 - params.docDrift.score : 100;
2318
- const depsHealthScore = params.dependencyHealth ? params.dependencyHealth.score : 100;
2225
+ const depsHealthScore = params.dependencyHealth?.score ?? 100;
2319
2226
  let totalWeight = 0.8;
2320
2227
  let overall = loadScore * 0.15 + entropyScore * 0.1 + cohesionScore * 0.1 + aiSignalClarityScore * 0.15 + groundingScore * 0.15 + testabilityScore * 0.15;
2321
2228
  if (params.docDrift) {
@@ -2346,7 +2253,7 @@ function calculateExtendedFutureProofScore(params) {
2346
2253
  {
2347
2254
  name: "AI Signal Clarity",
2348
2255
  impact: Math.round(aiSignalClarityScore - 50),
2349
- description: `${params.aiSignalClarity.rating} risk (${params.aiSignalClarity.score}/100 raw)`
2256
+ description: `${params.aiSignalClarity.rating} risk`
2350
2257
  },
2351
2258
  {
2352
2259
  name: "Agent Grounding",
@@ -2356,60 +2263,25 @@ function calculateExtendedFutureProofScore(params) {
2356
2263
  {
2357
2264
  name: "Testability",
2358
2265
  impact: Math.round(testabilityScore - 50),
2359
- description: `${params.testability.rating} \u2014 AI changes are ${params.testability.aiChangeSafetyRating}`
2266
+ description: params.testability.rating
2360
2267
  }
2361
2268
  ];
2362
2269
  if (params.docDrift) {
2363
2270
  factors.push({
2364
2271
  name: "Documentation Drift",
2365
2272
  impact: Math.round(docDriftScore - 50),
2366
- description: `${params.docDrift.rating} risk of AI signal clarity from drift`
2273
+ description: params.docDrift.rating
2367
2274
  });
2368
2275
  }
2369
2276
  if (params.dependencyHealth) {
2370
2277
  factors.push({
2371
2278
  name: "Dependency Health",
2372
2279
  impact: Math.round(depsHealthScore - 50),
2373
- description: `${params.dependencyHealth.rating} health \u2014 AI knowledge is ${params.dependencyHealth.aiKnowledgeConfidence}`
2374
- });
2375
- }
2376
- const recommendations = [];
2377
- for (const rec of params.aiSignalClarity.recommendations) {
2378
- recommendations.push({ action: rec, estimatedImpact: 8, priority: "high" });
2379
- }
2380
- for (const rec of params.agentGrounding.recommendations) {
2381
- recommendations.push({
2382
- action: rec,
2383
- estimatedImpact: 6,
2384
- priority: "medium"
2280
+ description: params.dependencyHealth.rating
2385
2281
  });
2386
2282
  }
2387
- for (const rec of params.testability.recommendations) {
2388
- const priority = params.testability.aiChangeSafetyRating === "blind-risk" ? "high" : "medium";
2389
- recommendations.push({ action: rec, estimatedImpact: 10, priority });
2390
- }
2391
- for (const rec of params.patternEntropy.recommendations) {
2392
- recommendations.push({ action: rec, estimatedImpact: 5, priority: "low" });
2393
- }
2394
- if (params.docDrift) {
2395
- for (const rec of params.docDrift.recommendations) {
2396
- recommendations.push({
2397
- action: rec,
2398
- estimatedImpact: 8,
2399
- priority: "high"
2400
- });
2401
- }
2402
- }
2403
- if (params.dependencyHealth) {
2404
- for (const rec of params.dependencyHealth.recommendations) {
2405
- recommendations.push({
2406
- action: rec,
2407
- estimatedImpact: 7,
2408
- priority: "medium"
2409
- });
2410
- }
2411
- }
2412
- const semanticDistanceAvg = params.semanticDistances && params.semanticDistances.length > 0 ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
2283
+ const recommendations = collectFutureProofRecommendations(params);
2284
+ const semanticDistanceAvg = params.semanticDistances?.length ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
2413
2285
  return {
2414
2286
  toolName: "future-proof",
2415
2287
  score: overall,
@@ -2585,16 +2457,19 @@ export {
2585
2457
  ParseError,
2586
2458
  ParserFactory,
2587
2459
  PythonParser,
2460
+ SEVERITY_TIME_ESTIMATES,
2588
2461
  SIZE_ADJUSTED_THRESHOLDS,
2589
2462
  TOOL_NAME_MAP,
2590
2463
  TypeScriptParser,
2591
2464
  VAGUE_FILE_NAMES,
2592
2465
  calculateAgentGrounding,
2593
2466
  calculateAiSignalClarity,
2467
+ calculateBusinessROI,
2594
2468
  calculateChangeAmplification,
2595
2469
  calculateCognitiveLoad,
2596
2470
  calculateComprehensionDifficulty,
2597
2471
  calculateConceptCohesion,
2472
+ calculateDebtInterest,
2598
2473
  calculateDependencyHealth,
2599
2474
  calculateDocDrift,
2600
2475
  calculateExtendedFutureProofScore,
@@ -2605,10 +2480,8 @@ export {
2605
2480
  calculateOverallScore,
2606
2481
  calculatePatternEntropy,
2607
2482
  calculateProductivityImpact,
2608
- calculateRemediationVelocity,
2609
- calculateScoreTrend,
2610
2483
  calculateSemanticDistance,
2611
- calculateTechnicalDebtInterest,
2484
+ calculateTechnicalValueChain,
2612
2485
  calculateTestabilityIndex,
2613
2486
  calculateTokenBudget,
2614
2487
  clearHistory,
@@ -2624,7 +2497,6 @@ export {
2624
2497
  formatToolScore,
2625
2498
  generateHTML,
2626
2499
  generateValueChain,
2627
- getDebtBreakdown,
2628
2500
  getElapsedTime,
2629
2501
  getFileCommitTimestamps,
2630
2502
  getFileExtension,
@@ -2638,6 +2510,9 @@ export {
2638
2510
  getRatingWithContext,
2639
2511
  getRecommendedThreshold,
2640
2512
  getRepoMetadata,
2513
+ getSafetyIcon,
2514
+ getScoreBar,
2515
+ getSeverityColor,
2641
2516
  getSupportedLanguages,
2642
2517
  getToolWeight,
2643
2518
  handleCLIError,