cchubber 0.5.4 → 0.5.5
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/package.json +4 -2
- package/src/renderers/html-report.js +15 -10
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cchubber",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.5",
|
|
4
4
|
"description": "What you spent. Why you spent it. Is that normal. — Claude Code usage diagnosis with beautiful HTML reports.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"cchubber": "./src/cli/index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"start": "node src/cli/index.js"
|
|
10
|
+
"start": "node src/cli/index.js",
|
|
11
|
+
"test": "node test/smoke.js",
|
|
12
|
+
"release": "bash release.sh"
|
|
11
13
|
},
|
|
12
14
|
"keywords": [
|
|
13
15
|
"claude",
|
|
@@ -1064,6 +1064,7 @@ ${cacheHealth.totalCacheBreaks > 0 ? `
|
|
|
1064
1064
|
// Community leaderboard — stats fetched at generation time, embedded as JSON
|
|
1065
1065
|
var MY_RATIO = ${cacheHealth.efficiencyRatio || 0};
|
|
1066
1066
|
var MY_GRADE = '${cacheHealth.grade?.letter || '?'}';
|
|
1067
|
+
var MY_COST = '${(() => { const c = totalCost; return c<10?'<10':c<50?'10-50':c<200?'50-200':c<500?'200-500':c<1000?'500-1K':c<5000?'1K-5K':'5K+'; })()}';
|
|
1067
1068
|
var gradeColors = {A:'#10b981',B:'#22d3ee',C:'#f59e0b',D:'#f97316',F:'#ef4444'};
|
|
1068
1069
|
var stats = ${JSON.stringify(report.communityStats || null)};
|
|
1069
1070
|
|
|
@@ -1125,20 +1126,24 @@ ${cacheHealth.totalCacheBreaks > 0 ? `
|
|
|
1125
1126
|
});
|
|
1126
1127
|
|
|
1127
1128
|
// Leaderboard table — re-graded, with user's position injected
|
|
1129
|
+
// Filter: minimum activity to qualify (exclude <$10 and $10-50 users — too little data for meaningful grade)
|
|
1130
|
+
var validCosts = {'50-200':1,'200-500':1,'500-1K':1,'1K-5K':1,'5K+':1};
|
|
1128
1131
|
var tbody = document.getElementById('leaderboard-body');
|
|
1129
|
-
var sorted = recent.filter(function(r){return r.ratio}).sort(function(a,b){return (a.ratio||9999)-(b.ratio||9999)});
|
|
1132
|
+
var sorted = recent.filter(function(r){return r.ratio && (validCosts[r.cost] || r.cost===MY_COST)}).sort(function(a,b){return (a.ratio||9999)-(b.ratio||9999)});
|
|
1130
1133
|
|
|
1131
|
-
//
|
|
1134
|
+
// Insert user's own entry into the sorted list at the right position
|
|
1135
|
+
var myEntry = {ratio:MY_RATIO, grade:MY_GRADE, cost:MY_COST, opus:${report.modelRouting?.opusPct ?? 'null'}, country:'You', os:'${process.platform}', isMe:true};
|
|
1132
1136
|
var myRank = sorted.findIndex(function(e){return (e.ratio||0) >= MY_RATIO});
|
|
1133
1137
|
if(myRank < 0) myRank = sorted.length;
|
|
1138
|
+
sorted.splice(myRank, 0, myEntry);
|
|
1134
1139
|
|
|
1135
1140
|
// Show top 10 + user's position (if not in top 10)
|
|
1136
1141
|
var showIndices = [];
|
|
1137
1142
|
for(var si=0;si<Math.min(10,sorted.length);si++) showIndices.push(si);
|
|
1138
1143
|
var userInTop = myRank < 10;
|
|
1139
|
-
if(!userInTop
|
|
1144
|
+
if(!userInTop){
|
|
1140
1145
|
showIndices.push(-1); // separator
|
|
1141
|
-
if(myRank >
|
|
1146
|
+
if(myRank > 1) showIndices.push(myRank-1);
|
|
1142
1147
|
showIndices.push(myRank);
|
|
1143
1148
|
if(myRank+1 < sorted.length) showIndices.push(myRank+1);
|
|
1144
1149
|
}
|
|
@@ -1147,28 +1152,28 @@ ${cacheHealth.totalCacheBreaks > 0 ? `
|
|
|
1147
1152
|
for(var si2=0;si2<showIndices.length;si2++){
|
|
1148
1153
|
var idx = showIndices[si2];
|
|
1149
1154
|
if(idx === -1){
|
|
1150
|
-
html += '<tr><td colspan="6" class="px-8 py-1 text-center text-[10px] text-[#908fa0]"
|
|
1155
|
+
html += '<tr><td colspan="6" class="px-8 py-1 text-center text-[10px] text-[#908fa0]">...</td></tr>';
|
|
1151
1156
|
continue;
|
|
1152
1157
|
}
|
|
1153
1158
|
var entry = sorted[idx];
|
|
1154
1159
|
if(!entry) continue;
|
|
1155
|
-
var g = getGrade(entry);
|
|
1156
|
-
var isMe =
|
|
1160
|
+
var g = entry.isMe ? MY_GRADE : getGrade(entry);
|
|
1161
|
+
var isMe = entry.isMe;
|
|
1157
1162
|
var rowStyle = isMe ? 'background:rgba(192,193,255,0.08);border-left:3px solid #c0c1ff;' : '';
|
|
1158
1163
|
html += '<tr style="border-bottom:1px solid rgba(70,69,84,0.1);'+rowStyle+'">';
|
|
1159
1164
|
html += '<td class="px-8 py-3 text-sm font-mono '+(isMe?'text-[#c0c1ff] font-bold':'text-[#908fa0]')+'">#'+(idx+1)+(isMe?' ← you':'')+'</td>';
|
|
1160
1165
|
html += '<td class="px-4 py-3 text-sm font-bold text-center" style="color:'+gradeColors[g]+'">'+g+'</td>';
|
|
1161
1166
|
html += '<td class="px-4 py-3 text-sm font-mono text-[#c7c4d7] text-right">'+(entry.ratio||'?')+':1</td>';
|
|
1162
1167
|
html += '<td class="px-4 py-3 text-sm font-mono text-[#908fa0]">'+(entry.cost||'?')+'</td>';
|
|
1163
|
-
html += '<td class="px-4 py-3 text-sm font-mono text-[#c7c4d7] text-right">'+(entry.opus
|
|
1164
|
-
html += '<td class="px-8 py-3 text-sm text-[#908fa0]">'+(entry.country||'?')+'</td>';
|
|
1168
|
+
html += '<td class="px-4 py-3 text-sm font-mono text-[#c7c4d7] text-right">'+(entry.opus!=null?entry.opus:'?')+'%</td>';
|
|
1169
|
+
html += '<td class="px-8 py-3 text-sm text-[#908fa0]">'+(isMe?'You':(entry.country||'?'))+'</td>';
|
|
1165
1170
|
html += '</tr>';
|
|
1166
1171
|
}
|
|
1167
1172
|
tbody.innerHTML = html;
|
|
1168
1173
|
|
|
1169
1174
|
// Update percentile text with rank
|
|
1170
1175
|
document.getElementById('community-percentile').innerHTML =
|
|
1171
|
-
"You are <strong style=\\"color:#c0c1ff\\">#"+(myRank+1)+" of "+sorted.length+"</strong> users by cache efficiency. Better than <strong style=\\"color:#c0c1ff\\">"+pctile+"%</strong>";
|
|
1176
|
+
"You are <strong style=\\"color:#c0c1ff\\">#"+(myRank+1)+" of "+(sorted.length)+"</strong> users by cache efficiency. Better than <strong style=\\"color:#c0c1ff\\">"+pctile+"%</strong>";
|
|
1172
1177
|
})(stats);
|
|
1173
1178
|
})();
|
|
1174
1179
|
</script>
|