A Claude Code session toolkit: search, distill, and publish your history.
Your ~/.claude/projects/ is hundreds of megabytes of .jsonl transcripts β unsearchable, unreadable, and expensive to point Claude at. ccblog turns that archive into something useful:
- π Search β BM25 full-text search over every past session. Sub-ms queries. No API key needed.
- π§ Learn β extract structured learnings from sessions so future agents can query "have we solved this before?"
- βοΈ Publish β generate polished blog posts + interactive HTML viewers from any session.
All local. All optional. Search works offline with zero network calls.
npm install -g github:varadhjain/claude-code-blog-generator
# Search (no API key required β fully local BM25 over SQLite FTS5)
ccblog index # one-time: build the search index
ccblog search "auth bug" # find past sessions by topic
ccblog watch # keep the index live-updated
# Learn + publish (requires an API key)
ccblog --setup # picks Anthropic or OpenAI
ccblog # interactive: session β blog postSearch is free and instant. Blog/learn features use Anthropic (Claude Haiku) or OpenAI (gpt-5-nano) at ~$0.001/session.
Session .jsonl β ccblog β 4 files:
βββ SUMMARY.md Narrative blog post
βββ summary.html Formatted blog
βββ index.html Interactive annotated viewer
βββ page-*.html Paginated full conversation
Blog summary includes:
- Session goal and outcome (synthesized, not just copied)
- ASCII flow diagram
- Phases with specific names like "Fixing token exhaustion: 1000β8000 tokens"
- Key prompts color-coded: new task / steering / pivot
- Code snippets from tool uses
- Deep links to exact conversation moments
BM25 full-text index over every session in ~/.claude/projects/ and ~/.codex/sessions/. Sub-ms queries. Zero network calls. The index lives at ~/.ccblog/session-index.db and filters out tool_result blobs (which make JSONL huge but rarely help search).
ccblog index # build/update the index (incremental)
ccblog watch # initial index + live tail on JSONL appends
ccblog search "jwt middleware" # BM25 search β ranked results + snippets
ccblog files "src/auth/middleware.ts" # every session that touched a file
ccblog sessions # 20 most recent sessionsCodex CLI support. Sessions from OpenAI's Codex CLI (~/.codex/sessions/YYYY/MM/DD/*.jsonl) are indexed alongside Claude Code sessions. Each result has a source: 'claude-code' | 'codex' field. The indexer auto-detects both β no flag required.
Tuning: four BM25 field weights in src/search/weights.ts control ranking (user text, assistant text, tool calls, file paths). No reindex needed after changes.
ccblog operates in three zones with different access rules:
| Zone | Examples | Access |
|---|---|---|
| Local-personal | ccblog search, MCP queries, learning extraction, on-demand blog generation |
Always frictionless. No gates. Every learning is visible. |
| Personal outputs | Blog posts you publish, gists you upload | You author each one. Per-action consent is built into the act of publishing. |
| Outbound / admin | Future: team sync, scheduled auto-share | Hard gate. Only ever ships items you've explicitly marked share-ready. Automation cannot bypass per-item approval. |
The share_status field on each learning means "ready to leave the machine," not "ready to use." Local features ignore it entirely. New extractions default to 'local'. Run ccblog review to triage.
ccblog extract
β
βΌ
βββββββββββββββ
β local β βββ default for every new learning
β π draft β (also: re-review with --all)
βββββββββββββββ
β β
share-ready keep-private
β β
βΌ βΌ
ββββββββββββββ ββββββββββββββ
β reviewed β β private β βββ stays here, never leaves
β β
eligibleβ β π kept β
β to share β β local-onlyβ
ββββββββββββββ ββββββββββββββ
β² β²
ββre-decideβββββ (ccblog review --all)
β local β reviewed β private β
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
ccblog search β β β β β β β Zone 1 β local
MCP search_learnings β β β β β β β (no gate)
MCP search_sessions β β β β β β β
ccblog blog (manual) β β β β β β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
You publish a gist β β* β β* β β* β Zone 2 β per
*consent = the act of running the publish command β -action consent
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
Future team sync β β β β β β β Zone 3 β hard gate,
Future scheduled push β β β β β β β automation can
never bypass
ccblog review # interactive triage of pending drafts
ccblog review --all # include already-decided items
ccblog status # how many drafts are pending? (--count for shell prompt)Each item shows: type, problem, solution, files touched, what the redactor stripped (counts + categories), and current status. Choose share-ready / keep-private / edit tags / delete / skip / quit.
The outbound publisher (Notion / git / S3 / etc) is intentionally not built yet β the contract is that the gate exists before any door does. Any future publisher MUST refuse to read anything other than share_status === 'reviewed'.
As an MCP server (one server, all tools β search + learnings):
{
"mcpServers": {
"ccblog": { "command": "ccblog", "args": ["serve"] }
}
}Exposes search_sessions, read_session_window, list_sessions_by_file, list_recent_sessions alongside the existing learnings tools. Tool descriptions instruct Claude not to persist snippets to MEMORY.md.
# Interactive mode β pick a session, analyze, upload to Gist
ccblog
# Auto mode β analyze latest session, save draft (great for hooks)
ccblog --auto --quiet --redact
# Setup wizard
ccblog --setup
# Help
ccblog --helpGenerate a blog draft after every Claude Code session β zero effort:
Add to ~/.claude/settings.json:
{
"hooks": {
"PostSessionStop": [{
"command": "ccblog --auto --quiet --redact"
}]
}
}Drafts accumulate in ~/.ccblog/drafts/. Review and publish when ready.
--redact scans for and replaces:
- API keys (Anthropic, OpenAI, AWS, GitHub)
- Email addresses
- IP addresses
- Home directory paths (
/Users/yourname/) - Database connection strings
- Private keys
Auto-prompted when uploading to Gist even without the flag.
Two-pass AI analysis:
- Phase detection β Identifies task boundaries and generates specific, action-oriented phase names
- Contextual annotations β Color-codes each message:
- π’ New task start
- π‘ Clarification / steering
- π΄ Pivot / major change
Smart summarization extracts goals, outcomes, key prompts, and code snippets. Generates deep links from summary to specific conversation messages.
Uses whichever API key is available (checks in order):
| Provider | Model | Cost/session | Env var |
|---|---|---|---|
| Anthropic | Claude Haiku 4.5 | ~$0.002 | ANTHROPIC_API_KEY |
| OpenAI | gpt-5-nano | ~$0.001 | OPENAI_API_KEY |
Set in .env file or environment. The setup wizard (ccblog --setup) walks you through it.
- Node.js 18+
- API key β Anthropic or OpenAI (only for blog/learn features; search works without)
- GitHub CLI (optional) β for Gist upload (
brew install gh)
Search is fully local. ccblog index, ccblog watch, and all search_* MCP tools make zero network calls. Your session transcripts never leave your machine for these paths. The index lives at ~/.ccblog/session-index.db (SQLite) and only stores filtered message text β tool_result blobs are dropped at ingest time.
Blog / learnings features are opt-in and use an API key you control. API keys are read from .env or the environment (never hardcoded or logged). .env and .jsonl files are gitignored. PII redaction is available for all published content.
Review generated files before uploading β sessions may contain sensitive code or credentials.
Uninstall: rm -rf ~/.ccblog clears everything this tool stored.
git clone https://github.com/varadhjain/claude-code-blog-generator.git
cd claude-code-blog-generator
npm install
echo "ANTHROPIC_API_KEY=sk-ant-..." > .env # or OPENAI_API_KEY
npm run build
npm link
ccblogMIT
Turn your best Claude Code sessions into content the world can learn from.