claude-token-saver 3.17.0 → 3.19.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 +43 -2
- package/README.md +101 -60
- package/bin/cli.js +7 -0
- package/package.json +1 -1
- package/presets/korean-style/LICENSE-fluent-korean +21 -0
- package/presets/korean-style/fluent-korean.md +52 -0
- package/src/commands/install.js +39 -0
- package/src/commands/korean.js +76 -0
- package/src/commands/route-scan.js +18 -1
- package/src/formatters/statusline.js +24 -1
- package/src/korean-style.js +131 -0
package/README.en.md
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|

|
|
10
10
|
|
|
11
|
-
**That figure on the first statusline row is the
|
|
11
|
+
**That figure on the first statusline row is the headline metric.** It is money actually saved by moving the easy work your expensive model kept repeating onto cheaper ones, and right beside it is which model that money moved off, and onto what.
|
|
12
|
+
|
|
13
|
+
The other half of the savings comes from the **🅷 Harness and ratchet**. The harness blocks the habits that waste the most tokens — completion claims with no evidence, skipped verification — with five principles, and the ratchet freezes each error you hit into a rule so it does not recur. The measured −18.6% figure below comes from adopting those two, not from routing ([details](#real-world-impact--beforeafter-report)); routing savings sit on top of it. One install sets up all three.
|
|
12
14
|
|
|
13
15
|
It is not an estimate or a marketing number: it comes out of a **ledger**. For every delegated subagent run it records
|
|
14
16
|
|
|
@@ -58,7 +60,15 @@ It never intercepts a request in realtime.
|
|
|
58
60
|
kept handling, and promotes them into rules so a cheaper model takes them **from the next session
|
|
59
61
|
onward**. Rules are scoped global or per-project.
|
|
60
62
|
|
|
61
|
-
|
|
63
|
+
### Why realtime model routing can cost more, not less
|
|
64
|
+
|
|
65
|
+
Never switching models mid-session is the point of this design.
|
|
66
|
+
|
|
67
|
+
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.
|
|
68
|
+
|
|
69
|
+
Teams shipping routing products have turned the feature off for exactly this reason: [LLM 라우터를 만든 사람들이 직접 껐습니다 #Shorts](https://www.youtube.com/shorts/SK-GoAABjbg) (Korean).
|
|
70
|
+
|
|
71
|
+
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.
|
|
62
72
|
|
|
63
73
|
```bash
|
|
64
74
|
npm i -g claude-token-saver@latest
|
|
@@ -136,6 +146,7 @@ Run these in your shell (inside Claude Code, the `/claude-token-saver` Skill is
|
|
|
136
146
|
| `claude-token-saver route-scan` | Detect recurring easy work on expensive models → propose haiku-delegation ratchet rules (below) |
|
|
137
147
|
| `claude-token-saver route-scan savings` | The routing-savings ledger — per-model-change rollup + per-run log (the evidence behind the figure) |
|
|
138
148
|
| `claude-token-saver compact-window` | Warn when a 1M-context session has no auto-compact cap → pin 400k with `set` (below) |
|
|
149
|
+
| `claude-token-saver korean on\|off\|status` | Inject Korean writing guidance at session start (below) |
|
|
139
150
|
| `claude-token-saver install` | Manually register Skill + statusline |
|
|
140
151
|
|
|
141
152
|
Switch output language with `mode ko` / `mode en` (English default; statusline chips stay symbolic).
|
|
@@ -257,6 +268,27 @@ For environments the learner cannot reach, write the mapping yourself in `<userD
|
|
|
257
268
|
|
|
258
269
|
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.
|
|
259
270
|
|
|
271
|
+
## 🇰🇷 Korean writing guidance
|
|
272
|
+
|
|
273
|
+
Injects guidance that corrects how Claude writes Korean (dropped sentence parts, noun-stopped sentences, translationese, em-dash overuse) **once per session.**
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
claude-token-saver korean on # on, for every project
|
|
277
|
+
claude-token-saver korean status # state, cost, provenance
|
|
278
|
+
claude-token-saver korean show # print the guidance itself
|
|
279
|
+
claude-token-saver korean off # off
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
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.
|
|
283
|
+
|
|
284
|
+
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.
|
|
285
|
+
|
|
286
|
+
**The default is decided at install time.** A Korean system locale (`ko_KR` and friends; on macOS the system setting is checked too) turns it on; anything else leaves it off, so users who never write Korean are not billed 1.5k tokens a session. **Once you have turned it on or off yourself, that choice sticks — an upgrade never overrides it.** Install with `CTS_NO_KOREAN=1` to skip the automatic decision.
|
|
287
|
+
|
|
288
|
+
> **Source and license**
|
|
289
|
+
> The guidance text comes from [fluent-korean](https://github.com/snflkd/fluent-korean). Copyright (c) 2026 snflkd, MIT License.
|
|
290
|
+
> 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`.
|
|
291
|
+
|
|
260
292
|
## Spike issue codes
|
|
261
293
|
|
|
262
294
|
| Code | Meaning |
|
|
@@ -337,6 +369,15 @@ Also update `statusLine.command` in `~/.claude/settings.json` to `claude-token-s
|
|
|
337
369
|
|
|
338
370
|
## Release notes
|
|
339
371
|
|
|
372
|
+
### v3.19.0 (2026-08-22)
|
|
373
|
+
- **Korean writing guidance** — corrects how Claude writes Korean (dropped sentence parts, noun-stopped sentences, translationese, em-dash overuse), injected once per session. Claude Code's output styles occupy a single global slot and must be configured per machine; this ships the guidance in the package and delivers it through the SessionStart hook already installed, so it **applies in every project and leaves the output-style slot free.** Text vendored from [fluent-korean](https://github.com/snflkd/fluent-korean) (Copyright (c) 2026 snflkd, MIT), license included.
|
|
374
|
+
- **Decided at install time** — a Korean system locale turns it on; anything else leaves it off. Your own on/off choice is preserved, so upgrades never override it. Skip with `CTS_NO_KOREAN=1`; a `가` chip shows in the statusline when active.
|
|
375
|
+
- **Corrected an overstated README claim** — routing savings were described as "the whole product", but the measured −18.6% comes from the harness and ratchet. The relationship between the three is now stated accurately.
|
|
376
|
+
|
|
377
|
+
### v3.18.0 (2026-08-22)
|
|
378
|
+
- **Korean documentation rewritten for clarity** — full sentences with explicit predicates, and em dashes replaced by colons and conjunctions where they were compressing too much meaning.
|
|
379
|
+
- **Added why realtime model routing can cost more** — prompt caches are per-model, so a mid-session switch cancels the savings via cache loss; this is why the tool delegates to subagents only.
|
|
380
|
+
|
|
340
381
|
### v3.17.0 (2026-08-22)
|
|
341
382
|
- **One install now sets up the 🅷 Harness too** — until now `harness init` was a separate step, without which the 🅷 score and ratchet-rule delivery did nothing. The install **appends** the 5-principle block to `~/.claude/CLAUDE.md` (existing content backed up and preserved; an existing block is left alone). Skip with `CTS_NO_HARNESS=1`, undo with `harness uninit --global`.
|
|
342
383
|
- **The README leads with the real statusline screenshot** — the capture replaces the code block at the top, and the duplicate image further down was removed.
|
package/README.md
CHANGED
|
@@ -4,19 +4,21 @@
|
|
|
4
4
|
|
|
5
5
|
# claude-token-saver
|
|
6
6
|
|
|
7
|
-
## 🔀 Routing saved
|
|
7
|
+
## 🔀 Routing saved: 이 도구가 존재하는 이유
|
|
8
8
|
|
|
9
|
-

|
|
10
10
|
|
|
11
|
-
**statusline
|
|
11
|
+
**statusline 첫째 줄의 이 금액이 이 도구의 대표 지표입니다.** 비싼 모델이 반복해서 처리해 온 쉬운 작업을 더 싼 모델에 넘겨서 **실제로 절감한 비용**이며, 그 옆에는 어느 모델에서 어느 모델로 작업이 옮겨 가면서 그 금액이 발생했는지가 함께 표시됩니다.
|
|
12
12
|
|
|
13
|
-
이
|
|
13
|
+
이 금액을 만들어 내는 나머지 절반은 **🅷 Harness와 ratchet**입니다. Harness는 증거 없는 완료 보고와 검증 생략처럼 토큰을 가장 많이 낭비하는 습관을 다섯 가지 원칙으로 막고, ratchet은 한 번 겪은 에러를 룰로 굳혀 같은 실수를 반복하지 않게 합니다. 실제로 비용을 18.6% 줄인 실측치는 라우팅이 아니라 이 두 가지를 도입한 전후를 비교한 값입니다([상세](#실제-효과-도입-전후-리포트)). 라우팅 절감액은 그 위에 얹히는 몫이며, 세 기능은 설치 한 번으로 함께 적용됩니다.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
- **결과(after)** — 실제로 그 일을 처리한 모델
|
|
17
|
-
- **차액** — 같은 토큰량에 두 모델의 가격표를 각각 적용한 값
|
|
15
|
+
이 숫자는 추정치나 홍보 문구가 아니라 **원장(ledger)에 기록된 실측값입니다.** 위임된 서브에이전트 실행 하나하나마다 다음 세 가지를 기록합니다.
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
- **기준 모델(before):** 룰을 승격하기 전에 그 유형을 실제로 처리하던 모델입니다.
|
|
18
|
+
- **실행 모델(after):** 그 작업을 실제로 처리한 모델입니다.
|
|
19
|
+
- **차액:** 동일한 토큰량에 두 모델의 가격표를 각각 적용해 계산한 값입니다.
|
|
20
|
+
|
|
21
|
+
그래서 `route-scan savings` 명령 한 번이면 **모든 금액을 어떤 룰이 만들어 냈는지까지 거슬러 확인할 수 있습니다.**
|
|
20
22
|
|
|
21
23
|
```bash
|
|
22
24
|
$ claude-token-saver route-scan savings
|
|
@@ -32,7 +34,7 @@ $ claude-token-saver route-scan savings
|
|
|
32
34
|
룰: T2|paste|-Users-me-projects-my-app
|
|
33
35
|
```
|
|
34
36
|
|
|
35
|
-
**정직하게
|
|
37
|
+
**정직하게 집계하는 것이 이 도구의 설계 원칙입니다.** 등록된 룰이 담당하지 않는 위임(`Explore`, 사용자가 직접 만든 에이전트, 플러그인이 제공하는 에이전트)은 이 도구가 라우팅한 결과가 아니므로 **금액에서 제외합니다.** 가격표가 모델명을 인식하지 못하는 경우에도 틀린 금액을 표시하는 대신 **그 실행을 집계에서 제외합니다.** 그래서 금액이 작게 나올 수는 있어도, 표시되는 값은 언제나 실제로 절감한 금액입니다.
|
|
36
38
|
|
|
37
39
|
```bash
|
|
38
40
|
npm i -g claude-token-saver # postinstall이 statusline + Skill 자동 등록
|
|
@@ -40,34 +42,42 @@ npm i -g claude-token-saver # postinstall이 statusline + Skill 자동 등록
|
|
|
40
42
|
|
|
41
43
|
---
|
|
42
44
|
|
|
43
|
-
## ⚡ 왜
|
|
45
|
+
## ⚡ 왜 쓰는가: 30초 요약
|
|
44
46
|
|
|
45
47
|
| | |
|
|
46
48
|
|---|---|
|
|
47
|
-
| 🔀 **라우팅
|
|
48
|
-
| 🎯 **모델 피팅 위임** | 상위 모델(opus
|
|
49
|
-
| 💸 **비용 실측 −18.6%** | harness
|
|
50
|
-
| 🚨 **한도 초과 예방** |
|
|
51
|
-
| 🧠 **캐시 낭비 감지** |
|
|
52
|
-
| 🅷 **같은 실수 차단** |
|
|
49
|
+
| 🔀 **라우팅 절감액 실측** | 위임으로 절감한 금액을 실행 단위로 원장에 기록하고, statusline 첫째 줄에 누적액과 모델 이동 내역을 함께 표시합니다 (`route-scan savings`로 전수 확인) |
|
|
50
|
+
| 🎯 **모델 피팅 위임** | 상위 모델(opus·fable)이 반복 처리해 온 쉬운 작업을 티어(T0/T1/T2)로 분류한 뒤 haiku·sonnet 위임 룰로 승격하고, 다음 세션부터 자동으로 적용합니다 |
|
|
51
|
+
| 💸 **비용 실측 −18.6%** | harness와 ratchet을 도입하기 전후로 사용자 메시지당 비용이 $2.35에서 $1.91로 줄었습니다 (저자의 실사용 로그, [상세](#실제-효과-도입-전후-리포트)) |
|
|
52
|
+
| 🚨 **한도 초과 예방** | 5시간·7일 rate-limit 윈도가 90%에 도달하면 즉시 경고하고, `handoff`로 진행 중인 작업을 백업합니다 |
|
|
53
|
+
| 🧠 **캐시 낭비 감지** | 히트율과 TTL 카운트다운, 1M 컨텍스트 사용 여부를 자동으로 감지해 토큰이 급증한 원인을 코드로 진단합니다 |
|
|
54
|
+
| 🅷 **같은 실수 차단** | 반복되는 에러를 감지해 ratchet 룰로 승격하고, 다음 세션부터 자동으로 적용합니다 |
|
|
55
|
+
|
|
56
|
+
## 라우터가 아닙니다: 60초 설명
|
|
57
|
+
|
|
58
|
+
이 도구는 요청을 실시간으로 가로채지 않습니다.
|
|
59
|
+
**세션이 끝난 뒤에** 로컬 기록을 읽어서 비싼 모델이 반복해서 처리해 온 쉬운 유형을 찾아내고,
|
|
60
|
+
그 유형은 **다음 세션부터** 더 싼 모델이 맡도록 룰로 등록합니다. 룰의 적용 범위는 글로벌과 프로젝트로 나뉩니다.
|
|
61
|
+
|
|
62
|
+
### 실시간 모델 라우팅이 오히려 비용을 키우는 이유
|
|
53
63
|
|
|
54
|
-
|
|
64
|
+
세션 도중에 모델을 바꾸지 않는다는 점이 이 도구의 핵심입니다.
|
|
55
65
|
|
|
56
|
-
|
|
57
|
-
**세션이 끝난 뒤** 로컬 기록을 읽어서, 비싼 모델이 반복해서 처리해 온 쉬운 유형을 뽑고,
|
|
58
|
-
그 유형은 **다음 세션부터** 싼 모델이 맡도록 룰로 겁니다. 룰은 글로벌·프로젝트로 범위가 나뉩니다.
|
|
66
|
+
프롬프트 캐시는 **모델별로 따로 유지됩니다.** 그래서 세션 중간에 더 싼 모델로 전환하면 새 모델은 빈 캐시에서 시작하고, 그때까지 쌓인 대화 전체를 정가로 다시 읽어야 합니다. 캐시 히트는 원래 입력가의 10분의 1 수준이므로, 대화가 2만 토큰만 넘어가도 **전환 한 번에 그날 아낀 금액이 통째로 사라집니다.** 싼 모델로 옮겼는데 청구서는 더 커지는, 실시간 라우팅의 대표적인 역설입니다.
|
|
59
67
|
|
|
60
|
-
|
|
68
|
+
실제로 라우팅 제품을 만들던 팀들이 같은 이유로 기능을 껐습니다: [LLM 라우터를 만든 사람들이 직접 껐습니다 #Shorts](https://www.youtube.com/shorts/SK-GoAABjbg)
|
|
69
|
+
|
|
70
|
+
이 도구는 그래서 메인 세션의 모델을 건드리지 않습니다. **서브에이전트 위임만 사용하므로** 메인 세션의 캐시는 그대로 유지되고, 위임된 작업만 별도 컨텍스트에서 싼 모델이 처리합니다. 절감액이 캐시 손실로 상쇄되지 않는 이유가 여기에 있습니다.
|
|
61
71
|
|
|
62
72
|
```bash
|
|
63
73
|
npm i -g claude-token-saver@latest
|
|
64
|
-
claude-token-saver route-scan #
|
|
74
|
+
claude-token-saver route-scan # 지난 세션에서 위임 후보 추출 (LLM 호출 없음)
|
|
65
75
|
claude-token-saver route-scan rules # 승격된 룰 확인 · rm <N> 으로 삭제
|
|
66
|
-
claude-token-saver route-scan savings # 위임으로
|
|
76
|
+
claude-token-saver route-scan savings # 위임으로 절감한 금액의 근거를 전수 확인
|
|
67
77
|
```
|
|
68
78
|
|
|
69
|
-
기준선은
|
|
70
|
-
|
|
79
|
+
판정 기준선은 다른 사람의 벤치마크가 아니라 **사용자 본인의 최근 14일 분포(p25/p75)** 로 잡습니다.
|
|
80
|
+
위임한 뒤에 실제로 성공했는지까지 측정하는 rule-health는 [v3.9.0](#v390-2026-08-01)에 들어갔습니다.
|
|
71
81
|
|
|
72
82
|
---
|
|
73
83
|
|
|
@@ -80,45 +90,45 @@ npm uninstall -g claude-cache-monitor # (구 패키지 사용자만)
|
|
|
80
90
|
npm i -g claude-token-saver
|
|
81
91
|
```
|
|
82
92
|
|
|
83
|
-
|
|
93
|
+
설치하면 Claude Code 화면 하단에 statusline이 곧바로 나타납니다. `--ignore-scripts` 옵션이나 sudo 사용 등으로 자동 등록이 되지 않았다면 `claude-token-saver install`을 실행해 직접 등록하십시오.
|
|
84
94
|
|
|
85
|
-
설치 한 번으로 **statusline
|
|
95
|
+
설치 한 번으로 **statusline과 Skill, SessionStart 훅, 🅷 Harness(5원칙), 최초 route-scan이** 모두 준비됩니다. Harness는 `~/.claude/CLAUDE.md`에 표시가 붙은 블록으로 **추가되며**, 기존에 작성해 둔 내용은 백업한 뒤 그대로 보존합니다. 이미 설정되어 있는 경우에는 아무것도 바꾸지 않습니다. 자동 설정을 원하지 않으면 `CTS_NO_HARNESS=1 npm i -g claude-token-saver`로 건너뛸 수 있고, 이미 적용한 설정을 되돌리려면 `claude-token-saver harness uninit --global`을 실행하십시오.
|
|
86
96
|
|
|
87
|
-
> ⚠️ sudo 글로벌
|
|
97
|
+
> ⚠️ sudo로 글로벌 설치를 하면 Skill이 사용자 계정이 아니라 root의 `~/.claude`에 등록되는 함정이 있습니다. nvm이나 fnm, Volta를 사용해 사용자 영역에 설치하기를 권장합니다.
|
|
88
98
|
|
|
89
99
|
## statusline 읽는 법
|
|
90
100
|
|
|
91
|
-
절감 원장에 기록이 쌓이면 **두
|
|
101
|
+
절감 원장에 기록이 쌓이면 statusline이 **두 줄로** 출력됩니다. 첫째 줄에는 라우팅 절감액만 표시하고, 둘째 줄에는 진단 칩을 표시합니다.
|
|
92
102
|
|
|
93
103
|
```
|
|
94
104
|
🔀 Routing saved $2.09 | fable→sonnet 1× $0.72 · opus→haiku 1× $0.57
|
|
95
105
|
⚠ Ctx 200k+ · 🅷 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
|
|
96
106
|
```
|
|
97
107
|
|
|
98
|
-
원장이 비어
|
|
108
|
+
원장이 비어 있으면, 다시 말해 아직 실측된 위임이 없으면 첫째 줄을 그리지 않고 종전처럼 한 줄로 출력합니다. 일부 환경(구버전 macOS Claude Code)에서 첫째 줄만 표시된다면 `--single-line` 옵션으로 한 줄 레이아웃을 유지하십시오.
|
|
99
109
|
|
|
100
110
|
| 세그먼트 | 의미 |
|
|
101
111
|
|---|---|
|
|
102
|
-
| `🔀`
|
|
112
|
+
| `🔀` **첫째 줄** | **라우팅으로 절감한 누적 금액과 모델 이동 내역입니다.** 누적 금액은 녹색으로, 내역은 회색으로 표시합니다. 내역을 모두 더하면 누적 금액과 정확히 일치하며(잘라내지 않고 전부 표시합니다), 버전 숫자는 계속 바뀌므로 계열명만 남깁니다(`claude-opus-4-5-…` → `opus`). 근거를 전부 확인하려면 `route-scan savings`를 실행하십시오 |
|
|
103
113
|
| `🤖` | 현재 모델 |
|
|
104
114
|
| `🅷 5/5` | harness 원칙 점수 ([Harness 모드](#-harness-모드)) |
|
|
105
115
|
| `🧠` | 캐시 히트율 (85%+ 녹색) |
|
|
106
|
-
| `⏳` | 캐시 TTL
|
|
116
|
+
| `⏳` | 캐시 TTL 카운트다운입니다. 만료되기 전에 메시지를 보내면 캐시가 유지됩니다 |
|
|
107
117
|
| `✦ current` / `📅 weekly` | 5시간 / 7일 rate-limit 윈도 사용률 + 리셋 시각 |
|
|
108
|
-
| `📦` | 컨텍스트
|
|
109
|
-
| `💰` | 프롬프트 캐시가
|
|
118
|
+
| `📦` | 컨텍스트 사용률입니다(예: `Ctx 68% of 1M`). 사용률에 따라 녹색·노란색·빨간색으로 표시합니다. 최신 모델은 1M 컨텍스트가 기본이고 별도 요금이 붙지 않지만, 토큰량 자체가 턴당 비용과 5시간·7일 한도를 빠르게 소모시킵니다 |
|
|
119
|
+
| `💰` | 프롬프트 캐시가 절약해 준 누적 금액입니다. 첫째 줄의 `🔀`(모델 라우팅 절감액)와는 **서로 다른 수치입니다** |
|
|
110
120
|
|
|
111
|
-
문제가 감지되면 **경고
|
|
121
|
+
문제가 감지되면 **경고 칩을 줄 맨 앞에** 붙입니다.
|
|
112
122
|
|
|
113
123
|
```
|
|
114
124
|
🚨 5H █████▓ 94% 🔄 12:36 · 🅷 5/5 · 🤖 Opus 4.8 · 🧠 Cache hit 72.1% · ⚠ Cache miss · 📅 weekly ▓░░░░░ 12% 🔄 Sun 14:26 · 📦 Ctx 200k · last 1d
|
|
115
125
|
```
|
|
116
126
|
|
|
117
|
-
|
|
127
|
+
칩의 종류는 다음과 같습니다. `🚨 5H/7D NN%`(한도 임박) · `⚠ Ctx 200k+`(단일 요청이 실제로 200k를 초과) · `⚠ Cache miss` · `⚠ Input spike` · `⚠ Output heavy` · `⚠ Call surge` · `⚠ Rebuild churn` · `⚠ 5m TTL`. 두 윈도가 동시에 90%를 넘으면 리셋이 더 임박한 쪽을 🚨로 올리고, 나머지 하나는 빨간 세그먼트로 계속 표시합니다 (v2.16.0 이상).
|
|
118
128
|
|
|
119
129
|
### 경고 칩이 떴을 때
|
|
120
130
|
|
|
121
|
-
Claude 안에서 `/claude-token-saver` Skill을
|
|
131
|
+
Claude Code 안에서 `/claude-token-saver` Skill을 실행하거나, 칩에 적힌 문구를 그대로 말하기만 해도("5H cap 떴어", "cache miss") Skill이 자동으로 활성화되어 **원인 코드와 단계별 해결 명령을** 보여 줍니다. 한도가 임박한 상황에서는 `claude-token-saver handoff`로 진행 중인 작업을 마크다운 파일에 백업한 뒤 새 세션에서 이어가는 방식을 권장합니다.
|
|
122
132
|
|
|
123
133
|
## 주요 명령
|
|
124
134
|
|
|
@@ -132,12 +142,13 @@ Claude 안에서 `/claude-token-saver` Skill을 실행하거나 칩 문구를
|
|
|
132
142
|
| `claude-token-saver handoff` | 작업 상태를 `HANDOFF-*.md`로 백업 (캡 임박 시) |
|
|
133
143
|
| `claude-token-saver mode [keywords...]` | 출력 설정 (`icon`/`text`, `ko`/`en`, `1h`~`30d` 윈도 등) |
|
|
134
144
|
| `claude-token-saver harness ...` | 🅷 Harness 관리 (아래 참고) |
|
|
135
|
-
| `claude-token-saver route-scan` | 상위 모델이 반복 처리한
|
|
136
|
-
| `claude-token-saver route-scan savings` | 라우팅 절감
|
|
137
|
-
| `claude-token-saver compact-window` | 1M
|
|
145
|
+
| `claude-token-saver route-scan` | 상위 모델이 반복 처리한 쉬운 작업을 감지해 haiku 위임 랫쳇 룰을 제안합니다 (아래 참고) |
|
|
146
|
+
| `claude-token-saver route-scan savings` | 라우팅 절감 원장입니다. 모델 이동별 합계와 실행별 내역을 함께 보여 주며, 표시되는 금액의 근거가 됩니다 |
|
|
147
|
+
| `claude-token-saver compact-window` | 1M 컨텍스트를 쓰면서 자동 압축 창이 설정되지 않았으면 경고하고, `set`으로 40만에 고정합니다 (아래 참고) |
|
|
148
|
+
| `claude-token-saver korean on\|off\|status` | 한국어 문체 지침을 세션 시작 시 주입합니다 (아래 참고) |
|
|
138
149
|
| `claude-token-saver install` | Skill·statusline 수동 등록 |
|
|
139
150
|
|
|
140
|
-
출력 언어는 `mode ko
|
|
151
|
+
출력 언어는 `mode ko`와 `mode en`으로 전환합니다. 기본값은 영어이며, statusline의 칩은 언제나 기호로 표시합니다. 전체 옵션은 [영문 README](./README.en.md#options)를 참고하십시오.
|
|
141
152
|
|
|
142
153
|
## 🅷 Harness 모드
|
|
143
154
|
|
|
@@ -145,7 +156,7 @@ Claude 안에서 `/claude-token-saver` Skill을 실행하거나 칩 문구를
|
|
|
145
156
|
|
|
146
157
|
```bash
|
|
147
158
|
claude-token-saver harness init # 이 프로젝트에 셋업
|
|
148
|
-
claude-token-saver harness init --global # ~/.claude/CLAUDE.md
|
|
159
|
+
claude-token-saver harness init --global # ~/.claude/CLAUDE.md, 모든 프로젝트에 적용
|
|
149
160
|
claude-token-saver harness check # 현재 점수 (글로벌 fallback 인정)
|
|
150
161
|
claude-token-saver harness promote <N> --project|--global # 경고 #N → ratchet 룰 (스코프 필수)
|
|
151
162
|
claude-token-saver harness promote "<룰 텍스트>" --project|--global # 내가 직접 정의한 룰도 같은 명령으로 등록
|
|
@@ -154,12 +165,12 @@ claude-token-saver harness list / rm <N> # 룰 조회 / 삭제 (자동 .ba
|
|
|
154
165
|
claude-token-saver harness off | on # 🅷 표시 토글
|
|
155
166
|
```
|
|
156
167
|
|
|
157
|
-
- `promote`는 non-TTY(
|
|
168
|
+
- `promote`는 non-TTY 환경(스크립트나 LLM 호출)에서 `--project` 또는 `--global` 플래그가 **반드시 필요합니다.** 적용 범위가 사용자에게 묻지 않은 채 결정되는 사고를 막기 위한 설계입니다.
|
|
158
169
|
- `pull`은 패키지에 동봉된 **제작자 큐레이션 랫쳇 룰**(`presets/ratchet-rules.md` — 실제 반복 사고에서 승격된 범용 룰만)을 내 글로벌 랫쳇(`~/.claude/ratchet.md`)에 등록합니다. 설치(`install`)나 `init`은 아무것도 자동 주입하지 않으며, `pull`은 항상 opt-in이고 재실행해도 중복이 없습니다(멱등). 마음에 안 드는 룰은 `harness rm`으로 제거하면 됩니다.
|
|
159
170
|
- 🅷⚠ 런타임 경고(`ratchet?` `no-evidence` `PEV-skip`)는 30분 후 자동 만료되고, 하위 디렉터리 세션도 프로젝트에 올바르게 매칭됩니다. PEV-skip은 변경성 도구(Edit/Write/Bash)만 카운트해 읽기 위주 세션에서는 발동하지 않습니다 (v2.16.0+).
|
|
160
171
|
|
|
161
172
|
<details>
|
|
162
|
-
<summary>⚠️ <code>harness rm</code>은 신중하게
|
|
173
|
+
<summary>⚠️ <code>harness rm</code>은 신중하게 사용하십시오: 삭제 전 확인 사항</summary>
|
|
163
174
|
|
|
164
175
|
ratchet의 가치는 **한 방향 누적**에 있습니다. 룰을 가볍게 지우면 같은 실수가 다시 새기 시작합니다.
|
|
165
176
|
|
|
@@ -171,13 +182,13 @@ ratchet의 가치는 **한 방향 누적**에 있습니다. 룰을 가볍게 지
|
|
|
171
182
|
</details>
|
|
172
183
|
|
|
173
184
|
|
|
174
|
-
## 📦 compact-window
|
|
185
|
+
## 📦 compact-window: 1M 컨텍스트의 자동 압축 지점 고정
|
|
175
186
|
|
|
176
187
|
Claude Code는 `min(autoCompactWindow, 모델 최대 창)`에 가까워지면 대화를 자동 압축합니다. 1M 창을 쓰면 이 값이 잡혀 있지 않은 한 80만 토큰 근처까지 가서야 압축이 걸리고, 그전까지 모든 요청이 전체 컨텍스트를 통째로 재과금합니다. **1M은 너무 크니 40만~70만 범위를 권장합니다** — 큰 붙여넣기용 여유는 200k 세션의 2~3.5배로 남기면서 꼬리만 잘라냅니다.
|
|
177
188
|
|
|
178
189
|
**권장 범위 안이면 경고하지 않습니다.** 40만은 절감이 압축 횟수를 이기는 하한이고, 긴 세션은 그보다 여유가 더 필요한 경우가 많습니다. 미설정이거나 70만을 넘을 때만 알립니다(그보다 낮게 잡은 건 더 공격적으로 아끼겠다는 선택이라 그냥 둡니다).
|
|
179
190
|
|
|
180
|
-
**200k 컨텍스트는 경고 대상이
|
|
191
|
+
**200k 컨텍스트는 경고 대상이 아닙니다.** 창이 이미 200k 이하이므로 이 설정으로 달라지는 것이 없기 때문입니다.
|
|
181
192
|
|
|
182
193
|
```bash
|
|
183
194
|
claude-token-saver compact-window # 현재 상태 (모델·창·설정값·출처)
|
|
@@ -188,29 +199,29 @@ claude-token-saver compact-window off | on # 경고 표시 토글
|
|
|
188
199
|
```
|
|
189
200
|
|
|
190
201
|
- 1M 모델인데 미설정이거나 40만을 넘으면 statusline에 `🅷⚠ compact-window?`가 뜨고, 세션 브리핑이 등록 명령까지 알려줍니다.
|
|
191
|
-
-
|
|
202
|
+
- 적용 범위(`--global` 또는 `--project`)는 `set`에서 **반드시 지정해야 합니다.** 글로벌 설정 파일을 사용자에게 묻지 않고 수정하는 일을 막기 위한 설계입니다.
|
|
192
203
|
- 기존 `settings.json`의 다른 키는 그대로 보존하고 `.bak`을 남깁니다. JSON이 깨져 있으면 아무것도 쓰지 않고 중단합니다.
|
|
193
204
|
- 셸에 `CLAUDE_CODE_AUTO_COMPACT_WINDOW`가 export돼 있으면 그쪽이 settings.json보다 우선합니다 (`set`이 이 경우를 감지해 알려줍니다).
|
|
194
205
|
|
|
195
|
-
## 🔀 route-scan
|
|
206
|
+
## 🔀 route-scan: "이 반복 작업은 더 싼 티어로 내려도 됩니다"
|
|
196
207
|
|
|
197
208
|
세션 로그에서 상위 모델(opus/fable)이 반복 처리해 온 쉬운 작업을 찾아 **haiku/sonnet 위임 룰로 승격**을 제안합니다. 전 과정 로컬, 토큰 비용 0.
|
|
198
209
|
|
|
199
|
-
- **T2 → haiku
|
|
200
|
-
- **T1 → sonnet
|
|
201
|
-
- **T0
|
|
210
|
+
- **T2 → haiku:** 탐색과 조회, 단순 실행에 해당합니다. 에러가 없고 변경도 거의 없는 작업입니다.
|
|
211
|
+
- **T1 → sonnet:** 빌드와 상태 점검에 해당합니다. 변경이 적고 에러가 1건 이하인 작업입니다.
|
|
212
|
+
- **T0 유지:** 에러가 반복되거나 변경이 많거나 설계와 분석이 필요한 작업입니다. 세션 모델이 계속 담당합니다.
|
|
202
213
|
|
|
203
214
|
핵심 설계는 세 가지입니다:
|
|
204
|
-
1.
|
|
205
|
-
2. 임계값은
|
|
206
|
-
3. 승격된 룰은
|
|
215
|
+
1. 난이도를 텍스트로 추측하지 않고 **실제 결과로 판정합니다.** 도구 에러와 변경을 일으킨 도구의 수, 출력 토큰을 근거로 삼습니다.
|
|
216
|
+
2. 임계값은 **사용자의 최근 14일 로그 분포에서 자동으로 보정합니다.** 고정된 상수는 워크로드가 바뀌면 곧 어긋나기 때문입니다.
|
|
217
|
+
3. 승격된 룰은 도구가 관리하는 별도 파일(`.claude/ratchet-model.md`)에서 **자동으로 갱신되며,** 위임한 뒤 에러율이 높아지면 `⚠ rule-health`로 경고합니다. 룰이 낡았다는 사실을 스스로 알리는 셈입니다.
|
|
207
218
|
|
|
208
219
|
```bash
|
|
209
220
|
claude-token-saver route-scan # 스캔 (24h 캐시) + 티어별 후보 출력
|
|
210
221
|
claude-token-saver harness promote R1 --project # 후보 R1을 모델 피팅 룰로 등록
|
|
211
222
|
claude-token-saver route-scan dismiss 1 # 관심 없으면 무시 (재스캔에도 안 뜸)
|
|
212
223
|
claude-token-saver route-scan rules # 등록된 모델 피팅 룰 목록 (rm <N>으로 제거)
|
|
213
|
-
claude-token-saver route-scan savings # 절감
|
|
224
|
+
claude-token-saver route-scan savings # 절감 원장: 어느 룰이 어떤 모델에서 어떤 모델로 옮겼는지
|
|
214
225
|
```
|
|
215
226
|
|
|
216
227
|
`route-scan savings`는 statusline의 `🔀 Routing saved` 한 줄 뒤에 있는 근거를 그대로 보여줍니다. 모델 이동별 합계와 실행별 내역이 함께 나오므로, 금액이 어디서 나왔는지 추적할 수 있습니다.
|
|
@@ -247,11 +258,32 @@ v3.10.0부터는 프로파일 ID를 역할(main·opus·sonnet·haiku)로 되돌
|
|
|
247
258
|
|
|
248
259
|
이 파일에는 사내 식별자가 평문으로 남으므로 저장소에 커밋하지 마십시오. 게이트웨이를 쓰지 않는 환경에서는 파일이 아예 만들어지지 않고 기존 동작이 그대로 유지됩니다.
|
|
249
260
|
|
|
261
|
+
## 🇰🇷 한국어 문체 지침
|
|
262
|
+
|
|
263
|
+
Claude가 한국어로 쓸 때 나타나는 문체 결함(문장 성분 생략, 명사형 종결, 번역체, 엠대시 남용)을 교정하는 지침을 **세션 시작 시 한 번 주입합니다.**
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
claude-token-saver korean on # 켜기 (모든 프로젝트에 적용)
|
|
267
|
+
claude-token-saver korean status # 상태·비용·출처 확인
|
|
268
|
+
claude-token-saver korean show # 지침 원문 출력
|
|
269
|
+
claude-token-saver korean off # 끄기
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Claude Code의 output style로도 같은 일을 할 수 있지만, output style은 **전역 슬롯 하나**라서 켜는 순간 다른 스타일을 못 쓰게 되고 머신마다 따로 설정해야 합니다. 이 기능은 지침을 패키지에 담고 이미 설치된 SessionStart 훅으로 전달하므로, **CLI가 설치된 모든 프로젝트에 적용되며 output style 슬롯은 비워 둡니다.** `/clear` 이후에도 훅이 다시 실행되어 유지됩니다.
|
|
273
|
+
|
|
274
|
+
비용은 **세션당 약 1,500 토큰이며 매 턴이 아니라 세션 시작에 한 번만** 주입됩니다. 두 번째 요청부터는 프롬프트 캐시에 올라가므로 추가 부담이 거의 없습니다. 켜져 있으면 statusline에 `가` 칩이 표시됩니다.
|
|
275
|
+
|
|
276
|
+
**설치할 때 자동으로 결정됩니다.** 시스템 로캘이 한국어이면(`ko_KR` 등, macOS는 시스템 설정까지 확인) 설치와 동시에 켜지고, 한국어 환경이 아니면 꺼진 채로 둡니다. 한국어를 쓰지 않는 사용자에게 세션마다 1,500 토큰을 청구하지 않기 위한 판단입니다. **한 번이라도 직접 켜거나 끈 뒤에는 그 선택을 유지하므로, 업데이트 설치가 사용자의 결정을 되돌리지 않습니다.** 자동 설정을 원하지 않으면 `CTS_NO_KOREAN=1`을 붙여 설치하십시오.
|
|
277
|
+
|
|
278
|
+
> **출처와 라이선스**
|
|
279
|
+
> 지침 원문은 [fluent-korean](https://github.com/snflkd/fluent-korean)에서 가져왔습니다. Copyright (c) 2026 snflkd, MIT License.
|
|
280
|
+
> 원문은 수정하지 않았고 output style 프런트매터만 제거했습니다. 라이선스 전문은 패키지의 `presets/korean-style/LICENSE-fluent-korean`에 함께 배포합니다.
|
|
281
|
+
|
|
250
282
|
## 토큰 급증 원인 코드
|
|
251
283
|
|
|
252
284
|
| 코드 | 의미 |
|
|
253
285
|
|---|---|
|
|
254
|
-
| `LARGE_INPUT_PER_REQUEST` | 단일
|
|
286
|
+
| `LARGE_INPUT_PER_REQUEST` | 단일 요청의 입력이 200k를 초과했습니다. 턴마다 다시 과금되고 한도 소모가 급격히 늘어납니다 |
|
|
255
287
|
| `LOW_HIT_RATE` | 캐시 히트율 50% 미만 |
|
|
256
288
|
| `BUCKET_5M_DOMINANT` | 캐시 쓰기의 70%+가 5분 버킷 (Pro 플랜/Max 다운그레이드) |
|
|
257
289
|
| `HIGH_OUTPUT_RATIO` | 출력/입력 비율 0.15 초과 (출력 단가는 입력의 5배) |
|
|
@@ -260,9 +292,9 @@ v3.10.0부터는 프로파일 ID를 역할(main·opus·sonnet·haiku)로 되돌
|
|
|
260
292
|
|
|
261
293
|
각 코드마다 OS별 해결 명령이 함께 출력됩니다.
|
|
262
294
|
|
|
263
|
-
## 실제
|
|
295
|
+
## 실제 효과: 도입 전후 리포트
|
|
264
296
|
|
|
265
|
-

|
|
266
298
|
|
|
267
299
|
harness 5/5 + ratchet을 실제 적용한 전후 비교입니다 (저자 Claude Code 로그, **사용자 메시지 1건당** 정규화, 2026-05-02 기준, Opus 4.7 가격):
|
|
268
300
|
|
|
@@ -276,7 +308,7 @@ harness 5/5 + ratchet을 실제 적용한 전후 비교입니다 (저자 Claude
|
|
|
276
308
|
같은 요청을 더 적은 왕복으로 끝낸다 = 첫 시도 적중률 ↑. PEV·Structured Task가 한 번에 가게 만든 효과로 보입니다.
|
|
277
309
|
|
|
278
310
|
<details>
|
|
279
|
-
<summary>측정
|
|
311
|
+
<summary>측정 배경: 캐시 히트율을 제외한 이유와 표본에 관한 주의 사항</summary>
|
|
280
312
|
|
|
281
313
|
- 저자는 Max 플랜(캐시 TTL 1시간)이라 히트율이 이미 ~98%에 수렴해 개선 여지가 작았습니다. **Pro 플랜(5분 TTL) 사용자는** 만료 직전 handoff 워크플로 조합으로 히트율 자체가 오를 가능성이 큽니다.
|
|
282
314
|
- 만료 직전 handoff 워크플로: statusline TTL 카운트다운을 보다가 만료 직전 `claude-token-saver handoff`로 작업 상태를 백업하고 새 캐시 사이클을 시작. 1M 경고·cap 칩도 같은 흐름으로 처리.
|
|
@@ -292,7 +324,7 @@ Node.js ≥ 18 · macOS / Linux / Windows / WSL · **의존성 0**.
|
|
|
292
324
|
<details>
|
|
293
325
|
<summary>알려진 환경 이슈 · 마이그레이션</summary>
|
|
294
326
|
|
|
295
|
-
**IntelliJ Claude Code plugin
|
|
327
|
+
**IntelliJ Claude Code plugin:** statusline 위젯이 프레임을 잘못 합성해 `59:548` 같은 잔재가 보이는 버그가 있습니다(이모지 출력에서만). v2.8.5+는 `TERMINAL_EMULATOR=JetBrains-JediTerm` 감지 시 자동으로 text 모드 폴백합니다.
|
|
296
328
|
|
|
297
329
|
**claude-cache-monitor에서 마이그레이션:**
|
|
298
330
|
```bash
|
|
@@ -303,6 +335,15 @@ npm uninstall -g claude-cache-monitor && npm i -g claude-token-saver
|
|
|
303
335
|
|
|
304
336
|
## 릴리스 노트
|
|
305
337
|
|
|
338
|
+
### v3.19.0 (2026-08-22)
|
|
339
|
+
- **한국어 문체 지침 기능을 추가했습니다** — Claude가 한국어를 쓸 때 나타나는 문체 결함(문장 성분 생략, 명사형 종결, 번역체, 엠대시 남용)을 교정하는 지침을 세션 시작 시 한 번 주입합니다. Claude Code의 output style은 전역 슬롯 하나를 차지하고 머신마다 설정해야 하지만, 이 기능은 지침을 패키지에 담고 이미 설치된 SessionStart 훅으로 전달하므로 **CLI가 설치된 모든 프로젝트에 적용되며 output style 슬롯은 비워 둡니다.** 지침 원문은 [fluent-korean](https://github.com/snflkd/fluent-korean)(Copyright (c) 2026 snflkd, MIT)에서 가져왔고 라이선스 전문을 함께 배포합니다.
|
|
340
|
+
- **설치와 동시에 결정됩니다** — 시스템 로캘이 한국어이면 설치 시 자동으로 켜지고, 아니면 꺼 둡니다. 직접 켜거나 끈 뒤에는 그 선택을 유지하므로 업데이트가 사용자의 결정을 되돌리지 않습니다. `CTS_NO_KOREAN=1`로 건너뛸 수 있고, 켜져 있으면 statusline에 `가` 칩이 표시됩니다.
|
|
341
|
+
- **README의 과장된 설명을 바로잡았습니다** — 라우팅 절감액을 "이 도구의 전부"라고 적었으나, 실측 −18.6%는 Harness와 ratchet을 도입한 효과입니다. 세 기능의 관계를 정확히 다시 썼습니다.
|
|
342
|
+
|
|
343
|
+
### v3.18.0 (2026-08-22)
|
|
344
|
+
- **한국어 문서를 다시 다듬었습니다** — 문장 성분을 생략하지 않고 서술어로 끝맺는 형태로 본문을 고쳐 썼습니다. 의미를 지나치게 함축하던 엠대시는 콜론과 접속사로 바꾸었습니다.
|
|
345
|
+
- **실시간 모델 라우팅이 비용을 키우는 이유를 설명에 추가했습니다** — 프롬프트 캐시가 모델별로 유지되기 때문에 세션 중간에 모델을 바꾸면 절감액이 캐시 손실로 상쇄된다는 점, 그래서 이 도구가 서브에이전트 위임만 사용한다는 점을 명시했습니다.
|
|
346
|
+
|
|
306
347
|
### v3.17.0 (2026-08-22)
|
|
307
348
|
- **설치 한 번으로 🅷 Harness까지 적용됩니다** — 지금까지는 설치 후 `harness init`을 따로 실행해야 statusline의 🅷 점수와 ratchet 룰 전달이 동작했습니다. 이제 설치가 `~/.claude/CLAUDE.md`에 5원칙 블록을 **추가**합니다(기존 내용은 백업 후 보존, 이미 있으면 건드리지 않음). 건너뛰려면 `CTS_NO_HARNESS=1`, 되돌리려면 `harness uninit --global`.
|
|
308
349
|
- **README 상단을 statusline 실제 스크린샷으로 교체** — 코드 블록 대신 실제 캡처를 최상단에 두고, 아래쪽에 중복으로 있던 이미지는 뺐습니다.
|
package/bin/cli.js
CHANGED
|
@@ -135,6 +135,13 @@ async function main() {
|
|
|
135
135
|
return (await import('../src/commands/route-scan.js')).run({ args, hasFlag, numArg });
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
// Subcommand: korean — Korean writing guidance injected at session start,
|
|
139
|
+
// so the rules apply in every project without an output-style switch.
|
|
140
|
+
// claude-token-saver korean on | off | status | show
|
|
141
|
+
if (args[0] === 'korean') {
|
|
142
|
+
return (await import('../src/commands/korean.js')).run({ args, hasFlag });
|
|
143
|
+
}
|
|
144
|
+
|
|
138
145
|
// Subcommand: harness — manage the project's CLAUDE.md harness rules.
|
|
139
146
|
// claude-token-saver harness init # write CLAUDE.md (5 sections) + ratchet.md
|
|
140
147
|
// claude-token-saver harness uninit # remove harness block from CLAUDE.md (backup kept)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-token-saver",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.19.0",
|
|
4
4
|
"description": "Route the easy work your expensive Claude model keeps repeating down to haiku/sonnet — post-hoc session analysis, no realtime router, no extra LLM calls.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 snflkd
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OF OR IN CONNECTION WITH
|
|
21
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Vendored from fluent-korean (https://github.com/snflkd/fluent-korean)
|
|
3
|
+
Copyright (c) 2026 snflkd — MIT License. Full text: ./LICENSE-fluent-korean
|
|
4
|
+
|
|
5
|
+
Only the output-style frontmatter was removed; the guidance below is
|
|
6
|
+
unmodified. claude-token-saver injects it at session start so the rules
|
|
7
|
+
apply in every project without installing the plugin or switching the
|
|
8
|
+
Claude Code output style.
|
|
9
|
+
-->
|
|
10
|
+
|
|
11
|
+
당신은 한국어를 활용해야 하는 상황이라면 본 문서에 제시된 지침들을 준수해야 합니다. 그럼으로써 의사 소통의 효율성을 높일 수 있습니다. 이 지침들은, 의미가 명확하며 비교적 가독성이 높고 안정적인 구조를 지닌 한국어 문장을 출력하는 방법을 자세히 설명합니다. 인용, 코드, 코드 주석에는 이 지침들을 적용하지 않습니다.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## 상황과 목표
|
|
15
|
+
|
|
16
|
+
- LLM은 한국어를 구사할 때 몇 가지 특징을 보이는데, 일부 특징은 결과물의 완성도를 낮추거나, 사용자가 소통에 더 많은 노력을 들이게 만듭니다. 이 문서에 작성된 사항들을 준수하면 이런 현상을 개선할 수 있습니다.
|
|
17
|
+
|
|
18
|
+
- 이 문서에서 제시하는 지침들을 요약하는 것은 일반적으로 권장되지 않습니다. 그렇게 한다면 조항마다 첨부된 예시를 확인할 수 없으므로 조항의 문구가 구체적으로 어떤 동작을 의도했는지 파악하기 어렵습니다. 또한 요약에 포함된 몇 가지 지침을 제외한 나머지 지침들은 잘 준수되지 않는 방향으로 서술 압력이 작동하게 될 수도 있습니다. 그리고 목적과 의도를 생략하고 제한 사항만 요약한다면 목적에 부합하지 않게 기계적으로 지침을 준수했는지 확인하게 될 수도 있습니다.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## 동작 범위
|
|
22
|
+
|
|
23
|
+
1. 본문의 지침들은 한국어를 활용하는 상황에서 그 한국어를 명확하게 출력하라는 지시입니다. 외국어 문장이나 어휘를 출력해야 하는 상황에서, 그것을 한국어로 번역하거나 대체하라는 지시가 아닙니다.
|
|
24
|
+
|
|
25
|
+
2. 변수명과 주석, 커밋 메시지, 로그 문자열처럼 코드에 속하는 텍스트는 프로젝트의 기존 관례를 준수해야 합니다. 이러한 텍스트는 지침을 적용하면 안 되기 때문에 이 조항에서 한 번 더 강조하고 있습니다.
|
|
26
|
+
|
|
27
|
+
3. 고유 명사와 기술 용어 등은, 통상적인 용례로 정착된 번역어 혹은 음차가 있다면 우선적으로 사용하고, 그렇지 않다면 원어를 유지함으로써, 한국어 사용자가 이해하기 편하고 의미를 잘 이해할 수 있도록 합니다.
|
|
28
|
+
|
|
29
|
+
4. 사용자가 어떤 어조나 어휘를 사용하든지, 사용자 메시지의 어조를 모방하지 않고, 본문에서 제시하는 지침들을 일관되게 유지합니다.
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
## 문장 단위
|
|
33
|
+
|
|
34
|
+
1. 읽는 이가 문장의 의미를 충분히 이해할 수 있어야 하므로, 의미가 있는 문장 성분을 생략하지 않습니다. [그러면 경고가 붙습니다.→ ('그러면 이미 작업중인 파일에도 경고 표지가 추가됩니다.'와 같이, 맥락과 정보를 충분히 제공하도록 수정) ] 특히 보조사 '의'를 필요 이상으로 사용한다면, 의미를 담고 있는 문장 성분을 생략하기 쉬우므로 유의해야 합니다. [사본의 문구는 작업의 상황을 → 사본에 기재된 문구는 작업이 진행되는 상황을]
|
|
35
|
+
|
|
36
|
+
2. (이 2번 조항은 헤더와 목록에는 강제로 적용되는 사항이 아닙니다.) 명사구나 부사구, 연결어미로 문장을 끝내지 말고, 서술어와 종결어미를 사용하여 완성된 형태의 문장으로 끝을 맺어야 합니다.
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
## 구 단위
|
|
40
|
+
|
|
41
|
+
1. 필수적인 경우가 아니라면 조사와 어미를 생략하지 말아야 합니다. 또한 부사, 보조사와 선어말어미, 보조 용언을 적극적으로 활용하면, 의미가 명확한 한국어 문장을 완성할 수 있습니다. [이 결정은 이후 중요 정책이 갈리는 자리. 컨텍스트 압축 전 신중 반영한다. → 이 결정은 이후 중요한 정책에 지속적으로 영향을 주기 때문에, 컨텍스트가 압축되기 전에 신중히 반영합니다. → 지금 답변해주신 결정 사항은 이후 중요한 정책에도 지속적으로 영향을 미치기 때문에, 컨텍스트가 압축되기 전에 미리 신중하게 반영해 놓겠습니다.]
|
|
42
|
+
|
|
43
|
+
2. 구체적인 의미를 담고 있는 한자어와 자연스러운 통사 구조를 결합하면, 풍부하고 명확한 의미를 전달할 수 있습니다. 따라서 맥락에 적합한 한자어를 적극적으로 활용하고, 그 한자어에 조사와 어미를 붙여서 어휘 사이의 관계를 확실하게 나타내야 합니다. [<쓴 비용을 구하는 토큰 카운트 함수에 문제가 생기면 (상황에 적합한 어휘가 사용되지 않아 의미가 불충분함) /지출 비용 추론 용도의 토큰 카운트 함수의 오류 상황에서 (조사와 어미가 없어 가독성이 낮고 의미 관계가 불분명함)> → 지출한 비용을 추론하는 토큰 카운트 함수에 오류가 발생하면 (이 지침의 목표 예시)]
|
|
44
|
+
|
|
45
|
+
3. 일반적인 어휘를 사용해야 하는 자리에 비유적 어휘를 사용하면 가독성이 낮고, 의미가 변질되기 쉽습니다. 따라서 꼭 필요한 경우가 아니라면 비유적 어휘로 일반적인 명사나 동사를 대체하지 않습니다. 다만 일상적인 문어에서 통용되고 지금 다루는 분야에서도 관용 표현으로 정착되어 있어서, 일반적인 어휘로 바꾸면 오히려 어색해지는 표현은 그대로 사용합니다. [<분석의 흐름 → 분석의 방향성>, <코드로 박는 자리 → 코드에 명시하는 상황 (혹은 코드에 명시하는 작업)>, <요청을 받습니다 -> 요청을 확인했습니다 (혹은 요청대로 수행하겠습니다)>]
|
|
46
|
+
|
|
47
|
+
4. 엠대시(—)는 앞뒤 문장의 관계를 지나치게 함축하기 때문에 자제하고, 문맥과 형식에 따라 콜론이나 접속사로 대체합니다.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
## 추가 사항
|
|
51
|
+
|
|
52
|
+
- 서브에이전트를 호출할 때, 한국어로 프롬프트를 작성했다면 실제로 서브에이전트 호출 도구를 사용하기 전에 이 본문의 지침들이 준수되어 있는지 점검합니다. 서브에이전트가 산출한 결과를 사용자에게 전달할 때에도 본문의 지침들이 그대로 적용됩니다.
|
package/src/commands/install.js
CHANGED
|
@@ -108,6 +108,45 @@ export async function run({ hasFlag }) {
|
|
|
108
108
|
: ' harness: auto-setup skipped — run `claude-token-saver harness init --global` yourself.');
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
// Korean writing guidance. Decided here rather than left to a command the
|
|
112
|
+
// user has to find, because the people who need it are exactly the ones
|
|
113
|
+
// who would not know to look for it. Enabled when the machine's locale
|
|
114
|
+
// says Korean; left alone once the user has answered either way, so an
|
|
115
|
+
// upgrade never re-enables something they turned off.
|
|
116
|
+
try {
|
|
117
|
+
const ks = await import('../korean-style.js');
|
|
118
|
+
if (process.env.CTS_NO_KOREAN === '1') {
|
|
119
|
+
console.log('');
|
|
120
|
+
console.log(lang === 'ko'
|
|
121
|
+
? ' korean: CTS_NO_KOREAN=1 이므로 건너뜁니다 (나중에 `korean on`).'
|
|
122
|
+
: ' korean: skipped (CTS_NO_KOREAN=1) — run `korean on` later.');
|
|
123
|
+
} else if (ks.koreanStyleDecided()) {
|
|
124
|
+
console.log('');
|
|
125
|
+
console.log(lang === 'ko'
|
|
126
|
+
? ` korean: 기존 설정 유지 — 한국어 문체 지침 ${ks.koreanStyleEnabled() ? '켜짐' : '꺼짐'}`
|
|
127
|
+
: ` korean: keeping your setting — Korean writing guidance is ${ks.koreanStyleEnabled() ? 'on' : 'off'}`);
|
|
128
|
+
} else if (ks.koreanLocaleDetected()) {
|
|
129
|
+
ks.setKoreanStyleEnabled(true);
|
|
130
|
+
console.log('');
|
|
131
|
+
console.log(lang === 'ko'
|
|
132
|
+
? ' korean: 한국어 환경이 감지되어 문체 지침을 켰습니다 — 모든 프로젝트의 세션 시작 시 주입됩니다.'
|
|
133
|
+
: ' korean: Korean locale detected — writing guidance enabled, injected at session start in every project.');
|
|
134
|
+
console.log(lang === 'ko'
|
|
135
|
+
? ` 출처: ${ks.KOREAN_STYLE_SOURCE}`
|
|
136
|
+
: ` source: ${ks.KOREAN_STYLE_SOURCE}`);
|
|
137
|
+
console.log(lang === 'ko'
|
|
138
|
+
? ' 끄려면: claude-token-saver korean off'
|
|
139
|
+
: ' turn off with: claude-token-saver korean off');
|
|
140
|
+
} else {
|
|
141
|
+
console.log('');
|
|
142
|
+
console.log(lang === 'ko'
|
|
143
|
+
? ' korean: 한국어 환경이 아니어서 꺼 두었습니다 — 필요하면 `claude-token-saver korean on`.'
|
|
144
|
+
: ' korean: left off (no Korean locale detected) — enable with `claude-token-saver korean on`.');
|
|
145
|
+
}
|
|
146
|
+
} catch (e) {
|
|
147
|
+
debug('install:korean-style', e); // optional feature; never fail install
|
|
148
|
+
}
|
|
149
|
+
|
|
111
150
|
console.log('');
|
|
112
151
|
console.log('Open Claude Code in any directory and just mention:');
|
|
113
152
|
console.log(' "cache hit rate" / "1M context" / "5H cap" — the skill auto-activates.');
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subcommand: korean — Korean writing guidance for every session.
|
|
3
|
+
* claude-token-saver korean on # inject at session start, all projects
|
|
4
|
+
* claude-token-saver korean off # stop injecting
|
|
5
|
+
* claude-token-saver korean status # current state, cost, and provenance
|
|
6
|
+
* claude-token-saver korean show # print the guidance itself
|
|
7
|
+
*
|
|
8
|
+
* Why this exists rather than pointing users at Claude Code's output styles:
|
|
9
|
+
* an output style is one global slot, so turning it on takes the slot away
|
|
10
|
+
* from whatever else the user had there, and it has to be configured on every
|
|
11
|
+
* machine. This ships the guidance with the package and delivers it through
|
|
12
|
+
* the SessionStart hook that is already installed, so it applies everywhere
|
|
13
|
+
* the CLI is installed and leaves the output-style slot free.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export async function run({ args }) {
|
|
17
|
+
const sub = args[1] || 'status';
|
|
18
|
+
const ks = await import('../korean-style.js');
|
|
19
|
+
const { userLanguage } = await import('../config.js');
|
|
20
|
+
const lang = userLanguage();
|
|
21
|
+
|
|
22
|
+
if (sub === 'show') {
|
|
23
|
+
const text = ks.koreanStyleText();
|
|
24
|
+
if (!text) {
|
|
25
|
+
console.error('Korean style guidance file is missing from the package.');
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
console.log(text);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (sub === 'on' || sub === 'off') {
|
|
33
|
+
const enabled = sub === 'on';
|
|
34
|
+
ks.setKoreanStyleEnabled(enabled);
|
|
35
|
+
if (enabled) {
|
|
36
|
+
console.log(lang === 'ko'
|
|
37
|
+
? '한국어 문체 지침을 켰습니다. 다음 세션부터 모든 프로젝트에 적용됩니다.'
|
|
38
|
+
: 'Korean writing guidance is on. It applies in every project from the next session.');
|
|
39
|
+
console.log(lang === 'ko'
|
|
40
|
+
? ' 주입 시점: 세션 시작 1회 (매 턴이 아니므로 두 번째 요청부터는 캐시에 올라갑니다)'
|
|
41
|
+
: ' Injected once per session (not per turn), so it rides the prompt cache from the second request on.');
|
|
42
|
+
console.log(lang === 'ko'
|
|
43
|
+
? ` 출처: ${ks.KOREAN_STYLE_SOURCE}`
|
|
44
|
+
: ` Source: ${ks.KOREAN_STYLE_SOURCE}`);
|
|
45
|
+
} else {
|
|
46
|
+
console.log(lang === 'ko'
|
|
47
|
+
? '한국어 문체 지침을 껐습니다. 다음 세션부터 주입하지 않습니다.'
|
|
48
|
+
: 'Korean writing guidance is off. Nothing is injected from the next session.');
|
|
49
|
+
}
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// status (default)
|
|
54
|
+
const on = ks.koreanStyleEnabled();
|
|
55
|
+
const text = ks.koreanStyleText();
|
|
56
|
+
// 4 bytes/token is the usual mixed ko/en approximation, same as the ratchet
|
|
57
|
+
// size report — this is the number the user is trading for the style.
|
|
58
|
+
const tokens = text ? Math.round(Buffer.byteLength(text, 'utf8') / 4) : 0;
|
|
59
|
+
if (lang === 'ko') {
|
|
60
|
+
console.log(`한국어 문체 지침: ${on ? '켜짐' : '꺼짐'}`);
|
|
61
|
+
console.log(` 비용: 세션당 약 ${tokens} 토큰 (세션 시작 1회 주입)`);
|
|
62
|
+
console.log(` 출처: ${ks.KOREAN_STYLE_SOURCE}`);
|
|
63
|
+
console.log(` 라이선스 전문: ${ks.KOREAN_STYLE_LICENSE_PATH}`);
|
|
64
|
+
console.log(on
|
|
65
|
+
? ' 끄려면: claude-token-saver korean off'
|
|
66
|
+
: ' 켜려면: claude-token-saver korean on');
|
|
67
|
+
} else {
|
|
68
|
+
console.log(`Korean writing guidance: ${on ? 'on' : 'off'}`);
|
|
69
|
+
console.log(` Cost: ~${tokens} tokens per session (injected once at session start)`);
|
|
70
|
+
console.log(` Source: ${ks.KOREAN_STYLE_SOURCE}`);
|
|
71
|
+
console.log(` License text: ${ks.KOREAN_STYLE_LICENSE_PATH}`);
|
|
72
|
+
console.log(on
|
|
73
|
+
? ' Turn off with: claude-token-saver korean off'
|
|
74
|
+
: ' Turn on with: claude-token-saver korean on');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -159,7 +159,21 @@ export async function run({ args, hasFlag, numArg }) {
|
|
|
159
159
|
.map((r, i) => ({ ...r, n: i + 1 }))
|
|
160
160
|
.filter((r) => r.status === 'review');
|
|
161
161
|
} catch (e) { debug('route-scan:load-rules', e); /* candidate briefing still goes out */ }
|
|
162
|
-
|
|
162
|
+
// Korean writing guidance, when the user enabled it. Printed before the
|
|
163
|
+
// route-scan briefing and independently of it: the style has to reach a
|
|
164
|
+
// session even when there is no candidate to report, which is the usual
|
|
165
|
+
// case. Injecting here rather than through a separate hook keeps it on
|
|
166
|
+
// one SessionStart round-trip and one cached prefix.
|
|
167
|
+
let koreanBlock = null;
|
|
168
|
+
try {
|
|
169
|
+
const { koreanStyleInjection } = await import('../korean-style.js');
|
|
170
|
+
koreanBlock = koreanStyleInjection();
|
|
171
|
+
} catch (e) { debug('route-scan:korean-style', e); /* style is optional */ }
|
|
172
|
+
|
|
173
|
+
if (open.length === 0 && reviewRules.length === 0) {
|
|
174
|
+
if (koreanBlock) console.log(koreanBlock);
|
|
175
|
+
return; // nothing else to inject
|
|
176
|
+
}
|
|
163
177
|
// This text is injected straight into the model's context, so it must
|
|
164
178
|
// follow the user's configured language — a Korean-only briefing in an
|
|
165
179
|
// English session steers the whole first response into Korean.
|
|
@@ -223,6 +237,9 @@ export async function run({ args, hasFlag, numArg }) {
|
|
|
223
237
|
lines.push(lang === 'ko'
|
|
224
238
|
? '위 내용은 사용자의 첫 메시지 주제와 무관하더라도(단순 인사여도) 첫 응답 말미에 `※ [claude-token-saver]` 라벨을 달아 요약 브리핑하세요 — 사용자가 물어볼 때까지 기다리지 말 것.'
|
|
225
239
|
: 'Summarize the above at the end of your first response under a `※ [claude-token-saver]` label, even if the user\'s first message is unrelated (a bare greeting counts) — do not wait to be asked.');
|
|
240
|
+
// Style first, briefing second: the briefing is Korean prose too, so the
|
|
241
|
+
// guidance has to be in context before the model reads it.
|
|
242
|
+
if (koreanBlock) console.log(koreanBlock + '\n');
|
|
226
243
|
console.log(lines.join('\n'));
|
|
227
244
|
// Record what was actually briefed so the UserPromptSubmit brief hook
|
|
228
245
|
// suppresses exactly these — a candidate landing after this read (e.g.
|
|
@@ -21,6 +21,7 @@ import { formatResetClock } from '../format-time.js';
|
|
|
21
21
|
import { labelForKey } from '../window-labels.js';
|
|
22
22
|
import { harnessStatusForStatusline } from '../harness.js';
|
|
23
23
|
import { loadConfig } from '../config.js';
|
|
24
|
+
import { koreanStyleEnabled } from '../korean-style.js';
|
|
24
25
|
|
|
25
26
|
// The 8-color ANSI defaults (RED=31, GREEN=32, YELLOW=33…) read as garish
|
|
26
27
|
// next to each other — terminal palettes set them with unbalanced perceptual
|
|
@@ -137,6 +138,24 @@ export function pickCapWarn(caps) {
|
|
|
137
138
|
return candidates[0];
|
|
138
139
|
}
|
|
139
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Korean-style chip builder. Renders only when the session-start injection is
|
|
143
|
+
* enabled, so nothing changes for anyone who never asked for it.
|
|
144
|
+
*/
|
|
145
|
+
function buildKoreanSeg(c, isIcon, verbose) {
|
|
146
|
+
try {
|
|
147
|
+
if (!koreanStyleEnabled()) return null;
|
|
148
|
+
// Deliberately quiet (gray, one syllable): this is a "yes, it is on"
|
|
149
|
+
// confirmation, not a warning. Without it a silently-failed hook looks
|
|
150
|
+
// exactly like a working one, because the style only shows up when the
|
|
151
|
+
// model happens to write Korean.
|
|
152
|
+
if (isIcon) return `${c(GRAY)}${verbose ? '가 Korean style' : '가'}${c(RESET)}`;
|
|
153
|
+
return `${c(GRAY)}Korean style${c(RESET)}`;
|
|
154
|
+
} catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
140
159
|
/**
|
|
141
160
|
* Harness 🅷 segment builder — shared by the full report and the no-session
|
|
142
161
|
* fallback line. Best-effort: never throws into the statusline (corrupted
|
|
@@ -211,7 +230,7 @@ export function formatNoSession({ caps = null, model = null, windowLabel = '' }
|
|
|
211
230
|
* @param {boolean} [opts.verbose=false] - longer layout with labels
|
|
212
231
|
* @param {boolean} [opts.timer=true] - show TTL countdown segment
|
|
213
232
|
* @param {'text'|'icon'} [opts.mode='text'] - label style. 'icon' uses 🧠 ⏳ 💰 instead of word labels.
|
|
214
|
-
* @param {string[]|null} [opts.segments] - whitelist of segments to render. Names: cap-warn, spike, harness, model, hit, ttl, saved, delegated, ctx, period, plus per-window keys (`five_hour`, `seven_day`, …). `5h`/`7d` are kept as aliases for back-compat. Null/undefined = all.
|
|
233
|
+
* @param {string[]|null} [opts.segments] - whitelist of segments to render. Names: cap-warn, spike, harness, korean, model, hit, ttl, saved, delegated, ctx, period, plus per-window keys (`five_hour`, `seven_day`, …). `5h`/`7d` are kept as aliases for back-compat. Null/undefined = all.
|
|
215
234
|
* @param {boolean} [opts.singleLine=false] - force the legacy one-line layout. By default, when the delegation ledger has lifetime savings, the routing totals lead on their own first line and everything else moves to line 2 (Claude Code renders multi-line statuslines; `--single-line` is the escape hatch for terminals that only show the first line).
|
|
216
235
|
*/
|
|
217
236
|
export function formatReport(data, { color = true, verbose = false, timer = true, mode = 'text', segments = null, singleLine = false } = {}) {
|
|
@@ -426,6 +445,9 @@ export function formatReport(data, { color = true, verbose = false, timer = true
|
|
|
426
445
|
// a missing section at a glance and know to run `harness init`.
|
|
427
446
|
const harnessSeg = buildHarnessSeg(c, isIcon);
|
|
428
447
|
|
|
448
|
+
// Korean-style chip — rendered only when the session-start injection is on.
|
|
449
|
+
const koreanSeg = buildKoreanSeg(c, isIcon, verbose);
|
|
450
|
+
|
|
429
451
|
// Model chip — pulled from Claude Code's stdin payload (`model.display_name`).
|
|
430
452
|
// Cheap identity context: useful when the user toggles between Sonnet/Opus
|
|
431
453
|
// mid-session and wants to confirm at a glance which one is answering.
|
|
@@ -528,6 +550,7 @@ export function formatReport(data, { color = true, verbose = false, timer = true
|
|
|
528
550
|
if (capWarnSeg && want('cap-warn')) segs.push(capWarnSeg);
|
|
529
551
|
if (spikeSeg && want('spike')) segs.push(spikeSeg);
|
|
530
552
|
if (harnessSeg && want('harness')) segs.push(harnessSeg);
|
|
553
|
+
if (koreanSeg && want('korean')) segs.push(koreanSeg);
|
|
531
554
|
if (modelSeg && want('model')) segs.push(modelSeg);
|
|
532
555
|
// Delegation savings ride up front, next to the model that would otherwise
|
|
533
556
|
// have done the work. "Cache saved" stays at the tail: it is a lifetime brag
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* korean-style — inject Korean writing guidance into every session.
|
|
3
|
+
*
|
|
4
|
+
* Claude Code's own mechanism for this is an output style, which is a global
|
|
5
|
+
* switch: turning it on replaces whatever style the user had, and it only
|
|
6
|
+
* applies where the user remembered to configure it. Projects opened on a
|
|
7
|
+
* different machine, or by a teammate, get nothing.
|
|
8
|
+
*
|
|
9
|
+
* This module carries the guidance inside the package instead and hands it to
|
|
10
|
+
* the model through the SessionStart hook claude-token-saver already installs.
|
|
11
|
+
* The rules then apply in every project on the machine, with no output-style
|
|
12
|
+
* change and no plugin to install, and they survive `/clear` because the hook
|
|
13
|
+
* fires again.
|
|
14
|
+
*
|
|
15
|
+
* Cost: the text is ~1.3k tokens, injected once per session (not per turn) and
|
|
16
|
+
* covered by the prompt cache from the second request on. A token-saving tool
|
|
17
|
+
* has no business spending that silently, so the feature is opt-in via
|
|
18
|
+
* `claude-token-saver korean on`.
|
|
19
|
+
*
|
|
20
|
+
* The guidance itself is vendored from fluent-korean
|
|
21
|
+
* (https://github.com/snflkd/fluent-korean), Copyright (c) 2026 snflkd, MIT
|
|
22
|
+
* License — see presets/korean-style/LICENSE-fluent-korean. Only the
|
|
23
|
+
* output-style frontmatter was stripped; the wording is unmodified.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
27
|
+
import { join, dirname } from 'node:path';
|
|
28
|
+
import { fileURLToPath } from 'node:url';
|
|
29
|
+
import { createRequire } from 'node:module';
|
|
30
|
+
import { loadConfig, saveConfig } from './config.js';
|
|
31
|
+
|
|
32
|
+
const require = createRequire(import.meta.url);
|
|
33
|
+
|
|
34
|
+
const packageRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
35
|
+
|
|
36
|
+
export const KOREAN_STYLE_PATH = join(packageRoot, 'presets', 'korean-style', 'fluent-korean.md');
|
|
37
|
+
export const KOREAN_STYLE_LICENSE_PATH = join(packageRoot, 'presets', 'korean-style', 'LICENSE-fluent-korean');
|
|
38
|
+
export const KOREAN_STYLE_SOURCE = 'fluent-korean by snflkd (MIT) — https://github.com/snflkd/fluent-korean';
|
|
39
|
+
|
|
40
|
+
/** Whether session-start injection is enabled. Off unless the user asked. */
|
|
41
|
+
export function koreanStyleEnabled(cfg = loadConfig()) {
|
|
42
|
+
return cfg?.koreanStyle?.enabled === true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** True once the user has turned the feature on or off explicitly. */
|
|
46
|
+
export function koreanStyleDecided(cfg = loadConfig()) {
|
|
47
|
+
return typeof cfg?.koreanStyle?.enabled === 'boolean';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Whether this machine looks like it writes Korean.
|
|
52
|
+
*
|
|
53
|
+
* Used only to decide the DEFAULT at install time. Turning the guidance on for
|
|
54
|
+
* everyone would bill ~1.5k tokens per session to users who never write a
|
|
55
|
+
* Korean sentence; leaving it off for everyone means the people who need it
|
|
56
|
+
* have to discover a command that exists for exactly them. Locale answers the
|
|
57
|
+
* question well enough, and the user can override either way afterwards.
|
|
58
|
+
*
|
|
59
|
+
* Signals, cheapest first: the tool's own language setting, then the POSIX
|
|
60
|
+
* locale variables, then (macOS only, where those are routinely unset) the
|
|
61
|
+
* system locale.
|
|
62
|
+
*/
|
|
63
|
+
export function koreanLocaleDetected({ env = process.env, platform = process.platform } = {}) {
|
|
64
|
+
try {
|
|
65
|
+
if (loadConfig().language === 'ko') return true;
|
|
66
|
+
} catch { /* unreadable config falls through to the env checks */ }
|
|
67
|
+
for (const v of [env.LC_ALL, env.LC_MESSAGES, env.LANG, env.LANGUAGE]) {
|
|
68
|
+
// `ko` must be a whole subtag: `ko`, `ko_KR.UTF-8`, `ko-KR`, and the
|
|
69
|
+
// colon-separated `LANGUAGE=ko:en` all count, while `kok` (Konkani) and
|
|
70
|
+
// `tok` do not.
|
|
71
|
+
if (typeof v === 'string' && /(^|[:._-])ko([:._-]|$)/i.test(v)) return true;
|
|
72
|
+
}
|
|
73
|
+
if (platform === 'darwin') {
|
|
74
|
+
try {
|
|
75
|
+
// `LANG` is commonly unset in macOS GUI-launched shells, so the system
|
|
76
|
+
// locale is the only reliable signal there.
|
|
77
|
+
const { execFileSync } = require('node:child_process');
|
|
78
|
+
const out = execFileSync('defaults', ['read', '-g', 'AppleLocale'], {
|
|
79
|
+
encoding: 'utf8', timeout: 2000, stdio: ['ignore', 'pipe', 'ignore'],
|
|
80
|
+
}).trim();
|
|
81
|
+
if (/^ko(_|-|$)/i.test(out)) return true;
|
|
82
|
+
} catch { /* `defaults` missing or slow — treat as "not detected" */ }
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function setKoreanStyleEnabled(enabled) {
|
|
88
|
+
const cfg = loadConfig();
|
|
89
|
+
cfg.koreanStyle = { ...(cfg.koreanStyle || {}), enabled: !!enabled };
|
|
90
|
+
saveConfig(cfg);
|
|
91
|
+
return cfg.koreanStyle;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The guidance text, with the vendoring comment stripped (it is provenance for
|
|
96
|
+
* readers of the repo, not instruction for the model — and every token of it
|
|
97
|
+
* would be charged on each session).
|
|
98
|
+
*
|
|
99
|
+
* Returns null when the file is missing, which the hook reads as "inject
|
|
100
|
+
* nothing" rather than failing a session start.
|
|
101
|
+
*/
|
|
102
|
+
export function koreanStyleText() {
|
|
103
|
+
try {
|
|
104
|
+
if (!existsSync(KOREAN_STYLE_PATH)) return null;
|
|
105
|
+
const raw = readFileSync(KOREAN_STYLE_PATH, 'utf8');
|
|
106
|
+
const body = raw.replace(/^<!--[\s\S]*?-->\s*/, '').trim();
|
|
107
|
+
return body || null;
|
|
108
|
+
} catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Block to inject at session start, or null when disabled/unavailable.
|
|
115
|
+
*
|
|
116
|
+
* The framing line matters: without it the model can read the guidance as
|
|
117
|
+
* "the user is asking about Korean writing rules" instead of "these rules
|
|
118
|
+
* govern how I write from now on".
|
|
119
|
+
*/
|
|
120
|
+
export function koreanStyleInjection({ cfg = loadConfig() } = {}) {
|
|
121
|
+
if (!koreanStyleEnabled(cfg)) return null;
|
|
122
|
+
const text = koreanStyleText();
|
|
123
|
+
if (!text) return null;
|
|
124
|
+
return [
|
|
125
|
+
'[claude-token-saver korean-style] 이 세션에서 한국어를 출력할 때는 아래 지침을 따르십시오.',
|
|
126
|
+
'이 지침은 사용자가 claude-token-saver에 설정한 것이며, 답변·문서·주석이 아닌 산문 전반에 적용됩니다.',
|
|
127
|
+
`(출처: ${KOREAN_STYLE_SOURCE})`,
|
|
128
|
+
'',
|
|
129
|
+
text,
|
|
130
|
+
].join('\n');
|
|
131
|
+
}
|