When Your AI Coding Assistant Forgets: Building Long-Term Memory with Lifecycle Hooks
AI coding tools forget across sessions because models are stateless and context windows are finite. This article explains how to capture, summarize, store, and inject structured memory with lifecycle hooks, plus retrieval, safety, and common pitfalls.
Confirm the scope first
This approach applies only to AI coding tools that support lifecycle hooks or extensible plugins. Pure web chat windows have no hook interface. In those cases you can only ask the model to summarize, then paste the summary into a new session. If your tool supports hooks, you can automate the flow described here.
Why AI coding assistants forget
Large language models are stateless. On each call, the model sees only what is sent in that request. Tools appear to “remember” because the client repackages conversation history and sends it with the new instruction.
Context windows are limited. Older content is truncated when the limit is exceeded. New sessions usually do not automatically include previous sessions. The model is not unwilling to remember; nothing persisted the important facts and fed them back when needed.
Once that is clear, the fix is also clear: store valuable conclusions externally and retrieve them on demand at the start of a new session.
Why the usual workarounds fall short
Many developers keep full chat logs or paste entire log files into a new session. That has two problems.
First, raw logs contain trial-and-error code and low-value discussion. They fill the window with little signal. Second, dumping everything dilutes attention and can lower answer quality. A multi-hour debugging session may yield only a few conclusions and a handful of file paths that actually matter.
Architecture of a minimal memory system
A smallest working system has three modules.
- Capture: listen to tool calls and session events, and append raw records to a local log.
- Process: at session end, generate a structured summary, confirm it manually, then store it.
- Inject: at session start, retrieve memories for the current project and place them in context.
Storage options differ. The table below compares them.
| Storage form | Storage cost | Retrieval precision | Injection risk |
|---|---|---|---|
| Raw logs | High, large volume | Low, noisy | High, easy to overflow the window and inject unrelated content |
| Structured summaries | Low | Medium, depends on field design | Medium, summaries can be wrong |
| Top-K summaries after vector search | Medium, requires an index | High, depends on embedding quality | Medium-low, needs a similarity threshold |
In practice, store structured summaries and inject only a few relevant items via vector search. Do not keep raw logs and inject them wholesale.
How to use lifecycle hooks
Take Claude Code’s Hooks as an example. Hooks are typically declared in project-level or user-level config. Field names, event names, and versions vary; follow the official documentation for your tool. Common events:
- SessionStart: runs when a session starts. A script can read the memory store, filter entries for the current project, and write results into context.
- UserPromptSubmit: runs when the user submits a prompt. Useful for detecting references to past concepts and adding background.
- PostToolUse: runs after a tool call. Suitable for recording file reads, code search, and test runs.
- Stop: runs when a reply finishes. You can filter greetings and other low-value content here.
- SessionEnd: runs when the session ends. Suitable for generating summaries and writing pending entries to the memory store.
A minimal config that only registers injection and persistence:
{"hooks": {"SessionStart": [{"hooks": [{ "type": "command", "command": "python3 ~/memory/inject.py" }]}],"SessionEnd": [{"hooks": [{ "type": "command", "command": "python3 ~/memory/summarize.py" }]}]}}
Before enabling this, Python 3 must be installed, the memory directory must exist, and the scripts must be executable. Hook scripts run with the current user’s privileges, so keep them simple and restrict them to the memory directory.
Structured summary template
Summary quality determines whether memory is useful. Have the summarizer emit fixed fields, for example:
{"id": "2026-11-12-001","project": "order-service","date": "2026-11-12","problem_background": "Order service occasionally double-charges under high concurrency","solution": "Changed inventory deduction lock granularity from table-level to row-level and added idempotency checks","involved_files": ["src/order/stock_service.py", "src/order/lock.py"],"open_issues": ["Load tests still show some timeouts; connection pool limits need confirmation"],"tags": ["concurrency", "lock granularity", "idempotency"],"expires_at": "2027-05-12","source_session": "session-abc123"}
Field roles: project isolates memories by project. involved_files matches currently open files. open_issues keeps leftover problems from being forgotten. expires_at supports periodic cleanup of stale architecture decisions.
Minimal retrieve-and-inject flow
A practical sequence:
- During the session, the PostToolUse hook appends events to a raw log file.
- The SessionEnd hook calls a lightweight model to generate a summary from the template above and writes it to a pending-review directory.
- A human reviews pending summaries and promotes them only after confirmation. Unconfirmed entries are not injected.
- The SessionStart hook filters by project name, then computes embeddings from current directory filenames and open-file contents, and takes the Top-K most similar items. K of 3 is a reasonable start. A similarity threshold of 0.75 can be used first and tuned later.
- If retrieval times out, the index is corrupt, or nothing matches, skip injection, log the event, and do not block session start.
Degrade safely: if memory fails, the tool must still work.
Security checklist
The memory store holds code and discussion, so manage it carefully.
- Do not store secrets, tokens, passwords, or personal data. Filter common patterns before write, such as strings starting with
sk-. - Redact source snippets; keep only necessary function names and file paths.
- Restrict the memory directory to the current user, mode 700.
- Set an expiry on each memory and archive expired entries automatically.
- Treat injected content as untrusted. If text such as “ignore previous instructions” appears, delete it or mark it to reduce prompt injection risk.
- Isolate retrieval by project so repositories do not contaminate each other.
Common beginner mistakes
- Full dump: stuffing all historical memories into context and crowding out the current task.
- Cross-project bleed: no project name at session start, so results mix other repositories.
- Stale memories left in place: outdated architecture decisions are treated as current and produce bad advice.
- Auto-commit without review: the summarizer can be wrong, and errors get injected repeatedly.
Adjust how you collaborate
After you add memory, change how you work with the assistant.
- After complex work, ask it to summarize conclusions, involved files, and open issues so extraction is easier.
- At the start of a new session, state the project name and current goal in one or two sentences.
- Review the memory store regularly, as you would a code repository, and delete stale or incorrect entries.
Conclusion
Cross-session forgetting comes from a stateless model and a limited context window, not from an inability to learn. Lifecycle hooks let you persist key conclusions as structured memory and inject them on demand. That reduces repeated background briefings, but results depend on hook support, retrieval quality, and how well you maintain the store.
Start with one project and one template. Confirm that memory helps and does not add noise, then expand.
