Files
dotfiles/claude-obsidian
noconnorandClaude Opus 5 11138d90a9 claude-obsidian: summarize Claude conversations into Obsidian notes
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
2026-09-17 16:14:45 -04:00
..

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

  1. 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).

  2. ./install.sh — links the binary into ~/.local/bin, enables the hourly user timer, and creates ~/.config/claude-obsidian/config.env from config.env.example if absent.

  3. Edit config.env (at minimum OBSIDIAN_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 its claude-* models.
  • --no-session-persistence is 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_RUN sessions 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 for SETTLE_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.