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 +148 -0
- package/README.md +54 -425
- package/bin/cli.js +8 -0
- package/package.json +3 -2
- package/src/formatters/statusline.js +23 -6
- package/src/installer.js +58 -1
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
|
-
|
|
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
|
-
> [](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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7
|
+
Claude Code의 **토큰 사용량을 진단·절약**하는 CLI. 캐시 히트율, TTL 카운트다운, 1M 컨텍스트 감지, 5h/7d 한도 경고를 statusline 한 줄로 보여줍니다.
|
|
27
8
|
|
|
28
|
-
|
|
9
|
+
📺 [출시 영상 (60초)](https://www.youtube.com/shorts/RaD8qMsPTnA)
|
|
29
10
|
|
|
30
11
|
---
|
|
31
12
|
|
|
32
|
-
|
|
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
|
-
#
|
|
47
|
-
|
|
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
|
-
#
|
|
53
|
-
|
|
19
|
+
# 2. 설치
|
|
20
|
+
npm i -g claude-token-saver
|
|
54
21
|
|
|
55
|
-
#
|
|
56
|
-
|
|
22
|
+
# 3. Claude Code 통합 (Skill + statusline 안내)
|
|
23
|
+
claude-token-saver install
|
|
57
24
|
```
|
|
58
25
|
|
|
59
|
-
|
|
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
|
-
##
|
|
28
|
+
## 설치 후 보이는 것
|
|
66
29
|
|
|
67
|
-
|
|
30
|
+
**(A) 단발 리포트** — `claude-token-saver` 실행 시:
|
|
68
31
|
|
|
69
32
|
```
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
- 🟢 >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
|
-
- 🔴 <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
|
-
|
|
47
|
+
위험 상황에서는 칩이 앞으로 튀어나옵니다 — `🚨 5H 94%`, `⚠ 1M ON`, `⚠ Cache miss`, `⚠ 5m TTL` 등.
|
|
174
48
|
|
|
175
|
-
|
|
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": "
|
|
184
|
-
"refreshInterval":
|
|
55
|
+
"command": "claude-token-saver --statusline --icon",
|
|
56
|
+
"refreshInterval": 5
|
|
185
57
|
}
|
|
186
58
|
}
|
|
187
59
|
```
|
|
188
60
|
|
|
189
|
-
|
|
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
|
-
|
|
63
|
+
## 주요 명령
|
|
247
64
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
|
253
|
-
|
|
|
254
|
-
| `
|
|
255
|
-
| `
|
|
256
|
-
| `
|
|
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
|
-
|
|
75
|
+
전체 옵션은 `--help` 또는 [영문 README](./README.en.md#options).
|
|
431
76
|
|
|
432
|
-
|
|
77
|
+
## 진단되는 급증 원인
|
|
433
78
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
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
|
-
|
|
87
|
+
각 코드마다 OS별 해결 명령(`~/.zshrc` / `setx`)이 함께 출력됩니다.
|
|
443
88
|
|
|
444
|
-
|
|
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
|
-
|
|
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
|
-
|
|
98
|
+
## 동작 원리
|
|
467
99
|
|
|
468
|
-
|
|
100
|
+
Claude Code는 모든 API 응답을 `~/.claude/projects/<dir>/<session>.jsonl`에 기록합니다. 이 도구는 `cache_read_input_tokens`, `cache_creation.ephemeral_5m/1h_input_tokens` 등을 `requestId`로 중복 제거 후 일·세션 단위로 집계합니다.
|
|
469
101
|
|
|
470
|
-
|
|
471
|
-
npx claude-token-saver@latest --statusline --icon
|
|
472
|
-
```
|
|
102
|
+
## 환경
|
|
473
103
|
|
|
474
|
-
|
|
104
|
+
Node.js ≥ 18 · macOS / Linux / Windows / WSL · 의존성 0.
|
|
475
105
|
|
|
476
|
-
##
|
|
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.
|
|
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
|
-
|
|
104
|
-
|
|
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
|
|
107
|
-
const
|
|
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
|
-
|
|
199
|
-
|
|
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
|
}
|