I built a second brain that runs on Claude Code.
A personal knowledge management system that captures every conversation, transcript, and note into an Obsidian vault, then curates it into a wiki an LLM can read directly. Drop files in, run /ingest, ask the wiki anything with /query. A nightly cron keeps it current while you sleep.
The pattern, end-to-end
The system is a working implementation of the LLM Wiki pattern Andrej Karpathy has talked about. Source notes stay raw and untouched; a curated layer above them does the synthesis, the linking, and the attribution. Every claim points back to where it came from.
RAW IS RAW
Conversations, transcripts, articles, notes — every source lands in raw/ and never gets edited again. It's the audit trail.
WIKI IS CURATED
Claude promotes raw sources into structured wiki pages with frontmatter, cross-links, and source attribution. One idea per page.
FEEDS ITSELF
A capture hook saves every Claude Code session into raw/. A nightly cron runs /ingest and /lint at 02:00.
Two domains, one rule
The vault has exactly two top-level concerns: read-only intake and curated output. Everything else — agents, content drafts, journal — is optional scaffolding you grow into.
second-brain/ ├── raw/ # read-only intake — never edited by hand │ ├── claude-exports/ │ │ └── auto/ # written by the SessionEnd hook │ ├── chatgpt-exports/ │ ├── articles/ │ ├── notes/ │ ├── voice-memos/ │ └── youtube-transcripts/ ├── wiki/ # Claude's domain — curated output │ ├── index.md # master catalog │ ├── log.md # append-only activity log │ ├── concepts/ │ ├── projects/ │ └── people/ ├── agents/ # optional persona files ├── content/ # optional drafts └── .claude/ ├── settings.json # hook config ├── commands/ # /ingest /log /query /lint ├── hooks/ # capture-session.py └── scripts/ # nightly-ingest.ps1 / .sh
The contract Claude reads on every session
One file at the vault root — CLAUDE.md — defines structure, frontmatter rules, link syntax, and style. The same six frontmatter fields show up on every wiki page.
--- title: "Home Server" type: concept # concept | source-summary | project | person sources: - raw/articles/2026-04-18-proxmox-setup.md - raw/claude-exports/auto/2026-04-19-103044-session-end.md related: - "[[proxmox]]" - "[[atlas]]" created: 2026-04-18 last-updated: 2026-05-01 --- # Home Server ## Overview 2-paragraph summary of the home-server stack... ## Key Points - Proxmox 8.2 on the [[atlas]] machine, three VMs running... ## Sources - (source: articles/2026-04-18-proxmox-setup.md)
FRONTMATTER · 6 FIELDS
title, type, sources, related, created, last-updated. /lint catches you the first time you forget one.
WIKI-LINKS · NATIVE OBSIDIAN
Cross-link with [[slug]]. Open the vault in Obsidian and the graph view + backlinks panel come for free.
Four commands. The whole loop.
Each command is a Markdown prompt at .claude/commands/. Drop them in, type the slash, Claude does the rest.
The system feeds itself
A single Python file, ~150 lines, hooked to Claude Code's SessionEnd and PreCompact events. Every session you run lands in raw/ as a Markdown snapshot.
# Fires on SessionEnd and PreCompact. Never raises. # One file per hook firing — vault-relative, local I/O only. def main(): payload = json.loads(sys.stdin.read()) transcript_path = payload.get("transcript_path") event = payload.get("hook_event_name", "unknown") vault_root = Path(__file__).resolve().parent.parent.parent out_dir = vault_root / "raw" / "claude-exports" / "auto" out_dir.mkdir(parents=True, exist_ok=True) messages = read_transcript_jsonl(transcript_path) rendered = render_transcript(messages) now = datetime.now() filename = f"{now:%Y-%m-%d-%H%M%S}-{event.lower()}-{short_id}.md" (out_dir / filename).write_text(frontmatter + body, encoding="utf-8")
Lock · snapshot · run · report
A clean PowerShell wrapper (or bash equivalent) that runs /ingest then /lint at 02:00 every night. Concurrency-locked, snapshots wiki/ first, writes a structured status JSON.
{
"startedAt": "2026-05-02T02:00:00Z",
"completedAt": "2026-05-02T02:04:18Z",
"vault": "/home/you/second-brain",
"overall": "ok",
"backupPath": ".claude/backups/nightly-ingest/20260502-020000-wiki.zip",
"ingest": {
"status": "ok",
"processed": 5,
"created": 3,
"updated": 4,
"remaining": 12
},
"lint": { "status": "ok", "issuesFound": 0 }
}
Six steps to a working second brain
30–45 minutes from git clone to a vault that captures itself. Full templates and prose in GUIDE.md.
SCAFFOLD THE VAULT
Single shell command creates raw/, wiki/, agents/, content/, journal/, .claude/ — and the suggested raw/ subfolders.
Read step 1 →INSTALL CLAUDE.MD
Copy the conventions template to the vault root. Edit Section 4 (Domain Context) for your life.
Read step 2 →INIT INDEX + LOG
Two seed files — index.md (catalog) and log.md (activity). Both grow as you use the system.
Read step 3 →INSTALL THE COMMANDS
Drop /ingest, /log, /query, /lint into .claude/commands/. Sanity-check with one test source.
Read step 4 →WIRE THE CAPTURE HOOK
capture-session.py + a SessionEnd block in settings.json. Verify by ending a session and looking in raw/claude-exports/auto/.
Read step 5 →SCHEDULE THE CRON
Run nightly-ingest.ps1 -DryRun to verify, then register the daily 02:00 task. Bash + cron equivalent included.
Read step 6 →Everything required, nothing exotic
Plain Markdown, a Python script, a PowerShell or bash wrapper, and Claude Code. No cloud, no database, no API keys beyond Claude.
BUILD ONE OF YOUR OWN
The full setup walkthrough, drop-in templates, and lessons learned live in the repo. Fork it, follow GUIDE.md, and message me when something breaks — feedback sharpens the templates for the next person.