Working memory for AI reasoning.
workmem is a local knowledge graph that keeps the thread between sessions. It stores facts, decays what stops mattering, and surfaces what still does. One binary, one SQLite file, any MCP client.
The RAG preserves knowledge. workmem preserves the thread.
This is not an archive. It's the context that helps a model think now: recent decisions, open problems, corrections, preferences, relationship patterns. Things that keep coming back get reinforced. Things that don't, fade. That's the feature.
LLMs forget everything between sessions. System prompts can't hold your project's history. RAG is great for reference material but bad for recency, continuity, and working context. workmem fills the gap: lightweight enough to call on every session start, smart enough to surface what's relevant without being asked.
brew tap marlian/tap
brew install workmem
workmem versionSource: marlian/homebrew-tap. Updates with brew update && brew upgrade workmem.
Download the archive for your platform from releases and extract. Each archive contains the workmem binary plus LICENSE and README.md:
# pick the archive that matches your OS/arch, e.g. darwin-arm64 / linux-amd64
VER=v0.1.0
curl -LO "https://github.com/marlian/workmem/releases/download/${VER}/workmem-darwin-arm64-${VER}.tar.gz"
tar -xzf workmem-darwin-arm64-${VER}.tar.gz
sudo install workmem-darwin-arm64-${VER}/workmem /usr/local/bin/workmem
workmem versionFor integrity, download SHA256SUMS from the release page and verify only the archive you actually fetched, using the checksum tool that ships with your platform:
curl -LO "https://github.com/marlian/workmem/releases/download/${VER}/SHA256SUMS"
# macOS
grep "workmem-darwin-arm64-${VER}" SHA256SUMS | shasum -a 256 -c
# Linux
grep "workmem-linux-amd64-${VER}" SHA256SUMS | sha256sum -cOn macOS, Gatekeeper will warn on first launch of an unsigned binary downloaded this way. Remove the quarantine attribute with xattr -d com.apple.quarantine /usr/local/bin/workmem, or install via Homebrew (which does not trigger the warning).
git clone https://github.com/marlian/workmem.git
cd workmem
go build -o workmem ./cmd/workmem
./workmem version # prints "workmem dev" without -ldflagsOr let Go fetch and install directly:
go install github.com/marlian/workmem/cmd/workmem@latestGo 1.26+ is required (see go.mod for the exact minimum). No CGO and no external runtime dependencies — the result is a single self-contained binary. Source builds report workmem dev for version; tagged release binaries carry the real vX.Y.Z plus commit SHA and build timestamp.
workmem speaks MCP over stdio. Add it to your client's config:
{
"mcpServers": {
"memory": {
"command": "/path/to/workmem"
}
}
}{
"mcpServers": {
"memory": {
"command": "/path/to/workmem"
}
}
}.vscode/mcp.json:
{
"servers": {
"memory": {
"command": "/path/to/workmem"
}
}
}/path/to/workmemNo arguments required. Configuration is optional via environment variables.
Facts don't live forever. workmem implements cognitive decay inspired by how human memory works:
effective_confidence = confidence * 0.5 ^ (age_weeks / stability)
stability = half_life * (1 + log2(access_count + 1))
- A fact recalled 0 times has stability equal to the half-life (12 weeks default)
- A fact recalled 3 times has stability of 24 weeks
- A fact recalled 7 times has stability of 36 weeks
- Frequently recalled facts resist decay. Forgotten facts fade naturally.
Decay is computed at read time. No background jobs.
Seven search channels feed a composite relevance score:
| Channel | Weight | What it matches |
|---|---|---|
fts_phrase |
1.15 | Adjacent terms in FTS |
fts |
1.0 | Any term match in FTS |
entity_exact |
0.9 | Exact entity name |
entity_like |
0.7 | Fuzzy entity name |
content_like |
0.5 | Substring in content |
type_like |
0.45 | Entity type match |
event_label |
0.4 | Event label match |
Final score blends relevance (70%) with decayed memory strength (30%), plus bonuses for FTS position and multi-channel hits.
remember({ entity: "API", observation: "rate limit 100/min", project: "~/my-app" })
Each project gets its own isolated SQLite database at <project>/.memory/memory.db, created lazily. Global memory (no project param) lives next to the binary.
12 MCP tools. No more, no less.
| Tool | Purpose |
|---|---|
remember |
Store a fact about an entity |
remember_batch |
Store multiple facts at once |
recall |
Search by free text (composite ranked) |
recall_entity |
Everything about one entity |
relate |
Link two entities |
forget |
Soft-delete a fact or entity |
list_entities |
Browse what's stored |
remember_event |
Group observations under a session/meeting/decision |
recall_events |
Search events by label, type, date |
recall_event |
Full event with all observations |
get_observations |
Fetch by ID (provenance) |
get_event_observations |
Fetch raw observations for an event |
recall accepts compact: true to return truncated snippets instead of full content. Use get_observations to expand specific items. This keeps context windows lean.
| Variable | Default | Description |
|---|---|---|
MEMORY_DB_PATH |
Next to binary | Path to the global SQLite database |
MEMORY_HALF_LIFE_WEEKS |
12 |
Decay half-life for global memory |
PROJECT_MEMORY_HALF_LIFE_WEEKS |
52 |
Decay half-life for project memory |
COMPACT_SNIPPET_LENGTH |
120 |
Max chars per observation in compact mode |
Some MCP clients (e.g. Kilo, opencode-derivatives) ignore the env block in their server config — only command and args are portable. Use the -env-file flag to load variables from a file:
workmem -env-file /path/to/.env
The parser mirrors the reference Node loader: KEY=value, single/double quotes, # comments, export KEY=value, BOM, CRLF. No variable interpolation, no multi-line, no escape sequences. Missing file is not an error (silent fallback to defaults).
Precedence: explicit process env > -env-file values > built-in defaults. A key already present in the environment — even set to an empty string — is never overwritten by the file.
A common pattern: one for general knowledge, one for private notes. The client sees them as separate tool namespaces:
{
"mcpServers": {
"memory": {
"command": "/path/to/workmem",
"args": ["-env-file", "/path/to/memory/.env"]
},
"private_memory": {
"command": "/path/to/workmem",
"args": ["-env-file", "/path/to/private-memory/.env"]
}
}
}Each .env holds that instance's MEMORY_DB_PATH, MEMORY_HALF_LIFE_WEEKS, and any other overrides — no duplication in the client config. For clients that support it, the env block still works and takes precedence over the file.
Add to your system prompt or CLAUDE.md:
## Persistent Memory
You have access to a persistent memory store. Use it proactively:
- **`remember`** when you learn something worth retaining across sessions
- **`recall`** at session start or when you need context (it's free — local SQLite)
- **`remember_event`** to group related facts under a session or decision
- **`forget`** to remove stale or incorrect facts
- **`relate`** to link entities with named relationships
Remember: preferences, corrections, names, decisions, conventions.
Don't remember: transient tasks, code snippets, things already in docs/git.SQLite with WAL mode. Tables: entities, observations, relations, events, memory_fts (FTS5). Schema created automatically. Soft-delete via deleted_at tombstones — forgotten facts are excluded from retrieval but remain in the database.
Produce an end-to-end encrypted snapshot with the backup subcommand. The snapshot is taken via VACUUM INTO (consistent, no lock on the live DB) and encrypted with age. The plaintext intermediate never leaves the temp directory; the output is written with 0600 permissions.
# single recipient
workmem backup --to backup.age --age-recipient age1yourpubkey...
# multiple recipients and/or a recipients file
workmem backup --to backup.age \
--age-recipient age1alpha... \
--age-recipient /path/to/recipients.txtRestore with the standard age CLI:
age -d -i my-identity.txt backup.age > memory.dbOnly the global memory DB is included. Project-scoped DBs live in their own workspaces and are out of scope. Telemetry data (if enabled) is operational and not included — rebuild freely.
- Stupidity of use, solidity of backend. The model doesn't think about memory. It just calls tools. The ranking, decay, and retrieval happen behind the curtain.
- 12 tools is the ceiling, not the floor. Every tool costs context tokens on every model invocation. Adding tool 13 requires strong evidence.
- Decay is the feature. What matters keeps surfacing. What doesn't, fades. This isn't a compromise — it's the mechanism.
- Evidence over intuition. The next feature ships when data says it should, not when it sounds interesting.
MIT — see LICENSE for the full text.
