The loop owns control flow; hooks observe at named moments

What You’ll Learn

  • Why hooks are better than modifying the loop directly
  • How to implement named lifecycle hooks
  • How hooks can observe, block, or annotate agent behavior

The Problem

As your agent grows, you want to add logging, validation, notifications. Without hooks, you end up modifying the core loop for every new concern — creating a tangled mess.

The Solution

Named hooks that fire at specific moments. The loop stays clean; hooks plug in.

Agent loop                    Hooks
+------------------+          +------------------+
|                  |          | pre_tool_use:    |
| [Hook point 1] --|-------->|   validate, log, |
|                  |          |   block if unsafe|
|  execute tool    |          +------------------+
|                  |          | post_tool_use:   |
| [Hook point 2] --|-------->|   log results,   |
|                  |          |   notify on error|
+------------------+          +------------------+

How It Works

  1. Define hook points with names like pre_tool_use, post_tool_use, on_session_start.

  2. Hooks receive context (tool name, input, output) and return decisions: continue, block, or modify.

class HookSystem:
    def __init__(self):
        self.hooks = defaultdict(list)

    def register(self, point: str, handler):
        self.hooks[point].append(handler)

    def fire(self, point: str, context: dict) -> str:
        for handler in self.hooks[point]:
            result = handler(context)
            if result == "block":
                return "block"
        return "continue"

What Changed From s07

ComponentBefore (s07)After (s08)
ExtensibilityModify loop bodyNamed hook points
LifecycleImplicitExplicit (pre/post/session)
PluginsNoneRegister/unregister

Try It

cd learn-claude-code
python agents/s08_hook_system.py
  1. Write a file and check the hook logs
  2. Try to delete a protected file (hook should block it)
  3. Run several commands and review the hook audit trail

Key Takeaway

Hooks let the loop grow without being rewritten. Register observers at named moments and let the loop fire them.