@aiready/core 0.9.26 → 0.9.27

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