apsolut-cortex
v0.14.0
Published
Persistent memory for Claude Code projects — stores corrections, decisions, and patterns across sessions
Maintainers
Readme
apsolut-cortex
Persistent memory for Claude Code. Corrections, decisions, and patterns that survive across sessions — so Claude stops repeating mistakes and forgetting what you decided last week.
Claude Code forgets everything between sessions. apsolut-cortex gives it a
durable, local memory: it captures tool failures, decisions, and your
corrections as they happen, compresses them into concise memories at the end of
each session, and injects the relevant ones back at the start of the next. All
of it runs automatically through Claude Code hooks — install once, then forget
it's there.
Everything lives on your machine in a single SQLite-compatible database. Nothing leaves except what you send to the Anthropic API for session compression (and that step runs fully local if you use Ollama instead).
Standalone — works on its own. Optional pairing: apsolut-seshat for a per-project markdown vault you curate by hand.
Documentation
Topic-focused docs live in docs/ — start there if you want the deep dive on one area:
- Operations — day-to-day runbook: health checks, logs, recovery
- Configuration — all
APSOLUT_CORTEX_*env vars - Storage — what lives under
~/.apsolut-cortex/and the DB schema - Compression providers — Anthropic API vs local alternatives
- Ollama setup — fully local compression, no API key
- Knowledge / research notes — memory-systems methodologies: implemented vs candidate
- Decision records — why libSQL, why async compression, etc.
Prefer one continuous read instead? The rest of this README covers install through troubleshooting.
Contents
- Install · Quick start · What's shipped
- How it works · MCP tools · Commands
- Configuration · Storage · Trust levels
- Troubleshooting · Project status · Contributing
Install
npm i -g apsolut-cortexThen, in any project:
cd your-project
apsolut-cortex initRestart Claude Code. Done.
Requirements
- Node.js ≥ 20
- A compression provider — set
ANTHROPIC_API_KEY, or run Ollama locally. Without one, cortex keeps captured observations for the next session and reports the failure in-session (a hooksystemMessageat session end, plus breaker state inapsolut-cortex status/doctor); nothing is silently lost. Note hooks inherit Claude Code's environment, not your shell profile — a key exported in.zshrcmay not reach them. - On Windows, hooks run through Git Bash. Slim/MinGit installs need one extra
setting — see Troubleshooting (or just run
apsolut-cortex doctor).
Dev setup (contributors)
git clone https://github.com/apsolut/apsolut-cortex.git
cd apsolut-cortex
bun install && bun run build
npm linkQuick start
After init and a restart, memory is fully automatic. To interact with it
directly, use the slash commands inside Claude Code:
/apsolut-recall <topic> search memory
/apsolut-store <content> save something explicitly
/apsolut-status show memory stats
/apsolut-forget <topic> delete a wrong memoryOr from your shell:
apsolut-cortex status # what's stored for this project
apsolut-cortex grep "some pattern" # substring search across memories
apsolut-cortex doctor # check hooks/env are healthyWhat's shipped
cortex is in active development. The core capture → compress → recall loop is stable and used daily; a couple of newer capabilities are opt-in and explicitly marked experimental. Nothing below is a promise — it either works today or it's flagged. See Project status for the honest caveats.
✅ Stable
| Capability | What it does |
|---|---|
| Automatic capture | Tool failures are stored, config-file reads are noted as discoveries, and the transcript is scanned for self-corrections when Claude stops. |
| Session-start injection | Last session summary + top relevant memories loaded at the start of every session; first session shows an onboarding guide. |
| Session-end compression | Observations are consolidated into memories via Claude Haiku (Ollama fallback), and a reflector pass merges large sessions into denser meta-memories. |
| Hybrid retrieval | BM25 + vector search, with a JSONL audit log of per-source ranks. apsolut-cortex correct flags the last retrieval as a miss and can store the fix in one gesture. |
| Range-linked memories | memory_recall(id) returns the raw conversation slice a compressed memory was derived from. |
| Visibility layer | export writes one markdown file per memory to an Obsidian-browseable vault, with compiled index / by-category / by-project / health views. Auto-exports on session end. |
| Curation CLI | promote / demote trust tiers, tag / untag, grep, and guarded single or bulk delete (every bulk delete previews and refuses without --yes). |
| Backup & restore | Snapshot the DB on demand; restore writes a safety snapshot first. |
| MCP tools | Six tools Claude can call directly (see below). |
| Cross-platform setup | Works on macOS, Windows, and WSL2. apsolut-cortex doctor diagnoses why hooks aren't firing. |
🧪 Experimental / opt-in
| Capability | Status |
|---|---|
| Encryption at rest | libSQL-native encryption via db re-encrypt, key stored in the OS keychain. Opt-in; default is plaintext. Works on Windows & macOS; not supported on native Linux. Key loss = data loss — always backup first. |
| In-session compression | Token-budget background worker + PreCompact safety capture. Compresses mid-session without blocking tool execution. Smoke-tested on limited hardware. |
🔜 Planned
- Provider-agnostic routing — Vercel AI SDK, token-tiered model routing, multi-provider health checks.
- Simplification pass — env-var audit, trust-tier collapse (4 → 2), taxonomy audit.
Full build order: docs/PHASE2-BUILD-ORDER.md
How it works
Everything is automatic. Hooks are registered per project, in
.claude/settings.local.json — they never fire in projects you haven't run
init in. Each hook exits before loading its database client when the project
has no .apsolut-cortex/project.json, so an uninitialised repository pays only
Node's startup.
Memories injected into context — at session start, per prompt, and after compaction — are framed as data and quoted. Memory text is written by past sessions and by the compressor, so an instruction stored inside a memory is never presented to Claude as a live directive.
Hooks fire across the Claude Code session lifecycle:
Session start
- Last session summary injected
- Top relevant memories loaded
- First session shows an onboarding guide
During the session
- Tool failures captured and stored (via
PostToolUse+ the dedicatedPostToolUseFailureevent) - Config-file reads noted as discoveries
- Transcript scanned for self-corrections when Claude stops
- Every user prompt checked for pushback ("no, that's wrong…") — detected corrections are stored and linked to the memory they likely contradict
- Relevant high-weight corrections/decisions injected per-prompt when they match what you're asking (disable with
CORTEX_PROMPT_INJECT=0) - Capture hooks run with
async: trueon Claude Code ≥ 2.1.196 — zero latency added to tool calls - Observations carry the
prompt_idof the prompt that caused them, for precise attribution
Session end
- Observations compressed into memories via Claude Haiku (Ollama fallback if no API key)
- Final transcript slice persisted to
raw_messagessomemory_recallcan return real history - Reflector pass consolidates large sessions into denser meta-memories
- Stale memories decay; low-value ones are pruned over time
- Vault auto-exported to
~/.apsolut-cortex/obsidian/
In-session (wired by init; install-hooks re-wires an existing install)
- Token budget exceeded → detached background worker compresses mid-session, never blocking tool execution
PreCompactevent → safety capture before Claude Code compacts its own context; runs in the background (asyncRewake) on Claude Code ≥ 2.1.196 and wakes Claude if the capture fails- After compaction →
SessionStartre-fires withsource: "compact"and cortex re-injects the top corrections/decisions, so the post-compaction context isn't memory-blind
MCP tools
Tools Claude can call directly during a session:
| Tool | When |
|------|------|
| memory_search(query) | /apsolut-recall, or when Claude is uncertain |
| memory_store(content, category, tier) | After a decision or discovery |
| memory_rate(id, score) | After using a retrieved memory (0–3) |
| memory_contradict(id, correction?) | When a memory is wrong |
| memory_status() | Overview of what's stored |
| memory_recall(id, offset?) | Need the exact wording / chronology a memory was derived from (capped at ~4k tokens; offset resumes) |
Commands
Setup
apsolut-cortex init # set up memory for this project (full hook set, project-scoped)
apsolut-cortex install-hooks # re-wire or repair the hook set (--global for machine-wide)
apsolut-cortex doctor # check project, hooks, MCP, DB, provider, Windows Git Bash
apsolut-cortex uninstall # remove hooks and MCP config (DB kept)Daily
apsolut-cortex status # show what's stored
apsolut-cortex grep <pattern> # substring search across this project's memories
apsolut-cortex export # write the Obsidian vault now (runs on session end too)
apsolut-cortex correct # flag the most recent retrieval as a miss
apsolut-cortex correct --with "the correct answer" # …and store the fix as a new memoryCuration
apsolut-cortex promote <id> # walk trust tier up: observed → … → canonical
apsolut-cortex demote <id> # walk it back down
apsolut-cortex tag <id> <tag> # apply a free-form label
apsolut-cortex untag <id> <tag>
apsolut-cortex delete --id <id>
apsolut-cortex delete --project <id> --yes
apsolut-cortex delete --tag <name> --yes
apsolut-cortex delete --before YYYY-MM-DD --yes
apsolut-cortex delete --grep <pattern> --yes
# all bulk deletes show a preview and refuse without --yesOps
apsolut-cortex migrate # apply pending schema migrations
apsolut-cortex backup # snapshot DB under ~/.apsolut-cortex/backup/
apsolut-cortex restore # list snapshots
apsolut-cortex restore <path> --yes # restore (writes a safety snapshot first)
apsolut-cortex db re-encrypt # dry-run encryption migration plan
apsolut-cortex db re-encrypt --yes # opt in to libSQL-native encryption at restEval (maintainer-only, run from a cloned repo)
apsolut-cortex eval run # score hybrid vs grep retrieval against golden.jsonl
apsolut-cortex eval baseline # snapshot scores for delta trackingCompression providers
Set one of these. Without a reachable provider, session-end compression reports
the failure through a hook systemMessage and keeps the observations for the next
session — nothing is lost, but no memories are extracted either. After three
consecutive failures a circuit breaker pauses attempts for an hour;
apsolut-cortex status and apsolut-cortex doctor both show its state:
Option 1 — Anthropic API
export ANTHROPIC_API_KEY="sk-ant-..."Option 2 — Ollama (free, local, private)
ollama pull qwen2.5-coder:7b
ollama serveOverride the model with APSOLUT_CORTEX_OLLAMA_MODEL=llama3.1, or the host with
OLLAMA_HOST=http://localhost:11434. See docs/OLLAMA.md.
Configuration
All env vars use the APSOLUT_CORTEX_ prefix and have sane defaults — most
people never set one. The five most commonly tweaked:
| Env var | Default | What it does |
|---|---|---|
| ANTHROPIC_API_KEY | (unset) | Required for primary compression (Haiku). Without it, falls back to Ollama. |
| APSOLUT_CORTEX_OBSERVE_THRESHOLD | 30000 | Conversation tokens that fire mid-session compression. |
| APSOLUT_CORTEX_DECAY_DAYS | 7 | Days unused before a memory's weight starts decaying, and the length of one decay window (decay applies at most once per window, per memory). |
| APSOLUT_CORTEX_DUPLICATE_THRESHOLD | 0.92 | Cosine similarity floor for the dedup-on-insert check. |
| APSOLUT_CORTEX_SHADOW | (unset) | When truthy, retrieval logs to ~/.apsolut-cortex/logs/shadow.jsonl without injecting. |
The full reference — all 21 vars, grouped by concern with descriptions and trade-offs — lives in docs/CONFIG.md.
Storage
~/.apsolut-cortex/
├── memory.db ← all memories, all projects, libSQL (Turso's SQLite fork)
├── registry.json ← project registry
├── models/ ← embedding model cache (downloads once)
├── logs/ ← retrievals.jsonl, corrections.jsonl, shadow.jsonl
├── obsidian/ ← exported vault (regenerated on session end)
├── buffer/ ← per-session compression lock + cursor
└── backup/ ← manual + pre-encrypt + pre-restore snapshotsAll projects share one DB, namespaced by project UUID. No data leaves your machine except what you send to the Anthropic API for session compression.
The on-disk format is libSQL — fully SQLite-compatible at the file level (any
sqlite3 CLI can read it) and Turso-compatible, so remote sync is a plausible
next step. It is not implemented: cortex only ever opens file: URLs and exposes
no URL/token configuration today. See docs/STORAGE.md for the
full schema.
Raw transcripts (raw_messages, the source memory_recall reads) are stored
unencrypted by default — encryption is opt-in, see below. <private>…</private>
blocks are stripped before anything is stored, and rows older than
APSOLUT_CORTEX_RAW_RETENTION_DAYS (90) are deleted at session end.
Memory trust levels
observed → validated → proven → canonicalMemories start at observed and are promoted automatically as they prove
useful. Canonical memories never decay.
Troubleshooting
Windows: hooks not firing?
Claude Code runs all command-type hooks through Git Bash on Windows —
not cmd, not PowerShell, not WSL. It resolves the real bash at
<git>\usr\bin\bash.exe. If that binary is missing, every hook (from cortex or
any other tool) fails with a non-blocking error like:
SessionStart:startup hook error
Failed with non-blocking status code: Skipping command-line
'"C:\Program Files\Git\bin\..\usr\bin\bash.exe"' (not found)The usual cause is a partial / MinGit Git for Windows install (the usr\
tree stripped out — common when another tool's installer overwrites your Git).
The <git>\bin\bash.exe stub still exists but is only a wrapper to that missing
binary, so pointing CLAUDE_CODE_GIT_BASH_PATH at it does not help.
Fix — install the full Git for Windows:
winget install --id Git.Git -e --source winget(or download it from https://git-scm.com/download/win), then fully quit and
relaunch Claude Code. That restores usr\bin\bash.exe and hooks work with no
extra config. If you have a real bash in a non-standard location, set
CLAUDE_CODE_GIT_BASH_PATH to that bash.exe instead.
Run apsolut-cortex doctor to diagnose it — it actually runs your resolved
bash (not just a file check), so it catches broken wrappers and prints the exact
remedy; init warns at install time too.
Project status
cortex is in active Phase 2 development. Most surface area is solid; a few pieces ship but are still being hardened. In brief:
- Encryption — experimental. Opt-in only; default is plaintext. Works cleanly
on Windows (Credential Manager) and macOS (Keychain). WSL2 needs
gnome-keyring-daemonrunning with thesecretscomponent. Native Linux is not supported — libSQL's local encryption errors withSQLITE_IOERR(the re-encrypt test suite is skipped on Linux CI), anddb re-encryptrefuses there with a clear message instead of producing an unreadable database. Key loss = data loss: always runbackupbefore enabling. - In-session compression — opt-in, new. The token-budget worker +
PreCompacthooks have only been smoke-tested on limited hardware. If a long session leaves stuck buffer files under~/.apsolut-cortex/buffer/,migrateclears the lock on next start; if compression itself fails, the session-end fallback still captures everything. - Eval signal — sparse. The harness ships with 5 seed entries; the "is hybrid retrieval worth it?" question won't have a defensible answer until that grows to 20+ paraphrased queries. The hybrid stack is the default until then.
- Provider routing & simplification — deferred. Compression is currently hardcoded to Anthropic Haiku → Ollama fallback, and the 4-tier trust ladder hasn't been collapsed yet.
If you're running cortex on a single dev machine with backups, none of this is alarming. For a higher-stakes setting, wait for a 1.0 cut.
Contributing
Issues and PRs welcome. See CONTRIBUTING.md for setup, code conventions, and migration-safety rules; SECURITY.md for disclosure; and CHANGELOG.md for release history.
