sprag-cli 3.54.0 → 3.56.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.
Files changed (42) hide show
  1. package/README.ko.md +17 -5
  2. package/README.md +23 -6
  3. package/bin/cli.js +6 -6
  4. package/docs/CODEX.ko.md +57 -10
  5. package/docs/CODEX.md +89 -20
  6. package/package.json +6 -3
  7. package/src/claude-price.js +28 -0
  8. package/src/codex-brief.js +20 -0
  9. package/src/codex-cache-policy.js +26 -41
  10. package/src/codex-delegation.js +45 -5
  11. package/src/codex-doctor.js +1 -1
  12. package/src/codex-harness.js +20 -9
  13. package/src/codex-hooks.js +114 -19
  14. package/src/codex-ledger.js +94 -16
  15. package/src/codex-panel-setup.js +98 -0
  16. package/src/codex-panel.js +12 -7
  17. package/src/codex-parser.js +41 -4
  18. package/src/codex-route-scan.js +14 -7
  19. package/src/codex-rule-health.js +18 -0
  20. package/src/commands/codex-delegate.js +12 -3
  21. package/src/commands/codex.js +41 -11
  22. package/src/commands/harness.js +13 -2
  23. package/src/commands/install.js +106 -10
  24. package/src/commands/route-scan.js +8 -3
  25. package/src/commands/seed.js +7 -4
  26. package/src/commands/uninstall.js +23 -0
  27. package/src/commands/upgrade.js +13 -0
  28. package/src/cost.js +57 -3
  29. package/src/formatters/statusline.js +18 -7
  30. package/src/formatters/table.js +1 -0
  31. package/src/gateway-prices.js +39 -0
  32. package/src/harness-templates.js +33 -1
  33. package/src/harness.js +26 -1
  34. package/src/litellm-models.js +72 -10
  35. package/src/model-rules.js +2 -1
  36. package/src/money.js +9 -0
  37. package/src/month-spend.js +10 -5
  38. package/src/openai-prices.js +33 -0
  39. package/src/preset-ratchet.js +133 -0
  40. package/src/route-scan.js +16 -8
  41. package/src/savings-ledger.js +10 -6
  42. package/src/seed-rules.js +32 -7
package/README.ko.md CHANGED
@@ -5,7 +5,7 @@
5
5
  <img alt="Sprag" src="https://raw.githubusercontent.com/rootstudioyaml/sprag/main/site/assets/logo/sprag-lockup-light.svg" width="220">
6
6
  </picture>
7
7
 
8
- **Claude Code와 Codex용 품질 래칫**
8
+ **Claude Code와 Codex가 쓰는 토큰을 첫 세션부터 줄입니다**
9
9
 
10
10
  [![npm](https://img.shields.io/npm/v/sprag-cli.svg?label=sprag-cli)](https://www.npmjs.com/package/sprag-cli)
11
11
  [![downloads](https://img.shields.io/npm/dm/sprag-cli.svg)](https://www.npmjs.com/package/sprag-cli)
@@ -15,10 +15,17 @@
15
15
 
16
16
  [sprag.io](https://sprag.io) · [벤치마크](https://github.com/rootstudioyaml/sprag/blob/main/docs/BENCHMARK.md)
17
17
 
18
+ <a href="https://sprag.io/assets/video/sprag-compare-ko.mp4"><img src="https://raw.githubusercontent.com/rootstudioyaml/sprag/main/docs/sprag-compare-ko.gif" width="760" alt="두 터미널에 같은 요청 12건을 넣습니다. sprag 를 적용한 쪽은 조회와 테스트 실행을 저렴한 모델에 넘기고 문서를 Markdown 으로 바꿔 넣어, 하루를 $6.68 대신 $1.83 로 마칩니다."></a>
19
+
20
+ 같은 요청 12건, 두 터미널: sprag 적용 시 **$6.68 → $1.83 (72.6% 절감)**<br>
21
+ <sub>예시 요청을 공개 단가로 계산했습니다. <a href="https://sprag.io/assets/video/sprag-compare-ko.mp4">전체 영상 (41초)</a></sub>
22
+
18
23
  </div>
19
24
 
20
25
  ---
21
26
 
27
+ **일하는 방식은 그대로 두고 토큰만 줄입니다.** 조회와 확인, 테스트 실행은 저렴한 모델에 넘기고, 문서는 가벼운 Markdown으로 바꿔 넣습니다(54만 토큰짜리 발표 자료가 2.3만 토큰이 됩니다). 세션마다 쓴 금액과 아낀 금액은 상태줄에 바로 나타납니다. `npm i -g sprag-cli` 하나로 설정이 끝나며, 프로젝트마다 따로 설정할 것은 없습니다.
28
+
22
29
  **에이전트는 그대로 일하고, 퇴행만 막습니다.** 이름은 스프래그 클러치에서 왔습니다. 앞으로 도는 힘은 그대로 통과시키고, 역회전하는 순간 잠기는 부품입니다.
23
30
 
24
31
  Sprag는 Claude Code와 Codex가 남기는 세션 기록을 읽어 반복 실패와 위임 후보를 찾습니다. 사용자가 승인한 규칙은 다음 세션에도 적용하며, 위임 기록과 모델 단가를 확인할 수 있으면 예상 차액을 원장에 적습니다. 캐시와 사용량 한도 문제는 상태줄이나 패널에서 확인할 수 있습니다.
@@ -33,6 +40,8 @@ npm i -g sprag-cli # 예전 이름 claude-token-saver 와 같은 패키지입
33
40
 
34
41
  Codex 기본 훅과 `AGENTS.md` 하네스, 범위별 래칫 규칙,
35
42
  한국어 검사, 프롬프트의 문서 경로 자동 변환, 로컬 토큰 보고서를 사용할 수 있습니다.
43
+ Codex가 설치되어 있으면 `npm i -g sprag-cli` 하나로 Codex도 설정하고, 추천 룰을 묻지 않고
44
+ 전역으로 등록합니다(단계마다 확인하려면 `sprag install --manual`). Claude Code를 건드리지 않고
36
45
  Codex만 쓴다면 다음과 같이 설치하십시오.
37
46
 
38
47
  ```bash
@@ -41,14 +50,13 @@ sprag install --agent codex
41
50
  sprag --agent codex --days 7
42
51
  sprag panel --agent codex # 별도 터미널에서 실시간 패널 실행
43
52
  sprag --statusline --agent codex --text
44
- sprag doctor --agent codex
53
+ sprag doctor --agent codex # 문제가 생겼을 때만
45
54
  sprag route-scan --agent codex
46
55
  sprag seed --agent codex
47
56
  ```
48
57
 
49
- 설치 후 Codex의 `/hooks`에서 새 훅을 검토하고 신뢰해야 실행됩니다. 기본 대상은
50
- 여전히 Claude Code입니다. 이미 Sprag를 설치했다면 두 번째 명령만 실행하여
51
- Codex 연동을 추가할 수 있습니다. Codex에서도 세션 브리핑, 경고 이력, 인수인계
58
+ 설치 후 Codex의 `/hooks`에서 새 훅을 검토하고 신뢰해야 실행됩니다. Codex가 요구하는
59
+ 절차입니다. 명령의 기본 대상은 여전히 Claude Code이며, `--agent codex`를 붙이면 Codex를 고릅니다. Codex에서도 세션 브리핑, 경고 이력, 인수인계
52
60
  파일을 지원합니다. `sprag delegate on --agent codex`로 모델 위임과 하위 에이전트
53
61
  지침 전달을 켤 수 있습니다. 상태 표시는 Sprag 터미널 상태줄과 패널을 사용하십시오.
54
62
  Codex 기본 상태줄은 셸 명령을 실행하지 않습니다. 반복된 단순 작업은 위임 후보로,
@@ -201,6 +209,10 @@ JetBrains IDE 내장 터미널에서는 이모지 대신 한 칸 글리프로
201
209
  | [한국어 문체 지침](https://github.com/rootstudioyaml/sprag/blob/main/docs/KOREAN-STYLE.ko.md) | 주입 내용, 쓰기 시점 검사, 적용 전후 비교 |
202
210
  | [라우터가 아닌 이유](https://github.com/rootstudioyaml/sprag/blob/main/docs/NOT-A-ROUTER.ko.md) | 실시간 라우팅이 캐시를 깨서 비용을 키우는 구조 |
203
211
  | [게이트웨이와 환경](https://github.com/rootstudioyaml/sprag/blob/main/docs/GATEWAYS.ko.md) | Bedrock·Vertex·LiteLLM, 가격표, FAQ, 동작 원리 |
212
+ | [클로드 코드 토큰 줄이는 법](https://sprag.io/docs/ko/reduce-claude-code-tokens/) | 압축·초기화 타이밍, 서브에이전트 탐색, 문서 변환, 캐시 TTL |
213
+ | [클로드 코드 사용 한도](https://sprag.io/docs/ko/claude-code-usage-limits/) | 5시간·7일 윈도우, 함께 지켜보는 법, 턴당 비용 줄이기 |
214
+ | [코덱스 토큰 사용량](https://sprag.io/docs/ko/codex-token-usage/) | 세션 범위 나누기, 네이티브 서브에이전트 위임, 코덱스 문서 변환 |
215
+ | [도구 비교](https://sprag.io/docs/ko/compare/) | ccusage·claude-usage·CCometixLine 같은 스테이터스라인 도구와 Sprag의 차이 |
204
216
 
205
217
  LiteLLM 게이트웨이 환경에서는 `sprag profile-map --refresh` 가 게이트웨이의 `GET /model/info` 에서 모델 별칭을 바로 읽어 오므로, route-scan 이 위임 투표가 쌓이길 기다리거나 `profile-map.json` 을 손으로 편집할 필요가 없어졌습니다. 이 갱신은 LiteLLM 예산 게이지와 같은 5분 점검·24시간 캐시 주기를 씁니다. 인증 토큰은 저장하지 않으며, 호출 시점마다 `apiKeyHelper` 에서 읽어 그 헬퍼의 수명 그대로 만료되게 둡니다. 자세한 내용은 [route-scan](https://github.com/rootstudioyaml/sprag/blob/main/docs/ROUTE_SCAN.md) 문서에 있습니다.
206
218
 
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  <img alt="Sprag" src="https://raw.githubusercontent.com/rootstudioyaml/sprag/main/site/assets/logo/sprag-lockup-light.svg" width="220">
6
6
  </picture>
7
7
 
8
- **A quality ratchet for Claude Code and Codex.**
8
+ **Cut the tokens Claude Code and Codex spend, from the first session.**
9
9
 
10
10
  [![npm](https://img.shields.io/npm/v/sprag-cli.svg?label=sprag-cli)](https://www.npmjs.com/package/sprag-cli)
11
11
  [![downloads](https://img.shields.io/npm/dm/sprag-cli.svg)](https://www.npmjs.com/package/sprag-cli)
@@ -15,10 +15,21 @@
15
15
 
16
16
  [sprag.io](https://sprag.io) · [Benchmark](https://github.com/rootstudioyaml/sprag/blob/main/docs/BENCHMARK.md)
17
17
 
18
+ <a href="https://sprag.io/assets/video/sprag-compare-en.mp4"><img src="https://raw.githubusercontent.com/rootstudioyaml/sprag/main/docs/sprag-compare-en.gif" width="760" alt="Two terminals receive the same twelve requests. With Sprag, lookups and test runs go to cheaper models and documents go in as Markdown; the day ends at $1.83 against $6.68 without Sprag."></a>
19
+
20
+ Same 12 requests, two terminals: **$6.68 → $1.83 (−72.6%)** with Sprag.<br>
21
+ <sub>Example requests priced at public list rates. <a href="https://sprag.io/assets/video/sprag-compare-en.mp4">Full video (41s)</a></sub>
22
+
18
23
  </div>
19
24
 
20
25
  ---
21
26
 
27
+ **Sprag saves tokens without changing how you work.** Lookups, checks and test
28
+ runs go to a cheaper model, documents reach the context as compact Markdown (a
29
+ 540k-token deck becomes 23k tokens), and the statusline shows what each session
30
+ spent and saved while it runs. `npm i -g sprag-cli` sets it all up; nothing to
31
+ configure per project.
32
+
22
33
  Sprag is named after the sprag clutch: forward motion passes, backspin locks.
23
34
  Your agent keeps working, it just stops regressing.
24
35
 
@@ -34,14 +45,16 @@ budget and price lookups contact your configured LiteLLM endpoint using its
34
45
  existing credentials. The CLI requires Node.js 18+ and one TOML parser dependency.
35
46
 
36
47
  ```bash
37
- npm i -g sprag-cli # default: Claude Code integration
48
+ npm i -g sprag-cli # Claude Code, plus Codex when it is installed
38
49
  ```
39
50
 
40
51
  ## Codex
41
52
 
42
53
  Native hooks, an `AGENTS.md` harness, scoped ratchet rules, Korean lint,
43
54
  document conversion, token reports, and a session-bound companion panel.
44
- For a Codex-only install:
55
+ `npm i -g sprag-cli` sets Codex up too when it is installed, with the rule
56
+ presets registered globally and nothing asked (`sprag install --manual` asks at
57
+ each step). For a Codex-only install that leaves Claude Code alone:
45
58
 
46
59
  ```bash
47
60
  npm i -g sprag-cli --ignore-scripts
@@ -49,13 +62,13 @@ sprag install --agent codex
49
62
  sprag --agent codex --days 7
50
63
  sprag panel --agent codex # live companion in a separate terminal
51
64
  sprag --statusline --agent codex --text
52
- sprag doctor --agent codex
65
+ sprag doctor --agent codex # troubleshooting only
53
66
  sprag route-scan --agent codex
54
67
  sprag seed --agent codex
55
68
  ```
56
69
 
57
- Review and trust the new hooks in Codex's `/hooks` menu. The default remains
58
- Claude Code; existing users can add Codex with the second command alone.
70
+ Review and trust the new hooks in Codex's `/hooks` menu; Codex requires that
71
+ step. Commands still default to Claude Code; add `--agent codex` to pick Codex.
59
72
  Codex also supports session briefings, warning history, handoffs, and opt-in
60
73
  model routing and subagent guidance (`sprag delegate on --agent codex`). The native Codex footer
61
74
  does not accept shell commands; Sprag provides a terminal statusline and panel.
@@ -241,6 +254,10 @@ Every subcommand, flag and the output-language setting: [command reference](http
241
254
  | [Korean style](https://github.com/rootstudioyaml/sprag/blob/main/docs/KOREAN-STYLE.md) | What gets injected, the write-time check, before/after |
242
255
  | [Not a router](https://github.com/rootstudioyaml/sprag/blob/main/docs/NOT-A-ROUTER.md) | Why realtime routing breaks the cache and costs more |
243
256
  | [Gateways & environment](https://github.com/rootstudioyaml/sprag/blob/main/docs/GATEWAYS.md) | Bedrock, Vertex, LiteLLM budgets, pricing table, FAQ, how it works |
257
+ | [Reduce Claude Code tokens](https://sprag.io/docs/reduce-claude-code-tokens/) | Compact/clear timing, subagent exploration, document conversion, cache TTL |
258
+ | [Claude Code usage limits](https://sprag.io/docs/claude-code-usage-limits/) | The 5-hour and 7-day windows, watching both, cutting per-turn cost |
259
+ | [Codex token usage](https://sprag.io/docs/codex-token-usage/) | Session scoping, native subagent delegation, document conversion for Codex |
260
+ | [Compare](https://sprag.io/docs/compare/) | Sprag vs ccusage, claude-usage, and statusline tools like CCometixLine |
244
261
 
245
262
  Behind a LiteLLM gateway, `sprag profile-map --refresh` now reads model aliases straight from the gateway's own `GET /model/info`, so route-scan no longer needs to wait on learned votes or a hand-edited `profile-map.json`. The refresh uses the same 5-minute check and 24-hour cache as the LiteLLM budget gauge. It never stores your auth token: each refresh reads it from `apiKeyHelper` at call time and lets it expire on the helper's own schedule. Details: [route-scan](https://github.com/rootstudioyaml/sprag/blob/main/docs/ROUTE_SCAN.md).
246
263
 
package/bin/cli.js CHANGED
@@ -10,8 +10,8 @@
10
10
  * npx sprag --format csv # CSV output
11
11
  * npx sprag --project myproj # filter by project
12
12
  * npx sprag route-scan # detect recurring easy work → haiku-delegation candidates
13
- * npx sprag install # set up skill/hooks/statusline; asks about harness + Korean guidance
14
- * npx sprag install --yes # take the defaults without asking (same as --no-input)
13
+ * npx sprag install # set up everything, presets and Codex included, without asking
14
+ * npx sprag install --manual # confirm each optional step
15
15
  * npx sprag --install-hook # install PostToolUse hook
16
16
  * npx sprag --uninstall-hook # remove hook
17
17
  * npx claude-token-saver --hook-run # internal: called by hook
@@ -52,7 +52,7 @@ import {
52
52
  sessionMetrics,
53
53
  diagnoseSession,
54
54
  } from '../src/stats.js';
55
- import { estimateCostAcross } from '../src/cost.js';
55
+ import { sessionCostAcross } from '../src/claude-price.js';
56
56
  import { chipForIssues } from '../src/advice.js';
57
57
  import { debug } from '../src/debug.js';
58
58
  import { createArgs } from '../src/cli-args.js';
@@ -123,8 +123,8 @@ Usage:
123
123
  --agent claude|codex (default: claude; accepted before or after subcommands)
124
124
  sprag route-scan detect recurring easy work → delegation candidates
125
125
  sprag profile-map show/refresh the gateway model map (LiteLLM /model/info)
126
- sprag install set up skill/hooks/statusline
127
- sprag install --yes take the defaults without asking
126
+ sprag install set up everything (presets, and Codex when present) without asking
127
+ sprag install --manual confirm each optional step
128
128
  sprag uninstall remove everything install added
129
129
  sprag harness check score the harness setup in CLAUDE.md
130
130
  sprag harness analyze run the harness transcript analysis manually
@@ -592,7 +592,7 @@ async function main() {
592
592
  const ttl = ttlBreakdown(sessions);
593
593
  const sum = summary(sessions);
594
594
  const anomalies = detectAnomalies(trend);
595
- const cost = estimateCostAcross(sessions);
595
+ const cost = sessionCostAcross(sessions);
596
596
  const spikeReport = detectSpikes(sessions, { recentHours: 24, multiplier: 3 });
597
597
  const contextWindow = detectContextWindow(sessions, { recentHours: 24 });
598
598
 
package/docs/CODEX.ko.md CHANGED
@@ -7,13 +7,20 @@ Sprag는 Codex의 기본 훅과 `AGENTS.md`를 사용합니다. Claude Code와
7
7
 
8
8
  ## 설치
9
9
 
10
+ ```sh
11
+ npm i -g sprag-cli
12
+ ```
13
+
14
+ Codex가 설치되어 있으면 패키지 설치가 Claude Code 연동, 추천 룰과 함께 Codex도 설정합니다.
15
+ 설치 중에 묻는 것은 없으며, 단계마다 확인하려면 `sprag install --manual`을 쓰십시오.
16
+ Claude Code 설정을 건드리지 않고 Codex만 쓰려면 postinstall을 건너뛰고 Codex 설치를 직접 실행합니다.
17
+
10
18
  ```sh
11
19
  npm i -g sprag-cli --ignore-scripts
12
20
  sprag install --agent codex
13
- sprag doctor --agent codex
14
21
  ```
15
22
 
16
- 이미 Sprag를 설치했다면 두 번째 명령부터 실행하십시오. Codex의 `/hooks`에서 등록된 명령을
23
+ `sprag doctor --agent codex`는 문제가 생겼을 때 쓰는 진단 명령입니다. Codex의 `/hooks`에서 등록된 명령을
17
24
  검토하고 신뢰해야 훅이 동작합니다. 설치는 훅을 등록할 뿐 신뢰 설정을 바꾸지 않습니다.
18
25
  관리자 정책이나 `features.hooks = false` 설정이 훅을 막을 수도 있습니다.
19
26
 
@@ -22,7 +29,12 @@ sprag doctor --agent codex
22
29
  `AGENTS.override.md`가 있으면 그 파일을 사용하며, 변경 전 사본을 남깁니다.
23
30
  Claude 설정이나 `config.toml`은 고치지 않습니다.
24
31
 
25
- macOS에서는 별도 패널을 여는 훅도 기본 등록합니다. `--no-panel`로 제외할 수 있고,
32
+ macOS zsh 환경에서는 설치가 tmux 설치 여부를 먼저 확인합니다. tmux가 없으면 Homebrew로 설치하고,
33
+ `CTS_NO_TMUX=1`을 지정하면 이 단계를 건너뜁니다. 이어서 zsh 연동을 추가해 `codex`를 입력하면 같은
34
+ 터미널 안에 패널이 인라인으로 붙으며, 이때는 별도 창을 여는 훅을 등록하지 않습니다. tmux를 설치할 수
35
+ 없거나 로그인 셸이 zsh가 아니거나 이미 사용자의 `codex` 별칭·함수가 있으면 별도 Terminal 창 방식으로
36
+ 대신 동작합니다. `sprag panel shell remove --agent codex`로 끈 설정은 재설치와 업그레이드에도 그대로
37
+ 유지되며, `--no-panel`은 tmux 설정과 zsh 연동을 모두 건너뜁니다.
26
38
  `sprag panel auto off --agent codex`로 끈 설정은 재설치해도 유지합니다.
27
39
  패널 실행 여부는 `sprag panel doctor --agent codex`로 확인하십시오.
28
40
 
@@ -39,7 +51,7 @@ macOS에서는 별도 패널을 여는 훅도 기본 등록합니다. `--no-pane
39
51
  | 한국어 검사 | 파일 쓰기 후 문체 위반을 검사하며 block·warn·off를 지원합니다 |
40
52
  | 상태 표시 | 별도 패널, tmux 패널, 일회성 상태줄을 제공합니다 |
41
53
  | 브리핑·이력 | 최신 컨텍스트와 사용량 경고를 알리고 세션별로 저장합니다 |
42
- | 위임 원장 | Sprag가 위임한 하위 실행과 LiteLLM 단가로 예상 차액을 계산합니다 |
54
+ | 위임 원장 | Sprag가 위임한 하위 실행의 토큰에 호출 경로의 단가를 적용해 예상 차액을 계산합니다. 직접 호출은 OpenAI 정가를, LiteLLM 공급자는 그 게이트웨이의 단가를 씁니다 |
43
55
 
44
56
  ## 패널과 보고서
45
57
 
@@ -62,8 +74,8 @@ tmux 실행은 `--no-daemon`과 호출별 상태줄 설정을 사용합니다. C
62
74
  설정 파일은 바꾸지 않으며, `--keep-native-statusline`으로 기존 상태줄을 유지할 수 있습니다.
63
75
  패널은 Codex 기본 상태줄에 셸 명령을 넣는 방식이 아닙니다.
64
76
 
65
- macOS zsh에서 `codex`를 입력할 때 tmux 패널을 함께 열려면 다음 명령을 사용하십시오.
66
- 기존 별칭이나 함수는 덮어쓰지 않습니다.
77
+ 설치는 이제 이 연동을 기본으로 추가합니다. macOS zsh에서 `codex`를 입력할 때 tmux 패널을 함께 열려면
78
+ 다음 명령을 사용하십시오. 기존 별칭이나 함수는 덮어쓰지 않습니다.
67
79
 
68
80
  ```sh
69
81
  sprag panel shell install --agent codex
@@ -96,8 +108,34 @@ sprag seed reset --agent codex
96
108
  등록할 때 `--model ID`를 요구합니다. 모델 사용 가능 여부는 실제 공급자 설정에 달려 있습니다.
97
109
  명시한 모델과 사용자 정의 역할은 유지하며, 사용할 수 없는 하위 에이전트 도구를 만들어 내지 않습니다.
98
110
 
111
+ Codex 0.159.2에서는 `spawn_agent`가 `PreToolUse`를 거치지 않습니다(기록용 훅을 붙여 직접
112
+ 확인한 결과입니다). 그래서 실제로 동작하는 경로는 프롬프트 시점입니다. 규칙에 맞는 프롬프트가
113
+ 오면 `[Sprag model routing]` 안내가 붙고, `harness init`이 넣는 AGENTS.md 블록이 이 안내를
114
+ "사용자가 위임을 요청했다"는 뜻으로 읽게 합니다. 자식은 `fork_turns "none"`으로 뜨고, 부모는
115
+ 같은 일을 직접 하지 않고 `wait_agent`(제한 시간 120초 이상)로 자식이 끝나기를 기다립니다. 귀속은
116
+ 자식이 route id를 되받아 적는 것에 기대지 않습니다. 대신 `SubagentStart`가 대기 중인 기록(같은
117
+ 부모 세션, 같은 목표 모델, 30분 이내)에 새로 뜬 자식을 매칭해 묶습니다.
118
+
119
+ 이 안내는 부모 세션의 컨텍스트가 `codex.delegateMinContext`(기본 60000 토큰) 아래면 붙지
120
+ 않습니다. 2026-09-30 실측에서 작은 세션은 스폰·대기·검증이라는 조율 비용이 자식이 아끼는
121
+ 비용보다 커서, 문턱 아래에서는 안내를 붙이는 쪽이 오히려 손해였습니다. 아직 전용 CLI 명령은
122
+ 없고, `sprag mode` 실행 시 "Stored config file"로 나오는 설정 파일(맥은
123
+ `~/Library/Application Support/claude-token-saver/config.json`)에서 `codex.delegateMinContext`
124
+ 값을 직접 고치면 됩니다. `PreToolUse` 재작성은 spawn을 훅으로 보내는 Codex 버전을 위해 남아
125
+ 있을 뿐, 0.159.2에서는 호출되지 않습니다. 위임 원장은 자식이 쓴 토큰을 부모 모델 단가로 환산한
126
+ 추정치이며, 부모가 조율에 쓴 턴은 빼지 않습니다. 끝나기 전에 중단된 자식은 부모의 일을 대신하지
127
+ 못했으므로, 그 비용 전액을 손실로 기록하고 항목에 `aborted`를 표시합니다.
128
+
99
129
  SessionStart는 캐시된 후보를 읽고 필요할 때 별도 프로세스에서 갱신합니다. 명시적인 `--refresh`는
100
- 대기 주기를 건너뜁니다. `seed`는 Claude의 Haiku·Sonnet 프리셋을 가져오지 않으며,
130
+ 대기 주기를 건너뜁니다.
131
+
132
+ Codex 0.159.2는 훅 하나가 넘긴 컨텍스트를 약 2,450토큰까지만 보존하고, 그보다 길면 가운데를
133
+ 잘라 냅니다. 그래서 SessionStart는 추정치 2,000토큰 안에 들어가는 항목만 잘리지 않은 상태로
134
+ 넣습니다. 우선순위는 ratchet 룰, 한국어 지침, 응집성 지침, 문서 변환 안내, 제안 순입니다. 들어가지
135
+ 못한 룰과 지침은 Sprag 데이터 디렉터리의 프로젝트별 파일에 쓰고, 컨텍스트 첫머리에서 그 파일을
136
+ 먼저 읽으라고 안내합니다. rollout의 `originator`로 `codex exec` 실행을 알아보며, 답할 사람이
137
+ 없으므로 seed 제안과 route 안내를 넣지 않습니다.
138
+ `seed`는 Claude의 Haiku·Sonnet 프리셋을 가져오지 않으며,
101
139
  Codex의 수락·거절 기록도 Claude 기록과 분리합니다.
102
140
 
103
141
  ## 래칫 규칙
@@ -146,9 +184,18 @@ sprag capabilities --agent codex
146
184
  하위 실행의 토큰을 고정하고 부모·자식 단가를 각각 적용한 차이이므로, 실제 청구서의 절감액은
147
185
  아닙니다. 손해는 음수로 남기며 단가나 사용량이 없으면 0 대신 미상으로 표시합니다.
148
186
 
149
- 단가는 설정된 LiteLLM의 `/model/info`에서 읽습니다. 공급자가 다른 동명 모델을 섞지 않고,
150
- 같은 별칭 아래 배포 단가가 다르면 계산하지 않습니다. 단가 캐시는 7일 동안 사용하며 이미
151
- 계산한 실행은 단가 사본을 보존합니다. 직접 OpenAI 연결이나 구독 요금은 추정하지 않습니다.
187
+ 단가는 호출이 실제로 거친 경로로 정하며, 두 출처는 서로를 대신하지 않습니다.
188
+
189
+ - ChatGPT·OpenAI 로그인이나 `api.openai.com`으로 바로 가는 API 키는 내장된 OpenAI 정가를
190
+ 씁니다. 정가는 Standard 등급의 짧은 컨텍스트 단가이고 2026-10-01에 확인했습니다. 긴 컨텍스트
191
+ 할증은 반영하지 않으므로, 그 기준을 넘는 요청은 실제보다 낮게 계산됩니다.
192
+ - LiteLLM 공급자는 그 게이트웨이의 `/model/info` 단가를 씁니다. 같은 별칭 아래 배포 단가가
193
+ 다르면 계산하지 않고, 단가 캐시는 7일 동안 사용합니다. 게이트웨이가 단가를 주지 않는 모델은
194
+ 정가로 채우지 않고 미상으로 둡니다.
195
+
196
+ 공급자가 다른 동명 모델은 섞지 않으며, 이미 계산한 실행은 단가 사본을 보존합니다.
197
+ `sprag delegate rules --agent codex`는 룰마다 실측 위임 건수와 실패율, 절감액을 보여 줍니다.
198
+ 실패율의 95% 신뢰 하한이 20%를 넘으면(실측 5건 이상) 조건을 좁히거나 룰을 지우라고 알립니다.
152
199
  위임 후 `--refresh`로 원장을 갱신할 수 있고, 아직 일치하지 않은 기록은 7일 동안 찾습니다.
153
200
 
154
201
  캐시 타이머는 확인된 공급자 정책과 기록된 캐시 사용 시각을 보여 줍니다. 정확한 만료 시각이나
package/docs/CODEX.md CHANGED
@@ -8,14 +8,21 @@ subcommand to select Codex explicitly; this does not change a global default.
8
8
 
9
9
  ## Install
10
10
 
11
- For a Codex-only installation, skip npm's existing Claude Code postinstall:
11
+ ```sh
12
+ npm i -g sprag-cli
13
+ ```
14
+
15
+ The package install sets up Codex on its own when Codex is present, together
16
+ with the Claude Code integration and the bundled presets; nothing is asked
17
+ (`sprag install --manual` asks at each step). For a Codex-only installation that
18
+ leaves Claude Code settings alone, skip the postinstall and run the Codex
19
+ install directly:
12
20
 
13
21
  ```sh
14
22
  npm i -g sprag-cli --ignore-scripts
15
23
  sprag install --agent codex
16
24
  ```
17
25
 
18
- If Sprag is already installed for Claude Code, just run the second command.
19
26
  Both integrations can coexist. Codex installation uses `$CODEX_HOME` when set,
20
27
  otherwise `~/.codex`. It adds the integration hooks to `hooks.json` and a marked harness
21
28
  block to the global `AGENTS.md`. A non-empty `AGENTS.override.md` takes precedence,
@@ -28,10 +35,15 @@ changed hooks do not run until trusted. Use a Codex release that supports the
28
35
  events documented below. Admin policies or `features.hooks = false` may disable
29
36
  hooks. The `sprag` executable and Node.js must be on Codex's PATH.
30
37
 
31
- On macOS, installation also registers the panel-start hook in the same step,
32
- so all six hooks can be reviewed together (five without the optional panel).
33
- `install --no-panel --agent codex`
34
- skips it; an explicit `panel auto off` preference survives reinstall. Installation
38
+ On macOS with zsh, installation also makes sure tmux is present: it installs
39
+ tmux with Homebrew when missing (skip this with `CTS_NO_TMUX=1`) and adds the
40
+ zsh integration so typing `codex` shows the panel inline in the same terminal.
41
+ The separate-window hook is then not registered. When tmux cannot be
42
+ installed, the login shell is not zsh, or an existing `codex` alias or
43
+ function is already present, installation falls back to the separate window
44
+ instead. `sprag panel shell remove --agent codex` is remembered across
45
+ reinstalls and upgrades, and `--no-panel` skips both.
46
+ An explicit `panel auto off` preference survives reinstall. Installation
35
47
  reports registration, not successful activation. After approval and restarting
36
48
  Codex, run `sprag panel doctor --agent codex` to check the last hook execution,
37
49
  any launch failure, and recent rendered frames. These diagnostics are local;
@@ -64,14 +76,14 @@ shared hooks needed by other features.
64
76
  | Token report | Table, JSON, or CSV from local Codex rollout logs, with time and project filters |
65
77
  | Statusline | `--statusline` or `--format statusline` renders the compact panel once, with wrapping and label/color preferences |
66
78
  | Briefing | UserPromptSubmit reports fresh context/limit threshold crossings once per session and threshold cycle |
67
- | Warning history | `last` and `history` read Codex-only, per-session warning records |
79
+ | Warning history | `last` and `history` read Codex-only, per-session records of context (80%/95%) and rate-limit (90%) warnings and handoffs; cache and TTL chips are not recorded |
68
80
  | Handoff | Git state, session/model, recorded context/tokens/limits, and fillable remaining-work sections |
69
81
  | Subagents | Opt-in native spawn model routing, scoped category rules, and SubagentStart guidance |
70
82
  | Cache timer | Resolved provider retention policy after recorded cache usage; exact expiry is unknown |
71
83
  | Route discovery | Repeated simple turns grouped by project, provider, and parent model; no automatic registration |
72
84
  | Ratchet detection | Recent repeated command/patch failures produce `ratchet?` candidates |
73
85
  | Seed presets | Compatible ratchet rules, separate decisions, explicit registration scope |
74
- | Routing saved | Sprag-attributed child tokens priced with matching LiteLLM rates; an estimate, not billed savings |
86
+ | Routing saved | Sprag-attributed child tokens priced by the path the call took (OpenAI list prices for direct calls, the gateway's `/model/info` prices for a LiteLLM provider); signed, so a pricier child is a loss; an estimate, not billed savings |
75
87
  | Diagnostics | `doctor` checks user config, hook registration, harness, logs, panel activity, and preferences |
76
88
  | Capability matrix | `capabilities` lists supported, partial, native, and unsupported areas without invoking a model |
77
89
 
@@ -139,12 +151,39 @@ warning records. A handoff file is never overwritten and identifies quota
139
151
  values as recorded snapshots, not live responses. Fill in its remaining-work
140
152
  sections before using it to continue in another session.
141
153
 
142
- Delegation is independently opt-in via `delegate on`. PreToolUse rewrites native
143
- `spawn_agent` arguments using Codex's `message`, `model`, and `reasoning_effort`
144
- fields. It preserves explicit model choices and custom roles, including custom
145
- overrides of built-in roles. SubagentStart injects scoped ratchets and enabled
146
- writing guidance. Bounds are instructions, not an enforced tool-call budget.
147
- Normal Codex sandbox and approval rules still apply. Review new hooks in `/hooks`.
154
+ Delegation is independently opt-in via `delegate on`. As of Codex 0.159.2,
155
+ `spawn_agent` never reaches `PreToolUse` (measured directly: a recording hook
156
+ saw only `Bash` calls while a spawn happened), so the rewrite below cannot see
157
+ or steer it. The live path instead runs at prompt time: `UserPromptSubmit`
158
+ checks the prompt against your category rules and, on a match, appends a
159
+ `[Sprag model routing]` note naming the target model, effort, and a route id.
160
+ That note by itself changes nothing, because Codex's own instructions tell it not to
161
+ spawn subagents unless something explicitly asked for it. So `harness init`'s
162
+ AGENTS.md block adds the missing half: a `[Sprag model routing]` note *is* the
163
+ user's delegation request, and the agent should spawn exactly what it describes
164
+ with `fork_turns "none"`, then wait for it (`wait_agent` with a timeout of at least
165
+ 120 seconds) instead of doing the same task itself. Attribution does not depend on the child echoing the
166
+ route id back: `SubagentStart` binds the id to whichever spawned child matches
167
+ the pending route (same parent thread, same target model, within 30 minutes),
168
+ so a route is credited even when the child's own first message never repeats it.
169
+
170
+ The hint is withheld below `codex.delegateMinContext` (default 60000 input
171
+ tokens, read from the parent's own rollout). Measured 2026-09-30: a small
172
+ parent session spends more on the coordinating turn (spawning, waiting,
173
+ verifying) than a delegated child saves, so under the floor Sprag leaves the
174
+ request on the main agent instead of proposing a loss. There is no CLI flag for
175
+ it yet; set it by editing `codex.delegateMinContext` (an integer) in the config
176
+ file `sprag mode` prints under "Stored config file" (`~/Library/Application
177
+ Support/claude-token-saver/config.json` on macOS, `$XDG_CONFIG_HOME/claude-token-saver/config.json`
178
+ or `~/.config/claude-token-saver/config.json` on Linux).
179
+
180
+ The `PreToolUse` rewrite (native `spawn_agent` arguments using Codex's
181
+ `message`, `model`, and `reasoning_effort` fields, preserving explicit model
182
+ choices and custom roles) is kept for Codex versions that do route spawns
183
+ through hooks; on 0.159.2 it simply never fires. `SubagentStart` injects scoped
184
+ ratchets and enabled writing guidance regardless of which path routed the
185
+ child. Bounds are instructions, not an enforced tool-call budget. Normal Codex
186
+ sandbox and approval rules still apply. Review new hooks in `/hooks`.
148
187
 
149
188
  ```sh
150
189
  sprag delegate on --agent codex
@@ -162,6 +201,13 @@ exact parent model, and prefer project rules over global rules. Matching prompts
162
201
  request native delegation; unavailable subagent tools leave work on the main
163
202
  agent. Claude tier presets and history are not imported.
164
203
 
204
+ The routing ledger (`Routing Saved` below) prices a child's own tokens at the
205
+ parent model's rate as the counterfactual; it does not charge or subtract the
206
+ parent's coordination turn, so a delegation that barely clears the context
207
+ floor can still show a positive but overstated saving. A child that was
208
+ interrupted before it finished replaced no parent work, so its whole cost is
209
+ booked as a loss and the entry is marked `aborted`.
210
+
165
211
  ## Harness Commands
166
212
 
167
213
  ```sh
@@ -219,6 +265,15 @@ SessionStart reads cached candidates and schedules an eligible refresh outside
219
265
  the hook. Small log changes refresh at most daily; a 5 MiB increase can refresh
220
266
  after an hour. `--refresh` bypasses that schedule.
221
267
 
268
+ Codex 0.159.2 keeps about 2,450 tokens of one hook's context and cuts the
269
+ middle of anything longer. SessionStart therefore fits its parts whole within a
270
+ 2,000-token estimate, in priority order: ratchet rules, Korean guidance,
271
+ cohesion guidance, the document note, then offers. Rules and guidance that do
272
+ not fit are written to a per-project file under the Sprag data directory, and
273
+ the context opens with an instruction to read it. `codex exec` runs, recognized
274
+ from the rollout's `originator`, get no seed offer or route notice, since no one
275
+ can answer them.
276
+
222
277
  `seed` offers only compatible ratchet presets, not Claude model-tier presets.
223
278
  Accept and skip decisions are separate from Claude. Both seed acceptance and
224
279
  route approval require the user's choice of global or project scope. A project
@@ -294,10 +349,24 @@ subagent earns no credit. The estimate holds the child's measured tokens
294
349
  constant and prices them at the parent and child rates. Negative differences
295
350
  remain negative; this is not a comparison of actual billed totals.
296
351
 
297
- Rates come from the configured LiteLLM `/model/info` endpoint, with agreement
298
- required across deployments behind an alias. Price snapshots last seven days.
299
- Providers are kept separate; direct OpenAI/subscription pricing is not inferred.
300
- Missing rates or usage produce unpriced entries, never invented zeroes. A priced
352
+ Prices follow the path each call took, and the two sources never fall back to
353
+ each other:
354
+
355
+ - ChatGPT/OpenAI login or an API key straight to `api.openai.com` uses the
356
+ built-in OpenAI list prices (Standard tier, short-context rates, checked
357
+ 2026-10-01). Long-context surcharges are not modeled, so requests past the
358
+ long-context threshold are under-priced.
359
+ - A LiteLLM provider uses that gateway's `/model/info` prices, with agreement
360
+ required across deployments behind an alias. Price snapshots last seven days.
361
+ A model the gateway does not price stays unpriced; it is not filled in from
362
+ the list prices.
363
+
364
+ `sprag delegate rules --agent codex` shows each rule's measured runs, failure
365
+ rate and signed saving. A rule is flagged for review when the 95% lower bound
366
+ of its failure rate exceeds 20% over at least five runs, as on the Claude side.
367
+
368
+ Providers are kept separate. Negative values are losses: the child ran on a
369
+ pricier model than the parent, and the loss reduces the totals. Missing rates or usage produce unpriced entries, never invented zeroes. A priced
301
370
  entry keeps its rate snapshot across later outages. Pending route records are
302
371
  matched for seven days; saved ledger entries remain afterward. Run `--refresh`
303
372
  after delegation to update the ledger without waiting for the next route scan.
@@ -308,8 +377,8 @@ counts rather than a Codex dollar saving or billing measurement.
308
377
  ### Live Companion Panel
309
378
 
310
379
  Codex 0.157.1 was observed to defer SessionStart until the first submitted prompt,
311
- even after hook trust is granted. For immediate opening when typing `codex` in
312
- macOS zsh, install the optional shell integration:
380
+ even after hook trust is granted. Installation now adds this shell integration by
381
+ default on macOS zsh; to install or remove it manually:
313
382
 
314
383
  ```sh
315
384
  sprag panel shell install --agent codex
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sprag-cli",
3
- "version": "3.54.0",
4
- "description": "Sprag - a local harness for Claude Code and Codex: ratchet rules, model delegation, token telemetry, companion panels, document conversion, and Korean writing checks.",
3
+ "version": "3.56.0",
4
+ "description": "Cut the tokens Claude Code and Codex spend: routes simple work to cheaper models, converts documents to compact Markdown, and shows spend and savings live. Ratchet rules and Korean writing checks included.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "sprag": "bin/cli.js",
@@ -14,7 +14,8 @@
14
14
  "publish:legacy": "node scripts/publish-legacy.mjs",
15
15
  "release:notes": "node scripts/release-notes.mjs",
16
16
  "site:docs": "node scripts/build-site-docs.mjs",
17
- "test": "node --test"
17
+ "test": "node --test",
18
+ "version": "node scripts/sync-site-version.mjs"
18
19
  },
19
20
  "files": [
20
21
  "bin/",
@@ -48,6 +49,8 @@
48
49
  "subagent",
49
50
  "haiku",
50
51
  "cost-savings",
52
+ "token-saver",
53
+ "reduce-tokens",
51
54
  "token-usage",
52
55
  "prompt-caching",
53
56
  "cache",
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Claude-side cost by the path the call took. A session on a LiteLLM gateway
3
+ * (gatewayBase(env) set) is billed by the gateway, so it is priced from its
4
+ * /model/info table and stays unpriced (null) when the table has no entry;
5
+ * the Anthropic list table is never a fallback there. Every other session is a
6
+ * direct call and uses the list table. Kept apart from cost.js because
7
+ * litellm-models.js imports cost.js, and this needs both.
8
+ */
9
+ import { estimateCost, estimateCostAcross } from './cost.js';
10
+ import { gatewayPriceFor } from './litellm-models.js';
11
+ import { gatewayBase } from './gateway-auth.js';
12
+
13
+ export function sessionCost(totals, model, { env = process.env, now } = {}) {
14
+ if (!gatewayBase(env)) return estimateCost(totals, model);
15
+ const price = gatewayPriceFor(model, env, now);
16
+ return price ? estimateCost(totals, model, { price }) : null;
17
+ }
18
+
19
+ /** Like estimateCostAcross, plus how many sessions had no price and were left out. */
20
+ export function sessionCostAcross(sessions, opts = {}) {
21
+ let unpriced = 0;
22
+ const total = estimateCostAcross(sessions, (totals, model) => {
23
+ const c = sessionCost(totals, model, opts);
24
+ if (!c) unpriced++;
25
+ return c;
26
+ });
27
+ return { ...total, unpriced };
28
+ }
@@ -77,6 +77,26 @@ export function runCodexBrief({ sessionId, transcriptPath, cwd, now = Date.now()
77
77
  return next.length ? ['[Sprag Codex briefing]', ...next.map((issue) => `${issue.message} ${issue.advice}`)].join('\n') : null;
78
78
  }
79
79
 
80
+ /**
81
+ * Append one event outside the warning dedup, such as a handoff the user
82
+ * wrote. Claude's history records handoffs too, so both agents' timelines
83
+ * show when work was preserved.
84
+ */
85
+ export function recordCodexHistoryEvent({ sessionId, project = '', key, level = 'info', message, advice = '', now = Date.now(), dir = userDataDir() } = {}) {
86
+ if (typeof message !== 'string' || !message) return false;
87
+ const owner = typeof sessionId === 'string' && sessionId ? sessionId : 'no-session';
88
+ const directory = stateDir(dir);
89
+ const file = join(directory, `${hash(owner)}.json`);
90
+ const state = readState(file);
91
+ const events = [...state.events, { key, tier: 0, signature: key, level, message, advice,
92
+ at: new Date(now).toISOString(), sessionId: owner, project }].slice(-200);
93
+ mkdirSync(directory, { recursive: true });
94
+ const tmp = `${file}.${process.pid}.tmp`;
95
+ writeFileSync(tmp, JSON.stringify({ ...state, events }) + '\n', { mode: 0o600 });
96
+ renameSync(tmp, file);
97
+ return true;
98
+ }
99
+
80
100
  export function readCodexHistory({ days = 7, sessionId, project, dir = userDataDir(), now = Date.now() } = {}) {
81
101
  const directory = stateDir(dir);
82
102
  let files;