From obsidian-brain
Diagnostic and repair skill for the Obsidian vault. Runs a battery of checks against vault notes and offers to fix detected issues. Dry-run by default — requires 'fix' to write. Use when: (1) /vault-doctor command to scan for vault health issues, (2) /vault-doctor fix to apply repairs, (3) /vault-doctor --check <name> for a specific check, (4) user reports stale backlinks or wants to audit vault integrity.
How this skill is triggered — by the user, by Claude, or both
Slash command
/obsidian-brain:vault-doctorThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Audit and repair the Obsidian vault. Ships with one check initially (`source-sessions`); more can be added as separate modules under `scripts/vault_doctor_checks/` without changing this skill.
Audit and repair the Obsidian vault. Ships with one check initially (source-sessions); more can be added as separate modules under scripts/vault_doctor_checks/ without changing this skill.
Tools needed: Bash, Read
/vault-doctor — run all checks, report only (dry-run)/vault-doctor fix — run all checks, apply after per-project confirmation/vault-doctor --check source-sessions — run one specific check; notes carrying imported: true frontmatter or the claude/imported tag are silently skipped (their source_session refers to another vault and can never resolve locally)/vault-doctor --check snapshot-integrity — snapshot orphans, broken backlinks, stale/missing session snapshot lists, status/summary mismatches/vault-doctor --check snapshot-migration — migrate pre-spec snapshots (legacy filenames, missing status/backlink fields, missing session snapshot lists). Runs 4 ordered sub-checks; idempotent./vault-doctor --check project-name-canonicalization — one-time backfill check that rewrites worktree-slug project names to the canonical main-repo basename in session notes and insights. Phase 1: for each session note with a project_path:, derives canonical via git rev-parse --git-common-dir (cached per path) and proposes rewriting project: + the observed claude/project/* tag lines (production tags are slugified/40-char-truncated — both forms matched; sibling tags never touched). Phase 2: for each insight with a source_session: UUID, looks up the Phase-1 canonical (not the stale frontmatter value) and proposes the same rewrite. WARN rows for: missing project_path, path no longer exists, git unavailable/timed out, git errors (dubious ownership etc. — never silently treated as non-repo), empty project: field, insight source_session not in index. Non-git project dirs left alone; snapshot notes skipped. --project matches the old name OR the derived canonical (filtered sessions still seed the Phase-2 index); --days is ignored (full-vault backfill). Opt-in — excluded from default all-checks sweep (OPT_IN=True); run via --check project-name-canonicalization. Conceptually run after --check project-name-normalization (underscore → hyphen) for clean input./vault-doctor --check session-coverage — detect SessionEnd-hook coverage gaps: JSONLs in ~/.claude/projects/ with no corresponding session note. Opt-in — excluded from the default all-checks sweep (heavy all-projects JSONL walk); must be named via --check. Sessions below the configured min_messages/min_duration_minutes thresholds are excluded (the hook would also skip them; only text-bearing user messages count). Add --strict to emit FAIL: (not WARN:) when any note references the orphaned session via source_session (changes the reason prefix only, not the exit code). Add --reconstruct to enable --apply to reconstruct the missing note by re-running the SessionEnd hook via replay-sessionend.py (never automatic; always requires --apply). --days bounds JSONL mtime age (default 30). Note: the per-gap project name is derived from the JSONL's cwd basename, so --project expects the cwd-basename slug — worktree sessions may display a non-canonical expected note path (detection itself is session_id/hash-based and unaffected)./vault-doctor --check audit-historic-repairs — one-shot audit of historic source-sessions repairs: diffs doctor backups against current notes, classifies each repair (A restore / B keep / C ambiguous / D both-wrong) by date agreement, and restores category-A mtime-bug corruptions on fix. Opt-in — excluded from the default all-checks sweep; must be named via --check. --days bounds backup-run age (default 180)./vault-doctor --days 14 — override default window (default: 7 days)/vault-doctor --project obsidian-brain — limit to one project/vault-doctor fix --check source-sessions --days 7 — combine flags/vault-doctor --min-confidence 0.9 — dry-run showing only issues with confidence >= 0.9; report header notes the active filter and dropped count/vault-doctor fix --min-confidence 0.9 — apply only the high-confidence subset (conf >= 0.9); preview matches apply scope exactlyFollow these steps exactly. Do not skip steps or reorder them.
Parse the user's invocation into flags:
fix → apply mode, all checks--check <name> → specific check only--days <N> → window override--project <name> → project filter--strict → set STRICT=1 (session-coverage only: FAIL instead of WARN on referenced gaps)--reconstruct → set RECONSTRUCT=1 (session-coverage only: mark gaps resolvable for apply)--min-confidence <FLOAT> → set MIN_CONFIDENCE (0.0–1.0 inclusive; default 0.0 keeps all; applies to both dry-run report and --apply); note: unresolved/WARN rows (confidence=0.0) are hidden at any threshold > 0 — drop the flag to audit themLocate the Python dispatcher via the standard plugin cache glob, with a fallback for local dev sessions where the repo is checked out as $PWD:
DISPATCHER="$(ls -dt ~/.claude/plugins/cache/*/obsidian-brain/*/scripts/vault_doctor.py 2>/dev/null | head -1)"
if [[ -z "$DISPATCHER" ]]; then
if [[ -f "$(pwd)/scripts/vault_doctor.py" ]]; then
DISPATCHER="$(pwd)/scripts/vault_doctor.py"
fi
fi
if [[ -z "$DISPATCHER" || ! -f "$DISPATCHER" ]]; then
echo "ERROR: could not find scripts/vault_doctor.py" >&2
exit 1
fi
If the dispatcher cannot be located, tell the user:
Could not find
scripts/vault_doctor.py. Make sure the obsidian-brain plugin is installed via/dev-test install(for local dev) or the marketplace.
Stop here if the dispatcher is missing.
Always run with --json first so you can parse the output deterministically. Pass through only the flags the user provided:
ARGS=()
[[ -n "${CHECK:-}" ]] && ARGS+=(--check "$CHECK")
[[ -n "${DAYS:-}" ]] && ARGS+=(--days "$DAYS")
[[ -n "${PROJECT:-}" ]] && ARGS+=(--project "$PROJECT")
[[ -n "${STRICT:-}" ]] && ARGS+=(--strict)
[[ -n "${RECONSTRUCT:-}" ]] && ARGS+=(--reconstruct)
[[ -n "${MIN_CONFIDENCE:-}" ]] && ARGS+=(--min-confidence "$MIN_CONFIDENCE")
ARGS+=(--json)
python3 "$DISPATCHER" "${ARGS[@]}"
Capture stdout as the JSON report. Exit codes:
0 — clean vault, nothing to do1 — issues found (expected for a dry-run that finds things)2 — apply errors OR one or more checks crashed (results incomplete; see crashed_checks in JSON)3 — usage error (bad args, missing config)If exit code is 3, surface the stderr message directly to the user and stop.
Parse the JSON and present a grouped-by-project table.
For each issue, after the proposed: line (when present), render a
signal: <capture_signal> (conf <capture_confidence>) line. The values
come from the top-level capture_signal and capture_confidence fields
in the JSON payload (not from extra.*). capture_confidence reports
how reliable the capture-time signal is (created_at=1.0, date=0.9,
filename=0.85, mtime=0.5); the issue's top-level confidence field
reports the rewrite-proposal confidence per the strict 3-band taxonomy:
0.99 = uuid-basename-stale (auto-applyable basename-only repair);
0.5 = date-window-hint (operator must content-grep before applying);
0.0 = unresolved / uuid-day-mismatch / missing-session-note (never auto-apply).
The two fields are distinct — render capture_confidence here so
heuristic-fall cases are visible (e.g., signal=mtime conf=0.5 indicates
no immutable signal was available — the operator should sample a few flagged
notes before running fix). For unresolved issues with no proposed: line,
render signal: after reason:.
Render signal_class (from the top-level signal_class field) as a prefix tag so operators
can distinguish: [uuid-basename-stale], [uuid-day-mismatch],
[missing-session-note], [date-window-hint], [unresolved]. The
convergence_warning/convergence_count fields are deprecated as of #106
(UUID-first matching obsoleted the convergence guard) — they remain in the
JSON payload as hard-coded defaults for output schema stability but should
not drive rendering.
The crashed_checks key is conditional — it is only present when one or
more checks crashed during the scan or apply phase (exit code 2 on a
dry-run). If the payload contains crashed_checks, tell the user which
checks crashed and that the report is INCOMPLETE — do not present it
as a complete scan. Example: "Warning: checks [source-sessions] crashed
during this scan — results are incomplete. Re-run after the crash is
resolved to get a full report."
Example:
vault_doctor report — 3 issue(s) across 1 check(s)
## source-sessions
### Project: obsidian-brain (2 issues)
[FAIL] 2026-04-10-recall-profiling.md
current: [[2026-04-09-obsidian-brain-abcd]]
proposed: [[2026-04-10-obsidian-brain-ef01]]
signal: date (conf 0.9)
reason: note calendar day 2026-04-10 (signal=date, conf=0.9) overlaps session ef010000 window most, not current source abcd0000
### Project: tiny-vacation-agent (1 issue)
[FAIL] 2026-04-11-enrichment-scope.md
current: [[2026-04-10-tiny-vacation-agent-aaaa]]
proposed: [[2026-04-11-tiny-vacation-agent-bbbb]]
signal: created_at (conf 1.0)
reason: note capture_time 2026-04-11T09:15:00+00:00 (signal=created_at, conf=1.0) matches session bbbb0000 window, not current source aaaa0000
Use [FAIL] for actionable issues (those with a proposed fix) and [WARN] for unresolved ones (those the check could not auto-repair). Always include a one-line summary at the top with the total count.
If the report is empty (exit code 0), tell the user:
Vault is clean. No issues found.
Stop here.
fix was requested)If the user did NOT pass fix:
Dry-run complete. Found N stale backlink(s) across K project(s). Run
/vault-doctor fixto apply repairs. Backups will be written to~/.claude/obsidian-brain-doctor-backup/<timestamp>/.
Stop here.
If the user DID pass fix:
Found N repairable issue(s) across K project(s). I'll apply per project with confirmation.
Re-run the dispatcher with --apply (do NOT pass --yes — let the dispatcher prompt per project interactively):
ARGS=()
[[ -n "${CHECK:-}" ]] && ARGS+=(--check "$CHECK")
[[ -n "${DAYS:-}" ]] && ARGS+=(--days "$DAYS")
[[ -n "${PROJECT:-}" ]] && ARGS+=(--project "$PROJECT")
[[ -n "${STRICT:-}" ]] && ARGS+=(--strict)
[[ -n "${RECONSTRUCT:-}" ]] && ARGS+=(--reconstruct)
[[ -n "${MIN_CONFIDENCE:-}" ]] && ARGS+=(--min-confidence "$MIN_CONFIDENCE")
ARGS+=(--apply)
python3 "$DISPATCHER" "${ARGS[@]}"
The dispatcher will prompt Apply N fix(es) for project 'X' in check 'Y'? [y/N] on stderr for each project. Relay each prompt to the user and pipe their response to the dispatcher's stdin.
Parse the final stderr output from the dispatcher and summarize:
vault_doctor apply complete
obsidian-brain: 3 applied, 0 unresolved, 0 errors
tiny-vacation-agent: 1 applied, 0 unresolved, 0 errors
Backups saved to: ~/.claude/obsidian-brain-doctor-backup/2026-04-11T17-04-22+00-00/
If exit code is 2, distinguish the source:
After a successful fix run:
Repairs applied. You can diff any fixed note against its backup under the backup root. Re-run
/vault-doctorto confirm the vault is clean.
scripts/vault_doctor.py and scripts/vault_doctor_checks/*.py. Do not re-implement any of it in this skill. The skill is pure orchestration and presentation.--apply only when the user explicitly requests fix.~/.claude/obsidian-brain-doctor-backup/<ISO-timestamp>/<project>/<basename>. Always mention the backup path in your summary so the user knows where to look.npx claudepluginhub abhattacherjee/obsidian-brain --plugin obsidian-brainMines projects and conversations into a searchable memory palace. Activates on queries about MemPalace, memory palace, mining, searching, palace setup, wings, rooms, drawers, or recalling past work.
Guides Payload CMS config (payload.config.ts), collections, fields, hooks, access control, APIs. Debugs validation errors, security, relationships, queries, transactions, hook behavior.
Implements vector databases with Pinecone, Weaviate, Qdrant, Milvus, pgvector for semantic search, RAG, recommendations, and similarity systems. Optimizes embeddings, indexing, and hybrid search.