Lifecycle hooks
Claude Code lifecycle hooks fire at predictable session events and write memory automatically. This guide explains each hook, what it writes, and how to configure the pack.
Hook pack overview
Section titled “Hook pack overview”The hooks pack lives at examples/claude-code/hooks.json. It wires five hook events:
| Hook | Claude Code event | What it does |
|---|---|---|
| SessionStart | Session begins | Exports bounded Markdown memory manifest into context |
| PostToolUseFailure | Bash command fails | Writes tool_failure episode to session scope |
| Stop | Claude finishes a turn | Backfills transcript errors; stores turn summary fact |
| PreCompact | Before /compact |
Flushes user snippets and custom instructions to repository facts |
| SessionEnd | Session ends | Optional consolidation (opt-in via env var) |
Copy hooks to your Claude Code project settings:
cp examples/claude-code/hooks.json \ .claude/hooks.jsonOr merge into your existing hooks file manually. Each hook entry points to a JavaScript runner in examples/claude-code/.
SessionStart hook
Section titled “SessionStart hook”When: Claude Code starts a new session.
What it writes: Nothing directly. It calls memory_profile (for the user-profile shelf) and memory_search across user and repository scopes to assemble a bounded Markdown manifest, then injects it into the system context as <session-memory-manifest>.
Effect: The agent starts the session already aware of:
- Recent repository facts
- User preferences
- Open-ended beliefs pending confirmation
Configuration:
| Variable | Default | Purpose |
|---|---|---|
MEMORY_SERVICE_DATA_DIR |
.ai-memory/data |
Data directory |
MEMORY_SERVICE_NAMESPACE |
workspace basename | Namespace for session scope |
PostToolUseFailure hook
Section titled “PostToolUseFailure hook”When: A Bash tool call fails (non-zero exit code).
What it writes: An episode record in session scope with observation.kind=tool_failure:
{ "scope": "session", "topic": "tool_failures", "field": "bash_error_<timestamp>", "memory_type": "episode", "value": { "command": "npm run build", "exit_code": 1, "stderr": "Error: Cannot find module 'esbuild'" }}Effect: The agent can recall recent tool failures with memory_search(scope=session, query="esbuild error"). This is especially useful when debugging a persistent error across multiple turns.
Stop hook
Section titled “Stop hook”When: Claude finishes a turn (the Stop event fires after each response).
What it writes:
- Backfill: Scans the transcript for Bash tool errors that the PostToolUseFailure hook may have missed. Writes missing episodes.
- Turn summary: Writes a
factto repository scope summarizing the turn’s key outcomes.
PreCompact hook
Section titled “PreCompact hook”When: Before Claude Code compacts the conversation (/compact or auto-compact).
What it writes: Flushes recent user snippets (custom instructions, preferences mentioned in chat) and compaction-checkpoint facts to repository scope.
Effect: After a /compact, the agent retains awareness of key preferences and project facts even though the raw conversation is compressed. The session-start manifest re-injects this context at the next session.
Optional veto:
export MEMORY_SERVICE_BLOCK_AUTO_COMPACT=1export MEMORY_SERVICE_COMPACTION_SAFE_FILE=.ai-memory/.compact-safeWhen set, the hook can veto auto-compact until the safe file exists.
SessionEnd hook (optional)
Section titled “SessionEnd hook (optional)”When: The Claude Code session ends (explicit close).
What it writes: Nothing by default. When opt-in consolidation is enabled, it queues a memory_consolidate dry-run.
To enable:
export MEMORY_SERVICE_RUN_CONSOLIDATION_ON_SESSION_END=1export MEMORY_SERVICE_REVIEW_EXPORT_PATH=.ai-memory/review.mdDebugging hooks
Section titled “Debugging hooks”Enable verbose hook logging:
export MEMORY_SERVICE_HOOK_DEBUG=1Each hook will log ingest counts and write results to stderr. Check Claude Code’s hook log output for errors.
Hook CLIs (no MCP required)
Section titled “Hook CLIs (no MCP required)”Hooks call into app/hook_remember.py and app/hook_search.py directly — these work without the MCP server running:
# Search session scope (no MCP needed)python3 app/hook_search.py \ --data-dir .ai-memory/data \ --scope session \ --namespace "$(basename "$PWD")" \ --query "ModuleNotFoundError" \ --include-episodesNamespace patterns
Section titled “Namespace patterns”Session scope writes use namespace {workspace_basename}. Repository scope flushes (PreCompact, Stop summaries) use namespace {workspace_basename}-repo.
Example for project my-project:
- Session scope namespace:
my-project - Repository scope namespace:
my-project-repo
Next steps
Section titled “Next steps”- Export & import — review exports and Markdown import
- Claude Code integration — full Claude Code setup including MCP
- Scopes — what each scope stores and when to use each