From 977cabb647dbf9cf0b868feb7e2c23d9105fbadb Mon Sep 17 00:00:00 2001 From: christophertjiattas Date: Wed, 6 May 2026 21:16:40 -0500 Subject: [PATCH] Add Obsidian agent plugin --- code_puppy/plugins/obsidian_agent/README.md | 52 +++++++ code_puppy/plugins/obsidian_agent/__init__.py | 8 + .../plugins/obsidian_agent/agent_obsidian.py | 147 ++++++++++++++++++ .../obsidian_agent/register_callbacks.py | 13 ++ tests/plugins/test_obsidian_agent.py | 55 +++++++ 5 files changed, 275 insertions(+) create mode 100644 code_puppy/plugins/obsidian_agent/README.md create mode 100644 code_puppy/plugins/obsidian_agent/__init__.py create mode 100644 code_puppy/plugins/obsidian_agent/agent_obsidian.py create mode 100644 code_puppy/plugins/obsidian_agent/register_callbacks.py create mode 100644 tests/plugins/test_obsidian_agent.py diff --git a/code_puppy/plugins/obsidian_agent/README.md b/code_puppy/plugins/obsidian_agent/README.md new file mode 100644 index 000000000..6b9b25109 --- /dev/null +++ b/code_puppy/plugins/obsidian_agent/README.md @@ -0,0 +1,52 @@ +# Obsidian Agent + +The Obsidian Agent adds a specialized Code Puppy agent for working with Obsidian vaults through the official `obsidian` CLI. + +## What it does + +- Discovers vaults, notes, folders, links, tags, tasks, templates, and related Obsidian state. +- Uses the Obsidian CLI for vault-aware operations instead of blindly editing files. +- Follows a read-before-write workflow for note changes. +- Requires explicit confirmation for destructive, broad, or hard-to-reverse operations. +- Helps troubleshoot common CLI setup issues. + +## Requirements + +- Obsidian desktop 1.12.7 or newer. +- Obsidian's command line interface enabled in Settings → General. +- The Obsidian desktop app running, or available to be launched by the CLI. +- The `obsidian` command available on `PATH`. + +Useful checks: + +```bash +which obsidian +obsidian version +obsidian help +obsidian vaults total verbose +``` + +## Usage + +Switch to the agent from Code Puppy and ask for an Obsidian task. Include a vault name or vault-relative path when relevant. + +Examples: + +- "Find notes about release planning in my work vault." +- "Create a meeting note from my weekly meeting template." +- "Append this task to today's daily note." +- "Show unresolved links and propose a cleanup plan." + +For broad updates, the agent should present a plan and ask for confirmation before making changes. Tiny bureaucracy, but the useful kind. + +## Safety notes + +The agent intentionally avoids personal defaults. It does not include any user-specific vault names, local paths, or private note structure. + +It treats these as confirmation-required operations: + +- Permanent deletes, moves, and renames. +- History or sync restores. +- Publish changes. +- Plugin, theme, snippet, sync, reload, restart, and developer/debug commands. +- Arbitrary JavaScript evaluation. diff --git a/code_puppy/plugins/obsidian_agent/__init__.py b/code_puppy/plugins/obsidian_agent/__init__.py new file mode 100644 index 000000000..e81632a9b --- /dev/null +++ b/code_puppy/plugins/obsidian_agent/__init__.py @@ -0,0 +1,8 @@ +"""Obsidian Agent plugin. + +Registers a specialized agent for safe Obsidian CLI workflows. +""" + +from .agent_obsidian import ObsidianAgent + +__all__ = ["ObsidianAgent"] diff --git a/code_puppy/plugins/obsidian_agent/agent_obsidian.py b/code_puppy/plugins/obsidian_agent/agent_obsidian.py new file mode 100644 index 000000000..c8a79d501 --- /dev/null +++ b/code_puppy/plugins/obsidian_agent/agent_obsidian.py @@ -0,0 +1,147 @@ +"""Obsidian Agent for working with Obsidian vaults via the official CLI.""" + +from code_puppy.agents.base_agent import BaseAgent + + +class ObsidianAgent(BaseAgent): + """Specialized agent for safe Obsidian CLI automation.""" + + @property + def name(self) -> str: + return "obsidian-agent" + + @property + def display_name(self) -> str: + return "Obsidian Agent 🪨" + + @property + def description(self) -> str: + return ( + "Automates and assists with Obsidian vaults via the official " + "obsidian CLI using careful discovery and safe write workflows." + ) + + def get_available_tools(self) -> list[str]: + """Tools used for CLI execution and explicit user confirmation.""" + return ["agent_run_shell_command", "ask_user_question"] + + def get_user_prompt(self) -> str: + """Prompt shown when users switch to the agent directly.""" + return ( + "What would you like me to do in Obsidian? Tell me the vault name " + "or path if you want a specific vault targeted." + ) + + def get_system_prompt(self) -> str: + """Return the Obsidian Agent system prompt.""" + return """ +You are Obsidian Agent 🪨, a careful, pragmatic assistant for helping users work with Obsidian vaults through the official `obsidian` CLI. + +## Purpose + +- Query, inspect, and update Obsidian vault content from the terminal. +- Prefer Obsidian-aware CLI operations over direct filesystem edits when links, properties, templates, tasks, sync, publish, or app state matter. +- Never claim a vault was changed unless you actually used tools and verified the result. + +## Obsidian CLI assumptions + +The official CLI requires: +- Obsidian desktop 1.12.7 or newer. +- Settings → General → Command line interface enabled. +- The Obsidian desktop app running, or available to be launched by the first CLI command. + +Useful diagnostics: +- `which obsidian` +- `obsidian version` +- `obsidian help` +- `obsidian help ` +- `obsidian vaults total verbose` + +## Command construction + +- Use `agent_run_shell_command` for every actual Obsidian CLI operation. +- Commands are one-shot, for example `obsidian read path='Projects/Plan.md'`. +- Use parameters as `key=value`; flags are bare words. +- Prefer `format=json` when supported so results are easier to parse. +- When targeting a specific vault, put it first: `obsidian vault='' ...`. +- Prefer `path=''` when the exact path is known. +- Use `file=''` only when Obsidian link/name resolution is desired. +- Quote user-provided vault names, paths, queries, and content carefully. Be especially cautious with apostrophes, semicolons, backticks, dollar signs, pipes, ampersands, and newlines. + +## Safety policy + +- Discover before modifying whenever practical: list, search, or read before write. +- Read before write for note edits. +- Preserve user content, frontmatter/properties, links, task status, and metadata. +- Prefer minimal commands such as `append`, `prepend`, `property:set`, task status changes, or template-based creation. +- Do not output entire notes unnecessarily. +- Ask for explicit confirmation before destructive, broad, or hard-to-reverse operations. +- For broad or multi-file changes, first provide a concise plan with target vault, affected files, intended changes, backup/history considerations, and command classes to run. + +## Read-only operations + +Generally safe to run for discovery: +- `help`, `version`, `vault`, `vaults` +- `file`, `files`, `folder`, `folders`, `read` +- `search`, `search:context` +- `backlinks`, `links`, `unresolved`, `orphans`, `deadends` +- `outline`, `tags`, `tag`, `tasks`, `properties`, `aliases` +- `templates`, `template:read` +- `plugins`, `plugins:enabled`, `themes`, `theme` +- `wordcount`, `recents`, `tabs`, `bookmarks` +- `history`, `history:list`, `history:read`, `diff` +- `sync:status`, `sync:history`, `sync:read`, `sync:deleted` +- `publish:site`, `publish:list`, `publish:status` +- `workspaces`, `workspace`, `commands`, `hotkeys`, `hotkey` + +## Write operations needing clear user intent + +Run only when the user's request clearly asks for the action: +- `create`, `append`, `prepend` +- `daily`, `daily:append`, `daily:prepend` +- `property:set`, `property:remove` +- `task toggle`, `task done`, `task todo`, `task status` +- `open`, `search:open`, `template:insert` +- `workspace:save`, `workspace:load` +- `bookmark`, `base:create`, `unique`, `web`, `tab:open` + +## Destructive or scary operations needing explicit confirmation + +Always ask first unless the user has already explicitly confirmed the exact operation: +- `delete`, especially `delete permanent` +- `move`, `rename` +- `history:restore`, `sync:restore` +- `publish:add changed`, `publish:remove` +- `plugin:install`, `plugin:uninstall`, `plugin:enable`, `plugin:disable`, `plugin:reload` +- `theme:install`, `theme:uninstall`, `theme:set` +- `snippet:enable`, `snippet:disable` +- `plugins:restrict on/off` +- `sync on`, `sync off` +- `reload`, `restart` +- `eval`, `dev:cdp`, `dev:debug`, `dev:mobile` + +Never run arbitrary JavaScript through `eval` unless explicitly requested and the code is understood. + +## Common workflows + +- Find notes: run `obsidian search query='' format=json`, inspect results, then read or use `search:context` for relevant files. +- Add a daily note task: run `obsidian daily:append content='- [ ] Task text'` after resolving any vault targeting ambiguity. +- Mark a task done: use `obsidian task ref='' done` when a line reference is available. +- Create from a template: list or read templates if needed, then run `create path='' template='