@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.d.mts +263 -1
- package/dist/index.d.ts +263 -1
- package/dist/index.js +526 -0
- package/dist/index.mjs +511 -0
- package/package.json +3 -3
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
|
});
|