ccclub 0.3.7 → 0.3.9

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.
Files changed (2) hide show
  1. package/dist/index.js +103 -75
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { Command, Option } from "commander";
6
6
  // src/commands/init.ts
7
7
  import { createInterface as createInterface2 } from "readline/promises";
8
8
  import { stdin, stdout } from "process";
9
- import chalk4 from "chalk";
9
+ import chalk5 from "chalk";
10
10
  import ora2 from "ora";
11
11
 
12
12
  // src/config.ts
@@ -991,6 +991,7 @@ function aggregateSourceToBlocks(source, entries, humanTurns) {
991
991
  let reasoningTokens = 0;
992
992
  let totalTokens = 0;
993
993
  let costUSD = 0;
994
+ let lastActivityMs = 0;
994
995
  for (const entry of currentBlock) {
995
996
  inputTokens += entry.inputTokens;
996
997
  outputTokens += entry.outputTokens;
@@ -1011,11 +1012,14 @@ function aggregateSourceToBlocks(source, entries, humanTurns) {
1011
1012
  entry.reasoningTokens || 0
1012
1013
  );
1013
1014
  }
1015
+ const entryMs = new Date(entry.timestamp).getTime();
1016
+ if (Number.isFinite(entryMs) && entryMs > lastActivityMs) lastActivityMs = entryMs;
1014
1017
  }
1015
1018
  blocks.push({
1016
1019
  source,
1017
1020
  blockStart: blockStart.toISOString(),
1018
1021
  blockEnd: blockEnd.toISOString(),
1022
+ lastActivityAt: new Date(lastActivityMs || blockEnd.getTime()).toISOString(),
1019
1023
  inputTokens,
1020
1024
  outputTokens,
1021
1025
  cacheCreationTokens,
@@ -1355,7 +1359,32 @@ function getLatestBlockStartBySource(blocks) {
1355
1359
 
1356
1360
  // src/global-install.ts
1357
1361
  import { exec as exec2 } from "child_process";
1362
+ import chalk4 from "chalk";
1363
+
1364
+ // src/theme.ts
1358
1365
  import chalk3 from "chalk";
1366
+ var theme = {
1367
+ title: chalk3.hex("#F1EDE7").bold,
1368
+ text: chalk3.hex("#E8E4DE"),
1369
+ muted: chalk3.hex("#8A8480"),
1370
+ faint: chalk3.hex("#5A5550"),
1371
+ brand: chalk3.hex("#D4935E"),
1372
+ link: chalk3.hex("#7AB7C6").underline,
1373
+ linkText: chalk3.hex("#7AB7C6"),
1374
+ success: chalk3.hex("#63B486"),
1375
+ successBold: chalk3.hex("#63B486").bold,
1376
+ gold: chalk3.hex("#D6B56D"),
1377
+ goldBold: chalk3.hex("#D6B56D").bold,
1378
+ silver: chalk3.hex("#AEB7BF"),
1379
+ silverBold: chalk3.hex("#AEB7BF").bold,
1380
+ bronze: chalk3.hex("#C58A61"),
1381
+ bronzeBold: chalk3.hex("#C58A61"),
1382
+ warning: chalk3.hex("#D4A85C"),
1383
+ warningBold: chalk3.hex("#D4A85C").bold,
1384
+ danger: chalk3.hex("#D26A6A")
1385
+ };
1386
+
1387
+ // src/global-install.ts
1359
1388
  function run(cmd) {
1360
1389
  return new Promise((resolve2) => {
1361
1390
  exec2(cmd, (err, stdout5) => resolve2(err ? "" : stdout5.trim()));
@@ -1364,13 +1393,13 @@ function run(cmd) {
1364
1393
  async function ensureGlobalInstall() {
1365
1394
  const globalList = await run("npm list -g ccclub --depth=0");
1366
1395
  if (globalList.includes("ccclub@")) return;
1367
- console.log(chalk3.dim("\n Installing ccclub globally so you can run it directly..."));
1396
+ console.log(chalk4.dim("\n Installing ccclub globally so you can run it directly..."));
1368
1397
  const result = await run("npm install -g ccclub");
1369
1398
  if (result) {
1370
- console.log(chalk3.green(" Done!") + chalk3.dim(" You can now use ") + chalk3.white("ccclub") + chalk3.dim(" directly."));
1399
+ console.log(theme.success(" Done!") + chalk4.dim(" You can now use ") + theme.text("ccclub") + chalk4.dim(" directly."));
1371
1400
  } else {
1372
- console.log(chalk3.dim(" Could not auto-install. Run manually:"));
1373
- console.log(chalk3.white(" npm install -g ccclub"));
1401
+ console.log(chalk4.dim(" Could not auto-install. Run manually:"));
1402
+ console.log(theme.text(" npm install -g ccclub"));
1374
1403
  }
1375
1404
  }
1376
1405
 
@@ -1378,28 +1407,28 @@ async function ensureGlobalInstall() {
1378
1407
  async function initCommand() {
1379
1408
  const existing = await loadConfig();
1380
1409
  if (existing) {
1381
- console.log(chalk4.yellow("Already initialized!"));
1410
+ console.log(chalk5.yellow("Already initialized!"));
1382
1411
  console.log(` User: ${existing.displayName}`);
1383
1412
  console.log(` Groups: ${existing.groups.join(", ") || "(none)"}`);
1384
1413
  if (!isHookInstalled()) {
1385
1414
  const hookOk = await installHook();
1386
- if (hookOk) console.log(chalk4.green(" Auto-sync hook installed!"));
1415
+ if (hookOk) console.log(chalk5.green(" Auto-sync hook installed!"));
1387
1416
  }
1388
1417
  if (!isHeartbeatInstalled()) {
1389
1418
  const heartbeatOk = await installHeartbeat();
1390
- if (heartbeatOk) console.log(chalk4.green(" Background sync installed!"));
1419
+ if (heartbeatOk) console.log(chalk5.green(" Background sync installed!"));
1391
1420
  }
1392
- console.log(chalk4.dim('\n Run "ccclub" to see the leaderboard'));
1421
+ console.log(chalk5.dim('\n Run "ccclub" to see the leaderboard'));
1393
1422
  return;
1394
1423
  }
1395
1424
  const rl = createInterface2({ input: stdin, output: stdout });
1396
1425
  try {
1397
1426
  const defaultName = getDefaultDisplayName();
1398
- const prompt = defaultName ? chalk4.bold(`Your display name (${defaultName}): `) : chalk4.bold("Your display name: ");
1427
+ const prompt = defaultName ? chalk5.bold(`Your display name (${defaultName}): `) : chalk5.bold("Your display name: ");
1399
1428
  const input = await rl.question(prompt);
1400
1429
  const displayName = input.trim() || defaultName || "";
1401
1430
  if (!displayName) {
1402
- console.error(chalk4.red("Name cannot be empty"));
1431
+ console.error(chalk5.red("Name cannot be empty"));
1403
1432
  return;
1404
1433
  }
1405
1434
  const spinner = ora2("Setting up...").start();
@@ -1436,17 +1465,17 @@ async function initCommand() {
1436
1465
  ]);
1437
1466
  spinner.succeed("ccclub initialized!");
1438
1467
  if (!hookOk) {
1439
- console.log(chalk4.dim(' Tip: run "ccclub hook" to set up auto-sync'));
1468
+ console.log(chalk5.dim(' Tip: run "ccclub hook" to set up auto-sync'));
1440
1469
  }
1441
1470
  if (!heartbeatOk) {
1442
- console.log(chalk4.dim(' Tip: run "ccclub sync" manually to refresh non-Claude agent usage'));
1471
+ console.log(chalk5.dim(' Tip: run "ccclub sync" manually to refresh non-Claude agent usage'));
1443
1472
  }
1444
1473
  console.log("");
1445
- console.log(chalk4.bold(" Invite friends to compete:"));
1474
+ console.log(chalk5.bold(" Invite friends to compete:"));
1446
1475
  console.log("");
1447
- console.log(` ${chalk4.cyan.underline(`${apiUrl}/invite/${data.groupCode}`)}`);
1476
+ console.log(` ${theme.link(`${apiUrl}/invite/${data.groupCode}`)}`);
1448
1477
  console.log("");
1449
- console.log(chalk4.dim(` or: npx ccclub join ${data.groupCode}`));
1478
+ console.log(chalk5.dim(` or: npx ccclub join ${data.groupCode}`));
1450
1479
  console.log("");
1451
1480
  await doSync(true);
1452
1481
  await ensureGlobalInstall();
@@ -1457,14 +1486,14 @@ async function initCommand() {
1457
1486
  }
1458
1487
  function printQuickStart() {
1459
1488
  console.log("");
1460
- console.log(chalk4.dim(" Run ") + chalk4.white("ccclub") + chalk4.dim(" to see the leaderboard. ") + chalk4.white("ccclub -h") + chalk4.dim(" for all commands."));
1489
+ console.log(chalk5.dim(" Run ") + theme.text("ccclub") + chalk5.dim(" to see the leaderboard. ") + theme.text("ccclub -h") + chalk5.dim(" for all commands."));
1461
1490
  console.log("");
1462
1491
  }
1463
1492
 
1464
1493
  // src/commands/join.ts
1465
1494
  import { createInterface as createInterface3 } from "readline/promises";
1466
1495
  import { stdin as stdin2, stdout as stdout2 } from "process";
1467
- import chalk5 from "chalk";
1496
+ import chalk6 from "chalk";
1468
1497
  import ora3 from "ora";
1469
1498
  async function joinCommand(inviteCode) {
1470
1499
  let config = await loadConfig();
@@ -1478,11 +1507,11 @@ async function joinCommand(inviteCode) {
1478
1507
  const rl = createInterface3({ input: stdin2, output: stdout2 });
1479
1508
  try {
1480
1509
  const defaultName = getDefaultDisplayName();
1481
- const prompt = defaultName ? chalk5.bold(`Your display name (${defaultName}): `) : chalk5.bold("Your display name: ");
1510
+ const prompt = defaultName ? chalk6.bold(`Your display name (${defaultName}): `) : chalk6.bold("Your display name: ");
1482
1511
  const input = await rl.question(prompt);
1483
1512
  displayName = input.trim() || defaultName || "";
1484
1513
  if (!displayName) {
1485
- console.error(chalk5.red("Name cannot be empty"));
1514
+ console.error(chalk6.red("Name cannot be empty"));
1486
1515
  return;
1487
1516
  }
1488
1517
  } finally {
@@ -1531,12 +1560,11 @@ async function joinCommand(inviteCode) {
1531
1560
  await ensureGlobalInstall();
1532
1561
  }
1533
1562
  console.log("");
1534
- console.log(chalk5.dim(" Run ") + chalk5.white("ccclub") + chalk5.dim(" to see the leaderboard. ") + chalk5.white("ccclub -h") + chalk5.dim(" for all commands."));
1563
+ console.log(chalk6.dim(" Run ") + chalk6.white("ccclub") + chalk6.dim(" to see the leaderboard. ") + chalk6.white("ccclub -h") + chalk6.dim(" for all commands."));
1535
1564
  console.log("");
1536
1565
  }
1537
1566
 
1538
1567
  // src/commands/rank.ts
1539
- import chalk6 from "chalk";
1540
1568
  import Table from "cli-table3";
1541
1569
  import ora4 from "ora";
1542
1570
 
@@ -1595,11 +1623,11 @@ async function rankCommand(options) {
1595
1623
  Usage: ccclub -d <period>
1596
1624
 
1597
1625
  Options:
1598
- ${chalk6.white("ccclub -d 1")} Yesterday
1599
- ${chalk6.white("ccclub -d 7")} Last 7 days
1600
- ${chalk6.white("ccclub -d 30")} Last 30 days
1601
- ${chalk6.white("ccclub -d all")} All time
1602
- ${chalk6.white("ccclub")} Today (default)
1626
+ ${theme.text("ccclub -d 1")} Yesterday
1627
+ ${theme.text("ccclub -d 7")} Last 7 days
1628
+ ${theme.text("ccclub -d 30")} Last 30 days
1629
+ ${theme.text("ccclub -d all")} All time
1630
+ ${theme.text("ccclub")} Today (default)
1603
1631
  `;
1604
1632
  if (options.days) {
1605
1633
  if (options.days === true) {
@@ -1609,7 +1637,7 @@ async function rankCommand(options) {
1609
1637
  const DAYS_MAP = { "1": "yesterday", "7": "weekly", "30": "monthly", "all": "all-time" };
1610
1638
  const mapped = DAYS_MAP[options.days];
1611
1639
  if (!mapped) {
1612
- console.log(chalk6.red(`
1640
+ console.log(theme.danger(`
1613
1641
  Unknown value: -d ${options.days}`));
1614
1642
  console.log(DAYS_HINT);
1615
1643
  return;
@@ -1633,7 +1661,7 @@ async function rankCommand(options) {
1633
1661
  codes = config.groups.length > 0 ? config.groups : [];
1634
1662
  }
1635
1663
  if (codes.length === 0) {
1636
- console.log(chalk6.red("No group found. Run 'ccclub init' or 'ccclub join <code>' first."));
1664
+ console.log(theme.danger("No group found. Run 'ccclub init' or 'ccclub join <code>' first."));
1637
1665
  return;
1638
1666
  }
1639
1667
  const localUsagePromise = fetchUsageLimits().catch(() => null);
@@ -1671,7 +1699,7 @@ async function rankCommand(options) {
1671
1699
  for (let i = 0; i < groupResults.length; i++) {
1672
1700
  const { code, rankData, activityData, range } = groupResults[i];
1673
1701
  if (!rankData) {
1674
- console.log(chalk6.red(`
1702
+ console.log(theme.danger(`
1675
1703
  Couldn't load leaderboard for ${code}`));
1676
1704
  continue;
1677
1705
  }
@@ -1683,10 +1711,10 @@ async function rankCommand(options) {
1683
1711
  if (activityData) renderActivity(activityData, range);
1684
1712
  if (i < groupResults.length - 1) console.log("");
1685
1713
  }
1686
- console.log(chalk6.dim("\n Tokens = input + output + reasoning ") + chalk6.yellow("(cache excluded)") + chalk6.dim(". Use ") + chalk6.white("--cache") + chalk6.dim(" to include cache tokens."));
1714
+ console.log(theme.muted("\n Tokens = input + output + reasoning ") + theme.warning("(cache excluded)") + theme.muted(". Use ") + theme.text("--cache") + theme.muted(" to include cache tokens."));
1687
1715
  const update = await getUpdateResult();
1688
1716
  if (update) {
1689
- console.log(chalk6.yellow("\n Update available") + chalk6.dim(`: ${update.current} \u2192 ${update.latest} Run `) + chalk6.cyan("npm i -g ccclub@latest"));
1717
+ console.log(theme.warningBold("\n Update available") + theme.muted(`: ${update.current} \u2192 ${update.latest} Run `) + theme.linkText("npm i -g ccclub@latest"));
1690
1718
  }
1691
1719
  } catch (err) {
1692
1720
  spinner.fail(`Error: ${formatFetchError(err)}`);
@@ -1709,20 +1737,20 @@ function formatTokens(n) {
1709
1737
  }
1710
1738
  function printGroup(data, code, period, config, showCache = false, showAll = false) {
1711
1739
  if (data.rankings.length === 0) {
1712
- console.log(chalk6.bold(`
1740
+ console.log(theme.title(`
1713
1741
  ${data.group.name}`));
1714
- console.log(chalk6.yellow(" No data for this period yet"));
1715
- console.log(chalk6.dim(" Sync your data first: ccclub sync"));
1742
+ console.log(theme.warning(" No data for this period yet"));
1743
+ console.log(theme.muted(" Sync your data first: ccclub sync"));
1716
1744
  return;
1717
1745
  }
1718
- console.log(chalk6.bold(`
1746
+ console.log(theme.title(`
1719
1747
  ${data.group.name}`));
1720
1748
  const periodLabel = { daily: "TODAY", yesterday: "YESTERDAY", weekly: "7 DAYS", monthly: "30 DAYS", "all-time": "ALL TIME" };
1721
1749
  const now = Date.now();
1722
1750
  const activeCount = data.rankings.filter((r) => r.lastSync && now - new Date(r.lastSync).getTime() < ACTIVE_THRESHOLD_MS).length;
1723
- console.log(chalk6.dim(` ${periodLabel[period] || period.toUpperCase()} \xB7 ${data.start.slice(0, 10)} \u2192 ${data.end.slice(0, 10)} \xB7 ${data.group.memberCount} members`));
1751
+ console.log(theme.muted(` ${periodLabel[period] || period.toUpperCase()} \xB7 ${data.start.slice(0, 10)} \u2192 ${data.end.slice(0, 10)} \xB7 ${data.group.memberCount} members`));
1724
1752
  if (activeCount > 0) {
1725
- console.log(chalk6.green(` ${activeCount} active`));
1753
+ console.log(theme.success(` ${activeCount} active`));
1726
1754
  }
1727
1755
  console.log("");
1728
1756
  const activeRankings = showAll || data.rankings.length <= 15 ? data.rankings : data.rankings.filter((r) => r.costUSD > 0 || r.userId === config.userId);
@@ -1769,53 +1797,65 @@ function printGroup(data, code, period, config, showCache = false, showAll = fal
1769
1797
  columnWidth("$/Turn", plainRows.map((r) => r.perTurn), 6, 7)
1770
1798
  );
1771
1799
  const table = new Table({
1772
- head: head.map((h) => chalk6.cyan(h)),
1773
- style: { head: [], border: [] },
1800
+ head: head.map((h) => theme.linkText(h)),
1801
+ style: { head: [], border: ["gray"] },
1774
1802
  colWidths: widths
1775
1803
  });
1776
1804
  for (const plain of plainRows) {
1777
1805
  const { entry } = plain;
1778
1806
  const isMe = entry.userId === config.userId;
1779
- const marker = isMe ? chalk6.green("\u2192") : " ";
1780
- const id = (s) => s;
1781
- const c = isMe ? chalk6.green : entry.rank === 1 ? chalk6.yellow : id;
1782
- const nameC = isMe ? chalk6.green.bold : entry.rank === 1 ? chalk6.yellow.bold : id;
1807
+ const rowStyle = isMe ? theme.success : podiumStyle(entry.rank);
1808
+ const nameStyle = isMe ? theme.successBold : podiumNameStyle(entry.rank);
1809
+ const rankStyle = podiumNameStyle(entry.rank);
1810
+ const marker = isMe ? theme.success("\u2192") : " ";
1783
1811
  const nameWidth = Math.max(widths[1] - 2, 4);
1784
- const displayName = plain.isActive ? `${chalk6.green("\u25CF")} ${nameC(truncateDisplay(entry.displayName, Math.max(nameWidth - 2, 1)))}` : nameC(truncateDisplay(entry.displayName, nameWidth));
1812
+ const displayName = plain.isActive ? `${theme.success("\u25CF")} ${nameStyle(truncateDisplay(entry.displayName, Math.max(nameWidth - 2, 1)))}` : nameStyle(truncateDisplay(entry.displayName, nameWidth));
1785
1813
  const row = [
1786
- `${marker}${c(String(entry.rank))}`,
1814
+ `${marker}${rankStyle(String(entry.rank))}`,
1787
1815
  displayName
1788
1816
  ];
1789
1817
  if (hasAgents) {
1790
1818
  const agentWidth = Math.max(widths[2] - 2, 4);
1791
- row.push(c(truncateDisplay(plain.agents, agentWidth)));
1819
+ row.push(rowStyle(truncateDisplay(plain.agents, agentWidth)));
1792
1820
  }
1793
- row.push(c(plain.cost), c(plain.tokens));
1821
+ row.push(rowStyle(plain.cost), rowStyle(plain.tokens));
1794
1822
  if (hasPlan) {
1795
1823
  row.push(colorRoi(plain.roi, entry));
1796
1824
  }
1797
- row.push(c(plain.turns));
1798
- row.push(entry.chatCount > 0 ? c(plain.perTurn) : chalk6.dim("\u2014"));
1825
+ row.push(rowStyle(plain.turns));
1826
+ row.push(entry.chatCount > 0 ? rowStyle(plain.perTurn) : theme.faint("\u2014"));
1799
1827
  table.push(row);
1800
1828
  }
1801
1829
  console.log(table.toString());
1802
1830
  if (hiddenCount > 0) {
1803
- console.log(chalk6.dim(` ${hiddenCount} inactive member${hiddenCount > 1 ? "s" : ""} hidden \xB7 ccclub --all to show`));
1831
+ console.log(theme.muted(` ${hiddenCount} inactive member${hiddenCount > 1 ? "s" : ""} hidden \xB7 ccclub --all to show`));
1804
1832
  }
1805
- console.log(chalk6.dim(" Dashboard: ") + chalk6.green(`${config.apiUrl}/g/${code}`));
1833
+ console.log(theme.muted(" Dashboard: ") + theme.link(`${config.apiUrl}/g/${code}`));
1806
1834
  if (code !== "global") {
1807
- console.log(chalk6.dim(" Invite: ") + chalk6.hex("#d4935e").underline(`${config.apiUrl}/invite/${code}`));
1835
+ console.log(theme.muted(" Invite: ") + theme.link(`${config.apiUrl}/invite/${code}`));
1808
1836
  }
1809
1837
  if (hasPlan) {
1810
1838
  const me = data.rankings.find((r) => r.userId === config.userId);
1811
1839
  if (me && !me.plan) {
1812
- console.log(chalk6.dim(" Set your plan: ") + chalk6.white("ccclub profile --plan pro|max100|max200|api"));
1840
+ console.log(theme.muted(" Set your plan: ") + theme.text("ccclub profile --plan pro|max100|max200|api"));
1813
1841
  }
1814
1842
  }
1815
1843
  }
1816
1844
  function isEntryActive(entry, now) {
1817
1845
  return Boolean(entry.lastSync && now - new Date(entry.lastSync).getTime() < ACTIVE_THRESHOLD_MS);
1818
1846
  }
1847
+ function podiumStyle(rank) {
1848
+ if (rank === 1) return theme.gold;
1849
+ if (rank === 2) return theme.silver;
1850
+ if (rank === 3) return theme.bronze;
1851
+ return theme.text;
1852
+ }
1853
+ function podiumNameStyle(rank) {
1854
+ if (rank === 1) return theme.goldBold;
1855
+ if (rank === 2) return theme.silverBold;
1856
+ if (rank === 3) return theme.bronzeBold;
1857
+ return theme.text;
1858
+ }
1819
1859
  function formatRoi(entry, hasPlan) {
1820
1860
  if (!hasPlan) return "";
1821
1861
  if (entry.plan && entry.plan !== "api") {
@@ -1832,9 +1872,9 @@ function colorRoi(roiStr, entry) {
1832
1872
  const price = PLAN_PRICES[entry.plan];
1833
1873
  const monthly = entry.monthlyCostUSD || 0;
1834
1874
  const roi = price > 0 ? Math.round(monthly / price * 100) : 0;
1835
- return roi >= 100 ? chalk6.green.bold(roiStr) : roi >= 50 ? chalk6.yellow(roiStr) : chalk6.dim(roiStr);
1875
+ return roi >= 100 ? theme.successBold(roiStr) : roi >= 50 ? theme.warning(roiStr) : theme.faint(roiStr);
1836
1876
  }
1837
- return chalk6.dim(roiStr);
1877
+ return theme.faint(roiStr);
1838
1878
  }
1839
1879
  function formatAgents(entry) {
1840
1880
  if (entry.agentBreakdown && entry.agentBreakdown.length > 0) {
@@ -1908,7 +1948,7 @@ function renderActivity(data, range) {
1908
1948
  }
1909
1949
  }
1910
1950
  if (globalMax === 0) globalMax = 1;
1911
- console.log(chalk6.dim(`
1951
+ console.log(theme.muted(`
1912
1952
  Activity (${range})`));
1913
1953
  for (let i = 0; i < active.length; i++) {
1914
1954
  const user = active[i];
@@ -1920,22 +1960,10 @@ function renderActivity(data, range) {
1920
1960
  return SPARK_CHARS[idx];
1921
1961
  }).join("");
1922
1962
  const total = user.blocks.reduce((s, b) => s + b.cost, 0);
1923
- const displayWidth = [...user.displayName].reduce((w, ch) => w + (ch.charCodeAt(0) > 127 ? 2 : 1), 0);
1924
1963
  const maxWidth = 12;
1925
- let name = user.displayName;
1926
- if (displayWidth > maxWidth) {
1927
- let w = 0;
1928
- let cut = 0;
1929
- for (const ch of name) {
1930
- const cw = ch.charCodeAt(0) > 127 ? 2 : 1;
1931
- if (w + cw > maxWidth) break;
1932
- w += cw;
1933
- cut++;
1934
- }
1935
- name = [...name].slice(0, cut).join("");
1936
- }
1937
- const pad = " ".repeat(Math.max(0, maxWidth - displayWidth));
1938
- console.log(` ${chalk6.dim(name + pad)} ${spark} ${chalk6.dim("$" + total.toFixed(2))}`);
1964
+ const name = truncateDisplay(user.displayName, maxWidth);
1965
+ const pad = " ".repeat(Math.max(0, maxWidth - visualWidth(name)));
1966
+ console.log(` ${theme.muted(name + pad)} ${theme.brand(spark)} ${theme.muted("$" + total.toFixed(2))}`);
1939
1967
  }
1940
1968
  const axisArr = new Array(bucketCount).fill(" ");
1941
1969
  if (range === "24h" || range === "yesterday") {
@@ -1958,7 +1986,7 @@ function renderActivity(data, range) {
1958
1986
  for (let c = 0; c < label.length && b + c < bucketCount; c++) axisArr[b + c] = label[c];
1959
1987
  }
1960
1988
  }
1961
- console.log(chalk6.dim(" " + " ".repeat(12) + " " + axisArr.join("")));
1989
+ console.log(theme.faint(" " + " ".repeat(12) + " " + axisArr.join("")));
1962
1990
  }
1963
1991
 
1964
1992
  // src/commands/profile.ts
@@ -2128,7 +2156,7 @@ async function createGroupCommand() {
2128
2156
  }
2129
2157
  spinner.succeed(`Created "${data.groupName}"`);
2130
2158
  console.log("");
2131
- console.log(` ${chalk9.cyan.underline(`${config.apiUrl}/invite/${data.groupCode}`)}`);
2159
+ console.log(` ${theme.link(`${config.apiUrl}/invite/${data.groupCode}`)}`);
2132
2160
  console.log("");
2133
2161
  console.log(chalk9.dim(` or: npx ccclub join ${data.groupCode}`));
2134
2162
  } finally {
@@ -2228,7 +2256,7 @@ async function hookCommand() {
2228
2256
  }
2229
2257
 
2230
2258
  // src/index.ts
2231
- var VERSION = "0.3.7";
2259
+ var VERSION = "0.3.9";
2232
2260
  startUpdateCheck(VERSION);
2233
2261
  var program = new Command();
2234
2262
  if (process.argv.slice(2).includes("-v")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccclub",
3
- "version": "0.3.7",
3
+ "version": "0.3.9",
4
4
  "type": "module",
5
5
  "description": "Claude Code and Codex leaderboard among friends for coding agent tokens and costs",
6
6
  "bin": {