Scadrial · Keeper Knowledge · CLI

metalmind

A metalmind for Claude. Store decisions in copper. Tap them when you need them. Persistent memory for Claude. Save decisions. Recall them when you need them.

Scadrial flavor: commands are named after Mistborn Era 1 metals — copper stores memory, bronze reads the code graph, iron/steel navigate and rename symbols. Prefer plain verbs? Switch to in the top-right — every themed command has a plain alias (tap copperrecall, burn bronzegraph, etc.) and both always work at the CLI.

Every claude invocation is a first meeting. Yesterday's architectural call, the reason you rejected that library, the 40-minute debug you just finished — gone by tomorrow. metalmind is the place Claude stores things, and the way Claude gets them back — without burning context tokens on tool schemas to do it.

$ npm install -g metalmind
Live on npm · v0.5.3 Then run metalmind init.

Today, metalmind only supports Claude Code. Cursor, Codex, Copilot, and Gemini CLI are on the roadmap.


What it's actually for

metalmind pays off when your knowledge lives across more than one repo. A single-repo user gets a lot from Claude Code's native /memory — text in CLAUDE.md, free, no moving parts. A multi-repo engineer — same vault across every project, decisions that outlive any single codebase, code graphs that cross service boundaries — is who metalmind is built for.

Where native /memory still wins: solo repo, under ~50 notes of context, no historical lookback needed. Below the break-even, it's simpler and free. metalmind earns its install cost when you've got more to remember than a single CLAUDE.md can cheaply hold.


What it adds

Persistent memory that survives your claude ending. One CLI, one verb per concern. Code-graph capabilities live on a dedicated forge page — same project, separate pitch.

Persistent memory across sessions
store copper "<insight>"save "<insight>" deposits a decision into your local Obsidian vault. metalmind proposes the path, wikilinks, and frontmatter; you approve; it writes. Tomorrow's session recalls it as if yesterday never ended.
Recall without the MCP token tax
tap copper "<query>"recall "<query>" is a Bash call, not an MCP tool — zero schema bloat per session. Most memory tools silently inject a handful of tool schemas — often heavily over-specified — into every Claude Code session before you've typed a prompt. Measured: metalmind costs ~2.5× less than mem0 as shipped. We stamp the command into your CLAUDE.md so Claude reaches for it naturally. --deep follows backlinks; --expand returns hits plus the surrounding graph.
Measured at scale on the recall bench (bench/recall-v0/, 12 hand-authored gold notes + up to 1,000 seeded same-domain distractors, 20 queries, v0.5.0):
vault hit@1 hybrid hit@5 hybrid hit@1 +rerank hit@5 +rerank median
12 notes90%95%90%95%6 ms
100 notes85%95%90%95%7 ms
500 notes85%90%90%95%7 ms
1,000 notes85%85%90%95%8 ms

v0.5.0 runs the entire retrieval stack in-process — sqlite-vec for vectors, fastembed (BAAI/bge-small-en-v1.5) for embeddings, FTS5 for keyword. No Docker, no Ollama daemon. Hybrid hit@1 holds 85% at every scale; switching to bge-small-en-v1.5 from the previous nomic-embed-text lifted pure-semantic hit@5 at 1,000 notes from 55% to 90%. Median hybrid latency dropped 43 ms → 8 ms — no HTTP RTT on the hot path. Rerank (--rerank, opt-in) adds a cross-encoder rescore at ~2 s per query for the +5pp lift to hit@5 = 95%.

vs qmd 2.1.0 on the same fixture
vault metalmind hit@1
+rerank
qmd hit@1 metalmind hit@5
+rerank
qmd hit@5
12 notes90%85%95%100%
100 notes90%70%95%100%
500 notes90%85%95%90%
1,000 notes90%80%95%90%

qmd ships the same shape (BM25 + vector + RRF + cross-encoder rerank) but different defaults — qwen3-reranker, a fine-tuned 1.7B query expansion, GGUF model stack, ~2 GB on first run. The fixture is identical: same 12 gold + same 988 seeded distractors + same 20 paraphrase queries. qmd wins hit@5 at small scales (more recall headroom from query expansion); metalmind wins hit@1 across the curve (better top-of-list precision after the v0.4.0 weighted-RRF fix). Both are reasonable choices — pick the one whose shape fits your workflow.

The tax we avoid is the standing tool-schema cost, not the result tokens — hit payloads are billed like any other bash output.

Vault writes without drift
scribe <verb>note <verb> is the CRUD interface agents use instead of raw Write — full flow: create · update · patch · delete · archive · rename · list · show. It stamps frontmatter, picks the right folder (Plans/Learnings/Work/Daily/Inbox/MOCs/Archive), auto-links the project MOC, and on rename rewrites [[wikilinks]] across the vault. Body on stdin; every verb supports --dry-run.
Daily notes that roll themselves forward
atium new | adddaily new | add creates or appends to Daily/YYYY-MM-DD.md with checkbox action items. metalmind routine install eodmetalmind routine install eod schedules a launchd agent at 17:30 Mon–Fri that runs atium new --date next-workday --from <today> and archives today's note via goldscribe archive. Unchecked - [ ] items move forward; - [x] stays behind. No manual EOD ritual, no hand-rolled cron — the primitives all compose through the CLI.
Reversible to zero
metalmind uninstall stops containers, unloads the watcher service (launchd on macOS, systemd on Linux), restores your prior output style, clears the settings we changed, removes shell aliases. Never touches your notes. Everything we did is undoable in one command — a design constraint, not an afterthought.
And: cross-repo code graph forge →
metalmind also ships a graphify-backed cross-repo code graph with HTTP-route-match edges, OpenAPI-shelf-driven route extraction, symbol navigation, coordinated rename, and team-debug dispatch. Different job from memory — so it has its own page. Use the forge if Claude needs to answer "which service calls this handler?" across five repos.

How metalmind compares

Five "memory for AI" tools, side-by-side on the dimensions that actually differ. Recall numbers on a fixed corpus are above; this is the shape comparison — what each tool stores, how the agent reaches it, and where your data lives.

Dimension metalmind qmd mem0 Letta Mastra
Memory primitive Markdown chunks (file-mapped) Markdown chunks (file-mapped) LLM-extracted facts Managed memory blocks Threads + working memory
Source preservation Yes — notes intact Yes — files intact No — facts replace docs Partial — buffer + summaries Yes — message history
Recall determinism Deterministic Deterministic LLM extracts on every write LLM mediates updates Deterministic + LLM summary
How agents call it Bash → loopback HTTP Bash CLI (MCP optional) MCP server or SDK HTTP server (own runtime) TS framework API
Standing tokens in Claude Code ~519 (prose in CLAUDE.md) ~0 (CLI; MCP opt-in) ~1,319 (3 MCP schemas) n/a — different host model n/a — different host model
Where state lives Local Obsidian vault + sqlite-vec Local files + sqlite Cloud or self-hosted vector DB Cloud or self-hosted Letta server Pluggable (pgvector / cloud)
Walk-away cost Zero — vault is plain markdown Zero — files stay readable Export needed; facts ≠ docs Export needed; data lives in Letta Depends on chosen store

Letta and Mastra are agent frameworks with built-in memory — a different category from metalmind, qmd, and mem0 (memory tools you bolt onto an existing host). Listed anyway because they surface in "memory for AI" searches; "different host model" rows are honest about the apples-vs-oranges shape, not a metalmind win.

Pick metalmind when your notes are the source of truth and you want Claude Code to read them verbatim with zero standing tax. Pick mem0 for fact extraction from conversations. Pick Letta or Mastra when you're building agents inside their framework. Pick qmd if you want the same shape on a non-Claude host.


Why it isn't an MCP server

Most memory tools register themselves as MCP servers. That design injects a handful of tool schemas (search_memory, recall, store, …) into every Claude session before you prompt anything. Those schemas eat context tokens you could be using for the actual task.

metalmind takes the opposite bet: the recall surface is a CLI, Claude learns the command once from your stamped CLAUDE.md, and every session starts with a clean context. The watcher, indexer, and embedding stack still live locally — they just don't live in Claude's tool registry.

Measured in bench/mcp-tax-v0/ — first-turn token tax on a cold session, before any user turn:

SystemTransportToolsFirst-turn tokens
metalmind (default)loopback HTTP0~519 (one-time CLAUDE.md block)
Claude Code native /memoryCLAUDE.md text0~1
metalmind (stdio MCP fallback)MCP stdio3~157
mem0 (pinkpixel-dev/mem0-mcp)MCP stdio3~1,319

~2.5× lower than mem0 as shipped (loopback-HTTP vs stdio MCP) · ~8.4× lower on the apples-to-apples MCP comparison (metalmind's stdio fallback vs mem0 — same transport, different schema discipline).

The ~519 tokens metalmind spends up front are prose in ~/.claude/CLAUDE.md that teaches Claude when to recall — work that mem0's schema-tax doesn't do. Approximation via chars / 4; rerun with pnpm bench:mcp-tax (add ANTHROPIC_API_KEY for exact counts). Shape is what matters: two zero-schema transports, one modest, one bloat. Per-call result tokens are billed like any tool output and are excluded here — this is standing cost before a single call.


Who should NOT use metalmind

Honest anti-personas — install the wrong tool and you'll bounce in an hour:


Will this still be around?

Fair question for any solo-maintainer tool. The sustainability story:


Commands

Metal Command Description
Copper ↓ $ metalmind store copper "..." $ metalmind save "..." Deposit an insight to the vault.
Copper ↑ $ metalmind tap copper "<q>" $ metalmind recall "<q>" Retrieve semantically (--deep / --expand).
Bronze $ metalmind burn bronze "<q>" $ metalmind graph "<q>" Query the code graph (Seeker).
Iron $ metalmind burn iron "<sym>" $ metalmind symbol "<sym>" Pull a symbol and its neighbors.
Steel $ metalmind burn steel <o> <n> $ metalmind rename <o> <n> Coordinated rename via Serena.
Tin $ metalmind burn tin $ metalmind verbose Enhanced output — verbose toggle.
Pewter $ metalmind burn pewter $ metalmind reindex Force-rebuild the code graph.
Zinc $ metalmind burn zinc "<bug>" $ metalmind debug "<bug>" Rioter — dispatch a team-debug session.
Aluminum $ metalmind burn aluminum $ metalmind uninstall Reversible teardown (wipes install).
Brass $ metalmind burn brass $ metalmind stamp Soother — re-imprint metalmind managed files (upgrade in place).
Atium $ metalmind atium new | add $ metalmind daily new | add Future daily notes — --date today|tomorrow|next-workday|YYYY-MM-DD, --from carries unchecked items.
Gold $ metalmind gold <note> $ metalmind scribe archive <note> Burn gold — archive a note (move to Archive/).
Flare $ metalmind flare banner | dialog | sticky $ metalmind notify banner | dialog | sticky macOS desktop notifications — amplify a signal.
Routine $ metalmind routine install eod $ metalmind routine install eod Schedule EOD carry-forward + archive — launchd agent 17:30 Mon–Fri (macOS).
Forge $ metalmind forge create <g> $ metalmind group create <g> Define a cross-repo group.
Forge $ metalmind forge add <g> <r> $ metalmind group add <g> <r> Add a repo to the group.
Forge $ metalmind forge capture-spec <r> <url> $ metalmind group capture-spec <r> <url> Seed the OpenAPI shelf for cross-repo route edges.
Forge $ metalmind burn bronze "<q>" --forge <g> $ metalmind graph "<q>" --group <g> Query across every repo in the group (--include-literals for URL-literal fallback).
Scribe $ metalmind scribe <verb> $ metalmind note <verb> Vault CRUD — create · update · patch · delete · archive · rename · list · show.
Release $ metalmind release-check $ metalmind release-check Preflight before tagging — working tree, branch, version sync, tests, build, stamped block.
Seeker $ metalmind pulse $ metalmind doctor Pulse-check the install — prereqs, config, MCP state.

Both spellings always work at the CLI. Use the Scadrial / Classic toggle in the nav to re-spell the page.


Install flow

A single metalmind init drives the whole install — and every step is reversible via metalmind uninstall, which never touches your notes. Scroll the rail below to trace what happens.

  1. Prereqs, detected

    Claude Code, Python 3.11+, uv, git — each checked with a per-failure remediation. Failing prereqs halt the wizard with a concrete fix. Docker is only required when you opt into --legacy.

    ◆  Claude Code   2.1.114
    ◆  Python 3.11+  via python3.12
    ◆  uv            0.11.7
    ◆  git           2.53.0
  2. Vault scaffold

    Picks or creates your Obsidian vault. Drops in Work / Personal / Learnings / Daily / Inbox / Archive / Memory, stamps a CLAUDE.md that teaches Claude to recall via CLI — not MCP tools.

    ◇  Setting up vault
    ◆  Vault at ~/Knowledge
       created: Work, Personal, Learnings, Daily, Inbox, Archive, Memory
  3. Engines installed

    Three Python tools land on PATH via uv — Serena (LSP navigation), graphify (code graph), vault-rag (recall server + watcher + indexer + doctor). Mirror of how you install anything else.

    ◇  Installing Serena      → uv tool install serena-agent
    ◇  Installing graphify    → uv tool install graphifyy
    ◇  Installing vault-rag   → uv tool install metalmind-vault-rag
  4. In-process retrieval stack

    sqlite-vec for vectors and fastembed (BAAI/bge-small-en-v1.5) for embeddings live inside the watcher process — no Docker, no daemon. The 30 MB ONNX model downloads once on first index. Pass --legacy to opt into the older Qdrant + Ollama Docker stack instead.

    ◇  Embedded backend (sqlite-vec + fastembed) — no Docker stack needed
    ◆  Python tool venv at ~/.local/share/uv/tools/metalmind-vault-rag
       model cache at ~/.cache/fastembed/
  5. Services, hook & routing

    Auto-reindex watcher runs as a background service — launchd on macOS, systemd --user on Linux. MCP registers Serena only; vault recall is a CLI call (no schema tokens). A SessionStart hook injects a memory-available reminder so fresh Claude sessions discover the vault without prompting. Optional: disable native auto-memory and route all memory to the vault.

    ◇  Installing watcher service
    ◆  wrote com.metalmind.vault-indexer.plist
    ◇  Installing SessionStart hook
    ◆  registered in settings.json
    ◇  Applying memory routing
    ◆  disabled native auto-memory
  6. Ready

    metalmind pulse verifies the whole chain end-to-end. metalmind uninstall rolls everything back — watcher, MCP entries, settings, aliases (and Docker containers if you used --legacy) — without ever touching your notes.

    $ metalmind pulse
    ◇  Prerequisites
    ◇  Config
    ●  flavor:     scadrial
    ●  vaultPath:  ~/Knowledge
    ●  mcp:        serena, graphify
    └  All systems nominal.

Under the metalmind Under the hood

One verb, one job. Each engine is swappable — so if a backend gets replaced later, your muscle memory doesn't move. Your notes, embeddings, and code graphs never leave your machine.

Vault
Obsidian at ~/Knowledge/. sqlite-vec + fastembed (BAAI/bge-small-en-v1.5) run in-process — no Docker, no daemon. Incremental watcher re-embeds only changed files; queries stay answerable throughout reindex.
Serena
LSP-backed symbol navigation and coordinated renames. Zero-LLM per query.
graphify
Per-repo code graph — clusters, god nodes, call paths. Tree-sitter AST; optional LLM for docs.
Forge Group
Cross-repo merge layer — name-match + HTTP-route edges tagged with INFERRED_NAME / INFERRED_ROUTE provenance.