claude-token-saver 3.23.0 → 3.23.2
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/package.json +1 -1
- package/src/commands/route-scan.js +8 -0
- package/src/korean-style.js +1 -0
- package/src/model-rules.js +26 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-token-saver",
|
|
3
|
-
"version": "3.23.
|
|
3
|
+
"version": "3.23.2",
|
|
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": {
|
|
@@ -161,6 +161,14 @@ export async function run({ args, hasFlag, numArg }) {
|
|
|
161
161
|
// one rule and the file carries another.
|
|
162
162
|
const mrHook = await import('../model-rules.js');
|
|
163
163
|
const composed = (base, c) => mrHook.composeRuleText(base, c, lang);
|
|
164
|
+
// Re-render ratchet-model.md from THIS version's template. Rule text is
|
|
165
|
+
// composed in code, so an upgrade that rewords it leaves every existing
|
|
166
|
+
// file stale, and the rewrite used to ride along with refreshModelRules
|
|
167
|
+
// — which only runs on a rescan. A session that starts on a warm cache
|
|
168
|
+
// would keep reading the old wording indefinitely. The call writes only
|
|
169
|
+
// when the rendering differs, so the common case is a read and a string
|
|
170
|
+
// compare.
|
|
171
|
+
try { mrHook.syncAllFiles(); } catch (e) { debug('route-scan:sync-ratchet', e); }
|
|
164
172
|
// Registered rules whose delegated-category error rate crossed the
|
|
165
173
|
// health threshold since promotion — the user approved these, so a
|
|
166
174
|
// status change must be briefed, not just written into the md file.
|
package/src/korean-style.js
CHANGED
|
@@ -126,6 +126,7 @@ export function koreanStyleInjection({ cfg = loadConfig() } = {}) {
|
|
|
126
126
|
'이 지침은 사용자가 claude-token-saver에 설정한 것입니다.',
|
|
127
127
|
'적용 대상: 대화 답변, 그리고 새로 작성하거나 수정하는 마크다운·문서·보고서의 한국어 산문을 모두 포함합니다.',
|
|
128
128
|
'적용 예외: 원문을 그대로 옮기는 인용, 코드, 코드 주석, 그리고 프로젝트의 기존 표기 관례를 따라야 하는 커밋 메시지와 로그 문자열입니다.',
|
|
129
|
+
'표기 규칙(예외 없이 적용): 도구 호출 인자에 한국어를 비롯한 비ASCII 문자열을 담을 때에는 반드시 리터럴 UTF-8로 작성하고, \\uXXXX 유니코드 이스케이프로는 절대 작성하지 마십시오. 이스케이프로 작성하면 글자가 깨진 채 파일에 기록되는 사례가 자주 발생합니다.',
|
|
129
130
|
`(출처: ${KOREAN_STYLE_SOURCE})`,
|
|
130
131
|
'',
|
|
131
132
|
text,
|
package/src/model-rules.js
CHANGED
|
@@ -288,8 +288,15 @@ export function modelRatchetPathFor(scope, targetRoot) {
|
|
|
288
288
|
|
|
289
289
|
/**
|
|
290
290
|
* Regenerate ratchet-model.md for every target that carries model rules.
|
|
291
|
-
* A target whose rules are all gone gets its file
|
|
292
|
-
*
|
|
291
|
+
* A target whose rules are all gone gets its file emptied — not deleted,
|
|
292
|
+
* because CLAUDE.md imports the path (see the note at the loop below).
|
|
293
|
+
*
|
|
294
|
+
* Writes are content-conditional: a target whose rendering already matches
|
|
295
|
+
* what is on disk is left alone, and the returned list names only the files
|
|
296
|
+
* that actually changed. That makes the call cheap enough to run on a cache
|
|
297
|
+
* hit, which is what closes the upgrade gap — the file is rendered from THIS
|
|
298
|
+
* version's template, so an upgrade that reworded the rules reaches the disk
|
|
299
|
+
* on the next session rather than waiting for a rescan to happen to fire.
|
|
293
300
|
*/
|
|
294
301
|
export function syncAllFiles({ previousPaths = [] } = {}) {
|
|
295
302
|
const data = loadModelRules();
|
|
@@ -299,19 +306,30 @@ export function syncAllFiles({ previousPaths = [] } = {}) {
|
|
|
299
306
|
if (!byPath.has(p)) byPath.set(p, []);
|
|
300
307
|
byPath.get(p).push(r);
|
|
301
308
|
}
|
|
302
|
-
|
|
303
|
-
|
|
309
|
+
// Skip the write when the file already says exactly this. An unreadable or
|
|
310
|
+
// missing file reads as "" and therefore always differs, which is the right
|
|
311
|
+
// outcome — it gets written.
|
|
312
|
+
const writeIfChanged = (p, text) => {
|
|
313
|
+
try {
|
|
314
|
+
if (existsSync(p) && readFileSync(p, 'utf8') === text) return false;
|
|
315
|
+
} catch { /* unreadable → fall through and rewrite it */ }
|
|
304
316
|
try {
|
|
305
317
|
mkdirSync(dirname(p), { recursive: true });
|
|
306
|
-
writeFileSync(p,
|
|
307
|
-
|
|
308
|
-
} catch {
|
|
318
|
+
writeFileSync(p, text);
|
|
319
|
+
return true;
|
|
320
|
+
} catch {
|
|
321
|
+
return false; // unwritable target — skip, registry stays authoritative
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
const written = [];
|
|
325
|
+
for (const [p, rules] of byPath) {
|
|
326
|
+
if (writeIfChanged(p, renderModelRatchet(rules))) written.push(p);
|
|
309
327
|
}
|
|
310
328
|
// A target that lost its last rule is emptied, NOT deleted: CLAUDE.md
|
|
311
329
|
// imports this path, and a dangling `@` import is worse than an empty file.
|
|
312
330
|
for (const p of previousPaths) {
|
|
313
331
|
if (!byPath.has(p) && existsSync(p)) {
|
|
314
|
-
|
|
332
|
+
if (writeIfChanged(p, renderModelRatchet([]))) written.push(p);
|
|
315
333
|
}
|
|
316
334
|
}
|
|
317
335
|
return written;
|