Skip to main content

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:

  1. Scaffolds a vault at ~/mnemo/ (or wherever you point it)
  2. Injects four hooks into ~/.claude/settings.json: SessionStart, SessionEnd, PreToolUse (rule activation, v0.5), and UserPromptSubmit (Prompt Reflex, v0.8)
  3. Registers a stdio MCP server in ~/.claude.json so Claude Code can call mnemo's tools
  4. Wires an additive status line composer (preserves your existing one if you have it)
  5. Writes ~/.claude/commands/<name>.md slash 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 cd into 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.json is gitignored by default. Each developer runs their own mnemo init --project to 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 🔴 in bots/<repo>/logs/YYYY-MM-DD.md
  • Claude memory mirror — anything Claude saves to ~/.claude/projects/*/memory/ is mirrored into bots/<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.md regenerates 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 servermnemo init registers a long-running JSON-RPC server in ~/.claude.json exposing three read-only tools. All three default to scope="project" (only rules owned by the current repo); pass scope="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 + frontmatter
    • get_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_visible predicate, 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 UserPromptSubmit hook 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_cache in .mnemo/mcp-call-counter.json). The PreToolUse enrichment 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 logs silence_reason: session_cap_reached for 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.json
  • 9 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 via UserPromptSubmit this 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 defaultmnemo 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 (default true) — at every session end, if there are at least minNewMemories new files (default 1) and at least minIntervalMinutes since the last run (default 60), spawn a detached background extraction. The hook returns in <100ms; extraction runs asynchronously. Check progress via mnemo status, diagnose with mnemo doctor.
  • briefings.enabled (default true) — at every session end, generate a per-session briefing into bots/<repo>/briefings/sessions/. Briefings feed the next extraction run as dense input.
  • injection.enabled (default true, v0.5) — at every session start, emit the MCP topic list into Claude's additionalContext. The MCP tools are always available once mnemo init has run; this flag controls only whether Claude is told about the topics at session start.
  • enrichment.enabled (default true, v0.5) — when Claude is about to Edit/Write/MultiEdit a file matching a rule's path_globs, the PreToolUse hook injects the rule body as additional context before the tool runs. See "Rule activation" below for details.
  • reflex.enabled (default true, v0.8) — on every user prompt, the UserPromptSubmit hook 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). Tune reflex.thresholds.*, reflex.bm25f.fieldWeights, and reflex.maxEmissionsPerSession in mnemo.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 Bash command that matches a deny_pattern regex or a deny_command prefix from any rule owned by the current project, the hook emits permissionDecision: deny and Claude Code blocks the call with the rule's reason string visible to the model. Use for hard guardrails: "never commit with Co-Authored-By", "never run drizzle-kit push".
  • Enrichment — when Claude is about to run Edit, Write, or MultiEdit on a file path that matches one of a rule's path_globs, the hook emits additionalContext containing 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.enabled defaults to true. This is safe because enforcement is inert until you own a rule with an enforce: 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.enabled defaults to true during the dogfood phase. Enrichment surfaces context every time you edit a file matching a rule's path_globs. If you want to silence it temporarily while iterating on rules, set to false in mnemo.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 (aliases field weight 2.5). Emit when the rule carries domain terms a developer would naturally type in a different language or abbreviation (PT↔EN, dbdatabasebanco). Omit for generic rules without natural synonyms; extraction auto-emits aliases for high-signal rules.

  • enforce.tool must be "Bash" in v0.5 (the only tool v1 supports for hard-blocking).

  • enforce.deny_pattern is a regex compiled with re.IGNORECASE | re.DOTALL and pre-validated against ReDoS at index build time (a time-budget probe rejects pathological patterns like (a+)+b).

  • enforce.deny_command is an alternative to the regex: a list of command prefixes; the hook normalizes the command (strips sudo, env FOO=bar, shell inline env) before the match.

  • activates_on.tools must be a subset of {Edit, Write, MultiEdit}.

  • activates_on.path_globs supports ** (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 malformed enforce:/activates_on: blocks, stale activation index, suspicious deny_pattern regex, and overly broad path_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 by build_index after every extraction and on session start. If you suspect drift, delete it; the next session rebuilds it.

Reflex observability (v0.8)

  • mnemo status — shows whether reflex.enabled and today's emission count.
  • mnemo doctor — three new checks:
    • reflex-index-stale — flags when reflex.enabled=true but reflex-index.json is missing (usually: never ran mnemo extract).
    • reflex-session-cap-hit — scans the last 7 days of reflex-log.jsonl and warns when >20% of sessions hit session_cap_reached. Tune reflex.maxEmissionsPerSession up or raise thresholds.absoluteFloor.
    • reflex-bilingual-gap — flags when 3+ rules have non-ASCII descriptions but no aliases: field (probable recall loss on PT prompts).
  • <vault>/.mnemo/reflex-index.json — the vault-wide BM25F index, rebuilt on every SessionStart. Schema v1; load_index returns None on 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mnemo_claude-0.13.3.tar.gz (149.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mnemo_claude-0.13.3-py3-none-any.whl (185.6 kB view details)

Uploaded Python 3

File details

Details for the file mnemo_claude-0.13.3.tar.gz.

File metadata

  • Download URL: mnemo_claude-0.13.3.tar.gz
  • Upload date:
  • Size: 149.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mnemo_claude-0.13.3.tar.gz
Algorithm Hash digest
SHA256 f224db877e1bfddb5a41dc0bd828f095fc7ef90502ca256155aefa35346b3eef
MD5 0c54e447c94e3f07f359a27512cdda89
BLAKE2b-256 5d51bdd7f4c40bcb5e9bb17a4f3923644fdfe6ec972864a1f4c76fd4ef9830f2

See more details on using hashes here.

File details

Details for the file mnemo_claude-0.13.3-py3-none-any.whl.

File metadata

  • Download URL: mnemo_claude-0.13.3-py3-none-any.whl
  • Upload date:
  • Size: 185.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mnemo_claude-0.13.3-py3-none-any.whl
Algorithm Hash digest
SHA256 abf9c5f624cda91403f0a729860708eea428904a9baf3d0ec7f2f3c34a644221
MD5 5cb8b8b0c0cd9c33c8e61a9d7ba2b60f
BLAKE2b-256 0633f98158cf30d7a05e0b08b3b70354c334830e381a957c8b064a7fd2f45644

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page