Live
Black Hat USADark ReadingBlack Hat AsiaAI BusinessChinese firms trail US peers in AI adoption due to corporate culture: ex-OpenAI executiveSCMP Tech (Asia AI)Chinese AI Firms Track US Troop Movements in Iran War - Kyiv PostGNews AI China14. Observability in AI Systems – How to Know What Your AI Is Actually DoingMedium AIAI Citation Registries and Provenance Absence Failure ModesDev.to AIGitHub Actions for AI: Automating NeuroLink in Your CI/CD PipelineDev.to AIWorld-Building with Persistence: Narrative Layers in AI AgentsDev.to AIBuilding a Claude Agent with Persistent Memory in 30 MinutesDev.to AISamsung, Mistral AI in talks for stable chip supply - NewsBytesGNews AI Mistral50 Useful Prompts I Use in Gemini That Actually Save Me TimeMedium AIThe Two-Line Prompt That Made 7 AIs Develop Distinct PersonalitiesMedium AINLP Token Classification Explained Simply (NER, POS, Chunking + Code)Medium AIAutomate Your Grant Workflow: A Practical AI Guide for NonprofitsDev.to AIBlack Hat USADark ReadingBlack Hat AsiaAI BusinessChinese firms trail US peers in AI adoption due to corporate culture: ex-OpenAI executiveSCMP Tech (Asia AI)Chinese AI Firms Track US Troop Movements in Iran War - Kyiv PostGNews AI China14. Observability in AI Systems – How to Know What Your AI Is Actually DoingMedium AIAI Citation Registries and Provenance Absence Failure ModesDev.to AIGitHub Actions for AI: Automating NeuroLink in Your CI/CD PipelineDev.to AIWorld-Building with Persistence: Narrative Layers in AI AgentsDev.to AIBuilding a Claude Agent with Persistent Memory in 30 MinutesDev.to AISamsung, Mistral AI in talks for stable chip supply - NewsBytesGNews AI Mistral50 Useful Prompts I Use in Gemini That Actually Save Me TimeMedium AIThe Two-Line Prompt That Made 7 AIs Develop Distinct PersonalitiesMedium AINLP Token Classification Explained Simply (NER, POS, Chunking + Code)Medium AIAutomate Your Grant Workflow: A Practical AI Guide for NonprofitsDev.to AI
AI NEWS HUBbyEIGENVECTOREigenvector

Claude Code hooks: auto-format, auto-test, and self-heal on every save

Dev.to AIby brian austinApril 5, 20265 min read0 views
Source Quiz

Claude Code hooks: auto-format, auto-test, and self-heal on every save Claude Code hooks let you run shell commands automatically before or after Claude uses a tool. This means you can build a fully automated loop: Claude edits a file → your linter runs → tests execute → if something breaks, Claude fixes it. Here's exactly how to set it up. What hooks actually do Hooks fire at specific events in Claude Code's tool execution lifecycle: PreToolUse — runs before Claude uses a tool (read file, write file, bash, etc.) PostToolUse — runs after Claude uses a tool Stop — runs when Claude finishes a turn Notification — runs when Claude sends you a notification You configure them in ~/.claude/settings.json (global) or .claude/settings.json (project). The auto-format hook This hook runs Prettier auto

Claude Code hooks: auto-format, auto-test, and self-heal on every save

Claude Code hooks let you run shell commands automatically before or after Claude uses a tool. This means you can build a fully automated loop: Claude edits a file → your linter runs → tests execute → if something breaks, Claude fixes it.

Here's exactly how to set it up.

What hooks actually do

Hooks fire at specific events in Claude Code's tool execution lifecycle:

  • PreToolUse — runs before Claude uses a tool (read file, write file, bash, etc.)

  • PostToolUse — runs after Claude uses a tool

  • Stop — runs when Claude finishes a turn

  • Notification — runs when Claude sends you a notification

You configure them in ~/.claude/settings.json (global) or .claude/settings.json (project).

The auto-format hook

This hook runs Prettier automatically whenever Claude writes a file:

{  "hooks": {  "PostToolUse": [  {  "matcher": "Write",  "hooks": [  {  "type": "command",  "command": "prettier --write $CLAUDE_TOOL_INPUT_FILE_PATH 2>/dev/null || true"  }  ]  }  ]  } }

Enter fullscreen mode

Exit fullscreen mode

$CLAUDE_TOOL_INPUT_FILE_PATH is the environment variable Claude Code sets to the file path being written. Every time Claude edits a file, Prettier runs on it automatically. No manual formatting.

The auto-test hook

Run your test suite after every file write:

{  "hooks": {  "PostToolUse": [  {  "matcher": "Write",  "hooks": [  {  "type": "command",  "command": "npm test --silent 2>&1 | tail -5"  }  ]  }  ]  } }

Enter fullscreen mode

Exit fullscreen mode

Claude sees the test output. If tests fail, it knows immediately and can fix the file before moving to the next task.

Combining format + test + auto-fix

Here's the full self-healing loop:

{  "hooks": {  "PostToolUse": [  {  "matcher": "Write",  "hooks": [  {  "type": "command",  "command": "prettier --write $CLAUDE_TOOL_INPUT_FILE_PATH 2>/dev/null || true && npm test --silent 2>&1 | tail -10"  }  ]  }  ]  } }

Enter fullscreen mode

Exit fullscreen mode

The flow:

  • Claude writes a file

  • Prettier formats it automatically

  • Tests run

  • If tests fail, Claude sees the output in its context

  • Claude fixes the failure

  • Repeat until tests pass

This turns Claude Code into a proper TDD loop without you manually running anything.

The .env blocker hook

Prevent Claude from reading your secrets file:

{  "hooks": {  "PreToolUse": [  {  "matcher": "Read",  "hooks": [  {  "type": "command",  "command": "if [[ $CLAUDE_TOOL_INPUT_FILE_PATH == *'.env'* ]]; then echo 'BLOCKED: .env files are not accessible to Claude'; exit 1; fi"  }  ]  }  ]  } }

Enter fullscreen mode

Exit fullscreen mode

Exit code 1 from a PreToolUse hook cancels the tool call. Claude gets the error message in its context and won't try again.

The ESLint hook (with auto-fix)

{  "hooks": {  "PostToolUse": [  {  "matcher": "Write",  "hooks": [  {  "type": "command",  "command": "eslint --fix $CLAUDE_TOOL_INPUT_FILE_PATH 2>&1 | head -20"  }  ]  }  ]  } }

Enter fullscreen mode

Exit fullscreen mode

--fix auto-corrects fixable lint errors. The remaining unfixable ones get printed to stdout, which Claude sees and can address manually.

The Stop hook — run after every Claude turn

Stop hooks fire when Claude finishes responding. Useful for running a full test suite at the end of a session:

{  "hooks": {  "Stop": [  {  "hooks": [  {  "type": "command",  "command": "npm test 2>&1 | tail -20"  }  ]  }  ]  } }

Enter fullscreen mode

Exit fullscreen mode

This gives you a final health check at the end of every Claude Code session.

Combining everything in one settings file

{  "hooks": {  "PreToolUse": [  {  "matcher": "Read",  "hooks": [  {  "type": "command",  "command": "if [[ $CLAUDE_TOOL_INPUT_FILE_PATH == *'.env'* ]]; then echo 'BLOCKED: cannot read .env'; exit 1; fi"  }  ]  }  ],  "PostToolUse": [  {  "matcher": "Write",  "hooks": [  {  "type": "command",  "command": "prettier --write $CLAUDE_TOOL_INPUT_FILE_PATH 2>/dev/null || true && eslint --fix $CLAUDE_TOOL_INPUT_FILE_PATH 2>&1 | head -5 || true && npm test --silent 2>&1 | tail -10"  }  ]  }  ],  "Stop": [  {  "hooks": [  {  "type": "command",  "command": "echo 'Session complete. Running final test suite...' && npm test 2>&1 | tail -20"  }  ]  }  ]  } }

Enter fullscreen mode

Exit fullscreen mode

Why this matters for rate limits

The self-healing loop means Claude finishes tasks faster with fewer back-and-forth turns. Fewer turns = fewer tokens = less chance of hitting rate limits mid-session.

If you're hitting Claude Code rate limits anyway, the pattern is to route through a proxy that removes the limit entirely:

export ANTHROPIC_BASE_URL=https://simplylouie.com

Enter fullscreen mode

Exit fullscreen mode

Set that in your shell profile and claude uses it automatically. Rate limits gone. ✌️$2/month at simplylouie.com.

The matcher field

The matcher field accepts any Claude Code tool name:

  • Write — file writes

  • Read — file reads

  • Bash — bash commands

  • Edit — file edits (alternative to Write in some versions)

  • Leave it empty to match all tools

Summary

  • PostToolUse Write = auto-format + auto-test after every file edit

  • PreToolUse Read = block sensitive files before Claude reads them

  • Stop = full suite at end of session

  • $CLAUDE_TOOL_INPUT_FILE_PATH = the file being written (use in your hook commands)

  • Exit code 1 in PreToolUse = cancels the tool call

Hooks turn Claude Code from a chat interface into a proper CI loop running in your terminal. Once configured, you stop running lint and tests manually entirely.

Was this article helpful?

Sign in to highlight and annotate this article

AI
Ask AI about this article
Powered by Eigenvector · full article context loaded
Ready

Conversation starters

Ask anything about this article…

Daily AI Digest

Get the top 5 AI stories delivered to your inbox every morning.

Knowledge Map

Knowledge Map
TopicsEntitiesSource
Claude Code…claudeversionglobalinterfaceclaude codeDev.to AI

Connected Articles — Knowledge Graph

This article is connected to other articles through shared AI topics and tags.

Knowledge Graph100 articles · 186 connections
Scroll to zoom · drag to pan · click to open

Discussion

Sign in to join the discussion

No comments yet — be the first to share your thoughts!