Session continuity — without the copy-paste.
Save the directive of a wrap-up session and surface it at the start of the next one. Replaces the manual "paste a prompt into the new window" workflow with an auto-save + confirm-on-resume loop.
claude plugin install handoff@apurvbazari-plugins
That's it. The SessionStart hook activates automatically — no setup step required.
The save → resume loop
One save, one active slot, one confirmed resume. Click a stage to see detail in the side panel.
Wrap-up phrases like "pick this up later", "continue in new session", or "save handoff" auto-invoke /handoff:save. It presents a confirm before writing — false positives cost one click. The SessionStart hook on the next session reads active.md, runs stale and snooze checks, and routes Claude to the four-option pickup flow. Handoff never auto-executes a directive — you always confirm.
Trust model
The SessionStart hook wraps directive content in <untrusted-source> tags. Routing instructions and metadata are trusted; the saved directive is data you review, not commands Claude runs automatically. The four-option AskUserQuestion ensures confirmation before any action.
Cowork
The SessionStart hook is CLI-only — plugin-scoped hooks don't fire in Claude Cowork (it loads user-scoped settings only). A saved handoff won't surface automatically there; run /handoff:pickup manually to resume.
Four skills — save, pickup, check, discard
All under /handoff:<name>. save and pickup auto-invoke on intent; check auto-invokes but is read-only; discard is destructive and user-invoked only.
| Skill | Invocation | What it does |
|---|---|---|
| /handoff:save | auto | Auto-invokes on a wrap-up phrase. Drafts a directive (next step, pointers, constraints, open questions) and confirms via AskUserQuestion before writing. Captures saved-at, saved-at-sha, saved-at-branch, saved-from-cwd frontmatter. |
| /handoff:pickup | auto — after hook | Auto-invokes after the hook surfaces a handoff. Re-reads from disk (surfaced content is treated as stale), runs cwd guard, then presents four options via AskUserQuestion: Execute / Edit / Discard / Save for later. |
| /handoff:check | auto · read-only | Reports whether an active handoff exists, its age, saved-at SHA and branch, how far the repo has moved past the saved SHA, and snooze status. Useful for "why isn't this surfacing?". |
| /handoff:discard | user-only | Archives the active handoff without acting — same effect as Discard in pickup, invocable from anywhere. disable-model-invocation prevents accidental auto-invocation. |
| Option | Effect |
|---|---|
| Execute | Act on the directive in this session (guidance for judgement, not commands to run), then archive to archive/consumed-<ts>.md. |
| Edit | Open active.md in $EDITOR, revise the directive, then re-present for another confirm round-trip. |
| Discard | Archive to archive/discarded-<ts>.md without acting. Use when the saved work is no longer relevant. |
| Save for later | Leave in place, snooze for 24h (configurable) so the next session start doesn't re-surface immediately. |
Optional settings.md — defaults work out of the box
Configuration is optional. Create .claude/handoff/settings.md to override any threshold. Settings are read as frontmatter by the hook and pickup skill on every invocation.
Zero-config default
Install and walk away. The built-in defaults (90-day stale threshold, 24-hour snooze, archive retention cap of 10) work for most workflows. Override only what you want to tune.
---
stale-commit-threshold: 3
stale-day-threshold: 90
deferral-snooze-hours: 24
gitignore-prompt: ask
archive-retention: 10
trigger-phrases: []
---
| Key | Default | Effect |
|---|---|---|
| stale-commit-threshold | 3 | Commits past saved-at-sha before the handoff is tagged "progress made" — a surface signal, not a behavior change. |
| stale-day-threshold | 90 | Days past saved-at before the hook silently auto-archives to expired-<ts>.md. |
| deferral-snooze-hours | 24 | Hours to suppress re-surface after "Save for later". No permanent suppression — re-surfaces after the window. |
| gitignore-prompt | ask | ask or never — whether the first save offers to add .claude/handoff/ to .gitignore. |
| archive-retention | 10 | Cap on archive file count (shared across consumed/discarded/expired). 0, unlimited, or -1 for no cap. |
| trigger-phrases | (built-in list) | Additions or overrides for the wrap-up phrase NL trigger list that auto-invokes /handoff:save. |
One active slot, a timestamped archive
Everything lives under .claude/handoff/. A single active file, an archive directory for consumed/discarded/expired handoffs, and an optional settings file. The first save offers to add the folder to .gitignore.
Single slot per repo. Frontmatter holds saved-at, saved-at-sha, saved-at-branch, saved-from-cwd. An existing handoff prompts before overwrite.
The directive that was acted on, renamed (not deleted) — stays recoverable if you need to look back.
Archived without acting — from the pickup flow or /handoff:discard. No action taken on the directive.
Moved here automatically once older than stale-day-threshold (default 90 days). No user action required.
Override thresholds, snooze window, gitignore behavior, archive retention, and trigger phrases. Edit directly — no re-install needed.