WEEK 2 LEARN

Hooks & Automation

Intercept and control Claude's actions with powerful hooks for automated workflows.

Learning Progress 0/5 Lessons
Quiz Score: 0/10

LESSONS

1

Introducing Hooks

PreToolUse, PostToolUse, and hook concepts

Not Started

What are Hooks?

Hooks allow you to run commands before or after Claude Code does something. You can also optionally block Claude's action.

PreToolUse

Runs before a tool executes. Can prevent the tool call from running.

PostToolUse

Runs after a tool has executed. Cannot block, but can provide feedback.

Example Use Cases

  • Run a code formatter after Claude edits a file
  • Stop Claude from reading sensitive files like .env
  • Check for TODO comments and log them automatically
  • Run tests automatically after a file is changed
  • Block file edits that violate naming conventions
  • Prevent usage of deprecated functions

Available Tools to Monitor

Tools
Read       # Read a file
Edit       # Edit an existing file
MultiEdit  # Multiple edits in one operation
Write      # Create a file and write to it
Bash       # Execute a shell command
Glob       # Find files/folders by pattern
Grep       # Search for content in files
Task       # Create a sub-agent
2

Defining Hooks

Configuration, matchers, and exit codes

Not Started

Building a Hook: 4 Steps

1
Choose PreToolUse or PostToolUse

PreToolUse can prevent actions; PostToolUse runs after

2
Determine which tools to watch

Use matchers like "Read|Grep" or "*" for all tools

3
Write a command to receive the tool call

Your command receives JSON data via stdin

4
Provide feedback via exit codes

Exit code determines if action is allowed or blocked

Exit Codes

Exit Code 0

Allow the tool call to proceed

Exit Code 2

Block the tool call (PreToolUse only). Stderr is sent to Claude as feedback.

Configuration File

.claude/settings.local.json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Read|Grep",
        "hooks": [
          {
            "type": "command",
            "command": "node /path/to/hook.js"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write"
          }
        ]
      }
    ]
  }
}
3

Implementing Hooks

Practical examples: .env protection, TypeScript checking

Not Started

Example 1: Protect .env Files

This hook prevents Claude from reading sensitive files like .env that contain API keys and secrets.

hooks/read_hook.js
async function main() {
  const chunks = [];
  for await (const chunk of process.stdin) {
    chunks.push(chunk);
  }
  const toolArgs = JSON.parse(Buffer.concat(chunks).toString());

  // Get the file path Claude is trying to read
  const readPath = toolArgs.tool_input?.file_path ||
                   toolArgs.tool_input?.path || "";

  // Block .env files
  if (readPath.includes(".env")) {
    console.error("Access denied: .env files are protected");
    process.exit(2); // Block the read
  }

  process.exit(0); // Allow other reads
}

main();

Example 2: TypeScript Type Checking

Run the TypeScript compiler after every edit to catch type errors immediately.

hooks/tsc.js
import * as ts from "typescript";

async function main() {
  const input = await readInput();
  const file = input.tool_response?.filePath ||
               input.tool_input?.file_path;

  // Only check TypeScript files
  if (!file || !/\.(ts|tsx)$/.test(file)) {
    process.exit(0);
  }

  const errors = runTypeCheck("./tsconfig.json");
  if (errors) {
    console.error(errors);
    process.exit(2); // Report errors to Claude
  }
}

main();
⚠️
Security Best Practices
  • • Always validate and sanitize inputs
  • • Use absolute paths for scripts (prevents path interception)
  • • Quote shell variables: "$VAR" not $VAR
  • • Block path traversal: check for ".." in paths
4

Other Hook Types

Notification, Stop, UserPromptSubmit, and more

Not Started

Beyond PreToolUse and PostToolUse

Claude Code provides several additional hook types for different lifecycle events:

Notification

Runs when Claude needs permission or after 60 seconds of idle time

Stop

Runs when Claude has finished responding

SubagentStop

Runs when a subagent (Task) has finished

PreCompact

Runs before a compact operation (manual or automatic)

UserPromptSubmit

Runs when user submits a prompt, before Claude processes it

SessionStart / SessionEnd

Runs when starting/resuming or ending a session

💡
Debugging Tip

Create a helper hook that logs all inputs to a file with jq . > hook-log.json. This helps you understand the exact structure of data your command receives.

Practical Example: Auto-Format on Save

settings.local.json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_response.filePath // .tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}
5

Pro Tips & Plugins

Boris Cherny's hook tips and official plugins

Not Started
💡
Boris Cherny's Claude Code Tips

Practical tips shared by Boris Cherny from Anthropic Developer Relations.

Tip 9: Code Formatting with PostToolUse

Automatically runs Prettier every time Claude modifies a file, keeping code style consistent.

settings.local.json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_response.filePath // .tool_input.file_path' | xargs npx prettier --write 2>/dev/null || true"
          }
        ]
      }
    ]
  }
}

Tip 10: Permission Management with /permissions

Use the /permissions command to check and manage the current session's permissions.

Check Permissions

View currently allowed tools and paths

/permissions
Add Permissions

Approve permissions for specific operations

/allowed-tools

Tip 12: ralph-loop Plugin (Long-running Tasks)

Implements a self-referencing loop that automatically restarts Claude after task completion. Can be used with background agents to automate long-running tasks.

Stop Hook - Auto Restart
# Re-invoke Claude from the Stop hook
{
  "hooks": {
    "Stop": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "claude --continue"
          }
        ]
      }
    ]
  }
}

Official Plugins

🪝
hookify

A plugin for easily creating and managing custom hooks.

# Install
claude plugins add github:anthropics/hookify

# Run hook creation wizard
/hookify:create
🔄
ralph-loop

A plugin that implements self-referencing loop cycles. Automates long-running tasks in the background.

# Install
claude plugins add github:anthropics/ralph-loop

# Start long-running task
/ralph-loop:start "Perform large-scale refactoring"

TERMINOLOGY

PreToolUse Hook

A hook triggered before a tool executes. Returning exit code 2 can block the tool from running. Used for blocking .env file reads, preventing dangerous command execution, etc.

PostToolUse Hook

A hook triggered after a tool executes. Cannot block actions, but can provide feedback to Claude. Used for code formatting, type checking, logging, etc.

Exit Code

The exit code returned by a hook script.
0 = Allow tool execution
2 = Block tool execution (PreToolUse only)
Messages printed to stderr are passed to Claude as feedback.

Background Agent

A mode where Claude runs in the background and sends a notification when the task is complete. Start a long task and check back when it finishes. Activated with run_in_background: true.

Agent Stop Hook

A hook triggered when Claude finishes responding. Used for automatic notifications, logging, or implementing self-referencing loops (ralph-loop).

KNOWLEDGE CHECK

Quiz: Hooks & Automation

Question 1/10

Ready for the Challenge?

Build automated workflows with hooks in a real project.

Start Challenge