ccclub 0.3.4 → 0.3.6
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 +116 -39
- package/package.json +8 -2
package/dist/index.js
CHANGED
|
@@ -1732,68 +1732,78 @@ function printGroup(data, code, period, config, showCache = false, showAll = fal
|
|
|
1732
1732
|
const hasAgents = activeRankings.some(
|
|
1733
1733
|
(r) => r.agents && r.agents.length > 0 && !(r.agents.length === 1 && r.agents[0] === "claude")
|
|
1734
1734
|
);
|
|
1735
|
+
const plainRows = activeRankings.map((entry) => {
|
|
1736
|
+
const isActive = isEntryActive(entry, now);
|
|
1737
|
+
const tokens = showCache ? entry.totalTokens : entry.inputTokens + entry.outputTokens + (entry.reasoningTokens || 0);
|
|
1738
|
+
const roi = formatRoi(entry, hasPlan);
|
|
1739
|
+
return {
|
|
1740
|
+
entry,
|
|
1741
|
+
isActive,
|
|
1742
|
+
rank: `${entry.userId === config.userId ? "\u2192" : " "}${entry.rank}`,
|
|
1743
|
+
name: `${isActive ? "\u25CF " : ""}${entry.displayName}`,
|
|
1744
|
+
agents: formatAgents(entry),
|
|
1745
|
+
cost: `$${entry.costUSD.toFixed(2)}`,
|
|
1746
|
+
tokens: formatTokens(tokens),
|
|
1747
|
+
roi,
|
|
1748
|
+
turns: String(entry.chatCount),
|
|
1749
|
+
perTurn: entry.chatCount > 0 ? `$${(entry.costUSD / entry.chatCount).toFixed(2)}` : "\u2014",
|
|
1750
|
+
usage: entry.usageSnapshot ? `${Math.round(entry.usageSnapshot.sevenDay)}%` : "\u2014"
|
|
1751
|
+
};
|
|
1752
|
+
});
|
|
1735
1753
|
const head = ["#", "Name", "Cost", "Tokens"];
|
|
1736
|
-
const
|
|
1737
|
-
|
|
1754
|
+
const widths = [
|
|
1755
|
+
columnWidth("#", plainRows.map((r) => r.rank), 2, 3),
|
|
1756
|
+
columnWidth("Name", plainRows.map((r) => r.name), 10, 18),
|
|
1757
|
+
columnWidth("Cost", plainRows.map((r) => r.cost), 5, 9),
|
|
1758
|
+
columnWidth("Tokens", plainRows.map((r) => r.tokens), 6, 8)
|
|
1759
|
+
];
|
|
1738
1760
|
if (hasAgents) {
|
|
1739
1761
|
head.splice(2, 0, "Agents");
|
|
1740
|
-
widths.splice(2, 0,
|
|
1762
|
+
widths.splice(2, 0, columnWidth("Agents", plainRows.map((r) => r.agents), 6, 24));
|
|
1741
1763
|
}
|
|
1742
1764
|
if (hasPlan) {
|
|
1743
|
-
head.push("
|
|
1744
|
-
widths.push(
|
|
1765
|
+
head.push("ROI");
|
|
1766
|
+
widths.push(columnWidth("ROI", plainRows.map((r) => r.roi), 3, 11));
|
|
1745
1767
|
}
|
|
1746
1768
|
head.push("Turns", "$/Turn");
|
|
1747
|
-
widths.push(
|
|
1769
|
+
widths.push(
|
|
1770
|
+
columnWidth("Turns", plainRows.map((r) => r.turns), 3, 6),
|
|
1771
|
+
columnWidth("$/Turn", plainRows.map((r) => r.perTurn), 6, 7)
|
|
1772
|
+
);
|
|
1748
1773
|
if (hasUsage) {
|
|
1749
|
-
head.push("Usage
|
|
1750
|
-
widths.push(
|
|
1774
|
+
head.push("Usage");
|
|
1775
|
+
widths.push(columnWidth("Usage", plainRows.map((r) => r.usage), 5, 6));
|
|
1751
1776
|
}
|
|
1752
1777
|
const table = new Table({
|
|
1753
1778
|
head: head.map((h) => chalk6.cyan(h)),
|
|
1754
1779
|
style: { head: [], border: [] },
|
|
1755
1780
|
colWidths: widths
|
|
1756
1781
|
});
|
|
1757
|
-
for (const
|
|
1782
|
+
for (const plain of plainRows) {
|
|
1783
|
+
const { entry } = plain;
|
|
1758
1784
|
const isMe = entry.userId === config.userId;
|
|
1759
|
-
const tokens = showCache ? entry.totalTokens : entry.inputTokens + entry.outputTokens + (entry.reasoningTokens || 0);
|
|
1760
1785
|
const marker = isMe ? chalk6.green("\u2192") : " ";
|
|
1761
|
-
const isActive = entry.lastSync && now - new Date(entry.lastSync).getTime() < ACTIVE_THRESHOLD_MS;
|
|
1762
1786
|
const id = (s) => s;
|
|
1763
1787
|
const c = isMe ? chalk6.green : entry.rank === 1 ? chalk6.yellow : id;
|
|
1764
1788
|
const nameC = isMe ? chalk6.green.bold : entry.rank === 1 ? chalk6.yellow.bold : id;
|
|
1765
|
-
const
|
|
1789
|
+
const nameWidth = Math.max(widths[1] - 2, 4);
|
|
1790
|
+
const displayName = plain.isActive ? `${chalk6.green("\u25CF")} ${nameC(truncateDisplay(entry.displayName, Math.max(nameWidth - 2, 1)))}` : nameC(truncateDisplay(entry.displayName, nameWidth));
|
|
1766
1791
|
const row = [
|
|
1767
1792
|
`${marker}${c(String(entry.rank))}`,
|
|
1768
|
-
|
|
1793
|
+
displayName
|
|
1769
1794
|
];
|
|
1770
1795
|
if (hasAgents) {
|
|
1771
|
-
|
|
1796
|
+
const agentWidth = Math.max(widths[2] - 2, 4);
|
|
1797
|
+
row.push(c(truncateDisplay(plain.agents, agentWidth)));
|
|
1772
1798
|
}
|
|
1773
|
-
row.push(c(
|
|
1799
|
+
row.push(c(plain.cost), c(plain.tokens));
|
|
1774
1800
|
if (hasPlan) {
|
|
1775
|
-
|
|
1776
|
-
const price = PLAN_PRICES[entry.plan];
|
|
1777
|
-
const monthly = entry.monthlyCostUSD || 0;
|
|
1778
|
-
const roi = price > 0 ? Math.round(monthly / price * 100) : 0;
|
|
1779
|
-
const roiStr = `$${price}/${roi}%`;
|
|
1780
|
-
const roiC = roi >= 100 ? chalk6.green.bold(roiStr) : roi >= 50 ? chalk6.yellow(roiStr) : chalk6.dim(roiStr);
|
|
1781
|
-
row.push(roiC);
|
|
1782
|
-
} else if (entry.plan === "api") {
|
|
1783
|
-
row.push(chalk6.dim("API"));
|
|
1784
|
-
} else {
|
|
1785
|
-
row.push(chalk6.dim("\u2014"));
|
|
1786
|
-
}
|
|
1801
|
+
row.push(colorRoi(plain.roi, entry));
|
|
1787
1802
|
}
|
|
1788
|
-
row.push(c(
|
|
1789
|
-
row.push(entry.chatCount > 0 ? c(
|
|
1803
|
+
row.push(c(plain.turns));
|
|
1804
|
+
row.push(entry.chatCount > 0 ? c(plain.perTurn) : chalk6.dim("\u2014"));
|
|
1790
1805
|
if (hasUsage) {
|
|
1791
|
-
|
|
1792
|
-
const { sevenDay } = entry.usageSnapshot;
|
|
1793
|
-
row.push(c(`${Math.round(sevenDay)}%`));
|
|
1794
|
-
} else {
|
|
1795
|
-
row.push(chalk6.dim("\u2014"));
|
|
1796
|
-
}
|
|
1806
|
+
row.push(entry.usageSnapshot ? c(plain.usage) : chalk6.dim("\u2014"));
|
|
1797
1807
|
}
|
|
1798
1808
|
table.push(row);
|
|
1799
1809
|
}
|
|
@@ -1812,9 +1822,76 @@ function printGroup(data, code, period, config, showCache = false, showAll = fal
|
|
|
1812
1822
|
}
|
|
1813
1823
|
}
|
|
1814
1824
|
}
|
|
1815
|
-
function
|
|
1816
|
-
|
|
1817
|
-
|
|
1825
|
+
function isEntryActive(entry, now) {
|
|
1826
|
+
return Boolean(entry.lastSync && now - new Date(entry.lastSync).getTime() < ACTIVE_THRESHOLD_MS);
|
|
1827
|
+
}
|
|
1828
|
+
function formatRoi(entry, hasPlan) {
|
|
1829
|
+
if (!hasPlan) return "";
|
|
1830
|
+
if (entry.plan && entry.plan !== "api") {
|
|
1831
|
+
const price = PLAN_PRICES[entry.plan];
|
|
1832
|
+
const monthly = entry.monthlyCostUSD || 0;
|
|
1833
|
+
const roi = price > 0 ? Math.round(monthly / price * 100) : 0;
|
|
1834
|
+
return `$${price}/${roi}%`;
|
|
1835
|
+
}
|
|
1836
|
+
if (entry.plan === "api") return "API";
|
|
1837
|
+
return "\u2014";
|
|
1838
|
+
}
|
|
1839
|
+
function colorRoi(roiStr, entry) {
|
|
1840
|
+
if (entry.plan && entry.plan !== "api") {
|
|
1841
|
+
const price = PLAN_PRICES[entry.plan];
|
|
1842
|
+
const monthly = entry.monthlyCostUSD || 0;
|
|
1843
|
+
const roi = price > 0 ? Math.round(monthly / price * 100) : 0;
|
|
1844
|
+
return roi >= 100 ? chalk6.green.bold(roiStr) : roi >= 50 ? chalk6.yellow(roiStr) : chalk6.dim(roiStr);
|
|
1845
|
+
}
|
|
1846
|
+
return chalk6.dim(roiStr);
|
|
1847
|
+
}
|
|
1848
|
+
function formatAgents(entry) {
|
|
1849
|
+
if (entry.agentBreakdown && entry.agentBreakdown.length > 0) {
|
|
1850
|
+
if (entry.agentBreakdown.length === 1) {
|
|
1851
|
+
return formatAgentLabel(entry.agentBreakdown[0].source);
|
|
1852
|
+
}
|
|
1853
|
+
const visible = entry.agentBreakdown.slice(0, 2).map((agent) => `${formatAgentLabel(agent.source)} ${agent.percent}%`);
|
|
1854
|
+
if (entry.agentBreakdown.length > visible.length) {
|
|
1855
|
+
visible.push(`+${entry.agentBreakdown.length - visible.length}`);
|
|
1856
|
+
}
|
|
1857
|
+
return visible.join(", ");
|
|
1858
|
+
}
|
|
1859
|
+
if (!entry.agents || entry.agents.length === 0) return "\u2014";
|
|
1860
|
+
return entry.agents.map(formatAgentLabel).join(", ");
|
|
1861
|
+
}
|
|
1862
|
+
function formatAgentLabel(agent) {
|
|
1863
|
+
return AGENT_LABELS[agent] ?? agent;
|
|
1864
|
+
}
|
|
1865
|
+
function columnWidth(header, values, minContent, maxContent) {
|
|
1866
|
+
const contentWidth = Math.max(visualWidth(header), ...values.map(visualWidth));
|
|
1867
|
+
return Math.min(Math.max(contentWidth, minContent), maxContent) + 2;
|
|
1868
|
+
}
|
|
1869
|
+
function visualWidth(value) {
|
|
1870
|
+
let width = 0;
|
|
1871
|
+
for (const char of value) width += charWidth(char);
|
|
1872
|
+
return width;
|
|
1873
|
+
}
|
|
1874
|
+
function charWidth(char) {
|
|
1875
|
+
const code = char.codePointAt(0) ?? 0;
|
|
1876
|
+
if (code === 0 || code < 32 || code >= 127 && code < 160) return 0;
|
|
1877
|
+
if (code >= 4352 && (code <= 4447 || code === 9001 || code === 9002 || code >= 11904 && code <= 42191 && code !== 12351 || code >= 44032 && code <= 55203 || code >= 63744 && code <= 64255 || code >= 65040 && code <= 65049 || code >= 65072 && code <= 65135 || code >= 65280 && code <= 65376 || code >= 65504 && code <= 65510)) {
|
|
1878
|
+
return 2;
|
|
1879
|
+
}
|
|
1880
|
+
return 1;
|
|
1881
|
+
}
|
|
1882
|
+
function truncateDisplay(value, maxWidth) {
|
|
1883
|
+
if (visualWidth(value) <= maxWidth) return value;
|
|
1884
|
+
if (maxWidth <= 1) return "\u2026";
|
|
1885
|
+
let result = "";
|
|
1886
|
+
let width = 0;
|
|
1887
|
+
const limit = maxWidth - 1;
|
|
1888
|
+
for (const char of value) {
|
|
1889
|
+
const next = charWidth(char);
|
|
1890
|
+
if (width + next > limit) break;
|
|
1891
|
+
result += char;
|
|
1892
|
+
width += next;
|
|
1893
|
+
}
|
|
1894
|
+
return `${result}\u2026`;
|
|
1818
1895
|
}
|
|
1819
1896
|
var SPARK_CHARS = "\u2581\u2582\u2583\u2584\u2585\u2586\u2587";
|
|
1820
1897
|
function renderActivity(data, range) {
|
|
@@ -2160,7 +2237,7 @@ async function hookCommand() {
|
|
|
2160
2237
|
}
|
|
2161
2238
|
|
|
2162
2239
|
// src/index.ts
|
|
2163
|
-
var VERSION = "0.3.
|
|
2240
|
+
var VERSION = "0.3.6";
|
|
2164
2241
|
startUpdateCheck(VERSION);
|
|
2165
2242
|
var program = new Command();
|
|
2166
2243
|
program.name("ccclub").description("Coding agent usage leaderboard among friends").version(VERSION, "-v, -V, --version");
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccclub",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Claude Code and Codex leaderboard among friends for coding agent tokens and costs",
|
|
6
6
|
"bin": {
|
|
7
7
|
"ccclub": "dist/index.js"
|
|
8
8
|
},
|
|
@@ -35,10 +35,16 @@
|
|
|
35
35
|
"keywords": [
|
|
36
36
|
"claude",
|
|
37
37
|
"claude-code",
|
|
38
|
+
"claude-code-leaderboard",
|
|
38
39
|
"codex",
|
|
40
|
+
"codex-leaderboard",
|
|
39
41
|
"opencode",
|
|
40
42
|
"amp",
|
|
41
43
|
"coding-agent",
|
|
44
|
+
"ai-coding-agent",
|
|
45
|
+
"token-usage",
|
|
46
|
+
"usage-tracker",
|
|
47
|
+
"cost-tracking",
|
|
42
48
|
"usage",
|
|
43
49
|
"ranking",
|
|
44
50
|
"leaderboard"
|