| name | handoff |
| description | Generate a structured handoff document and ready-to-paste starting prompt for the next session. Use `/handoff` for manual mode (output only). Use `/handoff:auto` to auto-launch the new session via `claude` CLI after the document is written. |
| argument-hint | [:auto] [optional extra instructions for the next session] |
| user-invocable | true |
| allowed-tools | ["Read","Write","Bash","Glob","Grep"] |
/handoff โ Session Handoff & Continuation
You are executing the handoff skill. This is the only entry point for
handoff โ nothing triggers automatically. Follow these steps precisely.
Invocation modes
Parse $ARGUMENTS:
| Trigger | Mode | Behavior |
|---|
/handoff (no args) | manual | Write doc + output starting prompt in chat. Do NOT launch a new session. Old session stays alive by user choice. |
/handoff <instructions> | manual + extra | Same as manual; put <instructions> into the "User Instructions" section of the handoff doc. |
/handoff:auto | auto | Write doc + output starting prompt + auto-launch new session via claude CLI after Pre-Termination Checklist passes. Old session should /exit after โ auto mode treats the old session as a terminal event. |
/handoff:auto <instructions> | auto + extra | Same as auto, with extra instructions embedded. |
/handoff auto (legacy) | auto | Same as /handoff:auto. Accept both syntaxes. |
Default (no explicit auto): manual mode. Never auto-launch without an
explicit :auto / auto token.
Strict parsing: :auto must be either the first argument or part of
the command syntax (/handoff:auto). /handoff auto is accepted only
when auto is the sole argument or the first whitespace-delimited
token (i.e., /handoff auto <extra> is auto+extra, /handoff automatic
is manual with instructions "automatic"). This prevents accidental
auto-spawn from user instructions that happen to start with "auto".
Terminal-event semantics: "handoff is a terminal event for the old
session" only applies to auto mode โ the old session is expected to
/exit immediately after spawning the new one. In manual mode the
skill does NOT terminate the session; the user decides whether to /exit,
paste the prompt into a fresh session elsewhere, or keep working. Both are
valid; the skill itself writes the doc and outputs the prompt, nothing
else.
Step 1: Gather Context
Layer these sources (each optional):
Layer 1: Conversation context (always available)
You have the full conversation in context. Synthesize:
- What the user was working on
- Decisions made and their rationale
- Problems encountered and solutions found
- Incomplete work and known next steps
Layer 2: Memory search (agentic)
Search the project's auto-memory directory:
~/.claude/projects/<encoded_cwd>/memory/
Where <encoded_cwd> is the cwd with : \ / replaced by -, leading
- stripped.
Glob for *.md. Read previous handoffs, checkpoints, project memories.
Layer 3: Project documentation (if present)
Skim CLAUDE.md, AGENTS.md, README.md at project root or parents.
Extract what's relevant to the handoff.
Layer 4: Version control (optional)
If git is available:
git status --short 2>/dev/null
git log --oneline -5 2>/dev/null
git branch --show-current 2>/dev/null
Layer 5: GitHub Project / Issues (best-effort โ skip cleanly if unavailable)
If the project uses GitHub Issues + a GitHub Project (v2), query for next-task selection. This layer is best-effort: if gh is missing, unauthenticated, or the repo has no Project, skip this layer and fall back to Layers 1โ4 + .mercury/docs/EXECUTION-PLAN.md (or the repo's equivalent plan). Never let a Layer 5 failure block the handoff.
if ! command -v gh >/dev/null 2>&1 || ! gh auth status >/dev/null 2>&1; then
echo "INFO: gh CLI unavailable โ skipping Layer 5"
else
gh issue list --label "P0" --state open --json number,title,labels --limit 50 2>/dev/null || true
gh issue list --label "P1" --state open --json number,title,labels --limit 50 2>/dev/null || true
OWNER=$(gh repo view --json owner --jq '.owner.login' 2>/dev/null)
PROJ_NUM="${HANDOFF_PROJECT_NUM:-}"
if [ -n "$PROJ_NUM" ]; then
gh project item-list "$PROJ_NUM" --owner "$OWNER" --format json --limit 100 2>/dev/null | \
python -c "
import json, sys
try:
data = json.loads(sys.stdin.read() or '{}')
except json.JSONDecodeError:
sys.exit(0) # gh returned empty/invalid โ silently skip
items = [i for i in data.get('items', []) if i.get('status') in ('Todo', 'In Progress')]
status_order = {'In Progress': 0, 'Todo': 1}
for i in sorted(items, key=lambda x: (status_order.get(x.get('status', ''), 9), x.get('priority', 'P9'))):
num = i.get('content', {}).get('number', '?')
print(f'#{num} [{i.get(\"priority\",\"?\")}] {i.get(\"title\",\"?\")} ({i.get(\"status\",\"?\")})')
" 2>/dev/null || true
else
echo "INFO: HANDOFF_PROJECT_NUM not set โ skipping Project query (set it to enable)"
fi
fi
Selection criteria (in order):
- Actively blocked P1 bugs with known root cause
- In-Progress items from the Project board
- Highest-priority P0 Todo from Project board
- Next Phase sub-item per
.mercury/docs/EXECUTION-PLAN.md (or equivalent)
Pick one primary task + one secondary fallback. Never produce a menu.
Step 2: Generate Handoff Document
Write to:
~/.claude/projects/<encoded_cwd>/memory/session-handoff.md
---
name: session_handoff
description: "Session handoff โ <one-line summary>"
type: project
---
# Session Handoff โ <YYYY-MM-DD>
## Starting Prompt
่ฟๆฏ S{N+1}ใ<1-line context>ใ
### ๅฝๅ็ถๆ
<repo / branch / commit / clean or dirty>
### S{N+1} ไธปไปปๅก๏ผ<Issue #N โ specific title>
**่ๆฏ**๏ผ<1-2 lines why this is highest priority, cite Issue/Project>
**ๆง่กๆญฅ้ชค**๏ผ
1. <actionable step with file paths / commands>
2. <actionable step>
3. <verification>
4. <commit / PR>
**ๆฌก่ฆไปปๅก๏ผไธปไปปๅกๅฎๆๅ๏ผ**๏ผ<Issue #N or Phase X-Y, one line>
### ๅ่ๆๆกฃ
<only main-task-related docs>
## Task State
- **Primary Issue**: #N [title] (status)
- **Branch**: <branch>
- **Worktree**: <path, or omit if not using a git worktree>
- **Completed**: <commits + what they did>
- **In Progress**: <current step / blockers>
- **Pending**: <remaining items>
## Key Context (compact-loss protection)
- <architecture decisions not recoverable from code>
- <gotchas / constraints>
- <important file paths + roles>
## User Instructions
<If args passed in, embed here. Else "No additional instructions.">
CRITICAL RULE for Starting Prompt: one primary task with numbered
execution steps. Never a menu of options. The next session must be able to
start executing step 1 without asking for direction.
Step 3: Session-chain update (best-effort, optional)
Session-chain tracking is provided by the claude-handoff plugin (see
github.com/392fyc/claude-handoff). If the plugin's session_chain DB
exists, record this handoff edge:
python -c "
import os, sys
from pathlib import Path
# Plugin DB default location (claude-handoff plugin)
db_path = Path(os.environ.get('CLAUDE_HANDOFF_DB') or
Path.home() / '.claude' / 'handoff' / 'session_chain.db')
if not db_path.exists():
print('session_chain DB not found โ skipping (plugin not installed or scaffold-only)')
sys.exit(0)
# Defer actual writes to the plugin's session_chain package; do not duplicate
# schema logic here. If the package is importable, use it; else skip.
try:
from session_chain import SessionChainDB
except ImportError:
print('session_chain package not importable โ skipping (scaffold not wired yet)')
sys.exit(0)
db = SessionChainDB(db_path)
parent = os.environ.get('CLAUDE_SESSION_ID')
if not parent:
print('CLAUDE_SESSION_ID not set โ cannot record handoff edge')
sys.exit(0)
db.record_handoff(
chain_id=os.environ.get('CLAUDE_HANDOFF_CHAIN_ID') or parent,
parent_session_id=parent,
child_session_id=None, # bound later by child session's SessionStart hook
project_dir=os.getcwd(),
task_ref=os.environ.get('CLAUDE_HANDOFF_TASK_REF'),
worktree_path=os.environ.get('CLAUDE_HANDOFF_WORKTREE_PATH'), # optional: git worktree path
)
print('session_chain edge recorded (child pending)')
"
IMPORTANT: the AGENTKB-based orchestrator path ($AGENTKB_DIR/scripts/handoff-orchestrator.py)
is deprecated. Do not call it. The replacement is the claude-handoff
plugin's session_chain module (above), currently a scaffold โ write side
may not yet be wired at session-start.py.
Step 4: Pre-Termination Checklist
Before launching a new session (auto mode) OR outputting the prompt (manual
mode), verify all in-flight work has finished. A handoff is a
terminal event for the old session โ nothing carries over automatically.
Confirm each:
- No pending tool calls. All Bash / file / tool operations returned.
- No background processes.
run_in_background tasks, builds, spawned
subprocesses have completed OR the user has explicitly accepted they
continue after handoff.
- No unsaved state. Edits / commits / writes are actually on disk.
- No pending user questions. If the old session owes a reply, answer it.
If any item is incomplete, finish or defer explicitly. Surface status:
"All pending work done โ ready to hand off?"
Step 5: Output & Dispatch
Always do both of these โ never skip either:
- Output the Starting Prompt section directly in chat โ PRIMARY
artifact. User pastes it verbatim as the first message of a new session.
- Save the full handoff document to the memory path above. The auto-
memory system will load it at next session start.
Manual mode (/handoff default)
After Step 5.1 + 5.2, stop. Tell the user the old session stays alive;
they can copy the prompt to a new session manually or continue working in
this one. Do NOT spawn any new process.
Optional: offer to launch if the user later says so (Step 6).
Auto mode (/handoff:auto)
After Step 5.1 + 5.2, and Pre-Termination Checklist passed:
Required launch pattern โ use a SHORT reference prompt, never inline the
full handoff content into the command line. The SessionStart hook already
injects the full handoff document as additionalContext, so the new session
receives everything automatically. Inlining multi-line/multi-KB content
into wt/tmux/shell commands causes catastrophic failures on Windows
(multi-line expansion breaks argument parsing โ error 0x80070002, multiple
ghost terminal windows).
Short prompt construction: compute the handoff doc path (same path
written in Step 2), then build a one-liner:
HANDOFF_PATH=~/.claude/projects/<encoded_cwd>/memory/session-handoff.md
SHORT_PROMPT="Continue from session handoff. The SessionStart hook injects the full document. Fallback: read ${HANDOFF_PATH}"
Replace <encoded_cwd> using the same encode_project_path() logic from
Step 2 (: \ / โ -, strip leading -).
Optional flag propagation: if CLAUDE_HANDOFF_AUTO_LAUNCH_FLAGS is set
in the user's environment (e.g. via ~/.claude/settings.json env block),
its contents are injected into the launch command between claude and the
-- sentinel. This lets users propagate per-session flags like
--channels server:my-channel --dangerously-load-development-channels to
auto-spawned sessions without editing this skill. When the env var is
unset, expansion is empty and the command behaves as before.
Use the ${CLAUDE_HANDOFF_AUTO_LAUNCH_FLAGS:-} form so an unset variable
expands to nothing rather than a literal ${...}.
Windows (Windows Terminal, new tab):
wt -w 0 nt --title "Handoff" -d "<cwd>" -- claude ${CLAUDE_HANDOFF_AUTO_LAUNCH_FLAGS:-} -- "$SHORT_PROMPT"
macOS / Linux with tmux (real new window, detached from current TTY):
tmux new-window -n handoff "claude ${CLAUDE_HANDOFF_AUTO_LAUNCH_FLAGS:-} -- '$SHORT_PROMPT'"
macOS / Linux without tmux โ there is no portable "new terminal"
primitive. Use tmux new-session -d or the terminal emulator's own CLI
(e.g. osascript -e 'tell app "Terminal" to do script ...' on macOS,
gnome-terminal -- on Linux). Otherwise fall back to manual mode.
tmux new-session -d -s handoff "claude ${CLAUDE_HANDOFF_AUTO_LAUNCH_FLAGS:-} -- '$SHORT_PROMPT'"
The positional argument after -- is the session's first user message โ
documented at https://code.claude.com/docs/en/cli-reference. The --
sentinel ensures a prompt beginning with - is not parsed as a CLI option
(https://github.com/anthropics/claude-code/issues/3844). The
SessionStart hook will inject the full handoff document as
additionalContext, so the new session has everything โ the short prompt
is a bootstrap trigger, not the content carrier.
After spawning the new process, do NOT continue producing output in the old
session. The old session's job is done. Advise user to /exit (or close
tab) once they confirm the new session is running.
Step 6: Post-Dispatch (manual mode only, optional)
If the user returns after manual mode and says "launch it now", re-enter
the auto path from Step 5 (auto mode).
Rules
- Starting Prompt must be self-contained โ zero context assumed in the
new session.
- Include specific file paths, line numbers, commands.
- Never include secrets, API keys, credentials.
- The chat-output prompt is the PRIMARY deliverable โ never skip it.
- Do NOT add automatic hooks for SessionEnd or PreCompact โ handoff is
explicit only.
- Mode-scoped termination: auto mode treats handoff as a terminal
event for the old session (spawn new โ /exit old). Manual mode does
NOT terminate; the user decides. Never apply auto-mode termination to
a manual invocation.
- Before terminating (auto mode) verify all pending work has completed.
Nothing carries over automatically.
- Manual mode MUST NOT spawn processes. Only
:auto (or legacy auto) token
triggers the claude CLI launch.
- The legacy
$AGENTKB_DIR/scripts/handoff-orchestrator.py path is
DEPRECATED. Do not invoke it. The claude-handoff plugin is the
canonical session-continuity module
(https://github.com/392fyc/claude-handoff).