ccstatusline-usage 2.1.1 → 2.1.2
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 +26 -103
- package/dist/ccstatusline.js +64 -126
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -14,44 +14,18 @@
|
|
|
14
14
|
**🎨 A highly customizable status line formatter for Claude Code CLI**
|
|
15
15
|
*Display model info, git branch, token usage, and other metrics in your terminal*
|
|
16
16
|
|
|
17
|
-
[](https://www.npmjs.com/package/ccstatusline)
|
|
18
|
+
[](https://www.npmjs.com/package/ccstatusline)
|
|
19
19
|
[](https://github.com/sirmalloc/ccstatusline/blob/main/LICENSE)
|
|
20
|
-
[](https://nodejs.org)
|
|
21
|
+
[](https://packagephobia.com/result?p=ccstatusline)
|
|
22
22
|
[](https://github.com/sirmalloc/ccstatusline/graphs/commit-activity)
|
|
23
23
|
|
|
24
24
|
[](https://github.com/hesreallyhim/awesome-claude-code)
|
|
25
25
|
[](https://claudelog.com/)
|
|
26
26
|
|
|
27
|
-
## Fork Enhancements
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
- **Session/Weekly Usage** - Real utilization from Anthropic API with progress bars
|
|
32
|
-
- **Extra Usage** - When weekly limit is reached, shows extra usage spending (e.g. `Extra: €41.24/€47.00`)
|
|
33
|
-
- **Reset Timer** - Time until 5-hour session window resets
|
|
34
|
-
- **Context Window Display** - Visual bar showing context usage
|
|
35
|
-
- **Two-line Layout** - Session info on line 1, context on line 2
|
|
36
|
-
- **Narrow Terminal Detection** - Auto-compact rendering on terminals < 80 cols via tmux pane width detection
|
|
37
|
-
|
|
38
|
-
### Enhanced Status Line Preview
|
|
39
|
-
|
|
40
|
-
**Desktop (full width):**
|
|
41
|
-
```
|
|
42
|
-
Session: [████░░░░░░░░░░░] 27.0% | Weekly: [████████████░░░] 86.0% | 1:56 hr | Extra: $41.24/$47.00 | Model: Opus 4.6 | Session ID: 714aa815-8a...
|
|
43
|
-
Context: [███████░░░░░░░░] 103k/200k (51%)
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
**Narrow terminal (< 80 cols, e.g. mobile SSH via tmux):**
|
|
47
|
-
```
|
|
48
|
-
S: [██░░] 51.0% | W: [████] 92.0% | 0:00 hr
|
|
49
|
-
C: [██░░] 100k/200k
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
These widgets are enabled by default. Just install and run!
|
|
53
|
-
|
|
54
|
-

|
|
28
|
+

|
|
55
29
|
|
|
56
30
|
</div>
|
|
57
31
|
|
|
@@ -65,7 +39,6 @@ These widgets are enabled by default. Just install and run!
|
|
|
65
39
|
- [API Documentation](#-api-documentation)
|
|
66
40
|
- [Development](#️-development)
|
|
67
41
|
- [Contributing](#-contributing)
|
|
68
|
-
- [Uninstall](#️-uninstall)
|
|
69
42
|
- [License](#-license)
|
|
70
43
|
- [Related Projects](#-related-projects)
|
|
71
44
|
|
|
@@ -73,32 +46,11 @@ These widgets are enabled by default. Just install and run!
|
|
|
73
46
|
|
|
74
47
|
## 🆕 Recent Updates
|
|
75
48
|
|
|
76
|
-
### [v2.1.
|
|
77
|
-
|
|
78
|
-
- **Automatic compact rendering** on terminals < 80 columns via tmux pane width detection:
|
|
79
|
-
```
|
|
80
|
-
S: [██░░] 51.0% | W: [████] 92.0% | 0:00 hr
|
|
81
|
-
C: [██░░] 100k/200k
|
|
82
|
-
```
|
|
83
|
-
- Widgets hidden on narrow terminals: Model, Session Clock, Session ID
|
|
84
|
-
- Progress bars shrink from 15 to 4 characters, labels abbreviated (Session → S, Weekly → W, Context → C)
|
|
85
|
-
- Orphaned separators automatically removed when adjacent widgets are hidden
|
|
86
|
-
- FlexMode `-40` override on narrow terminals (no auto-compact sidebar)
|
|
87
|
-
- Works with any tmux-based remote access setup (SSH clients, mobile terminals, etc.)
|
|
88
|
-
|
|
89
|
-
### [v2.0.46](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.0.46) - Upstream sync
|
|
90
|
-
|
|
91
|
-
- Merged 101 upstream commits: line reorder move mode, Claude Session ID widget, `CLAUDE_CONFIG_DIR` support, Windows fixes, Block Timer fixes, model string/object handling fix, and more
|
|
92
|
-
|
|
93
|
-
### [v2.0.45](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.0.45) - Extra usage spending display
|
|
94
|
-
|
|
95
|
-
- Extra usage spending display in Reset Timer widget (e.g. `Extra: €41.24/€47.00`)
|
|
96
|
-
- Auto-detect currency from timezone (Europe/* = €, else $)
|
|
49
|
+
### [v2.1.2](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.2) - Token auth fallback and 401 handling
|
|
97
50
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
-
|
|
101
|
-
- Merged latest changes from upstream ccstatusline
|
|
51
|
+
- [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): macOS file credential fallback — reads `~/.claude/.credentials.json` when Keychain entry is missing
|
|
52
|
+
- [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): 401 auth error handling — invalidates cached token so next call re-reads from disk after `/login`
|
|
53
|
+
- Synced with upstream (101 commits)
|
|
102
54
|
|
|
103
55
|
### v2.0.16 - Add fish style path abbreviation toggle to Current Working Directory widget
|
|
104
56
|
|
|
@@ -114,7 +66,7 @@ These widgets are enabled by default. Just install and run!
|
|
|
114
66
|
|
|
115
67
|
- **👾 Emoji Support** - You can now paste emoji into the custom text widget. You can also turn on the merge option to get emoji labels for your widgets like this:
|
|
116
68
|
|
|
117
|
-

|
|
69
|
+

|
|
118
70
|
|
|
119
71
|
### v2.0.11 - Unlimited Status Lines
|
|
120
72
|
|
|
@@ -127,13 +79,13 @@ These widgets are enabled by default. Just install and run!
|
|
|
127
79
|
|
|
128
80
|
### v2.0.8 - Powerline Auto-Alignment
|
|
129
81
|
|
|
130
|
-

|
|
82
|
+

|
|
131
83
|
|
|
132
84
|
- **🎯 Widget Alignment** - Auto-align widgets across multiple status lines in Powerline mode for a clean, columnar layout (toggle with 'a' in Powerline Setup)
|
|
133
85
|
|
|
134
86
|
### v2.0.7 - Current Working Directory & Session Cost
|
|
135
87
|
|
|
136
|
-

|
|
88
|
+

|
|
137
89
|
|
|
138
90
|
- **📁 Current Working Directory** - Display the current working directory with configurable segment display
|
|
139
91
|
- Set the number of path segments to show (e.g., show only last 2 segments: `.../Personal/ccstatusline`)
|
|
@@ -151,7 +103,7 @@ These widgets are enabled by default. Just install and run!
|
|
|
151
103
|
|
|
152
104
|
### v2.0.2 - Block Timer Widget
|
|
153
105
|
|
|
154
|
-

|
|
106
|
+

|
|
155
107
|
|
|
156
108
|
- **⏱️ Block Timer** - Track your progress through 5-hour Claude Code blocks
|
|
157
109
|
- Displays time elapsed in current block as hours/minutes (e.g., "3hr 45m")
|
|
@@ -170,13 +122,9 @@ These widgets are enabled by default. Just install and run!
|
|
|
170
122
|
|
|
171
123
|
---
|
|
172
124
|
|
|
173
|
-
</div>
|
|
174
|
-
|
|
175
125
|
## ✨ Features
|
|
176
126
|
|
|
177
127
|
- **📊 Real-time Metrics** - Display model name, git branch, token usage, session duration, block timer, and more
|
|
178
|
-
- **📈 API Usage Tracking** - Real-time 5-hour session and weekly utilization from Anthropic API with progress bars
|
|
179
|
-
- **⏱️ Reset Timer** - Countdown to when your 5-hour session window resets
|
|
180
128
|
- **🎨 Fully Customizable** - Choose what to display and customize colors for each element
|
|
181
129
|
- **⚡ Powerline Support** - Beautiful Powerline-style rendering with arrow separators, caps, and custom fonts
|
|
182
130
|
- **📐 Multi-line Support** - Configure multiple independent status lines
|
|
@@ -184,7 +132,6 @@ These widgets are enabled by default. Just install and run!
|
|
|
184
132
|
- **⚙️ Global Options** - Apply consistent formatting across all widgets (padding, separators, bold, background)
|
|
185
133
|
- **🚀 Cross-platform** - Works seamlessly with both Bun and Node.js
|
|
186
134
|
- **🔧 Flexible Configuration** - Supports custom Claude Code config directory via `CLAUDE_CONFIG_DIR` environment variable
|
|
187
|
-
- **📱 Narrow Terminal Detection** - Auto-compact rendering on terminals < 80 columns via tmux pane width detection
|
|
188
135
|
- **📏 Smart Width Detection** - Automatically adapts to terminal width with flex separators
|
|
189
136
|
- **⚡ Zero Config** - Sensible defaults that work out of the box
|
|
190
137
|
|
|
@@ -196,10 +143,10 @@ These widgets are enabled by default. Just install and run!
|
|
|
196
143
|
|
|
197
144
|
```bash
|
|
198
145
|
# Run the configuration TUI with npm
|
|
199
|
-
npx ccstatusline
|
|
146
|
+
npx ccstatusline@latest
|
|
200
147
|
|
|
201
148
|
# Or with Bun (faster)
|
|
202
|
-
bunx ccstatusline
|
|
149
|
+
bunx ccstatusline@latest
|
|
203
150
|
```
|
|
204
151
|
|
|
205
152
|
### Configure ccstatusline
|
|
@@ -238,19 +185,19 @@ ccstatusline works seamlessly on Windows with full feature compatibility across
|
|
|
238
185
|
irm bun.sh/install.ps1 | iex
|
|
239
186
|
|
|
240
187
|
# Run ccstatusline
|
|
241
|
-
bunx ccstatusline
|
|
188
|
+
bunx ccstatusline@latest
|
|
242
189
|
```
|
|
243
190
|
|
|
244
191
|
#### Option 2: Using Node.js
|
|
245
192
|
```powershell
|
|
246
193
|
# Using npm
|
|
247
|
-
npx ccstatusline
|
|
194
|
+
npx ccstatusline@latest
|
|
248
195
|
|
|
249
196
|
# Or with Yarn
|
|
250
|
-
yarn dlx ccstatusline
|
|
197
|
+
yarn dlx ccstatusline@latest
|
|
251
198
|
|
|
252
199
|
# Or with pnpm
|
|
253
|
-
pnpm dlx ccstatusline
|
|
200
|
+
pnpm dlx ccstatusline@latest
|
|
254
201
|
```
|
|
255
202
|
|
|
256
203
|
### Windows-Specific Features
|
|
@@ -305,7 +252,7 @@ winget install Git.Git
|
|
|
305
252
|
**Issue**: Permission errors during installation
|
|
306
253
|
```powershell
|
|
307
254
|
# Use non-global installation (recommended)
|
|
308
|
-
npx ccstatusline
|
|
255
|
+
npx ccstatusline@latest
|
|
309
256
|
|
|
310
257
|
# Or run PowerShell as Administrator for global install
|
|
311
258
|
```
|
|
@@ -333,7 +280,7 @@ ccstatusline works perfectly in WSL environments:
|
|
|
333
280
|
# Install in WSL Ubuntu/Debian
|
|
334
281
|
curl -fsSL https://bun.sh/install | bash
|
|
335
282
|
source ~/.bashrc
|
|
336
|
-
bunx ccstatusline
|
|
283
|
+
bunx ccstatusline@latest
|
|
337
284
|
```
|
|
338
285
|
|
|
339
286
|
**WSL Benefits**:
|
|
@@ -370,14 +317,14 @@ Configure ccstatusline in your Claude Code settings:
|
|
|
370
317
|
**For Bun users**:
|
|
371
318
|
```json
|
|
372
319
|
{
|
|
373
|
-
"statusLine": "bunx ccstatusline
|
|
320
|
+
"statusLine": "bunx ccstatusline@latest"
|
|
374
321
|
}
|
|
375
322
|
```
|
|
376
323
|
|
|
377
324
|
**For npm users**:
|
|
378
325
|
```json
|
|
379
326
|
{
|
|
380
|
-
"statusLine": "npx ccstatusline
|
|
327
|
+
"statusLine": "npx ccstatusline@latest"
|
|
381
328
|
}
|
|
382
329
|
```
|
|
383
330
|
|
|
@@ -408,15 +355,7 @@ Once configured, ccstatusline automatically formats your Claude Code status line
|
|
|
408
355
|
|
|
409
356
|
### 📊 Available Widgets
|
|
410
357
|
|
|
411
|
-
#### API Usage Widgets ([pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage))
|
|
412
|
-
- **Session Usage** - 5-hour session utilization with progress bar (e.g., "Session: [████░░░░░░░░░░░] 29.0%")
|
|
413
|
-
- **Weekly Usage** - 7-day utilization with progress bar (e.g., "Weekly: [█░░░░░░░░░░░░░░] 7.0%")
|
|
414
|
-
- **Reset Timer / Extra Usage** - Time until 5-hour session window resets (e.g., "2:59 hr"), or extra usage spending when weekly limit is reached (e.g., "Extra: €41.24/€47.00")
|
|
415
|
-
- **Context Bar** - Visual context window usage with bar (e.g., "Context: [███████░░░░░░░░] 103k/200k (51%)")
|
|
416
|
-
|
|
417
|
-
#### Standard Widgets
|
|
418
358
|
- **Model Name** - Shows the current Claude model (e.g., "Claude 3.5 Sonnet")
|
|
419
|
-
- **Claude Session ID** - Shows the current session UUID
|
|
420
359
|
- **Git Branch** - Displays current git branch name
|
|
421
360
|
- **Git Changes** - Shows uncommitted insertions/deletions (e.g., "+42,-10")
|
|
422
361
|
- **Git Worktree** - Shows the name of the current git worktree
|
|
@@ -546,7 +485,7 @@ Execute shell commands and display their output dynamically:
|
|
|
546
485
|
3. Set timeout: `5000` (5 seconds for initial download)
|
|
547
486
|
4. Enable "preserve colors" to keep ccusage's color formatting
|
|
548
487
|
|
|
549
|
-

|
|
488
|
+

|
|
550
489
|
|
|
551
490
|
> 📄 **How it works:** The command receives Claude Code's JSON data via stdin, allowing ccusage to access session information, model details, and transcript data for accurate usage tracking.
|
|
552
491
|
|
|
@@ -667,17 +606,6 @@ Contributions are welcome! Please feel free to submit a Pull Request.
|
|
|
667
606
|
|
|
668
607
|
---
|
|
669
608
|
|
|
670
|
-
## 🗑️ Uninstall
|
|
671
|
-
|
|
672
|
-
```bash
|
|
673
|
-
npm uninstall -g ccstatusline-usage
|
|
674
|
-
rm -rf ~/.npm/_npx ~/.config/ccstatusline ~/.cache/ccstatusline*
|
|
675
|
-
jq 'del(.statusLine)' ~/.claude/settings.json > /tmp/cs.json && cat /tmp/cs.json > ~/.claude/settings.json
|
|
676
|
-
```
|
|
677
|
-
|
|
678
|
-
---
|
|
679
|
-
|
|
680
|
-
|
|
681
609
|
## 📄 License
|
|
682
610
|
|
|
683
611
|
[MIT](LICENSE) © Matthew Breedlove
|
|
@@ -690,11 +618,6 @@ jq 'del(.statusLine)' ~/.claude/settings.json > /tmp/cs.json && cat /tmp/cs.json
|
|
|
690
618
|
|
|
691
619
|
- GitHub: [@sirmalloc](https://github.com/sirmalloc)
|
|
692
620
|
|
|
693
|
-
**PC van Velzen** ([pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage) fork)
|
|
694
|
-
|
|
695
|
-
- GitHub: [@pcvelz](https://github.com/pcvelz)
|
|
696
|
-
|
|
697
|
-
|
|
698
621
|
---
|
|
699
622
|
|
|
700
623
|
## 🔗 Related Projects
|
|
@@ -732,8 +655,8 @@ Give a ⭐ if this project helped you!
|
|
|
732
655
|
[](https://github.com/sirmalloc/ccstatusline/network/members)
|
|
733
656
|
[](https://github.com/sirmalloc/ccstatusline/watchers)
|
|
734
657
|
|
|
735
|
-
[](https://www.npmjs.com/package/ccstatusline)
|
|
659
|
+
[](https://www.npmjs.com/package/ccstatusline)
|
|
737
660
|
[](https://github.com/sirmalloc/ccstatusline/blob/main/LICENSE)
|
|
738
661
|
[](https://bun.sh)
|
|
739
662
|
|
package/dist/ccstatusline.js
CHANGED
|
@@ -51450,8 +51450,7 @@ import { execSync as execSync3 } from "child_process";
|
|
|
51450
51450
|
import * as fs5 from "fs";
|
|
51451
51451
|
import * as path4 from "path";
|
|
51452
51452
|
var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils";
|
|
51453
|
-
var
|
|
51454
|
-
var PACKAGE_VERSION = "2.1.1";
|
|
51453
|
+
var PACKAGE_VERSION = "2.1.2";
|
|
51455
51454
|
function getPackageVersion() {
|
|
51456
51455
|
if (/^\d+\.\d+\.\d+/.test(PACKAGE_VERSION)) {
|
|
51457
51456
|
return PACKAGE_VERSION;
|
|
@@ -51531,60 +51530,6 @@ function canDetectTerminalWidth() {
|
|
|
51531
51530
|
return false;
|
|
51532
51531
|
}
|
|
51533
51532
|
}
|
|
51534
|
-
function parseTmuxClients() {
|
|
51535
|
-
try {
|
|
51536
|
-
const output = execSync3("tmux list-clients -F '#{client_width} #{client_height} #{client_tty}'", { encoding: "utf8", stdio: ["pipe", "pipe", "ignore"], timeout: 2000 }).trim();
|
|
51537
|
-
if (!output)
|
|
51538
|
-
return [];
|
|
51539
|
-
const clients = [];
|
|
51540
|
-
for (const line of output.split(`
|
|
51541
|
-
`)) {
|
|
51542
|
-
const parts = line.trim().split(" ");
|
|
51543
|
-
if (parts.length >= 3) {
|
|
51544
|
-
const width = parseInt(parts[0] ?? "", 10);
|
|
51545
|
-
const height = parseInt(parts[1] ?? "", 10);
|
|
51546
|
-
const tty2 = parts.slice(2).join(" ");
|
|
51547
|
-
if (!isNaN(width) && !isNaN(height) && tty2) {
|
|
51548
|
-
clients.push({ width, height, tty: tty2 });
|
|
51549
|
-
}
|
|
51550
|
-
}
|
|
51551
|
-
}
|
|
51552
|
-
return clients;
|
|
51553
|
-
} catch {
|
|
51554
|
-
return [];
|
|
51555
|
-
}
|
|
51556
|
-
}
|
|
51557
|
-
function getTmuxPaneWidth() {
|
|
51558
|
-
try {
|
|
51559
|
-
const output = execSync3("tmux display-message -p '#{pane_width}'", { encoding: "utf8", stdio: ["pipe", "pipe", "ignore"], timeout: 2000 }).trim();
|
|
51560
|
-
const parsed = parseInt(output, 10);
|
|
51561
|
-
if (!isNaN(parsed) && parsed > 0)
|
|
51562
|
-
return parsed;
|
|
51563
|
-
} catch {}
|
|
51564
|
-
return null;
|
|
51565
|
-
}
|
|
51566
|
-
function detectTerminalEnvironment() {
|
|
51567
|
-
const inTmux = Boolean(process.env.TMUX);
|
|
51568
|
-
let terminalWidth = getTerminalWidth();
|
|
51569
|
-
let smallestClientWidth = null;
|
|
51570
|
-
let tmuxClients = [];
|
|
51571
|
-
if (inTmux) {
|
|
51572
|
-
const paneWidth = getTmuxPaneWidth();
|
|
51573
|
-
if (paneWidth !== null)
|
|
51574
|
-
terminalWidth = paneWidth;
|
|
51575
|
-
tmuxClients = parseTmuxClients();
|
|
51576
|
-
if (tmuxClients.length > 0)
|
|
51577
|
-
smallestClientWidth = Math.min(...tmuxClients.map((c) => c.width));
|
|
51578
|
-
}
|
|
51579
|
-
const isMobile = terminalWidth !== null && terminalWidth < MOBILE_THRESHOLD;
|
|
51580
|
-
return {
|
|
51581
|
-
inTmux,
|
|
51582
|
-
tmuxClients,
|
|
51583
|
-
terminalWidth,
|
|
51584
|
-
isMobile,
|
|
51585
|
-
smallestClientWidth
|
|
51586
|
-
};
|
|
51587
|
-
}
|
|
51588
51533
|
|
|
51589
51534
|
// node_modules/ink-select-input/build/Indicator.js
|
|
51590
51535
|
var import_react27 = __toESM(require_react(), 1);
|
|
@@ -52352,8 +52297,6 @@ class ModelWidget {
|
|
|
52352
52297
|
if (context.isPreview) {
|
|
52353
52298
|
return item.rawValue ? "Claude" : "Model: Claude";
|
|
52354
52299
|
}
|
|
52355
|
-
if (context.terminalEnv?.isMobile)
|
|
52356
|
-
return null;
|
|
52357
52300
|
const model = context.data?.model;
|
|
52358
52301
|
const modelDisplayName = typeof model === "string" ? model : model?.display_name ?? model?.id;
|
|
52359
52302
|
if (modelDisplayName) {
|
|
@@ -52701,8 +52644,6 @@ function renderPowerlineStatusLine(widgets, settings, context, lineIndex = 0, gl
|
|
|
52701
52644
|
} else if (flexMode === "full-until-compact") {
|
|
52702
52645
|
terminalWidth = detectedWidth - 6;
|
|
52703
52646
|
}
|
|
52704
|
-
} else if (context.terminalEnv?.isMobile) {
|
|
52705
|
-
terminalWidth = detectedWidth - 6;
|
|
52706
52647
|
} else {
|
|
52707
52648
|
if (flexMode === "full") {
|
|
52708
52649
|
terminalWidth = detectedWidth - 6;
|
|
@@ -53077,8 +53018,6 @@ function renderStatusLine(widgets, settings, context, preRenderedWidgets, preCal
|
|
|
53077
53018
|
} else if (flexMode === "full-until-compact") {
|
|
53078
53019
|
terminalWidth = detectedWidth - 6;
|
|
53079
53020
|
}
|
|
53080
|
-
} else if (context.terminalEnv?.isMobile) {
|
|
53081
|
-
terminalWidth = detectedWidth - 6;
|
|
53082
53021
|
} else {
|
|
53083
53022
|
if (flexMode === "full") {
|
|
53084
53023
|
terminalWidth = detectedWidth - 6;
|
|
@@ -53114,18 +53053,6 @@ function renderStatusLine(widgets, settings, context, preRenderedWidgets, preCal
|
|
|
53114
53053
|
}
|
|
53115
53054
|
if (!hasContentBefore)
|
|
53116
53055
|
continue;
|
|
53117
|
-
let hasContentAfter = false;
|
|
53118
|
-
for (let j = i + 1;j < widgets.length; j++) {
|
|
53119
|
-
const nextWidget = widgets[j];
|
|
53120
|
-
if (nextWidget && nextWidget.type !== "separator" && nextWidget.type !== "flex-separator") {
|
|
53121
|
-
if (preRenderedWidgets[j]?.content) {
|
|
53122
|
-
hasContentAfter = true;
|
|
53123
|
-
break;
|
|
53124
|
-
}
|
|
53125
|
-
}
|
|
53126
|
-
}
|
|
53127
|
-
if (!hasContentAfter)
|
|
53128
|
-
continue;
|
|
53129
53056
|
const sepChar = widget.character ?? (settings.defaultSeparator ?? "|");
|
|
53130
53057
|
const formattedSep = formatSeparator(sepChar);
|
|
53131
53058
|
let separatorColor = widget.color ?? "gray";
|
|
@@ -53529,18 +53456,6 @@ class ContextPercentageWidget {
|
|
|
53529
53456
|
const usedPercentage = Math.min(100, context.tokenMetrics.contextLength / contextConfig.maxTokens * 100);
|
|
53530
53457
|
const displayPercentage = isInverse ? 100 - usedPercentage : usedPercentage;
|
|
53531
53458
|
return item.rawValue ? `${displayPercentage.toFixed(1)}%` : `Ctx: ${displayPercentage.toFixed(1)}%`;
|
|
53532
|
-
} else if (context.data?.context_window) {
|
|
53533
|
-
const ctxWindow = context.data.context_window;
|
|
53534
|
-
let usedPercentage = null;
|
|
53535
|
-
if (typeof ctxWindow.used_percentage === "number") {
|
|
53536
|
-
usedPercentage = ctxWindow.used_percentage;
|
|
53537
|
-
} else if (typeof ctxWindow.current_usage === "number" && ctxWindow.context_window_size) {
|
|
53538
|
-
usedPercentage = Math.min(100, ctxWindow.current_usage / ctxWindow.context_window_size * 100);
|
|
53539
|
-
}
|
|
53540
|
-
if (usedPercentage !== null) {
|
|
53541
|
-
const displayPercentage = isInverse ? 100 - usedPercentage : usedPercentage;
|
|
53542
|
-
return item.rawValue ? `${displayPercentage.toFixed(1)}%` : `Ctx: ${displayPercentage.toFixed(1)}%`;
|
|
53543
|
-
}
|
|
53544
53459
|
}
|
|
53545
53460
|
return null;
|
|
53546
53461
|
}
|
|
@@ -53636,8 +53551,6 @@ class SessionClockWidget {
|
|
|
53636
53551
|
if (context.isPreview) {
|
|
53637
53552
|
return item.rawValue ? "2hr 15m" : "Session: 2hr 15m";
|
|
53638
53553
|
}
|
|
53639
|
-
if (context.terminalEnv?.isMobile)
|
|
53640
|
-
return null;
|
|
53641
53554
|
const duration3 = context.sessionDuration ?? "0m";
|
|
53642
53555
|
return item.rawValue ? duration3 : `Session: ${duration3}`;
|
|
53643
53556
|
}
|
|
@@ -54463,13 +54376,13 @@ class ClaudeSessionIdWidget {
|
|
|
54463
54376
|
render(item, context, settings) {
|
|
54464
54377
|
if (context.isPreview) {
|
|
54465
54378
|
return item.rawValue ? "preview-session-id" : "Session ID: preview-session-id";
|
|
54379
|
+
} else {
|
|
54380
|
+
const sessionId = context.data?.session_id;
|
|
54381
|
+
if (!sessionId) {
|
|
54382
|
+
return null;
|
|
54383
|
+
}
|
|
54384
|
+
return item.rawValue ? sessionId : `Session ID: ${sessionId}`;
|
|
54466
54385
|
}
|
|
54467
|
-
if (context.terminalEnv?.isMobile)
|
|
54468
|
-
return null;
|
|
54469
|
-
const sessionId = context.data?.session_id;
|
|
54470
|
-
if (!sessionId)
|
|
54471
|
-
return null;
|
|
54472
|
-
return item.rawValue ? sessionId : `Session ID: ${sessionId}`;
|
|
54473
54386
|
}
|
|
54474
54387
|
supportsRawValue() {
|
|
54475
54388
|
return true;
|
|
@@ -54494,35 +54407,43 @@ var cachedData = null;
|
|
|
54494
54407
|
var cacheTime = 0;
|
|
54495
54408
|
var cachedToken = null;
|
|
54496
54409
|
var tokenCacheTime = 0;
|
|
54410
|
+
var CRED_FILE = path5.join(process.env.HOME ?? "", ".claude", ".credentials.json");
|
|
54411
|
+
function readTokenFromFile() {
|
|
54412
|
+
try {
|
|
54413
|
+
const creds = JSON.parse(fs6.readFileSync(CRED_FILE, "utf8"));
|
|
54414
|
+
return creds?.claudeAiOauth?.accessToken ?? null;
|
|
54415
|
+
} catch {
|
|
54416
|
+
return null;
|
|
54417
|
+
}
|
|
54418
|
+
}
|
|
54419
|
+
function readTokenFromKeychain() {
|
|
54420
|
+
try {
|
|
54421
|
+
const result = execSync8('security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null', { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
54422
|
+
const parsed = JSON.parse(result);
|
|
54423
|
+
return parsed?.claudeAiOauth?.accessToken ?? null;
|
|
54424
|
+
} catch {
|
|
54425
|
+
return null;
|
|
54426
|
+
}
|
|
54427
|
+
}
|
|
54428
|
+
function invalidateTokenCache() {
|
|
54429
|
+
cachedToken = null;
|
|
54430
|
+
tokenCacheTime = 0;
|
|
54431
|
+
}
|
|
54497
54432
|
function getToken() {
|
|
54498
54433
|
const now = Math.floor(Date.now() / 1000);
|
|
54499
54434
|
if (cachedToken && now - tokenCacheTime < TOKEN_CACHE_MAX_AGE) {
|
|
54500
54435
|
return cachedToken;
|
|
54501
54436
|
}
|
|
54502
|
-
|
|
54503
|
-
|
|
54504
|
-
|
|
54505
|
-
|
|
54506
|
-
|
|
54507
|
-
|
|
54508
|
-
|
|
54509
|
-
|
|
54510
|
-
tokenCacheTime = now;
|
|
54511
|
-
}
|
|
54512
|
-
return token;
|
|
54513
|
-
} else {
|
|
54514
|
-
const credFile = path5.join(process.env.HOME ?? "", ".claude", ".credentials.json");
|
|
54515
|
-
const creds = JSON.parse(fs6.readFileSync(credFile, "utf8"));
|
|
54516
|
-
const token = creds?.claudeAiOauth?.accessToken ?? null;
|
|
54517
|
-
if (token) {
|
|
54518
|
-
cachedToken = token;
|
|
54519
|
-
tokenCacheTime = now;
|
|
54520
|
-
}
|
|
54521
|
-
return token;
|
|
54522
|
-
}
|
|
54523
|
-
} catch {
|
|
54524
|
-
return null;
|
|
54437
|
+
let token = null;
|
|
54438
|
+
if (process.platform === "darwin")
|
|
54439
|
+
token = readTokenFromKeychain();
|
|
54440
|
+
if (!token)
|
|
54441
|
+
token = readTokenFromFile();
|
|
54442
|
+
if (token) {
|
|
54443
|
+
cachedToken = token;
|
|
54444
|
+
tokenCacheTime = now;
|
|
54525
54445
|
}
|
|
54446
|
+
return token;
|
|
54526
54447
|
}
|
|
54527
54448
|
function readStaleCache() {
|
|
54528
54449
|
try {
|
|
@@ -54550,6 +54471,8 @@ function fetchFromApi(token) {
|
|
|
54550
54471
|
res.on('end', () => {
|
|
54551
54472
|
if (res.statusCode === 200) {
|
|
54552
54473
|
process.stdout.write(data);
|
|
54474
|
+
} else if (res.statusCode === 401) {
|
|
54475
|
+
process.exit(2);
|
|
54553
54476
|
} else {
|
|
54554
54477
|
process.exit(1);
|
|
54555
54478
|
}
|
|
@@ -54564,7 +54487,14 @@ function fetchFromApi(token) {
|
|
|
54564
54487
|
timeout: 6000,
|
|
54565
54488
|
env: { ...process.env, TOKEN: token }
|
|
54566
54489
|
});
|
|
54567
|
-
if (result.error ||
|
|
54490
|
+
if (result.error || !result.stdout) {
|
|
54491
|
+
if (result.status === 2)
|
|
54492
|
+
return "auth-error";
|
|
54493
|
+
return null;
|
|
54494
|
+
}
|
|
54495
|
+
if (result.status !== 0) {
|
|
54496
|
+
if (result.status === 2)
|
|
54497
|
+
return "auth-error";
|
|
54568
54498
|
return null;
|
|
54569
54499
|
}
|
|
54570
54500
|
return result.stdout;
|
|
@@ -54612,6 +54542,13 @@ function fetchApiData() {
|
|
|
54612
54542
|
}
|
|
54613
54543
|
try {
|
|
54614
54544
|
const response = fetchFromApi(token);
|
|
54545
|
+
if (response === "auth-error") {
|
|
54546
|
+
invalidateTokenCache();
|
|
54547
|
+
const stale = readStaleCache();
|
|
54548
|
+
if (stale && !stale.error)
|
|
54549
|
+
return stale;
|
|
54550
|
+
return { error: "api-error" };
|
|
54551
|
+
}
|
|
54615
54552
|
if (!response) {
|
|
54616
54553
|
const stale = readStaleCache();
|
|
54617
54554
|
if (stale && !stale.error)
|
|
@@ -54668,8 +54605,12 @@ function getErrorMessage(error43) {
|
|
|
54668
54605
|
return "[Parse Error]";
|
|
54669
54606
|
}
|
|
54670
54607
|
}
|
|
54608
|
+
var MOBILE_THRESHOLD = 80;
|
|
54671
54609
|
var MOBILE_BAR_WIDTH = 4;
|
|
54672
54610
|
var DEFAULT_BAR_WIDTH = 15;
|
|
54611
|
+
function isMobileWidth(context) {
|
|
54612
|
+
return (context.terminalWidth ?? 0) > 0 && (context.terminalWidth ?? 0) < MOBILE_THRESHOLD;
|
|
54613
|
+
}
|
|
54673
54614
|
function makeProgressBar(percent, width = DEFAULT_BAR_WIDTH) {
|
|
54674
54615
|
const filled = Math.round(percent / 100 * width);
|
|
54675
54616
|
const empty = width - filled;
|
|
@@ -54701,7 +54642,7 @@ class SessionUsageWidget {
|
|
|
54701
54642
|
return getErrorMessage(data.error);
|
|
54702
54643
|
if (data.sessionUsage === undefined)
|
|
54703
54644
|
return null;
|
|
54704
|
-
return formatUsageBar("Session", "S", data.sessionUsage,
|
|
54645
|
+
return formatUsageBar("Session", "S", data.sessionUsage, isMobileWidth(context));
|
|
54705
54646
|
}
|
|
54706
54647
|
supportsRawValue() {
|
|
54707
54648
|
return false;
|
|
@@ -54732,7 +54673,7 @@ class WeeklyUsageWidget {
|
|
|
54732
54673
|
return getErrorMessage(data.error);
|
|
54733
54674
|
if (data.weeklyUsage === undefined)
|
|
54734
54675
|
return null;
|
|
54735
|
-
return formatUsageBar("Weekly", "W", data.weeklyUsage,
|
|
54676
|
+
return formatUsageBar("Weekly", "W", data.weeklyUsage, isMobileWidth(context));
|
|
54736
54677
|
}
|
|
54737
54678
|
supportsRawValue() {
|
|
54738
54679
|
return false;
|
|
@@ -54833,7 +54774,7 @@ class ContextBarWidget {
|
|
|
54833
54774
|
const percent = total > 0 ? used / total * 100 : 0;
|
|
54834
54775
|
const usedK = Math.round(used / 1000);
|
|
54835
54776
|
const totalK = Math.round(total / 1000);
|
|
54836
|
-
const mobile =
|
|
54777
|
+
const mobile = isMobileWidth(context);
|
|
54837
54778
|
const bar = makeProgressBar(percent, mobile ? MOBILE_BAR_WIDTH : DEFAULT_BAR_WIDTH);
|
|
54838
54779
|
const label = mobile ? "C" : "Context";
|
|
54839
54780
|
const suffix = mobile ? "" : ` (${Math.round(percent)}%)`;
|
|
@@ -58506,7 +58447,7 @@ Continue?`;
|
|
|
58506
58447
|
bold: true,
|
|
58507
58448
|
children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(dist_default4, {
|
|
58508
58449
|
name: "retro",
|
|
58509
|
-
children: "CCStatusline
|
|
58450
|
+
children: "CCStatusline Configuration"
|
|
58510
58451
|
}, undefined, false, undefined, this)
|
|
58511
58452
|
}, undefined, false, undefined, this),
|
|
58512
58453
|
/* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
|
|
@@ -59812,15 +59753,12 @@ async function renderMultipleLines(data) {
|
|
|
59812
59753
|
if (hasBlockTimer) {
|
|
59813
59754
|
blockMetrics = getBlockMetrics();
|
|
59814
59755
|
}
|
|
59815
|
-
const terminalEnv = detectTerminalEnvironment();
|
|
59816
59756
|
const context = {
|
|
59817
59757
|
data,
|
|
59818
59758
|
tokenMetrics,
|
|
59819
59759
|
sessionDuration,
|
|
59820
59760
|
blockMetrics,
|
|
59821
|
-
|
|
59822
|
-
isPreview: false,
|
|
59823
|
-
terminalEnv
|
|
59761
|
+
isPreview: false
|
|
59824
59762
|
};
|
|
59825
59763
|
const preRenderedLines = preRenderAllWidgets(lines, settings, context);
|
|
59826
59764
|
const preCalculatedMaxWidths = calculateMaxWidthsFromPreRendered(preRenderedLines, settings);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccstatusline-usage",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"description": "A customizable status line formatter for Claude Code CLI",
|
|
5
5
|
"module": "src/ccstatusline.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"prepublishOnly": "bun run build",
|
|
19
19
|
"lint": "bun tsc --noEmit; eslint . --config eslint.config.js --max-warnings=999999 --fix",
|
|
20
20
|
"test": "bun vitest",
|
|
21
|
+
"publish:safe": "bash scripts/safe-publish.sh",
|
|
21
22
|
"docs": "typedoc",
|
|
22
23
|
"docs:clean": "rm -rf docs"
|
|
23
24
|
},
|