Three Hooks That Give Claude Code Memory¶
Every Claude Code session starts from zero. You know your codebase, your conventions, your past decisions. Claude doesn't — until you explain them. Again. Every time.
This is not a Claude problem. It's an architecture problem. The context window is the right unit of work, but it has no built-in mechanism for accumulating knowledge across sessions.
I've been running three passive hooks to fix this for months. Today I packaged them up: kcp-hooks.
The re-explaining tax¶
Here's what the first few prompts of an un-augmented Claude Code session look like in practice:
"This is a Java 21 project, we use PostgreSQL, deployments go through GitHub Actions, don't touch the legacy auth module, and we're migrating away from Hibernate to jooq..."
You've paid the re-explaining tax. You do it every session. For every project you context-switch into.
The usual solution is CLAUDE.md. Put your conventions there, commit it, Claude reads it at session start. That works for stable, always-relevant context. It breaks down for dynamic context: which runbook applies to this prompt? What did you actually do last Tuesday in the auth service? Which skill file is relevant when you're asking about database migrations vs. when you're asking about CI pipelines?
CLAUDE.md is a static file. It's good at being a static file. The problem is dynamic.
Three hooks, zero configuration¶
The hooks fire on every UserPromptSubmit event — before Claude sees your prompt. You don't invoke them. They complete in under 200ms. They fail silently if anything goes wrong.
prompt-router.py — Skills routing. Scores every *.yaml file in ~/.claude/skills/ against the current prompt using keyword matching (name ×3, tags ×2, trigger phrases ×2, domain ×2, description ×1). If something scores above the threshold, it injects a short preamble listing the top matches.
## Prompt Router -- Relevant Context
- `my-deploy-runbook` (devops) — Step-by-step deploy checklist: staging gate, DB migration order, rollback
- `postgres-schema` (backend) — Current schema, migration conventions, index strategy for user_events
Claude now knows which skill files exist for this topic and can load them if the work requires it. No /skill invocations. No remembering what's in your library.
prompt-recall.py — Episodic memory. Detects temporal signals ("yesterday", "last week", "what did I fix...", "do you remember...") and queries kcp-memory for relevant past sessions. kcp-memory indexes your Claude Code session transcripts into SQLite+FTS5 — everything you've ever built, discussed, or fixed.
## Episodic Memory — relevant past sessions (query: "auth service token refresh")
- 2026-06-19 [payments-api] Implemented JWT refresh token rotation — fixed race condition in concurrent refresh attempts
- 2026-06-12 [auth-service] Debugged token expiry issue — root cause was clock skew, fixed with NTP sync
prompt-hygiene.py — Influence detection. Pure regex scan for emotional pressure, false urgency, repetition tactics, false dilemmas, authority overload. Silent on clean prompts. Single line when it fires:
This last one is the most underrated. At scale, prompt injection and social-engineering-style prompts (usually accidental, sometimes not) are real. A deterministic filter that costs 2ms and never touches an API is worth having permanently.
The design constraint that mattered¶
The private versions of these hooks accumulated domain knowledge specific to my projects — Mynder, Cantara, Elprint, LetsReg all had explicit routing signals. The skill library had 540+ entries with a private registry format.
Packaging for distribution meant making a different design decision: auto-discovery over registration.
The public prompt-router.py discovers all *.yaml files in ~/.claude/skills/ at startup. No registry file. No registration step. Drop a file in the directory, it routes. Delete it, it stops.
The skill format is a clean YAML spec with documented fields:
name: my-deploy-runbook
description: Deploy checklist for the payments service.
domain: devops
tags:
- deploy
- migrations
- rollback
trigger_phrases:
- "deploy to production"
- "run migrations"
instructions: |
# Payments Service — Deploy Runbook
...
The DOMAIN_SIGNALS dict in the router ships with a minimal example and a comment to add your own project vocabulary. Domain signals give in-domain skills a 1.2× score boost and penalize cross-domain noise at 0.4×. Adding "payments": ["stripe", "webhook", "idempotency"] is five seconds of work.
The three-layer model¶
kcp-hooks occupies two of the three layers of what I've been calling ExoCortex memory:
| Layer | What it holds | Provided by |
|---|---|---|
| Procedural | How to do things — runbooks, conventions, patterns | prompt-router + your skill files |
| Episodic | What happened — past sessions, decisions, bugs fixed | prompt-recall + kcp-memory |
| Semantic | What the codebase means — structure, relationships | Synthesis |
Working memory (the current context window) sits on top of all three.
The insight is that these layers need different retrieval mechanisms. Procedural is best served by keyword routing against a structured library — the content is human-authored, stable, and well-indexed by topic. Episodic is best served by full-text search against session transcripts — unstructured, temporal, best queried from natural language. Semantic is best served by graph traversal — relationships between files, dependencies, architecture.
Running all three passive hooks means Claude enters each prompt with relevant procedural context, relevant episodic context, and hygiene filtering — without the user doing anything.
What's not in the package¶
The hooks here are the retrieval layer. They don't build the episodic memory — that's kcp-memory, which runs as a daemon and indexes transcripts in the background. prompt-recall.py is optional: if kcp-memory isn't running, it fails silently. Your prompts are unaffected.
The semantic layer (Synthesis) is a separate project entirely. The hooks don't touch it.
And prompt-hygiene.py is standalone — pure Python stdlib, no dependencies, no external calls. It's the one hook that's genuinely zero-setup and always active.
Install¶
Copies hooks to ~/.kcp/hooks/, wires them into ~/.claude/settings.json (merges, never overwrites), creates ~/.claude/skills/ if it doesn't exist.
Restart Claude Code. Done.
The session amnesia problem isn't going away. The context window is the right unit of work. But three passive hooks running at the prompt boundary, surfacing what's relevant now rather than front-loading everything on every session — that's the fixable version of the problem.