ccstatusline-usage 2.1.1 → 2.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -102
- package/dist/ccstatusline.js +237 -211
- 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,17 @@ 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
|
|
49
|
+
### [v2.1.3](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.3) - Mobile/compact mode and thinking effort bars
|
|
94
50
|
|
|
95
|
-
-
|
|
96
|
-
-
|
|
51
|
+
- [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Mobile/compact mode** — in narrow terminals (< 80 effective columns, e.g. tmux panes), widgets auto-switch to compact rendering: `M: o4.6` instead of `Model: claude-opus-4-6`, session ID truncated to 8 chars, and multiple status lines merge into one
|
|
52
|
+
- [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Thinking effort bars** — Model widget shows `▌▌▌` bars indicating Claude Code's effort level (1=low, 2=medium, 3=high). Reads from `CLAUDE_CODE_EFFORT_LEVEL` env var or `~/.claude/settings.json`
|
|
53
|
+
- [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Tmux pane width detection** — correctly detects actual pane width via `tmux display-message` instead of falling back to default 80 columns
|
|
97
54
|
|
|
98
|
-
### [v2.
|
|
55
|
+
### [v2.1.2](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.2) - Token auth fallback and 401 handling
|
|
99
56
|
|
|
100
|
-
-
|
|
101
|
-
-
|
|
57
|
+
- [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): macOS file credential fallback — reads `~/.claude/.credentials.json` when Keychain entry is missing
|
|
58
|
+
- [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`
|
|
59
|
+
- Synced with upstream (101 commits)
|
|
102
60
|
|
|
103
61
|
### v2.0.16 - Add fish style path abbreviation toggle to Current Working Directory widget
|
|
104
62
|
|
|
@@ -114,7 +72,7 @@ These widgets are enabled by default. Just install and run!
|
|
|
114
72
|
|
|
115
73
|
- **👾 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
74
|
|
|
117
|
-

|
|
75
|
+

|
|
118
76
|
|
|
119
77
|
### v2.0.11 - Unlimited Status Lines
|
|
120
78
|
|
|
@@ -127,13 +85,13 @@ These widgets are enabled by default. Just install and run!
|
|
|
127
85
|
|
|
128
86
|
### v2.0.8 - Powerline Auto-Alignment
|
|
129
87
|
|
|
130
|
-

|
|
88
|
+

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

|
|
94
|
+

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

|
|
112
|
+

|
|
155
113
|
|
|
156
114
|
- **⏱️ Block Timer** - Track your progress through 5-hour Claude Code blocks
|
|
157
115
|
- Displays time elapsed in current block as hours/minutes (e.g., "3hr 45m")
|
|
@@ -170,13 +128,9 @@ These widgets are enabled by default. Just install and run!
|
|
|
170
128
|
|
|
171
129
|
---
|
|
172
130
|
|
|
173
|
-
</div>
|
|
174
|
-
|
|
175
131
|
## ✨ Features
|
|
176
132
|
|
|
177
133
|
- **📊 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
134
|
- **🎨 Fully Customizable** - Choose what to display and customize colors for each element
|
|
181
135
|
- **⚡ Powerline Support** - Beautiful Powerline-style rendering with arrow separators, caps, and custom fonts
|
|
182
136
|
- **📐 Multi-line Support** - Configure multiple independent status lines
|
|
@@ -184,7 +138,6 @@ These widgets are enabled by default. Just install and run!
|
|
|
184
138
|
- **⚙️ Global Options** - Apply consistent formatting across all widgets (padding, separators, bold, background)
|
|
185
139
|
- **🚀 Cross-platform** - Works seamlessly with both Bun and Node.js
|
|
186
140
|
- **🔧 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
141
|
- **📏 Smart Width Detection** - Automatically adapts to terminal width with flex separators
|
|
189
142
|
- **⚡ Zero Config** - Sensible defaults that work out of the box
|
|
190
143
|
|
|
@@ -196,10 +149,10 @@ These widgets are enabled by default. Just install and run!
|
|
|
196
149
|
|
|
197
150
|
```bash
|
|
198
151
|
# Run the configuration TUI with npm
|
|
199
|
-
npx ccstatusline
|
|
152
|
+
npx ccstatusline@latest
|
|
200
153
|
|
|
201
154
|
# Or with Bun (faster)
|
|
202
|
-
bunx ccstatusline
|
|
155
|
+
bunx ccstatusline@latest
|
|
203
156
|
```
|
|
204
157
|
|
|
205
158
|
### Configure ccstatusline
|
|
@@ -238,19 +191,19 @@ ccstatusline works seamlessly on Windows with full feature compatibility across
|
|
|
238
191
|
irm bun.sh/install.ps1 | iex
|
|
239
192
|
|
|
240
193
|
# Run ccstatusline
|
|
241
|
-
bunx ccstatusline
|
|
194
|
+
bunx ccstatusline@latest
|
|
242
195
|
```
|
|
243
196
|
|
|
244
197
|
#### Option 2: Using Node.js
|
|
245
198
|
```powershell
|
|
246
199
|
# Using npm
|
|
247
|
-
npx ccstatusline
|
|
200
|
+
npx ccstatusline@latest
|
|
248
201
|
|
|
249
202
|
# Or with Yarn
|
|
250
|
-
yarn dlx ccstatusline
|
|
203
|
+
yarn dlx ccstatusline@latest
|
|
251
204
|
|
|
252
205
|
# Or with pnpm
|
|
253
|
-
pnpm dlx ccstatusline
|
|
206
|
+
pnpm dlx ccstatusline@latest
|
|
254
207
|
```
|
|
255
208
|
|
|
256
209
|
### Windows-Specific Features
|
|
@@ -305,7 +258,7 @@ winget install Git.Git
|
|
|
305
258
|
**Issue**: Permission errors during installation
|
|
306
259
|
```powershell
|
|
307
260
|
# Use non-global installation (recommended)
|
|
308
|
-
npx ccstatusline
|
|
261
|
+
npx ccstatusline@latest
|
|
309
262
|
|
|
310
263
|
# Or run PowerShell as Administrator for global install
|
|
311
264
|
```
|
|
@@ -333,7 +286,7 @@ ccstatusline works perfectly in WSL environments:
|
|
|
333
286
|
# Install in WSL Ubuntu/Debian
|
|
334
287
|
curl -fsSL https://bun.sh/install | bash
|
|
335
288
|
source ~/.bashrc
|
|
336
|
-
bunx ccstatusline
|
|
289
|
+
bunx ccstatusline@latest
|
|
337
290
|
```
|
|
338
291
|
|
|
339
292
|
**WSL Benefits**:
|
|
@@ -370,14 +323,14 @@ Configure ccstatusline in your Claude Code settings:
|
|
|
370
323
|
**For Bun users**:
|
|
371
324
|
```json
|
|
372
325
|
{
|
|
373
|
-
"statusLine": "bunx ccstatusline
|
|
326
|
+
"statusLine": "bunx ccstatusline@latest"
|
|
374
327
|
}
|
|
375
328
|
```
|
|
376
329
|
|
|
377
330
|
**For npm users**:
|
|
378
331
|
```json
|
|
379
332
|
{
|
|
380
|
-
"statusLine": "npx ccstatusline
|
|
333
|
+
"statusLine": "npx ccstatusline@latest"
|
|
381
334
|
}
|
|
382
335
|
```
|
|
383
336
|
|
|
@@ -408,15 +361,7 @@ Once configured, ccstatusline automatically formats your Claude Code status line
|
|
|
408
361
|
|
|
409
362
|
### 📊 Available Widgets
|
|
410
363
|
|
|
411
|
-
|
|
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
|
-
- **Model Name** - Shows the current Claude model (e.g., "Claude 3.5 Sonnet")
|
|
419
|
-
- **Claude Session ID** - Shows the current session UUID
|
|
364
|
+
- **Model Name** - Shows the current Claude model with thinking effort bars (`▌▌▌`) indicating low/medium/high effort
|
|
420
365
|
- **Git Branch** - Displays current git branch name
|
|
421
366
|
- **Git Changes** - Shows uncommitted insertions/deletions (e.g., "+42,-10")
|
|
422
367
|
- **Git Worktree** - Shows the name of the current git worktree
|
|
@@ -546,7 +491,7 @@ Execute shell commands and display their output dynamically:
|
|
|
546
491
|
3. Set timeout: `5000` (5 seconds for initial download)
|
|
547
492
|
4. Enable "preserve colors" to keep ccusage's color formatting
|
|
548
493
|
|
|
549
|
-

|
|
494
|
+

|
|
550
495
|
|
|
551
496
|
> 📄 **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
497
|
|
|
@@ -667,17 +612,6 @@ Contributions are welcome! Please feel free to submit a Pull Request.
|
|
|
667
612
|
|
|
668
613
|
---
|
|
669
614
|
|
|
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
615
|
## 📄 License
|
|
682
616
|
|
|
683
617
|
[MIT](LICENSE) © Matthew Breedlove
|
|
@@ -690,11 +624,6 @@ jq 'del(.statusLine)' ~/.claude/settings.json > /tmp/cs.json && cat /tmp/cs.json
|
|
|
690
624
|
|
|
691
625
|
- GitHub: [@sirmalloc](https://github.com/sirmalloc)
|
|
692
626
|
|
|
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
627
|
---
|
|
699
628
|
|
|
700
629
|
## 🔗 Related Projects
|
|
@@ -732,8 +661,8 @@ Give a ⭐ if this project helped you!
|
|
|
732
661
|
[](https://github.com/sirmalloc/ccstatusline/network/members)
|
|
733
662
|
[](https://github.com/sirmalloc/ccstatusline/watchers)
|
|
734
663
|
|
|
735
|
-
[](https://www.npmjs.com/package/ccstatusline)
|
|
665
|
+
[](https://www.npmjs.com/package/ccstatusline)
|
|
737
666
|
[](https://github.com/sirmalloc/ccstatusline/blob/main/LICENSE)
|
|
738
667
|
[](https://bun.sh)
|
|
739
668
|
|
package/dist/ccstatusline.js
CHANGED
|
@@ -32383,8 +32383,8 @@ var require_utils = __commonJS((exports) => {
|
|
|
32383
32383
|
}
|
|
32384
32384
|
return output;
|
|
32385
32385
|
};
|
|
32386
|
-
exports.basename = (
|
|
32387
|
-
const segs =
|
|
32386
|
+
exports.basename = (path7, { windows } = {}) => {
|
|
32387
|
+
const segs = path7.split(windows ? /[\\/]/ : "/");
|
|
32388
32388
|
const last = segs[segs.length - 1];
|
|
32389
32389
|
if (last === "") {
|
|
32390
32390
|
return segs[segs.length - 2];
|
|
@@ -50988,7 +50988,7 @@ var SettingsSchema = exports_external.object({
|
|
|
50988
50988
|
{ id: "sep2", type: "separator" },
|
|
50989
50989
|
{ id: "reset-timer", type: "reset-timer", color: "brightBlue" },
|
|
50990
50990
|
{ id: "sep3", type: "separator" },
|
|
50991
|
-
{ id: "model", type: "model", color: "
|
|
50991
|
+
{ id: "model", type: "model", color: "ansi256:124" },
|
|
50992
50992
|
{ id: "sep4", type: "separator" },
|
|
50993
50993
|
{ id: "session-id", type: "claude-session-id", color: "cyan" }
|
|
50994
50994
|
],
|
|
@@ -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.3";
|
|
51455
51454
|
function getPackageVersion() {
|
|
51456
51455
|
if (/^\d+\.\d+\.\d+/.test(PACKAGE_VERSION)) {
|
|
51457
51456
|
return PACKAGE_VERSION;
|
|
@@ -51471,6 +51470,14 @@ function getPackageVersion() {
|
|
|
51471
51470
|
return "";
|
|
51472
51471
|
}
|
|
51473
51472
|
function getTerminalWidth() {
|
|
51473
|
+
if (process.env.TMUX) {
|
|
51474
|
+
try {
|
|
51475
|
+
const output = execSync3("tmux display-message -p '#{pane_width}'", { encoding: "utf8", stdio: ["pipe", "pipe", "ignore"], timeout: 2000 }).trim();
|
|
51476
|
+
const parsed = parseInt(output, 10);
|
|
51477
|
+
if (!isNaN(parsed) && parsed > 0)
|
|
51478
|
+
return parsed;
|
|
51479
|
+
} catch {}
|
|
51480
|
+
}
|
|
51474
51481
|
try {
|
|
51475
51482
|
const tty2 = execSync3("ps -o tty= -p $(ps -o ppid= -p $$)", {
|
|
51476
51483
|
encoding: "utf8",
|
|
@@ -51531,60 +51538,6 @@ function canDetectTerminalWidth() {
|
|
|
51531
51538
|
return false;
|
|
51532
51539
|
}
|
|
51533
51540
|
}
|
|
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
51541
|
|
|
51589
51542
|
// node_modules/ink-select-input/build/Indicator.js
|
|
51590
51543
|
var import_react27 = __toESM(require_react(), 1);
|
|
@@ -52335,9 +52288,61 @@ function getDefaultPowerlineTheme() {
|
|
|
52335
52288
|
}
|
|
52336
52289
|
|
|
52337
52290
|
// src/widgets/Model.ts
|
|
52291
|
+
import * as fs6 from "fs";
|
|
52292
|
+
import { homedir as homedir4 } from "os";
|
|
52293
|
+
import * as path5 from "path";
|
|
52294
|
+
var MOBILE_THRESHOLD = 80;
|
|
52295
|
+
function compactModelName(name) {
|
|
52296
|
+
const stripped = name.replace(/^claude-/, "");
|
|
52297
|
+
const match = /^([a-z]+)-(\d+)-(\d+)/.exec(stripped);
|
|
52298
|
+
if (match) {
|
|
52299
|
+
const letter = match[1]?.charAt(0) ?? "";
|
|
52300
|
+
return `${letter}${match[2]}.${match[3]}`;
|
|
52301
|
+
}
|
|
52302
|
+
return stripped;
|
|
52303
|
+
}
|
|
52304
|
+
function getEffortLevel() {
|
|
52305
|
+
const envLevel = process.env.CLAUDE_CODE_EFFORT_LEVEL;
|
|
52306
|
+
if (envLevel)
|
|
52307
|
+
return envLevel.toLowerCase();
|
|
52308
|
+
try {
|
|
52309
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR ?? path5.join(homedir4(), ".claude");
|
|
52310
|
+
const settingsPath = path5.join(configDir, "settings.json");
|
|
52311
|
+
const content = fs6.readFileSync(settingsPath, "utf-8");
|
|
52312
|
+
const settings = JSON.parse(content);
|
|
52313
|
+
if (settings.effortLevel)
|
|
52314
|
+
return settings.effortLevel.toLowerCase();
|
|
52315
|
+
} catch {}
|
|
52316
|
+
return "high";
|
|
52317
|
+
}
|
|
52318
|
+
function effortToLevel(effort) {
|
|
52319
|
+
switch (effort) {
|
|
52320
|
+
case "low":
|
|
52321
|
+
return 1;
|
|
52322
|
+
case "medium":
|
|
52323
|
+
return 2;
|
|
52324
|
+
default:
|
|
52325
|
+
return 3;
|
|
52326
|
+
}
|
|
52327
|
+
}
|
|
52328
|
+
function renderThinkingBars(level, settings) {
|
|
52329
|
+
if (level <= 0)
|
|
52330
|
+
return "";
|
|
52331
|
+
const colorLevel = getColorLevelString(settings.colorLevel);
|
|
52332
|
+
const activeChalk = getChalkColor("red", colorLevel);
|
|
52333
|
+
const dimChalk = getChalkColor("brightBlack", colorLevel);
|
|
52334
|
+
const bars = ["▌", "▌", "▌"];
|
|
52335
|
+
return " " + bars.map((bar, i) => {
|
|
52336
|
+
if (i < level) {
|
|
52337
|
+
return activeChalk ? activeChalk(bar) : bar;
|
|
52338
|
+
}
|
|
52339
|
+
return dimChalk ? dimChalk(bar) : bar;
|
|
52340
|
+
}).join("");
|
|
52341
|
+
}
|
|
52342
|
+
|
|
52338
52343
|
class ModelWidget {
|
|
52339
52344
|
getDefaultColor() {
|
|
52340
|
-
return "
|
|
52345
|
+
return "ansi256:124";
|
|
52341
52346
|
}
|
|
52342
52347
|
getDescription() {
|
|
52343
52348
|
return "Displays the Claude model name (e.g., Claude 3.5 Sonnet)";
|
|
@@ -52350,16 +52355,21 @@ class ModelWidget {
|
|
|
52350
52355
|
}
|
|
52351
52356
|
render(item, context, settings) {
|
|
52352
52357
|
if (context.isPreview) {
|
|
52353
|
-
|
|
52358
|
+
const bars2 = renderThinkingBars(3, settings);
|
|
52359
|
+
return item.rawValue ? `Claude${bars2}` : `Model: Claude${bars2}`;
|
|
52354
52360
|
}
|
|
52355
|
-
if (context.terminalEnv?.isMobile)
|
|
52356
|
-
return null;
|
|
52357
52361
|
const model = context.data?.model;
|
|
52362
|
+
const modelId = typeof model === "string" ? model : model?.id;
|
|
52358
52363
|
const modelDisplayName = typeof model === "string" ? model : model?.display_name ?? model?.id;
|
|
52359
|
-
if (modelDisplayName)
|
|
52360
|
-
return
|
|
52364
|
+
if (!modelDisplayName)
|
|
52365
|
+
return null;
|
|
52366
|
+
const level = effortToLevel(getEffortLevel());
|
|
52367
|
+
const bars = renderThinkingBars(level, settings);
|
|
52368
|
+
const mobile = (context.terminalWidth ?? 0) > 0 && (context.terminalWidth ?? 0) < MOBILE_THRESHOLD;
|
|
52369
|
+
if (mobile && modelId) {
|
|
52370
|
+
return `M: ${compactModelName(modelId)}${bars}`;
|
|
52361
52371
|
}
|
|
52362
|
-
return
|
|
52372
|
+
return item.rawValue ? `${modelDisplayName}${bars}` : `Model: ${modelDisplayName}${bars}`;
|
|
52363
52373
|
}
|
|
52364
52374
|
supportsRawValue() {
|
|
52365
52375
|
return true;
|
|
@@ -52701,8 +52711,6 @@ function renderPowerlineStatusLine(widgets, settings, context, lineIndex = 0, gl
|
|
|
52701
52711
|
} else if (flexMode === "full-until-compact") {
|
|
52702
52712
|
terminalWidth = detectedWidth - 6;
|
|
52703
52713
|
}
|
|
52704
|
-
} else if (context.terminalEnv?.isMobile) {
|
|
52705
|
-
terminalWidth = detectedWidth - 6;
|
|
52706
52714
|
} else {
|
|
52707
52715
|
if (flexMode === "full") {
|
|
52708
52716
|
terminalWidth = detectedWidth - 6;
|
|
@@ -53077,8 +53085,6 @@ function renderStatusLine(widgets, settings, context, preRenderedWidgets, preCal
|
|
|
53077
53085
|
} else if (flexMode === "full-until-compact") {
|
|
53078
53086
|
terminalWidth = detectedWidth - 6;
|
|
53079
53087
|
}
|
|
53080
|
-
} else if (context.terminalEnv?.isMobile) {
|
|
53081
|
-
terminalWidth = detectedWidth - 6;
|
|
53082
53088
|
} else {
|
|
53083
53089
|
if (flexMode === "full") {
|
|
53084
53090
|
terminalWidth = detectedWidth - 6;
|
|
@@ -53114,18 +53120,6 @@ function renderStatusLine(widgets, settings, context, preRenderedWidgets, preCal
|
|
|
53114
53120
|
}
|
|
53115
53121
|
if (!hasContentBefore)
|
|
53116
53122
|
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
53123
|
const sepChar = widget.character ?? (settings.defaultSeparator ?? "|");
|
|
53130
53124
|
const formattedSep = formatSeparator(sepChar);
|
|
53131
53125
|
let separatorColor = widget.color ?? "gray";
|
|
@@ -53529,18 +53523,6 @@ class ContextPercentageWidget {
|
|
|
53529
53523
|
const usedPercentage = Math.min(100, context.tokenMetrics.contextLength / contextConfig.maxTokens * 100);
|
|
53530
53524
|
const displayPercentage = isInverse ? 100 - usedPercentage : usedPercentage;
|
|
53531
53525
|
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
53526
|
}
|
|
53545
53527
|
return null;
|
|
53546
53528
|
}
|
|
@@ -53636,8 +53618,6 @@ class SessionClockWidget {
|
|
|
53636
53618
|
if (context.isPreview) {
|
|
53637
53619
|
return item.rawValue ? "2hr 15m" : "Session: 2hr 15m";
|
|
53638
53620
|
}
|
|
53639
|
-
if (context.terminalEnv?.isMobile)
|
|
53640
|
-
return null;
|
|
53641
53621
|
const duration3 = context.sessionDuration ?? "0m";
|
|
53642
53622
|
return item.rawValue ? duration3 : `Session: ${duration3}`;
|
|
53643
53623
|
}
|
|
@@ -54359,13 +54339,13 @@ class CurrentWorkingDirWidget {
|
|
|
54359
54339
|
supportsColors(item) {
|
|
54360
54340
|
return true;
|
|
54361
54341
|
}
|
|
54362
|
-
abbreviatePath(
|
|
54342
|
+
abbreviatePath(path6) {
|
|
54363
54343
|
const homeDir = os5.homedir();
|
|
54364
|
-
const useBackslash =
|
|
54344
|
+
const useBackslash = path6.includes("\\") && !path6.includes("/");
|
|
54365
54345
|
const sep = useBackslash ? "\\" : "/";
|
|
54366
|
-
let normalizedPath =
|
|
54367
|
-
if (
|
|
54368
|
-
normalizedPath = "~" +
|
|
54346
|
+
let normalizedPath = path6;
|
|
54347
|
+
if (path6.startsWith(homeDir)) {
|
|
54348
|
+
normalizedPath = "~" + path6.slice(homeDir.length);
|
|
54369
54349
|
}
|
|
54370
54350
|
const parts = normalizedPath.split(/[\\/]+/).filter((part) => part !== "");
|
|
54371
54351
|
const abbreviated = parts.map((part, index) => {
|
|
@@ -54464,11 +54444,14 @@ class ClaudeSessionIdWidget {
|
|
|
54464
54444
|
if (context.isPreview) {
|
|
54465
54445
|
return item.rawValue ? "preview-session-id" : "Session ID: preview-session-id";
|
|
54466
54446
|
}
|
|
54467
|
-
if (context.terminalEnv?.isMobile)
|
|
54468
|
-
return null;
|
|
54469
54447
|
const sessionId = context.data?.session_id;
|
|
54470
|
-
if (!sessionId)
|
|
54448
|
+
if (!sessionId) {
|
|
54471
54449
|
return null;
|
|
54450
|
+
}
|
|
54451
|
+
const mobile = (context.terminalWidth ?? 0) > 0 && (context.terminalWidth ?? 0) < 80;
|
|
54452
|
+
if (mobile) {
|
|
54453
|
+
return `S: ${sessionId.slice(0, 8)}`;
|
|
54454
|
+
}
|
|
54472
54455
|
return item.rawValue ? sessionId : `Session ID: ${sessionId}`;
|
|
54473
54456
|
}
|
|
54474
54457
|
supportsRawValue() {
|
|
@@ -54483,10 +54466,10 @@ import {
|
|
|
54483
54466
|
execSync as execSync8,
|
|
54484
54467
|
spawnSync
|
|
54485
54468
|
} from "child_process";
|
|
54486
|
-
import * as
|
|
54487
|
-
import * as
|
|
54488
|
-
var CACHE_FILE =
|
|
54489
|
-
var LOCK_FILE =
|
|
54469
|
+
import * as fs7 from "fs";
|
|
54470
|
+
import * as path6 from "path";
|
|
54471
|
+
var CACHE_FILE = path6.join(process.env.HOME ?? "", ".cache", "ccstatusline-api.json");
|
|
54472
|
+
var LOCK_FILE = path6.join(process.env.HOME ?? "", ".cache", "ccstatusline-api.lock");
|
|
54490
54473
|
var CACHE_MAX_AGE = 180;
|
|
54491
54474
|
var LOCK_MAX_AGE = 30;
|
|
54492
54475
|
var TOKEN_CACHE_MAX_AGE = 3600;
|
|
@@ -54494,39 +54477,47 @@ var cachedData = null;
|
|
|
54494
54477
|
var cacheTime = 0;
|
|
54495
54478
|
var cachedToken = null;
|
|
54496
54479
|
var tokenCacheTime = 0;
|
|
54480
|
+
var CRED_FILE = path6.join(process.env.HOME ?? "", ".claude", ".credentials.json");
|
|
54481
|
+
function readTokenFromFile() {
|
|
54482
|
+
try {
|
|
54483
|
+
const creds = JSON.parse(fs7.readFileSync(CRED_FILE, "utf8"));
|
|
54484
|
+
return creds?.claudeAiOauth?.accessToken ?? null;
|
|
54485
|
+
} catch {
|
|
54486
|
+
return null;
|
|
54487
|
+
}
|
|
54488
|
+
}
|
|
54489
|
+
function readTokenFromKeychain() {
|
|
54490
|
+
try {
|
|
54491
|
+
const result = execSync8('security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null', { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
54492
|
+
const parsed = JSON.parse(result);
|
|
54493
|
+
return parsed?.claudeAiOauth?.accessToken ?? null;
|
|
54494
|
+
} catch {
|
|
54495
|
+
return null;
|
|
54496
|
+
}
|
|
54497
|
+
}
|
|
54498
|
+
function invalidateTokenCache() {
|
|
54499
|
+
cachedToken = null;
|
|
54500
|
+
tokenCacheTime = 0;
|
|
54501
|
+
}
|
|
54497
54502
|
function getToken() {
|
|
54498
54503
|
const now = Math.floor(Date.now() / 1000);
|
|
54499
54504
|
if (cachedToken && now - tokenCacheTime < TOKEN_CACHE_MAX_AGE) {
|
|
54500
54505
|
return cachedToken;
|
|
54501
54506
|
}
|
|
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;
|
|
54507
|
+
let token = null;
|
|
54508
|
+
if (process.platform === "darwin")
|
|
54509
|
+
token = readTokenFromKeychain();
|
|
54510
|
+
if (!token)
|
|
54511
|
+
token = readTokenFromFile();
|
|
54512
|
+
if (token) {
|
|
54513
|
+
cachedToken = token;
|
|
54514
|
+
tokenCacheTime = now;
|
|
54525
54515
|
}
|
|
54516
|
+
return token;
|
|
54526
54517
|
}
|
|
54527
54518
|
function readStaleCache() {
|
|
54528
54519
|
try {
|
|
54529
|
-
return JSON.parse(
|
|
54520
|
+
return JSON.parse(fs7.readFileSync(CACHE_FILE, "utf8"));
|
|
54530
54521
|
} catch {
|
|
54531
54522
|
return null;
|
|
54532
54523
|
}
|
|
@@ -54550,6 +54541,8 @@ function fetchFromApi(token) {
|
|
|
54550
54541
|
res.on('end', () => {
|
|
54551
54542
|
if (res.statusCode === 200) {
|
|
54552
54543
|
process.stdout.write(data);
|
|
54544
|
+
} else if (res.statusCode === 401) {
|
|
54545
|
+
process.exit(2);
|
|
54553
54546
|
} else {
|
|
54554
54547
|
process.exit(1);
|
|
54555
54548
|
}
|
|
@@ -54564,7 +54557,14 @@ function fetchFromApi(token) {
|
|
|
54564
54557
|
timeout: 6000,
|
|
54565
54558
|
env: { ...process.env, TOKEN: token }
|
|
54566
54559
|
});
|
|
54567
|
-
if (result.error ||
|
|
54560
|
+
if (result.error || !result.stdout) {
|
|
54561
|
+
if (result.status === 2)
|
|
54562
|
+
return "auth-error";
|
|
54563
|
+
return null;
|
|
54564
|
+
}
|
|
54565
|
+
if (result.status !== 0) {
|
|
54566
|
+
if (result.status === 2)
|
|
54567
|
+
return "auth-error";
|
|
54568
54568
|
return null;
|
|
54569
54569
|
}
|
|
54570
54570
|
return result.stdout;
|
|
@@ -54575,10 +54575,10 @@ function fetchApiData() {
|
|
|
54575
54575
|
return cachedData;
|
|
54576
54576
|
}
|
|
54577
54577
|
try {
|
|
54578
|
-
const stat =
|
|
54578
|
+
const stat = fs7.statSync(CACHE_FILE);
|
|
54579
54579
|
const fileAge = now - Math.floor(stat.mtimeMs / 1000);
|
|
54580
54580
|
if (fileAge < CACHE_MAX_AGE) {
|
|
54581
|
-
const fileData = JSON.parse(
|
|
54581
|
+
const fileData = JSON.parse(fs7.readFileSync(CACHE_FILE, "utf8"));
|
|
54582
54582
|
if (!fileData.error) {
|
|
54583
54583
|
cachedData = fileData;
|
|
54584
54584
|
cacheTime = now;
|
|
@@ -54587,7 +54587,7 @@ function fetchApiData() {
|
|
|
54587
54587
|
}
|
|
54588
54588
|
} catch {}
|
|
54589
54589
|
try {
|
|
54590
|
-
const lockStat =
|
|
54590
|
+
const lockStat = fs7.statSync(LOCK_FILE);
|
|
54591
54591
|
const lockAge = now - Math.floor(lockStat.mtimeMs / 1000);
|
|
54592
54592
|
if (lockAge < LOCK_MAX_AGE) {
|
|
54593
54593
|
const stale = readStaleCache();
|
|
@@ -54597,11 +54597,11 @@ function fetchApiData() {
|
|
|
54597
54597
|
}
|
|
54598
54598
|
} catch {}
|
|
54599
54599
|
try {
|
|
54600
|
-
const lockDir =
|
|
54601
|
-
if (!
|
|
54602
|
-
|
|
54600
|
+
const lockDir = path6.dirname(LOCK_FILE);
|
|
54601
|
+
if (!fs7.existsSync(lockDir)) {
|
|
54602
|
+
fs7.mkdirSync(lockDir, { recursive: true });
|
|
54603
54603
|
}
|
|
54604
|
-
|
|
54604
|
+
fs7.writeFileSync(LOCK_FILE, "");
|
|
54605
54605
|
} catch {}
|
|
54606
54606
|
const token = getToken();
|
|
54607
54607
|
if (!token) {
|
|
@@ -54612,6 +54612,13 @@ function fetchApiData() {
|
|
|
54612
54612
|
}
|
|
54613
54613
|
try {
|
|
54614
54614
|
const response = fetchFromApi(token);
|
|
54615
|
+
if (response === "auth-error") {
|
|
54616
|
+
invalidateTokenCache();
|
|
54617
|
+
const stale = readStaleCache();
|
|
54618
|
+
if (stale && !stale.error)
|
|
54619
|
+
return stale;
|
|
54620
|
+
return { error: "api-error" };
|
|
54621
|
+
}
|
|
54615
54622
|
if (!response) {
|
|
54616
54623
|
const stale = readStaleCache();
|
|
54617
54624
|
if (stale && !stale.error)
|
|
@@ -54640,11 +54647,11 @@ function fetchApiData() {
|
|
|
54640
54647
|
return { error: "parse-error" };
|
|
54641
54648
|
}
|
|
54642
54649
|
try {
|
|
54643
|
-
const cacheDir =
|
|
54644
|
-
if (!
|
|
54645
|
-
|
|
54650
|
+
const cacheDir = path6.dirname(CACHE_FILE);
|
|
54651
|
+
if (!fs7.existsSync(cacheDir)) {
|
|
54652
|
+
fs7.mkdirSync(cacheDir, { recursive: true });
|
|
54646
54653
|
}
|
|
54647
|
-
|
|
54654
|
+
fs7.writeFileSync(CACHE_FILE, JSON.stringify(apiData));
|
|
54648
54655
|
} catch {}
|
|
54649
54656
|
cachedData = apiData;
|
|
54650
54657
|
cacheTime = now;
|
|
@@ -54668,8 +54675,12 @@ function getErrorMessage(error43) {
|
|
|
54668
54675
|
return "[Parse Error]";
|
|
54669
54676
|
}
|
|
54670
54677
|
}
|
|
54678
|
+
var MOBILE_THRESHOLD2 = 80;
|
|
54671
54679
|
var MOBILE_BAR_WIDTH = 4;
|
|
54672
54680
|
var DEFAULT_BAR_WIDTH = 15;
|
|
54681
|
+
function isMobileWidth(context) {
|
|
54682
|
+
return (context.terminalWidth ?? 0) > 0 && (context.terminalWidth ?? 0) < MOBILE_THRESHOLD2;
|
|
54683
|
+
}
|
|
54673
54684
|
function makeProgressBar(percent, width = DEFAULT_BAR_WIDTH) {
|
|
54674
54685
|
const filled = Math.round(percent / 100 * width);
|
|
54675
54686
|
const empty = width - filled;
|
|
@@ -54701,7 +54712,7 @@ class SessionUsageWidget {
|
|
|
54701
54712
|
return getErrorMessage(data.error);
|
|
54702
54713
|
if (data.sessionUsage === undefined)
|
|
54703
54714
|
return null;
|
|
54704
|
-
return formatUsageBar("Session", "S", data.sessionUsage,
|
|
54715
|
+
return formatUsageBar("Session", "S", data.sessionUsage, isMobileWidth(context));
|
|
54705
54716
|
}
|
|
54706
54717
|
supportsRawValue() {
|
|
54707
54718
|
return false;
|
|
@@ -54732,7 +54743,7 @@ class WeeklyUsageWidget {
|
|
|
54732
54743
|
return getErrorMessage(data.error);
|
|
54733
54744
|
if (data.weeklyUsage === undefined)
|
|
54734
54745
|
return null;
|
|
54735
|
-
return formatUsageBar("Weekly", "W", data.weeklyUsage,
|
|
54746
|
+
return formatUsageBar("Weekly", "W", data.weeklyUsage, isMobileWidth(context));
|
|
54736
54747
|
}
|
|
54737
54748
|
supportsRawValue() {
|
|
54738
54749
|
return false;
|
|
@@ -54833,7 +54844,7 @@ class ContextBarWidget {
|
|
|
54833
54844
|
const percent = total > 0 ? used / total * 100 : 0;
|
|
54834
54845
|
const usedK = Math.round(used / 1000);
|
|
54835
54846
|
const totalK = Math.round(total / 1000);
|
|
54836
|
-
const mobile =
|
|
54847
|
+
const mobile = isMobileWidth(context);
|
|
54837
54848
|
const bar = makeProgressBar(percent, mobile ? MOBILE_BAR_WIDTH : DEFAULT_BAR_WIDTH);
|
|
54838
54849
|
const label = mobile ? "C" : "Context";
|
|
54839
54850
|
const suffix = mobile ? "" : ` (${Math.round(percent)}%)`;
|
|
@@ -58506,7 +58517,7 @@ Continue?`;
|
|
|
58506
58517
|
bold: true,
|
|
58507
58518
|
children: /* @__PURE__ */ jsx_dev_runtime17.jsxDEV(dist_default4, {
|
|
58508
58519
|
name: "retro",
|
|
58509
|
-
children: "CCStatusline
|
|
58520
|
+
children: "CCStatusline Configuration"
|
|
58510
58521
|
}, undefined, false, undefined, this)
|
|
58511
58522
|
}, undefined, false, undefined, this),
|
|
58512
58523
|
/* @__PURE__ */ jsx_dev_runtime17.jsxDEV(Text, {
|
|
@@ -58740,42 +58751,42 @@ var StatusJSONSchema = exports_external.looseObject({
|
|
|
58740
58751
|
});
|
|
58741
58752
|
|
|
58742
58753
|
// src/utils/jsonl.ts
|
|
58743
|
-
import * as
|
|
58744
|
-
import
|
|
58754
|
+
import * as fs8 from "fs";
|
|
58755
|
+
import path8 from "node:path";
|
|
58745
58756
|
|
|
58746
58757
|
// node_modules/tinyglobby/dist/index.mjs
|
|
58747
|
-
import
|
|
58758
|
+
import path7, { posix } from "path";
|
|
58748
58759
|
|
|
58749
58760
|
// node_modules/fdir/dist/index.mjs
|
|
58750
58761
|
import { createRequire as createRequire2 } from "module";
|
|
58751
58762
|
import { basename as basename2, dirname as dirname3, normalize, relative, resolve as resolve2, sep } from "path";
|
|
58752
58763
|
import * as nativeFs from "fs";
|
|
58753
58764
|
var __require2 = /* @__PURE__ */ createRequire2(import.meta.url);
|
|
58754
|
-
function cleanPath(
|
|
58755
|
-
let normalized = normalize(
|
|
58765
|
+
function cleanPath(path7) {
|
|
58766
|
+
let normalized = normalize(path7);
|
|
58756
58767
|
if (normalized.length > 1 && normalized[normalized.length - 1] === sep)
|
|
58757
58768
|
normalized = normalized.substring(0, normalized.length - 1);
|
|
58758
58769
|
return normalized;
|
|
58759
58770
|
}
|
|
58760
58771
|
var SLASHES_REGEX = /[\\/]/g;
|
|
58761
|
-
function convertSlashes(
|
|
58762
|
-
return
|
|
58772
|
+
function convertSlashes(path7, separator) {
|
|
58773
|
+
return path7.replace(SLASHES_REGEX, separator);
|
|
58763
58774
|
}
|
|
58764
58775
|
var WINDOWS_ROOT_DIR_REGEX = /^[a-z]:[\\/]$/i;
|
|
58765
|
-
function isRootDirectory(
|
|
58766
|
-
return
|
|
58776
|
+
function isRootDirectory(path7) {
|
|
58777
|
+
return path7 === "/" || WINDOWS_ROOT_DIR_REGEX.test(path7);
|
|
58767
58778
|
}
|
|
58768
|
-
function normalizePath(
|
|
58779
|
+
function normalizePath(path7, options) {
|
|
58769
58780
|
const { resolvePaths, normalizePath: normalizePath$1, pathSeparator } = options;
|
|
58770
|
-
const pathNeedsCleaning = process.platform === "win32" &&
|
|
58781
|
+
const pathNeedsCleaning = process.platform === "win32" && path7.includes("/") || path7.startsWith(".");
|
|
58771
58782
|
if (resolvePaths)
|
|
58772
|
-
|
|
58783
|
+
path7 = resolve2(path7);
|
|
58773
58784
|
if (normalizePath$1 || pathNeedsCleaning)
|
|
58774
|
-
|
|
58775
|
-
if (
|
|
58785
|
+
path7 = cleanPath(path7);
|
|
58786
|
+
if (path7 === ".")
|
|
58776
58787
|
return "";
|
|
58777
|
-
const needsSeperator =
|
|
58778
|
-
return convertSlashes(needsSeperator ?
|
|
58788
|
+
const needsSeperator = path7[path7.length - 1] !== pathSeparator;
|
|
58789
|
+
return convertSlashes(needsSeperator ? path7 + pathSeparator : path7, pathSeparator);
|
|
58779
58790
|
}
|
|
58780
58791
|
function joinPathWithBasePath(filename, directoryPath) {
|
|
58781
58792
|
return directoryPath + filename;
|
|
@@ -58815,9 +58826,9 @@ var pushDirectory = (directoryPath, paths) => {
|
|
|
58815
58826
|
paths.push(directoryPath || ".");
|
|
58816
58827
|
};
|
|
58817
58828
|
var pushDirectoryFilter = (directoryPath, paths, filters) => {
|
|
58818
|
-
const
|
|
58819
|
-
if (filters.every((filter) => filter(
|
|
58820
|
-
paths.push(
|
|
58829
|
+
const path7 = directoryPath || ".";
|
|
58830
|
+
if (filters.every((filter) => filter(path7, true)))
|
|
58831
|
+
paths.push(path7);
|
|
58821
58832
|
};
|
|
58822
58833
|
var empty$2 = () => {};
|
|
58823
58834
|
function build$6(root, options) {
|
|
@@ -58874,29 +58885,29 @@ var empty = () => {};
|
|
|
58874
58885
|
function build$3(options) {
|
|
58875
58886
|
return options.group ? groupFiles : empty;
|
|
58876
58887
|
}
|
|
58877
|
-
var resolveSymlinksAsync = function(
|
|
58878
|
-
const { queue, fs:
|
|
58888
|
+
var resolveSymlinksAsync = function(path7, state, callback$1) {
|
|
58889
|
+
const { queue, fs: fs8, options: { suppressErrors } } = state;
|
|
58879
58890
|
queue.enqueue();
|
|
58880
|
-
|
|
58891
|
+
fs8.realpath(path7, (error43, resolvedPath) => {
|
|
58881
58892
|
if (error43)
|
|
58882
58893
|
return queue.dequeue(suppressErrors ? null : error43, state);
|
|
58883
|
-
|
|
58894
|
+
fs8.stat(resolvedPath, (error$1, stat) => {
|
|
58884
58895
|
if (error$1)
|
|
58885
58896
|
return queue.dequeue(suppressErrors ? null : error$1, state);
|
|
58886
|
-
if (stat.isDirectory() && isRecursive(
|
|
58897
|
+
if (stat.isDirectory() && isRecursive(path7, resolvedPath, state))
|
|
58887
58898
|
return queue.dequeue(null, state);
|
|
58888
58899
|
callback$1(stat, resolvedPath);
|
|
58889
58900
|
queue.dequeue(null, state);
|
|
58890
58901
|
});
|
|
58891
58902
|
});
|
|
58892
58903
|
};
|
|
58893
|
-
var resolveSymlinks = function(
|
|
58894
|
-
const { queue, fs:
|
|
58904
|
+
var resolveSymlinks = function(path7, state, callback$1) {
|
|
58905
|
+
const { queue, fs: fs8, options: { suppressErrors } } = state;
|
|
58895
58906
|
queue.enqueue();
|
|
58896
58907
|
try {
|
|
58897
|
-
const resolvedPath =
|
|
58898
|
-
const stat =
|
|
58899
|
-
if (stat.isDirectory() && isRecursive(
|
|
58908
|
+
const resolvedPath = fs8.realpathSync(path7);
|
|
58909
|
+
const stat = fs8.statSync(resolvedPath);
|
|
58910
|
+
if (stat.isDirectory() && isRecursive(path7, resolvedPath, state))
|
|
58900
58911
|
return;
|
|
58901
58912
|
callback$1(stat, resolvedPath);
|
|
58902
58913
|
} catch (e) {
|
|
@@ -58909,10 +58920,10 @@ function build$2(options, isSynchronous) {
|
|
|
58909
58920
|
return null;
|
|
58910
58921
|
return isSynchronous ? resolveSymlinks : resolveSymlinksAsync;
|
|
58911
58922
|
}
|
|
58912
|
-
function isRecursive(
|
|
58923
|
+
function isRecursive(path7, resolved, state) {
|
|
58913
58924
|
if (state.options.useRealPaths)
|
|
58914
58925
|
return isRecursiveUsingRealPaths(resolved, state);
|
|
58915
|
-
let parent = dirname3(
|
|
58926
|
+
let parent = dirname3(path7);
|
|
58916
58927
|
let depth = 1;
|
|
58917
58928
|
while (parent !== state.root && depth < 2) {
|
|
58918
58929
|
const resolvedPath = state.symlinks.get(parent);
|
|
@@ -58922,7 +58933,7 @@ function isRecursive(path6, resolved, state) {
|
|
|
58922
58933
|
else
|
|
58923
58934
|
parent = dirname3(parent);
|
|
58924
58935
|
}
|
|
58925
|
-
state.symlinks.set(
|
|
58936
|
+
state.symlinks.set(path7, resolved);
|
|
58926
58937
|
return depth > 1;
|
|
58927
58938
|
}
|
|
58928
58939
|
function isRecursiveUsingRealPaths(resolved, state) {
|
|
@@ -58978,23 +58989,23 @@ var walkAsync = (state, crawlPath, directoryPath, currentDepth, callback$1) => {
|
|
|
58978
58989
|
state.queue.enqueue();
|
|
58979
58990
|
if (currentDepth < 0)
|
|
58980
58991
|
return state.queue.dequeue(null, state);
|
|
58981
|
-
const { fs:
|
|
58992
|
+
const { fs: fs8 } = state;
|
|
58982
58993
|
state.visited.push(crawlPath);
|
|
58983
58994
|
state.counts.directories++;
|
|
58984
|
-
|
|
58995
|
+
fs8.readdir(crawlPath || ".", readdirOpts, (error43, entries = []) => {
|
|
58985
58996
|
callback$1(entries, directoryPath, currentDepth);
|
|
58986
58997
|
state.queue.dequeue(state.options.suppressErrors ? null : error43, state);
|
|
58987
58998
|
});
|
|
58988
58999
|
};
|
|
58989
59000
|
var walkSync = (state, crawlPath, directoryPath, currentDepth, callback$1) => {
|
|
58990
|
-
const { fs:
|
|
59001
|
+
const { fs: fs8 } = state;
|
|
58991
59002
|
if (currentDepth < 0)
|
|
58992
59003
|
return;
|
|
58993
59004
|
state.visited.push(crawlPath);
|
|
58994
59005
|
state.counts.directories++;
|
|
58995
59006
|
let entries = [];
|
|
58996
59007
|
try {
|
|
58997
|
-
entries =
|
|
59008
|
+
entries = fs8.readdirSync(crawlPath || ".", readdirOpts);
|
|
58998
59009
|
} catch (e) {
|
|
58999
59010
|
if (!state.options.suppressErrors)
|
|
59000
59011
|
throw e;
|
|
@@ -59100,21 +59111,21 @@ var Walker = class {
|
|
|
59100
59111
|
const filename = this.joinPath(entry.name, directoryPath);
|
|
59101
59112
|
this.pushFile(filename, files, this.state.counts, filters);
|
|
59102
59113
|
} else if (entry.isDirectory()) {
|
|
59103
|
-
let
|
|
59104
|
-
if (exclude && exclude(entry.name,
|
|
59114
|
+
let path7 = joinDirectoryPath(entry.name, directoryPath, this.state.options.pathSeparator);
|
|
59115
|
+
if (exclude && exclude(entry.name, path7))
|
|
59105
59116
|
continue;
|
|
59106
|
-
this.pushDirectory(
|
|
59107
|
-
this.walkDirectory(this.state,
|
|
59117
|
+
this.pushDirectory(path7, paths, filters);
|
|
59118
|
+
this.walkDirectory(this.state, path7, path7, depth - 1, this.walk);
|
|
59108
59119
|
} else if (this.resolveSymlink && entry.isSymbolicLink()) {
|
|
59109
|
-
let
|
|
59110
|
-
this.resolveSymlink(
|
|
59120
|
+
let path7 = joinPathWithBasePath(entry.name, directoryPath);
|
|
59121
|
+
this.resolveSymlink(path7, this.state, (stat, resolvedPath) => {
|
|
59111
59122
|
if (stat.isDirectory()) {
|
|
59112
59123
|
resolvedPath = normalizePath(resolvedPath, this.state.options);
|
|
59113
|
-
if (exclude && exclude(entry.name, useRealPaths ? resolvedPath :
|
|
59124
|
+
if (exclude && exclude(entry.name, useRealPaths ? resolvedPath : path7 + pathSeparator))
|
|
59114
59125
|
return;
|
|
59115
|
-
this.walkDirectory(this.state, resolvedPath, useRealPaths ? resolvedPath :
|
|
59126
|
+
this.walkDirectory(this.state, resolvedPath, useRealPaths ? resolvedPath : path7 + pathSeparator, depth - 1, this.walk);
|
|
59116
59127
|
} else {
|
|
59117
|
-
resolvedPath = useRealPaths ? resolvedPath :
|
|
59128
|
+
resolvedPath = useRealPaths ? resolvedPath : path7;
|
|
59118
59129
|
const filename = basename2(resolvedPath);
|
|
59119
59130
|
const directoryPath$1 = normalizePath(dirname3(resolvedPath), this.state.options);
|
|
59120
59131
|
resolvedPath = this.joinPath(filename, directoryPath$1);
|
|
@@ -59274,7 +59285,7 @@ var Builder = class {
|
|
|
59274
59285
|
isMatch = globFn(patterns, ...options);
|
|
59275
59286
|
this.globCache[patterns.join("\x00")] = isMatch;
|
|
59276
59287
|
}
|
|
59277
|
-
this.options.filters.push((
|
|
59288
|
+
this.options.filters.push((path7) => isMatch(path7));
|
|
59278
59289
|
return this;
|
|
59279
59290
|
}
|
|
59280
59291
|
};
|
|
@@ -59353,7 +59364,7 @@ function normalizePattern(pattern, expandDirectories, cwd2, props, isIgnore) {
|
|
|
59353
59364
|
if (!result.endsWith("*") && expandDirectories)
|
|
59354
59365
|
result += "/**";
|
|
59355
59366
|
const escapedCwd = escapePath(cwd2);
|
|
59356
|
-
if (
|
|
59367
|
+
if (path7.isAbsolute(result.replace(ESCAPING_BACKSLASHES, "")))
|
|
59357
59368
|
result = posix.relative(escapedCwd, result);
|
|
59358
59369
|
else
|
|
59359
59370
|
result = posix.normalize(result);
|
|
@@ -59390,7 +59401,7 @@ function normalizePattern(pattern, expandDirectories, cwd2, props, isIgnore) {
|
|
|
59390
59401
|
}
|
|
59391
59402
|
props.depthOffset = newCommonPath.length;
|
|
59392
59403
|
props.commonPath = newCommonPath;
|
|
59393
|
-
props.root = newCommonPath.length > 0 ?
|
|
59404
|
+
props.root = newCommonPath.length > 0 ? path7.posix.join(cwd2, ...newCommonPath) : cwd2;
|
|
59394
59405
|
}
|
|
59395
59406
|
return result;
|
|
59396
59407
|
}
|
|
@@ -59523,18 +59534,18 @@ function globSync(patternsOrOptions, options) {
|
|
|
59523
59534
|
...options,
|
|
59524
59535
|
patterns: patternsOrOptions
|
|
59525
59536
|
} : patternsOrOptions;
|
|
59526
|
-
const cwd2 = opts.cwd ?
|
|
59537
|
+
const cwd2 = opts.cwd ? path7.resolve(opts.cwd).replace(BACKSLASHES, "/") : process.cwd().replace(BACKSLASHES, "/");
|
|
59527
59538
|
return crawl(opts, cwd2, true);
|
|
59528
59539
|
}
|
|
59529
59540
|
|
|
59530
59541
|
// src/utils/jsonl.ts
|
|
59531
59542
|
import { promisify } from "util";
|
|
59532
|
-
var readFile4 = promisify(
|
|
59533
|
-
var
|
|
59534
|
-
var statSync5 =
|
|
59543
|
+
var readFile4 = promisify(fs8.readFile);
|
|
59544
|
+
var readFileSync6 = fs8.readFileSync;
|
|
59545
|
+
var statSync5 = fs8.statSync;
|
|
59535
59546
|
async function getSessionDuration(transcriptPath) {
|
|
59536
59547
|
try {
|
|
59537
|
-
if (!
|
|
59548
|
+
if (!fs8.existsSync(transcriptPath)) {
|
|
59538
59549
|
return null;
|
|
59539
59550
|
}
|
|
59540
59551
|
const content = await readFile4(transcriptPath, "utf-8");
|
|
@@ -59586,7 +59597,7 @@ async function getSessionDuration(transcriptPath) {
|
|
|
59586
59597
|
}
|
|
59587
59598
|
async function getTokenMetrics(transcriptPath) {
|
|
59588
59599
|
try {
|
|
59589
|
-
if (!
|
|
59600
|
+
if (!fs8.existsSync(transcriptPath)) {
|
|
59590
59601
|
return { inputTokens: 0, outputTokens: 0, cachedTokens: 0, totalTokens: 0, contextLength: 0 };
|
|
59591
59602
|
}
|
|
59592
59603
|
const content = await readFile4(transcriptPath, "utf-8");
|
|
@@ -59639,7 +59650,7 @@ function getBlockMetrics() {
|
|
|
59639
59650
|
function findMostRecentBlockStartTime(rootDir, sessionDurationHours = 5) {
|
|
59640
59651
|
const sessionDurationMs = sessionDurationHours * 60 * 60 * 1000;
|
|
59641
59652
|
const now = new Date;
|
|
59642
|
-
const pattern =
|
|
59653
|
+
const pattern = path8.posix.join(rootDir.replace(/\\/g, "/"), "projects", "**", "*.jsonl");
|
|
59643
59654
|
const files = globSync([pattern], {
|
|
59644
59655
|
absolute: true,
|
|
59645
59656
|
cwd: rootDir
|
|
@@ -59733,7 +59744,7 @@ function findMostRecentBlockStartTime(rootDir, sessionDurationHours = 5) {
|
|
|
59733
59744
|
function getAllTimestampsFromFile(filePath) {
|
|
59734
59745
|
const timestamps = [];
|
|
59735
59746
|
try {
|
|
59736
|
-
const content =
|
|
59747
|
+
const content = readFileSync6(filePath, "utf-8");
|
|
59737
59748
|
const lines = content.trim().split(`
|
|
59738
59749
|
`).filter((line) => line.length > 0);
|
|
59739
59750
|
for (const line of lines) {
|
|
@@ -59812,18 +59823,21 @@ async function renderMultipleLines(data) {
|
|
|
59812
59823
|
if (hasBlockTimer) {
|
|
59813
59824
|
blockMetrics = getBlockMetrics();
|
|
59814
59825
|
}
|
|
59815
|
-
const
|
|
59826
|
+
const rawWidth = getTerminalWidth();
|
|
59827
|
+
const deduction = settings.flexMode === "full-minus-40" ? 40 : 6;
|
|
59828
|
+
const effectiveWidth = rawWidth ? rawWidth - deduction : null;
|
|
59816
59829
|
const context = {
|
|
59817
59830
|
data,
|
|
59818
59831
|
tokenMetrics,
|
|
59819
59832
|
sessionDuration,
|
|
59820
59833
|
blockMetrics,
|
|
59821
|
-
terminalWidth:
|
|
59822
|
-
isPreview: false
|
|
59823
|
-
terminalEnv
|
|
59834
|
+
terminalWidth: effectiveWidth,
|
|
59835
|
+
isPreview: false
|
|
59824
59836
|
};
|
|
59825
59837
|
const preRenderedLines = preRenderAllWidgets(lines, settings, context);
|
|
59826
59838
|
const preCalculatedMaxWidths = calculateMaxWidthsFromPreRendered(preRenderedLines, settings);
|
|
59839
|
+
const mobile = effectiveWidth !== null && effectiveWidth > 0 && effectiveWidth < 80;
|
|
59840
|
+
const renderedLines = [];
|
|
59827
59841
|
let globalSeparatorIndex = 0;
|
|
59828
59842
|
for (let i = 0;i < lines.length; i++) {
|
|
59829
59843
|
const lineItems = lines[i];
|
|
@@ -59836,12 +59850,24 @@ async function renderMultipleLines(data) {
|
|
|
59836
59850
|
const nonMergedWidgets = lineItems.filter((_, idx) => idx === lineItems.length - 1 || !lineItems[idx]?.merge);
|
|
59837
59851
|
if (nonMergedWidgets.length > 1)
|
|
59838
59852
|
globalSeparatorIndex += nonMergedWidgets.length - 1;
|
|
59839
|
-
|
|
59840
|
-
outputLine = "\x1B[0m" + outputLine;
|
|
59841
|
-
console.log(outputLine);
|
|
59853
|
+
renderedLines.push(line);
|
|
59842
59854
|
}
|
|
59843
59855
|
}
|
|
59844
59856
|
}
|
|
59857
|
+
if (mobile && renderedLines.length > 1) {
|
|
59858
|
+
const sep2 = settings.defaultSeparator ?? "|";
|
|
59859
|
+
const separator = sep2 === "|" ? " | " : ` ${sep2} `;
|
|
59860
|
+
const merged = renderedLines.join(separator);
|
|
59861
|
+
let outputLine = merged.replace(/ /g, " ");
|
|
59862
|
+
outputLine = "\x1B[0m" + outputLine;
|
|
59863
|
+
console.log(outputLine);
|
|
59864
|
+
} else {
|
|
59865
|
+
for (const line of renderedLines) {
|
|
59866
|
+
let outputLine = line.replace(/ /g, " ");
|
|
59867
|
+
outputLine = "\x1B[0m" + outputLine;
|
|
59868
|
+
console.log(outputLine);
|
|
59869
|
+
}
|
|
59870
|
+
}
|
|
59845
59871
|
if (settings.updatemessage?.message && settings.updatemessage.message.trim() !== "" && settings.updatemessage.remaining && settings.updatemessage.remaining > 0) {
|
|
59846
59872
|
console.log(settings.updatemessage.message);
|
|
59847
59873
|
const newRemaining = settings.updatemessage.remaining - 1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccstatusline-usage",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.3",
|
|
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
|
},
|