Recipe · Knowledge management

Set up Claude Code with a Brain Vault

An Obsidian vault at ~/brain wired to Claude Code as persistent memory — so the next session knows what the last one figured out.

Time: ~25 min Difficulty: intermediate Companion skill: set-up-claude-code-with-brain-vault Reference repo: github.com/sethshoultes/building-with-ai-brain

Why a brain vault

I work across a lot of projects with Claude Code. Each session has a context window, which closes when the session closes. A pattern I figured out yesterday in one project is gone tomorrow when I'm in a different project unless I wrote it down somewhere both me and Claude can read.

The fix is a personal knowledge vault — an Obsidian vault at ~/brain that lives outside any single project. Claude Code reads from it on startup, can save to it during a session, and rebuilds a search index on every commit. It is the canonical context the orchestrator reads first, the way every persona in the bible reads its bible before speaking.

When working in a new project, Claude Code knows about the patterns I've already proved out in old ones. When debugging an issue, it can grep ~/brain/learnings/ for "I've seen this before" notes. When standing up a new piece of infrastructure, it can read the relevant runbook. The vault is the institutional memory I never had.

What you'll build

Prerequisites


Step 1 — Create the vault

Make a new git repo at ~/brain with the directory layout. The names are conventions — you can rename, but anything that pulls from the vault expects this shape.

mkdir -p ~/brain/{journal,learnings,projects,repos,runbooks,scripts,templates,claude-memory,backups}
cd ~/brain
git init -b main

Create one starter README so the repo isn't empty:

cat > ~/brain/README.md <<'EOF'
# Brain

Personal knowledge vault. Projects, learnings, runbooks, repos, journal entries.
EOF

Step 2 — Write the operating manual (CLAUDE.md)

This is the most important file in the vault. Claude Code auto-loads CLAUDE.md at the root of any directory you're working in, so a vault-level CLAUDE.md teaches every session the structure and the rules.

cat > ~/brain/CLAUDE.md <<'EOF'
# Brain Vault — Claude Code Operating Manual

This is an Obsidian knowledge base at `~/brain`. When working in this vault,
follow these conventions exactly.

## Vault Structure

| Folder | Purpose | Template |
|--------|---------|----------|
| `journal/` | Daily logs — what happened, decisions, frustrations | `YYYY-MM-DD.md` |
| `learnings/` | Hard-won lessons from building | `templates/learning.md` |
| `projects/` | Living project notes | `templates/project.md` |
| `repos/` | Repository reference cards | `templates/repo.md` |
| `runbooks/` | Step-by-step operational procedures | Numbered steps |
| `scripts/` | Automation scripts (bash, python) | N/A — executable |
| `templates/` | Note templates | N/A |
| `claude-memory/` | Synced Claude Code memories (auto-managed) | Don't edit by hand |

## Note Creation Rules

### Frontmatter is mandatory
Every `.md` note must have YAML frontmatter:

- **Learnings**: `title`, `date`, `source`, `tags`
- **Projects**: `name`, `status`, `repo`, `url`, `created`, `updated`, `tags`
- **Repos**: `name`, `url`, `status`, `language`, `created`, `last_active`, `tags`
- **Journal**: `date`, `tags`
- **Runbooks**: `title`, `date`, `tags`

### File naming
- Journal: `YYYY-MM-DD.md`
- Everything else: `kebab-case.md` (lowercase, hyphens). Grep-friendly.

### Wikilinks
A note without links is a bug. Every note should link to at least one related note via `[[note-name]]`.

## When saving a note

1. Pick the right folder. Learnings vs runbooks vs project notes are different shapes.
2. Use the matching template if one exists in `templates/`.
3. Add at least one wikilink to a related note.
4. Run the search-index rebuild and commit (see `scripts/brain-search.py`).

## What NOT to put here

- Code that belongs in a project repo. The vault is for knowledge ABOUT building, not the build artifacts.
- Long-form prose drafts. Those go in a writing/manuscript directory.
- Secrets. The vault is a public-ish git repo. Secrets live in `~/.config/dev-secrets/secrets.env`.
EOF

Step 3 — Add the templates

Three templates make notes consistent and one-line creatable. They live in templates/ so Obsidian's Templates plugin can use them too.

cat > ~/brain/templates/learning.md <<'EOF'
---
title:
date:
source:
tags: []
---

#

## The Lesson

## Context

## How to Apply

## Related
- [[]]
EOF

cat > ~/brain/templates/project.md <<'EOF'
---
name:
status: active
repo:
url:
created:
updated:
tags: []
---

#

## What Is It

## Status

## Recent Activity

## Open Questions

## Related
- [[]]
EOF

cat > ~/brain/templates/repo.md <<'EOF'
---
name:
url:
status: active
language:
created:
last_active:
tags: []
---

#

## What It Does

## How It Connects

## Related
- [[]]
EOF

Step 4 — Open the vault in Obsidian

  1. Open Obsidian.
  2. Click the vault icon (bottom-left of the sidebar) → Open folder as vault → select ~/brain.
  3. Settings → Community plugins → Turn on community plugins (you'll get a one-time warning; this is fine).
  4. Settings → Community plugins → Browse → search Git → install + enable.
  5. Settings → Git → set Vault backup interval to 10 (minutes). Push on every commit: ON.

The Git plugin will commit and push every 10 minutes from now on. The vault stays in sync across every machine you connect.

Don't have an Obsidian license? The free version is enough for this. Sync across devices via the Git plugin — Obsidian's paid Sync product is convenient but unnecessary if you're already running git.

Step 5 — Push the repo to GitHub

cd ~/brain
git add -A
git commit -m "Initial vault structure"
gh repo create brain --private --source=. --push
# (or: gh repo create brain --public --source=. --push  if you're comfortable
#  publishing your knowledge base — most people start private and graduate)
Gotcha — secrets. Don't commit anything from ~/.config/dev-secrets/ into the brain vault. Even if the repo is private, anything that reaches a git history is one accidental publish away from being public. Keep the vault for knowledge about infrastructure, not credentials for it.

Step 6 — Add the /brain skill

Skills live at ~/.claude/skills/<skill-name>/SKILL.md and activate when an agent matches their description. Create the brain skill so any Claude Code session can save a note when you say "save this as a learning" or similar.

mkdir -p ~/.claude/skills/brain
cat > ~/.claude/skills/brain/SKILL.md <<'EOF'
---
name: brain
description: Use when a session produced something worth remembering — a fix, decision, runbook, or learning. Also use when the user says /brain or asks to save something to the vault.
---

# Brain — Save to Knowledge Vault

Save a note to the Obsidian brain vault (`~/brain`) from the current session.

## How it works

1. Ask the user what type of note to save:
   - **learning** — a pattern or lesson learned (goes in `learnings/`)
   - **runbook** — a step-by-step procedure (goes in `runbooks/`)
   - **project** — project status or context (goes in `projects/`)

2. Draft the note based on what happened in the session. Use the matching template from `~/brain/templates/` if one exists.

3. Show the draft to the user for approval.

4. Write the file to `~/brain//.md`

5. Rebuild the search index and commit:
```bash
cd ~/brain && python3 scripts/brain-search.py index
git add -A && git commit -m "brain: add /" && git push origin main
```

## Note guidelines

- Use the session context — don't ask the user to re-explain what just happened.
- Keep notes concise and scannable (headers, bullet points, code blocks).
- Add `[[wiki links]]` to related notes when obvious.
- Runbooks must have: When to use, Prerequisites, Steps (with checkboxes), Verify.
- Learnings must have: What happened, What we learned, How to apply next time.
EOF

Restart Claude Code (or run /reload-plugins). The skill will appear in the available list — typing /brain in a session invokes it.

Step 7 — Wire the hooks

Hooks fire on lifecycle events: SessionStart when a Claude Code session opens, Stop when it ends. Two minimal hooks make the vault a real persistent-memory system.

Edit ~/.claude/settings.json:

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/Users/<you>/.claude/hooks/check-messages.sh",
            "timeout": 5
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "echo '{\"systemMessage\": \"\\ud83e\\udde0 Anything worth saving to the brain vault? Run /brain to save a learning, runbook, or project note.\"}'",
            "timeout": 5
          }
        ]
      }
    ]
  }
}

The Stop hook is the workhorse — every session ends with a brain-vault prompt, which trains the habit of saving what was just learned. The SessionStart hook is a placeholder for whatever you want to surface when a session opens. Mine checks an agent message bus for cross-session signals; yours might cat the latest journal entry, surface a TODO, or do nothing.

Make the SessionStart script if you want one:

mkdir -p ~/.claude/hooks
cat > ~/.claude/hooks/check-messages.sh <<'EOF'
#!/usr/bin/env bash
# Surface a one-line context blip at session start. Customize freely.
LATEST_JOURNAL=$(ls -t ~/brain/journal/*.md 2>/dev/null | head -1)
if [ -n "$LATEST_JOURNAL" ]; then
  echo "{\"systemMessage\": \"📓 Latest journal: $(basename "$LATEST_JOURNAL")\"}"
fi
EOF
chmod +x ~/.claude/hooks/check-messages.sh
Gotcha — JSON in the hook command. Hook commands echo JSON; backslashes inside the JSON in settings.json have to be double-escaped (\\u, not \u). If the hook silently does nothing, validate your JSON.

Step 8 — Add the semantic search index

Grep is fine for keyword search. For "I learned something about X" queries, embeddings are better — they find conceptually similar notes even when the wording is different. Ollama runs the embeddings locally for free.

# Pull a small embedding model (one-time, ~274MB)
ollama pull nomic-embed-text

Save a small Python script at ~/brain/scripts/brain-search.py that builds and queries the index. The full version (with chunking, rebuild-on-change, and a clean CLI) lives in the brain repo; the minimum useful version is ~80 lines and stores a SQLite index next to the vault.

Add a shell alias so the search is one keystroke:

# In ~/.zshrc (or ~/.bashrc)
brain() {
  python3 ~/brain/scripts/brain-search.py "$@"
}

Then:

source ~/.zshrc
brain index                              # build the index
brain "ElevenLabs tool registration"     # semantic query
brain "the time I broke production"      # works even with vague queries

Step 9 — Verify end-to-end

  1. Open a Claude Code session in any project. The Stop-hook reminder fires when you end the session — confirms the hook is wired.
  2. In the same session, do something worth remembering. Then say "save this as a learning". Claude should invoke the /brain skill, draft a note, ask for approval, write to ~/brain/learnings/<slug>.md, and commit.
  3. Within ~10 minutes the Obsidian Git plugin pushes to GitHub. Refresh your repo on github.com to confirm.
  4. Run brain "<keyword from the note>" — the new note should surface.

What this gets you, day-to-day

The vault is a flywheel. Each session adds to it; each session benefits from what's already there. Specifically:

The companion skill

Everything above is also packaged as an installable Agent Skill: set-up-claude-code-with-brain-vault. Install it with:

npx skills add github:sethshoultes/building-with-ai-skills --skill set-up-claude-code-with-brain-vault

Then in any new session: "set up a brain vault for me" — the skill walks the agent through the procedure on a fresh machine.

What's next

Once the vault has 20+ notes, add a few patterns that make it more powerful:

Seth Shoultes builds at garagedoorscience.com and writes about it at sethshoultes.com/blog.