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.
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
- An Obsidian vault at
~/brainwith a structured layout (journal, learnings, projects, repos, runbooks, scripts, templates). - A
CLAUDE.mdat the vault root that tells Claude the structure and the conventions — every session in any project that pulls from this vault gets the rules automatically. - A
/brainskill at~/.claude/skills/brain/SKILL.md— invoked when the user says "save this as a learning" or similar. Drafts a note, gets approval, writes it to disk, commits. - A SessionStart hook — runs a small script when a Claude Code session opens (mine checks the agent message bus; yours can prime context, surface alerts, anything).
- A Stop hook — fires when the session ends, prompts "Anything worth saving to the brain vault? Run /brain to save a learning, runbook, or project note."
- An auto-committing git repo — every change pushes to GitHub on a 10-minute timer via Obsidian's Git plugin, so the vault syncs across machines.
- A semantic search index — a small Python script that embeds every note locally and exposes a
brainshell command for grep-with-meaning.
Prerequisites
- macOS or Linux (works on Windows/WSL but the shell commands assume bash/zsh).
- Obsidian installed.
- Claude Code installed (any recent version).
- A GitHub account for the auto-commit sync.
- Ollama installed for local embeddings (optional but cheap and good).
- Python 3.9+ for the search script.
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
- Open Obsidian.
- Click the vault icon (bottom-left of the sidebar) → Open folder as vault → select
~/brain. - Settings → Community plugins → Turn on community plugins (you'll get a one-time warning; this is fine).
- Settings → Community plugins → Browse → search Git → install + enable.
- 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.
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)
~/.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
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
- Open a Claude Code session in any project. The Stop-hook reminder fires when you end the session — confirms the hook is wired.
- In the same session, do something worth remembering. Then say "save this as a learning". Claude should invoke the
/brainskill, draft a note, ask for approval, write to~/brain/learnings/<slug>.md, and commit. - Within ~10 minutes the Obsidian Git plugin pushes to GitHub. Refresh your repo on github.com to confirm.
- 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:
- Cross-project memory. A pattern you proved out in project A is instantly available when working in project B.
- Searchable history. "Have I seen this error before?" —
brain "Cloudflare 525". The answer is inlearnings/cloudflare-525-zone-mismatch.md. - Documented runbooks. "How do I deploy a Worker?" —
~/brain/runbooks/Cloudflare Worker Deploy.md. Every operational procedure I've ever solved is one grep away. - The agent does the saving. The Stop hook makes saving the default action at end of session. You don't have to remember to write it down — Claude reminds you, and the
/brainskill drafts the note for you.
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:
- Daily journal hook — a cron that creates today's
journal/YYYY-MM-DD.mdfile at 6 a.m. with a stub template, so writing into the day's entry is friction-free. - Cross-vault links — when a learning emerges from a project, link it both ways. The Obsidian graph view becomes a real map.
- Weekly consolidation — a slash command (
/consolidate) that scans recent journal entries and proposes which ones should be promoted into the permanentlearnings/folder. Pattern documented at The Bible Reads First. - Brain-as-RAG — point an LLM agent at the vault for retrieval. Same shape as the project bible, just personal scope.
Seth Shoultes builds at garagedoorscience.com and writes about it at sethshoultes.com/blog.