@aiready/core 0.9.25 → 0.9.26

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
@@ -40,12 +40,23 @@ __export(index_exports, {
40
40
  PythonParser: () => PythonParser,
41
41
  TOOL_NAME_MAP: () => TOOL_NAME_MAP,
42
42
  TypeScriptParser: () => TypeScriptParser,
43
+ calculateCognitiveLoad: () => calculateCognitiveLoad,
43
44
  calculateComprehensionDifficulty: () => calculateComprehensionDifficulty,
45
+ calculateConceptCohesion: () => calculateConceptCohesion,
46
+ calculateFutureProofScore: () => calculateFutureProofScore,
44
47
  calculateImportSimilarity: () => calculateImportSimilarity,
48
+ calculateKnowledgeConcentration: () => calculateKnowledgeConcentration,
45
49
  calculateMonthlyCost: () => calculateMonthlyCost,
46
50
  calculateOverallScore: () => calculateOverallScore,
51
+ calculatePatternEntropy: () => calculatePatternEntropy,
47
52
  calculateProductivityImpact: () => calculateProductivityImpact,
53
+ calculateRemediationVelocity: () => calculateRemediationVelocity,
54
+ calculateScoreTrend: () => calculateScoreTrend,
55
+ calculateSemanticDistance: () => calculateSemanticDistance,
56
+ calculateTechnicalDebtInterest: () => calculateTechnicalDebtInterest,
57
+ clearHistory: () => clearHistory,
48
58
  estimateTokens: () => estimateTokens,
59
+ exportHistory: () => exportHistory,
49
60
  extractFunctions: () => extractFunctions,
50
61
  extractImports: () => extractImports,
51
62
  formatAcceptanceRate: () => formatAcceptanceRate,
@@ -54,8 +65,10 @@ __export(index_exports, {
54
65
  formatScore: () => formatScore,
55
66
  formatToolScore: () => formatToolScore,
56
67
  generateHTML: () => generateHTML,
68
+ getDebtBreakdown: () => getDebtBreakdown,
57
69
  getElapsedTime: () => getElapsedTime,
58
70
  getFileExtension: () => getFileExtension,
71
+ getHistorySummary: () => getHistorySummary,
59
72
  getParser: () => getParser,
60
73
  getRating: () => getRating,
61
74
  getRatingDisplay: () => getRatingDisplay,
@@ -67,6 +80,7 @@ __export(index_exports, {
67
80
  isSourceFile: () => isSourceFile,
68
81
  loadConfig: () => loadConfig,
69
82
  loadMergedConfig: () => loadMergedConfig,
83
+ loadScoreHistory: () => loadScoreHistory,
70
84
  mergeConfigWithDefaults: () => mergeConfigWithDefaults,
71
85
  normalizeToolName: () => normalizeToolName,
72
86
  parseCode: () => parseCode,
@@ -75,6 +89,7 @@ __export(index_exports, {
75
89
  predictAcceptanceRate: () => predictAcceptanceRate,
76
90
  readFileContent: () => readFileContent,
77
91
  resolveOutputPath: () => resolveOutputPath,
92
+ saveScoreEntry: () => saveScoreEntry,
78
93
  scanFiles: () => scanFiles
79
94
  });
80
95
  module.exports = __toCommonJS(index_exports);
@@ -978,6 +993,200 @@ function formatHours(hours) {
978
993
  function formatAcceptanceRate(rate) {
979
994
  return `${Math.round(rate * 100)}%`;
980
995
  }
996
+ function calculateScoreTrend(history) {
997
+ if (history.length < 2) {
998
+ return {
999
+ direction: "stable",
1000
+ change30Days: 0,
1001
+ change90Days: 0,
1002
+ velocity: 0,
1003
+ projectedScore: history[0]?.overallScore || 100
1004
+ };
1005
+ }
1006
+ const now = /* @__PURE__ */ new Date();
1007
+ const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1e3);
1008
+ const ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1e3);
1009
+ const last30Days = history.filter((e) => new Date(e.timestamp) >= thirtyDaysAgo);
1010
+ const last90Days = history.filter((e) => new Date(e.timestamp) >= ninetyDaysAgo);
1011
+ const currentScore = history[history.length - 1].overallScore;
1012
+ const thirtyDaysAgoScore = last30Days[0]?.overallScore || currentScore;
1013
+ const ninetyDaysAgoScore = last90Days[0]?.overallScore || thirtyDaysAgoScore;
1014
+ const change30Days = currentScore - thirtyDaysAgoScore;
1015
+ const change90Days = currentScore - ninetyDaysAgoScore;
1016
+ const weeksOfData = Math.max(1, history.length / 7);
1017
+ const totalChange = currentScore - history[0].overallScore;
1018
+ const velocity = totalChange / weeksOfData;
1019
+ let direction;
1020
+ if (change30Days > 3) direction = "improving";
1021
+ else if (change30Days < -3) direction = "degrading";
1022
+ else direction = "stable";
1023
+ const projectedScore = Math.max(0, Math.min(100, currentScore + velocity * 4));
1024
+ return {
1025
+ direction,
1026
+ change30Days,
1027
+ change90Days,
1028
+ velocity: Math.round(velocity * 10) / 10,
1029
+ projectedScore: Math.round(projectedScore)
1030
+ };
1031
+ }
1032
+ function calculateRemediationVelocity(history, currentIssues) {
1033
+ if (history.length < 2) {
1034
+ return {
1035
+ issuesFixedThisWeek: 0,
1036
+ avgIssuesPerWeek: 0,
1037
+ trend: "stable",
1038
+ estimatedCompletionWeeks: currentIssues > 0 ? Infinity : 0
1039
+ };
1040
+ }
1041
+ const now = /* @__PURE__ */ new Date();
1042
+ const oneWeekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3);
1043
+ const twoWeeksAgo = new Date(now.getTime() - 14 * 24 * 60 * 60 * 1e3);
1044
+ const thisWeek = history.filter((e) => new Date(e.timestamp) >= oneWeekAgo);
1045
+ const lastWeek = history.filter(
1046
+ (e) => new Date(e.timestamp) >= twoWeeksAgo && new Date(e.timestamp) < oneWeekAgo
1047
+ );
1048
+ const issuesFixedThisWeek = thisWeek.length > 1 ? thisWeek[0].totalIssues - thisWeek[thisWeek.length - 1].totalIssues : 0;
1049
+ const totalIssuesFixed = history[0].totalIssues - history[history.length - 1].totalIssues;
1050
+ const weeksOfData = Math.max(1, history.length / 7);
1051
+ const avgIssuesPerWeek = totalIssuesFixed / weeksOfData;
1052
+ let trend;
1053
+ if (lastWeek.length > 1) {
1054
+ const lastWeekFixed = lastWeek[0].totalIssues - lastWeek[lastWeek.length - 1].totalIssues;
1055
+ if (issuesFixedThisWeek > lastWeekFixed * 1.2) trend = "accelerating";
1056
+ else if (issuesFixedThisWeek < lastWeekFixed * 0.8) trend = "decelerating";
1057
+ else trend = "stable";
1058
+ } else {
1059
+ trend = "stable";
1060
+ }
1061
+ const estimatedCompletionWeeks = avgIssuesPerWeek > 0 ? Math.ceil(currentIssues / avgIssuesPerWeek) : Infinity;
1062
+ return {
1063
+ issuesFixedThisWeek: Math.max(0, issuesFixedThisWeek),
1064
+ avgIssuesPerWeek: Math.round(avgIssuesPerWeek * 10) / 10,
1065
+ trend,
1066
+ estimatedCompletionWeeks
1067
+ };
1068
+ }
1069
+ function calculateKnowledgeConcentration(files, authorData) {
1070
+ if (files.length === 0) {
1071
+ return {
1072
+ score: 0,
1073
+ rating: "low",
1074
+ analysis: {
1075
+ uniqueConceptFiles: 0,
1076
+ totalFiles: 0,
1077
+ concentrationRatio: 0,
1078
+ singleAuthorFiles: 0,
1079
+ orphanFiles: 0
1080
+ },
1081
+ recommendations: ["No files to analyze"]
1082
+ };
1083
+ }
1084
+ const orphanFiles = files.filter((f) => f.exports < 2 && f.imports < 2).length;
1085
+ const avgExports = files.reduce((sum, f) => sum + f.exports, 0) / files.length;
1086
+ const uniqueConceptFiles = files.filter((f) => f.exports > avgExports * 2).length;
1087
+ const totalExports = files.reduce((sum, f) => sum + f.exports, 0);
1088
+ const concentrationRatio = totalExports > 0 ? uniqueConceptFiles / files.length : 0;
1089
+ let singleAuthorFiles = 0;
1090
+ if (authorData) {
1091
+ for (const files2 of authorData.values()) {
1092
+ if (files2.length === 1) singleAuthorFiles++;
1093
+ }
1094
+ }
1095
+ const orphanRisk = orphanFiles / files.length * 30;
1096
+ const uniqueRisk = concentrationRatio * 40;
1097
+ const singleAuthorRisk = authorData ? singleAuthorFiles / files.length * 30 : 0;
1098
+ const score = Math.min(100, Math.round(orphanRisk + uniqueRisk + singleAuthorRisk));
1099
+ let rating;
1100
+ if (score < 20) rating = "low";
1101
+ else if (score < 40) rating = "moderate";
1102
+ else if (score < 70) rating = "high";
1103
+ else rating = "critical";
1104
+ const recommendations = [];
1105
+ if (orphanFiles > files.length * 0.2) {
1106
+ recommendations.push(`Reduce ${orphanFiles} orphan files by connecting them to main modules`);
1107
+ }
1108
+ if (uniqueConceptFiles > files.length * 0.1) {
1109
+ recommendations.push("Distribute high-export files into more focused modules");
1110
+ }
1111
+ if (authorData && singleAuthorFiles > files.length * 0.3) {
1112
+ recommendations.push("Increase knowledge sharing to reduce single-author dependencies");
1113
+ }
1114
+ return {
1115
+ score,
1116
+ rating,
1117
+ analysis: {
1118
+ uniqueConceptFiles,
1119
+ totalFiles: files.length,
1120
+ concentrationRatio: Math.round(concentrationRatio * 100) / 100,
1121
+ singleAuthorFiles,
1122
+ orphanFiles
1123
+ },
1124
+ recommendations
1125
+ };
1126
+ }
1127
+ function calculateTechnicalDebtInterest(params) {
1128
+ const { currentMonthlyCost, issues, monthsOpen } = params;
1129
+ const criticalCount = issues.filter((i) => i.severity === "critical").length;
1130
+ const majorCount = issues.filter((i) => i.severity === "major").length;
1131
+ const minorCount = issues.filter((i) => i.severity === "minor").length;
1132
+ const severityWeight = (criticalCount * 3 + majorCount * 2 + minorCount * 1) / Math.max(1, issues.length);
1133
+ const baseRate = 0.02 + severityWeight * 0.01;
1134
+ const timeMultiplier = Math.max(1, 1 + monthsOpen * 0.1);
1135
+ const monthlyRate = baseRate * timeMultiplier;
1136
+ const projectDebt = (principal2, months) => {
1137
+ let debt = principal2;
1138
+ for (let i = 0; i < months; i++) {
1139
+ debt = debt * (1 + monthlyRate);
1140
+ }
1141
+ return Math.round(debt);
1142
+ };
1143
+ const principal = currentMonthlyCost * 12;
1144
+ const projections = {
1145
+ months6: projectDebt(principal, 6),
1146
+ months12: projectDebt(principal, 12),
1147
+ months24: projectDebt(principal, 24)
1148
+ };
1149
+ return {
1150
+ monthlyRate: Math.round(monthlyRate * 1e4) / 100,
1151
+ annualRate: Math.round((Math.pow(1 + monthlyRate, 12) - 1) * 1e4) / 100,
1152
+ principal,
1153
+ projections,
1154
+ monthlyCost: Math.round(currentMonthlyCost * (1 + monthlyRate) * 100) / 100
1155
+ };
1156
+ }
1157
+ function getDebtBreakdown(patternCost, contextCost, consistencyCost) {
1158
+ const breakdowns = [
1159
+ {
1160
+ category: "Semantic Duplication",
1161
+ currentCost: patternCost,
1162
+ monthlyGrowthRate: 5,
1163
+ // Grows as devs copy-paste
1164
+ priority: patternCost > 1e3 ? "high" : "medium",
1165
+ fixCost: patternCost * 3
1166
+ // Fixing costs 3x current waste
1167
+ },
1168
+ {
1169
+ category: "Context Fragmentation",
1170
+ currentCost: contextCost,
1171
+ monthlyGrowthRate: 3,
1172
+ // Grows with new features
1173
+ priority: contextCost > 500 ? "high" : "medium",
1174
+ fixCost: contextCost * 2.5
1175
+ },
1176
+ {
1177
+ category: "Consistency Issues",
1178
+ currentCost: consistencyCost,
1179
+ monthlyGrowthRate: 2,
1180
+ // Grows with new devs
1181
+ priority: consistencyCost > 200 ? "medium" : "low",
1182
+ fixCost: consistencyCost * 1.5
1183
+ }
1184
+ ];
1185
+ return breakdowns.sort((a, b) => {
1186
+ const priorityOrder = { high: 0, medium: 1, low: 2 };
1187
+ return priorityOrder[a.priority] - priorityOrder[b.priority];
1188
+ });
1189
+ }
981
1190
 
982
1191
  // src/parsers/typescript-parser.ts
983
1192
  var import_typescript_estree2 = require("@typescript-eslint/typescript-estree");
@@ -1458,6 +1667,308 @@ function isFileSupported(filePath) {
1458
1667
  function getSupportedLanguages() {
1459
1668
  return ParserFactory.getInstance().getSupportedLanguages();
1460
1669
  }
1670
+
1671
+ // src/future-proof-metrics.ts
1672
+ function calculateCognitiveLoad(params) {
1673
+ const { linesOfCode, exportCount, importCount, uniqueConcepts, cyclomaticComplexity = 1 } = params;
1674
+ const sizeFactor = {
1675
+ name: "Size Complexity",
1676
+ score: Math.min(100, Math.max(0, (linesOfCode - 50) / 10)),
1677
+ weight: 0.3,
1678
+ description: `${linesOfCode} lines of code`
1679
+ };
1680
+ const interfaceFactor = {
1681
+ name: "Interface Complexity",
1682
+ score: Math.min(100, exportCount * 5),
1683
+ weight: 0.25,
1684
+ description: `${exportCount} exported concepts`
1685
+ };
1686
+ const dependencyFactor = {
1687
+ name: "Dependency Complexity",
1688
+ score: Math.min(100, importCount * 8),
1689
+ weight: 0.25,
1690
+ description: `${importCount} dependencies`
1691
+ };
1692
+ const conceptDensity = linesOfCode > 0 ? uniqueConcepts / linesOfCode : 0;
1693
+ const conceptFactor = {
1694
+ name: "Conceptual Density",
1695
+ score: Math.min(100, conceptDensity * 500),
1696
+ weight: 0.2,
1697
+ description: `${uniqueConcepts} unique concepts`
1698
+ };
1699
+ const factors = [sizeFactor, interfaceFactor, dependencyFactor, conceptFactor];
1700
+ const score = factors.reduce((sum, f) => sum + f.score * f.weight, 0);
1701
+ let rating;
1702
+ if (score < 20) rating = "trivial";
1703
+ else if (score < 40) rating = "easy";
1704
+ else if (score < 60) rating = "moderate";
1705
+ else if (score < 80) rating = "difficult";
1706
+ else rating = "expert";
1707
+ return {
1708
+ score: Math.round(score),
1709
+ rating,
1710
+ factors,
1711
+ rawValues: {
1712
+ size: linesOfCode,
1713
+ complexity: cyclomaticComplexity,
1714
+ dependencyCount: importCount,
1715
+ conceptCount: uniqueConcepts
1716
+ }
1717
+ };
1718
+ }
1719
+ function calculateSemanticDistance(params) {
1720
+ const { file1, file2, file1Domain, file2Domain, sharedDependencies } = params;
1721
+ const domainDistance = file1Domain === file2Domain ? 0 : file1Domain && file2Domain ? 0.5 : 0.8;
1722
+ const importOverlap = sharedDependencies.length / Math.max(1, Math.min(params.file1Imports.length, params.file2Imports.length));
1723
+ const importDistance = 1 - importOverlap;
1724
+ const distance = domainDistance * 0.4 + importDistance * 0.3 + (sharedDependencies.length > 0 ? 0 : 0.3);
1725
+ let relationship;
1726
+ if (file1 === file2) relationship = "same-file";
1727
+ else if (file1Domain === file2Domain) relationship = "same-domain";
1728
+ else if (sharedDependencies.length > 0) relationship = "cross-domain";
1729
+ else relationship = "unrelated";
1730
+ const pathItems = [file1Domain, ...sharedDependencies, file2Domain].filter((s) => typeof s === "string" && s.length > 0);
1731
+ return {
1732
+ between: [file1, file2],
1733
+ distance: Math.round(distance * 100) / 100,
1734
+ relationship,
1735
+ path: pathItems,
1736
+ reason: relationship === "same-domain" ? `Both in "${file1Domain}" domain` : relationship === "cross-domain" ? `Share ${sharedDependencies.length} dependency(ies)` : "No strong semantic relationship detected"
1737
+ };
1738
+ }
1739
+ function calculatePatternEntropy(files) {
1740
+ if (files.length === 0) {
1741
+ return {
1742
+ domain: "unknown",
1743
+ entropy: 0,
1744
+ rating: "crystalline",
1745
+ distribution: { locationCount: 0, dominantLocation: "", giniCoefficient: 0 },
1746
+ recommendations: ["No files to analyze"]
1747
+ };
1748
+ }
1749
+ const dirGroups = /* @__PURE__ */ new Map();
1750
+ for (const file of files) {
1751
+ const parts = file.path.split("/").slice(0, 4).join("/") || "root";
1752
+ dirGroups.set(parts, (dirGroups.get(parts) || 0) + 1);
1753
+ }
1754
+ const counts = Array.from(dirGroups.values());
1755
+ const total = counts.reduce((a, b) => a + b, 0);
1756
+ let entropy = 0;
1757
+ for (const count of counts) {
1758
+ const p = count / total;
1759
+ if (p > 0) entropy -= p * Math.log2(p);
1760
+ }
1761
+ const maxEntropy = Math.log2(dirGroups.size || 1);
1762
+ const normalizedEntropy = maxEntropy > 0 ? entropy / maxEntropy : 0;
1763
+ const sortedCounts = counts.sort((a, b) => a - b);
1764
+ let gini = 0;
1765
+ for (let i = 0; i < sortedCounts.length; i++) {
1766
+ gini += (2 * (i + 1) - sortedCounts.length - 1) * sortedCounts[i];
1767
+ }
1768
+ gini /= total * sortedCounts.length;
1769
+ let dominantLocation = "";
1770
+ let maxCount = 0;
1771
+ for (const [loc, count] of dirGroups.entries()) {
1772
+ if (count > maxCount) {
1773
+ maxCount = count;
1774
+ dominantLocation = loc;
1775
+ }
1776
+ }
1777
+ let rating;
1778
+ if (normalizedEntropy < 0.2) rating = "crystalline";
1779
+ else if (normalizedEntropy < 0.4) rating = "well-structured";
1780
+ else if (normalizedEntropy < 0.6) rating = "moderate";
1781
+ else if (normalizedEntropy < 0.8) rating = "fragmented";
1782
+ else rating = "chaotic";
1783
+ const recommendations = [];
1784
+ if (normalizedEntropy > 0.5) {
1785
+ recommendations.push(`Consolidate ${files.length} files into fewer directories by domain`);
1786
+ }
1787
+ if (dirGroups.size > 5) {
1788
+ recommendations.push("Consider barrel exports to reduce directory navigation");
1789
+ }
1790
+ if (gini > 0.5) {
1791
+ recommendations.push("Redistribute files more evenly across directories");
1792
+ }
1793
+ const firstFile = files.length > 0 ? files[0] : null;
1794
+ const domainValue = firstFile ? firstFile.domain : "mixed";
1795
+ return {
1796
+ domain: domainValue,
1797
+ entropy: Math.round(normalizedEntropy * 100) / 100,
1798
+ rating,
1799
+ distribution: {
1800
+ locationCount: dirGroups.size,
1801
+ dominantLocation,
1802
+ giniCoefficient: Math.round(gini * 100) / 100
1803
+ },
1804
+ recommendations
1805
+ };
1806
+ }
1807
+ function calculateConceptCohesion(params) {
1808
+ const { exports: exports2 } = params;
1809
+ if (exports2.length === 0) {
1810
+ return {
1811
+ score: 1,
1812
+ rating: "excellent",
1813
+ analysis: { uniqueDomains: 0, domainConcentration: 0, exportPurposeClarity: 1 }
1814
+ };
1815
+ }
1816
+ const allDomains = [];
1817
+ for (const exp of exports2) {
1818
+ if (exp.inferredDomain) allDomains.push(exp.inferredDomain);
1819
+ if (exp.domains) allDomains.push(...exp.domains);
1820
+ }
1821
+ const uniqueDomains = new Set(allDomains);
1822
+ const domainCounts = /* @__PURE__ */ new Map();
1823
+ for (const d of allDomains) {
1824
+ domainCounts.set(d, (domainCounts.get(d) || 0) + 1);
1825
+ }
1826
+ const maxCount = Math.max(...Array.from(domainCounts.values()), 1);
1827
+ const domainConcentration = maxCount / allDomains.length;
1828
+ const exportPurposeClarity = 1 - (uniqueDomains.size - 1) / Math.max(1, exports2.length);
1829
+ const score = domainConcentration * 0.5 + exportPurposeClarity * 0.5;
1830
+ let rating;
1831
+ if (score > 0.8) rating = "excellent";
1832
+ else if (score > 0.6) rating = "good";
1833
+ else if (score > 0.4) rating = "moderate";
1834
+ else rating = "poor";
1835
+ return {
1836
+ score: Math.round(score * 100) / 100,
1837
+ rating,
1838
+ analysis: {
1839
+ uniqueDomains: uniqueDomains.size,
1840
+ domainConcentration: Math.round(domainConcentration * 100) / 100,
1841
+ exportPurposeClarity: Math.round(exportPurposeClarity * 100) / 100
1842
+ }
1843
+ };
1844
+ }
1845
+ function calculateFutureProofScore(params) {
1846
+ const loadScore = 100 - params.cognitiveLoad.score;
1847
+ const entropyScore = 100 - params.patternEntropy.entropy * 100;
1848
+ const cohesionScore = params.conceptCohesion.score * 100;
1849
+ const overall = Math.round(
1850
+ loadScore * 0.4 + entropyScore * 0.3 + cohesionScore * 0.3
1851
+ );
1852
+ const factors = [
1853
+ {
1854
+ name: "Cognitive Load",
1855
+ impact: Math.round(loadScore - 50),
1856
+ description: params.cognitiveLoad.rating
1857
+ },
1858
+ {
1859
+ name: "Pattern Entropy",
1860
+ impact: Math.round(entropyScore - 50),
1861
+ description: params.patternEntropy.rating
1862
+ },
1863
+ {
1864
+ name: "Concept Cohesion",
1865
+ impact: Math.round(cohesionScore - 50),
1866
+ description: params.conceptCohesion.rating
1867
+ }
1868
+ ];
1869
+ const recommendations = [];
1870
+ for (const rec of params.patternEntropy.recommendations) {
1871
+ recommendations.push({
1872
+ action: rec,
1873
+ estimatedImpact: 5,
1874
+ priority: "medium"
1875
+ });
1876
+ }
1877
+ if (params.conceptCohesion.rating === "poor") {
1878
+ recommendations.push({
1879
+ action: "Improve concept cohesion by grouping related exports",
1880
+ estimatedImpact: 8,
1881
+ priority: "high"
1882
+ });
1883
+ }
1884
+ const semanticDistanceAvg = params.semanticDistances && params.semanticDistances.length > 0 ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
1885
+ return {
1886
+ toolName: "future-proof",
1887
+ score: overall,
1888
+ rawMetrics: {
1889
+ cognitiveLoadScore: params.cognitiveLoad.score,
1890
+ entropyScore: params.patternEntropy.entropy,
1891
+ cohesionScore: params.conceptCohesion.score,
1892
+ semanticDistanceAvg
1893
+ },
1894
+ factors,
1895
+ recommendations
1896
+ };
1897
+ }
1898
+
1899
+ // src/utils/history.ts
1900
+ var import_fs4 = require("fs");
1901
+ var import_path4 = require("path");
1902
+ function getHistoryPath(rootDir) {
1903
+ return (0, import_path4.join)(rootDir, ".aiready", "history.json");
1904
+ }
1905
+ function loadScoreHistory(rootDir) {
1906
+ const historyPath = getHistoryPath(rootDir);
1907
+ if (!(0, import_fs4.existsSync)(historyPath)) {
1908
+ return [];
1909
+ }
1910
+ try {
1911
+ const data = (0, import_fs4.readFileSync)(historyPath, "utf-8");
1912
+ return JSON.parse(data);
1913
+ } catch (error) {
1914
+ console.warn("Failed to load score history:", error);
1915
+ return [];
1916
+ }
1917
+ }
1918
+ function saveScoreEntry(rootDir, entry) {
1919
+ const historyPath = getHistoryPath(rootDir);
1920
+ const historyDir = (0, import_path4.dirname)(historyPath);
1921
+ if (!(0, import_fs4.existsSync)(historyDir)) {
1922
+ (0, import_fs4.mkdirSync)(historyDir, { recursive: true });
1923
+ }
1924
+ const history = loadScoreHistory(rootDir);
1925
+ const newEntry = {
1926
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1927
+ ...entry
1928
+ };
1929
+ const oneYearAgo = Date.now() - 365 * 24 * 60 * 60 * 1e3;
1930
+ const filteredHistory = history.filter(
1931
+ (e) => new Date(e.timestamp).getTime() > oneYearAgo
1932
+ );
1933
+ filteredHistory.push(newEntry);
1934
+ (0, import_fs4.writeFileSync)(historyPath, JSON.stringify(filteredHistory, null, 2));
1935
+ }
1936
+ function getHistorySummary(rootDir) {
1937
+ const history = loadScoreHistory(rootDir);
1938
+ if (history.length === 0) {
1939
+ return {
1940
+ totalScans: 0,
1941
+ firstScan: null,
1942
+ lastScan: null,
1943
+ avgScore: 0
1944
+ };
1945
+ }
1946
+ const scores = history.map((e) => e.overallScore);
1947
+ const avgScore = scores.reduce((a, b) => a + b, 0) / scores.length;
1948
+ return {
1949
+ totalScans: history.length,
1950
+ firstScan: history[0].timestamp,
1951
+ lastScan: history[history.length - 1].timestamp,
1952
+ avgScore: Math.round(avgScore)
1953
+ };
1954
+ }
1955
+ function exportHistory(rootDir, format = "json") {
1956
+ const history = loadScoreHistory(rootDir);
1957
+ if (format === "csv") {
1958
+ const headers = "timestamp,overallScore,totalIssues,totalTokens,patternScore,contextScore,consistencyScore\n";
1959
+ const rows = history.map(
1960
+ (e) => `${e.timestamp},${e.overallScore},${e.totalIssues},${e.totalTokens},${e.breakdown?.["pattern-detect"] || ""},${e.breakdown?.["context-analyzer"] || ""},${e.breakdown?.["consistency"] || ""}`
1961
+ ).join("\n");
1962
+ return headers + rows;
1963
+ }
1964
+ return JSON.stringify(history, null, 2);
1965
+ }
1966
+ function clearHistory(rootDir) {
1967
+ const historyPath = getHistoryPath(rootDir);
1968
+ if ((0, import_fs4.existsSync)(historyPath)) {
1969
+ (0, import_fs4.writeFileSync)(historyPath, JSON.stringify([]));
1970
+ }
1971
+ }
1461
1972
  // Annotate the CommonJS export names for ESM import in node:
1462
1973
  0 && (module.exports = {
1463
1974
  DEFAULT_COST_CONFIG,
@@ -1470,12 +1981,23 @@ function getSupportedLanguages() {
1470
1981
  PythonParser,
1471
1982
  TOOL_NAME_MAP,
1472
1983
  TypeScriptParser,
1984
+ calculateCognitiveLoad,
1473
1985
  calculateComprehensionDifficulty,
1986
+ calculateConceptCohesion,
1987
+ calculateFutureProofScore,
1474
1988
  calculateImportSimilarity,
1989
+ calculateKnowledgeConcentration,
1475
1990
  calculateMonthlyCost,
1476
1991
  calculateOverallScore,
1992
+ calculatePatternEntropy,
1477
1993
  calculateProductivityImpact,
1994
+ calculateRemediationVelocity,
1995
+ calculateScoreTrend,
1996
+ calculateSemanticDistance,
1997
+ calculateTechnicalDebtInterest,
1998
+ clearHistory,
1478
1999
  estimateTokens,
2000
+ exportHistory,
1479
2001
  extractFunctions,
1480
2002
  extractImports,
1481
2003
  formatAcceptanceRate,
@@ -1484,8 +2006,10 @@ function getSupportedLanguages() {
1484
2006
  formatScore,
1485
2007
  formatToolScore,
1486
2008
  generateHTML,
2009
+ getDebtBreakdown,
1487
2010
  getElapsedTime,
1488
2011
  getFileExtension,
2012
+ getHistorySummary,
1489
2013
  getParser,
1490
2014
  getRating,
1491
2015
  getRatingDisplay,
@@ -1497,6 +2021,7 @@ function getSupportedLanguages() {
1497
2021
  isSourceFile,
1498
2022
  loadConfig,
1499
2023
  loadMergedConfig,
2024
+ loadScoreHistory,
1500
2025
  mergeConfigWithDefaults,
1501
2026
  normalizeToolName,
1502
2027
  parseCode,
@@ -1505,5 +2030,6 @@ function getSupportedLanguages() {
1505
2030
  predictAcceptanceRate,
1506
2031
  readFileContent,
1507
2032
  resolveOutputPath,
2033
+ saveScoreEntry,
1508
2034
  scanFiles
1509
2035
  });