ccstatusline-usage 2.3.1 → 2.3.3

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/README.md CHANGED
@@ -34,13 +34,14 @@ This fork adds API-based usage widgets beyond the upstream:
34
34
  - **Weekly Pace** - Pendulum bar showing if you're ahead or behind expected usage pace
35
35
  - **Reset Timer** - Time until 5-hour session window resets
36
36
  - **Context Window Display** - Visual bar showing context usage
37
+ - **Off Peak** - Shows peak/off-peak 2x status during Anthropic usage promotions
37
38
  - **Two-line Layout** - Session info on line 1, context on line 2
38
39
 
39
40
  ### Enhanced Status Line Preview
40
41
 
41
42
  ```
42
43
  Session: [████░░░░░░░░░░░] 27.0% | Weekly: [███████████████] 100.0% | Extra: €2.50/€50.00 | Model: Opus 4.6 | Session ID: 0109b99d...
43
- Context: [███████░░░░░░░░] 103k/200k (51%) | Pace: [░░░░░░░|██░░░░░] D5/7 +12%
44
+ Context: [███████░░░░░░░░] 103k/200k (51%) | Pace: [░░░░░░░|██░░░░░] D5/7 +12% | Off peak: Off-peak 2x
44
45
  ```
45
46
 
46
47
  ![Demo](https://raw.githubusercontent.com/sirmalloc/ccstatusline/main/screenshots/demo.gif)
@@ -65,6 +66,17 @@ Session: [████░░░░░░░░░░░] 27.0% | Weekly: [██
65
66
 
66
67
  ## 🆕 Recent Updates
67
68
 
69
+ ### [v2.3.3](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.3.3) - CI pipeline fix
70
+
71
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **CI pipeline fix** — Fixed TypeScript errors (model union type narrowing, missing `extraUsageBalance` setting), disabled broken `import/order` ESLint rule (incompatible with ESLint 10), resolved all remaining lint errors in fork-specific files
72
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Release workflow** — Added mandatory CI pipeline check before tagging/releasing
73
+
74
+ ### [v2.3.2](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.3.2) - Off Peak widget + WeeklyPace compact fix
75
+
76
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Off Peak widget** — New widget showing `Off peak: Off-peak 2x` or `Off peak: Peak` during the Anthropic March 2026 Spring Break promotion (off-peak = weekdays outside 8 AM–2 PM ET, weekends all day). Widget automatically hides after March 28, 2026 23:59 PT.
77
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **WeeklyPace compact fix** — Pendulum bar now falls back to text mode (`D6/7: -18%`) on narrow terminals (< 80 chars)
78
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Sonnet [1m] extra usage fix** — Extra usage spending now shown when a charged `[1m]` model (e.g. Sonnet) is active, regardless of weekly limit status
79
+
68
80
  ### [v2.3.1](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.3.1) - Weekly Pace widget
69
81
 
70
82
  - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Weekly Pace widget** — New widget showing if weekly usage pace is on track, with pendulum bar or text display mode (PR #1 by @BenIsLegit)
@@ -550,6 +562,7 @@ bun run example
550
562
  - **Skills** - Shows skill activity as last used, total count, or unique list (with optional list limit and hide-when-empty toggle)
551
563
  - **Thinking Effort** - Shows the current Claude Code thinking effort level
552
564
  - **Vim Mode** - Displays current vim editor mode
565
+ - **Off Peak** *(ccstatusline-usage)* - Shows `Off peak: Off-peak 2x` or `Off peak: Peak` during Anthropic usage promotions (auto-hides when promotion ends)
553
566
  - **Separator** - Visual divider between widgets (available when Powerline mode is off and no default separator is configured)
554
567
  - **Flex Separator** - Expands to fill available space (available when Powerline mode is off)
555
568
 
@@ -52428,7 +52428,9 @@ var init_Settings = __esm(() => {
52428
52428
  { id: "session-id", type: "claude-session-id", color: "cyan" }
52429
52429
  ],
52430
52430
  [
52431
- { id: "context-bar", type: "context-bar", color: "blue" }
52431
+ { id: "context-bar", type: "context-bar", color: "blue" },
52432
+ { id: "sep-off-peak", type: "separator" },
52433
+ { id: "off-peak", type: "off-peak", color: "green" }
52432
52434
  ],
52433
52435
  []
52434
52436
  ]),
@@ -52450,6 +52452,7 @@ var init_Settings = __esm(() => {
52450
52452
  theme: undefined,
52451
52453
  autoAlign: false
52452
52454
  }),
52455
+ extraUsageBalance: exports_external.number().optional(),
52453
52456
  updatemessage: exports_external.object({
52454
52457
  message: exports_external.string().nullable().optional(),
52455
52458
  remaining: exports_external.number().nullable().optional()
@@ -54069,7 +54072,7 @@ function getTerminalWidth() {
54069
54072
  function canDetectTerminalWidth() {
54070
54073
  return probeTerminalWidth() !== null;
54071
54074
  }
54072
- var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils", PACKAGE_VERSION = "2.3.1";
54075
+ var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils", PACKAGE_VERSION = "2.3.3";
54073
54076
  var init_terminal = () => {};
54074
54077
 
54075
54078
  // src/utils/renderer.ts
@@ -61429,7 +61432,7 @@ import * as path7 from "path";
61429
61432
  function readTokenFromFile() {
61430
61433
  try {
61431
61434
  const creds = JSON.parse(fs9.readFileSync(CRED_FILE, "utf8"));
61432
- return creds?.claudeAiOauth?.accessToken ?? null;
61435
+ return creds.claudeAiOauth?.accessToken ?? null;
61433
61436
  } catch {
61434
61437
  return null;
61435
61438
  }
@@ -61438,7 +61441,7 @@ function readTokenFromKeychain() {
61438
61441
  try {
61439
61442
  const result2 = execSync5('security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null', { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
61440
61443
  const parsed = JSON.parse(result2);
61441
- return parsed?.claudeAiOauth?.accessToken ?? null;
61444
+ return parsed.claudeAiOauth?.accessToken ?? null;
61442
61445
  } catch {
61443
61446
  return null;
61444
61447
  }
@@ -61455,8 +61458,7 @@ function getToken() {
61455
61458
  let token = null;
61456
61459
  if (process.platform === "darwin")
61457
61460
  token = readTokenFromKeychain();
61458
- if (!token)
61459
- token = readTokenFromFile();
61461
+ token ??= readTokenFromFile();
61460
61462
  if (token) {
61461
61463
  cachedToken = token;
61462
61464
  tokenCacheTime = now2;
@@ -61743,7 +61745,12 @@ class ResetTimerWidget {
61743
61745
  const data = fetchApiData();
61744
61746
  if (data.error)
61745
61747
  return getErrorMessage(data.error);
61746
- if (data.extraUsageEnabled && data.weeklyUsage !== undefined && data.weeklyUsage >= 100 && data.extraUsageUsed !== undefined && data.extraUsageLimit !== undefined) {
61748
+ const model = context.data?.model;
61749
+ const modelId = (typeof model === "string" ? model : model?.id) ?? "";
61750
+ const is1mModel = modelId.includes("[1m]");
61751
+ const isOpus = modelId.includes("opus");
61752
+ const isChargedModel = is1mModel && !isOpus;
61753
+ if (data.extraUsageEnabled && data.extraUsageUsed !== undefined && data.extraUsageLimit !== undefined && (data.weeklyUsage !== undefined && data.weeklyUsage >= 100 || isChargedModel)) {
61747
61754
  const used = formatCents(data.extraUsageUsed);
61748
61755
  const displayLimit = settings.extraUsageBalance ?? data.extraUsageLimit;
61749
61756
  const limit = formatCents(displayLimit);
@@ -62569,7 +62576,8 @@ class WeeklyPaceWidget {
62569
62576
  return null;
62570
62577
  const actualPercent = Math.max(0, Math.min(100, data.weeklyUsage));
62571
62578
  const { delta, dayOfWeek, status } = computePace(actualPercent, window2.elapsedPercent);
62572
- if (displayMode === "pendulum") {
62579
+ const mobile = (context.terminalWidth ?? 0) > 0 && (context.terminalWidth ?? 0) < MOBILE_THRESHOLD3;
62580
+ if (displayMode === "pendulum" && !mobile) {
62573
62581
  const sign = delta >= 0 ? "+" : "";
62574
62582
  const barDisplay = `${makePendulumBar(delta)} D${dayOfWeek}/7 ${sign}${Math.round(delta)}%`;
62575
62583
  return formatRawOrLabeledValue(item, "Pace: ", barDisplay);
@@ -62588,10 +62596,91 @@ class WeeklyPaceWidget {
62588
62596
  return true;
62589
62597
  }
62590
62598
  }
62599
+ var MOBILE_THRESHOLD3 = 80;
62591
62600
  var init_WeeklyPace = __esm(() => {
62592
62601
  init_usage();
62593
62602
  });
62594
62603
 
62604
+ // src/widgets/OffPeak.ts
62605
+ function isOffPeak(now2) {
62606
+ const ms = now2.getTime();
62607
+ if (ms < PROMO_START_MS || ms >= PROMO_END_MS) {
62608
+ return null;
62609
+ }
62610
+ const dayOfWeek = now2.getUTCDay();
62611
+ const isWeekend = dayOfWeek === 0 || dayOfWeek === 6;
62612
+ if (isWeekend)
62613
+ return true;
62614
+ const utcHour = now2.getUTCHours();
62615
+ const isPeak = utcHour >= PEAK_START_UTC_HOUR && utcHour < PEAK_END_UTC_HOUR;
62616
+ return !isPeak;
62617
+ }
62618
+ function minutesUntilFlip(now2) {
62619
+ const dayOfWeek = now2.getUTCDay();
62620
+ const isWeekend = dayOfWeek === 0 || dayOfWeek === 6;
62621
+ if (isWeekend)
62622
+ return null;
62623
+ const utcHour = now2.getUTCHours();
62624
+ const isPeak = utcHour >= PEAK_START_UTC_HOUR && utcHour < PEAK_END_UTC_HOUR;
62625
+ const flipHour = isPeak ? PEAK_END_UTC_HOUR : PEAK_START_UTC_HOUR;
62626
+ const target = new Date(now2);
62627
+ if (!isPeak && utcHour >= PEAK_END_UTC_HOUR) {
62628
+ target.setUTCDate(now2.getUTCDate() + 1);
62629
+ }
62630
+ target.setUTCHours(flipHour, 0, 0, 0);
62631
+ return Math.max(0, Math.round((target.getTime() - now2.getTime()) / 60000));
62632
+ }
62633
+ function formatCountdown(minutes) {
62634
+ const h = Math.floor(minutes / 60);
62635
+ const m = minutes % 60;
62636
+ return `${h}:${String(m).padStart(2, "0")}`;
62637
+ }
62638
+
62639
+ class OffPeakWidget {
62640
+ getDefaultColor() {
62641
+ return "green";
62642
+ }
62643
+ getDescription() {
62644
+ return "Shows peak / off-peak 2x status during the March 2026 Anthropic usage promotion";
62645
+ }
62646
+ getDisplayName() {
62647
+ return "Off Peak";
62648
+ }
62649
+ getCategory() {
62650
+ return "Usage";
62651
+ }
62652
+ getEditorDisplay(_item) {
62653
+ return { displayText: this.getDisplayName() };
62654
+ }
62655
+ render(item, context, _settings) {
62656
+ if (context.isPreview) {
62657
+ return item.rawValue ? "Off-peak 2x (3:42)" : "Off-peak 2x (3:42)";
62658
+ }
62659
+ const now2 = new Date;
62660
+ const offPeak = isOffPeak(now2);
62661
+ if (offPeak === null)
62662
+ return null;
62663
+ const mins = minutesUntilFlip(now2);
62664
+ const countdown = mins !== null ? ` (${formatCountdown(mins)} hr)` : "";
62665
+ const mobile = (context.terminalWidth ?? 0) > 0 && (context.terminalWidth ?? 0) < 80;
62666
+ if (offPeak) {
62667
+ return mobile ? `2x${countdown}` : `Off-peak 2x${countdown}`;
62668
+ }
62669
+ return `Peak${countdown}`;
62670
+ }
62671
+ supportsRawValue() {
62672
+ return true;
62673
+ }
62674
+ supportsColors(_item) {
62675
+ return true;
62676
+ }
62677
+ }
62678
+ var PROMO_START_MS, PROMO_END_MS, PEAK_START_UTC_HOUR = 12, PEAK_END_UTC_HOUR = 18;
62679
+ var init_OffPeak = __esm(() => {
62680
+ PROMO_START_MS = Date.UTC(2026, 2, 13, 7, 0, 0);
62681
+ PROMO_END_MS = Date.UTC(2026, 2, 29, 6, 59, 0);
62682
+ });
62683
+
62595
62684
  // src/widgets/index.ts
62596
62685
  var init_widgets = __esm(async () => {
62597
62686
  init_GitBranch();
@@ -62611,6 +62700,7 @@ var init_widgets = __esm(async () => {
62611
62700
  init_Battery();
62612
62701
  init_VimMode();
62613
62702
  init_WeeklyPace();
62703
+ init_OffPeak();
62614
62704
  await __promiseAll([
62615
62705
  init_TokensInput(),
62616
62706
  init_TokensOutput(),
@@ -62671,7 +62761,8 @@ var init_widget_manifest = __esm(async () => {
62671
62761
  { type: "thinking-effort", create: () => new ThinkingEffortWidget },
62672
62762
  { type: "battery", create: () => new BatteryWidget },
62673
62763
  { type: "vim-mode", create: () => new VimModeWidget },
62674
- { type: "weekly-pace", create: () => new WeeklyPaceWidget }
62764
+ { type: "weekly-pace", create: () => new WeeklyPaceWidget },
62765
+ { type: "off-peak", create: () => new OffPeakWidget }
62675
62766
  ];
62676
62767
  LAYOUT_WIDGET_MANIFEST = [
62677
62768
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccstatusline-usage",
3
- "version": "2.3.1",
3
+ "version": "2.3.3",
4
4
  "description": "A customizable status line formatter for Claude Code CLI",
5
5
  "module": "src/ccstatusline.ts",
6
6
  "type": "module",