Skip to content

Hooks for the non-negotiables — CLAUDE.md only asks nicely

← Tips Workflow & commands

Hooks for the non-negotiables — CLAUDE.md only asks nicely

is advisory — Claude follows it about 80% of the time. are deterministic — they run every time, no exceptions. If something must happen 100% of the time (lint, security check, format), make it a hook.

advanced External reference ↗

There’s one detail about that takes most teams a few weeks to internalise: it’s advisory. Claude reads it at the start of every session and tries to follow it. Most of the time it does. Not every time.

The empirical number that floats around the team and community guides: ~80% adherence. Good enough for “prefer const over let”. Not good enough for “never force-push to main” or “always run the linter after editing”.

For the things that must happen, use . A hook is a runs on a specific event — and it runs deterministically, every time.

The events worth knowing:

  • PreToolUse — fires before Claude calls a tool. Use to block dangerous commands or require a confirmation. Example: “if Claude tries to run git push --force, refuse.”
  • PostToolUse — fires after. Use to enforce post-edit invariants. Example: “after any file edit, run npm run lint --fix.”
  • Stop — fires when Claude finishes a turn. Use for safety nets. Example: “after every turn, run the test suite; if it fails, surface the failures to the next .”
  • UserPromptSubmit — fires when you press enter on a prompt. Use to inject context. Example: “before every prompt, append the current git name.”

Hooks are configured in .claude/settings.json (project) or ~/.claude/settings.json (personal). A minimal example — auto-lint after every edit:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "npm run lint:fix --silent"
      }
    ]
  }
}

After every Edit or Write, the lint runs. Claude can’t forget it. The hook doesn’t need permission. It just happens.

You don’t have to memorise the JSON — ask Claude to set it up

Most newcomers see the snippet above and freeze: “do I edit this by hand?” You don’t. Tell Claude what you want enforced and let it wire the hook for you.

Add a hook to this project that runs npm run lint:fix after every file edit. Make sure it doesn’t run on .md files.

Claude finds (or creates) .claude/settings.json, drops the right hook block in, and shows you the diff before applying. You stay in the “describe the outcome” lane; the syntax is Claude’s job. Same trick for PreToolUse blockers, Stop test runs, or UserPromptSubmit context injections — describe the rule, let Claude write it.

The mental that helps:

If the rule is……put it in…
”Prefer this style” / “Use this pattern”CLAUDE.md
”Always run this after editing”PostToolUse hook
”Block this dangerous command”PreToolUse hook
”Surface this on every prompt”UserPromptSubmit hook

A small warning: hooks compose with permission modes and in non-obvious ways. Start with one hook, see how it behaves, then add more. Hooks that fail noisily are annoying; hooks that fail silently are dangerous. Log generously while you’re tuning them.

Next tip →

One session, one task — don't pile work into a long conversation

Long mixed-topic sessions confuse Claude and burn context. Finish the auth refactor, then `/clear` and start the cart bug fresh. Sessions are cheap; misremembering isn't.