claude-token-saver 3.0.0 → 3.2.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 +37 -25
- package/README.md +37 -25
- package/bin/cli.js +102 -110
- package/package.json +2 -1
- package/presets/ratchet-rules.md +16 -0
- package/src/harness-templates.js +4 -0
- package/src/harness.js +38 -58
- package/src/model-rules.js +174 -0
- package/src/route-scan.js +206 -58
- package/src/session-records.js +112 -0
- package/src/frugon-export.js +0 -211
package/README.en.md
CHANGED
|
@@ -82,7 +82,6 @@ Run these in your shell (inside Claude Code, the `/claude-token-saver` Skill is
|
|
|
82
82
|
| `claude-token-saver handoff` | Back work up to `HANDOFF-*.md` before a cap blocks you |
|
|
83
83
|
| `claude-token-saver mode [keywords...]` | Output config (`icon`/`text`, `en`/`ko`, `1h`–`30d` window, …) |
|
|
84
84
|
| `claude-token-saver harness ...` | 🅷 Harness management (below) |
|
|
85
|
-
| `claude-token-saver frugon` | Export sessions → [frugon](https://github.com/Rodiun/frugon)-compatible JSONL (model-routing savings analysis, below) |
|
|
86
85
|
| `claude-token-saver route-scan` | Detect recurring easy work on expensive models → propose haiku-delegation ratchet rules (below) |
|
|
87
86
|
| `claude-token-saver install` | Manually register Skill + statusline |
|
|
88
87
|
|
|
@@ -117,14 +116,13 @@ claude-token-saver harness init --global # ~/.claude/CLAUDE.md — every p
|
|
|
117
116
|
claude-token-saver harness check # current score (global fallback honored)
|
|
118
117
|
claude-token-saver harness promote <N> --project|--global # warning #N → ratchet rule (scope required)
|
|
119
118
|
claude-token-saver harness promote "<rule text>" --project|--global # register your own hand-written rules the same way
|
|
120
|
-
claude-token-saver harness pull #
|
|
121
|
-
claude-token-saver harness pull --harness # also pull the global harness block (5 sections)
|
|
119
|
+
claude-token-saver harness pull # register the package's curated ratchet rules into your global ratchet (opt-in, dedupes)
|
|
122
120
|
claude-token-saver harness list / rm <N> # view / delete rules (auto .bak)
|
|
123
121
|
claude-token-saver harness off | on # toggle the 🅷 chip
|
|
124
122
|
```
|
|
125
123
|
|
|
126
124
|
- `promote` **requires** `--project`/`--global` in non-TTY contexts (scripts, LLM calls) — a scope choice is never silently made for the caller.
|
|
127
|
-
- `
|
|
125
|
+
- `pull` registers the **author-curated ratchet rules** bundled with the package (`presets/ratchet-rules.md` — only general-purpose rules promoted from real recurring mistakes) into your global ratchet (`~/.claude/ratchet.md`). `install`/`init` never auto-inject anything; `pull` is always opt-in and idempotent. Drop any rule you dislike with `harness rm`.
|
|
128
126
|
- 🅷⚠ runtime warnings (`ratchet?` `no-evidence` `PEV-skip`) expire after 30 minutes, subdirectory sessions match their project correctly, and PEV-skip counts only mutating tools (Edit/Write/Bash) so read-only research sessions don't trip it (v2.16.0+).
|
|
129
127
|
|
|
130
128
|
<details>
|
|
@@ -139,37 +137,39 @@ The whole point of the ratchet is **one-direction accumulation**. Deleting rules
|
|
|
139
137
|
An auto `.bak` is kept, but **the session context that earned the rule its place is not recoverable.**
|
|
140
138
|
</details>
|
|
141
139
|
|
|
142
|
-
## 🔀 frugon integration — "which calls could a cheaper model handle?"
|
|
143
140
|
|
|
144
|
-
|
|
141
|
+
## 🔀 route-scan — "this recurring task could run on a cheaper tier"
|
|
145
142
|
|
|
146
|
-
|
|
147
|
-
claude-token-saver frugon # last 30 days → ./frugon-export.jsonl
|
|
148
|
-
claude-token-saver frugon --run # export + run frugon analyze immediately
|
|
149
|
-
claude-token-saver frugon --days 7 --project myproj --out logs.jsonl
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
- Converts `~/.claude/projects/` transcripts into the OpenAI-compatible JSONL frugon reads. **Analysis is fully local** — no logs or keys leave your machine (same principle frugon holds).
|
|
153
|
-
- **Cache-weighted tokens (default):** frugon doesn't know about prompt caching, so raw physical tokens would overstate your spend ~10x. By default the export folds in Anthropic's cache multipliers (read 0.1x · 5m write 1.25x · 1h write 2x) so frugon's dollar figures match your real bill. Use `--raw-tokens` for physical counts.
|
|
154
|
-
- Preserves the signals frugon's easy/hard router reads (prompt/completion tokens, conversation depth) plus the last user prompt and reply text for `--measure` quality sampling. Strip text with `--no-content`.
|
|
155
|
-
- Install frugon with `pipx install frugon` (if models show as unpriced, run `frugon update`).
|
|
143
|
+
Analyzes your session history at the **episode (user request) level**, classifies the work your expensive model (opus/fable) keeps doing into **tiers**, and proposes delegation rules. Fully local, zero token cost. Criteria design and evidence: [docs/TIER_CRITERIA.md](./docs/TIER_CRITERIA.md).
|
|
156
144
|
|
|
157
|
-
|
|
145
|
+
- **T2 → haiku**: few calls, tiny output, near-zero mutation, no errors (lookups, pasted-screen Q&A, simple runs)
|
|
146
|
+
- **T1 → sonnet**: moderate output, few mutations, ≤1 error (build pipelines, status checks)
|
|
147
|
+
- **T0 stays**: repeated errors, heavy mutation, big output, design/analysis keywords — the session model keeps it
|
|
158
148
|
|
|
159
|
-
|
|
149
|
+
Signals: call count, output tokens, **mutating tool calls (Edit/Write/Bash)**, **tool errors**, and request text. Output thresholds are auto-calibrated from your own 14-day distribution (fixed constants drift with workload).
|
|
160
150
|
|
|
161
151
|
```bash
|
|
162
|
-
claude-token-saver route-scan # scan (24h cache) +
|
|
163
|
-
claude-token-saver harness promote R1 --project # promote candidate R1 to a
|
|
152
|
+
claude-token-saver route-scan # scan (24h cache) + tiered candidates
|
|
153
|
+
claude-token-saver harness promote R1 --project # promote candidate R1 to a model-fitting rule
|
|
164
154
|
claude-token-saver route-scan dismiss 1 # not interested — won't resurface
|
|
155
|
+
claude-token-saver route-scan rules # list model-fitting rules (rm <N> to remove)
|
|
165
156
|
```
|
|
166
157
|
|
|
158
|
+
### Model-fitting ratchet — a separate file, continuously refreshed
|
|
159
|
+
|
|
160
|
+
Promoted delegation rules never mix with hand-written ratchet rules: they live in a **separate, fully tool-owned file** — `.claude/ratchet-model.md` per project, `~/.claude/ratchet-model.md` for global scope — regenerated wholesale on every scan, and they stay alive afterward:
|
|
161
|
+
|
|
162
|
+
- **Auto-refresh**: every rescan recomputes recurrence counts and the category's error rate from fresh logs and rewrites the file. Your `ratchet.md` is never touched by stat churn, so repos that commit `.claude/` see no diff noise (`ratchet-model.md` is safe to gitignore — it's always regenerable from the registry).
|
|
163
|
+
- **rule-health**: when the delegated category's error rate exceeds 20%, the rule gets a `⚠ rule-health` flag suggesting you narrow or remove it — the "define difficulty by outcome" principle applied to rule lifecycle.
|
|
164
|
+
- Your rules are managed by `harness list/rm`; model-fitting rules by `route-scan rules [rm <N>]` — separate files, separate indexes.
|
|
165
|
+
- The CLAUDE.md ratchet section planted by `harness init` references both files, so Claude applies them together (existing users: re-run `harness init` to refresh the block).
|
|
166
|
+
|
|
167
167
|
How it works (session-boundary calibration, NOT a real-time router):
|
|
168
|
-
1. `install` registers a SessionStart hook that injects the cached scan results as session context on startup and `/clear
|
|
169
|
-
2. When a recurring (≥3×)
|
|
170
|
-
3. Promoted rules
|
|
168
|
+
1. `install` registers a SessionStart hook that injects the cached scan results as session context on startup and `/clear`. Rescans are **data-triggered, not time-triggered**: ~5MB of new transcripts since the last scan rescans immediately, a small trickle rescans daily, and no change means no rescan at all (an unchanged-input scan is deterministic). A 1-hour minimum-interval guard applies, and promoting a rule triggers one immediate refresh to establish its stat baseline.
|
|
169
|
+
2. When a recurring (≥3×) pattern exists, the statusline shows a `🅷⚠ route? R1` chip and Claude asks you whether to register it, and at which scope (`--project`/`--global`).
|
|
170
|
+
3. Promoted rules make **the main model delegate that work type to a haiku/sonnet subagent automatically from the next session on**.
|
|
171
171
|
|
|
172
|
-
Recommended companion setup: create `model: haiku` subagents under `~/.claude/agents/` (e.g. haiku-explore / haiku-runner / haiku-translate) so the rules are immediately actionable.
|
|
172
|
+
Recommended companion setup: create `model: haiku` subagents under `~/.claude/agents/` (e.g. haiku-explore / haiku-runner / haiku-translate) plus a `model: sonnet` general worker so the rules are immediately actionable.
|
|
173
173
|
|
|
174
174
|
## Spike issue codes
|
|
175
175
|
|
|
@@ -251,10 +251,22 @@ Also update `statusLine.command` in `~/.claude/settings.json` to `claude-token-s
|
|
|
251
251
|
|
|
252
252
|
## Release notes
|
|
253
253
|
|
|
254
|
+
### v3.2.0 (2026-07-13)
|
|
255
|
+
- **Tier classification (T0/T1/T2)** — route-scan grows from a binary easy/other split into three tiers. New signals: mutating tool calls and tool errors; output thresholds auto-calibrate to the user's own distribution (clamped); a dedicated category for pasted screen/log Q&A; conversational episodes (<100 output tokens) excluded. Design and research evidence in `docs/TIER_CRITERIA.md`.
|
|
256
|
+
- **Model-fitting ratchet separated** — promoted delegation rules live in their own file (`.claude/ratchet-model.md` / `~/.claude/ratchet-model.md`), file-level-separated from hand-written rules, managed via `route-scan rules [rm <N>]`; the harness CLAUDE.md block references both files.
|
|
257
|
+
- **Log-driven auto-refresh + rule-health** — every rescan recomputes each registered rule's recurrence count and error rate (over delegation-shaped episodes) and rewrites the file. An error rate >20% flags the rule with `⚠ rule-health`, suggesting narrowing or removal.
|
|
258
|
+
- **Data-triggered rescans** — the fixed 24h TTL is gone; new transcript volume triggers rescans (~5MB → immediately, a trickle → daily, no change → skip, 1h minimum interval, one immediate refresh after promote).
|
|
259
|
+
|
|
260
|
+
### v3.1.0 (2026-07-13)
|
|
261
|
+
- **frugon integration removed** — the `claude-token-saver frugon` JSONL-export subcommand is gone. An external analyzer's aggregate report can't be turned into ratchet rules (condition → action), so it never fed the delegation pipeline; 3.x instead invests in **first-party tier classification over session logs**. route-scan is unaffected (the shared parser moved to `src/session-records.js`).
|
|
262
|
+
|
|
263
|
+
### v3.0.1 (2026-07-13)
|
|
264
|
+
- **`harness pull` redefined** — v3.0.0's "copy global ratchet → project" was pointless (the global ratchet already applies to every project as the upper layer of the hierarchy) and is removed. `pull` now registers the **author-curated ratchet rules** bundled with the package (`presets/ratchet-rules.md`) into your global ratchet — six general-purpose rules promoted from real recurring mistakes; opt-in and idempotent.
|
|
265
|
+
|
|
254
266
|
### v3.0.0 (2026-07-13)
|
|
255
267
|
- **Major bump** — with v2.19's frugon integration and v2.20's route-scan, the product's character shifted from "after-the-fact token monitor" to "a routing layer that pushes recurring easy work down to cheaper models", so this ships as a major. No breaking changes (every existing command and setting remains compatible).
|
|
256
268
|
- **route-scan promote fix** — `harness promote R<N> --project` now writes the rule into the `.claude/ratchet.md` of the project the candidate was **detected in** (previously it landed in whatever directory the CLI ran from). The scan stores each candidate's real session path (`projectPath`); promoting a foreign-project candidate from a pre-3.0 cache without that field is refused with a pointer to `route-scan --refresh`.
|
|
257
|
-
- **New `harness pull`** —
|
|
269
|
+
- **New `harness pull`** — redefined in v3.0.1 (see above).
|
|
258
270
|
|
|
259
271
|
### v2.20.0 (2026-07-13)
|
|
260
272
|
- **route-scan**: detect recurring easy work on expensive models → `🅷⚠ route? R<N>` chip + SessionStart hook context injection + `harness promote R<N> --project|--global` to promote haiku-delegation ratchet rules.
|
package/README.md
CHANGED
|
@@ -82,7 +82,6 @@ Claude 안에서 `/claude-token-saver` Skill을 실행하거나 칩 문구를
|
|
|
82
82
|
| `claude-token-saver handoff` | 작업 상태를 `HANDOFF-*.md`로 백업 (캡 임박 시) |
|
|
83
83
|
| `claude-token-saver mode [keywords...]` | 출력 설정 (`icon`/`text`, `ko`/`en`, `1h`~`30d` 윈도 등) |
|
|
84
84
|
| `claude-token-saver harness ...` | 🅷 Harness 관리 (아래 참고) |
|
|
85
|
-
| `claude-token-saver frugon` | 세션 기록 → [frugon](https://github.com/Rodiun/frugon) 호환 JSONL 내보내기 (모델 라우팅 절감 분석, 아래 참고) |
|
|
86
85
|
| `claude-token-saver route-scan` | 상위 모델이 반복 처리한 easy 작업 감지 → haiku 위임 랫쳇 룰 제안 (아래 참고) |
|
|
87
86
|
| `claude-token-saver install` | Skill·statusline 수동 등록 |
|
|
88
87
|
|
|
@@ -98,14 +97,13 @@ claude-token-saver harness init --global # ~/.claude/CLAUDE.md — 모든
|
|
|
98
97
|
claude-token-saver harness check # 현재 점수 (글로벌 fallback 인정)
|
|
99
98
|
claude-token-saver harness promote <N> --project|--global # 경고 #N → ratchet 룰 (스코프 필수)
|
|
100
99
|
claude-token-saver harness promote "<룰 텍스트>" --project|--global # 내가 직접 정의한 룰도 같은 명령으로 등록
|
|
101
|
-
claude-token-saver harness pull #
|
|
102
|
-
claude-token-saver harness pull --harness # 글로벌 하네스 블록(5개 섹션)까지 함께 가져오기
|
|
100
|
+
claude-token-saver harness pull # 패키지 동봉 큐레이션 룰 → 내 글로벌 랫쳇에 등록 (opt-in, 중복 스킵)
|
|
103
101
|
claude-token-saver harness list / rm <N> # 룰 조회 / 삭제 (자동 .bak)
|
|
104
102
|
claude-token-saver harness off | on # 🅷 표시 토글
|
|
105
103
|
```
|
|
106
104
|
|
|
107
105
|
- `promote`는 non-TTY(스크립트·LLM 호출)에서 `--project`/`--global` 플래그가 **필수** — 스코프가 묻지 않고 결정되는 사고를 막기 위한 설계입니다.
|
|
108
|
-
- 설치(`install`)나 `init`은
|
|
106
|
+
- `pull`은 패키지에 동봉된 **제작자 큐레이션 랫쳇 룰**(`presets/ratchet-rules.md` — 실제 반복 사고에서 승격된 범용 룰만)을 내 글로벌 랫쳇(`~/.claude/ratchet.md`)에 등록합니다. 설치(`install`)나 `init`은 아무것도 자동 주입하지 않으며, `pull`은 항상 opt-in이고 재실행해도 중복이 없습니다(멱등). 마음에 안 드는 룰은 `harness rm`으로 제거하면 됩니다.
|
|
109
107
|
- 🅷⚠ 런타임 경고(`ratchet?` `no-evidence` `PEV-skip`)는 30분 후 자동 만료되고, 하위 디렉터리 세션도 프로젝트에 올바르게 매칭됩니다. PEV-skip은 변경성 도구(Edit/Write/Bash)만 카운트해 읽기 위주 세션에서는 발동하지 않습니다 (v2.16.0+).
|
|
110
108
|
|
|
111
109
|
<details>
|
|
@@ -120,37 +118,39 @@ ratchet의 가치는 **한 방향 누적**에 있습니다. 룰을 가볍게 지
|
|
|
120
118
|
삭제 시 `.bak`이 남지만 **그 룰이 박힌 세션 컨텍스트(왜)는 복원되지 않습니다.**
|
|
121
119
|
</details>
|
|
122
120
|
|
|
123
|
-
## 🔀 frugon 연계 — "어떤 호출을 싼 모델로 내릴 수 있나"
|
|
124
121
|
|
|
125
|
-
|
|
122
|
+
## 🔀 route-scan — "이 반복 작업, 더 싼 티어로 내려도 됩니다"
|
|
126
123
|
|
|
127
|
-
|
|
128
|
-
claude-token-saver frugon # 최근 30일 세션 → ./frugon-export.jsonl
|
|
129
|
-
claude-token-saver frugon --run # 내보내기 + frugon analyze 바로 실행
|
|
130
|
-
claude-token-saver frugon --days 7 --project myproj --out logs.jsonl
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
- `~/.claude/projects/`의 transcript를 frugon이 읽는 OpenAI 호환 JSONL로 변환합니다. **분석은 전부 로컬** — 로그도 키도 밖으로 나가지 않습니다 (frugon의 원칙과 동일).
|
|
134
|
-
- **캐시 가중 토큰(기본):** frugon은 프롬프트 캐싱을 모르기 때문에 물리 토큰을 그대로 주면 비용이 ~10배 과대평가됩니다. 기본값은 캐시 read 0.1x · 5m write 1.25x · 1h write 2x를 접어 넣은 유효 토큰이라 frugon의 달러 견적이 실제 청구액과 일치합니다. 물리 토큰이 필요하면 `--raw-tokens`.
|
|
135
|
-
- frugon의 easy/hard 분류가 쓰는 신호(프롬프트·응답 토큰, 대화 깊이)와 `--measure` 품질 검증에 쓰는 마지막 유저 프롬프트·응답 텍스트를 보존합니다. 텍스트를 빼고 싶으면 `--no-content`.
|
|
136
|
-
- frugon 설치: `pipx install frugon` (모델이 unpriced로 나오면 `frugon update`).
|
|
124
|
+
세션 기록을 **에피소드(사용자 요청) 단위**로 분석해 상위 모델(opus/fable)이 반복 처리해 온 작업을 **티어로 분류**하고, 위임 룰로 승격하도록 제안합니다. 전 과정 로컬, 토큰 비용 0. 기준 설계와 근거는 [docs/TIER_CRITERIA.md](./docs/TIER_CRITERIA.md) 참고.
|
|
137
125
|
|
|
138
|
-
|
|
126
|
+
- **T2 → haiku**: 적은 호출·작은 출력·변경 거의 없음·에러 0으로 끝난 요청 (탐색·조회, 붙여넣은 화면 질문, 단순 실행 등)
|
|
127
|
+
- **T1 → sonnet**: 중간 출력·변경 소수·에러 ≤1 (빌드 파이프라인, 상태 점검 등)
|
|
128
|
+
- **T0 유지**: 에러 반복, 대량 변경, 큰 출력, 설계·분석 키워드 — 세션 모델이 계속 담당
|
|
139
129
|
|
|
140
|
-
|
|
130
|
+
판정 신호는 호출 수·출력 토큰·**변경성 도구 수(Edit/Write/Bash)**·**도구 에러 수**·요청 텍스트이고, 출력 임계값은 사용자 자신의 최근 14일 분포에서 자동 보정됩니다 (고정 상수는 워크로드가 바뀌면 어긋나기 때문).
|
|
141
131
|
|
|
142
132
|
```bash
|
|
143
|
-
claude-token-saver route-scan # 스캔 (24h 캐시) + 후보 출력
|
|
144
|
-
claude-token-saver harness promote R1 --project # 후보 R1을
|
|
133
|
+
claude-token-saver route-scan # 스캔 (24h 캐시) + 티어별 후보 출력
|
|
134
|
+
claude-token-saver harness promote R1 --project # 후보 R1을 모델 피팅 룰로 등록
|
|
145
135
|
claude-token-saver route-scan dismiss 1 # 관심 없으면 무시 (재스캔에도 안 뜸)
|
|
136
|
+
claude-token-saver route-scan rules # 등록된 모델 피팅 룰 목록 (rm <N>으로 제거)
|
|
146
137
|
```
|
|
147
138
|
|
|
139
|
+
### 모델 피팅 랫쳇 — 사용자 룰과 파일부터 분리, 로그 기반 자동 갱신
|
|
140
|
+
|
|
141
|
+
승격된 위임 룰은 손으로 쓴 랫쳇 룰과 섞이지 않도록 **별도 파일**에 저장됩니다 — 프로젝트는 `.claude/ratchet-model.md`, 글로벌은 `~/.claude/ratchet-model.md`. 이 파일은 전적으로 도구 소유라 매 스캔마다 통째로 재생성되며, 이후에도 살아 움직입니다:
|
|
142
|
+
|
|
143
|
+
- **자동 갱신**: 매 스캔마다 반복 횟수·해당 유형의 에러율을 최신 로그로 다시 계산해 파일을 재작성합니다. 통계가 바뀌어도 사용자의 `ratchet.md`는 전혀 건드리지 않으므로, `.claude/`를 커밋하는 프로젝트에서도 diff 소음이 없습니다 (`ratchet-model.md`는 gitignore해도 무방 — 레지스트리에서 항상 재생성 가능).
|
|
144
|
+
- **rule-health**: 위임 대상 유형의 에러율이 20%를 넘으면 룰에 `⚠ rule-health` 경고가 붙어 조건을 좁히거나 제거하라고 알립니다 — "결과(outcome)로 난이도를 정의"하는 원칙을 룰 수명 관리에 재적용한 것.
|
|
145
|
+
- 사용자 룰은 `harness list/rm`, 모델 피팅 룰은 `route-scan rules [rm <N>]`로 각각 관리 — 서로의 파일도 인덱스도 침범하지 않습니다.
|
|
146
|
+
- `harness init`이 심는 CLAUDE.md 랫쳇 섹션이 두 파일을 모두 참조하므로 Claude가 세션에서 함께 적용합니다 (기존 사용자는 `harness init` 재실행으로 블록 갱신).
|
|
147
|
+
|
|
148
148
|
동작 구조 (실시간 라우팅이 아니라 **세션 경계 캘리브레이션**):
|
|
149
|
-
1. `install` 시 SessionStart 훅이 등록되어, 새 세션 시작·`/clear` 때 캐시된 스캔 결과를 세션 컨텍스트로
|
|
150
|
-
2. 반복(≥3회)
|
|
151
|
-
3. 등록된 룰은
|
|
149
|
+
1. `install` 시 SessionStart 훅이 등록되어, 새 세션 시작·`/clear` 때 캐시된 스캔 결과를 세션 컨텍스트로 주입합니다. 재스캔은 시간이 아니라 **데이터가 트리거**: 마지막 스캔 이후 새 transcript가 ~5MB 이상 쌓이면 즉시, 소량이면 하루 1회, 아무 변화가 없으면 아예 돌지 않습니다 (변화 없는 재스캔은 결과가 동일하므로). 최소 간격 1시간 가드 포함, 룰 등록(promote) 직후에는 통계 기준선 확보를 위해 즉시 1회.
|
|
150
|
+
2. 반복(≥3회) 패턴이 있으면 statusline에 `🅷⚠ route? R1` 칩이 뜨고, Claude가 등록 여부와 scope(`--project`/`--global`)를 물어봅니다.
|
|
151
|
+
3. 등록된 룰은 **다음 세션부터 메인 모델이 해당 유형을 haiku/sonnet 서브에이전트로 자동 위임**하게 합니다.
|
|
152
152
|
|
|
153
|
-
권장 사전 준비: `~/.claude/agents/`에 `model: haiku` 서브에이전트(예: haiku-explore / haiku-runner / haiku-translate)
|
|
153
|
+
권장 사전 준비: `~/.claude/agents/`에 `model: haiku` 서브에이전트(예: haiku-explore / haiku-runner / haiku-translate)와 `model: sonnet` 범용 서브에이전트를 만들어 두면 룰이 바로 실행 가능해집니다.
|
|
154
154
|
|
|
155
155
|
## 토큰 급증 원인 코드
|
|
156
156
|
|
|
@@ -208,10 +208,22 @@ npm uninstall -g claude-cache-monitor && npm i -g claude-token-saver
|
|
|
208
208
|
|
|
209
209
|
## 릴리스 노트
|
|
210
210
|
|
|
211
|
+
### v3.2.0 (2026-07-13)
|
|
212
|
+
- **티어 분류 (T0/T1/T2)** — route-scan이 이분법(easy/그외)에서 3티어로 진화. 신호에 변경성 도구 수·도구 에러 수 추가, 출력 임계값은 사용자 분포 기반 자동 보정(클램프 포함), 붙여넣은 화면·로그 질문 전용 카테고리 신설, 대화성 응답(출력 <100토큰) 제외. 기준 설계·리서치 근거는 `docs/TIER_CRITERIA.md`.
|
|
213
|
+
- **모델 피팅 랫쳇 분리** — 승격된 위임 룰은 별도 파일(`.claude/ratchet-model.md` / `~/.claude/ratchet-model.md`)에 저장돼 사용자 룰과 파일 단위로 분리. `route-scan rules [rm <N>]`로 관리하며, 하네스 CLAUDE.md 블록이 두 파일을 함께 참조.
|
|
214
|
+
- **로그 기반 자동 갱신 + rule-health** — 매 스캔마다 등록 룰의 반복 횟수·에러율(위임 적격 모양의 에피소드 기준)을 재계산해 파일을 재작성. 에러율 >20%면 `⚠ rule-health` 플래그로 조건 좁히기/제거를 제안.
|
|
215
|
+
- **데이터 트리거 재스캔** — 고정 24h TTL을 폐기하고 신규 transcript 양이 재스캔을 트리거 (~5MB 즉시 / 소량 일 1회 / 무변화 스킵 / 최소 간격 1h / promote 직후 즉시 1회).
|
|
216
|
+
|
|
217
|
+
### v3.1.0 (2026-07-13)
|
|
218
|
+
- **frugon 연계 제거** — `claude-token-saver frugon` 서브커맨드(JSONL 내보내기)를 삭제했습니다. 외부 분석기의 집계 리포트는 랫쳇 룰(조건→행동)로 변환할 수 없어 위임 파이프라인에 기여하지 못했고, 3.x의 방향은 **세션 로그 기반 티어 분류를 자체적으로 탄탄히** 가져가는 것입니다. route-scan은 영향 없이 그대로 동작합니다 (공용 파서는 `src/session-records.js`로 분리).
|
|
219
|
+
|
|
220
|
+
### v3.0.1 (2026-07-13)
|
|
221
|
+
- **`harness pull` 재정의** — v3.0.0의 "글로벌 랫쳇 → 프로젝트 복사"는 글로벌 랫쳇이 이미 프로젝트의 상위 계층으로 항상 적용되므로 무의미해 제거. `pull`은 이제 패키지에 동봉된 **제작자 큐레이션 랫쳇 룰**(`presets/ratchet-rules.md`)을 사용자의 글로벌 랫쳇에 등록합니다 — 실제 반복 사고에서 승격된 범용 룰 6종, opt-in·멱등.
|
|
222
|
+
|
|
211
223
|
### v3.0.0 (2026-07-13)
|
|
212
224
|
- **메이저 승격** — v2.19 frugon 연계 + v2.20 route-scan으로 "사후 토큰 모니터링 도구"에서 "반복 easy 작업을 싼 모델로 내려보내는 라우팅 계층"으로 제품 성격이 바뀌어 메이저 버전을 올립니다. Breaking change는 없습니다 (기존 명령·설정 전부 호환).
|
|
213
225
|
- **route-scan promote 교정** — `harness promote R<N> --project`가 이제 후보가 **감지된 프로젝트**의 `.claude/ratchet.md`에 룰을 기록합니다 (이전에는 CLI를 실행한 디렉터리에 기록되는 버그). 스캔이 후보에 실제 세션 경로(`projectPath`)를 저장하며, 이 필드가 없는 구버전 캐시에서 다른 프로젝트 후보를 승격하려 하면 `route-scan --refresh`를 안내하고 중단합니다.
|
|
214
|
-
- **`harness pull` 신설** —
|
|
226
|
+
- **`harness pull` 신설** — v3.0.1에서 재정의됨 (위 참고).
|
|
215
227
|
|
|
216
228
|
### v2.20.0 (2026-07-13)
|
|
217
229
|
- **route-scan**: 상위 모델이 반복 처리한 easy 작업 감지 → `🅷⚠ route? R<N>` 칩 + SessionStart 훅 컨텍스트 주입 + `harness promote R<N> --project|--global`로 haiku 위임 랫쳇 룰 승격.
|
package/bin/cli.js
CHANGED
|
@@ -10,8 +10,6 @@
|
|
|
10
10
|
* npx claude-token-saver --format csv # CSV output
|
|
11
11
|
* npx claude-token-saver --project myproj # filter by project
|
|
12
12
|
* npx claude-token-saver route-scan # detect recurring easy work → haiku-delegation candidates
|
|
13
|
-
* npx claude-token-saver frugon # export transcripts → frugon JSONL (model-routing analysis)
|
|
14
|
-
* npx claude-token-saver frugon --run # export + run `frugon analyze`
|
|
15
13
|
* npx claude-token-saver --install-hook # install PostToolUse hook
|
|
16
14
|
* npx claude-token-saver --uninstall-hook # remove hook
|
|
17
15
|
* npx claude-token-saver --hook-run # internal: called by hook
|
|
@@ -480,6 +478,37 @@ async function main() {
|
|
|
480
478
|
const { userLanguage } = await import('../src/config.js');
|
|
481
479
|
const lang = userLanguage();
|
|
482
480
|
|
|
481
|
+
// route-scan rules [rm <N>] — the model-fitting rule registry (rules
|
|
482
|
+
// promoted from candidates; auto-refreshed from logs on every rescan).
|
|
483
|
+
if (args[1] === 'rules') {
|
|
484
|
+
const mr = await import('../src/model-rules.js');
|
|
485
|
+
if (args[2] === 'rm') {
|
|
486
|
+
const n = parseInt(args[3], 10);
|
|
487
|
+
const removed = Number.isFinite(n) ? mr.removeModelRule(n) : null;
|
|
488
|
+
if (!removed) {
|
|
489
|
+
console.error('Usage: claude-token-saver route-scan rules rm <N> # N from `route-scan rules`');
|
|
490
|
+
process.exit(1);
|
|
491
|
+
}
|
|
492
|
+
// A target whose last rule was removed gets its (tool-owned) file deleted.
|
|
493
|
+
mr.syncAllFiles({ previousPaths: [mr.modelRatchetPathFor(removed.scope, removed.targetRoot)] });
|
|
494
|
+
console.log(`Removed model-fitting rule #${n}: ${removed.rule}`);
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
const { rules } = mr.loadModelRules();
|
|
498
|
+
if (rules.length === 0) {
|
|
499
|
+
console.log(lang === 'ko' ? '등록된 모델 피팅 룰 없음.' : 'No model-fitting rules registered.');
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
console.log(lang === 'ko' ? '📐 모델 피팅 룰 (로그 기반 자동 갱신):' : '📐 Model-fitting rules (auto-refreshed from logs):');
|
|
503
|
+
rules.forEach((r, i) => {
|
|
504
|
+
const health = r.status === 'review' ? ' ⚠ rule-health' : '';
|
|
505
|
+
console.log(` #${i + 1} [${r.tier}|${r.scope}] ×${r.count || 0} err ${Math.round((r.errRate || 0) * 100)}%${health}`);
|
|
506
|
+
console.log(` ${r.rule}`);
|
|
507
|
+
});
|
|
508
|
+
console.log('\n제거: claude-token-saver route-scan rules rm <N>');
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
|
|
483
512
|
if (args[1] === 'dismiss') {
|
|
484
513
|
const n = parseInt(args[2], 10);
|
|
485
514
|
if (!Number.isFinite(n)) {
|
|
@@ -502,7 +531,7 @@ async function main() {
|
|
|
502
531
|
// prints delegation-candidate context for the new session.
|
|
503
532
|
if (hasFlag('--hook')) {
|
|
504
533
|
let cache = rs.readRouteScan();
|
|
505
|
-
if (
|
|
534
|
+
if (await rs.shouldRescan(cache)) {
|
|
506
535
|
try {
|
|
507
536
|
const { spawn } = await import('node:child_process');
|
|
508
537
|
spawn(process.execPath, [process.argv[1], 'route-scan', '--refresh', '--quiet'],
|
|
@@ -512,9 +541,10 @@ async function main() {
|
|
|
512
541
|
const open = rs.openCandidates(cache);
|
|
513
542
|
if (open.length === 0) return; // silent — nothing to inject
|
|
514
543
|
const lines = [];
|
|
515
|
-
lines.push(`[claude-token-saver route-scan] 최근 ${cache.days}일 세션에서 상위 모델(opus/fable)이 처리한 반복
|
|
544
|
+
lines.push(`[claude-token-saver route-scan] 최근 ${cache.days}일 세션에서 상위 모델(opus/fable)이 처리한 위임 가능 반복 작업이 감지되었습니다:`);
|
|
516
545
|
for (const c of open) {
|
|
517
|
-
|
|
546
|
+
const tierNote = c.tier === 'T1' ? 'sonnet 위임(중간 난도)' : `${c.agent} 위임(경량)`;
|
|
547
|
+
lines.push(` R${c.id} [${c.tier || 'T2'}] (×${c.count}, ${c.project}): ${c.label} → ${tierNote} 권장 (scope 제안: ${c.suggestedScope})`);
|
|
518
548
|
lines.push(` 예시: "${c.example}"`);
|
|
519
549
|
}
|
|
520
550
|
lines.push('이 패턴을 랫쳇 룰로 등록하면 다음 세션부터 자동 위임됩니다. 적절한 시점에 사용자에게 등록 여부와 scope를 물어본 뒤 실행하세요:');
|
|
@@ -526,7 +556,7 @@ async function main() {
|
|
|
526
556
|
|
|
527
557
|
const days = parseFloat(getArg('--days') || '14');
|
|
528
558
|
let cache = rs.readRouteScan();
|
|
529
|
-
if (hasFlag('--refresh') ||
|
|
559
|
+
if (hasFlag('--refresh') || (cache && cache.days !== days) || await rs.shouldRescan(cache, { days })) {
|
|
530
560
|
cache = await rs.runRouteScan({ days });
|
|
531
561
|
}
|
|
532
562
|
if (hasFlag('--quiet')) return;
|
|
@@ -547,7 +577,7 @@ async function main() {
|
|
|
547
577
|
}
|
|
548
578
|
console.log(lang === 'ko' ? '\n위임 후보:' : '\nDelegation candidates:');
|
|
549
579
|
for (const c of open) {
|
|
550
|
-
console.log(` R${c.id} ×${c.count} ${c.label} → ${c.agent} [${c.project}] (scope 제안: ${c.suggestedScope})`);
|
|
580
|
+
console.log(` R${c.id} [${c.tier || 'T2'}] ×${c.count} ${c.label} → ${c.agent} [${c.project}] (scope 제안: ${c.suggestedScope})`);
|
|
551
581
|
console.log(` 예시: "${c.example}"`);
|
|
552
582
|
console.log(` 룰: ${c.rule}`);
|
|
553
583
|
}
|
|
@@ -558,81 +588,12 @@ async function main() {
|
|
|
558
588
|
return;
|
|
559
589
|
}
|
|
560
590
|
|
|
561
|
-
// Subcommand: frugon — export Claude Code transcripts to the JSONL format
|
|
562
|
-
// frugon (local LLM cost analyzer, github.com/Rodiun/frugon) analyzes, so
|
|
563
|
-
// users can see which calls could route to a cheaper model.
|
|
564
|
-
// claude-token-saver frugon # export last 30 days → ./frugon-export.jsonl
|
|
565
|
-
// claude-token-saver frugon --days 7 # narrower window
|
|
566
|
-
// claude-token-saver frugon --project myproj # filter by project dir substring
|
|
567
|
-
// claude-token-saver frugon --out PATH # custom output path
|
|
568
|
-
// claude-token-saver frugon --raw-tokens # physical token counts (no cache weighting)
|
|
569
|
-
// claude-token-saver frugon --no-content # strip prompt/reply text (counts only)
|
|
570
|
-
// claude-token-saver frugon --run # run `frugon analyze` on the export
|
|
571
|
-
if (args[0] === 'frugon') {
|
|
572
|
-
const { exportFrugonLogs } = await import('../src/frugon-export.js');
|
|
573
|
-
const { userLanguage } = await import('../src/config.js');
|
|
574
|
-
const lang = userLanguage();
|
|
575
|
-
const days = parseFloat(getArg('--days') || '30');
|
|
576
|
-
const outPath = getArg('--out') || 'frugon-export.jsonl';
|
|
577
|
-
const cacheWeighted = !hasFlag('--raw-tokens');
|
|
578
|
-
const includeContent = !hasFlag('--no-content');
|
|
579
|
-
const res = await exportFrugonLogs({
|
|
580
|
-
days,
|
|
581
|
-
projectFilter: getArg('--project') || undefined,
|
|
582
|
-
outPath,
|
|
583
|
-
cacheWeighted,
|
|
584
|
-
includeContent,
|
|
585
|
-
});
|
|
586
|
-
if (res.records === 0) {
|
|
587
|
-
console.log(lang === 'ko'
|
|
588
|
-
? `최근 ${days}일 내 세션 기록이 없습니다 (~/.claude/projects).`
|
|
589
|
-
: `No session records in the last ${days} days (~/.claude/projects).`);
|
|
590
|
-
return;
|
|
591
|
-
}
|
|
592
|
-
console.log(lang === 'ko'
|
|
593
|
-
? `frugon 로그 내보내기 완료: ${res.outPath}`
|
|
594
|
-
: `frugon log export complete: ${res.outPath}`);
|
|
595
|
-
console.log(` ${res.records} calls / ${res.sessions} sessions / last ${days}d`);
|
|
596
|
-
const byModel = Object.entries(res.models).sort((a, b) => b[1] - a[1]);
|
|
597
|
-
for (const [model, count] of byModel) console.log(` ${model}: ${count}`);
|
|
598
|
-
console.log(cacheWeighted
|
|
599
|
-
? (lang === 'ko'
|
|
600
|
-
? ' prompt_tokens는 캐시 가중치 적용값 (read 0.1x, 5m write 1.25x, 1h write 2x) — frugon 비용이 실제 청구액과 일치. 해제: --raw-tokens'
|
|
601
|
-
: ' prompt_tokens are cache-weighted (read 0.1x, 5m write 1.25x, 1h write 2x) so frugon costs match your real bill. Disable: --raw-tokens')
|
|
602
|
-
: (lang === 'ko'
|
|
603
|
-
? ' prompt_tokens는 물리 토큰 수 (캐시 가중치 없음 — frugon 비용이 실제보다 크게 나옴)'
|
|
604
|
-
: ' prompt_tokens are raw physical counts (no cache weighting — frugon will overstate cost)'));
|
|
605
|
-
if (hasFlag('--run')) {
|
|
606
|
-
const { spawnSync } = await import('node:child_process');
|
|
607
|
-
console.log('');
|
|
608
|
-
const run = spawnSync('frugon', ['analyze', res.outPath], { stdio: 'inherit' });
|
|
609
|
-
if (run.error && run.error.code === 'ENOENT') {
|
|
610
|
-
console.error(lang === 'ko'
|
|
611
|
-
? 'frugon이 PATH에 없습니다. 설치: pipx install frugon (또는 pip install frugon)'
|
|
612
|
-
: 'frugon not found on PATH. Install: pipx install frugon (or pip install frugon)');
|
|
613
|
-
process.exit(1);
|
|
614
|
-
}
|
|
615
|
-
if (typeof run.status === 'number' && run.status !== 0) process.exit(run.status);
|
|
616
|
-
return;
|
|
617
|
-
}
|
|
618
|
-
console.log('');
|
|
619
|
-
console.log(lang === 'ko' ? '다음 단계:' : 'Next step:');
|
|
620
|
-
console.log(` frugon analyze ${res.outPath}`);
|
|
621
|
-
console.log(lang === 'ko'
|
|
622
|
-
? ' (frugon 미설치 시: pipx install frugon — 분석은 전부 로컬에서 실행됩니다)'
|
|
623
|
-
: ' (if frugon is not installed: pipx install frugon — analysis runs fully local)');
|
|
624
|
-
console.log(lang === 'ko'
|
|
625
|
-
? ' (unpriced 모델이 나오면: frugon update 로 가격표를 갱신하세요)'
|
|
626
|
-
: ' (if models show as unpriced: run `frugon update` to refresh the pricing table)');
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
|
|
630
591
|
// Subcommand: harness — manage the project's CLAUDE.md harness rules.
|
|
631
592
|
// claude-token-saver harness init # write CLAUDE.md (5 sections) + ratchet.md
|
|
632
593
|
// claude-token-saver harness uninit # remove harness block from CLAUDE.md (backup kept)
|
|
633
594
|
// claude-token-saver harness check # show 🅷 N/5 + which sections are missing
|
|
634
595
|
// claude-token-saver harness promote "<rule>" # append a rule to ratchet.md
|
|
635
|
-
// claude-token-saver harness pull [--
|
|
596
|
+
// claude-token-saver harness pull [--global|--project] # register the package's curated preset rules (default global)
|
|
636
597
|
// claude-token-saver harness off | on # toggle the statusline 🅷 segment
|
|
637
598
|
if (args[0] === 'harness') {
|
|
638
599
|
const sub = args[1];
|
|
@@ -792,31 +753,63 @@ async function main() {
|
|
|
792
753
|
process.exit(1);
|
|
793
754
|
}
|
|
794
755
|
}
|
|
795
|
-
// Route candidates
|
|
796
|
-
//
|
|
797
|
-
//
|
|
798
|
-
//
|
|
799
|
-
|
|
800
|
-
if (routeCandidate && scope === 'project') {
|
|
756
|
+
// Route candidates become MODEL-FITTING rules: they live in a
|
|
757
|
+
// tool-managed block (separate from user-authored ratchet rules) and
|
|
758
|
+
// keep updating from subsequent logs — recurrence counts, error rates,
|
|
759
|
+
// rule-health — on every rescan.
|
|
760
|
+
if (routeCandidate) {
|
|
801
761
|
const rs = await import('../src/route-scan.js');
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
762
|
+
const mr = await import('../src/model-rules.js');
|
|
763
|
+
// A --project rule must land in THE project the pattern was detected
|
|
764
|
+
// in, not the cwd's. Old caches without projectPath: verify cwd match.
|
|
765
|
+
let targetRoot = null;
|
|
766
|
+
if (scope === 'project') {
|
|
767
|
+
if (routeCandidate.projectPath) {
|
|
768
|
+
targetRoot = findProjectRoot(routeCandidate.projectPath);
|
|
769
|
+
} else if (rs.mungeProjectPath(findProjectRoot()) === routeCandidate.project) {
|
|
770
|
+
targetRoot = findProjectRoot();
|
|
771
|
+
} else {
|
|
772
|
+
console.error(`Route candidate R${routeCandidateId} was detected in another project (${routeCandidate.project}),`);
|
|
773
|
+
console.error('but this cached scan predates project-path tracking.');
|
|
774
|
+
console.error('Re-scan to capture it, then promote again:');
|
|
775
|
+
console.error(' claude-token-saver route-scan --refresh');
|
|
776
|
+
process.exit(1);
|
|
777
|
+
}
|
|
810
778
|
}
|
|
779
|
+
const entry = mr.addModelRule({
|
|
780
|
+
signature: routeCandidate.signature,
|
|
781
|
+
tier: routeCandidate.tier || 'T2',
|
|
782
|
+
category: routeCandidate.category,
|
|
783
|
+
label: routeCandidate.label,
|
|
784
|
+
agent: routeCandidate.agent,
|
|
785
|
+
scope,
|
|
786
|
+
targetRoot,
|
|
787
|
+
project: routeCandidate.project,
|
|
788
|
+
rule: routeCandidate.rule,
|
|
789
|
+
example: routeCandidate.example,
|
|
790
|
+
count: routeCandidate.count,
|
|
791
|
+
promotedAt: new Date().toISOString().slice(0, 10),
|
|
792
|
+
lastSeen: new Date().toISOString().slice(0, 10),
|
|
793
|
+
});
|
|
794
|
+
const written = mr.syncAllFiles();
|
|
795
|
+
rs.resolveCandidate(routeCandidateId);
|
|
796
|
+
console.log(`Model-fitting rule registered [${scope}${targetRoot ? ` → ${targetRoot}` : ''}] (tier ${entry.tier}):`);
|
|
797
|
+
console.log(` - ${entry.rule}`);
|
|
798
|
+
for (const p of written) console.log(` ratchet-model.md updated: ${p}`);
|
|
799
|
+
console.log('(route candidate R' + routeCandidateId + ' resolved — 다음 세션부터 자동 위임, 이후 스캔마다 로그 기반 갱신됩니다)');
|
|
800
|
+
console.log('룰 목록/제거: claude-token-saver route-scan rules [rm <N>]');
|
|
801
|
+
// Event-triggered refresh: establish the new rule's stat baseline
|
|
802
|
+
// right away instead of waiting for the next data-gated rescan.
|
|
803
|
+
try {
|
|
804
|
+
const { spawn } = await import('node:child_process');
|
|
805
|
+
spawn(process.execPath, [process.argv[1], 'route-scan', '--refresh', '--quiet'],
|
|
806
|
+
{ detached: true, stdio: 'ignore' }).unref();
|
|
807
|
+
} catch { /* baseline arrives on the next gated rescan instead */ }
|
|
808
|
+
return;
|
|
811
809
|
}
|
|
812
|
-
const r = harnessPromote(rule,
|
|
810
|
+
const r = harnessPromote(rule, { scope });
|
|
813
811
|
console.log(`Appended to ${r.path} [${r.scope}]:`);
|
|
814
812
|
console.log(` - ${rule}`);
|
|
815
|
-
if (routeCandidateId !== null) {
|
|
816
|
-
const rs = await import('../src/route-scan.js');
|
|
817
|
-
rs.resolveCandidate(routeCandidateId);
|
|
818
|
-
console.log(`(route candidate R${routeCandidateId} resolved — 다음 세션부터 자동 위임 룰로 적용됩니다)`);
|
|
819
|
-
}
|
|
820
813
|
if (/^\d+$/.test(raw)) {
|
|
821
814
|
console.log('\n👉 ratchet.md를 열어 TODO 부분을 실제 룰로 다듬어주세요.');
|
|
822
815
|
}
|
|
@@ -870,22 +863,21 @@ async function main() {
|
|
|
870
863
|
}
|
|
871
864
|
|
|
872
865
|
if (sub === 'pull') {
|
|
873
|
-
//
|
|
874
|
-
//
|
|
875
|
-
//
|
|
876
|
-
|
|
877
|
-
const
|
|
878
|
-
|
|
866
|
+
// Register the package's curated preset rules (presets/ratchet-rules.md)
|
|
867
|
+
// into the user's ratchet — global by default (they're tool/environment
|
|
868
|
+
// rules, and a project ratchet inherits global anyway). Strictly opt-in:
|
|
869
|
+
// install/init never auto-injects rules.
|
|
870
|
+
const scope = parseHarnessScope(args.slice(2), 'global');
|
|
871
|
+
const r = harnessPull({ scope });
|
|
872
|
+
console.log(`Curated preset rules → ${r.path} [${r.scope}]`);
|
|
879
873
|
if (r.added.length) {
|
|
880
|
-
console.log(`✅ ${r.added.length} rule(s)
|
|
874
|
+
console.log(`✅ ${r.added.length}/${r.presets} rule(s) registered:`);
|
|
881
875
|
for (const t of r.added) console.log(` - ${t}`);
|
|
882
876
|
} else {
|
|
883
|
-
console.log(
|
|
877
|
+
console.log(`No new rules — all ${r.presets} presets already registered.`);
|
|
884
878
|
}
|
|
885
|
-
if (r.skippedRules) console.log(` (${r.skippedRules} already present — skipped)`);
|
|
886
|
-
|
|
887
|
-
for (const f of r.skipped) console.log(` skip: ${f}`);
|
|
888
|
-
if (!includeBlock) console.log('\n글로벌 하네스 블록(CLAUDE.md 5개 섹션)까지 가져오려면: claude-token-saver harness pull --harness');
|
|
879
|
+
if (r.skippedRules && r.added.length) console.log(` (${r.skippedRules} already present — skipped)`);
|
|
880
|
+
console.log('\n필요 없는 룰은 언제든: claude-token-saver harness list / rm <N>');
|
|
889
881
|
return;
|
|
890
882
|
}
|
|
891
883
|
|
|
@@ -952,7 +944,7 @@ async function main() {
|
|
|
952
944
|
}
|
|
953
945
|
|
|
954
946
|
console.error(`Unknown harness subcommand: ${sub}`);
|
|
955
|
-
console.error('Usage: claude-token-saver harness [check|init|uninit [--purge-ratchet]|promote "<rule>"|pull [--
|
|
947
|
+
console.error('Usage: claude-token-saver harness [check|init|uninit [--purge-ratchet]|promote "<rule>"|pull [--global|--project]|list|rm <N>|off|on]');
|
|
956
948
|
process.exit(1);
|
|
957
949
|
}
|
|
958
950
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-token-saver",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.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": {
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"files": [
|
|
13
13
|
"bin/",
|
|
14
14
|
"src/",
|
|
15
|
+
"presets/",
|
|
15
16
|
"examples/",
|
|
16
17
|
"README.md",
|
|
17
18
|
"README.en.md"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# claude-token-saver 추천 Ratchet 룰 (curated presets)
|
|
2
|
+
|
|
3
|
+
이 파일은 패키지에 동봉되는 제작자 큐레이션 랫쳇 룰입니다.
|
|
4
|
+
`claude-token-saver harness pull`을 실행하면 아래 룰이 사용자의
|
|
5
|
+
글로벌 랫쳇(`~/.claude/ratchet.md`)에 등록됩니다 (opt-in, 중복 자동 스킵).
|
|
6
|
+
|
|
7
|
+
실제 반복 실수에서 승격된 룰만 담습니다 — 이론이 아니라 사고 이력.
|
|
8
|
+
|
|
9
|
+
## Rules
|
|
10
|
+
|
|
11
|
+
- 웹 fetch/search에서 robots·UA 차단('unable to fetch' 등)이 발생하면 → curl에 브라우저 UA(Mozilla/5.0 ... Chrome/...) 헤더를 붙여 재시도. reddit 등은 .json 엔드포인트 + UA 조합으로 접근 가능
|
|
12
|
+
- ratchet 룰 promote 호출 직전 사용자에게 scope(project/global)를 먼저 묻는다 — Bash 환경은 non-TTY라 CLI readline 프롬프트가 안 뜨므로, 호출자(LLM)가 대신 묻고 --project/--global 플래그를 명시해 실행
|
|
13
|
+
- 자동화 도구가 grep·정규식으로 찾는 마커 텍스트(헤더·앵커·키워드 라인)는 리팩터링·압축·수정 금지 — 정확 매칭 검출은 한 글자만 바뀌어도 깨짐
|
|
14
|
+
- 자동화 설계에서 vision/screenshot 루프가 보이면 → 호출 가능한 함수·API·MCP tool부터 찾는다. 통제 가능한 내부 도구는 무조건 API, 통제 불가 외부 SaaS만 vision 차선책. 인터페이스를 바꾼 뒤에 모델을 내린다 — 거꾸로 가면 토큰 폭발
|
|
15
|
+
- 입력 데이터(ground-truth) 변경 시 그에 의존하는 모든 파생 자산을 끝까지 재생성한다 — 중간 단계를 생략하면 stale 산출물이 새 입력 위에 얹혀 결과가 깨짐
|
|
16
|
+
- 탐색·조회·상태 확인·명령 실행 같은 단순 요청은 haiku 서브에이전트(haiku-explore·haiku-runner 등)로 위임해 상위 모델 토큰을 아낀다
|
package/src/harness-templates.js
CHANGED
|
@@ -37,6 +37,10 @@ ${HARNESS_SECTIONS[0].heading}
|
|
|
37
37
|
non-TTY라 CLI의 readline 프롬프트가 안 뜨므로, 호출자(LLM)가 직접 묻고
|
|
38
38
|
플래그를 명시해야 함. 묻지 않고 기본값으로 등록하지 말 것.
|
|
39
39
|
- 승인된 룰은 다음 세션부터 자동 적용.
|
|
40
|
+
- **모델 피팅 랫쳇**: \`.claude/ratchet-model.md\`(프로젝트)와
|
|
41
|
+
\`~/.claude/ratchet-model.md\`(글로벌)에 있는 티어 위임 룰도 ratchet.md와
|
|
42
|
+
동일하게 따를 것. 이 파일은 claude-token-saver가 로그 기반으로 자동
|
|
43
|
+
생성·갱신하므로 직접 수정하지 말 것 (관리: \`route-scan rules\`).
|
|
40
44
|
|
|
41
45
|
${HARNESS_SECTIONS[1].heading}
|
|
42
46
|
완료 보고("다 됐어요", "테스트 통과") 시 다음 중 1개 이상을 항상 첨부:
|