/handoff writes a handoff note so a fresh Claude session can continue the work.

A Claude Code mod that adds /handoff. It writes a handoff note so a fresh Claude session can pick up the work.
It gathers the git branch, the last 10 commits, git status --short and the open PR (via gh, if any), then asks the model to write the note from those facts plus the current conversation. The note has a # Handoff: <topic>, <date> title and Done / Next / Gotchas sections.
The note is saved to <git root>/.claude/handoffs/<YYYY-MM-DD>-<branch>.md. The command prints the path and the line to start the next session with: read <path>.
/plugin install handoff --marketplace AliakbarNateghi/cc-handoff
Answer y to add the marketplace, then pick a scope.
dir: the folder for notes, relative to the git root. Default: .claude/handoffs.hooks/register.ts 44 lines1import type { Register } from 'claude-code'
2
3export const register: Register = (on, options) => {
4 on('session.start', async ($, e, next) => {
5 await $.command.register({ name: 'handoff', description: 'Write a handoff note for a fresh session.' })
6 return next(e)
7 })
8
9 on('command.run', { command: 'handoff' }, async $ => {
10 const sh = async (...argv: string[]) => {
11 const r = await $.process.run(argv).catch(() => undefined)
12 return r && r.exitCode === 0 ? r.stdout.trim() : ''
13 }
14 const root = await sh('git', 'rev-parse', '--show-toplevel')
15 if (!root) return { text: 'handoff: not inside a git repo.' }
16
17 const branch = (await sh('git', 'branch', '--show-current')) || 'detached'
18 const facts = [
19 `Branch: ${branch}`,
20 `git log --oneline -10:\n${await sh('git', 'log', '--oneline', '-10')}`,
21 `git status --short:\n${(await sh('git', 'status', '--short')) || '(clean)'}`,
22 `Open PR: ${(await sh('gh', 'pr', 'view', '--json', 'number,title,url')) || 'none'}`,
23 ].join('\n\n')
24 const d = new Date()
25 const date = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
26
27 const ask =
28 `Write a handoff note so a fresh Claude session can continue this work. ` +
29 `Format: first line "# Handoff: <topic>, ${date}", then sections "## Done" ` +
30 `(include PR numbers and merge commits where known), "## Next", and "## Gotchas". ` +
31 `Be concise and concrete. Output only the markdown.\n\nRepo facts:\n${facts}`
32
33 // fork reads the session's own conversation; before the first reply there is none.
34 let r = await $.model.fork({ prompt: ask })
35 if (!r.isAnswered && r.reason === 'nothing-to-fork')
36 r = await $.model.complete({ model: await $.session.model(), prompt: ask })
37 if (!r.isAnswered) return { text: `handoff: model gave no note (${r.reason}).` }
38
39 const path = `${root}/${options.dir}/${date}-${branch.replace(/[^\w.-]+/g, '-')}.md`
40 await $.fs.write(path, r.text.trim() + '\n')
41 return { text: `Handoff saved: ${path}\nstart the next session with: read ${path}` }
42 })
43}
44