A lightweight persistent-memory companion for OpenCode. Captures terminal sessions (and tool calls) as memories, serves a viewer, and exposes an OpenCode plugin that records tool usage automatically.
- Persistent cross-session memory for coding agents, stored locally in SQLite
- Built-in viewer for memory feed, session history, and observer tuning
- Peer-to-peer sync so your memory graph can travel across machines without a central service
- OpenCode-native plugin + MCP tools for low-friction usage in daily workflows
- Python 3.11+
- uv (recommended) or pip
- Install the CLI:
uv tool install --upgrade codemem- Add the plugin in OpenCode config (
~/.config/opencode/opencode.jsonc):
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["@kunickiaj/codemem"]
}- Restart OpenCode, then verify:
codemem stats
codemem raw-events-statusIf you are migrating from opencode-mem, use the step-by-step checklist in
docs/rename-migration.md.
SSH access is only needed for git-based fallback installs.
# Create virtual environment and install with dependencies
uv sync
# Run commands via the venv
.venv/bin/codemem --help
# Or activate the venv first
source .venv/bin/activate # bash/zsh
source .venv/bin/activate.fish # fish
codemem --helpRun directly without installing:
# Run latest
uvx --from git+ssh://git@github.com/kunickiaj/codemem.git codemem stats
# Run specific version
uvx --from git+ssh://git@github.com/kunickiaj/codemem.git@v0.9.17 codemem stats
# Run from local clone
uvx --from . codemem stats# Install latest
uv pip install git+ssh://git@github.com/kunickiaj/codemem.git
# Install specific version
uv pip install git+ssh://git@github.com/kunickiaj/codemem.git@v0.9.17Optionally point the SQLite store somewhere else:
export CODEMEM_DB=~/.codemem/mem.sqlitecodemem init-db– initialize the database.codemem stats/codemem recent/codemem search– inspect stored memories.codemem embed– backfill semantic embeddings for existing memories.codemem db prune-memories– deactivate low-signal memories (use--dry-runto preview).codemem serve– launch the web viewer (the plugin also auto-starts it).codemem export-memories/codemem import-memories– export and import memories by project for sharing or backup.codemem sync– enable peer sync, pair devices, and run the sync daemon.
- The plugin captures tool activity via
tool.execute.afterand conversation messages from event payloads. - Flush boundaries are event-driven:
session.idle,session.created,/newprompt, andsession.error. - Force-flush triggers fire on heavy sessions:
>=50tools,>=15prompts, or>=10msession duration. - Raw events are delivered to the viewer ingest API (
/api/raw-events).
For usage and troubleshooting details, see docs/user-guide.md and docs/plugin-reference.md.
By default, sync replicates all projects. You can restrict which projects a device will apply by project basename (folder name).
Config keys (in ~/.config/codemem/config.json):
{
"sync_projects_include": ["codemem"],
"sync_projects_exclude": ["work-repo"]
}Notes:
- If
sync_projects_includeis non-empty, only those projects sync. sync_projects_excludealways wins.- Filtering is reversible: excluded projects are skipped without advancing cursors, so toggling filters later will allow previously filtered ops to sync.
Global filters apply to all peers by default. When accepting a pairing payload, you can set a per-peer override so one peer only syncs shared work projects while your other devices still sync everything.
Example:
# Accept a peer and only sync specific projects with them
codemem sync pair --accept '<payload>' --include shared-repo-1,shared-repo-2
# Accept a peer and exclude a project
codemem sync pair --accept '<payload>' --exclude private-repo
# Clear per-peer override (inherit global defaults)
codemem sync pair --accept '<payload>' --default
# Force per-peer override to sync all projects with this peer
codemem sync pair --accept '<payload>' --allSemantic recall stores vector embeddings for memory items using sqlite-vec and fastembed. Embeddings are written when memories are created; use codemem embed to backfill existing memories.
Notes:
- Requires a Python SQLite build that supports extension loading (sqlite-vec).
- If sqlite-vec cannot load, semantic recall is skipped and keyword search still works.
The PyPI wheels for sqlite-vec currently ship a 32-bit vec0.so on aarch64, which fails to load in 64-bit Python with ELFCLASS32. Use the aarch64 release build instead:
# Download the aarch64 loadable extension (0.1.7a2)
curl -L -o /tmp/sqlite-vec-0.1.7a2-linux-aarch64.tar.gz \
https://github.com/asg017/sqlite-vec/releases/download/v0.1.7-alpha.2/sqlite-vec-0.1.7-alpha.2-loadable-linux-aarch64.tar.gz
# Extract and replace the bundled vec0.so inside the venv
tar -xzf /tmp/sqlite-vec-0.1.7a2-linux-aarch64.tar.gz -C /tmp
cp /tmp/vec0.so .venv/lib/python*/site-packages/sqlite_vec/vec0.soThis keeps sqlite-vec installed but swaps in a 64-bit aarch64 loadable, unblocking vector search and imports on Debian 13 arm64.
Share your project knowledge with teammates or back up memories to transfer between machines.
# Export all memories for current project
codemem export-memories myproject.json
# Export a specific project
codemem export-memories myproject.json --project /path/to/myproject
# Export all projects
codemem export-memories all.json --all-projects
# Export including deactivated memories
codemem export-memories myproject.json --project myproject --include-inactive
# Export to stdout and compress
codemem export-memories - --project myproject | gzip > myproject.json.gz
# Export memories from a specific date
codemem export-memories recent.json --project myproject --since 2025-01-01Imports are idempotent. You can safely re-run the same import file to pick up new entries without duplicating existing data.
# Preview what will be imported (dry run)
codemem import-memories myproject.json --dry-run
# Import memories
codemem import-memories myproject.json
# Import with project remapping (teammate has different paths)
codemem import-memories myproject.json --remap-project /Users/teammate/workspace/myproject
# Import from compressed file
gunzip -c myproject.json.gz | codemem import-memories -Use the claude-mem SQLite database directly (not the JSON export). Imports are idempotent, so re-running is safe.
codemem import-from-claude-mem ~/.claude-mem/claude-mem.dbWhen a teammate joins your project:
# You: export your project memories
codemem export-memories project-knowledge.json --project myproject
# Share the file (Slack, email, git, etc.)
# Teammate: import into their codemem
codemem import-memories project-knowledge.json --remap-project ~/workspace/myprojectNow their LLM has access to all your discoveries, patterns, and decisions about the codebase.
Contributor setup, tests, linting, and release workflow live in CONTRIBUTING.md.
Configuration is stored in ~/.config/codemem/config.json (override with CODEMEM_CONFIG). Environment variables always take precedence.
# Enable sync (generates device keys)
codemem sync enable
# Start daemon (foreground)
codemem sync daemonPair on device A (CLI or viewer UI QR):
codemem sync pairCopy the payload to device B:
codemem sync pair --accept '<payload>'Status and one-off sync:
codemem sync status
codemem sync onceAutostart (macOS + Linux):
codemem sync installRelevant config keys (override with env vars):
sync_enabled/CODEMEM_SYNC_ENABLEDsync_host/CODEMEM_SYNC_HOSTsync_port/CODEMEM_SYNC_PORTsync_interval_s/CODEMEM_SYNC_INTERVAL_Ssync_mdns/CODEMEM_SYNC_MDNSsync_key_store/CODEMEM_SYNC_KEY_STORE("file" or "keychain")
Note: macOS keychain storage uses the security CLI and may expose the key via process arguments. Keep sync_key_store=file if that’s a concern.
The viewer includes a Settings modal for the observer provider, model, and max chars. Changes write to the config file; environment variables still override those values.
docs/architecture.mdcovers the data flow and components.docs/user-guide.mdcovers viewer usage and troubleshooting.docs/plugin-reference.mdcovers plugin behavior, env vars, and stream reliability knobs.docs/rename-migration.mdcovers migration fromopencode-memtocodemem.CONTRIBUTING.mdcovers contributor setup, testing, and release workflow.
To let the LLM call memory tools (search/timeline/pack), run:
codemem install-mcpThis writes/updates your global OpenCode config at ~/.config/opencode/opencode.json. The MCP entry looks like:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"codemem": {
"type": "local",
"command": ["uvx", "codemem", "mcp"],
"enabled": true
}
}
}Restart OpenCode and the MCP tools will be available to the model.
Recommended (npm package):
Add @kunickiaj/codemem to your OpenCode plugin config, then restart OpenCode.
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["@kunickiaj/codemem"]
}Notes:
- The key is
plugin(singular).pluginsis rejected as an unknown key. - To pin a specific plugin release, use
"@kunickiaj/codemem@0.9.20".
Git fallback one-liner (advanced):
uvx --from git+ssh://git@github.com/kunickiaj/codemem.git codemem install-pluginThat's it! Restart OpenCode and the plugin is active.
For development (working on codemem):
Just start OpenCode inside the repo directory — the plugin auto-loads from .opencode/plugin/.
When OpenCode starts, the plugin loads and:
-
Auto-detects mode:
- If in the
codememrepo → usesuv run(dev mode, picks up changes) - Otherwise → uses
uvx --from git+ssh://...(installed mode)
- If in the
-
Tracks every tool invocation (
tool.execute.after) -
Flushes captured events on session boundaries (
session.idle,session.created,/new,session.error) -
Auto-starts the viewer by default (set
CODEMEM_VIEWER_AUTO=0to disable) -
Injects a memory pack into the system prompt (disable with
CODEMEM_INJECT_CONTEXT=0)
Observer/settings panel and advanced plugin controls are documented in docs/plugin-reference.md.
