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.
Files changed (3) hide show
  1. package/README.md +31 -102
  2. package/dist/ccstatusline.js +237 -211
  3. 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
- [![npm version](https://img.shields.io/npm/v/ccstatusline-usage.svg)](https://www.npmjs.com/package/ccstatusline-usage)
18
- [![npm downloads](https://img.shields.io/npm/dm/ccstatusline-usage.svg)](https://www.npmjs.com/package/ccstatusline-usage)
17
+ [![npm version](https://img.shields.io/npm/v/ccstatusline.svg)](https://www.npmjs.com/package/ccstatusline)
18
+ [![npm downloads](https://img.shields.io/npm/dm/ccstatusline.svg)](https://www.npmjs.com/package/ccstatusline)
19
19
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/sirmalloc/ccstatusline/blob/main/LICENSE)
20
- [![Node.js Version](https://img.shields.io/node/v/ccstatusline-usage.svg)](https://nodejs.org)
21
- [![install size](https://packagephobia.com/badge?p=ccstatusline-usage)](https://packagephobia.com/result?p=ccstatusline-usage)
20
+ [![Node.js Version](https://img.shields.io/node/v/ccstatusline.svg)](https://nodejs.org)
21
+ [![install size](https://packagephobia.com/badge?p=ccstatusline)](https://packagephobia.com/result?p=ccstatusline)
22
22
  [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/sirmalloc/ccstatusline/graphs/commit-activity)
23
23
 
24
24
  [![Mentioned in Awesome Claude Code](https://awesome.re/mentioned-badge.svg)](https://github.com/hesreallyhim/awesome-claude-code)
25
25
  [![ClaudeLog - A comprehensive knowledge base for Claude](https://claudelog.com/img/claude_log_badge.svg)](https://claudelog.com/)
26
26
 
27
- ## Fork Enhancements
28
27
 
29
- This fork adds API-based usage widgets beyond the upstream:
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
- ![Status Bar Demo](screenshots/status-bar-demo.png)
28
+ ![Demo](https://raw.githubusercontent.com/sirmalloc/ccstatusline/main/screenshots/demo.gif)
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.1](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.1) - Automatic mobile/narrow terminal detection
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
- - Extra usage spending display in Reset Timer widget (e.g. `Extra: €41.24/€47.00`)
96
- - Auto-detect currency from timezone (Europe/* = €, else $)
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.0.42](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.0.42) - Fix reset timer API field
55
+ ### [v2.1.2](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.2) - Token auth fallback and 401 handling
99
56
 
100
- - Fixed API field name from `reset_at` to `resets_at` reset timer now displays correctly
101
- - Merged latest changes from upstream ccstatusline
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
- ![Emoji Support](screenshots/emojiSupport.png)
75
+ ![Emoji Support](https://raw.githubusercontent.com/sirmalloc/ccstatusline/main/screenshots/emojiSupport.png)
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
- ![Powerline Auto-Alignment](screenshots/autoAlign.png)
88
+ ![Powerline Auto-Alignment](https://raw.githubusercontent.com/sirmalloc/ccstatusline/main/screenshots/autoAlign.png)
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
- ![Current Working Directory and Session Cost](screenshots/cwdAndSessionCost.png)
94
+ ![Current Working Directory and Session Cost](https://raw.githubusercontent.com/sirmalloc/ccstatusline/main/screenshots/cwdAndSessionCost.png)
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
- ![Block Timer](screenshots/blockTimerSmall.png)
112
+ ![Block Timer](https://raw.githubusercontent.com/sirmalloc/ccstatusline/main/screenshots/blockTimerSmall.png)
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-usage@latest
152
+ npx ccstatusline@latest
200
153
 
201
154
  # Or with Bun (faster)
202
- bunx ccstatusline-usage@latest
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-usage@latest
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-usage@latest
200
+ npx ccstatusline@latest
248
201
 
249
202
  # Or with Yarn
250
- yarn dlx ccstatusline-usage@latest
203
+ yarn dlx ccstatusline@latest
251
204
 
252
205
  # Or with pnpm
253
- pnpm dlx ccstatusline-usage@latest
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-usage@latest
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-usage@latest
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-usage@latest"
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-usage@latest"
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
- #### 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
- - **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
- ![ccusage integration](screenshots/ccusage.png)
494
+ ![ccusage integration](https://raw.githubusercontent.com/sirmalloc/ccstatusline/main/screenshots/ccusage.png)
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
  [![GitHub forks](https://img.shields.io/github/forks/sirmalloc/ccstatusline?style=social)](https://github.com/sirmalloc/ccstatusline/network/members)
733
662
  [![GitHub watchers](https://img.shields.io/github/watchers/sirmalloc/ccstatusline?style=social)](https://github.com/sirmalloc/ccstatusline/watchers)
734
663
 
735
- [![npm version](https://img.shields.io/npm/v/ccstatusline-usage.svg)](https://www.npmjs.com/package/ccstatusline-usage)
736
- [![npm downloads](https://img.shields.io/npm/dm/ccstatusline-usage.svg)](https://www.npmjs.com/package/ccstatusline-usage)
664
+ [![npm version](https://img.shields.io/npm/v/ccstatusline.svg)](https://www.npmjs.com/package/ccstatusline)
665
+ [![npm downloads](https://img.shields.io/npm/dm/ccstatusline.svg)](https://www.npmjs.com/package/ccstatusline)
737
666
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/sirmalloc/ccstatusline/blob/main/LICENSE)
738
667
  [![Made with Bun](https://img.shields.io/badge/Made%20with-Bun-000000.svg?logo=bun)](https://bun.sh)
739
668
 
@@ -32383,8 +32383,8 @@ var require_utils = __commonJS((exports) => {
32383
32383
  }
32384
32384
  return output;
32385
32385
  };
32386
- exports.basename = (path6, { windows } = {}) => {
32387
- const segs = path6.split(windows ? /[\\/]/ : "/");
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: "magenta" },
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 MOBILE_THRESHOLD = 80;
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 "cyan";
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
- return item.rawValue ? "Claude" : "Model: Claude";
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 item.rawValue ? modelDisplayName : `Model: ${modelDisplayName}`;
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 null;
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(path5) {
54342
+ abbreviatePath(path6) {
54363
54343
  const homeDir = os5.homedir();
54364
- const useBackslash = path5.includes("\\") && !path5.includes("/");
54344
+ const useBackslash = path6.includes("\\") && !path6.includes("/");
54365
54345
  const sep = useBackslash ? "\\" : "/";
54366
- let normalizedPath = path5;
54367
- if (path5.startsWith(homeDir)) {
54368
- normalizedPath = "~" + path5.slice(homeDir.length);
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 fs6 from "fs";
54487
- import * as path5 from "path";
54488
- var CACHE_FILE = path5.join(process.env.HOME ?? "", ".cache", "ccstatusline-api.json");
54489
- var LOCK_FILE = path5.join(process.env.HOME ?? "", ".cache", "ccstatusline-api.lock");
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
- try {
54503
- const isMac = process.platform === "darwin";
54504
- if (isMac) {
54505
- const result = execSync8('security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null', { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
54506
- const parsed = JSON.parse(result);
54507
- const token = parsed?.claudeAiOauth?.accessToken ?? null;
54508
- if (token) {
54509
- cachedToken = token;
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(fs6.readFileSync(CACHE_FILE, "utf8"));
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 || result.status !== 0 || !result.stdout) {
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 = fs6.statSync(CACHE_FILE);
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(fs6.readFileSync(CACHE_FILE, "utf8"));
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 = fs6.statSync(LOCK_FILE);
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 = path5.dirname(LOCK_FILE);
54601
- if (!fs6.existsSync(lockDir)) {
54602
- fs6.mkdirSync(lockDir, { recursive: true });
54600
+ const lockDir = path6.dirname(LOCK_FILE);
54601
+ if (!fs7.existsSync(lockDir)) {
54602
+ fs7.mkdirSync(lockDir, { recursive: true });
54603
54603
  }
54604
- fs6.writeFileSync(LOCK_FILE, "");
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 = path5.dirname(CACHE_FILE);
54644
- if (!fs6.existsSync(cacheDir)) {
54645
- fs6.mkdirSync(cacheDir, { recursive: true });
54650
+ const cacheDir = path6.dirname(CACHE_FILE);
54651
+ if (!fs7.existsSync(cacheDir)) {
54652
+ fs7.mkdirSync(cacheDir, { recursive: true });
54646
54653
  }
54647
- fs6.writeFileSync(CACHE_FILE, JSON.stringify(apiData));
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, Boolean(context.terminalEnv?.isMobile));
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, Boolean(context.terminalEnv?.isMobile));
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 = Boolean(context.terminalEnv?.isMobile);
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 Usage"
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 fs7 from "fs";
58744
- import path7 from "node:path";
58754
+ import * as fs8 from "fs";
58755
+ import path8 from "node:path";
58745
58756
 
58746
58757
  // node_modules/tinyglobby/dist/index.mjs
58747
- import path6, { posix } from "path";
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(path6) {
58755
- let normalized = normalize(path6);
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(path6, separator) {
58762
- return path6.replace(SLASHES_REGEX, separator);
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(path6) {
58766
- return path6 === "/" || WINDOWS_ROOT_DIR_REGEX.test(path6);
58776
+ function isRootDirectory(path7) {
58777
+ return path7 === "/" || WINDOWS_ROOT_DIR_REGEX.test(path7);
58767
58778
  }
58768
- function normalizePath(path6, options) {
58779
+ function normalizePath(path7, options) {
58769
58780
  const { resolvePaths, normalizePath: normalizePath$1, pathSeparator } = options;
58770
- const pathNeedsCleaning = process.platform === "win32" && path6.includes("/") || path6.startsWith(".");
58781
+ const pathNeedsCleaning = process.platform === "win32" && path7.includes("/") || path7.startsWith(".");
58771
58782
  if (resolvePaths)
58772
- path6 = resolve2(path6);
58783
+ path7 = resolve2(path7);
58773
58784
  if (normalizePath$1 || pathNeedsCleaning)
58774
- path6 = cleanPath(path6);
58775
- if (path6 === ".")
58785
+ path7 = cleanPath(path7);
58786
+ if (path7 === ".")
58776
58787
  return "";
58777
- const needsSeperator = path6[path6.length - 1] !== pathSeparator;
58778
- return convertSlashes(needsSeperator ? path6 + pathSeparator : path6, pathSeparator);
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 path6 = directoryPath || ".";
58819
- if (filters.every((filter) => filter(path6, true)))
58820
- paths.push(path6);
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(path6, state, callback$1) {
58878
- const { queue, fs: fs7, options: { suppressErrors } } = state;
58888
+ var resolveSymlinksAsync = function(path7, state, callback$1) {
58889
+ const { queue, fs: fs8, options: { suppressErrors } } = state;
58879
58890
  queue.enqueue();
58880
- fs7.realpath(path6, (error43, resolvedPath) => {
58891
+ fs8.realpath(path7, (error43, resolvedPath) => {
58881
58892
  if (error43)
58882
58893
  return queue.dequeue(suppressErrors ? null : error43, state);
58883
- fs7.stat(resolvedPath, (error$1, stat) => {
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(path6, resolvedPath, state))
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(path6, state, callback$1) {
58894
- const { queue, fs: fs7, options: { suppressErrors } } = state;
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 = fs7.realpathSync(path6);
58898
- const stat = fs7.statSync(resolvedPath);
58899
- if (stat.isDirectory() && isRecursive(path6, resolvedPath, state))
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(path6, resolved, state) {
58923
+ function isRecursive(path7, resolved, state) {
58913
58924
  if (state.options.useRealPaths)
58914
58925
  return isRecursiveUsingRealPaths(resolved, state);
58915
- let parent = dirname3(path6);
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(path6, resolved);
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: fs7 } = state;
58992
+ const { fs: fs8 } = state;
58982
58993
  state.visited.push(crawlPath);
58983
58994
  state.counts.directories++;
58984
- fs7.readdir(crawlPath || ".", readdirOpts, (error43, entries = []) => {
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: fs7 } = state;
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 = fs7.readdirSync(crawlPath || ".", readdirOpts);
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 path6 = joinDirectoryPath(entry.name, directoryPath, this.state.options.pathSeparator);
59104
- if (exclude && exclude(entry.name, path6))
59114
+ let path7 = joinDirectoryPath(entry.name, directoryPath, this.state.options.pathSeparator);
59115
+ if (exclude && exclude(entry.name, path7))
59105
59116
  continue;
59106
- this.pushDirectory(path6, paths, filters);
59107
- this.walkDirectory(this.state, path6, path6, depth - 1, this.walk);
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 path6 = joinPathWithBasePath(entry.name, directoryPath);
59110
- this.resolveSymlink(path6, this.state, (stat, resolvedPath) => {
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 : path6 + pathSeparator))
59124
+ if (exclude && exclude(entry.name, useRealPaths ? resolvedPath : path7 + pathSeparator))
59114
59125
  return;
59115
- this.walkDirectory(this.state, resolvedPath, useRealPaths ? resolvedPath : path6 + pathSeparator, depth - 1, this.walk);
59126
+ this.walkDirectory(this.state, resolvedPath, useRealPaths ? resolvedPath : path7 + pathSeparator, depth - 1, this.walk);
59116
59127
  } else {
59117
- resolvedPath = useRealPaths ? resolvedPath : path6;
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((path6) => isMatch(path6));
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 (path6.isAbsolute(result.replace(ESCAPING_BACKSLASHES, "")))
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 ? path6.posix.join(cwd2, ...newCommonPath) : cwd2;
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 ? path6.resolve(opts.cwd).replace(BACKSLASHES, "/") : process.cwd().replace(BACKSLASHES, "/");
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(fs7.readFile);
59533
- var readFileSync5 = fs7.readFileSync;
59534
- var statSync5 = fs7.statSync;
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 (!fs7.existsSync(transcriptPath)) {
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 (!fs7.existsSync(transcriptPath)) {
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 = path7.posix.join(rootDir.replace(/\\/g, "/"), "projects", "**", "*.jsonl");
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 = readFileSync5(filePath, "utf-8");
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 terminalEnv = detectTerminalEnvironment();
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: terminalEnv.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
- let outputLine = line.replace(/ /g, " ");
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.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
  },