Browse Source

fix(skills): Recover chips reuse original session name; batch fans to one chip each

Two refinements to the summon recover-chip convention:
- Chip title = the original session name verbatim (e.g. "revoicing"), not a
  "Recover ..." label, so a recovered session reads as a continuation of the
  original in the Desktop sidebar rather than a new errand.
- Batch recover (multi-select) sends a single prompt that fans into one chip
  PER session — each recovering independently in its own project root — not
  one mega-chip. Documented explicitly.

Updates per-card + batch recover sendPrompt wording and the SKILL.md
in-chat recover convention.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
0xDarkMatter 3 weeks ago
parent
commit
7f04e6e88f
2 changed files with 6 additions and 3 deletions
  1. 4 1
      skills/summon/SKILL.md
  2. 2 2
      skills/summon/assets/picker-widget.html

+ 4 - 1
skills/summon/SKILL.md

@@ -172,7 +172,10 @@ When summon is invoked from **inside a Claude chat session** (Desktop chat, clau
 1. Run **`summon pick --json --rich`** with a **generous window** (e.g. `--days 30`; add `--cwd`/`--title` filters as asked) and parse the envelope (schema `claude-mods.summon.pick/v2`). `--rich` costs one transcript read per session and adds the per-card display metrics; the widget has its own client-side "last X" age filter (24h/3d/7d/30d/all), so the user narrows in-widget without a CLI re-run.
 2. Render [`assets/picker-widget.html`](assets/picker-widget.html) via the `show_widget` tool: drop the envelope's `data` array straight into the `S` array inside the marked `<script id="D">` block — the widget consumes the pick/v2 **objects verbatim** (keys documented in the template header). Archived sessions are hidden by default (a "show archived" toggle reveals them); cross-account copies dedupe by `sessionId`. Each card shows a **context-usage donut + token count** (last-turn occupancy vs the session's window — 200k or 1M, auto-detected from peak), an **activity-density strip** (24 buckets over the session's lifetime), and chips for model/effort, age, turns, messages, tool calls, on-disk size, and duration, plus **grid/list** and **sort** controls. `firstAsk` is the mechanical opening ask; you MAY replace it with a distilled one-liner by setting a `summary` field on the row before injecting.
 3. Act on the `sendPrompt` callbacks the widget fires. Per-card `↗ summon` and `⟳ recover` (and the footer's "Recover/Summon selected") are worded to be **spawned as background chips** — when one arrives, call `spawn_task` (one chip per session) rather than doing the work inline, so the user's current turn keeps flowing:
-   - **"Recover … as a background chip"** → one `spawn_task` per session, with the chip `cwd` set to the session's **project root** (strip any `\.claude\worktrees\<name>` suffix). Word the chip prompt so the chip *is* the recovered session: it reads the original transcript (resolve via `summon recover <id>` / the wrapper→transcript logic), writes a hand-off brief, and **resumes the work in place** in the project folder. The chip must **not** spawn a further chip and must **not** open the original worktree path as a separate session — that path is reference-only, for locating the branch and any in-progress changes. (The failure mode this prevents: a chip prompt that says "start a fresh session there" plus a worktree path makes the recovering chip spawn a *second* chip into the worktree. The chip already **is** the fresh session — tell it to continue, not to spawn.)
+   - **"Recover … as a background chip"** → one `spawn_task` per session (the batch button sends a single prompt listing all selected — fan it into one chip **per session**, not one mega-chip, so each recovers independently in its own project folder). For each chip:
+     - **Title = the original session name, verbatim** (e.g. `revoicing`) — never a `Recover "…" session` label. The chip should look like a continuation of the original in the sidebar, not a new errand.
+     - **`cwd` = the session's project root** (strip any `\.claude\worktrees\<name>` suffix).
+     - Word the prompt so the chip *is* the recovered session: it reads the original transcript (resolve via `summon recover <id>` / the wrapper→transcript logic), writes a hand-off brief, and **resumes the work in place** in the project folder. The chip must **not** spawn a further chip and must **not** open the original worktree path as a separate session — that path is reference-only, for locating the branch and any in-progress changes. (The failure mode this prevents: a chip prompt that says "start a fresh session there" plus a worktree path makes the recovering chip spawn a *second* chip into the worktree. The chip already **is** the fresh session — tell it to continue, not to spawn.)
    - **"Summon (copy) these…"** → transfer flow: `summon` with `--select` for exactly those sessions, `--dry-run` preview first, then the real run once the user confirms.
    - **"Peek session…"** → `summon --peek <id>`.
 

+ 2 - 2
skills/summon/assets/picker-widget.html

@@ -192,7 +192,7 @@ function render(){
     el.querySelector('.act-peek').addEventListener('click',function(){sendPrompt('Peek session '+s.sessionId+' ("'+s.title+'") — run summon --peek '+s.sessionId+' and show me its last few messages.');});
     el.querySelector('.act-copy').addEventListener('click',function(){var b=this;navigator.clipboard&&navigator.clipboard.writeText(s.sessionId);var i=b.querySelector('i');i.className='ti ti-check';setTimeout(function(){i.className='ti ti-copy';},900);});
     el.querySelector('.act-summon').addEventListener('click',function(){sendPrompt('Summon (copy) the "'+s.title+'" session ('+s.sessionId+') into my other account — show the plan with summon --dry-run first.');});
-    el.querySelector('.act-recover').addEventListener('click',function(){sendPrompt('Recover the "'+s.title+'" session ('+s.sessionId+', '+s.cwd+'): spawn ONE background chip whose cwd is the PROJECT ROOT of that path (drop any \\.claude\\worktrees\\... suffix). That chip IS the recovered session — it reads the original transcript, writes a concise hand-off summary, and resumes the work in place in the project folder. It must NOT spawn any further chip and must NOT open the worktree folder as a separate session (the worktree path is reference-only, for locating the branch and in-progress changes).');});
+    el.querySelector('.act-recover').addEventListener('click',function(){sendPrompt('Recover the "'+s.title+'" session ('+s.sessionId+', '+s.cwd+'): spawn ONE background chip TITLED with the original session name verbatim — "'+s.title+'" — NOT a "Recover ..." label. Set its cwd to the PROJECT ROOT of that path (drop any \\.claude\\worktrees\\... suffix). That chip IS the recovered session — it reads the original transcript, writes a concise hand-off summary, and resumes the work in place in the project folder. It must NOT spawn any further chip and must NOT open the worktree folder as a separate session (the worktree path is reference-only, for locating the branch and in-progress changes).');});
   });
   refreshFoot();
 }
@@ -200,7 +200,7 @@ function render(){
 document.getElementById('bGrid').addEventListener('click',function(){deck.className='deck grid';this.classList.add('on');document.getElementById('bList').classList.remove('on');});
 document.getElementById('bList').addEventListener('click',function(){deck.className='deck list';this.classList.add('on');document.getElementById('bGrid').classList.remove('on');});
 document.getElementById('bClr').addEventListener('click',function(){picked={};render();});
-document.getElementById('bRec').addEventListener('click',function(){var l=DATA.filter(function(s){return picked[s.sessionId];});sendPrompt('Recover these '+l.length+' sessions — spawn ONE background chip per session, each with its cwd set to the PROJECT ROOT (drop any \\.claude\\worktrees\\... suffix). Each chip IS its recovered session: read the original transcript, distil a hand-off brief, and resume the work in place in the project folder. No chip may spawn a further chip or open a worktree folder as a separate session:\n'+fmtList(l));});
+document.getElementById('bRec').addEventListener('click',function(){var l=DATA.filter(function(s){return picked[s.sessionId];});sendPrompt('Recover these '+l.length+' sessions — from this single prompt, spawn ONE background chip per session. Title each chip with that session'+String.fromCharCode(39)+'s original name verbatim (the quoted name below) — NOT a "Recover ..." label — and set its cwd to the PROJECT ROOT (drop any \\.claude\\worktrees\\... suffix). Each chip IS its recovered session: read the original transcript, distil a hand-off brief, and resume the work in place in the project folder. No chip may spawn a further chip or open a worktree folder as a separate session:\n'+fmtList(l));});
 document.getElementById('bSum').addEventListener('click',function(){var l=DATA.filter(function(s){return picked[s.sessionId];});sendPrompt('Summon (copy) these '+l.length+' sessions into my other account — run summon --select for exactly these, show the plan with --dry-run first:\n'+fmtList(l));});
 render();
 })();