Your personal memory, across sessions, agents, and devices.

Claude Code Hooks: Events, Examples and Memory

memU Team memU Team

Claude Code hooks let you run your own code at fixed points in a session, such as before a tool call or when a session starts. They are the only part of Claude Code's customization that runs every time, whatever the model decides. Everything below comes from Anthropic's hooks reference and hooks guide. It shows how a hook allows or blocks an action, with three examples to paste. The last part covers what hooks can do for memory across sessions.

What are Claude Code hooks, in one sentence?

Claude Code hooks are handlers in your settings that Claude Code runs automatically at lifecycle events, so an action happens whether or not the model chooses it.

Anthropic's guide calls this "deterministic control". There are five hook types. Three run code. A command hook runs a shell command, an HTTP hook posts to an endpoint, and an MCP tool hook calls a tool on an MCP server. Two ask a model: a single-turn prompt, or an experimental agent hook that can read files before it decides. Most hooks people write are shell commands. Claude Code sends them a JSON description of the event on stdin and reads their exit code and stdout.

How do hooks differ from CLAUDE.md, skills and permissions?

Hooks and permission rules are the two that Claude Code enforces. CLAUDE.md and skills depend on the model.

Hooks CLAUDE.md Skills Permission rules
What it is Code that runs on an event Instructions loaded into context Instructions and scripts loaded when relevant Allow and deny rules for tools
Who acts Claude Code runs it The model reads it The model decides to use it Claude Code enforces it
Can block an action? Yes, with exit code 2 or a JSON decision No. Anthropic describes it as context the model reads No Yes
Can add context? Yes, on eleven events Yes, at session start Yes, when loaded No
Where it lives settings.json files, plugins, skill and subagent front matter Project, user or org CLAUDE.md files .claude/skills/ folders settings.json files

Sources: Anthropic's hooks reference and memory docs.

The memory docs say it directly. To block an action regardless of what Claude decides, use a PreToolUse hook.

Which lifecycle events can you hook?

The current reference lists 33 events. These are the ones most setups use:

Event When it fires Can it block?
SessionStart A session starts, resumes, clears or compacts No
UserPromptSubmit You submit a prompt, before Claude sees it Yes
PreToolUse Before any tool call Yes
PermissionRequest A tool call needs a permission decision Allow or deny via JSON
PostToolUse After a tool call succeeds Feedback only, the tool already ran
Stop Claude finishes responding Yes, it can make Claude keep going
SubagentStart, SubagentStop A subagent spawns or finishes Stop can block
PreCompact, PostCompact Around context compaction PreCompact can block
SessionEnd The session ends No

The list has grown well past the original set. Newer events cover model switches and worktree changes. Some fire when files change on disk or when an agent-team teammate goes idle. Matchers narrow a hook to what you care about. For tool events the matcher is the tool name, such as Edit|Write. For SessionStart it is the source: startup, resume, clear, compact or fork.

How does a hook allow, block or add context?

A command hook answers through its exit code:

  • Exit 0 means success. If stdout is a single JSON object, Claude Code parses it. On SessionStart and UserPromptSubmit, plain text on stdout is added to Claude's context.
  • Exit 2 is a blocking error on events that can block. Stderr becomes the reason Claude sees.
  • Any other code, including 1, is a non-blocking error. The action goes ahead and you see a notice.

Hooks fail open by default. A script that crashes or times out does not stop the action. If a hook guards something that matters, exit 2 on purpose. In Claude Code v2.1.295 and later you can also set the onFailure field to block. Command hooks time out after 600 seconds by default. Prompt hooks get 30 seconds and agent hooks get 60.

For finer control a hook prints JSON. A PreToolUse hook can return a permission decision (allow, deny, ask or defer). It can also rewrite the tool input. On eleven events, including SessionStart and PostToolUse, a hook can return an additionalContext string that Claude receives as a system reminder. The reference adds two cautions. Strings over 10,000 characters are saved to a file and Claude gets a preview. Text phrased like system commands can trip prompt-injection defenses, so write plain facts.

Hooks run with your user's full permissions. Project hooks follow the workspace trust rule, and admins can restrict hooks to managed settings only.

What are some Claude Code hooks examples?

All three go in .claude/settings.json at your project root. The first two come from Anthropic's hooks guide.

Format every file Claude edits with Prettier:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }
        ]
      }
    ]
  }
}

Put key facts back after compaction, when the summary may have dropped them:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          { "type": "command", "command": "echo 'Reminder: use Bun, not npm. Run bun test before committing.'" }
        ]
      }
    ]
  }
}

Block destructive shell commands, failing closed if the script itself breaks:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(rm *)",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh",
            "onFailure": "block"
          }
        ]
      }
    ]
  }
}

The script reads the command from stdin and exits 2 with a reason on stderr when it wants to refuse. Type /hooks in a session to open the hooks browser and confirm your hook loaded. The guide notes a gap in the Prettier example: Claude can also change files through Bash. For full coverage, add a Stop hook that checks the working tree once per turn.

Can hooks give Claude Code memory across sessions?

Partly. A hook can read from somewhere and write into context. A SessionStart hook that runs git log or prints a notes file is the common pattern, and Anthropic's own example does this after compaction. A hook can also write things down. A SessionEnd or Stop hook receives the transcript path and can copy or summarize it.

What hooks lack is judgment. A command hook does the same thing every time, so it cannot decide which part of a two-hour session is worth keeping. Prompt and agent hooks can judge, but they run under tight timeouts on every event they match. SessionEnd hooks share a 1.5-second budget by default. Claude Code's built-in auto memory fills part of this gap. Claude writes its own notes as plain Markdown. The first 200 lines or 25KB load each session. The memory docs say auto memory is machine-local and is not shared across machines or cloud environments.

The bottom line

Use hooks for anything that must happen every time, such as formatting and blocking dangerous commands. Make guards fail closed with exit 2 or onFailure, and run /hooks to confirm they loaded. Use CLAUDE.md for advice and auto memory for Claude's own notes. Do not try to build a full memory system out of shell hooks, because the hard part is deciding what to keep.

How do you give Claude Code memory that hooks cannot?

Hooks are good at the two mechanical ends of memory: saving logs and injecting text. The middle step is choosing what a session taught. Auto memory handles that inside one machine and one tool. If you switch laptops, or move from Claude Code to Codex, it stays behind.

memU binds two seams in each host. On the record side, a scheduled background task slices new session logs into jobs. The agent distills each job into memory and skill Markdown, and memU commits the result. On the inject side, a standing instruction in CLAUDE.md tells Claude to retrieve before it answers.

memU itself makes no LLM or chat calls. It stores the Markdown the agent prepared and retrieves it by embedding search. All supported hosts share one memory backend, so a fix Claude Code learned is retrievable from Codex.

The hosted version on memu.so is free and syncs across devices. Get an API key there and send Claude Code one message:

Read https://memu.pro/SKILL.md, follow its instructions to install and configure memU, API Key is YOUR_MEMU_API_KEY.

Self-hosting and the support matrix are in the memU GitHub repository.

FAQ

Where do Claude Code hooks go?

In a settings file. Use ~/.claude/settings.json for all your projects and .claude/settings.json for a shared project. Personal project hooks go in .claude/settings.local.json. Plugins and skill or subagent front matter can also define hooks.

Why does my PreToolUse hook not block the command?

Only exit code 2 or a JSON deny decision blocks. Other exit codes and timeouts count as non-blocking errors. The tool call goes ahead unless the hook sets onFailure to block.

Can a hook add text to Claude's context?

Yes. Return an additionalContext string in the JSON output on a supported event such as SessionStart. Plain stdout also works on SessionStart and UserPromptSubmit.

Can a hook change a tool call before it runs?

Yes. A PreToolUse hook can return updated tool input along with its permission decision, and a PermissionRequest hook can do the same when it allows a call.

Do Claude Code hooks run in subagents?

Yes. Hooks fire inside subagents too. SubagentStart and SubagentStop let you act when one spawns or finishes. A subagent's own front matter can define hooks that run only while it runs.