claude-obsidian: Claude conversations -> Obsidian notes (+ MCP) #1

Merged
noconnor merged 4 commits from claude-obsidian-sync into main 2026-09-17 20:18:11 +00:00
Owner

Adds claude-obsidian/ — summarizes Claude conversations into one Obsidian note each.

  • Claude Code sessions: incremental, hourly systemd user timer.
  • claude.ai: import-claude-ai <export.zip> (no API exists, so exports are manual).
  • Written via the Obsidian Local REST API plugin, whose built-in /mcp/ server is also how Claude reads the notes back — verified end-to-end with Claude Code (16 vault tools, read + list).

Design points worth reviewing

  • Summarizer = headless claude -p on your own plan, with --no-session-persistence (otherwise summaries become transcripts the next run re-summarizes — confirmed the transcript count is unchanged after a call). The LiteLLM gateway can't be used: its claude-* models pass through the caller's subscription and it has no Anthropic key.
  • Usage limits are shared with interactive use. A parallel ~100-session backfill hit a session limit within minutes. Now: JOBS=1, MAX_PER_RUN=10 newest-first, and the run stops at the first usage-limit error with exit 75 (SuccessExitStatus=75) — using a stop flag, since Future.cancel() can't stop a job a worker already started. Offline test covers stop (JOBS=1 and 3 → exactly 2 model calls), resume after reset, empty sessions, and idempotent rerun.
  • Redaction before anything leaves the machine and again in the note. Tested against the three real secrets present in local transcripts in six contexts each — all redacted — while git SHAs, md5s, paths and hostnames survive.
  • ~60× condensation: 271 MB of transcripts → 4.4 MB; one session needs map-reduce.
  • Public repo: nothing environment-specific is committed; scanned for internal names, IPs, paths and secrets. Hosts and keys live in ~/.config/claude-obsidian/.

Heads-up

install.sh symlinks the binary from this checkout, so it currently points at this branch. It stays correct once merged.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KF322NzozeaFjpgWvh4F4g

Adds `claude-obsidian/` — summarizes Claude conversations into one Obsidian note each. - **Claude Code** sessions: incremental, hourly systemd user timer. - **claude.ai**: `import-claude-ai <export.zip>` (no API exists, so exports are manual). - Written via the **Obsidian Local REST API** plugin, whose built-in `/mcp/` server is also how Claude reads the notes back — verified end-to-end with Claude Code (16 vault tools, read + list). ### Design points worth reviewing - **Summarizer = headless `claude -p`** on your own plan, with `--no-session-persistence` (otherwise summaries become transcripts the next run re-summarizes — confirmed the transcript count is unchanged after a call). The LiteLLM gateway can't be used: its `claude-*` models pass through the *caller's* subscription and it has no Anthropic key. - **Usage limits are shared with interactive use.** A parallel ~100-session backfill hit a session limit within minutes. Now: `JOBS=1`, `MAX_PER_RUN=10` newest-first, and the run stops at the first usage-limit error with exit 75 (`SuccessExitStatus=75`) — using a stop flag, since `Future.cancel()` can't stop a job a worker already started. Offline test covers stop (JOBS=1 and 3 → exactly 2 model calls), resume after reset, empty sessions, and idempotent rerun. - **Redaction before anything leaves the machine** and again in the note. Tested against the three real secrets present in local transcripts in six contexts each — all redacted — while git SHAs, md5s, paths and hostnames survive. - **~60× condensation**: 271 MB of transcripts → 4.4 MB; one session needs map-reduce. - **Public repo**: nothing environment-specific is committed; scanned for internal names, IPs, paths and secrets. Hosts and keys live in `~/.config/claude-obsidian/`. ### Heads-up `install.sh` symlinks the binary from this checkout, so it currently points at this branch. It stays correct once merged. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01KF322NzozeaFjpgWvh4F4g
noconnor added 4 commits 2026-09-17 20:16:31 +00:00
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
The user asked for usage to be monitored closely, and measuring it turned
up that most of the spend was avoidable.

Metering: every model call is appended to usage.jsonl with BILLED tokens
(modelUsage -- the visible `usage` turn undercounted by half) and the
CLI's list-price cost. `usage` reports 5h/24h/7d. Each call's cost is
estimated first and refused if it would cross the per-run or rolling 24h
budget, so budgets cannot be overshot. Removed --max-budget-usd: it
aborts after spending and discards the result ($0.26 lost in one call).

Cost, measured on one session ($0.057 -> $0.010, same summary content):
- alwaysThinkingEnabled=false: extended thinking was ~85% of output.
- DISABLE_PROMPT_CACHING=1: one-shot prompts were being written to the
  1h cache at 2x input price.
- CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1: a hidden side request
  re-read the whole prompt, doubling billed input (9,638 vs 4,663).

Summaries are cached until their note is written, so a vault failure
never re-pays; --prefetch summarizes while the vault is unreachable.
Offline tests (fake claude binary): budget refusal pre-flight, cache
reuse with zero calls, map-reduce, env/argv levers, billed-token ledger.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KF322NzozeaFjpgWvh4F4g
Needed to leave a full backlog drain running unattended:

- Run lock (flock): enabling the timer fired an immediate catch-up run
  (Persistent=true) while a manual run was about to start; without a
  lock the two would race on state.json and usage.jsonl.
- OBSIDIAN_API_URL accepts a comma-separated list, first reachable wins,
  so the permanent address and an interim port-forward share one config.
- Fix: inline "# comments" in config.env were parsed into values, so
  BUDGET_USD_PER_RUN=0.50  # ... crashed float(). The shipped
  config.env.example had exactly that shape on several lines -- anyone
  copying it would have hit the crash. A '#' inside a value (URL fragment)
  still survives. Tested against both the example and a real config.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KF322NzozeaFjpgWvh4F4g
The first full drain reported "105 written, 0 failed, 9 still queued" when
the 9 were empty sessions already recorded as skipped -- nothing was left.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KF322NzozeaFjpgWvh4F4g
noconnor force-pushed claude-obsidian-sync from 154dc18eb9 to db3a3e6e82 2026-09-17 20:16:31 +00:00 Compare
noconnor merged commit d3393edfdd into main 2026-09-17 20:18:11 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: public/dotfiles#1