dual-tool-handoff
Skill2 starsChanged 3 months agoMITView on GitHub
Opt-in only. Write, resume, or fetch/hydrate a named Cursor ↔ Claude Code handoff under `.scratch/handoffs/`. File first; else hydrate from the other tool's local session store (confirm required). Diff check warns if shipped ≠ tree — never blocks. Switch-only, not live sync. Do not run proactively.
In this repository2 other files it gives its agents.
- agent-designSkill
- render-checkSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
27 rules. About 0 are common, 16 are distinctive, and 11 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈16
Distinctive · estimate
This author's own choices. Worth a look.
11
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 17
warns: when they look misaligned — it never rewrites the story and never blocks the write.
- Very specific: line 21
Named: every handoff gets a short thread-summary name (e.g.
- Distinctive: line 23
Always ask which thread: before write or resume (unless the user already named one in the same message).
- Very specific: line 25
Switch-only: use this when leaving one tool for the other.
- Very specific: line 27
Not parallel sync: if two chats write at once, they will clobber LATEST.
- Distinctive: line 30
Fetch / hydrate: if the handoff file is missing (or the user force-refreshes), resolve a session from the other tool’s local store → …
- Distinctive: line 51
Before writing or reading, stop and ask which thread — unless the user’s message already includes a clear name (“hand off auth form UI” / …
- Very specific: line 61
Wait for the answer. Do not default to LATEST when more than one named file exists.
- Distinctive: line 63
If the folder is empty and this is a write: ask for a new thread name (you may suggest one from shipped/left, but the user confirms).
- Distinctive: line 65
If the folder is empty and this is a resume/fetch: continue to Fetch / hydrate (or say there is nothing to resume if they refuse hydrate).
- Distinctive: line 70
Run Pick the thread. Reuse an existing name to continue that thread.
- Distinctive: line 71
New: only for a new line of work.
- Very specific: line 74
Run the script with --name (fills Meta, Working tree, Diff check Tree+Paths):
- Distinctive: line 91
Read Tree and Paths.
- Very specific: line 105
Use --tool cursor or --tool claude-code (or other) for the tool you’re leaving.
- Very specific: line 107
If the script is unavailable, copy templates/LATEST.template.md → .scratch/handoffs/<slug>.md, fill it (include Name: and Diff check), then …
- Distinctive: line 125
Run Pick the thread (unless the user already named one).
- Very specific: line 126
If .scratch/handoffs/<slug>.md exists and the user did not use a force from Claude / from Cursor phrase → read that file (no store, no …
- Very specific: line 128
Only if there is exactly one named handoff and the user said a bare “resume from handoff” with no name: you may use that file or LATEST.md …
- Distinctive: line 131
If they named a thread that doesn’t exist as a file → go to Fetch / hydrate.
- Distinctive: line 132
Use branch, ticket/spec, shipped/left, Diff check (if warn, treat as advisory), resume one-liner.
- Distinctive: line 137
Use when the user asks to fetch / pull / re-hydrate, or resume a name with no handoff file.
- Very specific: line 175
Write with agent-handoff, passing --claude-session-id and/or --cursor-session-id.
- Very specific: line 186
Missing
--name: never write anonymous; ask. - Distinctive: line 193
Guessed store when tool unknown: ask.
- Distinctive: line 202
After hydrate: Meta store session id filled.
- Distinctive: line 206
No secrets / transcript bodies
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Dual-tool handoff — opt-in, named, switch-only
Chats do not sync across Cursor, Claude Code, Codex, etc. The repo file does. Off by default — only act when the user asks for a handoff / resume / fetch.
Diff ≠ why. Git supplies paths; the agent curates shipped / left. Diff check
warns when they look misaligned — it never rewrites the story and never blocks the write.
Contract
- Named: every handoff gets a short thread-summary name (e.g.
auth-form-ui,oss-skills-launch) — same string both tools use to resume. - Always ask which thread before write or resume (unless the user already named one in the same message).
- Switch-only: use this when leaving one tool for the other. It does not keep
two live sessions in sync. Last write to
LATEST.mdis “what I’m switching to now.” - Not parallel sync: if two chats write at once, they will clobber
LATEST; prefer one active switch at a time. Older named files remain for explicit resume. - Diff check: script fills Tree + Paths; agent fills Alignment + Note. warn ≠ block.
- Fetch / hydrate: if the handoff file is missing (or the user force-refreshes), resolve a session from the other tool’s local store → confirm → summarize into the handoff schema. Titles move; session ids are the join key.
When not to use
- The user did not ask to hand off / resume / fetch.
- Same-tool session compact (use that product’s native handoff).
- Trying to mirror two tools running at the same time — out of scope; pick one writer, then switch.
- Live cross-platform text pings, batch migrate, or Codex store adapters — out of scope.
- Writing into Claude/Cursor session databases — read-only resolve only.
Cursor store caveat
Cursor’s on-disk format is undocumented and can change; WAL may require a DB copy
while Cursor is open. If session-resolve --from cursor fails, fall back to a manual
handoff write. File-first still works.
Pick the thread (do this first)
Before writing or reading, stop and ask which thread — unless the user’s message already includes a clear name (“hand off auth form UI” / “resume oss-skills-launch”).
- List existing threads: non-
LATEST, non-dated-snapshot files in.scratch/handoffs/whose names look like<slug>.md(skipYYYY-MM-DD-HHMM-*.mdsnapshots if you can tell them apart; when unsure, list all.mdexceptLATEST.mdand let the user pick). - Present choices like:
- existing:
auth-form-ui,oss-skills-launch, … - New thread — ask for a 2–5 word summary to use as the name
- existing:
- Wait for the answer. Do not default to
LATESTwhen more than one named file exists. - If the folder is empty and this is a write: ask for a new thread name (you may suggest one from shipped/left, but the user confirms).
- If the folder is empty and this is a resume/fetch: continue to Fetch / hydrate (or say there is nothing to resume if they refuse hydrate).
Write (when asked)
- Run Pick the thread. Reuse an existing name to continue that thread; choose New only for a new line of work. Say the chosen name aloud so the other tool can quote it.
- Draft shipped / left from the session (agent narrative — not from the diff).
- Run the script with
--name(fills Meta, Working tree, Diff check Tree+Paths):
path/to/ashwin-skills/skills/dual-tool-handoff/scripts/agent-handoff \
--name "auth form UI" \
--tool cursor \
--ticket <path or n/a> \
--spec <path or n/a> \
--shipped "<what this session completed>" \
--left "<what remains>" \
--gates "none" \
--questions "<none or list>" \
--claude-session-id n/a \
--cursor-session-id n/a- Fill Diff check (required before done):
- Read Tree and Paths.
- Set Alignment using the bar below.
- Set Note (required if warn).
- Edit the named file +
LATEST.md(same body) so placeholders are gone.
- Tell the user the session name to resume on the other tool.
Writes:
| File | Role |
.scratch/handoffs/<slug>.md | Canonical named handoff (resume by name) |
.scratch/handoffs/LATEST.md | Copy of the same body — current switch target |
.scratch/handoffs/<stamp>-<slug>.md | Immutable snapshot |
Use --tool cursor or --tool claude-code (or other) for the tool you’re leaving.
If the script is unavailable, copy templates/LATEST.template.md →
.scratch/handoffs/<slug>.md, fill it (include Name: and Diff check), then copy
to LATEST.md. Collect paths yourself via git status / git diff --name-only /
git diff --cached --name-only.
Alignment bar (shipped only)
| Case | Alignment |
shipped themes overlap touched paths | ok |
| Decision-only / narrative; clean or unrelated paths; Note explains | ok (Note required) |
Clean tree + shipped claims code landed | warn |
| Overclaim (e.g. “auth done”, diff only README) | warn |
Unsure → warn + Note. Never rewrite shipped/left from the diff. Do not validate left.
Read (when asked to resume — file path)
- Run Pick the thread (unless the user already named one).
- If
.scratch/handoffs/<slug>.mdexists and the user did not use a forcefrom Claude/from Cursorphrase → read that file (no store, no confirm). - Only if there is exactly one named handoff and the user said a bare
“resume from handoff” with no name: you may use that file or
LATEST.mdwithout asking — still tell them which name you opened. - If they named a thread that doesn’t exist as a file → go to Fetch / hydrate.
- Use branch, ticket/spec, shipped/left, Diff check (if warn, treat as advisory), resume one-liner. Do not reopen closed decisions.
Fetch / hydrate (when file missing or force refresh)
Use when the user asks to fetch / pull / re-hydrate, or resume a name with no handoff file.
Store selection
| User said | Store |
from Claude (force phrases below) | Claude |
from Cursor | Cursor |
Bare fetch / pull / fetch … again (no from X) | Opposite of current tool (in Cursor → Claude; in Claude → Cursor) |
| Tool unknown | Ask — never guess |
Force phrases (file may exist — still hydrate):fetch <name> from Claude · re-hydrate <name> from Claude · refresh <name> from Claude · fetch <name> again from Claude
(and the same with Cursor).
Bare fetch / pull with an existing file and no force phrase → read file only (same as resume).
Steps
- Pick thread name (ask if missing).
- File exists and not force
from X→ Read path above; stop. - Choose store (table above). If that store returns 0 matches → ask before trying the other.
- Resolve:
path/to/ashwin-skills/skills/dual-tool-handoff/scripts/session-resolve \
--from claude|cursor resolve "<name>" --json
# or: resolve --id <uuid|composerId> --json
# list: session-resolve --from claude|cursor list --jsonMatch order the script applies: exact title/id → slugified title → substring.
Exit 2 = none, 3 = many — list candidates; never pick silent closest.
5. Confirm card (required): title · short id (8) · updated · branch/mode · cwd/workspace ·
handoff slug if different · size warn if huge. Preview = title or ~80-char last user
prompt — no transcript dump. Wait for yes.
6. Summarize in-session into shipped / left (no full transcript in the handoff).
7. Write with agent-handoff, passing --claude-session-id and/or --cursor-session-id.
Overwrites <slug>.md + LATEST.md; keeps stamped snapshot. Do not rename the
slug because the other tool renamed its session title.
8. Fill Diff check; tell the user the name + one-liner.
Rename: titles move; persist store session id in Meta; re-hydrate via --id + confirm.
Failure modes
- Skipped the ask: wrote or resumed without confirming the thread when multiple exist (or when the user didn’t name one) → rewind and ask.
- Missing
--name: never write anonymous; ask. - Skipped Diff check / left placeholders: incomplete — fill Alignment (+ Note if warn).
- Marked ok without reading Paths: rewind; apply the alignment bar.
- Rewrote shipped from the diff: undo — Diff ≠ why.
- Skipped hydrate confirm / silent closest match: rewind.
- Transcript dump into handoff or card: redact; keep IDs/titles only.
- Hydrated over a file without a force
from Xphrase: rewind; read file instead. - Guessed store when tool unknown: ask.
- PHI / secrets: redact — IDs and paths only.
- Parallel writers: warn that this skill is switch-only;
LATESTreflects the last write, not a merge of two sessions.
Done when
- Thread was confirmed (or clearly named in the user’s message)
- Named file +
LATEST.mdexist and match (on write / hydrate) - After hydrate: Meta store session id filled; confirm was completed
- Diff check present: Alignment filled; Note present if warn
- User has been told the name to quote on the other tool
- Resume one-liner is printable
- No secrets / transcript bodies
Example
- User (in Cursor): “fetch button-change from Claude”
- Agent runs
session-resolve --from claude resolve "button-change"→ confirm card → yes - Summarizes →
agent-handoffwith--claude-session-id→ fills Diff check - Continues from the handoff file
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

