claude-token-saver 2.9.0 โ†’ 2.9.2

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 CHANGED
@@ -33,17 +33,21 @@ claude-token-saver install
33
33
  After install, Claude Code's bottom statusline updates every 5 seconds with cache state (postinstall registers it in `~/.claude/settings.json` automatically).
34
34
 
35
35
  ```
36
- ๐Ÿค– Opus 4.7 ยท ๐Ÿง  Cache hit 98.0% ยท โณ Cache expires 59:51 ยท ๐Ÿ“ฆ Ctx 200k ยท ๐Ÿ’ฐ Cache saved $221 ยท last 1d
36
+ ๐Ÿค– Opus 4.7 ยท ๐Ÿง  Cache hit 98.0% ยท โณ Cache expires 58:38 ยท โœฆ current โ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘ 15% ๐Ÿ”„ 08:50 ยท ๐Ÿ“… weekly โ–ˆโ–’โ–‘โ–‘โ–‘โ–‘ 24% ๐Ÿ”„ Thu 13:00 ยท ๐Ÿ“ฆ Ctx 200k ยท ๐Ÿ’ฐ Cache saved $205 ยท last 1d
37
37
  ```
38
38
 
39
- Risk chips lead when something's wrong:
39
+ Segments โ€” `๐Ÿค– model` ยท `๐Ÿง  cache hit rate` ยท `โณ TTL countdown` ยท `โœฆ current` (5-hour window) ยท `๐Ÿ“… weekly` (7-day window) ยท `๐Ÿ“ฆ context` ยท `๐Ÿ’ฐ cumulative savings` ยท `last <window>`.
40
+
41
+ When excessive token usage is detected, a warning chip is prepended at the front of the statusline:
40
42
 
41
43
  ```
42
- ๐Ÿšจ 5H 94% (resets in 12m) ยท ๐Ÿค– Opus 4.7 ยท ๐Ÿง  Cache hit 72.1% ยท โš  Cache miss ยท ๐Ÿ“ฆ Ctx 200k ยท last 1d
44
+ ๐Ÿšจ 5H 94% (resets in 12m) ยท ๐Ÿค– Opus 4.7 ยท ๐Ÿง  Cache hit 72.1% ยท โš  Cache miss ยท โœฆ current โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ 94% ยท ๐Ÿ“ฆ Ctx 200k ยท last 1d
43
45
  ```
44
46
 
45
47
  Risk chips: `๐Ÿšจ 5H/7D NN%`, `โš  1M ON`, `โš  Input spike`, `โš  Cache miss`, `โš  5m TTL`, `โš  Rebuild churn`, `โš  Output heavy`, `โš  Call surge`.
46
48
 
49
+ **What to do** โ€” run the `/claude-token-saver` Skill in Claude. It calls `claude-token-saver last` and surfaces the root cause + step-by-step fix. Saying the chip wording out loud (e.g. "5H cap is up", "cache miss") also auto-activates the same Skill. See the [Skill workflow](#when-a-warning-chip-appears--skill-workflow) section below for the full flow.
50
+
47
51
  If postinstall was skipped (you already use a different statusline, etc.), wire it manually:
48
52
 
49
53
  ```json
@@ -58,9 +62,16 @@ If postinstall was skipped (you already use a different statusline, etc.), wire
58
62
 
59
63
  `refreshInterval: 5` keeps the TTL countdown ticking while idle. For Windows PowerShell see `examples/statusline-command.ps1`.
60
64
 
61
- ## Skill โ€” mention a chip, it activates
65
+ ## When a warning chip appears โ€” Skill workflow
66
+
67
+ The Claude Code Skill registered at install time bridges "warning chip โ†’ remediation":
68
+
69
+ 1. **A risk chip appears in the statusline** โ€” e.g. `๐Ÿšจ 5H 94%`, `โš  Cache miss`, `โš  1M ON`.
70
+ 2. **Run `/claude-token-saver`** โ€” invoking the Skill via slash is the simplest path. Mentioning the chip wording to Claude ("5H cap is up", "cache miss showing", "why is 1M context on?") auto-activates the same Skill.
71
+ 3. **The Skill fetches the remediation.** Internally it runs `claude-token-saver last` to surface the most recent warning + root-cause code + step-by-step fix, and recommends `claude-token-saver handoff` when a cap is imminent.
72
+ 4. **Run manually any time.** `claude-token-saver last` (latest event), `claude-token-saver history` (last 7 days of transitions), `claude-token-saver handoff` (back up before a cap blocks you) โ€” same information, on demand.
62
73
 
63
- The Claude Code Skill registered at install time auto-activates whenever you mention chip wording in chat ("cache hit rate", "1M context", "5H cap"). It runs `claude-token-saver last` to surface the most recent warning + remediation, then points you at `history` / `handoff` for follow-up. (v2.6.0 folded the legacy `/token-monitor` slash command into this Skill โ€” older installs are cleaned up automatically the next time you run `claude-token-saver install`.)
74
+ > v2.6.0 folded the legacy `/token-monitor` slash command into this Skill. On older installs, run `claude-token-saver install` once and the legacy file is cleaned up automatically.
64
75
 
65
76
  ## One-shot report
66
77
 
@@ -90,6 +101,8 @@ claude-token-saver mode # show current settings
90
101
 
91
102
  ## Commands
92
103
 
104
+ All of the commands below run in your **shell (terminal)**. Inside a Claude Code session, the only entry point is the `/claude-token-saver` Skill, which calls these commands for you. The `--statusline` form is invoked automatically by Claude Code on each statusline refresh โ€” you never type it yourself.
105
+
93
106
  | Command | What it does |
94
107
  |---|---|
95
108
  | `claude-token-saver` | Last-1-day diagnostic report (`--days N` to change window) |
@@ -178,6 +191,14 @@ Node.js โ‰ฅ 18 ยท macOS / Windows / Linux / WSL ยท zero dependencies.
178
191
 
179
192
  ## Release notes
180
193
 
194
+ ### v2.9.2 (2026-04-27)
195
+ - `last` / `history` empty-state messages now respect the language setting too. Previously they were hard-coded English, so users on `mode ko` still saw English when there were no warnings to report.
196
+
197
+ ### v2.9.1 (2026-04-27)
198
+ - Fix the README statusline sample so it matches actual output (includes the `โœฆ current` / `๐Ÿ“… weekly` window segments that were missing).
199
+ - Add a 4-step "When a warning chip appears" Skill workflow โ€” spot the chip โ†’ mention its wording to Claude โ†’ Skill runs `last` โ†’ apply remediation.
200
+ - Move `language` from `cfg.statusline.language` to top-level `cfg.language` (it doesn't belong with statusline toggles). The legacy location is still read as a fallback so existing configs migrate transparently. `mode` output also splits the statusline section from the output-language section.
201
+
181
202
  ### v2.9.0 (2026-04-27)
182
203
  - **Output language is now configurable.** `last` / `history` / advice render in a single language at a time. English by default; switch with `claude-token-saver mode ko`. Statusline chips remain symbolic.
183
204
  - History files stay bilingual on disk; the language toggle is applied at display time.
package/README.md CHANGED
@@ -33,16 +33,20 @@ claude-token-saver install
33
33
  ์„ค์น˜ ํ›„ Claude Code ํ•˜๋‹จ statusline์— ์บ์‹œ ์ƒํƒœ๊ฐ€ 5์ดˆ๋งˆ๋‹ค ๊ฐฑ์‹ ๋ฉ๋‹ˆ๋‹ค (postinstall์ด `~/.claude/settings.json`์— ์ž๋™ ๋“ฑ๋ก).
34
34
 
35
35
  ```
36
- ๐Ÿค– Opus 4.7 ยท ๐Ÿง  Cache hit 98.0% ยท โณ Cache expires 59:51 ยท ๐Ÿ“ฆ Ctx 200k ยท ๐Ÿ’ฐ Cache saved $221 ยท last 1d
36
+ ๐Ÿค– Opus 4.7 ยท ๐Ÿง  Cache hit 98.0% ยท โณ Cache expires 58:38 ยท โœฆ current โ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘ 15% ๐Ÿ”„ 08:50 ยท ๐Ÿ“… weekly โ–ˆโ–’โ–‘โ–‘โ–‘โ–‘ 24% ๐Ÿ”„ Thu 13:00 ยท ๐Ÿ“ฆ Ctx 200k ยท ๐Ÿ’ฐ Cache saved $205 ยท last 1d
37
37
  ```
38
38
 
39
- ์œ„ํ—˜ ์ƒํ™ฉ์—์„œ๋Š” ๊ฒฝ๊ณ  ์นฉ์ด ๋งจ ์•ž์œผ๋กœ ๋‚˜์˜ต๋‹ˆ๋‹ค.
39
+ ์„ธ๊ทธ๋จผํŠธ โ€” `๐Ÿค– ๋ชจ๋ธ` ยท `๐Ÿง  ์บ์‹œ ํžˆํŠธ์œจ` ยท `โณ TTL ์นด์šดํŠธ๋‹ค์šด` ยท `โœฆ current` (5์‹œ๊ฐ„ ์œˆ๋„) ยท `๐Ÿ“… weekly` (7์ผ ์œˆ๋„) ยท `๐Ÿ“ฆ ์ปจํ…์ŠคํŠธ` ยท `๐Ÿ’ฐ ๋ˆ„์  ์ ˆ๊ฐ์•ก` ยท `last <๊ธฐ๊ฐ„>`.
40
+
41
+ ํ† ํฐ์ด ๊ณผ๋„ํ•˜๊ฒŒ ์‚ฌ์šฉ๋˜๋Š” ์ƒํ™ฉ์ด ๊ฐ์ง€๋˜๋ฉด ๊ฒฝ๊ณ  ์นฉ์ด ๋งจ ์•ž์— ๋…ธ์ถœ๋ฉ๋‹ˆ๋‹ค.
40
42
 
41
43
  ```
42
- ๐Ÿšจ 5H 94% (resets in 12m) ยท ๐Ÿค– Opus 4.7 ยท ๐Ÿง  Cache hit 72.1% ยท โš  Cache miss ยท ๐Ÿ“ฆ Ctx 200k ยท last 1d
44
+ ๐Ÿšจ 5H 94% (resets in 12m) ยท ๐Ÿค– Opus 4.7 ยท ๐Ÿง  Cache hit 72.1% ยท โš  Cache miss ยท โœฆ current โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ 94% ยท ๐Ÿ“ฆ Ctx 200k ยท last 1d
43
45
  ```
44
46
 
45
- ํ‘œ์‹œ๋˜๋Š” ๊ฒฝ๊ณ  ์นฉ โ€” `๐Ÿšจ 5H/7D NN%`, `โš  1M ON`, `โš  Input spike`, `โš  Cache miss`, `โš  5m TTL`, `โš  Rebuild churn`, `โš  Output heavy`, `โš  Call surge`.
47
+ ๊ฒฝ๊ณ  ์นฉ ์ข…๋ฅ˜ โ€” `๐Ÿšจ 5H/7D NN%`, `โš  1M ON`, `โš  Input spike`, `โš  Cache miss`, `โš  5m TTL`, `โš  Rebuild churn`, `โš  Output heavy`, `โš  Call surge`.
48
+
49
+ **ํ•ด์•ผ ํ•  ์ผ** โ€” Claude์—์„œ `/claude-token-saver` Skill์„ ์‹คํ–‰ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. Skill์ด `claude-token-saver last`๋ฅผ ํ˜ธ์ถœํ•ด ์›์ธ ์ฝ”๋“œ์™€ ๋‹จ๊ณ„๋ณ„ ํ•ด๊ฒฐ ๋ช…๋ น์„ ์ž๋™์œผ๋กœ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์นฉ ๋ฌธ๊ตฌ(์˜ˆ: "5H cap ๋–ด์–ด", "cache miss")๋งŒ ๋งํ•ด๋„ ๋™์ผํ•œ Skill์ด ์ž๋™ ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ํ๋ฆ„์€ ์•„๋ž˜ [Skill ์›Œํฌํ”Œ๋กœ](#๊ฒฝ๊ณ -์นฉ์ด-๋–ด์„-๋•Œ--skill-์›Œํฌํ”Œ๋กœ) ์ฐธ๊ณ .
46
50
 
47
51
  ์ˆ˜๋™ ๋“ฑ๋ก์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ(๋‹ค๋ฅธ statusline์„ ์ด๋ฏธ ์“ฐ๊ณ  ์žˆ์–ด postinstall์ด ๊ฑด๋„ˆ๋›ด ๊ฒฝ์šฐ ๋“ฑ):
48
52
 
@@ -58,9 +62,16 @@ claude-token-saver install
58
62
 
59
63
  `refreshInterval: 5`๋Š” idle ์ƒํƒœ์—์„œ๋„ TTL ์นด์šดํŠธ๋‹ค์šด์„ 5์ดˆ๋งˆ๋‹ค ๊ฐฑ์‹ ํ•ฉ๋‹ˆ๋‹ค. Windows(PowerShell)๋Š” `examples/statusline-command.ps1` ์ฐธ๊ณ .
60
64
 
61
- ## Skill โ€” ์นฉ์„ ์–ธ๊ธ‰ํ•˜๋ฉด ์ž๋™ ํ™œ์„ฑํ™”
65
+ ## ๊ฒฝ๊ณ  ์นฉ์ด ๋–ด์„ ๋•Œ โ€” Skill ์›Œํฌํ”Œ๋กœ
66
+
67
+ ์„ค์น˜ ์‹œ ํ•จ๊ป˜ ๋“ฑ๋ก๋˜๋Š” Claude Code Skill์ด "๊ฒฝ๊ณ  ์นฉ โ†’ ์ฒ˜๋ฐฉ"์˜ ๋‹ค๋ฆฌ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
68
+
69
+ 1. **statusline์— ๊ฒฝ๊ณ  ์นฉ์ด ๋œฌ๋‹ค** โ€” ์˜ˆ: `๐Ÿšจ 5H 94%`, `โš  Cache miss`, `โš  1M ON`.
70
+ 2. **`/claude-token-saver` Skill์„ ์‹คํ–‰ํ•œ๋‹ค** โ€” Claude์—์„œ ์Šฌ๋ž˜์‹œ๋กœ Skill์„ ์ง์ ‘ ํ˜ธ์ถœํ•˜๋Š” ๊ฒŒ ๊ฐ€์žฅ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ๋˜๋Š” ์นฉ ๋ฌธ๊ตฌ๋ฅผ ๊ทธ๋Œ€๋กœ ๋งํ•ด๋„ ๋™์ผํ•œ Skill์ด ์ž๋™ ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค ("5H cap ๋–ด์–ด", "cache miss ๋–ด์–ด", "1M context ์™œ ์ผœ์กŒ์ง€?" ๋“ฑ โ€” ์นฉ ํ…์ŠคํŠธ๊ฐ€ ํŠธ๋ฆฌ๊ฑฐ ๋‹จ์–ด๋กœ ๋“ฑ๋ก๋ผ ์žˆ์Œ).
71
+ 3. **Skill์ด ์ฒ˜๋ฐฉ์„ ๊ฐ€์ ธ์˜จ๋‹ค** โ€” ๋‚ด๋ถ€์ ์œผ๋กœ `claude-token-saver last`๋ฅผ ์‹คํ–‰ํ•ด ๊ฐ€์žฅ ์ตœ๊ทผ ๊ฒฝ๊ณ  + ์›์ธ ์ฝ”๋“œ + ๋‹จ๊ณ„๋ณ„ ํ•ด๊ฒฐ ๋ช…๋ น์„ ํ•œ ๋ฒˆ์— ๋ณด์—ฌ์ฃผ๊ณ , ์บก ์ž„๋ฐ• ์‹œ์—๋Š” `claude-token-saver handoff`๋กœ ํ˜„์žฌ ์ž‘์—… ๋ฐฑ์—…์„ ๊ถŒํ•ฉ๋‹ˆ๋‹ค.
72
+ 4. **์ˆ˜๋™ ํ™•์ธ์ด ํ•„์š”ํ•˜๋ฉด** โ€” `claude-token-saver last` (์ตœ๊ทผ 1๊ฑด), `claude-token-saver history` (์ตœ๊ทผ 7์ผ ์ „์ด ๋กœ๊ทธ), `claude-token-saver handoff` (cap ์ง์ „ ๋ฐฑ์—…)๋ฅผ ์ง์ ‘ ์‹คํ–‰ํ•ด๋„ ๊ฐ™์€ ์ •๋ณด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
62
73
 
63
- ์„ค์น˜ ์‹œ ํ•จ๊ป˜ ๋“ฑ๋ก๋˜๋Š” Claude Code Skill ๋•๋ถ„์— ๋Œ€ํ™” ์ค‘ "cache hit rate", "1M context", "5H cap" ๊ฐ™์€ ์นฉ ๋‹จ์–ด๋ฅผ ์–ธ๊ธ‰ํ•˜๋ฉด Claude๊ฐ€ ์ž๋™์œผ๋กœ `claude-token-saver last`๋ฅผ ์‹คํ–‰ํ•ด ๊ฐ€์žฅ ์ตœ๊ทผ ๊ฒฝ๊ณ ์™€ ์ฒ˜๋ฐฉ์„ ๋ณด์—ฌ์ฃผ๊ณ , ํ•„์š” ์‹œ `history` / `handoff`๋กœ ํ›„์† ์กฐ์น˜๋ฅผ ์•ˆ๋‚ดํ•ฉ๋‹ˆ๋‹ค. (v2.6.0์—์„œ ๋ ˆ๊ฑฐ์‹œ `/token-monitor` ์Šฌ๋ž˜์‹œ ์ปค๋งจ๋“œ๋Š” Skill๋กœ ํก์ˆ˜๋์Šต๋‹ˆ๋‹ค โ€” ์ด์ „ ๋ฒ„์ „ ์‚ฌ์šฉ์ž๋Š” `claude-token-saver install`์„ ํ•œ ๋ฒˆ ๋‹ค์‹œ ์‹คํ–‰ํ•˜๋ฉด ์ž๋™ ์ •๋ฆฌ๋ฉ๋‹ˆ๋‹ค.)
74
+ > v2.6.0์—์„œ ๋ ˆ๊ฑฐ์‹œ `/token-monitor` ์Šฌ๋ž˜์‹œ ์ปค๋งจ๋“œ๋Š” ์ด Skill๋กœ ํก์ˆ˜๋์Šต๋‹ˆ๋‹ค. ์ด์ „ ๋ฒ„์ „ ์‚ฌ์šฉ์ž๋Š” `claude-token-saver install`์„ ํ•œ ๋ฒˆ ๋” ์‹คํ–‰ํ•˜๋ฉด ์ž๋™ ์ •๋ฆฌ๋ฉ๋‹ˆ๋‹ค.
64
75
 
65
76
  ## ๋‹จ๋ฐœ ๋ฆฌํฌํŠธ
66
77
 
@@ -90,6 +101,8 @@ claude-token-saver mode # ํ˜„์žฌ ์„ค์ • ํ™•์ธ
90
101
 
91
102
  ## ์ฃผ์š” ๋ช…๋ น
92
103
 
104
+ ์•„๋ž˜ ๋ช…๋ น์€ ๋ชจ๋‘ **์…ธ(ํ„ฐ๋ฏธ๋„)์—์„œ ์ง์ ‘ ์‹คํ–‰**ํ•ฉ๋‹ˆ๋‹ค. Claude Code ์„ธ์…˜ ์•ˆ์—์„œ๋Š” `/claude-token-saver` Skill ํ•˜๋‚˜๋งŒ ์“ฐ๋ฉฐ, Skill์ด ๋‚ด๋ถ€์ ์œผ๋กœ ์ด ๋ช…๋ น๋“ค์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. `--statusline` ํ˜•์‹์€ Claude Code๊ฐ€ statusline ๊ฐฑ์‹ ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ํ˜ธ์ถœํ•˜๋ฏ€๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ์ž…๋ ฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
105
+
93
106
  | ๋ช…๋ น | ์„ค๋ช… |
94
107
  |---|---|
95
108
  | `claude-token-saver` | ์ตœ๊ทผ 1์ผ ์ง„๋‹จ ๋ฆฌํฌํŠธ (`--days N`๋กœ ๊ธฐ๊ฐ„ ๋ณ€๊ฒฝ) |
@@ -138,6 +151,14 @@ Node.js โ‰ฅ 18 ยท macOS / Linux / Windows / WSL ยท ์˜์กด์„ฑ 0.
138
151
 
139
152
  ## ๋ฆด๋ฆฌ์Šค ๋…ธํŠธ
140
153
 
154
+ ### v2.9.2 (2026-04-27)
155
+ - `last`/`history`๊ฐ€ ๊ฒฝ๊ณ  ์—†์„ ๋•Œ ์ถœ๋ ฅํ•˜๋Š” ์•ˆ๋‚ด ๋ฌธ๊ตฌ๋„ ์–ธ์–ด ์„ค์ •์„ ๋”ฐ๋ฅด๋„๋ก ์ˆ˜์ • (์ด์ „์—” ํ•ญ์ƒ ์˜๋ฌธ ์ถœ๋ ฅ โ†’ ํ•œ๊ตญ์–ด ๋ชจ๋“œ์ธ๋ฐ๋„ ์˜๋ฌธ์ด ๋ณด์ด๋Š” ๋ฒ„๊ทธ).
156
+
157
+ ### v2.9.1 (2026-04-27)
158
+ - README์˜ statusline ์˜ˆ์‹œ๋ฅผ ์‹ค์ œ ์ถœ๋ ฅ(`โœฆ current` / `๐Ÿ“… weekly` ์œˆ๋„ ์„ธ๊ทธ๋จผํŠธ ํฌํ•จ)์œผ๋กœ ์ •์ •.
159
+ - "๊ฒฝ๊ณ  ์นฉ์ด ๋–ด์„ ๋•Œ" Skill ์›Œํฌํ”Œ๋กœ 4๋‹จ๊ณ„ ๊ฐ€์ด๋“œ ์ถ”๊ฐ€ โ€” ์นฉ ๋ฐœ๊ฒฌ โ†’ Claude์—๊ฒŒ ์นฉ ๋ฌธ๊ตฌ ๊ทธ๋Œ€๋กœ ๋งํ•˜๊ธฐ โ†’ Skill์ด `last` ์‹คํ–‰ โ†’ ์ฒ˜๋ฐฉ ์ ์šฉ.
160
+ - `language` ์„ค์ • ์œ„์น˜๋ฅผ `cfg.statusline` ํ•˜์œ„์—์„œ top-level `cfg.language`๋กœ ์ด๋™(statusline ํ† ๊ธ€์ด ์•„๋‹ˆ๋ฏ€๋กœ). ๊ตฌ๋ฒ„์ „ ์œ„์น˜๋„ fallback์œผ๋กœ ๊ณ„์† ์ฝ์–ด ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์€ ์ž๋™. `mode` ์ถœ๋ ฅ๋„ statusline / output language๋ฅผ ๋ถ„๋ฆฌํ•ด์„œ ํ‘œ์‹œ.
161
+
141
162
  ### v2.9.0 (2026-04-27)
142
163
  - **์ถœ๋ ฅ ์–ธ์–ด ์ „ํ™˜ ์ถ”๊ฐ€** โ€” `last` / `history` / ์ฒ˜๋ฐฉ ๋ฉ”์‹œ์ง€๊ฐ€ ํ•œ ๋ฒˆ์— ํ•œ ์–ธ์–ด๋งŒ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์€ ์˜์–ด, `claude-token-saver mode ko`๋กœ ํ•œ๊ตญ์–ด ์ „ํ™˜ (statusline ์นฉ์€ ์˜ํ–ฅ ์—†์Œ).
143
164
  - ๊ธฐ์กด history ํŒŒ์ผ์€ ์ด์ค‘์–ธ์–ด๋กœ ๋ณด๊ด€๋˜๋ฉฐ, ํ‘œ์‹œํ•  ๋•Œ ์„ ํƒํ•œ ์–ธ์–ด๋งŒ ํ•„ํ„ฐ๋ง๋ฉ๋‹ˆ๋‹ค.
package/bin/cli.js CHANGED
@@ -236,8 +236,13 @@ async function main() {
236
236
  const recent = readRecent(days);
237
237
  const latest = findLatestWarning(recent, CHIP_TO_CODES);
238
238
  if (!latest) {
239
- console.log(`No warnings in the last ${days} day${days === 1 ? '' : 's'}.`);
240
- console.log(`(History dir: ${historyDir()})`);
239
+ if (lang === 'ko') {
240
+ console.log(`์ตœ๊ทผ ${days}์ผ ๋‚ด ๊ฒฝ๊ณ ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.`);
241
+ console.log(`(ํžˆ์Šคํ† ๋ฆฌ ๋””๋ ‰ํ„ฐ๋ฆฌ: ${historyDir()})`);
242
+ } else {
243
+ console.log(`No warnings in the last ${days} day${days === 1 ? '' : 's'}.`);
244
+ console.log(`(History dir: ${historyDir()})`);
245
+ }
241
246
  return;
242
247
  }
243
248
  // Header
@@ -303,18 +308,25 @@ async function main() {
303
308
  if (hasFlag('--list')) {
304
309
  const dates = listDates();
305
310
  if (dates.length === 0) {
306
- console.log(`No history yet. Files will appear under: ${historyDir()}`);
311
+ console.log(lang === 'ko'
312
+ ? `ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ์•„์ง ์—†์Šต๋‹ˆ๋‹ค. ํŒŒ์ผ์€ ๋‹ค์Œ ์œ„์น˜์— ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค: ${historyDir()}`
313
+ : `No history yet. Files will appear under: ${historyDir()}`);
307
314
  return;
308
315
  }
309
- console.log(`History (${historyDir()}):`);
316
+ console.log(lang === 'ko' ? `ํžˆ์Šคํ† ๋ฆฌ (${historyDir()}):` : `History (${historyDir()}):`);
310
317
  for (const d of dates) console.log(` ${d}`);
311
318
  return;
312
319
  }
313
320
  const days = parseFloat(getArg('--days') || '7');
314
321
  const recent = readRecent(days);
315
322
  if (recent.length === 0) {
316
- console.log(`No warning history in the last ${days} day${days === 1 ? '' : 's'}.`);
317
- console.log(`(Files would be written to: ${historyDir()})`);
323
+ if (lang === 'ko') {
324
+ console.log(`์ตœ๊ทผ ${days}์ผ ๋‚ด ๊ฒฝ๊ณ  ํžˆ์Šคํ† ๋ฆฌ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.`);
325
+ console.log(`(ํŒŒ์ผ์ด ์ƒ์„ฑ๋  ์œ„์น˜: ${historyDir()})`);
326
+ } else {
327
+ console.log(`No warning history in the last ${days} day${days === 1 ? '' : 's'}.`);
328
+ console.log(`(Files would be written to: ${historyDir()})`);
329
+ }
318
330
  return;
319
331
  }
320
332
  for (const { content } of recent) {
@@ -396,21 +408,23 @@ async function main() {
396
408
  // claude-token-saver mode icon verbose # set icon + verbose
397
409
  // claude-token-saver mode reset # clear back to defaults
398
410
  if (args[0] === 'mode') {
399
- const { applyMode, loadConfig, configPath, statuslineDefaults, VALID_KEYWORDS } =
411
+ const { applyMode, loadConfig, configPath, statuslineDefaults, userLanguage, VALID_KEYWORDS } =
400
412
  await import('../src/config.js');
401
413
  const words = args.slice(1);
402
414
  if (words.length === 0) {
403
415
  const eff = statuslineDefaults();
404
- const raw = loadConfig().statusline || {};
405
- console.log('Statusline mode (effective):');
406
- console.log(` icon: ${eff.icon}`);
407
- console.log(` verbose: ${eff.verbose}`);
408
- console.log(` timer: ${eff.timer}`);
409
- console.log(` color: ${eff.color}`);
410
- console.log(` language: ${eff.language} (advice / history / last)`);
411
- console.log(` window: ${eff.windowLabel} (${eff.windowHours}h)`);
416
+ const raw = loadConfig();
417
+ console.log('Statusline (effective):');
418
+ console.log(` icon: ${eff.icon}`);
419
+ console.log(` verbose: ${eff.verbose}`);
420
+ console.log(` timer: ${eff.timer}`);
421
+ console.log(` color: ${eff.color}`);
422
+ console.log(` window: ${eff.windowLabel} (${eff.windowHours}h)`);
423
+ console.log('');
424
+ console.log('Output language (advice / history / last):');
425
+ console.log(` language: ${userLanguage()}`);
412
426
  console.log('');
413
- console.log(`Stored config (${configPath()}):`);
427
+ console.log(`Stored config file (${configPath()}):`);
414
428
  console.log(` ${Object.keys(raw).length === 0 ? '(none โ€” using defaults)' : JSON.stringify(raw)}`);
415
429
  console.log('');
416
430
  console.log('Change with: claude-token-saver mode <keywords...>');
@@ -425,7 +439,7 @@ async function main() {
425
439
  }
426
440
  const eff = statuslineDefaults();
427
441
  console.log(`Updated: ${applied.join(', ')}`);
428
- console.log(`Now: icon=${eff.icon} verbose=${eff.verbose} timer=${eff.timer} color=${eff.color} language=${eff.language} window=${eff.windowLabel}`);
442
+ console.log(`Now: icon=${eff.icon} verbose=${eff.verbose} timer=${eff.timer} color=${eff.color} window=${eff.windowLabel} language=${userLanguage()}`);
429
443
  console.log('Statusline picks up the change on the next refresh (~1s).');
430
444
  return;
431
445
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-token-saver",
3
- "version": "2.9.0",
3
+ "version": "2.9.2",
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": {
package/src/config.js CHANGED
@@ -36,6 +36,7 @@ export function saveConfig(cfg) {
36
36
 
37
37
  // Each keyword maps to a single statusline option toggle.
38
38
  // Orthogonal โ€” `mode icon verbose` flips both without resetting the rest.
39
+ // Statusline-only toggles. Stored under `cfg.statusline`.
39
40
  const KEYWORDS = {
40
41
  icon: { key: 'icon', value: true },
41
42
  text: { key: 'icon', value: false },
@@ -45,12 +46,16 @@ const KEYWORDS = {
45
46
  'no-timer': { key: 'timer', value: false },
46
47
  color: { key: 'color', value: true },
47
48
  'no-color': { key: 'color', value: false },
48
- // Language for advice / history / `last` output. Default is `en`.
49
- // Statusline chips themselves stay symbolic (e.g. `๐Ÿšจ 5H 94%`) regardless.
50
- en: { key: 'language', value: 'en' },
51
- english: { key: 'language', value: 'en' },
52
- ko: { key: 'language', value: 'ko' },
53
- korean: { key: 'language', value: 'ko' },
49
+ };
50
+
51
+ // Output language for `last`/`history`/advice. Stored at top level
52
+ // (`cfg.language`) because it has nothing to do with statusline rendering โ€”
53
+ // the chips themselves stay symbolic regardless.
54
+ const LANG_KEYWORDS = {
55
+ en: 'en',
56
+ english: 'en',
57
+ ko: 'ko',
58
+ korean: 'ko',
54
59
  };
55
60
 
56
61
  // Window preset accepts forms like:
@@ -71,13 +76,15 @@ function parseWindow(word) {
71
76
  return null;
72
77
  }
73
78
 
74
- export const VALID_KEYWORDS = Object.keys(KEYWORDS).concat([
75
- '<N>h (e.g. 1h, 6h, 24h)',
76
- '<N>d (e.g. 1d, 7d, 30d)',
77
- 'lang=en | lang=ko',
78
- 'reset',
79
- 'default',
80
- ]);
79
+ export const VALID_KEYWORDS = Object.keys(KEYWORDS)
80
+ .concat(Object.keys(LANG_KEYWORDS))
81
+ .concat([
82
+ '<N>h (e.g. 1h, 6h, 24h)',
83
+ '<N>d (e.g. 1d, 7d, 30d)',
84
+ 'lang=en | lang=ko',
85
+ 'reset',
86
+ 'default',
87
+ ]);
81
88
 
82
89
  /**
83
90
  * Apply user-supplied mode keywords to the persisted config.
@@ -94,16 +101,25 @@ export function applyMode(words) {
94
101
  const lower = String(w).toLowerCase();
95
102
  if (lower === 'reset' || lower === 'default') {
96
103
  cfg.statusline = {};
104
+ delete cfg.language;
97
105
  applied.push(lower);
98
106
  continue;
99
107
  }
100
108
  const langMatch = lower.match(/^lang(?:uage)?=(en|english|ko|korean)$/);
101
109
  if (langMatch) {
102
110
  const v = langMatch[1].startsWith('k') ? 'ko' : 'en';
103
- cfg.statusline.language = v;
111
+ cfg.language = v;
112
+ // Migrate any legacy value that lived under cfg.statusline.language.
113
+ if (cfg.statusline) delete cfg.statusline.language;
104
114
  applied.push(`lang=${v}`);
105
115
  continue;
106
116
  }
117
+ if (LANG_KEYWORDS[lower]) {
118
+ cfg.language = LANG_KEYWORDS[lower];
119
+ if (cfg.statusline) delete cfg.statusline.language;
120
+ applied.push(`lang=${cfg.language}`);
121
+ continue;
122
+ }
107
123
  const hours = parseWindow(lower);
108
124
  if (hours !== null && hours > 0) {
109
125
  cfg.statusline.windowHours = hours;
@@ -150,7 +166,6 @@ export function statuslineDefaults() {
150
166
  verbose: s.verbose !== false,
151
167
  timer: s.timer !== false,
152
168
  color: s.color !== false,
153
- language: s.language === 'ko' ? 'ko' : 'en',
154
169
  windowHours,
155
170
  windowLabel: formatWindow(windowHours),
156
171
  };
@@ -158,9 +173,13 @@ export function statuslineDefaults() {
158
173
 
159
174
  // Resolve the user's preferred output language for advice/history/last.
160
175
  // Returns 'en' or 'ko'; defaults to 'en' for first-time users.
176
+ // Reads `cfg.language` (current location) with a fallback to the legacy
177
+ // `cfg.statusline.language` slot so configs from earlier 2.9.x installs
178
+ // keep working.
161
179
  export function userLanguage() {
162
- const s = loadConfig().statusline || {};
163
- return s.language === 'ko' ? 'ko' : 'en';
180
+ const cfg = loadConfig();
181
+ const v = cfg.language || (cfg.statusline && cfg.statusline.language);
182
+ return v === 'ko' ? 'ko' : 'en';
164
183
  }
165
184
 
166
185
  /**