| name | vault-curator |
| description | This skill should be used when users ask to "analyze vault metadata", "check for schema drift", "fix duplicate notes", "update note properties", "generate canvas", "improve vault connections", "create discovery view", "validate frontmatter consistency", "build knowledge map", "check for orphaned notes", or "analyze note relationships". Also handles: "find duplicates", "merge notes", "redirect links", "suggest properties", "show connections", "extract meeting from log", "migrate vault notes", "visualize my notes", "show me a map", "update this note", "write to vault", or "create a note from URL". Curates and evolves existing vault content through pattern detection, migration workflows, metadata intelligence, consolidation, discovery, visualization, and direct vault writes. Do NOT use for creating new templates, schemas, Bases queries, or vault structures (use vault-architect for those).
|
| metadata | {"version":"1.15.0","plugin":"archivist","stage":"3"} |
| license | MIT |
| compatibility | Requires python3.11+ and uv for script execution. Obsidian CLI 1.12+ for intelligence workflows. |
Vault Curator
Curate and evolve vault content through pattern detection, metadata intelligence, consolidation, discovery, and visualization. Principles: evolve gradually (test on small batches), validate before executing (dry-run first), checkpoint with git before operations, discover existing patterns before suggesting changes.
Scope Selection
All intelligence workflows (metadata, consolidation, discovery, visualization) begin with scope selection. Large vaults (7K+ files) require scoped operations.
Workflow:
-
Discover vault structure using CLI or file tools:
obsidian folders
tree -L 2 -d ${VAULT_PATH}
-
Present choices via AskUserQuestion with top-level directories
-
User selects scope (or types a path directly)
-
Scope all operations to the selected path for the rest of the session
Quick path: If the user mentions a specific topic ("my Docker notes"), search first:
obsidian search query="Docker" format=json
Edge cases: Empty scope → inform and suggest broadening. Whole vault → warn and require confirmation. CLI unavailable → fall back to tree + Glob/Grep.
CLI delegation: obsidian-cli, obsidian-markdown, obsidian-bases, json-canvas. See references/cli-patterns.md for command rules, error handling, and fallback strategy.
Opportunistic drift detection: When frontmatter is sampled, watch for competing property names, mixed-case fileClass, or YAML corruption. Offer drift detection before continuing.
Migration & Metadata Workflows
Vault Write Quality Gate
Before writing any note to the vault:
- Frontmatter on line 1 —
--- must be the very first characters; a leading newline silently breaks Obsidian's property parsing.
- Linter compliance — non-compliant notes produce spurious git diffs. Bulk validate:
uv run ${CLAUDE_PLUGIN_ROOT}/skills/vault-architect/scripts/validate_frontmatter.py ${VAULT_PATH}
- Wikilinks over backticks — use
[[Target]] for all vault entity references. See references/linking-discipline.md.
3a. Entity wikilink check — before writing, scan the proposed content for capitalized proper-noun phrases that are NOT already wikilinked. Bound the scan:
- Skip content inside code fences, inline code, and YAML frontmatter.
- Skip sentence-initial single capitalized words and common acronyms (API, JSON, YAML, HTTP, URL) via a stop-list.
- Prefer multi-word proper-noun phrases (
Prisma AIRS Team, Labs as a Service) over single-word candidates.
- Cap candidates per write at ~20; if exceeded, surface a summary ("Found N phrases. Show first 20 or skip?") rather than continuing.
For surviving candidates, run a single batch existence check using
obsidian search query="<phrase>" (or obsidian aliases). At the end of the scan, surface unresolved or multi-match candidates in one consolidated message:
"These mentions may want to be wikilinks: - 'Prisma AIRS Team' — no vault note found (write as plain text, create a stub, or specify a target?) - 'AIRS' — matches 3 notes (specify which, or leave plain?) Reply with link decisions or 'skip all' to proceed."
The user makes the call; rewrite only the mentions confirmed. Do NOT scan-and-rewrite autonomously. Do NOT issue one search per candidate inline.
- NEVER use
obsidian create overwrite with placeholder content — the command is destructive and atomic; it replaces the entire note body instantly. Always prepare the FULL content first, then write in one operation. To test write permissions, use obsidian property:set on a non-critical property — it is additive, not destructive. Never write "test", empty strings, or partial content to existing notes.
- ALWAYS read before writing — the Read tool or
obsidian read must be called on the target path before any Edit, Write, or obsidian create overwrite operation. This ensures you have the current content and can prepare a complete replacement.
- fileClass validation — when
fileClass: is present in content being written:
a. Re-read _vault-profile.md from disk (do not rely on the cached session-init copy — the architect may have registered new fileClasses mid-session).
b. Parse ## Active fileClasses: if rendered as a markdown table, extract the first column; else if a - bulleted list, take the first capitalized token on each line; else emit warning ("fileClass validation skipped — _vault-profile.md ## Active fileClasses is empty or unparseable") and proceed.
c. If the value appears in the parsed set, allow the write.
d. If absent: refuse and surface the gap ("fileClass <value> is not in the vault's Active fileClasses registry. Consult vault-architect to register it, or choose an existing fileClass: [list]."). Never invent a fileClass to complete a write.
Drift Triage Protocol
When schema drift is detected, classify each finding before acting:
- Mechanical (rename, type coercion, default fill): apply via
detect_schema_drift.py or obsidian property:set inline — deterministic, reversible, no subagent needed
- Semantic (new property design, collection restructuring, template changes): flag for vault-architect, provide the drift report as context
Run drift scripts inline when fixes are mechanical. Escalate to architect when fixes need design decisions. Drift correction should never interrupt a curation workflow — finish the task, then offer drift check as a follow-up.
Write Boundaries
Schema-change guard — before accepting any request, check whether it involves: defining or registering a new fileClass, establishing a new folder naming convention, or designing a new template schema or Bases view from scratch. These are architect-territory. Refuse immediately and route explicitly:
"Creating a new fileClass/folder convention/template schema is vault-architect work. I can handle content migration and metadata backfill after the architect establishes the schema."
Do NOT attempt the operation, even partially.
Check curator_write_zones: in ${CLAUDE_PLUGIN_ROOT}/.local.md before any write. A write is allowed if the target path starts with a listed zone prefix. Canvas files and discovery views are always allowed as generated output regardless of zone.
Out-of-zone: refuse with Archivist policy: prefix and suggest vault-architect. No zones configured in .local.md: the archivist agent's initialization derives zones from _vault-profile.md's "Directory Trust Levels" table when present — if those derived zones cover the target path, proceed; otherwise refuse with Archivist policy: prefix and offer to run vault profiling.
All writes require confirmation — the zone model determines which skill may write, not whether to confirm.
Refusal phrasing: Always prefix policy-based refusals with Archivist policy: to disambiguate from Claude Code tooling-layer permission denials. Never use bare "denied" wording for your own rules.
Meeting Extraction from Logs
When asked to extract meeting information from daily notes or inline logs:
- Parse Selection using script:
uv run ${CLAUDE_PLUGIN_ROOT}/skills/vault-curator/scripts/extract_section_to_meeting.py \
${VAULT_PATH} "${CURRENT_NOTE}" "${SELECTED_TEXT}"
- Infer Metadata - time from log syntax, company from folder path, attendees from inline fields
- Prompt for Missing Data - title, scope, attendees, meeting type
- Create Meeting Note using template, replace selection with wikilink
See: references/migration-strategies.md for extraction patterns
Calendar Import and Person Matching
Import calendar events → match attendees to Person notes → infer company → create meeting note.
See: references/migration-strategies.md for calendar import patterns
Vault Migration Patterns
Migrate existing notes to new schemas:
- Dry-Run First - Show planned changes, get approval
- Batch Validation - Test on 5-10 notes, verify Bases queries work
- Rollback Strategy - Git commit before migration, reverse script available
Common migrations: add scope to meetings, consolidate Dataview to Bases.
See: references/migration-strategies.md for comprehensive patterns
Property Suggestions
When asked "what properties should this note have?" or "suggest metadata":
- Select scope (or use current note's folder/fileClass)
- Analyze peer notes - scan notes with same fileClass or in same folder
- Identify gaps - find common properties the target note is missing
- Present suggestions with rationale ("87% of Meeting notes have 'scope'")
- Apply with confirmation - user approves per property
uv run ${CLAUDE_PLUGIN_ROOT}/skills/vault-curator/scripts/suggest_properties.py \
${VAULT_PATH} "${NOTE_PATH}"
Returns JSON with suggestions and confidence scores.
Schema Drift Detection
When asked to "detect schema drift" or "find inconsistent metadata":
- Select scope and target fileClass (e.g., Meeting, Person, Project)
- Scan all notes of that fileClass within scope
- Report inconsistencies:
- Missing required properties
- Inconsistent property types (string vs array)
- Naming convention mismatches (camelCase vs kebab-case)
- Suggest standardization with specific fixes
uv run ${CLAUDE_PLUGIN_ROOT}/skills/vault-curator/scripts/detect_schema_drift.py \
${VAULT_PATH} --file-class Meeting --scope "${SCOPE_PATH}"
Property Bulk Updates
When asked to batch update frontmatter:
- Collect target notes within scope
- Validate changes on sample (5-10 notes)
- Apply with progress tracking and error logging
- Git commit before and after
Safety: Always validate vault path. Preserve YAML formatting. Handle missing properties gracefully.
Consolidation Workflows
Consolidation detects duplicates, merges notes, and redirects links. All operations require user confirmation and git checkpoint before execution.
See: references/consolidation-protocol.md for full merge semantics, conflict resolution, and rollback procedures.
Find Similar Notes
Detect duplicates within a scope using tiered detection:
- Select scope (required)
- Run detection:
uv run ${CLAUDE_PLUGIN_ROOT}/skills/vault-curator/scripts/find_similar_notes.py \
${VAULT_PATH} --scope "${SCOPE_PATH}"
- Present groups — Tier 1 (identical titles) first, then Tier 2 (similar titles, matching tags)
- Per-group decision: merge / create MOC / mark aliases / skip
Thresholds: Default Tier 2 cutoff is 80% title similarity. See references/duplicate-detection-thresholds.md for tuning guidance, and for the canvas (--max-nodes, --node-width/-height) and collection-health (--coverage-threshold) policy values.
Merge Notes
Merge source note into target (surviving) note:
- Git checkpoint before merge
- Dry-run first:
uv run ${CLAUDE_PLUGIN_ROOT}/skills/vault-curator/scripts/merge_notes.py \
${VAULT_PATH} --source "${SOURCE}" --target "${TARGET}" --dry-run
- Review frontmatter changes (added, conflicts, merged lists) and content append
- Resolve conflicts — present each to user for decision
- Execute merge (remove
--dry-run)
Redirect Links
After merge, redirect all vault-wide references from deleted note to surviving note:
- Dry-run first:
uv run ${CLAUDE_PLUGIN_ROOT}/skills/vault-curator/scripts/redirect_links.py \
${VAULT_PATH} --old "${OLD_NAME}" --new "${NEW_NAME}" --dry-run
- Review affected files and replacement counts
- Execute redirect (remove
--dry-run)
- Delete source note only after redirect is confirmed
Discovery Workflows
Discovery workflows surface connections, suggest links, and generate views that help users navigate large vaults.
Related Note Finding
When asked "find related notes", "show connections", or "what links to this":
- Select scope (or use current note's folder)
- Run discovery:
uv run ${CLAUDE_PLUGIN_ROOT}/skills/vault-curator/scripts/find_related.py \
${VAULT_PATH} "${NOTE_PATH}" --scope "${SCOPE_PATH}" --top 10
- Present results with relatedness rationale — see
references/available-scripts.md
- Offer to add wikilinks to connect related notes (with user confirmation)
Progressive Discovery Views
When asked "show discovery view" or "organize notes by depth":
- Select scope (required)
- Determine hierarchy from note metadata:
- Entry points: Notes with
noteType: MOC or fileClass: MOC (Maps of Content, overviews)
- Detailed notes: Notes with specific
fileClass values (Meeting, Project, Person, etc.)
- Raw captures: Notes without
fileClass/noteType, or with fileClass: Capture/Log
- Generate
.base file — filter by file.folder starts-with [scope], group by noteType, columns: file, noteType, fileClass, tags, file.ctime
- Save as
_discovery-view.base in the scoped directory
- Document the hierarchy model: explain what each tier means for the user's vault
Auto-Linking Suggestions
When asked "suggest links", "what should I link", or "improve connections":
- Select scope (or use current note's folder/fileClass)
- Analyze metadata patterns across scoped notes:
- Find notes with similar tags but no wikilinks between them
- Identify notes that share
scope or project properties but aren't linked
- Detect notes frequently co-referenced (both linked from the same third note)
- Suggest tag additions based on peer analysis:
- "Notes in this folder commonly use tags X, Y — consider adding to these notes"
- Suggest Bases formulas for automatic views:
- "Create a Bases view filtering by
fileClass=Meeting + scope=[[Project]] to see all related meetings"
- Present suggestions with rationale, apply tag/property changes with confirmation
Canvas Map Generation
When asked "show me a map", "generate canvas", "visualize my notes", or "show knowledge map":
- Select scope (required)
- Generate canvas:
uv run ${CLAUDE_PLUGIN_ROOT}/skills/vault-curator/scripts/generate_canvas.py \
${VAULT_PATH} --scope "${SCOPE_PATH}" --dry-run
- Review dry-run output — confirm node count, edge count, clustering
- Execute (remove
--dry-run) to write .canvas file
See: references/available-scripts.md for canvas layout, naming conventions, and --output/--max-nodes options. Default thresholds (--max-nodes 50, --node-width 300, --node-height 120) and tuning guidance live in references/duplicate-detection-thresholds.md.
Canvas: Edit Existing
When asked to update, reorganize, add nodes to, or redesign an existing .canvas file:
- Read the current canvas JSON with the Read tool
- Plan changes — identify nodes/edges to add, remove, or modify using
json-canvas skill knowledge (node types, edge anchors, color presets, layout guidelines)
- Construct updated JSON — generate unique 16-char hex IDs for new nodes; verify all
fromNode/toNode references resolve to existing node IDs
- Write the updated canvas with the Write tool to
${VAULT_PATH}/<canvas-path>
- Verify — immediately read the file back and confirm the content matches what was written
Critical: Do NOT use obsidian create, obsidian write, or any CLI command to write .canvas files — the CLI does not handle JSON canvas format reliably and silently drops writes. Always use the Write tool directly.
See: json-canvas skill for node types, edge spec, color presets, ID generation, and validation checklist.
Pattern Detection
- Orphaned notes:
obsidian orphans — files with no incoming links
- Note clusters:
find_related.py --scope — groups of related notes within a directory
See: references/available-scripts.md for full script inventory
Collection Health Check
Audit Collection Folder Pattern compliance across the vault. A collection is healthy when it has a folder note, a Bases file, and consistent member frontmatter.
uv run ${CLAUDE_PLUGIN_ROOT}/skills/vault-curator/scripts/check_collection_health.py \
${VAULT_PATH} [--scope "${SCOPE}"] [--folder "700 Notes/Workflows"]
Output fields: has_folder_note, folder_note_embeds_bases, has_bases_file, dominant_fileclass, schema_drift_issues, health — see references/collection-health-criteria.md for field definitions, health thresholds, and fix priority order.
After report, offer fixes in order: missing folder note or Bases file → scaffold via vault-architect; folder note missing Bases embed → add with confirmation; schema drift → run detect_schema_drift.py --scope <folder>.