sprag-cli 3.40.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.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.ko.md +637 -0
  3. package/README.md +758 -0
  4. package/bin/cli.js +801 -0
  5. package/examples/statusline-command.ps1 +43 -0
  6. package/examples/statusline-command.sh +36 -0
  7. package/package.json +62 -0
  8. package/presets/cohesion/cohesion-en.md +26 -0
  9. package/presets/doc2md/convert.py +363 -0
  10. package/presets/korean-style/LICENSE-fluent-korean +21 -0
  11. package/presets/korean-style/fluent-korean.md +52 -0
  12. package/presets/korean-style/supplement.md +93 -0
  13. package/presets/model-rules.json +115 -0
  14. package/presets/ratchet-rules.json +38 -0
  15. package/src/advice.js +564 -0
  16. package/src/agents.js +52 -0
  17. package/src/brief.js +264 -0
  18. package/src/caps-cache.js +84 -0
  19. package/src/cli-args.js +51 -0
  20. package/src/cohesion.js +70 -0
  21. package/src/commands/brief.js +31 -0
  22. package/src/commands/cohesion.js +59 -0
  23. package/src/commands/compact-window.js +93 -0
  24. package/src/commands/doc2md.js +166 -0
  25. package/src/commands/feedback.js +132 -0
  26. package/src/commands/handoff.js +33 -0
  27. package/src/commands/harness.js +459 -0
  28. package/src/commands/history.js +46 -0
  29. package/src/commands/install.js +358 -0
  30. package/src/commands/korean.js +220 -0
  31. package/src/commands/last.js +151 -0
  32. package/src/commands/mode.js +46 -0
  33. package/src/commands/route-scan.js +454 -0
  34. package/src/commands/seed.js +105 -0
  35. package/src/commands/uninstall.js +42 -0
  36. package/src/commands/update-check.js +77 -0
  37. package/src/commands/upgrade.js +68 -0
  38. package/src/compact-window.js +205 -0
  39. package/src/config.js +232 -0
  40. package/src/cost.js +253 -0
  41. package/src/debug.js +29 -0
  42. package/src/demo.js +331 -0
  43. package/src/doc2md-ledger.cjs +227 -0
  44. package/src/doc2md.cjs +997 -0
  45. package/src/fig2md-runner.cjs +21 -0
  46. package/src/fig2md.cjs +191 -0
  47. package/src/first-run-note.js +63 -0
  48. package/src/format-time.js +44 -0
  49. package/src/formatters/csv.js +8 -0
  50. package/src/formatters/json.js +3 -0
  51. package/src/formatters/statusline.js +750 -0
  52. package/src/formatters/table.js +299 -0
  53. package/src/handoff.js +161 -0
  54. package/src/harness-analyzer.cjs +264 -0
  55. package/src/harness-templates.js +153 -0
  56. package/src/harness.js +613 -0
  57. package/src/history.js +383 -0
  58. package/src/hook-manager.js +96 -0
  59. package/src/hook.cjs +196 -0
  60. package/src/installer.js +614 -0
  61. package/src/korean-lint.cjs +303 -0
  62. package/src/korean-style.js +187 -0
  63. package/src/litellm-budget.js +223 -0
  64. package/src/model-alias.js +484 -0
  65. package/src/model-rules.js +527 -0
  66. package/src/month-spend.js +47 -0
  67. package/src/parser.js +330 -0
  68. package/src/paths.js +41 -0
  69. package/src/prompt.js +52 -0
  70. package/src/route-scan.js +832 -0
  71. package/src/savings-ledger.js +137 -0
  72. package/src/seed-rules.js +280 -0
  73. package/src/session-cache.js +160 -0
  74. package/src/session-records.js +188 -0
  75. package/src/stats.js +380 -0
  76. package/src/stdin-payload.js +122 -0
  77. package/src/subagent-records.js +214 -0
  78. package/src/update-check.js +201 -0
  79. package/src/window-labels.js +64 -0
package/README.md ADDED
@@ -0,0 +1,758 @@
1
+ **English** ยท [ํ•œ๊ตญ์–ด](./README.ko.md)
2
+
3
+ [![npm](https://img.shields.io/npm/v/claude-token-saver.svg)](https://www.npmjs.com/package/claude-token-saver)
4
+
5
+ ๐ŸŒ **[sprag.io](https://sprag.io)** ยท project site (claude-token-saver is being renamed to **Sprag**)
6
+
7
+ # claude-token-saver
8
+
9
+ **Shows what it saved, on two lines.** It moves the easy work your expensive model keeps repeating onto cheaper ones, and turns documents the model cannot read into Markdown. Both figures are ledger entries rather than estimates, and whichever saved more takes the top line. Zero dependencies, one-line install.
10
+
11
+ ![statusline example โ€” routing savings on row 1, document conversion savings on row 2, diagnostics on row 3](./docs/statusline.png)
12
+
13
+ ```bash
14
+ npm i -g claude-token-saver
15
+ ```
16
+
17
+ Four numbers are the whole pitch.
18
+
19
+ - **Beats every single model on public benchmark data**: the shipped tier criteria score 59.1% on 11,696 LLMRouterBench instances against the best single model's 57.9% โ€” at 31% less than gpt-5, 64% less than gemini-2.5-pro ([benchmark](./docs/BENCHMARK.md))
20
+ - **95.8% fewer tokens per document**: a 30MB deck read as Markdown cost 22,610 tokens instead of 540,429 ([evidence](#-doc2md--documents-become-markdown-before-the-model-reads-them))
21
+ - **18.6% lower cost**: measured before/after adopting the Harness principles ([evidence](#real-world-impact--beforeafter-report))
22
+ - **Routing savings are a per-run ledger**: the price difference of each delegated run, not an estimate ([evidence](#-the-savings-figure-is-a-ledger-entry-not-an-estimate))
23
+
24
+ ```text
25
+ accuracy total cost, 11,696 queries
26
+ tier-criteria router 59.1% โ† best $268 โ–ฐโ–ฐโ–ฐโ–ฐโ–ฐโ–ฑโ–ฑโ–ฑโ–ฑโ–ฑโ–ฑโ–ฑโ–ฑโ–ฑโ–ฑ
27
+ gpt-5 57.8% $388 โ–ฐโ–ฐโ–ฐโ–ฐโ–ฐโ–ฐโ–ฐโ–ฐโ–ฑโ–ฑโ–ฑโ–ฑโ–ฑโ–ฑโ–ฑ
28
+ gemini-2.5-pro 57.9% $734 โ–ฐโ–ฐโ–ฐโ–ฐโ–ฐโ–ฐโ–ฐโ–ฐโ–ฐโ–ฐโ–ฐโ–ฐโ–ฐโ–ฐโ–ฐ
29
+ ```
30
+
31
+ Since v3.35.0 spend is visible too: month-to-date spend shows as `๐Ÿ’ต Sep $42`, and on LiteLLM gateways (Bedrock and friends) with no 5h/7d caps, your key budget renders as a `๐Ÿ”‘ budget โ–ฐโ–ฑ 34% $34/$100` gauge.
32
+
33
+ ## Four parts, working together
34
+
35
+ | | What it does | Effect |
36
+ |---|---|---|
37
+ | ๐Ÿ”€ **Routing** | Delegates recurring easy work to cheaper models | Savings recorded per run in a ledger; criteria [benchmarked](./docs/BENCHMARK.md) on public data |
38
+ | ๐Ÿ“„ **Document conversion** | Turns pptx/xlsx/pdf/docx/fig into Markdown before the model reads them | **510,000 tokens** saved on one deck ([below](#-doc2md--documents-become-markdown-before-the-model-reads-them)) |
39
+ | ๐Ÿ…ท **Harness** | Blocks the token-burning habits: unevidenced "done", skipped verification (5 principles) | **โˆ’18.6% cost** ([measured](#real-world-impact--beforeafter-report)) |
40
+ | โš™๏ธ **Ratchet** | Freezes each error you hit into a rule | Same mistake stops recurring |
41
+
42
+ One install sets up all four. The measured โˆ’18.6% comes from the harness and ratchet; routing and conversion savings sit on top of it.
43
+
44
+ The two savings figures are never added together, because they answer different questions. Routing says "the same work ran on a cheaper model". Conversion says "a file you could not read became readable, without pushing the original through the context window". The statusline gives each its own line and puts the larger one first.
45
+
46
+ ## Contents
47
+
48
+ - **Start here**: [Getting started](#getting-started) ยท [Reading the statusline](#reading-the-statusline) ยท [Commands](#commands)
49
+ - **Savings**: [The routing ledger](#-the-savings-figure-is-a-ledger-entry-not-an-estimate) ยท [route-scan](#-route-scan--this-recurring-task-could-run-on-a-cheaper-tier) ยท [doc2md](#-doc2md--documents-become-markdown-before-the-model-reads-them) ยท [seed](#-seed-delegation-that-works-from-the-first-session) ยท [Benchmark](./docs/BENCHMARK.md)
50
+ - **Guardrails**: [Harness](#-harness-mode) ยท [compact-window](#-compact-window--pin-where-a-1m-session-compacts) ยท [Korean writing guidance](#-korean-writing-guidance)
51
+ - **Spend & environments**: [Monthly spend ยท LiteLLM key budget](#litellm-your-key-budget-stands-in-for-the-missing-5h7d-caps-v3350) ยท [Gateways (Bedrock/Vertex)](#-behind-a-gateway-bedrock--vertex) ยท [Spike issue codes](#spike-issue-codes) ยท [Measured impact](#real-world-impact--beforeafter-report)
52
+
53
+ ## Getting started
54
+
55
+ **Prerequisite:** Node.js โ‰ฅ 18 (`node -v` ยท macOS `brew install node` ยท Windows `winget install OpenJS.NodeJS.LTS` ยท Linux/WSL: [nvm](https://github.com/nvm-sh/nvm) recommended)
56
+
57
+ ```bash
58
+ npm uninstall -g claude-cache-monitor # (previous-package users only)
59
+ npm i -g claude-token-saver
60
+ ```
61
+
62
+ The statusline appears at the bottom of Claude Code right away. If auto-registration was skipped (`--ignore-scripts`, sudo, sandboxed installs), run `claude-token-saver install`.
63
+
64
+ One install sets up everything: **statusline, Skill, SessionStart hook, the ๐Ÿ…ท Harness (5 principles), and a first route-scan.** The harness and the Korean writing guidance **show what they add and ask before enabling it.** The harness is **appended** to `~/.claude/CLAUDE.md` as a marked block (your existing content is backed up and preserved) and is left alone if one is already there.
65
+
66
+ Outside a terminal โ€” npm `postinstall`, CI, piped stdin โ€” the question is skipped and the old defaults apply. Use `--yes` or `--no-input` to skip it deliberately, `CTS_NO_HARNESS=1 npm i -g claude-token-saver` to skip the harness entirely, and `claude-token-saver harness uninit --global` to undo it.
67
+
68
+ > โš ๏ธ Avoid `sudo` global installs โ€” the Skill lands in root's `~/.claude` instead of yours. Use nvm/fnm/Volta or `npm config set prefix ~/.npm-global`.
69
+
70
+ ### What the install turns on, and what stays manual
71
+
72
+ Everything that costs nothing until it is needed is on after a plain install. The only manual items are the ones that change Claude Code's own settings or need a human to pick a scope.
73
+
74
+ | Feature | After install | How to turn it off |
75
+ |---|---|---|
76
+ | statusline (diagnostic chips, savings ledger) | on | `claude-token-saver uninstall` |
77
+ | `/claude-token-saver` Skill | on | same |
78
+ | SessionStart hook (route-scan refresh) | on | same |
79
+ | UserPromptSubmit hook (brief injection) | on | same |
80
+ | First route-scan (last 14 days of logs) | runs once during the install | n/a |
81
+ | ๐Ÿ…ท Harness 5 principles (`~/.claude/CLAUDE.md`) | on (shown and confirmed once at a terminal) | `harness uninit --global`, `CTS_NO_HARNESS=1` |
82
+ | doc2md hooks (Read, Edit/Write, prompt) | on | `doc2md off`, `CTS_NO_DOC2MD=1` |
83
+ | doc2md converter (markitdown venv) | offered at a terminal; an unattended install prints the command | install later with `doc2md install-converter` |
84
+ | Korean writing guidance | on when the locale is Korean (asked at a terminal) | `korean off`, `CTS_NO_KOREAN=1` |
85
+ | compact-window warning chip | on | `compact-window off` |
86
+ | Update-available chip | on | `CTS_NO_UPDATE_CHECK=1` |
87
+ | **Pinning compact-window** (`autoCompactWindow` 500k) | **off โ€” run it yourself** | `compact-window set --global` or `--project` |
88
+ | **Model-fitting rules** (`ratchet-model.md` delegations) | **candidates are proposed only** | review and approve with `route-scan rules` |
89
+ | `handoff` (back up work before a cap) | an on-demand command | n/a |
90
+
91
+ `compact-window set` writes into Claude Code's `settings.json` and a human has to choose global or project scope, so it is never run for you. Model-fitting rules keep an approval step for the same reason: which work belongs on a cheaper tier is your call.
92
+
93
+
94
+ ## ๐Ÿ”€ The savings figure is a ledger entry, not an estimate
95
+
96
+ Every delegated run is recorded like this:
97
+
98
+ ```
99
+ before after gap
100
+ claude-opus-5 โ†’ haiku-4-5 = $0.57
101
+ (the model (what (same token counts,
102
+ handling this actually priced against
103
+ before the rule) ran it) both models)
104
+ ```
105
+
106
+ ```bash
107
+ $ claude-token-saver route-scan savings # trace every dollar back to its rule
108
+
109
+ ๐Ÿ”€ Routing saved, lifetime $2.09 (last 7d $1.40 ยท 30d $2.09)
110
+
111
+ By model change:
112
+ claude-fable-5 โ†’ claude-sonnet-5 โ€” 1 run, $0.72
113
+ claude-opus-5 โ†’ claude-haiku-4-5 โ€” 1 run, $0.57
114
+
115
+ By run (newest first):
116
+ 2026-08-22 $0.51 claude-fable-5 โ†’ claude-haiku-4-5
117
+ rule: T2|paste|-Users-me-projects-my-app
118
+ ```
119
+
120
+ **What is excluded** โ€” an honest number beats a big one:
121
+
122
+ - Delegations no registered rule covers (`Explore`, your own agents, plugin subagents): this tool did not route them.
123
+ - Model ids the pricing table cannot recognize: the run is dropped rather than priced wrong.
124
+
125
+ ---
126
+
127
+ ## โšก What else the statusline catches
128
+
129
+ | | |
130
+ |---|---|
131
+ | ๐Ÿšจ **No surprise rate limits** | Warns when the 5H/7D window hits 90%; `handoff` backs up your work |
132
+ | ๐Ÿง  **Cache waste detection** | Hit rate, TTL, 1M-context detection โ€” spikes diagnosed with issue codes |
133
+ | ๐Ÿ’ต **Spend visibility** | Month-to-date spend (`๐Ÿ’ต Sep $42`) always on; behind a LiteLLM gateway the key budget gauge (`๐Ÿ”‘ budget 34% $34/$100`) stands in for the missing 5h/7d caps ([below](#litellm-your-key-budget-stands-in-for-the-missing-5h7d-caps-v3350)) |
134
+ | ๐Ÿ‡ฐ๐Ÿ‡ท **Korean writing guidance** | Offered at install time, defaulting to your locale ([below](#-korean-writing-guidance)) |
135
+
136
+ ## Not a router โ€” 60 seconds
137
+
138
+ It never intercepts a request in realtime.
139
+ **After a session ends** it reads your local logs, finds the easy patterns your expensive model
140
+ kept handling, and promotes them into rules so a cheaper model takes them **from the next session
141
+ onward**. Rules are scoped global or per-project.
142
+
143
+ ### Why realtime model routing can cost more, not less
144
+
145
+ Never switching models mid-session is the point of this design.
146
+
147
+ Prompt caches are **kept per model.** Switch to a cheaper model mid-session and it starts from a cold cache, re-reading the whole conversation at full input price. A cache hit costs about a tenth of that, so past roughly 20k tokens of history **one switch can erase everything the cheaper model was going to save.** You moved the work down a tier and the bill went up: the central paradox of realtime routing.
148
+
149
+ Teams shipping routing products have turned the feature off for exactly this reason: [LLM ๋ผ์šฐํ„ฐ๋ฅผ ๋งŒ๋“  ์‚ฌ๋žŒ๋“ค์ด ์ง์ ‘ ๊ป์Šต๋‹ˆ๋‹ค #Shorts](https://www.youtube.com/shorts/SK-GoAABjbg) (Korean).
150
+
151
+ So this tool never touches the main session's model. It delegates to **subagents only**, which leaves the main session's cache intact and runs the delegated work on a cheap model in its own context. That is why the savings are not cancelled out by cache loss.
152
+
153
+ ```bash
154
+ npm i -g claude-token-saver@latest
155
+ claude-token-saver route-scan # find delegation candidates in your own history (0 LLM calls)
156
+ claude-token-saver route-scan rules # list promoted rules ยท rm <N> to remove
157
+ claude-token-saver route-scan savings # audit every dollar the routing saved
158
+ ```
159
+
160
+ Thresholds come from **your own last-14-day distribution (p25/p75)**, not someone else's benchmark.
161
+ Measured rule-health โ€” whether a delegated run actually succeeded โ€” landed in [v3.9.0](#v390-2026-08-01).
162
+
163
+ ---
164
+
165
+ ## Reading the statusline
166
+
167
+ Once the savings ledger has entries it renders as **two rows** โ€” routing savings on row 1, diagnostics on row 2.
168
+
169
+ ```
170
+ ๐Ÿ”€ Routing saved $2.09 | fableโ†’sonnet 1ร— $0.72 ยท opusโ†’haiku 1ร— $0.57
171
+ โš  Ctx 500k+ ยท ๐Ÿ…ท 5/5 ยท ๐Ÿค– Opus 5 ยท ๐Ÿง  Cache hit 98.8% ยท โณ Cache expires 59:46 ยท โœฆ current โ–ˆโ–ˆโ–ˆโ–“โ–‘โ–‘ 62% ๐Ÿ”„ 21:33 ยท ๐Ÿ“… weekly โ–ˆโ–ˆโ–’โ–‘โ–‘โ–‘ 38% ๐Ÿ”„ Tue 19:33 ยท ๐Ÿ“ฆ Ctx 47% of 1M ยท ๐Ÿ’ฐ Cache saved $1.0K ยท last 1d
172
+ ```
173
+
174
+ With an empty ledger (no measured delegation yet) row 1 is not drawn and the layout stays single-line. If your build renders only the first row (some macOS Claude Code versions), pass `--single-line`.
175
+
176
+ | Segment | Meaning |
177
+ |---|---|
178
+ | `๐Ÿ”€` **row 1** | Lifetime routing savings + the model moves behind them. The breakdown sums exactly to the total, model names keep only the family (`opusโ†’haiku`). Full audit: `route-scan savings` |
179
+ | `๐Ÿ“„` **row 2** | Lifetime doc2md conversion savings with a per-format breakdown. Whichever of routing/conversion saved more takes row 1 |
180
+ | `๐Ÿค–` | Active model |
181
+ | `๐Ÿ…ท 5/5` | Harness principle score ([Harness mode](#-harness-mode)) |
182
+ | `๐Ÿง ` | Cache hit rate (green at 85%+) |
183
+ | `โณ` | Cache TTL countdown โ€” send a message before expiry to keep the cache warm. Ticking while idle requires Claude Code v2.1.97+ (see [If the countdown looks frozen](#how-it-works--environment)) |
184
+ | `โœฆ current` / `๐Ÿ“… weekly` | 5-hour / 7-day rate-limit window usage + reset time |
185
+ | `๐Ÿ“ฆ` | Context usage (e.g. `Ctx 68% of 1M`) โ€” colored by fill. Current models default to 1M with no premium, but token volume itself drives per-turn cost and 5H/7D burn |
186
+ | `๐Ÿ’ต Sep $42` | **Estimated spend since 00:00 on the 1st of this month** (local time). Summed per session with that session's model pricing; always shown, even on gateways with no 5h/7d caps (v3.35.0) |
187
+ | `๐Ÿ”‘ budget` | **LiteLLM key budget gauge.** When stdin carries no rate_limits, shows the key's `spend` against `max_budget` as `๐Ÿ”‘ budget โ–ฐโ–ฑ 34% $34/$100` (v3.35.0, [below](#-behind-a-gateway-bedrock--vertex)) |
188
+ | `๐Ÿ’ฐ` | Cumulative savings from prompt caching โ€” a **different** number from row 1's `๐Ÿ”€` (model routing) |
189
+ | `v3.24.0` | The version you are running. Gray, at the tail, when it is the latest one |
190
+ | `โฌ† v3.24.0 โ†’ 3.25.0` | A newer release exists. Actionable, so it moves to the front of the line ([Update notifications](#-update-notifications)) |
191
+
192
+ When something is wrong, a **warning chip leads the line**:
193
+
194
+ ```
195
+ ๐Ÿšจ 5H โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–“ 94% ๐Ÿ”„ 12:36 ยท ๐Ÿ…ท 5/5 ยท ๐Ÿค– Opus 4.8 ยท ๐Ÿง  Cache hit 72.1% ยท โš  Cache miss ยท ๐Ÿ“… weekly โ–“โ–‘โ–‘โ–‘โ–‘โ–‘ 12% ๐Ÿ”„ Sun 14:26 ยท ๐Ÿ“ฆ Ctx 200k ยท last 1d
196
+ ```
197
+
198
+ Chips โ€” `๐Ÿšจ 5H/7D NN%` (cap imminent) ยท `โš  Ctx 500k+` (a single request actually exceeded 500k) ยท `โš  Cache miss` ยท `โš  Input spike` ยท `โš  Output heavy` ยท `โš  Call surge` ยท `โš  Rebuild churn` ยท `โš  5m TTL`. When both windows cross 90% at once, the sooner-resetting one is promoted to ๐Ÿšจ and the other stays visible as a red segment (v2.16.0+).
199
+
200
+ ### When a chip appears
201
+
202
+ Run the `/claude-token-saver` Skill inside Claude โ€” or just say the chip wording ("5H cap is up", "cache miss") and it auto-activates. The Skill surfaces the **root-cause code + step-by-step fix**. When a cap is imminent, run `claude-token-saver handoff` to back up your work state to markdown and continue in a fresh session.
203
+
204
+ ## Commands
205
+
206
+ Run these in your shell (inside Claude Code, the `/claude-token-saver` Skill is the only entry point):
207
+
208
+ | Command | What it does |
209
+ |---|---|
210
+ | `claude-token-saver` | Last-1-day diagnostic report (`--days N` / `--hours N`) |
211
+ | `claude-token-saver last` | Most recent warning + remediation |
212
+ | `claude-token-saver history` | Last 7 days of warning transitions |
213
+ | `claude-token-saver handoff` | Back work up to `HANDOFF-*.md` before a cap blocks you |
214
+ | `claude-token-saver mode [keywords...]` | Output config (`icon`/`text`, `en`/`ko`, `1h`โ€“`30d` window, โ€ฆ) |
215
+ | `claude-token-saver harness ...` | ๐Ÿ…ท Harness management (below) |
216
+ | `claude-token-saver route-scan` | Detect recurring easy work on expensive models โ†’ propose haiku-delegation ratchet rules (below) |
217
+ | `claude-token-saver route-scan savings` | The routing-savings ledger โ€” per-model-change rollup + per-run log (the evidence behind the figure) |
218
+ | `claude-token-saver compact-window` | Warn when a 1M-context session has no auto-compact cap โ†’ pin 400k with `set` (below) |
219
+ | `claude-token-saver korean on\|off\|status` | Inject Korean writing guidance at session start and install the write-time check (below) |
220
+ | `claude-token-saver cohesion on\|off\|status\|show` | Inject English cohesion guidance (sentence-connection rules) at session start |
221
+ | `claude-token-saver korean lint block\|warn\|off` | How the write-time check handles findings |
222
+ | `claude-token-saver korean lint scope all\|prose` | Check every text file, or documents only |
223
+ | `claude-token-saver doc2md on\|off` | Convert attached documents to Markdown before the model reads them (below) |
224
+ | `claude-token-saver doc2md <file>` | Convert one file by hand. Diagnostic: it prints the refusal reason instead of swallowing it |
225
+ | `claude-token-saver mode ttl=5m\|1h\|auto` | Pin the cache TTL bucket. The default `auto` trusts the measured split, then falls back to gateway detection |
226
+ | `claude-token-saver --version` | Print the installed version |
227
+ | `claude-token-saver update-check` | Is a newer version out? (`--refresh` to ask now, `--dismiss` to mute this version's offer) |
228
+ | `claude-token-saver upgrade` | Install the latest release with the package manager that installed this copy (`--print` shows the command only) |
229
+ | `claude-token-saver install` | Manually register Skill + statusline |
230
+ | `claude-token-saver uninstall [--purge]` | Remove the hooks, statusline and skill it registered. Recorded savings are kept unless `--purge` is given |
231
+
232
+ The output language is decided once, at install time: a terminal install proposes the system locale and asks whether to use Korean, while an unattended install records what the locale says. Once recorded it is never asked again, not even on an upgrade. Change it later with `mode ko` / `mode en`, or pin it for a scripted install with `CTS_LANG=ko` / `CTS_LANG=en`. Statusline chips stay symbolic either way.
233
+
234
+ <details>
235
+ <summary>All CLI options</summary>
236
+
237
+ | Flag | Description | Default |
238
+ |------|-------------|---------|
239
+ | `--days, -d` | Analysis period in days | 30 |
240
+ | `--hours` | Analysis window in hours (overrides `--days`) | โ€“ |
241
+ | `--format, -f` | `table` / `json` / `csv` | table |
242
+ | `--project, -p` | Filter by project directory | all |
243
+ | `--threshold` | Hit-rate alert threshold (0.0โ€“1.0) | 0.7 |
244
+ | `--statusline` | One-line statusline output | โ€“ |
245
+ | `--icon` | Use ๐Ÿง  / โณ / ๐Ÿ’ฐ / ๐Ÿ“ฆ icons | text |
246
+ | `--verbose` | Longer labels | โ€“ |
247
+ | `--no-timer` | Hide TTL countdown | show |
248
+ | `--no-color` | Strip ANSI codes | โ€“ |
249
+ | `--segments=โ€ฆ` | Limit statusline segments (e.g. `model,five_hour,seven_day,saved`) | all |
250
+ | `--install-hook` / `--uninstall-hook` | Manage the PostToolUse hook | โ€“ |
251
+ </details>
252
+
253
+ ## โฌ† Update notifications
254
+
255
+ A statusline cannot open a dialog, and it re-renders every ~300ms, so it can never touch the network while drawing. The notification is therefore split in two:
256
+
257
+ - **The statusline tells you.** Up to date: a quiet gray `v3.24.0` at the tail. Newer release out: `โฌ† v3.24.0 โ†’ 3.25.0` in yellow, moved to the front. Never red โ€” nothing is broken.
258
+ - **Session start asks you.** On a new session or `/clear`, the SessionStart hook injects one line telling the model a newer version exists and to ask before installing anything. Only after you agree does it run `claude-token-saver upgrade`.
259
+ - **Declining sticks.** `claude-token-saver update-check --dismiss` mutes the offer for that version; the next release asks again. The statusline chip stays โ€” you declined the question, not the fact.
260
+
261
+ The registry lookup runs at most once every 24h in a detached background process and only ever writes a cache file (`update-check.json`) โ€” the same shape npm's `update-notifier` uses. A failed check still stamps its timestamp, so an offline machine backs off instead of retrying on every render. Turn checks off entirely with `CTS_NO_UPDATE_CHECK=1` or `NO_UPDATE_NOTIFIER`.
262
+
263
+ ## ๐Ÿ…ท Harness mode
264
+
265
+ Bootstrap five engineering principles (Ratchet ยท Evidence ยท PEV ยท Structured Task ยท Default Safe Path) into `CLAUDE.md` with one command; the statusline scores it as `๐Ÿ…ท 5/5`. When the same error keeps recurring, a `๐Ÿ…ทโš  ratchet?` nudge appears so you can promote it to a rule.
266
+
267
+ ```bash
268
+ claude-token-saver harness init # this project
269
+ claude-token-saver harness init --global # ~/.claude/CLAUDE.md โ€” every project
270
+ claude-token-saver harness check # current score (global fallback honored)
271
+ claude-token-saver harness analyze # run the transcript analysis manually (no hook needed); refreshes harness-state.json
272
+ claude-token-saver harness promote <N> --project|--global # warning #N โ†’ ratchet rule (scope required)
273
+ claude-token-saver harness promote "<rule text>" --project|--global # register your own hand-written rules the same way
274
+ claude-token-saver harness pull # register the package's curated ratchet rules into your global ratchet (opt-in, dedupes)
275
+ claude-token-saver harness list / rm <N> # view / delete rules (auto .bak)
276
+ claude-token-saver harness off | on # toggle the ๐Ÿ…ท chip
277
+ ```
278
+
279
+ - `promote` **requires** `--project`/`--global` in non-TTY contexts (scripts, LLM calls) โ€” a scope choice is never silently made for the caller.
280
+ - `pull` registers the **author-curated ratchet rules** bundled with the package (`presets/ratchet-rules.json` โ€” only general-purpose rules promoted from real recurring mistakes) into your global ratchet (`~/.claude/ratchet.md`). `install`/`init` never auto-inject anything; `pull` is always opt-in and idempotent. Drop any rule you dislike with `harness rm`.
281
+ - `seed` offers the same presets **one at a time**. Where `pull` registers the whole ratchet set in one go, `seed` covers the model-fitting presets too and asks about each of them in the first session after an install or upgrade ([below](#-seed-delegation-that-works-from-the-first-session)).
282
+ - ๐Ÿ…ทโš  runtime warnings (`ratchet?` `no-evidence` `PEV-skip`) expire after 30 minutes, subdirectory sessions match their project correctly, and PEV-skip counts only mutating tools (Edit/Write/Bash) so read-only research sessions don't trip it (v2.16.0+).
283
+
284
+ <details>
285
+ <summary>โš ๏ธ <code>harness rm</code> โ€” checklist before deleting</summary>
286
+
287
+ The whole point of the ratchet is **one-direction accumulation**. Deleting rules casually means the same mistakes return.
288
+
289
+ - **Rule too broad, blocking valid cases?** โ†’ โŒ delete โœ… narrow the condition (e.g. `"no hardcoded values"` โ†’ `"no hardcoded values outside tests"`)
290
+ - **Rule too narrow, almost never fires?** โ†’ โŒ delete โœ… leave it (zero cost)
291
+ - **Genuinely wrong?** โ†’ โœ… delete then
292
+
293
+ An auto `.bak` is kept, but **the session context that earned the rule its place is not recoverable.**
294
+ </details>
295
+
296
+
297
+ ## ๐Ÿ“ฆ compact-window โ€” pin where a 1M session compacts
298
+
299
+ Claude Code compacts when usage approaches `min(autoCompactWindow, model max context)`. On a 1M window, with that value unset, compaction only fires near 800k โ€” and until then every request re-bills the whole context. **1M is too large; the recommendation is a 400kโ€“700k band** โ€” 2โ€“3.5x a 200k session's headroom for the genuinely large pastes, with the runaway tail cut off.
300
+
301
+ **Anything inside the band is left alone.** 400k is the floor where the saving beats the extra compactions, and long sessions often want more room than that. Only an unset window, or one above 700k, is warned about (a smaller one is a deliberate, more aggressive choice).
302
+
303
+ **200k sessions are never warned** โ€” their window is already at or below 200k, so the setting cannot change anything.
304
+
305
+ ```bash
306
+ claude-token-saver compact-window # status (model, window, value, source)
307
+ claude-token-saver compact-window set --global # pin 500k (mid-band) in ~/.claude/settings.json
308
+ claude-token-saver compact-window set --project # pin it in <root>/.claude/settings.json
309
+ claude-token-saver compact-window set --global --value 600k # explicit value (100kโ€“1M)
310
+ claude-token-saver compact-window off | on # toggle the warning
311
+ ```
312
+
313
+ - On a 1M model with the value unset or above 700k, the statusline shows `๐Ÿ…ทโš  compact-window?` and the session briefing hands the model the exact registration command.
314
+ - Scope (`--global`/`--project`) is **required** for `set` โ€” a global settings file is never edited on a guess.
315
+ - Every other key in `settings.json` is preserved and a `.bak` is written first. Malformed JSON aborts the write untouched.
316
+ - An exported `CLAUDE_CODE_AUTO_COMPACT_WINDOW` beats settings.json; `set` detects that and says so.
317
+
318
+ ## ๐Ÿ”€ route-scan โ€” "this recurring task could run on a cheaper tier"
319
+
320
+ Finds the easy work your expensive model (opus/fable) keeps redoing in your session logs and proposes **haiku/sonnet delegation rules**. Fully local, zero token cost.
321
+
322
+ - **T2 โ†’ haiku**: lookups, pasted-screen Q&A, simple runs โ€” zero errors, near-zero mutation
323
+ - **T1 โ†’ sonnet**: build pipelines, status checks โ€” few mutations, โ‰ค1 error
324
+ - **T0 stays**: repeated errors, heavy mutation, design/analysis โ€” the session model keeps it
325
+
326
+ Three design pillars:
327
+ 1. Difficulty is judged by **outcome, not text guessing** โ€” tool errors, mutating tool calls, output tokens
328
+ 2. Thresholds **auto-calibrate to your own 14-day distribution** โ€” fixed constants drift with workload
329
+ 3. Promoted rules live in a tool-owned file (`.claude/ratchet-model.md`) that **refreshes itself every scan**, and a `โš  rule-health` flag fires when a delegated category's error rate climbs โ€” rules report their own staleness
330
+
331
+ ```bash
332
+ claude-token-saver route-scan # scan (24h cache) + tiered candidates
333
+ claude-token-saver harness promote R1 --project # promote candidate R1 to a model-fitting rule
334
+ claude-token-saver route-scan dismiss 1 # not interested โ€” won't resurface
335
+ claude-token-saver route-scan rules # list model-fitting rules (rm <N> to remove)
336
+ claude-token-saver route-scan savings # the savings ledger โ€” which rule moved work off which model, onto which
337
+ ```
338
+
339
+ Dig deeper: **tier criteria & research evidence** โ†’ [docs/TIER_CRITERIA.md](./docs/TIER_CRITERIA.md) (Korean) ยท **rule-file mechanics, scan triggers, subagent setup** โ†’ [docs/ROUTE_SCAN.md](./docs/ROUTE_SCAN.md) (Korean + English)
340
+
341
+ ### Behind a gateway (Bedrock / LiteLLM)
342
+
343
+ Through a corporate gateway the transcript records an inference-profile ARN where the model id belongs. That string says nothing about `opus` or `haiku`, so older versions read every session as Sonnet โ€” which made **T1 (โ†’sonnet) rules unreachable and zeroed the savings figures**.
344
+
345
+ Since v3.10.0 the profile id is mapped back to a role (main, opus, sonnet, haiku) and then to the alias your `ANTHROPIC_DEFAULT_*_MODEL` variables declare. The mapping is learned by joining each parent `Task` call to the subagent run it spawned via `toolUseId`. Below three observations, or when the role votes agree less than 80% of the time, the id stays `unknown` and drops out of the delegation aggregate rather than being guessed at.
346
+
347
+ For environments the learner cannot reach, write the mapping yourself in `<userDataDir>/profile-map.json`. Account id and region may be wildcarded:
348
+
349
+ ```jsonc
350
+ {
351
+ "modelAliases": {
352
+ "arn:aws:bedrock:*:*:application-inference-profile/<PROFILE_ID>": "claude-opus-5",
353
+ "prod-large": "claude-opus-5", // house aliases map the same way
354
+ "team-*": "claude-haiku-4-5"
355
+ }
356
+ }
357
+ ```
358
+
359
+ **Map house aliases that carry no family name** (`prod-large`, `team-fast`) here too. Shapes that keep the family name are recognized as-is โ€” Bedrock (`anthropic.claude-opus-4-5-v1:0`), Vertex (`claude-opus-4-5@20251101`), and the 1M suffix (`claude-sonnet-4-5[1m]`) โ€” but an alias without one cannot be priced. Rather than report a wrong figure, routing-savings **drops those runs from the aggregate** (both sides of the comparison must be recognizable); one line in the table above brings them back.
360
+
361
+ That file holds internal identifiers in plain text โ€” do not commit it. On a direct-API machine it is never created and behaviour is unchanged.
362
+
363
+ ## ๐ŸŒฑ seed: delegation that works from the first session
364
+
365
+ The model-fitting ratchet (`ratchet-model.md`) **starts empty.** A rule exists only after route-scan has seen the same kind of work recur in your own logs and you have approved that candidate. So a fresh install delegates nothing, and keeps delegating nothing for days โ€” precisely the stretch where the savings would matter most.
366
+
367
+ `seed` fills that gap from presets bundled with the package.
368
+
369
+ | Presets | What they cover | File |
370
+ |---|---|---|
371
+ | 9 model-fitting | running commands, lookup, status checks, questions about pasted logs, read-and-summarize โ€” each with a T2 (haiku) and a T1 (sonnet) rule | `presets/model-rules.json` |
372
+ | 6 ratchet | general-purpose rules promoted from mistakes that actually recurred | `presets/ratchet-rules.json` |
373
+
374
+ **How they get registered:** in the first session after an install or upgrade, the SessionStart hook hands the pending presets to the model, which walks the user through them **one at a time**. Each answer runs one of these immediately:
375
+
376
+ ```bash
377
+ claude-token-saver seed # pending presets + recorded answers
378
+ claude-token-saver seed accept <id> --global|--project # register one (scope required)
379
+ claude-token-saver seed accept all --global # when the user says "register them all"
380
+ claude-token-saver seed skip <id> # decline โ€” never offered again
381
+ claude-token-saver seed reset # clear the answers and offer everything again
382
+ ```
383
+
384
+ - **Nothing is written without a yes to that specific rule.** A declined rule stays declined across upgrades; a later release only surfaces the presets it actually added.
385
+ - A preset is withheld when you already approved a rule of the same shape (same tier and category).
386
+ - A seeded rule **does not pass someone else's statistics off as yours.** It is recorded as `preset (curated)` until a scan measures real firings and delegations, and then those numbers replace it. If its delegated error rate crosses the threshold it gets the same review flag as any other rule.
387
+ - The scope must be stated as `--global` or `--project`. The hook environment is non-TTY, so the CLI cannot ask โ€” the model confirms with the user and passes the flag.
388
+
389
+ ## ๐Ÿ‡ฐ๐Ÿ‡ท Korean writing guidance
390
+
391
+ Injects guidance that corrects how Claude writes Korean (dropped sentence parts, noun-stopped sentences, translationese, em-dash overuse) **once per session.**
392
+
393
+ ```bash
394
+ claude-token-saver korean on # on, for every project
395
+ claude-token-saver korean status # state, cost, provenance
396
+ claude-token-saver korean show # print the guidance itself
397
+ claude-token-saver korean off # off
398
+ ```
399
+
400
+ Claude Code's output styles can do the same thing, but an output style is **a single global slot**: turning it on takes that slot away from anything else and has to be configured per machine. This ships the guidance inside the package and delivers it through the SessionStart hook that is already installed, so it **applies wherever the CLI is installed and leaves the output-style slot free.** It survives `/clear`, because the hook fires again.
401
+
402
+ Cost is **~1.5k tokens per session, injected once at session start rather than per turn**, and covered by the prompt cache from the second request on. When it is on, a `โœ๏ธ` chip appears in the statusline.
403
+
404
+ ### Before and after
405
+
406
+ From a real pipeline (yaml-sns-agent), where a Slack bot shells out to `claude -p` to investigate the repository and answers in a thread. The guidance was toggled off and then on, with the same question both times: "explain in three Korean sentences how to narrow down a render failure."
407
+
408
+ **Before** (guidance off)
409
+
410
+ > ๋ Œ๋” ์‹คํŒจ ์›์ธ ์ขํžˆ๊ธฐ 3๋‹จ๊ณ„: ๋จผ์ € `hn:still --check`๋กœ ์Šคํ‹ธ์ด ๋ช…์„ธ๋ณด๋‹ค ์ตœ์‹ ์ธ์ง€(FRESH/STALE) ํ™•์ธ โ€” STALE์ด๋ฉด ์žฌ์ƒ์„ฑ ์•ˆ ํ•ด์„œ ์˜› ์ž์‚ฐ์œผ๋กœ ๋ Œ๋” ์‹œ๋„ํ•œ ๊ฒŒ ์›์ธ. ๋‹ค์Œ caption-blocks๊ฐ€ ์ตœ์‹  ๋‚˜๋ ˆ์ด์…˜๊ณผ ๋‹จ์–ด์—ด ์ผ์น˜ํ•˜๋Š”์ง€ ์ง์ ‘ ๋Œ€์กฐ โ€” TTS ์žฌ์ƒ์„ฑ ํ›„ blocks ์žฌ์ƒ์„ฑ ๋น ์ง€๋ฉด ์ž๋ง‰ยท์Œ์„ฑ ์–ด๊ธ‹๋‚˜ ๋ Œ๋” ๊นจ์ง.
411
+
412
+ **After** (guidance on)
413
+
414
+ > ๋จผ์ € `hn:still --check` ๋Œ๋ ค ์Šคํ‹ธ์ด ๋ช…์„ธ๋ณด๋‹ค ์ตœ์‹ ์ธ์ง€ ํ™•์ธํ•œ๋‹ค. STALE์ด๋ฉด ์žฌ์ƒ์„ฑ ์•ˆ ํ•ด์„œ ์ƒ๊ธด ๋ฌธ์ œ.
415
+ >
416
+ > ๋‹ค์Œ caption-blocks๊ฐ€ captions.json ๋‹จ์–ด์—ด๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ๋ณธ๋‹ค. ๋‚ด๋ ˆ์ด์…˜ ์žฌTTS ํ›„ blocks ์žฌ์ƒ์„ฑ ๋น ์ง€๋ฉด ์˜› ์ž๋ง‰์ด ์ƒˆ ์Œ์„ฑ ์œ„์— ๋œฌ๋‹ค.
417
+
418
+ Three things change. Clauses chained with em dashes become separate sentences, so one sentence carries one fact. Noun-stopped phrases (ํ™•์ธ, ๋Œ€์กฐ, ๋ Œ๋” ๊นจ์ง โ€” "check", "compare", "render breaks") become predicates (ํ™•์ธํ•œ๋‹ค, ๋ณธ๋‹ค, ๋œฌ๋‹ค), which makes it explicit that these are steps to take. And the particles come back where they had been dropped, so subject and object are legible on the first read.
419
+
420
+ The technical content is identical in both. The guidance touches sentence construction only, not judgement or accuracy: the answer does not change, it just stops needing a second read. In a channel people scroll through, that difference cuts follow-up questions โ€” and the tokens those follow-ups would have cost.
421
+
422
+ ### The supplement: cohesion and conservative correctness rules
423
+
424
+ The vendored fluent-korean text ships unmodified; everything collected since lives in a separate supplement (`presets/korean-style/supplement.md`) appended to the same injection. It was compiled conservatively โ€” only clauses that are nearly always an improvement, sourced from the National Institute of Korean Language's public-language guidelines, the Kubernetes Korean localization guide, and three peer-reviewed studies on text cohesion in Korean writing.
425
+
426
+ It adds three layers:
427
+
428
+ - **Translationese**: double passives, Japanese-derived calques, `~์— ์žˆ์–ด์„œ`, possession-verb renderings of English *have*. The machine-checkable ones also run in the write-time lint (below).
429
+ - **AI-writing tics**: automatic intensifiers ("๋‹ค์–‘ํ•œ", "ํ•ต์‹ฌ์ ์ธ"), signpost sentences, rhetorical question-then-answer, unconditionally upbeat endings.
430
+ - **Cohesion** โ€” how sentences connect, which no regex can check. The research finding that shapes this section: surface connectives (conjunctions, demonstratives) correlate *negatively or not at all* with judged text quality, while elaboration โ€” the next sentence picking up and unpacking what the previous one introduced โ€” is the only connection type with a positive correlation. So the guidance says: when a transition feels rough, fix the information order (given before new), don't add a connective.
431
+
432
+ **Most of the cohesion layer is not Korean-specific.** Given-before-new ordering (the "given-new contract"), one clear referent per pronoun, keeping one subject per paragraph, bridging sentences instead of leaping, and merging choppy repetitive sentences into a modifier-plus-core structure apply to English prose the same way โ€” the studies happen to be about Korean learners, but the principles they validate are the standard cohesion model from text linguistics. If you write English deliverables with Claude, run `claude-token-saver cohesion on` โ€” it injects exactly those five rules as a standalone English block (~0.5k tokens per session), no Korean feature required. While `korean on` is active the block is suppressed, because the Korean supplement already carries the same rules.
433
+
434
+ A final subsection lists what must **not** be "corrected": settled domain terms, formal register, and verbatim quotations โ€” every lint finding is a request to confirm, not a verdict.
435
+
436
+ ### The write-time check (v3.24.0)
437
+
438
+ Injecting the guidance once at session start turned out to be half the job. The model reads it, then writes dozens of files over the next hours with nothing re-reading the output. Sessions with the guidance active still shipped violations into documents, and it surfaced only when a human read the finished artifact. An August 2026 fix reworded the scope sentence to address this; it recurred, because rewording an instruction does not add a checkpoint.
439
+
440
+ From v3.24.0 `korean on` also installs a PostToolUse hook. It opens the file the model just wrote, runs the clauses a machine can decide, and hands any findings back. The file is already saved, so nothing is lost โ€” the model fixes it on the spot.
441
+
442
+ ```bash
443
+ claude-token-saver korean lint block # default: findings are handed back as blocking feedback
444
+ claude-token-saver korean lint warn # print findings, do not block
445
+ claude-token-saver korean lint off # disable the check
446
+
447
+ claude-token-saver korean lint scope all # default: every text file the session writes
448
+ claude-token-saver korean lint scope prose # documents only
449
+
450
+ claude-token-saver korean lint docs/*.md # check files already on disk
451
+ ```
452
+
453
+ Checked: 15 figurative phrases, translationese markers, separators (`โ€”`ยท`ใ…ก`ยท`|`), three or more `์˜` particles in one phrase, and a period after a nominal ending. Clauses that need judgement stay with the guidance text.
454
+
455
+ The default `all` scope covers code comments, UI strings, subtitles, templates, and build output, not just documents. The vendored guidance exempts comments, but comments are read by people and generated artifacts (PDF, HTML) are assembled from those strings, so exempting them reopens the exact gap that was reported. Only installed dependencies, VCS internals, lockfiles, and binary or image files are skipped; `dist/` and `build/` are checked. `korean lint scope prose` restores the narrow reading.
456
+
457
+ The scope sentence in the injected guidance is generated from the same setting, so the model is never told one rule while being corrected against another.
458
+
459
+ ### The encoding rule that ships with it (v3.23.2)
460
+
461
+ Alongside the writing guidance, one more line is injected: **non-ASCII strings in tool-call parameters must be written as literal UTF-8, never as `\uXXXX` unicode escapes.**
462
+
463
+ When the model puts Korean into a Write or Edit parameter as escapes, those escapes are sometimes not decoded into code points at all: the literal text `ํ•œ` lands in the file. The artifact carries mojibake, and the model keeps editing on top of it without noticing that what it wrote and what the file holds have diverged. Not writing escapes in the first place removes the path entirely, so the rule blocks the input instead of repairing the output.
464
+
465
+ This line lives in claude-token-saver's own framing paragraph, not in the vendored fluent-korean text. It governs encoding rather than style, and the vendored wording is kept unmodified. For the same reason it carries no exceptions, unlike the style rules that skip code and commit messages. It adds roughly 60 tokens per session.
466
+
467
+ > **Evidence**
468
+ > The same failure is reported against Claude Code: [#12417, unicode handling regression](https://github.com/anthropics/claude-code/issues/12417) and [#26141, Edit silently corrupting unicode](https://github.com/anthropics/claude-code/issues/26141).
469
+
470
+ ### Asked at install time
471
+
472
+ The install **prints what the guidance changes, its per-session cost and its source, then asks.** A Korean system locale (`ko_KR` and friends; on macOS the system setting is checked too) makes the question default to yes; anything else defaults to no, so users who never write Korean are not billed 1.5k tokens a session. The locale is only a default, so an English-locale machine used for Korean work can still turn it on right there.
473
+
474
+ Installs with nobody attached โ€” npm `postinstall`, CI, piped stdin โ€” skip the question and apply the locale default, because a blocked prompt hangs the install. In that case, if the locale is not Korean the setting is **left undecided rather than recorded**, so a later run at a terminal still gets to ask. Use `--yes` or `--no-input` to force the non-interactive path, or `CTS_NO_KOREAN=1` to skip the feature entirely. **Once you have turned it on or off yourself, that choice sticks โ€” an upgrade never overrides it.**
475
+
476
+ > **Source and license**
477
+ > The guidance text comes from [fluent-korean](https://github.com/snflkd/fluent-korean). Copyright (c) 2026 snflkd, MIT License.
478
+ > The wording is unmodified; only the output-style frontmatter was removed. The full license ships with the package at `presets/korean-style/LICENSE-fluent-korean`.
479
+
480
+ ## ๐Ÿ“„ doc2md โ€” documents become Markdown before the model reads them
481
+
482
+ `Read` a pptx, xlsx, pdf or docx and the raw bytes go into the context window, where the model cannot read them. This intercepts that `Read`, converts the file once, and hands over the Markdown instead.
483
+
484
+ **This is opt-in.** Installing the CLI does not turn it on: both commands below are required, and a registered hook with no converter behind it does nothing at all.
485
+
486
+ Three situations, three different interception points:
487
+
488
+ | Situation | Where it is caught |
489
+ |---|---|
490
+ | A document path typed in the prompt (`@path`, quoted, or relative) | `UserPromptSubmit`: converted, and the conversion's path is handed back as context |
491
+ | A document opened with `Read` mid-task | PDFs are caught by `PreToolUse(Read)`. pptx/xlsx/docx/fig are not: Claude Code refuses them as binary *before* any hook runs, so the session-start note tells the model to run `doc2md <path>` instead |
492
+ | A document attached to the message | **Not catchable.** No hook event receives attachment content. The session-start note has the model ask for a path next time |
493
+
494
+ That second row is measured, not assumed: a `.pdf` Read fires the hook, and a `.pptx` Read in the same session leaves no hook log entry at all.
495
+
496
+ ```bash
497
+ claude-token-saver doc2md on # register the hooks (the converter installs itself)
498
+ claude-token-saver doc2md # check converter + hook registration
499
+ claude-token-saver doc2md report.pptx # convert by hand and see the result
500
+ claude-token-saver doc2md install-converter # only to get the install out of the way early
501
+ ```
502
+
503
+ **The converter installs itself.** Any rollout step a person has to be told about is a step some of them skip, so the converter installs in the background the moment a document first shows up, and converts as soon as it is ready. Measured: about 30s for the first document (15s install plus markitdown's first import), then 3.7s for a new document and 0.1s on a cache hit. The `.fig` parser installs in half a second on the first Figma file.
504
+
505
+ It installs on first use rather than at `install` time: the venv is 47MB, and someone who never opens a document should not pay for it. Set `CTS_DOC2MD_NO_AUTOINSTALL=1` to turn the automatic install off.
506
+
507
+ **Python 3.10+ is required** โ€” markitdown's own floor, and macOS still ships 3.9 as `/usr/bin/python3`. The venv is built on an interpreter chosen by version rather than by PATH order. Built on 3.9, pip resolves markitdown to a 2019 placeholder release (0.0.1a1): the install looks like it worked and every conversion then dies at import. This was found by walking into it. When nothing on the machine is new enough, the message points at `brew install python` instead of at an install command that cannot succeed.
508
+
509
+ The converter goes into a venv this tool owns (`<state dir>/doc2md-venv`): no system interpreter is touched, and uninstalling the CLI takes it along. An existing markitdown on `uv tool` or `PATH` is preferred over building a new one.
510
+
511
+ Conversion is [markitdown](https://github.com/microsoft/markitdown). Slide numbers, heading levels, tables, speaker notes and per-sheet headings all survive, and non-Latin text comes through intact.
512
+
513
+ Several things it deliberately does not do:
514
+
515
+ - **Images are not converted.** markitdown returns nothing for them, and OCR misread resource names in testing (`c5.xlarge` as `c.xlarge`). In a document where those names *are* the content, wrong text is worse than none. The model reads images natively anyway.
516
+ - **A missing converter never fails silently.** The install command is shown once, then the original `Read` proceeds untouched. Repeating the notice on every read would be its own nuisance; saying nothing is how a broken converter hides. Run `doc2md` with no arguments to see the converter and hook registration together.
517
+ - **Conversions never land in your project.** They go under the tool's own state directory with mode `0700`, so there is nothing to add to `.gitignore`. Filenames matching payroll/contract/secret patterns are skipped entirely.
518
+ - **Zip bombs are refused.** pptx/xlsx/docx are zip containers: the declared sizes are checked first, and since those are written by whoever built the file, the real decompressed bytes are counted against a ceiling too.
519
+ - **Spreadsheets are capped by rows, not bytes.** Conversion time tracks row count (measured: a 6.3MB PDF in 0.9s, a 5.8MB workbook in 47.75s). Past 50,000 rows only the head is converted, and **the truncation and the true row count are both stated** in what the model is told.
520
+
521
+ ### What a conversion saves
522
+
523
+ Every conversion is stamped with a provenance header: which original, when, how many tokens. Savings show up on the statusline's own `๐Ÿ“„ Doc2md saved` line.
524
+
525
+ The baseline is what you would have done without a converter, and that differs by format. Both were measured on 2026-09-06.
526
+
527
+ **PDF is priced against attaching it.** The same one-line prompt was sent through `claude --print --input-format stream-json` with and without the file as a document block. The control turn cost 42,204 tokens, twice, to the token.
528
+
529
+ | Attached file | Size | Extra tokens | Per page |
530
+ |---|---|---|---|
531
+ | Rรฉsumรฉ PDF | 7 pages | +20,537 | 2,934 |
532
+ | Rรฉsumรฉ PDF | 5 pages | +12,709 | 2,542 |
533
+
534
+ An attached PDF is read whole, but every page costs 2,500โ€“2,900 tokens against 5,531 for the conversion. The coefficient used is 2,500 per page โ€” below both measurements, so the figure understates rather than flatters.
535
+
536
+ **pptx/xlsx/docx are priced against unpacking the container.** These never reach the model as attachments at all: the same probe on a docx added 78 tokens and the model replied that it had no file, and `Read` refuses the format outright. What you actually do without a converter is unzip the archive and read its XML, where tags and style attributes outweigh the words.
537
+
538
+ | Original | Body XML | Conversion | Ratio |
539
+ |---|---|---|---|
540
+ | Deck, pptx (31.8MB) | ~540,429 tokens | ~22,610 tokens | 23.8ร— |
541
+ | Rรฉsumรฉ, docx (189KB) | ~79,621 tokens | ~1,684 tokens | 47.3ร— |
542
+
543
+ This baseline is measured per file from the real XML size, not applied as a per-format ratio. `.xls` is not a zip container and has no markup to measure, so it claims nothing.
544
+
545
+ ### Figma `.fig` converts too
546
+
547
+ Planning documents are moving from PowerPoint to Figma, so the same hook catches `.fig`. A `.fig` is a zip, but the `canvas.fig` inside it is Figma's private binary (kiwi format), which markitdown cannot open โ€” so this one format is converted in Node with [openfig-core](https://github.com/OpenFig-org/openfig-core) (MIT). `doc2md install-converter` places it beside markitdown in the tool's state directory; the package itself still ships zero dependencies.
548
+
549
+ The result is an outline: pages and frames become headings, text nodes become body lines, and shapes are counted rather than listed โ€” in a planning document the words are the content, and two hundred `Rectangle 173` lines would drown them. A file with no text at all is refused rather than dressed up as an empty document.
550
+
551
+ Verified against real files: a community Bootstrap UI kit (8.1MB, 4,155 nodes, 1,312 of them text) and a 52MB Tailwind kit, each converting in under a second. Both `.fig` vintages parse โ€” the current zip container and the older bare fig-kiwi stream.
552
+
553
+ **`.fig` saves the most of any format.** Unlike the Office containers, `Read` does not refuse a `.fig`: the extension means nothing to it, so it pulls the binary in as text and the context window fills with tokenised noise. Measured against the same 42,760-token control:
554
+
555
+ | File | Size | Extra tokens for a Read | Conversion |
556
+ |---|---|---|---|
557
+ | plan.fig | 26KB | +44,195 | 100 tokens |
558
+ | bootstrap-kit.fig | 8.1MB | +43,994 | 18,397 tokens |
559
+
560
+ Two files three hundred times apart in size cost the same, because Read truncates long before the file ends โ€” you pay for a whole document and receive a fraction of one. The baseline is therefore a flat 44,000 tokens. For comparison, the same probe on a pptx cost +317 tokens and on a docx +185: a refusal message, and nothing else.
561
+
562
+ #### Why the baseline does not scale with file size
563
+
564
+ A baseline has to be what would actually have been spent without the converter. Intuition says a bigger file burns more, but the `Read` tool has a cap (2,000 lines by default, plus a per-line character limit), and a binary file hits it almost immediately: even the 26KB file was already truncated, which is why two files 300ร— apart came out 201 tokens apart. Had the 8.1MB file gone in whole it would have been millions of tokens โ€” money nobody could have spent, since it does not fit in a 200k context window. Claiming to have saved unspendable money is flattery, not measurement.
565
+
566
+ The same principle runs through every baseline here:
567
+
568
+ - **`.fig`, flat 44,000** โ€” set below both measurements (44,195 and 43,994). A model could burn size-proportional tokens by re-Reading at successive offsets, but one Read is what a sane agent does once the bytes turn out to be binary noise, so one Read is the honest counterfactual.
569
+ - **PDF, 2,500 per page** โ€” below both measured values (2,542 and 2,934).
570
+ - **Office formats, the file's actual XML size** โ€” the one case where proportional is right, because a person really does end up reading that XML; it is measured per file rather than applied as a ratio.
571
+
572
+ The common rule: wherever an estimate and a measurement diverge, the lower number wins. A figure the user can trust is worth more than one that flatters the tool.
573
+
574
+ ### Editing a document: copy, then script
575
+
576
+ Conversion is one-way โ€” editing the cached `.md` changes nothing in the source. The hook refuses `Edit`/`Write` on both the cache and the original binary, and points at the right path instead: copy the original, edit the copy with a script, re-convert the copy to verify.
577
+
578
+ `install-converter` puts the editing libraries (python-pptx, python-docx, openpyxl) in the same venv, so a structural request like "swap the chart on slide 23 for a line chart" is a short script the agent writes on the spot. `.fig` edits go through openfig-core, which encodes as well as parses.
579
+
580
+ All four formats were exercised end to end on 2026-09-06: 10 docx run replacements plus three consecutive re-saves, a pptx bar-to-line chart swap with an added data point, xlsx value edits and a new row, and a fig text edit with re-encode and re-parse. In every case the original was byte-identical afterwards and the re-converted copy showed the change. One caveat: removing a chart shape from a pptx leaves the old chart XML part orphaned โ€” PowerPoint ignores it, but delete the part and its rels for a clean file. Charts and images never appear in a conversion, so visual edits must be confirmed in the application itself.
581
+
582
+ ### DRM-wrapped documents
583
+
584
+ Encryption and DRM are different problems with different answers. Enterprise DRM (Fasoo, MarkAny, SoftCamp and the like) does not password a document โ€” it wraps the whole file, and only processes the vendor's agent has whitelisted ever see plaintext. Python is not one of them, so what sits on disk is ciphertext behind a vendor header, and **no password will open it.**
585
+
586
+ The first bytes decide which story to tell: a zip header means a truncated download, an OLE container means a password, and neither means the file is not that format at all.
587
+
588
+ ```
589
+ โœ— bad-archive: File is not a zip file โ†’ download it again
590
+ โœ— encrypted: password-protected Office file โ†’ ask for an unlocked copy
591
+ โœ— drm-protected: DRM-wrapped file (FASOO) โ†’ ask for a copy released from DRM
592
+ ```
593
+
594
+ Vendor names are matched only to say which client to go to; the classification stands without recognising the vendor. PDFs are judged the same way through their public DRM security-handler names (FOPN_foweb, EBX_HANDLER, Adobe.APS).
595
+
596
+ ### Locked documents, and Windows
597
+
598
+ **A password-protected document is a state, not an error.** Office encrypts by wrapping the package in an OLE compound file rather than a zip, so opening one as a zip used to report "not a zip file" โ€” which reads as a broken download and sends the user after the wrong problem. It is now identified before conversion:
599
+
600
+ ```
601
+ โœ— encrypted: password-protected Office file (OLE-wrapped)
602
+ โœ— encrypted: password-protected PDF
603
+ ```
604
+
605
+ The model is told to ask for an unlocked copy. This tool never asks for or stores a password, and never blocks the original `Read`, so work continues either way. A PDF that merely restricts printing still opens and still converts โ€” checked against a false positive โ€” and a legacy `.xls`, which is an OLE file by design, is not mistaken for an encrypted one.
606
+
607
+ **Windows is supported.** For teams with Windows machines:
608
+
609
+ - The Python search uses the `py -3` launcher. `python3` is rarely on PATH there, and a bare `python` may be the Store alias stub that opens a web page instead of running anything. Venv interpreters are looked for at `Scripts\python.exe`.
610
+ - The `.fig` parser installs through `npm.cmd` via the shell, and the package spec dropped its caret (`openfig-core@0.4.x`): in cmd.exe `^` is the escape character and never reaches npm.
611
+ - The background install and every child process set `windowsHide`, so no console window appears in the middle of someone's prompt.
612
+
613
+ `claude-token-saver doc2md --clean` empties the conversion cache; `doc2md off` removes the hook. Removal filters for this tool's own entry, so anything else you registered under `PreToolUse` stays.
614
+
615
+ ## ๐ŸŒ Behind a gateway (Bedrock / Vertex)
616
+
617
+ A gateway reports the cache-creation total but never the 5m/1h split. That left the tool unable to tell "nothing cached yet" from "this provider does not say", and the fallback assumed an hour โ€” for a window that is really five minutes on Bedrock, overstating it twelvefold.
618
+
619
+ Since v3.26.0 the gateway is detected from the model ids in the transcript, which fixes:
620
+
621
+ - The countdown falls back to 5 minutes, labelled `5m?`. Three grades of certainty get three labels: measured (`5m`), inferred (`5m?`), unknown (`?`).
622
+ - In a 5-minute bucket the countdown colour follows absolute time rather than a percentage. 30% of five minutes is 90 seconds, and green there promised comfort that was not there.
623
+ - The `โš  5m TTL` warning finally reaches these users โ€” with different advice, since no subscription plan changes a gateway's TTL.
624
+ - `Extra cost if 5m-only` is only asked of sessions that have 1h writes to lose. Elsewhere the arithmetically honest `+$0` read as an endorsement of the bucket you are already stuck in.
625
+ - Delegated runs dropped for an unpriceable model id show as `๐Ÿ”€ N unresolved` instead of nothing, which used to be indistinguishable from never having delegated.
626
+ - Environment variables set to a `foundation-model` ARN now resolve. An opaque `application-inference-profile` id still does not: guessing at it is how wrong prices enter the ledger.
627
+
628
+ If the detection is wrong, pin it with `claude-token-saver mode ttl=5m` (or `ttl=1h`). An explicit value outranks the measurement.
629
+
630
+ ### LiteLLM: your key budget stands in for the missing 5h/7d caps (v3.35.0)
631
+
632
+ Behind a LiteLLM proxy (Bedrock and friends), Claude Code's stdin never carries `rate_limits`, so the `โœฆ current` / `๐Ÿ“… weekly` gauges simply do not exist. LiteLLM does track per-key budgets, so the statusline draws a budget gauge in their place.
633
+
634
+ - Detection: `ANTHROPIC_BASE_URL` points somewhere other than the official endpoint, and `ANTHROPIC_AUTH_TOKEN` (or `ANTHROPIC_API_KEY`) is set.
635
+ - The proxy is asked via `GET /key/info` and `GET /user/info` โ€” only the calling key's own data. Renders read a cache file; a detached background process refreshes it every 5 minutes (same shape as the update check), so the statusline never waits on the network.
636
+ - Budget source priority follows real-world usage: the **team-membership budget** (`team_memberships[].spend` + its linked budget table row) first, then the key's own `max_budget`, then the internal-user budget. Verified against a Dockerized LiteLLM, including memberships whose budget diverges from the team max into a separate budget-table row.
637
+ - Unlimited keys (no `max_budget`) get no gauge. The `๐Ÿ’ต` monthly-spend segment still shows, since it comes from session logs.
638
+ - Inspect with `claude-token-saver litellm-budget` (cached) or `litellm-budget --refresh` (query now).
639
+
640
+ One related non-bug: if your session model is already sonnet, a sonnet-delegation (T1) rule can never save anything, because there is no price gap to capture. That is correct, but `route-scan rules` displayed it identically to "no delegations yet", so it now says outright that the rule does not apply at the current default model.
641
+
642
+ ## Spike issue codes
643
+
644
+ | Code | Meaning |
645
+ |---|---|
646
+ | `LARGE_INPUT_PER_REQUEST` | single request > 200k input tokens โ€” per-turn re-billing and cap burn spike |
647
+ | `LOW_HIT_RATE` | cache hit rate < 50% |
648
+ | `BUCKET_5M_DOMINANT` | > 70% of cache writes hit the 5m bucket |
649
+ | `HIGH_OUTPUT_RATIO` | output/input > 0.15 (output is 5ร— input price) |
650
+ | `HIGH_REQUEST_COUNT` | session made 3ร—+ your median (tool loop?) |
651
+ | `FREQUENT_CACHE_REBUILD` | `cache_creation` > `cache_read` |
652
+
653
+ Remediation commands are OS-aware (`~/.zshrc` for macOS/Linux/WSL, `setx` for Windows).
654
+
655
+ ## Real-world impact โ€” before/after report
656
+
657
+ ![claude-token-saver โ€” harness + ratchet adoption impact](./docs/harness-impact.png)
658
+
659
+ harness 5/5 + ratchet applied to the author's own Claude Code work, normalized **per user message** (cutoff 2026-05-02, Opus 4.7 pricing):
660
+
661
+ | metric | before (7d / 739 msgs) | after (2d / 157 msgs) | ฮ” |
662
+ |---|---:|---:|---:|
663
+ | cost / user message | $2.345 | $1.910 | **โˆ’18.6%** |
664
+ | output tokens / message | 7,391 | 6,052 | โˆ’18.1% |
665
+ | assistant turns / message | 9.73 | 8.83 | โˆ’9.2% |
666
+ | tool calls / message | 5.72 | 5.25 | โˆ’8.2% |
667
+
668
+ Same request resolved in fewer round-trips โ†’ first-try success rate up โ€” the effect of PEV + Structured Task forcing one-shot delivery.
669
+
670
+ <details>
671
+ <summary>Measurement notes โ€” why cache hit rate isn't included ยท sample caveats</summary>
672
+
673
+ - The author is on the Max plan (1-hour cache TTL) with hit rate already converged near ~98%, so little headroom there. **Pro-plan users (5-minute TTL)** likely see hit rate itself rise with the handoff-before-expiry workflow.
674
+ - Handoff-before-expiry: watch the TTL countdown, run `claude-token-saver handoff` just before expiry to dump work state into a markdown brief, start a fresh cache cycle. Same flow handles the 1M warning and cap chips.
675
+ - โš ๏ธ POST window is only 2 days (157 msgs); statistical confidence is low, and week-to-week topic mix differs, so the tool effect isn't cleanly isolated.
676
+ </details>
677
+
678
+ ## Pricing (Jul 2026)
679
+
680
+ Per million tokens (USD), as used by the cost estimator:
681
+
682
+ | Tier | Models | Input | 5m Write | 1h Write | Read | Output |
683
+ |---|---|---|---|---|---|---|
684
+ | `claude-fable-5` | Fable 5 / Mythos 5 | $10 | $12.50 | $20 | $1 | $50 |
685
+ | `claude-opus-new` | Opus 4.5 / 4.6 / 4.7 / 4.8 | $5 | $6.25 | $10 | $0.50 | $25 |
686
+ | `claude-opus-legacy` | Opus 4 / 4.1 / 3 | $15 | $18.75 | $30 | $1.50 | $75 |
687
+ | `claude-sonnet` | Sonnet 3.7 / 4 / 4.5 / 4.6 / 5 | $3 | $3.75 | $6 | $0.30 | $15 |
688
+ | `claude-haiku-4-5` | Haiku 4.5 | $1 | $1.25 | $2 | $0.10 | $5 |
689
+
690
+ Source: [Anthropic pricing docs](https://platform.claude.com/docs/en/about-claude/pricing). Sonnet 5 has an introductory $2/$10 rate through 2026-08-31; the estimator uses the standard sticker. Versions โ‰ค 2.16.x priced Fable 5 at the Sonnet tier (~3ร— under-estimate) โ€” upgrade to 2.17.0+.
691
+
692
+ ### Cache TTL by plan
693
+
694
+ | Plan | TTL | Controlled by |
695
+ |---|---|---|
696
+ | Max ($100โ€“200/mo) | **1h auto** | `tengu_prompt_cache_1h_config` flag |
697
+ | Pro ($20/mo) | **5m fixed** | not configurable |
698
+ | API key | 5m default (1h via beta header) | `cache_control.ttl` |
699
+
700
+ ## How it works ยท Environment
701
+
702
+ 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/1h_input_tokens` by day and session.
703
+
704
+ Node.js โ‰ฅ 18 ยท macOS / Linux / Windows / WSL ยท **zero dependencies**.
705
+
706
+ <details>
707
+ <summary>Known quirks ยท Migration ยท Background</summary>
708
+
709
+ **IntelliJ Claude Code plugin** โ€” the statusline widget fuses frames at the character level when emoji are present (`59:548` artifacts). v2.8.5+ detects `TERMINAL_EMULATOR=JetBrains-JediTerm` and falls back to text mode automatically.
710
+
711
+ **If the countdown looks frozen:** ticking while idle requires Claude Code to re-run the statusline command on a timer, controlled by `statusLine.refreshInterval` (seconds, Claude Code v2.1.97+) in `~/.claude/settings.json`. Without it the line only redraws when the conversation updates. If behavior differs per terminal, check three things: โ‘  that machine's Claude Code is โ‰ฅ 2.1.97; โ‘ก no project `.claude/settings.json` / `settings.local.json` overrides `statusLine` without a refreshInterval; โ‘ข the statusline wrapper actually finds `claude-token-saver` on PATH instead of falling back to a multi-second `npx` run on every render (typical when nvm is not loaded in non-login shells). Re-running `claude-token-saver install` restores refreshInterval=5.
712
+
713
+ **Migration from claude-cache-monitor:**
714
+ ```bash
715
+ npm uninstall -g claude-cache-monitor && npm i -g claude-token-saver
716
+ ```
717
+ Also update `statusLine.command` in `~/.claude/settings.json` to `claude-token-saver โ€ฆ`.
718
+
719
+ **Background:** [GitHub Issue #46829](https://github.com/anthropics/claude-code/issues/46829) (cache TTL regression) ยท [HN discussion](https://news.ycombinator.com/item?id=47736476)
720
+ </details>
721
+
722
+ ## Release notes
723
+
724
+ The full history moved to [CHANGELOG.md](./CHANGELOG.md) (Korean; version headings and command names are language-neutral). Recent changes:
725
+
726
+ - **v3.39.0**: `feedback` subcommand โ€” file bug reports and feature requests straight from the terminal or a Claude session, via the gh CLI, an anonymous no-login form (auto-filed as a GitHub issue by an Apps Script relay), or a local fallback. `install` now asks before replacing an existing statusline instead of silently skipping.
727
+ - **v3.38.0**: `cohesion on` โ€” the language-neutral cohesion rules from the Korean supplement become a standalone English injection (given-before-new, one referent per pronoun, subject consistency, bridging, merging choppy sentences). Opt-in, ~0.5k tokens per session, suppressed while `korean on` already carries them.
728
+ - **v3.37.0**: Korean guidance grows a conservative supplement (translationese, AI-writing tics, a research-backed cohesion section whose principles apply to English prose too) and the write-time lint gains 5 translationese patterns, validated at 1 false positive across 255 real files.
729
+ - **v3.35.0**: A `๐Ÿ’ต Sep $42` segment now shows estimated spend since 00:00 on the 1st of the current month, always on โ€” including gateway setups with no 5h/7d caps. LiteLLM gateway users get a `๐Ÿ”‘ budget โ–ฐโ–ฑ 34% $34/$100` gauge built from the key's budget (`GET /key/info` + `GET /user/info`, team-membership budget first, then key, then internal user โ€” verified against a Dockerized LiteLLM).
730
+ - **v3.34.0**: seed presets offered one at a time, output-language choice at install, context warning raised to 500k.
731
+
732
+ ## Feedback
733
+
734
+ Found a bug, or want a feature? Open an issue: https://github.com/rootstudioyaml/sprag/issues
735
+
736
+ No browser or GitHub login handy (corporate network, mid-session)? Submit straight from the terminal โ€” or ask Claude to do it for you:
737
+
738
+ ```bash
739
+ claude-token-saver feedback "the 5m TTL chip never clears on Bedrock"
740
+ ```
741
+
742
+ It files a GitHub issue via the `gh` CLI when one is authenticated; otherwise it submits anonymously (no login, works where github.com is blocked). Pass `--anonymous` to skip the `gh` path. Version and OS metadata are attached automatically.
743
+
744
+ When reporting a bug, please include the tool version (`claude-token-saver --version`), your OS, and โ€” if it is a statusline or warning issue โ€” the statusline output or the `claude-token-saver last` result.
745
+
746
+ ## License
747
+
748
+ MIT
749
+
750
+ ---
751
+
752
+ ## Who makes this
753
+
754
+ [![DeepPulse YouTube](https://img.shields.io/badge/YouTube-@DeepPulseKR-FF0000?logo=youtube&logoColor=white)](https://www.youtube.com/@DeepPulseKR)
755
+ [![DeepPulseEN YouTube](https://img.shields.io/badge/YouTube-@DeepPulseEN-FF0000?logo=youtube&logoColor=white)](https://www.youtube.com/@DeepPulseEN)
756
+ [![Homepage](https://img.shields.io/badge/Homepage-rootstudioyaml.github.io-2ea44f)](https://rootstudioyaml.github.io/)
757
+
758
+ Built and used at **DeepPulse**, a channel about AI developer tooling. The [launch Short (60s)](https://www.youtube.com/shorts/RaD8qMsPTnA) covers where this came from and how it is used.