DEV Community

techpotions
techpotions

Posted on • Originally published at techpotions.com

Claude Code hooks: the guardrails I wish I'd set up on day one

Our CLAUDE.md said "always use pnpm" for three months. Claude complied, usually. The day it didn't, the lockfile grew npm artifacts mid-sprint and nobody noticed until CI did.

Instructions are advice. Hooks are law. A hook is a script Claude Code runs at fixed points in its loop, and when a PreToolUse hook says deny, the command does not run. Ever.

The insight that changes how you write them

A hook's deny reason is fed straight back into Claude's context. So a good hook doesn't just say no, it says why:

pnpm-guard: this repo is managed by pnpm (found pnpm-lock.yaml). Re-run the equivalent command with pnpm so the lockfile stays consistent.

Claude reads that and immediately retries with pnpm. No failed session, no human intervention. Write your deny reasons for the model, not for the log file.

The contract in 20 seconds

Your script gets JSON on stdin (tool_name, tool_input with the exact command or file content, cwd, session_id). It answers on stdout:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "why, written for the model"
  }
}
Enter fullscreen mode Exit fullscreen mode

Exit 2 with a reason on stderr is the shorthand version.

Stop hooks are underrated quality gates

Stop hooks fire when Claude finishes a turn, and they can answer {"decision": "block", "reason": "..."} to refuse the handover. Our typecheck-gate runs tsc --noEmit at turn end and bounces the errors back to Claude, so it fixes its own type breakage before yielding to a human.

One hard-won warning: check the stop_hook_active flag on stdin before blocking, or a persistent failure loops forever.

The eight we run on client work

  • secret-shield: blocks commands and file writes containing API keys, tokens, private keys, credentialed connection strings
  • danger-blocker: rm -rf /, force-push to main, DROP DATABASE, disk writes, fork bombs
  • pnpm-guard: reads the lockfile, rejects the wrong package manager
  • dev-server-guard: the agent never starts npm run dev in its own shell
  • coauthor-scrubber: rejects commits carrying the Co-Authored-By trailer
  • typecheck-gate: the Stop-hook quality gate above
  • format-on-write: Prettier on touched files, only in repos with a config
  • turn-timer: shows how long each response took

All eight are plain dependency-free Node, MIT licensed, and downloadable with the settings.json snippets here: https://techpotions.com/products/claude-code-hooks

No signup, no email gate. Take one script or the whole plugin. And if you run hooks of your own, I'm genuinely collecting ideas: what belongs in v2?

Top comments (0)