cd /news/ai-tools/hooks-in-claude-code-a-beginner-s-gu… · home topics ai-tools article
[ARTICLE · art-134602] src=dev.to ↗ pub= topic=ai-tools verified=true sentiment=· neutral

Hooks in Claude Code: A Beginner's Guide

A developer published a beginner's guide to hooks in Claude Code, showing how scripts can run at lifecycle moments such as PreToolUse and PostToolUse to enforce custom logic beyond permission rules. The guide walks through a PreToolUse hook on git push that blocks the push when changed source files lack a matching changed test file, and notes the approach only governs pushes made inside a Claude Code session, not manual or third-party Git pushes.

by read3 min views1 publishedSep 19, 2026

Your .claude/settings.json can ask before git push — but that's where a permission's job ends. It's a gate, not a worker: it can't format a file the moment it's edited, catch a missing test before code ships, or keep a log of what actually happened in a session. None of that is "is this call allowed" — it's "let me run my own logic around this event, whatever that logic needs to do."

That's where hooks come in: they let you run your own code at specific points in Claude Code's workflow — for security checks, sure, but just as often for formatting, automation, or just keeping a record.

That's the gap a hook fills — not "does this call match a permission rule?" but "inspect what's actually happening, then decide what to do about it."

A hook is a script that runs automatically at a specific moment in Claude's workflow — not a declarative pattern match, but code you write that checks whatever it needs to check, then acts on what it finds.

Claude Code fires hooks at specific moments. Two matter most starting out:

PreToolUse`` PostToolUse One of the jobs a hook can take on — a PreToolUse hook on git push that checks whether the files being pushed have a matching test file changed alongside them. For this example, assume the project follows a simple convention: books.ts has a corresponding books.test.ts (same books endpoint from the earlier articles) — real projects vary (.spec.ts, BookServiceTests.cs, test_books.py), so this is a toy heuristic demonstrating what a hook can do, not a general-purpose test-verification system.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash(git push *)",
        "hooks": [
          { "type": "command", "command": "./scripts/check-tests-before-push.sh" }
        ]
      }
    ]
  }
}

check-tests-before-push.sh diffs the push against the base branch, lists the changed source files, and for each one checks whether a matching test file also changed. If a source file changed with no matching test file touched, the script exits non-zero — which causes the PreToolUse hook to prevent the push.

📄 Full script: https://github.com/Alimurrazi/claude-demo-book-api/blob/master/scripts/check-tests-before-push.sh

It's a nudge, not a guarantee — it only checks that a test file changed, not that it actually covers the new logic. A real coverage guarantee would mean running your suite with a coverage tool instead; skipped here for simplicity.

⚠️ This only governs Claude, not everyone else. A PreToolUse hook fires only on a push when Claude Code session runs itself — not a manual push, not one from a third-party Git client (GitHub Desktop, GitKraken etc.), and not from another agent. It's not a real git hook like .git/hooks/pre-push; for simplicity, this article skips setting that up, so anything outside Claude Code stays unchecked.

Lives in the same .claude/settings.json as your permission rules — hooks and permissions share the file, they just do different jobs inside it.

The test-before-push check is one example, not the whole picture. Once a hook can run any script, at any lifecycle moment, a lot opens up:

git push, running npm run build and blocking the push if it failsEdit, scanning new content for anything that looks like a real API key before it lands Same mechanism throughout — a script, a lifecycle moment, and some custom logic. Only the script changes. And it's not only about blocking, either — a hook can just as easily run automation, format a file, or collect information, the way the bullets above show.

A hook runs unconditionally, every time it matches — there's no judgment call, which is exactly the point, but also the risk. A hook with a bug can block legitimate work just as easily as it blocks a real problem, and it'll do it silently unless you test it.

Run claude --debug while you're writing a new hook, before you trust it in a real session.

Want more — the full event list, hooks that modify a call instead of just blocking it, or chaining multiple hooks? Worth its own deep-dive later; this is what you need to get started.

📦 Full example: https://github.com/Alimurrazi/claude-demo-book-api/tree/article-3-hooks

Previously in this series:

── more in #ai-tools 4 stories · sorted by recency
── more on @claude code 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/hooks-in-claude-code…] indexed:0 read:3min 2026-09-19 ·