Stop Hooks in Claude Code: Verify Before “Done”
Add a small Stop Hook check to Claude Code, verify tests or files with an exit code, and keep secrets out of local logs.
Stop Hooks in Claude Code: Verify Before You Say “Done”
Claude Code can stop after producing a plausible answer. A Stop Hook gives the project one small, repeatable check before that stop: run a test, inspect a file, or validate the working tree. It does not replace CI or prove that every requirement is correct.
Stop Hook, CLAUDE.md, and CI have different jobs
A Stop Hook runs the short command at the end of a turn. CLAUDE.md tells the agent which command and boundary to follow but does not execute anything. CI runs independently after push or pull request and remains the final gate.
Pick one claim and one signal
Write down the claim you want to verify, the command that proves it, and the exit code that means success. For a Node project, the contract might be: “the focused tests pass and dist/app.js exists.” Start with a read-only or local check. Do not put deploys, publishing, or external writes in a hook that runs at every stop.
Configure a minimal hook
Create .claude/settings.json with a Stop command hook and a short timeout:
Keep the script explicit:
Run the script manually first, make it executable, and replace the commands with the ones that exist in your repository. Then verify the workflow:
- Break the expected file name and run
printf '%s\n' '{"stop_hook_active":false}' | ./scripts/check-before-stop.sh; echo $?; expect stderr and code2. - Restore the path and repeat the command; expect code
0. - Ask Claude Code to complete a small task and confirm that exit
2prevents Stop and sends the short stderr reason back to Claude. - With the check still broken, pipe
{"stop_hook_active":true}into the script; expect asystemMessageJSON object and code0. Then restore the check and confirm normal exit0. Ifstop_hook_activeis true, the sample fails open after one remediation attempt: it returns0with asystemMessagefor the operator, preventing an endless loop while CI remains required.
Log only the check name and exit result. Never print API keys, .env contents, full prompts, or complete test logs.
Handle a failed stop
Run the named check directly, fix the code, and repeat the check in a fresh attempt. A passing git diff --check does not prove business logic; an existing build file may be stale. A hook can catch only the claims you wrote into it, so keep the list short and visible.
Keep the API workflow separate
For Claude Code API work, use your own API key and compare the current Anthropic-compatible settings with BetterToken’s Claude Code documentation. BetterToken is API access, not a Claude subscription. The local hook does not need access to the key or to full Dashboard logs.