ccstatusline-usage 2.1.5 → 2.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -46,30 +46,29 @@
46
46
 
47
47
  ## 🆕 Recent Updates
48
48
 
49
- ### [v2.1.5](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.5) - Claude Code Teams compatibility
49
+ ### [v2.1.7](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.7) - Battery widget, extra usage fix & upstream sync
50
50
 
51
- - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Teams mode support** — automatically detects Claude Code's native Teams sessions (orchestrator + teammates in tmux split panes) and applies compact rendering for the narrow orchestrator pane. Widgets render at half terminal width to fit CC's two-column status bar layout.
51
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Battery widget** — shows `B: xx%` on macOS and Linux when on battery power (hidden when charging)
52
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Extra usage conditional display** — Extra spending widget now only appears when the weekly budget is fully used (100%), instead of always when extra usage is enabled
53
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Upstream sync** — Memory Usage widget (v2.0.29)
52
54
 
53
- ### [v2.1.4](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.4) - Compact mode widget-boundary wrapping
55
+ ### [v2.1.6](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.6) - Upstream sync
54
56
 
55
- - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Widget-boundary wrapping** — compact mode now wraps at widget boundaries instead of mid-widget, using a dedicated compact renderer (`compact-renderer.ts`). Widgets that don't fit on the current line cleanly wrap to the next:
56
- ```
57
- S: [░░░░] 2.0% | W: [██░░] 43.0% | 4:36 hr
58
- M: o4.6 ▌▌▌ | S: 5647528d | C: [██░░] 119k/200k
59
- ```
60
- - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Terminal-agnostic compact detection** — compact mode now triggers on any narrow terminal (< 80 columns), not just tmux
57
+ - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): **Upstream sync** — merged 9 upstream commits: Session Name widget, Git Root Dir widget, CWD home abbreviation, block timer caching, git widget refactor, Windows UTF-8 fix, widget picker UX, TUI editor input fix
61
58
 
62
- ### [v2.1.3](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.3) - Mobile/compact mode and thinking effort bars
59
+ ### v2.0.26 - v2.0.29 - Performance, git internals, and workflow improvements
63
60
 
64
- - [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
65
- - [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`
66
- - [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
67
-
68
- ### [v2.1.2](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.2) - Token auth fallback and 401 handling
69
-
70
- - [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage): macOS file credential fallback reads `~/.claude/.credentials.json` when Keychain entry is missing
71
- - [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`
72
- - Synced with upstream (101 commits)
61
+ - **🧠 Memory Usage widget (v2.0.29)** - Added a new widget that shows current system memory usage (`Mem: used/total`).
62
+ - **⚡ Block timer cache (v2.0.28)** - Cache block timer metrics to reduce JSONL parsing on every render, with per-config hashed cache files and automatic 5-hour block invalidation.
63
+ - **🧱 Git widget command refactor (v2.0.28)** - Refactored git widgets to use shared git command helpers and expanded coverage for failure and edge-case tests.
64
+ - **🪟 Windows UTF-8 piped output fix (v2.0.28)** - Sets the Windows UTF-8 code page for piped status line rendering.
65
+ - **📁 Git Root Dir widget (v2.0.27)** - Added a new Git widget that shows the repository root directory name.
66
+ - **🏷️ Session Name widget (v2.0.26)** - Added a new widget that shows the current Claude Code session name from `/rename`.
67
+ - **🏠 Current Working Directory home abbreviation (v2.0.26)** - Added a `~` abbreviation option for CWD display in both preview and live rendering.
68
+ - **🧠 Context model suffix fix (v2.0.26)** - Context widgets now recognize the `[1m]` suffix across models, not just a single model path.
69
+ - **🧭 Widget picker UX updates (v2.0.26)** - Improved widget discovery/navigation and added clearer, safer clear-line behavior.
70
+ - **⌨️ TUI editor input fix (v2.0.26)** - Prevented shortcut/input leakage into widget editor flows.
71
+ - **📄 Repo docs update (v2.0.26)** - Migrated guidance from `CLAUDE.md` to `AGENTS.md` (with symlink compatibility).
73
72
 
74
73
  ### v2.0.16 - Add fish style path abbreviation toggle to Current Working Directory widget
75
74
 
@@ -374,12 +373,13 @@ Once configured, ccstatusline automatically formats your Claude Code status line
374
373
 
375
374
  ### 📊 Available Widgets
376
375
 
377
- - **Model Name** - Shows the current Claude model with thinking effort bars (`▌▌▌`) indicating low/medium/high effort
376
+ - **Model Name** - Shows the current Claude model (e.g., "Claude 3.5 Sonnet")
378
377
  - **Git Branch** - Displays current git branch name
379
378
  - **Git Changes** - Shows uncommitted insertions/deletions (e.g., "+42,-10")
380
379
  - **Git Worktree** - Shows the name of the current git worktree
381
380
  - **Session Clock** - Shows elapsed time since session start (e.g., "2hr 15m")
382
381
  - **Session Cost** - Shows total session cost in USD (e.g., "$1.23")
382
+ - **Session Name** - Shows the session name set via `/rename` command in Claude Code
383
383
  - **Block Timer** - Shows time elapsed in current 5-hour block or progress bar
384
384
  - **Current Working Directory** - Shows current working directory with configurable path segments
385
385
  - **Version** - Shows Claude Code version
@@ -389,9 +389,11 @@ Once configured, ccstatusline automatically formats your Claude Code status line
389
389
  - **Tokens Cached** - Shows cached tokens used
390
390
  - **Tokens Total** - Shows total tokens used
391
391
  - **Context Length** - Shows current context length in tokens
392
- - **Context Percentage** - Shows percentage of context limit used (dynamic: 1M for Sonnet 4.5/Opus 4.6 with `[1m]` suffix, 200k otherwise)
393
- - **Context Percentage (usable)** - Shows percentage of usable context (dynamic: 800k for Sonnet 4.5/Opus 4.6 with `[1m]` suffix, 160k otherwise, accounting for auto-compact at 80%)
392
+ - **Context Percentage** - Shows percentage of context limit used (dynamic: 1M for Sonnet 4.5 with `[1m]` suffix, 200k otherwise)
393
+ - **Context Percentage (usable)** - Shows percentage of usable context (dynamic: 800k for Sonnet 4.5 with `[1m]` suffix, 160k otherwise, accounting for auto-compact at 80%)
394
394
  - **Terminal Width** - Shows detected terminal width (for debugging)
395
+ - **Memory Usage** - Shows system memory usage (used/total, e.g., "Mem: 12.4G/16.0G")
396
+ - **Battery** - Shows battery percentage on macOS and Linux (only visible when on battery power, hidden when charging)
395
397
  - **Custom Text** - Add your own custom text to the status line
396
398
  - **Custom Command** - Execute shell commands and display their output (refreshes whenever the statusline is updated by Claude Code)
397
399
  - **Separator** - Visual divider between widgets (customizable: |, -, comma, space)
@@ -460,7 +462,7 @@ The Block Timer widget helps you track your progress through Claude Code's 5-hou
460
462
  ### 🔤 Raw Value Mode
461
463
 
462
464
  Some widgets support "raw value" mode which displays just the value without a label:
463
- - Normal: `Model: Claude Opus 4.6` → Raw: `Claude Opus 4.6`
465
+ - Normal: `Model: Claude 3.5 Sonnet` → Raw: `Claude 3.5 Sonnet`
464
466
  - Normal: `Session: 2hr 15m` → Raw: `2hr 15m`
465
467
  - Normal: `Block: 3hr 45m` → Raw: `3hr 45m`
466
468
  - Normal: `Ctx: 18.6k` → Raw: `18.6k`
@@ -625,6 +627,14 @@ Contributions are welcome! Please feel free to submit a Pull Request.
625
627
 
626
628
  ---
627
629
 
630
+ ## Support
631
+
632
+ If ccstatusline is useful to you, consider buying me a coffee:
633
+
634
+ <a href="https://www.buymeacoffee.com/sirmalloc" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
635
+
636
+ ---
637
+
628
638
  ## 📄 License
629
639
 
630
640
  [MIT](LICENSE) © Matthew Breedlove
@@ -637,11 +647,6 @@ Contributions are welcome! Please feel free to submit a Pull Request.
637
647
 
638
648
  - GitHub: [@sirmalloc](https://github.com/sirmalloc)
639
649
 
640
- **Fork maintained by Peter van Velzen**
641
-
642
- - GitHub: [@pcvelz](https://github.com/pcvelz)
643
- - Fork: [pcvelz/ccstatusline-usage](https://github.com/pcvelz/ccstatusline-usage)
644
-
645
650
  ---
646
651
 
647
652
  ## 🔗 Related Projects
@@ -661,11 +666,11 @@ Contributions are welcome! Please feel free to submit a Pull Request.
661
666
 
662
667
  ## Star History
663
668
 
664
- <a href="https://www.star-history.com/#pcvelz/ccstatusline-usage&Timeline">
669
+ <a href="https://www.star-history.com/#sirmalloc/ccstatusline&Timeline">
665
670
  <picture>
666
- <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=pcvelz/ccstatusline-usage&type=Timeline&theme=dark" />
667
- <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=pcvelz/ccstatusline-usage&type=Timeline" />
668
- <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=pcvelz/ccstatusline-usage&type=Timeline" />
671
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=sirmalloc/ccstatusline&type=Timeline&theme=dark" />
672
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=sirmalloc/ccstatusline&type=Timeline" />
673
+ <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=sirmalloc/ccstatusline&type=Timeline" />
669
674
  </picture>
670
675
  </a>
671
676
 
@@ -675,21 +680,21 @@ Contributions are welcome! Please feel free to submit a Pull Request.
675
680
 
676
681
  Give a ⭐ if this project helped you!
677
682
 
678
- [![GitHub stars](https://img.shields.io/github/stars/pcvelz/ccstatusline-usage?style=social)](https://github.com/pcvelz/ccstatusline-usage/stargazers)
679
- [![GitHub forks](https://img.shields.io/github/forks/pcvelz/ccstatusline-usage?style=social)](https://github.com/pcvelz/ccstatusline-usage/network/members)
680
- [![GitHub watchers](https://img.shields.io/github/watchers/pcvelz/ccstatusline-usage?style=social)](https://github.com/pcvelz/ccstatusline-usage/watchers)
683
+ [![GitHub stars](https://img.shields.io/github/stars/sirmalloc/ccstatusline?style=social)](https://github.com/sirmalloc/ccstatusline/stargazers)
684
+ [![GitHub forks](https://img.shields.io/github/forks/sirmalloc/ccstatusline?style=social)](https://github.com/sirmalloc/ccstatusline/network/members)
685
+ [![GitHub watchers](https://img.shields.io/github/watchers/sirmalloc/ccstatusline?style=social)](https://github.com/sirmalloc/ccstatusline/watchers)
681
686
 
682
- [![npm version](https://img.shields.io/npm/v/ccstatusline-usage.svg)](https://www.npmjs.com/package/ccstatusline-usage)
683
- [![npm downloads](https://img.shields.io/npm/dm/ccstatusline-usage.svg)](https://www.npmjs.com/package/ccstatusline-usage)
684
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/pcvelz/ccstatusline-usage/blob/main/LICENSE)
687
+ [![npm version](https://img.shields.io/npm/v/ccstatusline.svg)](https://www.npmjs.com/package/ccstatusline)
688
+ [![npm downloads](https://img.shields.io/npm/dm/ccstatusline.svg)](https://www.npmjs.com/package/ccstatusline)
689
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/sirmalloc/ccstatusline/blob/main/LICENSE)
685
690
  [![Made with Bun](https://img.shields.io/badge/Made%20with-Bun-000000.svg?logo=bun)](https://bun.sh)
686
691
 
687
- [![Issues](https://img.shields.io/github/issues/pcvelz/ccstatusline-usage)](https://github.com/pcvelz/ccstatusline-usage/issues)
688
- [![Pull Requests](https://img.shields.io/github/issues-pr/pcvelz/ccstatusline-usage)](https://github.com/pcvelz/ccstatusline-usage/pulls)
689
- [![Contributors](https://img.shields.io/github/contributors/pcvelz/ccstatusline-usage)](https://github.com/pcvelz/ccstatusline-usage/graphs/contributors)
692
+ [![Issues](https://img.shields.io/github/issues/sirmalloc/ccstatusline)](https://github.com/sirmalloc/ccstatusline/issues)
693
+ [![Pull Requests](https://img.shields.io/github/issues-pr/sirmalloc/ccstatusline)](https://github.com/sirmalloc/ccstatusline/pulls)
694
+ [![Contributors](https://img.shields.io/github/contributors/sirmalloc/ccstatusline)](https://github.com/sirmalloc/ccstatusline/graphs/contributors)
690
695
 
691
696
  ### 💬 Connect
692
697
 
693
- [Report Bug](https://github.com/pcvelz/ccstatusline-usage/issues) · [Request Feature](https://github.com/pcvelz/ccstatusline-usage/issues) · [Discussions](https://github.com/pcvelz/ccstatusline-usage/discussions)
698
+ [Report Bug](https://github.com/sirmalloc/ccstatusline/issues) · [Request Feature](https://github.com/sirmalloc/ccstatusline/issues) · [Discussions](https://github.com/sirmalloc/ccstatusline/discussions)
694
699
 
695
700
  </div>
@@ -50987,6 +50987,8 @@ var SettingsSchema = exports_external.object({
50987
50987
  { id: "weekly-usage", type: "weekly-usage", color: "brightBlue" },
50988
50988
  { id: "sep2", type: "separator" },
50989
50989
  { id: "reset-timer", type: "reset-timer", color: "brightBlue" },
50990
+ { id: "sep-battery", type: "separator" },
50991
+ { id: "battery", type: "battery", color: "yellow" },
50990
50992
  { id: "sep3", type: "separator" },
50991
50993
  { id: "model", type: "model", color: "ansi256:124" },
50992
50994
  { id: "sep4", type: "separator" },
@@ -51450,7 +51452,7 @@ import { execSync as execSync3 } from "child_process";
51450
51452
  import * as fs5 from "fs";
51451
51453
  import * as path4 from "path";
51452
51454
  var __dirname = "/Users/peter/Documents/Code/ccstatusline-usage/src/utils";
51453
- var PACKAGE_VERSION = "2.1.5";
51455
+ var PACKAGE_VERSION = "2.1.7";
51454
51456
  function getPackageVersion() {
51455
51457
  if (/^\d+\.\d+\.\d+/.test(PACKAGE_VERSION)) {
51456
51458
  return PACKAGE_VERSION;
@@ -52425,9 +52427,39 @@ class OutputStyleWidget {
52425
52427
  return true;
52426
52428
  }
52427
52429
  }
52428
- // src/widgets/GitBranch.ts
52430
+ // src/utils/git.ts
52429
52431
  import { execSync as execSync4 } from "child_process";
52432
+ function resolveGitCwd(context) {
52433
+ const candidates = [
52434
+ context.data?.cwd,
52435
+ context.data?.workspace?.current_dir,
52436
+ context.data?.workspace?.project_dir
52437
+ ];
52438
+ for (const candidate of candidates) {
52439
+ if (typeof candidate === "string" && candidate.trim().length > 0) {
52440
+ return candidate;
52441
+ }
52442
+ }
52443
+ return;
52444
+ }
52445
+ function runGit(command, context) {
52446
+ try {
52447
+ const cwd2 = resolveGitCwd(context);
52448
+ const output = execSync4(`git ${command}`, {
52449
+ encoding: "utf8",
52450
+ stdio: ["pipe", "pipe", "ignore"],
52451
+ ...cwd2 ? { cwd: cwd2 } : {}
52452
+ }).trim();
52453
+ return output.length > 0 ? output : null;
52454
+ } catch {
52455
+ return null;
52456
+ }
52457
+ }
52458
+ function isInsideGitWorkTree(context) {
52459
+ return runGit("rev-parse --is-inside-work-tree", context) === "true";
52460
+ }
52430
52461
 
52462
+ // src/widgets/GitBranch.ts
52431
52463
  class GitBranchWidget {
52432
52464
  getDefaultColor() {
52433
52465
  return "magenta";
@@ -52470,21 +52502,16 @@ class GitBranchWidget {
52470
52502
  if (context.isPreview) {
52471
52503
  return item.rawValue ? "main" : "⎇ main";
52472
52504
  }
52473
- const branch = this.getGitBranch();
52505
+ if (!isInsideGitWorkTree(context)) {
52506
+ return hideNoGit ? null : "⎇ no git";
52507
+ }
52508
+ const branch = this.getGitBranch(context);
52474
52509
  if (branch)
52475
52510
  return item.rawValue ? branch : `⎇ ${branch}`;
52476
52511
  return hideNoGit ? null : "⎇ no git";
52477
52512
  }
52478
- getGitBranch() {
52479
- try {
52480
- const branch = execSync4("git branch --show-current", {
52481
- encoding: "utf8",
52482
- stdio: ["pipe", "pipe", "ignore"]
52483
- }).trim();
52484
- return branch || null;
52485
- } catch {
52486
- return null;
52487
- }
52513
+ getGitBranch(context) {
52514
+ return runGit("branch --show-current", context);
52488
52515
  }
52489
52516
  getCustomKeybinds() {
52490
52517
  return [
@@ -52499,8 +52526,6 @@ class GitBranchWidget {
52499
52526
  }
52500
52527
  }
52501
52528
  // src/widgets/GitChanges.ts
52502
- import { execSync as execSync5 } from "child_process";
52503
-
52504
52529
  class GitChangesWidget {
52505
52530
  getDefaultColor() {
52506
52531
  return "yellow";
@@ -52543,40 +52568,107 @@ class GitChangesWidget {
52543
52568
  if (context.isPreview) {
52544
52569
  return "(+42,-10)";
52545
52570
  }
52546
- const changes = this.getGitChanges();
52571
+ if (!isInsideGitWorkTree(context)) {
52572
+ return hideNoGit ? null : "(no git)";
52573
+ }
52574
+ const changes = this.getGitChanges(context);
52547
52575
  if (changes)
52548
52576
  return `(+${changes.insertions},-${changes.deletions})`;
52549
52577
  else
52550
52578
  return hideNoGit ? null : "(no git)";
52551
52579
  }
52552
- getGitChanges() {
52553
- try {
52554
- let totalInsertions = 0;
52555
- let totalDeletions = 0;
52556
- const unstagedStat = execSync5("git diff --shortstat", {
52557
- encoding: "utf8",
52558
- stdio: ["pipe", "pipe", "ignore"]
52559
- }).trim();
52560
- const stagedStat = execSync5("git diff --cached --shortstat", {
52561
- encoding: "utf8",
52562
- stdio: ["pipe", "pipe", "ignore"]
52563
- }).trim();
52564
- if (unstagedStat) {
52565
- const insertMatch = /(\d+) insertion/.exec(unstagedStat);
52566
- const deleteMatch = /(\d+) deletion/.exec(unstagedStat);
52567
- totalInsertions += insertMatch?.[1] ? parseInt(insertMatch[1], 10) : 0;
52568
- totalDeletions += deleteMatch?.[1] ? parseInt(deleteMatch[1], 10) : 0;
52569
- }
52570
- if (stagedStat) {
52571
- const insertMatch = /(\d+) insertion/.exec(stagedStat);
52572
- const deleteMatch = /(\d+) deletion/.exec(stagedStat);
52573
- totalInsertions += insertMatch?.[1] ? parseInt(insertMatch[1], 10) : 0;
52574
- totalDeletions += deleteMatch?.[1] ? parseInt(deleteMatch[1], 10) : 0;
52575
- }
52576
- return { insertions: totalInsertions, deletions: totalDeletions };
52577
- } catch {
52578
- return null;
52580
+ getGitChanges(context) {
52581
+ let totalInsertions = 0;
52582
+ let totalDeletions = 0;
52583
+ const unstagedStat = runGit("diff --shortstat", context) ?? "";
52584
+ const stagedStat = runGit("diff --cached --shortstat", context) ?? "";
52585
+ if (unstagedStat) {
52586
+ const insertMatch = /(\d+) insertion/.exec(unstagedStat);
52587
+ const deleteMatch = /(\d+) deletion/.exec(unstagedStat);
52588
+ totalInsertions += insertMatch?.[1] ? parseInt(insertMatch[1], 10) : 0;
52589
+ totalDeletions += deleteMatch?.[1] ? parseInt(deleteMatch[1], 10) : 0;
52590
+ }
52591
+ if (stagedStat) {
52592
+ const insertMatch = /(\d+) insertion/.exec(stagedStat);
52593
+ const deleteMatch = /(\d+) deletion/.exec(stagedStat);
52594
+ totalInsertions += insertMatch?.[1] ? parseInt(insertMatch[1], 10) : 0;
52595
+ totalDeletions += deleteMatch?.[1] ? parseInt(deleteMatch[1], 10) : 0;
52596
+ }
52597
+ return { insertions: totalInsertions, deletions: totalDeletions };
52598
+ }
52599
+ getCustomKeybinds() {
52600
+ return [
52601
+ { key: "h", label: "(h)ide 'no git' message", action: "toggle-nogit" }
52602
+ ];
52603
+ }
52604
+ supportsRawValue() {
52605
+ return false;
52606
+ }
52607
+ supportsColors(item) {
52608
+ return true;
52609
+ }
52610
+ }
52611
+ // src/widgets/GitRootDir.ts
52612
+ class GitRootDirWidget {
52613
+ getDefaultColor() {
52614
+ return "cyan";
52615
+ }
52616
+ getDescription() {
52617
+ return "Shows the git repository root directory name";
52618
+ }
52619
+ getDisplayName() {
52620
+ return "Git Root Dir";
52621
+ }
52622
+ getCategory() {
52623
+ return "Git";
52624
+ }
52625
+ getEditorDisplay(item) {
52626
+ const hideNoGit = item.metadata?.hideNoGit === "true";
52627
+ const modifiers = [];
52628
+ if (hideNoGit) {
52629
+ modifiers.push("hide 'no git'");
52630
+ }
52631
+ return {
52632
+ displayText: this.getDisplayName(),
52633
+ modifierText: modifiers.length > 0 ? `(${modifiers.join(", ")})` : undefined
52634
+ };
52635
+ }
52636
+ handleEditorAction(action, item) {
52637
+ if (action === "toggle-nogit") {
52638
+ const currentState = item.metadata?.hideNoGit === "true";
52639
+ return {
52640
+ ...item,
52641
+ metadata: {
52642
+ ...item.metadata,
52643
+ hideNoGit: (!currentState).toString()
52644
+ }
52645
+ };
52646
+ }
52647
+ return null;
52648
+ }
52649
+ render(item, context, _settings) {
52650
+ const hideNoGit = item.metadata?.hideNoGit === "true";
52651
+ if (context.isPreview) {
52652
+ return "my-repo";
52653
+ }
52654
+ if (!isInsideGitWorkTree(context)) {
52655
+ return hideNoGit ? null : "no git";
52579
52656
  }
52657
+ const rootDir = this.getGitRootDir(context);
52658
+ if (rootDir) {
52659
+ return this.getRootDirName(rootDir);
52660
+ }
52661
+ return hideNoGit ? null : "no git";
52662
+ }
52663
+ getGitRootDir(context) {
52664
+ return runGit("rev-parse --show-toplevel", context);
52665
+ }
52666
+ getRootDirName(rootDir) {
52667
+ const trimmedRootDir = rootDir.replace(/[\\/]+$/, "");
52668
+ const normalizedRootDir = trimmedRootDir.length > 0 ? trimmedRootDir : rootDir;
52669
+ const parts = normalizedRootDir.split(/[\\/]/).filter(Boolean);
52670
+ const lastPart = parts[parts.length - 1];
52671
+ return lastPart && lastPart.length > 0 ? lastPart : normalizedRootDir;
52580
52672
  }
52581
52673
  getCustomKeybinds() {
52582
52674
  return [
@@ -52591,8 +52683,6 @@ class GitChangesWidget {
52591
52683
  }
52592
52684
  }
52593
52685
  // src/widgets/GitWorktree.ts
52594
- import { execSync as execSync6 } from "child_process";
52595
-
52596
52686
  class GitWorktreeWidget {
52597
52687
  getDefaultColor() {
52598
52688
  return "blue";
@@ -52634,24 +52724,29 @@ class GitWorktreeWidget {
52634
52724
  const hideNoGit = item.metadata?.hideNoGit === "true";
52635
52725
  if (context.isPreview)
52636
52726
  return item.rawValue ? "main" : "\uD81A\uDC30 main";
52637
- const worktree = this.getGitWorktree();
52727
+ if (!isInsideGitWorkTree(context)) {
52728
+ return hideNoGit ? null : "\uD81A\uDC30 no git";
52729
+ }
52730
+ const worktree = this.getGitWorktree(context);
52638
52731
  if (worktree)
52639
52732
  return item.rawValue ? worktree : `\uD81A\uDC30 ${worktree}`;
52640
52733
  return hideNoGit ? null : "\uD81A\uDC30 no git";
52641
52734
  }
52642
- getGitWorktree() {
52643
- try {
52644
- const worktreeDir = execSync6("git rev-parse --git-dir", {
52645
- encoding: "utf8",
52646
- stdio: ["pipe", "pipe", "ignore"]
52647
- }).trim();
52648
- if (worktreeDir.endsWith("/.git") || worktreeDir === ".git")
52649
- return "main";
52650
- const [, worktree] = worktreeDir.split(".git/worktrees/");
52651
- return worktree ?? null;
52652
- } catch {
52735
+ getGitWorktree(context) {
52736
+ const worktreeDir = runGit("rev-parse --git-dir", context);
52737
+ if (!worktreeDir) {
52738
+ return null;
52739
+ }
52740
+ const normalizedGitDir = worktreeDir.replace(/\\/g, "/");
52741
+ if (normalizedGitDir.endsWith("/.git") || normalizedGitDir === ".git")
52742
+ return "main";
52743
+ const marker = ".git/worktrees/";
52744
+ const markerIndex = normalizedGitDir.lastIndexOf(marker);
52745
+ if (markerIndex === -1) {
52653
52746
  return null;
52654
52747
  }
52748
+ const worktree = normalizedGitDir.slice(markerIndex + marker.length);
52749
+ return worktree.length > 0 ? worktree : null;
52655
52750
  }
52656
52751
  getCustomKeybinds() {
52657
52752
  return [
@@ -53935,7 +54030,7 @@ var CustomTextEditor = ({ widget, onComplete, onCancel }) => {
53935
54030
  }, undefined, true, undefined, this);
53936
54031
  };
53937
54032
  // src/widgets/CustomCommand.tsx
53938
- import { execSync as execSync7 } from "child_process";
54033
+ import { execSync as execSync5 } from "child_process";
53939
54034
  var import_react30 = __toESM(require_react(), 1);
53940
54035
  var jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
53941
54036
 
@@ -53984,7 +54079,7 @@ class CustomCommandWidget {
53984
54079
  try {
53985
54080
  const timeout = item.timeout ?? 1000;
53986
54081
  const jsonInput = JSON.stringify(context.data);
53987
- let output = execSync7(item.commandPath, {
54082
+ let output = execSync5(item.commandPath, {
53988
54083
  encoding: "utf8",
53989
54084
  input: jsonInput,
53990
54085
  timeout,
@@ -54324,7 +54419,11 @@ class CurrentWorkingDirWidget {
54324
54419
  getEditorDisplay(item) {
54325
54420
  const segments = item.metadata?.segments ? parseInt(item.metadata.segments, 10) : undefined;
54326
54421
  const fishStyle = item.metadata?.fishStyle === "true";
54422
+ const abbreviateHome = item.metadata?.abbreviateHome === "true";
54327
54423
  const modifiers = [];
54424
+ if (abbreviateHome) {
54425
+ modifiers.push("~");
54426
+ }
54328
54427
  if (fishStyle) {
54329
54428
  modifiers.push("fish-style");
54330
54429
  } else if (segments && segments > 0) {
@@ -54336,11 +54435,31 @@ class CurrentWorkingDirWidget {
54336
54435
  };
54337
54436
  }
54338
54437
  handleEditorAction(action, item) {
54438
+ if (action === "toggle-abbreviate-home") {
54439
+ const currentAbbreviateHome = item.metadata?.abbreviateHome === "true";
54440
+ const newAbbreviateHome = !currentAbbreviateHome;
54441
+ if (newAbbreviateHome) {
54442
+ const { fishStyle, ...restMetadata } = item.metadata ?? {};
54443
+ return {
54444
+ ...item,
54445
+ metadata: {
54446
+ ...restMetadata,
54447
+ abbreviateHome: "true"
54448
+ }
54449
+ };
54450
+ } else {
54451
+ const { abbreviateHome, ...restMetadata } = item.metadata ?? {};
54452
+ return {
54453
+ ...item,
54454
+ metadata: Object.keys(restMetadata).length > 0 ? restMetadata : undefined
54455
+ };
54456
+ }
54457
+ }
54339
54458
  if (action === "toggle-fish-style") {
54340
54459
  const currentFishStyle = item.metadata?.fishStyle === "true";
54341
54460
  const newFishStyle = !currentFishStyle;
54342
54461
  if (newFishStyle) {
54343
- const { segments, ...restMetadata } = item.metadata ?? {};
54462
+ const { segments, abbreviateHome, ...restMetadata } = item.metadata ?? {};
54344
54463
  return {
54345
54464
  ...item,
54346
54465
  metadata: {
@@ -54361,10 +54480,19 @@ class CurrentWorkingDirWidget {
54361
54480
  render(item, context, settings) {
54362
54481
  const segments = item.metadata?.segments ? parseInt(item.metadata.segments, 10) : undefined;
54363
54482
  const fishStyle = item.metadata?.fishStyle === "true";
54483
+ const abbreviateHome = item.metadata?.abbreviateHome === "true";
54364
54484
  if (context.isPreview) {
54365
54485
  let previewPath;
54366
54486
  if (fishStyle) {
54367
54487
  previewPath = "~/D/P/my-project";
54488
+ } else if (abbreviateHome && segments && segments > 0) {
54489
+ if (segments === 1) {
54490
+ previewPath = "~/.../my-project";
54491
+ } else {
54492
+ previewPath = "~/.../Projects/my-project";
54493
+ }
54494
+ } else if (abbreviateHome) {
54495
+ previewPath = "~/Documents/Projects/my-project";
54368
54496
  } else if (segments && segments > 0) {
54369
54497
  if (segments === 1) {
54370
54498
  previewPath = ".../project";
@@ -54382,20 +54510,27 @@ class CurrentWorkingDirWidget {
54382
54510
  let displayPath = cwd2;
54383
54511
  if (fishStyle) {
54384
54512
  displayPath = this.abbreviatePath(cwd2);
54385
- } else if (segments && segments > 0) {
54386
- const useBackslash = cwd2.includes("\\") && !cwd2.includes("/");
54387
- const outSep = useBackslash ? "\\" : "/";
54388
- const pathParts = cwd2.split(/[\\/]+/);
54389
- const filteredParts = pathParts.filter((part) => part !== "");
54390
- if (filteredParts.length > segments) {
54391
- const selectedSegments = filteredParts.slice(-segments);
54392
- displayPath = "..." + outSep + selectedSegments.join(outSep);
54513
+ } else {
54514
+ if (abbreviateHome) {
54515
+ displayPath = this.abbreviateHomeDir(displayPath);
54516
+ }
54517
+ if (segments && segments > 0) {
54518
+ const useBackslash = displayPath.includes("\\") && !displayPath.includes("/");
54519
+ const outSep = useBackslash ? "\\" : "/";
54520
+ const pathParts = displayPath.split(/[\\/]+/);
54521
+ const filteredParts = pathParts.filter((part) => part !== "");
54522
+ if (filteredParts.length > segments) {
54523
+ const selectedSegments = filteredParts.slice(-segments);
54524
+ const prefix = displayPath.startsWith("~") ? `~${outSep}` : "";
54525
+ displayPath = prefix + "..." + outSep + selectedSegments.join(outSep);
54526
+ }
54393
54527
  }
54394
54528
  }
54395
54529
  return item.rawValue ? displayPath : `cwd: ${displayPath}`;
54396
54530
  }
54397
54531
  getCustomKeybinds() {
54398
54532
  return [
54533
+ { key: "h", label: "(h)ome ~", action: "toggle-abbreviate-home" },
54399
54534
  { key: "s", label: "(s)egments", action: "edit-segments" },
54400
54535
  { key: "f", label: "(f)ish style", action: "toggle-fish-style" }
54401
54536
  ];
@@ -54411,6 +54546,20 @@ class CurrentWorkingDirWidget {
54411
54546
  supportsColors(item) {
54412
54547
  return true;
54413
54548
  }
54549
+ abbreviateHomeDir(path6) {
54550
+ const homeDir = os5.homedir();
54551
+ if (path6 === homeDir) {
54552
+ return "~";
54553
+ }
54554
+ if (path6.startsWith(homeDir)) {
54555
+ const boundaryChar = path6[homeDir.length];
54556
+ if (boundaryChar !== "/" && boundaryChar !== "\\") {
54557
+ return path6;
54558
+ }
54559
+ return "~" + path6.slice(homeDir.length);
54560
+ }
54561
+ return path6;
54562
+ }
54414
54563
  abbreviatePath(path6) {
54415
54564
  const homeDir = os5.homedir();
54416
54565
  const useBackslash = path6.includes("\\") && !path6.includes("/");
@@ -54538,7 +54687,7 @@ class ClaudeSessionIdWidget {
54538
54687
  }
54539
54688
  // src/widgets/ApiUsage.tsx
54540
54689
  import {
54541
- execSync as execSync8,
54690
+ execSync as execSync6,
54542
54691
  spawnSync
54543
54692
  } from "child_process";
54544
54693
  import * as fs7 from "fs";
@@ -54563,7 +54712,7 @@ function readTokenFromFile() {
54563
54712
  }
54564
54713
  function readTokenFromKeychain() {
54565
54714
  try {
54566
- const result = execSync8('security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null', { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
54715
+ const result = execSync6('security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null', { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
54567
54716
  const parsed = JSON.parse(result);
54568
54717
  return parsed?.claudeAiOauth?.accessToken ?? null;
54569
54718
  } catch {
@@ -54856,7 +55005,7 @@ class ResetTimerWidget {
54856
55005
  const data = fetchApiData();
54857
55006
  if (data.error)
54858
55007
  return getErrorMessage(data.error);
54859
- if (data.extraUsageEnabled && data.extraUsageUsed !== undefined && data.extraUsageLimit !== undefined) {
55008
+ if (data.extraUsageEnabled && data.weeklyUsage !== undefined && data.weeklyUsage >= 100 && data.extraUsageUsed !== undefined && data.extraUsageLimit !== undefined) {
54860
55009
  const used = formatCents(data.extraUsageUsed);
54861
55010
  const limit = formatCents(data.extraUsageLimit);
54862
55011
  return `Extra: ${used}/${limit}`;
@@ -54944,12 +55093,226 @@ class ContextBarWidget {
54944
55093
  return true;
54945
55094
  }
54946
55095
  }
55096
+ // src/widgets/SessionName.ts
55097
+ import * as fs8 from "fs";
55098
+
55099
+ class SessionNameWidget {
55100
+ getDefaultColor() {
55101
+ return "cyan";
55102
+ }
55103
+ getDescription() {
55104
+ return "Shows the session name set via /rename command in Claude Code";
55105
+ }
55106
+ getDisplayName() {
55107
+ return "Session Name";
55108
+ }
55109
+ getCategory() {
55110
+ return "Session";
55111
+ }
55112
+ getEditorDisplay(item) {
55113
+ return { displayText: this.getDisplayName() };
55114
+ }
55115
+ render(item, context, settings) {
55116
+ if (context.isPreview) {
55117
+ return item.rawValue ? "my-session" : "Session: my-session";
55118
+ }
55119
+ const transcriptPath = context.data?.transcript_path;
55120
+ if (!transcriptPath) {
55121
+ return null;
55122
+ }
55123
+ try {
55124
+ const content = fs8.readFileSync(transcriptPath, "utf-8");
55125
+ const lines = content.split(`
55126
+ `);
55127
+ for (let i = lines.length - 1;i >= 0; i--) {
55128
+ const line = lines[i]?.trim();
55129
+ if (!line)
55130
+ continue;
55131
+ try {
55132
+ const entry = JSON.parse(line);
55133
+ if (entry.type === "custom-title" && entry.customTitle) {
55134
+ return item.rawValue ? entry.customTitle : `Session: ${entry.customTitle}`;
55135
+ }
55136
+ } catch {}
55137
+ }
55138
+ } catch {}
55139
+ return null;
55140
+ }
55141
+ supportsRawValue() {
55142
+ return true;
55143
+ }
55144
+ supportsColors(item) {
55145
+ return true;
55146
+ }
55147
+ }
55148
+ // src/widgets/FreeMemory.ts
55149
+ import { execSync as execSync7 } from "child_process";
55150
+ import os6 from "os";
55151
+ function formatBytes(bytes) {
55152
+ const GB = 1024 ** 3;
55153
+ const MB = 1024 ** 2;
55154
+ const KB = 1024;
55155
+ if (bytes >= GB)
55156
+ return `${(bytes / GB).toFixed(1)}G`;
55157
+ if (bytes >= MB)
55158
+ return `${(bytes / MB).toFixed(0)}M`;
55159
+ if (bytes >= KB)
55160
+ return `${(bytes / KB).toFixed(0)}K`;
55161
+ return `${bytes}B`;
55162
+ }
55163
+ function getUsedMemoryMacOS() {
55164
+ try {
55165
+ const output = execSync7("vm_stat", { encoding: "utf8" });
55166
+ const lines = output.split(`
55167
+ `);
55168
+ const firstLine = lines[0];
55169
+ if (!firstLine)
55170
+ return null;
55171
+ const pageSizeMatch = /page size of (\d+) bytes/.exec(firstLine);
55172
+ const pageSizeString = pageSizeMatch?.[1];
55173
+ if (!pageSizeString)
55174
+ return null;
55175
+ const pageSize = parseInt(pageSizeString, 10);
55176
+ let activePages = 0;
55177
+ let wiredPages = 0;
55178
+ for (const line of lines) {
55179
+ const activeMatch = /Pages active:\s+(\d+)/.exec(line);
55180
+ const activeValue = activeMatch?.[1];
55181
+ if (activeValue)
55182
+ activePages = parseInt(activeValue, 10);
55183
+ const wiredMatch = /Pages wired down:\s+(\d+)/.exec(line);
55184
+ const wiredValue = wiredMatch?.[1];
55185
+ if (wiredValue)
55186
+ wiredPages = parseInt(wiredValue, 10);
55187
+ }
55188
+ return (activePages + wiredPages) * pageSize;
55189
+ } catch {
55190
+ return null;
55191
+ }
55192
+ }
55193
+
55194
+ class FreeMemoryWidget {
55195
+ getDefaultColor() {
55196
+ return "cyan";
55197
+ }
55198
+ getDescription() {
55199
+ return "Shows system memory usage (used/total)";
55200
+ }
55201
+ getDisplayName() {
55202
+ return "Memory Usage";
55203
+ }
55204
+ getCategory() {
55205
+ return "Environment";
55206
+ }
55207
+ getEditorDisplay(item) {
55208
+ return { displayText: this.getDisplayName() };
55209
+ }
55210
+ render(item, context, settings) {
55211
+ if (context.isPreview) {
55212
+ return item.rawValue ? "12.4G/16.0G" : "Mem: 12.4G/16.0G";
55213
+ }
55214
+ const total = os6.totalmem();
55215
+ let used;
55216
+ if (os6.platform() === "darwin") {
55217
+ used = getUsedMemoryMacOS() ?? total - os6.freemem();
55218
+ } else {
55219
+ used = total - os6.freemem();
55220
+ }
55221
+ const value = `${formatBytes(used)}/${formatBytes(total)}`;
55222
+ return item.rawValue ? value : `Mem: ${value}`;
55223
+ }
55224
+ supportsRawValue() {
55225
+ return true;
55226
+ }
55227
+ supportsColors(item) {
55228
+ return true;
55229
+ }
55230
+ }
55231
+ // src/widgets/Battery.ts
55232
+ import { execSync as execSync8 } from "child_process";
55233
+ import { readFileSync as readFileSync6 } from "fs";
55234
+ function getMacBatteryInfo() {
55235
+ try {
55236
+ const output = execSync8("pmset -g batt", { encoding: "utf-8", timeout: 2000 });
55237
+ const match = /(\d+)%;\s*(charging|discharging|charged|finishing charge|AC attached)/i.exec(output);
55238
+ const percentStr = match?.[1];
55239
+ const stateStr = match?.[2];
55240
+ if (!percentStr || !stateStr) {
55241
+ return null;
55242
+ }
55243
+ const percent = parseInt(percentStr, 10);
55244
+ const state = stateStr.toLowerCase();
55245
+ const charging = state !== "discharging";
55246
+ return { percent, charging };
55247
+ } catch {
55248
+ return null;
55249
+ }
55250
+ }
55251
+ function getLinuxBatteryInfo() {
55252
+ try {
55253
+ const capacity = readFileSync6("/sys/class/power_supply/BAT0/capacity", "utf-8").trim();
55254
+ const status = readFileSync6("/sys/class/power_supply/BAT0/status", "utf-8").trim().toLowerCase();
55255
+ const percent = parseInt(capacity, 10);
55256
+ if (isNaN(percent)) {
55257
+ return null;
55258
+ }
55259
+ const charging = status !== "discharging";
55260
+ return { percent, charging };
55261
+ } catch {
55262
+ return null;
55263
+ }
55264
+ }
55265
+ function getBatteryInfo() {
55266
+ if (process.platform === "darwin") {
55267
+ return getMacBatteryInfo();
55268
+ }
55269
+ if (process.platform === "linux") {
55270
+ return getLinuxBatteryInfo();
55271
+ }
55272
+ return null;
55273
+ }
55274
+
55275
+ class BatteryWidget {
55276
+ getDefaultColor() {
55277
+ return "yellow";
55278
+ }
55279
+ getDescription() {
55280
+ return "Shows battery percentage (only when on battery, hidden when charging)";
55281
+ }
55282
+ getDisplayName() {
55283
+ return "Battery";
55284
+ }
55285
+ getCategory() {
55286
+ return "Environment";
55287
+ }
55288
+ getEditorDisplay(item) {
55289
+ return { displayText: this.getDisplayName() };
55290
+ }
55291
+ render(item, context, settings) {
55292
+ if (context.isPreview) {
55293
+ return item.rawValue ? "72%" : "B: 72%";
55294
+ }
55295
+ const info = getBatteryInfo();
55296
+ if (!info || info.charging) {
55297
+ return null;
55298
+ }
55299
+ const label = item.rawValue ? "" : "B: ";
55300
+ return `${label}${info.percent}%`;
55301
+ }
55302
+ supportsRawValue() {
55303
+ return true;
55304
+ }
55305
+ supportsColors(item) {
55306
+ return true;
55307
+ }
55308
+ }
54947
55309
  // src/utils/widgets.ts
54948
55310
  var widgetRegistry = new Map([
54949
55311
  ["model", new ModelWidget],
54950
55312
  ["output-style", new OutputStyleWidget],
54951
55313
  ["git-branch", new GitBranchWidget],
54952
55314
  ["git-changes", new GitChangesWidget],
55315
+ ["git-root-dir", new GitRootDirWidget],
54953
55316
  ["git-worktree", new GitWorktreeWidget],
54954
55317
  ["current-working-dir", new CurrentWorkingDirWidget],
54955
55318
  ["tokens-input", new TokensInputWidget],
@@ -54970,7 +55333,10 @@ var widgetRegistry = new Map([
54970
55333
  ["session-usage", new SessionUsageWidget],
54971
55334
  ["weekly-usage", new WeeklyUsageWidget],
54972
55335
  ["reset-timer", new ResetTimerWidget],
54973
- ["context-bar", new ContextBarWidget]
55336
+ ["context-bar", new ContextBarWidget],
55337
+ ["session-name", new SessionNameWidget],
55338
+ ["free-memory", new FreeMemoryWidget],
55339
+ ["battery", new BatteryWidget]
54974
55340
  ]);
54975
55341
  function getWidget(type) {
54976
55342
  return widgetRegistry.get(type) ?? null;
@@ -56466,7 +56832,11 @@ var ItemsEditor = ({ widgets, onUpdate, onBack, lineNumber, settings }) => {
56466
56832
  }
56467
56833
  } else if (input === "r" && widgets.length > 0) {
56468
56834
  const currentWidget2 = widgets[selectedIndex];
56469
- if (currentWidget2 && currentWidget2.type !== "separator" && currentWidget2.type !== "flex-separator" && currentWidget2.type !== "custom-text") {
56835
+ if (currentWidget2 && currentWidget2.type !== "separator" && currentWidget2.type !== "flex-separator") {
56836
+ const widgetImpl = getWidget(currentWidget2.type);
56837
+ if (!widgetImpl?.supportsRawValue()) {
56838
+ return;
56839
+ }
56470
56840
  const newWidgets = [...widgets];
56471
56841
  newWidgets[selectedIndex] = { ...currentWidget2, rawValue: !currentWidget2.rawValue };
56472
56842
  onUpdate(newWidgets);
@@ -56875,6 +57245,7 @@ var ItemsEditor = ({ widgets, onUpdate, onBack, lineNumber, settings }) => {
56875
57245
  const isSelected = index === selectedIndex;
56876
57246
  const widgetImpl = widget.type !== "separator" && widget.type !== "flex-separator" ? getWidget(widget.type) : null;
56877
57247
  const { displayText, modifierText } = widgetImpl?.getEditorDisplay(widget) ?? { displayText: getWidgetDisplay(widget) };
57248
+ const supportsRawValue = widgetImpl?.supportsRawValue() ?? false;
56878
57249
  return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
56879
57250
  flexDirection: "row",
56880
57251
  flexWrap: "nowrap",
@@ -56897,7 +57268,7 @@ var ItemsEditor = ({ widgets, onUpdate, onBack, lineNumber, settings }) => {
56897
57268
  modifierText
56898
57269
  ]
56899
57270
  }, undefined, true, undefined, this),
56900
- widget.rawValue && /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
57271
+ supportsRawValue && widget.rawValue && /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
56901
57272
  dimColor: true,
56902
57273
  children: " (raw value)"
56903
57274
  }, undefined, false, undefined, this),
@@ -57325,7 +57696,7 @@ var MainMenu = ({ onSelect, isClaudeInstalled, hasChanges, initialSelection = 0,
57325
57696
  };
57326
57697
  // src/tui/components/PowerlineSetup.tsx
57327
57698
  var import_react41 = __toESM(require_react(), 1);
57328
- import * as os6 from "os";
57699
+ import * as os7 from "os";
57329
57700
 
57330
57701
  // src/tui/components/PowerlineSeparatorEditor.tsx
57331
57702
  var import_react39 = __toESM(require_react(), 1);
@@ -58042,7 +58413,7 @@ var PowerlineSetup = ({
58042
58413
  }, undefined, false, undefined, this)
58043
58414
  ]
58044
58415
  }, undefined, true, undefined, this),
58045
- os6.platform() === "darwin" && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
58416
+ os7.platform() === "darwin" && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
58046
58417
  children: [
58047
58418
  /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
58048
58419
  dimColor: true,
@@ -58058,7 +58429,7 @@ var PowerlineSetup = ({
58058
58429
  }, undefined, false, undefined, this)
58059
58430
  ]
58060
58431
  }, undefined, true, undefined, this),
58061
- os6.platform() === "linux" && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
58432
+ os7.platform() === "linux" && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
58062
58433
  children: [
58063
58434
  /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
58064
58435
  dimColor: true,
@@ -58074,7 +58445,7 @@ var PowerlineSetup = ({
58074
58445
  }, undefined, false, undefined, this)
58075
58446
  ]
58076
58447
  }, undefined, true, undefined, this),
58077
- os6.platform() === "win32" && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
58448
+ os7.platform() === "win32" && /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(jsx_dev_runtime13.Fragment, {
58078
58449
  children: [
58079
58450
  /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(Text, {
58080
58451
  dimColor: true,
@@ -59329,7 +59700,9 @@ function renderCompactOutput(preRenderedLines, settings, maxWidth) {
59329
59700
  }
59330
59701
 
59331
59702
  // src/utils/jsonl.ts
59332
- import * as fs8 from "fs";
59703
+ import * as fs9 from "fs";
59704
+ import { createHash } from "node:crypto";
59705
+ import os8 from "node:os";
59333
59706
  import path8 from "node:path";
59334
59707
 
59335
59708
  // node_modules/tinyglobby/dist/index.mjs
@@ -59464,12 +59837,12 @@ function build$3(options) {
59464
59837
  return options.group ? groupFiles : empty;
59465
59838
  }
59466
59839
  var resolveSymlinksAsync = function(path7, state, callback$1) {
59467
- const { queue, fs: fs8, options: { suppressErrors } } = state;
59840
+ const { queue, fs: fs9, options: { suppressErrors } } = state;
59468
59841
  queue.enqueue();
59469
- fs8.realpath(path7, (error43, resolvedPath) => {
59842
+ fs9.realpath(path7, (error43, resolvedPath) => {
59470
59843
  if (error43)
59471
59844
  return queue.dequeue(suppressErrors ? null : error43, state);
59472
- fs8.stat(resolvedPath, (error$1, stat) => {
59845
+ fs9.stat(resolvedPath, (error$1, stat) => {
59473
59846
  if (error$1)
59474
59847
  return queue.dequeue(suppressErrors ? null : error$1, state);
59475
59848
  if (stat.isDirectory() && isRecursive(path7, resolvedPath, state))
@@ -59480,11 +59853,11 @@ var resolveSymlinksAsync = function(path7, state, callback$1) {
59480
59853
  });
59481
59854
  };
59482
59855
  var resolveSymlinks = function(path7, state, callback$1) {
59483
- const { queue, fs: fs8, options: { suppressErrors } } = state;
59856
+ const { queue, fs: fs9, options: { suppressErrors } } = state;
59484
59857
  queue.enqueue();
59485
59858
  try {
59486
- const resolvedPath = fs8.realpathSync(path7);
59487
- const stat = fs8.statSync(resolvedPath);
59859
+ const resolvedPath = fs9.realpathSync(path7);
59860
+ const stat = fs9.statSync(resolvedPath);
59488
59861
  if (stat.isDirectory() && isRecursive(path7, resolvedPath, state))
59489
59862
  return;
59490
59863
  callback$1(stat, resolvedPath);
@@ -59567,23 +59940,23 @@ var walkAsync = (state, crawlPath, directoryPath, currentDepth, callback$1) => {
59567
59940
  state.queue.enqueue();
59568
59941
  if (currentDepth < 0)
59569
59942
  return state.queue.dequeue(null, state);
59570
- const { fs: fs8 } = state;
59943
+ const { fs: fs9 } = state;
59571
59944
  state.visited.push(crawlPath);
59572
59945
  state.counts.directories++;
59573
- fs8.readdir(crawlPath || ".", readdirOpts, (error43, entries = []) => {
59946
+ fs9.readdir(crawlPath || ".", readdirOpts, (error43, entries = []) => {
59574
59947
  callback$1(entries, directoryPath, currentDepth);
59575
59948
  state.queue.dequeue(state.options.suppressErrors ? null : error43, state);
59576
59949
  });
59577
59950
  };
59578
59951
  var walkSync = (state, crawlPath, directoryPath, currentDepth, callback$1) => {
59579
- const { fs: fs8 } = state;
59952
+ const { fs: fs9 } = state;
59580
59953
  if (currentDepth < 0)
59581
59954
  return;
59582
59955
  state.visited.push(crawlPath);
59583
59956
  state.counts.directories++;
59584
59957
  let entries = [];
59585
59958
  try {
59586
- entries = fs8.readdirSync(crawlPath || ".", readdirOpts);
59959
+ entries = fs9.readdirSync(crawlPath || ".", readdirOpts);
59587
59960
  } catch (e) {
59588
59961
  if (!state.options.suppressErrors)
59589
59962
  throw e;
@@ -60118,12 +60491,87 @@ function globSync(patternsOrOptions, options) {
60118
60491
 
60119
60492
  // src/utils/jsonl.ts
60120
60493
  import { promisify } from "util";
60121
- var readFile4 = promisify(fs8.readFile);
60122
- var readFileSync6 = fs8.readFileSync;
60123
- var statSync5 = fs8.statSync;
60494
+ var readFile4 = promisify(fs9.readFile);
60495
+ var readFileSync8 = fs9.readFileSync;
60496
+ var statSync5 = fs9.statSync;
60497
+ var writeFileSync3 = fs9.writeFileSync;
60498
+ var mkdirSync4 = fs9.mkdirSync;
60499
+ var existsSync8 = fs9.existsSync;
60500
+ function normalizeConfigDir(configDir) {
60501
+ return path8.resolve(configDir);
60502
+ }
60503
+ function getBlockCachePath(configDir = getClaudeConfigDir()) {
60504
+ const normalizedConfigDir = normalizeConfigDir(configDir);
60505
+ const configHash = createHash("sha256").update(normalizedConfigDir).digest("hex").slice(0, 16);
60506
+ return path8.join(os8.homedir(), ".cache", "ccstatusline", `block-cache-${configHash}.json`);
60507
+ }
60508
+ function readBlockCache(expectedConfigDir) {
60509
+ try {
60510
+ const normalizedExpectedConfigDir = expectedConfigDir !== undefined ? normalizeConfigDir(expectedConfigDir) : undefined;
60511
+ const cachePath = getBlockCachePath(normalizedExpectedConfigDir);
60512
+ if (!existsSync8(cachePath)) {
60513
+ return null;
60514
+ }
60515
+ const content = readFileSync8(cachePath, "utf-8");
60516
+ const cache3 = JSON.parse(content);
60517
+ if (typeof cache3.startTime !== "string") {
60518
+ return null;
60519
+ }
60520
+ if (normalizedExpectedConfigDir !== undefined) {
60521
+ if (typeof cache3.configDir !== "string") {
60522
+ return null;
60523
+ }
60524
+ if (cache3.configDir !== normalizedExpectedConfigDir) {
60525
+ return null;
60526
+ }
60527
+ }
60528
+ const date5 = new Date(cache3.startTime);
60529
+ if (Number.isNaN(date5.getTime())) {
60530
+ return null;
60531
+ }
60532
+ return date5;
60533
+ } catch {
60534
+ return null;
60535
+ }
60536
+ }
60537
+ function writeBlockCache(startTime, configDir = getClaudeConfigDir()) {
60538
+ try {
60539
+ const normalizedConfigDir = normalizeConfigDir(configDir);
60540
+ const cachePath = getBlockCachePath(normalizedConfigDir);
60541
+ const cacheDir = path8.dirname(cachePath);
60542
+ if (!existsSync8(cacheDir)) {
60543
+ mkdirSync4(cacheDir, { recursive: true });
60544
+ }
60545
+ const cache3 = {
60546
+ startTime: startTime.toISOString(),
60547
+ configDir: normalizedConfigDir
60548
+ };
60549
+ writeFileSync3(cachePath, JSON.stringify(cache3), "utf-8");
60550
+ } catch {}
60551
+ }
60552
+ function getCachedBlockMetrics(sessionDurationHours = 5) {
60553
+ const sessionDurationMs = sessionDurationHours * 60 * 60 * 1000;
60554
+ const now = new Date;
60555
+ const activeConfigDir = getClaudeConfigDir();
60556
+ const cachedStartTime = readBlockCache(activeConfigDir);
60557
+ if (cachedStartTime) {
60558
+ const blockEndTime = new Date(cachedStartTime.getTime() + sessionDurationMs);
60559
+ if (now.getTime() <= blockEndTime.getTime()) {
60560
+ return {
60561
+ startTime: cachedStartTime,
60562
+ lastActivity: now
60563
+ };
60564
+ }
60565
+ }
60566
+ const metrics = getBlockMetrics();
60567
+ if (metrics) {
60568
+ writeBlockCache(metrics.startTime, activeConfigDir);
60569
+ }
60570
+ return metrics;
60571
+ }
60124
60572
  async function getSessionDuration(transcriptPath) {
60125
60573
  try {
60126
- if (!fs8.existsSync(transcriptPath)) {
60574
+ if (!fs9.existsSync(transcriptPath)) {
60127
60575
  return null;
60128
60576
  }
60129
60577
  const content = await readFile4(transcriptPath, "utf-8");
@@ -60175,7 +60623,7 @@ async function getSessionDuration(transcriptPath) {
60175
60623
  }
60176
60624
  async function getTokenMetrics(transcriptPath) {
60177
60625
  try {
60178
- if (!fs8.existsSync(transcriptPath)) {
60626
+ if (!fs9.existsSync(transcriptPath)) {
60179
60627
  return { inputTokens: 0, outputTokens: 0, cachedTokens: 0, totalTokens: 0, contextLength: 0 };
60180
60628
  }
60181
60629
  const content = await readFile4(transcriptPath, "utf-8");
@@ -60322,7 +60770,7 @@ function findMostRecentBlockStartTime(rootDir, sessionDurationHours = 5) {
60322
60770
  function getAllTimestampsFromFile(filePath) {
60323
60771
  const timestamps = [];
60324
60772
  try {
60325
- const content = readFileSync6(filePath, "utf-8");
60773
+ const content = readFileSync8(filePath, "utf-8");
60326
60774
  const lines = content.trim().split(`
60327
60775
  `).filter((line) => line.length > 0);
60328
60776
  for (const line of lines) {
@@ -60390,6 +60838,15 @@ async function readStdin() {
60390
60838
  return null;
60391
60839
  }
60392
60840
  }
60841
+ async function ensureWindowsUtf8CodePage() {
60842
+ if (process.platform !== "win32") {
60843
+ return;
60844
+ }
60845
+ try {
60846
+ const { execFileSync } = await import("child_process");
60847
+ execFileSync("chcp.com", ["65001"], { stdio: "ignore" });
60848
+ } catch {}
60849
+ }
60393
60850
  async function renderMultipleLines(data) {
60394
60851
  const settings = await loadSettings();
60395
60852
  source_default.level = settings.colorLevel;
@@ -60408,7 +60865,7 @@ async function renderMultipleLines(data) {
60408
60865
  }
60409
60866
  let blockMetrics = null;
60410
60867
  if (hasBlockTimer) {
60411
- blockMetrics = getBlockMetrics();
60868
+ blockMetrics = getCachedBlockMetrics();
60412
60869
  }
60413
60870
  const terminalWidth = getTerminalWidth();
60414
60871
  const compact = shouldUseCompactMode(terminalWidth, data);
@@ -60465,6 +60922,7 @@ async function renderMultipleLines(data) {
60465
60922
  }
60466
60923
  async function main() {
60467
60924
  if (!process.stdin.isTTY) {
60925
+ await ensureWindowsUtf8CodePage();
60468
60926
  const input = await readStdin();
60469
60927
  if (input && input.trim() !== "") {
60470
60928
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccstatusline-usage",
3
- "version": "2.1.5",
3
+ "version": "2.1.7",
4
4
  "description": "A customizable status line formatter for Claude Code CLI",
5
5
  "module": "src/ccstatusline.ts",
6
6
  "type": "module",