Persistent cross-session memory for OpenCode.
A git-tracked memory store rooted at ~/opencode-memory/, plus optional read-only search over named knowledge bases. The OpenCode plugin provides tools, hooks, auto-applied config, and a bundled skill.
LLM agents forget everything between sessions. That means rediscovering the same repo structure, tool quirks, and gotchas over and over. This plugin gives them a place to put that knowledge and a strong enough social contract (tool-call tracking, compaction-time retrospectives) that they actually use it.
| Category | Additions |
|---|---|
| Memory tools | memory_search, memory_read, memory_list, memory_save, memory_access, memory_setup |
| Knowledge tools | knowledge_list, knowledge_base_search, knowledge_base_read for separately indexed source repositories |
| Session tools | session_search_all across OpenCode, Pi, and Codex; session_search, session_read, and session_list for OpenCode history |
| Hooks | Search-first nudge at 8 tool calls; discovery nudge on subagent outputs; retrospective reminder at compaction time (OpenCode only) |
| Skill | opencode-memory — auto-registered in OpenCode, dropped at ~/.agents/skills/opencode-memory for Zed & Pi |
| Agent prompts | Built-in subagents (general, explore, research, review, investigator) get a memory-aware prompt prepended non-destructively (OpenCode only) |
The package root is the OpenCode v1 plugin. OpenCode v2 loads the package's
./server export, which provides the v2 plugin. Both use the same tool definitions
and search v1 and v2 OpenCode session history. The v1 entry adapts the tools and
registers its hooks and config through the v1 plugin API.
OpenCode v2:
OpenCode v1:
// opencode.jsonc
{
"plugin": ["@mathew-cf/opencode-memory@2.1.0"]
}Then bootstrap the memory directory + embedding model + skill:
bunx @mathew-cf/opencode-memory initThis creates ~/opencode-memory/ (git repo, 7 category subdirs), downloads the ~90MB embedding model, and symlinks the bundled skill into ~/.agents/skills/opencode-memory (where Zed and Pi look). Idempotent — safe to re-run. Pass --skip-model to defer the download, --skip-skills to skip the symlink.
The plugin also auto-registers (OpenCode only):
- its bundled skill under
config.skills.paths - edit + external-directory permissions for
~/opencode-memory/** - memory-aware prompt prefixes on the five built-in subagents (only when their prompt isn't already set)
memory_search combines two complementary signals:
| Signal | Command | Purpose |
|---|---|---|
| Keyword | rag keyword |
Live text search over memory files |
| Semantic | rag search |
Similarity search via local embeddings |
Both commands come from the required @mathew-cf/rag-cli package, which installs a prebuilt binary on supported platforms. No Rust toolchain or $PATH setup is needed.
Pre-cache the embedding model once (~90MB) to make the first semantic search instant:
rag downloadmemory_setup reports whether the binary and its keyword command are available. Semantic search also needs an index and the cached embedding model.
bunx @mathew-cf/opencode-memory initCreates ~/opencode-memory/ (or $OPENCODE_MEMORY_DIR), runs git init, scaffolds the 7 advisory category subdirs (preferences/, repos/, technical/, people/, workflows/, snippets/, notes/), writes a rag.toml for the memory index, and pre-caches the embedding model for semantic search. Existing rag.toml files are preserved.
The generated config is:
[[index]]
name = "memory"
path = "."
output = ".rag"
exclude = ["rag.toml", ".rag.toml"]memory_search uses this entry for live keyword file selection and semantic search; memory_save rebuilds only this entry. Add exclude, include, no_ignore, or hidden to control file selection, following rag-cli's rag.toml rules. Keep the entry named memory. Other entries, if present, do not enter memory search. The plugin combines keyword and semantic results with its own frontmatter-aware ranking, so [search] hybrid is overridden for its semantic call. For direct CLI search, rag-cli's own [search] settings still apply. Stores without rag.toml retain path-based search and indexing.
If an existing .rag index predates rag-cli 2.0, rebuild it once with rag index --config ~/opencode-memory/rag.toml --only memory (adjust the path when using $OPENCODE_MEMORY_DIR).
Subcommands:
| Command | Purpose |
|---|---|
bunx @mathew-cf/opencode-memory init |
Create + git-init memory dir, download embedding model |
bunx @mathew-cf/opencode-memory init --skip-model |
Same, but skip the ~90MB download |
bunx @mathew-cf/opencode-memory status |
Report which search backends are resolvable |
Memory files are plain markdown with a small frontmatter block:
---
title: Framework uses custom error hierarchy
tags: [framework, error-handling]
summary: All errors must extend AppError; plain Error bypasses formatting
created: 2025-01-15
updated: 2025-01-15
importance: high
source: Code inspection of src/errors/
source_date: 2025-01-15
---
All errors in `src/errors/` must extend `AppError`. Throwing plain `Error`
bypasses the error formatter → raw 500s. Gotcha: `AuthError` must include a
`realm` field or auth middleware silently ignores it.After writing or editing files, call memory_save — it runs git add -A + commit and kicks off a background rag index re-build.
memory_search("retry jitter") # compact keyword + semantic results (up to 5)
memory_search("auth", category="repos") # filter to a category
memory_read("repos/example.md") # frontmatter + first 4,000 body chars
memory_read("repos/example.md", heading="Build") # retrieve one heading section
memory_list() # browse categories + counts
memory_list("technical") # list files in one category
Knowledge bases are separate from the writable memory store. Configure their
names and directories in ~/.config/opencode-memory/config.toml:
[[knowledge_base]]
name = "reference"
path = "~/opencode-knowledge-base"
default = true
[[knowledge_base]]
name = "project"
path = "~/projects/my-project/knowledge"Each directory must contain rag.toml (or .rag.toml) with one or more
[[index]] source entries. Index those sources with rag index --config <knowledge-base>/rag.toml; knowledge_base_search uses rag search --config and
honors that file's [search] settings, including hybrid = true. The plugin
does not build knowledge-base indexes or write to source repositories.
Names must be unique. With one configured base it becomes the default
automatically. With several, mark one default = true or pass base on each
call. Relative directory paths in this config resolve from the config file's
directory. Set OPENCODE_MEMORY_CONFIG to use another config file.
knowledge_list() # names and default
knowledge_base_search(query="cache retries") # default base
knowledge_base_search(query="cache retries", base="project")
knowledge_base_search(query="cache retries", all=true) # results grouped by base
knowledge_base_read(base="reference", index="docs", source="guide.md")
Search results carry the base name, index name, and source path. Pass those
fields to knowledge_base_read; it reads a bounded portion of the original file
under that index's configured source directory, even when the source repo is
outside the knowledge-base directory.
session_search_all queries OpenCode, Pi, and Codex concurrently and labels each
source. Missing harnesses are reported without failing the available searches.
The limit applies per source. Results include IDs and snippets; when a harness's
native reader is installed, use it to open that source's session.
Pi discovery scans its JSONL history, including historical branches; use Pi's
native reader when you need its active-branch and compaction-aware view.
The OpenCode-only session_search returns a message offset for each content
match. Pass that to session_read to jump to the relevant message. Session reads normalize invalid
pagination values, cap limit at 100 messages, and expose at most about 16,000
message-text characters per call. If that bound falls within one oversized
message, the response provides both offset and message_char_offset; pass
both back to continue at a UTF-safe boundary without skipping content.
See the bundled skill (skills/opencode-memory/SKILL.md) for the full protocol.
The plugin installs two hooks:
Tracks tool usage per session and injects short reminders into tool output when:
- 8 tool calls deep with no search: reminds the agent to call
memory_searchandsession_searchbefore going further. - A subagent's output contains "Discoveries worth saving": reminds the parent to actually save them, not defer to session end.
Reminders fire at most once per session each to avoid spam.
Injects memory-specific preservation rules so references to saved files and search results survive summarization. If the session is >10 tool calls and never called memory_save, adds a retrospective reminder.
bun install
bun run typecheck # tsc --noEmit
bun test
bun run build # bundle to dist/Apache-2.0 — see LICENSE.