ccstatusline-usage 2.1.5 → 2.1.6

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,22 @@
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.6](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.6) - 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): **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
52
52
 
53
- ### [v2.1.4](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.4) - Compact mode widget-boundary wrapping
53
+ ### v2.0.26 - v2.0.28 - Performance, git internals, and workflow improvements
54
54
 
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
61
-
62
- ### [v2.1.3](https://github.com/pcvelz/ccstatusline-usage/releases/tag/v2.1.3) - Mobile/compact mode and thinking effort bars
63
-
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)
55
+ - **⚡ 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.
56
+ - **🧱 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.
57
+ - **🪟 Windows UTF-8 piped output fix (v2.0.28)** - Sets the Windows UTF-8 code page for piped status line rendering.
58
+ - **📁 Git Root Dir widget (v2.0.27)** - Added a new Git widget that shows the repository root directory name.
59
+ - **🏷️ Session Name widget (v2.0.26)** - Added a new widget that shows the current Claude Code session name from `/rename`.
60
+ - **🏠 Current Working Directory home abbreviation (v2.0.26)** - Added a `~` abbreviation option for CWD display in both preview and live rendering.
61
+ - **🧠 Context model suffix fix (v2.0.26)** - Context widgets now recognize the `[1m]` suffix across models, not just a single model path.
62
+ - **🧭 Widget picker UX updates (v2.0.26)** - Improved widget discovery/navigation and added clearer, safer clear-line behavior.
63
+ - **⌨️ TUI editor input fix (v2.0.26)** - Prevented shortcut/input leakage into widget editor flows.
64
+ - **📄 Repo docs update (v2.0.26)** - Migrated guidance from `CLAUDE.md` to `AGENTS.md` (with symlink compatibility).
73
65
 
74
66
  ### v2.0.16 - Add fish style path abbreviation toggle to Current Working Directory widget
75
67
 
@@ -374,12 +366,13 @@ Once configured, ccstatusline automatically formats your Claude Code status line
374
366
 
375
367
  ### 📊 Available Widgets
376
368
 
377
- - **Model Name** - Shows the current Claude model with thinking effort bars (`▌▌▌`) indicating low/medium/high effort
369
+ - **Model Name** - Shows the current Claude model (e.g., "Claude 3.5 Sonnet")
378
370
  - **Git Branch** - Displays current git branch name
379
371
  - **Git Changes** - Shows uncommitted insertions/deletions (e.g., "+42,-10")
380
372
  - **Git Worktree** - Shows the name of the current git worktree
381
373
  - **Session Clock** - Shows elapsed time since session start (e.g., "2hr 15m")
382
374
  - **Session Cost** - Shows total session cost in USD (e.g., "$1.23")
375
+ - **Session Name** - Shows the session name set via `/rename` command in Claude Code
383
376
  - **Block Timer** - Shows time elapsed in current 5-hour block or progress bar
384
377
  - **Current Working Directory** - Shows current working directory with configurable path segments
385
378
  - **Version** - Shows Claude Code version
@@ -389,8 +382,8 @@ Once configured, ccstatusline automatically formats your Claude Code status line
389
382
  - **Tokens Cached** - Shows cached tokens used
390
383
  - **Tokens Total** - Shows total tokens used
391
384
  - **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%)
385
+ - **Context Percentage** - Shows percentage of context limit used (dynamic: 1M for Sonnet 4.5 with `[1m]` suffix, 200k otherwise)
386
+ - **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
387
  - **Terminal Width** - Shows detected terminal width (for debugging)
395
388
  - **Custom Text** - Add your own custom text to the status line
396
389
  - **Custom Command** - Execute shell commands and display their output (refreshes whenever the statusline is updated by Claude Code)
@@ -460,7 +453,7 @@ The Block Timer widget helps you track your progress through Claude Code's 5-hou
460
453
  ### 🔤 Raw Value Mode
461
454
 
462
455
  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`
456
+ - Normal: `Model: Claude 3.5 Sonnet` → Raw: `Claude 3.5 Sonnet`
464
457
  - Normal: `Session: 2hr 15m` → Raw: `2hr 15m`
465
458
  - Normal: `Block: 3hr 45m` → Raw: `3hr 45m`
466
459
  - Normal: `Ctx: 18.6k` → Raw: `18.6k`
@@ -637,11 +630,6 @@ Contributions are welcome! Please feel free to submit a Pull Request.
637
630
 
638
631
  - GitHub: [@sirmalloc](https://github.com/sirmalloc)
639
632
 
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
633
  ---
646
634
 
647
635
  ## 🔗 Related Projects
@@ -661,11 +649,11 @@ Contributions are welcome! Please feel free to submit a Pull Request.
661
649
 
662
650
  ## Star History
663
651
 
664
- <a href="https://www.star-history.com/#pcvelz/ccstatusline-usage&Timeline">
652
+ <a href="https://www.star-history.com/#sirmalloc/ccstatusline&Timeline">
665
653
  <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" />
654
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=sirmalloc/ccstatusline&type=Timeline&theme=dark" />
655
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=sirmalloc/ccstatusline&type=Timeline" />
656
+ <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=sirmalloc/ccstatusline&type=Timeline" />
669
657
  </picture>
670
658
  </a>
671
659
 
@@ -675,21 +663,21 @@ Contributions are welcome! Please feel free to submit a Pull Request.
675
663
 
676
664
  Give a ⭐ if this project helped you!
677
665
 
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)
666
+ [![GitHub stars](https://img.shields.io/github/stars/sirmalloc/ccstatusline?style=social)](https://github.com/sirmalloc/ccstatusline/stargazers)
667
+ [![GitHub forks](https://img.shields.io/github/forks/sirmalloc/ccstatusline?style=social)](https://github.com/sirmalloc/ccstatusline/network/members)
668
+ [![GitHub watchers](https://img.shields.io/github/watchers/sirmalloc/ccstatusline?style=social)](https://github.com/sirmalloc/ccstatusline/watchers)
681
669
 
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)
670
+ [![npm version](https://img.shields.io/npm/v/ccstatusline.svg)](https://www.npmjs.com/package/ccstatusline)
671
+ [![npm downloads](https://img.shields.io/npm/dm/ccstatusline.svg)](https://www.npmjs.com/package/ccstatusline)
672
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/sirmalloc/ccstatusline/blob/main/LICENSE)
685
673
  [![Made with Bun](https://img.shields.io/badge/Made%20with-Bun-000000.svg?logo=bun)](https://bun.sh)
686
674
 
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)
675
+ [![Issues](https://img.shields.io/github/issues/sirmalloc/ccstatusline)](https://github.com/sirmalloc/ccstatusline/issues)
676
+ [![Pull Requests](https://img.shields.io/github/issues-pr/sirmalloc/ccstatusline)](https://github.com/sirmalloc/ccstatusline/pulls)
677
+ [![Contributors](https://img.shields.io/github/contributors/sirmalloc/ccstatusline)](https://github.com/sirmalloc/ccstatusline/graphs/contributors)
690
678
 
691
679
  ### 💬 Connect
692
680
 
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)
681
+ [Report Bug](https://github.com/sirmalloc/ccstatusline/issues) · [Request Feature](https://github.com/sirmalloc/ccstatusline/issues) · [Discussions](https://github.com/sirmalloc/ccstatusline/discussions)
694
682
 
695
683
  </div>
@@ -51450,7 +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 PACKAGE_VERSION = "2.1.5";
51453
+ var PACKAGE_VERSION = "2.1.6";
51454
51454
  function getPackageVersion() {
51455
51455
  if (/^\d+\.\d+\.\d+/.test(PACKAGE_VERSION)) {
51456
51456
  return PACKAGE_VERSION;
@@ -52425,9 +52425,39 @@ class OutputStyleWidget {
52425
52425
  return true;
52426
52426
  }
52427
52427
  }
52428
- // src/widgets/GitBranch.ts
52428
+ // src/utils/git.ts
52429
52429
  import { execSync as execSync4 } from "child_process";
52430
+ function resolveGitCwd(context) {
52431
+ const candidates = [
52432
+ context.data?.cwd,
52433
+ context.data?.workspace?.current_dir,
52434
+ context.data?.workspace?.project_dir
52435
+ ];
52436
+ for (const candidate of candidates) {
52437
+ if (typeof candidate === "string" && candidate.trim().length > 0) {
52438
+ return candidate;
52439
+ }
52440
+ }
52441
+ return;
52442
+ }
52443
+ function runGit(command, context) {
52444
+ try {
52445
+ const cwd2 = resolveGitCwd(context);
52446
+ const output = execSync4(`git ${command}`, {
52447
+ encoding: "utf8",
52448
+ stdio: ["pipe", "pipe", "ignore"],
52449
+ ...cwd2 ? { cwd: cwd2 } : {}
52450
+ }).trim();
52451
+ return output.length > 0 ? output : null;
52452
+ } catch {
52453
+ return null;
52454
+ }
52455
+ }
52456
+ function isInsideGitWorkTree(context) {
52457
+ return runGit("rev-parse --is-inside-work-tree", context) === "true";
52458
+ }
52430
52459
 
52460
+ // src/widgets/GitBranch.ts
52431
52461
  class GitBranchWidget {
52432
52462
  getDefaultColor() {
52433
52463
  return "magenta";
@@ -52470,21 +52500,16 @@ class GitBranchWidget {
52470
52500
  if (context.isPreview) {
52471
52501
  return item.rawValue ? "main" : "⎇ main";
52472
52502
  }
52473
- const branch = this.getGitBranch();
52503
+ if (!isInsideGitWorkTree(context)) {
52504
+ return hideNoGit ? null : "⎇ no git";
52505
+ }
52506
+ const branch = this.getGitBranch(context);
52474
52507
  if (branch)
52475
52508
  return item.rawValue ? branch : `⎇ ${branch}`;
52476
52509
  return hideNoGit ? null : "⎇ no git";
52477
52510
  }
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
- }
52511
+ getGitBranch(context) {
52512
+ return runGit("branch --show-current", context);
52488
52513
  }
52489
52514
  getCustomKeybinds() {
52490
52515
  return [
@@ -52499,8 +52524,6 @@ class GitBranchWidget {
52499
52524
  }
52500
52525
  }
52501
52526
  // src/widgets/GitChanges.ts
52502
- import { execSync as execSync5 } from "child_process";
52503
-
52504
52527
  class GitChangesWidget {
52505
52528
  getDefaultColor() {
52506
52529
  return "yellow";
@@ -52543,40 +52566,107 @@ class GitChangesWidget {
52543
52566
  if (context.isPreview) {
52544
52567
  return "(+42,-10)";
52545
52568
  }
52546
- const changes = this.getGitChanges();
52569
+ if (!isInsideGitWorkTree(context)) {
52570
+ return hideNoGit ? null : "(no git)";
52571
+ }
52572
+ const changes = this.getGitChanges(context);
52547
52573
  if (changes)
52548
52574
  return `(+${changes.insertions},-${changes.deletions})`;
52549
52575
  else
52550
52576
  return hideNoGit ? null : "(no git)";
52551
52577
  }
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;
52578
+ getGitChanges(context) {
52579
+ let totalInsertions = 0;
52580
+ let totalDeletions = 0;
52581
+ const unstagedStat = runGit("diff --shortstat", context) ?? "";
52582
+ const stagedStat = runGit("diff --cached --shortstat", context) ?? "";
52583
+ if (unstagedStat) {
52584
+ const insertMatch = /(\d+) insertion/.exec(unstagedStat);
52585
+ const deleteMatch = /(\d+) deletion/.exec(unstagedStat);
52586
+ totalInsertions += insertMatch?.[1] ? parseInt(insertMatch[1], 10) : 0;
52587
+ totalDeletions += deleteMatch?.[1] ? parseInt(deleteMatch[1], 10) : 0;
52588
+ }
52589
+ if (stagedStat) {
52590
+ const insertMatch = /(\d+) insertion/.exec(stagedStat);
52591
+ const deleteMatch = /(\d+) deletion/.exec(stagedStat);
52592
+ totalInsertions += insertMatch?.[1] ? parseInt(insertMatch[1], 10) : 0;
52593
+ totalDeletions += deleteMatch?.[1] ? parseInt(deleteMatch[1], 10) : 0;
52594
+ }
52595
+ return { insertions: totalInsertions, deletions: totalDeletions };
52596
+ }
52597
+ getCustomKeybinds() {
52598
+ return [
52599
+ { key: "h", label: "(h)ide 'no git' message", action: "toggle-nogit" }
52600
+ ];
52601
+ }
52602
+ supportsRawValue() {
52603
+ return false;
52604
+ }
52605
+ supportsColors(item) {
52606
+ return true;
52607
+ }
52608
+ }
52609
+ // src/widgets/GitRootDir.ts
52610
+ class GitRootDirWidget {
52611
+ getDefaultColor() {
52612
+ return "cyan";
52613
+ }
52614
+ getDescription() {
52615
+ return "Shows the git repository root directory name";
52616
+ }
52617
+ getDisplayName() {
52618
+ return "Git Root Dir";
52619
+ }
52620
+ getCategory() {
52621
+ return "Git";
52622
+ }
52623
+ getEditorDisplay(item) {
52624
+ const hideNoGit = item.metadata?.hideNoGit === "true";
52625
+ const modifiers = [];
52626
+ if (hideNoGit) {
52627
+ modifiers.push("hide 'no git'");
52628
+ }
52629
+ return {
52630
+ displayText: this.getDisplayName(),
52631
+ modifierText: modifiers.length > 0 ? `(${modifiers.join(", ")})` : undefined
52632
+ };
52633
+ }
52634
+ handleEditorAction(action, item) {
52635
+ if (action === "toggle-nogit") {
52636
+ const currentState = item.metadata?.hideNoGit === "true";
52637
+ return {
52638
+ ...item,
52639
+ metadata: {
52640
+ ...item.metadata,
52641
+ hideNoGit: (!currentState).toString()
52642
+ }
52643
+ };
52579
52644
  }
52645
+ return null;
52646
+ }
52647
+ render(item, context, _settings) {
52648
+ const hideNoGit = item.metadata?.hideNoGit === "true";
52649
+ if (context.isPreview) {
52650
+ return "my-repo";
52651
+ }
52652
+ if (!isInsideGitWorkTree(context)) {
52653
+ return hideNoGit ? null : "no git";
52654
+ }
52655
+ const rootDir = this.getGitRootDir(context);
52656
+ if (rootDir) {
52657
+ return this.getRootDirName(rootDir);
52658
+ }
52659
+ return hideNoGit ? null : "no git";
52660
+ }
52661
+ getGitRootDir(context) {
52662
+ return runGit("rev-parse --show-toplevel", context);
52663
+ }
52664
+ getRootDirName(rootDir) {
52665
+ const trimmedRootDir = rootDir.replace(/[\\/]+$/, "");
52666
+ const normalizedRootDir = trimmedRootDir.length > 0 ? trimmedRootDir : rootDir;
52667
+ const parts = normalizedRootDir.split(/[\\/]/).filter(Boolean);
52668
+ const lastPart = parts[parts.length - 1];
52669
+ return lastPart && lastPart.length > 0 ? lastPart : normalizedRootDir;
52580
52670
  }
52581
52671
  getCustomKeybinds() {
52582
52672
  return [
@@ -52591,8 +52681,6 @@ class GitChangesWidget {
52591
52681
  }
52592
52682
  }
52593
52683
  // src/widgets/GitWorktree.ts
52594
- import { execSync as execSync6 } from "child_process";
52595
-
52596
52684
  class GitWorktreeWidget {
52597
52685
  getDefaultColor() {
52598
52686
  return "blue";
@@ -52634,24 +52722,29 @@ class GitWorktreeWidget {
52634
52722
  const hideNoGit = item.metadata?.hideNoGit === "true";
52635
52723
  if (context.isPreview)
52636
52724
  return item.rawValue ? "main" : "\uD81A\uDC30 main";
52637
- const worktree = this.getGitWorktree();
52725
+ if (!isInsideGitWorkTree(context)) {
52726
+ return hideNoGit ? null : "\uD81A\uDC30 no git";
52727
+ }
52728
+ const worktree = this.getGitWorktree(context);
52638
52729
  if (worktree)
52639
52730
  return item.rawValue ? worktree : `\uD81A\uDC30 ${worktree}`;
52640
52731
  return hideNoGit ? null : "\uD81A\uDC30 no git";
52641
52732
  }
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 {
52733
+ getGitWorktree(context) {
52734
+ const worktreeDir = runGit("rev-parse --git-dir", context);
52735
+ if (!worktreeDir) {
52736
+ return null;
52737
+ }
52738
+ const normalizedGitDir = worktreeDir.replace(/\\/g, "/");
52739
+ if (normalizedGitDir.endsWith("/.git") || normalizedGitDir === ".git")
52740
+ return "main";
52741
+ const marker = ".git/worktrees/";
52742
+ const markerIndex = normalizedGitDir.lastIndexOf(marker);
52743
+ if (markerIndex === -1) {
52653
52744
  return null;
52654
52745
  }
52746
+ const worktree = normalizedGitDir.slice(markerIndex + marker.length);
52747
+ return worktree.length > 0 ? worktree : null;
52655
52748
  }
52656
52749
  getCustomKeybinds() {
52657
52750
  return [
@@ -53935,7 +54028,7 @@ var CustomTextEditor = ({ widget, onComplete, onCancel }) => {
53935
54028
  }, undefined, true, undefined, this);
53936
54029
  };
53937
54030
  // src/widgets/CustomCommand.tsx
53938
- import { execSync as execSync7 } from "child_process";
54031
+ import { execSync as execSync5 } from "child_process";
53939
54032
  var import_react30 = __toESM(require_react(), 1);
53940
54033
  var jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
53941
54034
 
@@ -53984,7 +54077,7 @@ class CustomCommandWidget {
53984
54077
  try {
53985
54078
  const timeout = item.timeout ?? 1000;
53986
54079
  const jsonInput = JSON.stringify(context.data);
53987
- let output = execSync7(item.commandPath, {
54080
+ let output = execSync5(item.commandPath, {
53988
54081
  encoding: "utf8",
53989
54082
  input: jsonInput,
53990
54083
  timeout,
@@ -54324,7 +54417,11 @@ class CurrentWorkingDirWidget {
54324
54417
  getEditorDisplay(item) {
54325
54418
  const segments = item.metadata?.segments ? parseInt(item.metadata.segments, 10) : undefined;
54326
54419
  const fishStyle = item.metadata?.fishStyle === "true";
54420
+ const abbreviateHome = item.metadata?.abbreviateHome === "true";
54327
54421
  const modifiers = [];
54422
+ if (abbreviateHome) {
54423
+ modifiers.push("~");
54424
+ }
54328
54425
  if (fishStyle) {
54329
54426
  modifiers.push("fish-style");
54330
54427
  } else if (segments && segments > 0) {
@@ -54336,11 +54433,31 @@ class CurrentWorkingDirWidget {
54336
54433
  };
54337
54434
  }
54338
54435
  handleEditorAction(action, item) {
54436
+ if (action === "toggle-abbreviate-home") {
54437
+ const currentAbbreviateHome = item.metadata?.abbreviateHome === "true";
54438
+ const newAbbreviateHome = !currentAbbreviateHome;
54439
+ if (newAbbreviateHome) {
54440
+ const { fishStyle, ...restMetadata } = item.metadata ?? {};
54441
+ return {
54442
+ ...item,
54443
+ metadata: {
54444
+ ...restMetadata,
54445
+ abbreviateHome: "true"
54446
+ }
54447
+ };
54448
+ } else {
54449
+ const { abbreviateHome, ...restMetadata } = item.metadata ?? {};
54450
+ return {
54451
+ ...item,
54452
+ metadata: Object.keys(restMetadata).length > 0 ? restMetadata : undefined
54453
+ };
54454
+ }
54455
+ }
54339
54456
  if (action === "toggle-fish-style") {
54340
54457
  const currentFishStyle = item.metadata?.fishStyle === "true";
54341
54458
  const newFishStyle = !currentFishStyle;
54342
54459
  if (newFishStyle) {
54343
- const { segments, ...restMetadata } = item.metadata ?? {};
54460
+ const { segments, abbreviateHome, ...restMetadata } = item.metadata ?? {};
54344
54461
  return {
54345
54462
  ...item,
54346
54463
  metadata: {
@@ -54361,10 +54478,19 @@ class CurrentWorkingDirWidget {
54361
54478
  render(item, context, settings) {
54362
54479
  const segments = item.metadata?.segments ? parseInt(item.metadata.segments, 10) : undefined;
54363
54480
  const fishStyle = item.metadata?.fishStyle === "true";
54481
+ const abbreviateHome = item.metadata?.abbreviateHome === "true";
54364
54482
  if (context.isPreview) {
54365
54483
  let previewPath;
54366
54484
  if (fishStyle) {
54367
54485
  previewPath = "~/D/P/my-project";
54486
+ } else if (abbreviateHome && segments && segments > 0) {
54487
+ if (segments === 1) {
54488
+ previewPath = "~/.../my-project";
54489
+ } else {
54490
+ previewPath = "~/.../Projects/my-project";
54491
+ }
54492
+ } else if (abbreviateHome) {
54493
+ previewPath = "~/Documents/Projects/my-project";
54368
54494
  } else if (segments && segments > 0) {
54369
54495
  if (segments === 1) {
54370
54496
  previewPath = ".../project";
@@ -54382,20 +54508,27 @@ class CurrentWorkingDirWidget {
54382
54508
  let displayPath = cwd2;
54383
54509
  if (fishStyle) {
54384
54510
  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);
54511
+ } else {
54512
+ if (abbreviateHome) {
54513
+ displayPath = this.abbreviateHomeDir(displayPath);
54514
+ }
54515
+ if (segments && segments > 0) {
54516
+ const useBackslash = displayPath.includes("\\") && !displayPath.includes("/");
54517
+ const outSep = useBackslash ? "\\" : "/";
54518
+ const pathParts = displayPath.split(/[\\/]+/);
54519
+ const filteredParts = pathParts.filter((part) => part !== "");
54520
+ if (filteredParts.length > segments) {
54521
+ const selectedSegments = filteredParts.slice(-segments);
54522
+ const prefix = displayPath.startsWith("~") ? `~${outSep}` : "";
54523
+ displayPath = prefix + "..." + outSep + selectedSegments.join(outSep);
54524
+ }
54393
54525
  }
54394
54526
  }
54395
54527
  return item.rawValue ? displayPath : `cwd: ${displayPath}`;
54396
54528
  }
54397
54529
  getCustomKeybinds() {
54398
54530
  return [
54531
+ { key: "h", label: "(h)ome ~", action: "toggle-abbreviate-home" },
54399
54532
  { key: "s", label: "(s)egments", action: "edit-segments" },
54400
54533
  { key: "f", label: "(f)ish style", action: "toggle-fish-style" }
54401
54534
  ];
@@ -54411,6 +54544,20 @@ class CurrentWorkingDirWidget {
54411
54544
  supportsColors(item) {
54412
54545
  return true;
54413
54546
  }
54547
+ abbreviateHomeDir(path6) {
54548
+ const homeDir = os5.homedir();
54549
+ if (path6 === homeDir) {
54550
+ return "~";
54551
+ }
54552
+ if (path6.startsWith(homeDir)) {
54553
+ const boundaryChar = path6[homeDir.length];
54554
+ if (boundaryChar !== "/" && boundaryChar !== "\\") {
54555
+ return path6;
54556
+ }
54557
+ return "~" + path6.slice(homeDir.length);
54558
+ }
54559
+ return path6;
54560
+ }
54414
54561
  abbreviatePath(path6) {
54415
54562
  const homeDir = os5.homedir();
54416
54563
  const useBackslash = path6.includes("\\") && !path6.includes("/");
@@ -54538,7 +54685,7 @@ class ClaudeSessionIdWidget {
54538
54685
  }
54539
54686
  // src/widgets/ApiUsage.tsx
54540
54687
  import {
54541
- execSync as execSync8,
54688
+ execSync as execSync6,
54542
54689
  spawnSync
54543
54690
  } from "child_process";
54544
54691
  import * as fs7 from "fs";
@@ -54563,7 +54710,7 @@ function readTokenFromFile() {
54563
54710
  }
54564
54711
  function readTokenFromKeychain() {
54565
54712
  try {
54566
- const result = execSync8('security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null', { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
54713
+ const result = execSync6('security find-generic-password -s "Claude Code-credentials" -w 2>/dev/null', { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
54567
54714
  const parsed = JSON.parse(result);
54568
54715
  return parsed?.claudeAiOauth?.accessToken ?? null;
54569
54716
  } catch {
@@ -54944,12 +55091,65 @@ class ContextBarWidget {
54944
55091
  return true;
54945
55092
  }
54946
55093
  }
55094
+ // src/widgets/SessionName.ts
55095
+ import * as fs8 from "fs";
55096
+
55097
+ class SessionNameWidget {
55098
+ getDefaultColor() {
55099
+ return "cyan";
55100
+ }
55101
+ getDescription() {
55102
+ return "Shows the session name set via /rename command in Claude Code";
55103
+ }
55104
+ getDisplayName() {
55105
+ return "Session Name";
55106
+ }
55107
+ getCategory() {
55108
+ return "Session";
55109
+ }
55110
+ getEditorDisplay(item) {
55111
+ return { displayText: this.getDisplayName() };
55112
+ }
55113
+ render(item, context, settings) {
55114
+ if (context.isPreview) {
55115
+ return item.rawValue ? "my-session" : "Session: my-session";
55116
+ }
55117
+ const transcriptPath = context.data?.transcript_path;
55118
+ if (!transcriptPath) {
55119
+ return null;
55120
+ }
55121
+ try {
55122
+ const content = fs8.readFileSync(transcriptPath, "utf-8");
55123
+ const lines = content.split(`
55124
+ `);
55125
+ for (let i = lines.length - 1;i >= 0; i--) {
55126
+ const line = lines[i]?.trim();
55127
+ if (!line)
55128
+ continue;
55129
+ try {
55130
+ const entry = JSON.parse(line);
55131
+ if (entry.type === "custom-title" && entry.customTitle) {
55132
+ return item.rawValue ? entry.customTitle : `Session: ${entry.customTitle}`;
55133
+ }
55134
+ } catch {}
55135
+ }
55136
+ } catch {}
55137
+ return null;
55138
+ }
55139
+ supportsRawValue() {
55140
+ return true;
55141
+ }
55142
+ supportsColors(item) {
55143
+ return true;
55144
+ }
55145
+ }
54947
55146
  // src/utils/widgets.ts
54948
55147
  var widgetRegistry = new Map([
54949
55148
  ["model", new ModelWidget],
54950
55149
  ["output-style", new OutputStyleWidget],
54951
55150
  ["git-branch", new GitBranchWidget],
54952
55151
  ["git-changes", new GitChangesWidget],
55152
+ ["git-root-dir", new GitRootDirWidget],
54953
55153
  ["git-worktree", new GitWorktreeWidget],
54954
55154
  ["current-working-dir", new CurrentWorkingDirWidget],
54955
55155
  ["tokens-input", new TokensInputWidget],
@@ -54970,7 +55170,8 @@ var widgetRegistry = new Map([
54970
55170
  ["session-usage", new SessionUsageWidget],
54971
55171
  ["weekly-usage", new WeeklyUsageWidget],
54972
55172
  ["reset-timer", new ResetTimerWidget],
54973
- ["context-bar", new ContextBarWidget]
55173
+ ["context-bar", new ContextBarWidget],
55174
+ ["session-name", new SessionNameWidget]
54974
55175
  ]);
54975
55176
  function getWidget(type) {
54976
55177
  return widgetRegistry.get(type) ?? null;
@@ -56466,7 +56667,11 @@ var ItemsEditor = ({ widgets, onUpdate, onBack, lineNumber, settings }) => {
56466
56667
  }
56467
56668
  } else if (input === "r" && widgets.length > 0) {
56468
56669
  const currentWidget2 = widgets[selectedIndex];
56469
- if (currentWidget2 && currentWidget2.type !== "separator" && currentWidget2.type !== "flex-separator" && currentWidget2.type !== "custom-text") {
56670
+ if (currentWidget2 && currentWidget2.type !== "separator" && currentWidget2.type !== "flex-separator") {
56671
+ const widgetImpl = getWidget(currentWidget2.type);
56672
+ if (!widgetImpl?.supportsRawValue()) {
56673
+ return;
56674
+ }
56470
56675
  const newWidgets = [...widgets];
56471
56676
  newWidgets[selectedIndex] = { ...currentWidget2, rawValue: !currentWidget2.rawValue };
56472
56677
  onUpdate(newWidgets);
@@ -56875,6 +57080,7 @@ var ItemsEditor = ({ widgets, onUpdate, onBack, lineNumber, settings }) => {
56875
57080
  const isSelected = index === selectedIndex;
56876
57081
  const widgetImpl = widget.type !== "separator" && widget.type !== "flex-separator" ? getWidget(widget.type) : null;
56877
57082
  const { displayText, modifierText } = widgetImpl?.getEditorDisplay(widget) ?? { displayText: getWidgetDisplay(widget) };
57083
+ const supportsRawValue = widgetImpl?.supportsRawValue() ?? false;
56878
57084
  return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
56879
57085
  flexDirection: "row",
56880
57086
  flexWrap: "nowrap",
@@ -56897,7 +57103,7 @@ var ItemsEditor = ({ widgets, onUpdate, onBack, lineNumber, settings }) => {
56897
57103
  modifierText
56898
57104
  ]
56899
57105
  }, undefined, true, undefined, this),
56900
- widget.rawValue && /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
57106
+ supportsRawValue && widget.rawValue && /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Text, {
56901
57107
  dimColor: true,
56902
57108
  children: " (raw value)"
56903
57109
  }, undefined, false, undefined, this),
@@ -59329,7 +59535,9 @@ function renderCompactOutput(preRenderedLines, settings, maxWidth) {
59329
59535
  }
59330
59536
 
59331
59537
  // src/utils/jsonl.ts
59332
- import * as fs8 from "fs";
59538
+ import * as fs9 from "fs";
59539
+ import { createHash } from "node:crypto";
59540
+ import os7 from "node:os";
59333
59541
  import path8 from "node:path";
59334
59542
 
59335
59543
  // node_modules/tinyglobby/dist/index.mjs
@@ -59464,12 +59672,12 @@ function build$3(options) {
59464
59672
  return options.group ? groupFiles : empty;
59465
59673
  }
59466
59674
  var resolveSymlinksAsync = function(path7, state, callback$1) {
59467
- const { queue, fs: fs8, options: { suppressErrors } } = state;
59675
+ const { queue, fs: fs9, options: { suppressErrors } } = state;
59468
59676
  queue.enqueue();
59469
- fs8.realpath(path7, (error43, resolvedPath) => {
59677
+ fs9.realpath(path7, (error43, resolvedPath) => {
59470
59678
  if (error43)
59471
59679
  return queue.dequeue(suppressErrors ? null : error43, state);
59472
- fs8.stat(resolvedPath, (error$1, stat) => {
59680
+ fs9.stat(resolvedPath, (error$1, stat) => {
59473
59681
  if (error$1)
59474
59682
  return queue.dequeue(suppressErrors ? null : error$1, state);
59475
59683
  if (stat.isDirectory() && isRecursive(path7, resolvedPath, state))
@@ -59480,11 +59688,11 @@ var resolveSymlinksAsync = function(path7, state, callback$1) {
59480
59688
  });
59481
59689
  };
59482
59690
  var resolveSymlinks = function(path7, state, callback$1) {
59483
- const { queue, fs: fs8, options: { suppressErrors } } = state;
59691
+ const { queue, fs: fs9, options: { suppressErrors } } = state;
59484
59692
  queue.enqueue();
59485
59693
  try {
59486
- const resolvedPath = fs8.realpathSync(path7);
59487
- const stat = fs8.statSync(resolvedPath);
59694
+ const resolvedPath = fs9.realpathSync(path7);
59695
+ const stat = fs9.statSync(resolvedPath);
59488
59696
  if (stat.isDirectory() && isRecursive(path7, resolvedPath, state))
59489
59697
  return;
59490
59698
  callback$1(stat, resolvedPath);
@@ -59567,23 +59775,23 @@ var walkAsync = (state, crawlPath, directoryPath, currentDepth, callback$1) => {
59567
59775
  state.queue.enqueue();
59568
59776
  if (currentDepth < 0)
59569
59777
  return state.queue.dequeue(null, state);
59570
- const { fs: fs8 } = state;
59778
+ const { fs: fs9 } = state;
59571
59779
  state.visited.push(crawlPath);
59572
59780
  state.counts.directories++;
59573
- fs8.readdir(crawlPath || ".", readdirOpts, (error43, entries = []) => {
59781
+ fs9.readdir(crawlPath || ".", readdirOpts, (error43, entries = []) => {
59574
59782
  callback$1(entries, directoryPath, currentDepth);
59575
59783
  state.queue.dequeue(state.options.suppressErrors ? null : error43, state);
59576
59784
  });
59577
59785
  };
59578
59786
  var walkSync = (state, crawlPath, directoryPath, currentDepth, callback$1) => {
59579
- const { fs: fs8 } = state;
59787
+ const { fs: fs9 } = state;
59580
59788
  if (currentDepth < 0)
59581
59789
  return;
59582
59790
  state.visited.push(crawlPath);
59583
59791
  state.counts.directories++;
59584
59792
  let entries = [];
59585
59793
  try {
59586
- entries = fs8.readdirSync(crawlPath || ".", readdirOpts);
59794
+ entries = fs9.readdirSync(crawlPath || ".", readdirOpts);
59587
59795
  } catch (e) {
59588
59796
  if (!state.options.suppressErrors)
59589
59797
  throw e;
@@ -60118,12 +60326,87 @@ function globSync(patternsOrOptions, options) {
60118
60326
 
60119
60327
  // src/utils/jsonl.ts
60120
60328
  import { promisify } from "util";
60121
- var readFile4 = promisify(fs8.readFile);
60122
- var readFileSync6 = fs8.readFileSync;
60123
- var statSync5 = fs8.statSync;
60329
+ var readFile4 = promisify(fs9.readFile);
60330
+ var readFileSync7 = fs9.readFileSync;
60331
+ var statSync5 = fs9.statSync;
60332
+ var writeFileSync3 = fs9.writeFileSync;
60333
+ var mkdirSync4 = fs9.mkdirSync;
60334
+ var existsSync8 = fs9.existsSync;
60335
+ function normalizeConfigDir(configDir) {
60336
+ return path8.resolve(configDir);
60337
+ }
60338
+ function getBlockCachePath(configDir = getClaudeConfigDir()) {
60339
+ const normalizedConfigDir = normalizeConfigDir(configDir);
60340
+ const configHash = createHash("sha256").update(normalizedConfigDir).digest("hex").slice(0, 16);
60341
+ return path8.join(os7.homedir(), ".cache", "ccstatusline", `block-cache-${configHash}.json`);
60342
+ }
60343
+ function readBlockCache(expectedConfigDir) {
60344
+ try {
60345
+ const normalizedExpectedConfigDir = expectedConfigDir !== undefined ? normalizeConfigDir(expectedConfigDir) : undefined;
60346
+ const cachePath = getBlockCachePath(normalizedExpectedConfigDir);
60347
+ if (!existsSync8(cachePath)) {
60348
+ return null;
60349
+ }
60350
+ const content = readFileSync7(cachePath, "utf-8");
60351
+ const cache3 = JSON.parse(content);
60352
+ if (typeof cache3.startTime !== "string") {
60353
+ return null;
60354
+ }
60355
+ if (normalizedExpectedConfigDir !== undefined) {
60356
+ if (typeof cache3.configDir !== "string") {
60357
+ return null;
60358
+ }
60359
+ if (cache3.configDir !== normalizedExpectedConfigDir) {
60360
+ return null;
60361
+ }
60362
+ }
60363
+ const date5 = new Date(cache3.startTime);
60364
+ if (Number.isNaN(date5.getTime())) {
60365
+ return null;
60366
+ }
60367
+ return date5;
60368
+ } catch {
60369
+ return null;
60370
+ }
60371
+ }
60372
+ function writeBlockCache(startTime, configDir = getClaudeConfigDir()) {
60373
+ try {
60374
+ const normalizedConfigDir = normalizeConfigDir(configDir);
60375
+ const cachePath = getBlockCachePath(normalizedConfigDir);
60376
+ const cacheDir = path8.dirname(cachePath);
60377
+ if (!existsSync8(cacheDir)) {
60378
+ mkdirSync4(cacheDir, { recursive: true });
60379
+ }
60380
+ const cache3 = {
60381
+ startTime: startTime.toISOString(),
60382
+ configDir: normalizedConfigDir
60383
+ };
60384
+ writeFileSync3(cachePath, JSON.stringify(cache3), "utf-8");
60385
+ } catch {}
60386
+ }
60387
+ function getCachedBlockMetrics(sessionDurationHours = 5) {
60388
+ const sessionDurationMs = sessionDurationHours * 60 * 60 * 1000;
60389
+ const now = new Date;
60390
+ const activeConfigDir = getClaudeConfigDir();
60391
+ const cachedStartTime = readBlockCache(activeConfigDir);
60392
+ if (cachedStartTime) {
60393
+ const blockEndTime = new Date(cachedStartTime.getTime() + sessionDurationMs);
60394
+ if (now.getTime() <= blockEndTime.getTime()) {
60395
+ return {
60396
+ startTime: cachedStartTime,
60397
+ lastActivity: now
60398
+ };
60399
+ }
60400
+ }
60401
+ const metrics = getBlockMetrics();
60402
+ if (metrics) {
60403
+ writeBlockCache(metrics.startTime, activeConfigDir);
60404
+ }
60405
+ return metrics;
60406
+ }
60124
60407
  async function getSessionDuration(transcriptPath) {
60125
60408
  try {
60126
- if (!fs8.existsSync(transcriptPath)) {
60409
+ if (!fs9.existsSync(transcriptPath)) {
60127
60410
  return null;
60128
60411
  }
60129
60412
  const content = await readFile4(transcriptPath, "utf-8");
@@ -60175,7 +60458,7 @@ async function getSessionDuration(transcriptPath) {
60175
60458
  }
60176
60459
  async function getTokenMetrics(transcriptPath) {
60177
60460
  try {
60178
- if (!fs8.existsSync(transcriptPath)) {
60461
+ if (!fs9.existsSync(transcriptPath)) {
60179
60462
  return { inputTokens: 0, outputTokens: 0, cachedTokens: 0, totalTokens: 0, contextLength: 0 };
60180
60463
  }
60181
60464
  const content = await readFile4(transcriptPath, "utf-8");
@@ -60322,7 +60605,7 @@ function findMostRecentBlockStartTime(rootDir, sessionDurationHours = 5) {
60322
60605
  function getAllTimestampsFromFile(filePath) {
60323
60606
  const timestamps = [];
60324
60607
  try {
60325
- const content = readFileSync6(filePath, "utf-8");
60608
+ const content = readFileSync7(filePath, "utf-8");
60326
60609
  const lines = content.trim().split(`
60327
60610
  `).filter((line) => line.length > 0);
60328
60611
  for (const line of lines) {
@@ -60390,6 +60673,15 @@ async function readStdin() {
60390
60673
  return null;
60391
60674
  }
60392
60675
  }
60676
+ async function ensureWindowsUtf8CodePage() {
60677
+ if (process.platform !== "win32") {
60678
+ return;
60679
+ }
60680
+ try {
60681
+ const { execFileSync } = await import("child_process");
60682
+ execFileSync("chcp.com", ["65001"], { stdio: "ignore" });
60683
+ } catch {}
60684
+ }
60393
60685
  async function renderMultipleLines(data) {
60394
60686
  const settings = await loadSettings();
60395
60687
  source_default.level = settings.colorLevel;
@@ -60408,7 +60700,7 @@ async function renderMultipleLines(data) {
60408
60700
  }
60409
60701
  let blockMetrics = null;
60410
60702
  if (hasBlockTimer) {
60411
- blockMetrics = getBlockMetrics();
60703
+ blockMetrics = getCachedBlockMetrics();
60412
60704
  }
60413
60705
  const terminalWidth = getTerminalWidth();
60414
60706
  const compact = shouldUseCompactMode(terminalWidth, data);
@@ -60465,6 +60757,7 @@ async function renderMultipleLines(data) {
60465
60757
  }
60466
60758
  async function main() {
60467
60759
  if (!process.stdin.isTTY) {
60760
+ await ensureWindowsUtf8CodePage();
60468
60761
  const input = await readStdin();
60469
60762
  if (input && input.trim() !== "") {
60470
60763
  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.6",
4
4
  "description": "A customizable status line formatter for Claude Code CLI",
5
5
  "module": "src/ccstatusline.ts",
6
6
  "type": "module",