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 +36 -19
- package/dist/ccstatusline.js +193 -362
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -47,6 +47,7 @@ Session: [████░░░░░░░░░░░] 27.0% | Weekly: [██
|
|
|
47
47
|

|
|
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](
|
|
58
|
-
- [Usage](
|
|
59
|
-
- [
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
package/dist/ccstatusline.js
CHANGED
|
@@ -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(/\(
|
|
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.
|
|
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
|
-
|
|
61704
|
-
|
|
61705
|
-
|
|
61706
|
-
|
|
61707
|
-
|
|
61708
|
-
|
|
61709
|
-
|
|
61710
|
-
|
|
61711
|
-
|
|
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
|
|
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
|
-
|
|
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(
|
|
63200
|
+
getEditorDisplay(_item) {
|
|
63370
63201
|
return { displayText: this.getDisplayName() };
|
|
63371
63202
|
}
|
|
63372
|
-
render(
|
|
63203
|
+
render(_item, context, settings) {
|
|
63373
63204
|
if (context.isPreview)
|
|
63374
63205
|
return "Session: [███░░░░░░░░░░░░] 20%";
|
|
63375
|
-
const data =
|
|
63206
|
+
const data = context.usageData ?? {};
|
|
63376
63207
|
if (data.error)
|
|
63377
|
-
return
|
|
63208
|
+
return getUsageErrorMessage(data.error);
|
|
63378
63209
|
if (data.sessionUsage === undefined)
|
|
63379
63210
|
return null;
|
|
63380
|
-
|
|
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(
|
|
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(
|
|
63241
|
+
getEditorDisplay(_item) {
|
|
63404
63242
|
return { displayText: this.getDisplayName() };
|
|
63405
63243
|
}
|
|
63406
|
-
render(
|
|
63244
|
+
render(_item, context, settings) {
|
|
63407
63245
|
if (context.isPreview)
|
|
63408
63246
|
return "Weekly: [██░░░░░░░░░░░░░] 12%";
|
|
63409
|
-
const data =
|
|
63247
|
+
const data = context.usageData ?? {};
|
|
63410
63248
|
if (data.error)
|
|
63411
|
-
return
|
|
63249
|
+
return getUsageErrorMessage(data.error);
|
|
63412
63250
|
if (data.weeklyUsage === undefined)
|
|
63413
63251
|
return null;
|
|
63414
|
-
|
|
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(
|
|
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(
|
|
63282
|
+
getEditorDisplay(_item) {
|
|
63438
63283
|
return { displayText: this.getDisplayName() };
|
|
63439
63284
|
}
|
|
63440
|
-
render(
|
|
63285
|
+
render(_item, context, settings) {
|
|
63441
63286
|
if (context.isPreview)
|
|
63442
63287
|
return "4:30 hr";
|
|
63443
|
-
const data =
|
|
63288
|
+
const data = context.usageData ?? {};
|
|
63444
63289
|
if (data.error)
|
|
63445
|
-
return
|
|
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
|
|
63454
|
-
const limit = formatCents(
|
|
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(
|
|
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(
|
|
63338
|
+
getEditorDisplay(_item) {
|
|
63506
63339
|
return { displayText: this.getDisplayName() };
|
|
63507
63340
|
}
|
|
63508
|
-
render(
|
|
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(
|
|
63369
|
+
supportsColors(_item) {
|
|
63537
63370
|
return true;
|
|
63538
63371
|
}
|
|
63539
63372
|
}
|
|
63540
|
-
var
|
|
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
|
-
|
|
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
|
|
64122
|
-
import { readFileSync as
|
|
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 =
|
|
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 =
|
|
64143
|
-
const status =
|
|
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
|
|
65127
|
-
import * as
|
|
65128
|
-
import * as
|
|
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 ?
|
|
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 =
|
|
65140
|
-
const parsedPath =
|
|
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:
|
|
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 (
|
|
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 (!
|
|
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 =
|
|
65243
|
-
writeFile =
|
|
65244
|
-
mkdir =
|
|
65245
|
-
DEFAULT_SETTINGS_PATH =
|
|
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
|
|
65251
|
-
import * as
|
|
65252
|
-
import * as
|
|
65253
|
-
import * as
|
|
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 =
|
|
65278
|
-
if (
|
|
65279
|
-
const stats =
|
|
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
|
|
65119
|
+
return path10.join(os9.homedir(), ".claude");
|
|
65289
65120
|
}
|
|
65290
65121
|
function getClaudeSettingsPath() {
|
|
65291
|
-
return
|
|
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 (
|
|
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 (!
|
|
65141
|
+
if (!fs11.existsSync(settingsPath2)) {
|
|
65311
65142
|
return {};
|
|
65312
65143
|
}
|
|
65313
65144
|
try {
|
|
65314
|
-
const content =
|
|
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 (!
|
|
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 =
|
|
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
|
-
|
|
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 (!
|
|
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 =
|
|
65441
|
-
writeFile2 =
|
|
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
|
|
66035
|
-
import * as
|
|
65865
|
+
import { spawnSync } from "child_process";
|
|
65866
|
+
import * as os10 from "os";
|
|
66036
65867
|
function runOpenCommand(command, args) {
|
|
66037
|
-
const result2 =
|
|
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 =
|
|
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
|
|
66122
|
-
import * as
|
|
66123
|
-
import * as
|
|
66124
|
-
import * as
|
|
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 =
|
|
65971
|
+
const platform4 = os11.platform();
|
|
66141
65972
|
let fontPaths = [];
|
|
66142
65973
|
if (platform4 === "darwin") {
|
|
66143
65974
|
fontPaths = [
|
|
66144
|
-
|
|
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
|
-
|
|
66151
|
-
|
|
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
|
-
|
|
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 (
|
|
66004
|
+
if (fs12.existsSync(fontPath)) {
|
|
66174
66005
|
try {
|
|
66175
|
-
const files =
|
|
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 =
|
|
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 =
|
|
66064
|
+
const platform4 = os11.platform();
|
|
66234
66065
|
let fontDir;
|
|
66235
66066
|
if (platform4 === "darwin") {
|
|
66236
|
-
fontDir =
|
|
66067
|
+
fontDir = path11.join(os11.homedir(), "Library", "Fonts");
|
|
66237
66068
|
} else if (platform4 === "linux") {
|
|
66238
|
-
fontDir =
|
|
66069
|
+
fontDir = path11.join(os11.homedir(), ".local", "share", "fonts");
|
|
66239
66070
|
} else if (platform4 === "win32") {
|
|
66240
|
-
fontDir =
|
|
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 (!
|
|
66248
|
-
|
|
66078
|
+
if (!fs12.existsSync(fontDir)) {
|
|
66079
|
+
fs12.mkdirSync(fontDir, { recursive: true });
|
|
66249
66080
|
}
|
|
66250
|
-
const tempDir =
|
|
66081
|
+
const tempDir = path11.join(os11.tmpdir(), `ccstatusline-powerline-fonts-${Date.now()}`);
|
|
66251
66082
|
try {
|
|
66252
|
-
if (
|
|
66253
|
-
|
|
66083
|
+
if (fs12.existsSync(tempDir)) {
|
|
66084
|
+
fs12.rmSync(tempDir, { recursive: true, force: true });
|
|
66254
66085
|
}
|
|
66255
|
-
|
|
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 =
|
|
66261
|
-
if (
|
|
66262
|
-
|
|
66263
|
-
|
|
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
|
-
|
|
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 =
|
|
66119
|
+
const files = fs12.readdirSync(dir);
|
|
66289
66120
|
for (const file2 of files) {
|
|
66290
|
-
const filePath =
|
|
66291
|
-
const stat =
|
|
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 =
|
|
66306
|
-
const destPath =
|
|
66136
|
+
const fileName = path11.basename(fontFile);
|
|
66137
|
+
const destPath = path11.join(fontDir, fileName);
|
|
66307
66138
|
try {
|
|
66308
|
-
|
|
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 (
|
|
66157
|
+
if (fs12.existsSync(tempDir)) {
|
|
66327
66158
|
try {
|
|
66328
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
71343
|
-
import * as
|
|
71344
|
-
import * as
|
|
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
|
|
71178
|
+
return path12.join(os13.homedir(), ".cache", "ccstatusline", "skills");
|
|
71348
71179
|
}
|
|
71349
71180
|
function getSkillsFilePath(sessionId) {
|
|
71350
|
-
return
|
|
71181
|
+
return path12.join(getSkillsDir(), `skills-${sessionId}.jsonl`);
|
|
71351
71182
|
}
|
|
71352
71183
|
function getSkillsMetrics(sessionId) {
|
|
71353
71184
|
const filePath = getSkillsFilePath(sessionId);
|
|
71354
|
-
if (!
|
|
71185
|
+
if (!fs13.existsSync(filePath)) {
|
|
71355
71186
|
return EMPTY;
|
|
71356
71187
|
}
|
|
71357
71188
|
try {
|
|
71358
|
-
const invocations =
|
|
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
|
|
71694
|
-
const
|
|
71695
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
22
|
+
"docs:clean": "rm -rf typedoc"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@eslint/js": "^10.0.1",
|