ccstatusline-usage 2.3.10 → 2.3.12

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
@@ -47,6 +47,7 @@ Session: [████░░░░░░░░░░░] 27.0% | Weekly: [██
47
47
  ![Demo](https://raw.githubusercontent.com/sirmalloc/ccstatusline/main/screenshots/demo.gif)
48
48
 
49
49
  </div>
50
+ <br />
50
51
 
51
52
  ## 📚 Table of Contents
52
53
 
@@ -54,19 +55,29 @@ Session: [████░░░░░░░░░░░] 27.0% | Weekly: [██
54
55
  - [Features](#-features)
55
56
  - [Localizations](#-localizations)
56
57
  - [Quick Start](#-quick-start)
57
- - [Windows Support](#-windows-support)
58
- - [Usage](#-usage)
59
- - [API Documentation](#-api-documentation)
60
- - [Development](#️-development)
58
+ - [Windows Support](docs/WINDOWS.md)
59
+ - [Usage](docs/USAGE.md)
60
+ - [Development](docs/DEVELOPMENT.md)
61
61
  - [Contributing](#-contributing)
62
62
  - [Uninstall](#️-uninstall)
63
63
  - [License](#-license)
64
64
  - [Related Projects](#-related-projects)
65
65
 
66
- ---
66
+ <br />
67
67
 
68
68
  ## 🆕 Recent Updates
69
69
 
70
+ ### [v2.3.12](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.3.12) - Split bar for extra usage, stable effective-total, 100% clamp
71
+
72
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Split bar at 100%** — Session and Weekly usage bars switch to a split display when the limit is reached and extra usage is active: left half (full) shows the base usage, right half (dark red) shows how much of the extra budget has been spent.
73
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Stable effective-total** — Set `extraUsageBalance` (cents) in `~/.config/ccstatusline/settings.json` to your spending ceiling (`spent + account_balance` at config time, e.g. €153.23 + €56.06 → `20929`). The denominator stays fixed as spending continues, capped at the API monthly limit. Without this setting, the monthly limit is used directly.
74
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Extra usage color via config** — The `Extra: €X/€Y` Reset Timer output color is now driven by the widget's color setting (configurable in TUI or settings.json) instead of being hardcoded.
75
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Usage clamped to 100%** — Session and Weekly bars and percentage labels cap at 100% even if the API returns values above 100.
76
+
77
+ ### [v2.3.11](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.3.11) - Fix dual-cache drift between Weekly Usage and Weekly Pace
78
+
79
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Unified usage data source** — Session Usage, Weekly Usage, and Reset Timer widgets now read from the shared `context.usageData` pipeline instead of maintaining a separate API cache. Previously, around the weekly reset boundary the two caches could drift and show contradictory data (e.g. `Weekly: 99%` alongside `Pace: D1/7 +2%`). Deleted ~330 lines of duplicate fetching/caching infrastructure from `ApiUsage.tsx` (556 → 227 lines). Upstream merge: #278 (JSONL token overcounting fix), #283 (model display name parenthetical strip — integrated into fork's existing `[1m]`-aware display logic).
80
+
70
81
  ### [v2.3.10](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.3.10) - Extra usage on session limit, fix empty separator for null widgets
71
82
 
72
83
  - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Extra usage on session limit** — Reset Timer now shows extra usage spending when session limit reaches 100% (not just weekly limit or charged 1M model).
@@ -182,6 +193,10 @@ Session: [████░░░░░░░░░░░] 27.0% | Weekly: [██
182
193
  - **🔗 Git widget link modes (v2.2.6)** - `Git Branch` can render clickable GitHub branch links, and `Git Root Dir` can render clickable IDE links for VS Code and Cursor.
183
194
  - **🤝 Better subagent-aware speed reporting** - Token speed calculations continue to include referenced subagent activity so displayed speeds better reflect actual concurrent work.
184
195
 
196
+ <br />
197
+ <details>
198
+ <summary><b>Older updates (v2.1.10 and earlier)</b></summary>
199
+
185
200
  ### v2.1.0 - v2.1.10 - Usage widgets, links, new git insertions / deletions widgets, and reliability fixes
186
201
 
187
202
  - **🧩 New Usage widgets (v2.1.0)** - Added **Session Usage**, **Weekly Usage**, **Block Reset Timer**, and **Context Bar** widgets.
@@ -278,7 +293,9 @@ Session: [████░░░░░░░░░░░] 27.0% | Weekly: [██
278
293
  - **🔤 Custom Separators** - Add multiple Powerline separators with custom hex codes for font support
279
294
  - **🚀 Auto Font Install** - Automatic Powerline font installation with user consent
280
295
 
281
- ---
296
+ </details>
297
+
298
+ <br />
282
299
 
283
300
  ## ✨ Features
284
301
 
@@ -290,13 +307,13 @@ Session: [████░░░░░░░░░░░] 27.0% | Weekly: [██
290
307
  - **📐 Multi-line Support** - Configure multiple independent status lines
291
308
  - **🖥️ Interactive TUI** - Built-in configuration interface using React/Ink
292
309
  - **🔎 Fast Widget Picker** - Add/change widgets by category with search and ranked matching
293
- - **⚙️ Global Options** - Apply consistent formatting across all widgets (padding, separators, bold, background)
310
+ - **⚙️ Global Options** - Apply consistent formatting across all widgets (padding, separators, bold, minimalist mode, and color overrides)
294
311
  - **🚀 Cross-platform** - Works seamlessly with both Bun and Node.js
295
312
  - **🔧 Flexible Configuration** - Supports custom Claude Code config directory via `CLAUDE_CONFIG_DIR` environment variable
296
313
  - **📏 Smart Width Detection** - Automatically adapts to terminal width with flex separators
297
314
  - **⚡ Zero Config** - Sensible defaults that work out of the box
298
315
 
299
- ---
316
+ <br />
300
317
 
301
318
  ## 🌐 Localizations
302
319
 
@@ -304,7 +321,7 @@ The localizations in this section are third-party forks maintained outside this
304
321
 
305
322
  - 🌏 **中文版 (Chinese):** [ccstatusline-zh](https://github.com/huangguang1999/ccstatusline-zh)
306
323
 
307
- ---
324
+ <br />
308
325
 
309
326
  ## 🚀 Quick Start
310
327
 
@@ -318,7 +335,9 @@ npx -y ccstatusline-usage@latest
318
335
  bunx -y ccstatusline-usage@latest
319
336
  ```
320
337
 
321
- ### Configure ccstatusline
338
+ <br />
339
+ <details>
340
+ <summary><b>Configure ccstatusline</b></summary>
322
341
 
323
342
  The interactive configuration tool provides a terminal UI where you can:
324
343
  - Configure multiple separate status lines
@@ -335,14 +354,16 @@ The interactive configuration tool provides a terminal UI where you can:
335
354
  > ```bash
336
355
  > # Linux/macOS
337
356
  > export CLAUDE_CONFIG_DIR=/custom/path/to/.claude
338
- >
339
- > # Windows PowerShell
340
- > $env:CLAUDE_CONFIG_DIR="C:\custom\path\.claude"
341
357
  > ```
342
358
 
343
359
  > 🌐 **Usage API proxy:** Usage widgets honor the uppercase `HTTPS_PROXY` environment variable for their direct API call to Anthropic.
344
360
 
345
- ### Claude Code settings.json format
361
+ > 🪟 **Windows Support:** PowerShell examples, installation notes, fonts, troubleshooting, WSL, and Windows Terminal configuration are in [docs/WINDOWS.md](docs/WINDOWS.md).
362
+
363
+ </details>
364
+
365
+ <details>
366
+ <summary><b>Claude Code settings.json format</b></summary>
346
367
 
347
368
  When you install from the TUI, ccstatusline writes a `statusLine` command object to your Claude Code settings:
348
369
 
@@ -894,7 +915,6 @@ Contributions are welcome! Please feel free to submit a Pull Request.
894
915
  4. Push to the branch (`git push origin feature/amazing-feature`)
895
916
  5. Open a Pull Request
896
917
 
897
- ---
898
918
 
899
919
  ## 🗑️ Uninstall
900
920
 
@@ -904,13 +924,11 @@ rm -rf ~/.npm/_npx ~/.config/ccstatusline ~/.cache/ccstatusline*
904
924
  jq 'del(.statusLine)' ~/.claude/settings.json > /tmp/cs.json && cat /tmp/cs.json > ~/.claude/settings.json
905
925
  ```
906
926
 
907
- ---
908
927
 
909
928
  ## 📄 License
910
929
 
911
930
  [MIT](LICENSE) © Matthew Breedlove
912
931
 
913
- ---
914
932
 
915
933
  ## 👤 Author
916
934
 
@@ -930,7 +948,6 @@ jq 'del(.statusLine)' ~/.claude/settings.json > /tmp/cs.json && cat /tmp/cs.json
930
948
  - [ccusage](https://github.com/ryoppippi/ccusage) - Track and display Claude Code usage metrics.
931
949
  - [codachi](https://github.com/vincent-k2026/codachi) - A tamagotchi-style statusline pet that grows with your context window.
932
950
 
933
- ---
934
951
 
935
952
  ## 🙏 Acknowledgments
936
953
 
@@ -938,7 +955,7 @@ jq 'del(.statusLine)' ~/.claude/settings.json > /tmp/cs.json && cat /tmp/cs.json
938
955
  - Powered by [Ink](https://github.com/vadimdemedes/ink) for the terminal UI
939
956
  - Made with ❤️ for the Claude Code community
940
957
 
941
- ---
958
+ <br />
942
959
 
943
960
  ## Star History
944
961
 
@@ -52660,7 +52660,7 @@ class ModelWidget {
52660
52660
  return null;
52661
52661
  const is1m = modelId?.includes("[1m]") ?? false;
52662
52662
  const suffix = is1m ? "[1m]" : "";
52663
- const cleanDisplayName = modelDisplayName.replace(/\[1m\]/gi, "").replace(/\(1M context\)/gi, "").trim();
52663
+ const cleanDisplayName = modelDisplayName.replace(/\[1m\]/gi, "").replace(/\s*\(.*\)$/, "").trim();
52664
52664
  const mobile = (context.terminalWidth ?? 0) > 0 && (context.terminalWidth ?? 0) < MEDIUM_THRESHOLD;
52665
52665
  if (mobile && modelId) {
52666
52666
  return `M: ${compactModelName(modelId)}${suffix}`;
@@ -55593,7 +55593,7 @@ function getTerminalWidth() {
55593
55593
  function canDetectTerminalWidth() {
55594
55594
  return probeTerminalWidth() !== null;
55595
55595
  }
55596
- var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils", PACKAGE_VERSION = "2.3.10";
55596
+ var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils", PACKAGE_VERSION = "2.3.12";
55597
55597
  var init_terminal = () => {};
55598
55598
 
55599
55599
  // src/utils/renderer.ts
@@ -61697,19 +61697,35 @@ async function getTokenMetrics(transcriptPath) {
61697
61697
  let contextLength = 0;
61698
61698
  let mostRecentMainChainEntry = null;
61699
61699
  let mostRecentTimestamp = null;
61700
+ const parsedEntries = [];
61701
+ let hasStopReasonField = false;
61700
61702
  for (const line of lines) {
61701
61703
  const data = parseJsonlLine(line);
61702
61704
  if (data?.message?.usage) {
61703
- inputTokens += data.message.usage.input_tokens || 0;
61704
- outputTokens += data.message.usage.output_tokens || 0;
61705
- cachedTokens += data.message.usage.cache_read_input_tokens ?? 0;
61706
- cachedTokens += data.message.usage.cache_creation_input_tokens ?? 0;
61707
- if (data.isSidechain !== true && data.timestamp && !data.isApiErrorMessage) {
61708
- const entryTime = new Date(data.timestamp);
61709
- if (!mostRecentTimestamp || entryTime > mostRecentTimestamp) {
61710
- mostRecentTimestamp = entryTime;
61711
- mostRecentMainChainEntry = data;
61712
- }
61705
+ parsedEntries.push(data);
61706
+ if (Object.hasOwn(data.message, "stop_reason")) {
61707
+ hasStopReasonField = true;
61708
+ }
61709
+ }
61710
+ }
61711
+ const entriesToCount = hasStopReasonField ? parsedEntries.filter((data, index) => {
61712
+ const stopReason = data.message?.stop_reason;
61713
+ return Boolean(stopReason) || stopReason === null && index === parsedEntries.length - 1;
61714
+ }) : parsedEntries;
61715
+ for (const data of entriesToCount) {
61716
+ const usage = data.message?.usage;
61717
+ if (!usage) {
61718
+ continue;
61719
+ }
61720
+ inputTokens += usage.input_tokens || 0;
61721
+ outputTokens += usage.output_tokens || 0;
61722
+ cachedTokens += usage.cache_read_input_tokens ?? 0;
61723
+ cachedTokens += usage.cache_creation_input_tokens ?? 0;
61724
+ if (data.isSidechain !== true && data.timestamp && !data.isApiErrorMessage) {
61725
+ const entryTime = new Date(data.timestamp);
61726
+ if (!mostRecentTimestamp || entryTime > mostRecentTimestamp) {
61727
+ mostRecentTimestamp = entryTime;
61728
+ mostRecentMainChainEntry = data;
61713
61729
  }
61714
61730
  }
61715
61731
  }
@@ -62114,6 +62130,13 @@ function getUsageErrorMessage(error48) {
62114
62130
  return "[Parse Error]";
62115
62131
  }
62116
62132
  }
62133
+ function makeSplitUsageBar(extraPercent, totalWidth) {
62134
+ const halfWidth = Math.floor((totalWidth - 1) / 2);
62135
+ const clamped = Math.max(0, Math.min(100, extraPercent));
62136
+ const rightFilled = Math.round(clamped / 100 * halfWidth);
62137
+ const rightEmpty = halfWidth - rightFilled;
62138
+ return "[" + "█".repeat(halfWidth) + "|" + DARK_RED_OPEN + "█".repeat(rightFilled) + "░".repeat(rightEmpty) + DARK_RED_CLOSE + "]";
62139
+ }
62117
62140
  function makePendulumBar(delta, halfWidth = 7) {
62118
62141
  const clamped = Math.max(-100, Math.min(100, delta));
62119
62142
  const fill2 = Math.min(halfWidth, Math.round(Math.abs(clamped) / 100 * halfWidth));
@@ -62122,6 +62145,7 @@ function makePendulumBar(delta, halfWidth = 7) {
62122
62145
  }
62123
62146
  return "[" + "░".repeat(halfWidth) + "|" + "█".repeat(fill2) + "░".repeat(halfWidth - fill2) + "]";
62124
62147
  }
62148
+ var DARK_RED_OPEN = "\x1B[38;2;204;0;0m", DARK_RED_CLOSE = "\x1B[39m";
62125
62149
  var init_usage_windows = __esm(() => {
62126
62150
  init_jsonl();
62127
62151
  init_usage_types();
@@ -63108,226 +63132,6 @@ class SessionNameWidget {
63108
63132
  var init_SessionName = () => {};
63109
63133
 
63110
63134
  // src/widgets/ApiUsage.tsx
63111
- import {
63112
- execSync as execSync5,
63113
- spawnSync
63114
- } from "child_process";
63115
- import * as fs10 from "fs";
63116
- import * as os8 from "os";
63117
- import * as path9 from "path";
63118
- function readTokenFromFile() {
63119
- try {
63120
- const creds = JSON.parse(fs10.readFileSync(CRED_FILE, "utf8"));
63121
- return creds.claudeAiOauth?.accessToken ?? null;
63122
- } catch {
63123
- return null;
63124
- }
63125
- }
63126
- function readTokenFromKeychain() {
63127
- try {
63128
- const result2 = execSync5('security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null', { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
63129
- const parsed = JSON.parse(result2);
63130
- return parsed.claudeAiOauth?.accessToken ?? null;
63131
- } catch {
63132
- return null;
63133
- }
63134
- }
63135
- function invalidateTokenCache() {
63136
- cachedToken = null;
63137
- tokenCacheTime = 0;
63138
- }
63139
- function getToken() {
63140
- const now2 = Math.floor(Date.now() / 1000);
63141
- if (cachedToken && now2 - tokenCacheTime < TOKEN_CACHE_MAX_AGE) {
63142
- return cachedToken;
63143
- }
63144
- let token = null;
63145
- if (process.platform === "darwin")
63146
- token = readTokenFromKeychain();
63147
- token ??= readTokenFromFile();
63148
- if (token) {
63149
- cachedToken = token;
63150
- tokenCacheTime = now2;
63151
- }
63152
- return token;
63153
- }
63154
- function readStaleCache() {
63155
- try {
63156
- return JSON.parse(fs10.readFileSync(CACHE_FILE2, "utf8"));
63157
- } catch {
63158
- return null;
63159
- }
63160
- }
63161
- function fetchFromApi(token) {
63162
- const script = `
63163
- const https = require('https');
63164
- const options = {
63165
- hostname: 'api.anthropic.com',
63166
- path: '/api/oauth/usage',
63167
- method: 'GET',
63168
- headers: {
63169
- 'Authorization': 'Bearer ' + process.env.TOKEN,
63170
- 'anthropic-beta': 'oauth-2025-04-20'
63171
- },
63172
- timeout: 5000
63173
- };
63174
- const req = https.request(options, (res) => {
63175
- let data = '';
63176
- res.on('data', chunk => data += chunk);
63177
- res.on('end', () => {
63178
- if (res.statusCode === 200) {
63179
- process.stdout.write(data);
63180
- } else if (res.statusCode === 401) {
63181
- process.exit(2);
63182
- } else if (res.statusCode === 429) {
63183
- process.exit(3);
63184
- } else {
63185
- process.exit(1);
63186
- }
63187
- });
63188
- });
63189
- req.on('error', () => process.exit(1));
63190
- req.on('timeout', () => { req.destroy(); process.exit(1); });
63191
- req.end();
63192
- `;
63193
- const result2 = spawnSync("node", ["-e", script], {
63194
- encoding: "utf8",
63195
- timeout: 6000,
63196
- env: { ...process.env, TOKEN: token }
63197
- });
63198
- if (result2.error || !result2.stdout) {
63199
- if (result2.status === 2)
63200
- return "auth-error";
63201
- if (result2.status === 3)
63202
- return "rate-limited";
63203
- return null;
63204
- }
63205
- if (result2.status !== 0) {
63206
- if (result2.status === 2)
63207
- return "auth-error";
63208
- if (result2.status === 3)
63209
- return "rate-limited";
63210
- return null;
63211
- }
63212
- return result2.stdout;
63213
- }
63214
- function fetchApiData() {
63215
- const now2 = Math.floor(Date.now() / 1000);
63216
- if (cachedData && !cachedData.error && now2 - cacheTime < CACHE_MAX_AGE2) {
63217
- return cachedData;
63218
- }
63219
- try {
63220
- const stat = fs10.statSync(CACHE_FILE2);
63221
- const fileAge = now2 - Math.floor(stat.mtimeMs / 1000);
63222
- if (fileAge < CACHE_MAX_AGE2) {
63223
- const fileData = JSON.parse(fs10.readFileSync(CACHE_FILE2, "utf8"));
63224
- if (!fileData.error) {
63225
- cachedData = fileData;
63226
- cacheTime = now2;
63227
- return fileData;
63228
- }
63229
- }
63230
- } catch {}
63231
- try {
63232
- const lockStat = fs10.statSync(LOCK_FILE2);
63233
- const lockAge = now2 - Math.floor(lockStat.mtimeMs / 1000);
63234
- if (lockAge < LOCK_MAX_AGE2) {
63235
- const stale = readStaleCache();
63236
- if (stale && !stale.error)
63237
- return stale;
63238
- return { error: "timeout" };
63239
- }
63240
- } catch {}
63241
- try {
63242
- const lockDir = path9.dirname(LOCK_FILE2);
63243
- if (!fs10.existsSync(lockDir)) {
63244
- fs10.mkdirSync(lockDir, { recursive: true });
63245
- }
63246
- fs10.writeFileSync(LOCK_FILE2, "");
63247
- } catch {}
63248
- const token = getToken();
63249
- if (!token) {
63250
- const stale = readStaleCache();
63251
- if (stale && !stale.error)
63252
- return stale;
63253
- return { error: "no-credentials" };
63254
- }
63255
- try {
63256
- const response = fetchFromApi(token);
63257
- if (response === "auth-error") {
63258
- invalidateTokenCache();
63259
- const stale = readStaleCache();
63260
- if (stale && !stale.error)
63261
- return stale;
63262
- return { error: "api-error" };
63263
- }
63264
- if (response === "rate-limited") {
63265
- try {
63266
- const futureTime = new Date(Date.now() + 90000);
63267
- fs10.utimesSync(LOCK_FILE2, futureTime, futureTime);
63268
- } catch {}
63269
- const stale = readStaleCache();
63270
- if (stale && !stale.error)
63271
- return stale;
63272
- return { error: "timeout" };
63273
- }
63274
- if (!response) {
63275
- const stale = readStaleCache();
63276
- if (stale && !stale.error)
63277
- return stale;
63278
- return { error: "api-error" };
63279
- }
63280
- const data = JSON.parse(response);
63281
- const apiData = {};
63282
- if (data.five_hour) {
63283
- apiData.sessionUsage = data.five_hour.utilization;
63284
- apiData.sessionResetAt = data.five_hour.resets_at;
63285
- }
63286
- if (data.seven_day) {
63287
- apiData.weeklyUsage = data.seven_day.utilization;
63288
- }
63289
- if (data.extra_usage) {
63290
- apiData.extraUsageEnabled = data.extra_usage.is_enabled === true;
63291
- apiData.extraUsageLimit = data.extra_usage.monthly_limit;
63292
- apiData.extraUsageUsed = data.extra_usage.used_credits;
63293
- apiData.extraUsageUtilization = data.extra_usage.utilization;
63294
- }
63295
- if (apiData.sessionUsage === undefined && apiData.weeklyUsage === undefined) {
63296
- const stale = readStaleCache();
63297
- if (stale && !stale.error)
63298
- return stale;
63299
- return { error: "parse-error" };
63300
- }
63301
- apiData.fetchedAt = now2;
63302
- try {
63303
- const cacheDir = path9.dirname(CACHE_FILE2);
63304
- if (!fs10.existsSync(cacheDir)) {
63305
- fs10.mkdirSync(cacheDir, { recursive: true });
63306
- }
63307
- fs10.writeFileSync(CACHE_FILE2, JSON.stringify(apiData));
63308
- } catch {}
63309
- cachedData = apiData;
63310
- cacheTime = now2;
63311
- return apiData;
63312
- } catch {
63313
- const stale = readStaleCache();
63314
- if (stale && !stale.error)
63315
- return stale;
63316
- return { error: "parse-error" };
63317
- }
63318
- }
63319
- function getErrorMessage(error48) {
63320
- switch (error48) {
63321
- case "no-credentials":
63322
- return "[No credentials]";
63323
- case "timeout":
63324
- return "[Timeout]";
63325
- case "api-error":
63326
- return "[API Error]";
63327
- case "parse-error":
63328
- return "[Parse Error]";
63329
- }
63330
- }
63331
63135
  function getDisplaySize(context) {
63332
63136
  const w = context.terminalWidth ?? 0;
63333
63137
  if (w > 0 && w < MOBILE_THRESHOLD)
@@ -63344,13 +63148,40 @@ function getBarWidth(size2) {
63344
63148
  return DEFAULT_BAR_WIDTH;
63345
63149
  }
63346
63150
  function makeProgressBar(percent, width = DEFAULT_BAR_WIDTH) {
63347
- const filled = Math.round(percent / 100 * width);
63151
+ const clamped = Math.min(100, Math.max(0, percent));
63152
+ const filled = Math.round(clamped / 100 * width);
63348
63153
  const empty2 = width - filled;
63349
63154
  return "[" + "█".repeat(filled) + "░".repeat(empty2) + "]";
63350
63155
  }
63351
63156
  function formatUsageBar(label, shortLabel, percent, size2) {
63352
63157
  const bar = makeProgressBar(percent, getBarWidth(size2));
63353
- return `${size2 === "mobile" ? shortLabel : label}: ${bar} ${percent.toFixed(1)}%`;
63158
+ const display = Math.min(100, percent);
63159
+ return `${size2 === "mobile" ? shortLabel : label}: ${bar} ${display.toFixed(1)}%`;
63160
+ }
63161
+ function formatSplitUsageBar(label, shortLabel, extraPercent, size2) {
63162
+ const bar = makeSplitUsageBar(extraPercent, getBarWidth(size2));
63163
+ return `${size2 === "mobile" ? shortLabel : label}: ${bar} 100.0%`;
63164
+ }
63165
+ function computeEffectiveTotal(extraUsed, extraLimit, ceiling) {
63166
+ if (ceiling === undefined)
63167
+ return extraLimit;
63168
+ return Math.min(ceiling, extraLimit);
63169
+ }
63170
+ function computeExtraPercent(extraUsed, extraLimit, ceiling) {
63171
+ const denominator = computeEffectiveTotal(extraUsed, extraLimit, ceiling);
63172
+ return denominator > 0 ? extraUsed / denominator * 100 : 0;
63173
+ }
63174
+ function getCurrencySymbol() {
63175
+ try {
63176
+ const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
63177
+ if (tz.startsWith("Europe/"))
63178
+ return "€";
63179
+ } catch {}
63180
+ return "$";
63181
+ }
63182
+ function formatCents(cents) {
63183
+ const symbol2 = getCurrencySymbol();
63184
+ return `${symbol2}${(cents / 100).toFixed(2)}`;
63354
63185
  }
63355
63186
 
63356
63187
  class SessionUsageWidget {
@@ -63366,23 +63197,30 @@ class SessionUsageWidget {
63366
63197
  getCategory() {
63367
63198
  return "API Usage";
63368
63199
  }
63369
- getEditorDisplay(item) {
63200
+ getEditorDisplay(_item) {
63370
63201
  return { displayText: this.getDisplayName() };
63371
63202
  }
63372
- render(item, context, settings) {
63203
+ render(_item, context, settings) {
63373
63204
  if (context.isPreview)
63374
63205
  return "Session: [███░░░░░░░░░░░░] 20%";
63375
- const data = fetchApiData();
63206
+ const data = context.usageData ?? {};
63376
63207
  if (data.error)
63377
- return getErrorMessage(data.error);
63208
+ return getUsageErrorMessage(data.error);
63378
63209
  if (data.sessionUsage === undefined)
63379
63210
  return null;
63380
- return formatUsageBar("Session", "S", data.sessionUsage, getDisplaySize(context));
63211
+ const size2 = getDisplaySize(context);
63212
+ const extraUsed = data.extraUsageUsed;
63213
+ const extraLimit = data.extraUsageLimit;
63214
+ if (size2 !== "mobile" && data.extraUsageEnabled === true && extraUsed !== undefined && extraLimit !== undefined && data.sessionUsage >= 100 && (data.weeklyUsage === undefined || data.weeklyUsage < 100)) {
63215
+ const extraPercent = computeExtraPercent(extraUsed, extraLimit, settings.extraUsageBalance);
63216
+ return formatSplitUsageBar("Session", "S", extraPercent, size2);
63217
+ }
63218
+ return formatUsageBar("Session", "S", data.sessionUsage, size2);
63381
63219
  }
63382
63220
  supportsRawValue() {
63383
63221
  return false;
63384
63222
  }
63385
- supportsColors(item) {
63223
+ supportsColors(_item) {
63386
63224
  return true;
63387
63225
  }
63388
63226
  }
@@ -63400,23 +63238,30 @@ class WeeklyUsageWidget {
63400
63238
  getCategory() {
63401
63239
  return "API Usage";
63402
63240
  }
63403
- getEditorDisplay(item) {
63241
+ getEditorDisplay(_item) {
63404
63242
  return { displayText: this.getDisplayName() };
63405
63243
  }
63406
- render(item, context, settings) {
63244
+ render(_item, context, settings) {
63407
63245
  if (context.isPreview)
63408
63246
  return "Weekly: [██░░░░░░░░░░░░░] 12%";
63409
- const data = fetchApiData();
63247
+ const data = context.usageData ?? {};
63410
63248
  if (data.error)
63411
- return getErrorMessage(data.error);
63249
+ return getUsageErrorMessage(data.error);
63412
63250
  if (data.weeklyUsage === undefined)
63413
63251
  return null;
63414
- return formatUsageBar("Weekly", "W", data.weeklyUsage, getDisplaySize(context));
63252
+ const size2 = getDisplaySize(context);
63253
+ const extraUsed = data.extraUsageUsed;
63254
+ const extraLimit = data.extraUsageLimit;
63255
+ if (size2 !== "mobile" && data.extraUsageEnabled === true && extraUsed !== undefined && extraLimit !== undefined && data.weeklyUsage >= 100) {
63256
+ const extraPercent = computeExtraPercent(extraUsed, extraLimit, settings.extraUsageBalance);
63257
+ return formatSplitUsageBar("Weekly", "W", extraPercent, size2);
63258
+ }
63259
+ return formatUsageBar("Weekly", "W", data.weeklyUsage, size2);
63415
63260
  }
63416
63261
  supportsRawValue() {
63417
63262
  return false;
63418
63263
  }
63419
- supportsColors(item) {
63264
+ supportsColors(_item) {
63420
63265
  return true;
63421
63266
  }
63422
63267
  }
@@ -63434,15 +63279,15 @@ class ResetTimerWidget {
63434
63279
  getCategory() {
63435
63280
  return "API Usage";
63436
63281
  }
63437
- getEditorDisplay(item) {
63282
+ getEditorDisplay(_item) {
63438
63283
  return { displayText: this.getDisplayName() };
63439
63284
  }
63440
- render(item, context, settings) {
63285
+ render(_item, context, settings) {
63441
63286
  if (context.isPreview)
63442
63287
  return "4:30 hr";
63443
- const data = fetchApiData();
63288
+ const data = context.usageData ?? {};
63444
63289
  if (data.error)
63445
- return getErrorMessage(data.error);
63290
+ return getUsageErrorMessage(data.error);
63446
63291
  const model = context.data?.model;
63447
63292
  const modelId = (typeof model === "string" ? model : model?.id) ?? "";
63448
63293
  const is1mModel = modelId.includes("[1m]");
@@ -63450,8 +63295,8 @@ class ResetTimerWidget {
63450
63295
  const isChargedModel = is1mModel && !isOpus;
63451
63296
  if (data.extraUsageEnabled && data.extraUsageUsed !== undefined && data.extraUsageLimit !== undefined && (data.weeklyUsage !== undefined && data.weeklyUsage >= 100 || data.sessionUsage !== undefined && data.sessionUsage >= 100 || isChargedModel)) {
63452
63297
  const used = formatCents(data.extraUsageUsed);
63453
- const displayLimit = settings.extraUsageBalance ?? data.extraUsageLimit;
63454
- const limit = formatCents(displayLimit);
63298
+ const effectiveTotal = computeEffectiveTotal(data.extraUsageUsed, data.extraUsageLimit, settings.extraUsageBalance);
63299
+ const limit = formatCents(effectiveTotal);
63455
63300
  return `Extra: ${used}/${limit}`;
63456
63301
  }
63457
63302
  if (!data.sessionResetAt)
@@ -63472,22 +63317,10 @@ class ResetTimerWidget {
63472
63317
  supportsRawValue() {
63473
63318
  return false;
63474
63319
  }
63475
- supportsColors(item) {
63320
+ supportsColors(_item) {
63476
63321
  return true;
63477
63322
  }
63478
63323
  }
63479
- function getCurrencySymbol() {
63480
- try {
63481
- const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
63482
- if (tz.startsWith("Europe/"))
63483
- return "€";
63484
- } catch {}
63485
- return "$";
63486
- }
63487
- function formatCents(cents) {
63488
- const symbol2 = getCurrencySymbol();
63489
- return `${symbol2}${(cents / 100).toFixed(2)}`;
63490
- }
63491
63324
 
63492
63325
  class ContextBarWidget {
63493
63326
  getDefaultColor() {
@@ -63502,10 +63335,10 @@ class ContextBarWidget {
63502
63335
  getCategory() {
63503
63336
  return "API Usage";
63504
63337
  }
63505
- getEditorDisplay(item) {
63338
+ getEditorDisplay(_item) {
63506
63339
  return { displayText: this.getDisplayName() };
63507
63340
  }
63508
- render(item, context, settings) {
63341
+ render(_item, context, _settings) {
63509
63342
  if (context.isPreview)
63510
63343
  return "Context: [████░░░░░░░░░░░] 50k/200k (25%)";
63511
63344
  const cw = context.data?.context_window;
@@ -63533,15 +63366,13 @@ class ContextBarWidget {
63533
63366
  supportsRawValue() {
63534
63367
  return false;
63535
63368
  }
63536
- supportsColors(item) {
63369
+ supportsColors(_item) {
63537
63370
  return true;
63538
63371
  }
63539
63372
  }
63540
- var CACHE_FILE2, LOCK_FILE2, CACHE_MAX_AGE2 = 600, LOCK_MAX_AGE2 = 60, TOKEN_CACHE_MAX_AGE = 3600, cachedData = null, cacheTime = 0, cachedToken = null, tokenCacheTime = 0, CRED_FILE, MOBILE_THRESHOLD = 134, MEDIUM_THRESHOLD2 = 178, MOBILE_BAR_WIDTH = 4, MEDIUM_BAR_WIDTH = 8, DEFAULT_BAR_WIDTH = 15;
63373
+ var MOBILE_THRESHOLD = 134, MEDIUM_THRESHOLD2 = 178, MOBILE_BAR_WIDTH = 4, MEDIUM_BAR_WIDTH = 8, DEFAULT_BAR_WIDTH = 15;
63541
63374
  var init_ApiUsage = __esm(() => {
63542
- CACHE_FILE2 = path9.join(os8.homedir(), ".cache", "ccstatusline-api.json");
63543
- LOCK_FILE2 = path9.join(os8.homedir(), ".cache", "ccstatusline-api.lock");
63544
- CRED_FILE = path9.join(os8.homedir(), ".claude", ".credentials.json");
63375
+ init_usage();
63545
63376
  });
63546
63377
 
63547
63378
  // src/widgets/WeeklyResetTimer.ts
@@ -64118,11 +63949,11 @@ var init_ThinkingEffort = __esm(() => {
64118
63949
  });
64119
63950
 
64120
63951
  // src/widgets/Battery.ts
64121
- import { execSync as execSync6 } from "child_process";
64122
- import { readFileSync as readFileSync12 } from "fs";
63952
+ import { execSync as execSync5 } from "child_process";
63953
+ import { readFileSync as readFileSync11 } from "fs";
64123
63954
  function getMacBatteryInfo() {
64124
63955
  try {
64125
- const output = execSync6("pmset -g batt", { encoding: "utf-8", timeout: 2000 });
63956
+ const output = execSync5("pmset -g batt", { encoding: "utf-8", timeout: 2000 });
64126
63957
  const match = /(\d+)%;\s*(charging|discharging|charged|finishing charge|AC attached)/i.exec(output);
64127
63958
  const percentStr = match?.[1];
64128
63959
  const stateStr = match?.[2];
@@ -64139,8 +63970,8 @@ function getMacBatteryInfo() {
64139
63970
  }
64140
63971
  function getLinuxBatteryInfo() {
64141
63972
  try {
64142
- const capacity = readFileSync12("/sys/class/power_supply/BAT0/capacity", "utf-8").trim();
64143
- const status = readFileSync12("/sys/class/power_supply/BAT0/status", "utf-8").trim().toLowerCase();
63973
+ const capacity = readFileSync11("/sys/class/power_supply/BAT0/capacity", "utf-8").trim();
63974
+ const status = readFileSync11("/sys/class/power_supply/BAT0/status", "utf-8").trim().toLowerCase();
64144
63975
  const percent = parseInt(capacity, 10);
64145
63976
  if (isNaN(percent)) {
64146
63977
  return null;
@@ -65123,11 +64954,11 @@ var init_hooks = __esm(async () => {
65123
64954
  });
65124
64955
 
65125
64956
  // src/utils/config.ts
65126
- import * as fs11 from "fs";
65127
- import * as os9 from "os";
65128
- import * as path10 from "path";
64957
+ import * as fs10 from "fs";
64958
+ import * as os8 from "os";
64959
+ import * as path9 from "path";
65129
64960
  function initConfigPath(filePath) {
65130
- settingsPath = filePath ? path10.resolve(filePath) : DEFAULT_SETTINGS_PATH;
64961
+ settingsPath = filePath ? path9.resolve(filePath) : DEFAULT_SETTINGS_PATH;
65131
64962
  }
65132
64963
  function getConfigPath() {
65133
64964
  return settingsPath;
@@ -65136,13 +64967,13 @@ function isCustomConfigPath() {
65136
64967
  return settingsPath !== DEFAULT_SETTINGS_PATH;
65137
64968
  }
65138
64969
  function getSettingsPaths() {
65139
- const configDir = path10.dirname(settingsPath);
65140
- const parsedPath = path10.parse(settingsPath);
64970
+ const configDir = path9.dirname(settingsPath);
64971
+ const parsedPath = path9.parse(settingsPath);
65141
64972
  const backupBaseName = parsedPath.ext ? `${parsedPath.name}.bak` : `${parsedPath.base}.bak`;
65142
64973
  return {
65143
64974
  configDir,
65144
64975
  settingsPath,
65145
- settingsBackupPath: path10.join(configDir, backupBaseName)
64976
+ settingsBackupPath: path9.join(configDir, backupBaseName)
65146
64977
  };
65147
64978
  }
65148
64979
  async function ensureDir(dir) {
@@ -65159,7 +64990,7 @@ async function writeSettingsJson(settings, paths) {
65159
64990
  }
65160
64991
  async function backupBadSettings(paths) {
65161
64992
  try {
65162
- if (fs11.existsSync(paths.settingsPath)) {
64993
+ if (fs10.existsSync(paths.settingsPath)) {
65163
64994
  const content = await readFile3(paths.settingsPath, "utf-8");
65164
64995
  await writeFile(paths.settingsBackupPath, content, "utf-8");
65165
64996
  console.error(`Bad settings backed up to ${paths.settingsBackupPath}`);
@@ -65189,7 +65020,7 @@ async function recoverWithDefaults(paths) {
65189
65020
  async function loadSettings() {
65190
65021
  const paths = getSettingsPaths();
65191
65022
  try {
65192
- if (!fs11.existsSync(paths.settingsPath))
65023
+ if (!fs10.existsSync(paths.settingsPath))
65193
65024
  return await writeDefaultSettings(paths);
65194
65025
  const content = await readFile3(paths.settingsPath, "utf-8");
65195
65026
  let rawData;
@@ -65239,18 +65070,18 @@ var readFile3, writeFile, mkdir, DEFAULT_SETTINGS_PATH, settingsPath;
65239
65070
  var init_config = __esm(() => {
65240
65071
  init_Settings();
65241
65072
  init_migrations();
65242
- readFile3 = fs11.promises.readFile;
65243
- writeFile = fs11.promises.writeFile;
65244
- mkdir = fs11.promises.mkdir;
65245
- DEFAULT_SETTINGS_PATH = path10.join(os9.homedir(), ".config", "ccstatusline", "settings.json");
65073
+ readFile3 = fs10.promises.readFile;
65074
+ writeFile = fs10.promises.writeFile;
65075
+ mkdir = fs10.promises.mkdir;
65076
+ DEFAULT_SETTINGS_PATH = path9.join(os8.homedir(), ".config", "ccstatusline", "settings.json");
65246
65077
  settingsPath = DEFAULT_SETTINGS_PATH;
65247
65078
  });
65248
65079
 
65249
65080
  // src/utils/claude-settings.ts
65250
- import { execSync as execSync7 } from "child_process";
65251
- import * as fs12 from "fs";
65252
- import * as os10 from "os";
65253
- import * as path11 from "path";
65081
+ import { execSync as execSync6 } from "child_process";
65082
+ import * as fs11 from "fs";
65083
+ import * as os9 from "os";
65084
+ import * as path10 from "path";
65254
65085
  function isKnownCommand(command) {
65255
65086
  const prefixes = [CCSTATUSLINE_COMMANDS.NPM, CCSTATUSLINE_COMMANDS.BUNX, CCSTATUSLINE_COMMANDS.SELF_MANAGED];
65256
65087
  return prefixes.some((prefix) => command === prefix || command.startsWith(`${prefix} --config `));
@@ -65274,9 +65105,9 @@ function getClaudeConfigDir() {
65274
65105
  const envConfigDir = process.env.CLAUDE_CONFIG_DIR;
65275
65106
  if (envConfigDir) {
65276
65107
  try {
65277
- const resolvedPath = path11.resolve(envConfigDir);
65278
- if (fs12.existsSync(resolvedPath)) {
65279
- const stats = fs12.statSync(resolvedPath);
65108
+ const resolvedPath = path10.resolve(envConfigDir);
65109
+ if (fs11.existsSync(resolvedPath)) {
65110
+ const stats = fs11.statSync(resolvedPath);
65280
65111
  if (stats.isDirectory()) {
65281
65112
  return resolvedPath;
65282
65113
  }
@@ -65285,16 +65116,16 @@ function getClaudeConfigDir() {
65285
65116
  }
65286
65117
  } catch {}
65287
65118
  }
65288
- return path11.join(os10.homedir(), ".claude");
65119
+ return path10.join(os9.homedir(), ".claude");
65289
65120
  }
65290
65121
  function getClaudeSettingsPath() {
65291
- return path11.join(getClaudeConfigDir(), "settings.json");
65122
+ return path10.join(getClaudeConfigDir(), "settings.json");
65292
65123
  }
65293
65124
  async function backupClaudeSettings(suffix = ".bak") {
65294
65125
  const settingsPath2 = getClaudeSettingsPath();
65295
65126
  const backupPath = settingsPath2 + suffix;
65296
65127
  try {
65297
- if (fs12.existsSync(settingsPath2)) {
65128
+ if (fs11.existsSync(settingsPath2)) {
65298
65129
  const content = await readFile4(settingsPath2, "utf-8");
65299
65130
  await writeFile2(backupPath, content, "utf-8");
65300
65131
  return backupPath;
@@ -65307,11 +65138,11 @@ async function backupClaudeSettings(suffix = ".bak") {
65307
65138
  function loadClaudeSettingsSync(options = {}) {
65308
65139
  const { logErrors = true } = options;
65309
65140
  const settingsPath2 = getClaudeSettingsPath();
65310
- if (!fs12.existsSync(settingsPath2)) {
65141
+ if (!fs11.existsSync(settingsPath2)) {
65311
65142
  return {};
65312
65143
  }
65313
65144
  try {
65314
- const content = fs12.readFileSync(settingsPath2, "utf-8");
65145
+ const content = fs11.readFileSync(settingsPath2, "utf-8");
65315
65146
  return JSON.parse(content);
65316
65147
  } catch (error48) {
65317
65148
  if (logErrors) {
@@ -65323,7 +65154,7 @@ function loadClaudeSettingsSync(options = {}) {
65323
65154
  async function loadClaudeSettings(options = {}) {
65324
65155
  const { logErrors = true } = options;
65325
65156
  const settingsPath2 = getClaudeSettingsPath();
65326
- if (!fs12.existsSync(settingsPath2)) {
65157
+ if (!fs11.existsSync(settingsPath2)) {
65327
65158
  return {};
65328
65159
  }
65329
65160
  try {
@@ -65338,7 +65169,7 @@ async function loadClaudeSettings(options = {}) {
65338
65169
  }
65339
65170
  async function saveClaudeSettings(settings) {
65340
65171
  const settingsPath2 = getClaudeSettingsPath();
65341
- const dir = path11.dirname(settingsPath2);
65172
+ const dir = path10.dirname(settingsPath2);
65342
65173
  await backupClaudeSettings();
65343
65174
  await ensureDir(dir);
65344
65175
  await writeFile2(settingsPath2, JSON.stringify(settings, null, 2), "utf-8");
@@ -65356,7 +65187,7 @@ async function isInstalled() {
65356
65187
  function isBunxAvailable() {
65357
65188
  try {
65358
65189
  const command = process.platform === "win32" ? "where bunx" : "which bunx";
65359
- execSync7(command, { stdio: "ignore" });
65190
+ execSync6(command, { stdio: "ignore" });
65360
65191
  return true;
65361
65192
  } catch {
65362
65193
  return false;
@@ -65370,7 +65201,7 @@ function buildCommand(baseCommand) {
65370
65201
  }
65371
65202
  async function loadSavedSettingsForHookSync() {
65372
65203
  const configPath = getConfigPath();
65373
- if (!fs12.existsSync(configPath)) {
65204
+ if (!fs11.existsSync(configPath)) {
65374
65205
  return null;
65375
65206
  }
65376
65207
  try {
@@ -65437,8 +65268,8 @@ var readFile4, writeFile2, CCSTATUSLINE_COMMANDS;
65437
65268
  var init_claude_settings = __esm(() => {
65438
65269
  init_Settings();
65439
65270
  init_config();
65440
- readFile4 = fs12.promises.readFile;
65441
- writeFile2 = fs12.promises.writeFile;
65271
+ readFile4 = fs11.promises.readFile;
65272
+ writeFile2 = fs11.promises.writeFile;
65442
65273
  CCSTATUSLINE_COMMANDS = {
65443
65274
  NPM: "npx -y ccstatusline-usage@latest",
65444
65275
  BUNX: "bunx -y ccstatusline-usage@latest",
@@ -66031,10 +65862,10 @@ function cloneSettings(settings) {
66031
65862
  init_config();
66032
65863
 
66033
65864
  // src/utils/open-url.ts
66034
- import { spawnSync as spawnSync2 } from "child_process";
66035
- import * as os11 from "os";
65865
+ import { spawnSync } from "child_process";
65866
+ import * as os10 from "os";
66036
65867
  function runOpenCommand(command, args) {
66037
- const result2 = spawnSync2(command, args, {
65868
+ const result2 = spawnSync(command, args, {
66038
65869
  stdio: "ignore",
66039
65870
  windowsHide: true
66040
65871
  });
@@ -66091,7 +65922,7 @@ function openExternalUrl(url2) {
66091
65922
  error: "Only http(s) URLs are supported"
66092
65923
  };
66093
65924
  }
66094
- const platform3 = os11.platform();
65925
+ const platform3 = os10.platform();
66095
65926
  const plans = PLATFORM_OPEN_PLANS[platform3];
66096
65927
  if (!plans) {
66097
65928
  return {
@@ -66118,10 +65949,10 @@ function openExternalUrl(url2) {
66118
65949
  }
66119
65950
 
66120
65951
  // src/utils/powerline.ts
66121
- import { execSync as execSync8 } from "child_process";
66122
- import * as fs13 from "fs";
66123
- import * as os12 from "os";
66124
- import * as path12 from "path";
65952
+ import { execSync as execSync7 } from "child_process";
65953
+ import * as fs12 from "fs";
65954
+ import * as os11 from "os";
65955
+ import * as path11 from "path";
66125
65956
  var fontsInstalledThisSession = false;
66126
65957
  function checkPowerlineFonts() {
66127
65958
  if (process.env.DEBUG_FONT_INSTALL === "1" && !fontsInstalledThisSession) {
@@ -66137,24 +65968,24 @@ function checkPowerlineFonts() {
66137
65968
  leftArrow: "",
66138
65969
  leftThinArrow: ""
66139
65970
  };
66140
- const platform4 = os12.platform();
65971
+ const platform4 = os11.platform();
66141
65972
  let fontPaths = [];
66142
65973
  if (platform4 === "darwin") {
66143
65974
  fontPaths = [
66144
- path12.join(os12.homedir(), "Library", "Fonts"),
65975
+ path11.join(os11.homedir(), "Library", "Fonts"),
66145
65976
  "/Library/Fonts",
66146
65977
  "/System/Library/Fonts"
66147
65978
  ];
66148
65979
  } else if (platform4 === "linux") {
66149
65980
  fontPaths = [
66150
- path12.join(os12.homedir(), ".local", "share", "fonts"),
66151
- path12.join(os12.homedir(), ".fonts"),
65981
+ path11.join(os11.homedir(), ".local", "share", "fonts"),
65982
+ path11.join(os11.homedir(), ".fonts"),
66152
65983
  "/usr/share/fonts",
66153
65984
  "/usr/local/share/fonts"
66154
65985
  ];
66155
65986
  } else if (platform4 === "win32") {
66156
65987
  fontPaths = [
66157
- path12.join(os12.homedir(), "AppData", "Local", "Microsoft", "Windows", "Fonts"),
65988
+ path11.join(os11.homedir(), "AppData", "Local", "Microsoft", "Windows", "Fonts"),
66158
65989
  "C:\\Windows\\Fonts"
66159
65990
  ];
66160
65991
  }
@@ -66170,9 +66001,9 @@ function checkPowerlineFonts() {
66170
66001
  /fira.*code.*nerd/i
66171
66002
  ];
66172
66003
  for (const fontPath of fontPaths) {
66173
- if (fs13.existsSync(fontPath)) {
66004
+ if (fs12.existsSync(fontPath)) {
66174
66005
  try {
66175
- const files = fs13.readdirSync(fontPath);
66006
+ const files = fs12.readdirSync(fontPath);
66176
66007
  for (const file2 of files) {
66177
66008
  for (const pattern of powerlineFontPatterns) {
66178
66009
  if (pattern.test(file2)) {
@@ -66207,7 +66038,7 @@ async function checkPowerlineFontsAsync() {
66207
66038
  if (quickCheck.installed) {
66208
66039
  return quickCheck;
66209
66040
  }
66210
- const platform4 = os12.platform();
66041
+ const platform4 = os11.platform();
66211
66042
  if (platform4 === "linux" || platform4 === "darwin") {
66212
66043
  try {
66213
66044
  const { exec: exec2 } = await import("child_process");
@@ -66230,44 +66061,44 @@ async function checkPowerlineFontsAsync() {
66230
66061
  async function installPowerlineFonts() {
66231
66062
  await Promise.resolve();
66232
66063
  try {
66233
- const platform4 = os12.platform();
66064
+ const platform4 = os11.platform();
66234
66065
  let fontDir;
66235
66066
  if (platform4 === "darwin") {
66236
- fontDir = path12.join(os12.homedir(), "Library", "Fonts");
66067
+ fontDir = path11.join(os11.homedir(), "Library", "Fonts");
66237
66068
  } else if (platform4 === "linux") {
66238
- fontDir = path12.join(os12.homedir(), ".local", "share", "fonts");
66069
+ fontDir = path11.join(os11.homedir(), ".local", "share", "fonts");
66239
66070
  } else if (platform4 === "win32") {
66240
- fontDir = path12.join(os12.homedir(), "AppData", "Local", "Microsoft", "Windows", "Fonts");
66071
+ fontDir = path11.join(os11.homedir(), "AppData", "Local", "Microsoft", "Windows", "Fonts");
66241
66072
  } else {
66242
66073
  return {
66243
66074
  success: false,
66244
66075
  message: "Unsupported platform for font installation"
66245
66076
  };
66246
66077
  }
66247
- if (!fs13.existsSync(fontDir)) {
66248
- fs13.mkdirSync(fontDir, { recursive: true });
66078
+ if (!fs12.existsSync(fontDir)) {
66079
+ fs12.mkdirSync(fontDir, { recursive: true });
66249
66080
  }
66250
- const tempDir = path12.join(os12.tmpdir(), `ccstatusline-powerline-fonts-${Date.now()}`);
66081
+ const tempDir = path11.join(os11.tmpdir(), `ccstatusline-powerline-fonts-${Date.now()}`);
66251
66082
  try {
66252
- if (fs13.existsSync(tempDir)) {
66253
- fs13.rmSync(tempDir, { recursive: true, force: true });
66083
+ if (fs12.existsSync(tempDir)) {
66084
+ fs12.rmSync(tempDir, { recursive: true, force: true });
66254
66085
  }
66255
- execSync8(`git clone --depth=1 https://github.com/powerline/fonts.git "${tempDir}"`, {
66086
+ execSync7(`git clone --depth=1 https://github.com/powerline/fonts.git "${tempDir}"`, {
66256
66087
  stdio: "pipe",
66257
66088
  encoding: "utf8"
66258
66089
  });
66259
66090
  if (platform4 === "darwin" || platform4 === "linux") {
66260
- const installScript = path12.join(tempDir, "install.sh");
66261
- if (fs13.existsSync(installScript)) {
66262
- fs13.chmodSync(installScript, 493);
66263
- execSync8(`cd "${tempDir}" && ./install.sh`, {
66091
+ const installScript = path11.join(tempDir, "install.sh");
66092
+ if (fs12.existsSync(installScript)) {
66093
+ fs12.chmodSync(installScript, 493);
66094
+ execSync7(`cd "${tempDir}" && ./install.sh`, {
66264
66095
  stdio: "pipe",
66265
66096
  encoding: "utf8",
66266
66097
  shell: "/bin/bash"
66267
66098
  });
66268
66099
  if (platform4 === "linux") {
66269
66100
  try {
66270
- execSync8("fc-cache -f -v", {
66101
+ execSync7("fc-cache -f -v", {
66271
66102
  stdio: "pipe",
66272
66103
  encoding: "utf8"
66273
66104
  });
@@ -66285,10 +66116,10 @@ async function installPowerlineFonts() {
66285
66116
  }
66286
66117
  } else {
66287
66118
  let findFontFiles = function(dir) {
66288
- const files = fs13.readdirSync(dir);
66119
+ const files = fs12.readdirSync(dir);
66289
66120
  for (const file2 of files) {
66290
- const filePath = path12.join(dir, file2);
66291
- const stat = fs13.statSync(filePath);
66121
+ const filePath = path11.join(dir, file2);
66122
+ const stat = fs12.statSync(filePath);
66292
66123
  if (stat.isDirectory() && !file2.startsWith(".")) {
66293
66124
  findFontFiles(filePath);
66294
66125
  } else if (file2.endsWith(".ttf") || file2.endsWith(".otf")) {
@@ -66302,10 +66133,10 @@ async function installPowerlineFonts() {
66302
66133
  findFontFiles(tempDir);
66303
66134
  let installedCount = 0;
66304
66135
  for (const fontFile of fontFiles) {
66305
- const fileName = path12.basename(fontFile);
66306
- const destPath = path12.join(fontDir, fileName);
66136
+ const fileName = path11.basename(fontFile);
66137
+ const destPath = path11.join(fontDir, fileName);
66307
66138
  try {
66308
- fs13.copyFileSync(fontFile, destPath);
66139
+ fs12.copyFileSync(fontFile, destPath);
66309
66140
  installedCount++;
66310
66141
  } catch {}
66311
66142
  }
@@ -66323,9 +66154,9 @@ async function installPowerlineFonts() {
66323
66154
  message: "Platform-specific installation not implemented"
66324
66155
  };
66325
66156
  } finally {
66326
- if (fs13.existsSync(tempDir)) {
66157
+ if (fs12.existsSync(tempDir)) {
66327
66158
  try {
66328
- fs13.rmSync(tempDir, { recursive: true, force: true });
66159
+ fs12.rmSync(tempDir, { recursive: true, force: true });
66329
66160
  } catch {}
66330
66161
  }
66331
66162
  }
@@ -69326,7 +69157,7 @@ var MainMenu = ({
69326
69157
  // src/tui/components/PowerlineSetup.tsx
69327
69158
  await init_build2();
69328
69159
  var import_react43 = __toESM(require_react(), 1);
69329
- import * as os13 from "os";
69160
+ import * as os12 from "os";
69330
69161
 
69331
69162
  // src/utils/powerline-settings.ts
69332
69163
  init_colors();
@@ -70092,7 +69923,7 @@ var PowerlineSetup = ({
70092
69923
  }, undefined, false, undefined, this)
70093
69924
  ]
70094
69925
  }, undefined, true, undefined, this),
70095
- os13.platform() === "darwin" && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(jsx_dev_runtime18.Fragment, {
69926
+ os12.platform() === "darwin" && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(jsx_dev_runtime18.Fragment, {
70096
69927
  children: [
70097
69928
  /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
70098
69929
  dimColor: true,
@@ -70108,7 +69939,7 @@ var PowerlineSetup = ({
70108
69939
  }, undefined, false, undefined, this)
70109
69940
  ]
70110
69941
  }, undefined, true, undefined, this),
70111
- os13.platform() === "linux" && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(jsx_dev_runtime18.Fragment, {
69942
+ os12.platform() === "linux" && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(jsx_dev_runtime18.Fragment, {
70112
69943
  children: [
70113
69944
  /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
70114
69945
  dimColor: true,
@@ -70124,7 +69955,7 @@ var PowerlineSetup = ({
70124
69955
  }, undefined, false, undefined, this)
70125
69956
  ]
70126
69957
  }, undefined, true, undefined, this),
70127
- os13.platform() === "win32" && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(jsx_dev_runtime18.Fragment, {
69958
+ os12.platform() === "win32" && /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(jsx_dev_runtime18.Fragment, {
70128
69959
  children: [
70129
69960
  /* @__PURE__ */ jsx_dev_runtime18.jsxDEV(Text, {
70130
69961
  dimColor: true,
@@ -71339,23 +71170,23 @@ init_jsonl();
71339
71170
  await init_renderer2();
71340
71171
 
71341
71172
  // src/utils/skills.ts
71342
- import * as fs14 from "fs";
71343
- import * as os14 from "os";
71344
- import * as path13 from "path";
71173
+ import * as fs13 from "fs";
71174
+ import * as os13 from "os";
71175
+ import * as path12 from "path";
71345
71176
  var EMPTY = { totalInvocations: 0, uniqueSkills: [], lastSkill: null };
71346
71177
  function getSkillsDir() {
71347
- return path13.join(os14.homedir(), ".cache", "ccstatusline", "skills");
71178
+ return path12.join(os13.homedir(), ".cache", "ccstatusline", "skills");
71348
71179
  }
71349
71180
  function getSkillsFilePath(sessionId) {
71350
- return path13.join(getSkillsDir(), `skills-${sessionId}.jsonl`);
71181
+ return path12.join(getSkillsDir(), `skills-${sessionId}.jsonl`);
71351
71182
  }
71352
71183
  function getSkillsMetrics(sessionId) {
71353
71184
  const filePath = getSkillsFilePath(sessionId);
71354
- if (!fs14.existsSync(filePath)) {
71185
+ if (!fs13.existsSync(filePath)) {
71355
71186
  return EMPTY;
71356
71187
  }
71357
71188
  try {
71358
- const invocations = fs14.readFileSync(filePath, "utf-8").trim().split(`
71189
+ const invocations = fs13.readFileSync(filePath, "utf-8").trim().split(`
71359
71190
  `).filter((line) => line.trim()).map((line) => {
71360
71191
  try {
71361
71192
  return JSON.parse(line);
@@ -71690,16 +71521,16 @@ async function handleHook() {
71690
71521
  return;
71691
71522
  }
71692
71523
  const filePath = getSkillsFilePath(sessionId);
71693
- const fs15 = await import("fs");
71694
- const path14 = await import("path");
71695
- fs15.mkdirSync(path14.dirname(filePath), { recursive: true });
71524
+ const fs14 = await import("fs");
71525
+ const path13 = await import("path");
71526
+ fs14.mkdirSync(path13.dirname(filePath), { recursive: true });
71696
71527
  const entry = JSON.stringify({
71697
71528
  timestamp: new Date().toISOString(),
71698
71529
  session_id: sessionId,
71699
71530
  skill: skillName,
71700
71531
  source: data.hook_event_name
71701
71532
  });
71702
- fs15.appendFileSync(filePath, entry + `
71533
+ fs14.appendFileSync(filePath, entry + `
71703
71534
  `);
71704
71535
  } catch {}
71705
71536
  console.log("{}");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccstatusline-usage",
3
- "version": "2.3.10",
3
+ "version": "2.3.12",
4
4
  "description": "A customizable status line formatter for Claude Code CLI",
5
5
  "module": "src/ccstatusline.ts",
6
6
  "type": "module",
@@ -19,7 +19,7 @@
19
19
  "lint": "bun tsc --noEmit && eslint . --config eslint.config.js --max-warnings=0",
20
20
  "lint:fix": "bun tsc --noEmit && eslint . --config eslint.config.js --max-warnings=0 --fix",
21
21
  "docs": "typedoc",
22
- "docs:clean": "rm -rf docs"
22
+ "docs:clean": "rm -rf typedoc"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@eslint/js": "^10.0.1",