From forge-obsidian
Official Obsidian CLI (1.12+) — list files, search content, manage properties, query bases, navigate links, create/read/rename/delete notes. USE WHEN listing vault files, searching vault, managing frontmatter properties, querying bases, checking backlinks, creating notes via Obsidian, renaming with backlink updates, or any vault operation that benefits from Obsidian's internal index.
npx claudepluginhub n4m3z/forge-obsidianThis skill uses the workspace's default tool permissions.
Reference for the official Obsidian CLI (1.12+). Communicates with the running Obsidian desktop app via named pipe. Returns structured data to stdout.
Runs Obsidian CLI commands for vault ops: search, backlinks, tags, tasks, properties, daily notes, file read/write/create/move. Use for indexed features; fallback to file tools otherwise.
Manages Obsidian vault files and folders via official CLI: read, create, append, prepend, move, delete notes; list files/folders; handle daily notes.
Manages Obsidian vaults using obsidian-cli: creates daily notes, moves/renames notes preserving [[wiki-links]], searches content, organizes notes with templates.
Share bugs, ideas, or general feedback.
Reference for the official Obsidian CLI (1.12+). Communicates with the running Obsidian desktop app via named pipe. Returns structured data to stdout.
Falls back to file-system operations (Glob, Grep, safe-read, safe-write) when unavailable.
The CLI ships inside Obsidian 1.12+. Add to PATH (one-time):
export PATH="$PATH:/Applications/Obsidian.app/Contents/MacOS"
Or use the full path directly. Verify with make check in the module root.
key=valuecontent="Hello world"file=<name> (wikilink resolution) or path=<exact/path.md>vault=<name> or inferred from CWD or active vaultAll commands support: format=json|csv|tsv|md|paths|text|tree|yaml
Default is text. For programmatic consumption use format=json or format=paths.
obsidian files # all files
obsidian files folder=Resources ext=md # filter by directory and extension
obsidian files format=paths # file paths only (one per line)
obsidian files format=json # structured JSON
obsidian read file="Security" # by wikilink name
obsidian read path="Topics/Security.md" # by exact path
obsidian create name="New Note" content="---\ntitle: New Note\n---\n\nContent here."
obsidian create name="From Template" template=Daily
obsidian append file="Daily" content="- New item"
obsidian prepend path="Topics/Security.md" content="## Update\n\nNew section."
prepend inserts after frontmatter.
obsidian move file=Recipe to=Archive/
obsidian rename file="Old Name" to="New Name"
rename updates all wikilinks and markdown links across the vault. This is the only reliable way to rename with backlink tracking — file-system mv breaks links.
obsidian delete file=OldNote # to Obsidian trash
obsidian delete file=OldNote permanent # permanent
obsidian property:set file="Note" property=status value=active
Atomic update — avoids the Obsidian Linter race condition that affects read-modify-write cycles with safe-write edit.
Scalar values only. Cannot set arrays (tags, comments, sources) or nested objects. For complex field types, use eval with processFrontMatter (see Developer section).
obsidian property:remove file="Note" property=obsolete_key
obsidian property:read file="Note" property=status
obsidian property:read file="Note" property=status format=json
Stdout contamination. Like base:query, property:read can mix extraneous output into stdout. Redirect to temp file for reliable parsing, or use eval with processFrontMatter for programmatic reads.
obsidian property:rename old=old_key new=new_key
obsidian search query="forge-tlp"
obsidian search query="forge-tlp" format=json
Property filter syntax: [tag:project], [rating:>4], [status:active].
obsidian search:context query="forge-tlp"
obsidian backlinks file="Security"
obsidian backlinks file="Security" format=json
obsidian links file="Security"
obsidian orphans # notes with no links in or out
obsidian unresolved # broken wikilinks
# Redirect to temp file — stdout race condition on heavy queries (known upstream issue)
obsidian base:query path="Resources/Books.base" format=json > /tmp/base-out.json 2>&1
cat /tmp/base-out.json
command rm -f /tmp/base-out.json
# Path-only output
obsidian base:query path="Resources/Books.base" format=paths
When CLI is unavailable, use the standalone obsidian-base binary (see /ObsidianBase).
obsidian daily # open today's daily note (creates if missing)
obsidian daily:read # read content
obsidian daily:append content="- [ ] Task" # append to end
obsidian daily:prepend content="## Morning" # prepend after frontmatter
obsidian daily:path # get expected path (no creation)
Execute JavaScript in the running Obsidian context:
obsidian eval code="app.vault.getFiles().length"
obsidian eval code="app.vault.getMarkdownFiles().map(f => f.path).join('\n')"
Has access to app, vault, workspace, plugins. Use for operations the CLI doesn't expose natively.
The primary tool for vault-wide frontmatter migrations. Handles arrays, nested values, field deletion, and atomic read-modify-write — everything property:set cannot.
Single file:
obsidian eval code="
const file = app.vault.getAbstractFileByPath('path/to/Note.md');
await app.fileManager.processFrontMatter(file, fm => {
fm.tags = ['type/memory/insight'];
fm.comments = ['Context: what prompted this'];
delete fm.oldField;
});
"
Batch (filter + loop):
obsidian eval code="
const files = app.vault.getMarkdownFiles().filter(f => f.path.startsWith('Archives/Memory/Ideas/'));
for (const file of files) {
await app.fileManager.processFrontMatter(file, fm => {
if (fm.spark) { fm.context = fm.spark; delete fm.spark; }
if (fm.idea) { fm.description = fm.idea; delete fm.idea; }
});
}
console.log('Done: ' + files.length + ' files');
"
Key rules:
await — processFrontMatter is async. Without await, changes silently don't persist.for...of, never forEach — forEach doesn't await async callbacks.delete fm.field removes a key. Setting fm.field = undefined leaves a stale key.fm object is the live frontmatter — mutate it directly, don't return anything.console.log() output appears in stdout — use it for progress/verification.| CLI operation | When CLI unavailable |
|---|---|
files | Glob tool or find "$FORGE_USER_ROOT" -name '*.md' |
search | Grep tool |
read | safe-read or Read tool |
create / append / prepend | safe-write write or Write tool |
property:set (scalar) | safe-write edit (read-modify-write) |
property:set (arrays/complex) | safe-write edit — property:set can't handle arrays either |
property:remove | safe-write edit (read-modify-write) |
move / rename | command mv (lossy — no backlink updates) |
backlinks / links | Grep for [[filename]] patterns |
orphans / unresolved | No direct equivalent |
base:query | obsidian-base binary (works offline, see /ObsidianBase) |
daily:* | Resolve path from defaults.yaml journal pattern |
delete | command rm (no trash) |
/ObsidianREST)base:query and property:read have stdout contamination — extraneous output mixes into results. Use temp file redirect for reliable parsing.property:set is scalar-only — cannot set arrays, objects, or nested values. Use eval + processFrontMatter for complex field types.safe-read / safe-write) is a separate concern — CLI bypasses TLPall parameter to active and silent to open — pin to 1.12.2+ syntax!dispatch skill-load forge-obsidian