cc-safe-setup 28.5.0 → 28.6.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.
package/COOKBOOK.md CHANGED
@@ -135,6 +135,16 @@ All browser-based, nothing leaves your machine:
135
135
  - [Playground](https://yurukusa.github.io/cc-safe-setup/playground.html) — Write and test hooks
136
136
  - [Hook Builder](https://yurukusa.github.io/cc-safe-setup/builder.html) — Generate hooks from English
137
137
 
138
+ ## 27. Bypass Protected Directory Prompts (PermissionRequest)
139
+
140
+ PreToolUse hooks can't bypass built-in protected-directory checks — they run *before* those checks. Use PermissionRequest instead:
141
+
142
+ ```bash
143
+ npx cc-safe-setup --install-example allow-git-hooks-dir
144
+ ```
145
+
146
+ Or manually: create a PermissionRequest hook that outputs `permissionDecision: "allow"`. See [Troubleshooting](TROUBLESHOOTING.md#pretooluse-allow-doesnt-bypass-protected-directory-prompts) for details.
147
+
138
148
  ## Further Reading
139
149
 
140
150
  - [Getting Started](https://yurukusa.github.io/cc-safe-setup/getting-started.html)
package/README.md CHANGED
@@ -87,7 +87,7 @@ Each hook exists because a real incident happened without it.
87
87
  | `--scan [--apply]` | Tech stack detection |
88
88
  | `--export / --import` | Team config sharing |
89
89
  | `--verify` | Test each hook |
90
- | `--install-example <name>` | Install from 316 examples |
90
+ | `--install-example <name>` | Install from 331 examples |
91
91
  | `--examples [filter]` | Browse examples by keyword |
92
92
  | `--full` | All-in-one setup |
93
93
  | `--status` | Check installed hooks |
@@ -132,6 +132,34 @@ jq -n '...'
132
132
 
133
133
  Auto-approve JSON must go to stdout.
134
134
 
135
+ ## "PreToolUse allow doesn't bypass protected directory prompts"
136
+
137
+ This is expected behavior, not a bug.
138
+
139
+ **Execution order:**
140
+ 1. PreToolUse hooks run
141
+ 2. Built-in protected-directory checks run (`.claude/`, `.git/`, etc.)
142
+ 3. PermissionRequest hooks run
143
+
144
+ PreToolUse's `permissionDecision: "allow"` gets overridden by the built-in checks in step 2. To bypass protected directory prompts, use **PermissionRequest** hooks instead:
145
+
146
+ ```bash
147
+ #!/bin/bash
148
+ # Save as: ~/.claude/hooks/allow-protected-dir.sh
149
+ # Trigger: PermissionRequest (not PreToolUse)
150
+ INPUT=$(cat)
151
+ PATH_TARGET=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.command // empty')
152
+
153
+ # Allow writes to a specific protected directory
154
+ if echo "$PATH_TARGET" | grep -q '/my-project/.git/hooks'; then
155
+ jq -n '{hookSpecificOutput: {hookEventName: "PermissionRequest", permissionDecision: "allow", permissionDecisionReason: "Allowed: git hooks directory"}}'
156
+ exit 0
157
+ fi
158
+ exit 0
159
+ ```
160
+
161
+ **Rule of thumb:** PreToolUse = block dangerous actions. PermissionRequest = allow trusted actions that trigger built-in prompts.
162
+
135
163
  ## "Permission prompts still appear for compound commands"
136
164
 
137
165
  This is a known Claude Code limitation, not a hook issue. `Bash(git:*)` doesn't match `cd /path && git log`.
@@ -0,0 +1,29 @@
1
+ #!/bin/bash
2
+ # allow-git-hooks-dir.sh — PermissionRequest hook
3
+ # Trigger: PermissionRequest
4
+ # Matcher: Edit|Write
5
+ #
6
+ # Bypasses the built-in protected-directory prompt for .git/hooks/.
7
+ # PreToolUse hooks can't do this — they run before built-in checks.
8
+ # PermissionRequest runs after, so it can override the prompt.
9
+ #
10
+ # WARNING: Only allow specific subdirectories you trust.
11
+ # Never blanket-allow all of .git/ — that exposes HEAD, config, etc.
12
+
13
+ INPUT=$(cat)
14
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)
15
+ [ -z "$FILE_PATH" ] && exit 0
16
+
17
+ # Only allow .git/hooks/ writes (e.g., pre-commit, pre-push)
18
+ if echo "$FILE_PATH" | grep -qE '\.git/hooks/[^/]+$'; then
19
+ jq -n '{
20
+ hookSpecificOutput: {
21
+ hookEventName: "PermissionRequest",
22
+ permissionDecision: "allow",
23
+ permissionDecisionReason: "Allowed: git hooks directory"
24
+ }
25
+ }'
26
+ exit 0
27
+ fi
28
+
29
+ exit 0
package/index.mjs CHANGED
@@ -601,9 +601,12 @@ async function installExample(name) {
601
601
  let matcher = 'Bash';
602
602
 
603
603
  // Detect trigger from header comments
604
- if (content.includes('PostToolUse')) trigger = 'PostToolUse';
605
- if (content.includes('Notification')) trigger = 'Notification';
606
- if (content.includes('Stop')) trigger = 'Stop';
604
+ if (content.includes('TRIGGER: PostToolUse') || content.includes('PostToolUse')) trigger = 'PostToolUse';
605
+ if (content.includes('TRIGGER: Notification') || content.includes('Notification')) trigger = 'Notification';
606
+ if (content.includes('TRIGGER: Stop') || content.includes('Stop')) trigger = 'Stop';
607
+ if (content.includes('TRIGGER: SessionStart') || content.includes('SessionStart')) trigger = 'SessionStart';
608
+ if (content.includes('TRIGGER: PreCompact') || content.includes('PreCompact')) trigger = 'PreCompact';
609
+ if (content.includes('TRIGGER: SessionEnd') || content.includes('SessionEnd')) trigger = 'SessionEnd';
607
610
 
608
611
  // Detect matcher from header
609
612
  const matcherMatch = content.match(/"matcher":\s*"([^"]*)"/);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cc-safe-setup",
3
- "version": "28.5.0",
4
- "description": "One command to make Claude Code safe. 336 hooks (8 built-in + 330 examples). 49 CLI commands. 988 tests. 5 languages.",
3
+ "version": "28.6.0",
4
+ "description": "One command to make Claude Code safe. 337 hooks (8 built-in + 331 examples). 49 CLI commands. 996 tests. 5 languages.",
5
5
  "main": "index.mjs",
6
6
  "bin": {
7
7
  "cc-safe-setup": "index.mjs"