# Obs Memory

> Use at session start to orient from an Obsidian knowledge vault, during work to look up architecture/component/pattern notes, and when discoveries are made that should be written back to memory. Also use when the user mentions obsidian memory, obsidian vault, obsidian notes, or any /obs command (init, analyze, recap, project, note, todo, lookup, relate).

- Skill: `tianyili/obs-memory` (Agent Skill, multi-file: 22 files)
- Install (CLI): `npx skillmds@latest add tianyili/obs-memory`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tianyili/obs-memory/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- License: MIT
- Author: tianyili (https://skillmd.com/u/tianyili)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tianyili/obs-memory

---


# Obsidian Agent Memory

You have access to a persistent Obsidian knowledge vault — a graph-structured memory that persists across sessions. Use it to orient yourself, look up architecture and component knowledge, and write back discoveries.

## Vault Discovery

Resolve the vault path using this chain (first match wins):

1. **Environment variable**: `$OBSIDIAN_VAULT_PATH`
2. **Agent config reference**: Parse the vault path from the agent's project or global config (look for "Obsidian Knowledge Vault" section with a path like `~/Documents/SomeName/`)
3. **Default**: `~/Documents/AgentMemory`

Store the resolved path as `$VAULT` for all subsequent operations. Derive `$VAULT_NAME` as `basename "$VAULT"` for CLI calls.

Verify the vault exists by checking for `$VAULT/Home.md`. If the vault doesn't exist, inform the user and suggest running the `init` command to bootstrap a new vault from the bundled template.

## Session Start — Orientation

At the start of every session, orient yourself with **at most 2 operations**:

### Step 1: Read TODOs

**CLI-first**:
```bash
obsidian vault=$VAULT_NAME tasks path="todos" todo verbose
```
**Fallback**: Read the file at `$VAULT/todos/Active TODOs.md`.

Know what's pending, in-progress, and recently completed.

### Step 2: Detect current project and read its overview

Auto-detect the project from the current working directory:
```bash
basename $(git rev-parse --show-toplevel 2>/dev/null) 2>/dev/null || basename $(pwd)
```

Then check if a matching project exists by listing files in `$VAULT/projects/*/`. Match the git repo name (or directory name) against project folder names. If a match is found, read the project overview at `$VAULT/projects/{matched-name}/{matched-name}.md`.

This project overview contains wikilinks to all components, patterns, architecture decisions, and domains. **Do not read those linked notes yet** — follow them on demand when the current task requires that context.

### What NOT to read at session start
- `Home.md` (only if you're lost and can't find the project)
- `sessions/` (only if the user references prior work)
- Domain indexes (only if you need cross-project knowledge)
- Component notes (only when working on that component)

## Automatic Behaviors

These behaviors apply to any agent using this skill. They do not require explicit commands.

### On session start

Auto-orient (TODOs + project overview) without being asked, following the Session Start procedure above. If the vault doesn't exist at the resolved path, inform the user and suggest running `init`.

### On session end signals

When the user says "done", "wrapping up", "that's it", "let's stop", or similar end-of-session language — offer to write a session summary. Don't auto-run; ask first: "Want me to write a session summary to the vault before we wrap up?"

### On git commit — changelog line

Every git commit the agent creates in a project repo also logs ONE changelog line.
The destination follows the project's memory mode (§ Project Memory Modes) — one commit,
one line, one destination, never two:

- **`full`** → append to `$VAULT/projects/{name}/changelog.md` (create on first use, no
  frontmatter needed beyond a `# {name} changelog` title):
  `- YYYY-MM-DD {short-hash} {type(scope)}: {what changed & WHY, one line}`
  Keep the why — this is an inward record, never 去脈絡化 it.
- **`collab`** → the team's own surfaces: put the WHY in the commit message body (the
  repo's history IS the team changelog); if the team maintains a `CHANGELOG.md`, add an
  entry following their convention. Nothing project-identifying goes to the vault.
- **`off`** → skip entirely.

Several commits in one task may share a single summarizing line (list the hashes). The
changelog is an index of WHY the repo changed between recaps — it complements session
notes, it doesn't replace them.

### On component discovery

When you deeply analyze a component that has no vault note — and the project has an active vault — offer to create a component note and infer relationships from imports and dependencies. Example: "I noticed there's no vault note for the AuthMiddleware component. Want me to create one and map its dependencies?"

### On first run

When the vault doesn't exist at any resolved path, guide the user through `init`, then auto-scaffold the current project if inside a git repo.

## During Work — Graph Navigation

**Principle: Use CLI queries first, file reads second.** The Obsidian CLI provides structured access to properties, links, backlinks, tags, and search — prefer these over reading entire files.

### CLI-first lookups (preferred)

Use these CLI commands for targeted queries without consuming file-read tokens:

```bash
# Query a component's dependencies
obsidian vault=$VAULT_NAME property:read file="Component Name" name="depends-on"

# Find what depends on a component
obsidian vault=$VAULT_NAME property:read file="Component Name" name="depended-on-by"
obsidian vault=$VAULT_NAME backlinks file="Component Name"

# Find all outgoing links from a note
obsidian vault=$VAULT_NAME links file="Component Name"

# Find all notes of a type
obsidian vault=$VAULT_NAME tag verbose name="component"

# Search vault content
obsidian vault=$VAULT_NAME search format=json query="search term" matches limit=10

# Get note structure without full read
obsidian vault=$VAULT_NAME outline file="Component Name"

# Read a specific property
obsidian vault=$VAULT_NAME property:read file="Component Name" name="key-files"
```

Where `$VAULT_NAME` is the vault folder name (basename of `$VAULT`).

### File-read fallback (when CLI unavailable)

Fall back to file reads when the Obsidian CLI is not available:
- Need to understand a component? The project overview links to it. Read that one note.
- Need an architecture decision? The component note or project overview links to it. Follow the link.
- Need cross-project knowledge? Component/pattern notes link to domain notes. Follow the link.
- Need session history? Only read if you're stuck or the user references prior work.

### Frontmatter-first scanning
When you need to scan multiple notes to find the right one, read just the first ~10 lines of each file. The `tags`, `project`, `type`, and `status` fields in the frontmatter tell you if the note is relevant before reading the full body.

### Directory listing before reading
List directory contents before reading files — know what exists without consuming tokens:
- `$VAULT/projects/{name}/**/*.md` — all notes for a project
- `$VAULT/domains/{tech}/*.md` — domain knowledge files

## Project Memory Modes — personal vs collaborative

Not every project belongs in this personal vault. Team projects must keep their knowledge
where the TEAM can see it; client-sensitive work may belong in the vault not at all.

Registry: `$VAULT/projects/_memory-modes.md` — one line per repo:
`- {repo-name}: full|collab|off — {for collab/off: where team knowledge lives}`

**Ask-once protocol (binding)**: before the FIRST vault write for a project with no
registry entry — including `project`, `analyze`, `recap`, `todo` — ask the user:
「這個專案的記憶模式？full（個人專案，完整 vault）／collab（協作——知識進 repo docs，
vault 只留個人 TODO＋精簡 recap）／off（敏感——vault 完全不記）」
Record the answer in the registry. Never guess; never scaffold a vault project folder
for an unregistered project.

| Mode | Vault gets | Project knowledge & planning go to |
|---|---|---|
| full | everything (as documented in this skill) | vault |
| collab | Active-TODOs entries + thin personal recaps (your work state, open questions — NOT a knowledge base of the team's system) | the repo's own docs (follow the team's existing conventions — `docs/`, ADR dir, README; ask where if unclear) + the team tracker |
| off | nothing that identifies the project | repo docs / team tracker only |

Ongoing planning (roadmaps, specs, task breakdowns) in `collab`/`off` projects are TEAM
artifacts — write them in the repo/tracker where teammates can see and edit them. The
vault must never become a private shadow copy that drifts from the team's truth.
Also: in `collab`/`off` projects don't add personal obs/`.claude` scaffolding to the
shared repo — personal config stays in `.claude/settings.local.json` (untracked), nothing else.

## Writing to the Vault

Write concisely. Notes are for your future context, not human documentation. Prefer:
- Bullet points over prose
- Wikilinks over repeated explanations (link to it, don't re-state it)
- Frontmatter tags for discoverability over verbose descriptions

### When to write
- **New component discovered**: Create a component note when you deeply understand a part of the codebase
- **Architecture decision made**: Record ADRs when significant design choices are made
- **Pattern identified**: Document recurring patterns that future sessions should follow
- **Technique that worked**: A debugging trick, tool invocation, or workaround that cost real
  effort to find and will be needed again — `patterns/` (universal) or `domains/{tech}/`
  (stack-specific). These are what a machine switch must not lose.
- **Domain knowledge learned**: Write to domain notes when you discover cross-project knowledge

### Scoping rules

Filing test, in order: (1) Only true for THIS codebase? → `projects/{name}/`.
(2) True for this tech stack anywhere? → `domains/{tech}/`. (3) True regardless of
tech? → `patterns/`. (4) It's a narrative of what happened? → `sessions/`.
Never file "somewhere for now" — if genuinely unsure, `inbox/` and say so in your recap.

| Knowledge type | Location | Example |
|---|---|---|
| One project only | `projects/{name}/` | How this API handles auth |
| Shared across projects | `domains/{tech}/` | How Go interfaces work |
| Universal, tech-agnostic | `patterns/` | SOLID principles |
| Session summaries | `sessions/` | What was done and discovered |
| TODOs | `todos/Active TODOs.md` | Grouped by project |
| Unsure (temporary!) | `inbox/` | Anything — but see the hygiene rule below |

**Hygiene caps (enforced during `recap` and `analyze`):**
- `todos/Active TODOs.md` ≤ ~120 lines: `[x]` items move to the archive (never stay);
  pending items untouched for >30 days move to their project overview under `## Backlog`.
- `inbox/` is capture-only: every `recap`/`analyze` files or deletes what's there. An
  inbox note older than a week is a filing failure — fix it, don't skip it.
- One project = one folder under `projects/` named exactly like the repo (basename of
  `git rev-parse --show-toplevel`) — never create `projects/{name}-v2/` variants; new
  knowledge goes INTO the existing folder structure (overview / architecture /
  components / patterns / runbooks).

### Frontmatter conventions
Always include in new notes:
```yaml
---
tags: [category, project/short-name]
type: <component|adr|session|project>
project: "[[projects/{name}/{name}]]"
created: YYYY-MM-DD
---
```

### Wikilink conventions
- Link to related notes: `[[projects/{name}/components/Component Name|Component Name]]`
- Link to domains: `[[domains/{tech}/{Tech Name}|Tech Name]]`
- Link back to project: `[[projects/{name}/{name}|project-name]]`

### Note templates

**Component Note:**
```yaml
---
tags: [components, project/{short-name}]
type: component
project: "[[projects/{name}/{name}]]"
created: {date}
status: active
layer: ""
depends-on: []
depended-on-by: []
key-files: []
---
```
Sections: Purpose, Gotchas

**Architecture Decision:**
```yaml
---
tags: [architecture, decision, project/{short-name}]
type: adr
project: "[[projects/{name}/{name}]]"
status: proposed | accepted | superseded
created: {date}
---
```
Sections: Context, Decision, Alternatives Considered, Consequences

**Session Note:**
```yaml
---
tags: [sessions]
type: session
projects:
  - "[[projects/{name}/{name}]]"
created: {date}
branch: {branch-name}
agent: {claude|codex|antigravity|...}
---
```
Sections: Context, Work Done, Discoveries, Decisions, Next Steps
Filename: `sessions/{YYYY-MM-DD HH-mm} - {project} - {title} ({agent}).md` — the agent
suffix matters once multiple agents share the vault.

## Commands

Every command that writes project content runs the § Project Memory Modes check first:
unregistered project → ask-once; `collab` redirects knowledge writes to the team repo's
docs; `off` declines project-identifying vault writes entirely.

### `init` — Initialize the Vault

Bootstrap a new Obsidian Agent Memory vault from the bundled template.

**Usage**: `init [path]`

#### Steps:

1. **Determine vault path**: Use the first argument if provided, otherwise use the vault resolution chain (default: `~/Documents/AgentMemory`).

2. **Check if vault already exists**: Look for `$VAULT/Home.md`. If it exists, tell the user the vault already exists at that path and offer to open it.

3. **Locate the bundled template**: The template is at `vault-template/` relative to the skill package root. Search for the skill package installation directory — it may be in the agent's plugin/skill cache or a local checkout. Look for the `vault-template/Home.md` file to confirm the correct path.

4. **Create the vault**:
   ```bash
   mkdir -p "$VAULT"
   cp -r "$TEMPLATE_DIR/vault-template/"* "$VAULT/"
   ```

5. **Create Obsidian config directory**:
   ```bash
   mkdir -p "$VAULT/.obsidian"
   ```
   Write the following to `$VAULT/.obsidian/app.json`:
   ```json
   {
     "alwaysUpdateLinks": true,
     "newFileLocation": "folder",
     "newFileFolderPath": "inbox",
     "attachmentFolderPath": "attachments"
   }
   ```

6. **Create empty directories**:
   ```bash
   mkdir -p "$VAULT/inbox"
   mkdir -p "$VAULT/attachments"
   ```
   Create `.gitkeep` files in each empty directory.

7. **Report** the created vault and provide next steps:
   - Open in Obsidian: Vault Switcher → Open folder as vault → `$VAULT`
   - Set the vault path via `OBSIDIAN_VAULT_PATH` environment variable or agent config
   - Start working — the agent will build the knowledge graph as it goes

8. **Generate agent config snippet**: Output a vault path snippet appropriate for the user's agent. For Claude Code, output a `CLAUDE.md` snippet:
   ```markdown
   ## Obsidian Knowledge Vault
   Persistent knowledge vault at `$VAULT`.
   ```
   For other agents, output a generic instruction: "Add `OBSIDIAN_VAULT_PATH=$VAULT` to your environment or agent config."

9. **Auto-scaffold current project**: If inside a git repo, automatically run the `project` command to scaffold the current project in the vault.

10. **Concise output**: Keep the final output to 5-8 lines max: vault path created, project scaffolded (if applicable), how to open in Obsidian, how to set the vault path.

### `analyze` — Analyze Project & Hydrate Vault

Analyze the current codebase and populate the vault with interconnected, content-rich notes.

**Usage**: `analyze` (no arguments — uses current repo)

#### Phase 1: Discovery — Scan for Knowledge Sources

Scan the repo for files that contain pre-existing knowledge:

| Category | Files to scan |
|---|---|
| Agent configs | `CLAUDE.md`, `.claude/CLAUDE.md`, `.cursorrules`, `.windsurfrules`, `.clinerules`, `AGENTS.md`, `Agents.md` |
| Documentation | `README.md`, `CONTRIBUTING.md`, `ARCHITECTURE.md`, `docs/architecture.md`, `docs/ARCHITECTURE.md` |
| Existing ADRs | `docs/adr/ADR-*.md`, `architecture/ADR-*.md`, `adr/*.md`, `docs/decisions/*.md` |
| Project metadata | `package.json`, `go.mod`, `Cargo.toml`, `pyproject.toml`, `setup.py`, `Gemfile`, `pom.xml`, `build.gradle`, `*.csproj` |
| Build/CI | `Makefile`, `Dockerfile`, `docker-compose.yml`, `.github/workflows/*.yml`, `.gitlab-ci.yml` |
| Config | `tsconfig.json`, `.eslintrc.*`, `jest.config.*`, `.goreleaser.yml` |

Read each discovered file. For large files (README, agent configs), read fully. For metadata files, extract key fields (name, version, dependencies).

Also gather:
- Repo URL from `git remote get-url origin`
- Repo root path from `git rev-parse --show-toplevel`
- Active branch from `git branch --show-current`
- Directory tree (top 2 levels of source directories, excluding hidden/vendor/node_modules)
- File extension frequency (for language detection)

#### Phase 2: Analysis — Extract & Synthesize

Using the discovered content, synthesize:

1. **Project metadata**: name, language(s), framework(s), repo URL, local path
2. **Architecture summary**: Entry points, layer organization (e.g., `internal/` → Go service layers, `src/components/` → React app), build system
3. **Component inventory**: Major functional modules — each top-level source directory or logical grouping that represents a distinct unit. For each: purpose (from README/agent config context), key files, and relationships
4. **Pattern inventory**: Coding conventions, error handling strategies, testing approaches — extracted from agent config files (CLAUDE.md sections like "Coding Guidelines", "Testing", etc.)
5. **Domain mapping**: Detected technologies → vault domain notes (e.g., Go, TypeScript, Terraform, React)
6. **Existing decisions**: ADR files found in the repo → import as vault ADR notes
7. **Dependency summary**: Key dependencies from package manifests (listed in project overview, not separate notes)

#### Phase 3: Hydration — Write Vault Notes

**Idempotency rules:**
- If project directory doesn't exist → create everything (scaffold + populate)
- If project directory exists but overview is a skeleton → **replace** overview with populated version
- If individual component/pattern/ADR notes already exist → **skip** and report (don't overwrite manual work)
- Domain notes: create if missing, **append** project link if existing

**Notes to write:**

1. **Project overview** (`$VAULT/projects/{name}/{name}.md`) — Fully populated:
   ```yaml
   ---
   aliases: []
   tags: [project/{short-name}]
   type: project
   repo: {git remote url}
   path: {repo root path}
   language: {detected language(s)}
   framework: {detected framework(s)}
   created: {YYYY-MM-DD}
   status: active
   ---
   ```
   Sections:
   - **Architecture**: Real description from analysis
   - **Components**: Table with wikilinks to component notes
   - **Project Patterns**: Table with wikilinks to pattern notes
   - **Architecture Decisions**: List with wikilinks to ADR notes
   - **Key Dependencies**: From package manifests
   - **Domains**: Wikilinks to domain notes

2. **Component notes** (`$VAULT/projects/{name}/components/{Component}.md`) — One per major module:
   ```yaml
   ---
   tags: [components, project/{short-name}]
   type: component
   project: "[[projects/{name}/{name}]]"
   created: {YYYY-MM-DD}
   status: active
   layer: {detected layer}
   depends-on: []
   depended-on-by: []
   key-files: [{key files list}]
   ---
   ```
   Sections: Purpose, Gotchas

3. **Pattern notes** (`$VAULT/projects/{name}/patterns/{Pattern}.md`) — From agent config conventions:
   ```yaml
   ---
   tags: [patterns, project/{short-name}]
   type: pattern
   project: "[[projects/{name}/{name}]]"
   created: {YYYY-MM-DD}
   ---
   ```
   Sections: Pattern, When to Use, Implementation

4. **ADR imports** (`$VAULT/projects/{name}/architecture/ADR-{NNNN} {title}.md`) — From existing repo ADRs:
   ```yaml
   ---
   tags: [architecture, decision, project/{short-name}]
   type: adr
   project: "[[projects/{name}/{name}]]"
   status: accepted
   created: {YYYY-MM-DD}
   ---
   ```
   Preserve original content, add vault frontmatter.

5. **Domain notes** (`$VAULT/domains/{tech}/{Tech}.md`):
   - If new: create with project link
   - If existing: add this project to "Projects Using This Domain" section

6. **Index updates**:
   - `$VAULT/projects/Projects.md` — add/update row
   - `$VAULT/domains/Domains.md` — add/update rows for new domains

#### Phase 4: Report

Print a summary:
```
Analyzed: {project-name}
  Sources read: {N} knowledge files
  Created: project overview (populated)
  Created: {N} component notes
  Created: {N} pattern notes
  Imported: {N} architecture decisions
  Linked: {N} domain notes
  Skipped: {N} existing notes (preserved)
```

### `recap` — Write Session Summary

Write a session summary note and update TODOs.

**Usage**: `recap`

#### Steps:

1. **Gather session context** by running:
   ```bash
   git log --oneline -20
   git diff --stat HEAD~5..HEAD 2>/dev/null || git diff --stat
   git branch --show-current
   ```

2. **Read current TODOs** — CLI-first:
   ```bash
   obsidian vault=$VAULT_NAME tasks path="todos" todo verbose
   ```
   Fallback: Read `$VAULT/todos/Active TODOs.md`.

3. **Read project overview** from `$VAULT/projects/$PROJECT/$PROJECT.md` (for wikilinks and context).

4. **Write session note** — CLI-first:
   ```bash
   obsidian vault=$VAULT_NAME create path="sessions/{YYYY-MM-DD} - {title}" template="Session Note" silent
   obsidian vault=$VAULT_NAME property:set path="sessions/{YYYY-MM-DD} - {title}" name="type" value="session" type="text"
   obsidian vault=$VAULT_NAME property:set path="sessions/{YYYY-MM-DD} - {title}" name="branch" value="{current-branch}" type="text"
   obsidian vault=$VAULT_NAME property:set path="sessions/{YYYY-MM-DD} - {title}" name="projects" value="[[projects/$PROJECT/$PROJECT]]" type="list"
   ```
   Then append body content:
   ```bash
   obsidian vault=$VAULT_NAME append path="sessions/{YYYY-MM-DD} - {title}" content="..."
   ```
   Fallback: Write the file directly at `$VAULT/sessions/{YYYY-MM-DD} - {title}.md`:
   ```yaml
   ---
   tags: [sessions]
   type: session
   projects:
     - "[[projects/$PROJECT/$PROJECT]]"
   created: {YYYY-MM-DD}
   branch: {current-branch}
   ---
   ```
   Sections to fill:
   - **Context**: What was being worked on (from git log context)
   - **Work Done**: Numbered list of accomplishments (from commits and diffs)
   - **Discoveries**: Technical findings worth remembering
   - **Decisions**: Design choices made during this session
   - **Next Steps**: What should happen next (checkboxes)

5. **Update TODOs**: Edit `$VAULT/todos/Active TODOs.md`:
   - Remove completed `[x]` items from Active TODOs — append them to `$VAULT/todos/Completed TODOs Archive.md` under a dated `## $PROJECT (YYYY-MM-DD)` heading (create the file if it doesn't exist)
   - Add new items discovered during the session
   - Keep items grouped by project
   - **Never leave `[x]` items in Active TODOs** — they accumulate over time and waste context window on every session start

6. **Update Session Log**: Add an entry to `$VAULT/sessions/Session Log.md` with the date, project, branch, and a one-line summary.

7. **Empty the inbox**: file anything in `$VAULT/inbox/` per the Scoping rules (or delete
   if worthless). The inbox must not accumulate.

8. **Persist across machines**: run
   ```bash
   bash ~/.config/obs-memory/obs-git-sync.sh end {agent}
   ```
   (commits now, pulls+pushes in the background — see § Git Sync). Don't skip this: a
   recap that only exists on this machine is half a recap.

9. **Report** what was written.

### `project` — Scaffold New Project

Scaffold a new project in the vault. Uses the first argument as the project name, or defaults to `$PROJECT`.

**Usage**: `project [name]`

#### Steps:

1. **Determine project name**: Use the argument if provided, otherwise use `$PROJECT`.

2. **Check if project exists**: Look for `$VAULT/projects/{name}/{name}.md`. If it exists, tell the user and offer to open it instead.

3. **Create directory structure**:
   - `$VAULT/projects/{name}/`
   - `$VAULT/projects/{name}/architecture/`
   - `$VAULT/projects/{name}/components/`
   - `$VAULT/projects/{name}/patterns/`

4. **Create project overview** at `$VAULT/projects/{name}/{name}.md`:
   ```yaml
   ---
   aliases: []
   tags: [project/{short-name}]
   type: project
   repo: {git remote url if available}
   path: {working directory}
   language: {detected from files}
   framework:
   created: {YYYY-MM-DD}
   status: active
   ---
   ```
   Sections: Architecture, Components, Project Patterns, Architecture Decisions, Domains

   Auto-detect and fill:
   - Language from file extensions in the repo
   - Repo URL from `git remote get-url origin`
   - Link to relevant domains that exist in `$VAULT/domains/`

5. **Update Projects.md**: Add a row to the project table in `$VAULT/projects/Projects.md`.

6. **Report** the scaffolded structure.

### `note` — Create a Note from Template

Create a note using a template. The first argument specifies the type: `component`, `adr`, or `pattern`.

**Usage**: `note <component|adr|pattern> [name]`

#### `note component [name]`

Create at `$VAULT/projects/$PROJECT/components/{name}.md`:
```yaml
---
tags: [components, project/{short-name}]
type: component
project: "[[projects/$PROJECT/$PROJECT]]"
created: {YYYY-MM-DD}
status: active
layer: ""
depends-on: []
depended-on-by: []
key-files: []
---
```
Sections: Purpose, Gotchas

If a name argument is provided, use it as the component name. Otherwise, ask the user.

#### `note adr [title]`

Determine the next ADR number by listing existing ADRs in `$VAULT/projects/$PROJECT/architecture/ADR-*.md`.

Create at `$VAULT/projects/$PROJECT/architecture/ADR-{NNNN} {title}.md`:
```yaml
---
tags: [architecture, decision, project/{short-name}]
type: adr
project: "[[projects/$PROJECT/$PROJECT]]"
status: proposed
created: {YYYY-MM-DD}
---
```
Sections: Context, Decision, Alternatives Considered, Consequences

#### `note pattern [name]`

Create at `$VAULT/projects/$PROJECT/patterns/{name}.md`:
```yaml
---
tags: [patterns, project/{short-name}]
project: "[[projects/$PROJECT/$PROJECT]]"
created: {YYYY-MM-DD}
---
```
Sections: Pattern, When to Use, Implementation, Examples

After creating any note, add a wikilink to it from the project overview.

### `todo` — Manage TODOs

View and update the Active TODOs for the current project.

**Usage**: `todo [action]`

#### Steps:

1. **Read current TODOs** from `$VAULT/todos/Active TODOs.md`.

2. **If no additional arguments**: Display the current TODOs for `$PROJECT` and ask what to update.

3. **If arguments provided**: Parse as a TODO action:
   - Plain text → Add as a new pending item under `$PROJECT`
   - `done: <text>` → Mark item done: remove from Active TODOs, append to `$VAULT/todos/Completed TODOs Archive.md` under a dated `## $PROJECT (YYYY-MM-DD)` heading (create the file if it doesn't exist)
   - `remove: <text>` → Remove matching item

4. **Write back** Active TODOs (and archive file if items were completed).

### `lookup` — Search the Vault

Search the vault for knowledge. Supports targeted subcommands and freetext search.

**Usage**: `lookup <subcommand|freetext>`

#### `lookup deps <name>`

Query what a component depends on.

```bash
obsidian vault=$VAULT_NAME property:read file="<name>" name="depends-on"
```
Fallback: Read the component note and parse the `depends-on` frontmatter list.

#### `lookup consumers <name>`

Query what depends on a component (reverse dependencies).

```bash
obsidian vault=$VAULT_NAME property:read file="<name>" name="depended-on-by"
obsidian vault=$VAULT_NAME backlinks file="<name>"
```
Combine results — `depended-on-by` gives explicit relationships, `backlinks` catches implicit references. Fallback: Read the component note and search for backlinks via Grep.

#### `lookup related <name>`

Query all notes connected to a given note (both directions).

```bash
obsidian vault=$VAULT_NAME links file="<name>"
obsidian vault=$VAULT_NAME backlinks file="<name>"
```
Fallback: Read the note and extract wikilinks, then Grep for `[[<name>` across the vault.

#### `lookup type <type> [project]`

Find all notes of a given type (component, adr, session, project).

```bash
obsidian vault=$VAULT_NAME tag verbose name="<type>"
```
If `[project]` is specified, filter results to notes also tagged `project/<short-name>`:
```bash
obsidian vault=$VAULT_NAME search query="type: <type>" path="projects/<project>"
```
Fallback: Grep for `type: <type>` across `$VAULT`.

#### `lookup layer <layer> [project]`

Find all components in a specific layer.

```bash
obsidian vault=$VAULT_NAME search query="layer: <layer>" path="projects/<project>"
```
If no project specified, search across all projects:
```bash
obsidian vault=$VAULT_NAME search query="layer: <layer>" path="projects"
```
Fallback: Grep for `layer: <layer>` across `$VAULT/projects/`.

#### `lookup files <component>`

Query key files for a component.

```bash
obsidian vault=$VAULT_NAME property:read file="<component>" name="key-files"
```
Fallback: Read the component note and parse the `key-files` frontmatter list.

#### `lookup <freetext>`

General search across the vault.

```bash
obsidian vault=$VAULT_NAME search format=json query="<freetext>" matches limit=10
```
Fallback: Search file contents for the query across all `.md` files in `$VAULT`.

If the query looks like a tag (starts with `#` or `project/`):
```bash
obsidian vault=$VAULT_NAME tags name="<query>"
```

If the query matches a note name:
```bash
obsidian vault=$VAULT_NAME backlinks file="<query>"
```

**Present results**: Show matching notes with their frontmatter (first ~10 lines) so the user can decide which to read in full.

### `relate` — Manage Relationships

Create and query bidirectional relationships between notes via frontmatter properties.

**Usage**: `relate <subcommand> [args]`

#### Supported relationship types

| Forward property | Inverse property |
|---|---|
| `depends-on` | `depended-on-by` |
| `extends` | `extended-by` |
| `implements` | `implemented-by` |
| `consumes` | `consumed-by` |

#### `relate <source> <target> [type]`

Create a bidirectional relationship between two notes. Default type is `depends-on`/`depended-on-by`.

##### Steps:

1. **Resolve note names**: Use `file=` parameter for note display names. If ambiguity is possible (same name, different folders), use `path=` with full vault-relative path.

2. **Read current property on source** (forward direction):
   ```bash
   obsidian vault=$VAULT_NAME property:read file="<source>" name="<forward-property>"
   ```
   Fallback: Read the source note frontmatter.

3. **Check if relationship already exists**: If `<target>` (as a wikilink) is already in the list, skip and report "already related".

4. **Append to source** (forward direction):
   Build the new list locally by appending `[[<target>]]` to the current values, then set:
   ```bash
   obsidian vault=$VAULT_NAME property:set file="<source>" name="<forward-property>" value="<full-list>" type="list"
   ```
   Fallback: Edit the source note's frontmatter directly.

5. **Read current property on target** (inverse direction):
   ```bash
   obsidian vault=$VAULT_NAME property:read file="<target>" name="<inverse-property>"
   ```

6. **Append to target** (inverse direction):
   ```bash
   obsidian vault=$VAULT_NAME property:set file="<target>" name="<inverse-property>" value="<full-list>" type="list"
   ```

7. **Report** the created relationship.

**Safety**: Always read-then-set. Never blind-append. The full list is constructed locally and set atomically.

#### `relate show <name>`

Display all relationships for a note.

##### Steps:

1. **Query all 8 relationship properties**:
   ```bash
   obsidian vault=$VAULT_NAME property:read file="<name>" name="depends-on"
   obsidian vault=$VAULT_NAME property:read file="<name>" name="depended-on-by"
   obsidian vault=$VAULT_NAME property:read file="<name>" name="extends"
   obsidian vault=$VAULT_NAME property:read file="<name>" name="extended-by"
   obsidian vault=$VAULT_NAME property:read file="<name>" name="implements"
   obsidian vault=$VAULT_NAME property:read file="<name>" name="implemented-by"
   obsidian vault=$VAULT_NAME property:read file="<name>" name="consumes"
   obsidian vault=$VAULT_NAME property:read file="<name>" name="consumed-by"
   ```
   Fallback: Read the note frontmatter and parse all relationship properties.

2. **Query structural links**:
   ```bash
   obsidian vault=$VAULT_NAME links file="<name>"
   obsidian vault=$VAULT_NAME backlinks file="<name>"
   ```

3. **Present results** grouped by relationship type. Show explicit (property) relationships first, then structural (wikilink) relationships that aren't already covered.

#### `relate tree <name> [depth]`

Walk the dependency tree via BFS. Default depth is 2.

##### Steps:

1. **Initialize BFS**: Start with `<name>` at depth 0. Maintain a visited set and a queue.

2. **For each node in the queue**:
   ```bash
   obsidian vault=$VAULT_NAME property:read file="<current>" name="depends-on"
   ```
   Fallback: Read the note and parse `depends-on` from frontmatter.

3. **Add unvisited dependencies** to the queue at `current_depth + 1`. Stop when `depth` limit is reached.

4. **Present** the tree as an indented list showing the dependency chain.

### `sync` — Pull/Push the Vault Across Machines

Force a vault git sync now, or repair a failed one.

**Usage**: `sync`

#### Steps:

1. **Preflight**: `git -C $VAULT rev-parse --is-inside-work-tree` — if not a git repo,
   tell the user the vault isn't git-backed yet and offer:
   `git -C $VAULT init && git -C $VAULT add -A && git -C $VAULT commit -m "vault init"`,
   then ask them for a PRIVATE remote URL (never create or pick a remote yourself).
2. **Happy path**: run the shared helper (same one the hooks use):
   ```bash
   bash ~/.config/obs-memory/obs-git-sync.sh end {agent}
   ```
   Then check `git -C $VAULT status` and `cat $VAULT/.obs-sync-state 2>/dev/null` —
   report: commits made, pushed or not, any warning.
3. **If `.obs-sync-state` shows a pull conflict**: run
   `git -C $VAULT pull --rebase --autostash origin $(git -C $VAULT branch --show-current)`
   to reproduce it visibly. For each conflicted note: notes are knowledge, not code —
   resolve by KEEPING BOTH sides (merge the bullet lists, dedupe identical lines); never
   discard a side you didn't write. Then `git add -A && git rebase --continue`, push,
   and delete `.obs-sync-state`.
4. **Never** force-push the vault, and never resolve by `checkout --theirs/--ours` on a
   note you haven't read.

## Git Sync — how vault memory survives machine switches

The vault is a git repo with a private remote. The hooks make sync automatic:
- **Session start** (`obs-orient.sh` → `obs-git-sync.sh start`, background): commit any
  leftovers ("catch-up commit"), `pull --rebase --autostash`, push. Self-healing: even if
  an agent died or has no end-hook, the next session start persists its work.
- **Session end** (`obs-session-end.sh` → `obs-git-sync.sh end`): synchronous local
  commit (nothing is ever lost after this point), then pull+push in the background.
- **Failures never block you**: offline/conflict states are written to
  `$VAULT/.obs-sync-state` and surfaced in the next session-start banner as ⚠️ — when you
  see that warning, run `/obs sync` (step 3 above) before trusting cross-machine state.
- The banner's "last commit / uncommitted paths" line tells you how fresh the vault is.
  Working on a machine you haven't used in days? Wait a few seconds after session start
  (background pull), or run `/obs sync` explicitly before reading TODOs.

## Multi-Agent Conventions (claude · codex · antigravity · …)

One vault serves every agent; git serializes their writes. To keep the shared memory
coherent:

1. **Identify yourself**: session notes carry `agent:` frontmatter and the `({agent})`
   filename suffix; auto-breadcrumbs already log `agent=`. Commits from the sync helper
   say which agent committed.
2. **The vault is the handoff, not the chat**: before ending substantial work, the state
   another agent needs must be IN the vault — Active TODOs updated + project overview's
   "Now / Next" current. Assume the next reader is a different agent on a different
   machine with none of your context.
3. **Trust the file, not your memory of it**: another agent may have updated TODOs or
   notes since you last read them (even mid-session, via sync). Re-read before you
   rewrite; append rather than restructure.
4. **Don't restructure the vault unilaterally**: moving/renaming folders breaks other
   agents' wikilinks and muscle memory. Propose reorganizations to the user; only do
   them when asked, in one dedicated commit.
5. **Same rules, every agent**: codex reads this skill from `~/.codex/skills/obs-memory/`
   (orientation via the same SessionStart hook); antigravity gets a pointer block in
   `~/.gemini/GEMINI.md` (no hook — its hook events have no session-start equivalent).
   Any agent without hook support is still covered by the next session's catch-up commit
   (see § Git Sync).
6. **One durable memory**: agent-private memory features (Antigravity Knowledge Items,
   Claude's built-in per-project memory, serena memories) never hold project knowledge —
   they don't cross agents or machines. The vault is the only durable store; the others
   are at most session-local convenience.

## Token Budget Rules

1. **CLI over reads**: Use `obsidian` CLI for property reads, backlinks, links, tags, and search — these return targeted data without full file reads
2. **Session start**: At most 2 operations (TODOs + project overview)
3. **During work**: Use `lookup` subcommands and `relate show` before reading full notes
4. **Frontmatter first**: When scanning, read ~10 lines before committing to full read
5. **List before read**: List directory contents before reading files
6. **Write concisely**: Bullet points, links, tags — no prose when bullets suffice

## Error Handling

- If the vault doesn't exist → suggest running `/obs init` to bootstrap it
- If the project doesn't exist in the vault → check `projects/_memory-modes.md` FIRST:
  no entry → run the ask-once protocol (§ Project Memory Modes); `collab`/`off` → do NOT
  offer `/obs project` scaffolding; `full` or user says personal → offer it
- If a note already exists → show it instead of overwriting, offer to edit
- If no git repo is detected → use current directory name as project name
- If CLI command fails → fall back to file read for the same data
- If the session-start banner shows a ⚠️ sync warning → run the `sync` procedure before
  relying on cross-machine state; local work can proceed meanwhile

## Vault Structure Reference
```
$VAULT/
├── Home.md                           # Dashboard (read only if lost)
├── projects/{name}/
│   ├── {name}.md                     # Project overview — START HERE
│   ├── changelog.md                  # One line per agent commit (mode full) — date, hash, why
│   ├── architecture/                 # ADRs and design decisions
│   ├── components/                   # Per-component notes
│   └── patterns/                     # Project-specific patterns
├── domains/{tech}/                   # Cross-project knowledge
├── patterns/                         # Universal patterns
├── sessions/                         # Session logs (read only when needed)
├── todos/Active TODOs.md             # Pending work (read at session start; ≤~120 lines)
├── templates/                        # Note templates
├── inbox/                            # Capture-only — emptied by every recap/analyze
├── .gitignore                        # .obsidian, .obs-sync-state, .trash
└── .obs-sync-state                   # Last sync warning (gitignored; surfaced in banner)
```

