obsidian-health
The obsidian-health command runs a comprehensive audit of an Obsidian vault by scanning for broken links, duplicate concepts, missing frontmatter, stale claims, orphaned notes, contradictions, and concept gaps. Run this command when you need to identify structural issues and knowledge gaps in your vault, then use parallel agents to address each category of problems grouped by severity level from critical to informational.
mkdir -p ~/.claude/commands && curl -fsSL https://raw.githubusercontent.com/eugeniughelbur/obsidian-second-brain/HEAD/commands/obsidian-health.md -o ~/.claude/commands/obsidian-health.mdobsidian-health.md
Use the obsidian-second-brain skill. Execute `/obsidian-health`:
1. Read `_CLAUDE.md` first to find the vault path
2. Run the health scan from the skill root (its absolute path was given at session start as **Skill root**; substitute it for `SKILL_ROOT`): `uv run --directory "SKILL_ROOT" scripts/vault_health.py --path ~/path/to/vault --json`
(replace vault path with the one from `_CLAUDE.md`)
- **Large or noisy vault?** If the scan surfaces thousands of findings from directories the user does not maintain by hand (atomic-card pools, backup snapshots, imported dumps), do NOT hardcode fixes. Offer to write a `<vault>/.vault-config.json` that extends the exclude list: `{"exclude-dirs": ["_card-pool"], "exclude-paths": ["Archive/Backup"]}`. `exclude-dirs` matches directory names anywhere in the tree; `exclude-paths` matches vault-relative path prefixes. Both are additive - the built-in excludes always apply - and a missing or malformed file is ignored silently. Re-run the scan afterward.
3. Parse the JSON output and split findings into categories
4. Spawn parallel subagents to handle each category simultaneously:
- **Wanted-notes agent**: the script reports `wanted_note` items - links to a note that does not exist yet. These are NOT errors: in a wiki-style vault you link a thing the moment you mention it, so wanted notes are a demand-ranked wishlist of pages worth writing, not breakage. Triage them with `uv run --directory "SKILL_ROOT" scripts/triage_links.py --path <vault> --limit N`, which sorts each into keep (a deliberate seed, leave it), create (referenced enough to deserve a real note now), or delete (junk or a typo - fix the link). Report-only by default; needs `ANTHROPIC_API_KEY`. Headless on purpose: it can run unattended or on a schedule. The goal is to triage the backlog, never to drive the count to zero.
- **Duplicates agent**: confirm duplicates are truly the same concept, not just similar names
- **Taxonomy agent**: only fires when `<vault>/_meta/taxonomy.md` exists (format in `references/taxonomy-format.md`) - the script's `tag_synonym` and `tag_not_in_taxonomy` findings are empty otherwise, so a vault without the file sees nothing. `tag_synonym` means a note's tag is a known synonym of a canonical tag: the fix is unambiguous, so offer to rewrite that note's `tags:` frontmatter to the canonical form, per note, with explicit confirmation before editing - never batch-rewrite across the vault unattended. `tag_not_in_taxonomy` means the tag matches neither a canonical tag nor a synonym: report it as informational only - it may be a legitimate new tag - and never auto-fix it; the taxonomy file is a human-curated vocabulary, not something this command edits on its own.
- **Frontmatter agent**: identify notes missing required fields by type. If the script reports a `code_fence_wrapped` note (frontmatter trapped inside a leading ```` ```markdown ```` fence), the fix is to **unwrap it** - strip the opening fence line and the matching closing ```` ``` ```` so the inner `---` frontmatter and body become real markdown. **Never add a new frontmatter block to a wrapped note** - that produces duplicate frontmatter and leaves the body trapped. If the note already has both a prepended frontmatter block and an inner wrapped one, merge them (keep the richer fields) and unwrap.
- **Staleness agent**: check overdue tasks and unfilled template syntax
- **Orphans agent**: check orphaned notes and empty folders
- **Contradictions agent**: scan Key Decisions sections and reference/concept notes (the concept or knowledge folder per `references/folder-map.md` - wiki-style `wiki/concepts/`, Obsidian-style `Knowledge/`) for claims that conflict with each other or have been superseded by newer sources
- **Typed-edge lint agent**: run `uv run --directory "SKILL_ROOT" scripts/link_graph.py --path <vault> --lint`, which validates the `relations:` typed-edge layer (see `references/ai-first-rules.md` Rule 6 § Typed edges). It returns `findings` (each with `severity`, `kind`, `note`, `target`, `type`, `detail`) and a `summary`. Map them into the severity groups below: `contradiction` (A and B claim the same asymmetric type about each other) is 🔴 Critical; `unknown_type`, `dangling_target`, and `self_edge` are 🟡 Warning (fix the type name, the target link, or drop the self-edge); `missing_inverse` is ⚪ Info (offer to add the reciprocal edge on the target note, never required). If the vault uses no `relations:` blocks yet, this returns zero findings - not an error.
- **Concept gaps agent**: find terms mentioned 3+ times across different notes that lack a dedicated page - these are missing concepts the vault should have
- **Stale claims agent**: compare reference/concept notes (the concept or knowledge folder per `references/folder-map.md` - wiki-style `wiki/concepts/`, Obsidian-style `Knowledge/`) against their source dates - flag any note older than 6 months that references fast-moving topics (tools, APIs, pricing, team structure)
- **Freshness agent**: run the freshness lint - `uv run --directory "SKILL_ROOT" scripts/freshness_lint.py --path <vault> --json` - which enforces `references/freshness-policy.md`: every stored fact must be timeless, dated, or a pointer. FRESH-1 errors are present-tense claims about fast facts (counts, statuses, balances) with no `as of` stamp - the sentences that silently become lies. Report the top offenders grouped by folder; offer to fix by adding a stamp, converting to a pointer (where truth lives + last observed value), or moving the claim into a dated note. For aged stamps (FRESH-2 warnings) run the refresh loop from the policy: re-observe (check the source, update value + stamp), convert (keep only the pointer), or retire (move into a dated note as history). Never delete - restamp, convert, or mark superseded
5. Merge results and group by severity:
- 🔴 Critical: unfilled template syntax, contradictions between notes, typed-edge cont>
Create a new obsidian-second-brain command via interview - zero markdown editing required
Surface 3-5 next-direction candidates by reading ungraduated ideas, open project questions, and orphan research notes - what is worth working on next
Vault-first source-grounded research via Gemini File Search. One command, no browser. The grounded parallel to /research-deep (which is open-web via Perplexity).
Generate a decision record when the vault structure changes - the vault knows why it knows what it does
Read Google Calendar and write an AI-first snapshot to the vault - today, week, next week, or a custom range
Scan a codebase and write a maintained set of architecture notes into the vault - overview, per-module notes, key decisions. Re-run to refresh without clobbering your edits
Show or update a kanban board - flags overdue items, updates from conversation