From obsidian
Manage Obsidian vault notes using obsidian-cli from a devcontainer environment via SSH to macOS host
npx claudepluginhub manifoldlogic/claude-code-plugins --plugin obsidianThis skill uses the workspace's default tool permissions.
**Last Updated:** 2026-01-15
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
Last Updated: 2026-01-15 CLI Tool: obsidian-cli (Yakitrak)
The obsidian-cli tool provides command-line access to Obsidian vaults, enabling note creation, search, content retrieval, and vault management without opening the Obsidian GUI application.
This skill enables Claude Code to interact with Obsidian vaults on the macOS host from within a devcontainer environment. Commands are executed via SSH tunneling using the established host-communication pattern.
Capabilities:
open that require the Obsidian app to be runningHOST_USER environment variable configured in devcontainer.json~/.ssh/id_ed25519Before using obsidian-cli commands, verify the environment is properly configured:
# 1. Verify SSH connectivity to host
ssh -o BatchMode=yes -o ConnectTimeout=5 ${HOST_USER}@host.docker.internal "echo OK"
# 2. Verify obsidian-cli is installed on host
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "which obsidian-cli"
# 3. Verify default vault is configured
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "obsidian-cli print-default"
If verification fails:
brew tap yakitrak/yakitrak && brew install yakitrak/yakitrak/obsidian-cliobsidian-cli set-default "VaultName"CRITICAL: All obsidian-cli commands must be executed via SSH to the macOS host.
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "obsidian-cli <command> [arguments]"
Note on PATH: Non-interactive SSH sessions may not load the full shell profile, so Homebrew's PATH may not be available. If obsidian-cli is not found, use the full path:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "/opt/homebrew/bin/obsidian-cli <command> [arguments]"
| Component | Value | Purpose |
|---|---|---|
| SSH key | ~/.ssh/id_ed25519 | Authentication to host |
| Host user | ${HOST_USER} | macOS username from devcontainer.json |
| Host address | host.docker.internal | Docker DNS for host machine |
| CLI | obsidian-cli | Obsidian CLI tool on host |
# Create a note
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "obsidian-cli create 'My Note' --content 'Note content here'"
# Search note content (search by filename is interactive-only)
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "obsidian-cli search-content 'keyword'"
# Print note content
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "obsidian-cli print 'My Note'"
# Check default vault
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "obsidian-cli print-default"
WARNING: User-provided input (note names, content, search terms) must be properly escaped before inclusion in SSH commands to prevent command injection.
The following characters require careful escaping when passed through SSH:
' (single quote)" (double quote); (semicolon)$ (dollar sign)` (backtick)\ (backslash)| (pipe)& (ampersand)For single quotes (recommended for most cases):
# Escape single quotes by replacing ' with '\''
escaped="${var//\'/\'\\\'\'}"
# Use in command
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "obsidian-cli create '${escaped}' --content 'Safe content'"
For double quotes:
# Escape double quotes by replacing " with \"
escaped="${var//\"/\\\"}"
# Use in command (wrap outer command in single quotes)
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal 'obsidian-cli search "'"${escaped}"'"'
DO NOT USE - Direct variable interpolation without escaping:
# UNSAFE - DO NOT USE
note="User's Note; rm -rf ~"
ssh ... "obsidian-cli create '${note}'"
# This could execute arbitrary commands!
DO NOT USE - Unescaped backticks:
# UNSAFE - DO NOT USE
content="Note about `whoami` user"
ssh ... "obsidian-cli create 'Test' --content '${content}'"
# Backticks will be evaluated as command substitution!
DO NOT USE - Unescaped dollar signs:
# UNSAFE - DO NOT USE
note="Cost: $100"
ssh ... "obsidian-cli create '${note}'"
# $100 will be interpreted as variable expansion!
Use this command to verify escaping is working correctly:
# Test with potentially dangerous input
note="Test'; echo hacked"
escaped="${note//\'/\'\\\'\'}"
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "obsidian-cli create '${escaped}' --content 'Test content'"
# SUCCESS: Should create note literally named "Test'; echo hacked"
# FAILURE: If you see "hacked" printed to terminal, escaping failed
The following workflows demonstrate the most frequent operations with obsidian-cli. Each example uses the established SSH command pattern. For complete command documentation, see references/cli-reference.md.
Use Case: Create a new note with initial content for capturing ideas, meeting notes, or documentation.
Example:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli create 'Project Ideas' --content '# Project Ideas\n\nBrainstorm for Q1 initiatives:\n- API redesign\n- Performance optimization'"
Result: Creates note "Project Ideas.md" in the default vault with the specified markdown content.
Creating Note in a Folder:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli create 'Projects/Backend/API Redesign' --content '# API Redesign\n\nPlanning document for v2 API'"
Result: Creates note at Projects/Backend/API Redesign.md, creating parent folders if needed.
Escaping Note: If the note name contains single quotes, escape them using the pattern from the Shell Escaping section:
# Note name: "John's Meeting Notes"
note="John's Meeting Notes"
escaped="${note//\'/\'\\\'\'}"
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli create '${escaped}' --content 'Meeting notes content'"
Use Case: Find notes containing specific text when you need to locate existing content or verify if information exists in the vault.
Important: The search command (filename search) is interactive-only and cannot be used over SSH. Use search-content for programmatic searching.
Example:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli search-content 'architecture'"
Result: Returns notes containing "architecture" in their content, then opens an interactive selector. Over SSH, this will find matches but may not complete interactively.
Alternative - Direct File Search: If you know the vault path, you can search files directly:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"grep -rl 'architecture' '/Users/username/Obsidian/VaultName'"
Using Search Results: Once you identify a note name, use print to read it:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "obsidian-cli print 'Weekly Meeting Notes'"
Use Case: Retrieve the contents of an existing note for analysis, reference, or incorporation into other work.
Example:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli print 'Weekly Meeting Notes'"
Result: Outputs the full markdown content of the note:
# Weekly Meeting Notes
## 2025-01-15
### Attendees
- Alice
- Bob
### Action Items
- Complete API review
- Schedule follow-up
Capturing Content for Processing:
# Capture note content to a variable
content=$(ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli print 'Project Requirements'" 2>/dev/null)
# Use content in subsequent processing
echo "$content" | grep "Priority:"
Reading Note in Subfolder:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli print 'Projects/Backend/API Spec'"
Escaping Note: For notes with special characters in the name:
# Note name: "FAQ's & Tips"
note="FAQ's & Tips"
escaped="${note//\'/\'\\\'\'}"
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli print '${escaped}'"
Use Case: Create or access today's daily note for journaling, task tracking, or daily standups.
Example:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli daily"
Result: Creates today's daily note if it doesn't exist, or confirms it exists. The note is created using the vault's configured daily notes format (typically YYYY-MM-DD.md in the daily notes folder).
Opening Daily Note in Obsidian GUI:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli daily --open"
Result: Creates (if needed) and opens the daily note in the Obsidian application.
Note: The --open flag requires the Obsidian application to be running on the macOS host. Use without --open when only CLI access is needed.
Use Case: Modify metadata in a note's YAML frontmatter, such as status, tags, or custom properties.
Example:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli frontmatter 'Project Roadmap' --edit --key status --value 'in-progress'"
Result: Updates or adds the status field in the note's frontmatter:
---
status: in-progress
---
# Project Roadmap
...
Setting Multiple Fields: Run separate commands for each field:
# Set status
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli frontmatter 'Feature Spec' --edit --key status --value 'review'"
# Set priority
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli frontmatter 'Feature Spec' --edit --key priority --value 'high'"
# Set assignee
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli frontmatter 'Feature Spec' --edit --key assignee --value 'alice'"
Adding Tags via Frontmatter:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli frontmatter 'Meeting Notes' --edit --key tags --value '[meeting, backend, q1]'"
Escaping Note: For values with special characters:
# Value containing quotes: "John's Team"
value="John's Team"
escaped_value="${value//\'/\'\\\'\'}"
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli frontmatter 'Team Doc' --edit --key owner --value '${escaped_value}'"
Obsidian CLI supports multiple vaults. By default, commands operate on the configured default vault. You can target specific vaults or change the default as needed.
Most commands work on the default vault without requiring any vault specification. This is the recommended approach when working primarily with one vault.
Check Current Default Vault:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli print-default"
Result:
Default vault: Personal Notes
Path: /Users/username/Documents/Obsidian/Personal Notes
Commands Using Default Vault:
# All of these use the default vault automatically
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "obsidian-cli create 'New Note'"
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "obsidian-cli search-content 'keyword'"
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "obsidian-cli print 'Existing Note'"
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "obsidian-cli daily"
When you need to access a vault other than the default, use the --vault flag.
Find Available Vaults:
Obsidian stores vault information in ~/Library/Application Support/obsidian/obsidian.json on macOS:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"cat ~/Library/Application\ Support/obsidian/obsidian.json"
Create Note in Specific Vault:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli create 'Q1 Planning' --vault 'Work Projects' --content '# Q1 Planning\n\nObjectives...'"
Search Content in Specific Vault:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli search-content 'architecture' --vault 'Work Projects'"
Read Note from Specific Vault:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli print 'API Documentation' --vault 'Reference Library'"
Escaping Vault Names: If the vault name contains special characters:
# Vault name: "John's Notes"
vault="John's Notes"
escaped_vault="${vault//\'/\'\\\'\'}"
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli create 'Test Note' --vault '${escaped_vault}'"
When you need to switch your primary working vault, use the set-default command.
Set New Default Vault:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli set-default 'Work Projects'"
Result:
Default vault set to: Work Projects
Verify Change:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal \
"obsidian-cli print-default"
Use Case Scenarios:
--vault flags--vault flag for quick access to another vault without changing the default| Scenario | Approach |
|---|---|
| 90% of work in one vault | Set as default, use --vault for exceptions |
| Frequent vault switching | Use --vault flag explicitly |
| Starting extended work in different vault | Use set-default to change default |
| Quick lookup in another vault | Use --vault flag |
| Scripting across multiple vaults | Always use explicit --vault flag |
Commands that require interactive input (fuzzy finders, prompts) do not work over SSH non-interactive sessions. The SSH connection from the devcontainer is non-interactive, meaning it cannot display or receive input from terminal UI elements.
Not Available Over SSH:
obsidian-cli search - Always launches interactive fuzzy finder (does NOT accept a search pattern argument)obsidian-cli search-content - Also interactive; while it accepts a pattern, it opens a fuzzy finder to select from matchesAlternatives:
search-content "pattern" to find matches (may partially work for finding matches, but selection is interactive)grep if you know the vault path--vault "Name" flag to target specific vaults explicitlyset-default to avoid vault selection promptsWhy SSH Limits Interactivity:
SSH sessions from the devcontainer do not allocate a pseudo-terminal (PTY) by default, which is required for interactive TUI components like fuzzy finders. Adding -t to force PTY allocation does not resolve this since the container-side terminal cannot display the host's TUI.
This section documents common error scenarios, their causes, and resolution steps. Each error includes the error message pattern to help identify the issue.
Error Pattern:
ssh: connect to host host.docker.internal port 22: Connection refused
Likely Cause: The HOST_USER environment variable is not set or the SSH server on the macOS host is not running/accessible.
Resolution:
echo $HOST_USER
.devcontainer/devcontainer.json under remoteEnv:
"remoteEnv": {
"HOST_USER": "${localEnv:USER}"
}
ssh -o BatchMode=yes -o ConnectTimeout=5 ${HOST_USER}@host.docker.internal "echo OK"
Error Pattern:
Permission denied (publickey,password).
or
Warning: Identity file /home/vscode/.ssh/id_ed25519 not accessible: No such file or directory.
Likely Cause: SSH keys are not mounted into the container, or the key file has incorrect permissions.
Resolution:
ls -la ~/.ssh/id_ed25519
.devcontainer/devcontainer.json:
"mounts": [
"source=${localEnv:HOME}/.ssh,target=/home/vscode/.ssh,type=bind,readonly"
]
chmod 600 ~/.ssh/id_ed25519
# On macOS host, ensure your public key is in:
cat ~/.ssh/authorized_keys
Error Pattern:
bash: obsidian-cli: command not found
or
zsh: command not found: obsidian-cli
Likely Cause: The obsidian-cli tool is not installed on the macOS host.
Resolution:
# Run these commands directly on macOS, not through SSH
brew tap yakitrak/yakitrak
brew install yakitrak/yakitrak/obsidian-cli
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "which obsidian-cli"
which returns a path, the installation succeededError Pattern:
Error: No default vault set
or
Error: Please specify a vault or set a default vault
Likely Cause: obsidian-cli requires a default vault to be configured when not using the --vault flag.
Resolution:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "cat ~/Library/Application\ Support/obsidian/obsidian.json"
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "obsidian-cli set-default 'Your Vault Name'"
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "obsidian-cli print-default"
--vault "Name" flag with each command instead of setting a defaultError Pattern:
Error: Note not found
or
Error: Note 'NoteName' does not exist
Likely Cause: The specified note does not exist in the vault, or the path/name is incorrect.
Resolution:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "find /path/to/vault -name '*partial-name*' -type f"
# Instead of 'MyNote', try:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "obsidian-cli print 'Folder/Subfolder/MyNote'"
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "obsidian-cli print-default"
Error Pattern:
Error: Vault not found
or
Error: Vault 'VaultName' does not exist
Likely Cause: The specified vault name in the --vault flag does not match any registered Obsidian vault.
Resolution:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "cat ~/Library/Application\ Support/obsidian/obsidian.json"
Error Pattern:
Error: Permission denied
or
Error: EACCES: permission denied, open '/path/to/vault/note.md'
Likely Cause: The user account on macOS does not have read/write access to the vault directory.
Resolution:
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "ls -la '/path/to/vault'"
# On macOS host
chmod -R u+rw "/path/to/vault"
Error Pattern:
Likely Cause: User-provided input (note names, content) contains special characters that weren't properly escaped.
Resolution:
escaped="${var//\'/\'\\\'\'}"
note="Test'; echo hacked"
escaped="${note//\'/\'\\\'\'}"
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "obsidian-cli create '${escaped}' --content 'Test content'"
# SUCCESS: Should create note literally named "Test'; echo hacked"
# FAILURE: If you see "hacked" printed to terminal, escaping failed
# Capture output and check for errors
output=$(ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "obsidian-cli create 'MyNote' --content 'Content'" 2>&1)
if [[ $? -ne 0 ]]; then
echo "Error: ${output}"
else
echo "Success: Note created"
fi
This section organizes common issues by symptom for quick diagnosis.
Symptom: "Connection refused" when running any command
Symptom: "Permission denied" immediately after SSH attempt
Symptom: SSH hangs without response
host.docker.internal resolves:
ping -c 1 host.docker.internal
Symptom: Commands work on macOS but fail from container
ssh ... "/opt/homebrew/bin/obsidian-cli print-default"
Symptom: Note content appears garbled or truncated
Symptom: Command runs but nothing happens (silent failure)
output=$(ssh ... "obsidian-cli create 'Note'" 2>&1)
echo "$output"
Symptom: "No default vault set" on every command
Symptom: Notes appear in wrong vault
ssh ... "obsidian-cli print-default"
Symptom: Vault shows in Obsidian but obsidian-cli can't find it
ssh ... "cat ~/Library/Application\ Support/obsidian/obsidian.json"
Run these commands to quickly diagnose common issues:
# 1. Check HOST_USER is set
echo "HOST_USER: $HOST_USER"
# 2. Test basic SSH connectivity
ssh -o BatchMode=yes -o ConnectTimeout=5 ${HOST_USER}@host.docker.internal "echo SSH OK"
# 3. Verify SSH key exists and has correct permissions
ls -la ~/.ssh/id_ed25519
# 4. Check obsidian-cli is installed (use full path if 'which' fails)
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "which obsidian-cli || /opt/homebrew/bin/obsidian-cli --version"
# 5. Verify default vault
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "/opt/homebrew/bin/obsidian-cli print-default"
# 6. Find available vaults from Obsidian config
ssh -i ~/.ssh/id_ed25519 ${HOST_USER}@host.docker.internal "cat ~/Library/Application\ Support/obsidian/obsidian.json"
For complete command documentation including all options and parameters, see:
references/cli-reference.md