claude-token-saver 3.6.4 β 3.7.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 +3 -0
- package/README.md +3 -0
- package/package.json +1 -1
- package/presets/ratchet-rules.md +1 -1
- package/src/agents.js +52 -0
- package/src/model-rules.js +3 -2
- package/src/route-scan.js +3 -2
package/README.en.md
CHANGED
|
@@ -243,6 +243,9 @@ Also update `statusLine.command` in `~/.claude/settings.json` to `claude-token-s
|
|
|
243
243
|
|
|
244
244
|
## Release notes
|
|
245
245
|
|
|
246
|
+
### v3.7.0 (2026-07-29)
|
|
247
|
+
- **Fixed: delegation rules named subagents that may not exist** β generated T2 rules hard-coded preset names like `haiku-explore` / `haiku-runner`, which live in each user's own `~/.claude/agents/` and are not shipped by this package. On a machine without them, the rule told the model to delegate to a nonexistent agent. The default phrasing is now `model: haiku` (matching T1's `model: sonnet`), and the name is added only when the agent file is really present, e.g. `haiku-explore(model: haiku)`. Project-level `.claude/agents/` counts too. `ratchet-model.md` re-renders with the new phrasing on the next `route-scan`.
|
|
248
|
+
|
|
246
249
|
### v3.6.4 (2026-07-29)
|
|
247
250
|
- **Fixed: false `π
·β ratchet-unloaded`** β the import check looked at a single CLAUDE.md. When the project file carried the harness block, only that file was inspected, so the common layout of block-in-project + `@` imports-in-global raised the warning even though the rules loaded fine. Claude Code loads both files, so the flags are now the union of the two, and `importSource` (`project`/`global`/`both`) says which file carries them. The warning fires only when neither file imports.
|
|
248
251
|
|
package/README.md
CHANGED
|
@@ -200,6 +200,9 @@ npm uninstall -g claude-cache-monitor && npm i -g claude-token-saver
|
|
|
200
200
|
|
|
201
201
|
## λ¦΄λ¦¬μ€ λ
ΈνΈ
|
|
202
202
|
|
|
203
|
+
### v3.7.0 (2026-07-29)
|
|
204
|
+
- **μμ λ£°μ΄ μλ μλΈμμ΄μ νΈλ₯Ό κ°λ¦¬ν€λ λ¬Έμ μμ ** β μμ±λλ T2 λ£°μ΄ `haiku-explore`Β·`haiku-runner` κ°μ μ΄λ¦μ μ§μ μ μλλ°, μ΄ preset μμ΄μ νΈλ€μ κ°μμ `~/.claude/agents/`μ μλ κ²μ΄λΌ ν¨ν€μ§κ° λ°°ν¬νμ§ μμ΅λλ€. κ·Έλμ ν΄λΉ νμΌμ΄ μλ νκ²½μμλ "μ‘΄μ¬νμ§ μλ μμ΄μ νΈλ‘ μμνλΌ"λ λ£°μ΄ μλ μμ±λμ΅λλ€. μ΄μ κΈ°λ³Έ ννμ `model: haiku`(T1μ `model: sonnet`κ³Ό ν΅μΌ)μ΄κ³ , μμ΄μ νΈ νμΌμ΄ μ€μ λ‘ μμ λλ§ `haiku-explore(model: haiku)`μ²λΌ μ΄λ¦μ λ³κΈ°ν©λλ€. νλ‘μ νΈ `.claude/agents/`λ μΈμν©λλ€. λ€μ `route-scan` λ `ratchet-model.md`κ° μ ννμΌλ‘ λ€μ λ λλ©λλ€.
|
|
205
|
+
|
|
203
206
|
### v3.6.4 (2026-07-29)
|
|
204
207
|
- **`π
·β ratchet-unloaded`κ° μλͺ» λ¨λ λ²κ·Έ μμ ** β import μ¬λΆλ₯Ό CLAUDE.md ν νμΌμμλ§ νμΈνμ΅λλ€. νλ‘μ νΈ `CLAUDE.md`μ harness λΈλ‘μ΄ μμΌλ©΄ κ·Έ νμΌλ§ λ³΄κ³ νμ νκΈ° λλ¬Έμ, `@` importκ° κΈλ‘λ² `~/.claude/CLAUDE.md`μ μλ νν μ‘°ν©μμλ λ£°μ΄ μ μ λ‘λλλλ°λ κ²½κ³ κ° λ΄μ΅λλ€. Claude Codeλ λ νμΌμ λͺ¨λ λ‘λνλ―λ‘ μ΄μ μμͺ½μ importλ₯Ό ν©μ³μ νμ νκ³ , μ΄λ μͺ½μ΄ λ€κ³ μλμ§λ `importSource`(`project`/`global`/`both`)λ‘ μλ €μ€λλ€. λ νμΌ λ€ importκ° μμ λλ§ κ²½κ³ ν©λλ€.
|
|
205
208
|
|
package/package.json
CHANGED
package/presets/ratchet-rules.md
CHANGED
|
@@ -13,4 +13,4 @@
|
|
|
13
13
|
- μλν λκ΅¬κ° grepΒ·μ κ·μμΌλ‘ μ°Ύλ λ§μ»€ ν
μ€νΈ(ν€λΒ·μ΅μ»€Β·ν€μλ λΌμΈ)λ 리ν©ν°λ§Β·μμΆΒ·μμ κΈμ§ β μ ν λ§€μΉ κ²μΆμ ν κΈμλ§ λ°λμ΄λ κΉ¨μ§
|
|
14
14
|
- μλν μ€κ³μμ vision/screenshot 루νκ° λ³΄μ΄λ©΄ β νΈμΆ κ°λ₯ν ν¨μΒ·APIΒ·MCP toolλΆν° μ°Ύλλ€. ν΅μ κ°λ₯ν λ΄λΆ λꡬλ 무쑰건 API, ν΅μ λΆκ° μΈλΆ SaaSλ§ vision μ°¨μ μ±
. μΈν°νμ΄μ€λ₯Ό λ°κΎΌ λ€μ λͺ¨λΈμ λ΄λ¦°λ€ β κ±°κΎΈλ‘ κ°λ©΄ ν ν° νλ°
|
|
15
15
|
- μ
λ ₯ λ°μ΄ν°(ground-truth) λ³κ²½ μ κ·Έμ μμ‘΄νλ λͺ¨λ νμ μμ°μ λκΉμ§ μ¬μμ±νλ€ β μ€κ° λ¨κ³λ₯Ό μλ΅νλ©΄ stale μ°μΆλ¬Όμ΄ μ μ
λ ₯ μμ μΉν κ²°κ³Όκ° κΉ¨μ§
|
|
16
|
-
- νμΒ·μ‘°νΒ·μν νμΈΒ·λͺ
λ Ή μ€ν κ°μ λ¨μ μμ²μ
|
|
16
|
+
- νμΒ·μ‘°νΒ·μν νμΈΒ·λͺ
λ Ή μ€ν κ°μ λ¨μ μμ²μ `model: haiku` μλΈμμ΄μ νΈλ‘ μμν΄ μμ λͺ¨λΈ ν ν°μ μλλ€ (μ½κΈ° μ μ© preset μμ΄μ νΈκ° μ΄λ―Έ μμΌλ©΄ κ·Έκ±Έ μ°κ³ , μμΌλ©΄ ν°μ΄λ§ λ΄λ €μ μμ)
|
package/src/agents.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a delegation target is phrased in generated rules.
|
|
3
|
+
*
|
|
4
|
+
* Rules used to name concrete subagents (`haiku-explore`, `haiku-runner`, β¦).
|
|
5
|
+
* Those live in the user's own `~/.claude/agents/` and are NOT shipped by this
|
|
6
|
+
* package, so on any machine that never created them the generated rule told
|
|
7
|
+
* the model to delegate to an agent that does not exist. The model tier is the
|
|
8
|
+
* part that actually saves tokens and it is universal, so `model: haiku` is the
|
|
9
|
+
* default phrasing β the agent name is only added when the file is really
|
|
10
|
+
* there, which keeps the tool-restricted preset in play for users who have it.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { existsSync } from 'node:fs';
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
import { claudeUserDir } from './paths.js';
|
|
16
|
+
|
|
17
|
+
/** Tier each preset agent is pinned to, for the `model:` half of the phrase. */
|
|
18
|
+
const AGENT_MODEL = {
|
|
19
|
+
'haiku-explore': 'haiku',
|
|
20
|
+
'haiku-runner': 'haiku',
|
|
21
|
+
'haiku-translate': 'haiku',
|
|
22
|
+
'sonnet-worker': 'sonnet',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** True when `<name>.md` exists in the project or user agents directory. */
|
|
26
|
+
export function agentExists(name, root = process.cwd()) {
|
|
27
|
+
if (!name) return false;
|
|
28
|
+
return existsSync(join(root, '.claude', 'agents', `${name}.md`)) ||
|
|
29
|
+
existsSync(join(claudeUserDir(), 'agents', `${name}.md`));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Korean phrase for a delegation target, e.g.
|
|
34
|
+
* agent present β `haiku-explore(model: haiku)`
|
|
35
|
+
* agent absent β `model: haiku`
|
|
36
|
+
*/
|
|
37
|
+
export function agentPhrase(name, { model, root } = {}) {
|
|
38
|
+
const tier = model || AGENT_MODEL[name] || 'haiku';
|
|
39
|
+
return agentExists(name, root) ? `${name}(model: ${tier})` : `model: ${tier}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* English phrase, including the article, e.g.
|
|
44
|
+
* agent present β `the haiku-explore (model: haiku) subagent`
|
|
45
|
+
* agent absent β `a model: haiku subagent`
|
|
46
|
+
*/
|
|
47
|
+
export function agentPhraseEn(name, { model, root } = {}) {
|
|
48
|
+
const tier = model || AGENT_MODEL[name] || 'haiku';
|
|
49
|
+
return agentExists(name, root)
|
|
50
|
+
? `the ${name} (model: ${tier}) subagent`
|
|
51
|
+
: `a model: ${tier} subagent`;
|
|
52
|
+
}
|
package/src/model-rules.js
CHANGED
|
@@ -33,6 +33,7 @@ import { join, dirname } from 'node:path';
|
|
|
33
33
|
import { homedir } from 'node:os';
|
|
34
34
|
import { userDataDir } from './paths.js';
|
|
35
35
|
import { userLanguage } from './config.js';
|
|
36
|
+
import { agentPhrase, agentPhraseEn } from './agents.js';
|
|
36
37
|
|
|
37
38
|
// Post-promotion delegated-category error rate above this flags the rule
|
|
38
39
|
// for review (rule-health). Calibrated against local T0 avg error incidence.
|
|
@@ -144,10 +145,10 @@ export function renderModelRatchet(rules, lang = userLanguage()) {
|
|
|
144
145
|
const t1 = group.find((r) => r.tier === 'T1');
|
|
145
146
|
if (t2 && t1) {
|
|
146
147
|
const rule = ko
|
|
147
|
-
? `"${t2.label}" μ ν μμ²μ κΈ°λ³Έμ μΌλ‘ ${t2.agent}
|
|
148
|
+
? `"${t2.label}" μ ν μμ²μ κΈ°λ³Έμ μΌλ‘ ${agentPhrase(t2.agent)} μλΈμμ΄μ νΈλ‘ μμνλ€(μ: "${t2.example}"). ` +
|
|
148
149
|
`μ¬λ¬ λ¨κ³Β·μ¬λ¬ νμΌ μμ μ΄ μ½ν μ€κ° λλ μμ²(μ: "${t1.example}")μ model: sonnet μλΈμμ΄μ νΈλ‘ μμνλ€. ` +
|
|
149
150
|
`μ€κ³ νλ¨Β·λ°°ν¬Β·μ€ν μ΄ μ μΆ κ°μ λΉκ°μ μμ
μ΄ μμ΄κ±°λ μμ μ€ μλ¬κ° λ°λ³΅λλ©΄ μμνμ§ λ§κ³ λ©μΈ λͺ¨λΈμ΄ μ§μ μ²λ¦¬νλ€`
|
|
150
|
-
: `Delegate "${t2.labelEn || t2.label}" requests to
|
|
151
|
+
: `Delegate "${t2.labelEn || t2.label}" requests to ${agentPhraseEn(t2.agent)} by default (e.g. "${t2.example}"). ` +
|
|
151
152
|
`Escalate moderate ones that span multiple steps or file edits (e.g. "${t1.example}") to a model: sonnet subagent. ` +
|
|
152
153
|
`Do not delegate at all β handle it on the main model β when the request mixes in design judgement or irreversible work (deploy, release, store submission), or when errors repeat during delegation`;
|
|
153
154
|
lines.push(`- ${rule}${healthOf(t2)}${healthOf(t1)} <!-- T2 ${statsOf(t2)} / T1 ${statsOf(t1)} -->`);
|
package/src/route-scan.js
CHANGED
|
@@ -22,6 +22,7 @@ import { join } from 'node:path';
|
|
|
22
22
|
import { userDataDir } from './paths.js';
|
|
23
23
|
import { discoverSessionFiles } from './parser.js';
|
|
24
24
|
import { collectSessionRecords } from './session-records.js';
|
|
25
|
+
import { agentPhrase, agentPhraseEn } from './agents.js';
|
|
25
26
|
|
|
26
27
|
// ββ Tier bands (docs/TIER_CRITERIA.md Β§3) ββββββββββββββββββββββββββββββββ
|
|
27
28
|
// T2 (haiku): finished in few calls, tiny output, near-zero mutation, no
|
|
@@ -373,10 +374,10 @@ export async function runRouteScan({ days = 14 } = {}) {
|
|
|
373
374
|
// switching `language` later re-renders (and promotes) correctly without
|
|
374
375
|
// waiting for a rescan.
|
|
375
376
|
const ruleText = (g) => g.tier === 'T2'
|
|
376
|
-
? `"${g.label}" μ νμ λ¨μ μμ²(μ: "${g.example}")μ ${g.agent}
|
|
377
|
+
? `"${g.label}" μ νμ λ¨μ μμ²(μ: "${g.example}")μ ${agentPhrase(g.agent)} μλΈμμ΄μ νΈλ‘ μμνλ€ (μ€κ³ νλ¨Β·λ°°ν¬Β·μ€ν μ΄ μ μΆ κ°μ λΉκ°μ μμ
μ΄ μμ΄λ©΄ μμνμ§ μμ)`
|
|
377
378
|
: `"${g.label}" μ νμ μ€κ° λλ μμ²(μ: "${g.example}")μ model: sonnet μλΈμμ΄μ νΈλ‘ μμνλ€ (μ€κ³ νλ¨Β·λΉκ°μ μμ
Β·λ°λ³΅ μλ¬ λ°μ μ λ©μΈ λͺ¨λΈμ΄ μ΄μ΄λ°μ)`;
|
|
378
379
|
const ruleTextEn = (g) => g.tier === 'T2'
|
|
379
|
-
? `Delegate simple "${g.labelEn}" requests (e.g. "${g.example}") to
|
|
380
|
+
? `Delegate simple "${g.labelEn}" requests (e.g. "${g.example}") to ${agentPhraseEn(g.agent)} β never when the request mixes in design judgement or irreversible work like deploy/release/submission`
|
|
380
381
|
: `Delegate moderate "${g.labelEn}" requests (e.g. "${g.example}") to a model: sonnet subagent β hand back to the main model on design judgement, irreversible work, or repeated errors`;
|
|
381
382
|
|
|
382
383
|
const candidates = [...groups.values()]
|