AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Vault Health

skill-jojoprison-mnemo-vault-health · by jojoprison

Vault health audit — orphans, broken links, type-aware stale-review candidates, growth stats. Use whenever the user mentions vault maintenance, orphans, broken links, 'is my vault clean', 'проверь vault', 'сироты', 'битые ссылки', 'здоровье базы знаний', 'здоровье памяти', 'здоровье обсидиана', or asks for vault statistics — or proactively after creating 3+ notes in a session, after mass note cre…

— No reviews yet
0 installs
42 views
0.0% view→install

Install

$ agentstack add skill-jojoprison-mnemo-vault-health

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No 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 Used
  • ✓ Shell / process execution No
  • ✓ 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.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-jojoprison-mnemo-vault-health)

Reliability & compatibility

✓ Security review passed
0 installs to date
— no reviews yet
● 2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

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 →
Are you the author of Vault Health? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

mnemo:health — Vault Health Check & Analytics

Run a comprehensive health check on the Obsidian vault: orphans, broken links, missing sections, stale notes, and growth statistics.

Prerequisites & config

Obsidian must be open; obsidian CLI on PATH. Config at ~/.mnemo/config.json (required fields: vault, taxonomy, links_section) — schema in ${CLAUDE_PLUGIN_ROOT}/references/config-schema.md.

Workflow

Steps 1-4 run in parallel — single assistant message batching the independent CLI reads (orphans, unresolved, tag counts; Step 4 reuses Step 3's tag output, no extra call). These are independent queries against the same indexed vault, ~180ms total vs ~720ms sequential.

Step 0: claude-mem Sanity Check (optional, ~20ms)

Surface two common gotchas if claude-mem plugin is installed and enabled. If cascade.claude_mem.enabled is false, skip this section silently — many users intentionally disable claude-mem for CPU/RAM reasons.

bash "${CLAUDE_PLUGIN_ROOT}/scripts/check-cm-version.sh"
# Or from source: plugins/mnemo/scripts/check-cm-version.sh

Script emits three lines: version: X, stale: N, path: .... Interpret:

  • stale > 0 → warn: "claude-mem has {stale} old version folder(s) cached. Restart all Claude windows — old Stop hooks point to a path that no longer exists."
  • `version {const u=app.metadataCache.unresolvedLinks;const f={};Object.values(u).forEach(l=>Object.keys(l).forEach(t=>f[t]=(f[t]||0)+1));return JSON.stringify(Object.entries(f).sort((a,b)=>b[1]-a[1]).slice(0,10));})()" vault="{vault}"

A short name with many refs (e.g. `[[Diadoc]]` ×30) = create a hub note `Diadoc.md` → `[[MOC — …]]` so all those links resolve (alias doesn't work for bare links — by design).

### Step 3: Tag Distribution

```bash
obsidian tags counts sort=count vault="{vault}"

Show top 15 tags. Flag tags used only once (potential typos).

Step 4: Notes by Type

Reuse the obsidian tags counts output from Step 3 — do not call it again (the tag index is one query; Steps 3 and 4 are two views of the same result). From it, extract counts for taxonomy tags: #atom, #molecule, #source, #session, #moc. These correspond to config.taxonomy.*.tag values.

Total notes count:

obsidian files ext=md vault="{vault}" total

Step 5: Missing Links Section (batched grep — 3600x faster)

Do NOT loop obsidian read per file — on a 1000-note vault that's ~180s. Use a single filesystem grep against the vault directory.

VAULT_PATH=$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/get-vault-path.sh" "{vault}")

# Single recursive grep -L: files NOT containing the links section heading.
# Filter to taxonomy-prefixed notes.
grep -rL --include="*.md" "{links_section}" "$VAULT_PATH" 2>/dev/null \
  | grep -E "(Atom|Molecule|Source|Session|MOC) — "

Measured on 999-note vault: ~49ms vs ~180s serial — 3600x speedup. Safe to run always.

Report notes missing the section.

Step 6: Bad Filenames (# in names → permanent orphans)

VAULT_PATH=$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/get-vault-path.sh" "{vault}")
find "$VAULT_PATH" -name "*#*.md" -not -path "*/.obsidian/*" -not -path "*/.trash/*" 2>/dev/null | sed "s|$VAULT_PATH/||"

Files with # in the name are permanent orphans — [[Note #1]] parses as [[Note]] + heading anchor #1, so nothing resolves to them (even existing links). Flag for rename (# → — or drop the #). Same for . mid-name (breaks CLI create). See ${CLAUDE_PLUGIN_ROOT}/references/tool-routing.md (naming rules).

Step 7: Review Candidates (content-staleness, type-aware)

A temporal signal, distinct from orphans (Step 1, which is structural): notes untouched longer than the threshold for their type are candidates for a re-read. Threshold precedence: per-note ttl: → review.staleDays. → review.staleDays.default → 30 (legacy). Age is measured from the newest of date or reviewed — so stamping reviewed: {today} on a still-valid note resets its clock. That snooze is what stops a stale list from rotting into guilt-debt (the canonical failure mode of review dates — see Gotchas).

VAULT_PATH=$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/get-vault-path.sh" "{vault}")
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/review-candidates.py" "$VAULT_PATH" --limit 30
# Or from source: plugins/mnemo/scripts/review-candidates.py

Output: CANDIDATES\t{n}, then THRESHOLDS\t{json}, then one tab-separated row per note ({overdue_days} {type} {anchor_date} {anchor_src} {threshold_days} {relpath}), most-overdue first. {threshold_days} is the budget actually applied to that note (per-note ttl: if set, else its type's staleDays, else default) — show it as (type, Nd budget), never invent a ttl. Pure filesystem — independent of the obsidian CLI graph cache (no lag/lie risk). A missing review config section reproduces the legacy uniform 30-day behavior, so this is safe before any config migration.

Don't AND this with backlinks — a well-linked note can still hold outdated claims. Report candidates on their own; cross-reference Step 1 yourself if you specifically want "old and orphaned."

Step 7.5: Content Lint (optional deep pass — gated by review.lint.enabled)

Steps 1-7 are cheap and structural. This is the content pass — Karpathy's "lint": actually re-read the notes to judge whether claims have rotted, instead of trusting the calendar. Together the checks cover his four: orphans (Step 1) + concepts-mentioned-but-no-page (Step 2 unresolved links) + stale claims (Step 7) + contradictions (here). Gated by config.json → review.lint.enabled (default false) because it reads note bodies and costs tokens — skip this step silently when disabled.

When enabled, take the top review.lint.maxCandidates (default 15) candidates from Step 7 and have them read & judged on the model set by config.json → review.lint.model (default haiku; sonnet/opus for higher-quality verdicts). This health skill itself runs as a haiku fork and cannot upgrade its own model, so:

  • If review.lint.model is haiku (or unset) → do the lint inline in this fork.
  • Otherwise → spawn one subagent on model: {review.lint.model} (Claude Code: Task tool, subagent_type: Explore or general; Codex: spawn_agent) that reads the candidate note bodies in one batched pass (filesystem read, not one obsidian read per file) and returns the verdicts. Keeping the cheap Steps 1-7 on haiku while the lint runs on opus is the whole point of the split.
  • Report the subagent's verdicts verbatim — never assume "all still-valid". The fork only aggregates what the subagent returned; it does not re-judge. The Step 9 Content-lint block and its count MUST reflect the subagent's actual breakdown: if the subagent returned 13 still-valid + 2 update-needed out of 15, the report says 13 still-valid, 2 update-needed, NOT 15 still-valid. Defaulting the count to the candidate total (as if everything passed) is a reporting bug — wait for the subagent's verdicts before writing the report.

Emit a verdict per candidate:

  • still-valid → close the loop: stamp reviewed: {today} into the note's frontmatter via mcp__obsidian__str_replace — anchor on the frontmatter line inside the leading --- block (not a date:/reviewed: mention in the body): replace the existing reviewed: value, or if absent insert after date: (date: {d} → date: {d}\nreviewed: {today}). A confirmed-valid note then stops resurfacing without a manual edit. This auto-stamp is on by default (config.json → review.lint.autoStampReviewed, default true); only if the user set it to false do you just recommend the stamp and write nothing. When the lint runs in a spawned subagent (model ≠ haiku), that subagent does the stamping — it already holds the verdict and the note path. If a stamp write fails (Obsidian offline, or mcp__obsidian__str_replace unavailable in the subagent context), don't drop it silently — collect those note paths and surface them under the Content lint report block so the user can stamp them manually.
  • update-needed → one line on what specifically looks outdated.
  • contradicts [[Other Note]] → name the conflicting note; flag the older one against the newer.

Verdicts are triage, not truth — on haiku especially, expect false positives; even on opus, surface them as questions. The only write health ever performs is the reviewed: auto-stamp above, and only ever on a still-valid verdict — never content, never on update-needed/contradicts. It fires only inside this lint pass (which is itself off unless review.lint.enabled is true), and can be turned back to suggest-only with autoStampReviewed: false. The user stays in control.

Step 8: Top Hubs

Enumerate the MOC notes (by the moc taxonomy prefix), then count backlinks for each:

VAULT_PATH=$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/get-vault-path.sh" "{vault}")
find "$VAULT_PATH" -name "{moc_prefix}*.md" -not -path "*/.obsidian/*" -not -path "*/.trash/*" 2>/dev/null | sed "s|.*/||;s|\.md$||"   # MOC names

For each enumerated MOC name, count backlinks:

obsidian backlinks file="{moc_name}" vault="{vault}"

Sort by count, show top 5. Keep the enumerated MOC-name list — Step 8.5 reuses it to find topics that have no MOC.

Step 8.5: Research-Gap Candidates (report-only — where the vault wants to grow)

Karpathy's lint also proposes what to research next. Steps 1-8 already collected the raw signal — turn it into growth suggestions without any new CLI calls (reuse Step 2's obsidian eval top-10 unresolved targets, Step 3/4's tag counts, and the MOC-name list enumerated in Step 8). Two cheap, computable gap types:

  • Topic cluster with no MOC — a non-taxonomy topic tag with ≥5 notes (Milo's "mental squeeze point", the same trigger config-schema.md uses for creating a MOC) whose {moc_prefix}{Topic} is absent from Step 8's enumerated MOC-name list. Suggest: "12 notes tagged #auth, no MOC → create MOC — Auth?"
  • Recurring external with no Source note — a top unresolved target from Step 2's obsidian eval list (the authoritative metadataCache top-10, not the CLI unresolved output) cited ≥5 times that reads like an external tool/paper/vendor (not a short project name, which instead wants a hub note) and has no Source — … note. Suggest: "[[LangGraph]] cited ×9, no Source note → capture one?"

These are suggestions, never auto-created (same non-destructive stance as the rest of health). Skip a type silently when it yields nothing. Do not web-search to fill the gap — mnemo maintains a human-authored vault: it points at the gap, the user decides whether to fill it. (This is the on-philosophy half of Karpathy's "suggest new article candidates"; the auto-web-imputation half is deliberately omitted.)

Step 9: Output Report

📊 Vault Health Report ({date})

⚠️ claude-mem: {warning or "v12.3.9, clean"}

Total: {N} notes
  Atoms: {N} | Molecules: {N} | Sources: {N}
  Sessions: {N} | MOCs: {N} | Other: {N}

🔴 Orphans: {N}
  - Note Name 1
  - Note Name 2

🟡 Missing {links_section}: {N}
  - Note Name 1

🚫 Bad filenames (`#`/`.`): {N} — permanent broken links, rename
  - Atom — Foo (PR #12)  → rename to "PR 12"

🔍 Top unresolved targets (missing hub notes?):
  1. [[Diadoc]] ×34 → create hub note?
  2. [[Python]] ×28

🔗 Unresolved wikilinks: {N} total
📏 Tags: {N} total, {N} used once

🏆 Top-5 Hubs (most backlinks):
  1. MOC — Security (34)
  2. MOC — AI ML Tools (28)
  ...

💤 Review candidates (stale by type-aware age): {N}
  - Atom — API X gotcha — 45d overdue (atom, 60d budget)
  - Source — vendor API pricing — 35d overdue (source, 180d budget)
  (snooze a still-valid note: add `reviewed: {today}` to its frontmatter)

🔬 Content lint: {N judged} — {S} still-valid, {U} update-needed, {C} contradicts   ← only when review.lint.enabled
  (counts MUST equal the lint's actual verdicts — never default to all-still-valid; see Step 7.5)
  - Atom — API X gotcha → UPDATE-NEEDED: superseded by [[Atom — API X v2]]
  - Source — vendor API pricing → still-valid (stamped reviewed: {today})

🌱 Research-gap candidates (where the vault wants to grow): {N}
  - #auth ×12 notes, no MOC → create MOC — Auth?
  - [[LangGraph]] ×9, no Source note → capture one?

🧠 Claude memory/ index: {KB}KB / {lines} lines {✅ lean | ⚠️ bloated → autodream}

Omit the 🔬 Content lint block entirely when review.lint.enabled is false. Omit the 🌱 Research-gap candidates block when Step 8.5 found nothing. The still-valid line above shows the default (autoStampReviewed: true — the note was stamped); with autoStampReviewed: false render it as → still-valid (recommend reviewed: {today}) instead, since nothing was written.

Skip the ⚠️ claude-mem line entirely if Step 0 found nothing to warn about.

Step 10: Claude memory/ index health (autodream check)

Separate from the Obsidian vault, Claude Code keeps an always-loaded index at memory/MEMORY.md. Claude Code auto-memory hard-truncates this index at ~24.4 KB on load — beyond that, trailing rows silently vanish from Claude's context. So warn early (before the cliff), not at some lax size. Threshold is configurable via config.json → memory.indexWarnKB (default 22):

WARN=$(python3 -c "import json,os;print(json.load(open(os.path.expanduser('~/.mnemo/config.json'))).get('memory',{}).get('indexWarnKB',22))" 2>/dev/null || echo 22)
for f in "$HOME"/.claude/projects/-*/memory/MEMORY.md; do
  [ -f "$f" ] || continue
  kb=$(( $(wc -c ${WARN}KB warn (auto-memory truncates ~24.4KB) → run autodream (move sessions → MEMORY-archive-index.md, target <20KB)"
done

If flagged → recommend autodream (memory consolidation): slim the index into topic files + MEMORY-archive-index.md, no loss. Procedure: ~/.claude/memory/autodream-principles.md. This is the only memory/ check here — vault-health otherwise audits Obsidian, not Claude's memory/.

Gotchas

Common failures in ${CLAUDE_PLUGIN_ROOT}/references/gotchas.md. Skill-specific rules:

  • obsidian orphans may return empty on small vaults — this is OK, not an error.
  • Reference notes (taxonomy docs, templates) aren't orphans even if few backlinks — they're meant to be lookups.
  • Ghost notes (unresolved wikilinks) are a feature, not a bug — they enable entity discovery. Don't flag on raw count; instead surface the top targets (Step 2 eval) — frequent ones = missing hub notes (actionable).
  • CLI graph queries cache & can lie — orphans/unresolved/backlinks lag writes and have shown a note as resolved AND broken at once. For critical checks use obsidian eval on metadataCache (see ${CLAUDE_PLUGIN_ROOT}/references/gotchas.md). Treat counts as advisory if notes were created this session.
  • Do not auto-fix anything — only report. User decides what to clean up. The one exception is review.lint.autoStampReviewed (default true): it lets the content-lint stamp reviewed: {today} on a still-valid note (Step 7.5) to close the snooze loop. That is the sole frontmatter write health can make — only the reviewed: field of a confirmed-valid note, never content, never anything else. It fires only when the content lint itself is enabled (review.lint.enabled, default false), so a default install still writes nothing; set autoStampReviewed: false to keep the lint suggest-only.
  • Step 5 uses filesystem grep (~3600x faster than per-file reads — 49ms vs 180s on a 999-note vault) — safe on any vault size.
  • Review candidates (Step 7) are temporal, not structural — don't conflate with orphans. A note can be both, either, or neither. The script is age-only by design (cheap, no graph dependency).
  • Content-lint verdicts (Step 7.5) are triage-grade — t

…

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.