ccgather 1.3.24 → 1.3.26

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 +151 -69
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -156,9 +156,11 @@ Welcome, ${import_chalk2.default.white(pollData.username)}!`));
156
156
  process.exit(1);
157
157
  } catch (error2) {
158
158
  spinner.fail(import_chalk2.default.red("Authentication failed"));
159
- console.log(import_chalk2.default.red(`
159
+ console.log(
160
+ import_chalk2.default.red(`
160
161
  Error: ${error2 instanceof Error ? error2.message : "Unknown error"}
161
- `));
162
+ `)
163
+ );
162
164
  process.exit(1);
163
165
  }
164
166
  }
@@ -191,9 +193,11 @@ Welcome, ${import_chalk2.default.white(data.username)}!`));
191
193
  console.log(import_chalk2.default.dim("You can now submit your usage data.\n"));
192
194
  } catch (error2) {
193
195
  spinner.fail(import_chalk2.default.red("Authentication failed"));
194
- console.log(import_chalk2.default.red(`
196
+ console.log(
197
+ import_chalk2.default.red(`
195
198
  Error: ${error2 instanceof Error ? error2.message : "Unknown error"}
196
- `));
199
+ `)
200
+ );
197
201
  process.exit(1);
198
202
  }
199
203
  }
@@ -398,7 +402,13 @@ function scanUsageData(options = {}) {
398
402
  totalOutputTokens += outputTokens;
399
403
  totalCacheRead += cacheRead;
400
404
  totalCacheWrite += cacheWrite;
401
- const messageCost = estimateCost(model, inputTokens, outputTokens, cacheWrite, cacheRead);
405
+ const messageCost = estimateCost(
406
+ model,
407
+ inputTokens,
408
+ outputTokens,
409
+ cacheWrite,
410
+ cacheRead
411
+ );
402
412
  totalCost += messageCost;
403
413
  const totalModelTokens = inputTokens + outputTokens + cacheWrite + cacheRead;
404
414
  models[model] = (models[model] || 0) + totalModelTokens;
@@ -593,20 +603,30 @@ function createProfessionalHeader() {
593
603
  ` ${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")} `
594
604
  ];
595
605
  for (const l of logoLines) {
596
- lines.push(colors.dim(` ${v}`) + l + " ".repeat(Math.max(0, HEADER_WIDTH - stripAnsi(l).length)) + colors.dim(v));
606
+ lines.push(
607
+ colors.dim(` ${v}`) + l + " ".repeat(Math.max(0, HEADER_WIDTH - stripAnsi(l).length)) + colors.dim(v)
608
+ );
597
609
  }
598
610
  const versionStr = `v${VERSION}`;
599
611
  const versionPad = HEADER_WIDTH - versionStr.length - 2;
600
- lines.push(colors.dim(` ${v}`) + " ".repeat(versionPad) + colors.dim(versionStr) + " " + colors.dim(v));
612
+ lines.push(
613
+ colors.dim(` ${v}`) + " ".repeat(versionPad) + colors.dim(versionStr) + " " + colors.dim(v)
614
+ );
601
615
  lines.push(colors.dim(` ${boxRound.leftT}${h.repeat(HEADER_WIDTH)}${boxRound.rightT}`));
602
- lines.push(colors.dim(` ${v}`) + centerText(colors.muted("Where Claude Code Developers Gather"), HEADER_WIDTH) + colors.dim(v));
603
- lines.push(colors.dim(` ${v}`) + centerText(colors.dim("Gather \xB7 Compete \xB7 Rise"), HEADER_WIDTH) + colors.dim(v));
616
+ lines.push(
617
+ colors.dim(` ${v}`) + centerText(colors.muted("Where Claude Code Developers Gather"), HEADER_WIDTH) + colors.dim(v)
618
+ );
619
+ lines.push(
620
+ colors.dim(` ${v}`) + centerText(colors.dim("Gather \xB7 Compete \xB7 Rise"), HEADER_WIDTH) + colors.dim(v)
621
+ );
604
622
  lines.push(colors.dim(` ${v}`) + " ".repeat(HEADER_WIDTH) + colors.dim(v));
605
623
  const siteLabel = " \u{1F310} ccgather.com ";
606
624
  const siteLink = hyperlink(colors.secondary(siteLabel), "https://ccgather.com");
607
625
  const leftDashes = 13;
608
626
  const rightDashes = HEADER_WIDTH - leftDashes - siteLabel.length;
609
- lines.push(colors.dim(` ${boxRound.bottomLeft}${h.repeat(leftDashes)}`) + siteLink + colors.dim(`${h.repeat(rightDashes)}${boxRound.bottomRight}`));
627
+ lines.push(
628
+ colors.dim(` ${boxRound.bottomLeft}${h.repeat(leftDashes)}`) + siteLink + colors.dim(`${h.repeat(rightDashes)}${boxRound.bottomRight}`)
629
+ );
610
630
  return lines;
611
631
  }
612
632
  function createBox(lines, width = 47) {
@@ -656,23 +676,61 @@ function getCCplanBadge(ccplan) {
656
676
  };
657
677
  return badges[ccplan.toLowerCase()] || "";
658
678
  }
679
+ var LEVELS = [
680
+ { min: 0, level: 1, name: "Novice", icon: "\u{1F331}", color: colors.dim },
681
+ { min: 1e5, level: 2, name: "Apprentice", icon: "\u{1F4DA}", color: colors.muted },
682
+ { min: 5e5, level: 3, name: "Journeyman", icon: "\u26A1", color: colors.cyan },
683
+ { min: 1e6, level: 4, name: "Expert", icon: "\u{1F48E}", color: colors.pro },
684
+ { min: 5e6, level: 5, name: "Master", icon: "\u{1F525}", color: colors.warning },
685
+ { min: 1e7, level: 6, name: "Grandmaster", icon: "\u{1F451}", color: colors.max },
686
+ { min: 5e7, level: 7, name: "Legend", icon: "\u{1F31F}", color: colors.primary },
687
+ { min: 1e8, level: 8, name: "Mythic", icon: "\u{1F3C6}", color: colors.secondary }
688
+ ];
659
689
  function getLevelInfo(tokens) {
660
- const levels = [
661
- { min: 0, level: 1, name: "Novice", icon: "\u{1F331}", color: colors.dim },
662
- { min: 1e5, level: 2, name: "Apprentice", icon: "\u{1F4DA}", color: colors.muted },
663
- { min: 5e5, level: 3, name: "Journeyman", icon: "\u26A1", color: colors.cyan },
664
- { min: 1e6, level: 4, name: "Expert", icon: "\u{1F48E}", color: colors.pro },
665
- { min: 5e6, level: 5, name: "Master", icon: "\u{1F525}", color: colors.warning },
666
- { min: 1e7, level: 6, name: "Grandmaster", icon: "\u{1F451}", color: colors.max },
667
- { min: 5e7, level: 7, name: "Legend", icon: "\u{1F31F}", color: colors.primary },
668
- { min: 1e8, level: 8, name: "Mythic", icon: "\u{1F3C6}", color: colors.secondary }
669
- ];
670
- for (let i = levels.length - 1; i >= 0; i--) {
671
- if (tokens >= levels[i].min) {
672
- return levels[i];
690
+ for (let i = LEVELS.length - 1; i >= 0; i--) {
691
+ if (tokens >= LEVELS[i].min) {
692
+ return LEVELS[i];
693
+ }
694
+ }
695
+ return LEVELS[0];
696
+ }
697
+ function getLevelProgress(tokens) {
698
+ let currentIndex = 0;
699
+ for (let i = LEVELS.length - 1; i >= 0; i--) {
700
+ if (tokens >= LEVELS[i].min) {
701
+ currentIndex = i;
702
+ break;
673
703
  }
674
704
  }
675
- return levels[0];
705
+ const current = LEVELS[currentIndex];
706
+ const isMaxLevel = currentIndex === LEVELS.length - 1;
707
+ if (isMaxLevel) {
708
+ return {
709
+ current,
710
+ next: null,
711
+ progress: 100,
712
+ tokensToNext: 0,
713
+ isMaxLevel: true
714
+ };
715
+ }
716
+ const nextLevel = LEVELS[currentIndex + 1];
717
+ const currentMin = current.min;
718
+ const nextMin = nextLevel.min;
719
+ const progressInLevel = tokens - currentMin;
720
+ const levelRange = nextMin - currentMin;
721
+ const progress = Math.min(100, Math.round(progressInLevel / levelRange * 100));
722
+ return {
723
+ current,
724
+ next: {
725
+ level: nextLevel.level,
726
+ name: nextLevel.name,
727
+ icon: nextLevel.icon,
728
+ threshold: nextLevel.min
729
+ },
730
+ progress,
731
+ tokensToNext: nextMin - tokens,
732
+ isMaxLevel: false
733
+ };
676
734
  }
677
735
  function countryCodeToFlag(countryCode) {
678
736
  if (!countryCode || countryCode.length !== 2) return "\u{1F310}";
@@ -744,7 +802,9 @@ async function printAnimatedWelcomeBox(user) {
744
802
  const maxVisibleLength = Math.max(...lines.map((l) => stripAnsi(l).length));
745
803
  const boxWidth = Math.max(maxVisibleLength + 4, 47);
746
804
  const top = colors.dim(` ${box.topLeft}${box.horizontal.repeat(boxWidth)}${box.topRight}`);
747
- const bottom = colors.dim(` ${box.bottomLeft}${box.horizontal.repeat(boxWidth)}${box.bottomRight}`);
805
+ const bottom = colors.dim(
806
+ ` ${box.bottomLeft}${box.horizontal.repeat(boxWidth)}${box.bottomRight}`
807
+ );
748
808
  console.log(top);
749
809
  await sleep(20);
750
810
  for (const line of lines) {
@@ -825,35 +885,28 @@ function getRarityColor(rarity) {
825
885
  return colors.muted;
826
886
  }
827
887
  }
888
+ function formatBadgeDate(dateStr) {
889
+ if (!dateStr) return (/* @__PURE__ */ new Date()).toISOString().split("T")[0].replace(/-/g, ".");
890
+ return dateStr.split("T")[0].replace(/-/g, ".");
891
+ }
828
892
  function displayNewBadges(badges) {
829
893
  if (badges.length === 0) return;
830
894
  console.log();
831
- console.log(` ${colors.max("\u{1F389} NEW BADGE" + (badges.length > 1 ? "S" : "") + " UNLOCKED!")}`);
832
- console.log();
833
895
  for (const badge of badges) {
834
896
  const rarityColor = getRarityColor(badge.rarity);
835
897
  const rarityLabel = badge.rarity.toUpperCase();
836
- console.log(` ${badge.icon} ${colors.white.bold(badge.name)} ${rarityColor(`[${rarityLabel}]`)}`);
837
- console.log(` ${colors.muted(badge.description)}`);
898
+ console.log(
899
+ ` ${badge.icon} ${colors.white.bold(badge.name)} ${rarityColor(`[${rarityLabel}]`)}`
900
+ );
901
+ console.log(` ${colors.muted(badge.description)}`);
838
902
  if (badge.praise) {
839
- console.log(` ${colors.cyan(`"${badge.praise}"`)}`);
903
+ console.log(` ${colors.cyan(`"${badge.praise}"`)}`);
904
+ }
905
+ if (badge.category === "rank") {
906
+ console.log(` ${colors.dim(`\u{1F4C5} Achieved: ${formatBadgeDate(badge.earnedAt)}`)}`);
840
907
  }
841
- console.log();
842
908
  }
843
909
  }
844
- function displayBadgeProgress(progress, totalBadges) {
845
- if (progress.length === 0) return;
846
- console.log(` ${colors.muted("Badge Progress")} ${colors.dim(`(${totalBadges} earned)`)}`);
847
- for (const p of progress) {
848
- const barWidth = 16;
849
- const filled = Math.round(p.percent / 100 * barWidth);
850
- const empty = barWidth - filled;
851
- const bar = colors.primary("\u2588".repeat(filled)) + colors.dim("\u2591".repeat(empty));
852
- const badgeName = p.id.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
853
- console.log(` ${colors.dim("\u2022")} ${colors.muted(badgeName.padEnd(18))} [${bar}] ${colors.white(`${p.percent}%`)}`);
854
- }
855
- console.log();
856
- }
857
910
  async function submit(options) {
858
911
  console.log(header("Submit Usage Data", "\u{1F4E4}"));
859
912
  if (!isAuthenticated()) {
@@ -870,8 +923,10 @@ async function submit(options) {
870
923
  let usageData = null;
871
924
  const totalFiles = getSessionFileCount();
872
925
  if (totalFiles > 0) {
873
- console.log(`
874
- ${colors.muted("Scanning")} ${colors.white(totalFiles.toString())} ${colors.muted("sessions...")}`);
926
+ console.log(
927
+ `
928
+ ${colors.muted("Scanning")} ${colors.white(totalFiles.toString())} ${colors.muted("sessions...")}`
929
+ );
875
930
  const scannedData = scanUsageData({
876
931
  onProgress: (current, total) => {
877
932
  progressBar(current, total, "Scanning");
@@ -932,32 +987,57 @@ async function submit(options) {
932
987
  if (result.success) {
933
988
  submitSpinner.succeed(colors.success("Successfully submitted!"));
934
989
  console.log();
990
+ const sectionHeader = (icon, title) => {
991
+ const text = `${icon} ${title} `;
992
+ const lineLength = 40 - text.length;
993
+ return ` ${colors.white.bold(text)}${colors.dim("\u2500".repeat(Math.max(0, lineLength)))}`;
994
+ };
995
+ if (result.rank || result.countryRank) {
996
+ console.log();
997
+ console.log(sectionHeader("\u{1F4CA}", "Your Ranking"));
998
+ console.log();
999
+ if (result.rank) {
1000
+ const medal = result.rank === 1 ? "\u{1F947}" : result.rank === 2 ? "\u{1F948}" : result.rank === 3 ? "\u{1F949}" : result.rank <= 10 ? "\u{1F3C5}" : "\u{1F30D}";
1001
+ console.log(
1002
+ ` ${medal} ${colors.muted("Global:")} ${colors.primary.bold(`#${result.rank}`)}`
1003
+ );
1004
+ }
1005
+ if (result.countryRank) {
1006
+ const countryMedal = result.countryRank === 1 ? "\u{1F947}" : result.countryRank <= 3 ? "\u{1F3C6}" : "\u{1F3E0}";
1007
+ console.log(
1008
+ ` ${countryMedal} ${colors.muted("Country:")} ${colors.primary.bold(`#${result.countryRank}`)}`
1009
+ );
1010
+ }
1011
+ }
1012
+ console.log();
1013
+ console.log(sectionHeader("\u2B06\uFE0F", "Level Progress"));
1014
+ console.log();
1015
+ const levelProgress = getLevelProgress(usageData.totalTokens);
1016
+ const currentLevel = levelProgress.current;
1017
+ console.log(
1018
+ ` ${currentLevel.icon} ${currentLevel.color(`Level ${currentLevel.level}`)} ${colors.muted("\u2022")} ${colors.white(currentLevel.name)}`
1019
+ );
1020
+ if (!levelProgress.isMaxLevel && levelProgress.next) {
1021
+ const barWidth = 20;
1022
+ const filled = Math.round(levelProgress.progress / 100 * barWidth);
1023
+ const empty = barWidth - filled;
1024
+ const bar = colors.primary("\u2588".repeat(filled)) + colors.dim("\u2591".repeat(empty));
1025
+ console.log(` [${bar}] ${colors.white(`${levelProgress.progress}%`)}`);
1026
+ console.log(
1027
+ ` ${colors.dim("\u2192")} ${levelProgress.next.icon} ${colors.white(levelProgress.next.name)} ${colors.muted("in")} ${colors.primary(formatNumber(levelProgress.tokensToNext))}`
1028
+ );
1029
+ } else {
1030
+ console.log(` ${colors.max("\u2605")} ${colors.max("MAX LEVEL ACHIEVED!")}`);
1031
+ }
935
1032
  if (result.newBadges && result.newBadges.length > 0) {
1033
+ console.log();
1034
+ console.log(sectionHeader("\u{1F389}", "New Badge Unlocked"));
936
1035
  displayNewBadges(result.newBadges);
937
1036
  }
938
- if (result.badgeProgress && result.badgeProgress.length > 0) {
939
- displayBadgeProgress(result.badgeProgress, result.totalBadges || 0);
940
- }
941
- const levelInfo = getLevelInfo(usageData.totalTokens);
1037
+ console.log();
942
1038
  const leaderboardUrl = `https://ccgather.com/leaderboard?u=${username}`;
943
- const successLines = [
944
- `${colors.success("\u2713")} ${colors.white.bold("Submission Complete!")}`,
945
- "",
946
- `${levelInfo.icon} ${levelInfo.color(`Level ${levelInfo.level}`)} ${colors.muted("\u2022")} ${colors.white(levelInfo.name)}`,
947
- ""
948
- ];
949
- if (result.rank) {
950
- successLines.push(`${colors.muted("Global Rank:")} ${colors.primary(`#${result.rank}`)}`);
951
- }
952
- if (result.countryRank) {
953
- successLines.push(`${colors.muted("Country Rank:")} ${colors.primary(`#${result.countryRank}`)}`);
954
- }
955
- if (result.rank || result.countryRank) {
956
- successLines.push("");
957
- }
958
- successLines.push(`${colors.muted("Check your rank on the leaderboard:")}`);
959
- successLines.push(`${link(leaderboardUrl)}`);
960
- console.log(createBox(successLines));
1039
+ console.log(` ${colors.dim("\u2500".repeat(40))}`);
1040
+ console.log(` ${colors.muted("View full stats:")} ${link(leaderboardUrl)}`);
961
1041
  console.log();
962
1042
  } else {
963
1043
  submitSpinner.fail(colors.error("Failed to submit"));
@@ -1039,7 +1119,9 @@ async function status(options) {
1039
1119
  if (stats.countryRank && stats.countryCode) {
1040
1120
  const countryFlag = countryCodeToFlag(stats.countryCode);
1041
1121
  const countryMedal = getRankMedal(stats.countryRank);
1042
- console.log(` ${countryFlag} ${colors.muted("Country")} ${countryMedal} ${colors.white.bold(`#${stats.countryRank}`)}`);
1122
+ console.log(
1123
+ ` ${countryFlag} ${colors.muted("Country")} ${countryMedal} ${colors.white.bold(`#${stats.countryRank}`)}`
1124
+ );
1043
1125
  }
1044
1126
  console.log(` ${colors.dim(`Top ${stats.percentile.toFixed(1)}% globally`)}`);
1045
1127
  console.log();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccgather",
3
- "version": "1.3.24",
3
+ "version": "1.3.26",
4
4
  "description": "CLI tool for syncing Claude Code usage data to CCgather leaderboard",
5
5
  "bin": {
6
6
  "ccgather": "dist/index.js",