class HookEvent(Enum):
SESSION_START = 'SessionStart'
USER_PROMPT_SUBMIT = 'UserPromptSubmit'
PRE_TOOL_USE = 'PreToolUse'
POST_TOOL_USE = 'PostToolUse'
PRE_COMPACT = 'PreCompact'
STOP = 'Stop'
SUBAGENT_STOP = 'SubagentStop'
SESSION_END = 'SessionEnd'class HookPermissionMode(Enum):
AUTO = 'auto' # same as ASK
ASK = 'ask' # block destructive_tools
ALLOW = 'allow' # no restriction
DENY = 'deny' # block everythingRaised by a PreToolUseHookFn to veto the tool call. Message is surfaced to the user.
@dataclass
class HookConfig:
pre_hooks: list[PreToolUseHookFn]
post_hooks: list[PostToolUseHookFn]
session_start_hooks: list[SessionHookFn]
session_end_hooks: list[SessionHookFn]
user_prompt_submit_hooks: list[SessionHookFn]
pre_compact_hooks: list[PreCompactHookFn]
stop_hooks: list[SessionHookFn]
subagent_stop_hooks: list[SessionHookFn]
enabled: bool = Trueregister_pre_hook(fn: PreToolUseHookFn) -> None
register_post_hook(fn: PostToolUseHookFn) -> None
register_session_start_hook(fn: SessionHookFn) -> None
register_session_end_hook(fn: SessionHookFn) -> None
register_user_prompt_submit_hook(fn: SessionHookFn) -> None
register_pre_compact_hook(fn: PreCompactHookFn) -> None
register_stop_hook(fn: SessionHookFn) -> None
register_subagent_stop_hook(fn: SessionHookFn) -> Nonerun_pre_hooks(tool_name: str, arguments: dict) -> dict | NoneRuns all pre-hooks in order. Returns final modified args, or raises HookError.
run_post_hooks(tool_name: str, arguments: dict, result: dict) -> dict | NoneRuns all post-hooks in order. Returns final modified result or None.
run_session_start_hooks(session_id: str, context: dict) -> None
run_session_end_hooks(session_id: str, context: dict) -> None
run_user_prompt_submit_hooks(session_id: str, context: dict) -> None
run_stop_hooks(session_id: str, context: dict) -> None
run_subagent_stop_hooks(session_id: str, context: dict) -> None
run_pre_compact_hooks(context: dict) -> dict | None- Runs
ruff check <root>after file-write tools. - Raises
HookErrorwith first 500 chars of stderr on lint failure. - Silently skips if
ruffnot installed.
- Default command:
['uv', 'run', 'pytest', 'tests/', '-x', '-q'] - 120-second timeout.
- Raises
HookErroron failure; skips if command not found.
- Runs
ruff format <root>. - 30-second timeout.
- Runs arbitrary shell command via
shlex.split+subprocess.run(shell=False). - 30-second timeout (hardcoded, not configurable).
permission_check_hook(mode, *, allow_patterns, deny_patterns, destructive_tools) -> PreToolUseHookFn
- Raises
HookErrorforDENYmode or tools indestructive_toolswhen mode isASK/AUTO. - Path glob matching via
fnmatch.
- Raises
HookErrorif tool_name inblocked_toolsor not inallowed_tools.
- Injects
_samplingkey into arguments formcp_*tool names.
- SessionStart hook that reads
CLAUDE.md/AGENTS.mdviaload_project_instructions(root). - Writes result to
context['project_instructions'].