claude-token-saver 2.17.0 โ†’ 2.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -55,7 +55,7 @@ The statusline appears at the bottom of Claude Code right away. If auto-registra
55
55
  | `๐Ÿง ` | Cache hit rate (green at 85%+) |
56
56
  | `โณ` | Cache TTL countdown โ€” send a message before expiry to keep the cache warm |
57
57
  | `โœฆ current` / `๐Ÿ“… weekly` | 5-hour / 7-day rate-limit window usage + reset time |
58
- | `๐Ÿ“ฆ` | Context window (1M shows red โ€” a major cost driver) |
58
+ | `๐Ÿ“ฆ` | 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 |
59
59
  | `๐Ÿ’ฐ` | Cumulative savings from prompt caching |
60
60
 
61
61
  When something is wrong, a **warning chip leads the line**:
@@ -64,7 +64,7 @@ When something is wrong, a **warning chip leads the line**:
64
64
  ๐Ÿšจ 5H โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–“ 94% ๐Ÿ”„ 12:36 ยท ๐Ÿ…ท 5/5 ยท ๐Ÿค– Opus 4.8 ยท ๐Ÿง  Cache hit 72.1% ยท โš  Cache miss ยท ๐Ÿ“… weekly โ–“โ–‘โ–‘โ–‘โ–‘โ–‘ 12% ๐Ÿ”„ Sun 14:26 ยท ๐Ÿ“ฆ Ctx 200k ยท last 1d
65
65
  ```
66
66
 
67
- Chips โ€” `๐Ÿšจ 5H/7D NN%` (cap imminent) ยท `โš  1M ON` ยท `โš  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+).
67
+ Chips โ€” `๐Ÿšจ 5H/7D NN%` (cap imminent) ยท `โš  Ctx 200k+` (a single request actually exceeded 200k) ยท `โš  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+).
68
68
 
69
69
  ### When a chip appears
70
70
 
@@ -137,7 +137,7 @@ An auto `.bak` is kept, but **the session context that earned the rule its place
137
137
 
138
138
  | Code | Meaning |
139
139
  |---|---|
140
- | `LARGE_INPUT_PER_REQUEST` | single request > 250k tokens โ†’ 1M context likely |
140
+ | `LARGE_INPUT_PER_REQUEST` | single request > 200k input tokens โ€” per-turn re-billing and cap burn spike |
141
141
  | `LOW_HIT_RATE` | cache hit rate < 50% |
142
142
  | `BUCKET_5M_DOMINANT` | > 70% of cache writes hit the 5m bucket |
143
143
  | `HIGH_OUTPUT_RATIO` | output/input > 0.15 (output is 5ร— input price) |
@@ -213,6 +213,11 @@ Also update `statusLine.command` in `~/.claude/settings.json` to `claude-token-s
213
213
 
214
214
  ## Release notes
215
215
 
216
+ ### v2.18.0 (2026-07-02)
217
+ - **1M-context warning re-scoped** โ€” current models (Fable 5, Opus 4.6โ€“4.8, Sonnet 5) all default to a 1M window with no long-context premium since Opus 4.7, so the "1M mode ON = expensive" framing is retired. The warning is now a **usage signal**: `โš  1M ON` โ†’ `โš  Ctx 200k+` (a single request actually exceeded 200k), and remediation is reordered to lead with `/compact`/`/clear` + `/effort` instead of "disable 1M". The incorrect "long-context pricing kicks in past 200k" copy is fixed.
218
+ - **๐Ÿ“ฆ segment shows live usage** โ€” reads `context_window.used_percentage` from Claude Code's stdin and renders `๐Ÿ“ฆ Ctx 68% of 1M`, colored by fill (green <70 / yellow 70โ€“89 / red 90+). Falls back to transcript-inferred size when stdin is absent (1M now yellow, not red).
219
+ - Back-compat: the legacy `โš  1M ON` chip and old detail strings in existing history files still resolve.
220
+
216
221
  ### v2.17.0 (2026-07-02)
217
222
  - **Fable 5 pricing tier** โ€” `claude-fable-5`/`claude-mythos-5` previously fell through to the Sonnet tier ($3/$15), under-estimating costs ~3ร—. Now priced at the real rates ($10 input / $50 output / $12.50 5m-write / $20 1h-write / $1 read).
218
223
  - README overhaul โ€” top-level impact summary, segment table, harness scope-flag docs, pricing table brought current.
package/README.md CHANGED
@@ -55,7 +55,7 @@ npm i -g claude-token-saver
55
55
  | `๐Ÿง ` | ์บ์‹œ ํžˆํŠธ์œจ (85%+ ๋…น์ƒ‰) |
56
56
  | `โณ` | ์บ์‹œ TTL ์นด์šดํŠธ๋‹ค์šด โ€” ๋งŒ๋ฃŒ ์ „์— ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ด๋ฉด ์บ์‹œ ์œ ์ง€ |
57
57
  | `โœฆ current` / `๐Ÿ“… weekly` | 5์‹œ๊ฐ„ / 7์ผ rate-limit ์œˆ๋„ ์‚ฌ์šฉ๋ฅ  + ๋ฆฌ์…‹ ์‹œ๊ฐ |
58
- | `๐Ÿ“ฆ` | ์ปจํ…์ŠคํŠธ ์œˆ๋„ (1M์ด๋ฉด ๋นจ๊ฐ„ ๊ฒฝ๊ณ  โ€” ๋น„์šฉ ๊ธ‰์ฆ ์š”์ธ) |
58
+ | `๐Ÿ“ฆ` | ์ปจํ…์ŠคํŠธ ์‚ฌ์šฉ๋ฅ  (์˜ˆ: `Ctx 68% of 1M`) โ€” ์‚ฌ์šฉ๋ฅ  ๊ธฐ์ค€ ๋…น/ํ™ฉ/์ . ํ˜„์žฌ ๋ชจ๋ธ์€ 1M์ด ๊ธฐ๋ณธยทํ”„๋ฆฌ๋ฏธ์—„ ์—†์Œ์ด์ง€๋งŒ, ํ† ํฐ๋Ÿ‰ ์ž์ฒด๊ฐ€ ํ„ด๋‹น ๋น„์šฉ๊ณผ 5H/7D ํ•œ๋„๋ฅผ ํƒœ์›๋‹ˆ๋‹ค |
59
59
  | `๐Ÿ’ฐ` | ์บ์‹œ๊ฐ€ ์ ˆ์•ฝํ•ด์ค€ ๋ˆ„์  ๊ธˆ์•ก |
60
60
 
61
61
  ๋ฌธ์ œ๊ฐ€ ๊ฐ์ง€๋˜๋ฉด **๊ฒฝ๊ณ  ์นฉ์ด ๋งจ ์•ž์—** ๋ถ™์Šต๋‹ˆ๋‹ค:
@@ -64,7 +64,7 @@ npm i -g claude-token-saver
64
64
  ๐Ÿšจ 5H โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–“ 94% ๐Ÿ”„ 12:36 ยท ๐Ÿ…ท 5/5 ยท ๐Ÿค– Opus 4.8 ยท ๐Ÿง  Cache hit 72.1% ยท โš  Cache miss ยท ๐Ÿ“… weekly โ–“โ–‘โ–‘โ–‘โ–‘โ–‘ 12% ๐Ÿ”„ Sun 14:26 ยท ๐Ÿ“ฆ Ctx 200k ยท last 1d
65
65
  ```
66
66
 
67
- ์นฉ ์ข…๋ฅ˜ โ€” `๐Ÿšจ 5H/7D NN%`(์บก ์ž„๋ฐ•) ยท `โš  1M ON` ยท `โš  Cache miss` ยท `โš  Input spike` ยท `โš  Output heavy` ยท `โš  Call surge` ยท `โš  Rebuild churn` ยท `โš  5m TTL`. ๋‘ ์œˆ๋„๊ฐ€ ๋™์‹œ์— 90%+๋ฉด ๋ฆฌ์…‹์ด ์ž„๋ฐ•ํ•œ ์ชฝ์ด ๐Ÿšจ๋กœ ์Šน๊ฒฉ๋˜๊ณ  ๋‚˜๋จธ์ง€๋Š” ๋นจ๊ฐ„ ์„ธ๊ทธ๋จผํŠธ๋กœ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค (v2.16.0+).
67
+ ์นฉ ์ข…๋ฅ˜ โ€” `๐Ÿšจ 5H/7D NN%`(์บก ์ž„๋ฐ•) ยท `โš  Ctx 200k+`(๋‹จ์ผ ์š”์ฒญ์ด ์‹ค์ œ๋กœ 200k ์ดˆ๊ณผ) ยท `โš  Cache miss` ยท `โš  Input spike` ยท `โš  Output heavy` ยท `โš  Call surge` ยท `โš  Rebuild churn` ยท `โš  5m TTL`. ๋‘ ์œˆ๋„๊ฐ€ ๋™์‹œ์— 90%+๋ฉด ๋ฆฌ์…‹์ด ์ž„๋ฐ•ํ•œ ์ชฝ์ด ๐Ÿšจ๋กœ ์Šน๊ฒฉ๋˜๊ณ  ๋‚˜๋จธ์ง€๋Š” ๋นจ๊ฐ„ ์„ธ๊ทธ๋จผํŠธ๋กœ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค (v2.16.0+).
68
68
 
69
69
  ### ๊ฒฝ๊ณ  ์นฉ์ด ๋–ด์„ ๋•Œ
70
70
 
@@ -118,7 +118,7 @@ ratchet์˜ ๊ฐ€์น˜๋Š” **ํ•œ ๋ฐฉํ–ฅ ๋ˆ„์ **์— ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฃฐ์„ ๊ฐ€๋ณ๊ฒŒ ์ง€
118
118
 
119
119
  | ์ฝ”๋“œ | ์˜๋ฏธ |
120
120
  |---|---|
121
- | `LARGE_INPUT_PER_REQUEST` | ๋‹จ์ผ ์š”์ฒญ 250k+ โ†’ 1M ์ปจํ…์ŠคํŠธ ์˜์‹ฌ |
121
+ | `LARGE_INPUT_PER_REQUEST` | ๋‹จ์ผ ์š”์ฒญ ์ž…๋ ฅ์ด 200k ์ดˆ๊ณผ โ€” ํ„ด๋‹น ์žฌ๊ณผ๊ธˆยทํ•œ๋„ ์†Œ๋ชจ ๊ธ‰์ฆ |
122
122
  | `LOW_HIT_RATE` | ์บ์‹œ ํžˆํŠธ์œจ 50% ๋ฏธ๋งŒ |
123
123
  | `BUCKET_5M_DOMINANT` | ์บ์‹œ ์“ฐ๊ธฐ์˜ 70%+๊ฐ€ 5๋ถ„ ๋ฒ„ํ‚ท (Pro ํ”Œ๋žœ/Max ๋‹ค์šด๊ทธ๋ ˆ์ด๋“œ) |
124
124
  | `HIGH_OUTPUT_RATIO` | ์ถœ๋ ฅ/์ž…๋ ฅ ๋น„์œจ 0.15 ์ดˆ๊ณผ (์ถœ๋ ฅ ๋‹จ๊ฐ€๋Š” ์ž…๋ ฅ์˜ 5๋ฐฐ) |
@@ -170,6 +170,11 @@ npm uninstall -g claude-cache-monitor && npm i -g claude-token-saver
170
170
 
171
171
  ## ๋ฆด๋ฆฌ์Šค ๋…ธํŠธ
172
172
 
173
+ ### v2.18.0 (2026-07-02)
174
+ - **1M ์ปจํ…์ŠคํŠธ ๊ฒฝ๊ณ  ์˜๋ฏธ ์žฌ์ •์˜** โ€” ํ˜„์žฌ ๋ชจ๋ธ(Fable 5, Opus 4.6~4.8, Sonnet 5)์€ ์ „๋ถ€ 1M ์œˆ๋„๊ฐ€ ๊ธฐ๋ณธ์ด๊ณ  Opus 4.7๋ถ€ํ„ฐ ์žฅ๊ธฐ ์ปจํ…์ŠคํŠธ ํ”„๋ฆฌ๋ฏธ์—„๋„ ์—†์–ด, "1M ๋ชจ๋“œ ON = ๋น„์Œˆ" ํ”„๋ ˆ์ž„์„ ํ๊ธฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฒฝ๊ณ ๋Š” ์ด์ œ **์‹ค์‚ฌ์šฉ ์‹ ํ˜ธ**์ž…๋‹ˆ๋‹ค: `โš  1M ON` โ†’ `โš  Ctx 200k+`(๋‹จ์ผ ์š”์ฒญ์ด ์‹ค์ œ๋กœ 200k ์ดˆ๊ณผ), ์ฒ˜๋ฐฉ๋„ "1M ๋„๊ธฐ" ์šฐ์„ ์—์„œ "`/compact`/`/clear` + `/effort` ์ ๊ฒ€" ์šฐ์„ ์œผ๋กœ ์žฌ์ •๋ ฌ. ์ž˜๋ชป๋œ "200k ์ดˆ๊ณผ ์‹œ ์žฅ๊ธฐ ์š”๊ธˆ ์ ์šฉ" ๋ฌธ๊ตฌ ์ •์ •.
175
+ - **๐Ÿ“ฆ ์„ธ๊ทธ๋จผํŠธ๊ฐ€ ์‹ค์‹œ๊ฐ„ ์‚ฌ์šฉ๋ฅ  ํ‘œ์‹œ** โ€” Claude Code stdin์˜ `context_window.used_percentage`๋ฅผ ์‚ฌ์šฉํ•ด `๐Ÿ“ฆ Ctx 68% of 1M` ํ˜•ํƒœ๋กœ ๋ Œ๋” (์‚ฌ์šฉ๋ฅ  ๊ธฐ์ค€ ๋…น <70 / ํ™ฉ 70โ€“89 / ์  90+). stdin์ด ์—†์œผ๋ฉด ๊ธฐ์กด ํฌ๊ธฐ ์ถ”๋ก ์œผ๋กœ ํด๋ฐฑํ•˜๋˜ 1M์€ ๋นจ๊ฐ• ๋Œ€์‹  ๋…ธ๋ž‘.
176
+ - ๊ตฌ๋ฒ„์ „ ํžˆ์Šคํ† ๋ฆฌ ํ˜ธํ™˜: `โš  1M ON` ์นฉยท๊ตฌ ๋””ํ…Œ์ผ ๋ฌธ๊ตฌ๋„ ๊ณ„์† ํ•ด์„๋ฉ๋‹ˆ๋‹ค.
177
+
173
178
  ### v2.17.0 (2026-07-02)
174
179
  - **Fable 5 ๊ฐ€๊ฒฉ ํ‹ฐ์–ด ์ถ”๊ฐ€** โ€” `claude-fable-5`/`claude-mythos-5`๊ฐ€ Sonnet ๋‹จ๊ฐ€($3/$15)๋กœ ํด๋ฐฑ๋ผ ๋น„์šฉ์ด ~3๋ฐฐ ๊ณผ์†Œ ์ถ”์ •๋˜๋˜ ๋ฌธ์ œ ์ˆ˜์ •. ์‹ค์ œ ๋‹จ๊ฐ€(์ž…๋ ฅ $10 / ์ถœ๋ ฅ $50 / ์บ์‹œ์“ฐ๊ธฐ 5m $12.50ยท1h $20 / ์บ์‹œ์ฝ๊ธฐ $1) ์ ์šฉ.
175
180
  - README ์ „๋ฉด ๊ฐœํŽธ โ€” ์ตœ์ƒ์œ„ ์ž„ํŒฉํŠธ ์š”์•ฝ, ์„ธ๊ทธ๋จผํŠธ ํ‘œ, harness scope ํ”Œ๋ž˜๊ทธ ๋ฌธ์„œํ™”, ๊ฐ€๊ฒฉ ํ…Œ์ด๋ธ” ์ตœ์‹ ํ™”.
package/bin/cli.js CHANGED
@@ -93,6 +93,24 @@ function bedrockDisplayFromId(id) {
93
93
  return `${family} ${m[2]}.${m[3]}`;
94
94
  }
95
95
 
96
+ /**
97
+ * Live context usage from Claude Code's stdin payload (`context_window`).
98
+ * More accurate than inferring from transcripts: it's the CURRENT session's
99
+ * real fill level, updated every refresh. Shape (subset):
100
+ * "context_window": { "context_window_size": 200000, "used_percentage": 68 }
101
+ */
102
+ function extractContextUsage(stdinJson) {
103
+ const cw = stdinJson && stdinJson.context_window;
104
+ if (!cw || typeof cw !== 'object') return null;
105
+ const usedPct = Number(cw.used_percentage);
106
+ const size = Number(cw.context_window_size);
107
+ if (!Number.isFinite(usedPct)) return null;
108
+ return {
109
+ usedPct,
110
+ size: Number.isFinite(size) && size > 0 ? size : null,
111
+ };
112
+ }
113
+
96
114
  function extractModel(stdinJson) {
97
115
  if (!stdinJson || !stdinJson.model) return null;
98
116
  const m = stdinJson.model;
@@ -894,6 +912,7 @@ async function main() {
894
912
  const stdinJson = readStdinJson();
895
913
  let caps = extractCaps(stdinJson);
896
914
  let model = extractModel(stdinJson);
915
+ const ctxLive = extractContextUsage(stdinJson);
897
916
  if (isStatusline && (caps || model)) {
898
917
  try {
899
918
  const { persistSnapshot } = await import('../src/caps-cache.js');
@@ -923,7 +942,7 @@ async function main() {
923
942
  if (format === 'statusline') {
924
943
  if (contextWindow.size === '1M') {
925
944
  spikeChip = chipForIssues([], contextWindow);
926
- chipDetail = `Context auto-promoted to 1M (max single-request ${Math.round(contextWindow.maxContext / 1000)}k tokens)`;
945
+ chipDetail = `Single-request context exceeded 200k (max ${Math.round(contextWindow.maxContext / 1000)}k tokens)`;
927
946
  } else {
928
947
  const recentSession = sessions
929
948
  .slice()
@@ -994,6 +1013,7 @@ async function main() {
994
1013
  lastActivity,
995
1014
  spikeReport,
996
1015
  contextWindow,
1016
+ ctxLive,
997
1017
  spikeChip,
998
1018
  caps,
999
1019
  model,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-token-saver",
3
- "version": "2.17.0",
3
+ "version": "2.18.0",
4
4
  "description": "Save tokens on Claude Code โ€” spike diagnosis, 1M-context detection, TTL countdown, statusline. (formerly claude-cache-monitor)",
5
5
  "type": "module",
6
6
  "bin": {
package/src/advice.js CHANGED
@@ -39,25 +39,33 @@ function toggleShortcut() {
39
39
  */
40
40
  export const ISSUE_MESSAGES = {
41
41
  LARGE_INPUT_PER_REQUEST: {
42
- title: 'Per-request input tokens are unusually large (1M context suspected)',
43
- titleKo: '์š”์ฒญ๋‹น ์ž…๋ ฅ ํ† ํฐ์ด ๋น„์ •์ƒ์ ์œผ๋กœ ํผ (1M ์ปจํ…์ŠคํŠธ ์˜์‹ฌ)',
42
+ title: 'Per-request input tokens are unusually large (context past 200k)',
43
+ titleKo: '์š”์ฒญ๋‹น ์ž…๋ ฅ ํ† ํฐ์ด ๋น„์ •์ƒ์ ์œผ๋กœ ํผ (์ปจํ…์ŠคํŠธ 200k ์ดˆ๊ณผ)',
44
+ // Since Opus 4.7 there is NO long-context price premium โ€” 1M is standard
45
+ // rate and the default window on current models. The cost driver is the
46
+ // token volume itself: a 500k-token context re-reads ~500k tokens every
47
+ // turn (cache-read billed) and burns the subscription 5H/7D windows
48
+ // several times faster. That's what this warning is about.
44
49
  explain:
45
- 'Since Opus 4.7, 1M context is priced at the standard rate, and Max plans auto-promote ' +
46
- 'sessions to 1M. Once context goes past 200k, long-context pricing kicks in and cache reuse drops.',
50
+ 'Current models default to a 1M window with no long-context premium โ€” but the token ' +
51
+ 'volume itself is the cost: every turn re-reads the whole context (billed as cache reads) ' +
52
+ 'and drains the 5H/7D rate-limit windows several times faster. Cache reuse also drops.',
47
53
  explainKo:
48
- 'Opus 4.7๋ถ€ํ„ฐ 1M ์ปจํ…์ŠคํŠธ๊ฐ€ ํ‘œ์ค€ ์š”๊ธˆ์ด ๋˜์—ˆ๊ณ , Max ํ”Œ๋žœ์€ ์„ธ์…˜์„ ์ž๋™์œผ๋กœ 1M์œผ๋กœ ์Šน๊ฒฉํ•ฉ๋‹ˆ๋‹ค. ' +
49
- '200k๋ฅผ ๋„˜๋Š” ์ˆœ๊ฐ„ ์žฅ๊ธฐ ์ปจํ…์ŠคํŠธ ์š”๊ธˆ์ด ์ ์šฉ๋˜๋ฉฐ ์บ์‹œ ์žฌ์‚ฌ์šฉ๋ฅ ๋„ ๋–จ์–ด์ง‘๋‹ˆ๋‹ค.',
54
+ 'ํ˜„์žฌ ๋ชจ๋ธ์€ 1M ์œˆ๋„๊ฐ€ ๊ธฐ๋ณธ์ด๊ณ  ์žฅ๊ธฐ ์ปจํ…์ŠคํŠธ ํ”„๋ฆฌ๋ฏธ์—„๋„ ์—†์Šต๋‹ˆ๋‹ค โ€” ํ•˜์ง€๋งŒ ํ† ํฐ๋Ÿ‰ ์ž์ฒด๊ฐ€ ๋น„์šฉ์ž…๋‹ˆ๋‹ค. ' +
55
+ '๋งค ํ„ด ์ปจํ…์ŠคํŠธ ์ „์ฒด๋ฅผ ๋‹ค์‹œ ์ฝ๊ณ (์บ์‹œ ์ฝ๊ธฐ ๊ณผ๊ธˆ) 5H/7D ํ•œ๋„๋„ ๋ช‡ ๋ฐฐ ๋น ๋ฅด๊ฒŒ ์†Œ๋ชจ๋ฉ๋‹ˆ๋‹ค. ' +
56
+ '์บ์‹œ ์žฌ์‚ฌ์šฉ๋ฅ ๋„ ๋–จ์–ด์ง‘๋‹ˆ๋‹ค.',
50
57
  actions: () => [
51
58
  {
52
- label: 'Disable 1M context (env var)',
53
- labelKo: '1M ์ปจํ…์ŠคํŠธ ๋„๊ธฐ (ํ™˜๊ฒฝ๋ณ€์ˆ˜)',
54
- commands: disable1mEnvSnippet(),
55
- },
56
- {
57
- label: 'In-session toggle',
58
- labelKo: '์„ธ์…˜ ๋‚ด ์ฆ‰์‹œ ํ† ๊ธ€',
59
- commands: [`Press ${toggleShortcut()} to toggle on/off instantly`],
60
- commandsKo: [`${toggleShortcut()} ๋ˆ„๋ฅด๋ฉด ์ฆ‰์‹œ on/off ํ† ๊ธ€`],
59
+ label: 'Compact or clear when context grows (first lever)',
60
+ labelKo: '์ปจํ…์ŠคํŠธ๊ฐ€ ์ปค์ง€๋ฉด /compact ๋˜๋Š” /clear (1์ˆœ์œ„)',
61
+ commands: [
62
+ '/compact โ€” summarize the session history in place',
63
+ '/clear โ€” drop history entirely at a clean task boundary',
64
+ ],
65
+ commandsKo: [
66
+ '/compact โ€” ์„ธ์…˜ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ๊ทธ ์ž๋ฆฌ์—์„œ ์š”์•ฝ',
67
+ '/clear โ€” ์ž‘์—… ๋ถ„๊ธฐ์ ์—์„œ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ํ†ต์งธ๋กœ ๋น„์šฐ๊ธฐ',
68
+ ],
61
69
  },
62
70
  {
63
71
  label: 'Cap extended-thinking budget (โš  check /effort first)',
@@ -90,16 +98,14 @@ export const ISSUE_MESSAGES = {
90
98
  ],
91
99
  },
92
100
  {
93
- label: 'Compact or clear when context grows',
94
- labelKo: '์ปจํ…์ŠคํŠธ๊ฐ€ ์ปค์ง€๋ฉด /compact ๋˜๋Š” /clear',
95
- commands: [
96
- '/compact โ€” summarize the session history in place',
97
- '/clear โ€” drop history entirely at a clean task boundary',
98
- ],
99
- commandsKo: [
100
- '/compact โ€” ์„ธ์…˜ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ๊ทธ ์ž๋ฆฌ์—์„œ ์š”์•ฝ',
101
- '/clear โ€” ์ž‘์—… ๋ถ„๊ธฐ์ ์—์„œ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ํ†ต์งธ๋กœ ๋น„์šฐ๊ธฐ',
102
- ],
101
+ label: 'Cap the window at 200k if you never need more (optional)',
102
+ labelKo: '๋” ํฐ ์œˆ๋„๊ฐ€ ํ•„์š” ์—†์œผ๋ฉด 200k๋กœ ์ œํ•œ (์„ ํƒ)',
103
+ commands: disable1mEnvSnippet().concat([
104
+ `Or press ${toggleShortcut()} to toggle in-session`,
105
+ ]),
106
+ commandsKo: disable1mEnvSnippet().concat([
107
+ `๋˜๋Š” ${toggleShortcut()}๋กœ ์„ธ์…˜ ๋‚ด ์ฆ‰์‹œ ํ† ๊ธ€`,
108
+ ]),
103
109
  },
104
110
  {
105
111
  label: 'โš  Known bug #31640',
@@ -337,11 +343,13 @@ export const ISSUE_MESSAGES = {
337
343
  title: 'Context window is approaching the limit',
338
344
  titleKo: '์ปจํ…์ŠคํŠธ ์ฐฝ์ด ํ•œ๊ณ„์— ๊ทผ์ ‘',
339
345
  explain:
340
- 'As context grows toward 200k (or 1M), each turn becomes more expensive and cache ' +
341
- 'reuse efficiency drops. Past 200k, long-context pricing applies.',
346
+ 'As context grows, each turn becomes more expensive (the whole context is re-read every ' +
347
+ 'turn) and cache reuse efficiency drops. There is no price premium past 200k on current ' +
348
+ 'models โ€” the token volume itself is the cost, and it drains the 5H/7D caps faster.',
342
349
  explainKo:
343
- '์ปจํ…์ŠคํŠธ๊ฐ€ 200k(๋˜๋Š” 1M)์— ๊ฐ€๊นŒ์›Œ์งˆ์ˆ˜๋ก ๋งค ํ„ด ๋น„์šฉ์ด ๋†’์•„์ง€๊ณ  ์บ์‹œ ์žฌ์‚ฌ์šฉ ํšจ์œจ์ด ๋–จ์–ด์ง‘๋‹ˆ๋‹ค. ' +
344
- '200k๋ฅผ ๋„˜์œผ๋ฉด ์žฅ๊ธฐ ์ปจํ…์ŠคํŠธ ์š”๊ธˆ์ด ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.',
350
+ '์ปจํ…์ŠคํŠธ๊ฐ€ ์ปค์งˆ์ˆ˜๋ก ๋งค ํ„ด ๋น„์šฉ์ด ๋†’์•„์ง€๊ณ (์ „์ฒด ์ปจํ…์ŠคํŠธ๋ฅผ ๋งค ํ„ด ๋‹ค์‹œ ์ฝ์Œ) ์บ์‹œ ์žฌ์‚ฌ์šฉ ํšจ์œจ์ด ' +
351
+ '๋–จ์–ด์ง‘๋‹ˆ๋‹ค. ํ˜„์žฌ ๋ชจ๋ธ์€ 200k ์ดˆ๊ณผ ํ”„๋ฆฌ๋ฏธ์—„์ด ์—†์Šต๋‹ˆ๋‹ค โ€” ํ† ํฐ๋Ÿ‰ ์ž์ฒด๊ฐ€ ๋น„์šฉ์ด๋ฉฐ 5H/7D ํ•œ๋„๋„ ' +
352
+ '๋” ๋น ๋ฅด๊ฒŒ ์†Œ๋ชจ๋ฉ๋‹ˆ๋‹ค.',
345
353
  actions: () => [
346
354
  {
347
355
  label: '/compact at the next natural break',
@@ -435,8 +443,8 @@ export const ISSUE_MESSAGES = {
435
443
  */
436
444
  export const ISSUE_TIPS = {
437
445
  LARGE_INPUT_PER_REQUEST: {
438
- en: 'Check `/effort` โ€” `xhigh` is the #1 cap killer; switch to `/effort medium` (or `low`); disable 1M context; `/compact` when context grows',
439
- ko: '`/effort` ํ™•์ธ โ€” `xhigh`๊ฐ€ ์บก ์†Œ์ง„ 1์ˆœ์œ„ ์›์ธ, `medium`(๋˜๋Š” `low`)์œผ๋กœ ๋ณต๊ท€; 1M ์ปจํ…์ŠคํŠธ ๋„๊ธฐ; ์ปจํ…์ŠคํŠธ ์ปค์ง€๋ฉด `/compact`',
446
+ en: '`/compact` or `/clear` โ€” big contexts re-bill every turn and burn the 5H/7D caps; check `/effort` (`xhigh` is the #1 cap killer)',
447
+ ko: '`/compact` ๋˜๋Š” `/clear` โ€” ํฐ ์ปจํ…์ŠคํŠธ๋Š” ๋งค ํ„ด ์žฌ๊ณผ๊ธˆ๋˜๊ณ  5H/7D ํ•œ๋„๋ฅผ ํƒœ์›€; `/effort` ํ™•์ธ (`xhigh`๊ฐ€ ์บก ์†Œ์ง„ 1์ˆœ์œ„)',
440
448
  },
441
449
  LOW_HIT_RATE: {
442
450
  en: 'Continue with `claude --continue`; keep CLAUDE.md trim โ€” every line ships every turn',
@@ -463,8 +471,8 @@ export const ISSUE_TIPS = {
463
471
  ko: '์ง€๊ธˆ ๋ฐ”๋กœ ์•„๋ฌด ํ”„๋กฌํ”„ํŠธ๋‚˜ ๋ณด๋‚ด TTL ํƒ€์ด๋จธ ๋ฆฌ์…‹; ๋˜๋Š” ์ž๋ฆฌ ๋น„์šฐ๊ธฐ ์ „ `/compact` (Pro = 5๋ถ„, Max = 1์‹œ๊ฐ„)',
464
472
  },
465
473
  CONTEXT_NEAR_LIMIT: {
466
- en: '`/compact` before hitting the limit; `/clear` + re-attach only needed files; disable 1M: `export CLAUDE_MODEL_CONTEXT=200000`',
467
- ko: 'ํ•œ๊ณ„ ๋„๋‹ฌ ์ „ `/compact`; `/clear` ํ›„ ํ•„์š”ํ•œ ํŒŒ์ผ๋งŒ ์žฌ์ฒจ๋ถ€; 1M ๋„๊ธฐ: `export CLAUDE_MODEL_CONTEXT=200000`',
474
+ en: '`/compact` before hitting the limit; `/clear` + re-attach only needed files',
475
+ ko: 'ํ•œ๊ณ„ ๋„๋‹ฌ ์ „ `/compact`; `/clear` ํ›„ ํ•„์š”ํ•œ ํŒŒ์ผ๋งŒ ์žฌ์ฒจ๋ถ€',
468
476
  },
469
477
  };
470
478
 
@@ -474,6 +482,9 @@ export const ISSUE_TIPS = {
474
482
  * need a fallback so history.js can still surface the right tip.
475
483
  */
476
484
  export const CHIP_TO_CODES = {
485
+ 'โš  Ctx 200k+': ['LARGE_INPUT_PER_REQUEST'],
486
+ // Legacy chip name (pre-v2.18) โ€” kept so `last`/`history` can still resolve
487
+ // codes from history files written by older versions.
477
488
  'โš  1M ON': ['LARGE_INPUT_PER_REQUEST'],
478
489
  'โš  Cache miss': ['LOW_HIT_RATE'],
479
490
  'โš  Input spike': ['LARGE_INPUT_PER_REQUEST'],
@@ -502,7 +513,11 @@ export const CAP_TIPS = {
502
513
  * shown to a global audience.
503
514
  */
504
515
  export function chipForIssues(issues, contextWindow) {
505
- if (contextWindow?.size === '1M') return 'โš  1M ON';
516
+ // Fires on *actual usage* (a real request carried >210k input tokens), not
517
+ // on the model merely supporting 1M โ€” current models are all 1M by default
518
+ // with no price premium, so "1M ON" stopped being a meaningful alarm. The
519
+ // meaningful signal is "your context genuinely exceeded 200k".
520
+ if (contextWindow?.size === '1M') return 'โš  Ctx 200k+';
506
521
  const codes = issues.map((i) => i.code);
507
522
  if (codes.includes('LARGE_INPUT_PER_REQUEST')) return 'โš  Input spike';
508
523
  if (codes.includes('BUCKET_5M_DOMINANT')) return 'โš  5m TTL';
package/src/demo.js CHANGED
@@ -26,6 +26,7 @@ const SCENARIOS = [
26
26
  savings: 2123,
27
27
  elapsedSec: 30,
28
28
  contextSize: '200k',
29
+ ctxUsedPct: 34,
29
30
  spikeChip: null,
30
31
  caps: HEALTHY_CAPS,
31
32
  },
@@ -98,14 +99,15 @@ const SCENARIOS = [
98
99
  },
99
100
  {
100
101
  name: 'ctx-1m',
101
- label: 'โš  1M context auto-on',
102
+ label: 'โš  Context past 200k',
102
103
  data: {
103
104
  hitRate: 0.78,
104
105
  pct1h: 0.92,
105
106
  savings: 1340,
106
107
  elapsedSec: 30,
107
108
  contextSize: '1M',
108
- spikeChip: 'โš  1M ON',
109
+ ctxUsedPct: 28, // 28% of 1M โ‰ˆ 280k actually in context
110
+ spikeChip: 'โš  Ctx 200k+',
109
111
  caps: HEALTHY_CAPS,
110
112
  },
111
113
  },
@@ -271,7 +273,7 @@ export function buildTableDemoData(options = {}) {
271
273
  spikeReport: { spikes, baseline: { p95: 940_000 } },
272
274
  contextWindow: { size: '1M', maxContext: 280_000 },
273
275
  lastActivity: Date.now() - 60 * 1000,
274
- spikeChip: 'โš  1M ON',
276
+ spikeChip: 'โš  Ctx 200k+',
275
277
  };
276
278
  }
277
279
 
@@ -301,7 +303,7 @@ export function buildScenarioData(scenarioName, options) {
301
303
  if (!scenario) return null;
302
304
  }
303
305
 
304
- const { hitRate, pct1h, pct5m, savings, elapsedSec, contextSize, spikeChip, caps } = scenario.data;
306
+ const { hitRate, pct1h, pct5m, savings, elapsedSec, contextSize, ctxUsedPct, spikeChip, caps } = scenario.data;
305
307
  return {
306
308
  summary: { hitRate },
307
309
  ttl: { pct1h, pct5m: pct5m ?? (1 - pct1h) },
@@ -314,6 +316,11 @@ export function buildScenarioData(scenarioName, options) {
314
316
  },
315
317
  lastActivity: Date.now() - elapsedSec * 1000,
316
318
  contextWindow: { size: contextSize },
319
+ // Live fill level (`๐Ÿ“ฆ 68%`) โ€” scenarios that set ctxUsedPct exercise the
320
+ // stdin-driven segment; the rest fall back to the size-based chip.
321
+ ctxLive: ctxUsedPct != null
322
+ ? { usedPct: ctxUsedPct, size: contextSize === '1M' ? 1_000_000 : 200_000 }
323
+ : undefined,
317
324
  spikeChip,
318
325
  caps: buildCapsShape(caps),
319
326
  model: DEFAULT_MODEL,
@@ -214,7 +214,7 @@ export function formatNoSession({ caps = null, model = null, windowLabel = '' }
214
214
  * @param {string[]|null} [opts.segments] - whitelist of segments to render. Names: cap-warn, spike, harness, model, hit, ttl, saved, ctx, period, plus per-window keys (`five_hour`, `seven_day`, โ€ฆ). `5h`/`7d` are kept as aliases for back-compat. Null/undefined = all.
215
215
  */
216
216
  export function formatReport(data, { color = true, verbose = false, timer = true, mode = 'text', segments = null } = {}) {
217
- const { summary, ttl, cost, options, lastActivity, contextWindow, spikeChip, caps, model } = data;
217
+ const { summary, ttl, cost, options, lastActivity, contextWindow, ctxLive, spikeChip, caps, model } = data;
218
218
  const { hitRate } = summary;
219
219
 
220
220
  // Hit rate โ†’ color signal
@@ -321,15 +321,36 @@ export function formatReport(data, { color = true, verbose = false, timer = true
321
321
  }
322
322
  }
323
323
 
324
- // Context window chip (e.g. "๐Ÿ“ฆ 1M" or "๐Ÿ“ฆ 200k"). 1M gets a warning color
325
- // because it's the expensive default on Max plans after Opus 4.7.
324
+ // Context chip. Two data sources, best first:
325
+ //
326
+ // 1. Live fill level from Claude Code's stdin (`context_window.used_percentage`)
327
+ // โ€” the current session's actual usage, refreshed every render. Rendered
328
+ // as `๐Ÿ“ฆ 68%` and colored by fill (green <70, yellow 70โ€“89, red 90+),
329
+ // matching the cap-segment tone scale.
330
+ // 2. Fallback (table view / older Claude Code): transcript-inferred window
331
+ // size. Note the semantics: `size === '1M'` means a real request already
332
+ // carried >210k input tokens โ€” actual heavy usage, not just the model
333
+ // supporting 1M. Current models are all 1M by default with no price
334
+ // premium, so this renders yellow ("your context is genuinely big"),
335
+ // not red ("expensive mode on") like it used to.
326
336
  let ctxSeg = null;
327
- if (contextWindow && contextWindow.size && contextWindow.size !== 'unknown') {
337
+ if (ctxLive && Number.isFinite(ctxLive.usedPct)) {
338
+ const pct = Math.max(0, Math.round(ctxLive.usedPct));
339
+ const tone = pct >= 90 ? RED : pct >= 70 ? YELLOW : GREEN;
340
+ const sizeLabel = ctxLive.size
341
+ ? (ctxLive.size >= 900_000 ? '1M' : `${Math.round(ctxLive.size / 1000)}k`)
342
+ : null;
343
+ const longLabel = sizeLabel ? `${pct}% of ${sizeLabel}` : `${pct}%`;
344
+ if (isIcon && verbose) {
345
+ ctxSeg = `${c(tone)}๐Ÿ“ฆ Ctx ${longLabel}${c(RESET)}`;
346
+ } else if (isIcon) {
347
+ ctxSeg = `${c(tone)}๐Ÿ“ฆ ${pct}%${c(RESET)}`;
348
+ } else {
349
+ ctxSeg = `${c(tone)}Ctx ${longLabel}${c(RESET)}`;
350
+ }
351
+ } else if (contextWindow && contextWindow.size && contextWindow.size !== 'unknown') {
328
352
  const label = contextWindow.size === '1M' ? '1M' : '200k';
329
- const ctxColor = contextWindow.size === '1M' ? RED : GREEN;
330
- // `Ctx` (not the full word `Context`) across every mode โ€” the icon already
331
- // tells the eye what the chip is, and the short form fits the same cadence
332
- // as `Hit`/`Saved` peers when we eventually shorten those too.
353
+ const ctxColor = contextWindow.size === '1M' ? YELLOW : GREEN;
333
354
  if (isIcon && verbose) {
334
355
  ctxSeg = `${c(ctxColor)}๐Ÿ“ฆ Ctx ${label}${c(RESET)}`;
335
356
  } else if (isIcon) {
@@ -86,7 +86,7 @@ function renderSpikeSection(spikes, contextWindow) {
86
86
  lines.push(r(` ${'โ”€'.repeat(50)}`));
87
87
  if (contextWindow && contextWindow.size === '1M') {
88
88
  lines.push(
89
- r(` Context mode: 1M (max recent single-request input ${formatContextSize(contextWindow.maxContext)} tokens)`),
89
+ r(` Context usage exceeded 200k (max recent single-request input ${formatContextSize(contextWindow.maxContext)} tokens)`),
90
90
  );
91
91
  lines.push('');
92
92
  }
@@ -197,7 +197,7 @@ export function formatReport({ summary: sum, trend, ttl, anomalies, cost, option
197
197
  if (contextWindow && contextWindow.size !== 'unknown') {
198
198
  const note =
199
199
  contextWindow.size === '1M'
200
- ? 'โš  1M context active (Opus 4.7+ Max default). Disable with CLAUDE_CODE_DISABLE_1M_CONTEXT=1'
200
+ ? 'โš  Context exceeded 200k in recent requests โ€” big contexts re-bill every turn and drain the 5H/7D caps. Use /compact or /clear.'
201
201
  : 'โœ“ 200k context (standard)';
202
202
  lines.push(` Context window: ${contextWindow.size} ${note}`);
203
203
  lines.push(` (max recent single-request input ${formatContextSize(contextWindow.maxContext)} tokens)`);
package/src/history.js CHANGED
@@ -69,7 +69,8 @@ function saveState(state) {
69
69
  function chipKo(chip) {
70
70
  if (!chip) return chip;
71
71
  const map = {
72
- 'โš  1M ON': 'โš  1M ์ปจํ…์ŠคํŠธ ํ™œ์„ฑ',
72
+ 'โš  Ctx 200k+': 'โš  ์ปจํ…์ŠคํŠธ 200k ์ดˆ๊ณผ',
73
+ 'โš  1M ON': 'โš  1M ์ปจํ…์ŠคํŠธ ํ™œ์„ฑ', // legacy (pre-v2.18)
73
74
  'โš  Cache miss': 'โš  ์บ์‹œ ๋ฏธ์Šค',
74
75
  'โš  Rebuild churn': 'โš  ์บ์‹œ ์žฌ๋นŒ๋“œ ๋นˆ๋ฐœ',
75
76
  'โš  Input spike': 'โš  ์ž…๋ ฅ ๊ธ‰์ฆ',
@@ -92,6 +93,9 @@ function chipKo(chip) {
92
93
  */
93
94
  function detailKo(detail) {
94
95
  if (!detail) return detail;
96
+ const m0 = detail.match(/^Single-request context exceeded 200k \(max (\d+)k tokens\)$/);
97
+ if (m0) return `๋‹จ์ผ ์š”์ฒญ ์ปจํ…์ŠคํŠธ 200k ์ดˆ๊ณผ (์ตœ๋Œ€ ${m0[1]}k ํ† ํฐ)`;
98
+ // legacy detail shape (pre-v2.18)
95
99
  const m1 = detail.match(/^Context auto-promoted to 1M \(max single-request (\d+)k tokens\)$/);
96
100
  if (m1) return `1M ์ปจํ…์ŠคํŠธ ์ž๋™ ํ™œ์„ฑ (๋‹จ์ผ ์š”์ฒญ ์ตœ๋Œ€ ${m1[1]}k ํ† ํฐ)`;
97
101
  const m2 = detail.match(/^session ([^:]+): (.+)$/);