Teaches an agent to drive a vault through Obsidian's official CLI — reading notes, querying the link graph, searching, editing properties — without a plugin, an MCP server or a second copy of your vault. A SKILL.md, reference files loaded on demand, and a small script
The CLI ships its own help, which is generated by the running app and always matches the machine it runs on, so this skill does not restate it. It documents the half help leaves out: what each number counts, what the output looks like, and the several places where a command answers confidently with something other than what was asked
The script, obsi.sh, wraps the CLI rather than replacing it — anything it does not recognise passes straight through — and adds the questions the command set cannot express: obsi.sh find for which note is this, told apart from which notes merely mention it, obsi.sh graph for what shape the vault's link graph is in, and obsi.sh selftest for whether the app in front of it still answers the way these documents say. It never prints the graph itself, which is around a megabyte of JSON for 1300 notes
- Obsidian 1.12+ with the command line interface enabled, and the desktop app running — the CLI is a client to a live app, not a standalone vault reader
- a shell; nothing else is installed by this skill
npx skills add -g rokokol/obsidian-cli-skill-g installs it for you rather than into whichever directory you are standing in — your vault is not a property of one repository
Claude Code also takes it as a plugin:
/plugin marketplace add rokokol/obsidian-cli-skill
/plugin install obsidian-cli@rokokol-skills
or by hand — clone into whichever skills directory your agent reads:
git clone https://github.com/rokokol/obsidian-cli-skill \
~/.claude/skills/obsidian-cliNote
A skill has no version to pin — it is read at whatever revision you have checked out, so git pull is the whole upgrade path
In Obsidian: Settings → General → Command line interface, then follow the prompt to register it. Confirm it works before pointing an agent at it:
obsidian-cli version # or: obsidian versionTwo numbers come back — the app's version and the installer's, which drift apart on packaged installs
Tip
The command name depends on how Obsidian was installed. Where the app registers itself it is obsidian; where a package manager ships the client separately it is obsidian-cli, and obsidian stays the GUI launcher — running the wrong one opens a second window instead of answering
Registration failing is not the same as the CLI failing. On NixOS the in-app button reports Unable to add to command line because the executable is "electron" instead of "obsidian", because it is refusing to symlink a wrapper script — while the packaged obsidian-cli works fine. The state that matters is "cli": true in ~/.config/obsidian/obsidian.json and a live socket at $XDG_RUNTIME_DIR/.obsidian-cli.sock
Everything in the skill was measured against a running app on a 5500-file vault rather than transcribed from the help text, and the measurements are printed alongside the claims so a later version can be checked instead of trusted
| Area | The part that is not in help |
|---|---|
| Error handling | every application error exits 0 and prints to stdout, so set -e and 2>/dev/null both miss it |
| Arguments | an unknown parameter name is dropped in silence, and the command then answers about whichever file is open in the GUI |
| Counting | total means occurrences on backlinks and unique targets on orphans and unresolved |
| The link graph | anchors are stripped, targets deduplicated, links inside fenced code blocks absent, and a link written through an alias counts as broken in both directions |
| Frontmatter | property:set without type=list flattens an existing YAML list, and a comma inside a list value cannot be escaped at all |
| Timing | indexing is asynchronous even through the CLI — 9 of 20 reads immediately after a write answered from the previous state |
| Output size | an unbounded search:context on a common word returned 98 MB in one call |
The references: references/commands.md for output shape and counting semantics per command group, references/pitfalls.md for each trap with the reproduction that found it, and references/obsi.md for the wrapper
- The command list.
obsidian-cli helpprints it, generated by the app, and the set is not fixed — some commands,daily:*among them, exist only when their core plugin or service is enabled. A copied list is a second source of truth that goes stale silently and disagrees with the machine it is read on - How notes should be written. Naming, frontmatter conventions and folder layout belong to the vault, not to its command line
- Anything about Obsidian's UI. This is for an agent at a shell prompt
Obsidian publishes no skills as an organisation, but kepano/obsidian-skills — from Steph Ango, Obsidian's CEO, under his personal account — is the closest thing to a first-party one, and it covers ground this repository does not: Obsidian Flavored Markdown, Bases, JSON Canvas, and the plugin development cycle. Its CLI skill is an introduction to the syntax; this one starts where that ends, at what the commands actually return
nix develop -c ./check.shLints the shell, checks that SKILL.md still carries the frontmatter an agent loads it by, and resolves every relative link and heading anchor in the docs — then proves each of those checks able to go red, against throwaway copies of the repository with one planted defect each. A check that has never failed is a decoration
Every Obsidian CLI command and parameter the docs spell is held to what the CLI's own help declares, by the ci skill's check-interface.sh. No runner has the app, so the gate reads tests/obsidian-help.txt, the help one version answered; run it as OBSIDIAN_CLI=obsidian-cli nix develop -c ./check.sh with the app open and the recording is first compared with the live help, so the day Obsidian renames something the gate says the recording is stale. Once the recording is regenerated, a command it no longer has is a finding wherever the docs still name it: the gate reads every earlier recording from git, which is why it needs a full clone
The behavioural claims cannot be gated in CI, because they need a running Obsidian with a real vault. They carry their measurements instead, which is what makes them falsifiable by anyone with an app open