claude-token-saver 3.0.0 → 3.0.1
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 +6 -4
- package/README.md +6 -4
- package/bin/cli.js +13 -14
- package/package.json +2 -1
- package/presets/ratchet-rules.md +16 -0
- package/src/harness.js +36 -57
package/README.en.md
CHANGED
|
@@ -117,14 +117,13 @@ claude-token-saver harness init --global # ~/.claude/CLAUDE.md — every p
|
|
|
117
117
|
claude-token-saver harness check # current score (global fallback honored)
|
|
118
118
|
claude-token-saver harness promote <N> --project|--global # warning #N → ratchet rule (scope required)
|
|
119
119
|
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)
|
|
120
|
+
claude-token-saver harness pull # register the package's curated ratchet rules into your global ratchet (opt-in, dedupes)
|
|
122
121
|
claude-token-saver harness list / rm <N> # view / delete rules (auto .bak)
|
|
123
122
|
claude-token-saver harness off | on # toggle the 🅷 chip
|
|
124
123
|
```
|
|
125
124
|
|
|
126
125
|
- `promote` **requires** `--project`/`--global` in non-TTY contexts (scripts, LLM calls) — a scope choice is never silently made for the caller.
|
|
127
|
-
- `
|
|
126
|
+
- `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
127
|
- 🅷⚠ 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
128
|
|
|
130
129
|
<details>
|
|
@@ -251,10 +250,13 @@ Also update `statusLine.command` in `~/.claude/settings.json` to `claude-token-s
|
|
|
251
250
|
|
|
252
251
|
## Release notes
|
|
253
252
|
|
|
253
|
+
### v3.0.1 (2026-07-13)
|
|
254
|
+
- **`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.
|
|
255
|
+
|
|
254
256
|
### v3.0.0 (2026-07-13)
|
|
255
257
|
- **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
258
|
- **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`** —
|
|
259
|
+
- **New `harness pull`** — redefined in v3.0.1 (see above).
|
|
258
260
|
|
|
259
261
|
### v2.20.0 (2026-07-13)
|
|
260
262
|
- **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
|
@@ -98,14 +98,13 @@ claude-token-saver harness init --global # ~/.claude/CLAUDE.md — 모든
|
|
|
98
98
|
claude-token-saver harness check # 현재 점수 (글로벌 fallback 인정)
|
|
99
99
|
claude-token-saver harness promote <N> --project|--global # 경고 #N → ratchet 룰 (스코프 필수)
|
|
100
100
|
claude-token-saver harness promote "<룰 텍스트>" --project|--global # 내가 직접 정의한 룰도 같은 명령으로 등록
|
|
101
|
-
claude-token-saver harness pull #
|
|
102
|
-
claude-token-saver harness pull --harness # 글로벌 하네스 블록(5개 섹션)까지 함께 가져오기
|
|
101
|
+
claude-token-saver harness pull # 패키지 동봉 큐레이션 룰 → 내 글로벌 랫쳇에 등록 (opt-in, 중복 스킵)
|
|
103
102
|
claude-token-saver harness list / rm <N> # 룰 조회 / 삭제 (자동 .bak)
|
|
104
103
|
claude-token-saver harness off | on # 🅷 표시 토글
|
|
105
104
|
```
|
|
106
105
|
|
|
107
106
|
- `promote`는 non-TTY(스크립트·LLM 호출)에서 `--project`/`--global` 플래그가 **필수** — 스코프가 묻지 않고 결정되는 사고를 막기 위한 설계입니다.
|
|
108
|
-
- 설치(`install`)나 `init`은
|
|
107
|
+
- `pull`은 패키지에 동봉된 **제작자 큐레이션 랫쳇 룰**(`presets/ratchet-rules.md` — 실제 반복 사고에서 승격된 범용 룰만)을 내 글로벌 랫쳇(`~/.claude/ratchet.md`)에 등록합니다. 설치(`install`)나 `init`은 아무것도 자동 주입하지 않으며, `pull`은 항상 opt-in이고 재실행해도 중복이 없습니다(멱등). 마음에 안 드는 룰은 `harness rm`으로 제거하면 됩니다.
|
|
109
108
|
- 🅷⚠ 런타임 경고(`ratchet?` `no-evidence` `PEV-skip`)는 30분 후 자동 만료되고, 하위 디렉터리 세션도 프로젝트에 올바르게 매칭됩니다. PEV-skip은 변경성 도구(Edit/Write/Bash)만 카운트해 읽기 위주 세션에서는 발동하지 않습니다 (v2.16.0+).
|
|
110
109
|
|
|
111
110
|
<details>
|
|
@@ -208,10 +207,13 @@ npm uninstall -g claude-cache-monitor && npm i -g claude-token-saver
|
|
|
208
207
|
|
|
209
208
|
## 릴리스 노트
|
|
210
209
|
|
|
210
|
+
### v3.0.1 (2026-07-13)
|
|
211
|
+
- **`harness pull` 재정의** — v3.0.0의 "글로벌 랫쳇 → 프로젝트 복사"는 글로벌 랫쳇이 이미 프로젝트의 상위 계층으로 항상 적용되므로 무의미해 제거. `pull`은 이제 패키지에 동봉된 **제작자 큐레이션 랫쳇 룰**(`presets/ratchet-rules.md`)을 사용자의 글로벌 랫쳇에 등록합니다 — 실제 반복 사고에서 승격된 범용 룰 6종, opt-in·멱등.
|
|
212
|
+
|
|
211
213
|
### v3.0.0 (2026-07-13)
|
|
212
214
|
- **메이저 승격** — v2.19 frugon 연계 + v2.20 route-scan으로 "사후 토큰 모니터링 도구"에서 "반복 easy 작업을 싼 모델로 내려보내는 라우팅 계층"으로 제품 성격이 바뀌어 메이저 버전을 올립니다. Breaking change는 없습니다 (기존 명령·설정 전부 호환).
|
|
213
215
|
- **route-scan promote 교정** — `harness promote R<N> --project`가 이제 후보가 **감지된 프로젝트**의 `.claude/ratchet.md`에 룰을 기록합니다 (이전에는 CLI를 실행한 디렉터리에 기록되는 버그). 스캔이 후보에 실제 세션 경로(`projectPath`)를 저장하며, 이 필드가 없는 구버전 캐시에서 다른 프로젝트 후보를 승격하려 하면 `route-scan --refresh`를 안내하고 중단합니다.
|
|
214
|
-
- **`harness pull` 신설** —
|
|
216
|
+
- **`harness pull` 신설** — v3.0.1에서 재정의됨 (위 참고).
|
|
215
217
|
|
|
216
218
|
### v2.20.0 (2026-07-13)
|
|
217
219
|
- **route-scan**: 상위 모델이 반복 처리한 easy 작업 감지 → `🅷⚠ route? R<N>` 칩 + SessionStart 훅 컨텍스트 주입 + `harness promote R<N> --project|--global`로 haiku 위임 랫쳇 룰 승격.
|
package/bin/cli.js
CHANGED
|
@@ -632,7 +632,7 @@ async function main() {
|
|
|
632
632
|
// claude-token-saver harness uninit # remove harness block from CLAUDE.md (backup kept)
|
|
633
633
|
// claude-token-saver harness check # show 🅷 N/5 + which sections are missing
|
|
634
634
|
// claude-token-saver harness promote "<rule>" # append a rule to ratchet.md
|
|
635
|
-
// claude-token-saver harness pull [--
|
|
635
|
+
// claude-token-saver harness pull [--global|--project] # register the package's curated preset rules (default global)
|
|
636
636
|
// claude-token-saver harness off | on # toggle the statusline 🅷 segment
|
|
637
637
|
if (args[0] === 'harness') {
|
|
638
638
|
const sub = args[1];
|
|
@@ -870,22 +870,21 @@ async function main() {
|
|
|
870
870
|
}
|
|
871
871
|
|
|
872
872
|
if (sub === 'pull') {
|
|
873
|
-
//
|
|
874
|
-
//
|
|
875
|
-
//
|
|
876
|
-
|
|
877
|
-
const
|
|
878
|
-
|
|
873
|
+
// Register the package's curated preset rules (presets/ratchet-rules.md)
|
|
874
|
+
// into the user's ratchet — global by default (they're tool/environment
|
|
875
|
+
// rules, and a project ratchet inherits global anyway). Strictly opt-in:
|
|
876
|
+
// install/init never auto-injects rules.
|
|
877
|
+
const scope = parseHarnessScope(args.slice(2), 'global');
|
|
878
|
+
const r = harnessPull({ scope });
|
|
879
|
+
console.log(`Curated preset rules → ${r.path} [${r.scope}]`);
|
|
879
880
|
if (r.added.length) {
|
|
880
|
-
console.log(`✅ ${r.added.length} rule(s)
|
|
881
|
+
console.log(`✅ ${r.added.length}/${r.presets} rule(s) registered:`);
|
|
881
882
|
for (const t of r.added) console.log(` - ${t}`);
|
|
882
883
|
} else {
|
|
883
|
-
console.log(
|
|
884
|
+
console.log(`No new rules — all ${r.presets} presets already registered.`);
|
|
884
885
|
}
|
|
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');
|
|
886
|
+
if (r.skippedRules && r.added.length) console.log(` (${r.skippedRules} already present — skipped)`);
|
|
887
|
+
console.log('\n필요 없는 룰은 언제든: claude-token-saver harness list / rm <N>');
|
|
889
888
|
return;
|
|
890
889
|
}
|
|
891
890
|
|
|
@@ -952,7 +951,7 @@ async function main() {
|
|
|
952
951
|
}
|
|
953
952
|
|
|
954
953
|
console.error(`Unknown harness subcommand: ${sub}`);
|
|
955
|
-
console.error('Usage: claude-token-saver harness [check|init|uninit [--purge-ratchet]|promote "<rule>"|pull [--
|
|
954
|
+
console.error('Usage: claude-token-saver harness [check|init|uninit [--purge-ratchet]|promote "<rule>"|pull [--global|--project]|list|rm <N>|off|on]');
|
|
956
955
|
process.exit(1);
|
|
957
956
|
}
|
|
958
957
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-token-saver",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.1",
|
|
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.js
CHANGED
|
@@ -278,79 +278,58 @@ export function harnessPromote(ruleText, { root = findProjectRoot(), scope = 'pr
|
|
|
278
278
|
}
|
|
279
279
|
|
|
280
280
|
/**
|
|
281
|
-
* harness pull —
|
|
282
|
-
* into the
|
|
283
|
-
* install/init never auto-injects rules; this is the explicit "땡겨오기" verb.
|
|
281
|
+
* harness pull — register the CURATED ratchet rules bundled with this package
|
|
282
|
+
* (presets/ratchet-rules.md) into the user's ratchet, global by default.
|
|
284
283
|
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
284
|
+
* Rationale: a project ratchet already inherits the global one (global is the
|
|
285
|
+
* upper layer of the hierarchy), so there is nothing to copy between the
|
|
286
|
+
* user's own scopes. What CAN'T reach the user any other way is the package
|
|
287
|
+
* author's field-tested rules — pull ships those, strictly opt-in:
|
|
288
|
+
* install/init never auto-injects anything.
|
|
289
289
|
*
|
|
290
|
-
*
|
|
290
|
+
* Deduped by rule text (ignoring the YYYY-MM-DD stamp) — idempotent.
|
|
291
|
+
* Returns { path, scope, added, skippedRules, presets }.
|
|
291
292
|
*/
|
|
292
|
-
export function harnessPull({ root = findProjectRoot(),
|
|
293
|
-
const
|
|
293
|
+
export function harnessPull({ root = findProjectRoot(), scope = 'global' } = {}) {
|
|
294
|
+
const presets = presetRules();
|
|
295
|
+
const rmPath = resolveRatchetPath(scope, root);
|
|
296
|
+
const result = { path: rmPath, scope, added: [], skippedRules: 0, presets: presets.length };
|
|
294
297
|
const stripDate = (t) => t.replace(/^\d{4}-\d{2}-\d{2}:\s*/, '').trim();
|
|
295
298
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
const projPath = ratchetMdPath(root);
|
|
299
|
-
let content = existsSync(projPath)
|
|
300
|
-
? readFileSync(projPath, 'utf8')
|
|
299
|
+
let content = existsSync(rmPath)
|
|
300
|
+
? readFileSync(rmPath, 'utf8')
|
|
301
301
|
: harnessRatchetMdInitial();
|
|
302
302
|
const have = new Set(
|
|
303
|
-
harnessListRules({ root, scope
|
|
303
|
+
harnessListRules({ root, scope }).rules.map((r) => stripDate(r.text)),
|
|
304
304
|
);
|
|
305
|
-
for (const
|
|
306
|
-
|
|
307
|
-
if (have.has(key)) {
|
|
305
|
+
for (const rule of presets) {
|
|
306
|
+
if (have.has(rule)) {
|
|
308
307
|
result.skippedRules += 1;
|
|
309
308
|
continue;
|
|
310
309
|
}
|
|
311
|
-
content = appendRatchetRule(content,
|
|
312
|
-
have.add(
|
|
313
|
-
result.added.push(
|
|
310
|
+
content = appendRatchetRule(content, rule);
|
|
311
|
+
have.add(rule);
|
|
312
|
+
result.added.push(rule);
|
|
314
313
|
}
|
|
315
314
|
if (result.added.length) {
|
|
316
|
-
mkdirSync(dirname(
|
|
317
|
-
writeFileSync(
|
|
318
|
-
result.wrote.push(projPath);
|
|
315
|
+
mkdirSync(dirname(rmPath), { recursive: true });
|
|
316
|
+
writeFileSync(rmPath, content);
|
|
319
317
|
}
|
|
318
|
+
return result;
|
|
319
|
+
}
|
|
320
320
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
result.skipped.push(`${gPath} (no global harness block to pull)`);
|
|
333
|
-
} else {
|
|
334
|
-
const block = m[0];
|
|
335
|
-
const pPath = claudeMdPath(root);
|
|
336
|
-
if (existsSync(pPath)) {
|
|
337
|
-
const pc = readFileSync(pPath, 'utf8');
|
|
338
|
-
if (pc.includes(HARNESS_BLOCK_BEGIN)) {
|
|
339
|
-
writeFileSync(pPath, pc.replace(blockRe, block));
|
|
340
|
-
result.wrote.push(`${pPath} (harness block replaced with global copy)`);
|
|
341
|
-
} else {
|
|
342
|
-
const sep = pc.endsWith('\n') ? '\n' : '\n\n';
|
|
343
|
-
writeFileSync(pPath, pc + sep + block);
|
|
344
|
-
result.wrote.push(`${pPath} (harness block appended from global)`);
|
|
345
|
-
}
|
|
346
|
-
} else {
|
|
347
|
-
writeFileSync(pPath, block);
|
|
348
|
-
result.wrote.push(pPath);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
321
|
+
/** Parse the bundled preset rules (markdown bullets under presets/). */
|
|
322
|
+
export function presetRules() {
|
|
323
|
+
try {
|
|
324
|
+
const path = join(dirname(new URL(import.meta.url).pathname), '..', 'presets', 'ratchet-rules.md');
|
|
325
|
+
return readFileSync(path, 'utf8')
|
|
326
|
+
.split('\n')
|
|
327
|
+
.filter((l) => /^\s*-\s+/.test(l))
|
|
328
|
+
.map((l) => l.replace(/^\s*-\s+/, '').trim())
|
|
329
|
+
.filter(Boolean);
|
|
330
|
+
} catch {
|
|
331
|
+
return [];
|
|
351
332
|
}
|
|
352
|
-
|
|
353
|
-
return result;
|
|
354
333
|
}
|
|
355
334
|
|
|
356
335
|
/**
|