Skip to main content

claude-handoff

claude-handoff: turn a messy Claude Code session into one clean handoff.md

PyPI Python CI Downloads License: MIT

Summarize & export a Claude Code session into one clean handoff.md you can paste into Gemini, GPT, or another Claude — without the noise.

Claude Code stores every session locally as JSONL (~/.claude/projects/…/*.jsonl), full of tool calls, tool results, thinking blocks and system reminders. Existing exporters dump all of that into markdown. claude-handoff instead produces a handoff document: the actual conversation, what files were touched, what commands ran, and (optionally) an LLM-written summary of goal / decisions / current state / next steps — so the next model can just continue the work.

  • Zero dependencies. Stdlib only, Python 3.9+. A nine-module package — also shipped as a generated single-file script you can curl and audit.
  • Deterministic by default. No API call, no cost, works offline.
  • --llm when you want a real summary. Claude, OpenAI or Gemini via your own API key — or --llm claude-cli, which runs your locally-installed Claude Code CLI on your existing Pro/Max plan: no API key at all.
  • Noise-free. Drops tool results, thinking blocks, system reminders, subagent chatter, slash-command envelopes. Keeps user intent, assistant answers, files modified, commands run — including the files and commands of subagents (agent-*.jsonl), whose full transcripts stay behind --include-sidechains.
  • Project memory. chf --brief distills a project's ENTIRE session history into one living brief (decisions, fixes, conventions, open threads — with session citations); --install-brief-hook injects it into every new Claude Code session, so Claude starts already knowing the project.
  • Safe to paste. Secret-looking strings (API keys, tokens, password=…) are redacted from every output — the handoff you paste into a web chat is egress too. --no-redact opts out.

Install

pipx install claude-handoff        # or: pip install claude-handoff
brew install Vasilispapg/tap/claude-handoff   # Homebrew
# or just grab the generated single-file build — stdlib-only, auditable:
curl -O https://raw.githubusercontent.com/Vasilispapg/claude-handoff/main/single/claude_handoff.py
python3 claude_handoff.py --list

# tab completion (bash or zsh):
eval "$(claude-handoff --completions zsh)"

Installing the package gives you two commands: claude-handoff and its short alias chf — identical, use whichever you like typing.

Usage

claude-handoff                     # latest session → handoff.md
claude-handoff -i                  # pick from a numbered list
claude-handoff --list              # what sessions do I have? (title · first prompt)
claude-handoff --name "login bug"  # newest session whose title/prompt matches
claude-handoff "login bug"         # same — a non-path argument is a name search
claude-handoff --grep "CORS"       # newest session that *talked about* CORS
claude-handoff --fit 32k           # handoff sized to fit a 32k-token context
claude-handoff --project myrepo    # latest session of a specific project
claude-handoff path/to/session.jsonl -o -     # explicit file → stdout
claude-handoff -o clipboard        # straight to the clipboard — go paste it
claude-handoff --last 5            # only the last 5 user turns
claude-handoff --since 2h          # only the last 2 hours of the session
claude-handoff --include-tools     # keep collapsed per-tool-call detail
claude-handoff --include-sidechains  # append full subagent transcripts
chf --last 5                       # `chf` = short alias, same tool

# claude.ai AND ChatGPT web chats too (each app's data export):
claude-handoff conversations.json --list
claude-handoff conversations.json --name "webhook bug"

# whole project in one handoff, oldest → newest:
claude-handoff --project myrepo --merge

# machine-readable:
claude-handoff --format json -o session.json
claude-handoff --list --format json   # session list as JSON

# shareable in public (issues, forums): ~ paths, no emails/IPs/username
claude-handoff --anonymize

# auto-handoff: write one for every session when it ends
claude-handoff --install-hook

# project memory: distill ALL sessions into one brief, inject at start
chf --brief --llm claude-cli       # build/refresh ~/.claude/briefs/<project>.md
chf --install-brief-hook           # every new session starts with it

# real LLM summary (goal / decisions / current state / next steps):
claude-handoff --llm claude-cli                 # uses your Claude Code login — no API key
claude-handoff --llm ollama                     # local model — fully offline
export ANTHROPIC_API_KEY=sk-...
claude-handoff --llm claude
claude-handoff --llm openai --model gpt-4o
claude-handoff --llm gemini --with-transcript   # summary + cleaned transcript

Then paste handoff.md into any other model. The document opens with instructions to the receiving assistant, so no extra prompting is needed.

Project memory (--brief)

Claude Code forgets everything between sessions — but the whole history is on your disk. chf --brief reads every session of the current project and writes one memory document: a factual session timeline + most-touched files (deterministic, free), and with --llm a distilled memory — decisions with their why, fixed bugs, conventions, open threads — every bullet cited with the session id it came from. Per-session notes are cached, so refreshing after new sessions only pays for the new ones. chf --install-brief-hook installs two hooks: SessionStart injects the brief as context (Claude starts already knowing your project), SessionEnd auto-refreshes the factual part — free, no LLM ever runs from a hook. The distilled part refreshes only when you say so; the brief carries a freshness stamp, and both the file and the injection warn when newer sessions exist. Fully local; redaction applies as everywhere.

Auto-selection skips nearly-empty sessions (like the stub claude /login leaves behind) so "latest" means your latest real conversation. An explicit path or --name always wins.

What the output looks like

# Conversation handoff

> To the receiving assistant: … you are taking over …

## Session
- Project: /home/you/myapp (branch main)
- When: 2026-08-20 09:00 → 09:04
- Activity: 2 user messages, 4 assistant replies, 4 tool calls

## Files created / modified
- /home/you/myapp/auth.py

## Commands run
- python -m pytest tests/test_auth.py -q

_🤖 2 subagent(s) contributed to the work above (--include-sidechains for their transcripts)._

## Conversation
### 🧑 User
the login breaks on unicode passwords…
### 🤖 Assistant
Found it — ascii encoding. Changed to utf-8, tests pass.

Flags

Flag Meaning
--list list sessions (date, size, project, title · first prompt); with a conversations.json, list its chats
--name QUERY pick newest session (or web conversation) whose title/first prompt contains QUERY
--project NAME pick latest session whose project path contains NAME
--last N / --since 2h keep only the tail of the conversation (N user turns / a time window)
--merge merge every session in scope into ONE handoff (session-break markers, summed activity)
--format md|json markdown (default) or machine-readable JSON — also applies to --list
-o clipboard copy the handoff straight to the clipboard
--include-sidechains append full subagent transcripts (inline sidechains and <session-id>/subagents/agent-*.jsonl); their file/command activity is always counted
-i / --interactive pick session(s) from a numbered list — 1,3 or 2-4 merges several into one handoff
--brief distill the project's whole history into ~/.claude/briefs/<project>.md (deterministic; --llm for real distillation)
--install-brief-hook / --uninstall-brief-hook inject the brief as context into every new session (SessionStart hook)
--install-hook / --uninstall-hook auto-write a handoff to ~/.claude/handoffs/ when each session ends
--completions bash|zsh print a tab-completion snippet
--mcp run as an MCP server over stdio
--allow-llm with --mcp: let the handoff tool run LLM summaries (explicit opt-in)
-o FILE / -o - output file / stdout (default handoff.md)
--include-tools collapsed <details> blocks with each tool call
--max-chars N cap the transcript section (default 80 000; keeps start + recent end)
--any ignore the current directory; consider every project's sessions
--llm claude|openai|gemini|claude-cli LLM summary instead of raw cleaned transcript
--model ID override the LLM model
--focus TEXT extra instructions for the summary (e.g. --focus "emphasize the API decisions")
--with-transcript with --llm, also append the cleaned transcript
--grep TEXT pick newest session whose conversation contains TEXT; with --list/-i shows every match with a 🔍 preview
--fit TOKENS size the deterministic handoff to a token budget (32k, 128k, 1m) by tightening transcript truncation
--anonymize strip identity for public sharing: home paths → ~, emails/IPs/username → placeholders
--no-redact keep secret-looking strings (default: redacted from every output, LLM or not)
--no-cache disable the chunk-note cache (~/.cache/claude-handoff)

API keys (first set variable wins per provider):

Provider Env vars Notes
claude ANTHROPIC_API_KEY or CLAUDE_API Anthropic API
openai OPENAI_API_KEY or GPT_API OpenAI API
gemini GEMINI_API_KEY, GOOGLE_API_KEY or GEMINI_API Google AI API
claude-cli (none) Shells out to your installed Claude Code CLI; billed to your Pro/Max plan. Run claude once to log in.
ollama (none — local) Local Ollama server: fully offline, nothing leaves your machine. OLLAMA_MODEL / OLLAMA_BASE_URL to configure.

Nothing is sent anywhere unless you pass --llm.

Where does it look? Sessions live in Claude Code's global store (~/.claude/projects), so you can run claude-handoff from anywhere. If your current directory is a project (or a subfolder of one), it scopes to that project's sessions; a parent "master folder" scopes to every project under it; --any ignores the directory entirely.

Big sessions & privacy. Transcripts beyond one pass (~400k chars) are summarized map-reduce style: notes per chunk, then one synthesis — nothing is silently dropped, and finished chunks are cached in ~/.cache/claude-handoff so an interrupted run resumes for free. Secret-looking strings (API keys, tokens, password=…) are redacted from every output — what goes to an LLM and the handoff document itself. In a terminal you get a live progress bar with elapsed time and an ETA:

[█████████░░░░░░░░░░░░░░░] 3/9 chunks | 4m12s elapsed | ~8m left | summarizing part 4/8 (199,867 chars)…

Sessions with API usage data also get a Tokens line in the header (input incl. cache / output). Map-reduce chunks run 4-way parallel on API providers (claude/openai/gemini); claude-cli and ollama stay sequential by design.

Config (optional)

Put defaults you always use in ~/.config/claude-handoff/config.json (CLI flags always win; CLAUDE_HANDOFF_CONFIG overrides the path):

{ "llm": "claude-cli", "fit": "32k", "include_tools": true }

Allowed keys: llm, model, fit, output, include_tools, include_sidechains, max_chars, anonymize, focus. Security switches (no_redact) are deliberately not configurable — weakening redaction must be an explicit per-run choice. A broken config warns and is ignored, never fatal.

MCP server

Any MCP client (Claude Desktop, Claude Code, …) can pull handoffs directly:

claude mcp add claude-handoff -- claude-handoff --mcp

Tools: list_sessions (what's on this machine) and handoff (build the document for a session by name/project/path; pass anonymize for a shareable version). Deterministic by default — an MCP client can only trigger LLM summaries when you start the server with --allow-llm.

Roadmap

  • Gemini exports as input (Google Takeout ships HTML only — bring a real, redacted export to build against)
  • Session chains: auto-detect /compact-continued sessions and offer to merge the lineage (--follow)
  • Opt-in LLM summaries over MCP (--mcp --allow-llm)

PRs welcome.

How it compares

This space isn't empty — it's fragmented. Pick the tool that matches your situation:

  • Exportersclaude-conversation-extractor, claude-code-log, claude-code-transcripts, claude-to-markdown — turn transcripts into readable Markdown/HTML, tool noise included, no handoff framing.
  • Cross-CLI session moverscli-continues (npm i -g continues) reads 16 coding CLIs' native session stores (Claude Code included) and injects a context doc into another terminal tool. Excellent for Claude Code → Codex/Cursor/Gemini CLI; but it can't target web chats, does no LLM summarization, and needs Node 22.5+.
  • In-session handoff skills/pluginsthepushkarp/handoff, claude-session-handoff, claude-code-handoff — great if you remember to run them before the session ends; the model writes the summary using your session's context, and the output targets the next Claude session.
  • Browser extensions — Handoff, LLM Context Bridge, ContextSwitch — transfer web chats between ChatGPT/Claude/Gemini; they can't see Claude Code sessions.

claude-handoff is the post-hoc, paste-anywhere corner of this map: it works on the JSONL after the fact — old sessions, crashed sessions, sessions that hit the usage limit — needs nothing installed in advance, costs zero tokens by default, can write a real summary when you ask for one (--llm), and produces a document any receiving model can pick up, including claude.ai, ChatGPT and Gemini in the browser or on your phone.

Docs

INDEX.md — file map · docs/DEVELOPMENT.md — architecture, JSONL schema notes, design decisions · AGENTS.md — instructions for AI coding agents · CONTRIBUTING.md · CHANGELOG.md

License

MIT


mcp-name: io.github.Vasilispapg/claude-handoff

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

claude_handoff-0.13.0.tar.gz (63.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

claude_handoff-0.13.0-py3-none-any.whl (51.5 kB view details)

Uploaded Python 3

File details

Details for the file claude_handoff-0.13.0.tar.gz.

File metadata

  • Download URL: claude_handoff-0.13.0.tar.gz
  • Upload date:
  • Size: 63.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for claude_handoff-0.13.0.tar.gz
Algorithm Hash digest
SHA256 857fb68ad9710e6772c28e0677394132fcbb5e2267b3ede68e90782757bcdb77
MD5 c16bfbd1f693074255e32e1b911a6a7d
BLAKE2b-256 0d36a9f669356525f23bc7d109aa999cbbc333544f3b75693e09d581d06a895a

See more details on using hashes here.

Provenance

The following attestation bundles were made for claude_handoff-0.13.0.tar.gz:

Publisher: publish.yml on Vasilispapg/claude-handoff

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file claude_handoff-0.13.0-py3-none-any.whl.

File metadata

  • Download URL: claude_handoff-0.13.0-py3-none-any.whl
  • Upload date:
  • Size: 51.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for claude_handoff-0.13.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2ae313e15039e0769651b1fe57a2e0e375f858c3e45e7f9014f46444fd856a1f
MD5 baba3ae285b1fcc5ef11ee1d993866fd
BLAKE2b-256 583c5757e90d74ff1a986f4de64e0a8789a6db10d8c76d0566e9f10216cfad99

See more details on using hashes here.

Provenance

The following attestation bundles were made for claude_handoff-0.13.0-py3-none-any.whl:

Publisher: publish.yml on Vasilispapg/claude-handoff

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.19.0

2 files

0.18.0

2 files

0.17.0

2 files

0.16.0

2 files

0.15.0

2 files

0.14.0

2 files

This release

0.13.0 This release

2 files

0.12.0

2 files

0.11.0

2 files

0.10.0

2 files

0.9.0

2 files

0.8.1

2 files

0.8.0

2 files

0.7.0

2 files

0.6.1

2 files

0.6.0

2 files

0.5.0

2 files

0.4.0

2 files

0.3.1

2 files

0.3.0

2 files

0.2.0

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page