Project Guardian: A Claude Code Hooks Tutorial

Project Guardian: A Claude Code Hooks Tutorial

This tutorial walks you through building Project Guardian — a safety and observability layer that intercepts, inspects, and controls everything Claude Code does. By the end, you will have a working set of ten hooks covering audit logging, command blocking, credential protection, session reporting, and more. You will also understand where hooks fit in a real security posture and where they do not.

Every lesson builds on the previous one. Start at Lesson 01 and work forward. The code is already written and running — your job is to understand it, then modify it.


What Is Project Guardian?

Project Guardian is a pre-configured Claude Code hook system. It runs alongside Claude Code, intercepting every tool call, prompt, and session event. You do not change how you use Claude — you just get a layer of visibility and control underneath it.

Ten hooks work together as a single deployed agent:

  • A PreToolUse hook logs every tool call before it runs
  • A second PreToolUse hook blocks dangerous shell commands
  • A third blocks access to hidden files (.env, .ssh/, .aws/)
  • A fourth rewrites rm commands into safe previews instead of blocking them
  • A UserPromptSubmit hook intercepts each message before Claude sees it
  • A PostToolUse hook scans command output for exposed credentials
  • A second PostToolUse hook enriches command results with execution metadata
  • A third records every tool call to a structured ledger file
  • A SessionStart hook greets you and injects session context into Claude
  • A SessionEnd hook reports a session activity summary when you exit

None of these require changes to how you work. They activate when Claude Code opens and run silently in the background.


Prerequisites

  • Claude Code installed and working (claude --version should return a version)
  • Python 3 available (python3 --version should return 3.8 or higher)
  • Basic terminal familiarity: navigating directories, reading files, running commands

No Python packages beyond the standard library are required.


How to Use This Tutorial

  1. Open Claude Code from this directory — not from the repo root:

bash cd /path/to/hooks-pretooluse claude

This matters. Claude Code discovers .claude/settings.json by walking up from the launch directory. Opening from this directory scopes all hooks to this project only.

  1. Follow the lessons in order. Each lesson introduces a new hook and builds on the concepts from the previous one.

  2. Each lesson has three sections:

  3. A walkthrough explaining the code line by line
  4. A Try it section with hands-on steps to observe the hook working
  5. An Exercise to extend the code yourself
  6. A Deeper dive for experienced users who want more

  7. The code is already registered and running. You will be reading, testing, and modifying it — not writing it from scratch.


Lesson Index

Lesson Title Hook Type What You Will Build
01 How Hooks Work All Understand the hook system before writing any code
02 Audit Logging PreToolUse Log every tool call to a file
03 Blocking Dangerous Commands PreToolUse Block shell commands before they run
04 Protecting Hidden Files PreToolUse Guard credentials and config files
05 Input Mutation PreToolUse Rewrite commands instead of blocking them
06 User Prompt Hooks UserPromptSubmit Intercept and validate messages before Claude sees them
07 Scanning Outputs for Secrets PostToolUse Detect credentials in command output
08 Enriching Command Output PostToolUse Add metadata to tool results
09 Usage Tracking PostToolUse + SessionEnd Record every tool call and summarize sessions
10 Session Lifecycle SessionStart + SessionEnd Context injection and session reports
11 Project Guardian All Assemble all hooks into a deployable agent
12 Security Deep Dive All Understand the vulnerabilities and mitigations

File Structure

hooks-pretooluse/
  .claude/
    settings.json          — Hook configuration: all registered hooks and matchers

  lessons/                 — Tutorial lessons in order
    01-how-hooks-work.md
    02-audit-logging.md
    03-blocking-commands.md
    04-protecting-files.md
    05-input-mutation.md
    06-user-prompt-hooks.md
    07-post-scanning.md
    08-post-enhancement.md
    09-usage-tracking.md
    10-session-lifecycle.md
    11-project-guardian.md
    12-security.md

  demos/                   — Standalone demonstration scripts (not active by default)
    inject_as_message.py   — Shows prompt injection via file contents (intentionally insecure)
    error_demo.py          — Shows what a crashing hook looks like
    notify_external.py     — Shows how to send hook events to an external service

  log_hook.py              — Audit logging: appends every tool call to tool_calls.log
  block_dangerous.py       — Blocks Bash commands matching a danger pattern list
  block_hidden_files.py    — Blocks Read/Write/Bash access to hidden files and directories
  mutate_dryrun.py         — Rewrites rm commands into echo previews
  userprompt_hook.py       — Logs and optionally blocks user messages before Claude sees them
  post_secret_scan.py      — Scans Bash output for credential patterns
  post_enhance.py          — Adds duration, status, and line-count metadata to Bash results
  post_usage_ledger.py     — Appends one JSON record per tool call to usage_ledger.jsonl
  session_start.py         — Greets user at session open, injects context into Claude
  session_end_summary.py   — Reads usage_ledger.jsonl, prints session report, saves summary

  print_file.py            — Helper script: reads a file and prints its contents (used by inject_as_message.py demo)
  hooks-summary.md         — Reference summary of all hooks, matchers, and behaviors
  vulnerabilities.md       — Running record of security issues discovered during development
  tool_calls.log           — Runtime: audit log written by log_hook.py
  prompt_log.txt           — Runtime: prompt log written by userprompt_hook.py
  usage_ledger.jsonl       — Runtime: per-call records written by post_usage_ledger.py
  usage_summary.jsonl      — Runtime: per-session summaries written by session_end_summary.py

Quick Start

Three steps to verify the hooks are active:

Step 1: Open Claude Code from this directory.

claude

Step 2: Look for the Guardian greeting.

Immediately after Claude Code opens, before any prompt, you should see:

[Guardian] New session started | HH:MM:SS

If this appears, the SessionStart hook fired. All other hooks are also active.

Step 3: Ask Claude to list the files, then check the log.

In the Claude Code session:

list the files in this directory

Then, in a separate terminal:

tail -5 tool_calls.log

You should see a JSON entry for the tool call Claude just made. If the log is growing, the PreToolUse audit hook is running.

From here, proceed to Lesson 01.

Want to explore more?

Explore all tutorials →