@aiready/core 0.9.25 → 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,22 +30,40 @@ 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,
47
+ calculateCognitiveLoad: () => calculateCognitiveLoad,
43
48
  calculateComprehensionDifficulty: () => calculateComprehensionDifficulty,
49
+ calculateConceptCohesion: () => calculateConceptCohesion,
50
+ calculateExtendedFutureProofScore: () => calculateExtendedFutureProofScore,
51
+ calculateFutureProofScore: () => calculateFutureProofScore,
52
+ calculateHallucinationRisk: () => calculateHallucinationRisk,
44
53
  calculateImportSimilarity: () => calculateImportSimilarity,
54
+ calculateKnowledgeConcentration: () => calculateKnowledgeConcentration,
45
55
  calculateMonthlyCost: () => calculateMonthlyCost,
46
56
  calculateOverallScore: () => calculateOverallScore,
57
+ calculatePatternEntropy: () => calculatePatternEntropy,
47
58
  calculateProductivityImpact: () => calculateProductivityImpact,
59
+ calculateRemediationVelocity: () => calculateRemediationVelocity,
60
+ calculateScoreTrend: () => calculateScoreTrend,
61
+ calculateSemanticDistance: () => calculateSemanticDistance,
62
+ calculateTechnicalDebtInterest: () => calculateTechnicalDebtInterest,
63
+ calculateTestabilityIndex: () => calculateTestabilityIndex,
64
+ clearHistory: () => clearHistory,
48
65
  estimateTokens: () => estimateTokens,
66
+ exportHistory: () => exportHistory,
49
67
  extractFunctions: () => extractFunctions,
50
68
  extractImports: () => extractImports,
51
69
  formatAcceptanceRate: () => formatAcceptanceRate,
@@ -54,11 +72,17 @@ __export(index_exports, {
54
72
  formatScore: () => formatScore,
55
73
  formatToolScore: () => formatToolScore,
56
74
  generateHTML: () => generateHTML,
75
+ getDebtBreakdown: () => getDebtBreakdown,
57
76
  getElapsedTime: () => getElapsedTime,
58
77
  getFileExtension: () => getFileExtension,
78
+ getHistorySummary: () => getHistorySummary,
79
+ getModelPreset: () => getModelPreset,
59
80
  getParser: () => getParser,
81
+ getProjectSizeTier: () => getProjectSizeTier,
60
82
  getRating: () => getRating,
61
83
  getRatingDisplay: () => getRatingDisplay,
84
+ getRatingWithContext: () => getRatingWithContext,
85
+ getRecommendedThreshold: () => getRecommendedThreshold,
62
86
  getSupportedLanguages: () => getSupportedLanguages,
63
87
  getToolWeight: () => getToolWeight,
64
88
  handleCLIError: () => handleCLIError,
@@ -67,6 +91,7 @@ __export(index_exports, {
67
91
  isSourceFile: () => isSourceFile,
68
92
  loadConfig: () => loadConfig,
69
93
  loadMergedConfig: () => loadMergedConfig,
94
+ loadScoreHistory: () => loadScoreHistory,
70
95
  mergeConfigWithDefaults: () => mergeConfigWithDefaults,
71
96
  normalizeToolName: () => normalizeToolName,
72
97
  parseCode: () => parseCode,
@@ -75,6 +100,7 @@ __export(index_exports, {
75
100
  predictAcceptanceRate: () => predictAcceptanceRate,
76
101
  readFileContent: () => readFileContent,
77
102
  resolveOutputPath: () => resolveOutputPath,
103
+ saveScoreEntry: () => saveScoreEntry,
78
104
  scanFiles: () => scanFiles
79
105
  });
80
106
  module.exports = __toCommonJS(index_exports);
@@ -679,16 +705,57 @@ var DEFAULT_TOOL_WEIGHTS = {
679
705
  "pattern-detect": 40,
680
706
  "context-analyzer": 35,
681
707
  "consistency": 25,
682
- "doc-drift": 20,
683
- "deps": 15
708
+ "hallucination-risk": 20,
709
+ "agent-grounding": 18,
710
+ "testability": 18,
711
+ "doc-drift": 15,
712
+ "deps": 12
684
713
  };
685
714
  var TOOL_NAME_MAP = {
686
715
  "patterns": "pattern-detect",
687
716
  "context": "context-analyzer",
688
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",
689
724
  "doc-drift": "doc-drift",
725
+ "docs": "doc-drift",
690
726
  "deps": "deps"
691
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
+ }
692
759
  function normalizeToolName(shortName) {
693
760
  return TOOL_NAME_MAP[shortName] || shortName;
694
761
  }
@@ -771,6 +838,11 @@ function getRating(score) {
771
838
  if (score >= 40) return "Needs Work";
772
839
  return "Critical";
773
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
+ }
774
846
  function getRatingDisplay(rating) {
775
847
  switch (rating) {
776
848
  case "Excellent":
@@ -819,11 +891,87 @@ function formatToolScore(output) {
819
891
  }
820
892
 
821
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
+ }
822
970
  var DEFAULT_COST_CONFIG = {
823
- pricePer1KTokens: 0.01,
824
- // $0.01 per 1K tokens (GPT-4)
825
- queriesPerDevPerDay: 50,
826
- // 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)
827
975
  developerCount: 5,
828
976
  // Default team size
829
977
  daysPerMonth: 30
@@ -874,43 +1022,63 @@ function calculateProductivityImpact(issues, hourlyRate = DEFAULT_HOURLY_RATE) {
874
1022
  }
875
1023
  function predictAcceptanceRate(toolOutputs) {
876
1024
  const factors = [];
1025
+ const baseRate = 0.3;
877
1026
  const patterns = toolOutputs.get("pattern-detect");
878
1027
  if (patterns) {
879
- const patternImpact = (patterns.score - 50) * 0.3;
1028
+ const patternImpact = (patterns.score - 50) * 3e-3;
880
1029
  factors.push({
881
1030
  name: "Semantic Duplication",
882
- impact: Math.round(patternImpact)
1031
+ impact: Math.round(patternImpact * 100)
883
1032
  });
884
1033
  }
885
1034
  const context = toolOutputs.get("context-analyzer");
886
1035
  if (context) {
887
- const contextImpact = (context.score - 50) * 0.4;
1036
+ const contextImpact = (context.score - 50) * 4e-3;
888
1037
  factors.push({
889
1038
  name: "Context Efficiency",
890
- impact: Math.round(contextImpact)
1039
+ impact: Math.round(contextImpact * 100)
891
1040
  });
892
1041
  }
893
1042
  const consistency = toolOutputs.get("consistency");
894
1043
  if (consistency) {
895
- const consistencyImpact = (consistency.score - 50) * 0.3;
1044
+ const consistencyImpact = (consistency.score - 50) * 2e-3;
896
1045
  factors.push({
897
1046
  name: "Code Consistency",
898
- 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)
899
1056
  });
900
1057
  }
901
- const baseRate = 0.65;
902
1058
  const totalImpact = factors.reduce((sum, f) => sum + f.impact / 100, 0);
903
- const rate = Math.max(0.1, Math.min(0.95, baseRate + totalImpact));
904
- 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;
905
1065
  return {
906
1066
  rate: Math.round(rate * 100) / 100,
907
1067
  confidence,
908
1068
  factors
909
1069
  };
910
1070
  }
911
- function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentation, consistencyScore, totalFiles) {
912
- const budgetFactor = Math.min(100, Math.max(0, (contextBudget - 5e3) / 250));
913
- 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));
914
1082
  const fragmentationFactor = Math.min(100, Math.max(0, (fragmentation - 0.3) * 250));
915
1083
  const consistencyFactor = Math.min(100, Math.max(0, 100 - consistencyScore));
916
1084
  const fileFactor = Math.min(100, Math.max(0, (totalFiles - 50) / 5));
@@ -930,12 +1098,12 @@ function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentat
930
1098
  {
931
1099
  name: "Context Budget",
932
1100
  contribution: Math.round(budgetFactor * 0.35),
933
- description: `${Math.round(contextBudget)} tokens required`
1101
+ description: `${Math.round(contextBudget)} tokens required (${modelTier} model tier: ideal <${idealBudget.toLocaleString()})`
934
1102
  },
935
1103
  {
936
1104
  name: "Import Depth",
937
1105
  contribution: Math.round(depthFactor * 0.2),
938
- description: `${importDepth.toFixed(1)} average levels`
1106
+ description: `${importDepth.toFixed(1)} average levels (ideal <${idealDepth} for ${modelTier})`
939
1107
  },
940
1108
  {
941
1109
  name: "Code Fragmentation",
@@ -978,6 +1146,200 @@ function formatHours(hours) {
978
1146
  function formatAcceptanceRate(rate) {
979
1147
  return `${Math.round(rate * 100)}%`;
980
1148
  }
1149
+ function calculateScoreTrend(history) {
1150
+ if (history.length < 2) {
1151
+ return {
1152
+ direction: "stable",
1153
+ change30Days: 0,
1154
+ change90Days: 0,
1155
+ velocity: 0,
1156
+ projectedScore: history[0]?.overallScore || 100
1157
+ };
1158
+ }
1159
+ const now = /* @__PURE__ */ new Date();
1160
+ const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1e3);
1161
+ const ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1e3);
1162
+ const last30Days = history.filter((e) => new Date(e.timestamp) >= thirtyDaysAgo);
1163
+ const last90Days = history.filter((e) => new Date(e.timestamp) >= ninetyDaysAgo);
1164
+ const currentScore = history[history.length - 1].overallScore;
1165
+ const thirtyDaysAgoScore = last30Days[0]?.overallScore || currentScore;
1166
+ const ninetyDaysAgoScore = last90Days[0]?.overallScore || thirtyDaysAgoScore;
1167
+ const change30Days = currentScore - thirtyDaysAgoScore;
1168
+ const change90Days = currentScore - ninetyDaysAgoScore;
1169
+ const weeksOfData = Math.max(1, history.length / 7);
1170
+ const totalChange = currentScore - history[0].overallScore;
1171
+ const velocity = totalChange / weeksOfData;
1172
+ let direction;
1173
+ if (change30Days > 3) direction = "improving";
1174
+ else if (change30Days < -3) direction = "degrading";
1175
+ else direction = "stable";
1176
+ const projectedScore = Math.max(0, Math.min(100, currentScore + velocity * 4));
1177
+ return {
1178
+ direction,
1179
+ change30Days,
1180
+ change90Days,
1181
+ velocity: Math.round(velocity * 10) / 10,
1182
+ projectedScore: Math.round(projectedScore)
1183
+ };
1184
+ }
1185
+ function calculateRemediationVelocity(history, currentIssues) {
1186
+ if (history.length < 2) {
1187
+ return {
1188
+ issuesFixedThisWeek: 0,
1189
+ avgIssuesPerWeek: 0,
1190
+ trend: "stable",
1191
+ estimatedCompletionWeeks: currentIssues > 0 ? Infinity : 0
1192
+ };
1193
+ }
1194
+ const now = /* @__PURE__ */ new Date();
1195
+ const oneWeekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3);
1196
+ const twoWeeksAgo = new Date(now.getTime() - 14 * 24 * 60 * 60 * 1e3);
1197
+ const thisWeek = history.filter((e) => new Date(e.timestamp) >= oneWeekAgo);
1198
+ const lastWeek = history.filter(
1199
+ (e) => new Date(e.timestamp) >= twoWeeksAgo && new Date(e.timestamp) < oneWeekAgo
1200
+ );
1201
+ const issuesFixedThisWeek = thisWeek.length > 1 ? thisWeek[0].totalIssues - thisWeek[thisWeek.length - 1].totalIssues : 0;
1202
+ const totalIssuesFixed = history[0].totalIssues - history[history.length - 1].totalIssues;
1203
+ const weeksOfData = Math.max(1, history.length / 7);
1204
+ const avgIssuesPerWeek = totalIssuesFixed / weeksOfData;
1205
+ let trend;
1206
+ if (lastWeek.length > 1) {
1207
+ const lastWeekFixed = lastWeek[0].totalIssues - lastWeek[lastWeek.length - 1].totalIssues;
1208
+ if (issuesFixedThisWeek > lastWeekFixed * 1.2) trend = "accelerating";
1209
+ else if (issuesFixedThisWeek < lastWeekFixed * 0.8) trend = "decelerating";
1210
+ else trend = "stable";
1211
+ } else {
1212
+ trend = "stable";
1213
+ }
1214
+ const estimatedCompletionWeeks = avgIssuesPerWeek > 0 ? Math.ceil(currentIssues / avgIssuesPerWeek) : Infinity;
1215
+ return {
1216
+ issuesFixedThisWeek: Math.max(0, issuesFixedThisWeek),
1217
+ avgIssuesPerWeek: Math.round(avgIssuesPerWeek * 10) / 10,
1218
+ trend,
1219
+ estimatedCompletionWeeks
1220
+ };
1221
+ }
1222
+ function calculateKnowledgeConcentration(files, authorData) {
1223
+ if (files.length === 0) {
1224
+ return {
1225
+ score: 0,
1226
+ rating: "low",
1227
+ analysis: {
1228
+ uniqueConceptFiles: 0,
1229
+ totalFiles: 0,
1230
+ concentrationRatio: 0,
1231
+ singleAuthorFiles: 0,
1232
+ orphanFiles: 0
1233
+ },
1234
+ recommendations: ["No files to analyze"]
1235
+ };
1236
+ }
1237
+ const orphanFiles = files.filter((f) => f.exports < 2 && f.imports < 2).length;
1238
+ const avgExports = files.reduce((sum, f) => sum + f.exports, 0) / files.length;
1239
+ const uniqueConceptFiles = files.filter((f) => f.exports > avgExports * 2).length;
1240
+ const totalExports = files.reduce((sum, f) => sum + f.exports, 0);
1241
+ const concentrationRatio = totalExports > 0 ? uniqueConceptFiles / files.length : 0;
1242
+ let singleAuthorFiles = 0;
1243
+ if (authorData) {
1244
+ for (const files2 of authorData.values()) {
1245
+ if (files2.length === 1) singleAuthorFiles++;
1246
+ }
1247
+ }
1248
+ const orphanRisk = orphanFiles / files.length * 30;
1249
+ const uniqueRisk = concentrationRatio * 40;
1250
+ const singleAuthorRisk = authorData ? singleAuthorFiles / files.length * 30 : 0;
1251
+ const score = Math.min(100, Math.round(orphanRisk + uniqueRisk + singleAuthorRisk));
1252
+ let rating;
1253
+ if (score < 20) rating = "low";
1254
+ else if (score < 40) rating = "moderate";
1255
+ else if (score < 70) rating = "high";
1256
+ else rating = "critical";
1257
+ const recommendations = [];
1258
+ if (orphanFiles > files.length * 0.2) {
1259
+ recommendations.push(`Reduce ${orphanFiles} orphan files by connecting them to main modules`);
1260
+ }
1261
+ if (uniqueConceptFiles > files.length * 0.1) {
1262
+ recommendations.push("Distribute high-export files into more focused modules");
1263
+ }
1264
+ if (authorData && singleAuthorFiles > files.length * 0.3) {
1265
+ recommendations.push("Increase knowledge sharing to reduce single-author dependencies");
1266
+ }
1267
+ return {
1268
+ score,
1269
+ rating,
1270
+ analysis: {
1271
+ uniqueConceptFiles,
1272
+ totalFiles: files.length,
1273
+ concentrationRatio: Math.round(concentrationRatio * 100) / 100,
1274
+ singleAuthorFiles,
1275
+ orphanFiles
1276
+ },
1277
+ recommendations
1278
+ };
1279
+ }
1280
+ function calculateTechnicalDebtInterest(params) {
1281
+ const { currentMonthlyCost, issues, monthsOpen } = params;
1282
+ const criticalCount = issues.filter((i) => i.severity === "critical").length;
1283
+ const majorCount = issues.filter((i) => i.severity === "major").length;
1284
+ const minorCount = issues.filter((i) => i.severity === "minor").length;
1285
+ const severityWeight = (criticalCount * 3 + majorCount * 2 + minorCount * 1) / Math.max(1, issues.length);
1286
+ const baseRate = 0.02 + severityWeight * 0.01;
1287
+ const timeMultiplier = Math.max(1, 1 + monthsOpen * 0.1);
1288
+ const monthlyRate = baseRate * timeMultiplier;
1289
+ const projectDebt = (principal2, months) => {
1290
+ let debt = principal2;
1291
+ for (let i = 0; i < months; i++) {
1292
+ debt = debt * (1 + monthlyRate);
1293
+ }
1294
+ return Math.round(debt);
1295
+ };
1296
+ const principal = currentMonthlyCost * 12;
1297
+ const projections = {
1298
+ months6: projectDebt(principal, 6),
1299
+ months12: projectDebt(principal, 12),
1300
+ months24: projectDebt(principal, 24)
1301
+ };
1302
+ return {
1303
+ monthlyRate: Math.round(monthlyRate * 1e4) / 100,
1304
+ annualRate: Math.round((Math.pow(1 + monthlyRate, 12) - 1) * 1e4) / 100,
1305
+ principal,
1306
+ projections,
1307
+ monthlyCost: Math.round(currentMonthlyCost * (1 + monthlyRate) * 100) / 100
1308
+ };
1309
+ }
1310
+ function getDebtBreakdown(patternCost, contextCost, consistencyCost) {
1311
+ const breakdowns = [
1312
+ {
1313
+ category: "Semantic Duplication",
1314
+ currentCost: patternCost,
1315
+ monthlyGrowthRate: 5,
1316
+ // Grows as devs copy-paste
1317
+ priority: patternCost > 1e3 ? "high" : "medium",
1318
+ fixCost: patternCost * 3
1319
+ // Fixing costs 3x current waste
1320
+ },
1321
+ {
1322
+ category: "Context Fragmentation",
1323
+ currentCost: contextCost,
1324
+ monthlyGrowthRate: 3,
1325
+ // Grows with new features
1326
+ priority: contextCost > 500 ? "high" : "medium",
1327
+ fixCost: contextCost * 2.5
1328
+ },
1329
+ {
1330
+ category: "Consistency Issues",
1331
+ currentCost: consistencyCost,
1332
+ monthlyGrowthRate: 2,
1333
+ // Grows with new devs
1334
+ priority: consistencyCost > 200 ? "medium" : "low",
1335
+ fixCost: consistencyCost * 1.5
1336
+ }
1337
+ ];
1338
+ return breakdowns.sort((a, b) => {
1339
+ const priorityOrder = { high: 0, medium: 1, low: 2 };
1340
+ return priorityOrder[a.priority] - priorityOrder[b.priority];
1341
+ });
1342
+ }
981
1343
 
982
1344
  // src/parsers/typescript-parser.ts
983
1345
  var import_typescript_estree2 = require("@typescript-eslint/typescript-estree");
@@ -1458,24 +1820,672 @@ function isFileSupported(filePath) {
1458
1820
  function getSupportedLanguages() {
1459
1821
  return ParserFactory.getInstance().getSupportedLanguages();
1460
1822
  }
1823
+
1824
+ // src/future-proof-metrics.ts
1825
+ function calculateCognitiveLoad(params) {
1826
+ const { linesOfCode, exportCount, importCount, uniqueConcepts, cyclomaticComplexity = 1 } = params;
1827
+ const sizeFactor = {
1828
+ name: "Size Complexity",
1829
+ score: Math.min(100, Math.max(0, (linesOfCode - 50) / 10)),
1830
+ weight: 0.3,
1831
+ description: `${linesOfCode} lines of code`
1832
+ };
1833
+ const interfaceFactor = {
1834
+ name: "Interface Complexity",
1835
+ score: Math.min(100, exportCount * 5),
1836
+ weight: 0.25,
1837
+ description: `${exportCount} exported concepts`
1838
+ };
1839
+ const dependencyFactor = {
1840
+ name: "Dependency Complexity",
1841
+ score: Math.min(100, importCount * 8),
1842
+ weight: 0.25,
1843
+ description: `${importCount} dependencies`
1844
+ };
1845
+ const conceptDensity = linesOfCode > 0 ? uniqueConcepts / linesOfCode : 0;
1846
+ const conceptFactor = {
1847
+ name: "Conceptual Density",
1848
+ score: Math.min(100, conceptDensity * 500),
1849
+ weight: 0.2,
1850
+ description: `${uniqueConcepts} unique concepts`
1851
+ };
1852
+ const factors = [sizeFactor, interfaceFactor, dependencyFactor, conceptFactor];
1853
+ const score = factors.reduce((sum, f) => sum + f.score * f.weight, 0);
1854
+ let rating;
1855
+ if (score < 20) rating = "trivial";
1856
+ else if (score < 40) rating = "easy";
1857
+ else if (score < 60) rating = "moderate";
1858
+ else if (score < 80) rating = "difficult";
1859
+ else rating = "expert";
1860
+ return {
1861
+ score: Math.round(score),
1862
+ rating,
1863
+ factors,
1864
+ rawValues: {
1865
+ size: linesOfCode,
1866
+ complexity: cyclomaticComplexity,
1867
+ dependencyCount: importCount,
1868
+ conceptCount: uniqueConcepts
1869
+ }
1870
+ };
1871
+ }
1872
+ function calculateSemanticDistance(params) {
1873
+ const { file1, file2, file1Domain, file2Domain, sharedDependencies } = params;
1874
+ const domainDistance = file1Domain === file2Domain ? 0 : file1Domain && file2Domain ? 0.5 : 0.8;
1875
+ const importOverlap = sharedDependencies.length / Math.max(1, Math.min(params.file1Imports.length, params.file2Imports.length));
1876
+ const importDistance = 1 - importOverlap;
1877
+ const distance = domainDistance * 0.4 + importDistance * 0.3 + (sharedDependencies.length > 0 ? 0 : 0.3);
1878
+ let relationship;
1879
+ if (file1 === file2) relationship = "same-file";
1880
+ else if (file1Domain === file2Domain) relationship = "same-domain";
1881
+ else if (sharedDependencies.length > 0) relationship = "cross-domain";
1882
+ else relationship = "unrelated";
1883
+ const pathItems = [file1Domain, ...sharedDependencies, file2Domain].filter((s) => typeof s === "string" && s.length > 0);
1884
+ return {
1885
+ between: [file1, file2],
1886
+ distance: Math.round(distance * 100) / 100,
1887
+ relationship,
1888
+ path: pathItems,
1889
+ reason: relationship === "same-domain" ? `Both in "${file1Domain}" domain` : relationship === "cross-domain" ? `Share ${sharedDependencies.length} dependency(ies)` : "No strong semantic relationship detected"
1890
+ };
1891
+ }
1892
+ function calculatePatternEntropy(files) {
1893
+ if (files.length === 0) {
1894
+ return {
1895
+ domain: "unknown",
1896
+ entropy: 0,
1897
+ rating: "crystalline",
1898
+ distribution: { locationCount: 0, dominantLocation: "", giniCoefficient: 0 },
1899
+ recommendations: ["No files to analyze"]
1900
+ };
1901
+ }
1902
+ const dirGroups = /* @__PURE__ */ new Map();
1903
+ for (const file of files) {
1904
+ const parts = file.path.split("/").slice(0, 4).join("/") || "root";
1905
+ dirGroups.set(parts, (dirGroups.get(parts) || 0) + 1);
1906
+ }
1907
+ const counts = Array.from(dirGroups.values());
1908
+ const total = counts.reduce((a, b) => a + b, 0);
1909
+ let entropy = 0;
1910
+ for (const count of counts) {
1911
+ const p = count / total;
1912
+ if (p > 0) entropy -= p * Math.log2(p);
1913
+ }
1914
+ const maxEntropy = Math.log2(dirGroups.size || 1);
1915
+ const normalizedEntropy = maxEntropy > 0 ? entropy / maxEntropy : 0;
1916
+ const sortedCounts = counts.sort((a, b) => a - b);
1917
+ let gini = 0;
1918
+ for (let i = 0; i < sortedCounts.length; i++) {
1919
+ gini += (2 * (i + 1) - sortedCounts.length - 1) * sortedCounts[i];
1920
+ }
1921
+ gini /= total * sortedCounts.length;
1922
+ let dominantLocation = "";
1923
+ let maxCount = 0;
1924
+ for (const [loc, count] of dirGroups.entries()) {
1925
+ if (count > maxCount) {
1926
+ maxCount = count;
1927
+ dominantLocation = loc;
1928
+ }
1929
+ }
1930
+ let rating;
1931
+ if (normalizedEntropy < 0.2) rating = "crystalline";
1932
+ else if (normalizedEntropy < 0.4) rating = "well-structured";
1933
+ else if (normalizedEntropy < 0.6) rating = "moderate";
1934
+ else if (normalizedEntropy < 0.8) rating = "fragmented";
1935
+ else rating = "chaotic";
1936
+ const recommendations = [];
1937
+ if (normalizedEntropy > 0.5) {
1938
+ recommendations.push(`Consolidate ${files.length} files into fewer directories by domain`);
1939
+ }
1940
+ if (dirGroups.size > 5) {
1941
+ recommendations.push("Consider barrel exports to reduce directory navigation");
1942
+ }
1943
+ if (gini > 0.5) {
1944
+ recommendations.push("Redistribute files more evenly across directories");
1945
+ }
1946
+ const firstFile = files.length > 0 ? files[0] : null;
1947
+ const domainValue = firstFile ? firstFile.domain : "mixed";
1948
+ return {
1949
+ domain: domainValue,
1950
+ entropy: Math.round(normalizedEntropy * 100) / 100,
1951
+ rating,
1952
+ distribution: {
1953
+ locationCount: dirGroups.size,
1954
+ dominantLocation,
1955
+ giniCoefficient: Math.round(gini * 100) / 100
1956
+ },
1957
+ recommendations
1958
+ };
1959
+ }
1960
+ function calculateConceptCohesion(params) {
1961
+ const { exports: exports2 } = params;
1962
+ if (exports2.length === 0) {
1963
+ return {
1964
+ score: 1,
1965
+ rating: "excellent",
1966
+ analysis: { uniqueDomains: 0, domainConcentration: 0, exportPurposeClarity: 1 }
1967
+ };
1968
+ }
1969
+ const allDomains = [];
1970
+ for (const exp of exports2) {
1971
+ if (exp.inferredDomain) allDomains.push(exp.inferredDomain);
1972
+ if (exp.domains) allDomains.push(...exp.domains);
1973
+ }
1974
+ const uniqueDomains = new Set(allDomains);
1975
+ const domainCounts = /* @__PURE__ */ new Map();
1976
+ for (const d of allDomains) {
1977
+ domainCounts.set(d, (domainCounts.get(d) || 0) + 1);
1978
+ }
1979
+ const maxCount = Math.max(...Array.from(domainCounts.values()), 1);
1980
+ const domainConcentration = maxCount / allDomains.length;
1981
+ const exportPurposeClarity = 1 - (uniqueDomains.size - 1) / Math.max(1, exports2.length);
1982
+ const score = domainConcentration * 0.5 + exportPurposeClarity * 0.5;
1983
+ let rating;
1984
+ if (score > 0.8) rating = "excellent";
1985
+ else if (score > 0.6) rating = "good";
1986
+ else if (score > 0.4) rating = "moderate";
1987
+ else rating = "poor";
1988
+ return {
1989
+ score: Math.round(score * 100) / 100,
1990
+ rating,
1991
+ analysis: {
1992
+ uniqueDomains: uniqueDomains.size,
1993
+ domainConcentration: Math.round(domainConcentration * 100) / 100,
1994
+ exportPurposeClarity: Math.round(exportPurposeClarity * 100) / 100
1995
+ }
1996
+ };
1997
+ }
1998
+ function calculateFutureProofScore(params) {
1999
+ const loadScore = 100 - params.cognitiveLoad.score;
2000
+ const entropyScore = 100 - params.patternEntropy.entropy * 100;
2001
+ const cohesionScore = params.conceptCohesion.score * 100;
2002
+ const overall = Math.round(
2003
+ loadScore * 0.4 + entropyScore * 0.3 + cohesionScore * 0.3
2004
+ );
2005
+ const factors = [
2006
+ {
2007
+ name: "Cognitive Load",
2008
+ impact: Math.round(loadScore - 50),
2009
+ description: params.cognitiveLoad.rating
2010
+ },
2011
+ {
2012
+ name: "Pattern Entropy",
2013
+ impact: Math.round(entropyScore - 50),
2014
+ description: params.patternEntropy.rating
2015
+ },
2016
+ {
2017
+ name: "Concept Cohesion",
2018
+ impact: Math.round(cohesionScore - 50),
2019
+ description: params.conceptCohesion.rating
2020
+ }
2021
+ ];
2022
+ const recommendations = [];
2023
+ for (const rec of params.patternEntropy.recommendations) {
2024
+ recommendations.push({
2025
+ action: rec,
2026
+ estimatedImpact: 5,
2027
+ priority: "medium"
2028
+ });
2029
+ }
2030
+ if (params.conceptCohesion.rating === "poor") {
2031
+ recommendations.push({
2032
+ action: "Improve concept cohesion by grouping related exports",
2033
+ estimatedImpact: 8,
2034
+ priority: "high"
2035
+ });
2036
+ }
2037
+ const semanticDistanceAvg = params.semanticDistances && params.semanticDistances.length > 0 ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
2038
+ return {
2039
+ toolName: "future-proof",
2040
+ score: overall,
2041
+ rawMetrics: {
2042
+ cognitiveLoadScore: params.cognitiveLoad.score,
2043
+ entropyScore: params.patternEntropy.entropy,
2044
+ cohesionScore: params.conceptCohesion.score,
2045
+ semanticDistanceAvg
2046
+ },
2047
+ factors,
2048
+ recommendations
2049
+ };
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
+ }
2379
+
2380
+ // src/utils/history.ts
2381
+ var import_fs4 = require("fs");
2382
+ var import_path4 = require("path");
2383
+ function getHistoryPath(rootDir) {
2384
+ return (0, import_path4.join)(rootDir, ".aiready", "history.json");
2385
+ }
2386
+ function loadScoreHistory(rootDir) {
2387
+ const historyPath = getHistoryPath(rootDir);
2388
+ if (!(0, import_fs4.existsSync)(historyPath)) {
2389
+ return [];
2390
+ }
2391
+ try {
2392
+ const data = (0, import_fs4.readFileSync)(historyPath, "utf-8");
2393
+ return JSON.parse(data);
2394
+ } catch (error) {
2395
+ console.warn("Failed to load score history:", error);
2396
+ return [];
2397
+ }
2398
+ }
2399
+ function saveScoreEntry(rootDir, entry) {
2400
+ const historyPath = getHistoryPath(rootDir);
2401
+ const historyDir = (0, import_path4.dirname)(historyPath);
2402
+ if (!(0, import_fs4.existsSync)(historyDir)) {
2403
+ (0, import_fs4.mkdirSync)(historyDir, { recursive: true });
2404
+ }
2405
+ const history = loadScoreHistory(rootDir);
2406
+ const newEntry = {
2407
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2408
+ ...entry
2409
+ };
2410
+ const oneYearAgo = Date.now() - 365 * 24 * 60 * 60 * 1e3;
2411
+ const filteredHistory = history.filter(
2412
+ (e) => new Date(e.timestamp).getTime() > oneYearAgo
2413
+ );
2414
+ filteredHistory.push(newEntry);
2415
+ (0, import_fs4.writeFileSync)(historyPath, JSON.stringify(filteredHistory, null, 2));
2416
+ }
2417
+ function getHistorySummary(rootDir) {
2418
+ const history = loadScoreHistory(rootDir);
2419
+ if (history.length === 0) {
2420
+ return {
2421
+ totalScans: 0,
2422
+ firstScan: null,
2423
+ lastScan: null,
2424
+ avgScore: 0
2425
+ };
2426
+ }
2427
+ const scores = history.map((e) => e.overallScore);
2428
+ const avgScore = scores.reduce((a, b) => a + b, 0) / scores.length;
2429
+ return {
2430
+ totalScans: history.length,
2431
+ firstScan: history[0].timestamp,
2432
+ lastScan: history[history.length - 1].timestamp,
2433
+ avgScore: Math.round(avgScore)
2434
+ };
2435
+ }
2436
+ function exportHistory(rootDir, format = "json") {
2437
+ const history = loadScoreHistory(rootDir);
2438
+ if (format === "csv") {
2439
+ const headers = "timestamp,overallScore,totalIssues,totalTokens,patternScore,contextScore,consistencyScore\n";
2440
+ const rows = history.map(
2441
+ (e) => `${e.timestamp},${e.overallScore},${e.totalIssues},${e.totalTokens},${e.breakdown?.["pattern-detect"] || ""},${e.breakdown?.["context-analyzer"] || ""},${e.breakdown?.["consistency"] || ""}`
2442
+ ).join("\n");
2443
+ return headers + rows;
2444
+ }
2445
+ return JSON.stringify(history, null, 2);
2446
+ }
2447
+ function clearHistory(rootDir) {
2448
+ const historyPath = getHistoryPath(rootDir);
2449
+ if ((0, import_fs4.existsSync)(historyPath)) {
2450
+ (0, import_fs4.writeFileSync)(historyPath, JSON.stringify([]));
2451
+ }
2452
+ }
1461
2453
  // Annotate the CommonJS export names for ESM import in node:
1462
2454
  0 && (module.exports = {
2455
+ CONTEXT_TIER_THRESHOLDS,
1463
2456
  DEFAULT_COST_CONFIG,
1464
2457
  DEFAULT_EXCLUDE,
1465
2458
  DEFAULT_TOOL_WEIGHTS,
1466
2459
  LANGUAGE_EXTENSIONS,
1467
2460
  Language,
2461
+ MODEL_PRICING_PRESETS,
1468
2462
  ParseError,
1469
2463
  ParserFactory,
1470
2464
  PythonParser,
2465
+ SIZE_ADJUSTED_THRESHOLDS,
1471
2466
  TOOL_NAME_MAP,
1472
2467
  TypeScriptParser,
2468
+ calculateAgentGrounding,
2469
+ calculateCognitiveLoad,
1473
2470
  calculateComprehensionDifficulty,
2471
+ calculateConceptCohesion,
2472
+ calculateExtendedFutureProofScore,
2473
+ calculateFutureProofScore,
2474
+ calculateHallucinationRisk,
1474
2475
  calculateImportSimilarity,
2476
+ calculateKnowledgeConcentration,
1475
2477
  calculateMonthlyCost,
1476
2478
  calculateOverallScore,
2479
+ calculatePatternEntropy,
1477
2480
  calculateProductivityImpact,
2481
+ calculateRemediationVelocity,
2482
+ calculateScoreTrend,
2483
+ calculateSemanticDistance,
2484
+ calculateTechnicalDebtInterest,
2485
+ calculateTestabilityIndex,
2486
+ clearHistory,
1478
2487
  estimateTokens,
2488
+ exportHistory,
1479
2489
  extractFunctions,
1480
2490
  extractImports,
1481
2491
  formatAcceptanceRate,
@@ -1484,11 +2494,17 @@ function getSupportedLanguages() {
1484
2494
  formatScore,
1485
2495
  formatToolScore,
1486
2496
  generateHTML,
2497
+ getDebtBreakdown,
1487
2498
  getElapsedTime,
1488
2499
  getFileExtension,
2500
+ getHistorySummary,
2501
+ getModelPreset,
1489
2502
  getParser,
2503
+ getProjectSizeTier,
1490
2504
  getRating,
1491
2505
  getRatingDisplay,
2506
+ getRatingWithContext,
2507
+ getRecommendedThreshold,
1492
2508
  getSupportedLanguages,
1493
2509
  getToolWeight,
1494
2510
  handleCLIError,
@@ -1497,6 +2513,7 @@ function getSupportedLanguages() {
1497
2513
  isSourceFile,
1498
2514
  loadConfig,
1499
2515
  loadMergedConfig,
2516
+ loadScoreHistory,
1500
2517
  mergeConfigWithDefaults,
1501
2518
  normalizeToolName,
1502
2519
  parseCode,
@@ -1505,5 +2522,6 @@ function getSupportedLanguages() {
1505
2522
  predictAcceptanceRate,
1506
2523
  readFileContent,
1507
2524
  resolveOutputPath,
2525
+ saveScoreEntry,
1508
2526
  scanFiles
1509
2527
  });