One note per Claude Code session (incremental, hourly user timer) and a claude.ai data-export importer, written through the Obsidian Local REST API plugin -- whose built-in /mcp/ server is also how Claude reads them back. Transcripts are condensed (tool output dropped, one line per tool call, ~60x smaller), redacted, then summarized by headless `claude -p` on the user's own plan. Three things learned the hard way are encoded here: - --no-session-persistence, or every summary becomes a transcript that the next run summarizes. - A LiteLLM gateway passing through the caller's Claude subscription has no key of its own, so a background job cannot use its claude-* models. - Summaries share the plan's usage limits. A parallel ~100-session backfill exhausted a session limit in minutes. So one job, a per-run cap, and the run stops at the first usage-limit error (exit 75) instead of failing the whole queue; a stop flag, because Future.cancel() cannot stop work a thread already picked up. Covered by an offline test of stop / resume / idempotent rerun. Nothing environment-specific is committed: hosts, keys and paths live in ~/.config/claude-obsidian/ (this repo is public). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KF322NzozeaFjpgWvh4F4g
claude-obsidian — Claude conversations → Obsidian notes
Summarizes every Claude conversation into one searchable Obsidian note: what happened, what was decided and why, the facts and gotchas worth remembering, and what was left open.
- Claude Code sessions sync automatically and incrementally (hourly timer).
- claude.ai chats import from a data export (claude.ai has no API, so each export is manual).
Notes are written through the Obsidian Local REST API
plugin, so the vault can live anywhere the plugin is reachable. The same plugin exposes a
built-in MCP server (/mcp/), which is how Claude reads the notes back — no separate MCP
process.
~/.claude/projects/*/*.jsonl ─┐ ┌─ Claude/Code/2026/…md
claude.ai export .zip ────────┼─ condense → redact → summarize → PUT ────┼─ Claude/Web/2026/…md
└─ (headless claude -p, no transcript) └─ Claude/Index.md
Install
-
In Obsidian, install Local REST API, enable its HTTP server, and make it reachable from this machine. Put the API key in
~/.config/claude-obsidian/api-key(chmod 600). -
./install.sh— links the binary into~/.local/bin, enables the hourly user timer, and creates~/.config/claude-obsidian/config.envfromconfig.env.exampleif absent. -
Edit
config.env(at minimumOBSIDIAN_API_URL), then preview one note:claude-obsidian-sync stats # sizes, no network claude-obsidian-sync sync --dry-run --limit 1 # prints the note, writes nothing
Use
claude-obsidian-sync sync # what the timer runs
claude-obsidian-sync sync --session <session-id> # one session, now, ignoring the settle window
claude-obsidian-sync import-claude-ai ~/Downloads/data-2026-09-13.zip
Claude reads the vault through the plugin's MCP server:
claude mcp add --scope user --transport http obsidian http://<host>:27123/mcp/ \
--header "Authorization: Bearer $(cat ~/.config/claude-obsidian/api-key)"
How it behaves — and why
- Summaries run on your own Claude plan via headless
claude -p(LLM_BACKEND=claude-cli). No API key to manage. An OpenAI-compatible backend (LLM_BACKEND=openai) exists for local models. Note a LiteLLM gateway that fronts Claude by passing through the caller's subscription has no key of its own, so a background job cannot use itsclaude-*models. --no-session-persistenceis load-bearing. Without it every summary writes a new transcript into~/.claude/projects, and the next run summarizes its own summarizing.- It shares your plan's usage limits. A first backfill of ~100 sessions in parallel hit a
session limit within minutes — which also limits interactive use. So: one job at a time,
MAX_PER_RUNsessions per run (newest first), and the run stops at the first usage-limit error (exit 75, which the systemd unit treats as success) instead of failing the rest of the queue. The next hourly run resumes. A 100-session backlog drains in about ten hours. - Secrets are redacted before anything leaves the machine, and again in the note: private
keys, bearer tokens,
password=-style assignments, JWTs, provider tokens, ≥48-char hex, and random mixed-case strings. It over-redacts on purpose; git SHAs, md5s and paths survive. The summarizer is also told never to reproduce secrets. - Incremental and idempotent. State (
~/.local/state/claude-obsidian/state.json) records each session's size+mtime. A resumed session is re-summarized into the same note path. A session is left alone until idle forSETTLE_MINUTES, so a live conversation isn't summarized mid-flight. Empty sessions are recorded and skipped. - Condensed, not truncated. Tool output is dropped and each tool call becomes one line; prose is kept. Typical compression is ~60× (271 MB of transcripts → 4.4 MB). Sessions over 300 KB condensed are summarized in parts, then combined.
- Subagent transcripts (
<session>/subagents/**) are part of their parent session and are not synced separately.
Note format
type: claude-conversation
source: claude-code # or claude-ai
session_id: 3f9c2a1e-…
title: Fix flaky CI cache after runner upgrade
project: my-service # basename of the working directory
branch: …
started / ended: …
prompts: 1 # user prompts, not tool-loop records
summarized_by: claude-haiku-4-5-20251001
tags: [claude, claude-code, ci, caching, …]
followed by a summary callout and Outcomes / Decisions / Facts & gotchas / Open threads,
and a claude --resume <id> line. Claude/Index.md lists every note, newest first.