5 Claude Code Hooks That Automate My Entire Workflow
<p>Claude Code hooks are shell commands that fire at specific lifecycle events. They're configured in <code>.claude/settings.json</code> and run automatically — no manual intervention.</p> <p>I use 5 hooks across all my projects. Here's each one with the exact config.</p> <h2> 1. Auto-Format After Every File Write </h2> <p>Every time Claude writes or edits a file, Prettier runs on it automatically.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight json"><code><span class="p">{</span><span class="w"> </span><span class="nl">"hooks"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"PostToolUse"</span><span class="p">:</span><span class="w"> </span><span class="p">[{</span><span class="w"> </span><spa
Claude Code hooks are shell commands that fire at specific lifecycle events. They're configured in .claude/settings.json and run automatically — no manual intervention.
I use 5 hooks across all my projects. Here's each one with the exact config.
1. Auto-Format After Every File Write
Every time Claude writes or edits a file, Prettier runs on it automatically.
{ "hooks": { "PostToolUse": [{ "matcher": "Write|Edit", "hooks": [{ "type": "command", "command": "jq -r '.tool_response.filePath // .tool_input.file_path' | { read -r f; prettier --write \"$f\"; } 2>/dev/null || true" }] }] } }{ "hooks": { "PostToolUse": [{ "matcher": "Write|Edit", "hooks": [{ "type": "command", "command": "jq -r '.tool_response.filePath // .tool_input.file_path' | { read -r f; prettier --write \"$f\"; } 2>/dev/null || true" }] }] } }Enter fullscreen mode
Exit fullscreen mode
Why: Claude's code formatting is inconsistent. Sometimes it uses 2 spaces, sometimes 4. Prettier fixes it instantly. The || true ensures the hook never blocks Claude if Prettier fails.
2. Inbox Check on Every Message
Every time I send a message, Claude checks its message bus inbox for tasks from other agents.
{ "hooks": { "UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": "~/.claude/bus/check-inbox.sh my-agent" }] }] } }{ "hooks": { "UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": "~/.claude/bus/check-inbox.sh my-agent" }] }] } }Enter fullscreen mode
Exit fullscreen mode
The script checks if ~/.claude/bus/inbox-my-agent.md has content. If yes, it prints the messages. Claude sees them and acts on them before responding to my message.
Why: This is how my 6 agents communicate. No polling interval — messages are delivered the moment I interact with any agent.
3. Session Context on Start
When a new session starts, automatically load the last session's state.
{ "hooks": { "SessionStart": [{ "hooks": [{ "type": "command", "command": "cat docs/SESSION-HANDOFF.md 2>/dev/null && echo '---' && cat .claude/rules/known-issues.md 2>/dev/null || true" }] }] } }{ "hooks": { "SessionStart": [{ "hooks": [{ "type": "command", "command": "cat docs/SESSION-HANDOFF.md 2>/dev/null && echo '---' && cat .claude/rules/known-issues.md 2>/dev/null || true" }] }] } }Enter fullscreen mode
Exit fullscreen mode
Why: Claude reads the handoff file and known issues automatically. No need to say "read the handoff file" at the start of every session. It just knows what happened last time.
4. Reminder Before Session Ends
When Claude stops (session end, context limit, or user stops), remind it to save state.
{ "hooks": { "Stop": [{ "hooks": [{ "type": "command", "command": "echo '{\"systemMessage\": \"REMINDER: Update docs/SESSION-HANDOFF.md with what was done, what failed, and what is next.\"}'" }] }] } }{ "hooks": { "Stop": [{ "hooks": [{ "type": "command", "command": "echo '{\"systemMessage\": \"REMINDER: Update docs/SESSION-HANDOFF.md with what was done, what failed, and what is next.\"}'" }] }] } }Enter fullscreen mode
Exit fullscreen mode
Why: Without this, Claude often stops mid-task without saving context. The reminder fires every time, so the handoff file stays current.
5. Protect Critical Files
Before any edit to protected files, block the write.
{ "hooks": { "PreToolUse": [{ "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": "jq -r '.tool_input.file_path // \"\"' | grep -qE '(\\.env|credentials|secrets)' && echo '{\"decision\": \"block\", \"reason\": \"Cannot edit sensitive files\"}' || true" }] }] } }{ "hooks": { "PreToolUse": [{ "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": "jq -r '.tool_input.file_path // \"\"' | grep -qE '(\\.env|credentials|secrets)' && echo '{\"decision\": \"block\", \"reason\": \"Cannot edit sensitive files\"}' || true" }] }] } }Enter fullscreen mode
Exit fullscreen mode
Why: Claude occasionally tries to write API keys or modify .env files. This hook blocks any edit to files matching sensitive patterns. It's a safety net you set once and forget.
The Hook Lifecycle
Here's when each hook type fires:
Event When Use case
SessionStart
New session begins
Load context
UserPromptSubmit
You send a message
Check inbox
PreToolUse
Before a tool runs
Block dangerous actions
PostToolUse
After a tool succeeds
Format code, run tests
Stop
Session ends
Save state
PreCompact
Before context compression
Preserve critical info
Setting It Up
All hooks go in .claude/settings.json (project-level) or ~/.claude/settings.json (global).
# Create project settings mkdir -p .claude cat > .claude/settings.json << 'EOF' { "hooks": { "PostToolUse": [{ "matcher": "Write|Edit", "hooks": [{ "type": "command", "command": "echo 'file written'" }] }] } } EOF# Create project settings mkdir -p .claude cat > .claude/settings.json << 'EOF' { "hooks": { "PostToolUse": [{ "matcher": "Write|Edit", "hooks": [{ "type": "command", "command": "echo 'file written'" }] }] } } EOFEnter fullscreen mode
Exit fullscreen mode
Start simple. Add one hook. Verify it fires. Then add more.
Common Mistakes
-
Missing || true — If your hook command fails, it can block Claude entirely. Always add || true unless you intentionally want to block on failure.
-
Hooks that are too slow — Hooks run synchronously by default. A hook that takes 10 seconds runs on every single tool call. Keep them fast.
-
Forgetting the JSON output format — Stop hooks need to output JSON with systemMessage field. Plain text won't show up.
These 5 hooks run across 16 projects. Once set up, they work silently in the background. The best automation is the kind you forget exists.
Sign in to highlight and annotate this article

Conversation starters
Daily AI Digest
Get the top 5 AI stories delivered to your inbox every morning.
Knowledge Map
Connected Articles — Knowledge Graph
This article is connected to other articles through shared AI topics and tags.
More in Releases

NeutronX Appoints Former Adobe Enterprise Architect Focused on AI, Data Governance, and High-Speed API Edge Processing for Large-Scale Data Systems to Advance Next-Generation Infrastructure - Yahoo Finance
NeutronX Appoints Former Adobe Enterprise Architect Focused on AI, Data Governance, and High-Speed API Edge Processing for Large-Scale Data Systems to Advance Next-Generation Infrastructure Yahoo Finance

Same Model, Different Environment, Different Results
Same Model, Different Environment, Different Results I've been running the same foundation model in two different environments for the same project for several months. Not different models — the same one. Same underlying weights, same training, same capabilities. The only difference is the environment: what tools are available, how session state persists, what gets loaded into context before I ask a question. The outputs are systematically different. Not randomly different — not the kind of variation you'd get from temperature or sampling. Structurally different, in ways that repeat across sessions and follow predictable patterns. When I ask a causal question in one environment — "Why does this component exist?" — I get back a dependency chain. Clean, correct, verifiable against stored dat

I stopped managing translations manually (and built this instead)
Managing multilingual content has always felt… wrong to me. In most projects, it quickly turns into: duplicated fields ( title_en , title_fr ) messy i18n JSON files constant synchronization issues At some point, I started wondering: why is this even a developer problem? Rethinking the approach Instead of treating translations as something external (keys, files, etc.), I tried a different approach: What if multilingual support was part of the data model itself? So I built a small Airtable-like system where fields are multilingual by design. You write content once, and it becomes available in multiple languages automatically. Example: Title: "Hello world" → fr: Bonjour le monde → es: Hola mundo No keys. No duplication. No sync issues. How it works Each field stores multiple language versions





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