cc-safe-setup 29.6.37 → 29.6.39

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 ADDED
@@ -0,0 +1,2 @@
1
+ Invalid search query "token created: --json number,title,reactionGroups,commentsCount repo:anthropics/claude-code type:issue".
2
+ "" is not a recognized date/time format. Please provide an ISO 8601 date/time value, such as YYYY-MM-DD.
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
  [![npm downloads](https://img.shields.io/npm/dw/cc-safe-setup)](https://www.npmjs.com/package/cc-safe-setup)
5
5
  [![tests](https://github.com/yurukusa/cc-safe-setup/actions/workflows/test.yml/badge.svg)](https://github.com/yurukusa/cc-safe-setup/actions/workflows/test.yml)
6
6
 
7
- **One command to make Claude Code safe for autonomous operation.** 634 example hooks · 13,835 tests · 1,000+ installs/day · [日本語](docs/README.ja.md)
7
+ **One command to make Claude Code safe for autonomous operation.** 650 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 564 examples |
149
+ | `--install-example <name>` | Install from 650 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
- - **[Hook Design Guide (Zenn Book)](https://zenn.dev/yurukusa/books/6076c23b1cb18b)** — 14-chapter guide from 700+ hours of autonomous operation. Hook design patterns, testing strategies, real incident postmortems. [Chapter 2 free](https://zenn.dev/yurukusa/books/6076c23b1cb18b/viewer/2-safety-guards)
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?template=false_positive.md). Want a new hook? Open a [feature request](https://github.com/yurukusa/cc-safe-setup/issues/new?template=bug_report.md).
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
- 📘 **Want the full story?** [Production guide from 700+ hours of autonomous operation](https://zenn.dev/yurukusa/books/6076c23b1cb18b) — the incidents, fixes, and patterns behind every hook in this tool.
489
+ ## Also by yurukusa
441
490
 
442
- If cc-safe-setup saved your project from a destructive command, consider giving it a star it helps others find this tool.
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
 
@@ -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
@@ -10,9 +10,9 @@
10
10
  # API keys, wrong database URLs, or missing config values.
11
11
  #
12
12
  # TRIGGER: FileChanged
13
- # MATCHER: ".env" (watches .env filesalso catches .env.local, .env.production)
13
+ # MATCHER: ".env" (watches the .env file in cwd use ".env|.env.local" for multiple files)
14
14
  #
15
- # INPUT: {"file_path": "/path/to/.env", "event": "modified|created|deleted"}
15
+ # INPUT: {"file_path": "/path/to/.env", "event": "change|add|unlink"}
16
16
  #
17
17
  # DECISION CONTROL: None (notification only — shows stderr to user)
18
18
 
@@ -24,17 +24,17 @@ EVENT=$(echo "$INPUT" | jq -r '.event // empty' 2>/dev/null)
24
24
  FILENAME=$(basename "$FILE_PATH")
25
25
 
26
26
  case "$EVENT" in
27
- modified)
27
+ change)
28
28
  echo "⚠ Environment file changed: ${FILENAME}" >&2
29
29
  echo " Path: ${FILE_PATH}" >&2
30
30
  echo " Action: Verify environment variables are still correct" >&2
31
31
  ;;
32
- created)
32
+ add)
33
33
  echo "📝 New environment file: ${FILENAME}" >&2
34
34
  echo " Path: ${FILE_PATH}" >&2
35
35
  echo " Action: Review contents before use" >&2
36
36
  ;;
37
- deleted)
37
+ unlink)
38
38
  echo "🗑 Environment file deleted: ${FILENAME}" >&2
39
39
  echo " Path: ${FILE_PATH}" >&2
40
40
  echo " Action: Check if environment variables are still available" >&2
@@ -27,13 +27,13 @@ UNDO_FILE="/tmp/claude-undo-session-${PPID:-0}.sh"
27
27
  TIMESTAMP=$(date +"%H:%M:%S")
28
28
 
29
29
  # Log the change
30
- echo "${TIMESTAMP} ${EVENT:-modified} ${FILE}" >> "$LOG_FILE"
30
+ echo "${TIMESTAMP} ${EVENT:-change} ${FILE}" >> "$LOG_FILE"
31
31
 
32
32
  # Track for undo (git-tracked files only)
33
33
  if git ls-files --error-unmatch "$FILE" &>/dev/null 2>&1; then
34
34
  # File is git-tracked — can be reverted with git checkout
35
35
  if ! grep -qF "git checkout -- \"$FILE\"" "$UNDO_FILE" 2>/dev/null; then
36
- echo "git checkout -- \"$FILE\" # ${EVENT:-modified} at ${TIMESTAMP}" >> "$UNDO_FILE"
36
+ echo "git checkout -- \"$FILE\" # ${EVENT:-change} at ${TIMESTAMP}" >> "$UNDO_FILE"
37
37
  fi
38
38
  fi
39
39
 
@@ -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