fix(hooks): nest freeze/careful permissionDecision under hookSpecificOutput

Claude Code ignores a top-level permissionDecision, so the /freeze deny and
/careful ask guards silently allowed everything. Nest both under
hookSpecificOutput with permissionDecisionReason, update the shape-blind
tests to pin the nested form, and document the constraint in both skill
templates (regen included).

Closes half of #1459 (freeze enforcement chain).

Contributed by @jawadakram20 (PR #2331; team-init hunk deferred to the
dedicated team-init fix).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-08-14 15:34:35 -07:00
co-authored by Claude Fable 5
parent 1d41ee3ab3
commit 2918542c78
7 changed files with 92 additions and 80 deletions
+4 -2
View File
@@ -1,7 +1,9 @@
#!/usr/bin/env bash
# check-careful.sh — PreToolUse hook for /careful skill
# Reads JSON from stdin, checks Bash command for destructive patterns.
# Returns {"permissionDecision":"ask","message":"..."} to warn, or {} to allow.
# Returns a PreToolUse hookSpecificOutput with permissionDecision "ask" to warn,
# or {} to allow. The decision MUST be nested under hookSpecificOutput — Claude
# Code ignores a top-level permissionDecision, which silently no-ops the warning.
set -euo pipefail
# Read stdin (JSON with tool_input)
@@ -101,7 +103,7 @@ if [ -n "$WARN" ]; then
echo '{"event":"hook_fire","skill":"careful","pattern":"'"$PATTERN"'","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true
WARN_ESCAPED=$(printf '%s' "$WARN" | sed 's/"/\\"/g')
printf '{"permissionDecision":"ask","message":"[careful] %s"}\n' "$WARN_ESCAPED"
printf '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"ask","permissionDecisionReason":"[careful] %s"}}\n' "$WARN_ESCAPED"
else
echo '{}'
fi