Skip to content

fix(claude_code): wrap hook output in hookSpecificOutput envelope#64

Open
PabloLION wants to merge 1 commit intoClyra-AI:mainfrom
PabloLION:fix/claude-code-hook-response-wrapper
Open

fix(claude_code): wrap hook output in hookSpecificOutput envelope#64
PabloLION wants to merge 1 commit intoClyra-AI:mainfrom
PabloLION:fix/claude-code-hook-response-wrapper

Conversation

@PabloLION
Copy link

Summary

  • gait-gate.sh outputs {permissionDecision: "deny", ...} at the top level, but Claude Code requires {hookSpecificOutput: {hookEventName: "PreToolUse", permissionDecision: "deny", ...}}
  • Without the wrapper, Claude Code silently ignores the hook response and falls through to its default permission handling — making the hook appear advisory-only when it's actually being discarded
  • Fix wraps both emit_response() and the inline emit() function in the required hookSpecificOutput envelope

Detail

Claude Code's PreToolUse hook protocol expects stdout JSON in this shape:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow|deny|ask",
    "permissionDecisionReason": "..."
  }
}

The current script outputs permissionDecision at the top level, which CC ignores without error. This affects all three verdict paths (allow, deny, ask).

Test plan

Tested in Claude Code v2.1.47–v2.1.49 with a mixed-verdict policy (allow for reads, require_approval for exec, block for writes):

  • Read → gait maps to tool.read → allow → CC auto-runs (no prompt)
  • Bash (python3) → gait maps to tool.exec → require_approval → CC prompts user with gait reason
  • Write → gait maps to tool.write → block → CC blocks with gait reason
  • Empty payload → fail-open (allow)
  • Missing policy → fail-open (allow)

All paths verified in both manual pipe tests and live interactive CC sessions.

🤖 Generated with Claude Code

Claude Code's PreToolUse hook protocol requires responses wrapped in
{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": ...}}.
The previous implementation output permissionDecision at the top level, which
Claude Code silently ignores — making all gait verdicts (allow, deny, ask)
unenforceable regardless of policy.

Fix wraps both emit_response() and the inline emit() function in the required
envelope. Verified against Claude Code v2.1.47–v2.1.49 with all three verdict
paths (allow, require_approval, block) in live interactive sessions.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant