@aiready/core 0.9.32 → 0.9.33

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
@@ -207,7 +207,9 @@ async function scanFiles(options) {
207
207
  ignoreFromFile = [];
208
208
  }
209
209
  }
210
- const finalExclude = [.../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...DEFAULT_EXCLUDE])];
210
+ const finalExclude = [
211
+ .../* @__PURE__ */ new Set([...exclude || [], ...ignoreFromFile, ...DEFAULT_EXCLUDE])
212
+ ];
211
213
  const files = await (0, import_glob.glob)(include, {
212
214
  cwd: rootDir,
213
215
  ignore: finalExclude,
@@ -455,7 +457,9 @@ async function loadConfig(rootDir) {
455
457
  return config;
456
458
  } catch (error) {
457
459
  const errorMessage = error instanceof Error ? error.message : String(error);
458
- throw new Error(`Failed to load config from ${configPath}: ${errorMessage}`);
460
+ throw new Error(
461
+ `Failed to load config from ${configPath}: ${errorMessage}`
462
+ );
459
463
  }
460
464
  }
461
465
  }
@@ -707,26 +711,26 @@ function generateHTML(graph) {
707
711
  var DEFAULT_TOOL_WEIGHTS = {
708
712
  "pattern-detect": 22,
709
713
  "context-analyzer": 19,
710
- "consistency": 14,
714
+ consistency: 14,
711
715
  "ai-signal-clarity": 11,
712
716
  "agent-grounding": 10,
713
- "testability": 10,
717
+ testability: 10,
714
718
  "doc-drift": 8,
715
- "deps": 6
719
+ deps: 6
716
720
  };
717
721
  var TOOL_NAME_MAP = {
718
- "patterns": "pattern-detect",
719
- "context": "context-analyzer",
720
- "consistency": "consistency",
722
+ patterns: "pattern-detect",
723
+ context: "context-analyzer",
724
+ consistency: "consistency",
721
725
  "AI signal clarity": "ai-signal-clarity",
722
726
  "ai-signal-clarity": "ai-signal-clarity",
723
- "grounding": "agent-grounding",
727
+ grounding: "agent-grounding",
724
728
  "agent-grounding": "agent-grounding",
725
- "testability": "testability",
726
- "tests": "testability",
729
+ testability: "testability",
730
+ tests: "testability",
727
731
  "doc-drift": "doc-drift",
728
- "docs": "doc-drift",
729
- "deps": "deps"
732
+ docs: "doc-drift",
733
+ deps: "deps"
730
734
  };
731
735
  var CONTEXT_TIER_THRESHOLDS = {
732
736
  compact: { idealTokens: 3e3, criticalTokens: 1e4, idealDepth: 4 },
@@ -735,15 +739,15 @@ var CONTEXT_TIER_THRESHOLDS = {
735
739
  frontier: { idealTokens: 5e4, criticalTokens: 15e4, idealDepth: 10 }
736
740
  };
737
741
  var SIZE_ADJUSTED_THRESHOLDS = {
738
- "xs": 80,
742
+ xs: 80,
739
743
  // < 50 files
740
- "small": 75,
744
+ small: 75,
741
745
  // 50-200 files
742
- "medium": 70,
746
+ medium: 70,
743
747
  // 200-500 files
744
- "large": 65,
748
+ large: 65,
745
749
  // 500-2000 files
746
- "enterprise": 58
750
+ enterprise: 58
747
751
  // 2000+ files
748
752
  };
749
753
  function getProjectSizeTier(fileCount) {
@@ -816,10 +820,12 @@ function calculateOverallScore(toolOutputs, config, cliWeights) {
816
820
  }
817
821
  const overall = Math.round(weightedSum / totalWeight);
818
822
  const rating = getRating(overall);
819
- const formulaParts = Array.from(toolOutputs.entries()).map(([name, output]) => {
820
- const w = weights.get(name) || 10;
821
- return `(${output.score} \xD7 ${w})`;
822
- });
823
+ const formulaParts = Array.from(toolOutputs.entries()).map(
824
+ ([name, output]) => {
825
+ const w = weights.get(name) || 10;
826
+ return `(${output.score} \xD7 ${w})`;
827
+ }
828
+ );
823
829
  const formulaStr = `[${formulaParts.join(" + ")}] / ${totalWeight} = ${overall}`;
824
830
  return {
825
831
  overall,
@@ -951,7 +957,7 @@ var MODEL_PRICING_PRESETS = {
951
957
  contextTier: "frontier",
952
958
  typicalQueriesPerDevPerDay: 150
953
959
  },
954
- "copilot": {
960
+ copilot: {
955
961
  name: "GitHub Copilot (subscription)",
956
962
  // Amortized per-request cost for a $19/month plan at 80 queries/day
957
963
  pricePer1KInputTokens: 1e-4,
@@ -1017,9 +1023,18 @@ function calculateProductivityImpact(issues, hourlyRate = DEFAULT_HOURLY_RATE) {
1017
1023
  hourlyRate,
1018
1024
  totalCost: Math.round(totalCost),
1019
1025
  bySeverity: {
1020
- critical: { hours: Math.round(hours.critical * 10) / 10, cost: Math.round(hours.critical * hourlyRate) },
1021
- major: { hours: Math.round(hours.major * 10) / 10, cost: Math.round(hours.major * hourlyRate) },
1022
- minor: { hours: Math.round(hours.minor * 10) / 10, cost: Math.round(hours.minor * hourlyRate) }
1026
+ critical: {
1027
+ hours: Math.round(hours.critical * 10) / 10,
1028
+ cost: Math.round(hours.critical * hourlyRate)
1029
+ },
1030
+ major: {
1031
+ hours: Math.round(hours.major * 10) / 10,
1032
+ cost: Math.round(hours.major * hourlyRate)
1033
+ },
1034
+ minor: {
1035
+ hours: Math.round(hours.minor * 10) / 10,
1036
+ cost: Math.round(hours.minor * hourlyRate)
1037
+ }
1023
1038
  }
1024
1039
  };
1025
1040
  }
@@ -1077,12 +1092,18 @@ function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentat
1077
1092
  const criticalBudget = tierThresholds.criticalTokens;
1078
1093
  const idealDepth = tierThresholds.idealDepth;
1079
1094
  const budgetRange = criticalBudget - idealBudget;
1080
- const budgetFactor = Math.min(100, Math.max(
1081
- 0,
1082
- (contextBudget - idealBudget) / budgetRange * 100
1083
- ));
1084
- const depthFactor = Math.min(100, Math.max(0, (importDepth - idealDepth) * 10));
1085
- const fragmentationFactor = Math.min(100, Math.max(0, (fragmentation - 0.3) * 250));
1095
+ const budgetFactor = Math.min(
1096
+ 100,
1097
+ Math.max(0, (contextBudget - idealBudget) / budgetRange * 100)
1098
+ );
1099
+ const depthFactor = Math.min(
1100
+ 100,
1101
+ Math.max(0, (importDepth - idealDepth) * 10)
1102
+ );
1103
+ const fragmentationFactor = Math.min(
1104
+ 100,
1105
+ Math.max(0, (fragmentation - 0.3) * 250)
1106
+ );
1086
1107
  const consistencyFactor = Math.min(100, Math.max(0, 100 - consistencyScore));
1087
1108
  const fileFactor = Math.min(100, Math.max(0, (totalFiles - 50) / 5));
1088
1109
  const score = Math.round(
@@ -1162,8 +1183,12 @@ function calculateScoreTrend(history) {
1162
1183
  const now = /* @__PURE__ */ new Date();
1163
1184
  const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1e3);
1164
1185
  const ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1e3);
1165
- const last30Days = history.filter((e) => new Date(e.timestamp) >= thirtyDaysAgo);
1166
- const last90Days = history.filter((e) => new Date(e.timestamp) >= ninetyDaysAgo);
1186
+ const last30Days = history.filter(
1187
+ (e) => new Date(e.timestamp) >= thirtyDaysAgo
1188
+ );
1189
+ const last90Days = history.filter(
1190
+ (e) => new Date(e.timestamp) >= ninetyDaysAgo
1191
+ );
1167
1192
  const currentScore = history[history.length - 1].overallScore;
1168
1193
  const thirtyDaysAgoScore = last30Days[0]?.overallScore || currentScore;
1169
1194
  const ninetyDaysAgoScore = last90Days[0]?.overallScore || thirtyDaysAgoScore;
@@ -1176,7 +1201,10 @@ function calculateScoreTrend(history) {
1176
1201
  if (change30Days > 3) direction = "improving";
1177
1202
  else if (change30Days < -3) direction = "degrading";
1178
1203
  else direction = "stable";
1179
- const projectedScore = Math.max(0, Math.min(100, currentScore + velocity * 4));
1204
+ const projectedScore = Math.max(
1205
+ 0,
1206
+ Math.min(100, currentScore + velocity * 4)
1207
+ );
1180
1208
  return {
1181
1209
  direction,
1182
1210
  change30Days,
@@ -1237,9 +1265,13 @@ function calculateKnowledgeConcentration(files, authorData) {
1237
1265
  recommendations: ["No files to analyze"]
1238
1266
  };
1239
1267
  }
1240
- const orphanFiles = files.filter((f) => f.exports < 2 && f.imports < 2).length;
1268
+ const orphanFiles = files.filter(
1269
+ (f) => f.exports < 2 && f.imports < 2
1270
+ ).length;
1241
1271
  const avgExports = files.reduce((sum, f) => sum + f.exports, 0) / files.length;
1242
- const uniqueConceptFiles = files.filter((f) => f.exports > avgExports * 2).length;
1272
+ const uniqueConceptFiles = files.filter(
1273
+ (f) => f.exports > avgExports * 2
1274
+ ).length;
1243
1275
  const totalExports = files.reduce((sum, f) => sum + f.exports, 0);
1244
1276
  const concentrationRatio = totalExports > 0 ? uniqueConceptFiles / files.length : 0;
1245
1277
  let singleAuthorFiles = 0;
@@ -1251,7 +1283,10 @@ function calculateKnowledgeConcentration(files, authorData) {
1251
1283
  const orphanRisk = orphanFiles / files.length * 30;
1252
1284
  const uniqueRisk = concentrationRatio * 40;
1253
1285
  const singleAuthorRisk = authorData ? singleAuthorFiles / files.length * 30 : 0;
1254
- const score = Math.min(100, Math.round(orphanRisk + uniqueRisk + singleAuthorRisk));
1286
+ const score = Math.min(
1287
+ 100,
1288
+ Math.round(orphanRisk + uniqueRisk + singleAuthorRisk)
1289
+ );
1255
1290
  let rating;
1256
1291
  if (score < 20) rating = "low";
1257
1292
  else if (score < 40) rating = "moderate";
@@ -1259,13 +1294,19 @@ function calculateKnowledgeConcentration(files, authorData) {
1259
1294
  else rating = "critical";
1260
1295
  const recommendations = [];
1261
1296
  if (orphanFiles > files.length * 0.2) {
1262
- recommendations.push(`Reduce ${orphanFiles} orphan files by connecting them to main modules`);
1297
+ recommendations.push(
1298
+ `Reduce ${orphanFiles} orphan files by connecting them to main modules`
1299
+ );
1263
1300
  }
1264
1301
  if (uniqueConceptFiles > files.length * 0.1) {
1265
- recommendations.push("Distribute high-export files into more focused modules");
1302
+ recommendations.push(
1303
+ "Distribute high-export files into more focused modules"
1304
+ );
1266
1305
  }
1267
1306
  if (authorData && singleAuthorFiles > files.length * 0.3) {
1268
- recommendations.push("Increase knowledge sharing to reduce single-author dependencies");
1307
+ recommendations.push(
1308
+ "Increase knowledge sharing to reduce single-author dependencies"
1309
+ );
1269
1310
  }
1270
1311
  return {
1271
1312
  score,
@@ -1419,7 +1460,10 @@ var TypeScriptParser = class {
1419
1460
  specifiers,
1420
1461
  isTypeOnly,
1421
1462
  loc: node.loc ? {
1422
- start: { line: node.loc.start.line, column: node.loc.start.column },
1463
+ start: {
1464
+ line: node.loc.start.line,
1465
+ column: node.loc.start.column
1466
+ },
1423
1467
  end: { line: node.loc.end.line, column: node.loc.end.column }
1424
1468
  } : void 0
1425
1469
  });
@@ -1430,11 +1474,16 @@ var TypeScriptParser = class {
1430
1474
  extractExports(ast, imports) {
1431
1475
  const exports2 = [];
1432
1476
  const importedNames = new Set(
1433
- imports.flatMap((imp) => imp.specifiers.filter((s) => s !== "*" && s !== "default"))
1477
+ imports.flatMap(
1478
+ (imp) => imp.specifiers.filter((s) => s !== "*" && s !== "default")
1479
+ )
1434
1480
  );
1435
1481
  for (const node of ast.body) {
1436
1482
  if (node.type === "ExportNamedDeclaration" && node.declaration) {
1437
- const extracted = this.extractFromDeclaration(node.declaration, importedNames);
1483
+ const extracted = this.extractFromDeclaration(
1484
+ node.declaration,
1485
+ importedNames
1486
+ );
1438
1487
  exports2.push(...extracted);
1439
1488
  } else if (node.type === "ExportDefaultDeclaration") {
1440
1489
  let name = "default";
@@ -1450,7 +1499,10 @@ var TypeScriptParser = class {
1450
1499
  name,
1451
1500
  type,
1452
1501
  loc: node.loc ? {
1453
- start: { line: node.loc.start.line, column: node.loc.start.column },
1502
+ start: {
1503
+ line: node.loc.start.line,
1504
+ column: node.loc.start.column
1505
+ },
1454
1506
  end: { line: node.loc.end.line, column: node.loc.end.column }
1455
1507
  } : void 0
1456
1508
  });
@@ -1472,7 +1524,10 @@ var TypeScriptParser = class {
1472
1524
  line: declaration.loc.start.line,
1473
1525
  column: declaration.loc.start.column
1474
1526
  },
1475
- end: { line: declaration.loc.end.line, column: declaration.loc.end.column }
1527
+ end: {
1528
+ line: declaration.loc.end.line,
1529
+ column: declaration.loc.end.column
1530
+ }
1476
1531
  } : void 0
1477
1532
  });
1478
1533
  } else if (declaration.type === "ClassDeclaration" && declaration.id) {
@@ -1484,7 +1539,10 @@ var TypeScriptParser = class {
1484
1539
  line: declaration.loc.start.line,
1485
1540
  column: declaration.loc.start.column
1486
1541
  },
1487
- end: { line: declaration.loc.end.line, column: declaration.loc.end.column }
1542
+ end: {
1543
+ line: declaration.loc.end.line,
1544
+ column: declaration.loc.end.column
1545
+ }
1488
1546
  } : void 0
1489
1547
  });
1490
1548
  } else if (declaration.type === "VariableDeclaration") {
@@ -1515,7 +1573,10 @@ var TypeScriptParser = class {
1515
1573
  line: declaration.loc.start.line,
1516
1574
  column: declaration.loc.start.column
1517
1575
  },
1518
- end: { line: declaration.loc.end.line, column: declaration.loc.end.column }
1576
+ end: {
1577
+ line: declaration.loc.end.line,
1578
+ column: declaration.loc.end.column
1579
+ }
1519
1580
  } : void 0
1520
1581
  });
1521
1582
  } else if (declaration.type === "TSInterfaceDeclaration") {
@@ -1527,7 +1588,10 @@ var TypeScriptParser = class {
1527
1588
  line: declaration.loc.start.line,
1528
1589
  column: declaration.loc.start.column
1529
1590
  },
1530
- end: { line: declaration.loc.end.line, column: declaration.loc.end.column }
1591
+ end: {
1592
+ line: declaration.loc.end.line,
1593
+ column: declaration.loc.end.column
1594
+ }
1531
1595
  } : void 0
1532
1596
  });
1533
1597
  }
@@ -1552,7 +1616,9 @@ var PythonParser = class {
1552
1616
  try {
1553
1617
  this.initialized = true;
1554
1618
  } catch (error) {
1555
- throw new Error(`Failed to initialize Python parser: ${error.message}`);
1619
+ throw new Error(
1620
+ `Failed to initialize Python parser: ${error.message}`
1621
+ );
1556
1622
  }
1557
1623
  }
1558
1624
  parse(code, filePath) {
@@ -1563,7 +1629,9 @@ var PythonParser = class {
1563
1629
  exports: exports2,
1564
1630
  imports,
1565
1631
  language: "python" /* Python */,
1566
- warnings: ["Python parsing is currently using regex-based extraction. Tree-sitter support coming soon."]
1632
+ warnings: [
1633
+ "Python parsing is currently using regex-based extraction. Tree-sitter support coming soon."
1634
+ ]
1567
1635
  };
1568
1636
  } catch (error) {
1569
1637
  throw new ParseError(
@@ -1658,7 +1726,7 @@ var PythonParser = class {
1658
1726
  }
1659
1727
  /**
1660
1728
  * Regex-based export extraction (temporary implementation)
1661
- *
1729
+ *
1662
1730
  * Python doesn't have explicit exports like JavaScript.
1663
1731
  * We extract:
1664
1732
  * - Functions defined at module level (def)
@@ -1826,7 +1894,13 @@ function getSupportedLanguages() {
1826
1894
 
1827
1895
  // src/future-proof-metrics.ts
1828
1896
  function calculateCognitiveLoad(params) {
1829
- const { linesOfCode, exportCount, importCount, uniqueConcepts, cyclomaticComplexity = 1 } = params;
1897
+ const {
1898
+ linesOfCode,
1899
+ exportCount,
1900
+ importCount,
1901
+ uniqueConcepts,
1902
+ cyclomaticComplexity = 1
1903
+ } = params;
1830
1904
  const sizeFactor = {
1831
1905
  name: "Size Complexity",
1832
1906
  score: Math.min(100, Math.max(0, (linesOfCode - 50) / 10)),
@@ -1852,7 +1926,12 @@ function calculateCognitiveLoad(params) {
1852
1926
  weight: 0.2,
1853
1927
  description: `${uniqueConcepts} unique concepts`
1854
1928
  };
1855
- const factors = [sizeFactor, interfaceFactor, dependencyFactor, conceptFactor];
1929
+ const factors = [
1930
+ sizeFactor,
1931
+ interfaceFactor,
1932
+ dependencyFactor,
1933
+ conceptFactor
1934
+ ];
1856
1935
  const score = factors.reduce((sum, f) => sum + f.score * f.weight, 0);
1857
1936
  let rating;
1858
1937
  if (score < 20) rating = "trivial";
@@ -1875,7 +1954,10 @@ function calculateCognitiveLoad(params) {
1875
1954
  function calculateSemanticDistance(params) {
1876
1955
  const { file1, file2, file1Domain, file2Domain, sharedDependencies } = params;
1877
1956
  const domainDistance = file1Domain === file2Domain ? 0 : file1Domain && file2Domain ? 0.5 : 0.8;
1878
- const importOverlap = sharedDependencies.length / Math.max(1, Math.min(params.file1Imports.length, params.file2Imports.length));
1957
+ const importOverlap = sharedDependencies.length / Math.max(
1958
+ 1,
1959
+ Math.min(params.file1Imports.length, params.file2Imports.length)
1960
+ );
1879
1961
  const importDistance = 1 - importOverlap;
1880
1962
  const distance = domainDistance * 0.4 + importDistance * 0.3 + (sharedDependencies.length > 0 ? 0 : 0.3);
1881
1963
  let relationship;
@@ -1883,7 +1965,9 @@ function calculateSemanticDistance(params) {
1883
1965
  else if (file1Domain === file2Domain) relationship = "same-domain";
1884
1966
  else if (sharedDependencies.length > 0) relationship = "cross-domain";
1885
1967
  else relationship = "unrelated";
1886
- const pathItems = [file1Domain, ...sharedDependencies, file2Domain].filter((s) => typeof s === "string" && s.length > 0);
1968
+ const pathItems = [file1Domain, ...sharedDependencies, file2Domain].filter(
1969
+ (s) => typeof s === "string" && s.length > 0
1970
+ );
1887
1971
  return {
1888
1972
  between: [file1, file2],
1889
1973
  distance: Math.round(distance * 100) / 100,
@@ -1898,7 +1982,11 @@ function calculatePatternEntropy(files) {
1898
1982
  domain: "unknown",
1899
1983
  entropy: 0,
1900
1984
  rating: "crystalline",
1901
- distribution: { locationCount: 0, dominantLocation: "", giniCoefficient: 0 },
1985
+ distribution: {
1986
+ locationCount: 0,
1987
+ dominantLocation: "",
1988
+ giniCoefficient: 0
1989
+ },
1902
1990
  recommendations: ["No files to analyze"]
1903
1991
  };
1904
1992
  }
@@ -1938,10 +2026,14 @@ function calculatePatternEntropy(files) {
1938
2026
  else rating = "chaotic";
1939
2027
  const recommendations = [];
1940
2028
  if (normalizedEntropy > 0.5) {
1941
- recommendations.push(`Consolidate ${files.length} files into fewer directories by domain`);
2029
+ recommendations.push(
2030
+ `Consolidate ${files.length} files into fewer directories by domain`
2031
+ );
1942
2032
  }
1943
2033
  if (dirGroups.size > 5) {
1944
- recommendations.push("Consider barrel exports to reduce directory navigation");
2034
+ recommendations.push(
2035
+ "Consider barrel exports to reduce directory navigation"
2036
+ );
1945
2037
  }
1946
2038
  if (gini > 0.5) {
1947
2039
  recommendations.push("Redistribute files more evenly across directories");
@@ -1966,7 +2058,11 @@ function calculateConceptCohesion(params) {
1966
2058
  return {
1967
2059
  score: 1,
1968
2060
  rating: "excellent",
1969
- analysis: { uniqueDomains: 0, domainConcentration: 0, exportPurposeClarity: 1 }
2061
+ analysis: {
2062
+ uniqueDomains: 0,
2063
+ domainConcentration: 0,
2064
+ exportPurposeClarity: 1
2065
+ }
1970
2066
  };
1971
2067
  }
1972
2068
  const allDomains = [];
@@ -2072,7 +2168,10 @@ function calculateAiSignalClarity(params) {
2072
2168
  recommendations: []
2073
2169
  };
2074
2170
  }
2075
- const overloadRatio = Math.min(1, overloadedSymbols / Math.max(1, totalSymbols));
2171
+ const overloadRatio = Math.min(
2172
+ 1,
2173
+ overloadedSymbols / Math.max(1, totalSymbols)
2174
+ );
2076
2175
  const overloadSignal = {
2077
2176
  name: "Symbol Overloading",
2078
2177
  count: overloadedSymbols,
@@ -2096,7 +2195,10 @@ function calculateAiSignalClarity(params) {
2096
2195
  // 20% weight
2097
2196
  description: `${booleanTraps} boolean trap parameters \u2014 AI inverts intent`
2098
2197
  };
2099
- const sideEffectRatio = Math.min(1, implicitSideEffects / Math.max(1, totalExports));
2198
+ const sideEffectRatio = Math.min(
2199
+ 1,
2200
+ implicitSideEffects / Math.max(1, totalExports)
2201
+ );
2100
2202
  const sideEffectSignal = {
2101
2203
  name: "Implicit Side Effects",
2102
2204
  count: implicitSideEffects,
@@ -2104,7 +2206,10 @@ function calculateAiSignalClarity(params) {
2104
2206
  // 15% weight
2105
2207
  description: `${implicitSideEffects} functions with implicit side effects \u2014 AI misses contracts`
2106
2208
  };
2107
- const callbackRatio = Math.min(1, deepCallbacks / Math.max(1, totalSymbols * 0.1));
2209
+ const callbackRatio = Math.min(
2210
+ 1,
2211
+ deepCallbacks / Math.max(1, totalSymbols * 0.1)
2212
+ );
2108
2213
  const callbackSignal = {
2109
2214
  name: "Callback Nesting",
2110
2215
  count: deepCallbacks,
@@ -2112,7 +2217,10 @@ function calculateAiSignalClarity(params) {
2112
2217
  // 10% weight
2113
2218
  description: `${deepCallbacks} deep callback chains \u2014 AI loses control flow context`
2114
2219
  };
2115
- const ambiguousRatio = Math.min(1, ambiguousNames / Math.max(1, totalSymbols));
2220
+ const ambiguousRatio = Math.min(
2221
+ 1,
2222
+ ambiguousNames / Math.max(1, totalSymbols)
2223
+ );
2116
2224
  const ambiguousSignal = {
2117
2225
  name: "Ambiguous Names",
2118
2226
  count: ambiguousNames,
@@ -2120,7 +2228,10 @@ function calculateAiSignalClarity(params) {
2120
2228
  // 10% weight
2121
2229
  description: `${ambiguousNames} non-descriptive identifiers \u2014 AI guesses wrong intent`
2122
2230
  };
2123
- const undocRatio = Math.min(1, undocumentedExports / Math.max(1, totalExports));
2231
+ const undocRatio = Math.min(
2232
+ 1,
2233
+ undocumentedExports / Math.max(1, totalExports)
2234
+ );
2124
2235
  const undocSignal = {
2125
2236
  name: "Undocumented Exports",
2126
2237
  count: undocumentedExports,
@@ -2137,30 +2248,45 @@ function calculateAiSignalClarity(params) {
2137
2248
  ambiguousSignal,
2138
2249
  undocSignal
2139
2250
  ];
2140
- const score = Math.min(100, signals.reduce((sum, s) => sum + s.riskContribution, 0));
2251
+ const score = Math.min(
2252
+ 100,
2253
+ signals.reduce((sum, s) => sum + s.riskContribution, 0)
2254
+ );
2141
2255
  let rating;
2142
2256
  if (score < 10) rating = "minimal";
2143
2257
  else if (score < 25) rating = "low";
2144
2258
  else if (score < 50) rating = "moderate";
2145
2259
  else if (score < 75) rating = "high";
2146
2260
  else rating = "severe";
2147
- const topSignal = signals.reduce((a, b) => a.riskContribution > b.riskContribution ? a : b);
2261
+ const topSignal = signals.reduce(
2262
+ (a, b) => a.riskContribution > b.riskContribution ? a : b
2263
+ );
2148
2264
  const topRisk = topSignal.riskContribution > 0 ? topSignal.description : "No significant AI signal claritys detected";
2149
2265
  const recommendations = [];
2150
2266
  if (overloadSignal.riskContribution > 5) {
2151
- recommendations.push(`Rename ${overloadedSymbols} overloaded symbols to unique, intent-revealing names`);
2267
+ recommendations.push(
2268
+ `Rename ${overloadedSymbols} overloaded symbols to unique, intent-revealing names`
2269
+ );
2152
2270
  }
2153
2271
  if (magicSignal.riskContribution > 5) {
2154
- recommendations.push(`Extract ${magicLiterals} magic literals into named constants`);
2272
+ recommendations.push(
2273
+ `Extract ${magicLiterals} magic literals into named constants`
2274
+ );
2155
2275
  }
2156
2276
  if (trapSignal.riskContribution > 5) {
2157
- recommendations.push(`Replace ${booleanTraps} boolean traps with named options objects`);
2277
+ recommendations.push(
2278
+ `Replace ${booleanTraps} boolean traps with named options objects`
2279
+ );
2158
2280
  }
2159
2281
  if (undocSignal.riskContribution > 5) {
2160
- recommendations.push(`Add JSDoc/docstrings to ${undocumentedExports} undocumented public functions`);
2282
+ recommendations.push(
2283
+ `Add JSDoc/docstrings to ${undocumentedExports} undocumented public functions`
2284
+ );
2161
2285
  }
2162
2286
  if (sideEffectSignal.riskContribution > 5) {
2163
- recommendations.push("Mark functions with side effects explicitly in their names or docs");
2287
+ recommendations.push(
2288
+ "Mark functions with side effects explicitly in their names or docs"
2289
+ );
2164
2290
  }
2165
2291
  return {
2166
2292
  score: Math.round(score),
@@ -2185,7 +2311,10 @@ function calculateAgentGrounding(params) {
2185
2311
  domainVocabularySize
2186
2312
  } = params;
2187
2313
  const deepDirRatio = totalDirectories > 0 ? deepDirectories / totalDirectories : 0;
2188
- const structureClarityScore = Math.max(0, Math.round(100 - deepDirRatio * 80));
2314
+ const structureClarityScore = Math.max(
2315
+ 0,
2316
+ Math.round(100 - deepDirRatio * 80)
2317
+ );
2189
2318
  const vagueRatio = totalFiles > 0 ? vagueFileNames / totalFiles : 0;
2190
2319
  const selfDocumentationScore = Math.max(0, Math.round(100 - vagueRatio * 90));
2191
2320
  let entryPointScore = 60;
@@ -2197,7 +2326,10 @@ function calculateAgentGrounding(params) {
2197
2326
  const untypedRatio = totalExports > 0 ? untypedExports / totalExports : 0;
2198
2327
  const apiClarityScore = Math.max(0, Math.round(100 - untypedRatio * 70));
2199
2328
  const inconsistencyRatio = domainVocabularySize > 0 ? inconsistentDomainTerms / domainVocabularySize : 0;
2200
- const domainConsistencyScore = Math.max(0, Math.round(100 - inconsistencyRatio * 80));
2329
+ const domainConsistencyScore = Math.max(
2330
+ 0,
2331
+ Math.round(100 - inconsistencyRatio * 80)
2332
+ );
2201
2333
  const score = Math.round(
2202
2334
  structureClarityScore * 0.2 + selfDocumentationScore * 0.25 + entryPointScore * 0.2 + apiClarityScore * 0.15 + domainConsistencyScore * 0.2
2203
2335
  );
@@ -2209,21 +2341,33 @@ function calculateAgentGrounding(params) {
2209
2341
  else rating = "disorienting";
2210
2342
  const recommendations = [];
2211
2343
  if (structureClarityScore < 70) {
2212
- recommendations.push(`Flatten ${deepDirectories} overly-deep directories to improve agent navigation`);
2344
+ recommendations.push(
2345
+ `Flatten ${deepDirectories} overly-deep directories to improve agent navigation`
2346
+ );
2213
2347
  }
2214
2348
  if (selfDocumentationScore < 70) {
2215
- recommendations.push(`Rename ${vagueFileNames} vague files (utils, helpers, misc) to domain-specific names`);
2349
+ recommendations.push(
2350
+ `Rename ${vagueFileNames} vague files (utils, helpers, misc) to domain-specific names`
2351
+ );
2216
2352
  }
2217
2353
  if (!hasRootReadme) {
2218
- recommendations.push("Add a root README.md so agents understand the project context immediately");
2354
+ recommendations.push(
2355
+ "Add a root README.md so agents understand the project context immediately"
2356
+ );
2219
2357
  } else if (!readmeIsFresh) {
2220
- recommendations.push("Update README.md \u2014 stale entry-point documentation disorients agents");
2358
+ recommendations.push(
2359
+ "Update README.md \u2014 stale entry-point documentation disorients agents"
2360
+ );
2221
2361
  }
2222
2362
  if (apiClarityScore < 70) {
2223
- recommendations.push(`Add TypeScript types to ${untypedExports} untyped exports to improve API discoverability`);
2363
+ recommendations.push(
2364
+ `Add TypeScript types to ${untypedExports} untyped exports to improve API discoverability`
2365
+ );
2224
2366
  }
2225
2367
  if (domainConsistencyScore < 70) {
2226
- recommendations.push(`Unify ${inconsistentDomainTerms} inconsistent domain terms \u2014 agents need one word per concept`);
2368
+ recommendations.push(
2369
+ `Unify ${inconsistentDomainTerms} inconsistent domain terms \u2014 agents need one word per concept`
2370
+ );
2227
2371
  }
2228
2372
  return {
2229
2373
  score,
@@ -2256,7 +2400,9 @@ function calculateTestabilityIndex(params) {
2256
2400
  const purityRatio = totalFunctions > 0 ? pureFunctions / totalFunctions : 0.5;
2257
2401
  const purityScore = Math.round(purityRatio * 100);
2258
2402
  const injectionRatio = totalClasses > 0 ? injectionPatterns / totalClasses : 0.5;
2259
- const dependencyInjectionScore = Math.round(Math.min(100, injectionRatio * 100));
2403
+ const dependencyInjectionScore = Math.round(
2404
+ Math.min(100, injectionRatio * 100)
2405
+ );
2260
2406
  const bloatedRatio = totalInterfaces > 0 ? bloatedInterfaces / totalInterfaces : 0;
2261
2407
  const interfaceFocusScore = Math.max(0, Math.round(100 - bloatedRatio * 80));
2262
2408
  const mutationRatio = totalFunctions > 0 ? externalStateMutations / totalFunctions : 0;
@@ -2272,25 +2418,36 @@ function calculateTestabilityIndex(params) {
2272
2418
  else rating = "unverifiable";
2273
2419
  let aiChangeSafetyRating;
2274
2420
  if (rawCoverageRatio >= 0.5 && score >= 70) aiChangeSafetyRating = "safe";
2275
- else if (rawCoverageRatio >= 0.2 && score >= 50) aiChangeSafetyRating = "moderate-risk";
2421
+ else if (rawCoverageRatio >= 0.2 && score >= 50)
2422
+ aiChangeSafetyRating = "moderate-risk";
2276
2423
  else if (rawCoverageRatio > 0) aiChangeSafetyRating = "high-risk";
2277
2424
  else aiChangeSafetyRating = "blind-risk";
2278
2425
  const recommendations = [];
2279
2426
  if (!hasTestFramework) {
2280
- recommendations.push("Add a testing framework (Jest, Vitest, pytest) \u2014 AI changes cannot be verified without tests");
2427
+ recommendations.push(
2428
+ "Add a testing framework (Jest, Vitest, pytest) \u2014 AI changes cannot be verified without tests"
2429
+ );
2281
2430
  }
2282
2431
  if (rawCoverageRatio < 0.3) {
2283
2432
  const neededTests = Math.round(sourceFiles * 0.3 - testFiles);
2284
- recommendations.push(`Add ~${neededTests} test files to reach 30% coverage ratio \u2014 minimum for safe AI assistance`);
2433
+ recommendations.push(
2434
+ `Add ~${neededTests} test files to reach 30% coverage ratio \u2014 minimum for safe AI assistance`
2435
+ );
2285
2436
  }
2286
2437
  if (purityScore < 50) {
2287
- recommendations.push("Extract pure functions from side-effectful code \u2014 pure functions are trivially AI-testable");
2438
+ recommendations.push(
2439
+ "Extract pure functions from side-effectful code \u2014 pure functions are trivially AI-testable"
2440
+ );
2288
2441
  }
2289
2442
  if (dependencyInjectionScore < 50 && totalClasses > 0) {
2290
- recommendations.push("Adopt dependency injection \u2014 makes classes mockable and AI-generated code verifiable");
2443
+ recommendations.push(
2444
+ "Adopt dependency injection \u2014 makes classes mockable and AI-generated code verifiable"
2445
+ );
2291
2446
  }
2292
2447
  if (externalStateMutations > totalFunctions * 0.3) {
2293
- recommendations.push("Reduce direct state mutations \u2014 return values instead to improve observability");
2448
+ recommendations.push(
2449
+ "Reduce direct state mutations \u2014 return values instead to improve observability"
2450
+ );
2294
2451
  }
2295
2452
  return {
2296
2453
  score,
@@ -2307,7 +2464,12 @@ function calculateTestabilityIndex(params) {
2307
2464
  };
2308
2465
  }
2309
2466
  function calculateDocDrift(params) {
2310
- const { uncommentedExports, totalExports, outdatedComments, undocumentedComplexity } = params;
2467
+ const {
2468
+ uncommentedExports,
2469
+ totalExports,
2470
+ outdatedComments,
2471
+ undocumentedComplexity
2472
+ } = params;
2311
2473
  const uncommentedRatio = totalExports > 0 ? uncommentedExports / totalExports : 0;
2312
2474
  const outdatedScore = Math.min(100, outdatedComments * 15);
2313
2475
  const uncommentedScore = Math.min(100, uncommentedRatio * 100);
@@ -2324,13 +2486,19 @@ function calculateDocDrift(params) {
2324
2486
  else rating = "severe";
2325
2487
  const recommendations = [];
2326
2488
  if (outdatedComments > 0) {
2327
- recommendations.push(`Update or remove ${outdatedComments} outdated comments that contradict the code.`);
2489
+ recommendations.push(
2490
+ `Update or remove ${outdatedComments} outdated comments that contradict the code.`
2491
+ );
2328
2492
  }
2329
2493
  if (uncommentedRatio > 0.3) {
2330
- recommendations.push(`Add JSDoc to ${uncommentedExports} uncommented exports.`);
2494
+ recommendations.push(
2495
+ `Add JSDoc to ${uncommentedExports} uncommented exports.`
2496
+ );
2331
2497
  }
2332
2498
  if (undocumentedComplexity > 0) {
2333
- recommendations.push(`Explain the business logic for ${undocumentedComplexity} highly complex functions.`);
2499
+ recommendations.push(
2500
+ `Explain the business logic for ${undocumentedComplexity} highly complex functions.`
2501
+ );
2334
2502
  }
2335
2503
  return {
2336
2504
  score: finalScore,
@@ -2344,7 +2512,12 @@ function calculateDocDrift(params) {
2344
2512
  };
2345
2513
  }
2346
2514
  function calculateDependencyHealth(params) {
2347
- const { totalPackages, outdatedPackages, deprecatedPackages, trainingCutoffSkew } = params;
2515
+ const {
2516
+ totalPackages,
2517
+ outdatedPackages,
2518
+ deprecatedPackages,
2519
+ trainingCutoffSkew
2520
+ } = params;
2348
2521
  const outdatedRatio = totalPackages > 0 ? outdatedPackages / totalPackages : 0;
2349
2522
  const deprecatedRatio = totalPackages > 0 ? deprecatedPackages / totalPackages : 0;
2350
2523
  const outdatedScore = Math.max(0, 100 - outdatedRatio * 200);
@@ -2359,19 +2532,27 @@ function calculateDependencyHealth(params) {
2359
2532
  else if (score >= 30) rating = "poor";
2360
2533
  else rating = "hazardous";
2361
2534
  let aiKnowledgeConfidence;
2362
- if (trainingCutoffSkew < 0.2 && deprecatedPackages === 0) aiKnowledgeConfidence = "high";
2363
- else if (trainingCutoffSkew < 0.5 && deprecatedPackages <= 2) aiKnowledgeConfidence = "moderate";
2535
+ if (trainingCutoffSkew < 0.2 && deprecatedPackages === 0)
2536
+ aiKnowledgeConfidence = "high";
2537
+ else if (trainingCutoffSkew < 0.5 && deprecatedPackages <= 2)
2538
+ aiKnowledgeConfidence = "moderate";
2364
2539
  else if (trainingCutoffSkew < 0.8) aiKnowledgeConfidence = "low";
2365
2540
  else aiKnowledgeConfidence = "blind";
2366
2541
  const recommendations = [];
2367
2542
  if (deprecatedPackages > 0) {
2368
- recommendations.push(`Replace ${deprecatedPackages} deprecated packages, as AI will struggle to find modern solutions.`);
2543
+ recommendations.push(
2544
+ `Replace ${deprecatedPackages} deprecated packages, as AI will struggle to find modern solutions.`
2545
+ );
2369
2546
  }
2370
2547
  if (outdatedRatio > 0.2) {
2371
- recommendations.push(`Update ${outdatedPackages} outdated packages to keep APIs aligned with AI training data.`);
2548
+ recommendations.push(
2549
+ `Update ${outdatedPackages} outdated packages to keep APIs aligned with AI training data.`
2550
+ );
2372
2551
  }
2373
2552
  if (trainingCutoffSkew > 0.5) {
2374
- recommendations.push("High training cutoff skew detected. AI may hallucinate APIs that were introduced recently.");
2553
+ recommendations.push(
2554
+ "High training cutoff skew detected. AI may hallucinate APIs that were introduced recently."
2555
+ );
2375
2556
  }
2376
2557
  return {
2377
2558
  score,
@@ -2412,10 +2593,14 @@ function calculateChangeAmplification(params) {
2412
2593
  else if (score < 90) rating = "contained";
2413
2594
  const recommendations = [];
2414
2595
  if (score < 70 && hotspots.length > 0) {
2415
- recommendations.push(`Refactor top hotspot '${hotspots[0].file}' to reduce coupling (fan-out: ${hotspots[0].fanOut}, fan-in: ${hotspots[0].fanIn}).`);
2596
+ recommendations.push(
2597
+ `Refactor top hotspot '${hotspots[0].file}' to reduce coupling (fan-out: ${hotspots[0].fanOut}, fan-in: ${hotspots[0].fanIn}).`
2598
+ );
2416
2599
  }
2417
2600
  if (maxAmplification > 30) {
2418
- recommendations.push(`Break down key bottlenecks with amplification factor > 30.`);
2601
+ recommendations.push(
2602
+ `Break down key bottlenecks with amplification factor > 30.`
2603
+ );
2419
2604
  }
2420
2605
  return {
2421
2606
  score: Math.round(score),
@@ -2497,7 +2682,11 @@ function calculateExtendedFutureProofScore(params) {
2497
2682
  recommendations.push({ action: rec, estimatedImpact: 8, priority: "high" });
2498
2683
  }
2499
2684
  for (const rec of params.agentGrounding.recommendations) {
2500
- recommendations.push({ action: rec, estimatedImpact: 6, priority: "medium" });
2685
+ recommendations.push({
2686
+ action: rec,
2687
+ estimatedImpact: 6,
2688
+ priority: "medium"
2689
+ });
2501
2690
  }
2502
2691
  for (const rec of params.testability.recommendations) {
2503
2692
  const priority = params.testability.aiChangeSafetyRating === "blind-risk" ? "high" : "medium";
@@ -2508,12 +2697,20 @@ function calculateExtendedFutureProofScore(params) {
2508
2697
  }
2509
2698
  if (params.docDrift) {
2510
2699
  for (const rec of params.docDrift.recommendations) {
2511
- recommendations.push({ action: rec, estimatedImpact: 8, priority: "high" });
2700
+ recommendations.push({
2701
+ action: rec,
2702
+ estimatedImpact: 8,
2703
+ priority: "high"
2704
+ });
2512
2705
  }
2513
2706
  }
2514
2707
  if (params.dependencyHealth) {
2515
2708
  for (const rec of params.dependencyHealth.recommendations) {
2516
- recommendations.push({ action: rec, estimatedImpact: 7, priority: "medium" });
2709
+ recommendations.push({
2710
+ action: rec,
2711
+ estimatedImpact: 7,
2712
+ priority: "medium"
2713
+ });
2517
2714
  }
2518
2715
  }
2519
2716
  const semanticDistanceAvg = params.semanticDistances && params.semanticDistances.length > 0 ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;