Hand off to a fresh session: /next:go writes a handoff note, clears the context, and resumes from the note.

A Claude Code plugin that hands a session off to a fresh one.
/next:go writes a short handoff note (goal, state, decisions, next task), clears the context, and starts a new session that reads the note and continues with the next task. You do not confirm anything.
/plugin marketplace add pablofontanilla/claude-next
/plugin install next@pablofontanilla
Then restart Claude Code.
Requirements:
/clear, but the automatic clear does not happen.python3 on your PATH./next:go
/next:go rerun the failing e2e with the new fixture
The optional text sets the next task.
~/.claude/next/<key>.md (one note per launch directory) and arms it with a 60-minute expiry./clear and then sends a prompt that starts with "Starting from a new session".SessionStart hook injects the note into the new session and moves it to <key>.consumed.md, so it fires only once.Nothing is written inside your repository.
Open an issue in this repository.
hooks/register.ts 60 lines1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4import type { Phase } from '../types'
5
6const SKILL = 'next:go'
7// The first line is for the person: the /clear itself prints nothing.
8const RESUME =
9 'Starting from a new session: the previous context was cleared.\n\n' +
10 'Start the "Next task" from the handoff note loaded at session start now. ' +
11 'Do not ask for confirmation first.'
12
13// `python3 ".../handoff.py" arm --project-dir ...`
14const ARM = /handoff\.py["']?\s+arm\b/
15const OK = /"status"\s*:\s*"ok"/
16
17const phase = atom({ plugin: 'next', key: 'phase' } as const, 'idle' as Phase)
18
19export const register: Register = on => {
20 on('command.run', { command: SKILL }, async ($, e, next) => {
21 await update($, phase, () => 'running')
22 return next(e)
23 })
24
25 on('tool.call', { tool: 'Bash' }, async ($, e, next) => {
26 const ran = await next(e)
27 if (!ARM.test(e.command) || (await read($, phase)) !== 'running') return ran
28
29 const isArmed = ran.deny === undefined && ran.isError !== true && OK.test(ran.text ?? '')
30 if (isArmed) await update($, phase, () => 'armed')
31
32 return ran
33 })
34
35 on('turn.complete', async ($, e, next) => {
36 const done = await next(e)
37 if (e.agentId !== undefined) return done
38
39 const now = await read($, phase)
40 if (now === 'idle') return done
41
42 await update($, phase, () => 'idle')
43 if (now !== 'armed' || e.reason !== 'answer') {
44 $.ui.toast('next: the handoff was not armed. The session stays as it is.')
45 return done
46 }
47
48 $.ui.toast('next: handoff armed. Clearing and resuming…')
49 // Queued: both run once the session is idle, after this turn ends.
50 void $.command
51 .run({ command: 'clear' })
52 .then(() => $.prompt.submit({ text: RESUME }))
53 .catch((err: unknown) => {
54 $.ui.toast(`next: clear or resume failed: ${String(err)}. Run /clear by hand.`)
55 })
56
57 return done
58 })
59}
60types/index.d.ts 10 lines1// idle: nothing pending. running: /next:go is in its turn.
2// armed: the skill's `handoff.py arm` call returned status ok this turn.
3export type Phase = 'idle' | 'running' | 'armed'
4
5declare module 'claude-code' {
6 interface PluginState {
7 next: { phase: Phase }
8 }
9}
10