Hooks that fixed my agent's most annoying habits. Which one would you set up first?
Quick background if you haven't used them: hooks in Claude Code are your own scripts that run automatically at set points, like right before a command runs or right when the agent finishes its answer. A hook can block that step or send the answer back to be rewritten. CLAUDE.md is something the model reads and can forget after a long session. A hook just runs, every time.
Here are the ones I'd miss most. Each started as a rule in CLAUDE.md that the agent kept breaking.
• Replies drift into English after long sessions. A hook checks every answer and sends it back if it isn't in Japanese.
• It gets today's date or weekday wrong. Every prompt now comes with a two-week calendar, and any date in the answer is checked against it.
• It says "fixed" when it hasn't checked. Now it has to add a line saying how it verified it, or admit it didn't.
• It tells me to run commands myself. Now it has to explain why it can't do it, and usually it turns out it can.
• It runs things like DROP or rm -rf. Those get blocked before they run.
• It sends or publishes something before I've approved it. Also blocked.
• Its reports get long. Anything past a few lines goes back.
Which of these would you set up first? Or what's the thing your agent keeps doing that you'd want a hook for?
Replies
Mid-task, the agent lost context and rebuilt a helper that already existed two files away under a different name. Both versions worked, so tests passed and bloat slipped in. A pre-write hook checking new symbols against exports now forces a grep across existing utilities.
@konstantin_tikhaev That one's sneaky since the tests pass either way. Checking new names against what's already exported is a nice fix.
@hisashispace Forcing the model to read the matched utility file before allowing the write usually stops the duplicate. It saves a cleanup pass later.
@konstantin_tikhaev That's a neat way to do it. The read happens before the write instead of a cleanup after. Thanks for sharing ☺️
For me the first one would be a git hook, not any on your list. Several agent sessions work in my repo at once, and one of them ran a plain commit that took everything in the index, including a rename and a deletion another session had staged. Main didn't build until the next commit. Today that's a rule in my notes: stage files by name, commit by path, never add everything. It's exactly the kind of rule that should be a hook instead, a check before any git command that blocks "add ." and a commit without paths. Second would be your "fixed without checking" one. My notes ask the agent to analyze or run what it changed, and a forced line about how it verified would make skipping that visible.
@gbasovv A plain commit sweeping up what another session staged is a nasty one, and yeah, that rule belongs in a hook. I run several sessions in parallel too, so I get it. The "fixed without checking" one is the one I'd keep no matter what. It makes skipping the check visible right in the answer.
The rm -rf / DROP one first, since it's the only one where a miss can't be undone. One thing we learned writing Claude Code hooks: a hook can replace a successful tool result, but not the output of a failed command. So if a hook is there to stop something, test the failure path too, not only the happy path. Also, the Stop event only hands the hook the last text block of the turn, so a "did you verify it" check won't see anything said earlier in that turn.
@mertcansaglam Agreed, the ones you can't undo come first. Good point on testing the failure path. Before I trust a new hook, I deliberately do the thing it's meant to block and check that it actually stops. Thanks for the tip on Stop only seeing the last text block, that's useful to know.
Publish block for me too. Everything else on the list I can clean up after, that one I can't.
The thing I'd add is MCP tools. One server I use changed its tools six times after I'd approved it, and the agent never noticed any of it. So now a hook checks each tool against the version I said yes to and stops the call if it's changed.
@neelagiri_chettiyar That one I hadn't thought about. I moved most of my MCP servers to a command that only starts when it's called, and a hook runs the same safety checks on it, but nothing watches for a tool changing underneath me. Pinning the version you said yes to makes a lot of sense.