claude-token-saver 3.3.0 → 3.3.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 +3 -0
- package/README.md +3 -0
- package/bin/cli.js +29 -14
- package/package.json +1 -1
- package/src/route-scan.js +24 -0
package/README.en.md
CHANGED
|
@@ -269,6 +269,9 @@ Also update `statusLine.command` in `~/.claude/settings.json` to `claude-token-s
|
|
|
269
269
|
|
|
270
270
|
## Release notes
|
|
271
271
|
|
|
272
|
+
### v3.3.1 (2026-07-13)
|
|
273
|
+
- **Plain language instead of codes** — bare `R1`/`T2` codes were unreadable for first-time users. Every user-facing listing (SessionStart hook briefing, `route-scan` candidates, `route-scan rules`) now spells tiers out as `T2 (simple — haiku-class is enough)` and scopes as `this project only` / `all projects (global)`. The hook briefing also instructs the model to relay the spelled-out explanation, not the codes. (Statusline chips keep the terse `route? R1` for width; the session briefing carries the meaning.)
|
|
274
|
+
|
|
272
275
|
### v3.3.0 (2026-07-13)
|
|
273
276
|
- **Delegation visibility & briefing** — the whole model-fitting loop is now surfaced to the user:
|
|
274
277
|
- The SessionStart hook injects, per candidate, **the exact rule text that would be written to ratchet-model.md** — you see precisely what you're approving.
|
package/README.md
CHANGED
|
@@ -226,6 +226,9 @@ npm uninstall -g claude-cache-monitor && npm i -g claude-token-saver
|
|
|
226
226
|
|
|
227
227
|
## 릴리스 노트
|
|
228
228
|
|
|
229
|
+
### v3.3.1 (2026-07-13)
|
|
230
|
+
- **코드 대신 풀어쓴 설명** — `R1`, `T2` 같은 코드가 설명 없이 노출돼 처음 쓰는 사람이 알 수 없던 문제 수정. 모든 사용자 대면 출력(SessionStart 훅 브리핑, `route-scan` 후보 목록, `route-scan rules` 목록)에서 티어를 `T2 (단순 작업 — haiku급이면 충분)` 식으로 풀어쓰고, scope도 `이 프로젝트만`/`모든 프로젝트(글로벌)`로 표기. 훅 브리핑에는 "사용자에게 전달할 때 코드가 아니라 풀어쓴 설명으로 브리핑하라"는 지시 포함. (statusline 칩은 폭 제약상 `route? R1` 유지 — 의미는 세션 브리핑이 설명)
|
|
231
|
+
|
|
229
232
|
### v3.3.0 (2026-07-13)
|
|
230
233
|
- **위임 가시화·브리핑 강화** — 모델 피팅의 전 과정이 사용자에게 보이도록:
|
|
231
234
|
- SessionStart 훅이 후보마다 **등록 시 ratchet-model.md에 기록될 룰 원문**을 함께 주입 — 무엇이 등록될지 정확히 보고 승인.
|
package/bin/cli.js
CHANGED
|
@@ -520,8 +520,13 @@ async function main() {
|
|
|
520
520
|
}
|
|
521
521
|
console.log(lang === 'ko' ? '📐 모델 피팅 룰 (로그 기반 자동 갱신):' : '📐 Model-fitting rules (auto-refreshed from logs):');
|
|
522
522
|
rules.forEach((r, i) => {
|
|
523
|
-
const health = r.status === 'review'
|
|
524
|
-
|
|
523
|
+
const health = r.status === 'review'
|
|
524
|
+
? (lang === 'ko' ? ' ⚠ 에러율 초과 — 재검토 필요' : ' ⚠ error rate over threshold — needs review')
|
|
525
|
+
: '';
|
|
526
|
+
const stat = lang === 'ko'
|
|
527
|
+
? `${r.tier} (${rs.tierLabel(r.tier)}) · ${rs.scopeLabel(r.scope)} · 반복 ${r.count || 0}회 · 에러율 ${Math.round((r.errRate || 0) * 100)}%`
|
|
528
|
+
: `${r.tier} (${rs.tierLabel(r.tier, 'en')}) · ${rs.scopeLabel(r.scope, 'en')} · seen ×${r.count || 0} · err ${Math.round((r.errRate || 0) * 100)}%`;
|
|
529
|
+
console.log(` #${i + 1} ${stat}${health}`);
|
|
525
530
|
console.log(` ${r.rule}`);
|
|
526
531
|
});
|
|
527
532
|
console.log('\n제거: claude-token-saver route-scan rules rm <N>');
|
|
@@ -571,21 +576,24 @@ async function main() {
|
|
|
571
576
|
if (open.length === 0 && reviewRules.length === 0) return; // silent — nothing to inject
|
|
572
577
|
const lines = [];
|
|
573
578
|
if (open.length > 0) {
|
|
574
|
-
lines.push(`[claude-token-saver route-scan] 최근 ${cache.days}일 세션에서
|
|
579
|
+
lines.push(`[claude-token-saver route-scan] 최근 ${cache.days}일 세션에서 비싼 모델(opus/fable)이 반복 처리해 온, 더 싼 모델로 넘겨도 되는 작업이 감지되었습니다.`);
|
|
580
|
+
lines.push('(R<N>은 후보 번호, T2/T1은 난이도 등급입니다 — 사용자에게 전달할 때는 코드가 아니라 아래 풀어쓴 설명으로 브리핑하세요)');
|
|
575
581
|
for (const c of open) {
|
|
576
|
-
const
|
|
577
|
-
lines.push(` R${c.id}
|
|
578
|
-
lines.push(`
|
|
582
|
+
const tier = c.tier || 'T2';
|
|
583
|
+
lines.push(` 후보 R${c.id} — "${c.label}" 유형, ${c.count}회 반복 (프로젝트: ${c.project})`);
|
|
584
|
+
lines.push(` 판정: ${tier} (${rs.tierLabel(tier)}) → ${c.agent} 서브에이전트 위임 권장 · 적용 범위 제안: ${rs.scopeLabel(c.suggestedScope)}`);
|
|
585
|
+
lines.push(` 예시 요청: "${c.example}"`);
|
|
579
586
|
lines.push(` 등록 시 ratchet-model.md에 기록될 룰: "${c.rule}"`);
|
|
580
587
|
}
|
|
581
|
-
lines.push('등록하면 다음 세션부터 자동 위임됩니다. 사용자에게 등록 여부를 물을 때 위 룰 원문을 그대로 보여주고,
|
|
582
|
-
lines.push(' claude-token-saver harness promote R<N> --project|--global #
|
|
588
|
+
lines.push('등록하면 다음 세션부터 자동 위임됩니다. 사용자에게 등록 여부를 물을 때 위 룰 원문을 그대로 보여주고, 적용 범위까지 확인한 뒤 실행하세요:');
|
|
589
|
+
lines.push(' claude-token-saver harness promote R<N> --project|--global # 적용 범위는 반드시 사용자에게 확인');
|
|
583
590
|
lines.push(' claude-token-saver route-scan dismiss <N> # 사용자가 원치 않으면');
|
|
584
591
|
}
|
|
585
592
|
if (reviewRules.length > 0) {
|
|
586
|
-
lines.push(`[claude-token-saver rule-health]
|
|
593
|
+
lines.push(`[claude-token-saver rule-health] 사용자가 승인한 위임 룰 중, 위임 대상 유형의 최근 에러율이 기준(20%)을 넘어 재검토가 필요한 룰이 있습니다 — 사용자에게 브리핑하고 조건 좁히기/제거를 상의하세요:`);
|
|
587
594
|
for (const r of reviewRules) {
|
|
588
|
-
lines.push(` #${r.n}
|
|
595
|
+
lines.push(` 룰 #${r.n} (${r.tier} ${rs.tierLabel(r.tier)} · ${rs.scopeLabel(r.scope)}) — 최근 에러율 ${Math.round((r.errRate || 0) * 100)}%`);
|
|
596
|
+
lines.push(` "${r.rule}"`);
|
|
589
597
|
}
|
|
590
598
|
lines.push(' 제거: claude-token-saver route-scan rules rm <N>');
|
|
591
599
|
}
|
|
@@ -614,11 +622,18 @@ async function main() {
|
|
|
614
622
|
: 'No delegation candidates (below recurrence threshold or already resolved).');
|
|
615
623
|
return;
|
|
616
624
|
}
|
|
617
|
-
console.log(lang === 'ko' ? '\n위임
|
|
625
|
+
console.log(lang === 'ko' ? '\n위임 후보 (R<N>=후보 번호, T2/T1=난이도 등급):' : '\nDelegation candidates (R<N> = candidate id, T2/T1 = difficulty tier):');
|
|
618
626
|
for (const c of open) {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
627
|
+
const tier = c.tier || 'T2';
|
|
628
|
+
if (lang === 'ko') {
|
|
629
|
+
console.log(` R${c.id} "${c.label}" ×${c.count}회 [${c.project}]`);
|
|
630
|
+
console.log(` 판정: ${tier} (${rs.tierLabel(tier)}) → ${c.agent} 위임 권장 · 적용 범위 제안: ${rs.scopeLabel(c.suggestedScope)}`);
|
|
631
|
+
} else {
|
|
632
|
+
console.log(` R${c.id} "${c.label}" ×${c.count} [${c.project}]`);
|
|
633
|
+
console.log(` verdict: ${tier} (${rs.tierLabel(tier, 'en')}) → delegate to ${c.agent} · suggested scope: ${rs.scopeLabel(c.suggestedScope, 'en')}`);
|
|
634
|
+
}
|
|
635
|
+
console.log(` ${lang === 'ko' ? '예시' : 'example'}: "${c.example}"`);
|
|
636
|
+
console.log(` ${lang === 'ko' ? '룰' : 'rule'}: ${c.rule}`);
|
|
622
637
|
}
|
|
623
638
|
console.log('');
|
|
624
639
|
console.log(lang === 'ko' ? '등록 / 무시:' : 'Promote / dismiss:');
|
package/package.json
CHANGED
package/src/route-scan.js
CHANGED
|
@@ -406,6 +406,30 @@ export async function shouldRescan(cache, { days = 14 } = {}) {
|
|
|
406
406
|
return age >= RESCAN_MAX_AGE_MS;
|
|
407
407
|
}
|
|
408
408
|
|
|
409
|
+
/**
|
|
410
|
+
* Human-readable labels for the T0/T1/T2 codes and scope values — used by
|
|
411
|
+
* every user-facing listing so a bare code never appears without its meaning
|
|
412
|
+
* (first-time users can't be expected to know the tier vocabulary).
|
|
413
|
+
*/
|
|
414
|
+
export function tierLabel(tier, lang = 'ko') {
|
|
415
|
+
const ko = {
|
|
416
|
+
T2: '단순 작업 — haiku급이면 충분',
|
|
417
|
+
T1: '중간 난도 — sonnet급이면 충분',
|
|
418
|
+
T0: '고난도 — 지금 모델 유지',
|
|
419
|
+
};
|
|
420
|
+
const en = {
|
|
421
|
+
T2: 'simple — haiku-class is enough',
|
|
422
|
+
T1: 'moderate — sonnet-class is enough',
|
|
423
|
+
T0: 'hard — stays on the session model',
|
|
424
|
+
};
|
|
425
|
+
return (lang === 'ko' ? ko : en)[tier] || tier;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
export function scopeLabel(scope, lang = 'ko') {
|
|
429
|
+
if (lang === 'ko') return scope === 'global' ? '모든 프로젝트(글로벌)' : '이 프로젝트만';
|
|
430
|
+
return scope === 'global' ? 'all projects (global)' : 'this project only';
|
|
431
|
+
}
|
|
432
|
+
|
|
409
433
|
/** Candidates not yet promoted/dismissed. */
|
|
410
434
|
export function openCandidates(cache) {
|
|
411
435
|
if (!cache?.candidates) return [];
|