The Complete Claude Guide
← Back to topic
Chapter 17 · CLI

Hooks: automation on the session lifecycle

Hooks are commands that run automatically on defined events in the session lifecycle: before a tool, after a tool, on messages and more. They enforce quality and policy without relying on memory.

Verified against source on 2026-09-30

Video

Automate one event, not every permission

You will inspect hook scope, parse event input and test an inert script before considering registration. Outputs are illustrations.

Hook guide checked September 30, 2026 describes deterministic handlers on lifecycle events. They can run shell code, call endpoints or use other handler types. A hook is executable configuration, not a prompt shortcut.

Choose the event and the effect

A notification is a smaller first example than auto-approval or deployment. Define: Notification event, idle_prompt matcher, no network, no filesystem write and no permission decision. This lesson's script only validates a sample input and exits.

Design a Notification hook filtered to idle_prompt for an empty practice project.
First return a dry-run plan, expected JSON input and test cases.
No installation, network, logging of prompts, auto-approval or deployment.
Explain which settings scope would change and how to remove only this hook.

The /hooks menu is read-only in the current documentation. Actual registration requires a settings edit. Inspect existing settings before changing them so you do not overwrite unrelated hooks.

Understand input and output

Command hooks receive JSON on stdin. Fields depend on the event: a Notification is not a Bash PreToolUse payload. Parse input as data, not as shell code. Do not evaluate a command or path copied from event content.

For PreToolUse, exit 0 alone does not approve a tool call; ordinary permissions still apply. Exit 2 can block where the event supports blocking. Other error cases can be non-blocking. Behavior is event-specific, so a notification script is not a security gate.

Test an inert parser first

Save this as a local practice script, but do not register it yet:

import json
import sys

event = json.load(sys.stdin)
if not isinstance(event, dict):
    raise ValueError("Expected a JSON object")
if event.get("hook_event_name") != "Notification":
    raise ValueError("Expected Notification")
# No output, network, file write or permission decision.

Feed a synthetic object for the intended event and then a mismatched event. This verifies the parser, not the complete Claude hook path. An exception is intentionally a test failure; do not assume it blocks Claude operations in every event.

Illustrative test record:

Valid notification: parser exits normally.
Wrong event: parser fails validation.
Malformed JSON: parser fails before any side effect.
Live hook: not registered or tested yet.
Registration is a separate change

Once you understand the existing settings and event schema, you can add a narrowly scoped handler in a disposable project. Use a trusted, absolute script path, set an appropriate timeout and test the actual event. Avoid fragile shell strings containing untrusted paths. Never auto-approve every PermissionRequest just to remove interruptions.

Keep the initial example local. HTTP hooks send event data to an endpoint; full prompts, paths or command bodies may be sensitive. Model-backed hooks can consume usage. Both require a separate data and cost review.

Know what a hook cannot prove

PostToolUse runs after the action and cannot undo it. A matcher on Edit|Write misses changes made through Bash. Multiple matching handlers can all run even when another returns a denial, so a deny handler does not suppress sibling side effects.

A tiny keyword filter is not a complete security policy. Real protection needs permission controls, isolation and tests against the actual operation, not an impressive-looking hook.

Graded practice

Easy: event mapping

Choose one event and narrow matcher. Success: explain input and intended effect.

Intermediate: parser

Test valid, mismatched and malformed JSON. Success: no side effects and honest test scope.

Challenging: cleanup plan

Locate the exact settings entry and plan removal. Success: preserve all unrelated settings.

Troubleshooting

Hook not listed: check settings scope and JSON structure.

Runs too often: inspect matcher support for that event.

Unexpected permission: remove broad approval decisions, inspect actual mode.

Secret in a log: stop collection and review retention/access.

Timeout or non-blocking failure: inspect event-specific semantics instead of claiming enforcement.

Mechanism animation (illustration)

Filmed demo · Video is added in the media phase

Recap

Comprehension check

What are hooks for?

Recap

Sources and further reading

← Previous Back to topic Next →