Skip to content

Hooks

Hooks run shell commands at fixed points in the tool lifecycle. They are how you wire up auto-formatting after an edit, a lint pass when a session ends, or an audit log of every command the engine runs. You declare them in the config file.

EventFiresBehavior
pre_tool_useBefore a tool runs.A non-zero exit blocks the tool.
post_tool_useAfter a tool runs.Non-blocking; errors are logged.
stopWhen the session ends.Non-blocking.

Use pre_tool_use to gate a tool (for example, refuse a write to a protected path), post_tool_use for follow-up work like formatting, and stop for a final pass.

Each hook is a [[hooks.<event>]] table. Match on tool name, on file path, or on neither to match everything:

# Format Rust files after they change
[[hooks.post_tool_use]]
name = "rustfmt"
tool_match = ["Write", "Edit"]
file_match = ["*.rs"]
command = "rustfmt ${TOOL_INPUT_FILE_PATH}"
# Format TypeScript files
[[hooks.post_tool_use]]
name = "prettier"
tool_match = ["Write", "Edit"]
file_match = ["*.ts", "*.tsx"]
command = "npx prettier --write ${TOOL_INPUT_FILE_PATH}"
# Audit every Bash command
[[hooks.post_tool_use]]
name = "audit-log"
tool_match = ["Bash"]
command = "echo \"$(date): ${TOOL_INPUT_COMMAND}\" >> .wayland-core/audit.log"
# Lint once at session end
[[hooks.stop]]
name = "final-lint"
command = "cargo clippy --quiet 2>&1 | tail -5"

Hook commands reference these with ${VAR} syntax:

VariableDescription
TOOL_NAMEThe tool name.
TOOL_INPUTFull tool input as JSON.
TOOL_INPUT_FILE_PATHFile path, when the tool has one.
TOOL_INPUT_COMMANDCommand, when the tool has one.
TOOL_INPUT_PATTERNSearch pattern, when the tool has one.
TOOL_OUTPUTTool output (post_tool_use only).

tool_match takes glob patterns against tool names; an empty list matches all tools. file_match works the same against file paths. Each hook has a 30-second default timeout, set per hook with timeout_ms.