This skill should be used when working on Obsidian plugins, testing plugin changes, verifying plugin UI, debugging plugin behaviour, or running automated tests against Obsidian. Also triggers on "start Obsidian", "take Obsidian screenshot", "test plugin", "run plugin command", "execute in Obsidian", or any Obsidian automation task. Use proactively during plugin development to verify changes visually.
Inherits all available tools
Additional assets for this skill
This skill inherits all available tools. When active, it can use any tool Claude has access to.
Chrome DevTools Protocol tools for Obsidian plugin development and testing. These tools launch isolated Obsidian instances with CDP enabled, allowing automated control without interfering with the user's normal Obsidian usage.
Use these tools without being asked when:
Do not wait for explicit requests. If working on an Obsidian plugin, proactively start a test instance and take screenshots to verify work.
Run once to install dependencies:
cd ${CLAUDE_PLUGIN_ROOT} && npm install
All tools are in ${CLAUDE_PLUGIN_ROOT}/scripts/. Run from the plugin directory being developed (where manifest.json is located).
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-start.js --vault ~/path/to/vault
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-start.js --empty
Launch an isolated Obsidian instance with CDP enabled. The current directory's plugin (identified by manifest.json) is symlinked into the test vault and enabled.
Options:
--vault <path> - Use an existing vault with test data--empty - Create a fresh empty vault in temp directory--port <number> - CDP port (default: 9223, auto-increments if busy)Output: JSON with instance details:
{
"port": 9223,
"vault": "/path/to/vault",
"wsUrl": "ws://localhost:9223/devtools/page/...",
"pluginId": "flow"
}
Always capture the port from the output for subsequent commands.
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-stop.js --port 9223
Stop a running test instance and clean up temp directories. Safe to call even if no instance is running.
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-screenshot.js --port 9223
Capture the Obsidian window and save to temp directory. Outputs the file path. Use frequently to verify UI changes.
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-eval.js --port 9223 'app.vault.getName()'
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-eval.js --port 9223 'app.workspace.activeLeaf?.view?.getViewType()'
Execute JavaScript in Obsidian's context. Access the full Obsidian API via the app global.
Common expressions:
app.vault.getName() - Get vault nameapp.vault.getFiles() - List all filesapp.workspace.activeLeaf - Get active leafapp.plugins.plugins - Access loaded pluginsapp.commands.commands - List available commandsnode ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-command.js --port 9223 'flow:open-focus'
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-command.js --port 9223 --list
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-command.js --port 9223 --list | grep flow
Execute an Obsidian command by ID. Use --list to see all available commands.
# Start with test vault containing sample data
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-start.js --vault ~/Documents/flow-dev-vault/flow-dev
# Output: { "port": 9223, ... }
# Execute a plugin command
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-command.js --port 9223 'flow:open-focus'
# Verify visually
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-screenshot.js --port 9223
# Inspect state
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-eval.js --port 9223 'app.workspace.getLeavesOfType("flow-focus").length'
# Clean up
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-stop.js --port 9223
# Start with fresh empty vault
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-start.js --empty
# Output: { "port": 9223, "vault": "/tmp/obsidian-vault-9223", ... }
# Test plugin behaviour with no data
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-command.js --port 9223 'flow:process-inbox'
# Verify empty state handling
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-screenshot.js --port 9223
# Clean up (also removes temp vault)
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-stop.js --port 9223
Each agent uses a different port:
# Agent 1
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-start.js --empty --port 9223
# Agent 2
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-start.js --empty --port 9224
# Agent 3 (port auto-increments if 9225 busy)
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-start.js --empty --port 9225
"No manifest.json found"
manifest.json"Could not connect to CDP"
obsidian-start.js"Vault path does not exist"
--vault path is correct--empty for a fresh vault"No free port found"
obsidian-stop.jsTrust dialog blocking plugins
node ${CLAUDE_PLUGIN_ROOT}/scripts/obsidian-eval.js --port 9223 \
'Array.from(document.querySelectorAll("button")).find(b => b.textContent.includes("Trust"))?.click()'
/tmp/obsidian-test-<port>//tmp/obsidian-vault-<port>/