obsidian-brain

Sync Claude Code session transcripts into your Obsidian vault, generate end-of-day summaries that pull together your meetings, PRs, Jira work, and Claude sessions into one note, and extract actionable items as TaskNotes-formatted task files.

Installation

claude plugin install obsidian-brain@personio-claude-code-marketplace

Then set the vault path in your shell (~/.zshrc, ~/.bashrc, etc.):

export OBSIDIAN_BRAIN_VAULT_PATH="/absolute/path/to/your/obsidian/vault"

The plugin creates three folders under the vault on first use:

  • <vault>/claude-transcripts/YYYY/MM/DD/ — one Markdown file per Claude Code session, written automatically at session end. Transcripts are nested by date so the folder stays navigable as it grows.
  • <vault>/daily-summaries/ — one Markdown file per day, produced by the summarize-today skill and refreshed in place on re-run.
  • <vault>/tasks/ — one TaskNotes-formatted note per actionable item extracted by the summarize-today skill (open Jira issues, open PRs, daily-note checkboxes, transcript follow-ups). Folder name is configurable via OBSIDIAN_BRAIN_TASKS_FOLDER.

Requires: jq (brew install jq).

What does this plugin do?

Automatic transcript export (Stop + SessionEnd hooks)

The plugin writes a Markdown version of the running transcript into your Obsidian vault after every assistant turn (Stop hook) and once more at session end (SessionEnd) — no action needed.

Using Stop in addition to SessionEnd means the file stays current even if the session terminates in a way that doesn't fire SessionEnd (force-quit, crash, desktop window close). The script writes atomically (temp file + rename) so Obsidian never sees a half-written file.

What you need:

  • OBSIDIAN_BRAIN_VAULT_PATH set to your vault root.
  • jq on your PATH.

If the env var is unset or the vault doesn't exist, the hook exits quietly — no errors, no noise.

Quick Start:

# one-time setup
echo 'export OBSIDIAN_BRAIN_VAULT_PATH="$HOME/Documents/Obsidian Vault"' >> ~/.zshrc
source ~/.zshrc

# from then on, every session you end creates a file like:
# <vault>/claude-transcripts/2026/04/20/2026-04-20-1423-3f930ef3.md

Example output: 2026-04-20-1423-3f930ef3.md

---
session_id: '3f930ef3-6523-4867-918c-eb434846b159'
started_at: '2026-04-20T14:23:11.012Z'
ended_at: '2026-04-20T15:04:02.441Z'
cwd: '/Users/you/dev/my-project'
reason: 'exit'
message_count: 47
tags: [claude-code, transcript]
---

# Claude Code session — 2026-04-20-1423

- **Session:** `3f930ef3-6523-4867-918c-eb434846b159`
- **Working dir:** `/Users/you/dev/my-project`
- **Ended:** 2026-04-20T15:04:02.441Z (exit)

## User

Refactor the webhook retry logic to use exponential backoff.

## Assistant

I'll start by reading the current webhook handler and the retry config.

**Tool call: `Read`**

**Tool call: `Grep`**

...

Why would you want this?

  • Your Claude sessions become searchable Obsidian notes alongside your engineering notes, meeting notes, and design docs.
  • You can link to specific sessions from other notes (e.g., from a project page or a Jira ticket note) using [[2026-04-20-1423-3f930ef3]].
  • You get a persistent archive even if Claude Code's local transcript storage is cleared.
  • Frontmatter (session_id, cwd, message_count) makes filtering via Obsidian Dataview trivial.

summarize-today skill

Generates a single end-of-day summary note in your vault that aggregates everything you did across tools: today's Claude sessions, calendar meetings, PRs, Jira issues, and your existing daily note. Also extracts actionable items from those same sources and creates one TaskNotes-formatted note per task under <vault>/tasks/, deduped by external ID so re-runs refresh status instead of creating duplicates.

What you need:

  • OBSIDIAN_BRAIN_VAULT_PATH set (required).
  • Optional but recommended data sources — each is skipped cleanly if missing:
    • Google Calendar MCP (for meetings)
    • gh CLI authenticated (for PRs)
    • acli authenticated (for Jira)
    • OBSIDIAN_BRAIN_DAILY_NOTE_GLOB set to your daily note pattern (e.g. Daily Notes/{date}.md) to pull your own note into the summary
    • Obsidian CLI (obsidian, 1.12+) — if installed, the skill triggers a sync after writing the summary so it propagates to your other devices, and prefers the CLI for vault-aware lookups (tags, properties, backlinks) over raw filesystem operations. The Obsidian app must be running for the CLI to work; if it isn't, the skill falls back silently and the on-disk write still serves as the source of truth.

Quick Start:

In any Claude Code session:

Summarize today

Or Wrap up my day, Daily recap, What did I ship today? — the skill triggers on any of these.

Example:

Claude will run the data-collection steps in parallel (gcal MCP, gh search prs --author=@me, acli for Jira, Glob on today's transcripts, Read on your daily note), then write a file at <vault>/daily-summaries/2026-04-20.md. Running the skill multiple times in a day refreshes the same file — no duplicates.

---
date: 2026-04-20
type: daily-summary
tags: [daily, claude-brain]
---

# Daily summary — 2026-04-20

## Highlights

- Shipped webhook retry with exponential backoff ([apps#33297](https://github.com/x/apps/pull/33297), PLAT-412)
- Unblocked EO team on attribute_logs consistency — root cause is a race in the event publisher
- Pair session with Milan on the product brief for ARM roles

## Meetings

- 09:30 — **Product review — ARM** (6 attendees) — agreed to cut scope to roles-only for Q2
- 14:00 — **1:1 with Ilja** — discussed hiring loop pacing

## Pull requests

### Authored
- [personio/apps#33297](https://github.com/personio/apps/pull/33297) — Webhook retry w/ exp backoff — **OPEN** (30 min ago)

### Reviewed
- [personio/monolith#88102](https://github.com/personio/monolith/pull/88102) — Fix null deref in PayConnect — MERGED

## Jira

- **PLAT-412** — Webhook retry hardening (In Review) — "Ready for review, see PR linked"
- **ARM-188** — Roles-only scope for Q2 (To Do)

## Today's tasks

### Jira
- [[plat-412]] — Webhook retry hardening — in-progress (updated)
- [[arm-188]] — Roles-only scope for Q2 — open (new)

### Pull requests
- [[personio-apps-33297]] — Webhook retry w/ exp backoff — in-progress (new)

## Claude Code sessions

### Webhook retry
- [[2026-04-20-0915-3f930ef3]] — Refactored retry config, switched to exponential backoff

### Attribute logs investigation
- [[2026-04-20-1130-7a22cd01]] — Traced the race to `AttributeLogPublisher.publish`

## From my daily note

> Focus: finish the webhook PR before standup; review Milan's brief in the afternoon.
> Energy: low morning, rebounded after lunch.

## Open threads

- Need to rerun the failed migration on staging before EOD tomorrow.

Each linked task note in <vault>/tasks/ follows the TaskNotes frontmatter format:

---
tags:
  - task
title: Webhook retry hardening
status: in-progress
priority: high
due: 2026-04-25
contexts:
  - "@work"
projects:
  - "[[PLAT]]"
external_id: PLAT-412
source: jira
source_url: https://personio.atlassian.net/browse/PLAT-412
created: 2026-04-20
---

## Notes

Pulled in from Jira PLAT-412 — backend retry logic for the webhook delivery worker. Linked PR personio/apps#33297.

Re-running the skill on a later day refreshes only the frontmatter (status, priority, due, scheduled) of an existing task — your hand-written ## Notes and ## Meeting Notes content is preserved.

Why would you want this?

  • One place to look at the end of the day — no stitching together calendars, PRs, tickets, and notes by hand.
  • Outcome-focused, not activity-focused — the summary names what actually shipped, not what you fiddled with.
  • Becomes a permanent, searchable Obsidian note you can link back to from weekly reviews, retros, or 1:1 prep.
  • Open work surfaces as TaskNotes the moment it appears — Jira issues, open PRs, and daily-note checkboxes all flow into a single Obsidian task list you can manage with the TaskNotes plugin.
  • Gracefully degrades — any missing data source is labeled "skipped" rather than failing the whole summary.

Configuration

Env varPurposeRequired
OBSIDIAN_BRAIN_VAULT_PATHAbsolute path to your Obsidian vault rootYes
OBSIDIAN_BRAIN_DAILY_NOTE_GLOBPath pattern under the vault for your daily note. Supported tokens: {date}YYYY-MM-DD, {year}YYYY, {month}MM, {day}DD. Flat layout: Daily Notes/{date}.md. Nested by year/month: Daily Notes/{year}/{month}/{date}.md.No
OBSIDIAN_BRAIN_TASKS_FOLDERVault-relative folder where TaskNotes-formatted task files are written. Defaults to tasks. Must resolve inside the vault.No

File layout

<vault>/
├── claude-transcripts/
│   └── 2026/
│       └── 04/
│           └── 20/
│               ├── 2026-04-20-0915-3f930ef3.md    ← session export (hook)
│               └── 2026-04-20-1130-7a22cd01.md    ← session export (hook)
├── daily-summaries/
│   └── 2026-04-20.md                              ← summarize-today skill output (one per day, overwritten on re-run)
└── tasks/
    ├── plat-412.md                                ← TaskNotes file per actionable item (deduped by external_id, frontmatter refreshed on re-run)
    └── personio-apps-33297.md

Pre-existing flat transcripts (from before version 1.4.0) are migrated into the nested layout automatically on the next hook fire.

Troubleshooting

  • Nothing is being exported. Check echo $OBSIDIAN_BRAIN_VAULT_PATH in the shell Claude Code was launched from. The hook reads env from the Claude Code process, not the shell you use inside it.
  • jq not found in hook logs. Install with brew install jq.
  • Hook timed out. Default timeout is 30s. Very long transcripts may exceed this — raise it by editing hooks/hooks.json if needed.
  • Summary is missing a source. The skill prints which source was skipped and why. For gh/acli, check they're authenticated. For Google Calendar, reconnect via /mcp.