@aiready/core 0.23.19 → 0.23.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -91,6 +91,7 @@ __export(index_exports, {
91
91
  calculateConceptCohesion: () => calculateConceptCohesion,
92
92
  calculateDebtInterest: () => calculateDebtInterest,
93
93
  calculateDependencyHealth: () => calculateDependencyHealth,
94
+ calculateDetailedTokenROI: () => calculateDetailedTokenROI,
94
95
  calculateDocDrift: () => calculateDocDrift,
95
96
  calculateExtendedFutureProofScore: () => calculateExtendedFutureProofScore,
96
97
  calculateFutureProofScore: () => calculateFutureProofScore,
@@ -119,7 +120,12 @@ __export(index_exports, {
119
120
  formatHours: () => formatHours,
120
121
  formatScore: () => formatScore,
121
122
  formatToolScore: () => formatToolScore,
123
+ generateCompleteReport: () => generateCompleteReport,
122
124
  generateHTML: () => generateHTML,
125
+ generateReportFooter: () => generateReportFooter,
126
+ generateReportHead: () => generateReportHead,
127
+ generateStatCards: () => generateStatCards,
128
+ generateTable: () => generateTable,
123
129
  generateValueChain: () => generateValueChain,
124
130
  getElapsedTime: () => getElapsedTime,
125
131
  getFileCommitTimestamps: () => getFileCommitTimestamps,
@@ -128,9 +134,12 @@ __export(index_exports, {
128
134
  getLineRangeLastModifiedCached: () => getLineRangeLastModifiedCached,
129
135
  getModelPreset: () => getModelPreset,
130
136
  getParser: () => getParser,
137
+ getPriorityIcon: () => getPriorityIcon,
131
138
  getProjectSizeTier: () => getProjectSizeTier,
132
139
  getRating: () => getRating,
133
140
  getRatingDisplay: () => getRatingDisplay,
141
+ getRatingEmoji: () => getRatingEmoji,
142
+ getRatingLabel: () => getRatingLabel,
134
143
  getRatingSlug: () => getRatingSlug,
135
144
  getRatingWithContext: () => getRatingWithContext,
136
145
  getRecommendedThreshold: () => getRecommendedThreshold,
@@ -143,6 +152,7 @@ __export(index_exports, {
143
152
  getSeverityLevel: () => getSeverityLevel,
144
153
  getSeverityValue: () => getSeverityValue,
145
154
  getSupportedLanguages: () => getSupportedLanguages,
155
+ getToolEmoji: () => getToolEmoji,
146
156
  getToolWeight: () => getToolWeight,
147
157
  getWasmPath: () => getWasmPath,
148
158
  groupIssuesByFile: () => groupIssuesByFile,
@@ -172,7 +182,8 @@ __export(index_exports, {
172
182
  setupParser: () => setupParser,
173
183
  severityToAnnotationLevel: () => severityToAnnotationLevel,
174
184
  validateSpokeOutput: () => validateSpokeOutput,
175
- validateWithSchema: () => validateWithSchema
185
+ validateWithSchema: () => validateWithSchema,
186
+ wrapInCard: () => wrapInCard
176
187
  });
177
188
  module.exports = __toCommonJS(index_exports);
178
189
 
@@ -3568,13 +3579,84 @@ function generateHTML(graph) {
3568
3579
  </html>`;
3569
3580
  }
3570
3581
 
3571
- // src/scoring.ts
3572
- var RecommendationPriority = /* @__PURE__ */ ((RecommendationPriority2) => {
3573
- RecommendationPriority2["High"] = "high";
3574
- RecommendationPriority2["Medium"] = "medium";
3575
- RecommendationPriority2["Low"] = "low";
3576
- return RecommendationPriority2;
3577
- })(RecommendationPriority || {});
3582
+ // src/utils/html-report-builder.ts
3583
+ function generateReportHead(title) {
3584
+ return `<!DOCTYPE html>
3585
+ <html lang="en">
3586
+ <head>
3587
+ <meta charset="UTF-8">
3588
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
3589
+ <title>${title}</title>
3590
+ <style>
3591
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; line-height: 1.6; color: #333; max-width: 1200px; margin: 0 auto; padding: 2rem; background-color: #f9f9f9; }
3592
+ h1, h2, h3 { color: #1a1a1a; border-bottom: 2px solid #eaeaea; padding-bottom: 0.5rem; }
3593
+ .card { background: white; padding: 1.5rem; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); margin-bottom: 2rem; border: 1px solid #eaeaea; }
3594
+ .stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 2rem; }
3595
+ .stat-card { background: #fff; padding: 1rem; border-radius: 6px; text-align: center; border: 1px solid #eaeaea; }
3596
+ .stat-value { font-size: 1.8rem; font-weight: bold; color: #2563eb; }
3597
+ .stat-label { font-size: 0.875rem; color: #666; text-transform: uppercase; }
3598
+ table { width: 100%; border-collapse: collapse; margin-top: 1rem; background: white; border-radius: 4px; overflow: hidden; }
3599
+ th, td { text-align: left; padding: 0.875rem 1rem; border-bottom: 1px solid #eaeaea; }
3600
+ th { background-color: #f8fafc; font-weight: 600; color: #475569; }
3601
+ tr:last-child td { border-bottom: none; }
3602
+ .critical { color: #dc2626; font-weight: bold; }
3603
+ .major { color: #ea580c; font-weight: bold; }
3604
+ .minor { color: #2563eb; }
3605
+ code { background: #f1f5f9; padding: 0.2rem 0.4rem; border-radius: 4px; font-size: 0.875rem; color: #334155; }
3606
+ .footer { margin-top: 4rem; text-align: center; color: #94a3b8; font-size: 0.875rem; }
3607
+ </style>
3608
+ </head>`;
3609
+ }
3610
+ function generateStatCards(cards) {
3611
+ const cardsHtml = cards.map(
3612
+ (card) => `
3613
+ <div class="stat-card">
3614
+ <div class="stat-value"${card.color ? ` style="color: ${card.color}"` : ""}>${card.value}</div>
3615
+ <div class="stat-label">${card.label}</div>
3616
+ </div>`
3617
+ ).join("");
3618
+ return `<div class="stats">${cardsHtml}</div>`;
3619
+ }
3620
+ function generateTable(config) {
3621
+ const headersHtml = config.headers.map((h) => `<th>${h}</th>`).join("");
3622
+ const rowsHtml = config.rows.map((row) => `<tr>${row.map((cell) => `<td>${cell}</td>`).join("")}</tr>`).join("");
3623
+ return `<table>
3624
+ <thead><tr>${headersHtml}</tr></thead>
3625
+ <tbody>${rowsHtml}</tbody>
3626
+ </table>`;
3627
+ }
3628
+ function generateReportFooter(options) {
3629
+ const versionText = options.version ? ` v${options.version}` : "";
3630
+ const links = [];
3631
+ if (options.packageUrl) {
3632
+ links.push(`<a href="${options.packageUrl}">Star us on GitHub</a>`);
3633
+ }
3634
+ if (options.bugUrl) {
3635
+ links.push(`<a href="${options.bugUrl}">Report it here</a>`);
3636
+ }
3637
+ const linksHtml = links.length ? links.map((l) => `<p>Like AIReady? ${l}</p>`).join("\n ") : "";
3638
+ return `<div class="footer">
3639
+ <p>Generated by <strong>@aiready/${options.packageName}</strong>${versionText}</p>
3640
+ ${linksHtml}
3641
+ </div>`;
3642
+ }
3643
+ function wrapInCard(content, title) {
3644
+ const titleHtml = title ? `<h2>${title}</h2>` : "";
3645
+ return `<div class="card">
3646
+ ${titleHtml}
3647
+ ${content}
3648
+ </div>`;
3649
+ }
3650
+ function generateCompleteReport(options, bodyContent) {
3651
+ return `${generateReportHead(options.title)}
3652
+ <body>
3653
+ ${bodyContent}
3654
+ ${generateReportFooter(options)}
3655
+ </body>
3656
+ </html>`;
3657
+ }
3658
+
3659
+ // src/utils/rating-helpers.ts
3578
3660
  var ReadinessRating = /* @__PURE__ */ ((ReadinessRating2) => {
3579
3661
  ReadinessRating2["Excellent"] = "Excellent";
3580
3662
  ReadinessRating2["Good"] = "Good";
@@ -3583,6 +3665,59 @@ var ReadinessRating = /* @__PURE__ */ ((ReadinessRating2) => {
3583
3665
  ReadinessRating2["Critical"] = "Critical";
3584
3666
  return ReadinessRating2;
3585
3667
  })(ReadinessRating || {});
3668
+ function getRatingLabel(score) {
3669
+ if (score >= 90) return "Excellent";
3670
+ if (score >= 75) return "Good";
3671
+ if (score >= 60) return "Fair";
3672
+ if (score >= 40) return "Needs Work";
3673
+ return "Critical";
3674
+ }
3675
+ function getRatingSlug(score) {
3676
+ if (score >= 90) return "excellent";
3677
+ if (score >= 75) return "good";
3678
+ if (score >= 60) return "fair";
3679
+ if (score >= 40) return "needs-work";
3680
+ return "critical";
3681
+ }
3682
+ function getRatingEmoji(score) {
3683
+ if (score >= 90) return "\u2705";
3684
+ if (score >= 75) return "\u{1F44D}";
3685
+ if (score >= 60) return "\u{1F44C}";
3686
+ if (score >= 40) return "\u{1F528}";
3687
+ return "\u{1F6A8}";
3688
+ }
3689
+ function getToolEmoji(score) {
3690
+ return getRatingEmoji(score);
3691
+ }
3692
+ function getPriorityIcon(priority) {
3693
+ switch (priority) {
3694
+ case "critical":
3695
+ return "\u{1F534}";
3696
+ case "high":
3697
+ return "\u{1F7E0}";
3698
+ case "medium":
3699
+ return "\u{1F7E1}";
3700
+ case "low":
3701
+ return "\u{1F535}";
3702
+ default:
3703
+ return "\u26AA";
3704
+ }
3705
+ }
3706
+ function getRating(score) {
3707
+ if (score >= 90) return "Excellent" /* Excellent */;
3708
+ if (score >= 75) return "Good" /* Good */;
3709
+ if (score >= 60) return "Fair" /* Fair */;
3710
+ if (score >= 40) return "Needs Work" /* NeedsWork */;
3711
+ return "Critical" /* Critical */;
3712
+ }
3713
+
3714
+ // src/scoring.ts
3715
+ var RecommendationPriority = /* @__PURE__ */ ((RecommendationPriority2) => {
3716
+ RecommendationPriority2["High"] = "high";
3717
+ RecommendationPriority2["Medium"] = "medium";
3718
+ RecommendationPriority2["Low"] = "low";
3719
+ return RecommendationPriority2;
3720
+ })(RecommendationPriority || {});
3586
3721
  var DEFAULT_TOOL_WEIGHTS = {
3587
3722
  ["pattern-detect" /* PatternDetect */]: 22,
3588
3723
  ["context-analyzer" /* ContextAnalyzer */]: 19,
@@ -3761,20 +3896,6 @@ function calculateOverallScore(toolOutputs, config, cliWeights) {
3761
3896
  }
3762
3897
  };
3763
3898
  }
3764
- function getRating(score) {
3765
- if (score >= 90) return "Excellent" /* Excellent */;
3766
- if (score >= 75) return "Good" /* Good */;
3767
- if (score >= 60) return "Fair" /* Fair */;
3768
- if (score >= 40) return "Needs Work" /* NeedsWork */;
3769
- return "Critical" /* Critical */;
3770
- }
3771
- function getRatingSlug(score) {
3772
- if (score >= 90) return "excellent";
3773
- if (score >= 75) return "good";
3774
- if (score >= 60) return "fair";
3775
- if (score >= 40) return "needs-work";
3776
- return "critical";
3777
- }
3778
3899
  function getRatingWithContext(score, fileCount, modelTier = "standard") {
3779
3900
  const threshold = getRecommendedThreshold(fileCount, modelTier);
3780
3901
  const normalized = score - threshold + 70;
@@ -3897,20 +4018,52 @@ var DEFAULT_COST_CONFIG = {
3897
4018
  developerCount: 5,
3898
4019
  daysPerMonth: 30
3899
4020
  };
3900
- function calculateMonthlyCost(tokenWaste, config = {}) {
3901
- const multiplier = tokenWaste > 5e4 ? 5 : tokenWaste > 1e4 ? 3.5 : 2.5;
4021
+ function calculateMonthlyCost(tokenWaste, config = {}, options) {
4022
+ const baseMultiplier = tokenWaste > 5e4 ? 5 : tokenWaste > 1e4 ? 3.5 : 2.5;
4023
+ const contextMultiplier = options?.avgContextBudget ? options.avgContextBudget / Math.max(1, tokenWaste) : baseMultiplier;
4024
+ const fragRatio = options?.fragmentationScore ?? 0.3;
4025
+ const dupRatio = 1 - fragRatio;
3902
4026
  const budget = calculateTokenBudget({
3903
- totalContextTokens: tokenWaste * multiplier,
4027
+ totalContextTokens: tokenWaste * contextMultiplier,
3904
4028
  wastedTokens: {
3905
- duplication: tokenWaste * 0.7,
3906
- fragmentation: tokenWaste * 0.3,
4029
+ duplication: tokenWaste * dupRatio * (options?.potentialSavings ? 1.2 : 1),
4030
+ fragmentation: tokenWaste * fragRatio,
3907
4031
  chattiness: 0.1 * tokenWaste
3908
- // Added baseline chattiness
3909
4032
  }
3910
4033
  });
3911
- const preset = getModelPreset("gpt-5.4-mini");
4034
+ const preset = getModelPreset("claude-3.5-sonnet");
3912
4035
  return estimateCostFromBudget(budget, preset, config);
3913
4036
  }
4037
+ function calculateDetailedTokenROI(params) {
4038
+ const {
4039
+ totalTokens,
4040
+ avgContextBudget,
4041
+ potentialSavings,
4042
+ fragmentationScore,
4043
+ developerCount,
4044
+ queriesPerDevPerDay = 60
4045
+ } = params;
4046
+ const budget = calculateTokenBudget({
4047
+ totalContextTokens: avgContextBudget,
4048
+ wastedTokens: {
4049
+ duplication: potentialSavings * 0.8,
4050
+ // 80% of potential savings are duplication-based
4051
+ fragmentation: totalTokens * fragmentationScore * 0.5,
4052
+ // fragmentation impact
4053
+ chattiness: totalTokens * 0.1
4054
+ }
4055
+ });
4056
+ const model = getModelPreset("claude-3.5-sonnet");
4057
+ const cost = estimateCostFromBudget(budget, model, {
4058
+ developerCount,
4059
+ queriesPerDevPerDay
4060
+ });
4061
+ return {
4062
+ monthlySavings: Math.round(cost.total),
4063
+ contextTaxPerDev: Math.round(cost.total / (developerCount || 1) * 100) / 100,
4064
+ efficiencyGain: Math.round(budget.efficiencyRatio * 100) / 100
4065
+ };
4066
+ }
3914
4067
  function calculateTokenBudget(params) {
3915
4068
  const { totalContextTokens, wastedTokens } = params;
3916
4069
  const estimatedResponseTokens = params.estimatedResponseTokens ?? totalContextTokens * 0.2;
@@ -5438,6 +5591,7 @@ function emitIssuesAsAnnotations(issues) {
5438
5591
  calculateConceptCohesion,
5439
5592
  calculateDebtInterest,
5440
5593
  calculateDependencyHealth,
5594
+ calculateDetailedTokenROI,
5441
5595
  calculateDocDrift,
5442
5596
  calculateExtendedFutureProofScore,
5443
5597
  calculateFutureProofScore,
@@ -5466,7 +5620,12 @@ function emitIssuesAsAnnotations(issues) {
5466
5620
  formatHours,
5467
5621
  formatScore,
5468
5622
  formatToolScore,
5623
+ generateCompleteReport,
5469
5624
  generateHTML,
5625
+ generateReportFooter,
5626
+ generateReportHead,
5627
+ generateStatCards,
5628
+ generateTable,
5470
5629
  generateValueChain,
5471
5630
  getElapsedTime,
5472
5631
  getFileCommitTimestamps,
@@ -5475,9 +5634,12 @@ function emitIssuesAsAnnotations(issues) {
5475
5634
  getLineRangeLastModifiedCached,
5476
5635
  getModelPreset,
5477
5636
  getParser,
5637
+ getPriorityIcon,
5478
5638
  getProjectSizeTier,
5479
5639
  getRating,
5480
5640
  getRatingDisplay,
5641
+ getRatingEmoji,
5642
+ getRatingLabel,
5481
5643
  getRatingSlug,
5482
5644
  getRatingWithContext,
5483
5645
  getRecommendedThreshold,
@@ -5490,6 +5652,7 @@ function emitIssuesAsAnnotations(issues) {
5490
5652
  getSeverityLevel,
5491
5653
  getSeverityValue,
5492
5654
  getSupportedLanguages,
5655
+ getToolEmoji,
5493
5656
  getToolWeight,
5494
5657
  getWasmPath,
5495
5658
  groupIssuesByFile,
@@ -5519,5 +5682,6 @@ function emitIssuesAsAnnotations(issues) {
5519
5682
  setupParser,
5520
5683
  severityToAnnotationLevel,
5521
5684
  validateSpokeOutput,
5522
- validateWithSchema
5685
+ validateWithSchema,
5686
+ wrapInCard
5523
5687
  });
package/dist/index.mjs CHANGED
@@ -41,16 +41,20 @@ import {
41
41
  formatScore,
42
42
  formatToolScore,
43
43
  generateHTML,
44
+ getPriorityIcon,
44
45
  getProjectSizeTier,
45
46
  getRating,
46
47
  getRatingDisplay,
48
+ getRatingEmoji,
49
+ getRatingLabel,
47
50
  getRatingSlug,
48
51
  getRatingWithContext,
49
52
  getRecommendedThreshold,
53
+ getToolEmoji,
50
54
  getToolWeight,
51
55
  normalizeToolName,
52
56
  parseWeightString
53
- } from "./chunk-RMH2TPAT.mjs";
57
+ } from "./chunk-SQHS6PFL.mjs";
54
58
 
55
59
  // src/utils/normalization.ts
56
60
  function normalizeIssue(raw) {
@@ -2947,6 +2951,83 @@ function mergeConfigWithDefaults(userConfig, defaults) {
2947
2951
  return mergedConfig;
2948
2952
  }
2949
2953
 
2954
+ // src/utils/html-report-builder.ts
2955
+ function generateReportHead(title) {
2956
+ return `<!DOCTYPE html>
2957
+ <html lang="en">
2958
+ <head>
2959
+ <meta charset="UTF-8">
2960
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
2961
+ <title>${title}</title>
2962
+ <style>
2963
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; line-height: 1.6; color: #333; max-width: 1200px; margin: 0 auto; padding: 2rem; background-color: #f9f9f9; }
2964
+ h1, h2, h3 { color: #1a1a1a; border-bottom: 2px solid #eaeaea; padding-bottom: 0.5rem; }
2965
+ .card { background: white; padding: 1.5rem; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); margin-bottom: 2rem; border: 1px solid #eaeaea; }
2966
+ .stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 2rem; }
2967
+ .stat-card { background: #fff; padding: 1rem; border-radius: 6px; text-align: center; border: 1px solid #eaeaea; }
2968
+ .stat-value { font-size: 1.8rem; font-weight: bold; color: #2563eb; }
2969
+ .stat-label { font-size: 0.875rem; color: #666; text-transform: uppercase; }
2970
+ table { width: 100%; border-collapse: collapse; margin-top: 1rem; background: white; border-radius: 4px; overflow: hidden; }
2971
+ th, td { text-align: left; padding: 0.875rem 1rem; border-bottom: 1px solid #eaeaea; }
2972
+ th { background-color: #f8fafc; font-weight: 600; color: #475569; }
2973
+ tr:last-child td { border-bottom: none; }
2974
+ .critical { color: #dc2626; font-weight: bold; }
2975
+ .major { color: #ea580c; font-weight: bold; }
2976
+ .minor { color: #2563eb; }
2977
+ code { background: #f1f5f9; padding: 0.2rem 0.4rem; border-radius: 4px; font-size: 0.875rem; color: #334155; }
2978
+ .footer { margin-top: 4rem; text-align: center; color: #94a3b8; font-size: 0.875rem; }
2979
+ </style>
2980
+ </head>`;
2981
+ }
2982
+ function generateStatCards(cards) {
2983
+ const cardsHtml = cards.map(
2984
+ (card) => `
2985
+ <div class="stat-card">
2986
+ <div class="stat-value"${card.color ? ` style="color: ${card.color}"` : ""}>${card.value}</div>
2987
+ <div class="stat-label">${card.label}</div>
2988
+ </div>`
2989
+ ).join("");
2990
+ return `<div class="stats">${cardsHtml}</div>`;
2991
+ }
2992
+ function generateTable(config) {
2993
+ const headersHtml = config.headers.map((h) => `<th>${h}</th>`).join("");
2994
+ const rowsHtml = config.rows.map((row) => `<tr>${row.map((cell) => `<td>${cell}</td>`).join("")}</tr>`).join("");
2995
+ return `<table>
2996
+ <thead><tr>${headersHtml}</tr></thead>
2997
+ <tbody>${rowsHtml}</tbody>
2998
+ </table>`;
2999
+ }
3000
+ function generateReportFooter(options) {
3001
+ const versionText = options.version ? ` v${options.version}` : "";
3002
+ const links = [];
3003
+ if (options.packageUrl) {
3004
+ links.push(`<a href="${options.packageUrl}">Star us on GitHub</a>`);
3005
+ }
3006
+ if (options.bugUrl) {
3007
+ links.push(`<a href="${options.bugUrl}">Report it here</a>`);
3008
+ }
3009
+ const linksHtml = links.length ? links.map((l) => `<p>Like AIReady? ${l}</p>`).join("\n ") : "";
3010
+ return `<div class="footer">
3011
+ <p>Generated by <strong>@aiready/${options.packageName}</strong>${versionText}</p>
3012
+ ${linksHtml}
3013
+ </div>`;
3014
+ }
3015
+ function wrapInCard(content, title) {
3016
+ const titleHtml = title ? `<h2>${title}</h2>` : "";
3017
+ return `<div class="card">
3018
+ ${titleHtml}
3019
+ ${content}
3020
+ </div>`;
3021
+ }
3022
+ function generateCompleteReport(options, bodyContent) {
3023
+ return `${generateReportHead(options.title)}
3024
+ <body>
3025
+ ${bodyContent}
3026
+ ${generateReportFooter(options)}
3027
+ </body>
3028
+ </html>`;
3029
+ }
3030
+
2950
3031
  // src/business/pricing-models.ts
2951
3032
  var MODEL_PRICING_PRESETS = {
2952
3033
  "gpt-5.4-mini": {
@@ -3010,20 +3091,52 @@ var DEFAULT_COST_CONFIG = {
3010
3091
  developerCount: 5,
3011
3092
  daysPerMonth: 30
3012
3093
  };
3013
- function calculateMonthlyCost(tokenWaste, config = {}) {
3014
- const multiplier = tokenWaste > 5e4 ? 5 : tokenWaste > 1e4 ? 3.5 : 2.5;
3094
+ function calculateMonthlyCost(tokenWaste, config = {}, options) {
3095
+ const baseMultiplier = tokenWaste > 5e4 ? 5 : tokenWaste > 1e4 ? 3.5 : 2.5;
3096
+ const contextMultiplier = options?.avgContextBudget ? options.avgContextBudget / Math.max(1, tokenWaste) : baseMultiplier;
3097
+ const fragRatio = options?.fragmentationScore ?? 0.3;
3098
+ const dupRatio = 1 - fragRatio;
3015
3099
  const budget = calculateTokenBudget({
3016
- totalContextTokens: tokenWaste * multiplier,
3100
+ totalContextTokens: tokenWaste * contextMultiplier,
3017
3101
  wastedTokens: {
3018
- duplication: tokenWaste * 0.7,
3019
- fragmentation: tokenWaste * 0.3,
3102
+ duplication: tokenWaste * dupRatio * (options?.potentialSavings ? 1.2 : 1),
3103
+ fragmentation: tokenWaste * fragRatio,
3020
3104
  chattiness: 0.1 * tokenWaste
3021
- // Added baseline chattiness
3022
3105
  }
3023
3106
  });
3024
- const preset = getModelPreset("gpt-5.4-mini");
3107
+ const preset = getModelPreset("claude-3.5-sonnet");
3025
3108
  return estimateCostFromBudget(budget, preset, config);
3026
3109
  }
3110
+ function calculateDetailedTokenROI(params) {
3111
+ const {
3112
+ totalTokens,
3113
+ avgContextBudget,
3114
+ potentialSavings,
3115
+ fragmentationScore,
3116
+ developerCount,
3117
+ queriesPerDevPerDay = 60
3118
+ } = params;
3119
+ const budget = calculateTokenBudget({
3120
+ totalContextTokens: avgContextBudget,
3121
+ wastedTokens: {
3122
+ duplication: potentialSavings * 0.8,
3123
+ // 80% of potential savings are duplication-based
3124
+ fragmentation: totalTokens * fragmentationScore * 0.5,
3125
+ // fragmentation impact
3126
+ chattiness: totalTokens * 0.1
3127
+ }
3128
+ });
3129
+ const model = getModelPreset("claude-3.5-sonnet");
3130
+ const cost = estimateCostFromBudget(budget, model, {
3131
+ developerCount,
3132
+ queriesPerDevPerDay
3133
+ });
3134
+ return {
3135
+ monthlySavings: Math.round(cost.total),
3136
+ contextTaxPerDev: Math.round(cost.total / (developerCount || 1) * 100) / 100,
3137
+ efficiencyGain: Math.round(budget.efficiencyRatio * 100) / 100
3138
+ };
3139
+ }
3027
3140
  function calculateTokenBudget(params) {
3028
3141
  const { totalContextTokens, wastedTokens } = params;
3029
3142
  const estimatedResponseTokens = params.estimatedResponseTokens ?? totalContextTokens * 0.2;
@@ -4550,6 +4663,7 @@ export {
4550
4663
  calculateConceptCohesion,
4551
4664
  calculateDebtInterest,
4552
4665
  calculateDependencyHealth,
4666
+ calculateDetailedTokenROI,
4553
4667
  calculateDocDrift,
4554
4668
  calculateExtendedFutureProofScore,
4555
4669
  calculateFutureProofScore,
@@ -4578,7 +4692,12 @@ export {
4578
4692
  formatHours,
4579
4693
  formatScore,
4580
4694
  formatToolScore,
4695
+ generateCompleteReport,
4581
4696
  generateHTML,
4697
+ generateReportFooter,
4698
+ generateReportHead,
4699
+ generateStatCards,
4700
+ generateTable,
4582
4701
  generateValueChain,
4583
4702
  getElapsedTime,
4584
4703
  getFileCommitTimestamps,
@@ -4587,9 +4706,12 @@ export {
4587
4706
  getLineRangeLastModifiedCached,
4588
4707
  getModelPreset,
4589
4708
  getParser,
4709
+ getPriorityIcon,
4590
4710
  getProjectSizeTier,
4591
4711
  getRating,
4592
4712
  getRatingDisplay,
4713
+ getRatingEmoji,
4714
+ getRatingLabel,
4593
4715
  getRatingSlug,
4594
4716
  getRatingWithContext,
4595
4717
  getRecommendedThreshold,
@@ -4602,6 +4724,7 @@ export {
4602
4724
  getSeverityLevel,
4603
4725
  getSeverityValue,
4604
4726
  getSupportedLanguages,
4727
+ getToolEmoji,
4605
4728
  getToolWeight,
4606
4729
  getWasmPath,
4607
4730
  groupIssuesByFile,
@@ -4631,5 +4754,6 @@ export {
4631
4754
  setupParser,
4632
4755
  severityToAnnotationLevel,
4633
4756
  validateSpokeOutput,
4634
- validateWithSchema
4757
+ validateWithSchema,
4758
+ wrapInCard
4635
4759
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/core",
3
- "version": "0.23.19",
3
+ "version": "0.23.20",
4
4
  "description": "Shared utilities for AIReady analysis tools",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",