A thin Neovim wrapper for the official Obsidian CLI. Drives the live Obsidian app from inside Neovim — no Lua reimplementation of the vault format.
🚧 Early version (
v0.0.x). Commands, configuration, and keymaps may change without notice untilv0.1.0. Pin to a tag if you depend on stability.
Why this and not obsidian-nvim/obsidian.nvim?
The two plugins serve genuinely different audiences. Pick based on whether you're willing to keep the Obsidian desktop app running.
obsidian-cli.nvim (this) |
obsidian-nvim/obsidian.nvim |
|
|---|---|---|
| Implementation | Thin wrapper over the official obsidian CLI |
Pure Lua reimplementation, ~4-5k LOC |
| Lines of code | ~2,000 | ~4,000-5,000 |
| Commands shipped | 44 (daily-driver + navigation + CRUD + plugin management + escape hatch + Bases + tags + templates) | 28 (full feature set) |
| Test coverage | 122 plenary tests across 12 spec files | Community-tested, no bundled suite |
| Requires Obsidian app running | Yes — the CLI is a remote control for the live app | No — works fully headless |
| Vault discovery | Auto-detected via obsidian vault info=path |
Requires explicit workspaces config |
| Search | Uses Obsidian's actual search index (knows aliases, tags, link semantics) | Uses ripgrep (pure regex over file content) |
| Backlinks | Obsidian's link graph (instant, cached by the app) | ripgrep on-demand (re-scans vault each request) |
| Templates | Obsidian core Templates + Templater (wrapped via :ObsidianTemplates, :ObsidianNewFrom) |
Custom {{variable}} string substitution only |
| Daily notes path/format | Read at runtime from your Obsidian Settings | Plugin-side daily_notes config (must duplicate) |
| Frontmatter | Obsidian's typed property system | Custom Lua YAML parser |
| Templater, Dataview, Tasks, Periodic Notes | Reachable via :ObsidianCommand/:ObsidianCommandList escape hatch (dedicated wrappers planned for v0.1.0 if you install the plugins) |
Not accessible — these live in Obsidian's JavaScript runtime |
| Bases (Obsidian core DB feature) | Wrapped: :ObsidianBases, :ObsidianBaseViews, :ObsidianBaseQuery, :ObsidianBaseCreate |
Not accessible — Bases is Obsidian-native |
| Plugin management (install, enable, disable, reload) | Wrapped: :ObsidianPluginList and CRUD commands — install community plugins from Neovim |
Not applicable — doesn't use Obsidian's plugin ecosystem |
| Performance ceiling | Bound by Obsidian app speed | Slows past ~5,000 markdown files (no persistent index) |
| Update cadence | Inherits new features from Obsidian releases | Community-maintained, manual feature parity |
| First-launch cost | Cold-boot Obsidian (~2s) if not already running | Instant |
| Works on headless server / SSH / no display | No | Yes |
- You have Obsidian installed and don't mind keeping it running in the background
- You want Obsidian's real search index (knows aliases, tags, link semantics — not just regex over files)
- You want real backlinks from Obsidian's link graph (cached by the app, not re-scanned per request)
- You want the plugin to just work without configuring vault paths or daily-note formats — it reads them from Obsidian
- You value a small, focused codebase (~2,000 LOC) that inherits new Obsidian features as the CLI exposes them
- You want the option to call into Templater/Dataview/Tasks/Bases via the CLI's escape hatch (dedicated wrappers planned for v0.1.0)
- You need the plugin to work without the Obsidian desktop app running (SSH, headless server, battery-conscious use, no-GUI environments)
- You don't use Templater, Dataview, Tasks, or Bases — basic markdown and wiki links are enough
- You're OK with explicit vault configuration and a custom YAML/template parser
- Daily notes — open today, append tasks, list incomplete tasks, all from inside Neovim
- Task toggling —
<leader>octo toggle the checkbox on the current line - In-buffer edits — task capture, toggling, and note creation happen instantly without disk round-trips
- Vault-aware [[wiki link]] completion via blink.cmp — autocomplete from your real vault notes
- Follow wiki links —
<leader>olto jump to the note under cursor; offers to create it if missing - Live full-text search — Snacks picker that updates as you type
- Backlinks & unresolved links — surface broken links and resolve them with one keystroke
- Recent & find pickers — browse vault notes with preview pane
- Note creation from templates —
:ObsidianNewFromuses your Obsidian template setup - Resolve broken links —
<leader>oRcreates a note from the[[link]]under cursor
- Plugin browser —
:ObsidianPluginListwith action menu (enable/disable/uninstall/reload/info) - Install plugins from Neovim —
:ObsidianPluginInstall <id>,:ObsidianPluginUninstall <id>, etc. - Restricted mode —
:ObsidianRestrictedMode on/offto toggle Obsidian's safe mode - Generic command runner —
:ObsidianCommandListpicker to run any Obsidian command (Templater, Dataview, etc.)
- Database views —
:ObsidianBases,:ObsidianBaseViews,:ObsidianBaseQuery,:ObsidianBaseCreate - Native Obsidian database tables exposed as Neovim pickers
- Auto-pairs interop — wiki link completion plays nice with mini.pairs / nvim-autopairs
- Confirmation prompts — destructive actions (disable/uninstall) prompt before proceeding
- Health check —
:checkhealth obsidian-clidiagnoses the entire stack - 122 automated tests — plenary-based test suite covering all commands with mocked CLI, CI integration
- Obsidian desktop app, version 1.12.0+ (download)
- Obsidian CLI enabled via
Settings → General → Command line interface(must register and add to PATH) - The Obsidian app must be running when you invoke any command. The Obsidian CLI is a remote control client that talks to the live desktop app over IPC. Without the app running, every command will fail with
The CLI is unable to find Obsidian.See the official Obsidian CLI documentation for details. - Neovim 0.10+ (uses
vim.system,vim.diagnostic.is_enabled)
⚠️ The plugin does not auto-launch Obsidian. Trying to launch a GUI app silently is platform-dependent and unreliable. Instead, the plugin treats Obsidian as an always-on background service that you set up once.
Add Obsidian.app to your OS startup items so it's always running in the background. After the one-time setup, you'll never see the "Obsidian not running" error again.
- macOS:
System Settings → General → Login Items & Extensions → Open at Login → +→ selectObsidian.app. Check the "Hide" box if you don't want the window to appear at login. - Linux: depends on your desktop environment — typically
Settings → Startup Applications → Add→ commandobsidian(or your package's launcher). - Windows:
Settings → Apps → Startup → Obsidian → On.
Also enable Obsidian's own "Open previous vaults on startup" setting (Settings → About) so the app boots straight into your vault rather than the vault picker.
When the plugin can't reach the app, it errors with a clear hint:
:ObsidianToday: The CLI is unable to find Obsidian.
Hint: launch the Obsidian desktop app (e.g. `open -a Obsidian`) and retry.
You can launch it from inside Neovim with :ObsidianStart (cross-platform — runs the appropriate launcher for your OS) and retry the command.
{
"jeryldev/obsidian-cli.nvim",
event = "VeryLazy",
opts = {},
}That's it. The plugin registers all 44 commands and default keymaps during setup(). No need to list them in the spec — event = "VeryLazy" loads the plugin on startup and everything is available immediately.
After installing, run :checkhealth obsidian-cli to verify everything is wired up.
Why not
ft = "markdown"orcmd = {...}? Those are valid alternatives for stricter lazy-loading, butVeryLazyadds only ~10-20ms to startup and avoids the "command not found because plugin hasn't loaded yet" problem. If you want tighter control, see docs/configuration.md for the explicit cmd/keys spec.
Add a blink.cmp source override:
{
"saghen/blink.cmp",
optional = true,
opts = {
sources = {
default = { "lsp", "path", "snippets", "buffer", "obsidian" },
providers = {
obsidian = {
name = "Obsidian",
module = "obsidian-cli.completion.blink",
score_offset = 100,
},
},
},
},
}See docs/completion.md for the full setup including suppressing other sources inside [[...]].
<leader>ot " Open today's daily note
<leader>oT buy milk " Append a task to today's note
<leader>ox " Show today's incomplete tasks
<leader>oc " Toggle the task on the current line
<leader>fo " Find any note (Snacks picker)
<leader>os " Live full-text search
<leader>on Trip " Create a new note titled "Trip"
<leader>ob " Show backlinks for the current note
<leader>ou " Show unresolved [[wiki links]]
<leader>ol " Follow [[link]] under cursor (create if missing)
<leader>oR " Force-create a note from the [[link]] under cursorIn a vault markdown file, type [[ to get vault-aware autocompletion of all your notes.
:ObsidianPluginList " Browse installed plugins + enable/disable/uninstall
:ObsidianPluginInstall templater-obsidian
:ObsidianCommandList " Pick from every registered Obsidian command
:ObsidianCommand app:reload " Run a specific Obsidian command directly
:ObsidianRestrictedMode on " Toggle safe mode (disable all community plugins)
:ObsidianBases " Browse .base files (Obsidian's database feature)Full reference docs live in docs/:
- Commands — every
:Obsidian*command, what it does, what it wraps - Configuration — full
setup()options reference - Completion — wiki link completion via blink.cmp
- Troubleshooting — common issues and fixes
- Architecture — design decisions and CLI quirks (for contributors)
- Testing — writing and running the plenary test suite
- 14 new commands (44 total) — daily navigation, link auditing, tags, file CRUD, templates
:ObsidianYesterday/:ObsidianTomorrow— navigate between adjacent daily notes:ObsidianOutline— heading picker for the current note; jump to any section:ObsidianLinks— outgoing links from the current note:ObsidianOrphans/:ObsidianDeadends— link-graph health auditing:ObsidianTags/:ObsidianTag <name>— tag browser with two-step flow (tags → notes):ObsidianRename/:ObsidianMove/:ObsidianDelete— file CRUD with confirmations + collision detection:ObsidianOpenInApp— open current note in the Obsidian desktop app:ObsidianTemplates/:ObsidianTemplateInsert— browse and insert templates- Rename collision detection — blocks rename when target filename already exists
- 122 automated tests (up from 60 in v0.0.4) across 12 spec files
- Simplified install — just
event = "VeryLazy"+opts = {}, nocmd/keyslists needed
Planned for v0.1.0:
:ObsidianPrependToday:ObsidianRecentlyOpened(Obsidian's internal recency list, complements mtime-based:ObsidianRecent):ObsidianProperties/:ObsidianPropertySet(typed frontmatter editing):ObsidianWorkspace(multi-vault switching):ObsidianDiff(CLI sync history viewer — requires Obsidian Sync):ObsidianEval {js}(raw JavaScript escape hatch)gfpassthrough for[[wiki]]and[markdown](links)<CR>smart action (toggle checkbox / follow link)- Telescope and fzf-lua picker adapters
- Display-text alias completion (
[[Note|alias]]) - Heading and block reference completion (
[[Note#heading]]) - Dedicated wrappers for Templater / Dataview / Tasks / Periodic Notes (when installed)
- Keymap reorganization with sub-prefixes (task group under
<leader>ot*)
Considering for later:
- Headless mode for offline / SSH use (filesystem-based fallback for commands that don't need the index)
:ObsidianBookmark/ bookmarks browser- Image paste integration with
img-clip.nvim - Picker preview rendering for markdown files via render-markdown.nvim
- Official Obsidian CLI documentation: help.obsidian.md/cli
- Obsidian CLI overview & install: obsidian.md/cli
- Obsidian Headless (separate sync-only product, not used by this plugin): help.obsidian.md/obsidian-headless
- Obsidian app download: obsidian.md/download
- Obsidian community plugins: obsidian.md/plugins — Templater, Dataview, Tasks, Periodic Notes, etc. all work via
obsidian command id=...(planned for v0.1.0)
Issues and PRs welcome at github.com/jeryldev/obsidian-cli.nvim.
Until v0.1.0, expect breaking changes. Pin to a specific tag if you need stability.
See docs/architecture.md for the module breakdown and design rationale before submitting PRs.
MIT