@aiready/core 0.9.26 → 0.9.28

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.mjs CHANGED
@@ -1,19 +1,24 @@
1
1
  import {
2
+ CONTEXT_TIER_THRESHOLDS,
2
3
  DEFAULT_TOOL_WEIGHTS,
3
4
  LANGUAGE_EXTENSIONS,
4
5
  Language,
5
6
  ParseError,
7
+ SIZE_ADJUSTED_THRESHOLDS,
6
8
  TOOL_NAME_MAP,
7
9
  calculateOverallScore,
8
10
  formatScore,
9
11
  formatToolScore,
10
12
  generateHTML,
13
+ getProjectSizeTier,
11
14
  getRating,
12
15
  getRatingDisplay,
16
+ getRatingWithContext,
17
+ getRecommendedThreshold,
13
18
  getToolWeight,
14
19
  normalizeToolName,
15
20
  parseWeightString
16
- } from "./chunk-HKSARRCD.mjs";
21
+ } from "./chunk-JJ5JL5FX.mjs";
17
22
 
18
23
  // src/utils/file-scanner.ts
19
24
  import { glob } from "glob";
@@ -414,11 +419,87 @@ function getElapsedTime(startTime) {
414
419
  }
415
420
 
416
421
  // src/business-metrics.ts
422
+ var MODEL_PRICING_PRESETS = {
423
+ "gpt-4": {
424
+ name: "GPT-4",
425
+ pricePer1KInputTokens: 0.03,
426
+ pricePer1KOutputTokens: 0.06,
427
+ contextTier: "standard",
428
+ typicalQueriesPerDevPerDay: 40
429
+ },
430
+ "gpt-4o": {
431
+ name: "GPT-4o",
432
+ pricePer1KInputTokens: 5e-3,
433
+ pricePer1KOutputTokens: 0.015,
434
+ contextTier: "extended",
435
+ typicalQueriesPerDevPerDay: 60
436
+ },
437
+ "gpt-4o-mini": {
438
+ name: "GPT-4o mini",
439
+ pricePer1KInputTokens: 15e-5,
440
+ pricePer1KOutputTokens: 6e-4,
441
+ contextTier: "extended",
442
+ typicalQueriesPerDevPerDay: 120
443
+ },
444
+ "claude-3-5-sonnet": {
445
+ name: "Claude 3.5 Sonnet",
446
+ pricePer1KInputTokens: 3e-3,
447
+ pricePer1KOutputTokens: 0.015,
448
+ contextTier: "extended",
449
+ typicalQueriesPerDevPerDay: 80
450
+ },
451
+ "claude-3-7-sonnet": {
452
+ name: "Claude 3.7 Sonnet",
453
+ pricePer1KInputTokens: 3e-3,
454
+ pricePer1KOutputTokens: 0.015,
455
+ contextTier: "frontier",
456
+ typicalQueriesPerDevPerDay: 80
457
+ },
458
+ "claude-sonnet-4": {
459
+ name: "Claude Sonnet 4",
460
+ pricePer1KInputTokens: 3e-3,
461
+ pricePer1KOutputTokens: 0.015,
462
+ contextTier: "frontier",
463
+ typicalQueriesPerDevPerDay: 80
464
+ },
465
+ "gemini-1-5-pro": {
466
+ name: "Gemini 1.5 Pro",
467
+ pricePer1KInputTokens: 125e-5,
468
+ pricePer1KOutputTokens: 5e-3,
469
+ contextTier: "frontier",
470
+ typicalQueriesPerDevPerDay: 80
471
+ },
472
+ "gemini-2-0-flash": {
473
+ name: "Gemini 2.0 Flash",
474
+ pricePer1KInputTokens: 1e-4,
475
+ pricePer1KOutputTokens: 4e-4,
476
+ contextTier: "frontier",
477
+ typicalQueriesPerDevPerDay: 150
478
+ },
479
+ "copilot": {
480
+ name: "GitHub Copilot (subscription)",
481
+ // Amortized per-request cost for a $19/month plan at 80 queries/day
482
+ pricePer1KInputTokens: 1e-4,
483
+ pricePer1KOutputTokens: 1e-4,
484
+ contextTier: "extended",
485
+ typicalQueriesPerDevPerDay: 80
486
+ },
487
+ "cursor-pro": {
488
+ name: "Cursor Pro (subscription)",
489
+ pricePer1KInputTokens: 1e-4,
490
+ pricePer1KOutputTokens: 1e-4,
491
+ contextTier: "frontier",
492
+ typicalQueriesPerDevPerDay: 100
493
+ }
494
+ };
495
+ function getModelPreset(modelId) {
496
+ return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["gpt-4o"];
497
+ }
417
498
  var DEFAULT_COST_CONFIG = {
418
- pricePer1KTokens: 0.01,
419
- // $0.01 per 1K tokens (GPT-4)
420
- queriesPerDevPerDay: 50,
421
- // Average AI queries per developer
499
+ pricePer1KTokens: 5e-3,
500
+ // GPT-4o input price (updated from GPT-4 era 0.01)
501
+ queriesPerDevPerDay: 60,
502
+ // Average AI queries per developer (updated: 40→60 as of 2026)
422
503
  developerCount: 5,
423
504
  // Default team size
424
505
  daysPerMonth: 30
@@ -469,43 +550,63 @@ function calculateProductivityImpact(issues, hourlyRate = DEFAULT_HOURLY_RATE) {
469
550
  }
470
551
  function predictAcceptanceRate(toolOutputs) {
471
552
  const factors = [];
553
+ const baseRate = 0.3;
472
554
  const patterns = toolOutputs.get("pattern-detect");
473
555
  if (patterns) {
474
- const patternImpact = (patterns.score - 50) * 0.3;
556
+ const patternImpact = (patterns.score - 50) * 3e-3;
475
557
  factors.push({
476
558
  name: "Semantic Duplication",
477
- impact: Math.round(patternImpact)
559
+ impact: Math.round(patternImpact * 100)
478
560
  });
479
561
  }
480
562
  const context = toolOutputs.get("context-analyzer");
481
563
  if (context) {
482
- const contextImpact = (context.score - 50) * 0.4;
564
+ const contextImpact = (context.score - 50) * 4e-3;
483
565
  factors.push({
484
566
  name: "Context Efficiency",
485
- impact: Math.round(contextImpact)
567
+ impact: Math.round(contextImpact * 100)
486
568
  });
487
569
  }
488
570
  const consistency = toolOutputs.get("consistency");
489
571
  if (consistency) {
490
- const consistencyImpact = (consistency.score - 50) * 0.3;
572
+ const consistencyImpact = (consistency.score - 50) * 2e-3;
491
573
  factors.push({
492
574
  name: "Code Consistency",
493
- impact: Math.round(consistencyImpact)
575
+ impact: Math.round(consistencyImpact * 100)
576
+ });
577
+ }
578
+ const hallucinationRisk = toolOutputs.get("hallucination-risk");
579
+ if (hallucinationRisk) {
580
+ const hrImpact = (50 - hallucinationRisk.score) * 2e-3;
581
+ factors.push({
582
+ name: "Hallucination Risk",
583
+ impact: Math.round(hrImpact * 100)
494
584
  });
495
585
  }
496
- const baseRate = 0.65;
497
586
  const totalImpact = factors.reduce((sum, f) => sum + f.impact / 100, 0);
498
- const rate = Math.max(0.1, Math.min(0.95, baseRate + totalImpact));
499
- const confidence = toolOutputs.size >= 3 ? 0.8 : toolOutputs.size >= 2 ? 0.6 : 0.4;
587
+ const rate = Math.max(0.05, Math.min(0.8, baseRate + totalImpact));
588
+ let confidence;
589
+ if (toolOutputs.size >= 4) confidence = 0.75;
590
+ else if (toolOutputs.size >= 3) confidence = 0.65;
591
+ else if (toolOutputs.size >= 2) confidence = 0.5;
592
+ else confidence = 0.35;
500
593
  return {
501
594
  rate: Math.round(rate * 100) / 100,
502
595
  confidence,
503
596
  factors
504
597
  };
505
598
  }
506
- function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentation, consistencyScore, totalFiles) {
507
- const budgetFactor = Math.min(100, Math.max(0, (contextBudget - 5e3) / 250));
508
- const depthFactor = Math.min(100, Math.max(0, (importDepth - 5) * 10));
599
+ function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentation, consistencyScore, totalFiles, modelTier = "standard") {
600
+ const tierThresholds = CONTEXT_TIER_THRESHOLDS[modelTier];
601
+ const idealBudget = tierThresholds.idealTokens;
602
+ const criticalBudget = tierThresholds.criticalTokens;
603
+ const idealDepth = tierThresholds.idealDepth;
604
+ const budgetRange = criticalBudget - idealBudget;
605
+ const budgetFactor = Math.min(100, Math.max(
606
+ 0,
607
+ (contextBudget - idealBudget) / budgetRange * 100
608
+ ));
609
+ const depthFactor = Math.min(100, Math.max(0, (importDepth - idealDepth) * 10));
509
610
  const fragmentationFactor = Math.min(100, Math.max(0, (fragmentation - 0.3) * 250));
510
611
  const consistencyFactor = Math.min(100, Math.max(0, 100 - consistencyScore));
511
612
  const fileFactor = Math.min(100, Math.max(0, (totalFiles - 50) / 5));
@@ -525,12 +626,12 @@ function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentat
525
626
  {
526
627
  name: "Context Budget",
527
628
  contribution: Math.round(budgetFactor * 0.35),
528
- description: `${Math.round(contextBudget)} tokens required`
629
+ description: `${Math.round(contextBudget)} tokens required (${modelTier} model tier: ideal <${idealBudget.toLocaleString()})`
529
630
  },
530
631
  {
531
632
  name: "Import Depth",
532
633
  contribution: Math.round(depthFactor * 0.2),
533
- description: `${importDepth.toFixed(1)} average levels`
634
+ description: `${importDepth.toFixed(1)} average levels (ideal <${idealDepth} for ${modelTier})`
534
635
  },
535
636
  {
536
637
  name: "Code Fragmentation",
@@ -1475,6 +1576,449 @@ function calculateFutureProofScore(params) {
1475
1576
  recommendations
1476
1577
  };
1477
1578
  }
1579
+ function calculateHallucinationRisk(params) {
1580
+ const {
1581
+ overloadedSymbols,
1582
+ magicLiterals,
1583
+ booleanTraps,
1584
+ implicitSideEffects,
1585
+ deepCallbacks,
1586
+ ambiguousNames,
1587
+ undocumentedExports,
1588
+ totalSymbols,
1589
+ totalExports
1590
+ } = params;
1591
+ if (totalSymbols === 0) {
1592
+ return {
1593
+ score: 0,
1594
+ rating: "minimal",
1595
+ signals: [],
1596
+ topRisk: "No symbols to analyze",
1597
+ recommendations: []
1598
+ };
1599
+ }
1600
+ const overloadRatio = Math.min(1, overloadedSymbols / Math.max(1, totalSymbols));
1601
+ const overloadSignal = {
1602
+ name: "Symbol Overloading",
1603
+ count: overloadedSymbols,
1604
+ riskContribution: Math.round(overloadRatio * 100 * 0.25),
1605
+ // 25% weight
1606
+ description: `${overloadedSymbols} overloaded symbols \u2014 AI picks wrong signature`
1607
+ };
1608
+ const magicRatio = Math.min(1, magicLiterals / Math.max(1, totalSymbols * 2));
1609
+ const magicSignal = {
1610
+ name: "Magic Literals",
1611
+ count: magicLiterals,
1612
+ riskContribution: Math.round(magicRatio * 100 * 0.2),
1613
+ // 20% weight
1614
+ description: `${magicLiterals} unnamed constants \u2014 AI invents wrong values`
1615
+ };
1616
+ const trapRatio = Math.min(1, booleanTraps / Math.max(1, totalSymbols));
1617
+ const trapSignal = {
1618
+ name: "Boolean Traps",
1619
+ count: booleanTraps,
1620
+ riskContribution: Math.round(trapRatio * 100 * 0.2),
1621
+ // 20% weight
1622
+ description: `${booleanTraps} boolean trap parameters \u2014 AI inverts intent`
1623
+ };
1624
+ const sideEffectRatio = Math.min(1, implicitSideEffects / Math.max(1, totalExports));
1625
+ const sideEffectSignal = {
1626
+ name: "Implicit Side Effects",
1627
+ count: implicitSideEffects,
1628
+ riskContribution: Math.round(sideEffectRatio * 100 * 0.15),
1629
+ // 15% weight
1630
+ description: `${implicitSideEffects} functions with implicit side effects \u2014 AI misses contracts`
1631
+ };
1632
+ const callbackRatio = Math.min(1, deepCallbacks / Math.max(1, totalSymbols * 0.1));
1633
+ const callbackSignal = {
1634
+ name: "Callback Nesting",
1635
+ count: deepCallbacks,
1636
+ riskContribution: Math.round(callbackRatio * 100 * 0.1),
1637
+ // 10% weight
1638
+ description: `${deepCallbacks} deep callback chains \u2014 AI loses control flow context`
1639
+ };
1640
+ const ambiguousRatio = Math.min(1, ambiguousNames / Math.max(1, totalSymbols));
1641
+ const ambiguousSignal = {
1642
+ name: "Ambiguous Names",
1643
+ count: ambiguousNames,
1644
+ riskContribution: Math.round(ambiguousRatio * 100 * 0.1),
1645
+ // 10% weight
1646
+ description: `${ambiguousNames} non-descriptive identifiers \u2014 AI guesses wrong intent`
1647
+ };
1648
+ const undocRatio = Math.min(1, undocumentedExports / Math.max(1, totalExports));
1649
+ const undocSignal = {
1650
+ name: "Undocumented Exports",
1651
+ count: undocumentedExports,
1652
+ riskContribution: Math.round(undocRatio * 100 * 0.1),
1653
+ // 10% weight
1654
+ description: `${undocumentedExports} public functions without docs \u2014 AI fabricates behavior`
1655
+ };
1656
+ const signals = [
1657
+ overloadSignal,
1658
+ magicSignal,
1659
+ trapSignal,
1660
+ sideEffectSignal,
1661
+ callbackSignal,
1662
+ ambiguousSignal,
1663
+ undocSignal
1664
+ ];
1665
+ const score = Math.min(100, signals.reduce((sum, s) => sum + s.riskContribution, 0));
1666
+ let rating;
1667
+ if (score < 10) rating = "minimal";
1668
+ else if (score < 25) rating = "low";
1669
+ else if (score < 50) rating = "moderate";
1670
+ else if (score < 75) rating = "high";
1671
+ else rating = "severe";
1672
+ const topSignal = signals.reduce((a, b) => a.riskContribution > b.riskContribution ? a : b);
1673
+ const topRisk = topSignal.riskContribution > 0 ? topSignal.description : "No significant hallucination risks detected";
1674
+ const recommendations = [];
1675
+ if (overloadSignal.riskContribution > 5) {
1676
+ recommendations.push(`Rename ${overloadedSymbols} overloaded symbols to unique, intent-revealing names`);
1677
+ }
1678
+ if (magicSignal.riskContribution > 5) {
1679
+ recommendations.push(`Extract ${magicLiterals} magic literals into named constants`);
1680
+ }
1681
+ if (trapSignal.riskContribution > 5) {
1682
+ recommendations.push(`Replace ${booleanTraps} boolean traps with named options objects`);
1683
+ }
1684
+ if (undocSignal.riskContribution > 5) {
1685
+ recommendations.push(`Add JSDoc/docstrings to ${undocumentedExports} undocumented public functions`);
1686
+ }
1687
+ if (sideEffectSignal.riskContribution > 5) {
1688
+ recommendations.push("Mark functions with side effects explicitly in their names or docs");
1689
+ }
1690
+ return {
1691
+ score: Math.round(score),
1692
+ rating,
1693
+ signals: signals.filter((s) => s.count > 0),
1694
+ topRisk,
1695
+ recommendations
1696
+ };
1697
+ }
1698
+ function calculateAgentGrounding(params) {
1699
+ const {
1700
+ deepDirectories,
1701
+ totalDirectories,
1702
+ vagueFileNames,
1703
+ totalFiles,
1704
+ hasRootReadme,
1705
+ readmeIsFresh,
1706
+ barrelExports,
1707
+ untypedExports,
1708
+ totalExports,
1709
+ inconsistentDomainTerms,
1710
+ domainVocabularySize
1711
+ } = params;
1712
+ const deepDirRatio = totalDirectories > 0 ? deepDirectories / totalDirectories : 0;
1713
+ const structureClarityScore = Math.max(0, Math.round(100 - deepDirRatio * 80));
1714
+ const vagueRatio = totalFiles > 0 ? vagueFileNames / totalFiles : 0;
1715
+ const selfDocumentationScore = Math.max(0, Math.round(100 - vagueRatio * 90));
1716
+ let entryPointScore = 60;
1717
+ if (hasRootReadme) entryPointScore += 25;
1718
+ if (readmeIsFresh) entryPointScore += 10;
1719
+ const barrelRatio = totalFiles > 0 ? barrelExports / (totalFiles * 0.1) : 0;
1720
+ entryPointScore += Math.round(Math.min(5, barrelRatio * 5));
1721
+ entryPointScore = Math.min(100, entryPointScore);
1722
+ const untypedRatio = totalExports > 0 ? untypedExports / totalExports : 0;
1723
+ const apiClarityScore = Math.max(0, Math.round(100 - untypedRatio * 70));
1724
+ const inconsistencyRatio = domainVocabularySize > 0 ? inconsistentDomainTerms / domainVocabularySize : 0;
1725
+ const domainConsistencyScore = Math.max(0, Math.round(100 - inconsistencyRatio * 80));
1726
+ const score = Math.round(
1727
+ structureClarityScore * 0.2 + selfDocumentationScore * 0.25 + entryPointScore * 0.2 + apiClarityScore * 0.15 + domainConsistencyScore * 0.2
1728
+ );
1729
+ let rating;
1730
+ if (score >= 85) rating = "excellent";
1731
+ else if (score >= 70) rating = "good";
1732
+ else if (score >= 50) rating = "moderate";
1733
+ else if (score >= 30) rating = "poor";
1734
+ else rating = "disorienting";
1735
+ const recommendations = [];
1736
+ if (structureClarityScore < 70) {
1737
+ recommendations.push(`Flatten ${deepDirectories} overly-deep directories to improve agent navigation`);
1738
+ }
1739
+ if (selfDocumentationScore < 70) {
1740
+ recommendations.push(`Rename ${vagueFileNames} vague files (utils, helpers, misc) to domain-specific names`);
1741
+ }
1742
+ if (!hasRootReadme) {
1743
+ recommendations.push("Add a root README.md so agents understand the project context immediately");
1744
+ } else if (!readmeIsFresh) {
1745
+ recommendations.push("Update README.md \u2014 stale entry-point documentation disorients agents");
1746
+ }
1747
+ if (apiClarityScore < 70) {
1748
+ recommendations.push(`Add TypeScript types to ${untypedExports} untyped exports to improve API discoverability`);
1749
+ }
1750
+ if (domainConsistencyScore < 70) {
1751
+ recommendations.push(`Unify ${inconsistentDomainTerms} inconsistent domain terms \u2014 agents need one word per concept`);
1752
+ }
1753
+ return {
1754
+ score,
1755
+ rating,
1756
+ dimensions: {
1757
+ structureClarityScore,
1758
+ selfDocumentationScore,
1759
+ entryPointScore,
1760
+ apiClarityScore,
1761
+ domainConsistencyScore
1762
+ },
1763
+ recommendations
1764
+ };
1765
+ }
1766
+ function calculateTestabilityIndex(params) {
1767
+ const {
1768
+ testFiles,
1769
+ sourceFiles,
1770
+ pureFunctions,
1771
+ totalFunctions,
1772
+ injectionPatterns,
1773
+ totalClasses,
1774
+ bloatedInterfaces,
1775
+ totalInterfaces,
1776
+ externalStateMutations,
1777
+ hasTestFramework
1778
+ } = params;
1779
+ const rawCoverageRatio = sourceFiles > 0 ? testFiles / sourceFiles : 0;
1780
+ const testCoverageRatio = Math.min(100, Math.round(rawCoverageRatio * 100));
1781
+ const purityRatio = totalFunctions > 0 ? pureFunctions / totalFunctions : 0.5;
1782
+ const purityScore = Math.round(purityRatio * 100);
1783
+ const injectionRatio = totalClasses > 0 ? injectionPatterns / totalClasses : 0.5;
1784
+ const dependencyInjectionScore = Math.round(Math.min(100, injectionRatio * 100));
1785
+ const bloatedRatio = totalInterfaces > 0 ? bloatedInterfaces / totalInterfaces : 0;
1786
+ const interfaceFocusScore = Math.max(0, Math.round(100 - bloatedRatio * 80));
1787
+ const mutationRatio = totalFunctions > 0 ? externalStateMutations / totalFunctions : 0;
1788
+ const observabilityScore = Math.max(0, Math.round(100 - mutationRatio * 100));
1789
+ const frameworkWeight = hasTestFramework ? 1 : 0.8;
1790
+ const rawScore = (testCoverageRatio * 0.3 + purityScore * 0.25 + dependencyInjectionScore * 0.2 + interfaceFocusScore * 0.1 + observabilityScore * 0.15) * frameworkWeight;
1791
+ const score = Math.max(0, Math.min(100, Math.round(rawScore)));
1792
+ let rating;
1793
+ if (score >= 85) rating = "excellent";
1794
+ else if (score >= 70) rating = "good";
1795
+ else if (score >= 50) rating = "moderate";
1796
+ else if (score >= 30) rating = "poor";
1797
+ else rating = "unverifiable";
1798
+ let aiChangeSafetyRating;
1799
+ if (rawCoverageRatio >= 0.5 && score >= 70) aiChangeSafetyRating = "safe";
1800
+ else if (rawCoverageRatio >= 0.2 && score >= 50) aiChangeSafetyRating = "moderate-risk";
1801
+ else if (rawCoverageRatio > 0) aiChangeSafetyRating = "high-risk";
1802
+ else aiChangeSafetyRating = "blind-risk";
1803
+ const recommendations = [];
1804
+ if (!hasTestFramework) {
1805
+ recommendations.push("Add a testing framework (Jest, Vitest, pytest) \u2014 AI changes cannot be verified without tests");
1806
+ }
1807
+ if (rawCoverageRatio < 0.3) {
1808
+ const neededTests = Math.round(sourceFiles * 0.3 - testFiles);
1809
+ recommendations.push(`Add ~${neededTests} test files to reach 30% coverage ratio \u2014 minimum for safe AI assistance`);
1810
+ }
1811
+ if (purityScore < 50) {
1812
+ recommendations.push("Extract pure functions from side-effectful code \u2014 pure functions are trivially AI-testable");
1813
+ }
1814
+ if (dependencyInjectionScore < 50 && totalClasses > 0) {
1815
+ recommendations.push("Adopt dependency injection \u2014 makes classes mockable and AI-generated code verifiable");
1816
+ }
1817
+ if (externalStateMutations > totalFunctions * 0.3) {
1818
+ recommendations.push("Reduce direct state mutations \u2014 return values instead to improve observability");
1819
+ }
1820
+ return {
1821
+ score,
1822
+ rating,
1823
+ dimensions: {
1824
+ testCoverageRatio,
1825
+ purityScore,
1826
+ dependencyInjectionScore,
1827
+ interfaceFocusScore,
1828
+ observabilityScore
1829
+ },
1830
+ aiChangeSafetyRating,
1831
+ recommendations
1832
+ };
1833
+ }
1834
+ function calculateDocDrift(params) {
1835
+ const { uncommentedExports, totalExports, outdatedComments, undocumentedComplexity } = params;
1836
+ const uncommentedRatio = totalExports > 0 ? uncommentedExports / totalExports : 0;
1837
+ const outdatedScore = Math.min(100, outdatedComments * 15);
1838
+ const uncommentedScore = Math.min(100, uncommentedRatio * 100);
1839
+ const complexityScore = Math.min(100, undocumentedComplexity * 10);
1840
+ const score = Math.round(
1841
+ outdatedScore * 0.6 + uncommentedScore * 0.2 + complexityScore * 0.2
1842
+ );
1843
+ const finalScore = Math.min(100, Math.max(0, score));
1844
+ let rating;
1845
+ if (finalScore < 10) rating = "minimal";
1846
+ else if (finalScore < 30) rating = "low";
1847
+ else if (finalScore < 60) rating = "moderate";
1848
+ else if (finalScore < 85) rating = "high";
1849
+ else rating = "severe";
1850
+ const recommendations = [];
1851
+ if (outdatedComments > 0) {
1852
+ recommendations.push(`Update or remove ${outdatedComments} outdated comments that contradict the code.`);
1853
+ }
1854
+ if (uncommentedRatio > 0.3) {
1855
+ recommendations.push(`Add JSDoc to ${uncommentedExports} uncommented exports.`);
1856
+ }
1857
+ if (undocumentedComplexity > 0) {
1858
+ recommendations.push(`Explain the business logic for ${undocumentedComplexity} highly complex functions.`);
1859
+ }
1860
+ return {
1861
+ score: finalScore,
1862
+ rating,
1863
+ dimensions: {
1864
+ uncommentedExports,
1865
+ outdatedComments,
1866
+ undocumentedComplexity
1867
+ },
1868
+ recommendations
1869
+ };
1870
+ }
1871
+ function calculateDependencyHealth(params) {
1872
+ const { totalPackages, outdatedPackages, deprecatedPackages, trainingCutoffSkew } = params;
1873
+ const outdatedRatio = totalPackages > 0 ? outdatedPackages / totalPackages : 0;
1874
+ const deprecatedRatio = totalPackages > 0 ? deprecatedPackages / totalPackages : 0;
1875
+ const outdatedScore = Math.max(0, 100 - outdatedRatio * 200);
1876
+ const deprecatedScore = Math.max(0, 100 - deprecatedRatio * 500);
1877
+ const skewScore = Math.max(0, 100 - trainingCutoffSkew * 100);
1878
+ const rawScore = outdatedScore * 0.3 + deprecatedScore * 0.4 + skewScore * 0.3;
1879
+ const score = Math.round(Math.min(100, Math.max(0, rawScore)));
1880
+ let rating;
1881
+ if (score >= 85) rating = "excellent";
1882
+ else if (score >= 70) rating = "good";
1883
+ else if (score >= 50) rating = "moderate";
1884
+ else if (score >= 30) rating = "poor";
1885
+ else rating = "hazardous";
1886
+ let aiKnowledgeConfidence;
1887
+ if (trainingCutoffSkew < 0.2 && deprecatedPackages === 0) aiKnowledgeConfidence = "high";
1888
+ else if (trainingCutoffSkew < 0.5 && deprecatedPackages <= 2) aiKnowledgeConfidence = "moderate";
1889
+ else if (trainingCutoffSkew < 0.8) aiKnowledgeConfidence = "low";
1890
+ else aiKnowledgeConfidence = "blind";
1891
+ const recommendations = [];
1892
+ if (deprecatedPackages > 0) {
1893
+ recommendations.push(`Replace ${deprecatedPackages} deprecated packages, as AI will struggle to find modern solutions.`);
1894
+ }
1895
+ if (outdatedRatio > 0.2) {
1896
+ recommendations.push(`Update ${outdatedPackages} outdated packages to keep APIs aligned with AI training data.`);
1897
+ }
1898
+ if (trainingCutoffSkew > 0.5) {
1899
+ recommendations.push("High training cutoff skew detected. AI may hallucinate APIs that were introduced recently.");
1900
+ }
1901
+ return {
1902
+ score,
1903
+ rating,
1904
+ dimensions: {
1905
+ outdatedPackages,
1906
+ deprecatedPackages,
1907
+ trainingCutoffSkew
1908
+ },
1909
+ aiKnowledgeConfidence,
1910
+ recommendations
1911
+ };
1912
+ }
1913
+ function calculateExtendedFutureProofScore(params) {
1914
+ const loadScore = 100 - params.cognitiveLoad.score;
1915
+ const entropyScore = 100 - params.patternEntropy.entropy * 100;
1916
+ const cohesionScore = params.conceptCohesion.score * 100;
1917
+ const hallucinationScore = 100 - params.hallucinationRisk.score;
1918
+ const groundingScore = params.agentGrounding.score;
1919
+ const testabilityScore = params.testability.score;
1920
+ const docDriftScore = params.docDrift ? 100 - params.docDrift.score : 100;
1921
+ const depsHealthScore = params.dependencyHealth ? params.dependencyHealth.score : 100;
1922
+ let totalWeight = 0.8;
1923
+ let overall = loadScore * 0.15 + entropyScore * 0.1 + cohesionScore * 0.1 + hallucinationScore * 0.15 + groundingScore * 0.15 + testabilityScore * 0.15;
1924
+ if (params.docDrift) {
1925
+ overall += docDriftScore * 0.1;
1926
+ totalWeight += 0.1;
1927
+ }
1928
+ if (params.dependencyHealth) {
1929
+ overall += depsHealthScore * 0.1;
1930
+ totalWeight += 0.1;
1931
+ }
1932
+ overall = Math.round(overall / totalWeight);
1933
+ const factors = [
1934
+ {
1935
+ name: "Cognitive Load",
1936
+ impact: Math.round(loadScore - 50),
1937
+ description: params.cognitiveLoad.rating
1938
+ },
1939
+ {
1940
+ name: "Pattern Entropy",
1941
+ impact: Math.round(entropyScore - 50),
1942
+ description: params.patternEntropy.rating
1943
+ },
1944
+ {
1945
+ name: "Concept Cohesion",
1946
+ impact: Math.round(cohesionScore - 50),
1947
+ description: params.conceptCohesion.rating
1948
+ },
1949
+ {
1950
+ name: "Hallucination Risk",
1951
+ impact: Math.round(hallucinationScore - 50),
1952
+ description: `${params.hallucinationRisk.rating} risk (${params.hallucinationRisk.score}/100 raw)`
1953
+ },
1954
+ {
1955
+ name: "Agent Grounding",
1956
+ impact: Math.round(groundingScore - 50),
1957
+ description: params.agentGrounding.rating
1958
+ },
1959
+ {
1960
+ name: "Testability",
1961
+ impact: Math.round(testabilityScore - 50),
1962
+ description: `${params.testability.rating} \u2014 AI changes are ${params.testability.aiChangeSafetyRating}`
1963
+ }
1964
+ ];
1965
+ if (params.docDrift) {
1966
+ factors.push({
1967
+ name: "Documentation Drift",
1968
+ impact: Math.round(docDriftScore - 50),
1969
+ description: `${params.docDrift.rating} risk of hallucination from drift`
1970
+ });
1971
+ }
1972
+ if (params.dependencyHealth) {
1973
+ factors.push({
1974
+ name: "Dependency Health",
1975
+ impact: Math.round(depsHealthScore - 50),
1976
+ description: `${params.dependencyHealth.rating} health \u2014 AI knowledge is ${params.dependencyHealth.aiKnowledgeConfidence}`
1977
+ });
1978
+ }
1979
+ const recommendations = [];
1980
+ for (const rec of params.hallucinationRisk.recommendations) {
1981
+ recommendations.push({ action: rec, estimatedImpact: 8, priority: "high" });
1982
+ }
1983
+ for (const rec of params.agentGrounding.recommendations) {
1984
+ recommendations.push({ action: rec, estimatedImpact: 6, priority: "medium" });
1985
+ }
1986
+ for (const rec of params.testability.recommendations) {
1987
+ const priority = params.testability.aiChangeSafetyRating === "blind-risk" ? "high" : "medium";
1988
+ recommendations.push({ action: rec, estimatedImpact: 10, priority });
1989
+ }
1990
+ for (const rec of params.patternEntropy.recommendations) {
1991
+ recommendations.push({ action: rec, estimatedImpact: 5, priority: "low" });
1992
+ }
1993
+ if (params.docDrift) {
1994
+ for (const rec of params.docDrift.recommendations) {
1995
+ recommendations.push({ action: rec, estimatedImpact: 8, priority: "high" });
1996
+ }
1997
+ }
1998
+ if (params.dependencyHealth) {
1999
+ for (const rec of params.dependencyHealth.recommendations) {
2000
+ recommendations.push({ action: rec, estimatedImpact: 7, priority: "medium" });
2001
+ }
2002
+ }
2003
+ const semanticDistanceAvg = params.semanticDistances && params.semanticDistances.length > 0 ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
2004
+ return {
2005
+ toolName: "future-proof",
2006
+ score: overall,
2007
+ rawMetrics: {
2008
+ cognitiveLoadScore: params.cognitiveLoad.score,
2009
+ entropyScore: params.patternEntropy.entropy,
2010
+ cohesionScore: params.conceptCohesion.score,
2011
+ hallucinationRiskScore: params.hallucinationRisk.score,
2012
+ agentGroundingScore: params.agentGrounding.score,
2013
+ testabilityScore: params.testability.score,
2014
+ docDriftScore: params.docDrift?.score,
2015
+ dependencyHealthScore: params.dependencyHealth?.score,
2016
+ semanticDistanceAvg
2017
+ },
2018
+ factors,
2019
+ recommendations
2020
+ };
2021
+ }
1478
2022
 
1479
2023
  // src/utils/history.ts
1480
2024
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
@@ -1550,20 +2094,28 @@ function clearHistory(rootDir) {
1550
2094
  }
1551
2095
  }
1552
2096
  export {
2097
+ CONTEXT_TIER_THRESHOLDS,
1553
2098
  DEFAULT_COST_CONFIG,
1554
2099
  DEFAULT_EXCLUDE,
1555
2100
  DEFAULT_TOOL_WEIGHTS,
1556
2101
  LANGUAGE_EXTENSIONS,
1557
2102
  Language,
2103
+ MODEL_PRICING_PRESETS,
1558
2104
  ParseError,
1559
2105
  ParserFactory,
1560
2106
  PythonParser,
2107
+ SIZE_ADJUSTED_THRESHOLDS,
1561
2108
  TOOL_NAME_MAP,
1562
2109
  TypeScriptParser,
2110
+ calculateAgentGrounding,
1563
2111
  calculateCognitiveLoad,
1564
2112
  calculateComprehensionDifficulty,
1565
2113
  calculateConceptCohesion,
2114
+ calculateDependencyHealth,
2115
+ calculateDocDrift,
2116
+ calculateExtendedFutureProofScore,
1566
2117
  calculateFutureProofScore,
2118
+ calculateHallucinationRisk,
1567
2119
  calculateImportSimilarity,
1568
2120
  calculateKnowledgeConcentration,
1569
2121
  calculateMonthlyCost,
@@ -1574,6 +2126,7 @@ export {
1574
2126
  calculateScoreTrend,
1575
2127
  calculateSemanticDistance,
1576
2128
  calculateTechnicalDebtInterest,
2129
+ calculateTestabilityIndex,
1577
2130
  clearHistory,
1578
2131
  estimateTokens,
1579
2132
  exportHistory,
@@ -1589,9 +2142,13 @@ export {
1589
2142
  getElapsedTime,
1590
2143
  getFileExtension,
1591
2144
  getHistorySummary,
2145
+ getModelPreset,
1592
2146
  getParser,
2147
+ getProjectSizeTier,
1593
2148
  getRating,
1594
2149
  getRatingDisplay,
2150
+ getRatingWithContext,
2151
+ getRecommendedThreshold,
1595
2152
  getSupportedLanguages,
1596
2153
  getToolWeight,
1597
2154
  handleCLIError,