The Obsidian that populates itself so your Claude never forgets.
Project description
mnemo
The Obsidian that populates itself so your Claude never forgets.
mnemo is a Claude Code plugin that turns every coding session into a self-organizing knowledge base — and then feeds that knowledge back into Claude so it stops forgetting what you taught it last week.
It runs as a hooks-only, stdlib-only Python package. Zero third-party dependencies, zero network calls. Identical on Linux, macOS, and Windows.
Install
One command:
npx @xyrlan/mnemo install
That installs the Python package (via uv / pipx / pip --user, whichever is available), prompts you to choose global (every Claude Code session) or project (this directory only), wires the hooks + MCP server + slash commands, and you're done.
Non-interactive variants:
npx @xyrlan/mnemo install --yes # global, no prompts
npx @xyrlan/mnemo install --project --yes # project-scoped, no prompts
npx @xyrlan/mnemo uninstall [--scope global|project|both] reverses everything; the vault is preserved.
Prerequisites: Python 3.8+ on your machine. Node is already there if you run npx. mnemo's npm wrapper is zero-dep and ~150 LOC — it's a thin bootstrap, not the runtime.
mnemo init (run automatically by npx … install) is idempotent and does four things:
- Scaffolds a vault at
~/mnemo/(or wherever you point it) - Injects four hooks into
~/.claude/settings.json:SessionStart,SessionEnd,PreToolUse(rule activation, v0.5), andUserPromptSubmit(Prompt Reflex, v0.8) - Registers a stdio MCP server in
~/.claude.jsonso Claude Code can call mnemo's tools - Wires an additive status line composer (preserves your existing one if you have it)
- Writes
~/.claude/commands/<name>.mdslash command files so/init,/status,/doctor, etc. work inside Claude Code without a separate plugin install
That's it. Use Claude Code normally — your vault populates itself, the HOME dashboard regenerates after every extraction, and Claude starts consulting captured rules on its own.
Alternative install paths
If you'd rather skip npm entirely:
pipx install mnemo-claude # or: uv tool install mnemo-claude
mnemo init # global
mnemo init --project # project-only
The PyPI distribution is named mnemo-claude (the bare mnemo slot was already taken on PyPI by an unrelated package). The CLI binary, import name, and project repository are still mnemo — only pipx install / pip install see the dashed name.
If you'd rather wire the slash commands via Claude Code's marketplace:
/plugin marketplace add xyrlan/mnemo
/plugin install mnemo@mnemo-marketplace
(The marketplace path requires pipx install mnemo-claude first — /plugin install registers slash commands but does not install the Python runtime.)
Installation scope: global vs project (v0.12+)
By default mnemo init installs globally — every Claude Code session,
in any directory, fires mnemo. Pass --project (alias --local) to scope
the install to the current directory only:
mnemo init --project
This writes to <cwd>/.claude/settings.json + <cwd>/.mcp.json, scaffolds
a self-contained vault at <cwd>/.mnemo/, and appends .claude/ and
.mnemo/ to your project's .gitignore. ~/.claude/settings.json,
~/.claude.json, and ~/mnemo/ stay untouched. Claude Code only loads
mnemo when launched in that directory; sessions in any other path get the
unmodified global behavior (or no mnemo at all).
Use this when you want to:
- evaluate mnemo on a single project before committing to it globally,
- isolate per-project memory so different repos don't share a vault,
- ship a repo where mnemo is part of the dev environment without forcing
collaborators to install it globally — they get it automatically when
they
cdinto the project.
mnemo uninstall --project removes only the local install (the vault is
preserved, same as global uninstall). mnemo status --scope project|global|all
reports both scopes independently — Claude Code itself decides which is
"active" for any given session based on cwd.
Note on portability: hook commands embed an absolute Python path (
sys.executable), so<cwd>/.claude/settings.jsonis gitignored by default. Each developer runs their ownmnemo init --projectto wire their interpreter.
How it works — Capture → Present → Inject → Reflex
mnemo's tagline is one sentence: "so your Claude never forgets." That breaks into four stages, each shipped in a different release, all live together from v0.8 on.
1. Capture (v0.2 → v0.3.1)
mnemo watches Claude Code's lifecycle hooks and writes a structured trail into your vault as you work:
- Session start / end markers —
🟢and🔴inbots/<repo>/logs/YYYY-MM-DD.md - Claude memory mirror — anything Claude saves to
~/.claude/projects/*/memory/is mirrored intobots/<repo>/memory/so it lives next to the rest of the trail - Per-session briefings (opt-in, v0.3.1) — at session end, an LLM pass
summarizes the full transcript into a structured handoff document under
bots/<repo>/briefings/sessions/. Briefings are the dense input that feeds extraction; they're the difference between mnemo capturing ~1 file/day vs. capturing every meaningful decision.
The extraction pipeline (mnemo extract, also auto-run after sessions
when enabled) consolidates everything in bots/ into canonical Tier 2
pages under shared/{feedback,user,reference,project}/. Single-source
pages auto-promote into the canonical layer; multi-source clusters
(cross-agent merges) stage in shared/_inbox/<type>/ for review. Your
manual edits to auto-promoted files are protected by content-addressing —
a conflict produces a .proposed.md sibling instead of overwriting your
work.
2. Present (v0.4)
Capture without surfacing is just a dump. v0.4 added two consumer surfaces over the same data:
- HOME.md dashboard — a managed block at the top of
HOME.mdregenerates after every extraction. Pages are grouped by trust tier (cross-agent synthesized first, single-source auto-promoted second) and by topic tag, so you can scan the project brain in seconds inside Obsidian. Everything below the managed block is yours to edit; mnemo never touches it. - Dimensional tags — extraction asks the LLM to tag each page with
topic kebab-case identifiers (
auth,react,package-management), using a controlled-vocabulary hint built from your existing vault tags to prevent sprawl. Tags become the ontology Claude navigates in v0.5.
3. Inject (v0.5)
The loop closes. Claude Code itself reaches into mnemo at the start of every session, no manual command needed.
- MCP stdio server —
mnemo initregisters a long-running JSON-RPC server in~/.claude.jsonexposing three read-only tools. All three default toscope="project"(only rules owned by the current repo); passscope="vault"for cross-project lookups:list_rules_by_topic(topic, scope?)— slugs sorted by source count desc (multi-agent synthesized rules surface first)read_mnemo_rule(slug, scope?)— full body + frontmatterget_mnemo_topics(scope?)— sorted union of topic tags
- SessionStart topic injection (opt-in) — the SessionStart hook emits a ~120-token instruction listing the topics in your vault and telling Claude to call the MCP tools BEFORE writing code when the task matches a known topic. ~120 tokens regardless of vault size: the topic list is the only thing pre-loaded; rule bodies are fetched on demand.
- Filter parity — both the dashboard and the MCP tools call the same
is_consumer_visiblepredicate, so evolving and needs-review pages never reach Claude.
The result: Claude consumes in real time the rules you taught it weeks earlier in a different session, without you having to remember to copy them in.
4. Reflex (v0.8)
SessionStart tells Claude what topics exist. PreToolUse fires when Claude touches a file. Reflex closes the gap in between: it reacts to the actual prompt you just typed and injects the single most relevant rule before Claude thinks about the question.
- BM25F retrieval — the
UserPromptSubmithook tokenizes your prompt (lowercase + fenced-code stripping + EN/PT stopwords), scores every consumer-visible rule across five weighted fields (name,topic_tags,aliases,description,body), and returns the top candidate. Pure stdlib; p50 1.3 ms / p95 1.7 ms on a 500-rule vault. - Triple-gate confidence test — silence is the default. A rule is injected only if it clears all three gates: term-overlap ≥ 2, relative gap ≥ 1.5× over the runner-up, and absolute score ≥ 2.0. Low-confidence matches stay silent; wrong context is worse than no context.
aliases:bridges — rules can declare a list of lowercase synonym tokens (e.g.aliases: [banco, database, db]) so a prompt in Portuguese activates an English-written rule. Extraction auto-emits aliases when the rule body carries domain terms with bilingual synonyms.- Session-lifetime dedupe — a rule injected once is not re-injected
the same day (
injected_cachein.mnemo/mcp-call-counter.json). ThePreToolUseenrichment path reads the same cache so Reflex and enrichment never double-surface the same rule in one session. - Cap — each session emits at most
reflex.maxEmissionsPerSession(default 10). Hitting the cap logssilence_reason: session_cap_reachedfor doctor to analyze. - Fail-open absolute — any exception anywhere in the pipeline returns exit 0 with empty stdout. Reflex can never stall a prompt.
Last-briefing handoff (v0.10+)
When briefings.injectLastOnSessionStart is true (default), every new Claude
Code session in a project whose canonical agent has at least one briefing
on disk gets a [last-briefing session=… date=… duration_minutes=…] … [/last-briefing]
block appended to the SessionStart injection envelope. Worktrees of the same
repo share one briefing pool, resolved via .git worktree pointers.
If you have orphan worktree briefings from before this change, run
mnemo migrate-worktree-briefings --repos /path/to/repo --dry-run to preview
moves, then drop --dry-run to apply.
Known limitation (early upgraders): If you upgraded to v0.10 before any
canonical-repo session wrapped up, mnemo doctor cannot detect orphan
worktree briefings (the check requires the canonical agent to have at least
one briefing on disk). In that case, run the migration command manually and
inspect bots/ for <repo>-<suffix> subdirectories.
Cost telemetry (v0.10+)
Every llm.call() (briefing + extraction consolidations) writes a
tool: "llm.call" entry into .mnemo/mcp-access-log.jsonl with
usage.input_tokens / usage.output_tokens. Every SessionStart writes a
tool: "session_start.inject" entry with envelope_bytes. mnemo telemetry
aggregates both into per-purpose token totals + estimated USD (using a
hard-coded pricing table at src/mnemo/core/pricing.py — bump the table
when Anthropic prices change).
Scope model (v0.7+)
Rules in shared/{feedback,user,reference}/ are local by default: a rule
is visible only from projects that appear in its sources[] frontmatter. A
rule is automatically promoted to universal when it has been seen in at
least scoping.universalThreshold distinct projects (default: 2). Universal
rules are visible from every project.
MCP retrieval accepts three scope values:
scope |
Returns |
|---|---|
"project" |
local rules + universal rules (default) |
"local-only" |
local rules only (legacy v0.6.2 behaviour) |
"vault" |
every consumer-visible rule |
Promotion and demotion are automatic: the index is re-derived from sources[]
on every SessionStart. To change the threshold, set
scoping.universalThreshold in mnemo.config.json.
Status line
After mnemo init, your Claude Code status line shows the brain's heartbeat:
mnemo mcp · 9 topics · 7↓ today · 3⚡
mnemo mcp— MCP server is registered in~/.claude.json9 topics— topic tags currently known in your vault (live count)7↓ today— number of times Claude has called a mnemo MCP tool today (resets at midnight, atomic write)3⚡— Reflex emissions today (v0.8). The bolt only appears when at least one rule has been injected viaUserPromptSubmitthis day.
The status line is additive: if you already had a custom statusLine
in ~/.claude/settings.json, mnemo wraps it instead of overwriting.
Your original output appears first, then ·, then mnemo's segment.
mnemo uninstall restores your original cleanly. If you manually edit
settings.json after mnemo init, mnemo doctor warns about the drift.
Runtime flags
The Capture → Present → Inject → Reflex loop is on by default —
mnemo init gives you the full product, not an inert scaffold. The
five runtime flags can be flipped to false in ~/mnemo/mnemo.config.json
if you want to disable specific behaviors:
{
"extraction": {
"auto": {
"enabled": true,
"minNewMemories": 1,
"minIntervalMinutes": 60
}
},
"briefings": {
"enabled": true
},
"injection": {
"enabled": true
},
"enrichment": {
"enabled": true
},
"reflex": {
"enabled": true
}
}
extraction.auto.enabled(defaulttrue) — at every session end, if there are at leastminNewMemoriesnew files (default 1) and at leastminIntervalMinutessince the last run (default 60), spawn a detached background extraction. The hook returns in <100ms; extraction runs asynchronously. Check progress viamnemo status, diagnose withmnemo doctor.briefings.enabled(defaulttrue) — at every session end, generate a per-session briefing intobots/<repo>/briefings/sessions/. Briefings feed the next extraction run as dense input.injection.enabled(defaulttrue, v0.5) — at every session start, emit the MCP topic list into Claude'sadditionalContext. The MCP tools are always available oncemnemo inithas run; this flag controls only whether Claude is told about the topics at session start.enrichment.enabled(defaulttrue, v0.5) — when Claude is about toEdit/Write/MultiEdita file matching a rule'spath_globs, the PreToolUse hook injects the rule body as additional context before the tool runs. See "Rule activation" below for details.reflex.enabled(defaulttrue, v0.8) — on every user prompt, theUserPromptSubmithook runs BM25F retrieval over the vault-wide reflex index and injects the single highest-confidence rule preview inline (triple-gate: overlap ≥ 2, relative gap ≥ 1.5×, absolute floor ≥ 2.0). Tunereflex.thresholds.*,reflex.bm25f.fieldWeights, andreflex.maxEmissionsPerSessioninmnemo.config.json. See "Prompt Reflex" below.
Rule activation (v0.5)
The three flags above tell Claude that rules exist at session start. Rule
activation makes mnemo push a rule directly into Claude's turn at the exact
moment it's about to run a tool — not just once per session. Two modes share
the same per-project index and the same PreToolUse hook:
- Enforcement — when Claude is about to run a
Bashcommand that matches adeny_patternregex or adeny_commandprefix from any rule owned by the current project, the hook emitspermissionDecision: denyand Claude Code blocks the call with the rule'sreasonstring visible to the model. Use for hard guardrails: "never commit with Co-Authored-By", "never rundrizzle-kit push". - Enrichment — when Claude is about to run
Edit,Write, orMultiEditon a file path that matches one of a rule'spath_globs, the hook emitsadditionalContextcontaining the rule body preview (up to 3 matching rules, ordered by source count). Claude sees the text as a<system-reminder>before performing the edit. Use for advisory rules with file-level scope: "HeroUI v3 modals use the Drawer slot pattern", "React key remount is required for these components".
Both modes are strictly per-project — a rule captured while working on
project A never fires while working on project B. Project ownership is
derived from the sources: frontmatter field (e.g. bots/sg-imports/...
belongs to sg-imports). Cross-project rules self-heal via repeated
capture.
Defaults and kill switch
enforcement.enableddefaults totrue. This is safe because enforcement is inert until you own a rule with anenforce:block — a fresh vault has zero such rules, so the hook fires but matches nothing. Rules gain activation metadata either by hand-editing the frontmatter or when the extraction LLM emits it for a high-confidence rule (see "Rule frontmatter shape" below).enrichment.enableddefaults totrueduring the dogfood phase. Enrichment surfaces context every time you edit a file matching a rule'spath_globs. If you want to silence it temporarily while iterating on rules, set tofalseinmnemo.config.json.
To disable enforcement (kill switch), add to ~/mnemo/mnemo.config.json:
{
"enforcement": { "enabled": false }
}
The PreToolUse hook is absolutely fail-open: any error at any stage
(missing index, corrupt JSON, exception in match logic) returns exit code 0
with empty stdout. The hook can never block Claude Code from running. You
can trust it not to brick your session even if mnemo itself is broken.
Rule frontmatter shape
Both blocks are optional and live in a feedback page's YAML frontmatter:
---
name: no-co-authored-by-in-commits
description: Never add Co-Authored-By trailers in git commits
type: feedback
stability: stable
sources:
- bots/mnemo/memory/feedback_no_coauthored.md
tags:
- git
- workflow
aliases:
- coauthor
- trailer
- commit-footer
enforce:
tool: Bash
deny_pattern: git commit.*Co-Authored-By
reason: No Co-Authored-By trailers in commits
activates_on:
tools: [Edit, Write, MultiEdit]
path_globs:
- '**/components/modals/**'
- '**/*modal*.tsx'
---
-
aliases(v0.8, optional) — a list of 3-8 lowercase synonym tokens used by the Reflex BM25F index (aliasesfield weight 2.5). Emit when the rule carries domain terms a developer would naturally type in a different language or abbreviation (PT↔EN,db↔database↔banco). Omit for generic rules without natural synonyms; extraction auto-emits aliases for high-signal rules. -
enforce.toolmust be"Bash"in v0.5 (the only tool v1 supports for hard-blocking). -
enforce.deny_patternis a regex compiled withre.IGNORECASE | re.DOTALLand pre-validated against ReDoS at index build time (a time-budget probe rejects pathological patterns like(a+)+b). -
enforce.deny_commandis an alternative to the regex: a list of command prefixes; the hook normalizes the command (stripssudo,env FOO=bar, shell inline env) before the match. -
activates_on.toolsmust be a subset of{Edit, Write, MultiEdit}. -
activates_on.path_globssupports**(match any number of path segments), single*(no slash crossing), and bracket classes with[!...]negation.
Rules tagged needs-review or with stability: evolving, and rules still
in shared/_inbox/, never become activation rules — they're gated
through the same is_consumer_visible predicate that the dashboard and
MCP retrieval use.
Observability
mnemo status— shows per-project rule counts, recent denials, recent enrichments, and the last denied command.mnemo doctor— checks for malformedenforce:/activates_on:blocks, stale activation index, suspiciousdeny_patternregex, and overly broadpath_globs(**/*,*).<vault>/.mnemo/denial-log.jsonl— JSONL stream of every deny, with slug, reason, tool, full command (truncated to 500 chars), timestamp.<vault>/.mnemo/enrichment-log.jsonl— JSONL stream of every enrichment, with hit slugs, tool name, file path, timestamp.<vault>/.mnemo/rule-activation-index.json— the per-project index written bybuild_indexafter every extraction and on session start. If you suspect drift, delete it; the next session rebuilds it.
Reflex observability (v0.8)
mnemo status— shows whetherreflex.enabledand today's emission count.mnemo doctor— three new checks:reflex-index-stale— flags whenreflex.enabled=truebutreflex-index.jsonis missing (usually: never ranmnemo extract).reflex-session-cap-hit— scans the last 7 days ofreflex-log.jsonland warns when >20% of sessions hitsession_cap_reached. Tunereflex.maxEmissionsPerSessionup or raisethresholds.absoluteFloor.reflex-bilingual-gap— flags when 3+ rules have non-ASCII descriptions but noaliases:field (probable recall loss on PT prompts).
<vault>/.mnemo/reflex-index.json— the vault-wide BM25F index, rebuilt on everySessionStart. Schema v1;load_indexreturnsNoneon version mismatch so the hook silences cleanly if you downgrade.<vault>/.mnemo/reflex-log.jsonl— per-prompt telemetry: session id, project, SHA256-12 prompt hash, scores, emitted slugs, silence reason. Never contains raw prompt text. Rotates at 1 MiB.
Commands
npx @xyrlan/mnemo install one-command setup (recommended)
npx @xyrlan/mnemo uninstall remove mnemo (vault preserved)
mnemo init first-run setup if installed via pip directly
mnemo init --project scope the install to <cwd> only (v0.12+)
mnemo status [--scope ...] vault state + hook health + auto-brain state
(--scope project|global|all, default all)
mnemo doctor full diagnostic with actionable fixes
mnemo extract manually run the extraction pipeline (also rebuilds the dashboard)
mnemo open open vault in Obsidian / file manager
mnemo fix reset circuit breaker
mnemo uninstall [--project] remove hooks, MCP server, status line (vault preserved)
mnemo help list commands
Where things live
~/mnemo/ your vault
├── HOME.md dashboard at the top, your notes below
├── bots/<repo-name>/
│ ├── logs/YYYY-MM-DD.md session start/end markers
│ ├── memory/ mirror of Claude memories for this repo
│ └── briefings/sessions/ per-session shift handoffs (opt-in)
├── shared/
│ ├── feedback/ auto-promoted preference rules
│ ├── user/ user-profile facts
│ ├── reference/ pointers to external systems
│ ├── project/ per-repo project context
│ └── _inbox/<type>/ drafts awaiting review
└── .mnemo/
├── extract.lock background extraction lock
├── last-auto-run.json background extraction telemetry
├── mcp-call-counter.json daily MCP tool call counter + reflex cache (v0.5/v0.8)
├── mcp-access-log.jsonl per-call MCP telemetry (v0.5.3, local only)
├── reflex-index.json vault-wide BM25F index for Prompt Reflex (v0.8)
├── reflex-log.jsonl per-prompt Reflex telemetry (v0.8, local only)
└── statusline-original.json preserved user statusLine (v0.5)
~/.claude/settings.json hooks + status line composer
~/.claude.json MCP server registration
See docs/getting-started.md for a deeper tour.
Privacy
100% local. Zero network. No third-party Python packages
(pyproject.toml declares dependencies = [] as a load-bearing
architectural choice). MCP access telemetry (.mnemo/mcp-access-log.jsonl)
stays on disk — nothing leaves your machine. Read the source.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mnemo_claude-0.12.0.tar.gz.
File metadata
- Download URL: mnemo_claude-0.12.0.tar.gz
- Upload date:
- Size: 150.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f6b92195dfb23ebd1846eb9d1f5b637de8698415cc914576c36415009eca556c
|
|
| MD5 |
1b4868b76186d6f4e9a5be66b20bb761
|
|
| BLAKE2b-256 |
3718d2d48e958e658d59c211a352b783a6015ec1cb31787a762d71a89c478b21
|
File details
Details for the file mnemo_claude-0.12.0-py3-none-any.whl.
File metadata
- Download URL: mnemo_claude-0.12.0-py3-none-any.whl
- Upload date:
- Size: 185.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
20def602f98ad946a2732dd98bfc8450d2c2244757fcc7426f87fb8c6591b05b
|
|
| MD5 |
1e400f763b167587e382013bad866d60
|
|
| BLAKE2b-256 |
ef952d8de433a91a430c15444907cf3aa79eb8b3dbf9b8c86d5da7519252b718
|