ccgather 1.3.47 → 1.3.49

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 +40 -66
  2. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -40,13 +40,6 @@ function getConfig() {
40
40
  }
41
41
  return configInstance;
42
42
  }
43
- function resetConfig() {
44
- const config = getConfig();
45
- config.clear();
46
- Object.entries(defaults).forEach(([key, value]) => {
47
- config.set(key, value);
48
- });
49
- }
50
43
  function isAuthenticated() {
51
44
  const config = getConfig();
52
45
  return !!config.get("apiToken");
@@ -75,7 +68,7 @@ function hyperlink(text, url) {
75
68
  return `\x1B]8;;${url}\x07${text}\x1B]8;;\x07`;
76
69
  }
77
70
  function centerText(text, width) {
78
- const len = stripAnsi(text).length;
71
+ const len = getDisplayWidth(text);
79
72
  const pad = width - len;
80
73
  return " ".repeat(Math.max(0, Math.floor(pad / 2))) + text + " ".repeat(Math.max(0, pad - Math.floor(pad / 2)));
81
74
  }
@@ -85,13 +78,13 @@ function createProfessionalHeader() {
85
78
  const h = boxRound.horizontal;
86
79
  lines.push(colors.dim(` ${boxRound.topLeft}${h.repeat(HEADER_WIDTH)}${boxRound.topRight}`));
87
80
  const logoLines = [
88
- ` ${colors.primary("\u2584\u2588\u2580\u2580 \u2584\u2588\u2580\u2580")} ${colors.secondary("\u2584\u2588\u2580\u2580 \u2584\u2588\u2580\u2588\u2584 \u2580\u2588\u2580 \u2588 \u2588 \u2588\u2580\u2580 \u2588\u2580\u2588")} `,
89
- ` ${colors.primary("\u2588 \u2588 ")} ${colors.secondary("\u2588 \u2580\u2588 \u2588\u2580\u2580\u2588\u2580 \u2588 \u2588\u2580\u2580\u2588 \u2588\u2580\u2580 \u2588\u2588\u2580")} `,
90
- ` ${colors.primary("\u2580\u2588\u2584\u2584 \u2580\u2588\u2584\u2584")} ${colors.secondary("\u2580\u2588\u2584\u2584\u2580 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588\u2584\u2584 \u2588 \u2588")} `
81
+ `${colors.primary("\u2584\u2588\u2580\u2580 \u2584\u2588\u2580\u2580")} ${colors.secondary("\u2584\u2588\u2580\u2580 \u2584\u2588\u2580\u2588\u2584 \u2580\u2588\u2580 \u2588 \u2588 \u2588\u2580\u2580 \u2588\u2580\u2588")}`,
82
+ `${colors.primary("\u2588 \u2588 ")} ${colors.secondary("\u2588 \u2580\u2588 \u2588\u2580\u2580\u2588\u2580 \u2588 \u2588\u2580\u2580\u2588 \u2588\u2580\u2580 \u2588\u2588\u2580")}`,
83
+ `${colors.primary("\u2580\u2588\u2584\u2584 \u2580\u2588\u2584\u2584")} ${colors.secondary("\u2580\u2588\u2584\u2584\u2580 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588\u2584\u2584 \u2588 \u2588")}`
91
84
  ];
92
85
  for (const l of logoLines) {
93
86
  lines.push(
94
- colors.dim(` ${v}`) + l + " ".repeat(Math.max(0, HEADER_WIDTH - stripAnsi(l).length)) + colors.dim(v)
87
+ colors.dim(` ${v}`) + centerText(l, HEADER_WIDTH) + colors.dim(v)
95
88
  );
96
89
  }
97
90
  const versionStr = `v${VERSION}`;
@@ -118,7 +111,7 @@ function createProfessionalHeader() {
118
111
  }
119
112
  function createBox(lines, width = 47) {
120
113
  const paddedLines = lines.map((line) => {
121
- const visibleLength = stripAnsi(line).length;
114
+ const visibleLength = getDisplayWidth(line);
122
115
  const padding = width - 2 - visibleLength;
123
116
  return `${box.vertical} ${line}${" ".repeat(Math.max(0, padding))} ${box.vertical}`;
124
117
  });
@@ -126,8 +119,14 @@ function createBox(lines, width = 47) {
126
119
  const bottom = colors.dim(` ${box.bottomLeft}${box.horizontal.repeat(width)}${box.bottomRight}`);
127
120
  return [top, ...paddedLines.map((l) => colors.dim(" ") + l), bottom].join("\n");
128
121
  }
129
- function stripAnsi(str) {
130
- return str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
122
+ function getDisplayWidth(str) {
123
+ let width = (0, import_string_width.default)(str);
124
+ const flagRegex = /[🇦-🇿][🇦-🇿]/gu;
125
+ const flags = str.match(flagRegex);
126
+ if (flags) {
127
+ width += flags.length * 2 * 2;
128
+ }
129
+ return width;
131
130
  }
132
131
  function header(title, icon = "") {
133
132
  const iconPart = icon ? `${icon} ` : "";
@@ -203,8 +202,7 @@ function getLevelProgress(tokens) {
203
202
  }
204
203
  function countryCodeToFlag(countryCode) {
205
204
  if (!countryCode || countryCode.length !== 2) return "\u{1F310}";
206
- const codePoints = countryCode.toUpperCase().split("").map((char) => 127462 + char.charCodeAt(0) - 65);
207
- return String.fromCodePoint(...codePoints);
205
+ return countryCode.toUpperCase();
208
206
  }
209
207
  function success(message) {
210
208
  return `${colors.success("\u2713")} ${message}`;
@@ -361,7 +359,7 @@ async function printAnimatedWelcomeBox(user) {
361
359
  const flag = countryCodeToFlag(user.countryCode);
362
360
  lines.push(`${flag} Country Rank: ${colors.primary(`#${user.countryRank}`)}`);
363
361
  }
364
- const maxVisibleLength = Math.max(...lines.map((l) => stripAnsi(l).length));
362
+ const maxVisibleLength = Math.max(...lines.map((l) => getDisplayWidth(l)));
365
363
  const boxWidth = Math.max(maxVisibleLength + 4, 47);
366
364
  const top = colors.dim(` ${box.topLeft}${box.horizontal.repeat(boxWidth)}${box.topRight}`);
367
365
  const bottom = colors.dim(
@@ -370,7 +368,7 @@ async function printAnimatedWelcomeBox(user) {
370
368
  console.log(top);
371
369
  await sleep(20);
372
370
  for (const line of lines) {
373
- const visibleLength = stripAnsi(line).length;
371
+ const visibleLength = getDisplayWidth(line);
374
372
  const padding = boxWidth - 2 - visibleLength;
375
373
  const paddedLine = `${box.vertical} ${line}${" ".repeat(Math.max(0, padding))} ${box.vertical}`;
376
374
  console.log(colors.dim(" ") + paddedLine);
@@ -378,12 +376,13 @@ async function printAnimatedWelcomeBox(user) {
378
376
  }
379
377
  console.log(bottom);
380
378
  }
381
- var import_chalk, VERSION, colors, LOGO, LOGO_COMPACT, TAGLINE, SLOGAN, boxRound, box, HEADER_WIDTH, LEVELS;
379
+ var import_chalk, import_string_width, VERSION, colors, LOGO, LOGO_COMPACT, TAGLINE, SLOGAN, boxRound, box, HEADER_WIDTH, LEVELS;
382
380
  var init_ui = __esm({
383
381
  "src/lib/ui.ts"() {
384
382
  "use strict";
385
383
  import_chalk = __toESM(require("chalk"));
386
- VERSION = true ? "1.3.47" : "0.0.0";
384
+ import_string_width = __toESM(require("string-width"));
385
+ VERSION = true ? "1.3.48" : "0.0.0";
387
386
  colors = {
388
387
  primary: import_chalk.default.hex("#DA7756"),
389
388
  // Claude coral
@@ -1137,7 +1136,10 @@ async function submitToServer(data) {
1137
1136
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1138
1137
  dailyUsage: data.dailyUsage,
1139
1138
  // Session fingerprint for duplicate prevention
1140
- sessionFingerprint: data.sessionFingerprint
1139
+ sessionFingerprint: data.sessionFingerprint,
1140
+ // League placement audit trail
1141
+ leagueReason: data.leagueReason,
1142
+ leagueReasonDetails: data.leagueReasonDetails
1141
1143
  })
1142
1144
  });
1143
1145
  if (!response.ok) {
@@ -1259,12 +1261,8 @@ async function submit(options) {
1259
1261
  }
1260
1262
  return;
1261
1263
  }
1262
- verifySpinner.succeed(colors.success("Authenticated"));
1263
1264
  const username = tokenCheck.username || config.get("username");
1264
- if (username) {
1265
- console.log(`
1266
- ${colors.muted("Logged in as:")} ${colors.white(username)}`);
1267
- }
1265
+ verifySpinner.succeed(colors.success(`Authenticated as ${colors.white(username || "unknown")}`));
1268
1266
  const projectName = getCurrentProjectName();
1269
1267
  if (!hasProjectSessions()) {
1270
1268
  console.log(`
@@ -1364,6 +1362,9 @@ async function submit(options) {
1364
1362
  console.log(` ${success(`Selected: ${finalPlan.toUpperCase()}`)}`);
1365
1363
  } else {
1366
1364
  planDetectionReason = "current";
1365
+ console.log(
1366
+ ` ${colors.dim("\u{1F4CB} League: reading plan info only (subscriptionType, rateLimitTier)")}`
1367
+ );
1367
1368
  if (usageData.ccplan) {
1368
1369
  for (const line of currentPlanMessage(usageData.ccplan)) {
1369
1370
  console.log(line);
@@ -1371,6 +1372,14 @@ async function submit(options) {
1371
1372
  }
1372
1373
  }
1373
1374
  usageData.ccplan = finalPlan;
1375
+ usageData.leagueReason = planDetectionReason === "opus" ? "opus" : planDetectionReason === "user_choice" ? "user_choice" : "credential";
1376
+ if (planDetectionReason === "opus") {
1377
+ usageData.leagueReasonDetails = `Opus verified: ${opusCheck.opusModels.join(", ")}`;
1378
+ } else if (planDetectionReason === "user_choice") {
1379
+ usageData.leagueReasonDetails = `User selected: ${finalPlan} (data >${oldDataCheck.daysSinceOldest}d old)`;
1380
+ } else {
1381
+ usageData.leagueReasonDetails = `Credential: ${usageData.ccplan || "free"}`;
1382
+ }
1374
1383
  usageData.dailyUsage = usageData.dailyUsage.map((daily) => ({
1375
1384
  ...daily,
1376
1385
  ccplan: finalPlan
@@ -1677,8 +1686,10 @@ async function showSettingsMenu() {
1677
1686
  name: "settingsAction",
1678
1687
  message: "Settings:",
1679
1688
  choices: [
1680
- { name: `\u{1F510} ${colors.white("Re-authenticate")}`, value: "auth" },
1681
- { name: `\u{1F5D1}\uFE0F ${colors.white("Disconnect from CCgather")}`, value: "disconnect" },
1689
+ {
1690
+ name: `\u{1F510} ${colors.white("Re-authenticate")} ${colors.dim("\u2013 switch account or fix login issues")}`,
1691
+ value: "auth"
1692
+ },
1682
1693
  { name: `\u2B05\uFE0F ${import_chalk3.default.gray("Back")}`, value: "back" }
1683
1694
  ],
1684
1695
  loop: false
@@ -1690,48 +1701,11 @@ async function showSettingsMenu() {
1690
1701
  await auth2({});
1691
1702
  break;
1692
1703
  }
1693
- case "disconnect": {
1694
- await handleDisconnect();
1695
- break;
1696
- }
1697
1704
  case "back":
1698
1705
  await showMenuOnly();
1699
1706
  break;
1700
1707
  }
1701
1708
  }
1702
- async function handleDisconnect() {
1703
- const { confirmFirst } = await import_inquirer3.default.prompt([
1704
- {
1705
- type: "confirm",
1706
- name: "confirmFirst",
1707
- message: import_chalk3.default.yellow("Are you sure you want to disconnect from CCgather?"),
1708
- default: false
1709
- }
1710
- ]);
1711
- if (!confirmFirst) {
1712
- console.log(colors.dim("\n Cancelled.\n"));
1713
- await showSettingsMenu();
1714
- return;
1715
- }
1716
- const { confirmSecond } = await import_inquirer3.default.prompt([
1717
- {
1718
- type: "confirm",
1719
- name: "confirmSecond",
1720
- message: import_chalk3.default.red("This will remove all local settings. Are you really sure?"),
1721
- default: false
1722
- }
1723
- ]);
1724
- if (!confirmSecond) {
1725
- console.log(colors.dim("\n Cancelled.\n"));
1726
- await showSettingsMenu();
1727
- return;
1728
- }
1729
- resetConfig();
1730
- console.log();
1731
- console.log(colors.success(" \u2713 Disconnected from CCgather"));
1732
- console.log(colors.dim(" Your CCgather settings have been removed."));
1733
- console.log(colors.dim(" Run npx ccgather to set up again.\n"));
1734
- }
1735
1709
  program.action(async () => {
1736
1710
  await showMainMenu();
1737
1711
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccgather",
3
- "version": "1.3.47",
3
+ "version": "1.3.49",
4
4
  "description": "CLI tool for syncing Claude Code usage data to CCgather leaderboard",
5
5
  "bin": {
6
6
  "ccgather": "dist/index.js",
@@ -31,6 +31,7 @@
31
31
  "inquirer": "^9.2.23",
32
32
  "open": "^10.1.0",
33
33
  "ora": "^8.1.0",
34
+ "string-width": "^7.2.0",
34
35
  "update-notifier": "^7.3.1"
35
36
  },
36
37
  "devDependencies": {