@aiready/core 0.9.26 → 0.9.28

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.js CHANGED
@@ -30,20 +30,28 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ CONTEXT_TIER_THRESHOLDS: () => CONTEXT_TIER_THRESHOLDS,
33
34
  DEFAULT_COST_CONFIG: () => DEFAULT_COST_CONFIG,
34
35
  DEFAULT_EXCLUDE: () => DEFAULT_EXCLUDE,
35
36
  DEFAULT_TOOL_WEIGHTS: () => DEFAULT_TOOL_WEIGHTS,
36
37
  LANGUAGE_EXTENSIONS: () => LANGUAGE_EXTENSIONS,
37
38
  Language: () => Language,
39
+ MODEL_PRICING_PRESETS: () => MODEL_PRICING_PRESETS,
38
40
  ParseError: () => ParseError,
39
41
  ParserFactory: () => ParserFactory,
40
42
  PythonParser: () => PythonParser,
43
+ SIZE_ADJUSTED_THRESHOLDS: () => SIZE_ADJUSTED_THRESHOLDS,
41
44
  TOOL_NAME_MAP: () => TOOL_NAME_MAP,
42
45
  TypeScriptParser: () => TypeScriptParser,
46
+ calculateAgentGrounding: () => calculateAgentGrounding,
43
47
  calculateCognitiveLoad: () => calculateCognitiveLoad,
44
48
  calculateComprehensionDifficulty: () => calculateComprehensionDifficulty,
45
49
  calculateConceptCohesion: () => calculateConceptCohesion,
50
+ calculateDependencyHealth: () => calculateDependencyHealth,
51
+ calculateDocDrift: () => calculateDocDrift,
52
+ calculateExtendedFutureProofScore: () => calculateExtendedFutureProofScore,
46
53
  calculateFutureProofScore: () => calculateFutureProofScore,
54
+ calculateHallucinationRisk: () => calculateHallucinationRisk,
47
55
  calculateImportSimilarity: () => calculateImportSimilarity,
48
56
  calculateKnowledgeConcentration: () => calculateKnowledgeConcentration,
49
57
  calculateMonthlyCost: () => calculateMonthlyCost,
@@ -54,6 +62,7 @@ __export(index_exports, {
54
62
  calculateScoreTrend: () => calculateScoreTrend,
55
63
  calculateSemanticDistance: () => calculateSemanticDistance,
56
64
  calculateTechnicalDebtInterest: () => calculateTechnicalDebtInterest,
65
+ calculateTestabilityIndex: () => calculateTestabilityIndex,
57
66
  clearHistory: () => clearHistory,
58
67
  estimateTokens: () => estimateTokens,
59
68
  exportHistory: () => exportHistory,
@@ -69,9 +78,13 @@ __export(index_exports, {
69
78
  getElapsedTime: () => getElapsedTime,
70
79
  getFileExtension: () => getFileExtension,
71
80
  getHistorySummary: () => getHistorySummary,
81
+ getModelPreset: () => getModelPreset,
72
82
  getParser: () => getParser,
83
+ getProjectSizeTier: () => getProjectSizeTier,
73
84
  getRating: () => getRating,
74
85
  getRatingDisplay: () => getRatingDisplay,
86
+ getRatingWithContext: () => getRatingWithContext,
87
+ getRecommendedThreshold: () => getRecommendedThreshold,
75
88
  getSupportedLanguages: () => getSupportedLanguages,
76
89
  getToolWeight: () => getToolWeight,
77
90
  handleCLIError: () => handleCLIError,
@@ -694,16 +707,57 @@ var DEFAULT_TOOL_WEIGHTS = {
694
707
  "pattern-detect": 40,
695
708
  "context-analyzer": 35,
696
709
  "consistency": 25,
697
- "doc-drift": 20,
698
- "deps": 15
710
+ "hallucination-risk": 20,
711
+ "agent-grounding": 18,
712
+ "testability": 18,
713
+ "doc-drift": 15,
714
+ "deps": 12
699
715
  };
700
716
  var TOOL_NAME_MAP = {
701
717
  "patterns": "pattern-detect",
702
718
  "context": "context-analyzer",
703
719
  "consistency": "consistency",
720
+ "hallucination": "hallucination-risk",
721
+ "hallucination-risk": "hallucination-risk",
722
+ "grounding": "agent-grounding",
723
+ "agent-grounding": "agent-grounding",
724
+ "testability": "testability",
725
+ "tests": "testability",
704
726
  "doc-drift": "doc-drift",
727
+ "docs": "doc-drift",
705
728
  "deps": "deps"
706
729
  };
730
+ var CONTEXT_TIER_THRESHOLDS = {
731
+ compact: { idealTokens: 3e3, criticalTokens: 1e4, idealDepth: 4 },
732
+ standard: { idealTokens: 5e3, criticalTokens: 15e3, idealDepth: 5 },
733
+ extended: { idealTokens: 15e3, criticalTokens: 5e4, idealDepth: 7 },
734
+ frontier: { idealTokens: 5e4, criticalTokens: 15e4, idealDepth: 10 }
735
+ };
736
+ var SIZE_ADJUSTED_THRESHOLDS = {
737
+ "xs": 80,
738
+ // < 50 files
739
+ "small": 75,
740
+ // 50-200 files
741
+ "medium": 70,
742
+ // 200-500 files
743
+ "large": 65,
744
+ // 500-2000 files
745
+ "enterprise": 58
746
+ // 2000+ files
747
+ };
748
+ function getProjectSizeTier(fileCount) {
749
+ if (fileCount < 50) return "xs";
750
+ if (fileCount < 200) return "small";
751
+ if (fileCount < 500) return "medium";
752
+ if (fileCount < 2e3) return "large";
753
+ return "enterprise";
754
+ }
755
+ function getRecommendedThreshold(fileCount, modelTier = "standard") {
756
+ const sizeTier = getProjectSizeTier(fileCount);
757
+ const base = SIZE_ADJUSTED_THRESHOLDS[sizeTier];
758
+ const modelBonus = modelTier === "frontier" ? -3 : modelTier === "extended" ? -2 : 0;
759
+ return base + modelBonus;
760
+ }
707
761
  function normalizeToolName(shortName) {
708
762
  return TOOL_NAME_MAP[shortName] || shortName;
709
763
  }
@@ -786,6 +840,11 @@ function getRating(score) {
786
840
  if (score >= 40) return "Needs Work";
787
841
  return "Critical";
788
842
  }
843
+ function getRatingWithContext(score, fileCount, modelTier = "standard") {
844
+ const threshold = getRecommendedThreshold(fileCount, modelTier);
845
+ const normalized = score - threshold + 70;
846
+ return getRating(normalized);
847
+ }
789
848
  function getRatingDisplay(rating) {
790
849
  switch (rating) {
791
850
  case "Excellent":
@@ -834,11 +893,87 @@ function formatToolScore(output) {
834
893
  }
835
894
 
836
895
  // src/business-metrics.ts
896
+ var MODEL_PRICING_PRESETS = {
897
+ "gpt-4": {
898
+ name: "GPT-4",
899
+ pricePer1KInputTokens: 0.03,
900
+ pricePer1KOutputTokens: 0.06,
901
+ contextTier: "standard",
902
+ typicalQueriesPerDevPerDay: 40
903
+ },
904
+ "gpt-4o": {
905
+ name: "GPT-4o",
906
+ pricePer1KInputTokens: 5e-3,
907
+ pricePer1KOutputTokens: 0.015,
908
+ contextTier: "extended",
909
+ typicalQueriesPerDevPerDay: 60
910
+ },
911
+ "gpt-4o-mini": {
912
+ name: "GPT-4o mini",
913
+ pricePer1KInputTokens: 15e-5,
914
+ pricePer1KOutputTokens: 6e-4,
915
+ contextTier: "extended",
916
+ typicalQueriesPerDevPerDay: 120
917
+ },
918
+ "claude-3-5-sonnet": {
919
+ name: "Claude 3.5 Sonnet",
920
+ pricePer1KInputTokens: 3e-3,
921
+ pricePer1KOutputTokens: 0.015,
922
+ contextTier: "extended",
923
+ typicalQueriesPerDevPerDay: 80
924
+ },
925
+ "claude-3-7-sonnet": {
926
+ name: "Claude 3.7 Sonnet",
927
+ pricePer1KInputTokens: 3e-3,
928
+ pricePer1KOutputTokens: 0.015,
929
+ contextTier: "frontier",
930
+ typicalQueriesPerDevPerDay: 80
931
+ },
932
+ "claude-sonnet-4": {
933
+ name: "Claude Sonnet 4",
934
+ pricePer1KInputTokens: 3e-3,
935
+ pricePer1KOutputTokens: 0.015,
936
+ contextTier: "frontier",
937
+ typicalQueriesPerDevPerDay: 80
938
+ },
939
+ "gemini-1-5-pro": {
940
+ name: "Gemini 1.5 Pro",
941
+ pricePer1KInputTokens: 125e-5,
942
+ pricePer1KOutputTokens: 5e-3,
943
+ contextTier: "frontier",
944
+ typicalQueriesPerDevPerDay: 80
945
+ },
946
+ "gemini-2-0-flash": {
947
+ name: "Gemini 2.0 Flash",
948
+ pricePer1KInputTokens: 1e-4,
949
+ pricePer1KOutputTokens: 4e-4,
950
+ contextTier: "frontier",
951
+ typicalQueriesPerDevPerDay: 150
952
+ },
953
+ "copilot": {
954
+ name: "GitHub Copilot (subscription)",
955
+ // Amortized per-request cost for a $19/month plan at 80 queries/day
956
+ pricePer1KInputTokens: 1e-4,
957
+ pricePer1KOutputTokens: 1e-4,
958
+ contextTier: "extended",
959
+ typicalQueriesPerDevPerDay: 80
960
+ },
961
+ "cursor-pro": {
962
+ name: "Cursor Pro (subscription)",
963
+ pricePer1KInputTokens: 1e-4,
964
+ pricePer1KOutputTokens: 1e-4,
965
+ contextTier: "frontier",
966
+ typicalQueriesPerDevPerDay: 100
967
+ }
968
+ };
969
+ function getModelPreset(modelId) {
970
+ return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["gpt-4o"];
971
+ }
837
972
  var DEFAULT_COST_CONFIG = {
838
- pricePer1KTokens: 0.01,
839
- // $0.01 per 1K tokens (GPT-4)
840
- queriesPerDevPerDay: 50,
841
- // Average AI queries per developer
973
+ pricePer1KTokens: 5e-3,
974
+ // GPT-4o input price (updated from GPT-4 era 0.01)
975
+ queriesPerDevPerDay: 60,
976
+ // Average AI queries per developer (updated: 40→60 as of 2026)
842
977
  developerCount: 5,
843
978
  // Default team size
844
979
  daysPerMonth: 30
@@ -889,43 +1024,63 @@ function calculateProductivityImpact(issues, hourlyRate = DEFAULT_HOURLY_RATE) {
889
1024
  }
890
1025
  function predictAcceptanceRate(toolOutputs) {
891
1026
  const factors = [];
1027
+ const baseRate = 0.3;
892
1028
  const patterns = toolOutputs.get("pattern-detect");
893
1029
  if (patterns) {
894
- const patternImpact = (patterns.score - 50) * 0.3;
1030
+ const patternImpact = (patterns.score - 50) * 3e-3;
895
1031
  factors.push({
896
1032
  name: "Semantic Duplication",
897
- impact: Math.round(patternImpact)
1033
+ impact: Math.round(patternImpact * 100)
898
1034
  });
899
1035
  }
900
1036
  const context = toolOutputs.get("context-analyzer");
901
1037
  if (context) {
902
- const contextImpact = (context.score - 50) * 0.4;
1038
+ const contextImpact = (context.score - 50) * 4e-3;
903
1039
  factors.push({
904
1040
  name: "Context Efficiency",
905
- impact: Math.round(contextImpact)
1041
+ impact: Math.round(contextImpact * 100)
906
1042
  });
907
1043
  }
908
1044
  const consistency = toolOutputs.get("consistency");
909
1045
  if (consistency) {
910
- const consistencyImpact = (consistency.score - 50) * 0.3;
1046
+ const consistencyImpact = (consistency.score - 50) * 2e-3;
911
1047
  factors.push({
912
1048
  name: "Code Consistency",
913
- impact: Math.round(consistencyImpact)
1049
+ impact: Math.round(consistencyImpact * 100)
1050
+ });
1051
+ }
1052
+ const hallucinationRisk = toolOutputs.get("hallucination-risk");
1053
+ if (hallucinationRisk) {
1054
+ const hrImpact = (50 - hallucinationRisk.score) * 2e-3;
1055
+ factors.push({
1056
+ name: "Hallucination Risk",
1057
+ impact: Math.round(hrImpact * 100)
914
1058
  });
915
1059
  }
916
- const baseRate = 0.65;
917
1060
  const totalImpact = factors.reduce((sum, f) => sum + f.impact / 100, 0);
918
- const rate = Math.max(0.1, Math.min(0.95, baseRate + totalImpact));
919
- const confidence = toolOutputs.size >= 3 ? 0.8 : toolOutputs.size >= 2 ? 0.6 : 0.4;
1061
+ const rate = Math.max(0.05, Math.min(0.8, baseRate + totalImpact));
1062
+ let confidence;
1063
+ if (toolOutputs.size >= 4) confidence = 0.75;
1064
+ else if (toolOutputs.size >= 3) confidence = 0.65;
1065
+ else if (toolOutputs.size >= 2) confidence = 0.5;
1066
+ else confidence = 0.35;
920
1067
  return {
921
1068
  rate: Math.round(rate * 100) / 100,
922
1069
  confidence,
923
1070
  factors
924
1071
  };
925
1072
  }
926
- function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentation, consistencyScore, totalFiles) {
927
- const budgetFactor = Math.min(100, Math.max(0, (contextBudget - 5e3) / 250));
928
- const depthFactor = Math.min(100, Math.max(0, (importDepth - 5) * 10));
1073
+ function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentation, consistencyScore, totalFiles, modelTier = "standard") {
1074
+ const tierThresholds = CONTEXT_TIER_THRESHOLDS[modelTier];
1075
+ const idealBudget = tierThresholds.idealTokens;
1076
+ const criticalBudget = tierThresholds.criticalTokens;
1077
+ const idealDepth = tierThresholds.idealDepth;
1078
+ const budgetRange = criticalBudget - idealBudget;
1079
+ const budgetFactor = Math.min(100, Math.max(
1080
+ 0,
1081
+ (contextBudget - idealBudget) / budgetRange * 100
1082
+ ));
1083
+ const depthFactor = Math.min(100, Math.max(0, (importDepth - idealDepth) * 10));
929
1084
  const fragmentationFactor = Math.min(100, Math.max(0, (fragmentation - 0.3) * 250));
930
1085
  const consistencyFactor = Math.min(100, Math.max(0, 100 - consistencyScore));
931
1086
  const fileFactor = Math.min(100, Math.max(0, (totalFiles - 50) / 5));
@@ -945,12 +1100,12 @@ function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentat
945
1100
  {
946
1101
  name: "Context Budget",
947
1102
  contribution: Math.round(budgetFactor * 0.35),
948
- description: `${Math.round(contextBudget)} tokens required`
1103
+ description: `${Math.round(contextBudget)} tokens required (${modelTier} model tier: ideal <${idealBudget.toLocaleString()})`
949
1104
  },
950
1105
  {
951
1106
  name: "Import Depth",
952
1107
  contribution: Math.round(depthFactor * 0.2),
953
- description: `${importDepth.toFixed(1)} average levels`
1108
+ description: `${importDepth.toFixed(1)} average levels (ideal <${idealDepth} for ${modelTier})`
954
1109
  },
955
1110
  {
956
1111
  name: "Code Fragmentation",
@@ -1895,6 +2050,449 @@ function calculateFutureProofScore(params) {
1895
2050
  recommendations
1896
2051
  };
1897
2052
  }
2053
+ function calculateHallucinationRisk(params) {
2054
+ const {
2055
+ overloadedSymbols,
2056
+ magicLiterals,
2057
+ booleanTraps,
2058
+ implicitSideEffects,
2059
+ deepCallbacks,
2060
+ ambiguousNames,
2061
+ undocumentedExports,
2062
+ totalSymbols,
2063
+ totalExports
2064
+ } = params;
2065
+ if (totalSymbols === 0) {
2066
+ return {
2067
+ score: 0,
2068
+ rating: "minimal",
2069
+ signals: [],
2070
+ topRisk: "No symbols to analyze",
2071
+ recommendations: []
2072
+ };
2073
+ }
2074
+ const overloadRatio = Math.min(1, overloadedSymbols / Math.max(1, totalSymbols));
2075
+ const overloadSignal = {
2076
+ name: "Symbol Overloading",
2077
+ count: overloadedSymbols,
2078
+ riskContribution: Math.round(overloadRatio * 100 * 0.25),
2079
+ // 25% weight
2080
+ description: `${overloadedSymbols} overloaded symbols \u2014 AI picks wrong signature`
2081
+ };
2082
+ const magicRatio = Math.min(1, magicLiterals / Math.max(1, totalSymbols * 2));
2083
+ const magicSignal = {
2084
+ name: "Magic Literals",
2085
+ count: magicLiterals,
2086
+ riskContribution: Math.round(magicRatio * 100 * 0.2),
2087
+ // 20% weight
2088
+ description: `${magicLiterals} unnamed constants \u2014 AI invents wrong values`
2089
+ };
2090
+ const trapRatio = Math.min(1, booleanTraps / Math.max(1, totalSymbols));
2091
+ const trapSignal = {
2092
+ name: "Boolean Traps",
2093
+ count: booleanTraps,
2094
+ riskContribution: Math.round(trapRatio * 100 * 0.2),
2095
+ // 20% weight
2096
+ description: `${booleanTraps} boolean trap parameters \u2014 AI inverts intent`
2097
+ };
2098
+ const sideEffectRatio = Math.min(1, implicitSideEffects / Math.max(1, totalExports));
2099
+ const sideEffectSignal = {
2100
+ name: "Implicit Side Effects",
2101
+ count: implicitSideEffects,
2102
+ riskContribution: Math.round(sideEffectRatio * 100 * 0.15),
2103
+ // 15% weight
2104
+ description: `${implicitSideEffects} functions with implicit side effects \u2014 AI misses contracts`
2105
+ };
2106
+ const callbackRatio = Math.min(1, deepCallbacks / Math.max(1, totalSymbols * 0.1));
2107
+ const callbackSignal = {
2108
+ name: "Callback Nesting",
2109
+ count: deepCallbacks,
2110
+ riskContribution: Math.round(callbackRatio * 100 * 0.1),
2111
+ // 10% weight
2112
+ description: `${deepCallbacks} deep callback chains \u2014 AI loses control flow context`
2113
+ };
2114
+ const ambiguousRatio = Math.min(1, ambiguousNames / Math.max(1, totalSymbols));
2115
+ const ambiguousSignal = {
2116
+ name: "Ambiguous Names",
2117
+ count: ambiguousNames,
2118
+ riskContribution: Math.round(ambiguousRatio * 100 * 0.1),
2119
+ // 10% weight
2120
+ description: `${ambiguousNames} non-descriptive identifiers \u2014 AI guesses wrong intent`
2121
+ };
2122
+ const undocRatio = Math.min(1, undocumentedExports / Math.max(1, totalExports));
2123
+ const undocSignal = {
2124
+ name: "Undocumented Exports",
2125
+ count: undocumentedExports,
2126
+ riskContribution: Math.round(undocRatio * 100 * 0.1),
2127
+ // 10% weight
2128
+ description: `${undocumentedExports} public functions without docs \u2014 AI fabricates behavior`
2129
+ };
2130
+ const signals = [
2131
+ overloadSignal,
2132
+ magicSignal,
2133
+ trapSignal,
2134
+ sideEffectSignal,
2135
+ callbackSignal,
2136
+ ambiguousSignal,
2137
+ undocSignal
2138
+ ];
2139
+ const score = Math.min(100, signals.reduce((sum, s) => sum + s.riskContribution, 0));
2140
+ let rating;
2141
+ if (score < 10) rating = "minimal";
2142
+ else if (score < 25) rating = "low";
2143
+ else if (score < 50) rating = "moderate";
2144
+ else if (score < 75) rating = "high";
2145
+ else rating = "severe";
2146
+ const topSignal = signals.reduce((a, b) => a.riskContribution > b.riskContribution ? a : b);
2147
+ const topRisk = topSignal.riskContribution > 0 ? topSignal.description : "No significant hallucination risks detected";
2148
+ const recommendations = [];
2149
+ if (overloadSignal.riskContribution > 5) {
2150
+ recommendations.push(`Rename ${overloadedSymbols} overloaded symbols to unique, intent-revealing names`);
2151
+ }
2152
+ if (magicSignal.riskContribution > 5) {
2153
+ recommendations.push(`Extract ${magicLiterals} magic literals into named constants`);
2154
+ }
2155
+ if (trapSignal.riskContribution > 5) {
2156
+ recommendations.push(`Replace ${booleanTraps} boolean traps with named options objects`);
2157
+ }
2158
+ if (undocSignal.riskContribution > 5) {
2159
+ recommendations.push(`Add JSDoc/docstrings to ${undocumentedExports} undocumented public functions`);
2160
+ }
2161
+ if (sideEffectSignal.riskContribution > 5) {
2162
+ recommendations.push("Mark functions with side effects explicitly in their names or docs");
2163
+ }
2164
+ return {
2165
+ score: Math.round(score),
2166
+ rating,
2167
+ signals: signals.filter((s) => s.count > 0),
2168
+ topRisk,
2169
+ recommendations
2170
+ };
2171
+ }
2172
+ function calculateAgentGrounding(params) {
2173
+ const {
2174
+ deepDirectories,
2175
+ totalDirectories,
2176
+ vagueFileNames,
2177
+ totalFiles,
2178
+ hasRootReadme,
2179
+ readmeIsFresh,
2180
+ barrelExports,
2181
+ untypedExports,
2182
+ totalExports,
2183
+ inconsistentDomainTerms,
2184
+ domainVocabularySize
2185
+ } = params;
2186
+ const deepDirRatio = totalDirectories > 0 ? deepDirectories / totalDirectories : 0;
2187
+ const structureClarityScore = Math.max(0, Math.round(100 - deepDirRatio * 80));
2188
+ const vagueRatio = totalFiles > 0 ? vagueFileNames / totalFiles : 0;
2189
+ const selfDocumentationScore = Math.max(0, Math.round(100 - vagueRatio * 90));
2190
+ let entryPointScore = 60;
2191
+ if (hasRootReadme) entryPointScore += 25;
2192
+ if (readmeIsFresh) entryPointScore += 10;
2193
+ const barrelRatio = totalFiles > 0 ? barrelExports / (totalFiles * 0.1) : 0;
2194
+ entryPointScore += Math.round(Math.min(5, barrelRatio * 5));
2195
+ entryPointScore = Math.min(100, entryPointScore);
2196
+ const untypedRatio = totalExports > 0 ? untypedExports / totalExports : 0;
2197
+ const apiClarityScore = Math.max(0, Math.round(100 - untypedRatio * 70));
2198
+ const inconsistencyRatio = domainVocabularySize > 0 ? inconsistentDomainTerms / domainVocabularySize : 0;
2199
+ const domainConsistencyScore = Math.max(0, Math.round(100 - inconsistencyRatio * 80));
2200
+ const score = Math.round(
2201
+ structureClarityScore * 0.2 + selfDocumentationScore * 0.25 + entryPointScore * 0.2 + apiClarityScore * 0.15 + domainConsistencyScore * 0.2
2202
+ );
2203
+ let rating;
2204
+ if (score >= 85) rating = "excellent";
2205
+ else if (score >= 70) rating = "good";
2206
+ else if (score >= 50) rating = "moderate";
2207
+ else if (score >= 30) rating = "poor";
2208
+ else rating = "disorienting";
2209
+ const recommendations = [];
2210
+ if (structureClarityScore < 70) {
2211
+ recommendations.push(`Flatten ${deepDirectories} overly-deep directories to improve agent navigation`);
2212
+ }
2213
+ if (selfDocumentationScore < 70) {
2214
+ recommendations.push(`Rename ${vagueFileNames} vague files (utils, helpers, misc) to domain-specific names`);
2215
+ }
2216
+ if (!hasRootReadme) {
2217
+ recommendations.push("Add a root README.md so agents understand the project context immediately");
2218
+ } else if (!readmeIsFresh) {
2219
+ recommendations.push("Update README.md \u2014 stale entry-point documentation disorients agents");
2220
+ }
2221
+ if (apiClarityScore < 70) {
2222
+ recommendations.push(`Add TypeScript types to ${untypedExports} untyped exports to improve API discoverability`);
2223
+ }
2224
+ if (domainConsistencyScore < 70) {
2225
+ recommendations.push(`Unify ${inconsistentDomainTerms} inconsistent domain terms \u2014 agents need one word per concept`);
2226
+ }
2227
+ return {
2228
+ score,
2229
+ rating,
2230
+ dimensions: {
2231
+ structureClarityScore,
2232
+ selfDocumentationScore,
2233
+ entryPointScore,
2234
+ apiClarityScore,
2235
+ domainConsistencyScore
2236
+ },
2237
+ recommendations
2238
+ };
2239
+ }
2240
+ function calculateTestabilityIndex(params) {
2241
+ const {
2242
+ testFiles,
2243
+ sourceFiles,
2244
+ pureFunctions,
2245
+ totalFunctions,
2246
+ injectionPatterns,
2247
+ totalClasses,
2248
+ bloatedInterfaces,
2249
+ totalInterfaces,
2250
+ externalStateMutations,
2251
+ hasTestFramework
2252
+ } = params;
2253
+ const rawCoverageRatio = sourceFiles > 0 ? testFiles / sourceFiles : 0;
2254
+ const testCoverageRatio = Math.min(100, Math.round(rawCoverageRatio * 100));
2255
+ const purityRatio = totalFunctions > 0 ? pureFunctions / totalFunctions : 0.5;
2256
+ const purityScore = Math.round(purityRatio * 100);
2257
+ const injectionRatio = totalClasses > 0 ? injectionPatterns / totalClasses : 0.5;
2258
+ const dependencyInjectionScore = Math.round(Math.min(100, injectionRatio * 100));
2259
+ const bloatedRatio = totalInterfaces > 0 ? bloatedInterfaces / totalInterfaces : 0;
2260
+ const interfaceFocusScore = Math.max(0, Math.round(100 - bloatedRatio * 80));
2261
+ const mutationRatio = totalFunctions > 0 ? externalStateMutations / totalFunctions : 0;
2262
+ const observabilityScore = Math.max(0, Math.round(100 - mutationRatio * 100));
2263
+ const frameworkWeight = hasTestFramework ? 1 : 0.8;
2264
+ const rawScore = (testCoverageRatio * 0.3 + purityScore * 0.25 + dependencyInjectionScore * 0.2 + interfaceFocusScore * 0.1 + observabilityScore * 0.15) * frameworkWeight;
2265
+ const score = Math.max(0, Math.min(100, Math.round(rawScore)));
2266
+ let rating;
2267
+ if (score >= 85) rating = "excellent";
2268
+ else if (score >= 70) rating = "good";
2269
+ else if (score >= 50) rating = "moderate";
2270
+ else if (score >= 30) rating = "poor";
2271
+ else rating = "unverifiable";
2272
+ let aiChangeSafetyRating;
2273
+ if (rawCoverageRatio >= 0.5 && score >= 70) aiChangeSafetyRating = "safe";
2274
+ else if (rawCoverageRatio >= 0.2 && score >= 50) aiChangeSafetyRating = "moderate-risk";
2275
+ else if (rawCoverageRatio > 0) aiChangeSafetyRating = "high-risk";
2276
+ else aiChangeSafetyRating = "blind-risk";
2277
+ const recommendations = [];
2278
+ if (!hasTestFramework) {
2279
+ recommendations.push("Add a testing framework (Jest, Vitest, pytest) \u2014 AI changes cannot be verified without tests");
2280
+ }
2281
+ if (rawCoverageRatio < 0.3) {
2282
+ const neededTests = Math.round(sourceFiles * 0.3 - testFiles);
2283
+ recommendations.push(`Add ~${neededTests} test files to reach 30% coverage ratio \u2014 minimum for safe AI assistance`);
2284
+ }
2285
+ if (purityScore < 50) {
2286
+ recommendations.push("Extract pure functions from side-effectful code \u2014 pure functions are trivially AI-testable");
2287
+ }
2288
+ if (dependencyInjectionScore < 50 && totalClasses > 0) {
2289
+ recommendations.push("Adopt dependency injection \u2014 makes classes mockable and AI-generated code verifiable");
2290
+ }
2291
+ if (externalStateMutations > totalFunctions * 0.3) {
2292
+ recommendations.push("Reduce direct state mutations \u2014 return values instead to improve observability");
2293
+ }
2294
+ return {
2295
+ score,
2296
+ rating,
2297
+ dimensions: {
2298
+ testCoverageRatio,
2299
+ purityScore,
2300
+ dependencyInjectionScore,
2301
+ interfaceFocusScore,
2302
+ observabilityScore
2303
+ },
2304
+ aiChangeSafetyRating,
2305
+ recommendations
2306
+ };
2307
+ }
2308
+ function calculateDocDrift(params) {
2309
+ const { uncommentedExports, totalExports, outdatedComments, undocumentedComplexity } = params;
2310
+ const uncommentedRatio = totalExports > 0 ? uncommentedExports / totalExports : 0;
2311
+ const outdatedScore = Math.min(100, outdatedComments * 15);
2312
+ const uncommentedScore = Math.min(100, uncommentedRatio * 100);
2313
+ const complexityScore = Math.min(100, undocumentedComplexity * 10);
2314
+ const score = Math.round(
2315
+ outdatedScore * 0.6 + uncommentedScore * 0.2 + complexityScore * 0.2
2316
+ );
2317
+ const finalScore = Math.min(100, Math.max(0, score));
2318
+ let rating;
2319
+ if (finalScore < 10) rating = "minimal";
2320
+ else if (finalScore < 30) rating = "low";
2321
+ else if (finalScore < 60) rating = "moderate";
2322
+ else if (finalScore < 85) rating = "high";
2323
+ else rating = "severe";
2324
+ const recommendations = [];
2325
+ if (outdatedComments > 0) {
2326
+ recommendations.push(`Update or remove ${outdatedComments} outdated comments that contradict the code.`);
2327
+ }
2328
+ if (uncommentedRatio > 0.3) {
2329
+ recommendations.push(`Add JSDoc to ${uncommentedExports} uncommented exports.`);
2330
+ }
2331
+ if (undocumentedComplexity > 0) {
2332
+ recommendations.push(`Explain the business logic for ${undocumentedComplexity} highly complex functions.`);
2333
+ }
2334
+ return {
2335
+ score: finalScore,
2336
+ rating,
2337
+ dimensions: {
2338
+ uncommentedExports,
2339
+ outdatedComments,
2340
+ undocumentedComplexity
2341
+ },
2342
+ recommendations
2343
+ };
2344
+ }
2345
+ function calculateDependencyHealth(params) {
2346
+ const { totalPackages, outdatedPackages, deprecatedPackages, trainingCutoffSkew } = params;
2347
+ const outdatedRatio = totalPackages > 0 ? outdatedPackages / totalPackages : 0;
2348
+ const deprecatedRatio = totalPackages > 0 ? deprecatedPackages / totalPackages : 0;
2349
+ const outdatedScore = Math.max(0, 100 - outdatedRatio * 200);
2350
+ const deprecatedScore = Math.max(0, 100 - deprecatedRatio * 500);
2351
+ const skewScore = Math.max(0, 100 - trainingCutoffSkew * 100);
2352
+ const rawScore = outdatedScore * 0.3 + deprecatedScore * 0.4 + skewScore * 0.3;
2353
+ const score = Math.round(Math.min(100, Math.max(0, rawScore)));
2354
+ let rating;
2355
+ if (score >= 85) rating = "excellent";
2356
+ else if (score >= 70) rating = "good";
2357
+ else if (score >= 50) rating = "moderate";
2358
+ else if (score >= 30) rating = "poor";
2359
+ else rating = "hazardous";
2360
+ let aiKnowledgeConfidence;
2361
+ if (trainingCutoffSkew < 0.2 && deprecatedPackages === 0) aiKnowledgeConfidence = "high";
2362
+ else if (trainingCutoffSkew < 0.5 && deprecatedPackages <= 2) aiKnowledgeConfidence = "moderate";
2363
+ else if (trainingCutoffSkew < 0.8) aiKnowledgeConfidence = "low";
2364
+ else aiKnowledgeConfidence = "blind";
2365
+ const recommendations = [];
2366
+ if (deprecatedPackages > 0) {
2367
+ recommendations.push(`Replace ${deprecatedPackages} deprecated packages, as AI will struggle to find modern solutions.`);
2368
+ }
2369
+ if (outdatedRatio > 0.2) {
2370
+ recommendations.push(`Update ${outdatedPackages} outdated packages to keep APIs aligned with AI training data.`);
2371
+ }
2372
+ if (trainingCutoffSkew > 0.5) {
2373
+ recommendations.push("High training cutoff skew detected. AI may hallucinate APIs that were introduced recently.");
2374
+ }
2375
+ return {
2376
+ score,
2377
+ rating,
2378
+ dimensions: {
2379
+ outdatedPackages,
2380
+ deprecatedPackages,
2381
+ trainingCutoffSkew
2382
+ },
2383
+ aiKnowledgeConfidence,
2384
+ recommendations
2385
+ };
2386
+ }
2387
+ function calculateExtendedFutureProofScore(params) {
2388
+ const loadScore = 100 - params.cognitiveLoad.score;
2389
+ const entropyScore = 100 - params.patternEntropy.entropy * 100;
2390
+ const cohesionScore = params.conceptCohesion.score * 100;
2391
+ const hallucinationScore = 100 - params.hallucinationRisk.score;
2392
+ const groundingScore = params.agentGrounding.score;
2393
+ const testabilityScore = params.testability.score;
2394
+ const docDriftScore = params.docDrift ? 100 - params.docDrift.score : 100;
2395
+ const depsHealthScore = params.dependencyHealth ? params.dependencyHealth.score : 100;
2396
+ let totalWeight = 0.8;
2397
+ let overall = loadScore * 0.15 + entropyScore * 0.1 + cohesionScore * 0.1 + hallucinationScore * 0.15 + groundingScore * 0.15 + testabilityScore * 0.15;
2398
+ if (params.docDrift) {
2399
+ overall += docDriftScore * 0.1;
2400
+ totalWeight += 0.1;
2401
+ }
2402
+ if (params.dependencyHealth) {
2403
+ overall += depsHealthScore * 0.1;
2404
+ totalWeight += 0.1;
2405
+ }
2406
+ overall = Math.round(overall / totalWeight);
2407
+ const factors = [
2408
+ {
2409
+ name: "Cognitive Load",
2410
+ impact: Math.round(loadScore - 50),
2411
+ description: params.cognitiveLoad.rating
2412
+ },
2413
+ {
2414
+ name: "Pattern Entropy",
2415
+ impact: Math.round(entropyScore - 50),
2416
+ description: params.patternEntropy.rating
2417
+ },
2418
+ {
2419
+ name: "Concept Cohesion",
2420
+ impact: Math.round(cohesionScore - 50),
2421
+ description: params.conceptCohesion.rating
2422
+ },
2423
+ {
2424
+ name: "Hallucination Risk",
2425
+ impact: Math.round(hallucinationScore - 50),
2426
+ description: `${params.hallucinationRisk.rating} risk (${params.hallucinationRisk.score}/100 raw)`
2427
+ },
2428
+ {
2429
+ name: "Agent Grounding",
2430
+ impact: Math.round(groundingScore - 50),
2431
+ description: params.agentGrounding.rating
2432
+ },
2433
+ {
2434
+ name: "Testability",
2435
+ impact: Math.round(testabilityScore - 50),
2436
+ description: `${params.testability.rating} \u2014 AI changes are ${params.testability.aiChangeSafetyRating}`
2437
+ }
2438
+ ];
2439
+ if (params.docDrift) {
2440
+ factors.push({
2441
+ name: "Documentation Drift",
2442
+ impact: Math.round(docDriftScore - 50),
2443
+ description: `${params.docDrift.rating} risk of hallucination from drift`
2444
+ });
2445
+ }
2446
+ if (params.dependencyHealth) {
2447
+ factors.push({
2448
+ name: "Dependency Health",
2449
+ impact: Math.round(depsHealthScore - 50),
2450
+ description: `${params.dependencyHealth.rating} health \u2014 AI knowledge is ${params.dependencyHealth.aiKnowledgeConfidence}`
2451
+ });
2452
+ }
2453
+ const recommendations = [];
2454
+ for (const rec of params.hallucinationRisk.recommendations) {
2455
+ recommendations.push({ action: rec, estimatedImpact: 8, priority: "high" });
2456
+ }
2457
+ for (const rec of params.agentGrounding.recommendations) {
2458
+ recommendations.push({ action: rec, estimatedImpact: 6, priority: "medium" });
2459
+ }
2460
+ for (const rec of params.testability.recommendations) {
2461
+ const priority = params.testability.aiChangeSafetyRating === "blind-risk" ? "high" : "medium";
2462
+ recommendations.push({ action: rec, estimatedImpact: 10, priority });
2463
+ }
2464
+ for (const rec of params.patternEntropy.recommendations) {
2465
+ recommendations.push({ action: rec, estimatedImpact: 5, priority: "low" });
2466
+ }
2467
+ if (params.docDrift) {
2468
+ for (const rec of params.docDrift.recommendations) {
2469
+ recommendations.push({ action: rec, estimatedImpact: 8, priority: "high" });
2470
+ }
2471
+ }
2472
+ if (params.dependencyHealth) {
2473
+ for (const rec of params.dependencyHealth.recommendations) {
2474
+ recommendations.push({ action: rec, estimatedImpact: 7, priority: "medium" });
2475
+ }
2476
+ }
2477
+ const semanticDistanceAvg = params.semanticDistances && params.semanticDistances.length > 0 ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
2478
+ return {
2479
+ toolName: "future-proof",
2480
+ score: overall,
2481
+ rawMetrics: {
2482
+ cognitiveLoadScore: params.cognitiveLoad.score,
2483
+ entropyScore: params.patternEntropy.entropy,
2484
+ cohesionScore: params.conceptCohesion.score,
2485
+ hallucinationRiskScore: params.hallucinationRisk.score,
2486
+ agentGroundingScore: params.agentGrounding.score,
2487
+ testabilityScore: params.testability.score,
2488
+ docDriftScore: params.docDrift?.score,
2489
+ dependencyHealthScore: params.dependencyHealth?.score,
2490
+ semanticDistanceAvg
2491
+ },
2492
+ factors,
2493
+ recommendations
2494
+ };
2495
+ }
1898
2496
 
1899
2497
  // src/utils/history.ts
1900
2498
  var import_fs4 = require("fs");
@@ -1971,20 +2569,28 @@ function clearHistory(rootDir) {
1971
2569
  }
1972
2570
  // Annotate the CommonJS export names for ESM import in node:
1973
2571
  0 && (module.exports = {
2572
+ CONTEXT_TIER_THRESHOLDS,
1974
2573
  DEFAULT_COST_CONFIG,
1975
2574
  DEFAULT_EXCLUDE,
1976
2575
  DEFAULT_TOOL_WEIGHTS,
1977
2576
  LANGUAGE_EXTENSIONS,
1978
2577
  Language,
2578
+ MODEL_PRICING_PRESETS,
1979
2579
  ParseError,
1980
2580
  ParserFactory,
1981
2581
  PythonParser,
2582
+ SIZE_ADJUSTED_THRESHOLDS,
1982
2583
  TOOL_NAME_MAP,
1983
2584
  TypeScriptParser,
2585
+ calculateAgentGrounding,
1984
2586
  calculateCognitiveLoad,
1985
2587
  calculateComprehensionDifficulty,
1986
2588
  calculateConceptCohesion,
2589
+ calculateDependencyHealth,
2590
+ calculateDocDrift,
2591
+ calculateExtendedFutureProofScore,
1987
2592
  calculateFutureProofScore,
2593
+ calculateHallucinationRisk,
1988
2594
  calculateImportSimilarity,
1989
2595
  calculateKnowledgeConcentration,
1990
2596
  calculateMonthlyCost,
@@ -1995,6 +2601,7 @@ function clearHistory(rootDir) {
1995
2601
  calculateScoreTrend,
1996
2602
  calculateSemanticDistance,
1997
2603
  calculateTechnicalDebtInterest,
2604
+ calculateTestabilityIndex,
1998
2605
  clearHistory,
1999
2606
  estimateTokens,
2000
2607
  exportHistory,
@@ -2010,9 +2617,13 @@ function clearHistory(rootDir) {
2010
2617
  getElapsedTime,
2011
2618
  getFileExtension,
2012
2619
  getHistorySummary,
2620
+ getModelPreset,
2013
2621
  getParser,
2622
+ getProjectSizeTier,
2014
2623
  getRating,
2015
2624
  getRatingDisplay,
2625
+ getRatingWithContext,
2626
+ getRecommendedThreshold,
2016
2627
  getSupportedLanguages,
2017
2628
  getToolWeight,
2018
2629
  handleCLIError,