@softeneers/handoff
v1.0.0
Published
Write a structured handoff.md before /clear so a fresh AI agent resumes losslessly — a reflective context handoff, deeper than a transcript export.
Downloads
80
Maintainers
Readme
skill-handoff — skill specification (AI-optimized)
Source of truth for this skill. This
.mdis written for AI agents (dense, machine-parseable). The human view isREADME.html, generated from this file. Per toolkit convention:.md= AI optimization,.html= human view.
skill-handoff makes the agent reflect over the whole session and write one handoff.md so a
fresh agent (Claude or any other LLM) can resume the work losslessly when told to read it. You run
it before /clear — the moment that otherwise wipes all context. The output is not a transcript:
it is a structured, reflective, index+delta artifact — goal, state, the next exact move, and the
why behind every decision — written so the next agent starts producing in minutes, not re-deriving.
- Status: built (self-test passing).
- Targets: Claude Code · Codex · Cursor (one source of truth, three shells).
- Portable by design: the scripts self-locate (
import.meta.url) and operate under--root(default cwd). The context probe is read-only; the only write ishandoff.md.
1. The problem it solves (and how it beats /export)
/clear is destructive: the next agent starts blind. Claude Code's /export only dumps the raw
transcript — every dead end, every tangent, in order, with no judgment. A fresh agent then has to
re-read and re-derive the actual state, and a transcript silently leaks secrets that scrolled past.
skill-handoff goes deeper than a dump. It forces the agent to do the reflection a human lead would do at a handover:
| | /export (raw dump) | /handoff (this skill) |
|---|---|---|
| Form | Full transcript, chronological | Structured artifact, front-loaded by importance |
| Content | Everything, unweighted | Index + delta: what matters now, what changed, what's next |
| The why | Lost in chatter | Captured explicitly — decisions + rejected alternatives |
| Dead ends | Repeated verbatim | Distilled into "failed approaches" so they aren't re-tried |
| Next step | Reader infers it | One primary directive, file-anchored, first |
| Secrets | Leak whatever scrolled by | Never included (explicit redaction rule) |
| Size | Grows with the session | Bounded; references artifacts by path, never pastes them |
One line:
/exportanswers "what was said?";/handoffanswers "where are we, why, and what's the next move?" — the only thing a resuming agent actually needs.
2. What it produces — one handoff.md (canonical section schema)
Exactly one file: handoff.md at --root (default cwd). It uses these EXACT H2 titles,
in this order, formatted ## N. Title. This schema is the single source of truth; the linter
enforces it.
| # | Section | Status | Captures | |---|---|---|---| | 1 | Metadata & how to resume | REQUIRED | timestamp, model, repo path, git branch + last commit; a 2-line "you are a fresh agent" note pointing to Next actions. | | 2 | Objective | REQUIRED | the north-star goal + why it matters (one paragraph). | | 3 | Current state | REQUIRED | four buckets — Done / In-progress (note exactly where it stopped) / Blocked / Not started. | | 4 | Next actions | REQUIRED | exact, ordered, file-anchored steps; one primary directive first. | | 5 | Decisions & rationale | REQUIRED | each decision with the WHY + rejected alternatives; constraints preserved verbatim. | | 6 | Failed approaches | RECOMMENDED | what was tried and why it failed (stops re-attempts). | | 7 | Constraints & do-not-touch | optional | guardrails, invariants, naming conventions to preserve. | | 8 | File & artifact map | optional | key paths + one-line purpose; reference commits/PRs/ADRs by path/URL, don't paste. | | 9 | Open questions & blockers | optional | unresolved items + what unblocks each. | | 10 | Environment & verify | REQUIRED | install/run; build + test status (pass/fail counts); env gotchas; uncommitted-changes summary. | | 11 | Glossary | optional | project-specific terms. | | 12 | Confidence & freshness | optional | per-section HIGH/MEDIUM/LOW; flag anything likely stale. |
Authoring rules (baked into the prompt and checked by the linter): front-load goal/state/next-action;
non-duplication (reference artifacts by path, never paste them); always capture the WHY including
rejected alternatives; next steps must be specific and file-anchored; include a verification baseline;
tag confidence; NEVER include secrets/credentials; NEVER leave [TODO] placeholders.
See a worked artifact at example-output/handoff.md.
3. How it's invoked & the typical flow
/handoff [focus][focus]is optional — a hint that biases the reflection (e.g./handoff auth-refactorweights the state/next-actions toward that thread). With no focus, the whole session is summarized.- Claude Code:
/handoff [focus](Skill tool). - Codex: read
AGENTS.md→ run the bundled scripts. - Cursor: rule
handoff.mdc→ same scripts.
Typical flow
# 1. You're about to /clear. First capture the session.
/handoff # reflect over the session → write ./handoff.md
# (the skill probes git/env read-only, scaffolds the schema, fills it by reflection, then lints)
/clear # now safe to wipe context
# 2. Later — fresh agent, empty context:
# "Read handoff.md and resume." → agent reads top-to-bottom, starts at "Next actions".The artifact is the contract between the two agents: the outgoing one writes it; the incoming one treats Section 1's note as its boot instruction and Section 4 as its first task.
4. How the skill makes the agent REFLECT (not dump)
The skill's prompt is the product. It explicitly redirects the agent away from transcription:
- Reflect, don't transcribe. "Do not replay the conversation. Decide what the next agent needs to act, and write only that." Chronology is discarded; relevance ordering replaces it.
- Index + delta, not full text. Summarize and link to artifacts (files, commits, PRs) by path; pasting their contents is a lint failure. The handoff is a map, not the territory.
- Surface the WHY. For every non-obvious choice, record the rationale and the alternatives that were rejected — this is the information a transcript loses and a resuming agent most needs.
- Distill dead ends. Collapse every failed attempt into one "why it failed" line so the next agent doesn't burn turns re-attempting it.
- One primary directive. Force a single, concrete, file-anchored first action before any list — the reflective act of deciding what truly comes next.
- State the truth about state. Current state is split Done / In-progress / Blocked / Not started, and In-progress must note exactly where it stopped (file, function, line of thought).
- Self-audit confidence. The agent tags each section HIGH/MEDIUM/LOW and flags likely-stale facts, forcing it to distinguish what it knows from what it assumes.
5. The bundled scripts
All under scripts/ — zero-dependency Node ≥18, deterministic, read-only except the
single handoff.md write.
| Script | Writes? | What it does |
|---|---|---|
| scripts/gen-context.mjs | no | Read-only context probe. Gathers the deterministic facts Section 1/10 need — repo path, git branch, last commit, dirty/uncommitted summary, Node/OS, detected build & test commands — and prints them as JSON for the agent to fold into the reflection. Never executes builds; never reads secrets. |
| scripts/new-handoff.mjs | yes (scaffold) | Scaffolds handoff.md with all 12 canonical H2 headings in order, REQUIRED sections marked, and the resume note pre-seeded. Refuses to clobber an existing file unless --force. This is the only writer. |
| scripts/lint-handoff.mjs | no | Validates a handoff.md. Checks: all required sections present and correctly titled/ordered; no [TODO]/placeholder text; no secret-shaped strings (API keys, tokens, password=…, private-key headers); Next actions non-empty with a primary directive; confidence tags present. Exits non-zero on violations with line-anchored messages. |
| scripts/install.mjs | yes (gated by --write) | Installs the skill into a target home: SKILL.md + README.md + scripts/** → <home>/.claude/skills/handoff/, and cross-tool/handoff.mdc → <home>/.cursor/rules/handoff.mdc. Self-locating (import.meta.url); dry-run by default, --write to apply, --home <dir> for testing; idempotent; overwrites backed up under <home>/.claude/.handoff-backup/. |
| scripts/self-test.mjs | no (temp dir) | Offline self-test gate. Runs gen-context against a fixture repo, scaffolds into a temp dir, asserts the 12 headings exist in order, lints the committed example-output/handoff.md (must pass), feeds a deliberately broken file (missing section + a fake secret + a [TODO]) and asserts the linter fails on each, and confirms determinism (two runs byte-identical). Exits 0 on success. |
Pipeline: gen-context (facts) → new-handoff (skeleton) → agent reflection fills it → lint-handoff
(gate) → file is ready for /clear.
6. Determinism & safety
- Read-only by default.
gen-contextandlint-handoffnever write. The only write in the whole skill ishandoff.md(vianew-handoff, or the agent saving its filled draft). - No clobber.
new-handoffrefuses to overwrite an existinghandoff.mdwithout--force. - Secrets never leave. The reflection prompt forbids secrets; the linter independently scans for secret-shaped strings and fails the handoff if any appear. Defense in depth.
- Deterministic. Stable sort, no
Math.random. The probe's timestamp lives only in Section 1 metadata (a fact about when the handoff was taken) and never inside scored/lint payloads; two runs on an unchanged repo produce byte-identical scaffolds and identical lint output. - Zero dependencies. Node ≥18 only; nothing to install.
- Non-destructive probe.
gen-contextreads git plumbing and the filesystem; it never runs the project's build or tests (it only detects and reports the commands).
7. Acceptance criteria
- [ ]
/handoffproduces a singlehandoff.mdwith all 12 canonical H2 titles in exact order and format. - [ ] All REQUIRED sections (1, 2, 3, 4, 5, 10) are present and non-empty.
- [ ] Section 1 carries timestamp, model, repo path, git branch + last commit, and the 2-line fresh-agent note.
- [ ] Section 4 leads with exactly one primary directive and every step is file-anchored.
- [ ] Section 5 records each decision's WHY and its rejected alternatives.
- [ ] No artifact contents are pasted — commits/PRs/ADRs/files are referenced by path/URL only.
- [ ]
lint-handoff.mjspasses onexample-output/handoff.mdand fails on missing sections,[TODO]s, or secret-shaped strings. - [ ]
gen-context.mjsis read-only and emits valid JSON of the deterministic facts. - [ ]
self-test.mjspasses offline and deterministically (byte-identical reruns).
8. Build status
- ✅ This spec + the functionality docs + human
README.html. - ✅ Scripts:
scripts/—gen-context.mjs(read-only probe),new-handoff.mjs(scaffolder, no-clobber),lint-handoff.mjs(schema + secret + placeholder gate),self-test.mjs. - ✅ Cross-tool shells (source):
SKILL.md(Claude) ·cross-tool/handoff.mdc(Cursor) ·cross-tool/AGENTS.snippet.md(Codex).scripts/install.mjscopies them into<home>/.claude/skills/handoff/and<home>/.cursor/rules/handoff.mdc. - ✅ Self-test: scaffolds into a temp dir, asserts the 12 ordered headings, lints the example
(pass) and a broken fixture (fail on each violation), verifies byte-identical reruns, and covers install (dry-run writes nothing ·
--writelands all shells+scripts · re-install idempotent). PASSED (8/8). - ✅ Example output: committed at
example-output/handoff.md.
How to run
node scripts/gen-context.mjs --root . # read-only facts (JSON) for Sections 1 & 10
node scripts/new-handoff.mjs --root . # scaffold ./handoff.md (refuses to clobber)
node scripts/lint-handoff.mjs ./handoff.md # validate against the schema (gate before /clear)
node scripts/self-test.mjs # offline self-test gate (8/8)9. Install — wire the skill into your agents
The source shells live in this folder (SKILL.md, cross-tool/handoff.mdc,
cross-tool/AGENTS.snippet.md). scripts/install.mjs copies them — plus the
scripts and README — into your agent config so /handoff is available in every project.
node scripts/install.mjs # preview the plan (DRY-RUN — writes nothing)
node scripts/install.mjs --write # apply: install into $HOME/.claude + $HOME/.cursor
node scripts/install.mjs --home /tmp/h --write # install into a chosen home (used by the self-test)
# flags: --home <dir> · --write · --no-backupIt maps (one source → three agents):
| Source | Destination |
|---|---|
| SKILL.md | <home>/.claude/skills/handoff/SKILL.md (Claude Code) |
| README.md | <home>/.claude/skills/handoff/README.md (reference) |
| scripts/** | <home>/.claude/skills/handoff/scripts/** (impl travels with the skill) |
| cross-tool/handoff.mdc | <home>/.cursor/rules/handoff.mdc (Cursor) |
| cross-tool/AGENTS.snippet.md | merge into your AGENTS.md by hand (Codex) |
Dry-run by default (preview before any write), idempotent (re-running an unchanged install is a
no-op), and backed up (overwrites saved under <home>/.claude/.handoff-backup/<run>/). After a
--write, restart the agent (or rely on live skill detection) and /handoff is ready.
References
functionalities/— per-functionality detail on the uniform repo schema.example-output/handoff.md— a worked handoff artifact.SKILL.md(.claude/skills/handoff/SKILL.md) — the Claude shell (frontmatter name + description).cross-tool/— the shared shells (ClaudeSKILL.md+ Cursor.mdc), one source, three tools.
