Hooks Tutorial

Claude Code hooks tutorial: guardrails, logging, and rule packs

For developers who want practical control over agent behavior, not vague “safety” language.

If you searched for a claude code hooks tutorial, you probably want three concrete things: intercept what the agent does, keep an audit trail, and block risky actions before they happen. Claude Code does not ship with a built-in governance layer for that. agentcheck fills the gap by wrapping each Claude process with hook-based checks, logging tool calls to JSONL, and enforcing rule packs defined in YAML.

The useful mental model is simple: Claude runs as usual, but every tool invocation passes through hooks that can inspect, allow, or reject the action. That makes this setup helpful for local development, CI experiments, and teams that need to answer “what did the agent actually do?” after the fact.

Install and basic usage

Install agentcheck globally from GitHub:

npm install -g github:paprika-org/agentcheck

Once installed, you run Claude Code through agentcheck instead of launching it directly. The exact wrapper command may evolve with the project, but the practical pattern is:

agentcheck -- claude

That means agentcheck becomes the parent process, attaches hooks around the Claude session, and starts recording events. If you want a dedicated rules file, pass it explicitly:

agentcheck --rules ./rules.yaml -- claude

This is the first place where a claude code hooks tutorial should be honest: hooks are not magic. They can constrain tool usage and provide visibility, but they do not replace reviewing prompts, repository permissions, or sandboxing. They are a control layer, not a complete isolation boundary.

What a rule pack looks like

Rule packs are YAML files that express what the agent is allowed to do. A useful starter pack is one that permits read operations broadly but restricts destructive shell commands and writes outside the current repository.

version: 1

rules:
  - id: allow-safe-read
    description: Permit non-destructive read commands
    match:
      tool: shell
      command_regex: "^(ls|pwd|cat|sed|find|rg)\\b"
    action: allow

  - id: block-destructive-git
    description: Prevent hard resets and forced cleans
    match:
      tool: shell
      command_regex: "^(git reset --hard|git clean -fdx)\\b"
    action: deny

  - id: block-parent-writes
    description: Deny writes outside the repo root
    match:
      tool: write_file
      path_regex: "^\\.\\./"
    action: deny

  - id: log-everything
    description: Always emit an audit event
    match:
      tool: ".*"
    action: log

The exact schema depends on the project version, but the core idea is stable: match a tool call, decide whether to allow or deny it, and write the event to a log. If you have used policy engines before, this will feel familiar, but lighter-weight.

Reading the audit log

One of the most practical outcomes of using hooks is the JSONL log. Each line is an event you can grep, archive, or feed into a later analysis step. For example:

{"ts":"2026-04-23T11:14:07Z","tool":"shell","args":{"command":"rg TODO src"},"rule":"allow-safe-read","decision":"allow"}
{"ts":"2026-04-23T11:14:24Z","tool":"shell","args":{"command":"git reset --hard"},"rule":"block-destructive-git","decision":"deny"}
{"ts":"2026-04-23T11:15:01Z","tool":"write_file","args":{"path":"../secrets.txt"},"rule":"block-parent-writes","decision":"deny"}

This is enough to answer questions that normally become guesswork: which command was attempted, when it happened, and which rule made the decision. For teams running repeated agent tasks, JSONL is also easier to process than ad hoc terminal output.

rg '"decision":"deny"' ./agentcheck.log.jsonl
jq -c 'select(.tool == "shell")' ./agentcheck.log.jsonl
tail -n 20 ./agentcheck.log.jsonl

Where hooks help most

The best use cases are narrow and operational:

If your main problem is “I need the agent to never touch production credentials,” hooks are useful because you can deny known access patterns and log every attempt. If your problem is “I want a proof that no harmful behavior is possible,” hooks alone are not enough. You still need environment isolation and permission boundaries outside the agent process.

Limitation: a hook system can only govern the operations it can see and classify. If a risky action is disguised as a previously allowed shell command, your rules need to be specific enough to catch it.

Practical workflow for a repo

A workable pattern is to start permissive, inspect the log, then tighten rules based on what the agent actually tries to do. In other words, do not design a perfect rule pack in the abstract. Let the session reveal its real command and file access patterns.

# 1. Run with logging first
agentcheck --rules ./rules-observe.yaml -- claude

# 2. Review actual tool usage
jq -r '.tool' ./agentcheck.log.jsonl | sort | uniq -c

# 3. Tighten the policy
cp rules-observe.yaml rules-enforce.yaml

# 4. Re-run with deny rules enabled
agentcheck --rules ./rules-enforce.yaml -- claude

This is usually more productive than starting with a highly restrictive file that blocks basic navigation, search, and read operations. The agent needs enough freedom to inspect the repo, but not enough freedom to cause irreversible damage.

What this tutorial does not solve

No serious claude code hooks tutorial should pretend hooks solve everything. They do not guarantee prompt quality. They do not stop data exfiltration if the allowed tool surface is too broad. They do not automatically understand business context. They are a strong operational layer for behavior governance, especially when paired with repository scoping, network limits, and reviewable logs.

They also introduce maintenance cost. Someone has to own the rule packs, revisit them when workflows change, and decide whether a deny event represents a real attack, an expected false positive, or a missing safe path. That tradeoff is normal. If you want control, you have to encode policy somewhere.

Next step: if you want to try this on a real Claude session, start with the GitHub repo, install the tool, and build a small rules file around the commands your team already allows. The project is here: github.com/paprika-org/agentcheck.