@aiready/core 0.9.37 → 0.9.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-UIWL5JQB.mjs +410 -0
- package/dist/chunk-UQGI67WR.mjs +410 -0
- package/dist/client.d.mts +14 -6
- package/dist/client.d.ts +14 -6
- package/dist/client.js +4 -4
- package/dist/client.mjs +1 -1
- package/dist/index.d.mts +167 -280
- package/dist/index.d.ts +167 -280
- package/dist/index.js +434 -556
- package/dist/index.mjs +415 -540
- package/package.json +2 -1
package/dist/index.mjs
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
getToolWeight,
|
|
19
19
|
normalizeToolName,
|
|
20
20
|
parseWeightString
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-UQGI67WR.mjs";
|
|
22
22
|
|
|
23
23
|
// src/utils/file-scanner.ts
|
|
24
24
|
import { glob } from "glob";
|
|
@@ -513,8 +513,43 @@ function handleCLIError(error, commandName) {
|
|
|
513
513
|
function getElapsedTime(startTime) {
|
|
514
514
|
return ((Date.now() - startTime) / 1e3).toFixed(2);
|
|
515
515
|
}
|
|
516
|
+
function getScoreBar(val) {
|
|
517
|
+
return "\u2588".repeat(Math.round(val / 10)).padEnd(10, "\u2591");
|
|
518
|
+
}
|
|
519
|
+
function getSafetyIcon(rating) {
|
|
520
|
+
switch (rating) {
|
|
521
|
+
case "safe":
|
|
522
|
+
return "\u2705";
|
|
523
|
+
case "moderate-risk":
|
|
524
|
+
return "\u26A0\uFE0F ";
|
|
525
|
+
case "high-risk":
|
|
526
|
+
return "\u{1F534}";
|
|
527
|
+
case "blind-risk":
|
|
528
|
+
return "\u{1F480}";
|
|
529
|
+
default:
|
|
530
|
+
return "\u2753";
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
function getSeverityColor(severity, chalk) {
|
|
534
|
+
switch (severity.toLowerCase()) {
|
|
535
|
+
case "critical":
|
|
536
|
+
case "high-risk":
|
|
537
|
+
case "blind-risk":
|
|
538
|
+
return chalk.red;
|
|
539
|
+
case "major":
|
|
540
|
+
case "moderate-risk":
|
|
541
|
+
return chalk.yellow;
|
|
542
|
+
case "minor":
|
|
543
|
+
case "safe":
|
|
544
|
+
return chalk.green;
|
|
545
|
+
case "info":
|
|
546
|
+
return chalk.blue;
|
|
547
|
+
default:
|
|
548
|
+
return chalk.white;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
516
551
|
|
|
517
|
-
// src/business-
|
|
552
|
+
// src/business/pricing-models.ts
|
|
518
553
|
var MODEL_PRICING_PRESETS = {
|
|
519
554
|
"gpt-5.3": {
|
|
520
555
|
name: "GPT-5.3",
|
|
@@ -576,30 +611,17 @@ var MODEL_PRICING_PRESETS = {
|
|
|
576
611
|
function getModelPreset(modelId) {
|
|
577
612
|
return MODEL_PRICING_PRESETS[modelId] ?? MODEL_PRICING_PRESETS["claude-4.6"];
|
|
578
613
|
}
|
|
614
|
+
|
|
615
|
+
// src/business/cost-metrics.ts
|
|
579
616
|
var DEFAULT_COST_CONFIG = {
|
|
580
617
|
pricePer1KTokens: 5e-3,
|
|
581
|
-
// GPT-4o input price (updated from GPT-4 era 0.01)
|
|
582
618
|
queriesPerDevPerDay: 60,
|
|
583
|
-
// Average AI queries per developer (updated: 40→60 as of 2026)
|
|
584
619
|
developerCount: 5,
|
|
585
|
-
// Default team size
|
|
586
620
|
daysPerMonth: 30
|
|
587
621
|
};
|
|
588
|
-
var SEVERITY_TIME_ESTIMATES = {
|
|
589
|
-
critical: 4,
|
|
590
|
-
// Complex architectural issues (industry avg: 6.8h)
|
|
591
|
-
major: 2,
|
|
592
|
-
// Significant refactoring needed (avg: 3.4h)
|
|
593
|
-
minor: 0.5,
|
|
594
|
-
// Simple naming/style fixes (avg: 0.8h)
|
|
595
|
-
info: 0.25
|
|
596
|
-
// Documentation improvements
|
|
597
|
-
};
|
|
598
|
-
var DEFAULT_HOURLY_RATE = 75;
|
|
599
622
|
function calculateMonthlyCost(tokenWaste, config = {}) {
|
|
600
623
|
const budget = calculateTokenBudget({
|
|
601
624
|
totalContextTokens: tokenWaste * 2.5,
|
|
602
|
-
// Heuristic: context is larger than waste
|
|
603
625
|
wastedTokens: {
|
|
604
626
|
duplication: tokenWaste * 0.7,
|
|
605
627
|
fragmentation: tokenWaste * 0.3,
|
|
@@ -615,7 +637,10 @@ function calculateTokenBudget(params) {
|
|
|
615
637
|
const totalWaste = wastedTokens.duplication + wastedTokens.fragmentation + wastedTokens.chattiness;
|
|
616
638
|
const efficiencyRatio = Math.max(
|
|
617
639
|
0,
|
|
618
|
-
Math.min(
|
|
640
|
+
Math.min(
|
|
641
|
+
1,
|
|
642
|
+
(totalContextTokens - totalWaste) / Math.max(1, totalContextTokens)
|
|
643
|
+
)
|
|
619
644
|
);
|
|
620
645
|
return {
|
|
621
646
|
totalContextTokens: Math.round(totalContextTokens),
|
|
@@ -630,7 +655,6 @@ function calculateTokenBudget(params) {
|
|
|
630
655
|
},
|
|
631
656
|
efficiencyRatio: Math.round(efficiencyRatio * 100) / 100,
|
|
632
657
|
potentialRetrievableTokens: Math.round(totalWaste * 0.8)
|
|
633
|
-
// Heuristic: 80% is fixable
|
|
634
658
|
};
|
|
635
659
|
}
|
|
636
660
|
function estimateCostFromBudget(budget, model, config = {}) {
|
|
@@ -639,7 +663,8 @@ function estimateCostFromBudget(budget, model, config = {}) {
|
|
|
639
663
|
const tokensPerDay = wastePerQuery * cfg.queriesPerDevPerDay;
|
|
640
664
|
const tokensPerMonth = tokensPerDay * cfg.daysPerMonth;
|
|
641
665
|
const totalWeight = cfg.developerCount;
|
|
642
|
-
const
|
|
666
|
+
const price = config.pricePer1KTokens ?? model.pricePer1KInputTokens;
|
|
667
|
+
const baseCost = tokensPerMonth / 1e3 * price * totalWeight;
|
|
643
668
|
let confidence = 0.85;
|
|
644
669
|
if (model.contextTier === "frontier") confidence = 0.7;
|
|
645
670
|
const variance = 0.25;
|
|
@@ -653,6 +678,15 @@ function estimateCostFromBudget(budget, model, config = {}) {
|
|
|
653
678
|
confidence
|
|
654
679
|
};
|
|
655
680
|
}
|
|
681
|
+
|
|
682
|
+
// src/business/productivity-metrics.ts
|
|
683
|
+
var SEVERITY_TIME_ESTIMATES = {
|
|
684
|
+
critical: 4,
|
|
685
|
+
major: 2,
|
|
686
|
+
minor: 0.5,
|
|
687
|
+
info: 0.25
|
|
688
|
+
};
|
|
689
|
+
var DEFAULT_HOURLY_RATE = 75;
|
|
656
690
|
function calculateProductivityImpact(issues, hourlyRate = DEFAULT_HOURLY_RATE) {
|
|
657
691
|
const counts = {
|
|
658
692
|
critical: issues.filter((i) => i.severity === "critical").length,
|
|
@@ -693,108 +727,158 @@ function predictAcceptanceRate(toolOutputs) {
|
|
|
693
727
|
const baseRate = 0.3;
|
|
694
728
|
const patterns = toolOutputs.get("pattern-detect");
|
|
695
729
|
if (patterns) {
|
|
696
|
-
const patternImpact = (patterns.score - 50) * 3e-3;
|
|
697
730
|
factors.push({
|
|
698
731
|
name: "Semantic Duplication",
|
|
699
|
-
impact: Math.round(
|
|
732
|
+
impact: Math.round((patterns.score - 50) * 3e-3 * 100)
|
|
700
733
|
});
|
|
701
734
|
}
|
|
702
735
|
const context = toolOutputs.get("context-analyzer");
|
|
703
736
|
if (context) {
|
|
704
|
-
const contextImpact = (context.score - 50) * 4e-3;
|
|
705
737
|
factors.push({
|
|
706
738
|
name: "Context Efficiency",
|
|
707
|
-
impact: Math.round(
|
|
739
|
+
impact: Math.round((context.score - 50) * 4e-3 * 100)
|
|
708
740
|
});
|
|
709
741
|
}
|
|
710
742
|
const consistency = toolOutputs.get("consistency");
|
|
711
743
|
if (consistency) {
|
|
712
|
-
const consistencyImpact = (consistency.score - 50) * 2e-3;
|
|
713
744
|
factors.push({
|
|
714
745
|
name: "Code Consistency",
|
|
715
|
-
impact: Math.round(
|
|
746
|
+
impact: Math.round((consistency.score - 50) * 2e-3 * 100)
|
|
716
747
|
});
|
|
717
748
|
}
|
|
718
749
|
const aiSignalClarity = toolOutputs.get("ai-signal-clarity");
|
|
719
750
|
if (aiSignalClarity) {
|
|
720
|
-
const hrImpact = (50 - aiSignalClarity.score) * 2e-3;
|
|
721
751
|
factors.push({
|
|
722
752
|
name: "AI Signal Clarity",
|
|
723
|
-
impact: Math.round(
|
|
753
|
+
impact: Math.round((50 - aiSignalClarity.score) * 2e-3 * 100)
|
|
724
754
|
});
|
|
725
755
|
}
|
|
726
756
|
const totalImpact = factors.reduce((sum, f) => sum + f.impact / 100, 0);
|
|
727
757
|
const rate = Math.max(0.05, Math.min(0.8, baseRate + totalImpact));
|
|
728
|
-
let confidence;
|
|
758
|
+
let confidence = 0.35;
|
|
729
759
|
if (toolOutputs.size >= 4) confidence = 0.75;
|
|
730
760
|
else if (toolOutputs.size >= 3) confidence = 0.65;
|
|
731
761
|
else if (toolOutputs.size >= 2) confidence = 0.5;
|
|
732
|
-
|
|
733
|
-
return {
|
|
734
|
-
rate: Math.round(rate * 100) / 100,
|
|
735
|
-
confidence,
|
|
736
|
-
factors
|
|
737
|
-
};
|
|
762
|
+
return { rate: Math.round(rate * 100) / 100, confidence, factors };
|
|
738
763
|
}
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
const
|
|
743
|
-
const
|
|
744
|
-
const budgetRange = criticalBudget - idealBudget;
|
|
745
|
-
const budgetFactor = Math.min(
|
|
746
|
-
100,
|
|
747
|
-
Math.max(0, (contextBudget - idealBudget) / budgetRange * 100)
|
|
748
|
-
);
|
|
749
|
-
const depthFactor = Math.min(
|
|
750
|
-
100,
|
|
751
|
-
Math.max(0, (importDepth - idealDepth) * 10)
|
|
752
|
-
);
|
|
753
|
-
const fragmentationFactor = Math.min(
|
|
754
|
-
100,
|
|
755
|
-
Math.max(0, (fragmentation - 0.3) * 250)
|
|
756
|
-
);
|
|
757
|
-
const consistencyFactor = Math.min(100, Math.max(0, 100 - consistencyScore));
|
|
758
|
-
const fileFactor = Math.min(100, Math.max(0, (totalFiles - 50) / 5));
|
|
764
|
+
|
|
765
|
+
// src/business/risk-metrics.ts
|
|
766
|
+
function calculateKnowledgeConcentration(params) {
|
|
767
|
+
const { uniqueConceptFiles, totalFiles, singleAuthorFiles, orphanFiles } = params;
|
|
768
|
+
const concentrationRatio = totalFiles > 0 ? (uniqueConceptFiles + singleAuthorFiles) / (totalFiles * 2) : 0;
|
|
759
769
|
const score = Math.round(
|
|
760
|
-
|
|
770
|
+
Math.min(
|
|
771
|
+
100,
|
|
772
|
+
concentrationRatio * 100 + orphanFiles / Math.max(1, totalFiles) * 20
|
|
773
|
+
)
|
|
761
774
|
);
|
|
762
775
|
let rating;
|
|
763
|
-
if (score <
|
|
764
|
-
else if (score <
|
|
765
|
-
else if (score <
|
|
766
|
-
else
|
|
767
|
-
|
|
776
|
+
if (score < 30) rating = "low";
|
|
777
|
+
else if (score < 50) rating = "moderate";
|
|
778
|
+
else if (score < 75) rating = "high";
|
|
779
|
+
else rating = "critical";
|
|
780
|
+
const recommendations = [];
|
|
781
|
+
if (singleAuthorFiles > 0)
|
|
782
|
+
recommendations.push(
|
|
783
|
+
`Distribute knowledge for ${singleAuthorFiles} single-author files.`
|
|
784
|
+
);
|
|
785
|
+
if (orphanFiles > 0)
|
|
786
|
+
recommendations.push(
|
|
787
|
+
`Link ${orphanFiles} orphan files to the rest of the codebase.`
|
|
788
|
+
);
|
|
768
789
|
return {
|
|
769
790
|
score,
|
|
770
791
|
rating,
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
792
|
+
recommendations,
|
|
793
|
+
analysis: {
|
|
794
|
+
uniqueConceptFiles,
|
|
795
|
+
totalFiles,
|
|
796
|
+
concentrationRatio,
|
|
797
|
+
singleAuthorFiles,
|
|
798
|
+
orphanFiles
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
function calculateDebtInterest(principal, monthlyGrowthRate) {
|
|
803
|
+
const monthlyRate = monthlyGrowthRate;
|
|
804
|
+
const annualRate = Math.pow(1 + monthlyRate, 12) - 1;
|
|
805
|
+
const monthlyCost = principal * monthlyRate;
|
|
806
|
+
return {
|
|
807
|
+
monthlyRate,
|
|
808
|
+
annualRate,
|
|
809
|
+
principal,
|
|
810
|
+
monthlyCost,
|
|
811
|
+
projections: {
|
|
812
|
+
months6: principal * Math.pow(1 + monthlyRate, 6),
|
|
813
|
+
months12: principal * Math.pow(1 + monthlyRate, 12),
|
|
814
|
+
months24: principal * Math.pow(1 + monthlyRate, 24)
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// src/business/comprehension-metrics.ts
|
|
820
|
+
function calculateTechnicalValueChain(params) {
|
|
821
|
+
const { businessLogicDensity, dataAccessComplexity, apiSurfaceArea } = params;
|
|
822
|
+
const score = (businessLogicDensity * 0.5 + (1 - dataAccessComplexity / 10) * 0.3 + (1 - apiSurfaceArea / 20) * 0.2) * 100;
|
|
823
|
+
return {
|
|
824
|
+
score: Math.round(Math.max(0, Math.min(100, score))),
|
|
825
|
+
density: businessLogicDensity,
|
|
826
|
+
complexity: dataAccessComplexity,
|
|
827
|
+
surface: apiSurfaceArea
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
function calculateComprehensionDifficulty(contextBudget, importDepth, fragmentation, modelTier = "frontier") {
|
|
831
|
+
const tierMap = {
|
|
832
|
+
compact: "compact",
|
|
833
|
+
standard: "standard",
|
|
834
|
+
extended: "extended",
|
|
835
|
+
frontier: "frontier",
|
|
836
|
+
easy: "frontier",
|
|
837
|
+
// Map legacy 'easy' to 'frontier'
|
|
838
|
+
moderate: "standard",
|
|
839
|
+
difficult: "compact"
|
|
840
|
+
};
|
|
841
|
+
const tier = tierMap[modelTier] || "frontier";
|
|
842
|
+
const threshold = CONTEXT_TIER_THRESHOLDS[tier];
|
|
843
|
+
const budgetRatio = contextBudget / threshold.idealTokens;
|
|
844
|
+
const score = (budgetRatio * 0.6 + importDepth / 10 * 0.2 + fragmentation * 0.2) * 100;
|
|
845
|
+
const finalScore = Math.round(Math.max(0, Math.min(100, score)));
|
|
846
|
+
let rating;
|
|
847
|
+
if (finalScore < 20) rating = "trivial";
|
|
848
|
+
else if (finalScore < 40) rating = "easy";
|
|
849
|
+
else if (finalScore < 60) rating = "moderate";
|
|
850
|
+
else if (finalScore < 85) rating = "difficult";
|
|
851
|
+
else rating = "expert";
|
|
852
|
+
return {
|
|
853
|
+
score: finalScore,
|
|
854
|
+
rating,
|
|
855
|
+
factors: { budgetRatio, depthRatio: importDepth / 10, fragmentation }
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// src/business-metrics.ts
|
|
860
|
+
function calculateBusinessROI(params) {
|
|
861
|
+
const model = getModelPreset(params.modelId || "claude-4.6");
|
|
862
|
+
const devCount = params.developerCount || 5;
|
|
863
|
+
const budget = calculateTokenBudget({
|
|
864
|
+
totalContextTokens: params.tokenWaste * 2.5,
|
|
865
|
+
wastedTokens: {
|
|
866
|
+
duplication: params.tokenWaste * 0.7,
|
|
867
|
+
fragmentation: params.tokenWaste * 0.3,
|
|
868
|
+
chattiness: 0
|
|
869
|
+
}
|
|
870
|
+
});
|
|
871
|
+
const cost = estimateCostFromBudget(budget, model, {
|
|
872
|
+
developerCount: devCount
|
|
873
|
+
});
|
|
874
|
+
const productivity = calculateProductivityImpact(params.issues);
|
|
875
|
+
const monthlySavings = cost.total;
|
|
876
|
+
const productivityGainHours = productivity.totalHours;
|
|
877
|
+
const annualValue = (monthlySavings + productivityGainHours * 75) * 12;
|
|
878
|
+
return {
|
|
879
|
+
monthlySavings: Math.round(monthlySavings),
|
|
880
|
+
productivityGainHours: Math.round(productivityGainHours),
|
|
881
|
+
annualValue: Math.round(annualValue)
|
|
798
882
|
};
|
|
799
883
|
}
|
|
800
884
|
function formatCost(cost) {
|
|
@@ -820,220 +904,6 @@ function formatHours(hours) {
|
|
|
820
904
|
function formatAcceptanceRate(rate) {
|
|
821
905
|
return `${Math.round(rate * 100)}%`;
|
|
822
906
|
}
|
|
823
|
-
function calculateScoreTrend(history) {
|
|
824
|
-
if (history.length < 2) {
|
|
825
|
-
return {
|
|
826
|
-
direction: "stable",
|
|
827
|
-
change30Days: 0,
|
|
828
|
-
change90Days: 0,
|
|
829
|
-
velocity: 0,
|
|
830
|
-
projectedScore: history[0]?.overallScore || 100
|
|
831
|
-
};
|
|
832
|
-
}
|
|
833
|
-
const now = /* @__PURE__ */ new Date();
|
|
834
|
-
const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1e3);
|
|
835
|
-
const ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1e3);
|
|
836
|
-
const last30Days = history.filter(
|
|
837
|
-
(e) => new Date(e.timestamp) >= thirtyDaysAgo
|
|
838
|
-
);
|
|
839
|
-
const last90Days = history.filter(
|
|
840
|
-
(e) => new Date(e.timestamp) >= ninetyDaysAgo
|
|
841
|
-
);
|
|
842
|
-
const currentScore = history[history.length - 1].overallScore;
|
|
843
|
-
const thirtyDaysAgoScore = last30Days[0]?.overallScore || currentScore;
|
|
844
|
-
const ninetyDaysAgoScore = last90Days[0]?.overallScore || thirtyDaysAgoScore;
|
|
845
|
-
const change30Days = currentScore - thirtyDaysAgoScore;
|
|
846
|
-
const change90Days = currentScore - ninetyDaysAgoScore;
|
|
847
|
-
const weeksOfData = Math.max(1, history.length / 7);
|
|
848
|
-
const totalChange = currentScore - history[0].overallScore;
|
|
849
|
-
const velocity = totalChange / weeksOfData;
|
|
850
|
-
let direction;
|
|
851
|
-
if (change30Days > 3) direction = "improving";
|
|
852
|
-
else if (change30Days < -3) direction = "degrading";
|
|
853
|
-
else direction = "stable";
|
|
854
|
-
const projectedScore = Math.max(
|
|
855
|
-
0,
|
|
856
|
-
Math.min(100, currentScore + velocity * 4)
|
|
857
|
-
);
|
|
858
|
-
return {
|
|
859
|
-
direction,
|
|
860
|
-
change30Days,
|
|
861
|
-
change90Days,
|
|
862
|
-
velocity: Math.round(velocity * 10) / 10,
|
|
863
|
-
projectedScore: Math.round(projectedScore)
|
|
864
|
-
};
|
|
865
|
-
}
|
|
866
|
-
function calculateRemediationVelocity(history, currentIssues) {
|
|
867
|
-
if (history.length < 2) {
|
|
868
|
-
return {
|
|
869
|
-
issuesFixedThisWeek: 0,
|
|
870
|
-
avgIssuesPerWeek: 0,
|
|
871
|
-
trend: "stable",
|
|
872
|
-
estimatedCompletionWeeks: currentIssues > 0 ? Infinity : 0
|
|
873
|
-
};
|
|
874
|
-
}
|
|
875
|
-
const now = /* @__PURE__ */ new Date();
|
|
876
|
-
const oneWeekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3);
|
|
877
|
-
const twoWeeksAgo = new Date(now.getTime() - 14 * 24 * 60 * 60 * 1e3);
|
|
878
|
-
const thisWeek = history.filter((e) => new Date(e.timestamp) >= oneWeekAgo);
|
|
879
|
-
const lastWeek = history.filter(
|
|
880
|
-
(e) => new Date(e.timestamp) >= twoWeeksAgo && new Date(e.timestamp) < oneWeekAgo
|
|
881
|
-
);
|
|
882
|
-
const issuesFixedThisWeek = thisWeek.length > 1 ? thisWeek[0].totalIssues - thisWeek[thisWeek.length - 1].totalIssues : 0;
|
|
883
|
-
const totalIssuesFixed = history[0].totalIssues - history[history.length - 1].totalIssues;
|
|
884
|
-
const weeksOfData = Math.max(1, history.length / 7);
|
|
885
|
-
const avgIssuesPerWeek = totalIssuesFixed / weeksOfData;
|
|
886
|
-
let trend;
|
|
887
|
-
if (lastWeek.length > 1) {
|
|
888
|
-
const lastWeekFixed = lastWeek[0].totalIssues - lastWeek[lastWeek.length - 1].totalIssues;
|
|
889
|
-
if (issuesFixedThisWeek > lastWeekFixed * 1.2) trend = "accelerating";
|
|
890
|
-
else if (issuesFixedThisWeek < lastWeekFixed * 0.8) trend = "decelerating";
|
|
891
|
-
else trend = "stable";
|
|
892
|
-
} else {
|
|
893
|
-
trend = "stable";
|
|
894
|
-
}
|
|
895
|
-
const estimatedCompletionWeeks = avgIssuesPerWeek > 0 ? Math.ceil(currentIssues / avgIssuesPerWeek) : Infinity;
|
|
896
|
-
return {
|
|
897
|
-
issuesFixedThisWeek: Math.max(0, issuesFixedThisWeek),
|
|
898
|
-
avgIssuesPerWeek: Math.round(avgIssuesPerWeek * 10) / 10,
|
|
899
|
-
trend,
|
|
900
|
-
estimatedCompletionWeeks
|
|
901
|
-
};
|
|
902
|
-
}
|
|
903
|
-
function calculateKnowledgeConcentration(files, authorData) {
|
|
904
|
-
if (files.length === 0) {
|
|
905
|
-
return {
|
|
906
|
-
score: 0,
|
|
907
|
-
rating: "low",
|
|
908
|
-
analysis: {
|
|
909
|
-
uniqueConceptFiles: 0,
|
|
910
|
-
totalFiles: 0,
|
|
911
|
-
concentrationRatio: 0,
|
|
912
|
-
singleAuthorFiles: 0,
|
|
913
|
-
orphanFiles: 0
|
|
914
|
-
},
|
|
915
|
-
recommendations: ["No files to analyze"]
|
|
916
|
-
};
|
|
917
|
-
}
|
|
918
|
-
const orphanFiles = files.filter(
|
|
919
|
-
(f) => f.exports < 2 && f.imports < 2
|
|
920
|
-
).length;
|
|
921
|
-
const avgExports = files.reduce((sum, f) => sum + f.exports, 0) / files.length;
|
|
922
|
-
const uniqueConceptFiles = files.filter(
|
|
923
|
-
(f) => f.exports > avgExports * 2
|
|
924
|
-
).length;
|
|
925
|
-
const totalExports = files.reduce((sum, f) => sum + f.exports, 0);
|
|
926
|
-
const concentrationRatio = totalExports > 0 ? uniqueConceptFiles / files.length : 0;
|
|
927
|
-
let singleAuthorFiles = 0;
|
|
928
|
-
if (authorData) {
|
|
929
|
-
for (const files2 of authorData.values()) {
|
|
930
|
-
if (files2.length === 1) singleAuthorFiles++;
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
const orphanRisk = orphanFiles / files.length * 30;
|
|
934
|
-
const uniqueRisk = concentrationRatio * 40;
|
|
935
|
-
const singleAuthorRisk = authorData ? singleAuthorFiles / files.length * 30 : 0;
|
|
936
|
-
const score = Math.min(
|
|
937
|
-
100,
|
|
938
|
-
Math.round(orphanRisk + uniqueRisk + singleAuthorRisk)
|
|
939
|
-
);
|
|
940
|
-
let rating;
|
|
941
|
-
if (score < 20) rating = "low";
|
|
942
|
-
else if (score < 40) rating = "moderate";
|
|
943
|
-
else if (score < 70) rating = "high";
|
|
944
|
-
else rating = "critical";
|
|
945
|
-
const recommendations = [];
|
|
946
|
-
if (orphanFiles > files.length * 0.2) {
|
|
947
|
-
recommendations.push(
|
|
948
|
-
`Reduce ${orphanFiles} orphan files by connecting them to main modules`
|
|
949
|
-
);
|
|
950
|
-
}
|
|
951
|
-
if (uniqueConceptFiles > files.length * 0.1) {
|
|
952
|
-
recommendations.push(
|
|
953
|
-
"Distribute high-export files into more focused modules"
|
|
954
|
-
);
|
|
955
|
-
}
|
|
956
|
-
if (authorData && singleAuthorFiles > files.length * 0.3) {
|
|
957
|
-
recommendations.push(
|
|
958
|
-
"Increase knowledge sharing to reduce single-author dependencies"
|
|
959
|
-
);
|
|
960
|
-
}
|
|
961
|
-
return {
|
|
962
|
-
score,
|
|
963
|
-
rating,
|
|
964
|
-
analysis: {
|
|
965
|
-
uniqueConceptFiles,
|
|
966
|
-
totalFiles: files.length,
|
|
967
|
-
concentrationRatio: Math.round(concentrationRatio * 100) / 100,
|
|
968
|
-
singleAuthorFiles,
|
|
969
|
-
orphanFiles
|
|
970
|
-
},
|
|
971
|
-
recommendations
|
|
972
|
-
};
|
|
973
|
-
}
|
|
974
|
-
function calculateTechnicalDebtInterest(params) {
|
|
975
|
-
const { currentMonthlyCost, issues, monthsOpen } = params;
|
|
976
|
-
const criticalCount = issues.filter((i) => i.severity === "critical").length;
|
|
977
|
-
const majorCount = issues.filter((i) => i.severity === "major").length;
|
|
978
|
-
const minorCount = issues.filter((i) => i.severity === "minor").length;
|
|
979
|
-
const severityWeight = (criticalCount * 3 + majorCount * 2 + minorCount * 1) / Math.max(1, issues.length);
|
|
980
|
-
const baseRate = 0.02 + severityWeight * 0.01;
|
|
981
|
-
const timeMultiplier = Math.max(1, 1 + monthsOpen * 0.1);
|
|
982
|
-
const monthlyRate = baseRate * timeMultiplier;
|
|
983
|
-
const projectDebt = (principal2, months) => {
|
|
984
|
-
let debt = principal2;
|
|
985
|
-
for (let i = 0; i < months; i++) {
|
|
986
|
-
debt = debt * (1 + monthlyRate);
|
|
987
|
-
}
|
|
988
|
-
return Math.round(debt);
|
|
989
|
-
};
|
|
990
|
-
const principal = currentMonthlyCost * 12;
|
|
991
|
-
const projections = {
|
|
992
|
-
months6: projectDebt(principal, 6),
|
|
993
|
-
months12: projectDebt(principal, 12),
|
|
994
|
-
months24: projectDebt(principal, 24)
|
|
995
|
-
};
|
|
996
|
-
return {
|
|
997
|
-
monthlyRate: Math.round(monthlyRate * 1e4) / 100,
|
|
998
|
-
annualRate: Math.round((Math.pow(1 + monthlyRate, 12) - 1) * 1e4) / 100,
|
|
999
|
-
principal,
|
|
1000
|
-
projections,
|
|
1001
|
-
monthlyCost: Math.round(currentMonthlyCost * (1 + monthlyRate) * 100) / 100
|
|
1002
|
-
};
|
|
1003
|
-
}
|
|
1004
|
-
function getDebtBreakdown(patternCost, contextCost, consistencyCost) {
|
|
1005
|
-
const breakdowns = [
|
|
1006
|
-
{
|
|
1007
|
-
category: "Semantic Duplication",
|
|
1008
|
-
currentCost: patternCost,
|
|
1009
|
-
monthlyGrowthRate: 5,
|
|
1010
|
-
// Grows as devs copy-paste
|
|
1011
|
-
priority: patternCost > 1e3 ? "high" : "medium",
|
|
1012
|
-
fixCost: patternCost * 3
|
|
1013
|
-
// Fixing costs 3x current waste
|
|
1014
|
-
},
|
|
1015
|
-
{
|
|
1016
|
-
category: "Context Fragmentation",
|
|
1017
|
-
currentCost: contextCost,
|
|
1018
|
-
monthlyGrowthRate: 3,
|
|
1019
|
-
// Grows with new features
|
|
1020
|
-
priority: contextCost > 500 ? "high" : "medium",
|
|
1021
|
-
fixCost: contextCost * 2.5
|
|
1022
|
-
},
|
|
1023
|
-
{
|
|
1024
|
-
category: "Consistency Issues",
|
|
1025
|
-
currentCost: consistencyCost,
|
|
1026
|
-
monthlyGrowthRate: 2,
|
|
1027
|
-
// Grows with new devs
|
|
1028
|
-
priority: consistencyCost > 200 ? "medium" : "low",
|
|
1029
|
-
fixCost: consistencyCost * 1.5
|
|
1030
|
-
}
|
|
1031
|
-
];
|
|
1032
|
-
return breakdowns.sort((a, b) => {
|
|
1033
|
-
const priorityOrder = { high: 0, medium: 1, low: 2 };
|
|
1034
|
-
return priorityOrder[a.priority] - priorityOrder[b.priority];
|
|
1035
|
-
});
|
|
1036
|
-
}
|
|
1037
907
|
function generateValueChain(params) {
|
|
1038
908
|
const { issueType, count, severity } = params;
|
|
1039
909
|
const impacts = {
|
|
@@ -1073,9 +943,7 @@ function generateValueChain(params) {
|
|
|
1073
943
|
},
|
|
1074
944
|
businessOutcome: {
|
|
1075
945
|
directCost: count * 12,
|
|
1076
|
-
// Placeholder linear cost
|
|
1077
946
|
opportunityCost: productivityLoss * 15e3,
|
|
1078
|
-
// Monthly avg dev cost $15k
|
|
1079
947
|
riskLevel: impact.risk
|
|
1080
948
|
}
|
|
1081
949
|
};
|
|
@@ -1519,7 +1387,9 @@ var ParserFactory = class _ParserFactory {
|
|
|
1519
1387
|
registerParser(parser) {
|
|
1520
1388
|
this.parsers.set(parser.language, parser);
|
|
1521
1389
|
parser.extensions.forEach((ext) => {
|
|
1522
|
-
|
|
1390
|
+
const lang = LANGUAGE_EXTENSIONS[ext] || parser.language;
|
|
1391
|
+
this.extensionMap.set(ext, lang);
|
|
1392
|
+
this.parsers.set(lang, parser);
|
|
1523
1393
|
});
|
|
1524
1394
|
}
|
|
1525
1395
|
/**
|
|
@@ -1588,7 +1458,55 @@ function getSupportedLanguages() {
|
|
|
1588
1458
|
return ParserFactory.getInstance().getSupportedLanguages();
|
|
1589
1459
|
}
|
|
1590
1460
|
|
|
1591
|
-
// src/
|
|
1461
|
+
// src/metrics/remediation-utils.ts
|
|
1462
|
+
function collectFutureProofRecommendations(params) {
|
|
1463
|
+
const recommendations = [];
|
|
1464
|
+
for (const rec of params.aiSignalClarity.recommendations) {
|
|
1465
|
+
recommendations.push({ action: rec, estimatedImpact: 8, priority: "high" });
|
|
1466
|
+
}
|
|
1467
|
+
for (const rec of params.agentGrounding.recommendations) {
|
|
1468
|
+
recommendations.push({
|
|
1469
|
+
action: rec,
|
|
1470
|
+
estimatedImpact: 6,
|
|
1471
|
+
priority: "medium"
|
|
1472
|
+
});
|
|
1473
|
+
}
|
|
1474
|
+
for (const rec of params.testability.recommendations) {
|
|
1475
|
+
const priority = params.testability.aiChangeSafetyRating === "blind-risk" ? "high" : "medium";
|
|
1476
|
+
recommendations.push({ action: rec, estimatedImpact: 10, priority });
|
|
1477
|
+
}
|
|
1478
|
+
for (const rec of params.patternEntropy.recommendations) {
|
|
1479
|
+
recommendations.push({ action: rec, estimatedImpact: 5, priority: "low" });
|
|
1480
|
+
}
|
|
1481
|
+
if (params.conceptCohesion.rating === "poor") {
|
|
1482
|
+
recommendations.push({
|
|
1483
|
+
action: "Improve concept cohesion by grouping related exports",
|
|
1484
|
+
estimatedImpact: 8,
|
|
1485
|
+
priority: "high"
|
|
1486
|
+
});
|
|
1487
|
+
}
|
|
1488
|
+
if (params.docDrift) {
|
|
1489
|
+
for (const rec of params.docDrift.recommendations) {
|
|
1490
|
+
recommendations.push({
|
|
1491
|
+
action: rec,
|
|
1492
|
+
estimatedImpact: 8,
|
|
1493
|
+
priority: "high"
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
if (params.dependencyHealth) {
|
|
1498
|
+
for (const rec of params.dependencyHealth.recommendations) {
|
|
1499
|
+
recommendations.push({
|
|
1500
|
+
action: rec,
|
|
1501
|
+
estimatedImpact: 7,
|
|
1502
|
+
priority: "medium"
|
|
1503
|
+
});
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
return recommendations;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
// src/metrics/cognitive-load.ts
|
|
1592
1510
|
function calculateCognitiveLoad(params) {
|
|
1593
1511
|
const {
|
|
1594
1512
|
linesOfCode,
|
|
@@ -1647,13 +1565,20 @@ function calculateCognitiveLoad(params) {
|
|
|
1647
1565
|
}
|
|
1648
1566
|
};
|
|
1649
1567
|
}
|
|
1568
|
+
|
|
1569
|
+
// src/metrics/semantic-distance.ts
|
|
1650
1570
|
function calculateSemanticDistance(params) {
|
|
1651
|
-
const {
|
|
1571
|
+
const {
|
|
1572
|
+
file1,
|
|
1573
|
+
file2,
|
|
1574
|
+
file1Domain,
|
|
1575
|
+
file2Domain,
|
|
1576
|
+
sharedDependencies,
|
|
1577
|
+
file1Imports,
|
|
1578
|
+
file2Imports
|
|
1579
|
+
} = params;
|
|
1652
1580
|
const domainDistance = file1Domain === file2Domain ? 0 : file1Domain && file2Domain ? 0.5 : 0.8;
|
|
1653
|
-
const importOverlap = sharedDependencies.length / Math.max(
|
|
1654
|
-
1,
|
|
1655
|
-
Math.min(params.file1Imports.length, params.file2Imports.length)
|
|
1656
|
-
);
|
|
1581
|
+
const importOverlap = sharedDependencies.length / Math.max(1, Math.min(file1Imports.length, file2Imports.length));
|
|
1657
1582
|
const importDistance = 1 - importOverlap;
|
|
1658
1583
|
const distance = domainDistance * 0.4 + importDistance * 0.3 + (sharedDependencies.length > 0 ? 0 : 0.3);
|
|
1659
1584
|
let relationship;
|
|
@@ -1672,6 +1597,8 @@ function calculateSemanticDistance(params) {
|
|
|
1672
1597
|
reason: relationship === "same-domain" ? `Both in "${file1Domain}" domain` : relationship === "cross-domain" ? `Share ${sharedDependencies.length} dependency(ies)` : "No strong semantic relationship detected"
|
|
1673
1598
|
};
|
|
1674
1599
|
}
|
|
1600
|
+
|
|
1601
|
+
// src/metrics/structural-metrics.ts
|
|
1675
1602
|
function calculatePatternEntropy(files) {
|
|
1676
1603
|
if (files.length === 0) {
|
|
1677
1604
|
return {
|
|
@@ -1721,23 +1648,18 @@ function calculatePatternEntropy(files) {
|
|
|
1721
1648
|
else if (normalizedEntropy < 0.8) rating = "fragmented";
|
|
1722
1649
|
else rating = "chaotic";
|
|
1723
1650
|
const recommendations = [];
|
|
1724
|
-
if (normalizedEntropy > 0.5)
|
|
1651
|
+
if (normalizedEntropy > 0.5)
|
|
1725
1652
|
recommendations.push(
|
|
1726
1653
|
`Consolidate ${files.length} files into fewer directories by domain`
|
|
1727
1654
|
);
|
|
1728
|
-
|
|
1729
|
-
if (dirGroups.size > 5) {
|
|
1655
|
+
if (dirGroups.size > 5)
|
|
1730
1656
|
recommendations.push(
|
|
1731
1657
|
"Consider barrel exports to reduce directory navigation"
|
|
1732
1658
|
);
|
|
1733
|
-
|
|
1734
|
-
if (gini > 0.5) {
|
|
1659
|
+
if (gini > 0.5)
|
|
1735
1660
|
recommendations.push("Redistribute files more evenly across directories");
|
|
1736
|
-
}
|
|
1737
|
-
const firstFile = files.length > 0 ? files[0] : null;
|
|
1738
|
-
const domainValue = firstFile ? firstFile.domain : "mixed";
|
|
1739
1661
|
return {
|
|
1740
|
-
domain:
|
|
1662
|
+
domain: files[0]?.domain || "mixed",
|
|
1741
1663
|
entropy: Math.round(normalizedEntropy * 100) / 100,
|
|
1742
1664
|
rating,
|
|
1743
1665
|
distribution: {
|
|
@@ -1768,9 +1690,8 @@ function calculateConceptCohesion(params) {
|
|
|
1768
1690
|
}
|
|
1769
1691
|
const uniqueDomains = new Set(allDomains);
|
|
1770
1692
|
const domainCounts = /* @__PURE__ */ new Map();
|
|
1771
|
-
for (const d of allDomains)
|
|
1693
|
+
for (const d of allDomains)
|
|
1772
1694
|
domainCounts.set(d, (domainCounts.get(d) || 0) + 1);
|
|
1773
|
-
}
|
|
1774
1695
|
const maxCount = Math.max(...Array.from(domainCounts.values()), 1);
|
|
1775
1696
|
const domainConcentration = maxCount / allDomains.length;
|
|
1776
1697
|
const exportPurposeClarity = 1 - (uniqueDomains.size - 1) / Math.max(1, exports.length);
|
|
@@ -1790,59 +1711,8 @@ function calculateConceptCohesion(params) {
|
|
|
1790
1711
|
}
|
|
1791
1712
|
};
|
|
1792
1713
|
}
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
const entropyScore = 100 - params.patternEntropy.entropy * 100;
|
|
1796
|
-
const cohesionScore = params.conceptCohesion.score * 100;
|
|
1797
|
-
const overall = Math.round(
|
|
1798
|
-
loadScore * 0.4 + entropyScore * 0.3 + cohesionScore * 0.3
|
|
1799
|
-
);
|
|
1800
|
-
const factors = [
|
|
1801
|
-
{
|
|
1802
|
-
name: "Cognitive Load",
|
|
1803
|
-
impact: Math.round(loadScore - 50),
|
|
1804
|
-
description: params.cognitiveLoad.rating
|
|
1805
|
-
},
|
|
1806
|
-
{
|
|
1807
|
-
name: "Pattern Entropy",
|
|
1808
|
-
impact: Math.round(entropyScore - 50),
|
|
1809
|
-
description: params.patternEntropy.rating
|
|
1810
|
-
},
|
|
1811
|
-
{
|
|
1812
|
-
name: "Concept Cohesion",
|
|
1813
|
-
impact: Math.round(cohesionScore - 50),
|
|
1814
|
-
description: params.conceptCohesion.rating
|
|
1815
|
-
}
|
|
1816
|
-
];
|
|
1817
|
-
const recommendations = [];
|
|
1818
|
-
for (const rec of params.patternEntropy.recommendations) {
|
|
1819
|
-
recommendations.push({
|
|
1820
|
-
action: rec,
|
|
1821
|
-
estimatedImpact: 5,
|
|
1822
|
-
priority: "medium"
|
|
1823
|
-
});
|
|
1824
|
-
}
|
|
1825
|
-
if (params.conceptCohesion.rating === "poor") {
|
|
1826
|
-
recommendations.push({
|
|
1827
|
-
action: "Improve concept cohesion by grouping related exports",
|
|
1828
|
-
estimatedImpact: 8,
|
|
1829
|
-
priority: "high"
|
|
1830
|
-
});
|
|
1831
|
-
}
|
|
1832
|
-
const semanticDistanceAvg = params.semanticDistances && params.semanticDistances.length > 0 ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
|
|
1833
|
-
return {
|
|
1834
|
-
toolName: "future-proof",
|
|
1835
|
-
score: overall,
|
|
1836
|
-
rawMetrics: {
|
|
1837
|
-
cognitiveLoadScore: params.cognitiveLoad.score,
|
|
1838
|
-
entropyScore: params.patternEntropy.entropy,
|
|
1839
|
-
cohesionScore: params.conceptCohesion.score,
|
|
1840
|
-
semanticDistanceAvg
|
|
1841
|
-
},
|
|
1842
|
-
factors,
|
|
1843
|
-
recommendations
|
|
1844
|
-
};
|
|
1845
|
-
}
|
|
1714
|
+
|
|
1715
|
+
// src/metrics/ai-signal-clarity.ts
|
|
1846
1716
|
function calculateAiSignalClarity(params) {
|
|
1847
1717
|
const {
|
|
1848
1718
|
overloadedSymbols,
|
|
@@ -1864,75 +1734,53 @@ function calculateAiSignalClarity(params) {
|
|
|
1864
1734
|
recommendations: []
|
|
1865
1735
|
};
|
|
1866
1736
|
}
|
|
1867
|
-
const overloadRatio = Math.
|
|
1868
|
-
1,
|
|
1869
|
-
overloadedSymbols / Math.max(1, totalSymbols)
|
|
1870
|
-
);
|
|
1737
|
+
const overloadRatio = overloadedSymbols / Math.max(1, totalSymbols);
|
|
1871
1738
|
const overloadSignal = {
|
|
1872
1739
|
name: "Symbol Overloading",
|
|
1873
1740
|
count: overloadedSymbols,
|
|
1874
|
-
riskContribution: Math.round(overloadRatio * 100 * 0.25),
|
|
1875
|
-
// 25% weight
|
|
1741
|
+
riskContribution: Math.round(Math.min(1, overloadRatio) * 100 * 0.25),
|
|
1876
1742
|
description: `${overloadedSymbols} overloaded symbols \u2014 AI picks wrong signature`
|
|
1877
1743
|
};
|
|
1878
|
-
const magicRatio =
|
|
1744
|
+
const magicRatio = magicLiterals / Math.max(1, totalSymbols * 2);
|
|
1879
1745
|
const magicSignal = {
|
|
1880
1746
|
name: "Magic Literals",
|
|
1881
1747
|
count: magicLiterals,
|
|
1882
|
-
riskContribution: Math.round(magicRatio * 100 * 0.2),
|
|
1883
|
-
// 20% weight
|
|
1748
|
+
riskContribution: Math.round(Math.min(1, magicRatio) * 100 * 0.2),
|
|
1884
1749
|
description: `${magicLiterals} unnamed constants \u2014 AI invents wrong values`
|
|
1885
1750
|
};
|
|
1886
|
-
const trapRatio =
|
|
1751
|
+
const trapRatio = booleanTraps / Math.max(1, totalSymbols);
|
|
1887
1752
|
const trapSignal = {
|
|
1888
1753
|
name: "Boolean Traps",
|
|
1889
1754
|
count: booleanTraps,
|
|
1890
|
-
riskContribution: Math.round(trapRatio * 100 * 0.2),
|
|
1891
|
-
// 20% weight
|
|
1755
|
+
riskContribution: Math.round(Math.min(1, trapRatio) * 100 * 0.2),
|
|
1892
1756
|
description: `${booleanTraps} boolean trap parameters \u2014 AI inverts intent`
|
|
1893
1757
|
};
|
|
1894
|
-
const sideEffectRatio = Math.
|
|
1895
|
-
1,
|
|
1896
|
-
implicitSideEffects / Math.max(1, totalExports)
|
|
1897
|
-
);
|
|
1758
|
+
const sideEffectRatio = implicitSideEffects / Math.max(1, totalExports);
|
|
1898
1759
|
const sideEffectSignal = {
|
|
1899
1760
|
name: "Implicit Side Effects",
|
|
1900
1761
|
count: implicitSideEffects,
|
|
1901
|
-
riskContribution: Math.round(sideEffectRatio * 100 * 0.15),
|
|
1902
|
-
// 15% weight
|
|
1762
|
+
riskContribution: Math.round(Math.min(1, sideEffectRatio) * 100 * 0.15),
|
|
1903
1763
|
description: `${implicitSideEffects} functions with implicit side effects \u2014 AI misses contracts`
|
|
1904
1764
|
};
|
|
1905
|
-
const callbackRatio = Math.
|
|
1906
|
-
1,
|
|
1907
|
-
deepCallbacks / Math.max(1, totalSymbols * 0.1)
|
|
1908
|
-
);
|
|
1765
|
+
const callbackRatio = deepCallbacks / Math.max(1, totalSymbols * 0.1);
|
|
1909
1766
|
const callbackSignal = {
|
|
1910
1767
|
name: "Callback Nesting",
|
|
1911
1768
|
count: deepCallbacks,
|
|
1912
|
-
riskContribution: Math.round(callbackRatio * 100 * 0.1),
|
|
1913
|
-
// 10% weight
|
|
1769
|
+
riskContribution: Math.round(Math.min(1, callbackRatio) * 100 * 0.1),
|
|
1914
1770
|
description: `${deepCallbacks} deep callback chains \u2014 AI loses control flow context`
|
|
1915
1771
|
};
|
|
1916
|
-
const ambiguousRatio = Math.
|
|
1917
|
-
1,
|
|
1918
|
-
ambiguousNames / Math.max(1, totalSymbols)
|
|
1919
|
-
);
|
|
1772
|
+
const ambiguousRatio = ambiguousNames / Math.max(1, totalSymbols);
|
|
1920
1773
|
const ambiguousSignal = {
|
|
1921
1774
|
name: "Ambiguous Names",
|
|
1922
1775
|
count: ambiguousNames,
|
|
1923
|
-
riskContribution: Math.round(ambiguousRatio * 100 * 0.1),
|
|
1924
|
-
// 10% weight
|
|
1776
|
+
riskContribution: Math.round(Math.min(1, ambiguousRatio) * 100 * 0.1),
|
|
1925
1777
|
description: `${ambiguousNames} non-descriptive identifiers \u2014 AI guesses wrong intent`
|
|
1926
1778
|
};
|
|
1927
|
-
const undocRatio = Math.
|
|
1928
|
-
1,
|
|
1929
|
-
undocumentedExports / Math.max(1, totalExports)
|
|
1930
|
-
);
|
|
1779
|
+
const undocRatio = undocumentedExports / Math.max(1, totalExports);
|
|
1931
1780
|
const undocSignal = {
|
|
1932
1781
|
name: "Undocumented Exports",
|
|
1933
1782
|
count: undocumentedExports,
|
|
1934
|
-
riskContribution: Math.round(undocRatio * 100 * 0.1),
|
|
1935
|
-
// 10% weight
|
|
1783
|
+
riskContribution: Math.round(Math.min(1, undocRatio) * 100 * 0.1),
|
|
1936
1784
|
description: `${undocumentedExports} public functions without docs \u2014 AI fabricates behavior`
|
|
1937
1785
|
};
|
|
1938
1786
|
const signals = [
|
|
@@ -1957,33 +1805,28 @@ function calculateAiSignalClarity(params) {
|
|
|
1957
1805
|
const topSignal = signals.reduce(
|
|
1958
1806
|
(a, b) => a.riskContribution > b.riskContribution ? a : b
|
|
1959
1807
|
);
|
|
1960
|
-
const topRisk = topSignal.riskContribution > 0 ? topSignal.description : "No significant
|
|
1808
|
+
const topRisk = topSignal.riskContribution > 0 ? topSignal.description : "No significant issues detected";
|
|
1961
1809
|
const recommendations = [];
|
|
1962
|
-
if (overloadSignal.riskContribution > 5)
|
|
1810
|
+
if (overloadSignal.riskContribution > 5)
|
|
1963
1811
|
recommendations.push(
|
|
1964
1812
|
`Rename ${overloadedSymbols} overloaded symbols to unique, intent-revealing names`
|
|
1965
1813
|
);
|
|
1966
|
-
|
|
1967
|
-
if (magicSignal.riskContribution > 5) {
|
|
1814
|
+
if (magicSignal.riskContribution > 5)
|
|
1968
1815
|
recommendations.push(
|
|
1969
1816
|
`Extract ${magicLiterals} magic literals into named constants`
|
|
1970
1817
|
);
|
|
1971
|
-
|
|
1972
|
-
if (trapSignal.riskContribution > 5) {
|
|
1818
|
+
if (trapSignal.riskContribution > 5)
|
|
1973
1819
|
recommendations.push(
|
|
1974
1820
|
`Replace ${booleanTraps} boolean traps with named options objects`
|
|
1975
1821
|
);
|
|
1976
|
-
|
|
1977
|
-
if (undocSignal.riskContribution > 5) {
|
|
1822
|
+
if (undocSignal.riskContribution > 5)
|
|
1978
1823
|
recommendations.push(
|
|
1979
1824
|
`Add JSDoc/docstrings to ${undocumentedExports} undocumented public functions`
|
|
1980
1825
|
);
|
|
1981
|
-
|
|
1982
|
-
if (sideEffectSignal.riskContribution > 5) {
|
|
1826
|
+
if (sideEffectSignal.riskContribution > 5)
|
|
1983
1827
|
recommendations.push(
|
|
1984
1828
|
"Mark functions with side effects explicitly in their names or docs"
|
|
1985
1829
|
);
|
|
1986
|
-
}
|
|
1987
1830
|
return {
|
|
1988
1831
|
score: Math.round(score),
|
|
1989
1832
|
rating,
|
|
@@ -1992,6 +1835,8 @@ function calculateAiSignalClarity(params) {
|
|
|
1992
1835
|
recommendations
|
|
1993
1836
|
};
|
|
1994
1837
|
}
|
|
1838
|
+
|
|
1839
|
+
// src/metrics/agent-grounding.ts
|
|
1995
1840
|
function calculateAgentGrounding(params) {
|
|
1996
1841
|
const {
|
|
1997
1842
|
deepDirectories,
|
|
@@ -2006,25 +1851,33 @@ function calculateAgentGrounding(params) {
|
|
|
2006
1851
|
inconsistentDomainTerms,
|
|
2007
1852
|
domainVocabularySize
|
|
2008
1853
|
} = params;
|
|
2009
|
-
const deepDirRatio = totalDirectories > 0 ? deepDirectories / totalDirectories : 0;
|
|
2010
1854
|
const structureClarityScore = Math.max(
|
|
2011
1855
|
0,
|
|
2012
|
-
Math.round(
|
|
1856
|
+
Math.round(
|
|
1857
|
+
100 - (totalDirectories > 0 ? deepDirectories / totalDirectories * 80 : 0)
|
|
1858
|
+
)
|
|
1859
|
+
);
|
|
1860
|
+
const selfDocumentationScore = Math.max(
|
|
1861
|
+
0,
|
|
1862
|
+
Math.round(100 - (totalFiles > 0 ? vagueFileNames / totalFiles * 90 : 0))
|
|
2013
1863
|
);
|
|
2014
|
-
const vagueRatio = totalFiles > 0 ? vagueFileNames / totalFiles : 0;
|
|
2015
|
-
const selfDocumentationScore = Math.max(0, Math.round(100 - vagueRatio * 90));
|
|
2016
1864
|
let entryPointScore = 60;
|
|
2017
1865
|
if (hasRootReadme) entryPointScore += 25;
|
|
2018
1866
|
if (readmeIsFresh) entryPointScore += 10;
|
|
2019
1867
|
const barrelRatio = totalFiles > 0 ? barrelExports / (totalFiles * 0.1) : 0;
|
|
2020
1868
|
entryPointScore += Math.round(Math.min(5, barrelRatio * 5));
|
|
2021
1869
|
entryPointScore = Math.min(100, entryPointScore);
|
|
2022
|
-
const
|
|
2023
|
-
|
|
2024
|
-
|
|
1870
|
+
const apiClarityScore = Math.max(
|
|
1871
|
+
0,
|
|
1872
|
+
Math.round(
|
|
1873
|
+
100 - (totalExports > 0 ? untypedExports / totalExports * 70 : 0)
|
|
1874
|
+
)
|
|
1875
|
+
);
|
|
2025
1876
|
const domainConsistencyScore = Math.max(
|
|
2026
1877
|
0,
|
|
2027
|
-
Math.round(
|
|
1878
|
+
Math.round(
|
|
1879
|
+
100 - (domainVocabularySize > 0 ? inconsistentDomainTerms / domainVocabularySize * 80 : 0)
|
|
1880
|
+
)
|
|
2028
1881
|
);
|
|
2029
1882
|
const score = Math.round(
|
|
2030
1883
|
structureClarityScore * 0.2 + selfDocumentationScore * 0.25 + entryPointScore * 0.2 + apiClarityScore * 0.15 + domainConsistencyScore * 0.2
|
|
@@ -2036,35 +1889,30 @@ function calculateAgentGrounding(params) {
|
|
|
2036
1889
|
else if (score >= 30) rating = "poor";
|
|
2037
1890
|
else rating = "disorienting";
|
|
2038
1891
|
const recommendations = [];
|
|
2039
|
-
if (structureClarityScore < 70)
|
|
1892
|
+
if (structureClarityScore < 70)
|
|
2040
1893
|
recommendations.push(
|
|
2041
1894
|
`Flatten ${deepDirectories} overly-deep directories to improve agent navigation`
|
|
2042
1895
|
);
|
|
2043
|
-
|
|
2044
|
-
if (selfDocumentationScore < 70) {
|
|
1896
|
+
if (selfDocumentationScore < 70)
|
|
2045
1897
|
recommendations.push(
|
|
2046
1898
|
`Rename ${vagueFileNames} vague files (utils, helpers, misc) to domain-specific names`
|
|
2047
1899
|
);
|
|
2048
|
-
|
|
2049
|
-
if (!hasRootReadme) {
|
|
1900
|
+
if (!hasRootReadme)
|
|
2050
1901
|
recommendations.push(
|
|
2051
1902
|
"Add a root README.md so agents understand the project context immediately"
|
|
2052
1903
|
);
|
|
2053
|
-
|
|
1904
|
+
else if (!readmeIsFresh)
|
|
2054
1905
|
recommendations.push(
|
|
2055
1906
|
"Update README.md \u2014 stale entry-point documentation disorients agents"
|
|
2056
1907
|
);
|
|
2057
|
-
|
|
2058
|
-
if (apiClarityScore < 70) {
|
|
1908
|
+
if (apiClarityScore < 70)
|
|
2059
1909
|
recommendations.push(
|
|
2060
1910
|
`Add TypeScript types to ${untypedExports} untyped exports to improve API discoverability`
|
|
2061
1911
|
);
|
|
2062
|
-
|
|
2063
|
-
if (domainConsistencyScore < 70) {
|
|
1912
|
+
if (domainConsistencyScore < 70)
|
|
2064
1913
|
recommendations.push(
|
|
2065
1914
|
`Unify ${inconsistentDomainTerms} inconsistent domain terms \u2014 agents need one word per concept`
|
|
2066
1915
|
);
|
|
2067
|
-
}
|
|
2068
1916
|
return {
|
|
2069
1917
|
score,
|
|
2070
1918
|
rating,
|
|
@@ -2078,6 +1926,8 @@ function calculateAgentGrounding(params) {
|
|
|
2078
1926
|
recommendations
|
|
2079
1927
|
};
|
|
2080
1928
|
}
|
|
1929
|
+
|
|
1930
|
+
// src/metrics/testability-index.ts
|
|
2081
1931
|
function calculateTestabilityIndex(params) {
|
|
2082
1932
|
const {
|
|
2083
1933
|
testFiles,
|
|
@@ -2093,16 +1943,27 @@ function calculateTestabilityIndex(params) {
|
|
|
2093
1943
|
} = params;
|
|
2094
1944
|
const rawCoverageRatio = sourceFiles > 0 ? testFiles / sourceFiles : 0;
|
|
2095
1945
|
const testCoverageRatio = Math.min(100, Math.round(rawCoverageRatio * 100));
|
|
2096
|
-
const
|
|
2097
|
-
|
|
2098
|
-
|
|
1946
|
+
const purityScore = Math.round(
|
|
1947
|
+
(totalFunctions > 0 ? pureFunctions / totalFunctions : 0.5) * 100
|
|
1948
|
+
);
|
|
2099
1949
|
const dependencyInjectionScore = Math.round(
|
|
2100
|
-
Math.min(
|
|
1950
|
+
Math.min(
|
|
1951
|
+
100,
|
|
1952
|
+
(totalClasses > 0 ? injectionPatterns / totalClasses : 0.5) * 100
|
|
1953
|
+
)
|
|
1954
|
+
);
|
|
1955
|
+
const interfaceFocusScore = Math.max(
|
|
1956
|
+
0,
|
|
1957
|
+
Math.round(
|
|
1958
|
+
100 - (totalInterfaces > 0 ? bloatedInterfaces / totalInterfaces * 80 : 0)
|
|
1959
|
+
)
|
|
1960
|
+
);
|
|
1961
|
+
const observabilityScore = Math.max(
|
|
1962
|
+
0,
|
|
1963
|
+
Math.round(
|
|
1964
|
+
100 - (totalFunctions > 0 ? externalStateMutations / totalFunctions * 100 : 0)
|
|
1965
|
+
)
|
|
2101
1966
|
);
|
|
2102
|
-
const bloatedRatio = totalInterfaces > 0 ? bloatedInterfaces / totalInterfaces : 0;
|
|
2103
|
-
const interfaceFocusScore = Math.max(0, Math.round(100 - bloatedRatio * 80));
|
|
2104
|
-
const mutationRatio = totalFunctions > 0 ? externalStateMutations / totalFunctions : 0;
|
|
2105
|
-
const observabilityScore = Math.max(0, Math.round(100 - mutationRatio * 100));
|
|
2106
1967
|
const frameworkWeight = hasTestFramework ? 1 : 0.8;
|
|
2107
1968
|
const rawScore = (testCoverageRatio * 0.3 + purityScore * 0.25 + dependencyInjectionScore * 0.2 + interfaceFocusScore * 0.1 + observabilityScore * 0.15) * frameworkWeight;
|
|
2108
1969
|
const score = Math.max(0, Math.min(100, Math.round(rawScore)));
|
|
@@ -2119,32 +1980,28 @@ function calculateTestabilityIndex(params) {
|
|
|
2119
1980
|
else if (rawCoverageRatio > 0) aiChangeSafetyRating = "high-risk";
|
|
2120
1981
|
else aiChangeSafetyRating = "blind-risk";
|
|
2121
1982
|
const recommendations = [];
|
|
2122
|
-
if (!hasTestFramework)
|
|
1983
|
+
if (!hasTestFramework)
|
|
2123
1984
|
recommendations.push(
|
|
2124
1985
|
"Add a testing framework (Jest, Vitest, pytest) \u2014 AI changes cannot be verified without tests"
|
|
2125
1986
|
);
|
|
2126
|
-
}
|
|
2127
1987
|
if (rawCoverageRatio < 0.3) {
|
|
2128
1988
|
const neededTests = Math.round(sourceFiles * 0.3 - testFiles);
|
|
2129
1989
|
recommendations.push(
|
|
2130
1990
|
`Add ~${neededTests} test files to reach 30% coverage ratio \u2014 minimum for safe AI assistance`
|
|
2131
1991
|
);
|
|
2132
1992
|
}
|
|
2133
|
-
if (purityScore < 50)
|
|
1993
|
+
if (purityScore < 50)
|
|
2134
1994
|
recommendations.push(
|
|
2135
1995
|
"Extract pure functions from side-effectful code \u2014 pure functions are trivially AI-testable"
|
|
2136
1996
|
);
|
|
2137
|
-
|
|
2138
|
-
if (dependencyInjectionScore < 50 && totalClasses > 0) {
|
|
1997
|
+
if (dependencyInjectionScore < 50 && totalClasses > 0)
|
|
2139
1998
|
recommendations.push(
|
|
2140
1999
|
"Adopt dependency injection \u2014 makes classes mockable and AI-generated code verifiable"
|
|
2141
2000
|
);
|
|
2142
|
-
|
|
2143
|
-
if (externalStateMutations > totalFunctions * 0.3) {
|
|
2001
|
+
if (externalStateMutations > totalFunctions * 0.3)
|
|
2144
2002
|
recommendations.push(
|
|
2145
2003
|
"Reduce direct state mutations \u2014 return values instead to improve observability"
|
|
2146
2004
|
);
|
|
2147
|
-
}
|
|
2148
2005
|
return {
|
|
2149
2006
|
score,
|
|
2150
2007
|
rating,
|
|
@@ -2159,6 +2016,8 @@ function calculateTestabilityIndex(params) {
|
|
|
2159
2016
|
recommendations
|
|
2160
2017
|
};
|
|
2161
2018
|
}
|
|
2019
|
+
|
|
2020
|
+
// src/metrics/doc-drift.ts
|
|
2162
2021
|
function calculateDocDrift(params) {
|
|
2163
2022
|
const {
|
|
2164
2023
|
uncommentedExports,
|
|
@@ -2181,21 +2040,18 @@ function calculateDocDrift(params) {
|
|
|
2181
2040
|
else if (finalScore < 85) rating = "high";
|
|
2182
2041
|
else rating = "severe";
|
|
2183
2042
|
const recommendations = [];
|
|
2184
|
-
if (outdatedComments > 0)
|
|
2043
|
+
if (outdatedComments > 0)
|
|
2185
2044
|
recommendations.push(
|
|
2186
2045
|
`Update or remove ${outdatedComments} outdated comments that contradict the code.`
|
|
2187
2046
|
);
|
|
2188
|
-
|
|
2189
|
-
if (uncommentedRatio > 0.3) {
|
|
2047
|
+
if (uncommentedRatio > 0.3)
|
|
2190
2048
|
recommendations.push(
|
|
2191
2049
|
`Add JSDoc to ${uncommentedExports} uncommented exports.`
|
|
2192
2050
|
);
|
|
2193
|
-
|
|
2194
|
-
if (undocumentedComplexity > 0) {
|
|
2051
|
+
if (undocumentedComplexity > 0)
|
|
2195
2052
|
recommendations.push(
|
|
2196
2053
|
`Explain the business logic for ${undocumentedComplexity} highly complex functions.`
|
|
2197
2054
|
);
|
|
2198
|
-
}
|
|
2199
2055
|
return {
|
|
2200
2056
|
score: finalScore,
|
|
2201
2057
|
rating,
|
|
@@ -2207,6 +2063,8 @@ function calculateDocDrift(params) {
|
|
|
2207
2063
|
recommendations
|
|
2208
2064
|
};
|
|
2209
2065
|
}
|
|
2066
|
+
|
|
2067
|
+
// src/metrics/dependency-health.ts
|
|
2210
2068
|
function calculateDependencyHealth(params) {
|
|
2211
2069
|
const {
|
|
2212
2070
|
totalPackages,
|
|
@@ -2219,8 +2077,12 @@ function calculateDependencyHealth(params) {
|
|
|
2219
2077
|
const outdatedScore = Math.max(0, 100 - outdatedRatio * 200);
|
|
2220
2078
|
const deprecatedScore = Math.max(0, 100 - deprecatedRatio * 500);
|
|
2221
2079
|
const skewScore = Math.max(0, 100 - trainingCutoffSkew * 100);
|
|
2222
|
-
const
|
|
2223
|
-
|
|
2080
|
+
const score = Math.round(
|
|
2081
|
+
Math.min(
|
|
2082
|
+
100,
|
|
2083
|
+
Math.max(0, outdatedScore * 0.3 + deprecatedScore * 0.4 + skewScore * 0.3)
|
|
2084
|
+
)
|
|
2085
|
+
);
|
|
2224
2086
|
let rating;
|
|
2225
2087
|
if (score >= 85) rating = "excellent";
|
|
2226
2088
|
else if (score >= 70) rating = "good";
|
|
@@ -2235,33 +2097,22 @@ function calculateDependencyHealth(params) {
|
|
|
2235
2097
|
else if (trainingCutoffSkew < 0.8) aiKnowledgeConfidence = "low";
|
|
2236
2098
|
else aiKnowledgeConfidence = "blind";
|
|
2237
2099
|
const recommendations = [];
|
|
2238
|
-
if (deprecatedPackages > 0)
|
|
2239
|
-
recommendations.push(
|
|
2240
|
-
|
|
2241
|
-
);
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
recommendations.push(
|
|
2245
|
-
`Update ${outdatedPackages} outdated packages to keep APIs aligned with AI training data.`
|
|
2246
|
-
);
|
|
2247
|
-
}
|
|
2248
|
-
if (trainingCutoffSkew > 0.5) {
|
|
2249
|
-
recommendations.push(
|
|
2250
|
-
"High training cutoff skew detected. AI may hallucinate APIs that were introduced recently."
|
|
2251
|
-
);
|
|
2252
|
-
}
|
|
2100
|
+
if (deprecatedPackages > 0)
|
|
2101
|
+
recommendations.push(`Replace ${deprecatedPackages} deprecated packages.`);
|
|
2102
|
+
if (outdatedRatio > 0.2)
|
|
2103
|
+
recommendations.push(`Update ${outdatedPackages} outdated packages.`);
|
|
2104
|
+
if (trainingCutoffSkew > 0.5)
|
|
2105
|
+
recommendations.push("High training cutoff skew detected.");
|
|
2253
2106
|
return {
|
|
2254
2107
|
score,
|
|
2255
2108
|
rating,
|
|
2256
|
-
dimensions: {
|
|
2257
|
-
outdatedPackages,
|
|
2258
|
-
deprecatedPackages,
|
|
2259
|
-
trainingCutoffSkew
|
|
2260
|
-
},
|
|
2109
|
+
dimensions: { outdatedPackages, deprecatedPackages, trainingCutoffSkew },
|
|
2261
2110
|
aiKnowledgeConfidence,
|
|
2262
2111
|
recommendations
|
|
2263
2112
|
};
|
|
2264
2113
|
}
|
|
2114
|
+
|
|
2115
|
+
// src/metrics/change-amplification.ts
|
|
2265
2116
|
function calculateChangeAmplification(params) {
|
|
2266
2117
|
const { files } = params;
|
|
2267
2118
|
if (files.length === 0) {
|
|
@@ -2274,15 +2125,16 @@ function calculateChangeAmplification(params) {
|
|
|
2274
2125
|
recommendations: []
|
|
2275
2126
|
};
|
|
2276
2127
|
}
|
|
2277
|
-
const hotspots = files.map((f) => {
|
|
2278
|
-
const amplificationFactor = f.fanOut + f.fanIn * 0.5;
|
|
2279
|
-
return { ...f, amplificationFactor };
|
|
2280
|
-
}).sort((a, b) => b.amplificationFactor - a.amplificationFactor);
|
|
2128
|
+
const hotspots = files.map((f) => ({ ...f, amplificationFactor: f.fanOut + f.fanIn * 0.5 })).sort((a, b) => b.amplificationFactor - a.amplificationFactor);
|
|
2281
2129
|
const maxAmplification = hotspots[0].amplificationFactor;
|
|
2282
2130
|
const avgAmplification = hotspots.reduce((sum, h) => sum + h.amplificationFactor, 0) / hotspots.length;
|
|
2283
|
-
let score =
|
|
2284
|
-
|
|
2285
|
-
|
|
2131
|
+
let score = Math.max(
|
|
2132
|
+
0,
|
|
2133
|
+
Math.min(
|
|
2134
|
+
100,
|
|
2135
|
+
100 - avgAmplification * 5 - (maxAmplification > 20 ? maxAmplification - 20 : 0)
|
|
2136
|
+
)
|
|
2137
|
+
);
|
|
2286
2138
|
let rating = "isolated";
|
|
2287
2139
|
if (score < 40) rating = "explosive";
|
|
2288
2140
|
else if (score < 70) rating = "amplified";
|
|
@@ -2290,12 +2142,12 @@ function calculateChangeAmplification(params) {
|
|
|
2290
2142
|
const recommendations = [];
|
|
2291
2143
|
if (score < 70 && hotspots.length > 0) {
|
|
2292
2144
|
recommendations.push(
|
|
2293
|
-
`Refactor top hotspot '${hotspots[0].file}' to reduce coupling
|
|
2145
|
+
`Refactor top hotspot '${hotspots[0].file}' to reduce coupling.`
|
|
2294
2146
|
);
|
|
2295
2147
|
}
|
|
2296
2148
|
if (maxAmplification > 30) {
|
|
2297
2149
|
recommendations.push(
|
|
2298
|
-
|
|
2150
|
+
"Break down key bottlenecks with amplification factor > 30."
|
|
2299
2151
|
);
|
|
2300
2152
|
}
|
|
2301
2153
|
return {
|
|
@@ -2307,6 +2159,61 @@ function calculateChangeAmplification(params) {
|
|
|
2307
2159
|
recommendations
|
|
2308
2160
|
};
|
|
2309
2161
|
}
|
|
2162
|
+
|
|
2163
|
+
// src/future-proof-metrics.ts
|
|
2164
|
+
function calculateFutureProofScore(params) {
|
|
2165
|
+
const loadScore = 100 - params.cognitiveLoad.score;
|
|
2166
|
+
const entropyScore = 100 - params.patternEntropy.entropy * 100;
|
|
2167
|
+
const cohesionScore = params.conceptCohesion.score * 100;
|
|
2168
|
+
const overall = Math.round(
|
|
2169
|
+
loadScore * 0.4 + entropyScore * 0.3 + cohesionScore * 0.3
|
|
2170
|
+
);
|
|
2171
|
+
const factors = [
|
|
2172
|
+
{
|
|
2173
|
+
name: "Cognitive Load",
|
|
2174
|
+
impact: Math.round(loadScore - 50),
|
|
2175
|
+
description: params.cognitiveLoad.rating
|
|
2176
|
+
},
|
|
2177
|
+
{
|
|
2178
|
+
name: "Pattern Entropy",
|
|
2179
|
+
impact: Math.round(entropyScore - 50),
|
|
2180
|
+
description: params.patternEntropy.rating
|
|
2181
|
+
},
|
|
2182
|
+
{
|
|
2183
|
+
name: "Concept Cohesion",
|
|
2184
|
+
impact: Math.round(cohesionScore - 50),
|
|
2185
|
+
description: params.conceptCohesion.rating
|
|
2186
|
+
}
|
|
2187
|
+
];
|
|
2188
|
+
const recommendations = [];
|
|
2189
|
+
for (const rec of params.patternEntropy.recommendations) {
|
|
2190
|
+
recommendations.push({
|
|
2191
|
+
action: rec,
|
|
2192
|
+
estimatedImpact: 5,
|
|
2193
|
+
priority: "medium"
|
|
2194
|
+
});
|
|
2195
|
+
}
|
|
2196
|
+
if (params.conceptCohesion.rating === "poor") {
|
|
2197
|
+
recommendations.push({
|
|
2198
|
+
action: "Improve concept cohesion by grouping related exports",
|
|
2199
|
+
estimatedImpact: 8,
|
|
2200
|
+
priority: "high"
|
|
2201
|
+
});
|
|
2202
|
+
}
|
|
2203
|
+
const semanticDistanceAvg = params.semanticDistances?.length ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
|
|
2204
|
+
return {
|
|
2205
|
+
toolName: "future-proof",
|
|
2206
|
+
score: overall,
|
|
2207
|
+
rawMetrics: {
|
|
2208
|
+
cognitiveLoadScore: params.cognitiveLoad.score,
|
|
2209
|
+
entropyScore: params.patternEntropy.entropy,
|
|
2210
|
+
cohesionScore: params.conceptCohesion.score,
|
|
2211
|
+
semanticDistanceAvg
|
|
2212
|
+
},
|
|
2213
|
+
factors,
|
|
2214
|
+
recommendations
|
|
2215
|
+
};
|
|
2216
|
+
}
|
|
2310
2217
|
function calculateExtendedFutureProofScore(params) {
|
|
2311
2218
|
const loadScore = 100 - params.cognitiveLoad.score;
|
|
2312
2219
|
const entropyScore = 100 - params.patternEntropy.entropy * 100;
|
|
@@ -2315,7 +2222,7 @@ function calculateExtendedFutureProofScore(params) {
|
|
|
2315
2222
|
const groundingScore = params.agentGrounding.score;
|
|
2316
2223
|
const testabilityScore = params.testability.score;
|
|
2317
2224
|
const docDriftScore = params.docDrift ? 100 - params.docDrift.score : 100;
|
|
2318
|
-
const depsHealthScore = params.dependencyHealth
|
|
2225
|
+
const depsHealthScore = params.dependencyHealth?.score ?? 100;
|
|
2319
2226
|
let totalWeight = 0.8;
|
|
2320
2227
|
let overall = loadScore * 0.15 + entropyScore * 0.1 + cohesionScore * 0.1 + aiSignalClarityScore * 0.15 + groundingScore * 0.15 + testabilityScore * 0.15;
|
|
2321
2228
|
if (params.docDrift) {
|
|
@@ -2346,7 +2253,7 @@ function calculateExtendedFutureProofScore(params) {
|
|
|
2346
2253
|
{
|
|
2347
2254
|
name: "AI Signal Clarity",
|
|
2348
2255
|
impact: Math.round(aiSignalClarityScore - 50),
|
|
2349
|
-
description: `${params.aiSignalClarity.rating} risk
|
|
2256
|
+
description: `${params.aiSignalClarity.rating} risk`
|
|
2350
2257
|
},
|
|
2351
2258
|
{
|
|
2352
2259
|
name: "Agent Grounding",
|
|
@@ -2356,60 +2263,25 @@ function calculateExtendedFutureProofScore(params) {
|
|
|
2356
2263
|
{
|
|
2357
2264
|
name: "Testability",
|
|
2358
2265
|
impact: Math.round(testabilityScore - 50),
|
|
2359
|
-
description:
|
|
2266
|
+
description: params.testability.rating
|
|
2360
2267
|
}
|
|
2361
2268
|
];
|
|
2362
2269
|
if (params.docDrift) {
|
|
2363
2270
|
factors.push({
|
|
2364
2271
|
name: "Documentation Drift",
|
|
2365
2272
|
impact: Math.round(docDriftScore - 50),
|
|
2366
|
-
description:
|
|
2273
|
+
description: params.docDrift.rating
|
|
2367
2274
|
});
|
|
2368
2275
|
}
|
|
2369
2276
|
if (params.dependencyHealth) {
|
|
2370
2277
|
factors.push({
|
|
2371
2278
|
name: "Dependency Health",
|
|
2372
2279
|
impact: Math.round(depsHealthScore - 50),
|
|
2373
|
-
description:
|
|
2374
|
-
});
|
|
2375
|
-
}
|
|
2376
|
-
const recommendations = [];
|
|
2377
|
-
for (const rec of params.aiSignalClarity.recommendations) {
|
|
2378
|
-
recommendations.push({ action: rec, estimatedImpact: 8, priority: "high" });
|
|
2379
|
-
}
|
|
2380
|
-
for (const rec of params.agentGrounding.recommendations) {
|
|
2381
|
-
recommendations.push({
|
|
2382
|
-
action: rec,
|
|
2383
|
-
estimatedImpact: 6,
|
|
2384
|
-
priority: "medium"
|
|
2280
|
+
description: params.dependencyHealth.rating
|
|
2385
2281
|
});
|
|
2386
2282
|
}
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
recommendations.push({ action: rec, estimatedImpact: 10, priority });
|
|
2390
|
-
}
|
|
2391
|
-
for (const rec of params.patternEntropy.recommendations) {
|
|
2392
|
-
recommendations.push({ action: rec, estimatedImpact: 5, priority: "low" });
|
|
2393
|
-
}
|
|
2394
|
-
if (params.docDrift) {
|
|
2395
|
-
for (const rec of params.docDrift.recommendations) {
|
|
2396
|
-
recommendations.push({
|
|
2397
|
-
action: rec,
|
|
2398
|
-
estimatedImpact: 8,
|
|
2399
|
-
priority: "high"
|
|
2400
|
-
});
|
|
2401
|
-
}
|
|
2402
|
-
}
|
|
2403
|
-
if (params.dependencyHealth) {
|
|
2404
|
-
for (const rec of params.dependencyHealth.recommendations) {
|
|
2405
|
-
recommendations.push({
|
|
2406
|
-
action: rec,
|
|
2407
|
-
estimatedImpact: 7,
|
|
2408
|
-
priority: "medium"
|
|
2409
|
-
});
|
|
2410
|
-
}
|
|
2411
|
-
}
|
|
2412
|
-
const semanticDistanceAvg = params.semanticDistances && params.semanticDistances.length > 0 ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
|
|
2283
|
+
const recommendations = collectFutureProofRecommendations(params);
|
|
2284
|
+
const semanticDistanceAvg = params.semanticDistances?.length ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
|
|
2413
2285
|
return {
|
|
2414
2286
|
toolName: "future-proof",
|
|
2415
2287
|
score: overall,
|
|
@@ -2585,16 +2457,19 @@ export {
|
|
|
2585
2457
|
ParseError,
|
|
2586
2458
|
ParserFactory,
|
|
2587
2459
|
PythonParser,
|
|
2460
|
+
SEVERITY_TIME_ESTIMATES,
|
|
2588
2461
|
SIZE_ADJUSTED_THRESHOLDS,
|
|
2589
2462
|
TOOL_NAME_MAP,
|
|
2590
2463
|
TypeScriptParser,
|
|
2591
2464
|
VAGUE_FILE_NAMES,
|
|
2592
2465
|
calculateAgentGrounding,
|
|
2593
2466
|
calculateAiSignalClarity,
|
|
2467
|
+
calculateBusinessROI,
|
|
2594
2468
|
calculateChangeAmplification,
|
|
2595
2469
|
calculateCognitiveLoad,
|
|
2596
2470
|
calculateComprehensionDifficulty,
|
|
2597
2471
|
calculateConceptCohesion,
|
|
2472
|
+
calculateDebtInterest,
|
|
2598
2473
|
calculateDependencyHealth,
|
|
2599
2474
|
calculateDocDrift,
|
|
2600
2475
|
calculateExtendedFutureProofScore,
|
|
@@ -2605,10 +2480,8 @@ export {
|
|
|
2605
2480
|
calculateOverallScore,
|
|
2606
2481
|
calculatePatternEntropy,
|
|
2607
2482
|
calculateProductivityImpact,
|
|
2608
|
-
calculateRemediationVelocity,
|
|
2609
|
-
calculateScoreTrend,
|
|
2610
2483
|
calculateSemanticDistance,
|
|
2611
|
-
|
|
2484
|
+
calculateTechnicalValueChain,
|
|
2612
2485
|
calculateTestabilityIndex,
|
|
2613
2486
|
calculateTokenBudget,
|
|
2614
2487
|
clearHistory,
|
|
@@ -2624,7 +2497,6 @@ export {
|
|
|
2624
2497
|
formatToolScore,
|
|
2625
2498
|
generateHTML,
|
|
2626
2499
|
generateValueChain,
|
|
2627
|
-
getDebtBreakdown,
|
|
2628
2500
|
getElapsedTime,
|
|
2629
2501
|
getFileCommitTimestamps,
|
|
2630
2502
|
getFileExtension,
|
|
@@ -2638,6 +2510,9 @@ export {
|
|
|
2638
2510
|
getRatingWithContext,
|
|
2639
2511
|
getRecommendedThreshold,
|
|
2640
2512
|
getRepoMetadata,
|
|
2513
|
+
getSafetyIcon,
|
|
2514
|
+
getScoreBar,
|
|
2515
|
+
getSeverityColor,
|
|
2641
2516
|
getSupportedLanguages,
|
|
2642
2517
|
getToolWeight,
|
|
2643
2518
|
handleCLIError,
|