Hooks¶
The plugin hooks into 4 Claude Code lifecycle events and provides a memory-recall skill. A singleton memsearch watch process runs in the background, keeping the vector index in sync with markdown files as they change. (Milvus Lite falls back to one-time indexing at session start.)
Lifecycle Diagram¶
stateDiagram-v2
[*] --> SessionStart
SessionStart --> WatchRunning: start memsearch watch
SessionStart --> InjectRecent: load recent memories (cold start)
state WatchRunning {
[*] --> Watching
Watching --> Reindex: file changed
Reindex --> Watching: done
}
InjectRecent --> Prompting
state Prompting {
[*] --> UserInput
UserInput --> Hint: UserPromptSubmit hook
Hint --> ClaudeProcesses: "[memsearch] Memory available"
ClaudeProcesses --> MemoryRecall: needs context?
MemoryRecall --> Subagent: memory-recall skill [fork]
Subagent --> ClaudeResponds: curated summary
ClaudeProcesses --> ClaudeResponds: no memory needed
ClaudeResponds --> UserInput: next turn
ClaudeResponds --> Summary: Stop hook (async, non-blocking)
Summary --> WriteMD: append to YYYY-MM-DD.md
}
Prompting --> SessionEnd: user exits
SessionEnd --> StopWatch: stop memsearch watch
StopWatch --> [*]
Hook Summary¶
The plugin defines exactly 4 hooks, all declared in hooks/hooks.json:
| Hook | Type | Async | Timeout | What It Does |
|---|---|---|---|---|
| SessionStart | command | no | 10s | Start memsearch watch singleton, write session heading to today's .md, inject recent daily logs as cold-start context via additionalContext, display config status (provider/model/milvus) in systemMessage |
| UserPromptSubmit | command | no | 15s | Lightweight hint: returns systemMessage "[memsearch] Memory available" (skip if < 10 chars). No search — recall is handled by the memory-recall skill |
| Stop | command | yes | 120s | Extract last turn from transcript with parse-transcript.sh, call claude -p --model haiku to summarize as third-person notes, append summary with session/turn anchors to daily .md |
| SessionEnd | command | no | 10s | Stop the memsearch watch background process (cleanup) |
What Each Hook Does¶
SessionStart¶
Fires once when a Claude Code session begins. This hook:
- Reads config and checks API key. Runs
memsearch config getto read the configured embedding provider, model, and Milvus URI. Checks whether the required API key is set for the provider (OPENAI_API_KEY,GOOGLE_API_KEY,VOYAGE_API_KEY;ollamaandlocalneed no key). If missing, shows an error insystemMessageand exits early. - Starts the watcher. Launches
memsearch watch .memsearch/memory/as a singleton background process (PID file lock prevents duplicates). The watcher monitors markdown files and auto-re-indexes on changes with a 1500ms debounce. Milvus Lite falls back to a one-timememsearch indexat session start. - Writes a session heading. Appends
## Session HH:MMto today's memory file (.memsearch/memory/YYYY-MM-DD.md), creating the file if it does not exist. - Injects cold-start context. Reads the last 30 lines from the 2 most recent daily logs and returns them as
additionalContext. This gives Claude awareness of recent sessions, which helps it decide when to invoke the memory-recall skill. - Checks for updates. Queries PyPI (2s timeout) and compares with the installed version. If a newer version is available, appends an
UPDATEhint to the status line. - Displays config status. Every exit path returns a
systemMessageshowing the active configuration, e.g.[memsearch v0.1.10] embedding: openai/text-embedding-3-small | milvus: ~/.memsearch/milvus.db | collection: ms_my_app_a1b2c3d4(with| UPDATE: v0.1.12 availablewhen outdated).
UserPromptSubmit¶
Fires on every user prompt before Claude processes it. This hook:
- Extracts the prompt from the hook input JSON.
- Skips short prompts (under 10 characters) -- greetings and single words don't need memory hints.
- Returns a lightweight hint. Outputs
systemMessage: "[memsearch] Memory available"-- a visible one-liner that keeps Claude aware of the memory system without performing any search.
The actual memory retrieval is handled by the memory-recall skill, which Claude invokes automatically when it judges the user's question needs historical context.
Stop¶
Fires after Claude finishes each response. Runs asynchronously so it does not block the user. This hook:
- Guards against recursion. Checks
stop_hook_activeto prevent infinite loops (since the hook itself callsclaude -p). - Validates the transcript. Skips if the transcript file is missing or has fewer than 3 lines.
- Parses the last turn. Calls
parse-transcript.sh, which:- Scans backward from EOF to find the last real user message (content is a string, not a
tool_result) - Extracts only the last turn: from that user message to EOF
- Skips
progress,file-history-snapshot,system, andthinkingblocks - Formats output with clear role labels:
[Human]for user messages,[Claude Code]for assistant text,[Claude Code calls tool]for tool invocations,[Tool output]/[Tool error]for tool results — these labels help the summarizer treat the content as a third-party transcript rather than its own conversation - Truncates tool results to 1000 characters; uses Python 3 (no
jqdependency)
- Scans backward from EOF to find the last real user message (content is a string, not a
- Summarizes with Haiku. Pipes the parsed last turn to
claude -p --model haiku --no-session-persistencewith an external-observer system prompt that requests 2-6 third-person bullet points recording what the user asked and what Claude did (tools called, files changed, key findings). The summary language matches the user's language. - Appends to daily log. Writes a
### HH:MMsub-heading with an HTML comment anchor containing session ID, turn UUID, and transcript path. Then explicitly runsmemsearch indexto ensure the new content is indexed immediately, rather than relying on the watcher's debounce timer (which may not fire before SessionEnd kills the watcher).
SessionEnd¶
Fires when the user exits Claude Code. Simply calls stop_watch to kill the memsearch watch process and clean up the PID file, including a sweep for any orphaned processes.