From claude-handoff
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.
How this skill is triggered — by the user, by Claude, or both
Slash command
/claude-handoff:handoffThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are executing the handoff skill. This is the **only** entry point for
You are executing the handoff skill. This is the only entry point for handoff — nothing triggers automatically. Follow these steps precisely.
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.
Layer these sources (each optional):
You have the full conversation in context. Synthesize:
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.
Skim CLAUDE.md, AGENTS.md, README.md at project root or parents.
Extract what's relevant to the handoff.
If git is available:
git status --short 2>/dev/null
git log --oneline -5 2>/dev/null
git branch --show-current 2>/dev/null
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.
# Pre-flight: bail out gracefully if gh is unavailable or unauthenticated.
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
# Project number: configurable via $HANDOFF_PROJECT_NUM. No per-repo
# auto-fallback — the skill stays agnostic about which GitHub Project
# belongs to which repo. Callers that want Project integration (e.g.
# Mercury with Project #3) set the env var in their shell profile or
# per-session before invoking /handoff.
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):
.mercury/docs/EXECUTION-PLAN.md (or equivalent)Pick one primary task + one secondary fallback. Never produce a menu.
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.
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.
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:
run_in_background tasks, builds, spawned
subprocesses have completed OR the user has explicitly accepted they
continue after handoff.If any item is incomplete, finish or defer explicitly. Surface status: "All pending work done — ready to hand off?"
Always do both of these — never skip either:
/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).
/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.
If the user returns after manual mode and says "launch it now", re-enter the auto path from Step 5 (auto mode).
:auto (or legacy auto) token
triggers the claude CLI launch.$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).Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub 392fyc/claude-handoff --plugin claude-handoff