47 lines
1.2 KiB
Python
Executable File
47 lines
1.2 KiB
Python
Executable File
"""Common helpers for Claude Code hooks. Imported by the .py hook scripts.
|
|
|
|
Hooks receive JSON on stdin per the Claude Code hook spec, and may emit
|
|
JSON on stdout or exit with code 2 to block. This module wraps both.
|
|
"""
|
|
import json
|
|
import sys
|
|
|
|
|
|
def read_input() -> dict:
|
|
"""Parse stdin JSON. Empty input → empty dict."""
|
|
raw = sys.stdin.read().strip()
|
|
if not raw:
|
|
return {}
|
|
try:
|
|
return json.loads(raw)
|
|
except json.JSONDecodeError:
|
|
return {}
|
|
|
|
|
|
def emit(payload: dict) -> None:
|
|
"""Print JSON payload to stdout for the harness to interpret."""
|
|
print(json.dumps(payload))
|
|
|
|
|
|
def deny_pretooluse(reason: str) -> None:
|
|
"""Emit a PreToolUse denial with reason and exit 0."""
|
|
emit({
|
|
"hookSpecificOutput": {
|
|
"hookEventName": "PreToolUse",
|
|
"permissionDecision": "deny",
|
|
"permissionDecisionReason": reason,
|
|
}
|
|
})
|
|
sys.exit(0)
|
|
|
|
|
|
def add_context(text: str) -> None:
|
|
"""Emit additionalContext for SessionStart / UserPromptSubmit hooks."""
|
|
if text and text.strip():
|
|
emit({"additionalContext": text})
|
|
|
|
|
|
def warn_to_stderr(msg: str) -> None:
|
|
"""Non-blocking warning visible to the next agent turn via stderr."""
|
|
print(msg, file=sys.stderr)
|