Semantic memory search for OpenCode — gives your AI assistant persistent memory across sessions with zero user intervention.
- Auto-capture: Summarizes each conversation turn and saves to daily
.mdfiles - Semantic search: Hybrid search (BM25 + dense vectors + RRF) via Milvus
- Three-layer recall: Search → Expand → Transcript (progressive detail)
- Cold-start context: Injects recent memories into new sessions automatically
- Per-project isolation: Each project gets its own Milvus collection
- ONNX embeddings: CPU-only bge-m3 model, no API key required
# Install memsearch with ONNX embeddings
uv tool install 'memsearch[onnx]'
# or: pip install 'memsearch[onnx]'// In ~/.config/opencode/opencode.json
{
"plugin": ["@zilliz/memsearch-opencode"]
}Windows note: the current plugin shells out to
bashandpython3helper scripts. Plain Windows installs are not supported yet; use WSL2 (recommended) or a POSIX-compatible shell such as Git Bash. See issue #387.
# Clone the repo
git clone https://github.com/zilliztech/memsearch.git
cd memsearch
# Run the installer
bash plugins/opencode/install.sh# 1. Symlink the plugin
mkdir -p ~/.config/opencode/plugins
ln -sf /path/to/memsearch/plugins/opencode/index.ts ~/.config/opencode/plugins/memsearch.ts
# 2. Symlink the skill (optional, for !memory-recall)
mkdir -p ~/.agents/skills
ln -sf /path/to/memsearch/plugins/opencode/skills/memory-recall ~/.agents/skills/memory-recallOpenCode Session
├── chat.message hook ──→ Detect turn completion
│ │
│ ├── Extract last turn from SQLite
│ ├── Persist turn metadata to .memsearch/opencode-turns.db
│ ├── Summarize via LLM (third-person notes)
│ └── Append to .memsearch/memory/YYYY-MM-DD.md
│ │
│ └── memsearch index (background)
│
├── system.transform hook ──→ Inject recent memories
│
└── Tools
├── memory_search ──→ memsearch search (hybrid BM25+dense)
├── memory_get ──→ memsearch expand (full context)
└── memory_transcript ──→ parse-transcript.py (SQLite reader)
After restarting OpenCode, chat for a few turns in a project and confirm memory capture is happening:
ls .memsearch/memory/
cat .memsearch/memory/$(date +%Y-%m-%d).mdIf the plugin is active, daily markdown files should appear and grow as conversations finish.
Manual invocation — explicitly invoke the skill with a query:
/memory-recall what was the auth approach we discussed?
Auto invocation — just ask naturally, the LLM auto-invokes memory tools when it senses the question needs history:
We discussed the authentication flow before, what was the approach?
| Tool | Description |
|---|---|
memory_search |
Semantic search over past memories. Returns ranked chunks. |
memory_get |
Expand a chunk hash to see the full markdown section. |
memory_transcript |
Read original conversation from OpenCode SQLite DB, optionally centered on a turn cursor. |
<project>/.memsearch/opencode-turns.db stores derived capture checkpoints and
turn ordering only. It is rebuildable state, not the source of truth for
transcript recall.
Memory is stored as markdown in <project>/.memsearch/memory/:
.memsearch/
└── memory/
├── 2026-03-25.md
└── 2026-03-26.md
Each file contains timestamped entries with bullet-point summaries:
# 2026-03-26
## Session 14:30
### 14:30
<!-- session:ses_abc123 turn:msg_123abc db:~/.local/share/opencode/opencode.db -->
- User asked about the authentication flow.
- Assistant explained the OAuth2 implementation in auth.ts.
- Assistant modified the token refresh logic in refresh.ts.The plugin uses ONNX embeddings by default (no API key needed). To use a different provider:
memsearch config set embedding.provider openai
# Set the API key in your environment
export OPENAI_API_KEY=sk-...To override only the OpenCode native capture summarization model:
memsearch config set plugins.opencode.summarize.model anthropic/claude-haikuTo use a memsearch-managed API provider instead:
memsearch config set llm.providers.openai.type openai
memsearch config set llm.providers.openai.model gpt-5-mini
memsearch config set llm.providers.openai.api_key env:OPENAI_API_KEY
memsearch config set plugins.opencode.summarize.provider openaiLeave plugins.opencode.summarize.provider empty or set it to native to keep the current small_model / plugin default behavior. This setting does not fall back to llm.model.
Native summaries run with isolated XDG config and data directories, and the plugin does not copy the user's OpenCode auth store into them. If that environment cannot authenticate, capture retains only an unavailable note and the transcript anchor.
Native opencode run output must contain bullet lines. The managed-provider command keeps its existing contract and accepts any non-empty plain-text summary.
-
Capture: After each conversation turn, the plugin extracts the user+assistant exchange, sends it to the configured summarizer, and appends the result to a daily markdown file. If summarization fails, times out, exits unsuccessfully, or returns no usable output, the plugin writes a short unavailable note instead of the turn text. The transcript remains accessible through the entry's progressive-disclosure anchor.
-
Index: The markdown files are indexed by memsearch into a Milvus collection (Milvus Lite by default, runs in-process).
-
Recall: When the assistant needs historical context, it calls
memory_searchto find relevant chunks. Results can be expanded withmemory_getor drilled into withmemory_transcript, which reads the original transcript from OpenCode SQLite. -
Cold-start: At session start, recent memory bullets are injected into the system prompt so the assistant has immediate context.
| Feature | Claude Code | OpenCode | OpenClaw |
|---|---|---|---|
| Session storage | JSONL | SQLite | JSONL |
| Hook system | Shell scripts | TypeScript hooks | JS API |
| Summarizer | claude -p --model haiku | opencode run | openclaw agent |
| Context injection | SessionStart hook | system.transform | before_agent_start |
| Skill context | context: fork | N/A (no fork) | N/A |