| name | obsidian-vault |
| description | Headless Obsidian vault operations for a personal-assistant agent. Use when the user says "save to vault", "log this note", "find my notes about X", "who is Y", "what's in my inbox", "open loops", "add a follow-up", or whenever you process an incoming signal (email, chat, voice memo) and need to file it. Also use to create or update people/project/meeting/decision notes. Pure file-system; does NOT require Obsidian to be running. |
| license | Apache-2.0 |
| compatibility | Requires Python 3.10+ (stdlib only). Vault path via $OBSIDIAN_VAULT_PATH (or legacy $OCTOBOTS_VAULT_PATH). ripgrep recommended (falls back to grep). |
| metadata | {"author":"octobots","version":"0.1.0"} |
Obsidian Vault
Operate the user's Obsidian vault as a queryable second-brain — headlessly. The vault is plain markdown plus YAML frontmatter; the agent reads and writes files directly. Obsidian itself is for the user to browse the result later.
This skill is self-contained and stdlib-only. It pairs well with an agent-internal memory skill (e.g. the memory skill in octobots), but doesn't require one:
| Where | What goes here | Who reads it |
|---|
| Agent memory (if any) | Short-lived cognition, operational logs | The agent |
$OBSIDIAN_VAULT_PATH/ | User-facing knowledge base, signals, contacts | The user (in Obsidian); agent files into it |
If unsure where something belongs: short-lived agent thought → memory; user might want to read it later → vault.
The CLI is scripts/vault.py. Vault path comes from $OBSIDIAN_VAULT_PATH (fallback: $OCTOBOTS_VAULT_PATH) or --vault; the skill is a no-op if unset. Output is human-readable on stdout; errors → stderr; non-zero exit on failure.
Vault layout
The skill enforces this layout for any note it creates. Existing notes are not migrated.
<vault>/
inbox/ # raw, unprocessed signals — landing zone
daily/YYYY-MM-DD.md # user-facing daily note (append-only)
people/ # one note per contact — the leverage folder
projects/ # active projects
researches/ # deep-dive notes, reading captures
meetings/ # synchronous events with attendees + outcomes
decisions/ # decision log — "why did we do X"
memories/ # user-facing long-term memories
mails/ # processed email archive
chats/ # processed chat snippets (Telegram, Teams, Slack)
plans/ # plans, roadmaps, intent docs
attachments/ # binaries (images, PDFs, audio)
templates/ # frontmatter templates per note type
open-loops.md # active follow-ups (single flat file)
open-loops-archive/
YYYY-MM.md # one file per month, closed loops
Two hard rules:
- Folder = type. Frontmatter = state. PA filters by
status: inbox across folders to find work. Folders never carry state. Don't invent inbox/-flavored subfolders inside other folders.
- Always wikilinks. Use
[[people/anna-petrenko]], never [Anna](./people/anna-petrenko.md). Free backlinks, rename-tracking, and the user gets a graph view.
Frontmatter schema
Every note carries this baseline. Per-type extensions are listed below.
---
type: signal | project | research | decision | meeting | person | plan | memory | mail | chat
status: inbox | active | done | archived
created: 2026-04-07
source: email | telegram | teams | manual | web | voice
people: [[people/anna-petrenko]]
projects: [[projects/q2-launch]]
tags: [urgent, finance]
processed_at: 2026-04-07T15:23:00
---
Per-type extensions
| Type | Extra fields |
|---|
person | org, role, last_contact, topics, urgency |
meeting | attendees (list of [[people/...]]), agenda, actions, duration_min |
decision | alternatives, rationale, would_change_mind_if |
project | started, target_date, blocker, next_action |
mail | from, to, subject, received_at, thread_id |
chat | channel, participants, received_at |
Unknown fields are tolerated (warn-on-unknown, never blocking) — the user may extend the schema in Obsidian and the skill must not break their notes.
Decision tree — where does an incoming signal go?
Is it a synchronous event (call, meeting)? → meetings/
Is it an email? → mails/
Is it a chat fragment (Telegram/Slack/Teams)? → chats/
Is it a deep dive / web capture / reading note? → researches/
Is it a plan / roadmap / intent doc? → plans/
Is it a decision the user wants to remember why? → decisions/
Is it long-term knowledge for the user to reread? → memories/
Is it a person mentioned 2+ times? → people/<slug>.md (autocreate)
Is it about an active project? → also wikilink to projects/<slug>
None of the above + user might care later? → inbox/ (PA triages later)
None of the above + agent-internal only? → memory log (NOT vault)
The wikilink convention means a single signal can be filed in one place and referenced from many. Don't duplicate notes — link them.
CLI commands
All commands respect --vault <path> (overrides env) and --role <name> (defaults to $OCTOBOTS_ID).
Filing & creation
vault.py file inbox/2026-04-07-anna-question.md --type chat --person anna-petrenko --project q2-launch
vault.py new project q2-launch --description "Q2 launch coordination"
vault.py new meeting 2026-04-07-team-sync --attendees anna-petrenko,bob
vault.py new decision use-postgres-not-sqlite --rationale "We need MVCC and JSONB"
Querying
vault.py find --type meeting --since 30d
vault.py find --status inbox
vault.py find --person anna-petrenko --tag finance
vault.py find --project q2-launch --since 7d
vault.py show people/anna-petrenko.md
People
vault.py person anna-petrenko --touch
vault.py person "Anna Petrenko" --org "Acme" --role-field "CFO" --touch
People notes are autocreated only on the second touch to avoid noise from one-off mentions. The first touch records the person in <vault>/.obsidian-vault/<role>/people-pending.md (vault-local agent state, ignored by Obsidian); the second touch promotes it to a real people/<slug>.md.
Open loops
vault.py loop add "Follow up with Anna re: contract by Tue"
vault.py loop list
vault.py loop done --id <id>
<id> is a short hash printed by loop add and loop list. Closed loops archive into the month they were closed (not the month they were opened).
Daily note
vault.py daily append "Spoke with Anna about Q2 launch — green light"
vault.py daily show
vault.py daily show --date 2026-04-06
NEVER use the Write tool on daily notes. They're append-only and shared between you and the user. Only vault.py daily append is safe.
Linking
vault.py link meetings/2026-04-07-team-sync.md people/anna-petrenko.md
Bases (Obsidian dashboards for the user)
vault.py bases install
vault.py bases list
Bases require Obsidian 1.9+. If the user is on an older version they're harmless YAML files that don't render — no error.
Validation
vault.py validate
vault.py validate --strict
vault.py validate --type meeting
validate reports unknown fields, missing required fields, and invalid date formats. It is not blocking — the user is allowed to evolve the schema. The skill must continue to function on notes that fail validation.
Querying without Bases
Bases require Obsidian. For headless queries the skill uses ripgrep over frontmatter:
rg -l '^tags:.*urgent' "$OCTOBOTS_VAULT_PATH"
rg -l '^status: inbox' "$OCTOBOTS_VAULT_PATH"
rg -l '\[\[people/anna-petrenko\]\]' "$OCTOBOTS_VAULT_PATH"
vault.py find wraps these patterns. Don't roll your own grep — use the CLI so the schema stays consistent.
Conventions for content
Sync conflicts
If the vault is on Obsidian Sync / iCloud / syncthing, you may see conflict files like Note (Anna's iPhone).md. The skill detects them and refuses to operate on them — they require human resolution. vault.py validate lists conflict files in its report.
What NOT to do
- Never overwrite a daily note. Append only, via
vault.py daily append.
- Never duplicate a note across folders. Link instead.
- Never invent new top-level folders. The schema is fixed; extend via tags or new note types.
- Never store agent-internal state in the vault. That belongs in
.octobots/memory/.
- Never write a person note for a one-off mention. Wait for the second touch.
- Never use markdown links for internal references. Wikilinks only.
- Never operate on sync conflict files (
*(.* iPhone)*.md, *conflict*.md).