opencode-claude-memory
v2.0.0
Published
OpenCode plugin for Claude Code memory: persistent, local-first shared memory with Claude Code-compatible Markdown files, LLM recall, in-process auto extraction, and auto-dream consolidation
Maintainers
Readme
🧠 Claude Code-compatible memory for OpenCode
Persistent, local-first shared memory for OpenCode and Claude Code — one plugin, zero migration.
This OpenCode plugin lets OpenCode read and write Claude Code-compatible Markdown memory files, so both CLIs share the same project context.
Claude Code writes memory → OpenCode reads it. OpenCode writes memory → Claude Code reads it.
Quick Start • How it works • Configuration • Compatibility • Migrating from v1 • FAQ
✨ At a glance
- Memory tools —
memory_save/memory_delete/memory_list/memory_search/memory_read, plus the Claude Code memory instructions injected into every system prompt. - LLM recall — before each turn a hidden agent picks the memories relevant to the query; they appear in the first LLM call, including single-step questions.
- Automatic extraction — after a session goes idle, a sandboxed fork reviews only the new part of the conversation and saves what is worth keeping. Sessions closed before the fork ran are caught up at the next start.
- Auto-dream — periodic consolidation (merge / prune / rewrite) gated on time and session count, like Claude Code.
- Claude Code-compatible — same directory, same file format, same taxonomy, same worktree handling.
MEMORY.mdis edited line by line so hand-organised indexes stay intact. - Cross-platform, no shell hook — everything runs inside the OpenCode process through the plugin SDK. No
python3, nojq, no wrapper.
🚀 Quick Start
Requires OpenCode ≥ 1.18.
// opencode.json (project) or ~/.config/opencode/opencode.json (global)
{
"plugin": ["opencode-claude-memory"]
}That's it. Start opencode and use it as usual. Memories live in ~/.claude/projects/<project>/memory/ (or under $CLAUDE_CONFIG_DIR), exactly where Claude Code keeps them.
⚙️ How it works
graph LR
U[User turn] --> R[Hidden recall agent<br/>selects relevant memories]
R --> S[System prompt: instructions + MEMORY.md + recalled memories]
S --> A[Main agent answers<br/>memory_* tools available]
A --> I[session.idle]
I --> E[Extraction fork<br/>new messages only]
E --> M[(~/.claude/projects/<project>/memory/)]
E --> D{Auto-dream gate}
D -->|24h & 5 sessions| C[Consolidation fork]
C --> M- Recall —
experimental.chat.messages.transformstarts a selector prefetch for each new user turn (a hidden child session runningopencode-memory-recall).experimental.chat.system.transformwaits for it up torecall.waitMs(default 1.5 s) and injects the selected memories. Memories already in the conversation are not re-injected; after compaction they can surface again. - Extraction — every
session.idleis debounced (extract.debounceMs). The plugin fetches the session's messages, slices them after the per-session watermark, and — only if there is a new user message — runsopencode-memory-extractin a child session restricted tomemory_save/memory_list/memory_read. On success the watermark advances; if the main agent already saved memory in that stretch the fork is skipped. - Catch-up — on start-up the plugin lists the project's sessions and extracts the ones updated after their watermark (at most
extract.catchUpLimit). This covers "answer, then quit immediately". - Auto-dream — after each extracted session the gate is evaluated (
autodream.minHourssince the last pass andautodream.minSessionsextracted since). When it passes,opencode-memory-dreamruns with all five memory tools. A lock file prevents two OpenCode processes from consolidating at once. - Ignore memory — "ignore memory" in a user message switches memory off for the rest of the session (no index, no recall); "use memory again" switches it back on.
State that is private to the plugin (watermarks, auto-dream gate, lock) lives in <CLAUDE_CONFIG_DIR>/opencode-memory/<project>/, never inside the Claude Code project directory.
🔧 Configuration
All behaviour is configured through OpenCode's own configuration. There are no OPENCODE_MEMORY_* environment variables.
// opencode.json
{
"plugin": [
["opencode-claude-memory", {
"extract": { "enabled": true, "timeoutMs": 120000, "debounceMs": 10000, "maxConversationChars": 60000, "catchUpLimit": 5 },
"autodream": { "enabled": true, "minHours": 24, "minSessions": 5, "timeoutMs": 300000 },
"recall": { "enabled": true, "waitMs": 1500, "timeoutMs": 30000, "maxMemories": 5 }
}]
],
"agent": {
"opencode-memory-extract": { "model": "anthropic/claude-haiku-4-5", "steps": 20 },
"opencode-memory-recall": { "model": "anthropic/claude-haiku-4-5" },
"opencode-memory-dream": { "model": "anthropic/claude-sonnet-5" }
}
}- Every option above is optional; the values shown are the defaults. Unknown keys are rejected when the plugin loads.
- When the same plugin is listed in both the global and the project
opencode.json, OpenCode keeps the last declaration (project wins); options are not merged across files. - The three agents are registered hidden with a memory-only tool sandbox. Override any field (
model,steps,temperature, …) underagent.<name>; the plugin fills in the rest. CLAUDE_CONFIG_DIRis honoured exactly like Claude Code does, and is the only environment variable the plugin reads.
Logs go to the OpenCode service log (opencode log directory, service opencode-claude-memory).
🤝 Compatibility with Claude Code
| Aspect | Claude Code | This plugin |
|---|---|---|
| Memory directory | ~/.claude/projects/<sanitized canonical git root>/memory/ | identical (sanitizePath, worktree → main repo resolution ported byte for byte) |
| File format | Markdown + name / description / type frontmatter | identical; frontmatter parsed only within the first 30 lines, as in Claude Code |
| Taxonomy | user, feedback, project, reference | identical |
| MEMORY.md | one-line pointers, hand-organisable | read with the same truncation rules; written with minimal line-level edits |
| Sub-directories | team/x.md etc. | scanned, recalled and addressable from every tool |
| System prompt | memory instructions + index + recalled memories | ported sections (memoryTypes.ts, memdir.ts) |
| Recall | LLM side query | LLM side query in a hidden child session (findRelevantMemories.ts port) |
| Extraction / auto-dream | after session, gated | after session.idle + start-up catch-up, gated the same way |
Memory files written by either tool need no conversion in either direction.
📝 Memory format
---
name: User prefers terse responses
description: User wants concise answers without trailing summaries
type: feedback
---
Skip post-action summaries. User reads diffs directly.
**Why:** User explicitly requested terse output style.
**How to apply:** Don't summarize changes at the end of responses.🔁 Migrating from v1
v2 removes the shell wrapper, the opencode-memory CLI and every OPENCODE_MEMORY_* environment variable. Memory files are untouched and need no conversion.
# 1. remove the v1 shell hook, then the v1 package (v2 no longer needs a global install)
opencode-memory uninstall # or delete the ">>> opencode-memory auto-initialization >>>" block from your rc file
npm uninstall -g opencode-claude-memory
# 2. drop OPENCODE_MEMORY_* from your shell configuration
grep -n OPENCODE_MEMORY ~/.zshrc ~/.bashrc ~/.zshenv ~/.profile 2>/dev/null// 3. pin the major in opencode.json — OpenCode caches npm plugins per specifier,
// so a bare "opencode-claude-memory" keeps serving the v1 it installed earlier
{
"plugin": ["opencode-claude-memory@2"]
}Everything the environment variables used to control now lives under extract, autodream, recall and agent.opencode-memory-* in opencode.json — see Configuration. The v1 documentation, including the full list of environment variables, stays available in the v1 README.
❓ FAQ
Is this a new memory system? No. It is a compatibility layer around Claude Code's memory layout and conventions.
Do I need to migrate existing memory? No. Existing Claude Code memory files are used as they are.
Where is data stored? ~/.claude/projects/<project>/memory/ (or $CLAUDE_CONFIG_DIR/projects/...). Plugin state lives in $CLAUDE_CONFIG_DIR/opencode-memory/<project>/.
Can I disable extraction, auto-dream or recall? Yes — extract.enabled, autodream.enabled, recall.enabled in the plugin options.
Why did my first answer take a moment longer? The system prompt waits up to recall.waitMs for the selector. Set it to 0 to never wait (recalled memories then appear from the second LLM call of a turn onwards).
Does the extraction fork see my whole conversation? Only the messages after the last extraction, capped at extract.maxConversationChars (newest first). The fork can only call memory tools.
🧪 Development
bun install
bun test # unit, integration and eval tests
bun run evals # readable task-eval report
bun run lint # biome
bun run typecheck
bun run build # emits dist/Releases are cut by semantic-release on push to main.
