← 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)
1. Symlink, don't copy
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 viareaddirSyncto heal.mdcross-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*-docssymlink 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
-ceeclone docs orcypress/docs— those are snapshots / test fixtures, not thinking material. Unify curateddocs/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."