@aiready/core 0.9.38 → 0.9.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.mts +14 -6
- package/dist/client.d.ts +14 -6
- package/dist/index.d.mts +167 -280
- package/dist/index.d.ts +167 -280
- package/dist/index.js +412 -540
- package/dist/index.mjs +405 -536
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -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,
|
|
@@ -633,7 +655,6 @@ function calculateTokenBudget(params) {
|
|
|
633
655
|
},
|
|
634
656
|
efficiencyRatio: Math.round(efficiencyRatio * 100) / 100,
|
|
635
657
|
potentialRetrievableTokens: Math.round(totalWaste * 0.8)
|
|
636
|
-
// Heuristic: 80% is fixable
|
|
637
658
|
};
|
|
638
659
|
}
|
|
639
660
|
function estimateCostFromBudget(budget, model, config = {}) {
|
|
@@ -657,6 +678,15 @@ function estimateCostFromBudget(budget, model, config = {}) {
|
|
|
657
678
|
confidence
|
|
658
679
|
};
|
|
659
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;
|
|
660
690
|
function calculateProductivityImpact(issues, hourlyRate = DEFAULT_HOURLY_RATE) {
|
|
661
691
|
const counts = {
|
|
662
692
|
critical: issues.filter((i) => i.severity === "critical").length,
|
|
@@ -697,108 +727,158 @@ function predictAcceptanceRate(toolOutputs) {
|
|
|
697
727
|
const baseRate = 0.3;
|
|
698
728
|
const patterns = toolOutputs.get("pattern-detect");
|
|
699
729
|
if (patterns) {
|
|
700
|
-
const patternImpact = (patterns.score - 50) * 3e-3;
|
|
701
730
|
factors.push({
|
|
702
731
|
name: "Semantic Duplication",
|
|
703
|
-
impact: Math.round(
|
|
732
|
+
impact: Math.round((patterns.score - 50) * 3e-3 * 100)
|
|
704
733
|
});
|
|
705
734
|
}
|
|
706
735
|
const context = toolOutputs.get("context-analyzer");
|
|
707
736
|
if (context) {
|
|
708
|
-
const contextImpact = (context.score - 50) * 4e-3;
|
|
709
737
|
factors.push({
|
|
710
738
|
name: "Context Efficiency",
|
|
711
|
-
impact: Math.round(
|
|
739
|
+
impact: Math.round((context.score - 50) * 4e-3 * 100)
|
|
712
740
|
});
|
|
713
741
|
}
|
|
714
742
|
const consistency = toolOutputs.get("consistency");
|
|
715
743
|
if (consistency) {
|
|
716
|
-
const consistencyImpact = (consistency.score - 50) * 2e-3;
|
|
717
744
|
factors.push({
|
|
718
745
|
name: "Code Consistency",
|
|
719
|
-
impact: Math.round(
|
|
746
|
+
impact: Math.round((consistency.score - 50) * 2e-3 * 100)
|
|
720
747
|
});
|
|
721
748
|
}
|
|
722
749
|
const aiSignalClarity = toolOutputs.get("ai-signal-clarity");
|
|
723
750
|
if (aiSignalClarity) {
|
|
724
|
-
const hrImpact = (50 - aiSignalClarity.score) * 2e-3;
|
|
725
751
|
factors.push({
|
|
726
752
|
name: "AI Signal Clarity",
|
|
727
|
-
impact: Math.round(
|
|
753
|
+
impact: Math.round((50 - aiSignalClarity.score) * 2e-3 * 100)
|
|
728
754
|
});
|
|
729
755
|
}
|
|
730
756
|
const totalImpact = factors.reduce((sum, f) => sum + f.impact / 100, 0);
|
|
731
757
|
const rate = Math.max(0.05, Math.min(0.8, baseRate + totalImpact));
|
|
732
|
-
let confidence;
|
|
758
|
+
let confidence = 0.35;
|
|
733
759
|
if (toolOutputs.size >= 4) confidence = 0.75;
|
|
734
760
|
else if (toolOutputs.size >= 3) confidence = 0.65;
|
|
735
761
|
else if (toolOutputs.size >= 2) confidence = 0.5;
|
|
736
|
-
|
|
737
|
-
return {
|
|
738
|
-
rate: Math.round(rate * 100) / 100,
|
|
739
|
-
confidence,
|
|
740
|
-
factors
|
|
741
|
-
};
|
|
762
|
+
return { rate: Math.round(rate * 100) / 100, confidence, factors };
|
|
742
763
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
const
|
|
747
|
-
const
|
|
748
|
-
const budgetRange = criticalBudget - idealBudget;
|
|
749
|
-
const budgetFactor = Math.min(
|
|
750
|
-
100,
|
|
751
|
-
Math.max(0, (contextBudget - idealBudget) / budgetRange * 100)
|
|
752
|
-
);
|
|
753
|
-
const depthFactor = Math.min(
|
|
754
|
-
100,
|
|
755
|
-
Math.max(0, (importDepth - idealDepth) * 10)
|
|
756
|
-
);
|
|
757
|
-
const fragmentationFactor = Math.min(
|
|
758
|
-
100,
|
|
759
|
-
Math.max(0, (fragmentation - 0.3) * 250)
|
|
760
|
-
);
|
|
761
|
-
const consistencyFactor = Math.min(100, Math.max(0, 100 - consistencyScore));
|
|
762
|
-
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;
|
|
763
769
|
const score = Math.round(
|
|
764
|
-
|
|
770
|
+
Math.min(
|
|
771
|
+
100,
|
|
772
|
+
concentrationRatio * 100 + orphanFiles / Math.max(1, totalFiles) * 20
|
|
773
|
+
)
|
|
765
774
|
);
|
|
766
775
|
let rating;
|
|
767
|
-
if (score <
|
|
768
|
-
else if (score <
|
|
769
|
-
else if (score <
|
|
770
|
-
else
|
|
771
|
-
|
|
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
|
+
);
|
|
772
789
|
return {
|
|
773
790
|
score,
|
|
774
791
|
rating,
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
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)
|
|
802
882
|
};
|
|
803
883
|
}
|
|
804
884
|
function formatCost(cost) {
|
|
@@ -824,220 +904,6 @@ function formatHours(hours) {
|
|
|
824
904
|
function formatAcceptanceRate(rate) {
|
|
825
905
|
return `${Math.round(rate * 100)}%`;
|
|
826
906
|
}
|
|
827
|
-
function calculateScoreTrend(history) {
|
|
828
|
-
if (history.length < 2) {
|
|
829
|
-
return {
|
|
830
|
-
direction: "stable",
|
|
831
|
-
change30Days: 0,
|
|
832
|
-
change90Days: 0,
|
|
833
|
-
velocity: 0,
|
|
834
|
-
projectedScore: history[0]?.overallScore || 100
|
|
835
|
-
};
|
|
836
|
-
}
|
|
837
|
-
const now = /* @__PURE__ */ new Date();
|
|
838
|
-
const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1e3);
|
|
839
|
-
const ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1e3);
|
|
840
|
-
const last30Days = history.filter(
|
|
841
|
-
(e) => new Date(e.timestamp) >= thirtyDaysAgo
|
|
842
|
-
);
|
|
843
|
-
const last90Days = history.filter(
|
|
844
|
-
(e) => new Date(e.timestamp) >= ninetyDaysAgo
|
|
845
|
-
);
|
|
846
|
-
const currentScore = history[history.length - 1].overallScore;
|
|
847
|
-
const thirtyDaysAgoScore = last30Days[0]?.overallScore || currentScore;
|
|
848
|
-
const ninetyDaysAgoScore = last90Days[0]?.overallScore || thirtyDaysAgoScore;
|
|
849
|
-
const change30Days = currentScore - thirtyDaysAgoScore;
|
|
850
|
-
const change90Days = currentScore - ninetyDaysAgoScore;
|
|
851
|
-
const weeksOfData = Math.max(1, history.length / 7);
|
|
852
|
-
const totalChange = currentScore - history[0].overallScore;
|
|
853
|
-
const velocity = totalChange / weeksOfData;
|
|
854
|
-
let direction;
|
|
855
|
-
if (change30Days > 3) direction = "improving";
|
|
856
|
-
else if (change30Days < -3) direction = "degrading";
|
|
857
|
-
else direction = "stable";
|
|
858
|
-
const projectedScore = Math.max(
|
|
859
|
-
0,
|
|
860
|
-
Math.min(100, currentScore + velocity * 4)
|
|
861
|
-
);
|
|
862
|
-
return {
|
|
863
|
-
direction,
|
|
864
|
-
change30Days,
|
|
865
|
-
change90Days,
|
|
866
|
-
velocity: Math.round(velocity * 10) / 10,
|
|
867
|
-
projectedScore: Math.round(projectedScore)
|
|
868
|
-
};
|
|
869
|
-
}
|
|
870
|
-
function calculateRemediationVelocity(history, currentIssues) {
|
|
871
|
-
if (history.length < 2) {
|
|
872
|
-
return {
|
|
873
|
-
issuesFixedThisWeek: 0,
|
|
874
|
-
avgIssuesPerWeek: 0,
|
|
875
|
-
trend: "stable",
|
|
876
|
-
estimatedCompletionWeeks: currentIssues > 0 ? Infinity : 0
|
|
877
|
-
};
|
|
878
|
-
}
|
|
879
|
-
const now = /* @__PURE__ */ new Date();
|
|
880
|
-
const oneWeekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1e3);
|
|
881
|
-
const twoWeeksAgo = new Date(now.getTime() - 14 * 24 * 60 * 60 * 1e3);
|
|
882
|
-
const thisWeek = history.filter((e) => new Date(e.timestamp) >= oneWeekAgo);
|
|
883
|
-
const lastWeek = history.filter(
|
|
884
|
-
(e) => new Date(e.timestamp) >= twoWeeksAgo && new Date(e.timestamp) < oneWeekAgo
|
|
885
|
-
);
|
|
886
|
-
const issuesFixedThisWeek = thisWeek.length > 1 ? thisWeek[0].totalIssues - thisWeek[thisWeek.length - 1].totalIssues : 0;
|
|
887
|
-
const totalIssuesFixed = history[0].totalIssues - history[history.length - 1].totalIssues;
|
|
888
|
-
const weeksOfData = Math.max(1, history.length / 7);
|
|
889
|
-
const avgIssuesPerWeek = totalIssuesFixed / weeksOfData;
|
|
890
|
-
let trend;
|
|
891
|
-
if (lastWeek.length > 1) {
|
|
892
|
-
const lastWeekFixed = lastWeek[0].totalIssues - lastWeek[lastWeek.length - 1].totalIssues;
|
|
893
|
-
if (issuesFixedThisWeek > lastWeekFixed * 1.2) trend = "accelerating";
|
|
894
|
-
else if (issuesFixedThisWeek < lastWeekFixed * 0.8) trend = "decelerating";
|
|
895
|
-
else trend = "stable";
|
|
896
|
-
} else {
|
|
897
|
-
trend = "stable";
|
|
898
|
-
}
|
|
899
|
-
const estimatedCompletionWeeks = avgIssuesPerWeek > 0 ? Math.ceil(currentIssues / avgIssuesPerWeek) : Infinity;
|
|
900
|
-
return {
|
|
901
|
-
issuesFixedThisWeek: Math.max(0, issuesFixedThisWeek),
|
|
902
|
-
avgIssuesPerWeek: Math.round(avgIssuesPerWeek * 10) / 10,
|
|
903
|
-
trend,
|
|
904
|
-
estimatedCompletionWeeks
|
|
905
|
-
};
|
|
906
|
-
}
|
|
907
|
-
function calculateKnowledgeConcentration(files, authorData) {
|
|
908
|
-
if (files.length === 0) {
|
|
909
|
-
return {
|
|
910
|
-
score: 0,
|
|
911
|
-
rating: "low",
|
|
912
|
-
analysis: {
|
|
913
|
-
uniqueConceptFiles: 0,
|
|
914
|
-
totalFiles: 0,
|
|
915
|
-
concentrationRatio: 0,
|
|
916
|
-
singleAuthorFiles: 0,
|
|
917
|
-
orphanFiles: 0
|
|
918
|
-
},
|
|
919
|
-
recommendations: ["No files to analyze"]
|
|
920
|
-
};
|
|
921
|
-
}
|
|
922
|
-
const orphanFiles = files.filter(
|
|
923
|
-
(f) => f.exports < 2 && f.imports < 2
|
|
924
|
-
).length;
|
|
925
|
-
const avgExports = files.reduce((sum, f) => sum + f.exports, 0) / files.length;
|
|
926
|
-
const uniqueConceptFiles = files.filter(
|
|
927
|
-
(f) => f.exports > avgExports * 2
|
|
928
|
-
).length;
|
|
929
|
-
const totalExports = files.reduce((sum, f) => sum + f.exports, 0);
|
|
930
|
-
const concentrationRatio = totalExports > 0 ? uniqueConceptFiles / files.length : 0;
|
|
931
|
-
let singleAuthorFiles = 0;
|
|
932
|
-
if (authorData) {
|
|
933
|
-
for (const files2 of authorData.values()) {
|
|
934
|
-
if (files2.length === 1) singleAuthorFiles++;
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
const orphanRisk = orphanFiles / files.length * 30;
|
|
938
|
-
const uniqueRisk = concentrationRatio * 40;
|
|
939
|
-
const singleAuthorRisk = authorData ? singleAuthorFiles / files.length * 30 : 0;
|
|
940
|
-
const score = Math.min(
|
|
941
|
-
100,
|
|
942
|
-
Math.round(orphanRisk + uniqueRisk + singleAuthorRisk)
|
|
943
|
-
);
|
|
944
|
-
let rating;
|
|
945
|
-
if (score < 20) rating = "low";
|
|
946
|
-
else if (score < 40) rating = "moderate";
|
|
947
|
-
else if (score < 70) rating = "high";
|
|
948
|
-
else rating = "critical";
|
|
949
|
-
const recommendations = [];
|
|
950
|
-
if (orphanFiles > files.length * 0.2) {
|
|
951
|
-
recommendations.push(
|
|
952
|
-
`Reduce ${orphanFiles} orphan files by connecting them to main modules`
|
|
953
|
-
);
|
|
954
|
-
}
|
|
955
|
-
if (uniqueConceptFiles > files.length * 0.1) {
|
|
956
|
-
recommendations.push(
|
|
957
|
-
"Distribute high-export files into more focused modules"
|
|
958
|
-
);
|
|
959
|
-
}
|
|
960
|
-
if (authorData && singleAuthorFiles > files.length * 0.3) {
|
|
961
|
-
recommendations.push(
|
|
962
|
-
"Increase knowledge sharing to reduce single-author dependencies"
|
|
963
|
-
);
|
|
964
|
-
}
|
|
965
|
-
return {
|
|
966
|
-
score,
|
|
967
|
-
rating,
|
|
968
|
-
analysis: {
|
|
969
|
-
uniqueConceptFiles,
|
|
970
|
-
totalFiles: files.length,
|
|
971
|
-
concentrationRatio: Math.round(concentrationRatio * 100) / 100,
|
|
972
|
-
singleAuthorFiles,
|
|
973
|
-
orphanFiles
|
|
974
|
-
},
|
|
975
|
-
recommendations
|
|
976
|
-
};
|
|
977
|
-
}
|
|
978
|
-
function calculateTechnicalDebtInterest(params) {
|
|
979
|
-
const { currentMonthlyCost, issues, monthsOpen } = params;
|
|
980
|
-
const criticalCount = issues.filter((i) => i.severity === "critical").length;
|
|
981
|
-
const majorCount = issues.filter((i) => i.severity === "major").length;
|
|
982
|
-
const minorCount = issues.filter((i) => i.severity === "minor").length;
|
|
983
|
-
const severityWeight = (criticalCount * 3 + majorCount * 2 + minorCount * 1) / Math.max(1, issues.length);
|
|
984
|
-
const baseRate = 0.02 + severityWeight * 0.01;
|
|
985
|
-
const timeMultiplier = Math.max(1, 1 + monthsOpen * 0.1);
|
|
986
|
-
const monthlyRate = baseRate * timeMultiplier;
|
|
987
|
-
const projectDebt = (principal2, months) => {
|
|
988
|
-
let debt = principal2;
|
|
989
|
-
for (let i = 0; i < months; i++) {
|
|
990
|
-
debt = debt * (1 + monthlyRate);
|
|
991
|
-
}
|
|
992
|
-
return Math.round(debt);
|
|
993
|
-
};
|
|
994
|
-
const principal = currentMonthlyCost * 12;
|
|
995
|
-
const projections = {
|
|
996
|
-
months6: projectDebt(principal, 6),
|
|
997
|
-
months12: projectDebt(principal, 12),
|
|
998
|
-
months24: projectDebt(principal, 24)
|
|
999
|
-
};
|
|
1000
|
-
return {
|
|
1001
|
-
monthlyRate: Math.round(monthlyRate * 1e4) / 100,
|
|
1002
|
-
annualRate: Math.round((Math.pow(1 + monthlyRate, 12) - 1) * 1e4) / 100,
|
|
1003
|
-
principal,
|
|
1004
|
-
projections,
|
|
1005
|
-
monthlyCost: Math.round(currentMonthlyCost * (1 + monthlyRate) * 100) / 100
|
|
1006
|
-
};
|
|
1007
|
-
}
|
|
1008
|
-
function getDebtBreakdown(patternCost, contextCost, consistencyCost) {
|
|
1009
|
-
const breakdowns = [
|
|
1010
|
-
{
|
|
1011
|
-
category: "Semantic Duplication",
|
|
1012
|
-
currentCost: patternCost,
|
|
1013
|
-
monthlyGrowthRate: 5,
|
|
1014
|
-
// Grows as devs copy-paste
|
|
1015
|
-
priority: patternCost > 1e3 ? "high" : "medium",
|
|
1016
|
-
fixCost: patternCost * 3
|
|
1017
|
-
// Fixing costs 3x current waste
|
|
1018
|
-
},
|
|
1019
|
-
{
|
|
1020
|
-
category: "Context Fragmentation",
|
|
1021
|
-
currentCost: contextCost,
|
|
1022
|
-
monthlyGrowthRate: 3,
|
|
1023
|
-
// Grows with new features
|
|
1024
|
-
priority: contextCost > 500 ? "high" : "medium",
|
|
1025
|
-
fixCost: contextCost * 2.5
|
|
1026
|
-
},
|
|
1027
|
-
{
|
|
1028
|
-
category: "Consistency Issues",
|
|
1029
|
-
currentCost: consistencyCost,
|
|
1030
|
-
monthlyGrowthRate: 2,
|
|
1031
|
-
// Grows with new devs
|
|
1032
|
-
priority: consistencyCost > 200 ? "medium" : "low",
|
|
1033
|
-
fixCost: consistencyCost * 1.5
|
|
1034
|
-
}
|
|
1035
|
-
];
|
|
1036
|
-
return breakdowns.sort((a, b) => {
|
|
1037
|
-
const priorityOrder = { high: 0, medium: 1, low: 2 };
|
|
1038
|
-
return priorityOrder[a.priority] - priorityOrder[b.priority];
|
|
1039
|
-
});
|
|
1040
|
-
}
|
|
1041
907
|
function generateValueChain(params) {
|
|
1042
908
|
const { issueType, count, severity } = params;
|
|
1043
909
|
const impacts = {
|
|
@@ -1077,9 +943,7 @@ function generateValueChain(params) {
|
|
|
1077
943
|
},
|
|
1078
944
|
businessOutcome: {
|
|
1079
945
|
directCost: count * 12,
|
|
1080
|
-
// Placeholder linear cost
|
|
1081
946
|
opportunityCost: productivityLoss * 15e3,
|
|
1082
|
-
// Monthly avg dev cost $15k
|
|
1083
947
|
riskLevel: impact.risk
|
|
1084
948
|
}
|
|
1085
949
|
};
|
|
@@ -1594,7 +1458,55 @@ function getSupportedLanguages() {
|
|
|
1594
1458
|
return ParserFactory.getInstance().getSupportedLanguages();
|
|
1595
1459
|
}
|
|
1596
1460
|
|
|
1597
|
-
// 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
|
|
1598
1510
|
function calculateCognitiveLoad(params) {
|
|
1599
1511
|
const {
|
|
1600
1512
|
linesOfCode,
|
|
@@ -1653,13 +1565,20 @@ function calculateCognitiveLoad(params) {
|
|
|
1653
1565
|
}
|
|
1654
1566
|
};
|
|
1655
1567
|
}
|
|
1568
|
+
|
|
1569
|
+
// src/metrics/semantic-distance.ts
|
|
1656
1570
|
function calculateSemanticDistance(params) {
|
|
1657
|
-
const {
|
|
1571
|
+
const {
|
|
1572
|
+
file1,
|
|
1573
|
+
file2,
|
|
1574
|
+
file1Domain,
|
|
1575
|
+
file2Domain,
|
|
1576
|
+
sharedDependencies,
|
|
1577
|
+
file1Imports,
|
|
1578
|
+
file2Imports
|
|
1579
|
+
} = params;
|
|
1658
1580
|
const domainDistance = file1Domain === file2Domain ? 0 : file1Domain && file2Domain ? 0.5 : 0.8;
|
|
1659
|
-
const importOverlap = sharedDependencies.length / Math.max(
|
|
1660
|
-
1,
|
|
1661
|
-
Math.min(params.file1Imports.length, params.file2Imports.length)
|
|
1662
|
-
);
|
|
1581
|
+
const importOverlap = sharedDependencies.length / Math.max(1, Math.min(file1Imports.length, file2Imports.length));
|
|
1663
1582
|
const importDistance = 1 - importOverlap;
|
|
1664
1583
|
const distance = domainDistance * 0.4 + importDistance * 0.3 + (sharedDependencies.length > 0 ? 0 : 0.3);
|
|
1665
1584
|
let relationship;
|
|
@@ -1678,6 +1597,8 @@ function calculateSemanticDistance(params) {
|
|
|
1678
1597
|
reason: relationship === "same-domain" ? `Both in "${file1Domain}" domain` : relationship === "cross-domain" ? `Share ${sharedDependencies.length} dependency(ies)` : "No strong semantic relationship detected"
|
|
1679
1598
|
};
|
|
1680
1599
|
}
|
|
1600
|
+
|
|
1601
|
+
// src/metrics/structural-metrics.ts
|
|
1681
1602
|
function calculatePatternEntropy(files) {
|
|
1682
1603
|
if (files.length === 0) {
|
|
1683
1604
|
return {
|
|
@@ -1727,23 +1648,18 @@ function calculatePatternEntropy(files) {
|
|
|
1727
1648
|
else if (normalizedEntropy < 0.8) rating = "fragmented";
|
|
1728
1649
|
else rating = "chaotic";
|
|
1729
1650
|
const recommendations = [];
|
|
1730
|
-
if (normalizedEntropy > 0.5)
|
|
1651
|
+
if (normalizedEntropy > 0.5)
|
|
1731
1652
|
recommendations.push(
|
|
1732
1653
|
`Consolidate ${files.length} files into fewer directories by domain`
|
|
1733
1654
|
);
|
|
1734
|
-
|
|
1735
|
-
if (dirGroups.size > 5) {
|
|
1655
|
+
if (dirGroups.size > 5)
|
|
1736
1656
|
recommendations.push(
|
|
1737
1657
|
"Consider barrel exports to reduce directory navigation"
|
|
1738
1658
|
);
|
|
1739
|
-
|
|
1740
|
-
if (gini > 0.5) {
|
|
1659
|
+
if (gini > 0.5)
|
|
1741
1660
|
recommendations.push("Redistribute files more evenly across directories");
|
|
1742
|
-
}
|
|
1743
|
-
const firstFile = files.length > 0 ? files[0] : null;
|
|
1744
|
-
const domainValue = firstFile ? firstFile.domain : "mixed";
|
|
1745
1661
|
return {
|
|
1746
|
-
domain:
|
|
1662
|
+
domain: files[0]?.domain || "mixed",
|
|
1747
1663
|
entropy: Math.round(normalizedEntropy * 100) / 100,
|
|
1748
1664
|
rating,
|
|
1749
1665
|
distribution: {
|
|
@@ -1774,9 +1690,8 @@ function calculateConceptCohesion(params) {
|
|
|
1774
1690
|
}
|
|
1775
1691
|
const uniqueDomains = new Set(allDomains);
|
|
1776
1692
|
const domainCounts = /* @__PURE__ */ new Map();
|
|
1777
|
-
for (const d of allDomains)
|
|
1693
|
+
for (const d of allDomains)
|
|
1778
1694
|
domainCounts.set(d, (domainCounts.get(d) || 0) + 1);
|
|
1779
|
-
}
|
|
1780
1695
|
const maxCount = Math.max(...Array.from(domainCounts.values()), 1);
|
|
1781
1696
|
const domainConcentration = maxCount / allDomains.length;
|
|
1782
1697
|
const exportPurposeClarity = 1 - (uniqueDomains.size - 1) / Math.max(1, exports.length);
|
|
@@ -1796,59 +1711,8 @@ function calculateConceptCohesion(params) {
|
|
|
1796
1711
|
}
|
|
1797
1712
|
};
|
|
1798
1713
|
}
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
const entropyScore = 100 - params.patternEntropy.entropy * 100;
|
|
1802
|
-
const cohesionScore = params.conceptCohesion.score * 100;
|
|
1803
|
-
const overall = Math.round(
|
|
1804
|
-
loadScore * 0.4 + entropyScore * 0.3 + cohesionScore * 0.3
|
|
1805
|
-
);
|
|
1806
|
-
const factors = [
|
|
1807
|
-
{
|
|
1808
|
-
name: "Cognitive Load",
|
|
1809
|
-
impact: Math.round(loadScore - 50),
|
|
1810
|
-
description: params.cognitiveLoad.rating
|
|
1811
|
-
},
|
|
1812
|
-
{
|
|
1813
|
-
name: "Pattern Entropy",
|
|
1814
|
-
impact: Math.round(entropyScore - 50),
|
|
1815
|
-
description: params.patternEntropy.rating
|
|
1816
|
-
},
|
|
1817
|
-
{
|
|
1818
|
-
name: "Concept Cohesion",
|
|
1819
|
-
impact: Math.round(cohesionScore - 50),
|
|
1820
|
-
description: params.conceptCohesion.rating
|
|
1821
|
-
}
|
|
1822
|
-
];
|
|
1823
|
-
const recommendations = [];
|
|
1824
|
-
for (const rec of params.patternEntropy.recommendations) {
|
|
1825
|
-
recommendations.push({
|
|
1826
|
-
action: rec,
|
|
1827
|
-
estimatedImpact: 5,
|
|
1828
|
-
priority: "medium"
|
|
1829
|
-
});
|
|
1830
|
-
}
|
|
1831
|
-
if (params.conceptCohesion.rating === "poor") {
|
|
1832
|
-
recommendations.push({
|
|
1833
|
-
action: "Improve concept cohesion by grouping related exports",
|
|
1834
|
-
estimatedImpact: 8,
|
|
1835
|
-
priority: "high"
|
|
1836
|
-
});
|
|
1837
|
-
}
|
|
1838
|
-
const semanticDistanceAvg = params.semanticDistances && params.semanticDistances.length > 0 ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
|
|
1839
|
-
return {
|
|
1840
|
-
toolName: "future-proof",
|
|
1841
|
-
score: overall,
|
|
1842
|
-
rawMetrics: {
|
|
1843
|
-
cognitiveLoadScore: params.cognitiveLoad.score,
|
|
1844
|
-
entropyScore: params.patternEntropy.entropy,
|
|
1845
|
-
cohesionScore: params.conceptCohesion.score,
|
|
1846
|
-
semanticDistanceAvg
|
|
1847
|
-
},
|
|
1848
|
-
factors,
|
|
1849
|
-
recommendations
|
|
1850
|
-
};
|
|
1851
|
-
}
|
|
1714
|
+
|
|
1715
|
+
// src/metrics/ai-signal-clarity.ts
|
|
1852
1716
|
function calculateAiSignalClarity(params) {
|
|
1853
1717
|
const {
|
|
1854
1718
|
overloadedSymbols,
|
|
@@ -1870,75 +1734,53 @@ function calculateAiSignalClarity(params) {
|
|
|
1870
1734
|
recommendations: []
|
|
1871
1735
|
};
|
|
1872
1736
|
}
|
|
1873
|
-
const overloadRatio = Math.
|
|
1874
|
-
1,
|
|
1875
|
-
overloadedSymbols / Math.max(1, totalSymbols)
|
|
1876
|
-
);
|
|
1737
|
+
const overloadRatio = overloadedSymbols / Math.max(1, totalSymbols);
|
|
1877
1738
|
const overloadSignal = {
|
|
1878
1739
|
name: "Symbol Overloading",
|
|
1879
1740
|
count: overloadedSymbols,
|
|
1880
|
-
riskContribution: Math.round(overloadRatio * 100 * 0.25),
|
|
1881
|
-
// 25% weight
|
|
1741
|
+
riskContribution: Math.round(Math.min(1, overloadRatio) * 100 * 0.25),
|
|
1882
1742
|
description: `${overloadedSymbols} overloaded symbols \u2014 AI picks wrong signature`
|
|
1883
1743
|
};
|
|
1884
|
-
const magicRatio =
|
|
1744
|
+
const magicRatio = magicLiterals / Math.max(1, totalSymbols * 2);
|
|
1885
1745
|
const magicSignal = {
|
|
1886
1746
|
name: "Magic Literals",
|
|
1887
1747
|
count: magicLiterals,
|
|
1888
|
-
riskContribution: Math.round(magicRatio * 100 * 0.2),
|
|
1889
|
-
// 20% weight
|
|
1748
|
+
riskContribution: Math.round(Math.min(1, magicRatio) * 100 * 0.2),
|
|
1890
1749
|
description: `${magicLiterals} unnamed constants \u2014 AI invents wrong values`
|
|
1891
1750
|
};
|
|
1892
|
-
const trapRatio =
|
|
1751
|
+
const trapRatio = booleanTraps / Math.max(1, totalSymbols);
|
|
1893
1752
|
const trapSignal = {
|
|
1894
1753
|
name: "Boolean Traps",
|
|
1895
1754
|
count: booleanTraps,
|
|
1896
|
-
riskContribution: Math.round(trapRatio * 100 * 0.2),
|
|
1897
|
-
// 20% weight
|
|
1755
|
+
riskContribution: Math.round(Math.min(1, trapRatio) * 100 * 0.2),
|
|
1898
1756
|
description: `${booleanTraps} boolean trap parameters \u2014 AI inverts intent`
|
|
1899
1757
|
};
|
|
1900
|
-
const sideEffectRatio = Math.
|
|
1901
|
-
1,
|
|
1902
|
-
implicitSideEffects / Math.max(1, totalExports)
|
|
1903
|
-
);
|
|
1758
|
+
const sideEffectRatio = implicitSideEffects / Math.max(1, totalExports);
|
|
1904
1759
|
const sideEffectSignal = {
|
|
1905
1760
|
name: "Implicit Side Effects",
|
|
1906
1761
|
count: implicitSideEffects,
|
|
1907
|
-
riskContribution: Math.round(sideEffectRatio * 100 * 0.15),
|
|
1908
|
-
// 15% weight
|
|
1762
|
+
riskContribution: Math.round(Math.min(1, sideEffectRatio) * 100 * 0.15),
|
|
1909
1763
|
description: `${implicitSideEffects} functions with implicit side effects \u2014 AI misses contracts`
|
|
1910
1764
|
};
|
|
1911
|
-
const callbackRatio = Math.
|
|
1912
|
-
1,
|
|
1913
|
-
deepCallbacks / Math.max(1, totalSymbols * 0.1)
|
|
1914
|
-
);
|
|
1765
|
+
const callbackRatio = deepCallbacks / Math.max(1, totalSymbols * 0.1);
|
|
1915
1766
|
const callbackSignal = {
|
|
1916
1767
|
name: "Callback Nesting",
|
|
1917
1768
|
count: deepCallbacks,
|
|
1918
|
-
riskContribution: Math.round(callbackRatio * 100 * 0.1),
|
|
1919
|
-
// 10% weight
|
|
1769
|
+
riskContribution: Math.round(Math.min(1, callbackRatio) * 100 * 0.1),
|
|
1920
1770
|
description: `${deepCallbacks} deep callback chains \u2014 AI loses control flow context`
|
|
1921
1771
|
};
|
|
1922
|
-
const ambiguousRatio = Math.
|
|
1923
|
-
1,
|
|
1924
|
-
ambiguousNames / Math.max(1, totalSymbols)
|
|
1925
|
-
);
|
|
1772
|
+
const ambiguousRatio = ambiguousNames / Math.max(1, totalSymbols);
|
|
1926
1773
|
const ambiguousSignal = {
|
|
1927
1774
|
name: "Ambiguous Names",
|
|
1928
1775
|
count: ambiguousNames,
|
|
1929
|
-
riskContribution: Math.round(ambiguousRatio * 100 * 0.1),
|
|
1930
|
-
// 10% weight
|
|
1776
|
+
riskContribution: Math.round(Math.min(1, ambiguousRatio) * 100 * 0.1),
|
|
1931
1777
|
description: `${ambiguousNames} non-descriptive identifiers \u2014 AI guesses wrong intent`
|
|
1932
1778
|
};
|
|
1933
|
-
const undocRatio = Math.
|
|
1934
|
-
1,
|
|
1935
|
-
undocumentedExports / Math.max(1, totalExports)
|
|
1936
|
-
);
|
|
1779
|
+
const undocRatio = undocumentedExports / Math.max(1, totalExports);
|
|
1937
1780
|
const undocSignal = {
|
|
1938
1781
|
name: "Undocumented Exports",
|
|
1939
1782
|
count: undocumentedExports,
|
|
1940
|
-
riskContribution: Math.round(undocRatio * 100 * 0.1),
|
|
1941
|
-
// 10% weight
|
|
1783
|
+
riskContribution: Math.round(Math.min(1, undocRatio) * 100 * 0.1),
|
|
1942
1784
|
description: `${undocumentedExports} public functions without docs \u2014 AI fabricates behavior`
|
|
1943
1785
|
};
|
|
1944
1786
|
const signals = [
|
|
@@ -1963,33 +1805,28 @@ function calculateAiSignalClarity(params) {
|
|
|
1963
1805
|
const topSignal = signals.reduce(
|
|
1964
1806
|
(a, b) => a.riskContribution > b.riskContribution ? a : b
|
|
1965
1807
|
);
|
|
1966
|
-
const topRisk = topSignal.riskContribution > 0 ? topSignal.description : "No significant
|
|
1808
|
+
const topRisk = topSignal.riskContribution > 0 ? topSignal.description : "No significant issues detected";
|
|
1967
1809
|
const recommendations = [];
|
|
1968
|
-
if (overloadSignal.riskContribution > 5)
|
|
1810
|
+
if (overloadSignal.riskContribution > 5)
|
|
1969
1811
|
recommendations.push(
|
|
1970
1812
|
`Rename ${overloadedSymbols} overloaded symbols to unique, intent-revealing names`
|
|
1971
1813
|
);
|
|
1972
|
-
|
|
1973
|
-
if (magicSignal.riskContribution > 5) {
|
|
1814
|
+
if (magicSignal.riskContribution > 5)
|
|
1974
1815
|
recommendations.push(
|
|
1975
1816
|
`Extract ${magicLiterals} magic literals into named constants`
|
|
1976
1817
|
);
|
|
1977
|
-
|
|
1978
|
-
if (trapSignal.riskContribution > 5) {
|
|
1818
|
+
if (trapSignal.riskContribution > 5)
|
|
1979
1819
|
recommendations.push(
|
|
1980
1820
|
`Replace ${booleanTraps} boolean traps with named options objects`
|
|
1981
1821
|
);
|
|
1982
|
-
|
|
1983
|
-
if (undocSignal.riskContribution > 5) {
|
|
1822
|
+
if (undocSignal.riskContribution > 5)
|
|
1984
1823
|
recommendations.push(
|
|
1985
1824
|
`Add JSDoc/docstrings to ${undocumentedExports} undocumented public functions`
|
|
1986
1825
|
);
|
|
1987
|
-
|
|
1988
|
-
if (sideEffectSignal.riskContribution > 5) {
|
|
1826
|
+
if (sideEffectSignal.riskContribution > 5)
|
|
1989
1827
|
recommendations.push(
|
|
1990
1828
|
"Mark functions with side effects explicitly in their names or docs"
|
|
1991
1829
|
);
|
|
1992
|
-
}
|
|
1993
1830
|
return {
|
|
1994
1831
|
score: Math.round(score),
|
|
1995
1832
|
rating,
|
|
@@ -1998,6 +1835,8 @@ function calculateAiSignalClarity(params) {
|
|
|
1998
1835
|
recommendations
|
|
1999
1836
|
};
|
|
2000
1837
|
}
|
|
1838
|
+
|
|
1839
|
+
// src/metrics/agent-grounding.ts
|
|
2001
1840
|
function calculateAgentGrounding(params) {
|
|
2002
1841
|
const {
|
|
2003
1842
|
deepDirectories,
|
|
@@ -2012,25 +1851,33 @@ function calculateAgentGrounding(params) {
|
|
|
2012
1851
|
inconsistentDomainTerms,
|
|
2013
1852
|
domainVocabularySize
|
|
2014
1853
|
} = params;
|
|
2015
|
-
const deepDirRatio = totalDirectories > 0 ? deepDirectories / totalDirectories : 0;
|
|
2016
1854
|
const structureClarityScore = Math.max(
|
|
2017
1855
|
0,
|
|
2018
|
-
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))
|
|
2019
1863
|
);
|
|
2020
|
-
const vagueRatio = totalFiles > 0 ? vagueFileNames / totalFiles : 0;
|
|
2021
|
-
const selfDocumentationScore = Math.max(0, Math.round(100 - vagueRatio * 90));
|
|
2022
1864
|
let entryPointScore = 60;
|
|
2023
1865
|
if (hasRootReadme) entryPointScore += 25;
|
|
2024
1866
|
if (readmeIsFresh) entryPointScore += 10;
|
|
2025
1867
|
const barrelRatio = totalFiles > 0 ? barrelExports / (totalFiles * 0.1) : 0;
|
|
2026
1868
|
entryPointScore += Math.round(Math.min(5, barrelRatio * 5));
|
|
2027
1869
|
entryPointScore = Math.min(100, entryPointScore);
|
|
2028
|
-
const
|
|
2029
|
-
|
|
2030
|
-
|
|
1870
|
+
const apiClarityScore = Math.max(
|
|
1871
|
+
0,
|
|
1872
|
+
Math.round(
|
|
1873
|
+
100 - (totalExports > 0 ? untypedExports / totalExports * 70 : 0)
|
|
1874
|
+
)
|
|
1875
|
+
);
|
|
2031
1876
|
const domainConsistencyScore = Math.max(
|
|
2032
1877
|
0,
|
|
2033
|
-
Math.round(
|
|
1878
|
+
Math.round(
|
|
1879
|
+
100 - (domainVocabularySize > 0 ? inconsistentDomainTerms / domainVocabularySize * 80 : 0)
|
|
1880
|
+
)
|
|
2034
1881
|
);
|
|
2035
1882
|
const score = Math.round(
|
|
2036
1883
|
structureClarityScore * 0.2 + selfDocumentationScore * 0.25 + entryPointScore * 0.2 + apiClarityScore * 0.15 + domainConsistencyScore * 0.2
|
|
@@ -2042,35 +1889,30 @@ function calculateAgentGrounding(params) {
|
|
|
2042
1889
|
else if (score >= 30) rating = "poor";
|
|
2043
1890
|
else rating = "disorienting";
|
|
2044
1891
|
const recommendations = [];
|
|
2045
|
-
if (structureClarityScore < 70)
|
|
1892
|
+
if (structureClarityScore < 70)
|
|
2046
1893
|
recommendations.push(
|
|
2047
1894
|
`Flatten ${deepDirectories} overly-deep directories to improve agent navigation`
|
|
2048
1895
|
);
|
|
2049
|
-
|
|
2050
|
-
if (selfDocumentationScore < 70) {
|
|
1896
|
+
if (selfDocumentationScore < 70)
|
|
2051
1897
|
recommendations.push(
|
|
2052
1898
|
`Rename ${vagueFileNames} vague files (utils, helpers, misc) to domain-specific names`
|
|
2053
1899
|
);
|
|
2054
|
-
|
|
2055
|
-
if (!hasRootReadme) {
|
|
1900
|
+
if (!hasRootReadme)
|
|
2056
1901
|
recommendations.push(
|
|
2057
1902
|
"Add a root README.md so agents understand the project context immediately"
|
|
2058
1903
|
);
|
|
2059
|
-
|
|
1904
|
+
else if (!readmeIsFresh)
|
|
2060
1905
|
recommendations.push(
|
|
2061
1906
|
"Update README.md \u2014 stale entry-point documentation disorients agents"
|
|
2062
1907
|
);
|
|
2063
|
-
|
|
2064
|
-
if (apiClarityScore < 70) {
|
|
1908
|
+
if (apiClarityScore < 70)
|
|
2065
1909
|
recommendations.push(
|
|
2066
1910
|
`Add TypeScript types to ${untypedExports} untyped exports to improve API discoverability`
|
|
2067
1911
|
);
|
|
2068
|
-
|
|
2069
|
-
if (domainConsistencyScore < 70) {
|
|
1912
|
+
if (domainConsistencyScore < 70)
|
|
2070
1913
|
recommendations.push(
|
|
2071
1914
|
`Unify ${inconsistentDomainTerms} inconsistent domain terms \u2014 agents need one word per concept`
|
|
2072
1915
|
);
|
|
2073
|
-
}
|
|
2074
1916
|
return {
|
|
2075
1917
|
score,
|
|
2076
1918
|
rating,
|
|
@@ -2084,6 +1926,8 @@ function calculateAgentGrounding(params) {
|
|
|
2084
1926
|
recommendations
|
|
2085
1927
|
};
|
|
2086
1928
|
}
|
|
1929
|
+
|
|
1930
|
+
// src/metrics/testability-index.ts
|
|
2087
1931
|
function calculateTestabilityIndex(params) {
|
|
2088
1932
|
const {
|
|
2089
1933
|
testFiles,
|
|
@@ -2099,16 +1943,27 @@ function calculateTestabilityIndex(params) {
|
|
|
2099
1943
|
} = params;
|
|
2100
1944
|
const rawCoverageRatio = sourceFiles > 0 ? testFiles / sourceFiles : 0;
|
|
2101
1945
|
const testCoverageRatio = Math.min(100, Math.round(rawCoverageRatio * 100));
|
|
2102
|
-
const
|
|
2103
|
-
|
|
2104
|
-
|
|
1946
|
+
const purityScore = Math.round(
|
|
1947
|
+
(totalFunctions > 0 ? pureFunctions / totalFunctions : 0.5) * 100
|
|
1948
|
+
);
|
|
2105
1949
|
const dependencyInjectionScore = Math.round(
|
|
2106
|
-
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
|
+
)
|
|
2107
1966
|
);
|
|
2108
|
-
const bloatedRatio = totalInterfaces > 0 ? bloatedInterfaces / totalInterfaces : 0;
|
|
2109
|
-
const interfaceFocusScore = Math.max(0, Math.round(100 - bloatedRatio * 80));
|
|
2110
|
-
const mutationRatio = totalFunctions > 0 ? externalStateMutations / totalFunctions : 0;
|
|
2111
|
-
const observabilityScore = Math.max(0, Math.round(100 - mutationRatio * 100));
|
|
2112
1967
|
const frameworkWeight = hasTestFramework ? 1 : 0.8;
|
|
2113
1968
|
const rawScore = (testCoverageRatio * 0.3 + purityScore * 0.25 + dependencyInjectionScore * 0.2 + interfaceFocusScore * 0.1 + observabilityScore * 0.15) * frameworkWeight;
|
|
2114
1969
|
const score = Math.max(0, Math.min(100, Math.round(rawScore)));
|
|
@@ -2125,32 +1980,28 @@ function calculateTestabilityIndex(params) {
|
|
|
2125
1980
|
else if (rawCoverageRatio > 0) aiChangeSafetyRating = "high-risk";
|
|
2126
1981
|
else aiChangeSafetyRating = "blind-risk";
|
|
2127
1982
|
const recommendations = [];
|
|
2128
|
-
if (!hasTestFramework)
|
|
1983
|
+
if (!hasTestFramework)
|
|
2129
1984
|
recommendations.push(
|
|
2130
1985
|
"Add a testing framework (Jest, Vitest, pytest) \u2014 AI changes cannot be verified without tests"
|
|
2131
1986
|
);
|
|
2132
|
-
}
|
|
2133
1987
|
if (rawCoverageRatio < 0.3) {
|
|
2134
1988
|
const neededTests = Math.round(sourceFiles * 0.3 - testFiles);
|
|
2135
1989
|
recommendations.push(
|
|
2136
1990
|
`Add ~${neededTests} test files to reach 30% coverage ratio \u2014 minimum for safe AI assistance`
|
|
2137
1991
|
);
|
|
2138
1992
|
}
|
|
2139
|
-
if (purityScore < 50)
|
|
1993
|
+
if (purityScore < 50)
|
|
2140
1994
|
recommendations.push(
|
|
2141
1995
|
"Extract pure functions from side-effectful code \u2014 pure functions are trivially AI-testable"
|
|
2142
1996
|
);
|
|
2143
|
-
|
|
2144
|
-
if (dependencyInjectionScore < 50 && totalClasses > 0) {
|
|
1997
|
+
if (dependencyInjectionScore < 50 && totalClasses > 0)
|
|
2145
1998
|
recommendations.push(
|
|
2146
1999
|
"Adopt dependency injection \u2014 makes classes mockable and AI-generated code verifiable"
|
|
2147
2000
|
);
|
|
2148
|
-
|
|
2149
|
-
if (externalStateMutations > totalFunctions * 0.3) {
|
|
2001
|
+
if (externalStateMutations > totalFunctions * 0.3)
|
|
2150
2002
|
recommendations.push(
|
|
2151
2003
|
"Reduce direct state mutations \u2014 return values instead to improve observability"
|
|
2152
2004
|
);
|
|
2153
|
-
}
|
|
2154
2005
|
return {
|
|
2155
2006
|
score,
|
|
2156
2007
|
rating,
|
|
@@ -2165,6 +2016,8 @@ function calculateTestabilityIndex(params) {
|
|
|
2165
2016
|
recommendations
|
|
2166
2017
|
};
|
|
2167
2018
|
}
|
|
2019
|
+
|
|
2020
|
+
// src/metrics/doc-drift.ts
|
|
2168
2021
|
function calculateDocDrift(params) {
|
|
2169
2022
|
const {
|
|
2170
2023
|
uncommentedExports,
|
|
@@ -2187,21 +2040,18 @@ function calculateDocDrift(params) {
|
|
|
2187
2040
|
else if (finalScore < 85) rating = "high";
|
|
2188
2041
|
else rating = "severe";
|
|
2189
2042
|
const recommendations = [];
|
|
2190
|
-
if (outdatedComments > 0)
|
|
2043
|
+
if (outdatedComments > 0)
|
|
2191
2044
|
recommendations.push(
|
|
2192
2045
|
`Update or remove ${outdatedComments} outdated comments that contradict the code.`
|
|
2193
2046
|
);
|
|
2194
|
-
|
|
2195
|
-
if (uncommentedRatio > 0.3) {
|
|
2047
|
+
if (uncommentedRatio > 0.3)
|
|
2196
2048
|
recommendations.push(
|
|
2197
2049
|
`Add JSDoc to ${uncommentedExports} uncommented exports.`
|
|
2198
2050
|
);
|
|
2199
|
-
|
|
2200
|
-
if (undocumentedComplexity > 0) {
|
|
2051
|
+
if (undocumentedComplexity > 0)
|
|
2201
2052
|
recommendations.push(
|
|
2202
2053
|
`Explain the business logic for ${undocumentedComplexity} highly complex functions.`
|
|
2203
2054
|
);
|
|
2204
|
-
}
|
|
2205
2055
|
return {
|
|
2206
2056
|
score: finalScore,
|
|
2207
2057
|
rating,
|
|
@@ -2213,6 +2063,8 @@ function calculateDocDrift(params) {
|
|
|
2213
2063
|
recommendations
|
|
2214
2064
|
};
|
|
2215
2065
|
}
|
|
2066
|
+
|
|
2067
|
+
// src/metrics/dependency-health.ts
|
|
2216
2068
|
function calculateDependencyHealth(params) {
|
|
2217
2069
|
const {
|
|
2218
2070
|
totalPackages,
|
|
@@ -2225,8 +2077,12 @@ function calculateDependencyHealth(params) {
|
|
|
2225
2077
|
const outdatedScore = Math.max(0, 100 - outdatedRatio * 200);
|
|
2226
2078
|
const deprecatedScore = Math.max(0, 100 - deprecatedRatio * 500);
|
|
2227
2079
|
const skewScore = Math.max(0, 100 - trainingCutoffSkew * 100);
|
|
2228
|
-
const
|
|
2229
|
-
|
|
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
|
+
);
|
|
2230
2086
|
let rating;
|
|
2231
2087
|
if (score >= 85) rating = "excellent";
|
|
2232
2088
|
else if (score >= 70) rating = "good";
|
|
@@ -2241,33 +2097,22 @@ function calculateDependencyHealth(params) {
|
|
|
2241
2097
|
else if (trainingCutoffSkew < 0.8) aiKnowledgeConfidence = "low";
|
|
2242
2098
|
else aiKnowledgeConfidence = "blind";
|
|
2243
2099
|
const recommendations = [];
|
|
2244
|
-
if (deprecatedPackages > 0)
|
|
2245
|
-
recommendations.push(
|
|
2246
|
-
|
|
2247
|
-
);
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
recommendations.push(
|
|
2251
|
-
`Update ${outdatedPackages} outdated packages to keep APIs aligned with AI training data.`
|
|
2252
|
-
);
|
|
2253
|
-
}
|
|
2254
|
-
if (trainingCutoffSkew > 0.5) {
|
|
2255
|
-
recommendations.push(
|
|
2256
|
-
"High training cutoff skew detected. AI may hallucinate APIs that were introduced recently."
|
|
2257
|
-
);
|
|
2258
|
-
}
|
|
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.");
|
|
2259
2106
|
return {
|
|
2260
2107
|
score,
|
|
2261
2108
|
rating,
|
|
2262
|
-
dimensions: {
|
|
2263
|
-
outdatedPackages,
|
|
2264
|
-
deprecatedPackages,
|
|
2265
|
-
trainingCutoffSkew
|
|
2266
|
-
},
|
|
2109
|
+
dimensions: { outdatedPackages, deprecatedPackages, trainingCutoffSkew },
|
|
2267
2110
|
aiKnowledgeConfidence,
|
|
2268
2111
|
recommendations
|
|
2269
2112
|
};
|
|
2270
2113
|
}
|
|
2114
|
+
|
|
2115
|
+
// src/metrics/change-amplification.ts
|
|
2271
2116
|
function calculateChangeAmplification(params) {
|
|
2272
2117
|
const { files } = params;
|
|
2273
2118
|
if (files.length === 0) {
|
|
@@ -2280,15 +2125,16 @@ function calculateChangeAmplification(params) {
|
|
|
2280
2125
|
recommendations: []
|
|
2281
2126
|
};
|
|
2282
2127
|
}
|
|
2283
|
-
const hotspots = files.map((f) => {
|
|
2284
|
-
const amplificationFactor = f.fanOut + f.fanIn * 0.5;
|
|
2285
|
-
return { ...f, amplificationFactor };
|
|
2286
|
-
}).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);
|
|
2287
2129
|
const maxAmplification = hotspots[0].amplificationFactor;
|
|
2288
2130
|
const avgAmplification = hotspots.reduce((sum, h) => sum + h.amplificationFactor, 0) / hotspots.length;
|
|
2289
|
-
let score =
|
|
2290
|
-
|
|
2291
|
-
|
|
2131
|
+
let score = Math.max(
|
|
2132
|
+
0,
|
|
2133
|
+
Math.min(
|
|
2134
|
+
100,
|
|
2135
|
+
100 - avgAmplification * 5 - (maxAmplification > 20 ? maxAmplification - 20 : 0)
|
|
2136
|
+
)
|
|
2137
|
+
);
|
|
2292
2138
|
let rating = "isolated";
|
|
2293
2139
|
if (score < 40) rating = "explosive";
|
|
2294
2140
|
else if (score < 70) rating = "amplified";
|
|
@@ -2296,12 +2142,12 @@ function calculateChangeAmplification(params) {
|
|
|
2296
2142
|
const recommendations = [];
|
|
2297
2143
|
if (score < 70 && hotspots.length > 0) {
|
|
2298
2144
|
recommendations.push(
|
|
2299
|
-
`Refactor top hotspot '${hotspots[0].file}' to reduce coupling
|
|
2145
|
+
`Refactor top hotspot '${hotspots[0].file}' to reduce coupling.`
|
|
2300
2146
|
);
|
|
2301
2147
|
}
|
|
2302
2148
|
if (maxAmplification > 30) {
|
|
2303
2149
|
recommendations.push(
|
|
2304
|
-
|
|
2150
|
+
"Break down key bottlenecks with amplification factor > 30."
|
|
2305
2151
|
);
|
|
2306
2152
|
}
|
|
2307
2153
|
return {
|
|
@@ -2313,6 +2159,61 @@ function calculateChangeAmplification(params) {
|
|
|
2313
2159
|
recommendations
|
|
2314
2160
|
};
|
|
2315
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
|
+
}
|
|
2316
2217
|
function calculateExtendedFutureProofScore(params) {
|
|
2317
2218
|
const loadScore = 100 - params.cognitiveLoad.score;
|
|
2318
2219
|
const entropyScore = 100 - params.patternEntropy.entropy * 100;
|
|
@@ -2321,7 +2222,7 @@ function calculateExtendedFutureProofScore(params) {
|
|
|
2321
2222
|
const groundingScore = params.agentGrounding.score;
|
|
2322
2223
|
const testabilityScore = params.testability.score;
|
|
2323
2224
|
const docDriftScore = params.docDrift ? 100 - params.docDrift.score : 100;
|
|
2324
|
-
const depsHealthScore = params.dependencyHealth
|
|
2225
|
+
const depsHealthScore = params.dependencyHealth?.score ?? 100;
|
|
2325
2226
|
let totalWeight = 0.8;
|
|
2326
2227
|
let overall = loadScore * 0.15 + entropyScore * 0.1 + cohesionScore * 0.1 + aiSignalClarityScore * 0.15 + groundingScore * 0.15 + testabilityScore * 0.15;
|
|
2327
2228
|
if (params.docDrift) {
|
|
@@ -2352,7 +2253,7 @@ function calculateExtendedFutureProofScore(params) {
|
|
|
2352
2253
|
{
|
|
2353
2254
|
name: "AI Signal Clarity",
|
|
2354
2255
|
impact: Math.round(aiSignalClarityScore - 50),
|
|
2355
|
-
description: `${params.aiSignalClarity.rating} risk
|
|
2256
|
+
description: `${params.aiSignalClarity.rating} risk`
|
|
2356
2257
|
},
|
|
2357
2258
|
{
|
|
2358
2259
|
name: "Agent Grounding",
|
|
@@ -2362,60 +2263,25 @@ function calculateExtendedFutureProofScore(params) {
|
|
|
2362
2263
|
{
|
|
2363
2264
|
name: "Testability",
|
|
2364
2265
|
impact: Math.round(testabilityScore - 50),
|
|
2365
|
-
description:
|
|
2266
|
+
description: params.testability.rating
|
|
2366
2267
|
}
|
|
2367
2268
|
];
|
|
2368
2269
|
if (params.docDrift) {
|
|
2369
2270
|
factors.push({
|
|
2370
2271
|
name: "Documentation Drift",
|
|
2371
2272
|
impact: Math.round(docDriftScore - 50),
|
|
2372
|
-
description:
|
|
2273
|
+
description: params.docDrift.rating
|
|
2373
2274
|
});
|
|
2374
2275
|
}
|
|
2375
2276
|
if (params.dependencyHealth) {
|
|
2376
2277
|
factors.push({
|
|
2377
2278
|
name: "Dependency Health",
|
|
2378
2279
|
impact: Math.round(depsHealthScore - 50),
|
|
2379
|
-
description:
|
|
2380
|
-
});
|
|
2381
|
-
}
|
|
2382
|
-
const recommendations = [];
|
|
2383
|
-
for (const rec of params.aiSignalClarity.recommendations) {
|
|
2384
|
-
recommendations.push({ action: rec, estimatedImpact: 8, priority: "high" });
|
|
2385
|
-
}
|
|
2386
|
-
for (const rec of params.agentGrounding.recommendations) {
|
|
2387
|
-
recommendations.push({
|
|
2388
|
-
action: rec,
|
|
2389
|
-
estimatedImpact: 6,
|
|
2390
|
-
priority: "medium"
|
|
2280
|
+
description: params.dependencyHealth.rating
|
|
2391
2281
|
});
|
|
2392
2282
|
}
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
recommendations.push({ action: rec, estimatedImpact: 10, priority });
|
|
2396
|
-
}
|
|
2397
|
-
for (const rec of params.patternEntropy.recommendations) {
|
|
2398
|
-
recommendations.push({ action: rec, estimatedImpact: 5, priority: "low" });
|
|
2399
|
-
}
|
|
2400
|
-
if (params.docDrift) {
|
|
2401
|
-
for (const rec of params.docDrift.recommendations) {
|
|
2402
|
-
recommendations.push({
|
|
2403
|
-
action: rec,
|
|
2404
|
-
estimatedImpact: 8,
|
|
2405
|
-
priority: "high"
|
|
2406
|
-
});
|
|
2407
|
-
}
|
|
2408
|
-
}
|
|
2409
|
-
if (params.dependencyHealth) {
|
|
2410
|
-
for (const rec of params.dependencyHealth.recommendations) {
|
|
2411
|
-
recommendations.push({
|
|
2412
|
-
action: rec,
|
|
2413
|
-
estimatedImpact: 7,
|
|
2414
|
-
priority: "medium"
|
|
2415
|
-
});
|
|
2416
|
-
}
|
|
2417
|
-
}
|
|
2418
|
-
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;
|
|
2419
2285
|
return {
|
|
2420
2286
|
toolName: "future-proof",
|
|
2421
2287
|
score: overall,
|
|
@@ -2591,16 +2457,19 @@ export {
|
|
|
2591
2457
|
ParseError,
|
|
2592
2458
|
ParserFactory,
|
|
2593
2459
|
PythonParser,
|
|
2460
|
+
SEVERITY_TIME_ESTIMATES,
|
|
2594
2461
|
SIZE_ADJUSTED_THRESHOLDS,
|
|
2595
2462
|
TOOL_NAME_MAP,
|
|
2596
2463
|
TypeScriptParser,
|
|
2597
2464
|
VAGUE_FILE_NAMES,
|
|
2598
2465
|
calculateAgentGrounding,
|
|
2599
2466
|
calculateAiSignalClarity,
|
|
2467
|
+
calculateBusinessROI,
|
|
2600
2468
|
calculateChangeAmplification,
|
|
2601
2469
|
calculateCognitiveLoad,
|
|
2602
2470
|
calculateComprehensionDifficulty,
|
|
2603
2471
|
calculateConceptCohesion,
|
|
2472
|
+
calculateDebtInterest,
|
|
2604
2473
|
calculateDependencyHealth,
|
|
2605
2474
|
calculateDocDrift,
|
|
2606
2475
|
calculateExtendedFutureProofScore,
|
|
@@ -2611,10 +2480,8 @@ export {
|
|
|
2611
2480
|
calculateOverallScore,
|
|
2612
2481
|
calculatePatternEntropy,
|
|
2613
2482
|
calculateProductivityImpact,
|
|
2614
|
-
calculateRemediationVelocity,
|
|
2615
|
-
calculateScoreTrend,
|
|
2616
2483
|
calculateSemanticDistance,
|
|
2617
|
-
|
|
2484
|
+
calculateTechnicalValueChain,
|
|
2618
2485
|
calculateTestabilityIndex,
|
|
2619
2486
|
calculateTokenBudget,
|
|
2620
2487
|
clearHistory,
|
|
@@ -2630,7 +2497,6 @@ export {
|
|
|
2630
2497
|
formatToolScore,
|
|
2631
2498
|
generateHTML,
|
|
2632
2499
|
generateValueChain,
|
|
2633
|
-
getDebtBreakdown,
|
|
2634
2500
|
getElapsedTime,
|
|
2635
2501
|
getFileCommitTimestamps,
|
|
2636
2502
|
getFileExtension,
|
|
@@ -2644,6 +2510,9 @@ export {
|
|
|
2644
2510
|
getRatingWithContext,
|
|
2645
2511
|
getRecommendedThreshold,
|
|
2646
2512
|
getRepoMetadata,
|
|
2513
|
+
getSafetyIcon,
|
|
2514
|
+
getScoreBar,
|
|
2515
|
+
getSeverityColor,
|
|
2647
2516
|
getSupportedLanguages,
|
|
2648
2517
|
getToolWeight,
|
|
2649
2518
|
handleCLIError,
|