Persistent local memory for Claude Code. Longer sessions. Cross-session recall. Zero cloud.
Ψ 47 ●●○○○ 45% ✓ 5m
| Problem | Cortex Solution |
|---|---|
| Session limits hit mid-task | Auto-save on context step (5%) & session end |
/clear wipes everything |
SQLite persistence survives clears |
| Re-explaining context every session | Hybrid search recalls relevant memories |
| Cloud memory privacy concerns | 100% local — ~/.cortex/memory.db |
- Node.js: 18.0.0 or higher
- Disk Space: ~500MB (Includes 150MB embedding model cache + database growth)
- OS: macOS or Linux (Windows supported via WSL2)
Inside a Claude Code instance:
Step 1: Add the marketplace
/plugin marketplace add hjertefolger/cortex
Step 2: Install the plugin
⚠️ Linux users: Click here first
On Linux, /tmp is often a separate filesystem (tmpfs), which causes plugin installation to fail with:
EXDEV: cross-device link not permitted
Fix: Set TMPDIR before installing:
mkdir -p ~/.cache/tmp && TMPDIR=~/.cache/tmp claudeThen run the install command below in that session.
/plugin install cortex
Step 3: Run the setup wizard
Ask Claude to run the setup skill:
Please setup Cortex (or run /cortex-setup manually)
The wizard will initialize the database, download the embedding model, and configure the statusline.
Step 4: Customize settings
Ask Claude to configure your preferences:
Please configure Cortex (or run /cortex-configure manually)
Done! Restart Claude Code to activate the statusline.
To update Cortex to the latest version:
Step 1: Update the marketplace
/plugin marketplace update cortex
Step 2: Update the plugin
/plugin update cortex
Step 3: Run the setup wizard
Ask Claude to run the setup skill:
Please setup Cortex (or run /cortex-setup manually)
The wizard will install dependencies and download the embedding model for the new version. Your memories and configuration in ~/.cortex/ are preserved.
Step 4: Restart Claude Code to load the new version.
Note: Due to a known Claude Code issue, always update the marketplace first. After restart, you can ask Claude to verify the update by checking if the cached plugin matches the latest release.
The statusline is configured automatically by /cortex-setup. Restart Claude Code after setup to see it.
Ψ 47 ●●○○○ 45% ✓ 5m
Ψ— Cortex identifier47— Memory fragment count (compact: 1.1K, 10.2M, etc.)●●○○○— Context usage (filled/empty circles, color-coded)45%— Context percentage⠋ Saving— Animated spinner during background save✓ Autosaved— Success indicator (shows for ~5s after save)✓ 5m— Time since last save
┌──────────────────────────────────────┐
│ Claude Code │
└──────────────┬───────────────────────┘
│ stdin (JSON)
┌──────────────▼───────────────────────┐
│ Command Router │
│ (src/index.ts) │
└──────────────┬───────────────────────┘
│
┌───────────────────────────┼───────────────────────────┐
│ │ │
┌──────────▼──────────┐ ┌──────────▼──────────┐ ┌──────────▼──────────┐
│ Database │ │ Embeddings │ │ Search │
│ (sql.js/WASM) │ │ (Nomic Embed v1.5) │ │ (Vector + FTS5) │
│ + FTS5 + Backup │ │ 768 dims │ │ + RRF Fusion │
└──────────┬──────────┘ └──────────┬──────────┘ └──────────┬──────────┘
│ │ │
└───────────────────────────┼───────────────────────────┘
│
┌──────────────▼───────────────────────┐
│ ~/.cortex/memory.db │
│ (SQLite + Embeddings) │
└──────────────────────────────────────┘
| Module | Lines | Responsibility |
|---|---|---|
index.ts |
836 | Command router, hooks, statusline |
mcp-server.ts |
850 | MCP protocol, 11 tools exposed |
database.ts |
1143 | SQLite, FTS5, backups, recovery |
archive.ts |
873 | Transcript parsing, chunking |
embeddings.ts |
337 | Nomic Embed v1.5, quantization |
search.ts |
308 | Hybrid search, RRF fusion |
config.ts |
563 | Zod validation, presets |
analytics.ts |
288 | Session tracking, insights |
Total: ~5,700 lines TypeScript
Cortex uses a hybrid search combining three signals:
┌─────────────────────────────────────────────────────────────────┐
│ Query: "auth flow" │
└─────────────────────────┬───────────────────────────────────────┘
│
┌────────────────┼────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Vector │ │ FTS5 │ │ Recency │
│ Search │ │ Keyword │ │ Decay │
│ (60%) │ │ (40%) │ │ (7-day) │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└───────────────┼───────────────┘
▼
┌──────────────────┐
│ RRF Fusion │
│ (k=60) │
└────────┬─────────┘
▼
┌──────────────────┐
│ Ranked Results │
└──────────────────┘
- Vector similarity: Cosine distance on 768-dim embeddings
- FTS5 keyword: BM25 ranking with sqlite full-text search
- RRF fusion:
1/(k + rank)aggregation across both lists - Recency decay: 7-day half-life weights recent memories higher
Skills are for multi-step workflows. Atomic operations use MCP tools directly.
| Command | Purpose |
|---|---|
/cortex-setup |
First-time initialization wizard |
/cortex-configure <preset> |
Apply configuration preset |
/cortex-manage |
Delete or manage memories |
| Tool | Purpose | Side Effects |
|---|---|---|
cortex_recall |
Search memory | Read-only |
cortex_remember |
Save specific insight | Creates memory |
cortex_save |
Archive full session | Creates memories |
cortex_stats |
Get statistics + list projects | Read-only |
cortex_restore |
Get restoration context | Read-only |
cortex_analytics |
Usage insights | Read-only |
cortex_update |
Update memory content/project | Modifies memory |
cortex_rename_project |
Bulk rename project | Modifies memories |
cortex_delete |
Delete memory | Destructive |
cortex_forget_project |
Delete project memories | Destructive |
cortex_remember("JWT refresh tokens must use httpOnly cookies")
→ Creates ONE memory fragment from the string
cortex_save()
→ Parses transcript, extracts HIGH-VALUE content, creates MULTIPLE fragments
| Hook | Trigger | Behavior |
|---|---|---|
SessionStart |
New session | Shows memory count, injects awareness + restoration context |
PostToolUse |
After any tool | Monitors context %, triggers auto-save |
PreCompact |
Before /clear |
Archives session, prepares restoration |
Location: ~/.cortex/config.json
{
"statusline": {
"enabled": true,
"showFragments": true,
"showLastArchive": true,
"showContext": true
},
"archive": {
"projectScope": true,
"minContentLength": 50
},
"autosave": {
"onSessionEnd": true,
"onPreCompact": true,
"contextStep": {
"enabled": true,
"step": 5
}
},
"restoration": {
"tokenBudget": 2000,
"messageCount": 5,
"turnCount": 3
},
"awareness": {
"enabled": false,
"userName": null,
"timezone": null
}
}/cortex-configure full # All features (statusline, auto-archive, warnings)
/cortex-configure essential # Statusline + auto-archive only
/cortex-configure minimal # Commands only, no automation| Setting | Default | Description |
|---|---|---|
autosave.contextStep.step |
5 | % context increase to trigger auto-save |
autosave.onPreCompact |
true | Archive session before /compact or /clear |
autosave.onSessionEnd |
true | Archive session on exit (Ctrl+C x2) |
restoration.tokenBudget |
2000 | Max tokens for key decision restoration |
restoration.turnCount |
3 | Number of raw conversation turns to restore |
awareness.enabled |
false | Inject user/date/time at session start and after clear |
awareness.userName |
null | User name shown in awareness context |
awareness.timezone |
null | IANA timezone (null = auto-detect, "off" = omit date/time) |
CREATE TABLE memories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content TEXT NOT NULL,
content_hash TEXT UNIQUE, -- SHA256 for deduplication
embedding BLOB NOT NULL, -- 768 × float32 = 3KB
project_id TEXT, -- NULL for global scope
source_session TEXT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_memories_project_id ON memories(project_id);
CREATE INDEX idx_memories_timestamp ON memories(timestamp);
CREATE INDEX idx_memories_content_hash ON memories(content_hash);
-- Optional FTS5 (graceful fallback to LIKE if unavailable)
CREATE VIRTUAL TABLE memories_fts USING fts5(content);- Auto-backup: Created on database open
- Rotation: Keeps 5 most recent backups
- Recovery: Tests each backup until one validates
- Atomic writes: temp file + rename pattern
~/.cortex/
├── memory.db # SQLite database (~2-3MB per 1000 memories)
├── memory.db.backup.* # Rotated backups (max 5)
├── config.json # User configuration
├── analytics.json # Session tracking
└── autoSaveState.json # Transient state
npm testNote:
npm testruns with synthetic/fake data for speed and isolation.
To test against a real production transcript (35MB+):
-
Locate a real transcript: Find a large
.jsonlfile in your~/.claude/directory. -
Place it in tests: Copy it to
tests/sample_transcript.jsonl.cp ~/.claude/your-session-id.jsonl tests/sample_transcript.jsonl -
Run the E2E test:
node --test tests/e2e_transcript.test.js
200+ tests covering:
- Database CRUD, deduplication, recovery
- Vector and keyword search (Hybrid RRF)
- Archive parsing, chunking, and value extraction
- MCP tool handlers and stdin processing
- Configuration presets and validation
- Real-world E2E transcript replay
✔ Analytics Module (14 tests)
✔ Archive Module (35 tests)
✔ Config Module (5 tests)
✔ Database Module (38 tests)
✔ Embeddings Module (8 tests)
✔ Search Module (31 tests)
✔ Integration Tests (21 tests)
✔ MCP Tool Handlers (5 tests)
✔ Auto-Save Logic (10 tests)
✔ E2E Transcript Replay (2 tests)
ℹ tests 231
ℹ suites 54
ℹ pass 231
ℹ fail 0
npm install # Install dependencies
npm run build # Build index.js + mcp-server.js + copy WASM
npm run build:index # Build main entry only
npm run build:mcp # Build MCP server only
npm run typecheck # TypeScript strict check
npm test # Run test suitedist/
├── index.js # 302KB - Main entry point
├── mcp-server.js # 277KB - MCP server
└── sql-wasm.wasm # 660KB - SQLite WebAssembly
# Test stats command
echo '{"cwd":"/tmp/test"}' | node dist/index.js stats
# Test recall
echo '{"cwd":"/tmp/test"}' | node dist/index.js recall "authentication"
# Test MCP server
echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | node dist/mcp-server.js| Operation | Typical Latency |
|---|---|
| Embedding generation | ~100ms |
| Vector search (1000 memories) | ~50ms |
| FTS5 keyword search | ~10ms |
| Hybrid search (combined) | ~100ms |
| Database insert | ~5ms + embedding |
Memory footprint: ~50MB base (includes Nomic Embed model)
- Node.js: ≥18.0.0
- Claude Code: ≥2.0.12
- Disk: ~50MB for model + database
Cortex implements defensive error handling:
- Database corruption: Auto-recovery from rotated backups
- FTS5 unavailable: Graceful fallback to LIKE queries
- Embedding failures: Logged, operation continues
- Stdin parse errors: Discriminated union with context
- Missing config: Auto-created with defaults
- Zero cloud: All data local to
~/.cortex/ - No telemetry: No external network calls
- Plaintext storage: Acceptable for local-only use
- Deduplication: SHA256 hash prevents duplicates
/cortex-manage
rm -rf ~/.cortex
/cortex-setupsqlite3 ~/.cortex/memory.db "SELECT id, substr(content, 1, 50), timestamp FROM memories ORDER BY timestamp DESC LIMIT 10;"ls -la ~/.cache/huggingface/hub/models--nomic-ai--nomic-embed-text-v1.5/Thanks to everyone who has contributed to Cortex!
- @mikelane — Statusline chaining (#1)
- @djayatillake — Setup command fix (#4)
- @tkemperman — MCP notification fix (#7)
MIT
Tomas Krajcik
- Website: rootdeveloper.dev
- Email: support@rootdeveloper.dev
Built for developers who value their context.