obsidian-second-brain
v0.4.0
Published
Turn an Obsidian vault into a self-maintaining second brain for AI coding sessions
Readme
second-brain
Give your AI coding sessions a long-term memory — an Obsidian vault that becomes a curated, agent-maintained wiki.
Every Claude Code session ends the same way: the context dies with it. The transcript lands in a folder nobody reads back, and the next session starts cold. second-brain closes that loop — without a background process:
- Capture at intent — session transcripts mirror into a cold store; ending a task with
/wrapdistills it, in-session, into a small curated artifact and rewrites the project's wiki hub (what it is, what was decided and why, how projects relate). Memory is compiled by the agent that has the most context, at the moment it has it. - Code structure — graphify parses your repo into a code graph on a git post-commit hook (AST, offline, $0);
second-brain mapalso renders deterministic code pages on demand. - Recall — init adds a section to your
CLAUDE.mdand a SessionStart hook so each session consultswiki/index.mdand the project hub when the repository alone can't answer. Sessions start light and find context instead of being fed giant dumps.
No daemon, no cron, no hourly model calls. The tool is a set of deterministic, on-demand commands; the only marginal cost is the /wrap distillation that rides your already-paid session ($0 extra) and the optional on-demand map.
You work in Claude Code
├─ transcripts mirror to a cold store (evidence, not indexed)
└─ /wrap on finishing a task
├─ raw/artifacts/<project>/… curated, immutable source
└─ wiki/projects/<project>.md the hub — rewritten in place, bounded
│
git post-commit → graphify → wiki/code/<project>/ (code graph, $0)
│
second-brain index → wiki/index.md (deterministic catalog)
│
your next session reads wiki/index.md when it lacks context — and just knowsRequirements
| Requirement | Notes |
|---|---|
| macOS | primary target today (Windows/Linux not yet supported) |
| Node >= 20 | the build runs automatically on install |
| Obsidian | the vault folder must contain .obsidian/ — open it in Obsidian once, or let init create a fresh vault with --new-vault |
| Claude Code | used at least once, so ~/.claude/projects/ exists; its claude CLI powers /wrap and (optionally) code maps |
Install
npm install -g obsidian-second-brain
second-brain initAfter upgrading (
npm update -g obsidian-second-brain), runsecond-brain initagain: npm replaces the package folder, which holds the generated config. Re-running is idempotent and never touches your vault content.
git clone https://github.com/minhnhat08/second-brain.git ~/tools/second-brain
cd ~/tools/second-brain
npm install # installs dependencies and builds (via the prepare hook — if it fails, run `npm run build`)
npm link # puts the `second-brain` command on your PATH
second-brain initPrefer not to touch your global npm? Skip npm link and use node dist/cli.js wherever you see second-brain below.
init asks two questions — Enter accepts the defaults shown in brackets:
Claude folder [/Users/you/.claude]
Obsidian vault [/Users/you/Documents/Obsidian Vault]It refuses to continue if the Claude folder has no projects/ (run Claude Code once first) or the vault has no .obsidian/ (open it in Obsidian once, or pass --new-vault). Then it wires the loop:
- scaffolds the vault as an LLM wiki (
raw/,wiki/,AGENTS.md, its own git repo for history) - installs the
/wrapcommand and adds a marker-fenced section to your~/.claude/CLAUDE.md: the/wraphabit plus the retrieval policy - installs a SessionStart hook that injects the context pack at session start
- generates
config/projects.json
Your files are safe. Nothing is silently overwritten: conflicts prompt (or use --force / --keep-existing), every override is backed up next to the original, a symlinked CLAUDE.md is followed to its target and edited additively, and init prints a review: line whenever it touches an instruction file — read what it added.
Unattended install: second-brain init --claude <dir> --vault <dir> --new-vault --keep-existing. Without a terminal attached, unspecified paths take the defaults and any file conflict aborts unless --force or --keep-existing is given.
Daily use
There is no schedule to babysit — everything is on demand or event-triggered:
- End meaningful tasks with
/wrap. It writes the curated artifact and rewrites the project hub in the same session; the installed CLAUDE.md section reminds the agent to offer it. second-brain syncmirrors new provider transcripts into the vault and leaves a git commit; run it whenever you like.second-brain indexrebuildswiki/index.mddeterministically from page frontmatter.second-brain lintflags structural drift (missingsummary/sources, one-directional links, stale hubs, over-budget hubs);--scope <pages>is the/wrapacceptance gate.second-brain candidatesrecords un-wrapped work as debt ininbox/candidates/.- Browse
wiki/index.mdin Obsidian now and then.
Is it working?
second-brain doctor # config, claude binary, manifest, health, vault writable
ls -t "<vault>/wiki/" | head # index.md, projects/, code/ — <vault> is the path you chose in initIf init warned claude CLI preflight failed, the claude-backed features fall back to noop. Confirm claude --version works in a plain terminal, then set synthesis.backend to cli-claude in config/projects.json.
Configuration
config/projects.json — in the install folder, not the vault:
| Field | Meaning |
|---|---|
| vaultRoot | <vault>/raw/projects — where transcripts mirror to (must end in raw/projects) |
| coldTranscriptRoot | optional cold store for full transcripts outside the indexed vault |
| providers[] | one entry per source; all four fields required: name (claude / codex / gemini), sourceRoot, destinationPath, enabled |
| synthesis.backend | noop ($0: mirror only) · cli-claude (used by /wrap and map) |
| synthesis.claudeBin | absolute path to the claude binary — init records the one it verified |
| logsRoot | run logs directory |
Paths support ${ENV_VAR} expansion; the file is validated with clear errors. Edit it and the next run picks it up — no reload step.
Code maps
Two complementary paths render a code layer under wiki/code/<project>/:
- graphify (recommended) —
second-brain graphifyruns graphify to a staging dir and imports an allowlisted code graph + report.initcan wire it to a git post-commit hook so it updates on your commits. Missing/incompatible graphify degrades to "no code layer", never a broken run. second-brain map <repoPath>— captures a repository's structure as deterministic evidence underraw/code/<project>/($0 — no LLM in capture) and renders wiki pages: one architecture overview (with a Mermaid diagram) plus one page per module. Re-run it after changes; only changed evidence re-renders (content-hash manifest).
second-brain map <repoPath> [--project <name>] [--path <subdir>]
[--max-modules N] [--force]--pathscopes a monorepo package; register each package as its own project.--forceclears the project's code manifest entries and re-renders every page.- Page bodies are generated artifacts — manual edits are overwritten. Evidence under
raw/code/is the LLM-readable source of truth for structure.
Usage accounting
Every paid claude -p call (code maps today; /wrap rides your session at $0) is metered at the source. second-brain usage reports it the way the claude CLI surfaces its own usage:
second-brain usage [--since YYYY-MM-DD] [--until YYYY-MM-DD] [--json]
[--config <path>]It prints lifetime / last-7-day / last-30-day totals, broken down by phase and by model, with token columns and a cost figure. --since / --until are UTC days. --json emits the raw aggregate.
- The cost is the CLI's
total_cost_usdverbatim — an ≈ API-equivalent estimate. On a Claude subscription your actual marginal spend is $0. - Each run that made at least one paid call writes one record to
<logsRoot>/usage/<runId>.json(capped at the 500 most recent). Idle runs write nothing.
Migration (from v4 daemon layouts)
second-brain migrate --dry-run reports a provenance-preserving plan: snapshot the manifest and episode→source map, archive wiki/episodes/ to archive/episodes/, relocate raw/projects/ to the cold root, and rebuild the index — nothing is deleted until recoverability is proven. Drop --dry-run to apply.
Uninstall
second-brain uninstall
npm uninstall -g obsidian-second-brainRemoves the managed CLAUDE.md section, the SessionStart hook, /wrap, and generated files it installed — skipping any you modified. Your vault content is never touched: it is your data and your audit trail.
Development
npm run typecheck
npm test # unit/integration tests (node:test)
npm run coverage # per-file line/branch coverage
npm run e2e # drives the real built binary against temp worlds ($0)The E2E suites spawn dist/cli.js as a child process against disposable worlds (vault paths deliberately contain spaces): provider transcript mirroring, code-map rendering + re-render, and the init/uninstall lifecycle. The architecture lives in docs/design-v5.md.
Status
v5: no daemon, no launchd, on-demand tools only. Memory is compiled in-session at /wrap; the code layer comes from graphify. Marginal cost $0.
