Skip to content
Palimem Docsspec v1.7.0

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.


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:

Terminal window
cp examples/claude-code/hooks.json \
.claude/hooks.json

Or merge into your existing hooks file manually. Each hook entry points to a JavaScript runner in examples/claude-code/.


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

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.


When: Claude finishes a turn (the Stop event fires after each response).

What it writes:

  1. Backfill: Scans the transcript for Bash tool errors that the PostToolUseFailure hook may have missed. Writes missing episodes.
  2. Turn summary: Writes a fact to repository scope summarizing the turn’s key outcomes.

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:

Terminal window
export MEMORY_SERVICE_BLOCK_AUTO_COMPACT=1
export MEMORY_SERVICE_COMPACTION_SAFE_FILE=.ai-memory/.compact-safe

When set, the hook can veto auto-compact until the safe file exists.


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:

Terminal window
export MEMORY_SERVICE_RUN_CONSOLIDATION_ON_SESSION_END=1
export MEMORY_SERVICE_REVIEW_EXPORT_PATH=.ai-memory/review.md

Enable verbose hook logging:

Terminal window
export MEMORY_SERVICE_HOOK_DEBUG=1

Each hook will log ingest counts and write results to stderr. Check Claude Code’s hook log output for errors.


Hooks call into app/hook_remember.py and app/hook_search.py directly — these work without the MCP server running:

Terminal window
# Search session scope (no MCP needed)
python3 app/hook_search.py \
--data-dir .ai-memory/data \
--scope session \
--namespace "$(basename "$PWD")" \
--query "ModuleNotFoundError" \
--include-episodes

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