AI-101

Lesson 23

Hooks for Automation

AI Confidence: 90%

AI-generated

Hooks for Automation

Learning Objectives

Understand what hooks are and when they run

Create PreToolUse hooks that run before actions

Create PostToolUse hooks that run after actions

Configure hooks in settings.json

Use hooks for logging, validation, and custom workflows

Introduction

Hooks let you inject custom behavior into Claude Code's workflow. They are shell commands that run at specific points: before Claude uses a tool, after it uses a tool, or when a session starts.

Think of hooks as event listeners. When something happens, your hook runs. This opens up automation possibilities: logging every command Claude runs, validating changes before they happen, syncing state after edits, or enforcing project rules.

This lesson teaches you how hooks work and how to use them effectively.

How Hooks Work

Hooks are defined in your settings file and run automatically when their trigger condition is met.

Hook types:

PreToolUse - Runs before Claude uses a tool

PostToolUse - Runs after Claude uses a tool

SessionStart - Runs when a session begins

Each hook specifies:

When it triggers (the event type)

Which tools it applies to (optional filter)

What command to run

The hook receives context about the action as JSON on stdin. It can inspect this context and take action.

Configuring Hooks

Hooks live in your settings file. You can set them at user level (~/.claude/settings.json) or project level (.claude/settings.json).

Basic structure:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop

Matcher patterns:

"Bash" - Matches all Bash tool uses

"Edit" - Matches all Edit tool uses

"*" - Matches any tool

"Bash(npm *)" - Matches Bash commands starting with npm

PreToolUse Hooks

PreToolUse hooks run before Claude executes a tool. They can:

Log what Claude is about to do

Validate the action

Block actions that violate rules

Transform inputs

Example: Log all commands

Create a script that logs every Bash command:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop

Configure in settings:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop

Now every command Claude runs gets logged.

Example: Block dangerous patterns

Create a hook that blocks certain commands:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop

A non-zero exit code blocks the action.

PostToolUse Hooks

PostToolUse hooks run after Claude completes a tool use. They can:

Log results

Trigger side effects

Update external systems

Chain additional actions

Example: Auto-format after edits

Run a formatter after Claude edits files:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop

Configure:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop

Example: Notify on file changes

Send a notification when files are edited:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop
SessionStart Hooks

SessionStart hooks run when you begin a Claude Code session. They can:

Set up the environment

Check prerequisites

Load project-specific state

Display reminders

Example: Check environment

Verify required tools are installed:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop

Configure:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop
Hook Input Format

Hooks receive JSON on stdin with context about the action:

PreToolUse and PostToolUse:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop

SessionStart:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop

Use jq to extract fields you need.

Hook Output

Hooks communicate back to Claude Code through exit codes and stderr:

Exit codes:

0 - Success, continue normally

Non-zero - Failure, block the action (PreToolUse) or report error (PostToolUse)

Stderr output:

JSON on stderr with an error field gets displayed to Claude:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop
Practical Hook Patterns

Audit trail:

Log every action Claude takes for compliance or debugging:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop

Cost tracking:

Count tool uses per session:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop

Custom validation:

Enforce project rules before edits:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop

External integration:

Sync with external systems after changes:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop
Debugging Hooks

When hooks do not work as expected:

Check the script is executable:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop

Test manually:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop

Check stderr output:

Hooks that write non-JSON to stderr may cause issues. Keep stderr output clean.

Use logging:

Add logging to your hook to see what is happening:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop
Key Takeaways

Hooks run custom commands at specific points in Claude's workflow

PreToolUse hooks run before actions and can block them

PostToolUse hooks run after actions and can trigger side effects

SessionStart hooks run when sessions begin

Configure hooks in settings.json with matchers and commands

Hooks receive JSON context on stdin

Non-zero exit codes block or report errors

Use hooks for logging, validation, formatting, and integration

Try It Yourself

Create a simple logging hook:

Create a script at ~/claude-hooks/log-all.sh:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop

Make it executable: chmod +x ~/claude-hooks/log-all.sh

Add to ~/.claude/settings.json:

[@portabletext/react] Unknown block type "codeBlock", specify a component for it in the `components.types` prop

Start a Claude Code session and ask Claude to run a few commands.

Check ~/claude-activity.log to see logged activity.

Remove the hook when done experimenting.

This exercise shows how hooks can observe Claude's behavior.

Sources

https://code.claude.com/docs/en/hooks - Complete hooks documentation

https://code.claude.com/docs/en/hooks#hook-examples - Hook example patterns

https://code.claude.com/docs/en/settings - Settings file format for hooks