claude-pro-minmax 1.3.0 → 1.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.ko.md CHANGED
@@ -16,14 +16,14 @@ CPMM은 모델 라우팅, 출력 제어, 로컬 안전장치로 리셋 전까지
16
16
 
17
17
  > **설치 완료했다면 여기서 시작하세요: [사용자 가이드](docs/USER-MANUAL.ko.md)**
18
18
  >
19
- > **New in v1.3.0:** Bash-heavy 출력 축약을 위한 RTK 선택 통합.
19
+ > **New in v1.3.1:** RTK를 이미 켜둔 사용자에 대해 `cpmm setup`이 관리된 hook 순서와 timeout을 자동 복원합니다.
20
20
 
21
21
  ---
22
22
 
23
23
  > [!TIP]
24
24
  > **🚀 3초 요약: 왜 이걸 써야 하나요?**
25
25
  > 1. **배치 실행:** `/do`로 구현-검증을 한 흐름에서 처리하고, 필요할 때만 `/do-sonnet`/`/do-opus`로 승격합니다.
26
- > 2. **출력 비용 제어:** 응답 예산, CLI 필터링, 그리고 optional RTK로 Bash-heavy 출력량을 줄입니다.
26
+ > 2. **출력 비용 제어:** 응답 예산, CLI 필터링, 그리고 optional RTK로 Bash 출력이 Claude 입력 컨텍스트를 불필요하게 키우지 않도록 합니다.
27
27
  > 3. **로컬 안전장치:** 로컬 Hook + 원자적 롤백으로 실패 시 빠르게 복구합니다.
28
28
 
29
29
  ---
@@ -52,7 +52,7 @@ cpmm setup
52
52
  cpmm doctor
53
53
  ```
54
54
 
55
- > **v1.3.0 참고:** `cpmm setup`은 지원 환경에서 RTK 설치를 시도합니다. RTK 활성화는 여전히 opt-in입니다.
55
+ > **v1.3.1 참고:** `cpmm setup`은 지원 환경에서 RTK 설치를 계속 시도합니다. RTK 활성화는 여전히 opt-in이지만, 한 번 켠 뒤에는 CPMM이 관리된 hook 순서와 timeout을 자동 복원합니다.
56
56
 
57
57
  의존성 정책:
58
58
  - `required`: `jq`, `mgrep`, `tmux`
@@ -101,7 +101,45 @@ cpmm doctor
101
101
  프로젝트 초기화 팁:
102
102
  - `claude` 실행 전에 `project-templates/`를 참고해 프로젝트를 초기화하세요. (설치기는 `project-templates`를 `~/.claude`로 복사하지 않습니다.)
103
103
 
104
- ### 5. 고급 (선택)
104
+ ### 5. Bash 명령 출력 필터링 (RTK)
105
+
106
+ RTK는 CPMM이 지원하는 **선택적 Bash 명령 출력 필터링 계층**입니다. `cpmm setup`은 RTK 바이너리 설치를 시도하지만, RTK hook은 기본 활성화하지 않습니다.
107
+
108
+ RTK를 이번 릴리스에 선택 통합으로 넣은 이유는, Bash-heavy 워크플로우에서 긴 명령 출력이 Claude 입력 컨텍스트로 다시 들어가기 전에 이를 줄여 주기 때문입니다. CPMM은 권장 hook 순서를 문서화하고 `cpmm doctor`로 점검하며, 그 순서에서는 CPMM의 critical-action check가 RTK rewrite hook보다 먼저 실행되어야 합니다. 다만 hook 동작을 예측 가능하고 디버깅 가능하게 유지하기 위해 default-on이 아니라 opt-in으로 제공합니다.
109
+
110
+ 권장 opt-in 절차:
111
+
112
+ ```bash
113
+ rtk init -g --hook-only
114
+ # RTK를 켠 뒤에는 cpmm setup이 관리된 hook 순서와 timeout을 복원
115
+ cpmm setup
116
+ cpmm doctor
117
+ ```
118
+
119
+ 권장 `PreToolUse` 순서 (`~/.claude/settings.json`):
120
+ - 먼저 CPMM safety hook: `~/.claude/scripts/hooks/critical-action-check.sh` with `timeout: 5`
121
+ - 그 다음 RTK rewrite hook: `~/.claude/hooks/rtk-rewrite.sh` with `timeout: 10`
122
+
123
+ 업데이트 참고:
124
+ - `cpmm setup`은 업데이트 시 `~/.claude/settings.json`을 다시 씁니다.
125
+ - `cpmm setup` 전에 RTK가 이미 켜져 있었다면, CPMM이 설정 재작성 뒤에 관리된 RTK hook 순서와 `timeout: 10`을 자동 복원합니다.
126
+ - 관리된 RTK 상태를 확인하려면 setup 뒤에 `cpmm doctor`를 실행하세요.
127
+
128
+ 권장 검증:
129
+ - `/hooks`에서 CPMM hook과 RTK hook이 모두 로드되는지 확인
130
+ - 위험 명령이 여전히 CPMM에서 먼저 차단되는지 확인
131
+ - `cpmm doctor` 실행
132
+ - 실제 Bash-heavy 세션 후 `rtk gain --quota --tier pro` 확인
133
+
134
+ 공개된 [RTK 통합 사용자 사례](https://github.com/move-hoon/claude-pro-minmax/issues/3)에서는 `rtk gain --quota --tier pro` 기준으로 Bash-heavy 워크플로우에서 `1,664`개 명령 동안 입력 토큰 `8.5M` 절감(`49.4%`)이 보고되었습니다. 절감률은 작업 부하와 세션 형태에 따라 달라질 수 있습니다.
135
+
136
+ 롤백:
137
+
138
+ ```bash
139
+ rtk init -g --uninstall
140
+ ```
141
+
142
+ ### 6. 고급 (선택)
105
143
  <details>
106
144
  <summary>Perplexity, 언어, 수동 설치 보기</summary>
107
145
 
@@ -154,37 +192,6 @@ node bin/cpmm.js setup
154
192
 
155
193
  </details>
156
194
 
157
- ### 6. Bash 출력 최적화 (RTK)
158
-
159
- RTK는 CPMM이 지원하는 **선택적 출력 최적화 계층**입니다. `cpmm setup`은 RTK 바이너리 설치를 시도하지만, RTK hook은 기본 활성화하지 않습니다.
160
-
161
- 권장 opt-in 절차:
162
-
163
- ```bash
164
- rtk init -g --hook-only
165
- cpmm doctor
166
- ```
167
-
168
- 권장 `PreToolUse` 순서 (`~/.claude/settings.json`):
169
- - 먼저 CPMM safety hook: `~/.claude/scripts/hooks/critical-action-check.sh` with `timeout: 5`
170
- - 그 다음 RTK rewrite hook: `~/.claude/hooks/rtk-rewrite.sh` with `timeout: 10`
171
-
172
- 업데이트 참고:
173
- - `cpmm setup`은 업데이트 시 `~/.claude/settings.json`을 다시 씁니다.
174
- - RTK를 opt-in으로 사용 중이면 CPMM 업데이트 후 hook 순서와 timeout을 다시 확인하고, `cpmm doctor`를 다시 실행하세요.
175
-
176
- 권장 검증:
177
- - `/hooks`에서 CPMM hook과 RTK hook이 모두 로드되는지 확인
178
- - 위험 명령이 여전히 CPMM에서 먼저 차단되는지 확인
179
- - `cpmm doctor` 실행
180
- - 실제 Bash-heavy 세션 후 `rtk gain --quota --tier pro` 확인
181
-
182
- 롤백:
183
-
184
- ```bash
185
- rtk init -g --uninstall
186
- ```
187
-
188
195
  ---
189
196
 
190
197
  ## 🚀 빠른 시작 (Quick Start)
@@ -461,7 +468,7 @@ claude-pro-minmax
461
468
 
462
469
  A: Anthropic의 정확한 quota 알고리즘은 공개되지 않았습니다. 세 가지 축으로 최적화합니다:
463
470
  - **저비용 모델 우선 경로**: 기본 구현은 Haiku 중심으로 시작하고 필요 시에만 Sonnet/Opus로 승격합니다.
464
- - **출력 비용 인식**: Output 토큰은 Input 대비 단가가 높아 응답 예산/필터링으로 payload를 줄입니다.
471
+ - **출력 비용 인식**: 출력이 많은 턴일수록 비용 부담이 커지는 경향이 있으므로, 응답 예산과 필터링으로 payload를 줄입니다.
465
472
  - **작업 흐름 단순화**: `/do`와 `/plan`을 상황에 맞게 분리해 불필요한 고비용 턴을 줄입니다.
466
473
 
467
474
  근거 실측값은 [docs/CORE_STRATEGY_EXPERIMENT_ARCHIVE.ko.md](docs/CORE_STRATEGY_EXPERIMENT_ARCHIVE.ko.md)를 참고하세요.
package/README.md CHANGED
@@ -16,14 +16,14 @@ CPMM helps Pro users complete more verified tasks before reset through model rou
16
16
 
17
17
  > **Already installed? Start here: [User Guide](docs/USER-MANUAL.md)**
18
18
  >
19
- > **New in v1.3.0:** Optional RTK integration for Bash-heavy output reduction.
19
+ > **New in v1.3.1:** `cpmm setup` now restores the managed RTK hook order and timeout for users who already enabled RTK.
20
20
 
21
21
  ---
22
22
 
23
23
  > [!TIP]
24
24
  > **🚀 3-Second Summary: Why use this?**
25
25
  > 1. **Batch Execution:** Use `/do` to keep implementation and verification in one flow, and escalate to `/do-sonnet`/`/do-opus` only when needed.
26
- > 2. **Output Cost Control:** Use response budgets, CLI filtering, and optional RTK for Bash-heavy output reduction.
26
+ > 2. **Output Cost Control:** Use response budgets, CLI filtering, and optional RTK to keep Bash output from inflating Claude input context.
27
27
  > 3. **Local Safety Rails:** Local hooks and atomic rollback help you recover quickly on failure.
28
28
 
29
29
  ---
@@ -52,7 +52,7 @@ cpmm setup
52
52
  cpmm doctor
53
53
  ```
54
54
 
55
- > **v1.3.0 note:** `cpmm setup` now attempts RTK installation when supported. RTK activation remains opt-in.
55
+ > **v1.3.1 note:** `cpmm setup` still attempts RTK installation when supported. RTK activation remains opt-in, but once enabled CPMM now restores the managed hook order and timeout automatically.
56
56
 
57
57
  Dependency policy:
58
58
  - `required`: `jq`, `mgrep`, `tmux`
@@ -101,7 +101,45 @@ Dependency policy:
101
101
  Project initialization tip:
102
102
  - Before running `claude`, initialize your project with templates in `project-templates/` (not copied into `~/.claude`).
103
103
 
104
- ### 5. Advanced (Optional)
104
+ ### 5. Bash Command-Output Filtering (RTK)
105
+
106
+ CPMM supports RTK as an **optional Bash command-output filtering layer** for Bash-heavy workflows. `cpmm setup` attempts to install the RTK binary, but CPMM does **not** enable the RTK hook by default.
107
+
108
+ We’re shipping RTK as an official optional integration because it reduces noisy command output before that output expands Claude’s input context in Bash-heavy workflows. CPMM documents and validates the recommended hook order, where CPMM’s critical-action check should run before RTK’s rewrite hook. The integration remains opt-in so hook behavior stays predictable and easier to debug.
109
+
110
+ Recommended opt-in flow:
111
+
112
+ ```bash
113
+ rtk init -g --hook-only
114
+ # if RTK was enabled, cpmm setup restores the managed hook order + timeout
115
+ cpmm setup
116
+ cpmm doctor
117
+ ```
118
+
119
+ Recommended `PreToolUse` order in `~/.claude/settings.json`:
120
+ - CPMM safety hook first: `~/.claude/scripts/hooks/critical-action-check.sh` with `timeout: 5`
121
+ - RTK rewrite hook second: `~/.claude/hooks/rtk-rewrite.sh` with `timeout: 10`
122
+
123
+ Update note:
124
+ - `cpmm setup` rewrites `~/.claude/settings.json` on update.
125
+ - If RTK was already enabled before `cpmm setup`, CPMM restores the managed RTK hook order and `timeout: 10` automatically after rewriting settings.
126
+ - Run `cpmm doctor` after setup if you want to verify the managed RTK state.
127
+
128
+ Recommended verification:
129
+ - Run `/hooks` and confirm both CPMM and RTK hooks are loaded
130
+ - Confirm dangerous commands are still blocked by CPMM
131
+ - Run `cpmm doctor`
132
+ - After real Bash-heavy sessions, inspect `rtk gain --quota --tier pro`
133
+
134
+ Public example: in a [community RTK integration report](https://github.com/move-hoon/claude-pro-minmax/issues/3), `rtk gain --quota --tier pro` reported `8.5M` input tokens saved (`49.4%`) across `1,664` commands in a Bash-heavy workflow. Savings vary by workload and session shape.
135
+
136
+ Rollback:
137
+
138
+ ```bash
139
+ rtk init -g --uninstall
140
+ ```
141
+
142
+ ### 6. Advanced (Optional)
105
143
  <details>
106
144
  <summary>Perplexity, language, manual install</summary>
107
145
 
@@ -154,37 +192,6 @@ node bin/cpmm.js setup
154
192
 
155
193
  </details>
156
194
 
157
- ### 6. Bash Output Optimization (RTK)
158
-
159
- CPMM supports RTK as an **optional output-optimization layer** for Bash-heavy workflows. `cpmm setup` attempts to install the RTK binary, but CPMM does **not** enable the RTK hook by default.
160
-
161
- Recommended opt-in flow:
162
-
163
- ```bash
164
- rtk init -g --hook-only
165
- cpmm doctor
166
- ```
167
-
168
- Recommended `PreToolUse` order in `~/.claude/settings.json`:
169
- - CPMM safety hook first: `~/.claude/scripts/hooks/critical-action-check.sh` with `timeout: 5`
170
- - RTK rewrite hook second: `~/.claude/hooks/rtk-rewrite.sh` with `timeout: 10`
171
-
172
- Update note:
173
- - `cpmm setup` rewrites `~/.claude/settings.json` on update.
174
- - If you opt into RTK, re-check hook order and timeout after each CPMM update, then run `cpmm doctor`.
175
-
176
- Recommended verification:
177
- - Run `/hooks` and confirm both CPMM and RTK hooks are loaded
178
- - Confirm dangerous commands are still blocked by CPMM
179
- - Run `cpmm doctor`
180
- - After real Bash-heavy sessions, inspect `rtk gain --quota --tier pro`
181
-
182
- Rollback:
183
-
184
- ```bash
185
- rtk init -g --uninstall
186
- ```
187
-
188
195
  ---
189
196
 
190
197
  ## 🚀 Quick Start
@@ -461,7 +468,7 @@ To add a new runtime, copy and implement `scripts/runtime/adapters/_template.sh`
461
468
 
462
469
  A: Anthropic's exact quota algorithm is not public. Optimization is based on three pillars:
463
470
  - **Low-cost model-first path**: Start implementation with Haiku, and escalate to Sonnet/Opus only when needed.
464
- - **Output-cost awareness**: Output tokens are priced higher than input, so response budgets/filtering reduce payload.
471
+ - **Output-cost awareness**: Output-heavy turns tend to cost more, so response budgets and filtering help keep payloads smaller.
465
472
  - **Workflow simplification**: Use `/do` and `/plan` by task type to avoid unnecessary high-cost turns.
466
473
 
467
474
  For measured evidence, see [docs/CORE_STRATEGY_EXPERIMENT_ARCHIVE.md](docs/CORE_STRATEGY_EXPERIMENT_ARCHIVE.md).
package/install.sh CHANGED
@@ -304,8 +304,13 @@ fi
304
304
  if [ "$HAS_EXISTING_RTK_HOOK" = true ]; then
305
305
  echo "RTK Update Note:"
306
306
  echo " An RTK hook was detected before this CPMM update."
307
- echo " Because CPMM reinstalled ~/.claude/settings.json, re-check RTK hook order and timeout."
308
- echo " Run: cpmm doctor"
307
+ if [ "${CPMM_SETUP_WRAPPER:-}" = "1" ]; then
308
+ echo " cpmm setup will restore the managed RTK hook order and timeout after rewriting settings."
309
+ echo " Run: cpmm doctor"
310
+ else
311
+ echo " Re-run cpmm setup to restore the managed RTK hook order and timeout."
312
+ echo " Or re-check ~/.claude/settings.json manually, then run: cpmm doctor"
313
+ fi
309
314
  echo ""
310
315
  fi
311
316
  echo "Language:"
package/lib/cli.js CHANGED
@@ -9,6 +9,7 @@ const {
9
9
  attemptRtkInstall,
10
10
  getRtkManualInstallHints,
11
11
  inspectRtkStatus,
12
+ reconcileManagedRtkHook,
12
13
  } = require("./rtk");
13
14
 
14
15
  const DEPS = [
@@ -181,9 +182,26 @@ function printRtkSetupResult(result) {
181
182
 
182
183
  console.log(" Optional activation:");
183
184
  console.log(" 1) rtk init -g --hook-only");
184
- console.log(" 2) Add ~/.claude/hooks/rtk-rewrite.sh after CPMM critical-action-check.sh");
185
- console.log(" 3) Set RTK hook timeout to 10");
186
- console.log(" 4) Re-run: cpmm doctor");
185
+ console.log(" 2) Re-run cpmm setup to restore the CPMM-managed hook order + timeout");
186
+ console.log(" 3) Re-run: cpmm doctor");
187
+ console.log("");
188
+ }
189
+
190
+ function printRtkReconcileResult(result) {
191
+ if (!result || !result.attempted) return;
192
+
193
+ if (result.ok) {
194
+ const suffix = result.changed ? "restored" : "already canonical";
195
+ console.log("RTK Hook Management:");
196
+ console.log(` ${colorize("✓", "32")} managed hook ${suffix} (CPMM hook first, RTK timeout 10)`);
197
+ console.log("");
198
+ return;
199
+ }
200
+
201
+ console.log("RTK Hook Management:");
202
+ console.log(` ${colorize("⚠", "33")} managed hook ${result.message || "unable to restore RTK hook state automatically"}`);
203
+ console.log(" Run: rtk init -g --hook-only");
204
+ console.log(" Then: cpmm setup");
187
205
  console.log("");
188
206
  }
189
207
 
@@ -240,6 +258,7 @@ function printRtkDoctorStatus(status) {
240
258
  } else {
241
259
  const rendered = status.timeoutValue == null ? "missing" : String(status.timeoutValue);
242
260
  console.log(` ${colorize("⚠", "33")} RTK timeout ${rendered} (recommended: 10)`);
261
+ console.log(" Fix: re-run cpmm setup to restore the managed timeout");
243
262
  }
244
263
 
245
264
  console.log("");
@@ -248,6 +267,9 @@ function printRtkDoctorStatus(status) {
248
267
 
249
268
  function runSetup() {
250
269
  console.log(`CPMM v${pkg.version} - Setup`);
270
+ const settingsPath = path.join(process.env.HOME || process.env.USERPROFILE || "", ".claude", "settings.json");
271
+ const preSetupRtkStatus = inspectRtkStatus(settingsPath);
272
+ const shouldRestoreManagedRtkHook = preSetupRtkStatus.hookEnabled;
251
273
 
252
274
  const results = checkDeps();
253
275
  const missing = results.filter((r) => r.required && !r.installed);
@@ -285,6 +307,12 @@ function runSetup() {
285
307
  return 1;
286
308
  }
287
309
 
310
+ const rtkReconcile = reconcileManagedRtkHook({
311
+ enabledBeforeSetup: shouldRestoreManagedRtkHook,
312
+ settingsPath,
313
+ });
314
+ printRtkReconcileResult(rtkReconcile);
315
+
288
316
  console.log("Setup complete.");
289
317
  return 0;
290
318
  }
@@ -297,7 +325,7 @@ function runInstallScript() {
297
325
  console.log("Configuring CPMM...\n");
298
326
  const result = spawnSync("bash", [scriptPath], {
299
327
  stdio: "inherit",
300
- env: { ...process.env },
328
+ env: { ...process.env, CPMM_SETUP_WRAPPER: "1" },
301
329
  });
302
330
  if (result.status !== 0) {
303
331
  console.error("Config setup had issues. Run 'bash install.sh' manually if needed.");
package/lib/rtk.js CHANGED
@@ -9,6 +9,8 @@ const RTK_INSTALL_URL = "https://raw.githubusercontent.com/rtk-ai/rtk/refs/heads
9
9
  const RTK_DOCS_URL = "https://github.com/rtk-ai/rtk/blob/main/docs/INSTALL.md";
10
10
  const CPMM_HOOK_NAME = "critical-action-check.sh";
11
11
  const RTK_HOOK_NAME = "rtk-rewrite.sh";
12
+ const RTK_HOOK_COMMAND = "~/.claude/hooks/rtk-rewrite.sh";
13
+ const RTK_HOOK_TIMEOUT = 10;
12
14
 
13
15
  function commandExists(cmd) {
14
16
  try {
@@ -30,6 +32,10 @@ function runCommand(bin, args, label) {
30
32
  return true;
31
33
  }
32
34
 
35
+ function cloneJson(value) {
36
+ return JSON.parse(JSON.stringify(value));
37
+ }
38
+
33
39
  function getRtkManualInstallHints() {
34
40
  return [
35
41
  "RTK is optional. CPMM setup will continue without it.",
@@ -114,6 +120,198 @@ function flattenBashHooks(settings) {
114
120
  return flattened;
115
121
  }
116
122
 
123
+ function normalizeManagedRtkHookSettings(settings) {
124
+ const source = settings && typeof settings === "object" ? settings : {};
125
+ const next = cloneJson(source);
126
+ if (!next.hooks || typeof next.hooks !== "object") next.hooks = {};
127
+ const preToolUse = Array.isArray(next.hooks.PreToolUse) ? next.hooks.PreToolUse : [];
128
+ next.hooks.PreToolUse = preToolUse;
129
+
130
+ let cpmmEntryIndex = -1;
131
+ let cpmmHookIndex = -1;
132
+
133
+ preToolUse.forEach((entry, entryIndex) => {
134
+ if (!matcherIncludesBash(entry?.matcher)) return;
135
+ const hooks = Array.isArray(entry?.hooks) ? entry.hooks : [];
136
+ const foundIndex = hooks.findIndex((hook) => String(hook?.command || "").includes(CPMM_HOOK_NAME));
137
+ if (foundIndex !== -1 && cpmmEntryIndex === -1) {
138
+ cpmmEntryIndex = entryIndex;
139
+ cpmmHookIndex = foundIndex;
140
+ }
141
+ });
142
+
143
+ if (cpmmEntryIndex === -1 || cpmmHookIndex === -1) {
144
+ return {
145
+ ok: false,
146
+ failReason: "missing_cpmm_hook",
147
+ changed: false,
148
+ settings: next,
149
+ };
150
+ }
151
+
152
+ next.hooks.PreToolUse = preToolUse
153
+ .map((entry, entryIndex) => {
154
+ if (!matcherIncludesBash(entry?.matcher)) return entry;
155
+ const hooks = Array.isArray(entry?.hooks) ? entry.hooks : [];
156
+ const filteredHooks = hooks.filter((hook) => !String(hook?.command || "").includes(RTK_HOOK_NAME));
157
+ return {
158
+ ...entry,
159
+ hooks: filteredHooks,
160
+ __cpmmEntry: entryIndex === cpmmEntryIndex,
161
+ };
162
+ })
163
+ .filter((entry) => {
164
+ if (!matcherIncludesBash(entry?.matcher)) return true;
165
+ if (entry.__cpmmEntry) return true;
166
+ return Array.isArray(entry?.hooks) && entry.hooks.length > 0;
167
+ })
168
+ .map((entry) => {
169
+ const { __cpmmEntry, ...rest } = entry;
170
+ return rest;
171
+ });
172
+
173
+ const canonicalEntry = next.hooks.PreToolUse.find((entry) => {
174
+ if (!matcherIncludesBash(entry?.matcher)) return false;
175
+ const hooks = Array.isArray(entry?.hooks) ? entry.hooks : [];
176
+ return hooks.some((hook) => String(hook?.command || "").includes(CPMM_HOOK_NAME));
177
+ });
178
+
179
+ if (!canonicalEntry) {
180
+ return {
181
+ ok: false,
182
+ failReason: "missing_cpmm_hook",
183
+ changed: false,
184
+ settings: next,
185
+ };
186
+ }
187
+
188
+ const canonicalHooks = Array.isArray(canonicalEntry.hooks) ? canonicalEntry.hooks : [];
189
+ const canonicalCpmmIndex = canonicalHooks.findIndex((hook) => String(hook?.command || "").includes(CPMM_HOOK_NAME));
190
+ if (canonicalCpmmIndex === -1) {
191
+ return {
192
+ ok: false,
193
+ failReason: "missing_cpmm_hook",
194
+ changed: false,
195
+ settings: next,
196
+ };
197
+ }
198
+
199
+ const desiredRtkHook = {
200
+ type: "command",
201
+ command: RTK_HOOK_COMMAND,
202
+ timeout: RTK_HOOK_TIMEOUT,
203
+ };
204
+
205
+ canonicalHooks.splice(canonicalCpmmIndex + 1, 0, desiredRtkHook);
206
+ canonicalEntry.hooks = canonicalHooks;
207
+
208
+ return {
209
+ ok: true,
210
+ changed: JSON.stringify(source) !== JSON.stringify(next),
211
+ settings: next,
212
+ failReason: null,
213
+ };
214
+ }
215
+
216
+ function writeSettingsJsonAtomic(settingsPath, settings) {
217
+ const tempPath = `${settingsPath}.tmp`;
218
+ fs.writeFileSync(tempPath, `${JSON.stringify(settings, null, 2)}\n`, "utf8");
219
+ fs.renameSync(tempPath, settingsPath);
220
+ }
221
+
222
+ function ensureRtkHookArtifact() {
223
+ const hookPath = path.join(os.homedir(), ".claude", "hooks", RTK_HOOK_NAME);
224
+ if (fs.existsSync(hookPath)) {
225
+ return { ok: true, hookPath, initialized: false };
226
+ }
227
+
228
+ const ok = runCommand("rtk", ["init", "-g", "--hook-only", "--no-patch"], "rtk hook init");
229
+ if (!ok || !fs.existsSync(hookPath)) {
230
+ return {
231
+ ok: false,
232
+ hookPath,
233
+ initialized: ok,
234
+ failReason: "missing_rtk_hook_artifact",
235
+ message: "RTK hook artifact could not be created.",
236
+ };
237
+ }
238
+
239
+ return { ok: true, hookPath, initialized: true };
240
+ }
241
+
242
+ function reconcileManagedRtkHook({ enabledBeforeSetup, settingsPath }) {
243
+ const result = {
244
+ attempted: Boolean(enabledBeforeSetup),
245
+ changed: false,
246
+ ok: true,
247
+ message: null,
248
+ failReason: null,
249
+ };
250
+
251
+ if (!enabledBeforeSetup) {
252
+ return result;
253
+ }
254
+
255
+ if (!commandExists("rtk")) {
256
+ return {
257
+ ...result,
258
+ ok: false,
259
+ failReason: "rtk_binary_missing",
260
+ message: "RTK binary is not available, so CPMM could not restore the managed RTK hook.",
261
+ };
262
+ }
263
+
264
+ if (!fs.existsSync(settingsPath)) {
265
+ return {
266
+ ...result,
267
+ ok: false,
268
+ failReason: "missing_settings_json",
269
+ message: "CPMM settings.json is missing after setup.",
270
+ };
271
+ }
272
+
273
+ const hookArtifact = ensureRtkHookArtifact();
274
+ if (!hookArtifact.ok) {
275
+ return {
276
+ ...result,
277
+ ok: false,
278
+ failReason: hookArtifact.failReason || "missing_rtk_hook_artifact",
279
+ message: hookArtifact.message || "RTK hook artifact could not be created.",
280
+ };
281
+ }
282
+
283
+ let parsed;
284
+ try {
285
+ parsed = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
286
+ } catch {
287
+ return {
288
+ ...result,
289
+ ok: false,
290
+ failReason: "invalid_settings_json",
291
+ message: "CPMM settings.json is invalid JSON after setup.",
292
+ };
293
+ }
294
+
295
+ const normalized = normalizeManagedRtkHookSettings(parsed);
296
+ if (!normalized.ok) {
297
+ return {
298
+ ...result,
299
+ ok: false,
300
+ failReason: normalized.failReason,
301
+ message: "CPMM could not find its Bash safety hook while restoring RTK.",
302
+ };
303
+ }
304
+
305
+ if (normalized.changed) {
306
+ writeSettingsJsonAtomic(settingsPath, normalized.settings);
307
+ }
308
+
309
+ return {
310
+ ...result,
311
+ changed: normalized.changed,
312
+ };
313
+ }
314
+
117
315
  function inspectRtkStatus(settingsPath) {
118
316
  const result = {
119
317
  binaryInstalled: commandExists("rtk"),
@@ -162,7 +360,7 @@ function inspectRtkStatus(settingsPath) {
162
360
 
163
361
  const rtkHook = hooks[rtkIndex];
164
362
  result.timeoutValue = typeof rtkHook.timeout === "number" ? rtkHook.timeout : null;
165
- result.timeoutOk = result.timeoutValue === 10;
363
+ result.timeoutOk = result.timeoutValue === RTK_HOOK_TIMEOUT;
166
364
  if (!result.timeoutOk) {
167
365
  result.warnings.push("rtk_timeout_not_10");
168
366
  }
@@ -173,7 +371,11 @@ function inspectRtkStatus(settingsPath) {
173
371
  module.exports = {
174
372
  RTK_DOCS_URL,
175
373
  RTK_INSTALL_URL,
374
+ RTK_HOOK_COMMAND,
375
+ RTK_HOOK_TIMEOUT,
176
376
  attemptRtkInstall,
177
377
  getRtkManualInstallHints,
178
378
  inspectRtkStatus,
379
+ normalizeManagedRtkHookSettings,
380
+ reconcileManagedRtkHook,
179
381
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-pro-minmax",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "Quota-first routing and safety layer for Claude Code Pro workflows.",
5
5
  "author": "donghoon <move-hoon@users.noreply.github.com>",
6
6
  "license": "MIT",