Generic, scriptable maintenance commands for an Obsidian vault.
vault-cli is a small command-line toolkit for keeping an Obsidian vault:
- easy to navigate
- densely linked
- low-noise (fewer broken links / weird formatting)
It’s designed to be generic and safe-by-default.
Obsidian uses wikilinks like:
[[note]][[note|label]][[note#heading]][[note^block]]
This CLI understands these formats when linting.
A stub is a real markdown file that exists mainly to keep a link from being “broken”.
Example: you type [[vertical-saas]] while capturing a tweet. If there’s no vertical-saas.md note yet, Obsidian shows it as an unresolved link.
Stubs are useful because they:
- let you stay in flow while capturing (link now, structure later)
- keep the graph cleaner (links resolve)
- make it obvious what to “promote” later (turn a stub into a real person/concept/tool note)
vault-cli supports a plan/apply workflow to promote stubs safely.
When people/concepts are consistently linked:
- backlinks work (great for 1:1 prep and research)
- the graph becomes useful instead of a hairball
- you can jump from meeting → person → related concepts in 1–2 clicks
- Generic: works across many vault structures
- Safe-by-default: dry runs, explicit writes
- Configurable: vault structure assumptions live in config
bun install
bun run buildThis project is intended to be used via a local clone (no npm publishing).
git clone https://github.com/aaronvanston/vault-cli.git
cd vault-cli
bun install
bun run build
# Option A: run directly
node dist/cli.js --help
# Option B: install the `vault` command on PATH
npm link
vault --helpBy default the CLI treats the current working directory as the vault root.
You can add vault.config.json at the vault root:
{
"vaultRoot": ".",
"folders": {
"people": ["03-people"],
"concepts": ["02-concepts"],
"meetings": ["06-meetings"],
"stubs": ["99-stubs"]
},
"frontmatter": {
"peopleType": "person",
"conceptType": "concept",
"meetingType": "meeting-notes"
}
}Purpose: quickly answer “is my vault healthy?”.
Checks:
- Broken wikilinks (
[[...]]) that don’t resolve to any note (ignores code blocks/inline code) - Frontmatter presence (skips common vault docs by default:
README.md,AGENTS.md,CLAUDE.md,SPEC.md)
Examples:
vault lint --root /path/to/vault
vault lint --root /path/to/vault --jsonPurpose: turn plain text mentions into links using existing entity notes.
How it works:
- builds a dictionary from existing notes with
type: personortype: concept - uses
aliasesfrom YAML frontmatter for matching - skips existing wikilinks and fenced code blocks
Safety:
- Dry-run by default
--writeto apply
Examples:
vault linkify --root /path/to/vault 00-inbox/captures/2026-01-02-something.md
vault linkify --root /path/to/vault 00-inbox/captures/2026-01-02-something.md --writePurpose: linkify at scale.
Safety:
- Dry-run by default
- Use
--limitwhile testing - Skips
AGENTS.md/CLAUDE.md/README.md/SPEC.md
Examples:
vault linkify-all --root /path/to/vault --glob '00-inbox/**/*.md'
vault linkify-all --root /path/to/vault --glob '00-inbox/**/*.md' --limit 50 --writePurpose: find stubs that are referenced a lot and generate a plan you can review.
This produces a JSON file you should edit (choose where each stub should live).
vault promote-stubs --root /path/to/vault --min-refs 10 --out vault.promote-stubs.plan.jsonPurpose: apply the plan.
Safety:
- Default is dry-run (prints what it would move)
--writeactually moves files
vault promote-stubs-apply --root /path/to/vault --plan vault.promote-stubs.plan.json
vault promote-stubs-apply --root /path/to/vault --plan vault.promote-stubs.plan.json --writePurpose: rename a note and update all wikilinks across the vault.
Safety:
- Dry-run by default
--writerenames the file and updates links
Examples:
vault rename --root /path/to/vault --from old-note --to new-note.md
vault rename --root /path/to/vault --from 03-people/alice.md --to 03-people/alice-example.md --writePurpose: apply safe, low-risk automatic fixes.
Currently fixes:
- escaped pipe in wikilinks:
[[foo\\|Bar]]→[[foo|Bar]] - trailing slash in wikilinks:
[[nextjs/]]→[[nextjs]]
Safety:
- Dry-run by default
- Skips fenced code blocks
Examples:
vault fix --root /path/to/vault
vault fix --root /path/to/vault --write- Notes are
.md - Wikilinks use Obsidian format
[[target|label]] aliasesin YAML frontmatter is supported for entity matching
No plugins are required.
This tool complements:
- Obsidian backlinks + graph view (core)
- Dataview (optional) for dashboards
- No network calls
- No external analytics
MIT