Cortex Takeoff — Session Hand-off Baton
A baton is a curated continuation note for ONE work line (topic) in ONE repo.
It lives at <vault>/.takeoff/<repo-slug>/<topic>.md, is git-ignored, and is
consumed by a later session. A repo may hold several batons at once — one per
work line. Batons are independent of the Raw session dump (which SessionEnd
writes automatically). Do NOT commit them, distill them, broadcast them, index
them with cortex-vec, or log them to log.md.
Legacy layout: <vault>/.takeoff/<repo-slug>.md (single-baton era, no topic
key). Legacy batons still list, resume, and clear (via --legacy), and are
retired to the new layout on their next hand-off. Never create new ones.
Locating takeoff.sh
The repo slug and git-safety are handled by takeoff.sh, bundled in this plugin
at hooks/scripts/takeoff.sh. In every mode below, FIRST set TK to that helper,
resolved relative to THIS skill's base directory.
Claude Code announces that base directory when it loads the skill. Use exactly
the path it announced, verbatim — do not assume, reconstruct, or hard-code any
install layout; the marketplace, plugin name, and version all appear in real
paths and all of them change. The only stable fact is the relative one:
takeoff.sh sits two levels up from the skill's base directory.
TK="<the skill base dir Claude Code announced>/../../hooks/scripts/takeoff.sh"
test -f "$TK" || { echo "cortex: takeoff.sh not found at $TK" >&2; exit 1; }
Do NOT use claude plugin root (no such subcommand) or $CLAUDE_PLUGIN_ROOT
(unset for skill-run bash).
Resolving the repo root
Every bash "$TK" … call takes an EXPLICIT cwd argument — there is no $PWD
fallback (a drifted shell cwd once deleted the wrong repo's baton). Resolve
the repo root ONCE per mode and reuse it verbatim in every call:
cwd="$(git rev-parse --show-toplevel)"
If your shell may have cd'ed to another repo earlier in the session, resolve
from a path you KNOW belongs to this repo (e.g. a file you have been editing)
instead of trusting the shell's current directory.
Mode
Determined by the command argument:
| Argument |
Mode |
(none) or <topic> |
create — write/overwrite one work line's baton |
resume [topic] |
resume — load a baton (do not delete) |
done [topic] |
done — clear a baton (soft-delete to trash) |
Create (/cortexes:takeoff [topic])
Set TK, resolve cwd, then survey the existing work lines:
bash "$TK" list "$cwd"
Output: one baton per line, topic<TAB>summary<TAB>path, newest first.
Decide the topic — kebab-case [a-z0-9-], max 64 chars, not
resume/done/legacy:
- The user passed one explicitly → use it verbatim.
- This session earlier RESUMED a baton → reuse that topic. A continuation
is the same work line; do not mint a new name. (Hard rule.)
- Otherwise: if a listed baton is clearly this same work line, propose
reusing its topic; else derive a short new topic from the work line and
ANNOUNCE it to the user before writing.
Run the preflight (git-safety; also derives the slug):
bash "$TK" prepare "$cwd" "<topic>"
Output is TWO lines: line 1 = baton_path, line 2 = workdir. If it
exits non-zero, STOP and relay the message — do not write anything.
(Exit 2 = no vault / no repo; exit 3 = .takeoff/ not git-ignored;
exit 64 = invalid topic.)
Curate the current session into a hand-off. Content is free-form — write
whatever genuinely lets a fresh session continue without re-deriving
context. Typically worth capturing: the goal, what's done so far, the
immediate next step, key files and locations (path:line), open questions,
and gotchas. Omit anything not useful; do not pad to a template.
Compose one summary line (used verbatim as the SessionStart menu preview):
a single sentence naming the work line and the next step.
Write the baton to baton_path with the Write tool. workdir is line 2
of the prepare output, verbatim — do NOT re-derive it yourself:
---
repo: <slug>
topic: <topic>
workdir: <line 2 of prepare output>
created: <YYYY-MM-DDTHH:MM:SS>
summary: <one sentence: this work line / next step>
---
<free-form curation body>
If a baton already exists for this (repo, topic), this overwrites it (one
active baton per work line, new replaces old).
If this work line previously lived in the legacy single-baton file (you
resumed from topic legacy, or list shows a legacy entry that is this
same line), retire it now that the new-format baton exists:
bash "$TK" clear "$cwd" --legacy
Confirm to the user: baton written to <baton_path>, topic <topic>,
not committed.
Resume (resume [topic], or chosen from the SessionStart menu)
- Set
TK, resolve cwd, run bash "$TK" list "$cwd".
- No batons → tell the user there is nothing pending for this repo and stop.
- Pick the target: the explicit / menu-chosen topic if given; a single
listed baton is used directly; multiple batons with no topic given → show
the list (topic + summary) and ask which one.
Read the baton in full (path from the list output) and adopt it as
continuation context. REMEMBER the resumed topic for the rest of the
session — it is the default target of a later create or done.
- Do NOT delete it — loading is not completion; this session may itself
need to hand off again.
Done (done [topic])
Set TK, resolve cwd. Determine the target: the explicit topic if
given, else the topic this session resumed. Neither exists → REFUSE to
guess: run bash "$TK" list "$cwd", show it, and ask the user to name
the target.
Clear it (use --legacy in place of the topic when the target is the
legacy baton):
bash "$TK" clear "$cwd" "<topic>"
Exit 4 = the baton belongs to a different working directory (its
workdir does not match this repo's toplevel). Relay both paths to the
user and let THEM decide — never retry with --force on your own.
Exit 5 = no such baton; re-run list and re-check the target.
Confirm to the user: the baton was moved to the trash path printed by the
command (recoverable for 30 days, then pruned).
Overwrite vs done
- Re-running create replaces that topic's baton (normal re-hand-off).
done is the explicit "this work line is finished" exit — the baton is
soft-deleted into <vault>/.takeoff/.trash/ and pruned after 30 days.
- Merely loading via
resume never clears anything.
1---2name: cortex-takeoff3description: Create or resume a session takeoff baton — a curated, ephemeral, git-ignored hand-off note that lets the next Claude session continue a long-running task. A repo can hold several batons at once, one per work line (topic). Use when the user says "交接", "takeoff", "交棒", "context 快滿了交接給下個 session", "hand off to next session", "/cortexes:takeoff", "takeoff resume", or "takeoff done". The baton is scaffolding, not knowledge: it is never committed, distilled, broadcast, or indexed.4---56# Cortex Takeoff — Session Hand-off Baton78A baton is a curated continuation note for ONE work line (topic) in ONE repo.9It lives at `<vault>/.takeoff/<repo-slug>/<topic>.md`, is git-ignored, and is10consumed by a later session. A repo may hold several batons at once — one per11work line. Batons are independent of the Raw session dump (which SessionEnd12writes automatically). Do NOT commit them, distill them, broadcast them, index13them with `cortex-vec`, or log them to `log.md`.1415Legacy layout: `<vault>/.takeoff/<repo-slug>.md` (single-baton era, no topic16key). Legacy batons still list, resume, and clear (via `--legacy`), and are17retired to the new layout on their next hand-off. Never create new ones.1819## Locating takeoff.sh2021The repo slug and git-safety are handled by `takeoff.sh`, bundled in this plugin22at `hooks/scripts/takeoff.sh`. In every mode below, FIRST set `TK` to that helper,23resolved relative to THIS skill's base directory.2425Claude Code announces that base directory when it loads the skill. Use **exactly26the path it announced**, verbatim — do not assume, reconstruct, or hard-code any27install layout; the marketplace, plugin name, and version all appear in real28paths and all of them change. The only stable fact is the relative one:29`takeoff.sh` sits two levels up from the skill's base directory.3031```bash32TK="<the skill base dir Claude Code announced>/../../hooks/scripts/takeoff.sh"33test -f "$TK" || { echo "cortex: takeoff.sh not found at $TK" >&2; exit 1; }34```3536Do NOT use `claude plugin root` (no such subcommand) or `$CLAUDE_PLUGIN_ROOT`37(unset for skill-run bash).3839## Resolving the repo root4041Every `bash "$TK" …` call takes an EXPLICIT cwd argument — there is no $PWD42fallback (a drifted shell cwd once deleted the wrong repo's baton). Resolve43the repo root ONCE per mode and reuse it verbatim in every call:4445```bash46cwd="$(git rev-parse --show-toplevel)"47```4849If your shell may have cd'ed to another repo earlier in the session, resolve50from a path you KNOW belongs to this repo (e.g. a file you have been editing)51instead of trusting the shell's current directory.5253## Mode5455Determined by the command argument:5657| Argument | Mode |58|----------|------|59| (none) or `<topic>` | **create** — write/overwrite one work line's baton |60| `resume [topic]` | **resume** — load a baton (do not delete) |61| `done [topic]` | **done** — clear a baton (soft-delete to trash) |6263## Create (`/cortexes:takeoff [topic]`)64651. Set `TK`, resolve `cwd`, then survey the existing work lines:6667 ```bash68 bash "$TK" list "$cwd"69 ```7071 Output: one baton per line, `topic<TAB>summary<TAB>path`, newest first.72732. Decide the topic — kebab-case `[a-z0-9-]`, max 64 chars, not74 `resume`/`done`/`legacy`:75 - The user passed one explicitly → use it verbatim.76 - This session earlier RESUMED a baton → reuse that topic. A continuation77 is the same work line; do not mint a new name. (Hard rule.)78 - Otherwise: if a listed baton is clearly this same work line, propose79 reusing its topic; else derive a short new topic from the work line and80 ANNOUNCE it to the user before writing.81823. Run the preflight (git-safety; also derives the slug):8384 ```bash85 bash "$TK" prepare "$cwd" "<topic>"86 ```8788 Output is TWO lines: line 1 = `baton_path`, line 2 = `workdir`. If it89 exits non-zero, STOP and relay the message — do not write anything.90 (Exit 2 = no vault / no repo; exit 3 = `.takeoff/` not git-ignored;91 exit 64 = invalid topic.)92934. Curate the current session into a hand-off. Content is free-form — write94 whatever genuinely lets a fresh session continue without re-deriving95 context. Typically worth capturing: the goal, what's done so far, the96 immediate next step, key files and locations (`path:line`), open questions,97 and gotchas. Omit anything not useful; do not pad to a template.98995. Compose one `summary` line (used verbatim as the SessionStart menu preview):100 a single sentence naming the work line and the next step.1011026. Write the baton to `baton_path` with the Write tool. `workdir` is line 2103 of the prepare output, verbatim — do NOT re-derive it yourself:104105 ```markdown106 ---107 repo: <slug>108 topic: <topic>109 workdir: <line 2 of prepare output>110 created: <YYYY-MM-DDTHH:MM:SS>111 summary: <one sentence: this work line / next step>112 ---113 <free-form curation body>114 ```115116 If a baton already exists for this (repo, topic), this overwrites it (one117 active baton per work line, new replaces old).1181197. If this work line previously lived in the legacy single-baton file (you120 resumed from topic `legacy`, or `list` shows a legacy entry that is this121 same line), retire it now that the new-format baton exists:122123 ```bash124 bash "$TK" clear "$cwd" --legacy125 ```1261278. Confirm to the user: baton written to `<baton_path>`, topic `<topic>`,128 not committed.129130## Resume (`resume [topic]`, or chosen from the SessionStart menu)1311321. Set `TK`, resolve `cwd`, run `bash "$TK" list "$cwd"`.1332. No batons → tell the user there is nothing pending for this repo and stop.1343. Pick the target: the explicit / menu-chosen topic if given; a single135 listed baton is used directly; multiple batons with no topic given → show136 the list (topic + summary) and ask which one.1374. `Read` the baton in full (path from the list output) and adopt it as138 continuation context. REMEMBER the resumed topic for the rest of the139 session — it is the default target of a later create or done.1405. **Do NOT delete it** — loading is not completion; this session may itself141 need to hand off again.142143## Done (`done [topic]`)1441451. Set `TK`, resolve `cwd`. Determine the target: the explicit topic if146 given, else the topic this session resumed. Neither exists → REFUSE to147 guess: run `bash "$TK" list "$cwd"`, show it, and ask the user to name148 the target.1492. Clear it (use `--legacy` in place of the topic when the target is the150 legacy baton):151152 ```bash153 bash "$TK" clear "$cwd" "<topic>"154 ```155156 Exit 4 = the baton belongs to a different working directory (its157 `workdir` does not match this repo's toplevel). Relay both paths to the158 user and let THEM decide — never retry with `--force` on your own.159 Exit 5 = no such baton; re-run `list` and re-check the target.1603. Confirm to the user: the baton was moved to the trash path printed by the161 command (recoverable for 30 days, then pruned).162163## Overwrite vs done164165- Re-running create replaces that topic's baton (normal re-hand-off).166- `done` is the explicit "this work line is finished" exit — the baton is167 soft-deleted into `<vault>/.takeoff/.trash/` and pruned after 30 days.168- Merely loading via `resume` never clears anything.