@aiready/core 0.9.37 → 0.9.39
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/chunk-UIWL5JQB.mjs +410 -0
- package/dist/chunk-UQGI67WR.mjs +410 -0
- package/dist/client.d.mts +14 -6
- package/dist/client.d.ts +14 -6
- package/dist/client.js +4 -4
- package/dist/client.mjs +1 -1
- package/dist/index.d.mts +167 -280
- package/dist/index.d.ts +167 -280
- package/dist/index.js +434 -556
- package/dist/index.mjs +415 -540
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -40,16 +40,19 @@ __export(index_exports, {
|
|
|
40
40
|
ParseError: () => ParseError,
|
|
41
41
|
ParserFactory: () => ParserFactory,
|
|
42
42
|
PythonParser: () => PythonParser,
|
|
43
|
+
SEVERITY_TIME_ESTIMATES: () => SEVERITY_TIME_ESTIMATES,
|
|
43
44
|
SIZE_ADJUSTED_THRESHOLDS: () => SIZE_ADJUSTED_THRESHOLDS,
|
|
44
45
|
TOOL_NAME_MAP: () => TOOL_NAME_MAP,
|
|
45
46
|
TypeScriptParser: () => TypeScriptParser,
|
|
46
47
|
VAGUE_FILE_NAMES: () => VAGUE_FILE_NAMES,
|
|
47
48
|
calculateAgentGrounding: () => calculateAgentGrounding,
|
|
48
49
|
calculateAiSignalClarity: () => calculateAiSignalClarity,
|
|
50
|
+
calculateBusinessROI: () => calculateBusinessROI,
|
|
49
51
|
calculateChangeAmplification: () => calculateChangeAmplification,
|
|
50
52
|
calculateCognitiveLoad: () => calculateCognitiveLoad,
|
|
51
53
|
calculateComprehensionDifficulty: () => calculateComprehensionDifficulty,
|
|
52
54
|
calculateConceptCohesion: () => calculateConceptCohesion,
|
|
55
|
+
calculateDebtInterest: () => calculateDebtInterest,
|
|
53
56
|
calculateDependencyHealth: () => calculateDependencyHealth,
|
|
54
57
|
calculateDocDrift: () => calculateDocDrift,
|
|
55
58
|
calculateExtendedFutureProofScore: () => calculateExtendedFutureProofScore,
|
|
@@ -60,10 +63,8 @@ __export(index_exports, {
|
|
|
60
63
|
calculateOverallScore: () => calculateOverallScore,
|
|
61
64
|
calculatePatternEntropy: () => calculatePatternEntropy,
|
|
62
65
|
calculateProductivityImpact: () => calculateProductivityImpact,
|
|
63
|
-
calculateRemediationVelocity: () => calculateRemediationVelocity,
|
|
64
|
-
calculateScoreTrend: () => calculateScoreTrend,
|
|
65
66
|
calculateSemanticDistance: () => calculateSemanticDistance,
|
|
66
|
-
|
|
67
|
+
calculateTechnicalValueChain: () => calculateTechnicalValueChain,
|
|
67
68
|
calculateTestabilityIndex: () => calculateTestabilityIndex,
|
|
68
69
|
calculateTokenBudget: () => calculateTokenBudget,
|
|
69
70
|
clearHistory: () => clearHistory,
|
|
@@ -79,7 +80,6 @@ __export(index_exports, {
|
|
|
79
80
|
formatToolScore: () => formatToolScore,
|
|
80
81
|
generateHTML: () => generateHTML,
|
|
81
82
|
generateValueChain: () => generateValueChain,
|
|
82
|
-
getDebtBreakdown: () => getDebtBreakdown,
|
|
83
83
|
getElapsedTime: () => getElapsedTime,
|
|
84
84
|
getFileCommitTimestamps: () => getFileCommitTimestamps,
|
|
85
85
|
getFileExtension: () => getFileExtension,
|
|
@@ -93,6 +93,9 @@ __export(index_exports, {
|
|
|
93
93
|
getRatingWithContext: () => getRatingWithContext,
|
|
94
94
|
getRecommendedThreshold: () => getRecommendedThreshold,
|
|
95
95
|
getRepoMetadata: () => getRepoMetadata,
|
|
96
|
+
getSafetyIcon: () => getSafetyIcon,
|
|
97
|
+
getScoreBar: () => getScoreBar,
|
|
98
|
+
getSeverityColor: () => getSeverityColor,
|
|
96
99
|
getSupportedLanguages: () => getSupportedLanguages,
|
|
97
100
|
getToolWeight: () => getToolWeight,
|
|
98
101
|
handleCLIError: () => handleCLIError,
|
|
@@ -117,15 +120,15 @@ __export(index_exports, {
|
|
|
117
120
|
module.exports = __toCommonJS(index_exports);
|
|
118
121
|
|
|
119
122
|
// src/types/language.ts
|
|
120
|
-
var Language = /* @__PURE__ */ ((
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
return
|
|
123
|
+
var Language = /* @__PURE__ */ ((Language2) => {
|
|
124
|
+
Language2["TypeScript"] = "typescript";
|
|
125
|
+
Language2["JavaScript"] = "javascript";
|
|
126
|
+
Language2["Python"] = "python";
|
|
127
|
+
Language2["Java"] = "java";
|
|
128
|
+
Language2["Go"] = "go";
|
|
129
|
+
Language2["Rust"] = "rust";
|
|
130
|
+
Language2["CSharp"] = "csharp";
|
|
131
|
+
return Language2;
|
|
129
132
|
})(Language || {});
|
|
130
133
|
var LANGUAGE_EXTENSIONS = {
|
|
131
134
|
".ts": "typescript" /* TypeScript */,
|
|
@@ -640,6 +643,41 @@ function handleCLIError(error, commandName) {
|
|
|
640
643
|
function getElapsedTime(startTime) {
|
|
641
644
|
return ((Date.now() - startTime) / 1e3).toFixed(2);
|
|
642
645
|
}
|
|
646
|
+
function getScoreBar(val) {
|
|
647
|
+
return "\u2588".repeat(Math.round(val / 10)).padEnd(10, "\u2591");
|
|
648
|
+
}
|
|
649
|
+
function getSafetyIcon(rating) {
|
|
650
|
+
switch (rating) {
|
|
651
|
+
case "safe":
|
|
652
|
+
return "\u2705";
|
|
653
|
+
case "moderate-risk":
|
|
654
|
+
return "\u26A0\uFE0F ";
|
|
655
|
+
case "high-risk":
|
|
656
|
+
return "\u{1F534}";
|
|
657
|
+
case "blind-risk":
|
|
658
|
+
return "\u{1F480}";
|
|
659
|
+
default:
|
|
660
|
+
return "\u2753";
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
function getSeverityColor(severity, chalk) {
|
|
664
|
+
switch (severity.toLowerCase()) {
|
|
665
|
+
case "critical":
|
|
666
|
+
case "high-risk":
|
|
667
|
+
case "blind-risk":
|
|
668
|
+
return chalk.red;
|
|
669
|
+
case "major":
|
|
670
|
+
case "moderate-risk":
|
|
671
|
+
return chalk.yellow;
|
|
672
|
+
case "minor":
|
|
673
|
+
case "safe":
|
|
674
|
+
return chalk.green;
|
|
675
|
+
case "info":
|
|
676
|
+
return chalk.blue;
|
|
677
|
+
default:
|
|
678
|
+
return chalk.white;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
643
681
|
|
|
644
682
|
// src/utils/visualization.ts
|
|
645
683
|
function generateHTML(graph) {
|
|
@@ -901,7 +939,7 @@ function calculateOverallScore(toolOutputs, config, cliWeights) {
|
|
|
901
939
|
for (const [toolName] of toolOutputs.entries()) {
|
|
902
940
|
const cliWeight = cliWeights?.get(toolName);
|
|
903
941
|
const configWeight = config?.tools?.[toolName]?.scoreWeight;
|
|
904
|
-
const weight = cliWeight ?? configWeight ?? DEFAULT_TOOL_WEIGHTS[toolName] ??
|
|
942
|
+
const weight = cliWeight ?? configWeight ?? DEFAULT_TOOL_WEIGHTS[toolName] ?? 5;
|
|
905
943
|
weights.set(toolName, weight);
|
|
906
944
|
}
|
|
907
945
|
let weightedSum = 0;
|
|
@@ -910,7 +948,7 @@ function calculateOverallScore(toolOutputs, config, cliWeights) {
|
|
|
910
948
|
const toolsUsed = [];
|
|
911
949
|
const calculationWeights = {};
|
|
912
950
|
for (const [toolName, output] of toolOutputs.entries()) {
|
|
913
|
-
const weight = weights.get(toolName) ||
|
|
951
|
+
const weight = weights.get(toolName) || 5;
|
|
914
952
|
const weightedScore = output.score * weight;
|
|
915
953
|
weightedSum += weightedScore;
|
|
916
954
|
totalWeight += weight;
|
|
@@ -922,7 +960,7 @@ function calculateOverallScore(toolOutputs, config, cliWeights) {
|
|
|
922
960
|
const rating = getRating(overall);
|
|
923
961
|
const formulaParts = Array.from(toolOutputs.entries()).map(
|
|
924
962
|
([name, output]) => {
|
|
925
|
-
const w = weights.get(name) ||
|
|
963
|
+
const w = weights.get(name) || 5;
|
|
926
964
|
return `(${output.score} \xD7 ${w})`;
|
|
927
965
|
}
|
|
928
966
|
);
|
|
@@ -967,7 +1005,7 @@ function getRatingDisplay(rating) {
|
|
|
967
1005
|
}
|
|
968
1006
|
}
|
|
969
1007
|
function formatScore(result) {
|
|
970
|
-
const { emoji
|
|
1008
|
+
const { emoji } = getRatingDisplay(result.rating);
|
|
971
1009
|
return `${result.overall}/100 (${result.rating}) ${emoji}`;
|
|
972
1010
|
}
|
|
973
1011
|
function formatToolScore(output) {
|
|
@@ -999,7 +1037,7 @@ function formatToolScore(output) {
|
|
|
999
1037
|
return result;
|
|
1000
1038
|
}
|
|
1001
1039
|
|
|
1002
|
-
// src/business-
|
|
1040
|
+
// src/business/pricing-models.ts
|
|
1003
1041
|
var MODEL_PRICING_PRESETS = {
|
|
1004
1042
|
"gpt-5.3": {
|
|
1005
1043
|
name: "GPT-5.3",
|
|
@@ -1061,30 +1099,17 @@ var MODEL_PRICING_PRESETS = {
|
|
|
1061
1099
|
function getModelPreset(modelId) {
|
|
1062
1100
|
return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["claude-4.6"];
|
|
1063
1101
|
}
|
|
1102
|
+
|
|
1103
|
+
// src/business/cost-metrics.ts
|
|
1064
1104
|
var DEFAULT_COST_CONFIG = {
|
|
1065
1105
|
pricePer1KTokens: 5e-3,
|
|
1066
|
-
// GPT-4o input price (updated from GPT-4 era 0.01)
|
|
1067
1106
|
queriesPerDevPerDay: 60,
|
|
1068
|
-
// Average AI queries per developer (updated: 40→60 as of 2026)
|
|
1069
1107
|
developerCount: 5,
|
|
1070
|
-
// Default team size
|
|
1071
1108
|
daysPerMonth: 30
|
|
1072
1109
|
};
|
|
1073
|
-
var SEVERITY_TIME_ESTIMATES = {
|
|
1074
|
-
critical: 4,
|
|
1075
|
-
// Complex architectural issues (industry avg: 6.8h)
|
|
1076
|
-
major: 2,
|
|
1077
|
-
// Significant refactoring needed (avg: 3.4h)
|
|
1078
|
-
minor: 0.5,
|
|
1079
|
-
// Simple naming/style fixes (avg: 0.8h)
|
|
1080
|
-
info: 0.25
|
|
1081
|
-
// Documentation improvements
|
|
1082
|
-
};
|
|
1083
|
-
var DEFAULT_HOURLY_RATE = 75;
|
|
1084
1110
|
function calculateMonthlyCost(tokenWaste, config = {}) {
|
|
1085
1111
|
const budget = calculateTokenBudget({
|
|
1086
1112
|
totalContextTokens: tokenWaste * 2.5,
|
|
1087
|
-
// Heuristic: context is larger than waste
|
|
1088
1113
|
wastedTokens: {
|
|
1089
1114
|
duplication: tokenWaste * 0.7,
|
|
1090
1115
|
fragmentation: tokenWaste * 0.3,
|
|
@@ -1100,7 +1125,10 @@ function calculateTokenBudget(params) {
|
|
|
1100
1125
|
const totalWaste = wastedTokens.duplication + wastedTokens.fragmentation + wastedTokens.chattiness;
|
|
1101
1126
|
const efficiencyRatio = Math.max(
|
|
1102
1127
|
0,
|
|
1103
|
-
Math.min(
|
|
1128
|
+
Math.min(
|
|
1129
|
+
1,
|
|
1130
|
+
(totalContextTokens - totalWaste) / Math.max(1, totalContextTokens)
|
|
1131
|
+
)
|
|
1104
1132
|
);
|
|
1105
1133
|
return {
|
|
1106
1134
|
totalContextTokens: Math.round(totalContextTokens),
|
|
@@ -1115,7 +1143,6 @@ function calculateTokenBudget(params) {
|
|
|
1115
1143
|
},
|
|
1116
1144
|
efficiencyRatio: Math.round(efficiencyRatio * 100) / 100,
|
|
1117
1145
|
potentialRetrievableTokens: Math.round(totalWaste * 0.8)
|
|
1118
|
-
// Heuristic: 80% is fixable
|
|
1119
1146
|
};
|
|
1120
1147
|
}
|
|
1121
1148
|
function estimateCostFromBudget(budget, model, config = {}) {
|
|
@@ -1124,7 +1151,8 @@ function estimateCostFromBudget(budget, model, config = {}) {
|
|
|
1124
1151
|
const tokensPerDay = wastePerQuery * cfg.queriesPerDevPerDay;
|
|
1125
1152
|
const tokensPerMonth = tokensPerDay * cfg.daysPerMonth;
|
|
1126
1153
|
const totalWeight = cfg.developerCount;
|
|
1127
|
-
const
|
|
1154
|
+
const price = config.pricePer1KTokens ?? model.pricePer1KInputTokens;
|
|
1155
|
+
const baseCost = tokensPerMonth / 1e3 * price * totalWeight;
|
|
1128
1156
|
let confidence = 0.85;
|
|
1129
1157
|
if (model.contextTier === "frontier") confidence = 0.7;
|
|
1130
1158
|
const variance = 0.25;
|
|
@@ -1138,6 +1166,15 @@ function estimateCostFromBudget(budget, model, config = {}) {
|
|
|
1138
1166
|
confidence
|
|
1139
1167
|
};
|
|
1140
1168
|
}
|
|
1169
|
+
|
|
1170
|
+
// src/business/productivity-metrics.ts
|
|
1171
|
+
var SEVERITY_TIME_ESTIMATES = {
|
|
1172
|
+
critical: 4,
|
|
1173
|
+
major: 2,
|
|
1174
|
+
minor: 0.5,
|
|
1175
|
+
info: 0.25
|
|
1176
|
+
};
|
|
1177
|
+
var DEFAULT_HOURLY_RATE = 75;
|
|
1141
1178
|
function calculateProductivityImpact(issues, hourlyRate = DEFAULT_HOURLY_RATE) {
|
|
1142
1179
|
const counts = {
|
|
1143
1180
|
critical: issues.filter((i) => i.severity === "critical").length,
|
|
@@ -1178,108 +1215,158 @@ function predictAcceptanceRate(toolOutputs) {
|
|
|
1178
1215
|
const baseRate = 0.3;
|
|
1179
1216
|
const patterns = toolOutputs.get("pattern-detect");
|
|
1180
1217
|
if (patterns) {
|
|
1181
|
-
const patternImpact = (patterns.score - 50) * 3e-3;
|
|
1182
1218
|
factors.push({
|
|
1183
1219
|
name: "Semantic Duplication",
|
|
1184
|
-
impact: Math.round(
|
|
1220
|
+
impact: Math.round((patterns.score - 50) * 3e-3 * 100)
|
|
1185
1221
|
});
|
|
1186
1222
|
}
|
|
1187
1223
|
const context = toolOutputs.get("context-analyzer");
|
|
1188
1224
|
if (context) {
|
|
1189
|
-
const contextImpact = (context.score - 50) * 4e-3;
|
|
1190
1225
|
factors.push({
|
|
1191
1226
|
name: "Context Efficiency",
|
|
1192
|
-
impact: Math.round(
|
|
1227
|
+
impact: Math.round((context.score - 50) * 4e-3 * 100)
|
|
1193
1228
|
});
|
|
1194
1229
|
}
|
|
1195
1230
|
const consistency = toolOutputs.get("consistency");
|
|
1196
1231
|
if (consistency) {
|
|
1197
|
-
const consistencyImpact = (consistency.score - 50) * 2e-3;
|
|
1198
1232
|
factors.push({
|
|
1199
1233
|
name: "Code Consistency",
|
|
1200
|
-
impact: Math.round(
|
|
1234
|
+
impact: Math.round((consistency.score - 50) * 2e-3 * 100)
|
|
1201
1235
|
});
|
|
1202
1236
|
}
|
|
1203
1237
|
const aiSignalClarity = toolOutputs.get("ai-signal-clarity");
|
|
1204
1238
|
if (aiSignalClarity) {
|
|
1205
|
-
const hrImpact = (50 - aiSignalClarity.score) * 2e-3;
|
|
1206
1239
|
factors.push({
|
|
1207
1240
|
name: "AI Signal Clarity",
|
|
1208
|
-
impact: Math.round(
|
|
1241
|
+
impact: Math.round((50 - aiSignalClarity.score) * 2e-3 * 100)
|
|
1209
1242
|
});
|
|
1210
1243
|
}
|
|
1211
1244
|
const totalImpact = factors.reduce((sum, f) => sum + f.impact / 100, 0);
|
|
1212
1245
|
const rate = Math.max(0.05, Math.min(0.8, baseRate + totalImpact));
|
|
1213
|
-
let confidence;
|
|
1246
|
+
let confidence = 0.35;
|
|
1214
1247
|
if (toolOutputs.size >= 4) confidence = 0.75;
|
|
1215
1248
|
else if (toolOutputs.size >= 3) confidence = 0.65;
|
|
1216
1249
|
else if (toolOutputs.size >= 2) confidence = 0.5;
|
|
1217
|
-
|
|
1218
|
-
return {
|
|
1219
|
-
rate: Math.round(rate * 100) / 100,
|
|
1220
|
-
confidence,
|
|
1221
|
-
factors
|
|
1222
|
-
};
|
|
1250
|
+
return { rate: Math.round(rate * 100) / 100, confidence, factors };
|
|
1223
1251
|
}
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
const
|
|
1228
|
-
const
|
|
1229
|
-
const budgetRange = criticalBudget - idealBudget;
|
|
1230
|
-
const budgetFactor = Math.min(
|
|
1231
|
-
100,
|
|
1232
|
-
Math.max(0, (contextBudget - idealBudget) / budgetRange * 100)
|
|
1233
|
-
);
|
|
1234
|
-
const depthFactor = Math.min(
|
|
1235
|
-
100,
|
|
1236
|
-
Math.max(0, (importDepth - idealDepth) * 10)
|
|
1237
|
-
);
|
|
1238
|
-
const fragmentationFactor = Math.min(
|
|
1239
|
-
100,
|
|
1240
|
-
Math.max(0, (fragmentation - 0.3) * 250)
|
|
1241
|
-
);
|
|
1242
|
-
const consistencyFactor = Math.min(100, Math.max(0, 100 - consistencyScore));
|
|
1243
|
-
const fileFactor = Math.min(100, Math.max(0, (totalFiles - 50) / 5));
|
|
1252
|
+
|
|
1253
|
+
// src/business/risk-metrics.ts
|
|
1254
|
+
function calculateKnowledgeConcentration(params) {
|
|
1255
|
+
const { uniqueConceptFiles, totalFiles, singleAuthorFiles, orphanFiles } = params;
|
|
1256
|
+
const concentrationRatio = totalFiles > 0 ? (uniqueConceptFiles + singleAuthorFiles) / (totalFiles * 2) : 0;
|
|
1244
1257
|
const score = Math.round(
|
|
1245
|
-
|
|
1258
|
+
Math.min(
|
|
1259
|
+
100,
|
|
1260
|
+
concentrationRatio * 100 + orphanFiles / Math.max(1, totalFiles) * 20
|
|
1261
|
+
)
|
|
1246
1262
|
);
|
|
1247
1263
|
let rating;
|
|
1248
|
-
if (score <
|
|
1249
|
-
else if (score <
|
|
1250
|
-
else if (score <
|
|
1251
|
-
else
|
|
1252
|
-
|
|
1264
|
+
if (score < 30) rating = "low";
|
|
1265
|
+
else if (score < 50) rating = "moderate";
|
|
1266
|
+
else if (score < 75) rating = "high";
|
|
1267
|
+
else rating = "critical";
|
|
1268
|
+
const recommendations = [];
|
|
1269
|
+
if (singleAuthorFiles > 0)
|
|
1270
|
+
recommendations.push(
|
|
1271
|
+
`Distribute knowledge for ${singleAuthorFiles} single-author files.`
|
|
1272
|
+
);
|
|
1273
|
+
if (orphanFiles > 0)
|
|
1274
|
+
recommendations.push(
|
|
1275
|
+
`Link ${orphanFiles} orphan files to the rest of the codebase.`
|
|
1276
|
+
);
|
|
1253
1277
|
return {
|
|
1254
1278
|
score,
|
|
1255
1279
|
rating,
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1280
|
+
recommendations,
|
|
1281
|
+
analysis: {
|
|
1282
|
+
uniqueConceptFiles,
|
|
1283
|
+
totalFiles,
|
|
1284
|
+
concentrationRatio,
|
|
1285
|
+
singleAuthorFiles,
|
|
1286
|
+
orphanFiles
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
function calculateDebtInterest(principal, monthlyGrowthRate) {
|
|
1291
|
+
const monthlyRate = monthlyGrowthRate;
|
|
1292
|
+
const annualRate = Math.pow(1 + monthlyRate, 12) - 1;
|
|
1293
|
+
const monthlyCost = principal * monthlyRate;
|
|
1294
|
+
return {
|
|
1295
|
+
monthlyRate,
|
|
1296
|
+
annualRate,
|
|
1297
|
+
principal,
|
|
1298
|
+
monthlyCost,
|
|
1299
|
+
projections: {
|
|
1300
|
+
months6: principal * Math.pow(1 + monthlyRate, 6),
|
|
1301
|
+
months12: principal * Math.pow(1 + monthlyRate, 12),
|
|
1302
|
+
months24: principal * Math.pow(1 + monthlyRate, 24)
|
|
1303
|
+
}
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
// src/business/comprehension-metrics.ts
|
|
1308
|
+
function calculateTechnicalValueChain(params) {
|
|
1309
|
+
const { businessLogicDensity, dataAccessComplexity, apiSurfaceArea } = params;
|
|
1310
|
+
const score = (businessLogicDensity * 0.5 + (1 - dataAccessComplexity / 10) * 0.3 + (1 - apiSurfaceArea / 20) * 0.2) * 100;
|
|
1311
|
+
return {
|
|
1312
|
+
score: Math.round(Math.max(0, Math.min(100, score))),
|
|
1313
|
+
density: businessLogicDensity,
|
|
1314
|
+
complexity: dataAccessComplexity,
|
|
1315
|
+
surface: apiSurfaceArea
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentation, modelTier = "frontier") {
|
|
1319
|
+
const tierMap = {
|
|
1320
|
+
compact: "compact",
|
|
1321
|
+
standard: "standard",
|
|
1322
|
+
extended: "extended",
|
|
1323
|
+
frontier: "frontier",
|
|
1324
|
+
easy: "frontier",
|
|
1325
|
+
// Map legacy 'easy' to 'frontier'
|
|
1326
|
+
moderate: "standard",
|
|
1327
|
+
difficult: "compact"
|
|
1328
|
+
};
|
|
1329
|
+
const tier = tierMap[modelTier] || "frontier";
|
|
1330
|
+
const threshold = CONTEXT_TIER_THRESHOLDS[tier];
|
|
1331
|
+
const budgetRatio = contextBudget / threshold.idealTokens;
|
|
1332
|
+
const score = (budgetRatio * 0.6 + importDepth / 10 * 0.2 + fragmentation * 0.2) * 100;
|
|
1333
|
+
const finalScore = Math.round(Math.max(0, Math.min(100, score)));
|
|
1334
|
+
let rating;
|
|
1335
|
+
if (finalScore < 20) rating = "trivial";
|
|
1336
|
+
else if (finalScore < 40) rating = "easy";
|
|
1337
|
+
else if (finalScore < 60) rating = "moderate";
|
|
1338
|
+
else if (finalScore < 85) rating = "difficult";
|
|
1339
|
+
else rating = "expert";
|
|
1340
|
+
return {
|
|
1341
|
+
score: finalScore,
|
|
1342
|
+
rating,
|
|
1343
|
+
factors: { budgetRatio, depthRatio: importDepth / 10, fragmentation }
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
// src/business-metrics.ts
|
|
1348
|
+
function calculateBusinessROI(params) {
|
|
1349
|
+
const model = getModelPreset(params.modelId || "claude-4.6");
|
|
1350
|
+
const devCount = params.developerCount || 5;
|
|
1351
|
+
const budget = calculateTokenBudget({
|
|
1352
|
+
totalContextTokens: params.tokenWaste * 2.5,
|
|
1353
|
+
wastedTokens: {
|
|
1354
|
+
duplication: params.tokenWaste * 0.7,
|
|
1355
|
+
fragmentation: params.tokenWaste * 0.3,
|
|
1356
|
+
chattiness: 0
|
|
1357
|
+
}
|
|
1358
|
+
});
|
|
1359
|
+
const cost = estimateCostFromBudget(budget, model, {
|
|
1360
|
+
developerCount: devCount
|
|
1361
|
+
});
|
|
1362
|
+
const productivity = calculateProductivityImpact(params.issues);
|
|
1363
|
+
const monthlySavings = cost.total;
|
|
1364
|
+
const productivityGainHours = productivity.totalHours;
|
|
1365
|
+
const annualValue = (monthlySavings + productivityGainHours * 75) * 12;
|
|
1366
|
+
return {
|
|
1367
|
+
monthlySavings: Math.round(monthlySavings),
|
|
1368
|
+
productivityGainHours: Math.round(productivityGainHours),
|
|
1369
|
+
annualValue: Math.round(annualValue)
|
|
1283
1370
|
};
|
|
1284
1371
|
}
|
|
1285
1372
|
function formatCost(cost) {
|
|
@@ -1305,220 +1392,6 @@ function formatHours(hours) {
|
|
|
1305
1392
|
function formatAcceptanceRate(rate) {
|
|
1306
1393
|
return `${Math.round(rate * 100)}%`;
|
|
1307
1394
|
}
|
|
1308
|
-
function calculateScoreTrend(history) {
|
|
1309
|
-
if (history.length < 2) {
|
|
1310
|
-
return {
|
|
1311
|
-
direction: "stable",
|
|
1312
|
-
change30Days: 0,
|
|
1313
|
-
change90Days: 0,
|
|
1314
|
-
velocity: 0,
|
|
1315
|
-
projectedScore: history[0]?.overallScore || 100
|
|
1316
|
-
};
|
|
1317
|
-
}
|
|
1318
|
-
const now = /* @__PURE__ */ new Date();
|
|
1319
|
-
const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1e3);
|
|
1320
|
-
const ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1e3);
|
|
1321
|
-
const last30Days = history.filter(
|
|
1322
|
-
(e) => new Date(e.timestamp) >= thirtyDaysAgo
|
|
1323
|
-
);
|
|
1324
|
-
const last90Days = history.filter(
|
|
1325
|
-
(e) => new Date(e.timestamp) >= ninetyDaysAgo
|
|
1326
|
-
);
|
|
1327
|
-
const currentScore = history[history.length - 1].overallScore;
|
|
1328
|
-
const thirtyDaysAgoScore = last30Days[0]?.overallScore || currentScore;
|
|
1329
|
-
const ninetyDaysAgoScore = last90Days[0]?.overallScore || thirtyDaysAgoScore;
|
|
1330
|
-
const change30Days = currentScore - thirtyDaysAgoScore;
|
|
1331
|
-
const change90Days = currentScore - ninetyDaysAgoScore;
|
|
1332
|
-
const weeksOfData = Math.max(1, history.length / 7);
|
|
1333
|
-
const totalChange = currentScore - history[0].overallScore;
|
|
1334
|
-
const velocity = totalChange / weeksOfData;
|
|
1335
|
-
let direction;
|
|
1336
|
-
if (change30Days > 3) direction = "improving";
|
|
1337
|
-
else if (change30Days < -3) direction = "degrading";
|
|
1338
|
-
else direction = "stable";
|
|
1339
|
-
const projectedScore = Math.max(
|
|
1340
|
-
0,
|
|
1341
|
-
Math.min(100, currentScore + velocity * 4)
|
|
1342
|
-
);
|
|
1343
|
-
return {
|
|
1344
|
-
direction,
|
|
1345
|
-
change30Days,
|
|
1346
|
-
change90Days,
|
|
1347
|
-
velocity: Math.round(velocity * 10) / 10,
|
|
1348
|
-
projectedScore: Math.round(projectedScore)
|
|
1349
|
-
};
|
|
1350
|
-
}
|
|
1351
|
-
function calculateRemediationVelocity(history, currentIssues) {
|
|
1352
|
-
if (history.length < 2) {
|
|
1353
|
-
return {
|
|
1354
|
-
issuesFixedThisWeek: 0,
|
|
1355
|
-
avgIssuesPerWeek: 0,
|
|
1356
|
-
trend: "stable",
|
|
1357
|
-
estimatedCompletionWeeks: currentIssues > 0 ? Infinity : 0
|
|
1358
|
-
};
|
|
1359
|
-
}
|
|
1360
|
-
const now = /* @__PURE__ */ new Date();
|
|
1361
|
-
const oneWeekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3);
|
|
1362
|
-
const twoWeeksAgo = new Date(now.getTime() - 14 * 24 * 60 * 60 * 1e3);
|
|
1363
|
-
const thisWeek = history.filter((e) => new Date(e.timestamp) >= oneWeekAgo);
|
|
1364
|
-
const lastWeek = history.filter(
|
|
1365
|
-
(e) => new Date(e.timestamp) >= twoWeeksAgo && new Date(e.timestamp) < oneWeekAgo
|
|
1366
|
-
);
|
|
1367
|
-
const issuesFixedThisWeek = thisWeek.length > 1 ? thisWeek[0].totalIssues - thisWeek[thisWeek.length - 1].totalIssues : 0;
|
|
1368
|
-
const totalIssuesFixed = history[0].totalIssues - history[history.length - 1].totalIssues;
|
|
1369
|
-
const weeksOfData = Math.max(1, history.length / 7);
|
|
1370
|
-
const avgIssuesPerWeek = totalIssuesFixed / weeksOfData;
|
|
1371
|
-
let trend;
|
|
1372
|
-
if (lastWeek.length > 1) {
|
|
1373
|
-
const lastWeekFixed = lastWeek[0].totalIssues - lastWeek[lastWeek.length - 1].totalIssues;
|
|
1374
|
-
if (issuesFixedThisWeek > lastWeekFixed * 1.2) trend = "accelerating";
|
|
1375
|
-
else if (issuesFixedThisWeek < lastWeekFixed * 0.8) trend = "decelerating";
|
|
1376
|
-
else trend = "stable";
|
|
1377
|
-
} else {
|
|
1378
|
-
trend = "stable";
|
|
1379
|
-
}
|
|
1380
|
-
const estimatedCompletionWeeks = avgIssuesPerWeek > 0 ? Math.ceil(currentIssues / avgIssuesPerWeek) : Infinity;
|
|
1381
|
-
return {
|
|
1382
|
-
issuesFixedThisWeek: Math.max(0, issuesFixedThisWeek),
|
|
1383
|
-
avgIssuesPerWeek: Math.round(avgIssuesPerWeek * 10) / 10,
|
|
1384
|
-
trend,
|
|
1385
|
-
estimatedCompletionWeeks
|
|
1386
|
-
};
|
|
1387
|
-
}
|
|
1388
|
-
function calculateKnowledgeConcentration(files, authorData) {
|
|
1389
|
-
if (files.length === 0) {
|
|
1390
|
-
return {
|
|
1391
|
-
score: 0,
|
|
1392
|
-
rating: "low",
|
|
1393
|
-
analysis: {
|
|
1394
|
-
uniqueConceptFiles: 0,
|
|
1395
|
-
totalFiles: 0,
|
|
1396
|
-
concentrationRatio: 0,
|
|
1397
|
-
singleAuthorFiles: 0,
|
|
1398
|
-
orphanFiles: 0
|
|
1399
|
-
},
|
|
1400
|
-
recommendations: ["No files to analyze"]
|
|
1401
|
-
};
|
|
1402
|
-
}
|
|
1403
|
-
const orphanFiles = files.filter(
|
|
1404
|
-
(f) => f.exports < 2 && f.imports < 2
|
|
1405
|
-
).length;
|
|
1406
|
-
const avgExports = files.reduce((sum, f) => sum + f.exports, 0) / files.length;
|
|
1407
|
-
const uniqueConceptFiles = files.filter(
|
|
1408
|
-
(f) => f.exports > avgExports * 2
|
|
1409
|
-
).length;
|
|
1410
|
-
const totalExports = files.reduce((sum, f) => sum + f.exports, 0);
|
|
1411
|
-
const concentrationRatio = totalExports > 0 ? uniqueConceptFiles / files.length : 0;
|
|
1412
|
-
let singleAuthorFiles = 0;
|
|
1413
|
-
if (authorData) {
|
|
1414
|
-
for (const files2 of authorData.values()) {
|
|
1415
|
-
if (files2.length === 1) singleAuthorFiles++;
|
|
1416
|
-
}
|
|
1417
|
-
}
|
|
1418
|
-
const orphanRisk = orphanFiles / files.length * 30;
|
|
1419
|
-
const uniqueRisk = concentrationRatio * 40;
|
|
1420
|
-
const singleAuthorRisk = authorData ? singleAuthorFiles / files.length * 30 : 0;
|
|
1421
|
-
const score = Math.min(
|
|
1422
|
-
100,
|
|
1423
|
-
Math.round(orphanRisk + uniqueRisk + singleAuthorRisk)
|
|
1424
|
-
);
|
|
1425
|
-
let rating;
|
|
1426
|
-
if (score < 20) rating = "low";
|
|
1427
|
-
else if (score < 40) rating = "moderate";
|
|
1428
|
-
else if (score < 70) rating = "high";
|
|
1429
|
-
else rating = "critical";
|
|
1430
|
-
const recommendations = [];
|
|
1431
|
-
if (orphanFiles > files.length * 0.2) {
|
|
1432
|
-
recommendations.push(
|
|
1433
|
-
`Reduce ${orphanFiles} orphan files by connecting them to main modules`
|
|
1434
|
-
);
|
|
1435
|
-
}
|
|
1436
|
-
if (uniqueConceptFiles > files.length * 0.1) {
|
|
1437
|
-
recommendations.push(
|
|
1438
|
-
"Distribute high-export files into more focused modules"
|
|
1439
|
-
);
|
|
1440
|
-
}
|
|
1441
|
-
if (authorData && singleAuthorFiles > files.length * 0.3) {
|
|
1442
|
-
recommendations.push(
|
|
1443
|
-
"Increase knowledge sharing to reduce single-author dependencies"
|
|
1444
|
-
);
|
|
1445
|
-
}
|
|
1446
|
-
return {
|
|
1447
|
-
score,
|
|
1448
|
-
rating,
|
|
1449
|
-
analysis: {
|
|
1450
|
-
uniqueConceptFiles,
|
|
1451
|
-
totalFiles: files.length,
|
|
1452
|
-
concentrationRatio: Math.round(concentrationRatio * 100) / 100,
|
|
1453
|
-
singleAuthorFiles,
|
|
1454
|
-
orphanFiles
|
|
1455
|
-
},
|
|
1456
|
-
recommendations
|
|
1457
|
-
};
|
|
1458
|
-
}
|
|
1459
|
-
function calculateTechnicalDebtInterest(params) {
|
|
1460
|
-
const { currentMonthlyCost, issues, monthsOpen } = params;
|
|
1461
|
-
const criticalCount = issues.filter((i) => i.severity === "critical").length;
|
|
1462
|
-
const majorCount = issues.filter((i) => i.severity === "major").length;
|
|
1463
|
-
const minorCount = issues.filter((i) => i.severity === "minor").length;
|
|
1464
|
-
const severityWeight = (criticalCount * 3 + majorCount * 2 + minorCount * 1) / Math.max(1, issues.length);
|
|
1465
|
-
const baseRate = 0.02 + severityWeight * 0.01;
|
|
1466
|
-
const timeMultiplier = Math.max(1, 1 + monthsOpen * 0.1);
|
|
1467
|
-
const monthlyRate = baseRate * timeMultiplier;
|
|
1468
|
-
const projectDebt = (principal2, months) => {
|
|
1469
|
-
let debt = principal2;
|
|
1470
|
-
for (let i = 0; i < months; i++) {
|
|
1471
|
-
debt = debt * (1 + monthlyRate);
|
|
1472
|
-
}
|
|
1473
|
-
return Math.round(debt);
|
|
1474
|
-
};
|
|
1475
|
-
const principal = currentMonthlyCost * 12;
|
|
1476
|
-
const projections = {
|
|
1477
|
-
months6: projectDebt(principal, 6),
|
|
1478
|
-
months12: projectDebt(principal, 12),
|
|
1479
|
-
months24: projectDebt(principal, 24)
|
|
1480
|
-
};
|
|
1481
|
-
return {
|
|
1482
|
-
monthlyRate: Math.round(monthlyRate * 1e4) / 100,
|
|
1483
|
-
annualRate: Math.round((Math.pow(1 + monthlyRate, 12) - 1) * 1e4) / 100,
|
|
1484
|
-
principal,
|
|
1485
|
-
projections,
|
|
1486
|
-
monthlyCost: Math.round(currentMonthlyCost * (1 + monthlyRate) * 100) / 100
|
|
1487
|
-
};
|
|
1488
|
-
}
|
|
1489
|
-
function getDebtBreakdown(patternCost, contextCost, consistencyCost) {
|
|
1490
|
-
const breakdowns = [
|
|
1491
|
-
{
|
|
1492
|
-
category: "Semantic Duplication",
|
|
1493
|
-
currentCost: patternCost,
|
|
1494
|
-
monthlyGrowthRate: 5,
|
|
1495
|
-
// Grows as devs copy-paste
|
|
1496
|
-
priority: patternCost > 1e3 ? "high" : "medium",
|
|
1497
|
-
fixCost: patternCost * 3
|
|
1498
|
-
// Fixing costs 3x current waste
|
|
1499
|
-
},
|
|
1500
|
-
{
|
|
1501
|
-
category: "Context Fragmentation",
|
|
1502
|
-
currentCost: contextCost,
|
|
1503
|
-
monthlyGrowthRate: 3,
|
|
1504
|
-
// Grows with new features
|
|
1505
|
-
priority: contextCost > 500 ? "high" : "medium",
|
|
1506
|
-
fixCost: contextCost * 2.5
|
|
1507
|
-
},
|
|
1508
|
-
{
|
|
1509
|
-
category: "Consistency Issues",
|
|
1510
|
-
currentCost: consistencyCost,
|
|
1511
|
-
monthlyGrowthRate: 2,
|
|
1512
|
-
// Grows with new devs
|
|
1513
|
-
priority: consistencyCost > 200 ? "medium" : "low",
|
|
1514
|
-
fixCost: consistencyCost * 1.5
|
|
1515
|
-
}
|
|
1516
|
-
];
|
|
1517
|
-
return breakdowns.sort((a, b) => {
|
|
1518
|
-
const priorityOrder = { high: 0, medium: 1, low: 2 };
|
|
1519
|
-
return priorityOrder[a.priority] - priorityOrder[b.priority];
|
|
1520
|
-
});
|
|
1521
|
-
}
|
|
1522
1395
|
function generateValueChain(params) {
|
|
1523
1396
|
const { issueType, count, severity } = params;
|
|
1524
1397
|
const impacts = {
|
|
@@ -1558,9 +1431,7 @@ function generateValueChain(params) {
|
|
|
1558
1431
|
},
|
|
1559
1432
|
businessOutcome: {
|
|
1560
1433
|
directCost: count * 12,
|
|
1561
|
-
// Placeholder linear cost
|
|
1562
1434
|
opportunityCost: productivityLoss * 15e3,
|
|
1563
|
-
// Monthly avg dev cost $15k
|
|
1564
1435
|
riskLevel: impact.risk
|
|
1565
1436
|
}
|
|
1566
1437
|
};
|
|
@@ -2004,7 +1875,9 @@ var ParserFactory = class _ParserFactory {
|
|
|
2004
1875
|
registerParser(parser) {
|
|
2005
1876
|
this.parsers.set(parser.language, parser);
|
|
2006
1877
|
parser.extensions.forEach((ext) => {
|
|
2007
|
-
|
|
1878
|
+
const lang = LANGUAGE_EXTENSIONS[ext] || parser.language;
|
|
1879
|
+
this.extensionMap.set(ext, lang);
|
|
1880
|
+
this.parsers.set(lang, parser);
|
|
2008
1881
|
});
|
|
2009
1882
|
}
|
|
2010
1883
|
/**
|
|
@@ -2073,7 +1946,55 @@ function getSupportedLanguages() {
|
|
|
2073
1946
|
return ParserFactory.getInstance().getSupportedLanguages();
|
|
2074
1947
|
}
|
|
2075
1948
|
|
|
2076
|
-
// src/
|
|
1949
|
+
// src/metrics/remediation-utils.ts
|
|
1950
|
+
function collectFutureProofRecommendations(params) {
|
|
1951
|
+
const recommendations = [];
|
|
1952
|
+
for (const rec of params.aiSignalClarity.recommendations) {
|
|
1953
|
+
recommendations.push({ action: rec, estimatedImpact: 8, priority: "high" });
|
|
1954
|
+
}
|
|
1955
|
+
for (const rec of params.agentGrounding.recommendations) {
|
|
1956
|
+
recommendations.push({
|
|
1957
|
+
action: rec,
|
|
1958
|
+
estimatedImpact: 6,
|
|
1959
|
+
priority: "medium"
|
|
1960
|
+
});
|
|
1961
|
+
}
|
|
1962
|
+
for (const rec of params.testability.recommendations) {
|
|
1963
|
+
const priority = params.testability.aiChangeSafetyRating === "blind-risk" ? "high" : "medium";
|
|
1964
|
+
recommendations.push({ action: rec, estimatedImpact: 10, priority });
|
|
1965
|
+
}
|
|
1966
|
+
for (const rec of params.patternEntropy.recommendations) {
|
|
1967
|
+
recommendations.push({ action: rec, estimatedImpact: 5, priority: "low" });
|
|
1968
|
+
}
|
|
1969
|
+
if (params.conceptCohesion.rating === "poor") {
|
|
1970
|
+
recommendations.push({
|
|
1971
|
+
action: "Improve concept cohesion by grouping related exports",
|
|
1972
|
+
estimatedImpact: 8,
|
|
1973
|
+
priority: "high"
|
|
1974
|
+
});
|
|
1975
|
+
}
|
|
1976
|
+
if (params.docDrift) {
|
|
1977
|
+
for (const rec of params.docDrift.recommendations) {
|
|
1978
|
+
recommendations.push({
|
|
1979
|
+
action: rec,
|
|
1980
|
+
estimatedImpact: 8,
|
|
1981
|
+
priority: "high"
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
if (params.dependencyHealth) {
|
|
1986
|
+
for (const rec of params.dependencyHealth.recommendations) {
|
|
1987
|
+
recommendations.push({
|
|
1988
|
+
action: rec,
|
|
1989
|
+
estimatedImpact: 7,
|
|
1990
|
+
priority: "medium"
|
|
1991
|
+
});
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
return recommendations;
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
// src/metrics/cognitive-load.ts
|
|
2077
1998
|
function calculateCognitiveLoad(params) {
|
|
2078
1999
|
const {
|
|
2079
2000
|
linesOfCode,
|
|
@@ -2132,13 +2053,20 @@ function calculateCognitiveLoad(params) {
|
|
|
2132
2053
|
}
|
|
2133
2054
|
};
|
|
2134
2055
|
}
|
|
2056
|
+
|
|
2057
|
+
// src/metrics/semantic-distance.ts
|
|
2135
2058
|
function calculateSemanticDistance(params) {
|
|
2136
|
-
const {
|
|
2059
|
+
const {
|
|
2060
|
+
file1,
|
|
2061
|
+
file2,
|
|
2062
|
+
file1Domain,
|
|
2063
|
+
file2Domain,
|
|
2064
|
+
sharedDependencies,
|
|
2065
|
+
file1Imports,
|
|
2066
|
+
file2Imports
|
|
2067
|
+
} = params;
|
|
2137
2068
|
const domainDistance = file1Domain === file2Domain ? 0 : file1Domain && file2Domain ? 0.5 : 0.8;
|
|
2138
|
-
const importOverlap = sharedDependencies.length / Math.max(
|
|
2139
|
-
1,
|
|
2140
|
-
Math.min(params.file1Imports.length, params.file2Imports.length)
|
|
2141
|
-
);
|
|
2069
|
+
const importOverlap = sharedDependencies.length / Math.max(1, Math.min(file1Imports.length, file2Imports.length));
|
|
2142
2070
|
const importDistance = 1 - importOverlap;
|
|
2143
2071
|
const distance = domainDistance * 0.4 + importDistance * 0.3 + (sharedDependencies.length > 0 ? 0 : 0.3);
|
|
2144
2072
|
let relationship;
|
|
@@ -2157,6 +2085,8 @@ function calculateSemanticDistance(params) {
|
|
|
2157
2085
|
reason: relationship === "same-domain" ? `Both in "${file1Domain}" domain` : relationship === "cross-domain" ? `Share ${sharedDependencies.length} dependency(ies)` : "No strong semantic relationship detected"
|
|
2158
2086
|
};
|
|
2159
2087
|
}
|
|
2088
|
+
|
|
2089
|
+
// src/metrics/structural-metrics.ts
|
|
2160
2090
|
function calculatePatternEntropy(files) {
|
|
2161
2091
|
if (files.length === 0) {
|
|
2162
2092
|
return {
|
|
@@ -2206,23 +2136,18 @@ function calculatePatternEntropy(files) {
|
|
|
2206
2136
|
else if (normalizedEntropy < 0.8) rating = "fragmented";
|
|
2207
2137
|
else rating = "chaotic";
|
|
2208
2138
|
const recommendations = [];
|
|
2209
|
-
if (normalizedEntropy > 0.5)
|
|
2139
|
+
if (normalizedEntropy > 0.5)
|
|
2210
2140
|
recommendations.push(
|
|
2211
2141
|
`Consolidate ${files.length} files into fewer directories by domain`
|
|
2212
2142
|
);
|
|
2213
|
-
|
|
2214
|
-
if (dirGroups.size > 5) {
|
|
2143
|
+
if (dirGroups.size > 5)
|
|
2215
2144
|
recommendations.push(
|
|
2216
2145
|
"Consider barrel exports to reduce directory navigation"
|
|
2217
2146
|
);
|
|
2218
|
-
|
|
2219
|
-
if (gini > 0.5) {
|
|
2147
|
+
if (gini > 0.5)
|
|
2220
2148
|
recommendations.push("Redistribute files more evenly across directories");
|
|
2221
|
-
}
|
|
2222
|
-
const firstFile = files.length > 0 ? files[0] : null;
|
|
2223
|
-
const domainValue = firstFile ? firstFile.domain : "mixed";
|
|
2224
2149
|
return {
|
|
2225
|
-
domain:
|
|
2150
|
+
domain: files[0]?.domain || "mixed",
|
|
2226
2151
|
entropy: Math.round(normalizedEntropy * 100) / 100,
|
|
2227
2152
|
rating,
|
|
2228
2153
|
distribution: {
|
|
@@ -2253,9 +2178,8 @@ function calculateConceptCohesion(params) {
|
|
|
2253
2178
|
}
|
|
2254
2179
|
const uniqueDomains = new Set(allDomains);
|
|
2255
2180
|
const domainCounts = /* @__PURE__ */ new Map();
|
|
2256
|
-
for (const d of allDomains)
|
|
2181
|
+
for (const d of allDomains)
|
|
2257
2182
|
domainCounts.set(d, (domainCounts.get(d) || 0) + 1);
|
|
2258
|
-
}
|
|
2259
2183
|
const maxCount = Math.max(...Array.from(domainCounts.values()), 1);
|
|
2260
2184
|
const domainConcentration = maxCount / allDomains.length;
|
|
2261
2185
|
const exportPurposeClarity = 1 - (uniqueDomains.size - 1) / Math.max(1, exports2.length);
|
|
@@ -2275,59 +2199,8 @@ function calculateConceptCohesion(params) {
|
|
|
2275
2199
|
}
|
|
2276
2200
|
};
|
|
2277
2201
|
}
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
const entropyScore = 100 - params.patternEntropy.entropy * 100;
|
|
2281
|
-
const cohesionScore = params.conceptCohesion.score * 100;
|
|
2282
|
-
const overall = Math.round(
|
|
2283
|
-
loadScore * 0.4 + entropyScore * 0.3 + cohesionScore * 0.3
|
|
2284
|
-
);
|
|
2285
|
-
const factors = [
|
|
2286
|
-
{
|
|
2287
|
-
name: "Cognitive Load",
|
|
2288
|
-
impact: Math.round(loadScore - 50),
|
|
2289
|
-
description: params.cognitiveLoad.rating
|
|
2290
|
-
},
|
|
2291
|
-
{
|
|
2292
|
-
name: "Pattern Entropy",
|
|
2293
|
-
impact: Math.round(entropyScore - 50),
|
|
2294
|
-
description: params.patternEntropy.rating
|
|
2295
|
-
},
|
|
2296
|
-
{
|
|
2297
|
-
name: "Concept Cohesion",
|
|
2298
|
-
impact: Math.round(cohesionScore - 50),
|
|
2299
|
-
description: params.conceptCohesion.rating
|
|
2300
|
-
}
|
|
2301
|
-
];
|
|
2302
|
-
const recommendations = [];
|
|
2303
|
-
for (const rec of params.patternEntropy.recommendations) {
|
|
2304
|
-
recommendations.push({
|
|
2305
|
-
action: rec,
|
|
2306
|
-
estimatedImpact: 5,
|
|
2307
|
-
priority: "medium"
|
|
2308
|
-
});
|
|
2309
|
-
}
|
|
2310
|
-
if (params.conceptCohesion.rating === "poor") {
|
|
2311
|
-
recommendations.push({
|
|
2312
|
-
action: "Improve concept cohesion by grouping related exports",
|
|
2313
|
-
estimatedImpact: 8,
|
|
2314
|
-
priority: "high"
|
|
2315
|
-
});
|
|
2316
|
-
}
|
|
2317
|
-
const semanticDistanceAvg = params.semanticDistances && params.semanticDistances.length > 0 ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
|
|
2318
|
-
return {
|
|
2319
|
-
toolName: "future-proof",
|
|
2320
|
-
score: overall,
|
|
2321
|
-
rawMetrics: {
|
|
2322
|
-
cognitiveLoadScore: params.cognitiveLoad.score,
|
|
2323
|
-
entropyScore: params.patternEntropy.entropy,
|
|
2324
|
-
cohesionScore: params.conceptCohesion.score,
|
|
2325
|
-
semanticDistanceAvg
|
|
2326
|
-
},
|
|
2327
|
-
factors,
|
|
2328
|
-
recommendations
|
|
2329
|
-
};
|
|
2330
|
-
}
|
|
2202
|
+
|
|
2203
|
+
// src/metrics/ai-signal-clarity.ts
|
|
2331
2204
|
function calculateAiSignalClarity(params) {
|
|
2332
2205
|
const {
|
|
2333
2206
|
overloadedSymbols,
|
|
@@ -2349,75 +2222,53 @@ function calculateAiSignalClarity(params) {
|
|
|
2349
2222
|
recommendations: []
|
|
2350
2223
|
};
|
|
2351
2224
|
}
|
|
2352
|
-
const overloadRatio = Math.
|
|
2353
|
-
1,
|
|
2354
|
-
overloadedSymbols / Math.max(1, totalSymbols)
|
|
2355
|
-
);
|
|
2225
|
+
const overloadRatio = overloadedSymbols / Math.max(1, totalSymbols);
|
|
2356
2226
|
const overloadSignal = {
|
|
2357
2227
|
name: "Symbol Overloading",
|
|
2358
2228
|
count: overloadedSymbols,
|
|
2359
|
-
riskContribution: Math.round(overloadRatio * 100 * 0.25),
|
|
2360
|
-
// 25% weight
|
|
2229
|
+
riskContribution: Math.round(Math.min(1, overloadRatio) * 100 * 0.25),
|
|
2361
2230
|
description: `${overloadedSymbols} overloaded symbols \u2014 AI picks wrong signature`
|
|
2362
2231
|
};
|
|
2363
|
-
const magicRatio =
|
|
2232
|
+
const magicRatio = magicLiterals / Math.max(1, totalSymbols * 2);
|
|
2364
2233
|
const magicSignal = {
|
|
2365
2234
|
name: "Magic Literals",
|
|
2366
2235
|
count: magicLiterals,
|
|
2367
|
-
riskContribution: Math.round(magicRatio * 100 * 0.2),
|
|
2368
|
-
// 20% weight
|
|
2236
|
+
riskContribution: Math.round(Math.min(1, magicRatio) * 100 * 0.2),
|
|
2369
2237
|
description: `${magicLiterals} unnamed constants \u2014 AI invents wrong values`
|
|
2370
2238
|
};
|
|
2371
|
-
const trapRatio =
|
|
2239
|
+
const trapRatio = booleanTraps / Math.max(1, totalSymbols);
|
|
2372
2240
|
const trapSignal = {
|
|
2373
2241
|
name: "Boolean Traps",
|
|
2374
2242
|
count: booleanTraps,
|
|
2375
|
-
riskContribution: Math.round(trapRatio * 100 * 0.2),
|
|
2376
|
-
// 20% weight
|
|
2243
|
+
riskContribution: Math.round(Math.min(1, trapRatio) * 100 * 0.2),
|
|
2377
2244
|
description: `${booleanTraps} boolean trap parameters \u2014 AI inverts intent`
|
|
2378
2245
|
};
|
|
2379
|
-
const sideEffectRatio = Math.
|
|
2380
|
-
1,
|
|
2381
|
-
implicitSideEffects / Math.max(1, totalExports)
|
|
2382
|
-
);
|
|
2246
|
+
const sideEffectRatio = implicitSideEffects / Math.max(1, totalExports);
|
|
2383
2247
|
const sideEffectSignal = {
|
|
2384
2248
|
name: "Implicit Side Effects",
|
|
2385
2249
|
count: implicitSideEffects,
|
|
2386
|
-
riskContribution: Math.round(sideEffectRatio * 100 * 0.15),
|
|
2387
|
-
// 15% weight
|
|
2250
|
+
riskContribution: Math.round(Math.min(1, sideEffectRatio) * 100 * 0.15),
|
|
2388
2251
|
description: `${implicitSideEffects} functions with implicit side effects \u2014 AI misses contracts`
|
|
2389
2252
|
};
|
|
2390
|
-
const callbackRatio = Math.
|
|
2391
|
-
1,
|
|
2392
|
-
deepCallbacks / Math.max(1, totalSymbols * 0.1)
|
|
2393
|
-
);
|
|
2253
|
+
const callbackRatio = deepCallbacks / Math.max(1, totalSymbols * 0.1);
|
|
2394
2254
|
const callbackSignal = {
|
|
2395
2255
|
name: "Callback Nesting",
|
|
2396
2256
|
count: deepCallbacks,
|
|
2397
|
-
riskContribution: Math.round(callbackRatio * 100 * 0.1),
|
|
2398
|
-
// 10% weight
|
|
2257
|
+
riskContribution: Math.round(Math.min(1, callbackRatio) * 100 * 0.1),
|
|
2399
2258
|
description: `${deepCallbacks} deep callback chains \u2014 AI loses control flow context`
|
|
2400
2259
|
};
|
|
2401
|
-
const ambiguousRatio = Math.
|
|
2402
|
-
1,
|
|
2403
|
-
ambiguousNames / Math.max(1, totalSymbols)
|
|
2404
|
-
);
|
|
2260
|
+
const ambiguousRatio = ambiguousNames / Math.max(1, totalSymbols);
|
|
2405
2261
|
const ambiguousSignal = {
|
|
2406
2262
|
name: "Ambiguous Names",
|
|
2407
2263
|
count: ambiguousNames,
|
|
2408
|
-
riskContribution: Math.round(ambiguousRatio * 100 * 0.1),
|
|
2409
|
-
// 10% weight
|
|
2264
|
+
riskContribution: Math.round(Math.min(1, ambiguousRatio) * 100 * 0.1),
|
|
2410
2265
|
description: `${ambiguousNames} non-descriptive identifiers \u2014 AI guesses wrong intent`
|
|
2411
2266
|
};
|
|
2412
|
-
const undocRatio = Math.
|
|
2413
|
-
1,
|
|
2414
|
-
undocumentedExports / Math.max(1, totalExports)
|
|
2415
|
-
);
|
|
2267
|
+
const undocRatio = undocumentedExports / Math.max(1, totalExports);
|
|
2416
2268
|
const undocSignal = {
|
|
2417
2269
|
name: "Undocumented Exports",
|
|
2418
2270
|
count: undocumentedExports,
|
|
2419
|
-
riskContribution: Math.round(undocRatio * 100 * 0.1),
|
|
2420
|
-
// 10% weight
|
|
2271
|
+
riskContribution: Math.round(Math.min(1, undocRatio) * 100 * 0.1),
|
|
2421
2272
|
description: `${undocumentedExports} public functions without docs \u2014 AI fabricates behavior`
|
|
2422
2273
|
};
|
|
2423
2274
|
const signals = [
|
|
@@ -2442,33 +2293,28 @@ function calculateAiSignalClarity(params) {
|
|
|
2442
2293
|
const topSignal = signals.reduce(
|
|
2443
2294
|
(a, b) => a.riskContribution > b.riskContribution ? a : b
|
|
2444
2295
|
);
|
|
2445
|
-
const topRisk = topSignal.riskContribution > 0 ? topSignal.description : "No significant
|
|
2296
|
+
const topRisk = topSignal.riskContribution > 0 ? topSignal.description : "No significant issues detected";
|
|
2446
2297
|
const recommendations = [];
|
|
2447
|
-
if (overloadSignal.riskContribution > 5)
|
|
2298
|
+
if (overloadSignal.riskContribution > 5)
|
|
2448
2299
|
recommendations.push(
|
|
2449
2300
|
`Rename ${overloadedSymbols} overloaded symbols to unique, intent-revealing names`
|
|
2450
2301
|
);
|
|
2451
|
-
|
|
2452
|
-
if (magicSignal.riskContribution > 5) {
|
|
2302
|
+
if (magicSignal.riskContribution > 5)
|
|
2453
2303
|
recommendations.push(
|
|
2454
2304
|
`Extract ${magicLiterals} magic literals into named constants`
|
|
2455
2305
|
);
|
|
2456
|
-
|
|
2457
|
-
if (trapSignal.riskContribution > 5) {
|
|
2306
|
+
if (trapSignal.riskContribution > 5)
|
|
2458
2307
|
recommendations.push(
|
|
2459
2308
|
`Replace ${booleanTraps} boolean traps with named options objects`
|
|
2460
2309
|
);
|
|
2461
|
-
|
|
2462
|
-
if (undocSignal.riskContribution > 5) {
|
|
2310
|
+
if (undocSignal.riskContribution > 5)
|
|
2463
2311
|
recommendations.push(
|
|
2464
2312
|
`Add JSDoc/docstrings to ${undocumentedExports} undocumented public functions`
|
|
2465
2313
|
);
|
|
2466
|
-
|
|
2467
|
-
if (sideEffectSignal.riskContribution > 5) {
|
|
2314
|
+
if (sideEffectSignal.riskContribution > 5)
|
|
2468
2315
|
recommendations.push(
|
|
2469
2316
|
"Mark functions with side effects explicitly in their names or docs"
|
|
2470
2317
|
);
|
|
2471
|
-
}
|
|
2472
2318
|
return {
|
|
2473
2319
|
score: Math.round(score),
|
|
2474
2320
|
rating,
|
|
@@ -2477,6 +2323,8 @@ function calculateAiSignalClarity(params) {
|
|
|
2477
2323
|
recommendations
|
|
2478
2324
|
};
|
|
2479
2325
|
}
|
|
2326
|
+
|
|
2327
|
+
// src/metrics/agent-grounding.ts
|
|
2480
2328
|
function calculateAgentGrounding(params) {
|
|
2481
2329
|
const {
|
|
2482
2330
|
deepDirectories,
|
|
@@ -2491,25 +2339,33 @@ function calculateAgentGrounding(params) {
|
|
|
2491
2339
|
inconsistentDomainTerms,
|
|
2492
2340
|
domainVocabularySize
|
|
2493
2341
|
} = params;
|
|
2494
|
-
const deepDirRatio = totalDirectories > 0 ? deepDirectories / totalDirectories : 0;
|
|
2495
2342
|
const structureClarityScore = Math.max(
|
|
2496
2343
|
0,
|
|
2497
|
-
Math.round(
|
|
2344
|
+
Math.round(
|
|
2345
|
+
100 - (totalDirectories > 0 ? deepDirectories / totalDirectories * 80 : 0)
|
|
2346
|
+
)
|
|
2347
|
+
);
|
|
2348
|
+
const selfDocumentationScore = Math.max(
|
|
2349
|
+
0,
|
|
2350
|
+
Math.round(100 - (totalFiles > 0 ? vagueFileNames / totalFiles * 90 : 0))
|
|
2498
2351
|
);
|
|
2499
|
-
const vagueRatio = totalFiles > 0 ? vagueFileNames / totalFiles : 0;
|
|
2500
|
-
const selfDocumentationScore = Math.max(0, Math.round(100 - vagueRatio * 90));
|
|
2501
2352
|
let entryPointScore = 60;
|
|
2502
2353
|
if (hasRootReadme) entryPointScore += 25;
|
|
2503
2354
|
if (readmeIsFresh) entryPointScore += 10;
|
|
2504
2355
|
const barrelRatio = totalFiles > 0 ? barrelExports / (totalFiles * 0.1) : 0;
|
|
2505
2356
|
entryPointScore += Math.round(Math.min(5, barrelRatio * 5));
|
|
2506
2357
|
entryPointScore = Math.min(100, entryPointScore);
|
|
2507
|
-
const
|
|
2508
|
-
|
|
2509
|
-
|
|
2358
|
+
const apiClarityScore = Math.max(
|
|
2359
|
+
0,
|
|
2360
|
+
Math.round(
|
|
2361
|
+
100 - (totalExports > 0 ? untypedExports / totalExports * 70 : 0)
|
|
2362
|
+
)
|
|
2363
|
+
);
|
|
2510
2364
|
const domainConsistencyScore = Math.max(
|
|
2511
2365
|
0,
|
|
2512
|
-
Math.round(
|
|
2366
|
+
Math.round(
|
|
2367
|
+
100 - (domainVocabularySize > 0 ? inconsistentDomainTerms / domainVocabularySize * 80 : 0)
|
|
2368
|
+
)
|
|
2513
2369
|
);
|
|
2514
2370
|
const score = Math.round(
|
|
2515
2371
|
structureClarityScore * 0.2 + selfDocumentationScore * 0.25 + entryPointScore * 0.2 + apiClarityScore * 0.15 + domainConsistencyScore * 0.2
|
|
@@ -2521,35 +2377,30 @@ function calculateAgentGrounding(params) {
|
|
|
2521
2377
|
else if (score >= 30) rating = "poor";
|
|
2522
2378
|
else rating = "disorienting";
|
|
2523
2379
|
const recommendations = [];
|
|
2524
|
-
if (structureClarityScore < 70)
|
|
2380
|
+
if (structureClarityScore < 70)
|
|
2525
2381
|
recommendations.push(
|
|
2526
2382
|
`Flatten ${deepDirectories} overly-deep directories to improve agent navigation`
|
|
2527
2383
|
);
|
|
2528
|
-
|
|
2529
|
-
if (selfDocumentationScore < 70) {
|
|
2384
|
+
if (selfDocumentationScore < 70)
|
|
2530
2385
|
recommendations.push(
|
|
2531
2386
|
`Rename ${vagueFileNames} vague files (utils, helpers, misc) to domain-specific names`
|
|
2532
2387
|
);
|
|
2533
|
-
|
|
2534
|
-
if (!hasRootReadme) {
|
|
2388
|
+
if (!hasRootReadme)
|
|
2535
2389
|
recommendations.push(
|
|
2536
2390
|
"Add a root README.md so agents understand the project context immediately"
|
|
2537
2391
|
);
|
|
2538
|
-
|
|
2392
|
+
else if (!readmeIsFresh)
|
|
2539
2393
|
recommendations.push(
|
|
2540
2394
|
"Update README.md \u2014 stale entry-point documentation disorients agents"
|
|
2541
2395
|
);
|
|
2542
|
-
|
|
2543
|
-
if (apiClarityScore < 70) {
|
|
2396
|
+
if (apiClarityScore < 70)
|
|
2544
2397
|
recommendations.push(
|
|
2545
2398
|
`Add TypeScript types to ${untypedExports} untyped exports to improve API discoverability`
|
|
2546
2399
|
);
|
|
2547
|
-
|
|
2548
|
-
if (domainConsistencyScore < 70) {
|
|
2400
|
+
if (domainConsistencyScore < 70)
|
|
2549
2401
|
recommendations.push(
|
|
2550
2402
|
`Unify ${inconsistentDomainTerms} inconsistent domain terms \u2014 agents need one word per concept`
|
|
2551
2403
|
);
|
|
2552
|
-
}
|
|
2553
2404
|
return {
|
|
2554
2405
|
score,
|
|
2555
2406
|
rating,
|
|
@@ -2563,6 +2414,8 @@ function calculateAgentGrounding(params) {
|
|
|
2563
2414
|
recommendations
|
|
2564
2415
|
};
|
|
2565
2416
|
}
|
|
2417
|
+
|
|
2418
|
+
// src/metrics/testability-index.ts
|
|
2566
2419
|
function calculateTestabilityIndex(params) {
|
|
2567
2420
|
const {
|
|
2568
2421
|
testFiles,
|
|
@@ -2578,16 +2431,27 @@ function calculateTestabilityIndex(params) {
|
|
|
2578
2431
|
} = params;
|
|
2579
2432
|
const rawCoverageRatio = sourceFiles > 0 ? testFiles / sourceFiles : 0;
|
|
2580
2433
|
const testCoverageRatio = Math.min(100, Math.round(rawCoverageRatio * 100));
|
|
2581
|
-
const
|
|
2582
|
-
|
|
2583
|
-
|
|
2434
|
+
const purityScore = Math.round(
|
|
2435
|
+
(totalFunctions > 0 ? pureFunctions / totalFunctions : 0.5) * 100
|
|
2436
|
+
);
|
|
2584
2437
|
const dependencyInjectionScore = Math.round(
|
|
2585
|
-
Math.min(
|
|
2438
|
+
Math.min(
|
|
2439
|
+
100,
|
|
2440
|
+
(totalClasses > 0 ? injectionPatterns / totalClasses : 0.5) * 100
|
|
2441
|
+
)
|
|
2442
|
+
);
|
|
2443
|
+
const interfaceFocusScore = Math.max(
|
|
2444
|
+
0,
|
|
2445
|
+
Math.round(
|
|
2446
|
+
100 - (totalInterfaces > 0 ? bloatedInterfaces / totalInterfaces * 80 : 0)
|
|
2447
|
+
)
|
|
2448
|
+
);
|
|
2449
|
+
const observabilityScore = Math.max(
|
|
2450
|
+
0,
|
|
2451
|
+
Math.round(
|
|
2452
|
+
100 - (totalFunctions > 0 ? externalStateMutations / totalFunctions * 100 : 0)
|
|
2453
|
+
)
|
|
2586
2454
|
);
|
|
2587
|
-
const bloatedRatio = totalInterfaces > 0 ? bloatedInterfaces / totalInterfaces : 0;
|
|
2588
|
-
const interfaceFocusScore = Math.max(0, Math.round(100 - bloatedRatio * 80));
|
|
2589
|
-
const mutationRatio = totalFunctions > 0 ? externalStateMutations / totalFunctions : 0;
|
|
2590
|
-
const observabilityScore = Math.max(0, Math.round(100 - mutationRatio * 100));
|
|
2591
2455
|
const frameworkWeight = hasTestFramework ? 1 : 0.8;
|
|
2592
2456
|
const rawScore = (testCoverageRatio * 0.3 + purityScore * 0.25 + dependencyInjectionScore * 0.2 + interfaceFocusScore * 0.1 + observabilityScore * 0.15) * frameworkWeight;
|
|
2593
2457
|
const score = Math.max(0, Math.min(100, Math.round(rawScore)));
|
|
@@ -2604,32 +2468,28 @@ function calculateTestabilityIndex(params) {
|
|
|
2604
2468
|
else if (rawCoverageRatio > 0) aiChangeSafetyRating = "high-risk";
|
|
2605
2469
|
else aiChangeSafetyRating = "blind-risk";
|
|
2606
2470
|
const recommendations = [];
|
|
2607
|
-
if (!hasTestFramework)
|
|
2471
|
+
if (!hasTestFramework)
|
|
2608
2472
|
recommendations.push(
|
|
2609
2473
|
"Add a testing framework (Jest, Vitest, pytest) \u2014 AI changes cannot be verified without tests"
|
|
2610
2474
|
);
|
|
2611
|
-
}
|
|
2612
2475
|
if (rawCoverageRatio < 0.3) {
|
|
2613
2476
|
const neededTests = Math.round(sourceFiles * 0.3 - testFiles);
|
|
2614
2477
|
recommendations.push(
|
|
2615
2478
|
`Add ~${neededTests} test files to reach 30% coverage ratio \u2014 minimum for safe AI assistance`
|
|
2616
2479
|
);
|
|
2617
2480
|
}
|
|
2618
|
-
if (purityScore < 50)
|
|
2481
|
+
if (purityScore < 50)
|
|
2619
2482
|
recommendations.push(
|
|
2620
2483
|
"Extract pure functions from side-effectful code \u2014 pure functions are trivially AI-testable"
|
|
2621
2484
|
);
|
|
2622
|
-
|
|
2623
|
-
if (dependencyInjectionScore < 50 && totalClasses > 0) {
|
|
2485
|
+
if (dependencyInjectionScore < 50 && totalClasses > 0)
|
|
2624
2486
|
recommendations.push(
|
|
2625
2487
|
"Adopt dependency injection \u2014 makes classes mockable and AI-generated code verifiable"
|
|
2626
2488
|
);
|
|
2627
|
-
|
|
2628
|
-
if (externalStateMutations > totalFunctions * 0.3) {
|
|
2489
|
+
if (externalStateMutations > totalFunctions * 0.3)
|
|
2629
2490
|
recommendations.push(
|
|
2630
2491
|
"Reduce direct state mutations \u2014 return values instead to improve observability"
|
|
2631
2492
|
);
|
|
2632
|
-
}
|
|
2633
2493
|
return {
|
|
2634
2494
|
score,
|
|
2635
2495
|
rating,
|
|
@@ -2644,6 +2504,8 @@ function calculateTestabilityIndex(params) {
|
|
|
2644
2504
|
recommendations
|
|
2645
2505
|
};
|
|
2646
2506
|
}
|
|
2507
|
+
|
|
2508
|
+
// src/metrics/doc-drift.ts
|
|
2647
2509
|
function calculateDocDrift(params) {
|
|
2648
2510
|
const {
|
|
2649
2511
|
uncommentedExports,
|
|
@@ -2666,21 +2528,18 @@ function calculateDocDrift(params) {
|
|
|
2666
2528
|
else if (finalScore < 85) rating = "high";
|
|
2667
2529
|
else rating = "severe";
|
|
2668
2530
|
const recommendations = [];
|
|
2669
|
-
if (outdatedComments > 0)
|
|
2531
|
+
if (outdatedComments > 0)
|
|
2670
2532
|
recommendations.push(
|
|
2671
2533
|
`Update or remove ${outdatedComments} outdated comments that contradict the code.`
|
|
2672
2534
|
);
|
|
2673
|
-
|
|
2674
|
-
if (uncommentedRatio > 0.3) {
|
|
2535
|
+
if (uncommentedRatio > 0.3)
|
|
2675
2536
|
recommendations.push(
|
|
2676
2537
|
`Add JSDoc to ${uncommentedExports} uncommented exports.`
|
|
2677
2538
|
);
|
|
2678
|
-
|
|
2679
|
-
if (undocumentedComplexity > 0) {
|
|
2539
|
+
if (undocumentedComplexity > 0)
|
|
2680
2540
|
recommendations.push(
|
|
2681
2541
|
`Explain the business logic for ${undocumentedComplexity} highly complex functions.`
|
|
2682
2542
|
);
|
|
2683
|
-
}
|
|
2684
2543
|
return {
|
|
2685
2544
|
score: finalScore,
|
|
2686
2545
|
rating,
|
|
@@ -2692,6 +2551,8 @@ function calculateDocDrift(params) {
|
|
|
2692
2551
|
recommendations
|
|
2693
2552
|
};
|
|
2694
2553
|
}
|
|
2554
|
+
|
|
2555
|
+
// src/metrics/dependency-health.ts
|
|
2695
2556
|
function calculateDependencyHealth(params) {
|
|
2696
2557
|
const {
|
|
2697
2558
|
totalPackages,
|
|
@@ -2704,8 +2565,12 @@ function calculateDependencyHealth(params) {
|
|
|
2704
2565
|
const outdatedScore = Math.max(0, 100 - outdatedRatio * 200);
|
|
2705
2566
|
const deprecatedScore = Math.max(0, 100 - deprecatedRatio * 500);
|
|
2706
2567
|
const skewScore = Math.max(0, 100 - trainingCutoffSkew * 100);
|
|
2707
|
-
const
|
|
2708
|
-
|
|
2568
|
+
const score = Math.round(
|
|
2569
|
+
Math.min(
|
|
2570
|
+
100,
|
|
2571
|
+
Math.max(0, outdatedScore * 0.3 + deprecatedScore * 0.4 + skewScore * 0.3)
|
|
2572
|
+
)
|
|
2573
|
+
);
|
|
2709
2574
|
let rating;
|
|
2710
2575
|
if (score >= 85) rating = "excellent";
|
|
2711
2576
|
else if (score >= 70) rating = "good";
|
|
@@ -2720,33 +2585,22 @@ function calculateDependencyHealth(params) {
|
|
|
2720
2585
|
else if (trainingCutoffSkew < 0.8) aiKnowledgeConfidence = "low";
|
|
2721
2586
|
else aiKnowledgeConfidence = "blind";
|
|
2722
2587
|
const recommendations = [];
|
|
2723
|
-
if (deprecatedPackages > 0)
|
|
2724
|
-
recommendations.push(
|
|
2725
|
-
|
|
2726
|
-
);
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
recommendations.push(
|
|
2730
|
-
`Update ${outdatedPackages} outdated packages to keep APIs aligned with AI training data.`
|
|
2731
|
-
);
|
|
2732
|
-
}
|
|
2733
|
-
if (trainingCutoffSkew > 0.5) {
|
|
2734
|
-
recommendations.push(
|
|
2735
|
-
"High training cutoff skew detected. AI may hallucinate APIs that were introduced recently."
|
|
2736
|
-
);
|
|
2737
|
-
}
|
|
2588
|
+
if (deprecatedPackages > 0)
|
|
2589
|
+
recommendations.push(`Replace ${deprecatedPackages} deprecated packages.`);
|
|
2590
|
+
if (outdatedRatio > 0.2)
|
|
2591
|
+
recommendations.push(`Update ${outdatedPackages} outdated packages.`);
|
|
2592
|
+
if (trainingCutoffSkew > 0.5)
|
|
2593
|
+
recommendations.push("High training cutoff skew detected.");
|
|
2738
2594
|
return {
|
|
2739
2595
|
score,
|
|
2740
2596
|
rating,
|
|
2741
|
-
dimensions: {
|
|
2742
|
-
outdatedPackages,
|
|
2743
|
-
deprecatedPackages,
|
|
2744
|
-
trainingCutoffSkew
|
|
2745
|
-
},
|
|
2597
|
+
dimensions: { outdatedPackages, deprecatedPackages, trainingCutoffSkew },
|
|
2746
2598
|
aiKnowledgeConfidence,
|
|
2747
2599
|
recommendations
|
|
2748
2600
|
};
|
|
2749
2601
|
}
|
|
2602
|
+
|
|
2603
|
+
// src/metrics/change-amplification.ts
|
|
2750
2604
|
function calculateChangeAmplification(params) {
|
|
2751
2605
|
const { files } = params;
|
|
2752
2606
|
if (files.length === 0) {
|
|
@@ -2759,15 +2613,16 @@ function calculateChangeAmplification(params) {
|
|
|
2759
2613
|
recommendations: []
|
|
2760
2614
|
};
|
|
2761
2615
|
}
|
|
2762
|
-
const hotspots = files.map((f) => {
|
|
2763
|
-
const amplificationFactor = f.fanOut + f.fanIn * 0.5;
|
|
2764
|
-
return { ...f, amplificationFactor };
|
|
2765
|
-
}).sort((a, b) => b.amplificationFactor - a.amplificationFactor);
|
|
2616
|
+
const hotspots = files.map((f) => ({ ...f, amplificationFactor: f.fanOut + f.fanIn * 0.5 })).sort((a, b) => b.amplificationFactor - a.amplificationFactor);
|
|
2766
2617
|
const maxAmplification = hotspots[0].amplificationFactor;
|
|
2767
2618
|
const avgAmplification = hotspots.reduce((sum, h) => sum + h.amplificationFactor, 0) / hotspots.length;
|
|
2768
|
-
let score =
|
|
2769
|
-
|
|
2770
|
-
|
|
2619
|
+
let score = Math.max(
|
|
2620
|
+
0,
|
|
2621
|
+
Math.min(
|
|
2622
|
+
100,
|
|
2623
|
+
100 - avgAmplification * 5 - (maxAmplification > 20 ? maxAmplification - 20 : 0)
|
|
2624
|
+
)
|
|
2625
|
+
);
|
|
2771
2626
|
let rating = "isolated";
|
|
2772
2627
|
if (score < 40) rating = "explosive";
|
|
2773
2628
|
else if (score < 70) rating = "amplified";
|
|
@@ -2775,12 +2630,12 @@ function calculateChangeAmplification(params) {
|
|
|
2775
2630
|
const recommendations = [];
|
|
2776
2631
|
if (score < 70 && hotspots.length > 0) {
|
|
2777
2632
|
recommendations.push(
|
|
2778
|
-
`Refactor top hotspot '${hotspots[0].file}' to reduce coupling
|
|
2633
|
+
`Refactor top hotspot '${hotspots[0].file}' to reduce coupling.`
|
|
2779
2634
|
);
|
|
2780
2635
|
}
|
|
2781
2636
|
if (maxAmplification > 30) {
|
|
2782
2637
|
recommendations.push(
|
|
2783
|
-
|
|
2638
|
+
"Break down key bottlenecks with amplification factor > 30."
|
|
2784
2639
|
);
|
|
2785
2640
|
}
|
|
2786
2641
|
return {
|
|
@@ -2792,6 +2647,61 @@ function calculateChangeAmplification(params) {
|
|
|
2792
2647
|
recommendations
|
|
2793
2648
|
};
|
|
2794
2649
|
}
|
|
2650
|
+
|
|
2651
|
+
// src/future-proof-metrics.ts
|
|
2652
|
+
function calculateFutureProofScore(params) {
|
|
2653
|
+
const loadScore = 100 - params.cognitiveLoad.score;
|
|
2654
|
+
const entropyScore = 100 - params.patternEntropy.entropy * 100;
|
|
2655
|
+
const cohesionScore = params.conceptCohesion.score * 100;
|
|
2656
|
+
const overall = Math.round(
|
|
2657
|
+
loadScore * 0.4 + entropyScore * 0.3 + cohesionScore * 0.3
|
|
2658
|
+
);
|
|
2659
|
+
const factors = [
|
|
2660
|
+
{
|
|
2661
|
+
name: "Cognitive Load",
|
|
2662
|
+
impact: Math.round(loadScore - 50),
|
|
2663
|
+
description: params.cognitiveLoad.rating
|
|
2664
|
+
},
|
|
2665
|
+
{
|
|
2666
|
+
name: "Pattern Entropy",
|
|
2667
|
+
impact: Math.round(entropyScore - 50),
|
|
2668
|
+
description: params.patternEntropy.rating
|
|
2669
|
+
},
|
|
2670
|
+
{
|
|
2671
|
+
name: "Concept Cohesion",
|
|
2672
|
+
impact: Math.round(cohesionScore - 50),
|
|
2673
|
+
description: params.conceptCohesion.rating
|
|
2674
|
+
}
|
|
2675
|
+
];
|
|
2676
|
+
const recommendations = [];
|
|
2677
|
+
for (const rec of params.patternEntropy.recommendations) {
|
|
2678
|
+
recommendations.push({
|
|
2679
|
+
action: rec,
|
|
2680
|
+
estimatedImpact: 5,
|
|
2681
|
+
priority: "medium"
|
|
2682
|
+
});
|
|
2683
|
+
}
|
|
2684
|
+
if (params.conceptCohesion.rating === "poor") {
|
|
2685
|
+
recommendations.push({
|
|
2686
|
+
action: "Improve concept cohesion by grouping related exports",
|
|
2687
|
+
estimatedImpact: 8,
|
|
2688
|
+
priority: "high"
|
|
2689
|
+
});
|
|
2690
|
+
}
|
|
2691
|
+
const semanticDistanceAvg = params.semanticDistances?.length ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
|
|
2692
|
+
return {
|
|
2693
|
+
toolName: "future-proof",
|
|
2694
|
+
score: overall,
|
|
2695
|
+
rawMetrics: {
|
|
2696
|
+
cognitiveLoadScore: params.cognitiveLoad.score,
|
|
2697
|
+
entropyScore: params.patternEntropy.entropy,
|
|
2698
|
+
cohesionScore: params.conceptCohesion.score,
|
|
2699
|
+
semanticDistanceAvg
|
|
2700
|
+
},
|
|
2701
|
+
factors,
|
|
2702
|
+
recommendations
|
|
2703
|
+
};
|
|
2704
|
+
}
|
|
2795
2705
|
function calculateExtendedFutureProofScore(params) {
|
|
2796
2706
|
const loadScore = 100 - params.cognitiveLoad.score;
|
|
2797
2707
|
const entropyScore = 100 - params.patternEntropy.entropy * 100;
|
|
@@ -2800,7 +2710,7 @@ function calculateExtendedFutureProofScore(params) {
|
|
|
2800
2710
|
const groundingScore = params.agentGrounding.score;
|
|
2801
2711
|
const testabilityScore = params.testability.score;
|
|
2802
2712
|
const docDriftScore = params.docDrift ? 100 - params.docDrift.score : 100;
|
|
2803
|
-
const depsHealthScore = params.dependencyHealth
|
|
2713
|
+
const depsHealthScore = params.dependencyHealth?.score ?? 100;
|
|
2804
2714
|
let totalWeight = 0.8;
|
|
2805
2715
|
let overall = loadScore * 0.15 + entropyScore * 0.1 + cohesionScore * 0.1 + aiSignalClarityScore * 0.15 + groundingScore * 0.15 + testabilityScore * 0.15;
|
|
2806
2716
|
if (params.docDrift) {
|
|
@@ -2831,7 +2741,7 @@ function calculateExtendedFutureProofScore(params) {
|
|
|
2831
2741
|
{
|
|
2832
2742
|
name: "AI Signal Clarity",
|
|
2833
2743
|
impact: Math.round(aiSignalClarityScore - 50),
|
|
2834
|
-
description: `${params.aiSignalClarity.rating} risk
|
|
2744
|
+
description: `${params.aiSignalClarity.rating} risk`
|
|
2835
2745
|
},
|
|
2836
2746
|
{
|
|
2837
2747
|
name: "Agent Grounding",
|
|
@@ -2841,60 +2751,25 @@ function calculateExtendedFutureProofScore(params) {
|
|
|
2841
2751
|
{
|
|
2842
2752
|
name: "Testability",
|
|
2843
2753
|
impact: Math.round(testabilityScore - 50),
|
|
2844
|
-
description:
|
|
2754
|
+
description: params.testability.rating
|
|
2845
2755
|
}
|
|
2846
2756
|
];
|
|
2847
2757
|
if (params.docDrift) {
|
|
2848
2758
|
factors.push({
|
|
2849
2759
|
name: "Documentation Drift",
|
|
2850
2760
|
impact: Math.round(docDriftScore - 50),
|
|
2851
|
-
description:
|
|
2761
|
+
description: params.docDrift.rating
|
|
2852
2762
|
});
|
|
2853
2763
|
}
|
|
2854
2764
|
if (params.dependencyHealth) {
|
|
2855
2765
|
factors.push({
|
|
2856
2766
|
name: "Dependency Health",
|
|
2857
2767
|
impact: Math.round(depsHealthScore - 50),
|
|
2858
|
-
description:
|
|
2859
|
-
});
|
|
2860
|
-
}
|
|
2861
|
-
const recommendations = [];
|
|
2862
|
-
for (const rec of params.aiSignalClarity.recommendations) {
|
|
2863
|
-
recommendations.push({ action: rec, estimatedImpact: 8, priority: "high" });
|
|
2864
|
-
}
|
|
2865
|
-
for (const rec of params.agentGrounding.recommendations) {
|
|
2866
|
-
recommendations.push({
|
|
2867
|
-
action: rec,
|
|
2868
|
-
estimatedImpact: 6,
|
|
2869
|
-
priority: "medium"
|
|
2768
|
+
description: params.dependencyHealth.rating
|
|
2870
2769
|
});
|
|
2871
2770
|
}
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
recommendations.push({ action: rec, estimatedImpact: 10, priority });
|
|
2875
|
-
}
|
|
2876
|
-
for (const rec of params.patternEntropy.recommendations) {
|
|
2877
|
-
recommendations.push({ action: rec, estimatedImpact: 5, priority: "low" });
|
|
2878
|
-
}
|
|
2879
|
-
if (params.docDrift) {
|
|
2880
|
-
for (const rec of params.docDrift.recommendations) {
|
|
2881
|
-
recommendations.push({
|
|
2882
|
-
action: rec,
|
|
2883
|
-
estimatedImpact: 8,
|
|
2884
|
-
priority: "high"
|
|
2885
|
-
});
|
|
2886
|
-
}
|
|
2887
|
-
}
|
|
2888
|
-
if (params.dependencyHealth) {
|
|
2889
|
-
for (const rec of params.dependencyHealth.recommendations) {
|
|
2890
|
-
recommendations.push({
|
|
2891
|
-
action: rec,
|
|
2892
|
-
estimatedImpact: 7,
|
|
2893
|
-
priority: "medium"
|
|
2894
|
-
});
|
|
2895
|
-
}
|
|
2896
|
-
}
|
|
2897
|
-
const semanticDistanceAvg = params.semanticDistances && params.semanticDistances.length > 0 ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
|
|
2771
|
+
const recommendations = collectFutureProofRecommendations(params);
|
|
2772
|
+
const semanticDistanceAvg = params.semanticDistances?.length ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
|
|
2898
2773
|
return {
|
|
2899
2774
|
toolName: "future-proof",
|
|
2900
2775
|
score: overall,
|
|
@@ -3071,16 +2946,19 @@ function getRepoMetadata(directory) {
|
|
|
3071
2946
|
ParseError,
|
|
3072
2947
|
ParserFactory,
|
|
3073
2948
|
PythonParser,
|
|
2949
|
+
SEVERITY_TIME_ESTIMATES,
|
|
3074
2950
|
SIZE_ADJUSTED_THRESHOLDS,
|
|
3075
2951
|
TOOL_NAME_MAP,
|
|
3076
2952
|
TypeScriptParser,
|
|
3077
2953
|
VAGUE_FILE_NAMES,
|
|
3078
2954
|
calculateAgentGrounding,
|
|
3079
2955
|
calculateAiSignalClarity,
|
|
2956
|
+
calculateBusinessROI,
|
|
3080
2957
|
calculateChangeAmplification,
|
|
3081
2958
|
calculateCognitiveLoad,
|
|
3082
2959
|
calculateComprehensionDifficulty,
|
|
3083
2960
|
calculateConceptCohesion,
|
|
2961
|
+
calculateDebtInterest,
|
|
3084
2962
|
calculateDependencyHealth,
|
|
3085
2963
|
calculateDocDrift,
|
|
3086
2964
|
calculateExtendedFutureProofScore,
|
|
@@ -3091,10 +2969,8 @@ function getRepoMetadata(directory) {
|
|
|
3091
2969
|
calculateOverallScore,
|
|
3092
2970
|
calculatePatternEntropy,
|
|
3093
2971
|
calculateProductivityImpact,
|
|
3094
|
-
calculateRemediationVelocity,
|
|
3095
|
-
calculateScoreTrend,
|
|
3096
2972
|
calculateSemanticDistance,
|
|
3097
|
-
|
|
2973
|
+
calculateTechnicalValueChain,
|
|
3098
2974
|
calculateTestabilityIndex,
|
|
3099
2975
|
calculateTokenBudget,
|
|
3100
2976
|
clearHistory,
|
|
@@ -3110,7 +2986,6 @@ function getRepoMetadata(directory) {
|
|
|
3110
2986
|
formatToolScore,
|
|
3111
2987
|
generateHTML,
|
|
3112
2988
|
generateValueChain,
|
|
3113
|
-
getDebtBreakdown,
|
|
3114
2989
|
getElapsedTime,
|
|
3115
2990
|
getFileCommitTimestamps,
|
|
3116
2991
|
getFileExtension,
|
|
@@ -3124,6 +2999,9 @@ function getRepoMetadata(directory) {
|
|
|
3124
2999
|
getRatingWithContext,
|
|
3125
3000
|
getRecommendedThreshold,
|
|
3126
3001
|
getRepoMetadata,
|
|
3002
|
+
getSafetyIcon,
|
|
3003
|
+
getScoreBar,
|
|
3004
|
+
getSeverityColor,
|
|
3127
3005
|
getSupportedLanguages,
|
|
3128
3006
|
getToolWeight,
|
|
3129
3007
|
handleCLIError,
|