Persistent memory for AI coding agents
Agent-agnostic. Single binary. Zero dependencies.
Quick Start • How It Works • Agent Setup • Why Not claude-mem? • Terminal UI • Full Docs
engram
/ˈen.ɡræm/— neuroscience: the physical trace of a memory in the brain.
Your AI coding agent forgets everything when the session ends. Engram gives it a brain.
A Go binary with SQLite + FTS5 full-text search, exposed via CLI, HTTP API, MCP server, and an interactive TUI. Works with any agent that supports MCP — OpenCode, Claude Code, Cursor, Windsurf, or anything else.
Agent (OpenCode / Claude Code / Cursor / Windsurf / ...)
↓ MCP stdio
Engram (single Go binary)
↓
SQLite + FTS5 (~/.engram/engram.db)
brew install gentleman-programming/tap/engramgit clone https://github.com/Gentleman-Programming/engram.git
cd engram
go install ./cmd/engramGrab the latest release for your platform from GitHub Releases.
Then add Engram to your agent's MCP config — see Agent Setup below.
That's it. No Node.js, no Python, no Bun, no Docker, no ChromaDB, no vector database, no worker processes. One binary, one SQLite file.
The agent proactively calls mem_save after significant work — structured, searchable, no noise.
Engram trusts the agent to decide what's worth remembering — not a firehose of raw tool calls.
1. Agent completes significant work (bugfix, architecture decision, etc.)
2. Agent calls mem_save with a structured summary:
- title: "Fixed N+1 query in user list"
- type: "bugfix"
- content: What/Why/Where/Learned format
3. Engram persists to SQLite with FTS5 indexing
4. Next session: agent searches memory, gets relevant context
Session starts → Agent works → Agent saves memories proactively
↓
Session ends → Agent writes session summary (Goal/Discoveries/Accomplished/Files)
↓
Next session starts → Previous session context is injected automatically
| Tool | Purpose |
|---|---|
mem_save |
Save a structured observation (decision, bugfix, pattern, etc.) |
mem_search |
Full-text search across all memories |
mem_session_summary |
Save end-of-session summary |
mem_context |
Get recent context from previous sessions |
mem_timeline |
Chronological context around a specific observation |
mem_get_observation |
Get full content of a specific memory |
mem_save_prompt |
Save a user prompt for future context |
mem_stats |
Memory system statistics |
mem_session_start |
Register a session start |
mem_session_end |
Mark a session as completed |
Token-efficient memory retrieval — don't dump everything, drill in:
1. mem_search "auth middleware" → compact results with IDs (~100 tokens each)
2. mem_timeline observation_id=42 → what happened before/after in that session
3. mem_get_observation id=42 → full untruncated content
Engram works with any MCP-compatible agent. Add it to your agent's MCP config:
Add to your opencode.json (global: ~/.config/opencode/opencode.json or project-level):
{
"mcp": {
"engram": {
"type": "local",
"command": ["engram", "mcp"],
"enabled": true
}
}
}Optional: OpenCode plugin for enhanced session management (auto-session tracking, compaction memory persistence, system prompt injection):
cp plugin/opencode/engram.ts ~/.config/opencode/plugins/The plugin is auto-loaded from ~/.config/opencode/plugins/ — no config changes needed. It also needs the HTTP server running for session tracking:
engram serve &See OpenCode Plugin for details.
Add to your .claude/settings.json (project) or ~/.claude/settings.json (global):
{
"mcpServers": {
"engram": {
"command": "engram",
"args": ["mcp"]
}
}
}Add to your ~/.gemini/settings.json (global) or .gemini/settings.json (project):
{
"mcpServers": {
"engram": {
"command": "engram",
"args": ["mcp"]
}
}
}Or via the CLI:
gemini mcp add engram engram mcpAdd to your .cursor/mcp.json:
{
"mcpServers": {
"engram": {
"command": "engram",
"args": ["mcp"]
}
}
}Add to your ~/.windsurf/mcp.json:
{
"mcpServers": {
"engram": {
"command": "engram",
"args": ["mcp"]
}
}
}The pattern is always the same — point your agent's MCP config to engram mcp via stdio transport.
When your agent compacts (summarizes long conversations to free context), it starts fresh — and might forget about Engram. To make memory truly resilient, add this to your agent's system prompt or config file:
For Claude Code (CLAUDE.md):
## Memory
You have access to Engram persistent memory via MCP tools (mem_save, mem_search, mem_session_summary, etc.).
- Save proactively after significant work — don't wait to be asked.
- After any compaction or context reset, call `mem_context` to recover session state before continuing.For OpenCode (agent prompt in opencode.json):
After any compaction or context reset, call mem_context to recover session state before continuing.
Save memories proactively with mem_save after significant work.
For Gemini CLI (GEMINI.md):
## Memory
You have access to Engram persistent memory via MCP tools (mem_save, mem_search, mem_session_summary, etc.).
- Save proactively after significant work — don't wait to be asked.
- After any compaction or context reset, call `mem_context` to recover session state before continuing.For Cursor/Windsurf (.cursorrules or .windsurfrules):
You have access to Engram persistent memory (mem_save, mem_search, mem_context).
Save proactively after significant work. After context resets, call mem_context to recover state.
This is the nuclear option — system prompts survive everything, including compaction.
claude-mem is a great project (28K+ stars!) that inspired Engram. But we made fundamentally different design decisions:
| Engram | claude-mem | |
|---|---|---|
| Language | Go (single binary, zero runtime deps) | TypeScript + Python (needs Node.js, Bun, uv) |
| Agent lock-in | None. Works with any MCP agent | Claude Code only (uses Claude plugin hooks) |
| Search | SQLite FTS5 (built-in, zero setup) | ChromaDB vector database (separate process) |
| What gets stored | Agent-curated summaries only | Raw tool calls + AI compression |
| Compression | Agent does it inline (it already has the LLM) | Separate Claude API calls via agent-sdk |
| Dependencies | go install and done |
Node.js 18+, Bun, uv, Python, ChromaDB |
| Processes | One binary (or none — MCP stdio) | Worker service on port 37777 + ChromaDB |
| Database | Single ~/.engram/engram.db file |
SQLite + ChromaDB (two storage systems) |
| Web UI | Terminal TUI (engram tui) |
Web viewer on localhost:37777 |
| Privacy | <private> tags stripped at 2 layers |
<private> tags stripped |
| Auto-capture | No. Agent decides what matters | Yes. Captures all tool calls then compresses |
| License | MIT | AGPL-3.0 |
claude-mem captures everything and then compresses it with AI. This means:
- Extra API calls for compression (costs money, adds latency)
- Raw tool calls pollute search results until compressed
- Requires a worker process, ChromaDB, and multiple runtimes
- Locked to Claude Code's plugin system
Engram lets the agent decide what's worth remembering. The agent already has the LLM, the context, and understands what just happened. Why run a separate compression pipeline?
mem_saveafter a bugfix: "Fixed N+1 query — added eager loading in UserList"mem_session_summaryat session end: structured Goal/Discoveries/Accomplished/Files- No noise, no compression step, no extra API calls
- Works with ANY agent via standard MCP
The result: cleaner data, faster search, no infrastructure overhead, agent-agnostic.
Interactive terminal UI for browsing your memory. Built with Bubbletea.
engram tuiScreens: Dashboard, Search, Recent Observations, Observation Detail, Timeline, Sessions, Session Detail
Navigation: j/k vim keys, Enter to drill in, t for timeline, / to search, Esc to go back
Features:
- Catppuccin Mocha color palette
- Scroll indicators for long lists
- Full FTS5 search from the TUI
- Live data refresh on back-navigation
Share memories across machines and team members by committing them to your repo. Uses compressed chunks with a manifest index — no merge conflicts, no huge files.
# Export new memories as a compressed chunk
# (automatically filters by current directory name as project)
engram sync
# Commit to git
git add .engram/ && git commit -m "sync engram memories"
# On another machine / clone: import new chunks
engram sync --import
# Check sync status
engram sync --status
# Override project detection if needed
engram sync --project other-nameHow it works:
.engram/
├── manifest.json ← small index (git diffs this)
├── chunks/
│ ├── a3f8c1d2.jsonl.gz ← chunk by Alan (compressed, ~2KB)
│ ├── b7d2e4f1.jsonl.gz ← chunk by Juan
│ └── c9f1a2b3.jsonl.gz ← chunk by Alan (next day)
└── engram.db ← gitignored (local working DB)
- Each
engram synccreates a new chunk — never modifies old ones - Chunks are gzipped JSONL — small files, git treats as binary (no diff noise)
- The manifest is the only file git diffs — it's small and append-only
- Each chunk has a content hash ID — imported only once, no duplicates
- No merge conflicts on data — each dev creates independent chunks
Auto-import: The OpenCode plugin automatically runs engram sync --import when it detects .engram/manifest.json in the project directory. Clone a repo, open OpenCode, and the team's memories are loaded.
engram serve [port] Start HTTP API server (default: 7437)
engram mcp Start MCP server (stdio transport)
engram tui Launch interactive terminal UI
engram search <query> Search memories
engram save <title> <msg> Save a memory
engram timeline <obs_id> Chronological context around an observation
engram context [project] Recent context from previous sessions
engram stats Memory statistics
engram export [file] Export all memories to JSON
engram import <file> Import memories from JSON
engram sync Export new memories as compressed chunk to .engram/
engram version Show version
For OpenCode users, a thin TypeScript plugin adds enhanced session management on top of the MCP tools:
# Copy the plugin — auto-loaded from the plugins directory
cp plugin/opencode/engram.ts ~/.config/opencode/plugins/The plugin auto-starts the HTTP server if it's not already running — no manual engram serve needed.
The plugin:
- Auto-starts the engram server if not running
- Auto-imports git-synced memories from
.engram/manifest.jsonif present in the project - Creates sessions on-demand via
ensureSession()(resilient to restarts/reconnects) - Injects the Memory Protocol into the agent's system prompt via
chat.system.transform— strict rules for when to save, when to search, and a mandatory session close protocol - Injects previous session context into the compaction prompt
- Instructs the compressor to tell the new agent to persist the compacted summary via
mem_session_summary - Strips
<private>tags before sending data
No raw tool call recording — the agent handles all memory via mem_save and mem_session_summary.
The plugin injects a strict protocol into every agent message:
- WHEN TO SAVE: Mandatory after bugfixes, decisions, discoveries, config changes, patterns, preferences
- WHEN TO SEARCH: Reactive (user says "remember"/"recordar") + proactive (starting work that might overlap past sessions)
- SESSION CLOSE: Mandatory
mem_session_summarybefore ending — "This is NOT optional. If you skip this, the next session starts blind." - AFTER COMPACTION: Immediately call
mem_contextto recover state
The OpenCode plugin uses a defense-in-depth strategy to ensure memories survive compaction:
| Layer | Mechanism | Survives Compaction? |
|---|---|---|
| System Prompt | MEMORY_INSTRUCTIONS injected via chat.system.transform |
Always present |
| Compaction Hook | Auto-saves checkpoint + injects context + reminds compressor | Fires during compaction |
| Agent Config | "After compaction, call mem_context" in agent prompt |
Always present |
Wrap sensitive content in <private> tags — it gets stripped at TWO levels:
Set up API with <private>sk-abc123</private> key
→ Set up API with [REDACTED] key
- Plugin layer — stripped before data leaves the process
- Store layer —
stripPrivateTags()in Go before any DB write
engram/
├── cmd/engram/main.go # CLI entrypoint
├── internal/
│ ├── store/store.go # Core: SQLite + FTS5 + all data ops
│ ├── server/server.go # HTTP REST API (port 7437)
│ ├── mcp/mcp.go # MCP stdio server (10 tools)
│ ├── sync/sync.go # Git sync: manifest + compressed chunks
│ └── tui/ # Bubbletea terminal UI
│ ├── model.go # Screen constants, Model, Init()
│ ├── styles.go # Lipgloss styles (Catppuccin Mocha)
│ ├── update.go # Input handling, per-screen handlers
│ └── view.go # Rendering, per-screen views
├── plugin/
│ └── opencode/engram.ts # OpenCode adapter plugin
├── assets/ # Screenshots and media
├── DOCS.md # Full technical documentation
├── go.mod
└── go.sum
- Go 1.25+ to build from source (not needed if installing via Homebrew or downloading a binary)
- That's it. No runtime dependencies.
The binary includes SQLite (via modernc.org/sqlite — pure Go, no CGO).
| Variable | Description | Default |
|---|---|---|
ENGRAM_DATA_DIR |
Data directory | ~/.engram |
ENGRAM_PORT |
HTTP server port | 7437 |
MIT
Inspired by claude-mem — but agent-agnostic, simpler, and built different.



