@aiready/core 0.9.38 → 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
@@ -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,
@@ -633,7 +655,6 @@ function calculateTokenBudget(params) {
633
655
  },
634
656
  efficiencyRatio: Math.round(efficiencyRatio * 100) / 100,
635
657
  potentialRetrievableTokens: Math.round(totalWaste * 0.8)
636
- // Heuristic: 80% is fixable
637
658
  };
638
659
  }
639
660
  function estimateCostFromBudget(budget, model, config = {}) {
@@ -657,6 +678,15 @@ function estimateCostFromBudget(budget, model, config = {}) {
657
678
  confidence
658
679
  };
659
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;
660
690
  function calculateProductivityImpact(issues, hourlyRate = DEFAULT_HOURLY_RATE) {
661
691
  const counts = {
662
692
  critical: issues.filter((i) => i.severity === "critical").length,
@@ -697,108 +727,158 @@ function predictAcceptanceRate(toolOutputs) {
697
727
  const baseRate = 0.3;
698
728
  const patterns = toolOutputs.get("pattern-detect");
699
729
  if (patterns) {
700
- const patternImpact = (patterns.score - 50) * 3e-3;
701
730
  factors.push({
702
731
  name: "Semantic Duplication",
703
- impact: Math.round(patternImpact * 100)
732
+ impact: Math.round((patterns.score - 50) * 3e-3 * 100)
704
733
  });
705
734
  }
706
735
  const context = toolOutputs.get("context-analyzer");
707
736
  if (context) {
708
- const contextImpact = (context.score - 50) * 4e-3;
709
737
  factors.push({
710
738
  name: "Context Efficiency",
711
- impact: Math.round(contextImpact * 100)
739
+ impact: Math.round((context.score - 50) * 4e-3 * 100)
712
740
  });
713
741
  }
714
742
  const consistency = toolOutputs.get("consistency");
715
743
  if (consistency) {
716
- const consistencyImpact = (consistency.score - 50) * 2e-3;
717
744
  factors.push({
718
745
  name: "Code Consistency",
719
- impact: Math.round(consistencyImpact * 100)
746
+ impact: Math.round((consistency.score - 50) * 2e-3 * 100)
720
747
  });
721
748
  }
722
749
  const aiSignalClarity = toolOutputs.get("ai-signal-clarity");
723
750
  if (aiSignalClarity) {
724
- const hrImpact = (50 - aiSignalClarity.score) * 2e-3;
725
751
  factors.push({
726
752
  name: "AI Signal Clarity",
727
- impact: Math.round(hrImpact * 100)
753
+ impact: Math.round((50 - aiSignalClarity.score) * 2e-3 * 100)
728
754
  });
729
755
  }
730
756
  const totalImpact = factors.reduce((sum, f) => sum + f.impact / 100, 0);
731
757
  const rate = Math.max(0.05, Math.min(0.8, baseRate + totalImpact));
732
- let confidence;
758
+ let confidence = 0.35;
733
759
  if (toolOutputs.size >= 4) confidence = 0.75;
734
760
  else if (toolOutputs.size >= 3) confidence = 0.65;
735
761
  else if (toolOutputs.size >= 2) confidence = 0.5;
736
- else confidence = 0.35;
737
- return {
738
- rate: Math.round(rate * 100) / 100,
739
- confidence,
740
- factors
741
- };
762
+ return { rate: Math.round(rate * 100) / 100, confidence, factors };
742
763
  }
743
- function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentation, consistencyScore, totalFiles, modelTier = "standard") {
744
- const tierThresholds = CONTEXT_TIER_THRESHOLDS[modelTier];
745
- const idealBudget = tierThresholds.idealTokens;
746
- const criticalBudget = tierThresholds.criticalTokens;
747
- const idealDepth = tierThresholds.idealDepth;
748
- const budgetRange = criticalBudget - idealBudget;
749
- const budgetFactor = Math.min(
750
- 100,
751
- Math.max(0, (contextBudget - idealBudget) / budgetRange * 100)
752
- );
753
- const depthFactor = Math.min(
754
- 100,
755
- Math.max(0, (importDepth - idealDepth) * 10)
756
- );
757
- const fragmentationFactor = Math.min(
758
- 100,
759
- Math.max(0, (fragmentation - 0.3) * 250)
760
- );
761
- const consistencyFactor = Math.min(100, Math.max(0, 100 - consistencyScore));
762
- 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;
763
769
  const score = Math.round(
764
- 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
+ )
765
774
  );
766
775
  let rating;
767
- if (score < 20) rating = "trivial";
768
- else if (score < 40) rating = "easy";
769
- else if (score < 60) rating = "moderate";
770
- else if (score < 80) rating = "difficult";
771
- 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
+ );
772
789
  return {
773
790
  score,
774
791
  rating,
775
- factors: [
776
- {
777
- name: "Context Budget",
778
- contribution: Math.round(budgetFactor * 0.35),
779
- description: `${Math.round(contextBudget)} tokens required (${modelTier} model tier: ideal <${idealBudget.toLocaleString()})`
780
- },
781
- {
782
- name: "Import Depth",
783
- contribution: Math.round(depthFactor * 0.2),
784
- description: `${importDepth.toFixed(1)} average levels (ideal <${idealDepth} for ${modelTier})`
785
- },
786
- {
787
- name: "Code Fragmentation",
788
- contribution: Math.round(fragmentationFactor * 0.2),
789
- description: `${(fragmentation * 100).toFixed(0)}% fragmentation`
790
- },
791
- {
792
- name: "Consistency",
793
- contribution: Math.round(consistencyFactor * 0.15),
794
- description: `${consistencyScore}/100 consistency score`
795
- },
796
- {
797
- name: "Project Scale",
798
- contribution: Math.round(fileFactor * 0.1),
799
- description: `${totalFiles} files analyzed`
800
- }
801
- ]
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)
802
882
  };
803
883
  }
804
884
  function formatCost(cost) {
@@ -824,220 +904,6 @@ function formatHours(hours) {
824
904
  function formatAcceptanceRate(rate) {
825
905
  return `${Math.round(rate * 100)}%`;
826
906
  }
827
- function calculateScoreTrend(history) {
828
- if (history.length < 2) {
829
- return {
830
- direction: "stable",
831
- change30Days: 0,
832
- change90Days: 0,
833
- velocity: 0,
834
- projectedScore: history[0]?.overallScore || 100
835
- };
836
- }
837
- const now = /* @__PURE__ */ new Date();
838
- const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1e3);
839
- const ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1e3);
840
- const last30Days = history.filter(
841
- (e) => new Date(e.timestamp) >= thirtyDaysAgo
842
- );
843
- const last90Days = history.filter(
844
- (e) => new Date(e.timestamp) >= ninetyDaysAgo
845
- );
846
- const currentScore = history[history.length - 1].overallScore;
847
- const thirtyDaysAgoScore = last30Days[0]?.overallScore || currentScore;
848
- const ninetyDaysAgoScore = last90Days[0]?.overallScore || thirtyDaysAgoScore;
849
- const change30Days = currentScore - thirtyDaysAgoScore;
850
- const change90Days = currentScore - ninetyDaysAgoScore;
851
- const weeksOfData = Math.max(1, history.length / 7);
852
- const totalChange = currentScore - history[0].overallScore;
853
- const velocity = totalChange / weeksOfData;
854
- let direction;
855
- if (change30Days > 3) direction = "improving";
856
- else if (change30Days < -3) direction = "degrading";
857
- else direction = "stable";
858
- const projectedScore = Math.max(
859
- 0,
860
- Math.min(100, currentScore + velocity * 4)
861
- );
862
- return {
863
- direction,
864
- change30Days,
865
- change90Days,
866
- velocity: Math.round(velocity * 10) / 10,
867
- projectedScore: Math.round(projectedScore)
868
- };
869
- }
870
- function calculateRemediationVelocity(history, currentIssues) {
871
- if (history.length < 2) {
872
- return {
873
- issuesFixedThisWeek: 0,
874
- avgIssuesPerWeek: 0,
875
- trend: "stable",
876
- estimatedCompletionWeeks: currentIssues > 0 ? Infinity : 0
877
- };
878
- }
879
- const now = /* @__PURE__ */ new Date();
880
- const oneWeekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3);
881
- const twoWeeksAgo = new Date(now.getTime() - 14 * 24 * 60 * 60 * 1e3);
882
- const thisWeek = history.filter((e) => new Date(e.timestamp) >= oneWeekAgo);
883
- const lastWeek = history.filter(
884
- (e) => new Date(e.timestamp) >= twoWeeksAgo && new Date(e.timestamp) < oneWeekAgo
885
- );
886
- const issuesFixedThisWeek = thisWeek.length > 1 ? thisWeek[0].totalIssues - thisWeek[thisWeek.length - 1].totalIssues : 0;
887
- const totalIssuesFixed = history[0].totalIssues - history[history.length - 1].totalIssues;
888
- const weeksOfData = Math.max(1, history.length / 7);
889
- const avgIssuesPerWeek = totalIssuesFixed / weeksOfData;
890
- let trend;
891
- if (lastWeek.length > 1) {
892
- const lastWeekFixed = lastWeek[0].totalIssues - lastWeek[lastWeek.length - 1].totalIssues;
893
- if (issuesFixedThisWeek > lastWeekFixed * 1.2) trend = "accelerating";
894
- else if (issuesFixedThisWeek < lastWeekFixed * 0.8) trend = "decelerating";
895
- else trend = "stable";
896
- } else {
897
- trend = "stable";
898
- }
899
- const estimatedCompletionWeeks = avgIssuesPerWeek > 0 ? Math.ceil(currentIssues / avgIssuesPerWeek) : Infinity;
900
- return {
901
- issuesFixedThisWeek: Math.max(0, issuesFixedThisWeek),
902
- avgIssuesPerWeek: Math.round(avgIssuesPerWeek * 10) / 10,
903
- trend,
904
- estimatedCompletionWeeks
905
- };
906
- }
907
- function calculateKnowledgeConcentration(files, authorData) {
908
- if (files.length === 0) {
909
- return {
910
- score: 0,
911
- rating: "low",
912
- analysis: {
913
- uniqueConceptFiles: 0,
914
- totalFiles: 0,
915
- concentrationRatio: 0,
916
- singleAuthorFiles: 0,
917
- orphanFiles: 0
918
- },
919
- recommendations: ["No files to analyze"]
920
- };
921
- }
922
- const orphanFiles = files.filter(
923
- (f) => f.exports < 2 && f.imports < 2
924
- ).length;
925
- const avgExports = files.reduce((sum, f) => sum + f.exports, 0) / files.length;
926
- const uniqueConceptFiles = files.filter(
927
- (f) => f.exports > avgExports * 2
928
- ).length;
929
- const totalExports = files.reduce((sum, f) => sum + f.exports, 0);
930
- const concentrationRatio = totalExports > 0 ? uniqueConceptFiles / files.length : 0;
931
- let singleAuthorFiles = 0;
932
- if (authorData) {
933
- for (const files2 of authorData.values()) {
934
- if (files2.length === 1) singleAuthorFiles++;
935
- }
936
- }
937
- const orphanRisk = orphanFiles / files.length * 30;
938
- const uniqueRisk = concentrationRatio * 40;
939
- const singleAuthorRisk = authorData ? singleAuthorFiles / files.length * 30 : 0;
940
- const score = Math.min(
941
- 100,
942
- Math.round(orphanRisk + uniqueRisk + singleAuthorRisk)
943
- );
944
- let rating;
945
- if (score < 20) rating = "low";
946
- else if (score < 40) rating = "moderate";
947
- else if (score < 70) rating = "high";
948
- else rating = "critical";
949
- const recommendations = [];
950
- if (orphanFiles > files.length * 0.2) {
951
- recommendations.push(
952
- `Reduce ${orphanFiles} orphan files by connecting them to main modules`
953
- );
954
- }
955
- if (uniqueConceptFiles > files.length * 0.1) {
956
- recommendations.push(
957
- "Distribute high-export files into more focused modules"
958
- );
959
- }
960
- if (authorData && singleAuthorFiles > files.length * 0.3) {
961
- recommendations.push(
962
- "Increase knowledge sharing to reduce single-author dependencies"
963
- );
964
- }
965
- return {
966
- score,
967
- rating,
968
- analysis: {
969
- uniqueConceptFiles,
970
- totalFiles: files.length,
971
- concentrationRatio: Math.round(concentrationRatio * 100) / 100,
972
- singleAuthorFiles,
973
- orphanFiles
974
- },
975
- recommendations
976
- };
977
- }
978
- function calculateTechnicalDebtInterest(params) {
979
- const { currentMonthlyCost, issues, monthsOpen } = params;
980
- const criticalCount = issues.filter((i) => i.severity === "critical").length;
981
- const majorCount = issues.filter((i) => i.severity === "major").length;
982
- const minorCount = issues.filter((i) => i.severity === "minor").length;
983
- const severityWeight = (criticalCount * 3 + majorCount * 2 + minorCount * 1) / Math.max(1, issues.length);
984
- const baseRate = 0.02 + severityWeight * 0.01;
985
- const timeMultiplier = Math.max(1, 1 + monthsOpen * 0.1);
986
- const monthlyRate = baseRate * timeMultiplier;
987
- const projectDebt = (principal2, months) => {
988
- let debt = principal2;
989
- for (let i = 0; i < months; i++) {
990
- debt = debt * (1 + monthlyRate);
991
- }
992
- return Math.round(debt);
993
- };
994
- const principal = currentMonthlyCost * 12;
995
- const projections = {
996
- months6: projectDebt(principal, 6),
997
- months12: projectDebt(principal, 12),
998
- months24: projectDebt(principal, 24)
999
- };
1000
- return {
1001
- monthlyRate: Math.round(monthlyRate * 1e4) / 100,
1002
- annualRate: Math.round((Math.pow(1 + monthlyRate, 12) - 1) * 1e4) / 100,
1003
- principal,
1004
- projections,
1005
- monthlyCost: Math.round(currentMonthlyCost * (1 + monthlyRate) * 100) / 100
1006
- };
1007
- }
1008
- function getDebtBreakdown(patternCost, contextCost, consistencyCost) {
1009
- const breakdowns = [
1010
- {
1011
- category: "Semantic Duplication",
1012
- currentCost: patternCost,
1013
- monthlyGrowthRate: 5,
1014
- // Grows as devs copy-paste
1015
- priority: patternCost > 1e3 ? "high" : "medium",
1016
- fixCost: patternCost * 3
1017
- // Fixing costs 3x current waste
1018
- },
1019
- {
1020
- category: "Context Fragmentation",
1021
- currentCost: contextCost,
1022
- monthlyGrowthRate: 3,
1023
- // Grows with new features
1024
- priority: contextCost > 500 ? "high" : "medium",
1025
- fixCost: contextCost * 2.5
1026
- },
1027
- {
1028
- category: "Consistency Issues",
1029
- currentCost: consistencyCost,
1030
- monthlyGrowthRate: 2,
1031
- // Grows with new devs
1032
- priority: consistencyCost > 200 ? "medium" : "low",
1033
- fixCost: consistencyCost * 1.5
1034
- }
1035
- ];
1036
- return breakdowns.sort((a, b) => {
1037
- const priorityOrder = { high: 0, medium: 1, low: 2 };
1038
- return priorityOrder[a.priority] - priorityOrder[b.priority];
1039
- });
1040
- }
1041
907
  function generateValueChain(params) {
1042
908
  const { issueType, count, severity } = params;
1043
909
  const impacts = {
@@ -1077,9 +943,7 @@ function generateValueChain(params) {
1077
943
  },
1078
944
  businessOutcome: {
1079
945
  directCost: count * 12,
1080
- // Placeholder linear cost
1081
946
  opportunityCost: productivityLoss * 15e3,
1082
- // Monthly avg dev cost $15k
1083
947
  riskLevel: impact.risk
1084
948
  }
1085
949
  };
@@ -1594,7 +1458,55 @@ function getSupportedLanguages() {
1594
1458
  return ParserFactory.getInstance().getSupportedLanguages();
1595
1459
  }
1596
1460
 
1597
- // 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
1598
1510
  function calculateCognitiveLoad(params) {
1599
1511
  const {
1600
1512
  linesOfCode,
@@ -1653,13 +1565,20 @@ function calculateCognitiveLoad(params) {
1653
1565
  }
1654
1566
  };
1655
1567
  }
1568
+
1569
+ // src/metrics/semantic-distance.ts
1656
1570
  function calculateSemanticDistance(params) {
1657
- 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;
1658
1580
  const domainDistance = file1Domain === file2Domain ? 0 : file1Domain && file2Domain ? 0.5 : 0.8;
1659
- const importOverlap = sharedDependencies.length / Math.max(
1660
- 1,
1661
- Math.min(params.file1Imports.length, params.file2Imports.length)
1662
- );
1581
+ const importOverlap = sharedDependencies.length / Math.max(1, Math.min(file1Imports.length, file2Imports.length));
1663
1582
  const importDistance = 1 - importOverlap;
1664
1583
  const distance = domainDistance * 0.4 + importDistance * 0.3 + (sharedDependencies.length > 0 ? 0 : 0.3);
1665
1584
  let relationship;
@@ -1678,6 +1597,8 @@ function calculateSemanticDistance(params) {
1678
1597
  reason: relationship === "same-domain" ? `Both in "${file1Domain}" domain` : relationship === "cross-domain" ? `Share ${sharedDependencies.length} dependency(ies)` : "No strong semantic relationship detected"
1679
1598
  };
1680
1599
  }
1600
+
1601
+ // src/metrics/structural-metrics.ts
1681
1602
  function calculatePatternEntropy(files) {
1682
1603
  if (files.length === 0) {
1683
1604
  return {
@@ -1727,23 +1648,18 @@ function calculatePatternEntropy(files) {
1727
1648
  else if (normalizedEntropy < 0.8) rating = "fragmented";
1728
1649
  else rating = "chaotic";
1729
1650
  const recommendations = [];
1730
- if (normalizedEntropy > 0.5) {
1651
+ if (normalizedEntropy > 0.5)
1731
1652
  recommendations.push(
1732
1653
  `Consolidate ${files.length} files into fewer directories by domain`
1733
1654
  );
1734
- }
1735
- if (dirGroups.size > 5) {
1655
+ if (dirGroups.size > 5)
1736
1656
  recommendations.push(
1737
1657
  "Consider barrel exports to reduce directory navigation"
1738
1658
  );
1739
- }
1740
- if (gini > 0.5) {
1659
+ if (gini > 0.5)
1741
1660
  recommendations.push("Redistribute files more evenly across directories");
1742
- }
1743
- const firstFile = files.length > 0 ? files[0] : null;
1744
- const domainValue = firstFile ? firstFile.domain : "mixed";
1745
1661
  return {
1746
- domain: domainValue,
1662
+ domain: files[0]?.domain || "mixed",
1747
1663
  entropy: Math.round(normalizedEntropy * 100) / 100,
1748
1664
  rating,
1749
1665
  distribution: {
@@ -1774,9 +1690,8 @@ function calculateConceptCohesion(params) {
1774
1690
  }
1775
1691
  const uniqueDomains = new Set(allDomains);
1776
1692
  const domainCounts = /* @__PURE__ */ new Map();
1777
- for (const d of allDomains) {
1693
+ for (const d of allDomains)
1778
1694
  domainCounts.set(d, (domainCounts.get(d) || 0) + 1);
1779
- }
1780
1695
  const maxCount = Math.max(...Array.from(domainCounts.values()), 1);
1781
1696
  const domainConcentration = maxCount / allDomains.length;
1782
1697
  const exportPurposeClarity = 1 - (uniqueDomains.size - 1) / Math.max(1, exports.length);
@@ -1796,59 +1711,8 @@ function calculateConceptCohesion(params) {
1796
1711
  }
1797
1712
  };
1798
1713
  }
1799
- function calculateFutureProofScore(params) {
1800
- const loadScore = 100 - params.cognitiveLoad.score;
1801
- const entropyScore = 100 - params.patternEntropy.entropy * 100;
1802
- const cohesionScore = params.conceptCohesion.score * 100;
1803
- const overall = Math.round(
1804
- loadScore * 0.4 + entropyScore * 0.3 + cohesionScore * 0.3
1805
- );
1806
- const factors = [
1807
- {
1808
- name: "Cognitive Load",
1809
- impact: Math.round(loadScore - 50),
1810
- description: params.cognitiveLoad.rating
1811
- },
1812
- {
1813
- name: "Pattern Entropy",
1814
- impact: Math.round(entropyScore - 50),
1815
- description: params.patternEntropy.rating
1816
- },
1817
- {
1818
- name: "Concept Cohesion",
1819
- impact: Math.round(cohesionScore - 50),
1820
- description: params.conceptCohesion.rating
1821
- }
1822
- ];
1823
- const recommendations = [];
1824
- for (const rec of params.patternEntropy.recommendations) {
1825
- recommendations.push({
1826
- action: rec,
1827
- estimatedImpact: 5,
1828
- priority: "medium"
1829
- });
1830
- }
1831
- if (params.conceptCohesion.rating === "poor") {
1832
- recommendations.push({
1833
- action: "Improve concept cohesion by grouping related exports",
1834
- estimatedImpact: 8,
1835
- priority: "high"
1836
- });
1837
- }
1838
- const semanticDistanceAvg = params.semanticDistances && params.semanticDistances.length > 0 ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
1839
- return {
1840
- toolName: "future-proof",
1841
- score: overall,
1842
- rawMetrics: {
1843
- cognitiveLoadScore: params.cognitiveLoad.score,
1844
- entropyScore: params.patternEntropy.entropy,
1845
- cohesionScore: params.conceptCohesion.score,
1846
- semanticDistanceAvg
1847
- },
1848
- factors,
1849
- recommendations
1850
- };
1851
- }
1714
+
1715
+ // src/metrics/ai-signal-clarity.ts
1852
1716
  function calculateAiSignalClarity(params) {
1853
1717
  const {
1854
1718
  overloadedSymbols,
@@ -1870,75 +1734,53 @@ function calculateAiSignalClarity(params) {
1870
1734
  recommendations: []
1871
1735
  };
1872
1736
  }
1873
- const overloadRatio = Math.min(
1874
- 1,
1875
- overloadedSymbols / Math.max(1, totalSymbols)
1876
- );
1737
+ const overloadRatio = overloadedSymbols / Math.max(1, totalSymbols);
1877
1738
  const overloadSignal = {
1878
1739
  name: "Symbol Overloading",
1879
1740
  count: overloadedSymbols,
1880
- riskContribution: Math.round(overloadRatio * 100 * 0.25),
1881
- // 25% weight
1741
+ riskContribution: Math.round(Math.min(1, overloadRatio) * 100 * 0.25),
1882
1742
  description: `${overloadedSymbols} overloaded symbols \u2014 AI picks wrong signature`
1883
1743
  };
1884
- const magicRatio = Math.min(1, magicLiterals / Math.max(1, totalSymbols * 2));
1744
+ const magicRatio = magicLiterals / Math.max(1, totalSymbols * 2);
1885
1745
  const magicSignal = {
1886
1746
  name: "Magic Literals",
1887
1747
  count: magicLiterals,
1888
- riskContribution: Math.round(magicRatio * 100 * 0.2),
1889
- // 20% weight
1748
+ riskContribution: Math.round(Math.min(1, magicRatio) * 100 * 0.2),
1890
1749
  description: `${magicLiterals} unnamed constants \u2014 AI invents wrong values`
1891
1750
  };
1892
- const trapRatio = Math.min(1, booleanTraps / Math.max(1, totalSymbols));
1751
+ const trapRatio = booleanTraps / Math.max(1, totalSymbols);
1893
1752
  const trapSignal = {
1894
1753
  name: "Boolean Traps",
1895
1754
  count: booleanTraps,
1896
- riskContribution: Math.round(trapRatio * 100 * 0.2),
1897
- // 20% weight
1755
+ riskContribution: Math.round(Math.min(1, trapRatio) * 100 * 0.2),
1898
1756
  description: `${booleanTraps} boolean trap parameters \u2014 AI inverts intent`
1899
1757
  };
1900
- const sideEffectRatio = Math.min(
1901
- 1,
1902
- implicitSideEffects / Math.max(1, totalExports)
1903
- );
1758
+ const sideEffectRatio = implicitSideEffects / Math.max(1, totalExports);
1904
1759
  const sideEffectSignal = {
1905
1760
  name: "Implicit Side Effects",
1906
1761
  count: implicitSideEffects,
1907
- riskContribution: Math.round(sideEffectRatio * 100 * 0.15),
1908
- // 15% weight
1762
+ riskContribution: Math.round(Math.min(1, sideEffectRatio) * 100 * 0.15),
1909
1763
  description: `${implicitSideEffects} functions with implicit side effects \u2014 AI misses contracts`
1910
1764
  };
1911
- const callbackRatio = Math.min(
1912
- 1,
1913
- deepCallbacks / Math.max(1, totalSymbols * 0.1)
1914
- );
1765
+ const callbackRatio = deepCallbacks / Math.max(1, totalSymbols * 0.1);
1915
1766
  const callbackSignal = {
1916
1767
  name: "Callback Nesting",
1917
1768
  count: deepCallbacks,
1918
- riskContribution: Math.round(callbackRatio * 100 * 0.1),
1919
- // 10% weight
1769
+ riskContribution: Math.round(Math.min(1, callbackRatio) * 100 * 0.1),
1920
1770
  description: `${deepCallbacks} deep callback chains \u2014 AI loses control flow context`
1921
1771
  };
1922
- const ambiguousRatio = Math.min(
1923
- 1,
1924
- ambiguousNames / Math.max(1, totalSymbols)
1925
- );
1772
+ const ambiguousRatio = ambiguousNames / Math.max(1, totalSymbols);
1926
1773
  const ambiguousSignal = {
1927
1774
  name: "Ambiguous Names",
1928
1775
  count: ambiguousNames,
1929
- riskContribution: Math.round(ambiguousRatio * 100 * 0.1),
1930
- // 10% weight
1776
+ riskContribution: Math.round(Math.min(1, ambiguousRatio) * 100 * 0.1),
1931
1777
  description: `${ambiguousNames} non-descriptive identifiers \u2014 AI guesses wrong intent`
1932
1778
  };
1933
- const undocRatio = Math.min(
1934
- 1,
1935
- undocumentedExports / Math.max(1, totalExports)
1936
- );
1779
+ const undocRatio = undocumentedExports / Math.max(1, totalExports);
1937
1780
  const undocSignal = {
1938
1781
  name: "Undocumented Exports",
1939
1782
  count: undocumentedExports,
1940
- riskContribution: Math.round(undocRatio * 100 * 0.1),
1941
- // 10% weight
1783
+ riskContribution: Math.round(Math.min(1, undocRatio) * 100 * 0.1),
1942
1784
  description: `${undocumentedExports} public functions without docs \u2014 AI fabricates behavior`
1943
1785
  };
1944
1786
  const signals = [
@@ -1963,33 +1805,28 @@ function calculateAiSignalClarity(params) {
1963
1805
  const topSignal = signals.reduce(
1964
1806
  (a, b) => a.riskContribution > b.riskContribution ? a : b
1965
1807
  );
1966
- 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";
1967
1809
  const recommendations = [];
1968
- if (overloadSignal.riskContribution > 5) {
1810
+ if (overloadSignal.riskContribution > 5)
1969
1811
  recommendations.push(
1970
1812
  `Rename ${overloadedSymbols} overloaded symbols to unique, intent-revealing names`
1971
1813
  );
1972
- }
1973
- if (magicSignal.riskContribution > 5) {
1814
+ if (magicSignal.riskContribution > 5)
1974
1815
  recommendations.push(
1975
1816
  `Extract ${magicLiterals} magic literals into named constants`
1976
1817
  );
1977
- }
1978
- if (trapSignal.riskContribution > 5) {
1818
+ if (trapSignal.riskContribution > 5)
1979
1819
  recommendations.push(
1980
1820
  `Replace ${booleanTraps} boolean traps with named options objects`
1981
1821
  );
1982
- }
1983
- if (undocSignal.riskContribution > 5) {
1822
+ if (undocSignal.riskContribution > 5)
1984
1823
  recommendations.push(
1985
1824
  `Add JSDoc/docstrings to ${undocumentedExports} undocumented public functions`
1986
1825
  );
1987
- }
1988
- if (sideEffectSignal.riskContribution > 5) {
1826
+ if (sideEffectSignal.riskContribution > 5)
1989
1827
  recommendations.push(
1990
1828
  "Mark functions with side effects explicitly in their names or docs"
1991
1829
  );
1992
- }
1993
1830
  return {
1994
1831
  score: Math.round(score),
1995
1832
  rating,
@@ -1998,6 +1835,8 @@ function calculateAiSignalClarity(params) {
1998
1835
  recommendations
1999
1836
  };
2000
1837
  }
1838
+
1839
+ // src/metrics/agent-grounding.ts
2001
1840
  function calculateAgentGrounding(params) {
2002
1841
  const {
2003
1842
  deepDirectories,
@@ -2012,25 +1851,33 @@ function calculateAgentGrounding(params) {
2012
1851
  inconsistentDomainTerms,
2013
1852
  domainVocabularySize
2014
1853
  } = params;
2015
- const deepDirRatio = totalDirectories > 0 ? deepDirectories / totalDirectories : 0;
2016
1854
  const structureClarityScore = Math.max(
2017
1855
  0,
2018
- 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))
2019
1863
  );
2020
- const vagueRatio = totalFiles > 0 ? vagueFileNames / totalFiles : 0;
2021
- const selfDocumentationScore = Math.max(0, Math.round(100 - vagueRatio * 90));
2022
1864
  let entryPointScore = 60;
2023
1865
  if (hasRootReadme) entryPointScore += 25;
2024
1866
  if (readmeIsFresh) entryPointScore += 10;
2025
1867
  const barrelRatio = totalFiles > 0 ? barrelExports / (totalFiles * 0.1) : 0;
2026
1868
  entryPointScore += Math.round(Math.min(5, barrelRatio * 5));
2027
1869
  entryPointScore = Math.min(100, entryPointScore);
2028
- const untypedRatio = totalExports > 0 ? untypedExports / totalExports : 0;
2029
- const apiClarityScore = Math.max(0, Math.round(100 - untypedRatio * 70));
2030
- 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
+ );
2031
1876
  const domainConsistencyScore = Math.max(
2032
1877
  0,
2033
- Math.round(100 - inconsistencyRatio * 80)
1878
+ Math.round(
1879
+ 100 - (domainVocabularySize > 0 ? inconsistentDomainTerms / domainVocabularySize * 80 : 0)
1880
+ )
2034
1881
  );
2035
1882
  const score = Math.round(
2036
1883
  structureClarityScore * 0.2 + selfDocumentationScore * 0.25 + entryPointScore * 0.2 + apiClarityScore * 0.15 + domainConsistencyScore * 0.2
@@ -2042,35 +1889,30 @@ function calculateAgentGrounding(params) {
2042
1889
  else if (score >= 30) rating = "poor";
2043
1890
  else rating = "disorienting";
2044
1891
  const recommendations = [];
2045
- if (structureClarityScore < 70) {
1892
+ if (structureClarityScore < 70)
2046
1893
  recommendations.push(
2047
1894
  `Flatten ${deepDirectories} overly-deep directories to improve agent navigation`
2048
1895
  );
2049
- }
2050
- if (selfDocumentationScore < 70) {
1896
+ if (selfDocumentationScore < 70)
2051
1897
  recommendations.push(
2052
1898
  `Rename ${vagueFileNames} vague files (utils, helpers, misc) to domain-specific names`
2053
1899
  );
2054
- }
2055
- if (!hasRootReadme) {
1900
+ if (!hasRootReadme)
2056
1901
  recommendations.push(
2057
1902
  "Add a root README.md so agents understand the project context immediately"
2058
1903
  );
2059
- } else if (!readmeIsFresh) {
1904
+ else if (!readmeIsFresh)
2060
1905
  recommendations.push(
2061
1906
  "Update README.md \u2014 stale entry-point documentation disorients agents"
2062
1907
  );
2063
- }
2064
- if (apiClarityScore < 70) {
1908
+ if (apiClarityScore < 70)
2065
1909
  recommendations.push(
2066
1910
  `Add TypeScript types to ${untypedExports} untyped exports to improve API discoverability`
2067
1911
  );
2068
- }
2069
- if (domainConsistencyScore < 70) {
1912
+ if (domainConsistencyScore < 70)
2070
1913
  recommendations.push(
2071
1914
  `Unify ${inconsistentDomainTerms} inconsistent domain terms \u2014 agents need one word per concept`
2072
1915
  );
2073
- }
2074
1916
  return {
2075
1917
  score,
2076
1918
  rating,
@@ -2084,6 +1926,8 @@ function calculateAgentGrounding(params) {
2084
1926
  recommendations
2085
1927
  };
2086
1928
  }
1929
+
1930
+ // src/metrics/testability-index.ts
2087
1931
  function calculateTestabilityIndex(params) {
2088
1932
  const {
2089
1933
  testFiles,
@@ -2099,16 +1943,27 @@ function calculateTestabilityIndex(params) {
2099
1943
  } = params;
2100
1944
  const rawCoverageRatio = sourceFiles > 0 ? testFiles / sourceFiles : 0;
2101
1945
  const testCoverageRatio = Math.min(100, Math.round(rawCoverageRatio * 100));
2102
- const purityRatio = totalFunctions > 0 ? pureFunctions / totalFunctions : 0.5;
2103
- const purityScore = Math.round(purityRatio * 100);
2104
- const injectionRatio = totalClasses > 0 ? injectionPatterns / totalClasses : 0.5;
1946
+ const purityScore = Math.round(
1947
+ (totalFunctions > 0 ? pureFunctions / totalFunctions : 0.5) * 100
1948
+ );
2105
1949
  const dependencyInjectionScore = Math.round(
2106
- 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
+ )
2107
1966
  );
2108
- const bloatedRatio = totalInterfaces > 0 ? bloatedInterfaces / totalInterfaces : 0;
2109
- const interfaceFocusScore = Math.max(0, Math.round(100 - bloatedRatio * 80));
2110
- const mutationRatio = totalFunctions > 0 ? externalStateMutations / totalFunctions : 0;
2111
- const observabilityScore = Math.max(0, Math.round(100 - mutationRatio * 100));
2112
1967
  const frameworkWeight = hasTestFramework ? 1 : 0.8;
2113
1968
  const rawScore = (testCoverageRatio * 0.3 + purityScore * 0.25 + dependencyInjectionScore * 0.2 + interfaceFocusScore * 0.1 + observabilityScore * 0.15) * frameworkWeight;
2114
1969
  const score = Math.max(0, Math.min(100, Math.round(rawScore)));
@@ -2125,32 +1980,28 @@ function calculateTestabilityIndex(params) {
2125
1980
  else if (rawCoverageRatio > 0) aiChangeSafetyRating = "high-risk";
2126
1981
  else aiChangeSafetyRating = "blind-risk";
2127
1982
  const recommendations = [];
2128
- if (!hasTestFramework) {
1983
+ if (!hasTestFramework)
2129
1984
  recommendations.push(
2130
1985
  "Add a testing framework (Jest, Vitest, pytest) \u2014 AI changes cannot be verified without tests"
2131
1986
  );
2132
- }
2133
1987
  if (rawCoverageRatio < 0.3) {
2134
1988
  const neededTests = Math.round(sourceFiles * 0.3 - testFiles);
2135
1989
  recommendations.push(
2136
1990
  `Add ~${neededTests} test files to reach 30% coverage ratio \u2014 minimum for safe AI assistance`
2137
1991
  );
2138
1992
  }
2139
- if (purityScore < 50) {
1993
+ if (purityScore < 50)
2140
1994
  recommendations.push(
2141
1995
  "Extract pure functions from side-effectful code \u2014 pure functions are trivially AI-testable"
2142
1996
  );
2143
- }
2144
- if (dependencyInjectionScore < 50 && totalClasses > 0) {
1997
+ if (dependencyInjectionScore < 50 && totalClasses > 0)
2145
1998
  recommendations.push(
2146
1999
  "Adopt dependency injection \u2014 makes classes mockable and AI-generated code verifiable"
2147
2000
  );
2148
- }
2149
- if (externalStateMutations > totalFunctions * 0.3) {
2001
+ if (externalStateMutations > totalFunctions * 0.3)
2150
2002
  recommendations.push(
2151
2003
  "Reduce direct state mutations \u2014 return values instead to improve observability"
2152
2004
  );
2153
- }
2154
2005
  return {
2155
2006
  score,
2156
2007
  rating,
@@ -2165,6 +2016,8 @@ function calculateTestabilityIndex(params) {
2165
2016
  recommendations
2166
2017
  };
2167
2018
  }
2019
+
2020
+ // src/metrics/doc-drift.ts
2168
2021
  function calculateDocDrift(params) {
2169
2022
  const {
2170
2023
  uncommentedExports,
@@ -2187,21 +2040,18 @@ function calculateDocDrift(params) {
2187
2040
  else if (finalScore < 85) rating = "high";
2188
2041
  else rating = "severe";
2189
2042
  const recommendations = [];
2190
- if (outdatedComments > 0) {
2043
+ if (outdatedComments > 0)
2191
2044
  recommendations.push(
2192
2045
  `Update or remove ${outdatedComments} outdated comments that contradict the code.`
2193
2046
  );
2194
- }
2195
- if (uncommentedRatio > 0.3) {
2047
+ if (uncommentedRatio > 0.3)
2196
2048
  recommendations.push(
2197
2049
  `Add JSDoc to ${uncommentedExports} uncommented exports.`
2198
2050
  );
2199
- }
2200
- if (undocumentedComplexity > 0) {
2051
+ if (undocumentedComplexity > 0)
2201
2052
  recommendations.push(
2202
2053
  `Explain the business logic for ${undocumentedComplexity} highly complex functions.`
2203
2054
  );
2204
- }
2205
2055
  return {
2206
2056
  score: finalScore,
2207
2057
  rating,
@@ -2213,6 +2063,8 @@ function calculateDocDrift(params) {
2213
2063
  recommendations
2214
2064
  };
2215
2065
  }
2066
+
2067
+ // src/metrics/dependency-health.ts
2216
2068
  function calculateDependencyHealth(params) {
2217
2069
  const {
2218
2070
  totalPackages,
@@ -2225,8 +2077,12 @@ function calculateDependencyHealth(params) {
2225
2077
  const outdatedScore = Math.max(0, 100 - outdatedRatio * 200);
2226
2078
  const deprecatedScore = Math.max(0, 100 - deprecatedRatio * 500);
2227
2079
  const skewScore = Math.max(0, 100 - trainingCutoffSkew * 100);
2228
- const rawScore = outdatedScore * 0.3 + deprecatedScore * 0.4 + skewScore * 0.3;
2229
- 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
+ );
2230
2086
  let rating;
2231
2087
  if (score >= 85) rating = "excellent";
2232
2088
  else if (score >= 70) rating = "good";
@@ -2241,33 +2097,22 @@ function calculateDependencyHealth(params) {
2241
2097
  else if (trainingCutoffSkew < 0.8) aiKnowledgeConfidence = "low";
2242
2098
  else aiKnowledgeConfidence = "blind";
2243
2099
  const recommendations = [];
2244
- if (deprecatedPackages > 0) {
2245
- recommendations.push(
2246
- `Replace ${deprecatedPackages} deprecated packages, as AI will struggle to find modern solutions.`
2247
- );
2248
- }
2249
- if (outdatedRatio > 0.2) {
2250
- recommendations.push(
2251
- `Update ${outdatedPackages} outdated packages to keep APIs aligned with AI training data.`
2252
- );
2253
- }
2254
- if (trainingCutoffSkew > 0.5) {
2255
- recommendations.push(
2256
- "High training cutoff skew detected. AI may hallucinate APIs that were introduced recently."
2257
- );
2258
- }
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.");
2259
2106
  return {
2260
2107
  score,
2261
2108
  rating,
2262
- dimensions: {
2263
- outdatedPackages,
2264
- deprecatedPackages,
2265
- trainingCutoffSkew
2266
- },
2109
+ dimensions: { outdatedPackages, deprecatedPackages, trainingCutoffSkew },
2267
2110
  aiKnowledgeConfidence,
2268
2111
  recommendations
2269
2112
  };
2270
2113
  }
2114
+
2115
+ // src/metrics/change-amplification.ts
2271
2116
  function calculateChangeAmplification(params) {
2272
2117
  const { files } = params;
2273
2118
  if (files.length === 0) {
@@ -2280,15 +2125,16 @@ function calculateChangeAmplification(params) {
2280
2125
  recommendations: []
2281
2126
  };
2282
2127
  }
2283
- const hotspots = files.map((f) => {
2284
- const amplificationFactor = f.fanOut + f.fanIn * 0.5;
2285
- return { ...f, amplificationFactor };
2286
- }).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);
2287
2129
  const maxAmplification = hotspots[0].amplificationFactor;
2288
2130
  const avgAmplification = hotspots.reduce((sum, h) => sum + h.amplificationFactor, 0) / hotspots.length;
2289
- let score = 100 - avgAmplification * 5;
2290
- if (maxAmplification > 20) score -= maxAmplification - 20;
2291
- 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
+ );
2292
2138
  let rating = "isolated";
2293
2139
  if (score < 40) rating = "explosive";
2294
2140
  else if (score < 70) rating = "amplified";
@@ -2296,12 +2142,12 @@ function calculateChangeAmplification(params) {
2296
2142
  const recommendations = [];
2297
2143
  if (score < 70 && hotspots.length > 0) {
2298
2144
  recommendations.push(
2299
- `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.`
2300
2146
  );
2301
2147
  }
2302
2148
  if (maxAmplification > 30) {
2303
2149
  recommendations.push(
2304
- `Break down key bottlenecks with amplification factor > 30.`
2150
+ "Break down key bottlenecks with amplification factor > 30."
2305
2151
  );
2306
2152
  }
2307
2153
  return {
@@ -2313,6 +2159,61 @@ function calculateChangeAmplification(params) {
2313
2159
  recommendations
2314
2160
  };
2315
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
+ }
2316
2217
  function calculateExtendedFutureProofScore(params) {
2317
2218
  const loadScore = 100 - params.cognitiveLoad.score;
2318
2219
  const entropyScore = 100 - params.patternEntropy.entropy * 100;
@@ -2321,7 +2222,7 @@ function calculateExtendedFutureProofScore(params) {
2321
2222
  const groundingScore = params.agentGrounding.score;
2322
2223
  const testabilityScore = params.testability.score;
2323
2224
  const docDriftScore = params.docDrift ? 100 - params.docDrift.score : 100;
2324
- const depsHealthScore = params.dependencyHealth ? params.dependencyHealth.score : 100;
2225
+ const depsHealthScore = params.dependencyHealth?.score ?? 100;
2325
2226
  let totalWeight = 0.8;
2326
2227
  let overall = loadScore * 0.15 + entropyScore * 0.1 + cohesionScore * 0.1 + aiSignalClarityScore * 0.15 + groundingScore * 0.15 + testabilityScore * 0.15;
2327
2228
  if (params.docDrift) {
@@ -2352,7 +2253,7 @@ function calculateExtendedFutureProofScore(params) {
2352
2253
  {
2353
2254
  name: "AI Signal Clarity",
2354
2255
  impact: Math.round(aiSignalClarityScore - 50),
2355
- description: `${params.aiSignalClarity.rating} risk (${params.aiSignalClarity.score}/100 raw)`
2256
+ description: `${params.aiSignalClarity.rating} risk`
2356
2257
  },
2357
2258
  {
2358
2259
  name: "Agent Grounding",
@@ -2362,60 +2263,25 @@ function calculateExtendedFutureProofScore(params) {
2362
2263
  {
2363
2264
  name: "Testability",
2364
2265
  impact: Math.round(testabilityScore - 50),
2365
- description: `${params.testability.rating} \u2014 AI changes are ${params.testability.aiChangeSafetyRating}`
2266
+ description: params.testability.rating
2366
2267
  }
2367
2268
  ];
2368
2269
  if (params.docDrift) {
2369
2270
  factors.push({
2370
2271
  name: "Documentation Drift",
2371
2272
  impact: Math.round(docDriftScore - 50),
2372
- description: `${params.docDrift.rating} risk of AI signal clarity from drift`
2273
+ description: params.docDrift.rating
2373
2274
  });
2374
2275
  }
2375
2276
  if (params.dependencyHealth) {
2376
2277
  factors.push({
2377
2278
  name: "Dependency Health",
2378
2279
  impact: Math.round(depsHealthScore - 50),
2379
- description: `${params.dependencyHealth.rating} health \u2014 AI knowledge is ${params.dependencyHealth.aiKnowledgeConfidence}`
2380
- });
2381
- }
2382
- const recommendations = [];
2383
- for (const rec of params.aiSignalClarity.recommendations) {
2384
- recommendations.push({ action: rec, estimatedImpact: 8, priority: "high" });
2385
- }
2386
- for (const rec of params.agentGrounding.recommendations) {
2387
- recommendations.push({
2388
- action: rec,
2389
- estimatedImpact: 6,
2390
- priority: "medium"
2280
+ description: params.dependencyHealth.rating
2391
2281
  });
2392
2282
  }
2393
- for (const rec of params.testability.recommendations) {
2394
- const priority = params.testability.aiChangeSafetyRating === "blind-risk" ? "high" : "medium";
2395
- recommendations.push({ action: rec, estimatedImpact: 10, priority });
2396
- }
2397
- for (const rec of params.patternEntropy.recommendations) {
2398
- recommendations.push({ action: rec, estimatedImpact: 5, priority: "low" });
2399
- }
2400
- if (params.docDrift) {
2401
- for (const rec of params.docDrift.recommendations) {
2402
- recommendations.push({
2403
- action: rec,
2404
- estimatedImpact: 8,
2405
- priority: "high"
2406
- });
2407
- }
2408
- }
2409
- if (params.dependencyHealth) {
2410
- for (const rec of params.dependencyHealth.recommendations) {
2411
- recommendations.push({
2412
- action: rec,
2413
- estimatedImpact: 7,
2414
- priority: "medium"
2415
- });
2416
- }
2417
- }
2418
- 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;
2419
2285
  return {
2420
2286
  toolName: "future-proof",
2421
2287
  score: overall,
@@ -2591,16 +2457,19 @@ export {
2591
2457
  ParseError,
2592
2458
  ParserFactory,
2593
2459
  PythonParser,
2460
+ SEVERITY_TIME_ESTIMATES,
2594
2461
  SIZE_ADJUSTED_THRESHOLDS,
2595
2462
  TOOL_NAME_MAP,
2596
2463
  TypeScriptParser,
2597
2464
  VAGUE_FILE_NAMES,
2598
2465
  calculateAgentGrounding,
2599
2466
  calculateAiSignalClarity,
2467
+ calculateBusinessROI,
2600
2468
  calculateChangeAmplification,
2601
2469
  calculateCognitiveLoad,
2602
2470
  calculateComprehensionDifficulty,
2603
2471
  calculateConceptCohesion,
2472
+ calculateDebtInterest,
2604
2473
  calculateDependencyHealth,
2605
2474
  calculateDocDrift,
2606
2475
  calculateExtendedFutureProofScore,
@@ -2611,10 +2480,8 @@ export {
2611
2480
  calculateOverallScore,
2612
2481
  calculatePatternEntropy,
2613
2482
  calculateProductivityImpact,
2614
- calculateRemediationVelocity,
2615
- calculateScoreTrend,
2616
2483
  calculateSemanticDistance,
2617
- calculateTechnicalDebtInterest,
2484
+ calculateTechnicalValueChain,
2618
2485
  calculateTestabilityIndex,
2619
2486
  calculateTokenBudget,
2620
2487
  clearHistory,
@@ -2630,7 +2497,6 @@ export {
2630
2497
  formatToolScore,
2631
2498
  generateHTML,
2632
2499
  generateValueChain,
2633
- getDebtBreakdown,
2634
2500
  getElapsedTime,
2635
2501
  getFileCommitTimestamps,
2636
2502
  getFileExtension,
@@ -2644,6 +2510,9 @@ export {
2644
2510
  getRatingWithContext,
2645
2511
  getRecommendedThreshold,
2646
2512
  getRepoMetadata,
2513
+ getSafetyIcon,
2514
+ getScoreBar,
2515
+ getSeverityColor,
2647
2516
  getSupportedLanguages,
2648
2517
  getToolWeight,
2649
2518
  handleCLIError,