claude-token-saver 2.7.2 → 2.8.0

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.en.md ADDED
@@ -0,0 +1,148 @@
1
+ [한국어](./README.md) · **English**
2
+
3
+ # claude-token-saver
4
+
5
+ > Renamed from `claude-cache-monitor` in v2.0. Existing users — see [Migration](#migration-from-claude-cache-monitor).
6
+
7
+ A CLI to **diagnose and save tokens on Claude Code**. Cache hit rate, TTL countdown, 1M-context detection, 5h/7d cap warnings — all in one statusline chip.
8
+
9
+ 📺 [Launch Short (60s)](https://www.youtube.com/shorts/RaD8qMsPTnA)
10
+
11
+ ---
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ # 1. (existing users) remove the old package
17
+ npm uninstall -g claude-cache-monitor
18
+
19
+ # 2. install
20
+ npm i -g claude-token-saver
21
+
22
+ # 3. wire into Claude Code (Skill + statusline guidance)
23
+ claude-token-saver install
24
+ ```
25
+
26
+ Or run once with no install: `npx claude-token-saver`.
27
+
28
+ ## What you see after install
29
+
30
+ **(A) One-shot report** — `claude-token-saver`:
31
+
32
+ ```
33
+ Claude token saver — Last 30 days
34
+ Context window: 200k ✓ standard
35
+ Cache hit rate: 98.2% | Total input: 1957.94M tokens
36
+ TTL Breakdown / Cost Impact / Daily Trend ...
37
+ ```
38
+
39
+ If a session spiked, a `⚠ Spike detected` block leads the report with root cause + a paste-ready remediation command.
40
+
41
+ **(B) Statusline chip** — once wired into `~/.claude/settings.json`:
42
+
43
+ ```
44
+ 🧠 97.5% · ⏳ 1h 42:15 · 💰 $4.8K · 🤖 Opus 4.7 · ✦ 5H 47% · 📅 7D 9%
45
+ ```
46
+
47
+ Risk chips lead when something's wrong: `🚨 5H 94%`, `⚠ 1M ON`, `⚠ Cache miss`, `⚠ 5m TTL`.
48
+
49
+ ## Wire up the statusline
50
+
51
+ ```json
52
+ {
53
+ "statusLine": {
54
+ "type": "command",
55
+ "command": "claude-token-saver --statusline --icon",
56
+ "refreshInterval": 5
57
+ }
58
+ }
59
+ ```
60
+
61
+ `refreshInterval: 5` keeps the TTL countdown ticking while idle (Claude Code's statusline is otherwise event-driven). 1s also works, but 5s is the recommended default to avoid constant I/O. For Windows PowerShell, see `examples/statusline-command.ps1`.
62
+
63
+ ## Commands
64
+
65
+ | Command | What it does |
66
+ |---|---|
67
+ | `claude-token-saver` | Last 30 days diagnostic report |
68
+ | `claude-token-saver --days 7` | Change window |
69
+ | `claude-token-saver --statusline --icon` | One-line statusline output |
70
+ | `claude-token-saver install` | Register Claude Code Skill (auto-activates on chip wording) |
71
+ | `claude-token-saver history` | Last 7 days of chip transitions (1M ON, Cache miss, cap, …) |
72
+ | `claude-token-saver handoff` | Back current work up to `HANDOFF-YYYY-MM-DD-HHMM.md` before a cap blocks you |
73
+ | `claude-token-saver --install-hook` | Auto-log cache stats on every tool call |
74
+
75
+ ## Options
76
+
77
+ | Flag | Description | Default |
78
+ |------|-------------|---------|
79
+ | `--days, -d` | Analysis period in days | 30 |
80
+ | `--format, -f` | `table` / `json` / `csv` | table |
81
+ | `--project, -p` | Filter by project directory | all |
82
+ | `--threshold` | Hit-rate alert threshold (0.0–1.0) | 0.7 |
83
+ | `--statusline` | One-line statusline output | – |
84
+ | `--icon` | Use 🧠 / ⏳ / 💰 / 📦 icons | text |
85
+ | `--verbose` | Longer labels | – |
86
+ | `--no-timer` | Hide TTL countdown | show |
87
+ | `--no-color` | Strip ANSI codes | – |
88
+ | `--segments=…` | Limit statusline segments (e.g. `model,five_hour,seven_day,saved`) | all |
89
+ | `--install-hook` / `--uninstall-hook` | Manage the PostToolUse hook | – |
90
+
91
+ ## Spike issue codes
92
+
93
+ | Code | Meaning |
94
+ |---|---|
95
+ | `LARGE_INPUT_PER_REQUEST` | single request > 250k tokens → 1M context likely |
96
+ | `LOW_HIT_RATE` | cache hit rate < 50% |
97
+ | `BUCKET_5M_DOMINANT` | > 70% of cache writes hit the 5m bucket |
98
+ | `HIGH_OUTPUT_RATIO` | output/input > 0.15 (output is 5× input price) |
99
+ | `HIGH_REQUEST_COUNT` | session made 3×+ your median (tool loop?) |
100
+ | `FREQUENT_CACHE_REBUILD` | `cache_creation` > `cache_read` |
101
+
102
+ Remediation commands are OS-aware (`~/.zshrc` for macOS/Linux/WSL, `setx` for Windows).
103
+
104
+ ## Migration from claude-cache-monitor
105
+
106
+ ```bash
107
+ npm uninstall -g claude-cache-monitor
108
+ npm i -g claude-token-saver
109
+ ```
110
+
111
+ Then update `~/.claude/settings.json` — change `claude-cache-monitor …` to `claude-token-saver …`. The v2.0 alias bin was dropped because it caused `EEXIST` on global installs.
112
+
113
+ ## How it works
114
+
115
+ Claude Code logs every API call to `~/.claude/projects/<dir>/<session>.jsonl`. This tool dedupes streaming chunks by `requestId` and aggregates `cache_read_input_tokens` / `cache_creation.ephemeral_5m_input_tokens` / `cache_creation.ephemeral_1h_input_tokens` by day and session.
116
+
117
+ ## Pricing (Apr 2026)
118
+
119
+ | Tier | Models | Input | 5m Write | 1h Write | Read | Output |
120
+ |---|---|---|---|---|---|---|
121
+ | `claude-opus-new` | Opus 4.5 / 4.6 / 4.7 | $5 | $6.25 | $10 | $0.50 | $25 |
122
+ | `claude-opus-legacy` | Opus 4 / 4.1 / 3 | $15 | $18.75 | $30 | $1.50 | $75 |
123
+ | `claude-sonnet` | Sonnet 3.7 / 4 / 4.5 / 4.6 | $3 | $3.75 | $6 | $0.30 | $15 |
124
+ | `claude-haiku-4-5` | Haiku 4.5 | $1 | $1.25 | $2 | $0.10 | $5 |
125
+
126
+ Source: [Anthropic pricing docs](https://docs.claude.com/en/docs/about-claude/pricing). Versions ≤ 1.0.x over-estimated Opus 4.5+ by ~3× — upgrade if you're below 1.1.0.
127
+
128
+ ## Cache TTL by plan
129
+
130
+ | Plan | TTL | Controlled by |
131
+ |---|---|---|
132
+ | Max ($100–200/mo) | **1h auto** | `tengu_prompt_cache_1h_config` flag |
133
+ | Pro ($20/mo) | **5m fixed** | not configurable |
134
+ | API key | 5m default (1h via beta header) | `cache_control.ttl` |
135
+
136
+ ## Environment
137
+
138
+ Node.js ≥ 18 · macOS / Windows / Linux / WSL · zero dependencies.
139
+
140
+ ## Background
141
+
142
+ - [GitHub Issue #46829](https://github.com/anthropics/claude-code/issues/46829) — cache TTL regression
143
+ - [HN discussion](https://news.ycombinator.com/item?id=47736476) — 168 points, 142 comments
144
+ - [HNPulse KR](https://www.youtube.com/@HNPulseKR) — Korean HN tech deep-dives
145
+
146
+ ## License
147
+
148
+ MIT
package/README.md CHANGED
@@ -1,479 +1,108 @@
1
- # claude-token-saver
2
-
3
- > **Renamed from `claude-cache-monitor` in v2.0.** The old npm package is deprecated and points here. The `claude-cache-monitor` binary is gone — use `claude-token-saver` instead. See [migration notes](#migration-from-claude-cache-monitor).
4
-
5
- > 📺 **출시 영상 (60초)** — 왜 만들었고, 무엇을 보여주는지:
6
- > **[▶ Watch the launch Short](https://www.youtube.com/shorts/RaD8qMsPTnA)** · [All DeepPulse Shorts](https://www.youtube.com/@HNPulseKR/shorts)
7
- >
8
- > [![claude-token-saver launch](https://img.youtube.com/vi/RaD8qMsPTnA/hqdefault.jpg)](https://www.youtube.com/shorts/RaD8qMsPTnA)
9
- >
10
- > Earlier context (cache TTL 1h→5m incident, the reason this tool exists): [HNPulse #025](https://www.youtube.com/shorts/oSx2sg935nI)
11
-
12
- **Save tokens on Claude Code.** Catch the session that suddenly burned 10× your usual input, figure out *why*, and get a one-line remediation you can paste.
13
-
14
- **Why I built this.** I'm on the Max plan. On Opus 4.6 I never hit the *current-session* cap. After Opus 4.7 rolled out, I started hitting it on the same workflow — repeatedly. The official token statistics didn't match what I was actually feeling, and Claude Code's UI doesn't show prompt-cache health. This tool is what let me see *why*: low cache hit rate, 5m TTL writes that should have been 1h, 1M context auto-promoted in the background.
1
+ **한국어** · [English](./README.en.md)
15
2
 
16
- v2.1 (2026-04) adds the workflow that follows the diagnosis:
17
- - **`claude-token-saver install`** — one command writes a Claude Code Skill that auto-activates when you mention "cache hit rate" / "1M context" / "5H cap" — no slash command needed.
18
- - **`claude-token-saver history`** — every warning chip transition is auto-logged to a daily Markdown file, so you can answer "when did this start" without grepping logs.
19
- - **Cross-platform paths** — Windows (`%APPDATA%`), macOS (`~/Library/Application Support`), Linux (`~/.config` / XDG) all handled.
3
+ # claude-token-saver
20
4
 
21
- v1.5 adds three things on top of the original `claude-cache-monitor`:
22
- - **Spike diagnosis** — detect recent sessions whose input tokens exploded vs. your own baseline, and name the cause (1M context, 5m TTL churn, cache rebuild, chatty output).
23
- - **1M-context detection** — Opus 4.7+ auto-enables 1M context on Max plans, silently. This tool surfaces it on the statusline as `Ctx 1M` (red) vs. `Ctx 200k` (green), with the OS-specific command to turn it off.
24
- - **Actionable advice** — OS-aware remediation (`~/.zshrc` vs. `setx`) and the warning for the known `/model` toggle bug ([anthropics/claude-code#31640](https://github.com/anthropics/claude-code/issues/31640)).
5
+ > v2.0에서 `claude-cache-monitor` `claude-token-saver`로 이름이 바뀌었습니다. 기존 사용자는 [마이그레이션](#마이그레이션-claude-cache-monitor에서) 참고.
25
6
 
26
- The original functionality still works: cache hit rate, TTL breakdown, cost impact vs. no-cache, TTL countdown timer, and Claude Code statusline integration.
7
+ Claude Code의 **토큰 사용량을 진단·절약**하는 CLI. 캐시 히트율, TTL 카운트다운, 1M 컨텍스트 감지, 5h/7d 한도 경고를 statusline 줄로 보여줍니다.
27
8
 
28
- **Run it standalone or wire it into Claude Code's statusline.** Use `npx claude-token-saver` as a one-shot report, or wire it into Claude Code's native statusline for an always-on chip. See [Two Ways to Use It](#two-ways-to-use-it).
9
+ 📺 [출시 영상 (60초)](https://www.youtube.com/shorts/RaD8qMsPTnA)
29
10
 
30
11
  ---
31
12
 
32
- **Claude Code 토큰 아껴쓰기 도구.** 평소보다 10배 토큰을 태운 세션을 찾고, *왜 튀었는지* 진단하고, 붙여넣을 수 있는 한 줄 해결책까지 내줍니다.
33
-
34
- v1.5 신규:
35
- - **토큰 급증 진단** — 최근 세션 중 내 평소 기준보다 입력 토큰이 폭증한 세션을 찾아 원인까지 분류 (1M 컨텍스트 / 5분 TTL 반복 쓰기 / 캐시 재작성 / 출력 과다).
36
- - **1M 컨텍스트 감지** — Opus 4.7부터 Max 플랜은 1M 컨텍스트가 자동 ON. Statusline에 `Ctx 1M`(빨강) / `Ctx 200k`(초록)으로 표시하고, OS별 OFF 명령까지 안내.
37
- - **실행 가능한 권장 액션** — macOS/Linux/WSL는 `~/.zshrc`, Windows는 `setx` 명령. [`/model`로 200k 선택해도 1M에 머무는 알려진 버그](https://github.com/anthropics/claude-code/issues/31640)에 대한 경고 포함.
38
-
39
- 기존 기능(캐시 히트율·TTL 분포·비용 절감·TTL 카운트다운·statusline)은 그대로 유지됩니다.
40
-
41
- **단독 도구로도, Claude Code statusline 통합으로도 동작합니다.** `npx claude-token-saver` 한 줄로 진단 리포트만 보거나, 내장 statusline에 연결해 상시 표시할 수 있습니다. 자세한 용법은 [Two Ways to Use It](#two-ways-to-use-it) 참고.
42
-
43
- ## Quick Start
13
+ ## 설치
44
14
 
45
15
  ```bash
46
- # Run instantly (no install required)
47
- npx claude-token-saver
48
-
49
- # Last 7 days only
50
- npx claude-token-saver --days 7
16
+ # 1. (기존 사용자) 구 패키지 제거
17
+ npm uninstall -g claude-cache-monitor
51
18
 
52
- # JSON output (for pipelines)
53
- npx claude-token-saver --format json
19
+ # 2. 설치
20
+ npm i -g claude-token-saver
54
21
 
55
- # CSV output (for spreadsheets)
56
- npx claude-token-saver --format csv
22
+ # 3. Claude Code 통합 (Skill + statusline 안내)
23
+ claude-token-saver install
57
24
  ```
58
25
 
59
- > **Upgrading from `claude-cache-monitor`?** Uninstall the old package first, then install the new one and update your settings command name:
60
- > ```bash
61
- > npm uninstall -g claude-cache-monitor && npm i -g claude-token-saver
62
- > ```
63
- > After upgrading, change any `claude-cache-monitor …` invocations (including `statusLine.command` in `~/.claude/settings.json`) to `claude-token-saver …`. See [Migration](#migration-from-claude-cache-monitor).
26
+ 설치 없이 번만 실행하려면 `npx claude-token-saver`.
64
27
 
65
- ## Spike Diagnosis (new in v1.5.0)
28
+ ## 설치 보이는
66
29
 
67
- When you run `npx claude-token-saver`, sessions from the last 24 hours whose total input tokens are **≥ 3× your p95 baseline** (or whose single-request context exceeds 250k, indicating 1M context) appear at the top of the report with root causes and remediation commands. Example output:
30
+ **(A) 단발 리포트** `claude-token-saver` 실행 시:
68
31
 
69
32
  ```
70
- 토큰 급증 감지
71
- ──────────────────────────────────────────────────
72
- 컨텍스트 모드 추정: 1M (최근 단일 요청 최대 480k 토큰)
73
-
74
- • a1b2c3d4 [myproject] 총 입력 320.45M (5.2× p95, 요청 142회)
75
- 단일 요청 최대 컨텍스트: 480k 토큰
76
- · 요청당 입력 토큰이 평소보다 매우 큽니다 (1M 컨텍스트 의심)
77
-
78
- 권장 액션
79
- ──────────────────────────────────────────────────
80
- ▸ 요청당 입력 토큰이 평소보다 매우 큽니다 (1M 컨텍스트 의심)
81
- Opus 4.7부터 1M 컨텍스트가 표준 가격으로 풀리면서 Max 플랜은 자동으로 1M로 승격됩니다 ...
82
- - 1M 컨텍스트 OFF (환경변수)
83
- echo 'export CLAUDE_CODE_DISABLE_1M_CONTEXT=1' >> ~/.zshrc && source ~/.zshrc
84
- - 세션 내 토글
85
- 단축키 ⌥ P (mac) / Alt + P (linux) 로 즉시 On/Off
86
- - ⚠ 알려진 버그 #31640
87
- /model 로 200k 선택해도 컨텍스트가 1M에 머무는 케이스가 있습니다.
88
- 확실히 끄려면 위 환경변수를 설정한 뒤 Claude Code를 재시작하세요.
33
+ Claude 토큰 아껴쓰기 — Last 30 days
34
+ Context window: 200k ✓ 표준
35
+ Cache hit rate: 98.2% | Total input: 1957.94M tokens
36
+ TTL Breakdown / Cost Impact / Daily Trend ...
89
37
  ```
90
38
 
91
- Issue codes detected:
92
-
93
- | Code | Meaning |
94
- |---|---|
95
- | `LARGE_INPUT_PER_REQUEST` | avg input/request is 8×+ your baseline, or a single request > 250k tokens → 1M context likely |
96
- | `LOW_HIT_RATE` | cache hit rate < 50% and materially below your baseline |
97
- | `BUCKET_5M_DOMINANT` | > 70% of cache writes land in the 5m bucket (Pro plan, or Max users getting downgraded) |
98
- | `HIGH_OUTPUT_RATIO` | output/input > 0.15 (output is 5× input price — matters a lot) |
99
- | `HIGH_REQUEST_COUNT` | session made 3×+ your median request count (tool-loop suspect) |
100
- | `FREQUENT_CACHE_REBUILD` | `cache_creation` > `cache_read` (cache being made, not reused) |
101
-
102
- Remediation commands are chosen from `process.platform` — macOS/Linux/WSL get `~/.zshrc` snippets, Windows gets `setx` and the PowerShell equivalent.
103
-
104
- ## Two Ways to Use It
39
+ 급증 세션이 있으면 상단에 `⚠ 토큰 급증 감지` 블록과 원인·해결 명령이 함께 출력됩니다.
105
40
 
106
- | Mode | What you run | When to pick this |
107
- |---|---|---|
108
- | **1. Standalone CLI report** | `npx claude-token-saver` | One-off diagnosis. Prints the full report (spikes + cache + cost + trend). Zero setup. |
109
- | **2. Claude Code statusline** | `claude-token-saver --statusline` wired via `~/.claude/settings.json` | You want the chip (hit rate · TTL countdown · Ctx 200k/1M · spike) visible all the time. |
41
+ **(B) statusline 줄** `~/.claude/settings.json`에 등록 시:
110
42
 
111
- Detail for each mode below.
112
-
113
- ## Statusline Mode (new in v1.2.0)
114
-
115
- Always-on one-line display in Claude Code's native statusline — no need to run commands manually.
116
-
117
- Claude Code 내장 statusline에 한 줄로 상시 표시. 커맨드 수동 실행 불필요.
118
-
119
- ```bash
120
- # Preview (prints one line — text mode, default)
121
- npx claude-token-saver --statusline
122
- # → Cache hit 97.5% · Expires 1h 42:15 · Cost saved $4.8K · Ctx 200k · 7d
123
-
124
- # When 1M context is silently on and a session is spiking:
125
- # → Cache hit 88.0% · Expires 1h 42:15 · Cost saved $4.8K · Ctx 1M · ⚠ 1M컨텍스트 · 7d
126
-
127
- # Icon mode (🧠 / ⏳ / 💰 / 📦)
128
- npx claude-token-saver --statusline --icon
129
- # → 🧠 97.5% · ⏳ 1h 42:15 · 💰 $4.8K · 📦 200k · 7d
130
-
131
- # Verbose (longer labels; combines with --icon too)
132
- npx claude-token-saver --statusline --verbose
133
- # → Cache hit 97.5% · 1h bucket · expires in 42:15 · Cost saved $4.8K · last 7d
134
-
135
- npx claude-token-saver --statusline --icon --verbose
136
- # → 🧠 Cache hit 97.5% · ⏳ Expires 1h 42:15 · 💰 Cost saved $4.8K · last 7d
137
-
138
- # Hide the TTL countdown
139
- npx claude-token-saver --statusline --no-timer
140
-
141
- # No ANSI color (plain text)
142
- npx claude-token-saver --statusline --no-color
143
43
  ```
144
-
145
- ### TTL countdown (v1.2.1+)
146
-
147
- Your subscription plan fixes the TTL bucket (5m for Pro, 1h for Max) — the actionable number isn't the bucket, it's **how much time is left on your last API call's cache entry**. The `TTL 1h MM:SS` segment is a live stopwatch against the dominant bucket:
148
-
149
- 구독 플랜이 TTL 값(Pro = 5분, Max = 1시간)을 고정하므로 의미 있는 수치는 "버킷"이 아니라 "마지막 API 호출의 캐시가 만료되기까지 몇 초"입니다. `TTL 1h MM:SS` 세그먼트가 그 스톱워치입니다 (앞쪽이 버킷, 뒤쪽이 남은 시간):
150
-
151
- - 🟢 &gt;30% remaining — plenty of time to send the next prompt within TTL
152
- - 🟡 10–30% remaining — consider firing a cheap prompt soon to keep prefix cached
153
- - 🔴 &lt;10% remaining or `EXPIRED` — next prompt will pay cache-write cost again
154
-
155
- This enables the "5-minute rule" in practice: a quick dummy question before the timer hits zero resets the TTL and preserves the prefix cache.
156
-
157
- ### Enable in Claude Code
158
-
159
- The Claude Code statusline is event-driven — it only re-renders on assistant messages / mode changes. To keep the countdown ticking while you're idle, set `refreshInterval: 1` alongside the `statusLine` command.
160
-
161
- #### macOS / Linux / WSL
162
-
163
- ```json
164
- {
165
- "statusLine": {
166
- "type": "command",
167
- "command": "claude-token-saver --statusline --icon",
168
- "refreshInterval": 1
169
- }
170
- }
44
+ 🧠 97.5% · ⏳ 1h 42:15 · 💰 $4.8K · 🤖 Opus 4.7 · ✦ 5H 47% · 📅 7D 9%
171
45
  ```
172
46
 
173
- Or combine with an existing statusline script see [`examples/statusline-command.sh`](examples/statusline-command.sh) for a drop-in that prints `user@host:cwd | <token-saver segment>`.
47
+ 위험 상황에서는 칩이 앞으로 튀어나옵니다 `🚨 5H 94%`, `⚠ 1M ON`, `⚠ Cache miss`, `⚠ 5m TTL` 등.
174
48
 
175
- #### Windows (native PowerShell, not WSL)
176
-
177
- Use the PowerShell example in [`examples/statusline-command.ps1`](examples/statusline-command.ps1):
49
+ ## statusline 등록
178
50
 
179
51
  ```json
180
52
  {
181
53
  "statusLine": {
182
54
  "type": "command",
183
- "command": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File %USERPROFILE%\\.claude\\statusline-command.ps1",
184
- "refreshInterval": 1
55
+ "command": "claude-token-saver --statusline --icon",
56
+ "refreshInterval": 5
185
57
  }
186
58
  }
187
59
  ```
188
60
 
189
- Works best in **Windows Terminal** or **PowerShell 7+** (ANSI color + emoji). Classic `conhost cmd` may garble emoji — prefer `--no-icon`-style plain text or use Windows Terminal.
190
-
191
- #### Windows (WSL)
192
-
193
- Same as Linux — install the package in your WSL Node.js and point to the POSIX sh script.
194
-
195
- Claude Code calls this every ~300ms on events, plus once per `refreshInterval` second while idle. Colors are emitted when the terminal supports them:
196
-
197
- - **Hit rate** — 🟢 ≥85% · 🟡 70–85% · 🔴 <70%
198
- - **TTL bucket** — 🟢 1h (good) · 🟡 5m (warning)
199
- - **Countdown** — 🟢 >30% remaining · 🟡 10–30% · 🔴 <10% or EXPIRED
200
- - **Context window** — 🟢 `Ctx 200k` (standard) · 🔴 `Ctx 1M` (Opus 4.7+ Max auto-enabled — expensive if unintended)
201
- - **Spike chip** — appears only when the current session is flagged (e.g. `⚠ 1M컨텍스트`, `⚠ 5m TTL`, `⚠ 캐시미스`, `⚠ 입력폭주`)
202
-
203
- Statusline mode uses the last 7 days by default (override with `--days N`) and never emits multi-line errors, so your statusline stays clean even when there's no session data yet.
204
-
205
- ## Claude Code integration (`install`) — new in v2.1
206
-
207
- One command wires up everything else this README mentions:
208
-
209
- ```bash
210
- claude-token-saver install
211
- ```
212
-
213
- This writes one file under your Claude user dir:
214
- - `~/.claude/skills/claude-token-saver/SKILL.md` — auto-activates whenever you mention chip wording ("⚠ 1M ON", "cache miss", "5H cap", etc.) or ask for a token report. Claude Code will then read `claude-token-saver last`, drill into history if needed, and explain the warning.
215
-
216
- If you previously installed v2.5.x or earlier, `install` also removes the now-redundant legacy `~/.claude/commands/token-monitor.md` slash command — its workflow is fully absorbed into the skill (same behavior, triggered by intent rather than typing `/token-monitor`).
217
-
218
- Re-run with `--force` to overwrite the skill file.
219
-
220
- ## Warning history (`history`) — new in v2.1
221
-
222
- The statusline path auto-logs every chip transition (none → ⚠, ⚠ A → ⚠ B, ⚠ → resolved) to a daily Markdown file. Read it back with:
223
-
224
- ```bash
225
- claude-token-saver history # last 7 days
226
- claude-token-saver history --days 30 # wider window
227
- claude-token-saver history --list # just list available dates
228
- ```
229
-
230
- Sample output:
231
-
232
- ```
233
- # Token Monitor — 2026-04-25
234
-
235
- ## Events
236
- - 09:14:02 ⚠ 1M ON — Context auto-promoted to 1M (max single-request 280k tokens)
237
- - 09:42:18 ⚠ 1M ON → ⚠ Cache miss — session abc12345: LOW_HIT_RATE
238
- - 10:05:47 ✓ resolved (was ⚠ Cache miss)
239
- ```
240
-
241
- Storage paths (cross-platform):
242
- - **Windows**: `%APPDATA%\claude-token-saver\history\YYYY-MM-DD.md`
243
- - **macOS**: `~/Library/Application Support/claude-token-saver/history/YYYY-MM-DD.md`
244
- - **Linux**: `$XDG_CONFIG_HOME/claude-token-saver/history/YYYY-MM-DD.md` (or `~/.config/...`)
61
+ `refreshInterval: 5`는 TTL 카운트다운이 idle 상태에서도 5초마다 갱신되게 합니다 (1초도 가능하지만 상시 I/O 부담을 피하려고 5초를 기본값으로 권장). Windows(PowerShell)는 `examples/statusline-command.ps1` 참고.
245
62
 
246
- Each day's file is plain Markdown — open it in any editor. Transitions are deduped, so the 1Hz statusline refresh doesn't spam.
63
+ ## 주요 명령
247
64
 
248
- ## Model + /usage segments (new in v2.3, generic in v2.4)
249
-
250
- Three more segments mirror the data Claude Code's `/usage` slash command shows, so you don't have to slash for it every few minutes:
251
-
252
- | Segment | Icon mode example | Source |
253
- | ----------- | --------------------- | ------------------------------- |
254
- | `model` | `🤖 Opus 4.7` | stdin `model.display_name` |
255
- | `five_hour` | `✦ current ███▒░░ 47% 🔄 21:10` | stdin `rate_limits.five_hour` |
256
- | `seven_day` | `📅 weekly ▒░░░░░ 9% 🔄 Thu 13:00` | stdin `rate_limits.seven_day` |
257
-
258
- The window segments stay quiet under 70% (calm emerald), warm to amber at 70–89%, and yield to the leading `🚨 5H █████▓ 94%` / `🚨 7D █████▒ 92%` cap-warn chip at 90%+ — so you never see the same window twice. The gauge is 6 cells wide using a single density family (`█▓▒░`), so the fill→empty boundary reads as one smooth gradient instead of an awkward step between fractional and shaded glyphs. Colors render as a Tailwind-inspired muted palette (emerald-400 / amber-400 / rose-400) on truecolor terminals (`COLORTERM=truecolor`), with a graceful fallback to 8-color ANSI elsewhere. Filter the layout with `--segments=` if you only want a subset:
259
-
260
- ```bash
261
- claude-token-saver --statusline --icon --segments=model,five_hour,seven_day,saved
262
- ```
263
-
264
- `5h` and `7d` are kept as aliases for the older config files. Any new `rate_limits.*` window Anthropic ships (e.g. a Sonnet-only weekly bucket) renders automatically with a derived label — no config or version bump needed. As of 2026-04-25 the stdin contract exposes only `five_hour` + `seven_day`; the third row in `/usage` ("Current week — Sonnet only") is not in the payload yet, so we mirror what's there.
265
-
266
- ## Cap-warn + handoff (new in v2.2)
267
-
268
- Claude Code's statusline payload now includes rate-limit usage (`rate_limits.five_hour.used_percentage`, `rate_limits.seven_day.used_percentage`). claude-token-saver leads the statusline with a `🚨 5H 94%` (or `🚨 7D 92%`) chip the moment either window crosses **90%**, and writes the transition into history:
269
-
270
- ```
271
- - 14:32:08 🚨 5H 94% cap warning (resets in 1h 38m)
272
- - 16:10:21 ✓ 5H cap warning resolved
273
- ```
274
-
275
- When you see the chip, back up the work in flight before the cap blocks you:
276
-
277
- ```bash
278
- claude-token-saver handoff
279
- ```
280
-
281
- That writes `./HANDOFF-YYYY-MM-DD-HHMM.md` in the current directory with:
282
-
283
- - timestamp, cwd, git branch / HEAD / dirty file list
284
- - the 5h/7d cap snapshot (and "resets in Hh Mm")
285
- - empty fillable sections for *what I just did*, *TODO*, *where to pick up next*, *gotchas*
286
- - a one-line resume prompt for a fresh Claude Code session:
287
-
288
- ```
289
- Read the most recent HANDOFF-*.md in this directory and continue the work.
290
- ```
291
-
292
- The handoff write is also recorded in history (`📝 handoff written: …`), so `claude-token-saver history` and the auto-skill show both the cap-warn and the backup event next to each other.
293
-
294
- ## Hook Setup
295
-
296
- Automatically logs cache stats on every tool call and alerts when hit rate drops below a threshold.
297
-
298
- 매 도구 호출마다 자동으로 캐시 통계를 기록하고, 히트율이 임계값 이하로 떨어지면 경고합니다.
299
-
300
- ```bash
301
- # Install hook (default threshold 70%)
302
- npx claude-token-saver --install-hook
303
-
304
- # Custom threshold
305
- npx claude-token-saver --install-hook --threshold 0.8
306
-
307
- # Remove hook
308
- npx claude-token-saver --uninstall-hook
309
- ```
310
-
311
- When the hook is installed:
312
- - Session-level stats are automatically recorded to `~/.claude/cache-stats.jsonl`
313
- - A warning is displayed in Claude Code when hit rate falls below the threshold
314
-
315
- ## Output Example
316
-
317
- ```
318
- Claude 토큰 아껴쓰기 — Last 30 days
319
- (claude-token-saver v2.0.1)
320
- ══════════════════════════════════════════════════
321
-
322
- Context window: 200k ✓ 200k 컨텍스트 (표준)
323
- (최근 단일 요청 최대 83k 토큰)
324
-
325
- Summary
326
- Sessions: 380 | API calls: 10,813 | Model: claude-opus-new
327
- Cache hit rate: 98.2% | Total input: 1957.94M tokens
328
-
329
- TTL Breakdown
330
- ┌────────────────────┬──────────────────┬──────────────────┐
331
- │ │ 5m Ephemeral │ 1h Extended │
332
- ├────────────────────┼──────────────────┼──────────────────┤
333
- │ Cache writes │ 167.7K (0.5%) │ 34.8M (99.5%) │
334
- └────────────────────┴──────────────────┴──────────────────┘
335
-
336
- Cost Impact (estimated)
337
- ┌──────────────────────────┬──────────────┐
338
- │ Actual cost │ $793.93 │
339
- │ Without cache │ $5958.52 │
340
- ├──────────────────────────┼──────────────┤
341
- │ Savings │ $5164.6 (86.7%) │
342
- │ Extra cost if 5m-only │ +$239.99 │
343
- └──────────────────────────┴──────────────┘
344
-
345
- Daily Trend
346
- ┌────────────┬──────────┬─────────┬────────────┬────────────┬───────┐
347
- │ Date │ HitRate │ Calls │ Read │ Write │ 5m% │
348
- ├────────────┼──────────┼─────────┼────────────┼────────────┼───────┤
349
- │ 2026-04-10 │ 98.5% │ 341 │ 88.98M │ 1.35M │ 0.0% │
350
- │ 2026-04-11 │ 97.1% │ 118 │ 9.51M │ 0.27M │ 0.0% │
351
- │ 2026-04-12 │ 91.7% │ 77 │ 2.69M │ 0.22M │ 0.0% │
352
- └────────────┴──────────┴─────────┴────────────┴────────────┴───────┘
353
- ```
354
-
355
- ## Options
356
-
357
- | Flag | Description | Default |
358
- |------|-------------|---------|
359
- | `--days, -d` | Analysis period in days | 30 |
360
- | `--format, -f` | Output format: `table`, `json`, `csv` | table |
361
- | `--project, -p` | Filter by project directory | all |
362
- | `--threshold` | Cache hit rate alert threshold (0.0-1.0) | 0.7 |
363
- | `--install-hook` | Install Claude Code PostToolUse hook | - |
364
- | `--uninstall-hook` | Remove hook | - |
365
- | `--statusline` | Emit one-line output for Claude Code statusline API | - |
366
- | `--icon` | (with `--statusline`) use 🧠 / ⏳ / 💰 icons instead of word labels | text |
367
- | `--verbose` | (with `--statusline`) use longer labels | - |
368
- | `--no-timer` | (with `--statusline`) hide the TTL countdown | show |
369
- | `--no-color` | Strip ANSI escape codes | - |
370
-
371
- Statusline segments always include the context-window chip (`Ctx 200k`/`Ctx 1M` or `📦 200k`/`📦 1M` in `--icon` mode). A spike chip is appended only when the most recent session is diagnosed as a spike.
372
-
373
- ## How It Works
374
-
375
- Claude Code logs usage data for every API call into session JSONL files:
376
-
377
- ```
378
- ~/.claude/projects/<project-dir>/<session-id>.jsonl
379
- ```
380
-
381
- Each API response contains these fields:
382
- - `cache_read_input_tokens` — tokens read from cache (cheap, 0.1x)
383
- - `cache_creation_input_tokens` — tokens written to cache (expensive, 1.25x~2x)
384
- - `cache_creation.ephemeral_5m_input_tokens` — tokens created with 5-minute TTL
385
- - `cache_creation.ephemeral_1h_input_tokens` — tokens created with 1-hour TTL
386
-
387
- This tool deduplicates streaming chunks by `requestId` and aggregates by day/session.
388
-
389
- ## Key Findings
390
-
391
- Cache TTL is determined by your subscription plan, not by user choice:
392
-
393
- | Plan | Cache TTL | Controlled by |
394
- |------|-----------|---------------|
395
- | **Max** ($100~200/mo) | **1h automatic** | `tengu_prompt_cache_1h_config` feature flag |
396
- | **Pro** ($20/mo) | **5m fixed** | Not configurable |
397
- | **API key** | **5m default** (1h via beta header) | `cache_control.ttl` parameter |
398
-
399
- ## Pricing (updated 2026-04 for Opus 4.7)
400
-
401
- Cost estimates use current Anthropic pricing, auto-detected from the model id in session logs:
402
-
403
- | Tier (internal id) | Matching models | Input | 5m Cache Write | 1h Cache Write | Cache Read | Output |
404
- |---|---|---|---|---|---|---|
405
- | `claude-opus-new` | Opus **4.5 / 4.6 / 4.7** | $5 | $6.25 | $10 | $0.50 | $25 |
406
- | `claude-opus-legacy` | Opus 4 / 4.1 / 3 | $15 | $18.75 | $30 | $1.50 | $75 |
407
- | `claude-sonnet` | Sonnet 3.7 / 4 / 4.5 / 4.6 | $3 | $3.75 | $6 | $0.30 | $15 |
408
- | `claude-haiku-4-5` | Haiku 4.5 | $1 | $1.25 | $2 | $0.10 | $5 |
409
- | `claude-haiku-3-5` | Haiku 3.5 | $0.80 | $1 | $1.6 | $0.08 | $4 |
410
- | `claude-haiku-3` | Haiku 3 | $0.25 | $0.30 | $0.50 | $0.03 | $1.25 |
411
-
412
- 5m and 1h cache writes are now billed at separate rates (previously applied a single blended rate). Prior versions (≤ 1.0.x) used legacy Opus 4 pricing for all Opus models, over-estimating Opus 4.5+ costs by ~3x — **upgrade to 1.1.0 if you run Opus 4.5 or newer**.
413
-
414
- Source: [Anthropic pricing documentation](https://docs.claude.com/en/docs/about-claude/pricing)
415
-
416
- ## Platform Support
417
-
418
- Works on **macOS**, **Windows**, and **Linux**. Requires Node.js >= 18.
419
-
420
- Zero dependencies.
421
-
422
- ## Background
423
-
424
- - [GitHub Issue #46829](https://github.com/anthropics/claude-code/issues/46829): Cache TTL regression analysis
425
- - [HN Discussion](https://news.ycombinator.com/item?id=47736476): Community reaction (168 points, 142 comments)
426
- - [HNPulse KR](https://www.youtube.com/@HNPulseKR): Hacker News tech deep-dives in Korean ([Shorts](https://www.youtube.com/@HNPulseKR/shorts))
427
-
428
- ## Migration from claude-cache-monitor
65
+ | 명령 | 설명 |
66
+ |---|---|
67
+ | `claude-token-saver` | 최근 30일 진단 리포트 |
68
+ | `claude-token-saver --days 7` | 기간 변경 |
69
+ | `claude-token-saver --statusline --icon` | statusline용 한 줄 |
70
+ | `claude-token-saver install` | Claude Code Skill 자동 등록 (칩 단어 언급 시 자동 활성) |
71
+ | `claude-token-saver history` | 최근 7일간 전이 로그 (1M ON, Cache miss, cap 등) |
72
+ | `claude-token-saver handoff` | 현재 작업을 `HANDOFF-YYYY-MM-DD-HHMM.md`로 백업 (cap 임박 시) |
73
+ | `claude-token-saver --install-hook` | 도구 호출마다 캐시 통계 자동 로깅 |
429
74
 
430
- v2.0 renamed the package to reflect the expanded scope (spike diagnosis + 1M-context detection + remediation, not just cache monitoring).
75
+ 전체 옵션은 `--help` 또는 [영문 README](./README.en.md#options).
431
76
 
432
- ### New users
77
+ ## 진단되는 급증 원인
433
78
 
434
- ```bash
435
- npm i -g claude-token-saver
436
- # or, no install:
437
- npx claude-token-saver
438
- ```
439
-
440
- Skip the rest of this section.
79
+ | 코드 | 의미 |
80
+ |---|---|
81
+ | `LARGE_INPUT_PER_REQUEST` | 단일 요청 250k+ → 1M 컨텍스트 의심 |
82
+ | `LOW_HIT_RATE` | 캐시 히트율 < 50% |
83
+ | `BUCKET_5M_DOMINANT` | 캐시 쓰기의 70%+가 5분 버킷 (Pro 플랜 또는 Max 다운그레이드) |
84
+ | `HIGH_OUTPUT_RATIO` | 출력/입력 > 0.15 (출력은 입력의 5배 가격) |
85
+ | `FREQUENT_CACHE_REBUILD` | 캐시 재작성이 읽기보다 많음 |
441
86
 
442
- ### Upgrading from `claude-cache-monitor` v1.x
87
+ 코드마다 OS별 해결 명령(`~/.zshrc` / `setx`)이 함께 출력됩니다.
443
88
 
444
- Two steps:
89
+ ## 마이그레이션 (claude-cache-monitor에서)
445
90
 
446
91
  ```bash
447
- # 1. Remove the old package (its claude-cache-monitor bin is now obsolete).
448
92
  npm uninstall -g claude-cache-monitor
449
-
450
- # 2. Install the new one.
451
93
  npm i -g claude-token-saver
452
94
  ```
453
95
 
454
- Then update any `claude-cache-monitor …` references. The main one is `statusLine.command` in `~/.claude/settings.json`:
455
-
456
- ```jsonc
457
- // before
458
- "command": "claude-cache-monitor --statusline --icon"
459
-
460
- // after
461
- "command": "claude-token-saver --statusline --icon"
462
- ```
463
-
464
- ### Why we dropped the `claude-cache-monitor` bin alias
96
+ `~/.claude/settings.json`의 `statusLine.command`를 `claude-cache-monitor …` `claude-token-saver …`로 교체하세요. v2.0 이전의 `claude-cache-monitor` 바이너리 별칭은 npm 충돌(EEXIST) 때문에 제거됐습니다.
465
97
 
466
- Earlier v2.0 releases shipped a `claude-cache-monitor` bin alongside `claude-token-saver` so existing settings would keep working without edits. In practice this caused an `EEXIST: file already exists` error on `npm i -g claude-token-saver` when v1.x was still installed — and that collision forced the uninstall step anyway. Dropping the alias makes the upgrade path a clean two-liner and lets `npm i -g claude-token-saver` succeed directly if you've never installed the old one.
98
+ ## 동작 원리
467
99
 
468
- ### Zero-install (npx)
100
+ Claude Code는 모든 API 응답을 `~/.claude/projects/<dir>/<session>.jsonl`에 기록합니다. 이 도구는 `cache_read_input_tokens`, `cache_creation.ephemeral_5m/1h_input_tokens` 등을 `requestId`로 중복 제거 후 일·세션 단위로 집계합니다.
469
101
 
470
- ```bash
471
- npx claude-token-saver@latest --statusline --icon
472
- ```
102
+ ## 환경
473
103
 
474
- No uninstall needed; npm just fetches the new name.
104
+ Node.js 18 · macOS / Linux / Windows / WSL · 의존성 0.
475
105
 
476
- ## License
106
+ ## 라이선스
477
107
 
478
108
  MIT
479
-
package/bin/cli.js CHANGED
@@ -344,6 +344,14 @@ async function main() {
344
344
  };
345
345
  const r = installAll({ force });
346
346
  print('skill', r.skill);
347
+ {
348
+ const s = r.statusline;
349
+ const verb = s.action === 'exists' ? 'already configured (refreshInterval=5)'
350
+ : s.action === 'skipped' ? `skipped — ${s.reason}`
351
+ : s.reason ? `${s.action} — ${s.reason}`
352
+ : s.action;
353
+ console.log(` statusline: ${s.path} (${verb})`);
354
+ }
347
355
  if (r.legacy.action === 'removed') {
348
356
  print('legacy /token-monitor', r.legacy);
349
357
  console.log(' (consolidated into the skill — same workflow, triggered by intent)');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-token-saver",
3
- "version": "2.7.2",
3
+ "version": "2.8.0",
4
4
  "description": "Save tokens on Claude Code — spike diagnosis, 1M-context detection, TTL countdown, statusline. (formerly claude-cache-monitor)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,7 +10,8 @@
10
10
  "bin/",
11
11
  "src/",
12
12
  "examples/",
13
- "README.md"
13
+ "README.md",
14
+ "README.en.md"
14
15
  ],
15
16
  "engines": {
16
17
  "node": ">=18"
@@ -100,11 +100,18 @@ function gaugeBar(pct) {
100
100
  * Format a remaining-seconds countdown as MM:SS (or H:MM when ≥ 1h).
101
101
  */
102
102
  function formatTimer(remainingSec) {
103
- if (remainingSec <= 0) return 'EXPIRED';
104
- const totalSec = Math.floor(remainingSec);
103
+ // Defensive: non-finite/NaN inputs (e.g. clock skew, stringified Date) used
104
+ // to slip through and render as "NaN:NaN" or stretched seconds. Treat any
105
+ // weird input as expired rather than rendering garbage in the statusline.
106
+ if (!Number.isFinite(remainingSec) || remainingSec <= 0) return 'EXPIRED';
107
+ const totalSec = Math.max(0, Math.floor(remainingSec));
105
108
  const h = Math.floor(totalSec / 3600);
106
- const m = Math.floor((totalSec % 3600) / 60);
107
- const s = totalSec % 60;
109
+ const mRaw = Math.floor((totalSec % 3600) / 60);
110
+ const sRaw = totalSec % 60;
111
+ // Clamp explicitly so a future regression in the math (or padStart no-op
112
+ // truncation) can never produce m:sss like "4:547".
113
+ const m = Math.min(59, Math.max(0, mRaw));
114
+ const s = Math.min(59, Math.max(0, sRaw));
108
115
  if (h > 0) return `${h}:${String(m).padStart(2, '0')}`;
109
116
  return `${m}:${String(s).padStart(2, '0')}`;
110
117
  }
@@ -195,8 +202,18 @@ export function formatReport(data, { color = true, verbose = false, timer = true
195
202
  // icon verbose: "⏳ Expires 1h 59:58"
196
203
  let ttlSeg;
197
204
  if (timer && lastActivity) {
198
- const elapsed = (Date.now() - lastActivity) / 1000;
199
- const remaining = ttlSeconds - elapsed;
205
+ // Coerce to a numeric ms timestamp. Some upstream paths handed in a Date,
206
+ // a stringified ISO timestamp, or epoch-seconds — any of which silently
207
+ // produces NaN/huge values when subtracted from Date.now(), which then
208
+ // bypasses formatTimer's normal MM:SS shape.
209
+ const laMs =
210
+ typeof lastActivity === 'number'
211
+ ? (lastActivity < 1e12 ? lastActivity * 1000 : lastActivity) // seconds → ms
212
+ : (lastActivity instanceof Date ? lastActivity.getTime() : Date.parse(lastActivity));
213
+ const elapsed = Number.isFinite(laMs) ? (Date.now() - laMs) / 1000 : Infinity;
214
+ // Clamp remaining into the bucket so a clock-skew or stale-state edge case
215
+ // can't display a value larger than the bucket itself.
216
+ const remaining = Math.min(ttlSeconds, ttlSeconds - elapsed);
200
217
  const text = formatTimer(remaining);
201
218
  const pct = remaining / ttlSeconds;
202
219
  const timerColor =
package/src/installer.js CHANGED
@@ -13,10 +13,13 @@
13
13
  * exist on every platform.
14
14
  */
15
15
 
16
- import { writeFileSync, mkdirSync, existsSync, unlinkSync } from 'node:fs';
16
+ import { writeFileSync, mkdirSync, existsSync, unlinkSync, readFileSync } from 'node:fs';
17
17
  import { join } from 'node:path';
18
18
  import { claudeUserDir } from './paths.js';
19
19
 
20
+ const STATUSLINE_COMMAND = 'claude-token-saver --statusline --icon';
21
+ const STATUSLINE_REFRESH_INTERVAL = 5;
22
+
20
23
  const SKILL_BODY = `---
21
24
  name: claude-token-saver
22
25
  description: Use when the user mentions Claude Code token usage, prompt cache hit rate, TTL/expiry, the 1M context window, cache misses, output spikes, rate-limit caps (5h/7d), or anything in the statusline produced by claude-token-saver (chips like "🚨 5H 94%", "🚨 7D 92%", "⚠ 1M ON", "⚠ Input spike", "⚠ Cache miss", "⚠ 5m TTL", "⚠ Rebuild churn", "⚠ Output heavy", "⚠ Call surge", "⏳ Cache expires", "💰 Cache saved", "🧠 Cache hit"). Also use when they ask to view token-usage history, want to understand a warning they just saw, or want to back up work before a session cap with \`claude-token-saver handoff\`.
@@ -127,9 +130,63 @@ export function removeLegacyCommand() {
127
130
  return { path: file, action: 'removed' };
128
131
  }
129
132
 
133
+ // Registers/repairs the Claude Code statusLine entry in ~/.claude/settings.json.
134
+ // - No statusLine yet: insert ours with refreshInterval:1.
135
+ // - statusLine already points at claude-token-saver: ensure refreshInterval:1
136
+ // (this is the bit that makes the TTL countdown tick every second while idle).
137
+ // - statusLine points at a different command: leave it alone unless --force.
138
+ export function installStatusline({ force = false } = {}) {
139
+ const dir = claudeUserDir();
140
+ const file = join(dir, 'settings.json');
141
+ mkdirSync(dir, { recursive: true });
142
+
143
+ let settings = {};
144
+ if (existsSync(file)) {
145
+ try {
146
+ settings = JSON.parse(readFileSync(file, 'utf8'));
147
+ } catch (e) {
148
+ return { path: file, action: 'skipped', reason: `unreadable JSON (${e.message})` };
149
+ }
150
+ }
151
+
152
+ const cur = settings.statusLine;
153
+ const targetsUs = cur && typeof cur.command === 'string' && cur.command.includes('claude-token-saver');
154
+
155
+ if (!cur) {
156
+ settings.statusLine = {
157
+ type: 'command',
158
+ command: STATUSLINE_COMMAND,
159
+ refreshInterval: STATUSLINE_REFRESH_INTERVAL,
160
+ };
161
+ writeFileSync(file, JSON.stringify(settings, null, 2) + '\n');
162
+ return { path: file, action: 'created' };
163
+ }
164
+
165
+ if (targetsUs) {
166
+ if (cur.refreshInterval === STATUSLINE_REFRESH_INTERVAL) {
167
+ return { path: file, action: 'exists' };
168
+ }
169
+ cur.refreshInterval = STATUSLINE_REFRESH_INTERVAL;
170
+ writeFileSync(file, JSON.stringify(settings, null, 2) + '\n');
171
+ return { path: file, action: 'updated', reason: 'set refreshInterval=1' };
172
+ }
173
+
174
+ if (!force) {
175
+ return { path: file, action: 'skipped', reason: `existing statusLine command (${cur.command}) — re-run with --force to overwrite` };
176
+ }
177
+ settings.statusLine = {
178
+ type: 'command',
179
+ command: STATUSLINE_COMMAND,
180
+ refreshInterval: STATUSLINE_REFRESH_INTERVAL,
181
+ };
182
+ writeFileSync(file, JSON.stringify(settings, null, 2) + '\n');
183
+ return { path: file, action: 'updated', reason: 'replaced previous statusLine' };
184
+ }
185
+
130
186
  export function installAll({ force = false } = {}) {
131
187
  return {
132
188
  skill: installSkill({ force }),
189
+ statusline: installStatusline({ force }),
133
190
  legacy: removeLegacyCommand(),
134
191
  };
135
192
  }