Claude Code, Hook Development.

Liam explains why Claude Code hooks, fixed event-driven scripts rather than chat reminders, reliably block actions like writing to .env files, and why a mismatched config shape makes a hook silently never fire.

1:49 video3 min readWatch on YouTube

Liam, in for Bear, addresses a common mistake: asking Claude Code to simply remember to skip a file like .env in conversation. A reminder can slip once the context scrolls away or a new session starts. The reliable fix is a hook, a script tied to one exact, fixed moment in Claude Code's execution.

Why a reminder is not the same as a hook

A reminder lives inside the conversation. Once that context is gone, so is the reminder. A hook lives in a config file that Claude Code reads before it acts, regardless of whether the original conversation is still nearby. Hooks fire at nine fixed event types, ranging from before a tool runs to when a session starts or ends, and a hook does not forget.

Command hooks versus prompt-based hooks

There are two ways to write a hook. A command hook is a bash script that performs the same deterministic check every time. A prompt-based hook instead hands the decision to Claude's own judgment. For something like blocking writes to a sensitive file, a command hook is the deterministic option: PreToolUse fires before any tool runs, checks the file path, and returns one of three answers, allow, deny, or ask, stopping the write before it happens.

Two config shapes that are not interchangeable

Getting the hook to fire reliably requires getting its config shape right, and there are two shapes that look similar but are not interchangeable. A plugin's hooks.json wraps its events inside a "hooks" key. A project's settings.json puts those same events directly at the top level. Swap the shapes and the hook does not throw an error, it simply never fires, and nothing in the output tells you why.

What a hook actually is

The core distinction the video draws is this: a hook is not something you ask Claude to remember. It is a script wired to one exact moment, and it fires exactly as configured, or not at all. That reliability is the whole value of using a hook instead of a conversational instruction.

Try it yourself

The video ends with a direct prompt: ask Claude to create a PreToolUse hook for your plugin that blocks writes to .env files and system paths. When Claude answers, check two things. Does hooks.json wrap the event inside a "hooks" key, the way a plugin's config needs, rather than sitting directly at the top level the way a project's settings.json would? And does the script path use the ${CLAUDE_PLUGIN_ROOT} variable instead of a hardcoded path, so the hook keeps working once the plugin moves?

Key takeaways

  • A hook is a config-driven script tied to a fixed event, not a conversational reminder that can be forgotten once context scrolls away.
  • PreToolUse fires before any tool runs and can return allow, deny, or ask, among nine total event types.
  • Command hooks run a deterministic bash script every time; prompt-based hooks hand the decision to Claude's own judgment.
  • A plugin's hooks.json wraps events inside a "hooks" key, while a project's settings.json puts the same events at the top level; mixing up the shapes causes the hook to silently never fire.
  • Use ${CLAUDE_PLUGIN_ROOT} instead of a hardcoded path so a plugin's hook script keeps working after the plugin moves.

Who this is for

This is for developers building or configuring Claude Code plugins who need a reliable, automatic way to block or react to specific tool actions, such as protecting sensitive files from accidental writes.

Chapters

  1. 0:00How do I set a reminder for Claude Code to skip my .env file?
  2. 0:10A file, not a memory
  3. 0:28Two ways to write one — the anchor
  4. 0:47Reliable — then silent
  5. 1:07Carry-out
  6. 1:17Your turn
  7. 1:46Outro
Full transcript(auto-generated, with timestamps)

How do I set a reminder for Claude Code to skip my .env file?

[0:00]Someone asks Claude code to just remember to skip the .env file, but a reminder can slip. The real fix is a hook, a script tied to one exact moment, what's inside one.

A file, not a memory

[0:10]A reminder lives inside the conversation. Once the context scrolls away or a new session starts, it's gone. A hook lives in a config file that Claude code reads before it acts, whether or not those words are still nearby. It fires at fixed moments, nine of them, from before a tool runs to when a session starts or ends. Two ways to write a hook, a command hook

Two ways to write one — the anchor

[0:29]Is a bash script, the same deterministic check every time. A prompt-based hook hands the decision to Claude's own judgment instead. Watch pre-tool use handle the .env case. Before any tool runs, it checks the file path and returns one of three answers, allow, deny, or ask, stopping the write before it happens. Get the shape right and that

Reliable — then silent

[0:47]Block fires every single time, reliable because a hook doesn't forget. But there are two ways to write the config and they're not interchangeable. A plugin's hooks. JSON wraps its events inside a hooks key, a project settings. JSON puts those same events directly at the top level. Swap the shapes and the hook doesn't error, it simply never fires and nothing tells you why. A hook isn't

Carry-out

[1:07]Something you ask Claude to remember, it's a script wired to one exact moment and it fires exactly as configured or not at all. Your turn, here's the prompt, read it with me. Create a pre-tool use hook for

Your turn

[1:18]My plugin that blocks writes to .env files in system paths. Watch three things when Claude answers. Does hooks.json wrap the event inside a hooks key the way a plugin's config needs instead of sitting directly at the top level the way a project settings.json would? Does the script path use dollar Claude plugin root instead of a hard-coded path? So the hook still works once the plugin moves and is there a timeout set instead of leaning on the 60-second default? Lay 'em in for bear. Claude code hook development, lay 'em in

Outro

[1:46]For bear.

More from Claude Code

Humanitarians AI Lyrical Literacy Project