cc-safe-setup 29.6.37 → 29.6.38
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/2026-03-29 +2 -0
- package/COOKBOOK.md +35 -0
- package/README.md +57 -6
- package/TROUBLESHOOTING.md +45 -0
- package/examples/cch-cache-guard.sh +25 -0
- package/examples/compact-alert-notification.sh +39 -0
- package/examples/conversation-history-guard.sh +73 -0
- package/examples/image-file-validator.sh +40 -0
- package/examples/mcp-warmup-wait.sh +39 -0
- package/examples/permission-denial-enforcer.sh +92 -0
- package/examples/pre-compact-transcript-backup.sh +71 -0
- package/examples/prompt-usage-logger.sh +53 -0
- package/examples/read-only-mode.sh +77 -0
- package/examples/replace-all-guard.sh +56 -0
- package/examples/ripgrep-permission-fix.sh +58 -0
- package/examples/session-backup-on-start.sh +72 -0
- package/examples/session-index-repair.sh +87 -0
- package/examples/subagent-error-detector.sh +73 -0
- package/examples/subagent-scope-validator.sh +53 -29
- package/examples/task-integrity-guard.sh +82 -0
- package/examples/working-directory-fence.sh +91 -0
- package/index.mjs +16 -1
- package/package.json +7 -3
- package/scripts.json +9 -9
package/2026-03-29
ADDED
package/COOKBOOK.md
CHANGED
|
@@ -286,6 +286,40 @@ npx cc-safe-setup --install-example git-show-flag-sanitizer
|
|
|
286
286
|
|
|
287
287
|
Install in `.claude/settings.json` as a PreToolUse hook with matcher `Bash`. The hook detects `git show` + `--no-stat`, strips the invalid flag, and returns the corrected command via `updatedInput`. See [#13071](https://github.com/anthropics/claude-code/issues/13071).
|
|
288
288
|
|
|
289
|
+
## Recipe: Diagnose Token Consumption
|
|
290
|
+
|
|
291
|
+
If your Max Plan 5-hour limit is exhausting too fast, install these two hooks to track what's consuming tokens:
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
# Log every prompt with timestamps
|
|
295
|
+
npx cc-safe-setup --install-example prompt-usage-logger
|
|
296
|
+
|
|
297
|
+
# Alert when auto-compaction fires
|
|
298
|
+
npx cc-safe-setup --install-example compact-alert-notification
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
After a session, check `/tmp/claude-usage-log.txt` for prompt frequency and `/tmp/claude-compact-log.txt` for compaction count. If you see 3+ compactions per session, the compact-rebuild cycle is a major token sink — use manual `/compact` before the threshold.
|
|
302
|
+
|
|
303
|
+
Quick wins: reduce MCP servers (`claude mcp list`), use `offset`/`limit` on large file reads, trim CLAUDE.md to essentials. If on v2.1.89+, set `"ENABLE_TOOL_SEARCH": "false"` in settings.json `env` to prevent Deferred Tool Loading from breaking the cache prefix ([#41617](https://github.com/anthropics/claude-code/issues/41617)). See also [#41249](https://github.com/anthropics/claude-code/issues/41249), [#41788](https://github.com/anthropics/claude-code/issues/41788), [#40524](https://github.com/anthropics/claude-code/issues/40524).
|
|
304
|
+
|
|
305
|
+
## Recipe: Protect Session Data
|
|
306
|
+
|
|
307
|
+
Back up session JSONL files on every start (protects against silent deletion):
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
npx cc-safe-setup --install-example session-backup-on-start
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Keeps last 5 timestamped backups in `~/.claude/session-backups/`. Restore with `cp`. See [#41874](https://github.com/anthropics/claude-code/issues/41874).
|
|
314
|
+
|
|
315
|
+
Back up the full transcript before compaction (protects against rate-limit data loss):
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
npx cc-safe-setup --install-example pre-compact-transcript-backup
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Keeps last 3 backups in `~/.claude/compact-backups/`. If compaction fails and your transcript is corrupted, restore from the backup. See [#40352](https://github.com/anthropics/claude-code/issues/40352).
|
|
322
|
+
|
|
289
323
|
## Recipe: Disable Auto-Compaction
|
|
290
324
|
|
|
291
325
|
Power users who manage context manually can block auto-compaction entirely:
|
|
@@ -314,3 +348,4 @@ Install as a PreToolUse hook with matcher `WebFetch`. By default allows all doma
|
|
|
314
348
|
- [Credential Protection](https://yurukusa.github.io/cc-safe-setup/prevent-credential-leak.html)
|
|
315
349
|
- [Troubleshooting](TROUBLESHOOTING.md)
|
|
316
350
|
- [Settings Reference](SETTINGS_REFERENCE.md)
|
|
351
|
+
- **[Hook Design Guide (Zenn Book)](https://zenn.dev/yurukusa/books/6076c23b1cb18b)** — 14 chapters on hook design patterns, testing, and real incident postmortems. Chapter 3 free.
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/cc-safe-setup)
|
|
5
5
|
[](https://github.com/yurukusa/cc-safe-setup/actions/workflows/test.yml)
|
|
6
6
|
|
|
7
|
-
**One command to make Claude Code safe for autonomous operation.**
|
|
7
|
+
**One command to make Claude Code safe for autonomous operation.** 654 example hooks · 14,078 tests · 1,000+ installs/day · [日本語](docs/README.ja.md)
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
npx cc-safe-setup
|
|
@@ -12,6 +12,8 @@ npx cc-safe-setup
|
|
|
12
12
|
|
|
13
13
|
Installs 8 safety hooks in ~10 seconds. Blocks `rm -rf /`, prevents pushes to main, catches secret leaks, validates syntax after every edit. Zero dependencies.
|
|
14
14
|
|
|
15
|
+
> **What's a hook?** A checkpoint that runs before Claude executes a command. Like airport security — it inspects what's about to happen and blocks anything dangerous before it reaches the gate.
|
|
16
|
+
|
|
15
17
|
[**Getting Started**](https://yurukusa.github.io/cc-safe-setup/getting-started.html) · [**All Tools**](https://yurukusa.github.io/cc-safe-setup/hub.html) · [**Recipes**](https://yurukusa.github.io/cc-safe-setup/recipes.html) · [Validate your settings.json](https://yurukusa.github.io/cc-safe-setup/validator.html) · [**Check your score**](https://yurukusa.github.io/cc-health-check/) (`npx cc-health-check`)
|
|
16
18
|
|
|
17
19
|
```
|
|
@@ -64,6 +66,8 @@ Claude Code ships with no safety hooks by default. This tool fixes that.
|
|
|
64
66
|
| **cd+git Auto-Approver** | Permission prompt spam for `cd /path && git log` | [#32985](https://github.com/anthropics/claude-code/issues/32985) [#16561](https://github.com/anthropics/claude-code/issues/16561) |
|
|
65
67
|
| **API Error Alert** | Silent session death from rate limits or API errors — desktop notification + log | |
|
|
66
68
|
|
|
69
|
+
> 📘 Tokens disappearing too fast? [The practical guide](https://zenn.dev/yurukusa/books/6076c23b1cb18b) covers 10 token consumption patterns (cache corruption, excessive reads, compact cycles) and how to detect them — from 700+ hours of autonomous operation. Chapter 3 free.
|
|
70
|
+
|
|
67
71
|
Each hook exists because a real incident happened without it.
|
|
68
72
|
|
|
69
73
|
### v2.1.85: `if` Field Support
|
|
@@ -97,6 +101,28 @@ Override Claude Code's built-in confirmation prompts. These run **after** the bu
|
|
|
97
101
|
|
|
98
102
|
Install any of these: `npx cc-safe-setup --install-example <name>`
|
|
99
103
|
|
|
104
|
+
## Session Protection Hooks
|
|
105
|
+
|
|
106
|
+
Guards against issues that corrupt sessions or waste tokens silently.
|
|
107
|
+
|
|
108
|
+
| Hook | What It Solves | Issue |
|
|
109
|
+
|------|---------------|-------|
|
|
110
|
+
| `cch-cache-guard` | Blocks reads of Claude session/billing files that poison prompt cache via `cch=` substitution | [#40652](https://github.com/anthropics/claude-code/issues/40652) |
|
|
111
|
+
| `image-file-validator` | Blocks Read of fake image files (text in .png) that permanently corrupt sessions | [#24387](https://github.com/anthropics/claude-code/issues/24387) |
|
|
112
|
+
| `terminal-state-restore` | Restores Kitty keyboard protocol, cursor, bracketed paste on exit | [#39096](https://github.com/anthropics/claude-code/issues/39096) [#39272](https://github.com/anthropics/claude-code/issues/39272) |
|
|
113
|
+
| `large-read-guard` | Warns before reading large files via `cat`/`less` that waste context tokens | [#41617](https://github.com/anthropics/claude-code/issues/41617) |
|
|
114
|
+
| `prompt-usage-logger` | Logs every prompt with timestamps to track token consumption patterns | [#41249](https://github.com/anthropics/claude-code/issues/41249) |
|
|
115
|
+
| `compact-alert-notification` | Alerts when auto-compaction fires (tracks compact-rebuild cycles that burn tokens) | [#41788](https://github.com/anthropics/claude-code/issues/41788) |
|
|
116
|
+
| `token-budget-guard` | Blocks tool calls when estimated session cost exceeds a configurable threshold | [#38335](https://github.com/anthropics/claude-code/issues/38335) |
|
|
117
|
+
| `session-index-repair` | Rebuilds `sessions-index.json` on exit so `claude --resume` finds all sessions | [#25032](https://github.com/anthropics/claude-code/issues/25032) |
|
|
118
|
+
| `session-backup-on-start` | Backs up session JSONL files on start (protects against silent deletion) | [#41874](https://github.com/anthropics/claude-code/issues/41874) |
|
|
119
|
+
| `working-directory-fence` | Blocks Read/Edit/Write outside CWD (prevents operating on wrong project copy) | [#41850](https://github.com/anthropics/claude-code/issues/41850) |
|
|
120
|
+
| `mcp-warmup-wait` | Waits for MCP servers to initialize on session start (fixes first-turn tool errors) | [#41778](https://github.com/anthropics/claude-code/issues/41778) |
|
|
121
|
+
| `pre-compact-transcript-backup` | Full JSONL backup before compaction (protects against rate-limit data loss) | [#40352](https://github.com/anthropics/claude-code/issues/40352) |
|
|
122
|
+
| `conversation-history-guard` | Blocks access to session JSONL files (prevents 20x cache poisoning) | [#40524](https://github.com/anthropics/claude-code/issues/40524) |
|
|
123
|
+
| `replace-all-guard` | Warns/blocks Edit `replace_all:true` (prevents bulk data corruption) | [#41681](https://github.com/anthropics/claude-code/issues/41681) |
|
|
124
|
+
| `ripgrep-permission-fix` | Auto-fixes vendored ripgrep +x permission on start (fixes broken commands/skills) | [#41933](https://github.com/anthropics/claude-code/issues/41933) |
|
|
125
|
+
|
|
100
126
|
## All 49 Commands
|
|
101
127
|
|
|
102
128
|
| Command | What It Does |
|
|
@@ -120,7 +146,7 @@ Install any of these: `npx cc-safe-setup --install-example <name>`
|
|
|
120
146
|
| `--scan [--apply]` | Tech stack detection |
|
|
121
147
|
| `--export / --import` | Team config sharing |
|
|
122
148
|
| `--verify` | Test each hook |
|
|
123
|
-
| `--install-example <name>` | Install from
|
|
149
|
+
| `--install-example <name>` | Install from 654 examples |
|
|
124
150
|
| `--examples [filter]` | Browse examples by keyword |
|
|
125
151
|
| `--full` | All-in-one setup |
|
|
126
152
|
| `--status` | Check installed hooks |
|
|
@@ -403,7 +429,7 @@ See [Issue #1](https://github.com/yurukusa/cc-safe-setup/issues/1) for details.
|
|
|
403
429
|
|
|
404
430
|
## Learn More
|
|
405
431
|
|
|
406
|
-
- **[
|
|
432
|
+
- **[Practical Guide (Zenn Book)](https://zenn.dev/yurukusa/books/6076c23b1cb18b)** — Token consumption diagnosis, file loss prevention, autonomous operation safety. 14 chapters from 700+ hours of real incidents. [Chapter 3 free](https://zenn.dev/yurukusa/books/6076c23b1cb18b/viewer/3-code-quality)
|
|
407
433
|
- [Cookbook](COOKBOOK.md) — 26 practical recipes (block, approve, protect, monitor, diagnose)
|
|
408
434
|
- [Official Hooks Reference](https://code.claude.com/docs/en/hooks) — Claude Code hooks documentation
|
|
409
435
|
- [Hooks Cookbook](https://github.com/yurukusa/claude-code-hooks/blob/main/COOKBOOK.md) — 25 recipes from real GitHub Issues ([interactive version](https://yurukusa.github.io/claude-code-hooks/))
|
|
@@ -433,13 +459,38 @@ cc-safe-setup covers Safety Guards (75-100%) and Monitoring (context-monitor). T
|
|
|
433
459
|
|
|
434
460
|
No. Each hook runs in ~10ms. They only fire on specific events (before tool use, after edits, on stop). No polling, no background processes.
|
|
435
461
|
|
|
462
|
+
**Q: My permission patterns don't match compound commands like `cd /path && git status`**
|
|
463
|
+
|
|
464
|
+
This is a known limitation of Claude Code's permission system ([#16561](https://github.com/anthropics/claude-code/issues/16561), [#28240](https://github.com/anthropics/claude-code/issues/28240)). Permission matching evaluates only the first token (`cd`), not the actual command (`git status`). Use a PreToolUse hook instead — hooks see the full command string and can parse compound commands. See `compound-command-allow.sh` in examples.
|
|
465
|
+
|
|
466
|
+
**Q: `--dangerously-skip-permissions` still prompts for `.claude/` and `.git/` writes**
|
|
467
|
+
|
|
468
|
+
Since v2.1.78, protected directories always prompt regardless of permission mode ([#35668](https://github.com/anthropics/claude-code/issues/35668)). Use a PermissionRequest hook to auto-approve specific protected directory operations. See `allow-protected-dirs.sh` in examples.
|
|
469
|
+
|
|
470
|
+
**Q: `allow: ["Bash(*)"]` overrides my `ask` rules**
|
|
471
|
+
|
|
472
|
+
`allow` takes precedence over `ask`. If you allow all Bash, ask rules are ignored ([#6527](https://github.com/anthropics/claude-code/issues/6527)). Use PreToolUse hooks to block dangerous commands instead of relying on the ask/allow priority system.
|
|
473
|
+
|
|
474
|
+
**Still stuck?** See the full [Permission Troubleshooting Flowchart](https://gist.github.com/yurukusa/b64217ffcb908fa309dbfcfa368cd84d) for step-by-step diagnosis.
|
|
475
|
+
|
|
436
476
|
## Contributing
|
|
437
477
|
|
|
438
|
-
Found a false positive? Open an [issue](https://github.com/yurukusa/cc-safe-setup/issues/new
|
|
478
|
+
**Report a problem:** Found a false positive or a bypass? Open an [issue](https://github.com/yurukusa/cc-safe-setup/issues/new). Include the command that was incorrectly blocked/allowed and your OS.
|
|
479
|
+
|
|
480
|
+
**Request a hook:** Describe the problem you're trying to prevent (not the solution). We'll figure out the hook together.
|
|
481
|
+
|
|
482
|
+
**Write a hook:** Fork, add your `.sh` file to `examples/`, add tests to `test.sh`, and open a PR. Every hook needs:
|
|
483
|
+
- A comment header explaining what it blocks and why
|
|
484
|
+
- At least 7 test cases (block, allow, empty input, edge cases)
|
|
485
|
+
- `bash -n` syntax validation passing
|
|
486
|
+
|
|
487
|
+
**Share your experience:** Used cc-safe-setup and have feedback? Open a discussion or comment on any issue. We read everything.
|
|
439
488
|
|
|
440
|
-
|
|
489
|
+
## Also by yurukusa
|
|
441
490
|
|
|
442
|
-
|
|
491
|
+
- [quiet life](https://yurukusa.github.io/quiet-life/) — Touch the dark. Something alive appears
|
|
492
|
+
- [deep breath](https://yurukusa.github.io/deep-breath/) — Breathe with the light
|
|
493
|
+
- [star moss](https://yurukusa.github.io/star-moss/) — Drag to grow
|
|
443
494
|
|
|
444
495
|
## License
|
|
445
496
|
|
package/TROUBLESHOOTING.md
CHANGED
|
@@ -291,9 +291,54 @@ The hook has a strict whitelist. If a command isn't on the list, it passes throu
|
|
|
291
291
|
- `pip install` (not whitelisted — install `pip-venv-guard` instead)
|
|
292
292
|
- Custom scripts (unknown to the whitelist)
|
|
293
293
|
|
|
294
|
+
## Token Consumption Too Fast
|
|
295
|
+
|
|
296
|
+
**Symptom**: Max Plan 5-hour limit exhausted in 1-2 hours. Same usage pattern as before.
|
|
297
|
+
|
|
298
|
+
**Diagnosis**:
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
# Install token tracking hooks
|
|
302
|
+
npx cc-safe-setup --install-example prompt-usage-logger
|
|
303
|
+
npx cc-safe-setup --install-example compact-alert-notification
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
After a session, check:
|
|
307
|
+
- `/tmp/claude-usage-log.txt` — how many prompts, how frequently
|
|
308
|
+
- `/tmp/claude-compact-log.txt` — how many auto-compactions fired
|
|
309
|
+
|
|
310
|
+
**Common causes**:
|
|
311
|
+
|
|
312
|
+
| Cause | Check | Fix |
|
|
313
|
+
|-------|-------|-----|
|
|
314
|
+
| Too many MCP servers | `claude mcp list` | Remove unused servers |
|
|
315
|
+
| Large CLAUDE.md/MEMORY.md | `wc -c CLAUDE.md` | Move reference content to separate files |
|
|
316
|
+
| Auto-compact cycles | compact-alert count > 3 | Use manual `/compact` before threshold |
|
|
317
|
+
| Large file reads | prompt-usage-log timestamps | Use `offset`/`limit` parameters |
|
|
318
|
+
| Deferred Tool Loading cache miss | Check `ToolSearch` calls in transcript | Set `ENABLE_TOOL_SEARCH=false` in settings.json `env` ([#41617](https://github.com/anthropics/claude-code/issues/41617)) |
|
|
319
|
+
| `--resume` cache prefix breakage | "hi" costs 2-5% quota after resume | Start fresh sessions instead of resuming ([#40524](https://github.com/anthropics/claude-code/issues/40524)) |
|
|
320
|
+
| Session file self-reads (`cch=` header) | Claude reads its own `.jsonl` files | Install `read-budget-guard` to limit large reads ([#40652](https://github.com/anthropics/claude-code/issues/40652)) |
|
|
321
|
+
|
|
322
|
+
**Disabling Deferred Tool Loading** (v2.1.89+ workaround):
|
|
323
|
+
|
|
324
|
+
Deferred Tool Loading can break the cache prefix, causing every prompt to rebuild context from scratch. To disable it, add to `.claude/settings.json`:
|
|
325
|
+
|
|
326
|
+
```json
|
|
327
|
+
{
|
|
328
|
+
"env": {
|
|
329
|
+
"ENABLE_TOOL_SEARCH": "false"
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
This prevents `ToolSearch` deferred loading and preserves the cache prefix across turns. See community discussion in [#41617](https://github.com/anthropics/claude-code/issues/41617).
|
|
335
|
+
|
|
336
|
+
**Related issues**: [#41249](https://github.com/anthropics/claude-code/issues/41249), [#41788](https://github.com/anthropics/claude-code/issues/41788), [#38335](https://github.com/anthropics/claude-code/issues/38335), [#40524](https://github.com/anthropics/claude-code/issues/40524), [#41617](https://github.com/anthropics/claude-code/issues/41617)
|
|
337
|
+
|
|
294
338
|
## Still Stuck?
|
|
295
339
|
|
|
296
340
|
1. Wrap the hook with debug wrapper: `npx cc-safe-setup --install-example hook-debug-wrapper`
|
|
297
341
|
2. Check `~/.claude/hook-debug.log` for detailed I/O traces
|
|
298
342
|
3. Run `npx cc-safe-setup --doctor` for automated checks
|
|
299
343
|
4. Open an issue: [cc-safe-setup issues](https://github.com/yurukusa/cc-safe-setup/issues)
|
|
344
|
+
5. Read the full guide: **[Hook Design Guide (Zenn Book)](https://zenn.dev/yurukusa/books/6076c23b1cb18b)** — 14 chapters, Chapter 3 free
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# cch-cache-guard.sh — Block reads of Claude session files to prevent cache poisoning
|
|
3
|
+
#
|
|
4
|
+
# Solves: When Claude reads its own JSONL session files or proxy logs,
|
|
5
|
+
# the `cch=` billing hash substitution permanently breaks prompt cache
|
|
6
|
+
# for the entire session. Every subsequent turn pays full cache cost.
|
|
7
|
+
#
|
|
8
|
+
# Root cause: The CLI mutates historical tool results by substituting
|
|
9
|
+
# `cch=` billing hashes, invalidating the cache prefix.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: PreToolUse MATCHER: "Bash"
|
|
12
|
+
# Related: https://github.com/anthropics/claude-code/issues/40652
|
|
13
|
+
|
|
14
|
+
INPUT=$(cat)
|
|
15
|
+
CMD=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
16
|
+
[ -z "$CMD" ] && exit 0
|
|
17
|
+
|
|
18
|
+
# Block commands that read Claude's own session files or billing logs
|
|
19
|
+
if printf '%s' "$CMD" | grep -qE '\.(jsonl|log)' && \
|
|
20
|
+
printf '%s' "$CMD" | grep -qiE '(claude|session|billing|transcript)'; then
|
|
21
|
+
echo '{"decision": "block", "reason": "Blocked: reading Claude session/billing files can poison prompt cache via cch= substitution. Use an external terminal instead."}'
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
exit 0
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# compact-alert-notification.sh — Warn when compaction is imminent
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# Context compaction burns tokens to re-summarize the conversation.
|
|
7
|
+
# This hook alerts you when compaction is about to happen so you
|
|
8
|
+
# can use /compact or /clear proactively for a cheaper alternative.
|
|
9
|
+
#
|
|
10
|
+
# TRIGGER: Notification
|
|
11
|
+
# MATCHER: ""
|
|
12
|
+
#
|
|
13
|
+
# HOW IT WORKS:
|
|
14
|
+
# Checks the notification message for compaction-related keywords.
|
|
15
|
+
# Prints a warning to stderr (shown in the terminal) when detected.
|
|
16
|
+
#
|
|
17
|
+
# WHY THIS MATTERS:
|
|
18
|
+
# Auto-compact fires at a fixed threshold. Each cycle costs tokens
|
|
19
|
+
# to summarize. Manual /compact before the threshold lets you
|
|
20
|
+
# control timing and reduce surprise token consumption.
|
|
21
|
+
#
|
|
22
|
+
# OUTPUT:
|
|
23
|
+
# Warning message to stderr when compaction is detected.
|
|
24
|
+
# Always exits 0 (notifications should never block).
|
|
25
|
+
#
|
|
26
|
+
# RELATED ISSUES:
|
|
27
|
+
# https://github.com/anthropics/claude-code/issues/41249
|
|
28
|
+
# https://github.com/anthropics/claude-code/issues/17428
|
|
29
|
+
# ================================================================
|
|
30
|
+
|
|
31
|
+
INPUT=$(cat)
|
|
32
|
+
|
|
33
|
+
MSG=$(printf '%s' "$INPUT" | jq -r '.message // empty' 2>/dev/null)
|
|
34
|
+
|
|
35
|
+
if echo "$MSG" | grep -qi "compact"; then
|
|
36
|
+
echo "⚠ Context approaching limit — compaction imminent. Consider /compact or /clear now." >&2
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
exit 0
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# conversation-history-guard.sh — Block modifications to conversation history
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# Prevents Claude from reading or modifying its own conversation
|
|
7
|
+
# history files (.jsonl transcripts). When Claude reads these
|
|
8
|
+
# files, the `cch=` billing hash substitution permanently
|
|
9
|
+
# invalidates prompt cache, causing 20x token inflation.
|
|
10
|
+
# When Claude writes to them, it can corrupt the session.
|
|
11
|
+
#
|
|
12
|
+
# TRIGGER: PreToolUse
|
|
13
|
+
# MATCHER: "Bash|Read|Edit|Write"
|
|
14
|
+
#
|
|
15
|
+
# WHY THIS MATTERS:
|
|
16
|
+
# Claude Code uses prompt caching to reduce token consumption.
|
|
17
|
+
# The cache key is based on conversation history content.
|
|
18
|
+
# If Claude reads its own transcript, the CLI substitutes
|
|
19
|
+
# billing hashes (cch=), changing the content and invalidating
|
|
20
|
+
# the cache prefix. This causes every subsequent turn to pay
|
|
21
|
+
# full price instead of using cached tokens.
|
|
22
|
+
# Measured impact: cache read ratio drops from 89-99% to 4.3%,
|
|
23
|
+
# causing ~20x token inflation per turn.
|
|
24
|
+
#
|
|
25
|
+
# WHAT IT BLOCKS:
|
|
26
|
+
# - Read/cat/head/tail of .claude/projects/*/*.jsonl
|
|
27
|
+
# - Edit/Write to conversation transcript files
|
|
28
|
+
# - Bash commands that access session JSONL files
|
|
29
|
+
#
|
|
30
|
+
# CONFIGURATION:
|
|
31
|
+
# CC_ALLOW_HISTORY_ACCESS=1 — disable this guard
|
|
32
|
+
#
|
|
33
|
+
# RELATED ISSUES:
|
|
34
|
+
# https://github.com/anthropics/claude-code/issues/40524
|
|
35
|
+
# https://github.com/anthropics/claude-code/issues/34629
|
|
36
|
+
# https://github.com/anthropics/claude-code/issues/40652
|
|
37
|
+
# https://github.com/anthropics/claude-code/issues/41891
|
|
38
|
+
# ================================================================
|
|
39
|
+
|
|
40
|
+
set -u
|
|
41
|
+
|
|
42
|
+
[ "${CC_ALLOW_HISTORY_ACCESS:-0}" = "1" ] && exit 0
|
|
43
|
+
|
|
44
|
+
INPUT=$(cat)
|
|
45
|
+
|
|
46
|
+
# Check file_path for Read/Edit/Write tools
|
|
47
|
+
FILE_PATH=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
48
|
+
|
|
49
|
+
if [ -n "$FILE_PATH" ]; then
|
|
50
|
+
case "$FILE_PATH" in
|
|
51
|
+
*/.claude/projects/*.jsonl|*/.claude/projects/*/sessions/*.jsonl)
|
|
52
|
+
printf 'BLOCKED: Accessing conversation history invalidates prompt cache.\n' >&2
|
|
53
|
+
printf ' File: %s\n' "$FILE_PATH" >&2
|
|
54
|
+
printf ' Reading session JSONL causes cch= substitution, dropping cache ratio to ~4%%.\n' >&2
|
|
55
|
+
printf ' Use an external terminal to inspect session files.\n' >&2
|
|
56
|
+
exit 2
|
|
57
|
+
;;
|
|
58
|
+
esac
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# Check Bash commands
|
|
62
|
+
CMD=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
63
|
+
|
|
64
|
+
if [ -n "$CMD" ]; then
|
|
65
|
+
if printf '%s' "$CMD" | grep -qE '\.claude/projects/.*\.jsonl' && \
|
|
66
|
+
printf '%s' "$CMD" | grep -qE '(cat|head|tail|less|more|grep|jq|read|wc)'; then
|
|
67
|
+
printf 'BLOCKED: Command accesses conversation history (cache poisoning risk).\n' >&2
|
|
68
|
+
printf ' Command: %s\n' "$CMD" >&2
|
|
69
|
+
exit 2
|
|
70
|
+
fi
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
exit 0
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# image-file-validator.sh — Block Read of fake image files that corrupt sessions
|
|
3
|
+
#
|
|
4
|
+
# Solves: When Claude reads a file with an image extension (.png, .jpg, etc.)
|
|
5
|
+
# that isn't actually an image (text, Git LFS pointer, error log), the content
|
|
6
|
+
# gets base64-encoded and sent to the API, causing a 400 error. The corrupt
|
|
7
|
+
# block stays in the JSONL transcript, permanently breaking the session.
|
|
8
|
+
#
|
|
9
|
+
# Uses `file --mime-type` (magic byte detection) to verify the file is
|
|
10
|
+
# actually an image before allowing the Read tool to process it.
|
|
11
|
+
#
|
|
12
|
+
# TRIGGER: PreToolUse MATCHER: "Read"
|
|
13
|
+
# Related: https://github.com/anthropics/claude-code/issues/24387
|
|
14
|
+
|
|
15
|
+
INPUT=$(cat)
|
|
16
|
+
FILE=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
|
|
17
|
+
[ -z "$FILE" ] && exit 0
|
|
18
|
+
[ ! -f "$FILE" ] && exit 0
|
|
19
|
+
|
|
20
|
+
# Only check files with image extensions
|
|
21
|
+
case "${FILE,,}" in
|
|
22
|
+
*.png|*.jpg|*.jpeg|*.gif|*.bmp|*.webp|*.svg|*.ico|*.tiff|*.tif)
|
|
23
|
+
;;
|
|
24
|
+
*)
|
|
25
|
+
exit 0
|
|
26
|
+
;;
|
|
27
|
+
esac
|
|
28
|
+
|
|
29
|
+
# Verify the file is actually an image using magic bytes
|
|
30
|
+
MIME=$(file --mime-type -b "$FILE" 2>/dev/null)
|
|
31
|
+
case "$MIME" in
|
|
32
|
+
image/*)
|
|
33
|
+
# Valid image — allow
|
|
34
|
+
exit 0
|
|
35
|
+
;;
|
|
36
|
+
*)
|
|
37
|
+
echo "{\"decision\": \"block\", \"reason\": \"Blocked: ${FILE##*/} has an image extension but is actually ${MIME}. Reading non-image files with image extensions corrupts the session (base64-encoded garbage causes API 400 errors). Rename the file or check its contents in an external terminal.\"}"
|
|
38
|
+
exit 0
|
|
39
|
+
;;
|
|
40
|
+
esac
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# mcp-warmup-wait.sh — Wait for MCP servers to be ready on start
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# MCP servers take time to initialize after session start.
|
|
7
|
+
# In Remote Trigger / scheduled sessions, the first turn often
|
|
8
|
+
# fires before MCP tools are available. This hook adds a brief
|
|
9
|
+
# delay on SessionStart to let MCP servers spin up.
|
|
10
|
+
#
|
|
11
|
+
# TRIGGER: SessionStart
|
|
12
|
+
# MATCHER: (none)
|
|
13
|
+
#
|
|
14
|
+
# WHY THIS MATTERS:
|
|
15
|
+
# When Claude Code starts via Remote Trigger or cron, the
|
|
16
|
+
# first message is sent immediately. MCP servers may not
|
|
17
|
+
# be connected yet, causing "tool not available" errors
|
|
18
|
+
# on the first turn. A short warmup delay fixes this.
|
|
19
|
+
#
|
|
20
|
+
# CONFIGURATION:
|
|
21
|
+
# CC_MCP_WARMUP_SECONDS — seconds to wait (default: 3)
|
|
22
|
+
#
|
|
23
|
+
# RELATED ISSUES:
|
|
24
|
+
# https://github.com/anthropics/claude-code/issues/41778
|
|
25
|
+
# https://github.com/anthropics/claude-code/issues/35899
|
|
26
|
+
# ================================================================
|
|
27
|
+
|
|
28
|
+
WARMUP="${CC_MCP_WARMUP_SECONDS:-3}"
|
|
29
|
+
|
|
30
|
+
# Only wait if MCP servers are configured
|
|
31
|
+
MCP_CONFIG="${HOME}/.claude/settings.json"
|
|
32
|
+
if [ -f "$MCP_CONFIG" ]; then
|
|
33
|
+
if grep -q '"mcpServers"' "$MCP_CONFIG" 2>/dev/null; then
|
|
34
|
+
sleep "$WARMUP"
|
|
35
|
+
printf 'MCP warmup: waited %ds for server initialization\n' "$WARMUP" >&2
|
|
36
|
+
fi
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
exit 0
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# permission-denial-enforcer.sh — Block alternative write methods after permission denial
|
|
3
|
+
#
|
|
4
|
+
# Solves: After user denies Write permission, Claude circumvents by using
|
|
5
|
+
# Bash commands (pip install --target, path traversal to /tmp, etc.) to
|
|
6
|
+
# achieve the same write operation (#41103)
|
|
7
|
+
#
|
|
8
|
+
# How it works: Tracks when Write/Edit permissions are denied (via a
|
|
9
|
+
# temp file marker). When active, blocks Bash commands that perform
|
|
10
|
+
# write-equivalent operations: package installs, file creation via
|
|
11
|
+
# scripts, redirects to paths outside the project.
|
|
12
|
+
#
|
|
13
|
+
# The marker auto-expires after 5 minutes to avoid permanent lockout.
|
|
14
|
+
#
|
|
15
|
+
# Usage: Add TWO hooks — one PostToolUse to detect denials, one PreToolUse to enforce
|
|
16
|
+
#
|
|
17
|
+
# {
|
|
18
|
+
# "hooks": {
|
|
19
|
+
# "PostToolUse": [{
|
|
20
|
+
# "matcher": "Write|Edit",
|
|
21
|
+
# "hooks": [{ "type": "command", "command": "~/.claude/hooks/permission-denial-enforcer.sh" }]
|
|
22
|
+
# }],
|
|
23
|
+
# "PreToolUse": [{
|
|
24
|
+
# "matcher": "Bash",
|
|
25
|
+
# "hooks": [{ "type": "command", "command": "~/.claude/hooks/permission-denial-enforcer.sh" }]
|
|
26
|
+
# }]
|
|
27
|
+
# }
|
|
28
|
+
# }
|
|
29
|
+
#
|
|
30
|
+
# TRIGGER: PostToolUse+PreToolUse MATCHER: "Write|Edit|Bash"
|
|
31
|
+
|
|
32
|
+
MARKER="/tmp/.cc-write-denied-$$"
|
|
33
|
+
MARKER_GLOB="/tmp/.cc-write-denied-*"
|
|
34
|
+
|
|
35
|
+
INPUT=$(cat)
|
|
36
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
37
|
+
|
|
38
|
+
# --- PostToolUse path: detect Write/Edit denial ---
|
|
39
|
+
if [[ "$TOOL" == "Write" || "$TOOL" == "Edit" ]]; then
|
|
40
|
+
# Check if the tool result indicates denial/block
|
|
41
|
+
RESULT=$(echo "$INPUT" | jq -r '.tool_result // empty' 2>/dev/null)
|
|
42
|
+
if echo "$RESULT" | grep -qiE 'denied|rejected|blocked|permission.*denied|user.*denied'; then
|
|
43
|
+
# Create denial marker with timestamp
|
|
44
|
+
echo "$(date +%s)" > "$MARKER"
|
|
45
|
+
echo "Write permission denial detected. Write-equivalent Bash commands will be blocked for 5 minutes." >&2
|
|
46
|
+
fi
|
|
47
|
+
exit 0
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# --- PreToolUse path: enforce on Bash ---
|
|
51
|
+
[[ "$TOOL" != "Bash" ]] && exit 0
|
|
52
|
+
|
|
53
|
+
# Check if any denial marker exists and is recent (< 5 min)
|
|
54
|
+
ACTIVE=0
|
|
55
|
+
for f in $MARKER_GLOB; do
|
|
56
|
+
[[ -f "$f" ]] || continue
|
|
57
|
+
CREATED=$(cat "$f" 2>/dev/null)
|
|
58
|
+
NOW=$(date +%s)
|
|
59
|
+
AGE=$(( NOW - CREATED ))
|
|
60
|
+
if [[ "$AGE" -lt 300 ]]; then
|
|
61
|
+
ACTIVE=1
|
|
62
|
+
break
|
|
63
|
+
else
|
|
64
|
+
rm -f "$f" 2>/dev/null
|
|
65
|
+
fi
|
|
66
|
+
done
|
|
67
|
+
|
|
68
|
+
[[ "$ACTIVE" -eq 0 ]] && exit 0
|
|
69
|
+
|
|
70
|
+
# Denial is active — check for write-equivalent commands
|
|
71
|
+
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
72
|
+
[[ -z "$CMD" ]] && exit 0
|
|
73
|
+
|
|
74
|
+
# Block package installs (common bypass vector)
|
|
75
|
+
if echo "$CMD" | grep -qiE 'pip3?\s+install|npm\s+install|gem\s+install|cargo\s+install|go\s+install'; then
|
|
76
|
+
echo "BLOCKED: Package install blocked — you denied Write permission earlier. Ask the user before retrying." >&2
|
|
77
|
+
exit 2
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# Block file creation via Python/Node scripts
|
|
81
|
+
if echo "$CMD" | grep -qiE 'python3?\s+.*\.(py|sh)|node\s+.*\.js' && echo "$CMD" | grep -qiE 'create|write|save|output|generate'; then
|
|
82
|
+
echo "BLOCKED: Script execution that creates files blocked — Write permission was denied." >&2
|
|
83
|
+
exit 2
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# Block redirect/tee to outside project
|
|
87
|
+
if echo "$CMD" | grep -qiE '>\s*/tmp|>\s*/private|tee\s+/tmp|tee\s+/private|--target='; then
|
|
88
|
+
echo "BLOCKED: Writing to /tmp or external paths blocked — Write permission was denied." >&2
|
|
89
|
+
exit 2
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
exit 0
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ================================================================
|
|
3
|
+
# pre-compact-transcript-backup.sh — Backup transcript before compaction
|
|
4
|
+
# ================================================================
|
|
5
|
+
# PURPOSE:
|
|
6
|
+
# Creates a full copy of the session transcript JSONL before
|
|
7
|
+
# compaction begins. If compaction fails (rate limit, API error),
|
|
8
|
+
# the original transcript is preserved and can be restored.
|
|
9
|
+
#
|
|
10
|
+
# TRIGGER: PreCompact
|
|
11
|
+
# MATCHER: (none — PreCompact has no matcher)
|
|
12
|
+
#
|
|
13
|
+
# WHY THIS MATTERS:
|
|
14
|
+
# Compaction wipes message content from the JSONL transcript
|
|
15
|
+
# BEFORE the compaction API call succeeds. If the API call
|
|
16
|
+
# fails (e.g., rate limit), all original content is permanently
|
|
17
|
+
# lost — the transcript is left with thousands of empty messages
|
|
18
|
+
# and no compaction summary. This hook ensures a recoverable
|
|
19
|
+
# backup exists.
|
|
20
|
+
#
|
|
21
|
+
# WHAT IT DOES:
|
|
22
|
+
# 1. Reads transcript_path from stdin JSON
|
|
23
|
+
# 2. Copies the full JSONL file to a backup location
|
|
24
|
+
# 3. Keeps last 3 backups per session to save disk space
|
|
25
|
+
#
|
|
26
|
+
# CONFIGURATION:
|
|
27
|
+
# CC_COMPACT_BACKUP_DIR — backup directory
|
|
28
|
+
# (default: ~/.claude/compact-backups)
|
|
29
|
+
# CC_COMPACT_BACKUP_KEEP — number of backups to keep (default: 3)
|
|
30
|
+
#
|
|
31
|
+
# RECOVERY:
|
|
32
|
+
# cp ~/.claude/compact-backups/<session-id>/latest.jsonl \
|
|
33
|
+
# ~/.claude/projects/<project>/sessions/<session>.jsonl
|
|
34
|
+
#
|
|
35
|
+
# RELATED ISSUES:
|
|
36
|
+
# https://github.com/anthropics/claude-code/issues/40352
|
|
37
|
+
# ================================================================
|
|
38
|
+
|
|
39
|
+
set -u
|
|
40
|
+
|
|
41
|
+
INPUT=$(cat)
|
|
42
|
+
|
|
43
|
+
BACKUP_DIR="${CC_COMPACT_BACKUP_DIR:-${HOME}/.claude/compact-backups}"
|
|
44
|
+
KEEP="${CC_COMPACT_BACKUP_KEEP:-3}"
|
|
45
|
+
|
|
46
|
+
# Get transcript path from hook input
|
|
47
|
+
TRANSCRIPT=$(printf '%s' "$INPUT" | jq -r '.transcript_path // empty' 2>/dev/null)
|
|
48
|
+
|
|
49
|
+
if [ -z "$TRANSCRIPT" ] || [ ! -f "$TRANSCRIPT" ]; then
|
|
50
|
+
exit 0
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Create backup
|
|
54
|
+
BASENAME=$(basename "$TRANSCRIPT" .jsonl)
|
|
55
|
+
DEST_DIR="${BACKUP_DIR}/${BASENAME}"
|
|
56
|
+
mkdir -p "$DEST_DIR"
|
|
57
|
+
|
|
58
|
+
TIMESTAMP=$(date -u +"%Y%m%d-%H%M%S")
|
|
59
|
+
BACKUP_FILE="${DEST_DIR}/${TIMESTAMP}.jsonl"
|
|
60
|
+
|
|
61
|
+
cp "$TRANSCRIPT" "$BACKUP_FILE" 2>/dev/null
|
|
62
|
+
|
|
63
|
+
if [ -f "$BACKUP_FILE" ]; then
|
|
64
|
+
SIZE=$(du -sh "$BACKUP_FILE" 2>/dev/null | cut -f1)
|
|
65
|
+
printf 'Pre-compact backup: %s (%s)\n' "$BACKUP_FILE" "$SIZE" >&2
|
|
66
|
+
|
|
67
|
+
# Prune old backups
|
|
68
|
+
ls -1t "$DEST_DIR"/*.jsonl 2>/dev/null | tail -n +$((KEEP + 1)) | xargs rm -f 2>/dev/null
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
exit 0
|