Skip to content

interesting-moments

Captures intellectually interesting moments from your Claude sessions and saves them as Obsidian notes. Optionally queues Threads post drafts for review.

Works two ways:

  • Claude Code (automatic) -- a Stop hook that fires after every session across CLI, desktop app, web app, and IDE extensions
  • Claude Desktop (on demand) -- an MCP server that Claude can call during conversations

How it works

  1. Every time a Claude Code session ends, the Stop hook fires
  2. The hook reads the session transcript
  3. It sends a trimmed version to a local LLM (Ollama) and asks: "Was anything interesting discovered here? Score it 1-10."
  4. If the score meets your threshold (default: 7/10), it writes a markdown note to your Obsidian vault
  5. Optionally: it saves a Threads post draft to a separate folder for you to review, edit, and publish on your own schedule

Install (Claude Code)

1. Copy the hook script

mkdir -p ~/.claude/hooks
cp interesting-moments.py ~/.claude/hooks/
chmod +x ~/.claude/hooks/interesting-moments.py

2. Install Ollama and pull a model

# Install Ollama: https://ollama.com
ollama pull gemma4

The hook uses Ollama by default so transcript analysis runs locally with no API keys. You can swap in any model you prefer (see config below).

3. Install Python dependencies

pip3 install -r requirements.txt

4. Configure

Copy the config template and edit it:

cp interesting-moments.json ~/.claude/interesting-moments.json
Field Description
obsidian_vault Absolute path to your Obsidian vault root
obsidian_folder Subfolder inside vault for AI moment notes (default: AI-Moments)
llm_provider "ollama" (default, local) or "anthropic" (cloud API)
ollama_model Ollama model to use (default: gemma4)
ollama_url Ollama server URL (default: http://localhost:11434)
anthropic_api_key Only if llm_provider is "anthropic" (prefer ANTHROPIC_API_KEY env var)
min_interest_score Minimum score (1-10) to trigger a capture (default: 7)
threads_drafts_enabled Set true to save Threads post drafts for review
threads_drafts_folder Subfolder in vault for draft posts (default: Threads-Drafts)
threads_voice Voice description for Threads drafts (see Customizing your voice)
slack_channel_id Slack channel ID to post to (e.g. C0ASZLCE3S4). Queues a post for the next session
max_transcript_chars Trim transcripts longer than this before LLM call (default: 12000)
tag_prefix Obsidian tag added to every captured note (default: ai-moment)

5. Copy the SessionStart hook (optional, for Slack)

If you set slack_channel_id in the config, the Stop hook queues a pending Slack post. The SessionStart hook picks it up at the start of your next session and asks Claude to review and post it via the Slack MCP tool.

cp session-start-hook.py ~/.claude/hooks/
chmod +x ~/.claude/hooks/session-start-hook.py

6. Register the hooks in Claude Code

Open ~/.claude/settings.json (create it if it doesn't exist) and merge in:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "python3 ~/.claude/hooks/session-start-hook.py"
          }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "python3 ~/.claude/hooks/interesting-moments.py"
          }
        ]
      }
    ]
  }
}

If you already have a hooks key, append to the arrays rather than replacing them. The SessionStart hook is optional -- skip it if you don't need Slack integration.

Install (Claude Desktop)

The MCP server lets Claude Desktop save interesting moments on demand. No Ollama needed -- Claude Desktop evaluates the conversation itself.

1. Install the MCP Python dependency

pip3 install -r requirements-mcp.txt

2. Configure

Same config file as the hook. If you haven't already:

cp interesting-moments.json ~/.claude/interesting-moments.json

Edit obsidian_vault to point to your vault.

3. Add the MCP server to Claude Desktop

Open Claude Desktop settings and add this to your MCP servers config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):

{
  "mcpServers": {
    "interesting-moments": {
      "command": "python3",
      "args": ["/path/to/interesting-moments-mcp.py"]
    }
  }
}

Replace /path/to/ with the actual path to this repo or wherever you copy the file.

4. Add a Project system prompt (recommended)

Create a Project in Claude Desktop and add this to the system prompt:

When something intellectually interesting comes up in our conversation -- a novel technique, surprising insight, clever solution, or good mental model -- use the save_interesting_moment tool to capture it. Score the moment 1-10 for interestingness. Write in Cory's voice for the Threads draft: builder, civic tech practitioner, learning in public.

After saving a moment that scores 7 or above, also post a concise version to the #_how-i-ai-cory Slack channel (channel ID: C0ASZLCE3S4) using the slack_send_message tool. Keep the Slack post short and conversational -- what was interesting and why it matters. No need to post the full key insight, just the hook.

This nudges Claude to proactively capture moments and share them to Slack. Without it, you can still ask Claude to save or post manually.

Note: The Slack integration requires the Slack MCP connection in Claude Desktop. The Claude Code hook cannot post to Slack via MCP since it runs outside Claude's session -- see Roadmap for planned Slack API support in the hook.

Obsidian note format

Each captured note looks like this:

---
title: "Unexpected ColBERT Retrieval Win on Short Queries"
date: 2026-04-10T14:32:00
tags:
  - ai-moment
  - retrieval
  - colbert
source: claude-code
project: "/Users/yourname/code/my-project"
session: "sess_abc123"
interest_score: 9
---

# Unexpected ColBERT Retrieval Win on Short Queries

> ColBERT dramatically outperformed BM25 and dense retrieval on sub-5-word
> queries in the Arlington municipal code benchmark.

## Key Insight

Late interaction scoring appears to preserve token-level semantics even for
very short queries where dense retrieval collapses meaning into a single
vector prematurely.

---

*Captured automatically by interesting-moments hook on 2026-04-10*
*Project: `/Users/yourname/code/my-project`*

Threads draft queue

When threads_drafts_enabled is true, the hook also saves a short-form post draft to your vault's Threads-Drafts folder. Each draft is a markdown file you can review and edit in Obsidian:

---
status: draft
date: 2026-04-10T14:32:00
linked_note: "[[2026-04-10-1432-unexpected-colbert-retrieval-win]]"
interest_score: 9
title: "Unexpected ColBERT Retrieval Win"
---

TIL ColBERT crushes dense retrieval on short queries. We benchmarked
against Arlington municipal code and the gap was huge for sub-5-word
procedural queries like "permit fees." Late interaction > single vector
when tokens matter. #civictech #retrieval #buildinpublic

The draft links back to the full Obsidian note via [[wikilink]]. You can:

  • Review and edit the post text directly in Obsidian
  • Change the status frontmatter (draft / approved / published) to track what's ready
  • Use Obsidian Dataview to query all drafts by status, score, or date
  • Publish on your own schedule -- copy-paste or use a future Threads API integration

No Threads API keys are required for the draft queue.

Customizing your voice

The threads_voice config field tells the LLM how to write Threads drafts. The default is tuned for Cory's writing style. To match your own voice, replace it with a short description of how you write:

{
  "threads_voice": "Enthusiastic frontend dev. Uses analogies. Prefers short sentences. Skips jargon. Emoji-friendly."
}

A good voice description is 1-3 sentences covering: tone (casual/formal), domain (what you write about), and style quirks (sentence length, humor, emoji use). Read a few of your own posts and distill what makes them sound like you.

Canonical tags

The analysis prompt uses a tags.md file in your AI-Moments folder to keep tags consistent across notes. This drives cross-linking -- notes that share tags get a "Related Moments" section with [[wikilinks]].

The file lives at <your-vault>/AI-Moments/tags.md and looks like:

# Tags

## Domains
- civic-tech
- gov-tech
- developer-tooling

## Technologies
- python
- llms
- ollama

## Concepts
- system-architecture
- concurrency
- security

The LLM is told to prefer tags from this list and only create new ones if nothing fits. To add a new tag, edit tags.md directly in Obsidian -- it takes effect on the next session.

The MCP server also exposes a get_canonical_tags tool so Claude Desktop can read the list before choosing tags.

Tuning interest scores

The scoring prompt asks the LLM to evaluate based on:

  • Novel technique or pattern discovered
  • Clever or non-obvious solution to a hard problem
  • Surprising insight about a technology, tool, or domain
  • Something that would make a good Threads post or blog seed

Typical sessions score 3-5 (routine work). Interesting architecture discussions, debugging breakthroughs, or benchmark surprises tend to score 7-9.

Adjust min_interest_score to taste.

Testing with dry-run

The --dry-run flag runs the full analysis pipeline but prints the result to stderr instead of writing files:

echo '{"transcript": "USER: I just discovered that ColBERT dramatically outperforms dense retrieval on short queries"}' \
  | python3 interesting-moments.py --dry-run

This is useful for testing your config, tuning the interest threshold, or checking what the LLM produces before going live.

Background execution

The hook forks to the background after reading stdin, so it never blocks Claude Code from starting your next session. The Ollama call (which can take 10-60+ seconds depending on model and hardware) runs in a detached child process. You won't see output in your terminal, but notes will appear in your vault.

Troubleshooting

Nothing is being captured

  • Check ~/.claude/settings.json has the hook registered with the correct path
  • Test with dry-run: echo '{"transcript": "..."}' | python3 ~/.claude/hooks/interesting-moments.py --dry-run
  • If using Ollama: make sure it's running (ollama serve) and the model is pulled
  • If using Anthropic: ensure ANTHROPIC_API_KEY is set in your environment or config file

Notes are in the wrong folder

  • Confirm obsidian_vault is an absolute path (expand ~ manually or use the full path)
  • The folder is created automatically if it doesn't exist

Roadmap

  • MCP server for Claude Desktop
  • Slack integration via MCP (Claude Desktop)
  • Slack integration for Claude Code hook (SessionStart context injection)
  • Threads API integration for publishing approved drafts
  • LinkedIn draft queue (similar format)
  • Interest score dashboard via Obsidian Dataview
  • Weekly digest: summarize all AI-Moments notes into one Threads thread

About

A Claude Hook for writing interesting convos to my Obsidian Vault

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages