← the whole session plugin/skills/unify-obsidian-vaults/SKILL.md

Unify scattered repo documentation into one canonical Obsidian vault via symlinks, with source-repo provenance rendered without editing source files. Use when a person works across multiple repos, their docs are fragmented across those repos, and they want them navigable/searchable from a single vault, or when an accidental second .obsidian vault config needs collapsing into the canonical one.

Unify Obsidian Vaults Into One Canonical Vault

This applies only if the person actually works across multiple repos AND uses Obsidian for their documentation — if either isn't true, this skill has nothing to do.

Intent (what this is for)

Someone who works across several repos usually wants to think in ONE Obsidian vault, not several. Documentation that lives in other repos is invisible from their main vault, so institutional knowledge is split across roots they have to remember to open separately. That fragmentation defeats the purpose of a single intent map.

This skill makes every repo's docs visible and navigable inside the one canonical vault, each labeled with its source repo, without copying files and without editing the source files in their home repos.

The mechanism (and why each choice)

Obsidian follows directory symlinks. Use that:

INTENT=<path to the person's canonical vault>
ln -s <path to another repo>/docs   "$INTENT/<repo-name>-docs"
ln -s <path to a third repo>/docs   "$INTENT/<repo-name-2>-docs"

Example (opt-in illustration — swap in your own vault and repo paths):

INTENT=<project>/api/docs/intent
ln -s <project>/web-app/docs        "$INTENT/web-app-docs"
ln -s <project>/landing/docs  "$INTENT/landing-docs"

One intent vault already used this pattern for one other tree (agent-skills -> ~/.codex/skills) before extending it to repo docs — if your vault already symlinks something in, follow whatever pattern it already established.

Symlinks (not copies) because copies go stale the moment the source changes; symlinks always render current. Name the link <repo>-docs so the source repo is visible in every file's path.

Before creating any symlink — verify no recursion. If the target tree contains a copy of the vault or a path back into docs/intent, Obsidian will loop. Check:

find <target>/docs -maxdepth 3 -type d \( -name "$(basename "$INTENT")" -o -path "*$(basename "$(dirname "$INTENT")")*" \) 2>/dev/null

Empty result = safe to link.

2. Provenance WITHOUT editing source files

The symlinked files physically live in their home repos. Writing source_repo: front matter INTO them means editing thousands of files across other repos' git history and tripping their pre-commit hooks. Don't. Instead provenance is carried two ways that touch zero source files:

  • Folder name — <repo-name>-docs/... shows the source in every path (file explorer, search, graph, backlinks).
  • Folder note — one _index.md (or a folder-note) per linked tree at the vault root declaring the source repo as a dataview-readable property. One authoritative provenance statement per source, impossible to get out of sync, survives source files changing.

Only dataview is enabled as a community plugin in this vault — the folder note uses dataview/ front matter the vault can already render. Do not require a new plugin.

The folder note lives in the vault (NOT in the symlinked tree — writing into the symlink writes into the source repo). Place it adjacent:

<vault>/<repo-name>-docs.md        ← folder note for the <repo-name>-docs/ symlink
<vault>/<repo-name-2>-docs.md      ← folder note for the <repo-name-2>-docs/ symlink

with front matter:

---
source_repo: <repo-name>
unified_from: <path to that repo>/docs
unified_via: symlink
note: "Provenance label. Files render from the source repo; not copied, not edited."
---

3. Collapse accidental fragmentation

An accidental second .obsidian config at a repo ROOT means Obsidian was once opened on the repo root instead of on the canonical vault folder. That's the real fragmentation. Remove ONLY the stray config dir, leaving the canonical vault's own .obsidian as the single vault. Move to .trash rather than hard-delete for reversibility:

mv <repo root>/.obsidian \
   "$INTENT/.trash/stray-root-obsidian-$(date +%Y%m%d)"

Never delete the root .md files — only the stray vault marker.

Guardrails (the real risks)

  • If you have a link-healing script for your vault (one example is api/scripts/heal-vault-links.js, walking the vault via readdirSync to heal .md cross-refs), check it still works after adding symlinks — symlinked trees are followed by readdir, so if it ever errors on a symlinked path, scope it to skip the *-docs symlink roots rather than disabling it. If you have no such script, this doesn't apply.
  • graphify — after structural change run the code-graph rebuild noted in CLAUDE.md if the graph should reflect the new docs.
  • Never write into a symlinked path thinking you're writing into the vault — you're writing into the source repo's git tree. Folder notes go at the vault root, beside the symlink.
  • Don't symlink -cee clone docs or cypress/docs — those are snapshots / test fixtures, not thinking material. Unify curated docs/ trees only.

Verify

ls -la docs/intent/*-docs        # symlinks resolve, point at real dirs
# Open Obsidian on the canonical vault → the <repo-name>-docs/ folders render
# Search a known doc title from one of the linked repos → it appears

Verification is "open the vault and see the folders render," not "the symlink command exited 0."