Start or resume a thread — scratch workspace with a handoff doc
Topic requested: $ARGUMENTS
Project + config:
!bun "${CLAUDE_PLUGIN_ROOT}/scripts/where.ts"
Current branch: !git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "(not a git repo)"
Existing threads:
!bun "${CLAUDE_PLUGIN_ROOT}/scripts/threads.ts" --slugs
Do this
- If
$ARGUMENTS is empty, ask for the topic — don't guess.
- Normalize the topic to a slug — run via Bash:
bun "${CLAUDE_PLUGIN_ROOT}/scripts/slug.ts" "<topic>"
(one shared identity function — never re-implement slugging by hand; if the variable shows as a
literal, use the plugin root: path from the injected block above). With ticket_system: none
a thread is any unit of work; ticket-system adapters may extend this later.
- If
<scratch_root>/<slug>/ already has a STATE doc → RESUME. This is the normal day-2 path,
not an error: no questions, no "want me to open it?" menu — brief and get to work. Treat slugs
as the SAME thread when they match ignoring case and separators (CP-1758 ≡ cp-1758 ≡
cp1758 — the normalizeForMatch rule in scripts/lib/identity.ts): a near-match in the
existing-threads list above resumes THAT thread. Never overwrite or re-scaffold an existing
STATE (skip steps 4–6 entirely). Do all of this:
- Read the whole STATE doc — Status, the ask, plan, current state, open/deferred.
- Pull live deltas (parallel; each optional-degrade — an unreachable source gets a one-line
note, never stalls the resume): current branch; a
git worktree list / git branch --list
match on the slug; if a matching worktree/branch exists — git status --porcelain (dirty
files) and gh pr list --head <branch> --state all --json number,state,reviewDecision --limit 1
(when gh resolves).
- Report a resume briefing — where the user left off (Status, next plan step, open items),
then what changed since (branch moved / PR merged / dirty files). Explicitly flag any live
fact that CONTRADICTS the STATE. End ready to work — not with a menu.
- Resume is read-only on the STATE doc — flag drift in the briefing; the session updates
STATE as work actually proceeds (the
state-freshness-guard Stop hook enforces this before
the session ends).
- Seed the ask from the conversation — there is no ticket system to pull from. If the goal
isn't clear from context, ask for one line. Mark anything unknown as TODO rather than inventing.
- Create
<scratch_root>/<slug>/ and write the STATE doc from the template below, filling
the frontmatter (title: <slug> · kind: thread · summary: one-line blurb) and The ask.
Use the state_basename from the injected config block (default STATE.md).
- Version-control check: if this is a git repo and
<scratch_root> is not ignored
(git check-ignore -q <scratch_root> fails), ASK whether to add it to .gitignore — some
people want the notebook versioned, some never want notes in PRs. Don't silently do either.
- Report the path created and a one-line summary. (No need to run /ccx:save-state for
indexing — the
auto-compile-index hook recompiles the INDEX on every STATE write.)
STATE template
---
title: <slug>
kind: thread
summary: '<one-line INDEX blurb — the live working-state; YAML single-quoted, double any apostrophe>'
---
# <slug> — <short title> · handoff state
**Hub:** [[INDEX]]
**Status:** <one-line live status — done / in-flight / blocked + the key facts, as of <date>>
> **Read this first if context was cleared.** All <slug> work lives in `<scratch_root>/<slug>/` —
> throwaway scripts, probes, and notes stay here, organized per thread, never in the repo or a PR.
> One-off scripts: run from the project root, e.g. `<oneoff_script_runner> <scratch_root>/<slug>/probe.<ext>`.
## The ask (narrowed)
- <one-line goal — from the conversation; TODO if not yet agreed>
- Scope right now = _(TODO: fill when scope is agreed — what's in / explicitly out)_.
- **CORE PRINCIPLE — DATA-DRIVEN.** Derive values/options/categories from real data in this
project — not from an assumption. If a spec looks like a generated example, confirm it against
the source before building on it.
## Where things are
- Branch: `<branch>` (based off `<base>`).
- Source data / inputs: <paths, tables, endpoints — whatever this thread feeds on>.
## Plan (in run order)
1. <step> → <output>
## Current state (as of <date>)
- <what exists, what's verified, what's pending>
## Open / deferred (don't start without the user)
- <items, with any deadlines>
1---2name: start-thread3description: Use when opening OR RESUMING a unit of work — a feature, bug, research spike, refactor, an experiment — that should survive context loss in its own scratch workspace with a handoff doc. Idempotent create-or-resume: a fresh topic gets a scaffolded STATE doc; an existing thread gets a resume briefing (STATE + live git/PR deltas) and you keep working — one command, day 1 or day 20. Also when asked to "start a thread", "new thread", "continue the thread", "resume", "track this work", or "make a workspace for this".4---56# Start or resume a thread — scratch workspace with a handoff doc78Topic requested: **$ARGUMENTS**910**Project + config:**11!`bun "${CLAUDE_PLUGIN_ROOT}/scripts/where.ts"`1213**Current branch:** !`git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "(not a git repo)"`1415**Existing threads:**16!`bun "${CLAUDE_PLUGIN_ROOT}/scripts/threads.ts" --slugs`1718## Do this19201. **If `$ARGUMENTS` is empty**, ask for the topic — don't guess.212. **Normalize** the topic to a slug — run via Bash:22 `bun "${CLAUDE_PLUGIN_ROOT}/scripts/slug.ts" "<topic>"`23 (one shared identity function — never re-implement slugging by hand; if the variable shows as a24 literal, use the `plugin root:` path from the injected block above). With `ticket_system: none`25 a thread is any unit of work; ticket-system adapters may extend this later.263. **If `<scratch_root>/<slug>/` already has a STATE doc → RESUME.** This is the normal day-2 path,27 not an error: no questions, no "want me to open it?" menu — brief and get to work. Treat slugs28 as the SAME thread when they match ignoring case and separators (`CP-1758` ≡ `cp-1758` ≡29 `cp1758` — the `normalizeForMatch` rule in `scripts/lib/identity.ts`): a near-match in the30 existing-threads list above resumes THAT thread. Never overwrite or re-scaffold an existing31 STATE (skip steps 4–6 entirely). Do all of this:32 1. **Read the whole STATE doc** — Status, the ask, plan, current state, open/deferred.33 2. **Pull live deltas** (parallel; each optional-degrade — an unreachable source gets a one-line34 note, never stalls the resume): current branch; a `git worktree list` / `git branch --list`35 match on the slug; if a matching worktree/branch exists — `git status --porcelain` (dirty36 files) and `gh pr list --head <branch> --state all --json number,state,reviewDecision --limit 1`37 (when `gh` resolves).38 3. **Report a resume briefing** — where the user left off (Status, next plan step, open items),39 then **what changed since** (branch moved / PR merged / dirty files). Explicitly flag any live40 fact that CONTRADICTS the STATE. End ready to work — not with a menu.41 4. Resume is **read-only on the STATE doc** — flag drift in the briefing; the session updates42 STATE as work actually proceeds (the `state-freshness-guard` Stop hook enforces this before43 the session ends).444. **Seed the ask from the conversation** — there is no ticket system to pull from. If the goal45 isn't clear from context, ask for one line. Mark anything unknown as TODO rather than inventing.465. **Create** `<scratch_root>/<slug>/` and write the STATE doc from the template below, filling47 the frontmatter (`title: <slug>` · `kind: thread` · `summary:` one-line blurb) and **The ask**.48 Use the `state_basename` from the injected config block (default `STATE.md`).496. **Version-control check:** if this is a git repo and `<scratch_root>` is not ignored50 (`git check-ignore -q <scratch_root>` fails), ASK whether to add it to `.gitignore` — some51 people want the notebook versioned, some never want notes in PRs. Don't silently do either.527. **Report** the path created and a one-line summary. (No need to run /ccx:save-state for53 indexing — the `auto-compile-index` hook recompiles the INDEX on every STATE write.)5455### STATE template5657```markdown58---59title: <slug>60kind: thread61summary: '<one-line INDEX blurb — the live working-state; YAML single-quoted, double any apostrophe>'62---6364# <slug> — <short title> · handoff state6566**Hub:** [[INDEX]]67**Status:** <one-line live status — done / in-flight / blocked + the key facts, as of <date>>6869> **Read this first if context was cleared.** All <slug> work lives in `<scratch_root>/<slug>/` —70> throwaway scripts, probes, and notes stay here, organized per thread, never in the repo or a PR.71> One-off scripts: run from the project root, e.g. `<oneoff_script_runner> <scratch_root>/<slug>/probe.<ext>`.7273## The ask (narrowed)74- <one-line goal — from the conversation; TODO if not yet agreed>75- Scope right now = _(TODO: fill when scope is agreed — what's in / explicitly out)_.76- **CORE PRINCIPLE — DATA-DRIVEN.** Derive values/options/categories from real data in this77 project — not from an assumption. If a spec looks like a generated example, confirm it against78 the source before building on it.7980## Where things are81- Branch: `<branch>` (based off `<base>`).82- Source data / inputs: <paths, tables, endpoints — whatever this thread feeds on>.8384## Plan (in run order)851. <step> → <output>8687## Current state (as of <date>)88- <what exists, what's verified, what's pending>8990## Open / deferred (don't start without the user)91- <items, with any deadlines>92```