The hook system — register handlers for lifecycle events, fire them in-process, or drive them from config

Overview

attune.hooks is an event system: it lets code (and config) react to lifecycle events — before/after a tool runs, at session start/end, around compaction, and on stop. The public surface, exported from attune.hooks, is five symbols:

The plugin also ships concrete hook scripts under attune/hooks/scripts/ (e.g. security_guard, worktree_path_guard, lessons_reminder) — these are the hooks Claude Code actually runs.

Concepts

HookEvent

The lifecycle events. Their values are the Claude Code event names — e.g. HookEvent.PRE_TOOL_USE.value == "PreToolUse" — so the same enum labels in-process registration and the Claude Code hook contract.

HookRegistry — in-process handlers

HookRegistry(config=None) is the programmatic surface. register(event, handler, description="", matcher=None, priority=0) adds a handler and returns a hook id. Handlers receive the context dict unpacked as keyword arguments — write def handler(**context), not def handler(context). Fire with fire(event, context=None) (async) or fire_sync(event, context=None) (sync); both return a list of per-hook result dicts — a success record carries event, hook, description, success, output, error, duration_ms; an error record is a subset (event, hook, success, error). get_matching_hooks, unregister, get_execution_log, get_stats, and load_config round it out.

HookExecutor and HookDefinition

HookDefinition (a pydantic model) describes a configured hook: type (HookType.COMMAND / PYTHON / WEBHOOK, default PYTHON), command, description, timeout (1–300 s, default 30), async_execution (default False), on_error (default "log"). HookExecutor(python_handlers=None).execute(hook, context) (async) runs one.

HookConfig — declarative, config-driven hooks

HookConfig (pydantic) holds the declarative rules: hooks (a dict of event → list of HookRule), plus enabled, log_executions, and default_timeout. Load it with HookConfig.from_yaml(yaml_path), build it with add_hook(event, hook, matcher=None, priority=0), and query it with get_hooks_for_event(event). A HookRule carries a matcher, its hooks, enabled, priority, and description.