claude-token-saver 3.6.0 → 3.6.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 CHANGED
@@ -243,6 +243,10 @@ Also update `statusLine.command` in `~/.claude/settings.json` to `claude-token-s
243
243
 
244
244
  ## Release notes
245
245
 
246
+ ### v3.6.1 (2026-07-26)
247
+ - **Fixed state files splitting apart on macOS and Windows** — `route-scan.json`, `model-rules.json`, and `brief-state.json` each carried a private copy of the path resolver, and those copies honored `XDG_CONFIG_HOME` on Linux only. With XDG set on macOS or Windows, `config.json` and the session cache moved while those three stayed at the platform default, so delegation-candidate briefings and promoted rules silently vanished. All of them now resolve through `paths.js`. (Caught by the 3-OS CI added in v3.6.0; a regression test now keeps a fourth copy from reappearing.)
248
+ - Fixed `npm test` failing on Node 22 — Node 22 resolves the directory argument of `node --test test/` as a module path.
249
+
246
250
  ### v3.6.0 (2026-07-26)
247
251
  - **Statusline refreshes stop re-parsing everything** — parsed sessions are now cached under a `(path, mtime, size)` key. Transcripts are append-only, so that triple is a sound identity for a parse result. On a 217MB / 226-file 30-day window every refresh used to spend ~3s re-reading files that could not have changed; now only the files that actually grew (in practice, the current session) are read. A stale or corrupt cache is never fatal — every read path falls back to a full parse.
248
252
  - **ratchet-model.md renders in English** — the model reads this file as instructions, so a Korean-only file dragged English sessions into Korean. Header, rule text, and rule-health warnings now follow the `language` setting. Category labels and rule text are stored in both languages at scan time, so switching languages takes effect immediately without a rescan.
package/README.md CHANGED
@@ -200,6 +200,10 @@ npm uninstall -g claude-cache-monitor && npm i -g claude-token-saver
200
200
 
201
201
  ## 릴리스 노트
202
202
 
203
+ ### v3.6.1 (2026-07-26)
204
+ - **macOS·Windows에서 상태 파일이 갈라지던 버그 수정** — `route-scan.json` / `model-rules.json` / `brief-state.json`이 각자 복제된 경로 해석 함수를 갖고 있었고, 그 복사본들은 `XDG_CONFIG_HOME`을 리눅스에서만 인정했습니다. 그래서 macOS·Windows에서 XDG를 설정하면 `config.json`·세션 캐시만 옮겨가고 위 세 파일은 플랫폼 기본 경로에 남아, 위임 후보 브리핑과 룰이 조용히 사라졌습니다. 이제 전부 `paths.js` 한 곳에서 해석합니다. (v3.6.0에서 도입한 3-OS CI가 잡아낸 버그 — 복제본이 다시 생기지 않도록 회귀 테스트 추가)
205
+ - `npm test` 스크립트가 Node 22에서 실패하던 문제 수정 (`node --test test/`의 디렉터리 인자를 Node 22가 모듈 경로로 해석).
206
+
203
207
  ### v3.6.0 (2026-07-26)
204
208
  - **statusline 재실행 비용 제거** — 세션 파싱 결과를 `(경로, mtime, size)` 키로 캐싱. 트랜스크립트는 append-only라 이 조합이 파싱 결과의 정확한 신원이 됨. 217MB/226파일 30일 창 기준 매 갱신마다 3초씩 재파싱하던 것이 변경된 파일(사실상 현재 세션 1개)만 읽는 것으로 축소. 캐시가 깨져도 항상 전체 파싱으로 폴백하므로 실패 경로 없음.
205
209
  - **ratchet-model.md 영문 렌더링** — 이 파일은 LLM이 지시문으로 읽기 때문에, 한글 전용 파일은 영어 세션의 응답까지 한글로 끌어당김. `language` 설정에 따라 헤더·룰 원문·rule-health 경고를 영문으로 렌더. 카테고리 라벨과 룰 원문은 스캔 시점에 양쪽 언어로 저장돼, 언어를 바꿔도 재스캔 없이 즉시 반영.
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "claude-token-saver",
3
- "version": "3.6.0",
3
+ "version": "3.6.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": {
7
7
  "claude-token-saver": "bin/cli.js"
8
8
  },
9
9
  "scripts": {
10
- "test": "node --test test/",
10
+ "test": "node --test",
11
11
  "postinstall": "node bin/cli.js install || true"
12
12
  },
13
13
  "files": [
package/src/brief.js CHANGED
@@ -27,7 +27,7 @@
27
27
 
28
28
  import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync, openSync, readSync, closeSync } from 'node:fs';
29
29
  import { join } from 'node:path';
30
- import { homedir } from 'node:os';
30
+ import { userDataDir } from './paths.js';
31
31
 
32
32
  // Context tiers as a fraction of the session's context window. Tier 1 warns
33
33
  // (compaction/cost territory ahead), tier 2 urges wrapping up. A session only
@@ -41,16 +41,9 @@ const WINDOW_1M_MIN_INPUT = 250_000;
41
41
  const PRUNE_MS = 7 * 24 * 60 * 60 * 1000;
42
42
  const TAIL_BYTES = 256 * 1024;
43
43
 
44
- function stateDir() {
45
- if (process.platform === 'win32') {
46
- return join(process.env.APPDATA || homedir(), 'claude-token-saver');
47
- }
48
- if (process.platform === 'darwin') {
49
- return join(homedir(), 'Library', 'Application Support', 'claude-token-saver');
50
- }
51
- const xdg = process.env.XDG_CONFIG_HOME || join(homedir(), '.config');
52
- return join(xdg, 'claude-token-saver');
53
- }
44
+ // See the note in route-scan.js — paths.js is the only place that resolves
45
+ // this, so an XDG_CONFIG_HOME override moves every state file together.
46
+ const stateDir = userDataDir;
54
47
 
55
48
  export function briefStatePath() {
56
49
  return join(stateDir(), 'brief-state.json');
@@ -31,6 +31,7 @@
31
31
  import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync } from 'node:fs';
32
32
  import { join, dirname } from 'node:path';
33
33
  import { homedir } from 'node:os';
34
+ import { userDataDir } from './paths.js';
34
35
  import { userLanguage } from './config.js';
35
36
 
36
37
  // Post-promotion delegated-category error rate above this flags the rule
@@ -41,16 +42,9 @@ export const HEALTH_ERR_RATE = 0.2;
41
42
  // withheld until the sample is large enough to mean something.
42
43
  export const HEALTH_MIN_SAMPLE = 10;
43
44
 
44
- function stateDir() {
45
- if (process.platform === 'win32') {
46
- return join(process.env.APPDATA || homedir(), 'claude-token-saver');
47
- }
48
- if (process.platform === 'darwin') {
49
- return join(homedir(), 'Library', 'Application Support', 'claude-token-saver');
50
- }
51
- const xdg = process.env.XDG_CONFIG_HOME || join(homedir(), '.config');
52
- return join(xdg, 'claude-token-saver');
53
- }
45
+ // See the note in route-scan.js — paths.js is the only place that resolves
46
+ // this, so an XDG_CONFIG_HOME override moves every state file together.
47
+ const stateDir = userDataDir;
54
48
 
55
49
  export function modelRulesPath() {
56
50
  return join(stateDir(), 'model-rules.json');
package/src/route-scan.js CHANGED
@@ -19,7 +19,7 @@
19
19
 
20
20
  import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync } from 'node:fs';
21
21
  import { join } from 'node:path';
22
- import { homedir } from 'node:os';
22
+ import { userDataDir } from './paths.js';
23
23
  import { discoverSessionFiles } from './parser.js';
24
24
  import { collectSessionRecords } from './session-records.js';
25
25
 
@@ -159,16 +159,10 @@ function keywordScore(cat, text) {
159
159
  const SKIP_RE = /^(계속|이어서|continue|다음|proceed|진행|응|네|넵|ok|okay|yes|ㄱ+|고고)\b/i;
160
160
  const SKIP_PREFIX = ['<task-notification', '<system', '[Image:', '<local-command'];
161
161
 
162
- function stateDir() {
163
- if (process.platform === 'win32') {
164
- return join(process.env.APPDATA || homedir(), 'claude-token-saver');
165
- }
166
- if (process.platform === 'darwin') {
167
- return join(homedir(), 'Library', 'Application Support', 'claude-token-saver');
168
- }
169
- const xdg = process.env.XDG_CONFIG_HOME || join(homedir(), '.config');
170
- return join(xdg, 'claude-token-saver');
171
- }
162
+ // Single source of truth for the state dir (paths.js). A local copy used to
163
+ // live here and honored XDG_CONFIG_HOME on Linux only, so on macOS/Windows an
164
+ // XDG override split this cache away from config.json and the session cache.
165
+ const stateDir = userDataDir;
172
166
 
173
167
  export function routeScanCachePath() {
174
168
  return join(stateDir(), 'route-scan.json');