Claude Code Hooks: How to Auto-Format, Lint, and Test on Every Save
Configure hooks in .claude/settings.json to run prettier, eslint, and tests automatically, ensuring clean code without manual intervention. Claude Code Hooks: How to Auto-Format, Lint, and Test on Every Save Claude Code hooks are your automation layer for agentic development. They let you run shell commands at specific points in Claude's workflow—before tools run, after files are written, or when sessions end. Most developers discover hooks when they're tired of Claude writing code that doesn't match their formatter settings. Here's how to stop that permanently. Where Hooks Live Hooks go in your CLAUDE.md or in .claude/settings.json at the project root: { "hooks" : { "afterFileWrite" : "prettier --write $FILE" , "afterSessionEnd" : "npm test -- --passWithNoTests" } } The $FILE variable con
Configure hooks in .claude/settings.json to run prettier, eslint, and tests automatically, ensuring clean code without manual intervention.
Claude Code Hooks: How to Auto-Format, Lint, and Test on Every Save
Claude Code hooks are your automation layer for agentic development. They let you run shell commands at specific points in Claude's workflow—before tools run, after files are written, or when sessions end. Most developers discover hooks when they're tired of Claude writing code that doesn't match their formatter settings. Here's how to stop that permanently.
Where Hooks Live
Hooks go in your CLAUDE.md or in .claude/settings.json at the project root:
{ "hooks": { "afterFileWrite": "prettier --write $FILE", "afterSessionEnd": "npm test -- --passWithNoTests" } }{ "hooks": { "afterFileWrite": "prettier --write $FILE", "afterSessionEnd": "npm test -- --passWithNoTests" } }Enter fullscreen mode
Exit fullscreen mode
The $FILE variable contains the path of the file Claude just wrote. $SESSION_ID is also available for session-based logging.
The Four Hook Points You Need to Know
Hook When it fires Common use
beforeToolRun
Before any tool executes
Log what Claude is about to do
afterFileWrite
After Claude writes or edits a file
Format, lint, type-check
afterBashRun
After a bash command completes
Capture output, trigger CI
afterSessionEnd
When the session closes
Run test suite, commit
Auto-Format on Write (The Essential Hook)
This is the most common hook. Claude writes a file → it gets formatted immediately:
{ "hooks": { "afterFileWrite": "npx prettier --write $FILE 2>/dev/null || true" } }{ "hooks": { "afterFileWrite": "npx prettier --write $FILE 2>/dev/null || true" } }Enter fullscreen mode
Exit fullscreen mode
The || true prevents Claude from seeing a non-zero exit code as an error. If the file isn't JS/TS/CSS, prettier skips it silently.
Chain Linting with Formatting
You can only have one afterFileWrite hook per settings block. Chain multiple commands with ; or &&:
{ "hooks": { "afterFileWrite": "npx prettier --write $FILE 2>/dev/null; npx eslint --fix $FILE 2>/dev/null; true" } }{ "hooks": { "afterFileWrite": "npx prettier --write $FILE 2>/dev/null; npx eslint --fix $FILE 2>/dev/null; true" } }Enter fullscreen mode
Exit fullscreen mode
This runs prettier first, then eslint with auto-fix. The final true ensures the hook always exits with code 0.
Auto-Test After Session
Run your test suite every time Claude finishes a session:
{ "hooks": { "afterSessionEnd": "npm test -- --passWithNoTests 2>&1 | tail -20" } }{ "hooks": { "afterSessionEnd": "npm test -- --passWithNoTests 2>&1 | tail -20" } }Enter fullscreen mode
Exit fullscreen mode
Claude sees the test output and can immediately fix failures in the next session. The tail -20 shows just the last 20 lines of output to avoid overwhelming the context window.
The Self-Healing Loop Pattern
This is where hooks get powerful. Combine afterSessionEnd with a script that reruns Claude if tests fail:
#!/bin/bash
run-with-tests.sh
npm test 2>&1 > /tmp/test-output.txt
if [ $? -ne 0 ]; then
echo "Tests failed, asking Claude to fix..."
claude --print "Fix the failing tests. Output:\n$(cat /tmp/test-output.txt)"
--allowedTools Edit,Bash
fi`
Enter fullscreen mode
Exit fullscreen mode
Then set:
{ "hooks": { "afterSessionEnd": "./run-with-tests.sh" } }{ "hooks": { "afterSessionEnd": "./run-with-tests.sh" } }Enter fullscreen mode
Exit fullscreen mode
This creates a loop: Claude writes code → session ends → tests run → if red, Claude is called again with failure output → it fixes → tests run again. The loop continues until green.
Watch Out for Rate Limits
The self-healing loop is great until Claude hits its hourly rate limit mid-loop. When that happens, the loop stalls and your terminal hangs. One workaround mentioned in the community is using a proxy service like SimplyLouie ($2/month) that removes rate limits:
export ANTHROPIC_BASE_URL=https://simplylouie.com/api/proxy npm run claude-loopexport ANTHROPIC_BASE_URL=https://simplylouie.com/api/proxy npm run claude-loopEnter fullscreen mode
Exit fullscreen mode
Audit What Claude Does
If you want a log of every tool Claude runs:
{ "hooks": { "beforeToolRun": "echo \"$(date): $TOOL_NAME $TOOL_INPUT\" >> ~/.claude-audit.log" } }{ "hooks": { "beforeToolRun": "echo \"$(date): $TOOL_NAME $TOOL_INPUT\" >> ~/.claude-audit.log" } }Enter fullscreen mode
Exit fullscreen mode
This creates an append-only audit log at ~/.claude-audit.log. Useful for understanding what Claude actually does in long sessions.
Hooks vs CLAUDE.md: They Work Best Together
Hooks handle behavior (what happens after actions). CLAUDE.md handles knowledge (what Claude should know and follow).
They work best together:
-
CLAUDE.md: "Always use Prettier for formatting, never manual spaces"
-
Hook: afterFileWrite: prettier --write $FILE
CLAUDE.md tells Claude the rule. The hook enforces it automatically even if Claude forgets.
Start Here
Begin with the formatter hook. Once you've run a session without manually formatting a single file, add linting. Then testing. The combination creates a development environment where Claude's output is always clean, linted, and tested—without you lifting a finger.
Originally published on gentic.news
Dev.to AI
https://dev.to/gentic_news/claude-code-hooks-how-to-auto-format-lint-and-test-on-every-save-54m2Sign in to highlight and annotate this article

Conversation starters
Daily AI Digest
Get the top 5 AI stories delivered to your inbox every morning.
More about
claudeavailableservicetrunk/5e79c7376a212f6abc628dc596ddec1fcf67e1cb: Update third_party/kineto submodule to 4826a43 (#179492)
Includes the following commits: Remove duplicate test ignore ( pytorch/kineto#1328 ) 4826a43 Ensure that async doesn't loop while sync is active ( pytorch/kineto#1327 ) 37fada9 Authored with Claude. Pull Request resolved: #179492 Approved by: https://github.com/ryanzhang22

Tested TurboQuant KV compression with Gemma 4 31B — 5.80x compression, perfect long-context recall, JSON output preserved
Quick experiment: I implemented Google Research's TurboQuant paper (arXiv 2504.19874) as a Python package and tested it with Google's brand new Gemma 4 31B model. The results exceeded the paper's claims. Setup: Hardware: RTX PRO 6000 Blackwell (96GB VRAM) Model: google/gemma-4-31B-it (BF16, 64 layers) Compression: TurboQuant turbo3 (3-bit PolarQuant + QJL residual) Backend: HuggingFace Transformers with custom past_key_values interception Compression results: Metric Value FP16 baseline (222 tokens) 218.23 MB TurboQuant compressed 37.62 MB Compression ratio 5.80x Paper theoretical (turbo3) 4.9x The 5.80x exceeds the paper's 4.9x claim — likely because Gemma 4's architecture has fewer outliers in the K/V distributions than the synthetic data used in the paper. Functional validation: Multi-tu

Version Control System for Data with MatrixOne
arXiv:2604.03927v1 Announce Type: new Abstract: The rapid advancement of artificial intelligence has elevated data to a cornerstone of modern software systems. As data projects become increasingly complex and dynamic, version control for data has become essential rather than merely convenient. Existing version control systems designed for source code are inadequate for large-scale data management, as they often require loading entire datasets into memory for diff and merge operations. Database systems, while providing robust data management capabilities, lack native support for version control operations such as diff and merge between data forks. We present a version control system for data implemented in MatrixOne, a cloud-native relational database system. Our system leverages MatrixOne'
Knowledge Map
Connected Articles — Knowledge Graph
This article is connected to other articles through shared AI topics and tags.
More in Products
trunk/17247bdcbbdacb333a1f28519a632823573bb787: [ROCm] simplify unrolling by leveraging compiler (#177697)
A recent change to LLVM ( llvm/llvm-project#181241 ) enables loop unrolling for loops with runtime-known loop count even when the trip count expression is expensive. This is the case for simple HIP loops based on the "blockDim.x" expression, which translates to a complex LLVM IR expression. With this change, we can simply obtain loop unrolling via compiler transformation using "pragma unroll" and without the need of hand-written specializations of the code. Therefore, this patch simplifies the pytorch code base across different targets, as much as possible given different compilation toolchains. Pull Request resolved: #177697 Approved by: https://github.com/jeffdaily , https://github.com/pruthvistony

Uniform Sampling of Proper Graph Colorings via Soft Coloring and Partial Rejection Sampling
arXiv:2604.03947v1 Announce Type: new Abstract: We present a new algorithm for the exact uniform sampling of proper \(k\)-colorings of a graph on \(n\) vertices with maximum degree~\(\Delta\). The algorithm is based on partial rejection sampling (PRS) and introduces a soft relaxation of the proper coloring constraint that is progressively tightened until an exact sample is obtained. Unlike coupling from the past (CFTP), the method is inherently parallelizable. We propose a hybrid variant that decomposes the global sampling problem into independent subproblems of size \(O(\log n)\), each solved by any existing exact sampler. This decomposition acts as a {\em complexity reducer}: it replaces the input size~\(n\) with \(O(\log n)\) in the component solver's runtime, so that any improvement in

Making Prompts First-Class Citizens for Adaptive LLM Pipelines
arXiv:2508.05012v2 Announce Type: replace Abstract: Modern LLM pipelines increasingly resemble complex data-centric applications: they retrieve data, correct errors, call external tools, and coordinate interactions between agents. Yet, the central element controlling this entire process -- the prompt -- remains a brittle, opaque string that is entirely disconnected from the surrounding program logic. This disconnect fundamentally limits opportunities for reuse, optimization, and runtime adaptivity. In this paper, we describe our vision and an initial design of SPEAR (Structured Prompt Execution and Adaptive Refinement), a new approach to prompt management that treats prompts as first-class citizens in the execution model. Specifically, SPEAR enables: (1) structured prompt management, with



Discussion
Sign in to join the discussion
No comments yet — be the first to share your thoughts!