Install
$ agentstack add skill-jojoprison-mnemo-memory-routing ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ● Shell / process execution Used
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
mnemo:save — Memory Routing Cascade
Save information to multiple memory backends with graceful degradation. Each backend is tried independently — if one fails, others still work.
Prerequisites & config
Obsidian is preferred but not required (skill degrades gracefully). Config at ~/.mnemo/config.json — full schema including cascade.* toggles in ${CLAUDE_PLUGIN_ROOT}/references/config-schema.md.
Workflow
Step 0: Classify the Input
First — is this worth saving at all? (Especially when saving proactively, unprompted.) Save only if a future session would act differently for knowing it: a solved bug + root cause, a non-obvious decision + why, a gotcha that bit you, a durable fact, an actionable rule. Skip as a NOOP — say "nothing worth persisting here" and stop — for routine steps, anything the repo/git already records, one-off chatter, or what a future agent would re-derive trivially. Saving noise pollutes recall; a lean vault is the asset.
Never persist secrets — mask tokens / keys / passwords as `` before writing (a note is durable and may sync).
Then determine what type of information is being saved:
| Type | Goes to | Example | |------|---------|---------| | fact | Obsidian Atom + optional claude-mem | "Heroku standard-0 has 25 auto-backups" | | insight | Obsidian Molecule + optional claude-mem | "CLI-first is 70,000x cheaper because of token savings" | | decision | Obsidian Atom + optional claude-mem + memory/ | "We chose SCOPE over TextGrad for self-correction" | | gotcha | Obsidian Atom + memory/ + possibly CLAUDE.md | "execSync with shell=true is banned in antomate" | | source | Obsidian Source + optional claude-mem | External article, tool, research finding | | actionable rule | .claude/rules/.md (auto-inject, path-scoped) — Step 3.5 only | "After touching sign_epl.py, always gate the Kontur call on the flag" |
> Recall vs actionable rule — the fork this cascade turns on. A recall item (fact / insight / decision / source) answers "what / why" and is fetched on demand. An actionable rule — "never do X / always do Y" tied to specific code — must auto-surface when a future agent opens the relevant file, before it repeats the mistake. Its home is .claude/rules/ (native path-scoped auto-load, Step 3.5), not recall memory. Most saves are recall; route to .claude/rules/ only when the rule would have prevented an error by appearing at the right moment. > > Routing consequence — apply this to every step below. A recall item flows through Steps 1-4 normally. An actionable rule goes to Step 3.5 only: skip Steps 1-3 (Obsidian / claude-mem / memory/ are superseded by the auto-injecting rule file — don't double-write), and Step 4 (CLAUDE.md) fires only as the fallback if Step 3.5 declined (e.g. project_rules disabled). One kind → one home.
Step 1: Obsidian (Primary — for the user)
Skip if: cascade.obsidian.enabled is false, or Obsidian CLI returns "Unable to connect"
obsidian search query="{key words}" vault="{vault}"
If duplicate found → ask: update existing or create new?
Create note — MCP (shell-safe for markdown with code blocks):
mcp__obsidian__create(
path: "{type_prefix}{descriptive title}.md",
file_text: """---
type: {type}
tags: [{type}, {topic_tags}]
date: {YYYY-MM-DD}
source: "{where this came from}"
---
# {type_prefix}{title}
{content}
{links_section}
- [[{relevant MOC}]]
- [[{ghost notes for entities}]]
"""
)
Why MCP: content may contain code blocks with backticks or $(...) — CLI obsidian create content="..." would trigger zsh command substitution. See ${CLAUDE_PLUGIN_ROOT}/references/tool-routing.md for the full rule.
Note quality rules (details + tables in ${CLAUDE_PLUGIN_ROOT}/references/tool-routing.md):
- Naming: never
#/.///.mdin the title — they break wikilinks (#→heading anchor) or the CLI (.→truncation). Sanitize beforecreate. Use—or space. - Atom title = a statement, not a topic (Matuschak «title as API» / Умэсао):
Atom — Redis fail-open keeps reads alive when cache is down, NOTAtom — Redis. - Molecule = non-trivial synthesis of ≥2 atoms (new insight not in either alone), not "linked two notes."
- Molecule handed off with
cites:(e.g. from/mn:askcompounding): when the caller passestype: moleculeplus acites:source list and a pre-built{links_section}, writecites: [{sources}]into frontmatter (right afterdate:) and use the caller's links block verbatim instead of generating a bare MOC link. - Two link layers: inline with context in the body («contradicts [[X]]», «builds on [[Y]]») +
{links_section}for MOC/nav. A bare link without context is noise. - Short project names (
[[Diadoc]],[[BTS Holding]]) need a hub note — Obsidian doesn't resolve bare links via alias (by design). If[[ShortName]]is referenced and noShortName.mdexists, create it: a one-liner redirecting to[[MOC — …]]. - Staleness is type-driven, not stamped here. The
dateyou write is the review anchor —vault-healthderives review cadence from the note'stype(configreview.staleDays), so you don't add a review date. Exception: for a fast-rotting fact (a volatile API quirk, a "current as of" price) add an optionalttl:to the frontmatter to age it faster than its type default. Don't addreviewed:— that's the snooze health/the user stamps later. See${CLAUDE_PLUGIN_ROOT}/references/config-schema.md→ "Optional per-note frontmatter". - Load-bearing
[[links]]go OUTSIDE code fences — a wikilink inside a`block is NOT parsed into the graph (by design), so it's silently lost to backlinks. Agents emit code blocks constantly — keep navigable links in prose. Full vault conventions (Bases-first computed indexes, schema self-policing, concurrent-edit safety):${CLAUDE_PLUGIN_ROOT}/references/vault-conventions.md.
Add to MOC — MCP str_replace for targeted insert, or CLI for plain wikilinks:
mcp__obsidian__str_replace(
path: "{MOC}.md",
old_str: "{stable anchor line near list}",
new_str: "{same anchor}\n- [[{note name}]]"
)
CLI fallback for plain wikilink appends (safe — no backticks):
obsidian append file="{MOC}" vault="{vault}" content="- [[{note name}]]"
On error: Log ⚠️ Obsidian: skipped (not connected), continue to next backend.
Step 2: claude-mem (Optional Semantic Search — cross-session recall)
Skip if: cascade.claude_mem.enabled is false. This is the default in new installs because many users intentionally disable claude-mem for CPU/RAM reasons.
Use the bundled script — it auto-detects the claude-mem version for provenance, builds the JSON safely (python3, so a summary containing quotes / backticks / $(...) can't break the request), and bakes in the v12.3.9 gotchas documented below:
"${CLAUDE_PLUGIN_ROOT}/scripts/claude-mem-save.sh" \
"{claude_mem_url}" "{type}" "{current project or 'general'}" \
"{one-line summary of what was saved}" "{note name if created}" "{vault}"
API field name (v12.3.9): the request body key is text, not content. Earlier versions accepted content; as of v12.3.9 the API returns {"error": "text is required and must be non-empty"} if you send content. Confirmed during v0.7.3 smoke test — verified in claude-mem source.
v12.3.9 metadata gotcha — custom fields are dropped silently. POST returns {"success": true, "id": ...} but the stored observation only persists text + API-generated fields (type, title, narrative, facts, concepts, content_hash, created_at, ...). Custom metadata.* entries (including project, obsidian_note, obsidian_vault, claude_mem_version) are not retrievable from the observation record. The project field on the stored record is forced to the calling plugin's project (claude-mem), not metadata.project.
Workaround (used above): embed the key provenance fields (note name, vault, CM version) directly into text as a bracketed tail. Losing structured filtering hurts less than losing the data entirely — full-text search still finds the provenance. Keep the metadata: {...} block in the POST anyway so recovery is automatic once upstream fixes drop-silent behavior. Track upstream: thedotmack/claude-mem — search for metadata / project override.
Why obsidian_note + obsidian_vault: once upstream restores metadata persistence, claude-mem search results can link back to the full Obsidian note. Future /mn:ask --deep will show a direct wikilink alongside the observation.
Why claude_mem_version: v11.0.1 disabled semantic-inject by default, v12.0.0 introduced the file-read gate. Tagging observations by version lets retrieval logic filter legacy entries when needed.
On error: Log ⚠️ claude-mem: skipped (port {port} not responding), continue. Do not start ChromaDB or the claude-mem worker automatically.
Step 3: memory/ (For Claude — error prevention)
Skip if: cascade.memory_dir.enabled is false — or this is an actionable rule (it goes to Step 3.5; .claude/rules/ supersedes a memory/ copy, never write both).
Only write here if the information prevents the coding agent from making errors in future sessions and is not an actionable rule:
- Gotchas, commands, conventions
- NOT business context (that's Obsidian's job)
Path resolution:
- Claude Code:
~/.claude/projects/-{slugified-cwd}/memory/, not./memory/in the project root. - Codex:
~/.codex/memories/.
Find the correct Claude path by reading the MEMORY.md already loaded in the conversation context when available. Use ~/.claude/memory/ only for cross-project Claude rules. See ${CLAUDE_PLUGIN_ROOT}/references/gotchas.md for why this matters.
How to write — keep the index lean (autodream discipline):
memory/ is two layers: topic files (the detail) + MEMORY.md (a lean index — a table | File | Read when… |, one short "Read when…" row of recall triggers per topic, ≤~200 chars). Never dump prose into MEMORY.md — a bloated index gets truncated on load and old entries become invisible to Claude.
- Write the detail to a topic file (
{topic}.md) — create or update it. - Add/refresh one thin index row in
MEMORY.mdpointing to it (link + "Read when…" triggers: names / IDs / PR# / domain terms). Never a paragraph. - If
MEMORY.mdlinks aMEMORY-archive-index.mdat the top, aged/older rows live there — add aged rows there (not the lean index) and read it when recalling old context.
The index is periodically re-slimmed by autodream (memory consolidation). Full 4-phase procedure + no-loss rules: ~/.claude/memory/autodream-principles.md.
On error: Log ⚠️ memory/: skipped (directory not found), continue.
Step 3.5: .claude/rules/ — actionable path-scoped rules (auto-inject)
Skip if: cascade.project_rules.enabled is false (default true).
**Fires only for an actionable rule** (Step 0) — never for recall items. The test: would this rule have prevented an error if it had auto-surfaced the moment the agent opened the relevant file? Yes → here. "What we did / why" → recall memory (Steps 1-3), not here.
Why .claude/rules/ and not CLAUDE.md: Claude Code natively auto-loads .claude/rules/*.md. A file with paths: frontmatter loads only when the agent touches a matching file (path-scoped, zero idle-context cost); a file without paths: loads every session (always-on). It is the granular evolution of the old "dump a rule into CLAUDE.md" branch (Step 4). Docs: https://code.claude.com/docs/en/memory.md (§ "Path-specific rules", "User-level rules"). The load trigger is paths: — not description: (that field is for humans skimming the dir; it does not affect loading).
1 — Pick the level:
- Rule is specific to this repo (names its files, domains, deploy quirks) → project
.claude/rules/(committed in the repo). - Rule is generic / cross-project (a language convention, a git habit, a universal gotcha that applies in every repo) → user-global
~/.claude/rules/(auto-applies on every project on this machine).
2 — Find or create the target file (project shown; use ~/.claude/rules/ for global):
ls .claude/rules/*.md 2>/dev/null
- Read each file's
paths:/ domain. Append the rule to the file whose scope covers the code it governs (under the matching section, surgical insert). - No file matches by meaning → create a new
.md. Don't wedge an unrelated rule into the nearest file (that's the wrong-abstraction smell at the doc level). - **
.claude/rules/doesn't exist → create the dir and the file. A first rule bootstraps the convention; do not** silently fall back to CLAUDE.md just because the folder is missing.
3 — Frontmatter for a new file:
---
paths:
- "src//**" # globs for the files this rule governs → path-scoped auto-load
- "tests//**"
description: ""
---
Omit paths: only for an always-on rule (rare in a project — it costs context every session). A generic global rule usually omits paths: (it should always apply).
4 — Write it. These are plain repo / dotfiles outside the Obsidian vault → use Write/Edit, never the Obsidian CLI/MCP (no vault graph to join, and obsidian create content= would shell-expand backticks). Match the file's existing section style; append surgically, don't reformat neighbors. Verify the YAML after writing — a broken-indent paths: entry silently drops the whole file from auto-load (real incident: a 0-indent list item under paths: made the rule never load).
Codex / AGENTS.md gotcha: Codex does not read .claude/rules/ — only AGENTS.md (nested, 32 KiB hard limit, silent truncate past it). If the project has an assemble-AGENTS build-step (rules → AGENTS.md), the rule reaches Codex on rebuild — run it and confirm wc -c AGENTS.md stays .md (and the dir) when none matches — don't wedge it into an unrelated file. Verify the YAML: a broken-indent paths: entry silently drops the whole file from loading.
.claude/rules/files live outside the vault — likememory/files, never[[wikilink]]them and never write them via the Obsidian CLI/MCP. PlainWrite/Edit.- Codex is blind to
.claude/rules/— it reads onlyAGENTS.md(nested, 32 KiB silent-truncate). For a repo with Codex devs, route the critical rule into the AGENTS.md build-step (or by hand) too, and keepwc -c AGENTS.md < 32768. - Always check duplicates before creating Obsidian notes — clobbering a note silently is worse than any write latency.
- Ghost notes generously — wrap entities in
[[wikilinks]]even when the target doesn't exist yet. Enables future entity discovery. - Never
[[wikilink]]a memory/ file — use inline code —memory/files (feedback-*.md,reference-*.md, etc.) and project files (CLAUDE.md,AGENTS.md) live outside the Obsidian vault graph. Writing[[memory/foo]]or[[foo.md]]from a note creates a permanent unresolved link (a phantom ghost that pollutesorphans/unresolvedreports forever). Reference them as `memory/foo.mdinstead. If the memory file has a real vault counterpart (a MOC or Atom on the same topic), link THAT note — it strengthens the graph instead of dangling. See${CLAUDEPLUGINROOT}/references/tool-routing.md`. - MOC link mandatory for typed Obsidian notes (Atom/Molecule/Source/Session).
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: jojoprison
- Source: jojoprison/mnemo
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.