@aiready/core 0.9.38 → 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/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
- calculateTechnicalDebtInterest: () => calculateTechnicalDebtInterest,
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,
@@ -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) {
@@ -999,7 +1037,7 @@ function formatToolScore(output) {
999
1037
  return result;
1000
1038
  }
1001
1039
 
1002
- // src/business-metrics.ts
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,
@@ -1118,7 +1143,6 @@ function calculateTokenBudget(params) {
1118
1143
  },
1119
1144
  efficiencyRatio: Math.round(efficiencyRatio * 100) / 100,
1120
1145
  potentialRetrievableTokens: Math.round(totalWaste * 0.8)
1121
- // Heuristic: 80% is fixable
1122
1146
  };
1123
1147
  }
1124
1148
  function estimateCostFromBudget(budget, model, config = {}) {
@@ -1142,6 +1166,15 @@ function estimateCostFromBudget(budget, model, config = {}) {
1142
1166
  confidence
1143
1167
  };
1144
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;
1145
1178
  function calculateProductivityImpact(issues, hourlyRate = DEFAULT_HOURLY_RATE) {
1146
1179
  const counts = {
1147
1180
  critical: issues.filter((i) => i.severity === "critical").length,
@@ -1182,108 +1215,158 @@ function predictAcceptanceRate(toolOutputs) {
1182
1215
  const baseRate = 0.3;
1183
1216
  const patterns = toolOutputs.get("pattern-detect");
1184
1217
  if (patterns) {
1185
- const patternImpact = (patterns.score - 50) * 3e-3;
1186
1218
  factors.push({
1187
1219
  name: "Semantic Duplication",
1188
- impact: Math.round(patternImpact * 100)
1220
+ impact: Math.round((patterns.score - 50) * 3e-3 * 100)
1189
1221
  });
1190
1222
  }
1191
1223
  const context = toolOutputs.get("context-analyzer");
1192
1224
  if (context) {
1193
- const contextImpact = (context.score - 50) * 4e-3;
1194
1225
  factors.push({
1195
1226
  name: "Context Efficiency",
1196
- impact: Math.round(contextImpact * 100)
1227
+ impact: Math.round((context.score - 50) * 4e-3 * 100)
1197
1228
  });
1198
1229
  }
1199
1230
  const consistency = toolOutputs.get("consistency");
1200
1231
  if (consistency) {
1201
- const consistencyImpact = (consistency.score - 50) * 2e-3;
1202
1232
  factors.push({
1203
1233
  name: "Code Consistency",
1204
- impact: Math.round(consistencyImpact * 100)
1234
+ impact: Math.round((consistency.score - 50) * 2e-3 * 100)
1205
1235
  });
1206
1236
  }
1207
1237
  const aiSignalClarity = toolOutputs.get("ai-signal-clarity");
1208
1238
  if (aiSignalClarity) {
1209
- const hrImpact = (50 - aiSignalClarity.score) * 2e-3;
1210
1239
  factors.push({
1211
1240
  name: "AI Signal Clarity",
1212
- impact: Math.round(hrImpact * 100)
1241
+ impact: Math.round((50 - aiSignalClarity.score) * 2e-3 * 100)
1213
1242
  });
1214
1243
  }
1215
1244
  const totalImpact = factors.reduce((sum, f) => sum + f.impact / 100, 0);
1216
1245
  const rate = Math.max(0.05, Math.min(0.8, baseRate + totalImpact));
1217
- let confidence;
1246
+ let confidence = 0.35;
1218
1247
  if (toolOutputs.size >= 4) confidence = 0.75;
1219
1248
  else if (toolOutputs.size >= 3) confidence = 0.65;
1220
1249
  else if (toolOutputs.size >= 2) confidence = 0.5;
1221
- else confidence = 0.35;
1222
- return {
1223
- rate: Math.round(rate * 100) / 100,
1224
- confidence,
1225
- factors
1226
- };
1250
+ return { rate: Math.round(rate * 100) / 100, confidence, factors };
1227
1251
  }
1228
- function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentation, consistencyScore, totalFiles, modelTier = "standard") {
1229
- const tierThresholds = CONTEXT_TIER_THRESHOLDS[modelTier];
1230
- const idealBudget = tierThresholds.idealTokens;
1231
- const criticalBudget = tierThresholds.criticalTokens;
1232
- const idealDepth = tierThresholds.idealDepth;
1233
- const budgetRange = criticalBudget - idealBudget;
1234
- const budgetFactor = Math.min(
1235
- 100,
1236
- Math.max(0, (contextBudget - idealBudget) / budgetRange * 100)
1237
- );
1238
- const depthFactor = Math.min(
1239
- 100,
1240
- Math.max(0, (importDepth - idealDepth) * 10)
1241
- );
1242
- const fragmentationFactor = Math.min(
1243
- 100,
1244
- Math.max(0, (fragmentation - 0.3) * 250)
1245
- );
1246
- const consistencyFactor = Math.min(100, Math.max(0, 100 - consistencyScore));
1247
- 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;
1248
1257
  const score = Math.round(
1249
- budgetFactor * 0.35 + depthFactor * 0.2 + fragmentationFactor * 0.2 + consistencyFactor * 0.15 + fileFactor * 0.1
1258
+ Math.min(
1259
+ 100,
1260
+ concentrationRatio * 100 + orphanFiles / Math.max(1, totalFiles) * 20
1261
+ )
1250
1262
  );
1251
1263
  let rating;
1252
- if (score < 20) rating = "trivial";
1253
- else if (score < 40) rating = "easy";
1254
- else if (score < 60) rating = "moderate";
1255
- else if (score < 80) rating = "difficult";
1256
- else rating = "expert";
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
+ );
1257
1277
  return {
1258
1278
  score,
1259
1279
  rating,
1260
- factors: [
1261
- {
1262
- name: "Context Budget",
1263
- contribution: Math.round(budgetFactor * 0.35),
1264
- description: `${Math.round(contextBudget)} tokens required (${modelTier} model tier: ideal <${idealBudget.toLocaleString()})`
1265
- },
1266
- {
1267
- name: "Import Depth",
1268
- contribution: Math.round(depthFactor * 0.2),
1269
- description: `${importDepth.toFixed(1)} average levels (ideal <${idealDepth} for ${modelTier})`
1270
- },
1271
- {
1272
- name: "Code Fragmentation",
1273
- contribution: Math.round(fragmentationFactor * 0.2),
1274
- description: `${(fragmentation * 100).toFixed(0)}% fragmentation`
1275
- },
1276
- {
1277
- name: "Consistency",
1278
- contribution: Math.round(consistencyFactor * 0.15),
1279
- description: `${consistencyScore}/100 consistency score`
1280
- },
1281
- {
1282
- name: "Project Scale",
1283
- contribution: Math.round(fileFactor * 0.1),
1284
- description: `${totalFiles} files analyzed`
1285
- }
1286
- ]
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)
1287
1370
  };
1288
1371
  }
1289
1372
  function formatCost(cost) {
@@ -1309,220 +1392,6 @@ function formatHours(hours) {
1309
1392
  function formatAcceptanceRate(rate) {
1310
1393
  return `${Math.round(rate * 100)}%`;
1311
1394
  }
1312
- function calculateScoreTrend(history) {
1313
- if (history.length < 2) {
1314
- return {
1315
- direction: "stable",
1316
- change30Days: 0,
1317
- change90Days: 0,
1318
- velocity: 0,
1319
- projectedScore: history[0]?.overallScore || 100
1320
- };
1321
- }
1322
- const now = /* @__PURE__ */ new Date();
1323
- const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1e3);
1324
- const ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1e3);
1325
- const last30Days = history.filter(
1326
- (e) => new Date(e.timestamp) >= thirtyDaysAgo
1327
- );
1328
- const last90Days = history.filter(
1329
- (e) => new Date(e.timestamp) >= ninetyDaysAgo
1330
- );
1331
- const currentScore = history[history.length - 1].overallScore;
1332
- const thirtyDaysAgoScore = last30Days[0]?.overallScore || currentScore;
1333
- const ninetyDaysAgoScore = last90Days[0]?.overallScore || thirtyDaysAgoScore;
1334
- const change30Days = currentScore - thirtyDaysAgoScore;
1335
- const change90Days = currentScore - ninetyDaysAgoScore;
1336
- const weeksOfData = Math.max(1, history.length / 7);
1337
- const totalChange = currentScore - history[0].overallScore;
1338
- const velocity = totalChange / weeksOfData;
1339
- let direction;
1340
- if (change30Days > 3) direction = "improving";
1341
- else if (change30Days < -3) direction = "degrading";
1342
- else direction = "stable";
1343
- const projectedScore = Math.max(
1344
- 0,
1345
- Math.min(100, currentScore + velocity * 4)
1346
- );
1347
- return {
1348
- direction,
1349
- change30Days,
1350
- change90Days,
1351
- velocity: Math.round(velocity * 10) / 10,
1352
- projectedScore: Math.round(projectedScore)
1353
- };
1354
- }
1355
- function calculateRemediationVelocity(history, currentIssues) {
1356
- if (history.length < 2) {
1357
- return {
1358
- issuesFixedThisWeek: 0,
1359
- avgIssuesPerWeek: 0,
1360
- trend: "stable",
1361
- estimatedCompletionWeeks: currentIssues > 0 ? Infinity : 0
1362
- };
1363
- }
1364
- const now = /* @__PURE__ */ new Date();
1365
- const oneWeekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3);
1366
- const twoWeeksAgo = new Date(now.getTime() - 14 * 24 * 60 * 60 * 1e3);
1367
- const thisWeek = history.filter((e) => new Date(e.timestamp) >= oneWeekAgo);
1368
- const lastWeek = history.filter(
1369
- (e) => new Date(e.timestamp) >= twoWeeksAgo && new Date(e.timestamp) < oneWeekAgo
1370
- );
1371
- const issuesFixedThisWeek = thisWeek.length > 1 ? thisWeek[0].totalIssues - thisWeek[thisWeek.length - 1].totalIssues : 0;
1372
- const totalIssuesFixed = history[0].totalIssues - history[history.length - 1].totalIssues;
1373
- const weeksOfData = Math.max(1, history.length / 7);
1374
- const avgIssuesPerWeek = totalIssuesFixed / weeksOfData;
1375
- let trend;
1376
- if (lastWeek.length > 1) {
1377
- const lastWeekFixed = lastWeek[0].totalIssues - lastWeek[lastWeek.length - 1].totalIssues;
1378
- if (issuesFixedThisWeek > lastWeekFixed * 1.2) trend = "accelerating";
1379
- else if (issuesFixedThisWeek < lastWeekFixed * 0.8) trend = "decelerating";
1380
- else trend = "stable";
1381
- } else {
1382
- trend = "stable";
1383
- }
1384
- const estimatedCompletionWeeks = avgIssuesPerWeek > 0 ? Math.ceil(currentIssues / avgIssuesPerWeek) : Infinity;
1385
- return {
1386
- issuesFixedThisWeek: Math.max(0, issuesFixedThisWeek),
1387
- avgIssuesPerWeek: Math.round(avgIssuesPerWeek * 10) / 10,
1388
- trend,
1389
- estimatedCompletionWeeks
1390
- };
1391
- }
1392
- function calculateKnowledgeConcentration(files, authorData) {
1393
- if (files.length === 0) {
1394
- return {
1395
- score: 0,
1396
- rating: "low",
1397
- analysis: {
1398
- uniqueConceptFiles: 0,
1399
- totalFiles: 0,
1400
- concentrationRatio: 0,
1401
- singleAuthorFiles: 0,
1402
- orphanFiles: 0
1403
- },
1404
- recommendations: ["No files to analyze"]
1405
- };
1406
- }
1407
- const orphanFiles = files.filter(
1408
- (f) => f.exports < 2 && f.imports < 2
1409
- ).length;
1410
- const avgExports = files.reduce((sum, f) => sum + f.exports, 0) / files.length;
1411
- const uniqueConceptFiles = files.filter(
1412
- (f) => f.exports > avgExports * 2
1413
- ).length;
1414
- const totalExports = files.reduce((sum, f) => sum + f.exports, 0);
1415
- const concentrationRatio = totalExports > 0 ? uniqueConceptFiles / files.length : 0;
1416
- let singleAuthorFiles = 0;
1417
- if (authorData) {
1418
- for (const files2 of authorData.values()) {
1419
- if (files2.length === 1) singleAuthorFiles++;
1420
- }
1421
- }
1422
- const orphanRisk = orphanFiles / files.length * 30;
1423
- const uniqueRisk = concentrationRatio * 40;
1424
- const singleAuthorRisk = authorData ? singleAuthorFiles / files.length * 30 : 0;
1425
- const score = Math.min(
1426
- 100,
1427
- Math.round(orphanRisk + uniqueRisk + singleAuthorRisk)
1428
- );
1429
- let rating;
1430
- if (score < 20) rating = "low";
1431
- else if (score < 40) rating = "moderate";
1432
- else if (score < 70) rating = "high";
1433
- else rating = "critical";
1434
- const recommendations = [];
1435
- if (orphanFiles > files.length * 0.2) {
1436
- recommendations.push(
1437
- `Reduce ${orphanFiles} orphan files by connecting them to main modules`
1438
- );
1439
- }
1440
- if (uniqueConceptFiles > files.length * 0.1) {
1441
- recommendations.push(
1442
- "Distribute high-export files into more focused modules"
1443
- );
1444
- }
1445
- if (authorData && singleAuthorFiles > files.length * 0.3) {
1446
- recommendations.push(
1447
- "Increase knowledge sharing to reduce single-author dependencies"
1448
- );
1449
- }
1450
- return {
1451
- score,
1452
- rating,
1453
- analysis: {
1454
- uniqueConceptFiles,
1455
- totalFiles: files.length,
1456
- concentrationRatio: Math.round(concentrationRatio * 100) / 100,
1457
- singleAuthorFiles,
1458
- orphanFiles
1459
- },
1460
- recommendations
1461
- };
1462
- }
1463
- function calculateTechnicalDebtInterest(params) {
1464
- const { currentMonthlyCost, issues, monthsOpen } = params;
1465
- const criticalCount = issues.filter((i) => i.severity === "critical").length;
1466
- const majorCount = issues.filter((i) => i.severity === "major").length;
1467
- const minorCount = issues.filter((i) => i.severity === "minor").length;
1468
- const severityWeight = (criticalCount * 3 + majorCount * 2 + minorCount * 1) / Math.max(1, issues.length);
1469
- const baseRate = 0.02 + severityWeight * 0.01;
1470
- const timeMultiplier = Math.max(1, 1 + monthsOpen * 0.1);
1471
- const monthlyRate = baseRate * timeMultiplier;
1472
- const projectDebt = (principal2, months) => {
1473
- let debt = principal2;
1474
- for (let i = 0; i < months; i++) {
1475
- debt = debt * (1 + monthlyRate);
1476
- }
1477
- return Math.round(debt);
1478
- };
1479
- const principal = currentMonthlyCost * 12;
1480
- const projections = {
1481
- months6: projectDebt(principal, 6),
1482
- months12: projectDebt(principal, 12),
1483
- months24: projectDebt(principal, 24)
1484
- };
1485
- return {
1486
- monthlyRate: Math.round(monthlyRate * 1e4) / 100,
1487
- annualRate: Math.round((Math.pow(1 + monthlyRate, 12) - 1) * 1e4) / 100,
1488
- principal,
1489
- projections,
1490
- monthlyCost: Math.round(currentMonthlyCost * (1 + monthlyRate) * 100) / 100
1491
- };
1492
- }
1493
- function getDebtBreakdown(patternCost, contextCost, consistencyCost) {
1494
- const breakdowns = [
1495
- {
1496
- category: "Semantic Duplication",
1497
- currentCost: patternCost,
1498
- monthlyGrowthRate: 5,
1499
- // Grows as devs copy-paste
1500
- priority: patternCost > 1e3 ? "high" : "medium",
1501
- fixCost: patternCost * 3
1502
- // Fixing costs 3x current waste
1503
- },
1504
- {
1505
- category: "Context Fragmentation",
1506
- currentCost: contextCost,
1507
- monthlyGrowthRate: 3,
1508
- // Grows with new features
1509
- priority: contextCost > 500 ? "high" : "medium",
1510
- fixCost: contextCost * 2.5
1511
- },
1512
- {
1513
- category: "Consistency Issues",
1514
- currentCost: consistencyCost,
1515
- monthlyGrowthRate: 2,
1516
- // Grows with new devs
1517
- priority: consistencyCost > 200 ? "medium" : "low",
1518
- fixCost: consistencyCost * 1.5
1519
- }
1520
- ];
1521
- return breakdowns.sort((a, b) => {
1522
- const priorityOrder = { high: 0, medium: 1, low: 2 };
1523
- return priorityOrder[a.priority] - priorityOrder[b.priority];
1524
- });
1525
- }
1526
1395
  function generateValueChain(params) {
1527
1396
  const { issueType, count, severity } = params;
1528
1397
  const impacts = {
@@ -1562,9 +1431,7 @@ function generateValueChain(params) {
1562
1431
  },
1563
1432
  businessOutcome: {
1564
1433
  directCost: count * 12,
1565
- // Placeholder linear cost
1566
1434
  opportunityCost: productivityLoss * 15e3,
1567
- // Monthly avg dev cost $15k
1568
1435
  riskLevel: impact.risk
1569
1436
  }
1570
1437
  };
@@ -2079,7 +1946,55 @@ function getSupportedLanguages() {
2079
1946
  return ParserFactory.getInstance().getSupportedLanguages();
2080
1947
  }
2081
1948
 
2082
- // src/future-proof-metrics.ts
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
2083
1998
  function calculateCognitiveLoad(params) {
2084
1999
  const {
2085
2000
  linesOfCode,
@@ -2138,13 +2053,20 @@ function calculateCognitiveLoad(params) {
2138
2053
  }
2139
2054
  };
2140
2055
  }
2056
+
2057
+ // src/metrics/semantic-distance.ts
2141
2058
  function calculateSemanticDistance(params) {
2142
- const { file1, file2, file1Domain, file2Domain, sharedDependencies } = params;
2059
+ const {
2060
+ file1,
2061
+ file2,
2062
+ file1Domain,
2063
+ file2Domain,
2064
+ sharedDependencies,
2065
+ file1Imports,
2066
+ file2Imports
2067
+ } = params;
2143
2068
  const domainDistance = file1Domain === file2Domain ? 0 : file1Domain && file2Domain ? 0.5 : 0.8;
2144
- const importOverlap = sharedDependencies.length / Math.max(
2145
- 1,
2146
- Math.min(params.file1Imports.length, params.file2Imports.length)
2147
- );
2069
+ const importOverlap = sharedDependencies.length / Math.max(1, Math.min(file1Imports.length, file2Imports.length));
2148
2070
  const importDistance = 1 - importOverlap;
2149
2071
  const distance = domainDistance * 0.4 + importDistance * 0.3 + (sharedDependencies.length > 0 ? 0 : 0.3);
2150
2072
  let relationship;
@@ -2163,6 +2085,8 @@ function calculateSemanticDistance(params) {
2163
2085
  reason: relationship === "same-domain" ? `Both in "${file1Domain}" domain` : relationship === "cross-domain" ? `Share ${sharedDependencies.length} dependency(ies)` : "No strong semantic relationship detected"
2164
2086
  };
2165
2087
  }
2088
+
2089
+ // src/metrics/structural-metrics.ts
2166
2090
  function calculatePatternEntropy(files) {
2167
2091
  if (files.length === 0) {
2168
2092
  return {
@@ -2212,23 +2136,18 @@ function calculatePatternEntropy(files) {
2212
2136
  else if (normalizedEntropy < 0.8) rating = "fragmented";
2213
2137
  else rating = "chaotic";
2214
2138
  const recommendations = [];
2215
- if (normalizedEntropy > 0.5) {
2139
+ if (normalizedEntropy > 0.5)
2216
2140
  recommendations.push(
2217
2141
  `Consolidate ${files.length} files into fewer directories by domain`
2218
2142
  );
2219
- }
2220
- if (dirGroups.size > 5) {
2143
+ if (dirGroups.size > 5)
2221
2144
  recommendations.push(
2222
2145
  "Consider barrel exports to reduce directory navigation"
2223
2146
  );
2224
- }
2225
- if (gini > 0.5) {
2147
+ if (gini > 0.5)
2226
2148
  recommendations.push("Redistribute files more evenly across directories");
2227
- }
2228
- const firstFile = files.length > 0 ? files[0] : null;
2229
- const domainValue = firstFile ? firstFile.domain : "mixed";
2230
2149
  return {
2231
- domain: domainValue,
2150
+ domain: files[0]?.domain || "mixed",
2232
2151
  entropy: Math.round(normalizedEntropy * 100) / 100,
2233
2152
  rating,
2234
2153
  distribution: {
@@ -2259,9 +2178,8 @@ function calculateConceptCohesion(params) {
2259
2178
  }
2260
2179
  const uniqueDomains = new Set(allDomains);
2261
2180
  const domainCounts = /* @__PURE__ */ new Map();
2262
- for (const d of allDomains) {
2181
+ for (const d of allDomains)
2263
2182
  domainCounts.set(d, (domainCounts.get(d) || 0) + 1);
2264
- }
2265
2183
  const maxCount = Math.max(...Array.from(domainCounts.values()), 1);
2266
2184
  const domainConcentration = maxCount / allDomains.length;
2267
2185
  const exportPurposeClarity = 1 - (uniqueDomains.size - 1) / Math.max(1, exports2.length);
@@ -2281,59 +2199,8 @@ function calculateConceptCohesion(params) {
2281
2199
  }
2282
2200
  };
2283
2201
  }
2284
- function calculateFutureProofScore(params) {
2285
- const loadScore = 100 - params.cognitiveLoad.score;
2286
- const entropyScore = 100 - params.patternEntropy.entropy * 100;
2287
- const cohesionScore = params.conceptCohesion.score * 100;
2288
- const overall = Math.round(
2289
- loadScore * 0.4 + entropyScore * 0.3 + cohesionScore * 0.3
2290
- );
2291
- const factors = [
2292
- {
2293
- name: "Cognitive Load",
2294
- impact: Math.round(loadScore - 50),
2295
- description: params.cognitiveLoad.rating
2296
- },
2297
- {
2298
- name: "Pattern Entropy",
2299
- impact: Math.round(entropyScore - 50),
2300
- description: params.patternEntropy.rating
2301
- },
2302
- {
2303
- name: "Concept Cohesion",
2304
- impact: Math.round(cohesionScore - 50),
2305
- description: params.conceptCohesion.rating
2306
- }
2307
- ];
2308
- const recommendations = [];
2309
- for (const rec of params.patternEntropy.recommendations) {
2310
- recommendations.push({
2311
- action: rec,
2312
- estimatedImpact: 5,
2313
- priority: "medium"
2314
- });
2315
- }
2316
- if (params.conceptCohesion.rating === "poor") {
2317
- recommendations.push({
2318
- action: "Improve concept cohesion by grouping related exports",
2319
- estimatedImpact: 8,
2320
- priority: "high"
2321
- });
2322
- }
2323
- const semanticDistanceAvg = params.semanticDistances && params.semanticDistances.length > 0 ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
2324
- return {
2325
- toolName: "future-proof",
2326
- score: overall,
2327
- rawMetrics: {
2328
- cognitiveLoadScore: params.cognitiveLoad.score,
2329
- entropyScore: params.patternEntropy.entropy,
2330
- cohesionScore: params.conceptCohesion.score,
2331
- semanticDistanceAvg
2332
- },
2333
- factors,
2334
- recommendations
2335
- };
2336
- }
2202
+
2203
+ // src/metrics/ai-signal-clarity.ts
2337
2204
  function calculateAiSignalClarity(params) {
2338
2205
  const {
2339
2206
  overloadedSymbols,
@@ -2355,75 +2222,53 @@ function calculateAiSignalClarity(params) {
2355
2222
  recommendations: []
2356
2223
  };
2357
2224
  }
2358
- const overloadRatio = Math.min(
2359
- 1,
2360
- overloadedSymbols / Math.max(1, totalSymbols)
2361
- );
2225
+ const overloadRatio = overloadedSymbols / Math.max(1, totalSymbols);
2362
2226
  const overloadSignal = {
2363
2227
  name: "Symbol Overloading",
2364
2228
  count: overloadedSymbols,
2365
- riskContribution: Math.round(overloadRatio * 100 * 0.25),
2366
- // 25% weight
2229
+ riskContribution: Math.round(Math.min(1, overloadRatio) * 100 * 0.25),
2367
2230
  description: `${overloadedSymbols} overloaded symbols \u2014 AI picks wrong signature`
2368
2231
  };
2369
- const magicRatio = Math.min(1, magicLiterals / Math.max(1, totalSymbols * 2));
2232
+ const magicRatio = magicLiterals / Math.max(1, totalSymbols * 2);
2370
2233
  const magicSignal = {
2371
2234
  name: "Magic Literals",
2372
2235
  count: magicLiterals,
2373
- riskContribution: Math.round(magicRatio * 100 * 0.2),
2374
- // 20% weight
2236
+ riskContribution: Math.round(Math.min(1, magicRatio) * 100 * 0.2),
2375
2237
  description: `${magicLiterals} unnamed constants \u2014 AI invents wrong values`
2376
2238
  };
2377
- const trapRatio = Math.min(1, booleanTraps / Math.max(1, totalSymbols));
2239
+ const trapRatio = booleanTraps / Math.max(1, totalSymbols);
2378
2240
  const trapSignal = {
2379
2241
  name: "Boolean Traps",
2380
2242
  count: booleanTraps,
2381
- riskContribution: Math.round(trapRatio * 100 * 0.2),
2382
- // 20% weight
2243
+ riskContribution: Math.round(Math.min(1, trapRatio) * 100 * 0.2),
2383
2244
  description: `${booleanTraps} boolean trap parameters \u2014 AI inverts intent`
2384
2245
  };
2385
- const sideEffectRatio = Math.min(
2386
- 1,
2387
- implicitSideEffects / Math.max(1, totalExports)
2388
- );
2246
+ const sideEffectRatio = implicitSideEffects / Math.max(1, totalExports);
2389
2247
  const sideEffectSignal = {
2390
2248
  name: "Implicit Side Effects",
2391
2249
  count: implicitSideEffects,
2392
- riskContribution: Math.round(sideEffectRatio * 100 * 0.15),
2393
- // 15% weight
2250
+ riskContribution: Math.round(Math.min(1, sideEffectRatio) * 100 * 0.15),
2394
2251
  description: `${implicitSideEffects} functions with implicit side effects \u2014 AI misses contracts`
2395
2252
  };
2396
- const callbackRatio = Math.min(
2397
- 1,
2398
- deepCallbacks / Math.max(1, totalSymbols * 0.1)
2399
- );
2253
+ const callbackRatio = deepCallbacks / Math.max(1, totalSymbols * 0.1);
2400
2254
  const callbackSignal = {
2401
2255
  name: "Callback Nesting",
2402
2256
  count: deepCallbacks,
2403
- riskContribution: Math.round(callbackRatio * 100 * 0.1),
2404
- // 10% weight
2257
+ riskContribution: Math.round(Math.min(1, callbackRatio) * 100 * 0.1),
2405
2258
  description: `${deepCallbacks} deep callback chains \u2014 AI loses control flow context`
2406
2259
  };
2407
- const ambiguousRatio = Math.min(
2408
- 1,
2409
- ambiguousNames / Math.max(1, totalSymbols)
2410
- );
2260
+ const ambiguousRatio = ambiguousNames / Math.max(1, totalSymbols);
2411
2261
  const ambiguousSignal = {
2412
2262
  name: "Ambiguous Names",
2413
2263
  count: ambiguousNames,
2414
- riskContribution: Math.round(ambiguousRatio * 100 * 0.1),
2415
- // 10% weight
2264
+ riskContribution: Math.round(Math.min(1, ambiguousRatio) * 100 * 0.1),
2416
2265
  description: `${ambiguousNames} non-descriptive identifiers \u2014 AI guesses wrong intent`
2417
2266
  };
2418
- const undocRatio = Math.min(
2419
- 1,
2420
- undocumentedExports / Math.max(1, totalExports)
2421
- );
2267
+ const undocRatio = undocumentedExports / Math.max(1, totalExports);
2422
2268
  const undocSignal = {
2423
2269
  name: "Undocumented Exports",
2424
2270
  count: undocumentedExports,
2425
- riskContribution: Math.round(undocRatio * 100 * 0.1),
2426
- // 10% weight
2271
+ riskContribution: Math.round(Math.min(1, undocRatio) * 100 * 0.1),
2427
2272
  description: `${undocumentedExports} public functions without docs \u2014 AI fabricates behavior`
2428
2273
  };
2429
2274
  const signals = [
@@ -2448,33 +2293,28 @@ function calculateAiSignalClarity(params) {
2448
2293
  const topSignal = signals.reduce(
2449
2294
  (a, b) => a.riskContribution > b.riskContribution ? a : b
2450
2295
  );
2451
- const topRisk = topSignal.riskContribution > 0 ? topSignal.description : "No significant AI signal claritys detected";
2296
+ const topRisk = topSignal.riskContribution > 0 ? topSignal.description : "No significant issues detected";
2452
2297
  const recommendations = [];
2453
- if (overloadSignal.riskContribution > 5) {
2298
+ if (overloadSignal.riskContribution > 5)
2454
2299
  recommendations.push(
2455
2300
  `Rename ${overloadedSymbols} overloaded symbols to unique, intent-revealing names`
2456
2301
  );
2457
- }
2458
- if (magicSignal.riskContribution > 5) {
2302
+ if (magicSignal.riskContribution > 5)
2459
2303
  recommendations.push(
2460
2304
  `Extract ${magicLiterals} magic literals into named constants`
2461
2305
  );
2462
- }
2463
- if (trapSignal.riskContribution > 5) {
2306
+ if (trapSignal.riskContribution > 5)
2464
2307
  recommendations.push(
2465
2308
  `Replace ${booleanTraps} boolean traps with named options objects`
2466
2309
  );
2467
- }
2468
- if (undocSignal.riskContribution > 5) {
2310
+ if (undocSignal.riskContribution > 5)
2469
2311
  recommendations.push(
2470
2312
  `Add JSDoc/docstrings to ${undocumentedExports} undocumented public functions`
2471
2313
  );
2472
- }
2473
- if (sideEffectSignal.riskContribution > 5) {
2314
+ if (sideEffectSignal.riskContribution > 5)
2474
2315
  recommendations.push(
2475
2316
  "Mark functions with side effects explicitly in their names or docs"
2476
2317
  );
2477
- }
2478
2318
  return {
2479
2319
  score: Math.round(score),
2480
2320
  rating,
@@ -2483,6 +2323,8 @@ function calculateAiSignalClarity(params) {
2483
2323
  recommendations
2484
2324
  };
2485
2325
  }
2326
+
2327
+ // src/metrics/agent-grounding.ts
2486
2328
  function calculateAgentGrounding(params) {
2487
2329
  const {
2488
2330
  deepDirectories,
@@ -2497,25 +2339,33 @@ function calculateAgentGrounding(params) {
2497
2339
  inconsistentDomainTerms,
2498
2340
  domainVocabularySize
2499
2341
  } = params;
2500
- const deepDirRatio = totalDirectories > 0 ? deepDirectories / totalDirectories : 0;
2501
2342
  const structureClarityScore = Math.max(
2502
2343
  0,
2503
- Math.round(100 - deepDirRatio * 80)
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))
2504
2351
  );
2505
- const vagueRatio = totalFiles > 0 ? vagueFileNames / totalFiles : 0;
2506
- const selfDocumentationScore = Math.max(0, Math.round(100 - vagueRatio * 90));
2507
2352
  let entryPointScore = 60;
2508
2353
  if (hasRootReadme) entryPointScore += 25;
2509
2354
  if (readmeIsFresh) entryPointScore += 10;
2510
2355
  const barrelRatio = totalFiles > 0 ? barrelExports / (totalFiles * 0.1) : 0;
2511
2356
  entryPointScore += Math.round(Math.min(5, barrelRatio * 5));
2512
2357
  entryPointScore = Math.min(100, entryPointScore);
2513
- const untypedRatio = totalExports > 0 ? untypedExports / totalExports : 0;
2514
- const apiClarityScore = Math.max(0, Math.round(100 - untypedRatio * 70));
2515
- const inconsistencyRatio = domainVocabularySize > 0 ? inconsistentDomainTerms / domainVocabularySize : 0;
2358
+ const apiClarityScore = Math.max(
2359
+ 0,
2360
+ Math.round(
2361
+ 100 - (totalExports > 0 ? untypedExports / totalExports * 70 : 0)
2362
+ )
2363
+ );
2516
2364
  const domainConsistencyScore = Math.max(
2517
2365
  0,
2518
- Math.round(100 - inconsistencyRatio * 80)
2366
+ Math.round(
2367
+ 100 - (domainVocabularySize > 0 ? inconsistentDomainTerms / domainVocabularySize * 80 : 0)
2368
+ )
2519
2369
  );
2520
2370
  const score = Math.round(
2521
2371
  structureClarityScore * 0.2 + selfDocumentationScore * 0.25 + entryPointScore * 0.2 + apiClarityScore * 0.15 + domainConsistencyScore * 0.2
@@ -2527,35 +2377,30 @@ function calculateAgentGrounding(params) {
2527
2377
  else if (score >= 30) rating = "poor";
2528
2378
  else rating = "disorienting";
2529
2379
  const recommendations = [];
2530
- if (structureClarityScore < 70) {
2380
+ if (structureClarityScore < 70)
2531
2381
  recommendations.push(
2532
2382
  `Flatten ${deepDirectories} overly-deep directories to improve agent navigation`
2533
2383
  );
2534
- }
2535
- if (selfDocumentationScore < 70) {
2384
+ if (selfDocumentationScore < 70)
2536
2385
  recommendations.push(
2537
2386
  `Rename ${vagueFileNames} vague files (utils, helpers, misc) to domain-specific names`
2538
2387
  );
2539
- }
2540
- if (!hasRootReadme) {
2388
+ if (!hasRootReadme)
2541
2389
  recommendations.push(
2542
2390
  "Add a root README.md so agents understand the project context immediately"
2543
2391
  );
2544
- } else if (!readmeIsFresh) {
2392
+ else if (!readmeIsFresh)
2545
2393
  recommendations.push(
2546
2394
  "Update README.md \u2014 stale entry-point documentation disorients agents"
2547
2395
  );
2548
- }
2549
- if (apiClarityScore < 70) {
2396
+ if (apiClarityScore < 70)
2550
2397
  recommendations.push(
2551
2398
  `Add TypeScript types to ${untypedExports} untyped exports to improve API discoverability`
2552
2399
  );
2553
- }
2554
- if (domainConsistencyScore < 70) {
2400
+ if (domainConsistencyScore < 70)
2555
2401
  recommendations.push(
2556
2402
  `Unify ${inconsistentDomainTerms} inconsistent domain terms \u2014 agents need one word per concept`
2557
2403
  );
2558
- }
2559
2404
  return {
2560
2405
  score,
2561
2406
  rating,
@@ -2569,6 +2414,8 @@ function calculateAgentGrounding(params) {
2569
2414
  recommendations
2570
2415
  };
2571
2416
  }
2417
+
2418
+ // src/metrics/testability-index.ts
2572
2419
  function calculateTestabilityIndex(params) {
2573
2420
  const {
2574
2421
  testFiles,
@@ -2584,16 +2431,27 @@ function calculateTestabilityIndex(params) {
2584
2431
  } = params;
2585
2432
  const rawCoverageRatio = sourceFiles > 0 ? testFiles / sourceFiles : 0;
2586
2433
  const testCoverageRatio = Math.min(100, Math.round(rawCoverageRatio * 100));
2587
- const purityRatio = totalFunctions > 0 ? pureFunctions / totalFunctions : 0.5;
2588
- const purityScore = Math.round(purityRatio * 100);
2589
- const injectionRatio = totalClasses > 0 ? injectionPatterns / totalClasses : 0.5;
2434
+ const purityScore = Math.round(
2435
+ (totalFunctions > 0 ? pureFunctions / totalFunctions : 0.5) * 100
2436
+ );
2590
2437
  const dependencyInjectionScore = Math.round(
2591
- Math.min(100, injectionRatio * 100)
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
+ )
2592
2454
  );
2593
- const bloatedRatio = totalInterfaces > 0 ? bloatedInterfaces / totalInterfaces : 0;
2594
- const interfaceFocusScore = Math.max(0, Math.round(100 - bloatedRatio * 80));
2595
- const mutationRatio = totalFunctions > 0 ? externalStateMutations / totalFunctions : 0;
2596
- const observabilityScore = Math.max(0, Math.round(100 - mutationRatio * 100));
2597
2455
  const frameworkWeight = hasTestFramework ? 1 : 0.8;
2598
2456
  const rawScore = (testCoverageRatio * 0.3 + purityScore * 0.25 + dependencyInjectionScore * 0.2 + interfaceFocusScore * 0.1 + observabilityScore * 0.15) * frameworkWeight;
2599
2457
  const score = Math.max(0, Math.min(100, Math.round(rawScore)));
@@ -2610,32 +2468,28 @@ function calculateTestabilityIndex(params) {
2610
2468
  else if (rawCoverageRatio > 0) aiChangeSafetyRating = "high-risk";
2611
2469
  else aiChangeSafetyRating = "blind-risk";
2612
2470
  const recommendations = [];
2613
- if (!hasTestFramework) {
2471
+ if (!hasTestFramework)
2614
2472
  recommendations.push(
2615
2473
  "Add a testing framework (Jest, Vitest, pytest) \u2014 AI changes cannot be verified without tests"
2616
2474
  );
2617
- }
2618
2475
  if (rawCoverageRatio < 0.3) {
2619
2476
  const neededTests = Math.round(sourceFiles * 0.3 - testFiles);
2620
2477
  recommendations.push(
2621
2478
  `Add ~${neededTests} test files to reach 30% coverage ratio \u2014 minimum for safe AI assistance`
2622
2479
  );
2623
2480
  }
2624
- if (purityScore < 50) {
2481
+ if (purityScore < 50)
2625
2482
  recommendations.push(
2626
2483
  "Extract pure functions from side-effectful code \u2014 pure functions are trivially AI-testable"
2627
2484
  );
2628
- }
2629
- if (dependencyInjectionScore < 50 && totalClasses > 0) {
2485
+ if (dependencyInjectionScore < 50 && totalClasses > 0)
2630
2486
  recommendations.push(
2631
2487
  "Adopt dependency injection \u2014 makes classes mockable and AI-generated code verifiable"
2632
2488
  );
2633
- }
2634
- if (externalStateMutations > totalFunctions * 0.3) {
2489
+ if (externalStateMutations > totalFunctions * 0.3)
2635
2490
  recommendations.push(
2636
2491
  "Reduce direct state mutations \u2014 return values instead to improve observability"
2637
2492
  );
2638
- }
2639
2493
  return {
2640
2494
  score,
2641
2495
  rating,
@@ -2650,6 +2504,8 @@ function calculateTestabilityIndex(params) {
2650
2504
  recommendations
2651
2505
  };
2652
2506
  }
2507
+
2508
+ // src/metrics/doc-drift.ts
2653
2509
  function calculateDocDrift(params) {
2654
2510
  const {
2655
2511
  uncommentedExports,
@@ -2672,21 +2528,18 @@ function calculateDocDrift(params) {
2672
2528
  else if (finalScore < 85) rating = "high";
2673
2529
  else rating = "severe";
2674
2530
  const recommendations = [];
2675
- if (outdatedComments > 0) {
2531
+ if (outdatedComments > 0)
2676
2532
  recommendations.push(
2677
2533
  `Update or remove ${outdatedComments} outdated comments that contradict the code.`
2678
2534
  );
2679
- }
2680
- if (uncommentedRatio > 0.3) {
2535
+ if (uncommentedRatio > 0.3)
2681
2536
  recommendations.push(
2682
2537
  `Add JSDoc to ${uncommentedExports} uncommented exports.`
2683
2538
  );
2684
- }
2685
- if (undocumentedComplexity > 0) {
2539
+ if (undocumentedComplexity > 0)
2686
2540
  recommendations.push(
2687
2541
  `Explain the business logic for ${undocumentedComplexity} highly complex functions.`
2688
2542
  );
2689
- }
2690
2543
  return {
2691
2544
  score: finalScore,
2692
2545
  rating,
@@ -2698,6 +2551,8 @@ function calculateDocDrift(params) {
2698
2551
  recommendations
2699
2552
  };
2700
2553
  }
2554
+
2555
+ // src/metrics/dependency-health.ts
2701
2556
  function calculateDependencyHealth(params) {
2702
2557
  const {
2703
2558
  totalPackages,
@@ -2710,8 +2565,12 @@ function calculateDependencyHealth(params) {
2710
2565
  const outdatedScore = Math.max(0, 100 - outdatedRatio * 200);
2711
2566
  const deprecatedScore = Math.max(0, 100 - deprecatedRatio * 500);
2712
2567
  const skewScore = Math.max(0, 100 - trainingCutoffSkew * 100);
2713
- const rawScore = outdatedScore * 0.3 + deprecatedScore * 0.4 + skewScore * 0.3;
2714
- const score = Math.round(Math.min(100, Math.max(0, rawScore)));
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
+ );
2715
2574
  let rating;
2716
2575
  if (score >= 85) rating = "excellent";
2717
2576
  else if (score >= 70) rating = "good";
@@ -2726,33 +2585,22 @@ function calculateDependencyHealth(params) {
2726
2585
  else if (trainingCutoffSkew < 0.8) aiKnowledgeConfidence = "low";
2727
2586
  else aiKnowledgeConfidence = "blind";
2728
2587
  const recommendations = [];
2729
- if (deprecatedPackages > 0) {
2730
- recommendations.push(
2731
- `Replace ${deprecatedPackages} deprecated packages, as AI will struggle to find modern solutions.`
2732
- );
2733
- }
2734
- if (outdatedRatio > 0.2) {
2735
- recommendations.push(
2736
- `Update ${outdatedPackages} outdated packages to keep APIs aligned with AI training data.`
2737
- );
2738
- }
2739
- if (trainingCutoffSkew > 0.5) {
2740
- recommendations.push(
2741
- "High training cutoff skew detected. AI may hallucinate APIs that were introduced recently."
2742
- );
2743
- }
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.");
2744
2594
  return {
2745
2595
  score,
2746
2596
  rating,
2747
- dimensions: {
2748
- outdatedPackages,
2749
- deprecatedPackages,
2750
- trainingCutoffSkew
2751
- },
2597
+ dimensions: { outdatedPackages, deprecatedPackages, trainingCutoffSkew },
2752
2598
  aiKnowledgeConfidence,
2753
2599
  recommendations
2754
2600
  };
2755
2601
  }
2602
+
2603
+ // src/metrics/change-amplification.ts
2756
2604
  function calculateChangeAmplification(params) {
2757
2605
  const { files } = params;
2758
2606
  if (files.length === 0) {
@@ -2765,15 +2613,16 @@ function calculateChangeAmplification(params) {
2765
2613
  recommendations: []
2766
2614
  };
2767
2615
  }
2768
- const hotspots = files.map((f) => {
2769
- const amplificationFactor = f.fanOut + f.fanIn * 0.5;
2770
- return { ...f, amplificationFactor };
2771
- }).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);
2772
2617
  const maxAmplification = hotspots[0].amplificationFactor;
2773
2618
  const avgAmplification = hotspots.reduce((sum, h) => sum + h.amplificationFactor, 0) / hotspots.length;
2774
- let score = 100 - avgAmplification * 5;
2775
- if (maxAmplification > 20) score -= maxAmplification - 20;
2776
- score = Math.max(0, Math.min(100, score));
2619
+ let score = Math.max(
2620
+ 0,
2621
+ Math.min(
2622
+ 100,
2623
+ 100 - avgAmplification * 5 - (maxAmplification > 20 ? maxAmplification - 20 : 0)
2624
+ )
2625
+ );
2777
2626
  let rating = "isolated";
2778
2627
  if (score < 40) rating = "explosive";
2779
2628
  else if (score < 70) rating = "amplified";
@@ -2781,12 +2630,12 @@ function calculateChangeAmplification(params) {
2781
2630
  const recommendations = [];
2782
2631
  if (score < 70 && hotspots.length > 0) {
2783
2632
  recommendations.push(
2784
- `Refactor top hotspot '${hotspots[0].file}' to reduce coupling (fan-out: ${hotspots[0].fanOut}, fan-in: ${hotspots[0].fanIn}).`
2633
+ `Refactor top hotspot '${hotspots[0].file}' to reduce coupling.`
2785
2634
  );
2786
2635
  }
2787
2636
  if (maxAmplification > 30) {
2788
2637
  recommendations.push(
2789
- `Break down key bottlenecks with amplification factor > 30.`
2638
+ "Break down key bottlenecks with amplification factor > 30."
2790
2639
  );
2791
2640
  }
2792
2641
  return {
@@ -2798,6 +2647,61 @@ function calculateChangeAmplification(params) {
2798
2647
  recommendations
2799
2648
  };
2800
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
+ }
2801
2705
  function calculateExtendedFutureProofScore(params) {
2802
2706
  const loadScore = 100 - params.cognitiveLoad.score;
2803
2707
  const entropyScore = 100 - params.patternEntropy.entropy * 100;
@@ -2806,7 +2710,7 @@ function calculateExtendedFutureProofScore(params) {
2806
2710
  const groundingScore = params.agentGrounding.score;
2807
2711
  const testabilityScore = params.testability.score;
2808
2712
  const docDriftScore = params.docDrift ? 100 - params.docDrift.score : 100;
2809
- const depsHealthScore = params.dependencyHealth ? params.dependencyHealth.score : 100;
2713
+ const depsHealthScore = params.dependencyHealth?.score ?? 100;
2810
2714
  let totalWeight = 0.8;
2811
2715
  let overall = loadScore * 0.15 + entropyScore * 0.1 + cohesionScore * 0.1 + aiSignalClarityScore * 0.15 + groundingScore * 0.15 + testabilityScore * 0.15;
2812
2716
  if (params.docDrift) {
@@ -2837,7 +2741,7 @@ function calculateExtendedFutureProofScore(params) {
2837
2741
  {
2838
2742
  name: "AI Signal Clarity",
2839
2743
  impact: Math.round(aiSignalClarityScore - 50),
2840
- description: `${params.aiSignalClarity.rating} risk (${params.aiSignalClarity.score}/100 raw)`
2744
+ description: `${params.aiSignalClarity.rating} risk`
2841
2745
  },
2842
2746
  {
2843
2747
  name: "Agent Grounding",
@@ -2847,60 +2751,25 @@ function calculateExtendedFutureProofScore(params) {
2847
2751
  {
2848
2752
  name: "Testability",
2849
2753
  impact: Math.round(testabilityScore - 50),
2850
- description: `${params.testability.rating} \u2014 AI changes are ${params.testability.aiChangeSafetyRating}`
2754
+ description: params.testability.rating
2851
2755
  }
2852
2756
  ];
2853
2757
  if (params.docDrift) {
2854
2758
  factors.push({
2855
2759
  name: "Documentation Drift",
2856
2760
  impact: Math.round(docDriftScore - 50),
2857
- description: `${params.docDrift.rating} risk of AI signal clarity from drift`
2761
+ description: params.docDrift.rating
2858
2762
  });
2859
2763
  }
2860
2764
  if (params.dependencyHealth) {
2861
2765
  factors.push({
2862
2766
  name: "Dependency Health",
2863
2767
  impact: Math.round(depsHealthScore - 50),
2864
- description: `${params.dependencyHealth.rating} health \u2014 AI knowledge is ${params.dependencyHealth.aiKnowledgeConfidence}`
2865
- });
2866
- }
2867
- const recommendations = [];
2868
- for (const rec of params.aiSignalClarity.recommendations) {
2869
- recommendations.push({ action: rec, estimatedImpact: 8, priority: "high" });
2870
- }
2871
- for (const rec of params.agentGrounding.recommendations) {
2872
- recommendations.push({
2873
- action: rec,
2874
- estimatedImpact: 6,
2875
- priority: "medium"
2768
+ description: params.dependencyHealth.rating
2876
2769
  });
2877
2770
  }
2878
- for (const rec of params.testability.recommendations) {
2879
- const priority = params.testability.aiChangeSafetyRating === "blind-risk" ? "high" : "medium";
2880
- recommendations.push({ action: rec, estimatedImpact: 10, priority });
2881
- }
2882
- for (const rec of params.patternEntropy.recommendations) {
2883
- recommendations.push({ action: rec, estimatedImpact: 5, priority: "low" });
2884
- }
2885
- if (params.docDrift) {
2886
- for (const rec of params.docDrift.recommendations) {
2887
- recommendations.push({
2888
- action: rec,
2889
- estimatedImpact: 8,
2890
- priority: "high"
2891
- });
2892
- }
2893
- }
2894
- if (params.dependencyHealth) {
2895
- for (const rec of params.dependencyHealth.recommendations) {
2896
- recommendations.push({
2897
- action: rec,
2898
- estimatedImpact: 7,
2899
- priority: "medium"
2900
- });
2901
- }
2902
- }
2903
- 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;
2904
2773
  return {
2905
2774
  toolName: "future-proof",
2906
2775
  score: overall,
@@ -3077,16 +2946,19 @@ function getRepoMetadata(directory) {
3077
2946
  ParseError,
3078
2947
  ParserFactory,
3079
2948
  PythonParser,
2949
+ SEVERITY_TIME_ESTIMATES,
3080
2950
  SIZE_ADJUSTED_THRESHOLDS,
3081
2951
  TOOL_NAME_MAP,
3082
2952
  TypeScriptParser,
3083
2953
  VAGUE_FILE_NAMES,
3084
2954
  calculateAgentGrounding,
3085
2955
  calculateAiSignalClarity,
2956
+ calculateBusinessROI,
3086
2957
  calculateChangeAmplification,
3087
2958
  calculateCognitiveLoad,
3088
2959
  calculateComprehensionDifficulty,
3089
2960
  calculateConceptCohesion,
2961
+ calculateDebtInterest,
3090
2962
  calculateDependencyHealth,
3091
2963
  calculateDocDrift,
3092
2964
  calculateExtendedFutureProofScore,
@@ -3097,10 +2969,8 @@ function getRepoMetadata(directory) {
3097
2969
  calculateOverallScore,
3098
2970
  calculatePatternEntropy,
3099
2971
  calculateProductivityImpact,
3100
- calculateRemediationVelocity,
3101
- calculateScoreTrend,
3102
2972
  calculateSemanticDistance,
3103
- calculateTechnicalDebtInterest,
2973
+ calculateTechnicalValueChain,
3104
2974
  calculateTestabilityIndex,
3105
2975
  calculateTokenBudget,
3106
2976
  clearHistory,
@@ -3116,7 +2986,6 @@ function getRepoMetadata(directory) {
3116
2986
  formatToolScore,
3117
2987
  generateHTML,
3118
2988
  generateValueChain,
3119
- getDebtBreakdown,
3120
2989
  getElapsedTime,
3121
2990
  getFileCommitTimestamps,
3122
2991
  getFileExtension,
@@ -3130,6 +2999,9 @@ function getRepoMetadata(directory) {
3130
2999
  getRatingWithContext,
3131
3000
  getRecommendedThreshold,
3132
3001
  getRepoMetadata,
3002
+ getSafetyIcon,
3003
+ getScoreBar,
3004
+ getSeverityColor,
3133
3005
  getSupportedLanguages,
3134
3006
  getToolWeight,
3135
3007
  handleCLIError,