Content
Local-first Memory for Claude Code
Lightprivate memory** foraude Code](https://claude.com/claude-code):
automatically saves important dialogue picks them up at the start session, and can search
for them meaning" — while **embeddings are considered local** (Ollama), nothing extra goes outside.
Three building blocks:
1. **** — a folder of markdown one fact per file + index `MEMORY.md`.
2. **** — before ending the session, the model decides what to write to memory.
3. **Semantic search** — local (Ollama + `nomic-embed-text`), available to the as an MCP tool `semantic_search`. No external vector database: one Python script
without dependencies and a thin MCP wrapper.
Everything under **macOS** (under Linux, only paths differ; Keychain is not required).
## How the Memory is Structured
Memory is a folder of markdown files (by default `~/claude-memory/`). Rules:
- **One file = one fact**, name — short `kebab-case` slug.
- Each file has **frontmatter** with `name`, `description`, and `metadata.type`.
- Index **`MEMORY.md`** — by a string-link to each fact; Claude Code loads it
into context at the start of the session.
- Claude Code knows about the folder through the `autoMemoryDirectory` setting in `settings.json`.
Fact types (`metadata.type`):
| type | what we store |
|-------------|-----------------------------------------------------------------------|
| `user` | who the user is: role, expertise, preferences |
| `feedback` | how the assistant works — rules, adjustments; always with "why" |
| `project` | current tasks/goals/limitations, not derived from code and git history |
| `reference` | pointers to external resources (URL, dashboards, docs) |
Example memory file:
```markdown
---
name: prefers-typescript-strict
description: User always wants strict mode TypeScript in new projects
metadata:
type: feedback
---
Always enable `strict: true` in tsconfig for new projects.
**Why:** catches type errors at compile-time, user insists on it.
**How to apply:** when initializing any TS project, set strict without reminder.
```
String in `MEMORY.md`: `- [Title](file-name.md) — short hook`
## Installation
### 0. Dependencies
```bash
brew install jq # for the hook
brew install ollama # or https://ollama.com
ollama serve & # will start on 127.0.0.1:11434
ollama pull nomic-embed-text
mkdir -p ~/claude-memory
printf '# Memory index\n' > ~/claude-memory/MEMORY.md
```
### 1. Stop-hook for Auto-Saving
Copy [`hooks/memory-save-on-stop.sh`](hooks/memory-save-on-stop.sh):
```bash
mkdir -p ~/.claude/hooks
cp hooks/memory-save-on-stop.sh ~/.claude/hooks/
chmod +x ~/.claude/hooks/memory-save-on-stop.sh
```
In `~/.claude/settings.json` add (merge with existing keys, replace `$USER` with your
name — `echo $HOME` will show the full path):
```json
{
"autoMemoryDirectory": "/Users/<you>/claude-memory",
"hooks": {
"Stop": [
{ "hooks": [ { "type": "command",
"command": "/Users/<you>/.claude/hooks/memory-save-on-stop.sh",
"timeout": 30 } ] }
]
}
}
```
The path in `command` is absolute. Restart Claude Code.
### 2. Semantic Search
```bash
mkdir -p ~/semsearch
cp semsearch/semsearch.py semsearch/mcp_server.py ~/semsearch/
python3 ~/semsearch/semsearch.py reindex
python3 ~/semsearch/semsearch.py search "user preferences by code"
```
### 3. MCP Server in Claude Code
In `~/.claude.json` inside `mcpServers` add (do not overwrite the file entirely):
```json
{
"mcpServers": {
"semsearch": {
"type": "stdio",
"command": "python3",
"args": ["/Users/<you>/semsearch/mcp_server.py"]
}
}
}
```
Restart Claude Code — the `semantic_search` tool will appear.
## Usage
- **Accumulates automatically:** at the end of sessions, the assistant saves new facts to `~/claude-memory`.
- **Remembers at the beginning:** `MEMORY.md` is loaded into the context of a new session.
- **Searches by meaning:** with a large volume, the assistant calls `semantic_search`.
- **Periodically** run `python3 ~/semsearch/semsearch.py reindex` so that search sees
fresh facts (the index does not update itself — if desired, hang on cron or `SessionStart`-hook).
## Privacy
The hook and memory are local files. Embeddings are considered by local Ollama, the index lies on the disk.
Nothing goes outside except what is usually done when working with Claude Code.
## Extensibility
The engine is made according to sources (`ingest_memory` in `semsearch.py`). Want to index something
else (notes, docs, tickets) — add your own `ingest_*` function, returning records of the, and turn it on in `reindex`.
## License
MIT — see [LICENSE](LICENSE).