@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/chunk-JJ5JL5FX.mjs +408 -0
- package/dist/client.d.mts +70 -2
- package/dist/client.d.ts +70 -2
- package/dist/client.js +58 -2
- package/dist/client.mjs +11 -1
- package/dist/index.d.mts +260 -9
- package/dist/index.d.ts +260 -9
- package/dist/index.js +631 -20
- package/dist/index.mjs +576 -19
- package/package.json +1 -1
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-
|
|
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:
|
|
419
|
-
//
|
|
420
|
-
queriesPerDevPerDay:
|
|
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) *
|
|
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) *
|
|
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) *
|
|
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.
|
|
499
|
-
|
|
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
|
|
508
|
-
const
|
|
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,
|