diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json index 7e1c75e..c07899a 100644 --- a/.agents/plugins/marketplace.json +++ b/.agents/plugins/marketplace.json @@ -29,10 +29,10 @@ "category": "Engineering" }, { - "name": "prep-compact", + "name": "better-compact", "source": { "source": "local", - "path": "./plugins/prep-compact" + "path": "./plugins/better-compact" }, "policy": { "installation": "AVAILABLE", @@ -41,10 +41,10 @@ "category": "Engineering" }, { - "name": "prep-exit", + "name": "handoff", "source": { "source": "local", - "path": "./plugins/prep-exit" + "path": "./plugins/handoff" }, "policy": { "installation": "AVAILABLE", diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 47540ab..f614ef8 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -36,8 +36,8 @@ { "name": "cc-wrap-up", "source": "./plugins/cc-wrap-up", - "description": "Cues a wrap-up at a natural break in a long session, then compacts with prep-compact's message or hands off with prep-exit's resume prompt, with no copy-paste. Reads the context size at no cost and acts only when you press. Claude Code only.", - "version": "1.0.0" + "description": "Cues a wrap-up at a natural break in a long session, then compacts with better-compact's message or hands off with handoff's resume prompt, with no copy-paste. Reads the context size at no cost and acts only when you press or run a skill. Claude Code only.", + "version": "1.1.0" }, { "name": "cc-pitstop", @@ -64,16 +64,16 @@ "version": "1.0.0" }, { - "name": "prep-compact", - "source": "./plugins/prep-compact", - "description": "Audit in-flight session state before compaction truncates history, and save what would be lost. Commits real work locally, syncs stale plan and state files, and notes chat-only decisions, asking first only for risky items, then prints a paste-ready /compact message plus a queued follow-up. Portable across SKILL.md-native hosts.", - "version": "1.10.0" + "name": "better-compact", + "source": "./plugins/better-compact", + "description": "A better /compact: audit the session, save what compaction would lose, then compact with a focus message and resume with a follow-up. Commits real work locally, syncs stale state files and notes chat-only decisions, asking only for risky items; compacts in one shot with cc-wrap-up. Portable across SKILL.md-native hosts.", + "version": "2.0.0" }, { - "name": "prep-exit", - "source": "./plugins/prep-exit", + "name": "handoff", + "source": "./plugins/handoff", "description": "Save what only the conversation knows before a session ends, so a fresh session can pick up the work. Commits real work locally, syncs state files, writes a handoff note and a memory pointer, and prints a resume prompt; --close instead ties up loose ends for a clean exit. Portable across SKILL.md-native hosts.", - "version": "0.4.0" + "version": "1.0.0" }, { "name": "prep-goal", diff --git a/CLAUDE.md b/CLAUDE.md index 9de6d26..1fd5af3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,7 +18,7 @@ plugins// skills//agents/openai.yaml # GENERATED for user-only skills of non-cc- plugins skills//scripts/* # Optional helper scripts the skill calls hooks/*.sh # Optional event hooks declared in plugin.json (cc- only) - hooks/hooks.json + register.ts # Or a mod: a hooks module Claude Code runs in-process (cc- only, e.g. cc-keepwarm) + hooks/hooks.json + register.ts(x) # Or a mod: a hooks module Claude Code runs in-process (cc- only, e.g. cc-keepwarm) tests/test-*.sh # Optional offline test suite; tests/live-*.sh spend tokens scripts/ # sync-codex.sh, bump-plugin.sh, check.py, test-offline.sh docs/ # Repo notes that never ship, e.g. docs/experiments.md @@ -39,7 +39,7 @@ To add, rename or remove a plugin, or to flip its cc- prefix: the dir name, the ## Checks and tests -- `mise run verify` runs sync, fails if a generated file changed, then runs `scripts/check.py`. check.py fails on a broken contract (above), a description over its cap, `when_to_use` on a user-only skill, a cc- feature in an unprefixed plugin, or prep-compact's and prep-exit's `baseline.sh` copies differing (change both together). It warns on a SKILL.md over 100 lines. +- `mise run verify` runs sync, fails if a generated file changed, then runs `scripts/check.py`. check.py fails on a broken contract (above), a description over its cap, `when_to_use` on a user-only skill, a cc- feature in an unprefixed plugin, or better-compact's and handoff's `baseline.sh` copies differing (change both together). It warns on a SKILL.md over 100 lines. - `mise run test` runs every offline suite (`plugins/*/tests/test-*.sh`). Run the suite of any plugin whose hooks or scripts you touch. - CI (`.github/workflows/ci.yml`, on macOS because the hooks use BSD `stat` and `date`) runs sync, check.py and the offline suites on every pull request and on main. - `mise run test:keepwarm`, `mise run test:autotitle`, `mise run test:wrap-up` and `mise run test:dazzle` run the cc-keepwarm, cc-autotitle, cc-wrap-up and cc-dazzle mods' validate and tests through the `claude` CLI, which CI does not have. Run one after touching its mod or upgrading Claude Code. @@ -66,7 +66,7 @@ The body is a prompt, not docs: second-person imperative. Keep this file and eve **Progressive disclosure**: move reference content out of SKILL.md into sibling files (see `plugins/humanize/skills/humanize/references/`). Keep references one level deep: chains of `.md` -> `.md` -> `.md` cause partial reads. Aim for 100 lines or fewer in SKILL.md. -**Helper scripts**: put deterministic logic in `skills//scripts/` and have the skill call it (e.g. prep-compact's `scripts/baseline.sh`). Otherwise the model re-interprets the prose on every run. +**Helper scripts**: put deterministic logic in `skills//scripts/` and have the skill call it (e.g. better-compact's `scripts/baseline.sh`). Otherwise the model re-interprets the prose on every run. **Dynamic context injection** (cc- only): `` !`cmd` `` inline or `` ```! `` fenced blocks in the skill body run shell commands, and their output replaces the placeholder before the model reads the skill. Use `${CLAUDE_SKILL_DIR}` for script paths, `$ARGUMENTS` / `$0` for user args. diff --git a/README.md b/README.md index b469c75..e4fd5ff 100644 --- a/README.md +++ b/README.md @@ -66,13 +66,13 @@ Generated from each plugin's `plugin.json` by `scripts/sync-codex.sh`, so do not | `skill-polish` | any | Post-mortem for any skill: finds friction in a session, traces it to the responsible skill file, and edits that file's source checkout, not the plugin install copy. | | `cc-keepwarm` | CC only | Keeps Claude Code's prompt cache warm in every session with a silent background ping timed from your last request, and skips the ping once the cache is already gone. | | `cc-timeline` | CC only | A side pane that draws each Claude Code turn as a timeline: every tool call is a colored bar on the turn's time axis, with subagent calls under their Agent call. | -| `cc-wrap-up` | CC only | Cues a wrap-up at a natural break in a long session, then compacts with prep-compact's message or hands off with prep-exit's resume prompt, with no copy-paste. | +| `cc-wrap-up` | CC only | Cues a wrap-up at a natural break in a long session, then compacts with better-compact's message or hands off with handoff's resume prompt, with no copy-paste. | | `cc-pitstop` | CC only | Pauses Claude Code before the 5-hour or weekly usage limit, across every session on the machine, and resumes the paused work on its own when the window resets. | | `cc-quiet-watch` | CC only | Watches long jobs on a timer and asks Haiku only when the output changed. It wakes the session only when the job is done, failed, stuck or needs you. | | `cc-turn-review` | CC only | After each turn that changes files in a git repo, Haiku checks the diff for over-design and for comment and prose rules, and a band offers to fix what it finds. | | `cc-changes` | CC only | A side pane that lists every file the Claude Code session created, edited or deleted, with net line counts, and shows a file's diff when you press it. | -| `prep-compact` | any | Audit in-flight session state before compaction truncates history, and save what would be lost. | -| `prep-exit` | any | Save what only the conversation knows before a session ends, so a fresh session can pick up the work. | +| `better-compact` | any | A better /compact: audit the session, save what compaction would lose, then compact with a focus message and resume with a follow-up. | +| `handoff` | any | Save what only the conversation knows before a session ends, so a fresh session can pick up the work. | | `prep-goal` | any | Interrogate a rough objective into a tight, verifiable /goal completion condition before handing it to an autonomous goal loop. | | `humanize` | any | Strip AI-writing tells from text. Detects em-dash overuse, AI vocabulary, inflated significance, rule-of-three, sycophancy, compound coinages, and 20+ more patterns. | | `brainstorming` | any | Design-before-code gate that sizes each request as a spike, a bounded change, or architectural work, and gets approval on a design before any code. | diff --git a/docs/experiments.md b/docs/experiments.md index 3ddb606..c3a9948 100644 --- a/docs/experiments.md +++ b/docs/experiments.md @@ -35,6 +35,7 @@ Findings that have been verified ourselves, plus tests still pending. Only move | 32 | A git tree snapshot through a private index and a private object folder is cheap, catches heredoc edits and commits made during the turn, and leaves the repo untouched | **TRUE** | Shell spike, 2026-10-10: `GIT_INDEX_FILE` seeded from the real index, `GIT_OBJECT_DIRECTORY` set to a scratch folder, `GIT_ALTERNATE_OBJECT_DIRECTORIES` set to the repo's objects, then `git add -A` and `git write-tree`. About 30 ms for the first snapshot and 10 to 20 ms after in revelo-pipelines, this repo, one of its worktrees and a knowledge-base repo (130 to 280 tracked files). `git diff-tree -p` of two trees showed a heredoc file and a commit made in between. A `git commit` run under these variables writes its objects into the private folder and leaves HEAD pointing at an object the repo does not have, so cc-turn-review sets them only on its own `$.process.run` calls. In the last 25 transcripts, 20 turns that wrote files touched one repo, 2 touched two and 1 touched five; the write commands outside git went to scratch folders and `~/.claude`. | | 33 | Haiku 5.5 at low effort can check a turn's diff for over-design and the comment and prose rules | **TRUE** | `claude -p --model claude-haiku-5-5 --effort low` with cc-turn-review's prompt as the system prompt, 2026-10-10. Caught 3 of 3 planted cases: restating comments, a Markdown paragraph wrapped by hand, and a one-value change grown into a policy class, an env var and a parser. On 3 real merged commits: 2 CLEAN, 1 soft over-design finding (a description rewording called "unrelated", because the ask given was only the commit subject). About $0.002 per review at list price, 2 to 8 s. Thinking took up to 1,750 output tokens, so the mod sets `maxTokens` to 4,000. | | 34 | cc-turn-review works live: snapshots before the first tool call that names a repo, a review after the turn, and a band whose Fix sends the findings back | **TRUE** | Live run in a hot-reloaded session on Claude Code 2.1.295, 2026-10-10. A turn of real edits in this repo's worktree (5 files, 88 changed lines) came back clean, at 7,043 input and 705 output tokens. A planted turn in a scratch repo (a one-value change grown into a policy class with backoff modes, comments that restate the code, a README paragraph wrapped by hand) showed the band with 5 findings, all real: the policy type, the class used once, two restating comments and the wrapped paragraph; one more restating comment was past the cap of 5. Fix sent them as the next prompt. The scratch repo was made in one command and edited in the next, so the snapshot taken before the edit counted it; a repo made and edited in one command is not seen. | +| 35 | A mod can compact from a `turn.complete` hook through `$.clock.after(0, ...)`, and its follow-up runs as the next turn | **TRUE** | cc-wrap-up 1.1.0 live run on Claude Code 2.1.310, Haiku 5.5, 2026-10-27, scratch repo, all installed plugins disabled, better-compact and cc-wrap-up loaded with `--plugin-dir`. `/better-compact` with an uncommitted change committed it, called `ready` with `now: true`, printed the one line the tool result asked for, and ended the turn; the mod compacted with the message ("Conversation compacted") and the follow-up ran as "Prompt from the cc-wrap-up plugin", with no press. A prompt queued during the skill's turn was delivered inside that turn, before `ready` was called: the model folded it into the commit body, and compaction and the follow-up still ran. In the first run Haiku added an unasked design remark after the "Compacting now" line but still set `now`; the steering message kept it as an open caveat. | ## Pending tests diff --git a/mise.toml b/mise.toml index 2437e38..356b552 100644 --- a/mise.toml +++ b/mise.toml @@ -52,9 +52,9 @@ run = "claude plugin validate plugins/cc-wrap-up && claude plugin test plugins/c description = "Run the cc-dazzle mod tests (needs the claude CLI, so CI does not run them)" run = "claude plugin validate plugins/cc-dazzle && claude plugin test plugins/cc-dazzle" -[tasks."test:prep-exit"] -description = "Run the prep-exit script checks (handoff.sh and baseline.sh, offline)" -run = "bash plugins/prep-exit/tests/test-prep-exit.sh" +[tasks."test:handoff"] +description = "Run the handoff script checks (handoff.sh and baseline.sh, offline)" +run = "bash plugins/handoff/tests/test-handoff.sh" [tasks."test:open-pr"] description = "Run the open-pr body checker tests (offline)" diff --git a/plugins/better-compact/.claude-plugin/plugin.json b/plugins/better-compact/.claude-plugin/plugin.json new file mode 100644 index 0000000..3178c49 --- /dev/null +++ b/plugins/better-compact/.claude-plugin/plugin.json @@ -0,0 +1,14 @@ +{ + "name": "better-compact", + "version": "2.0.0", + "description": "A better /compact: audit the session, save what compaction would lose, then compact with a focus message and resume with a follow-up. Commits real work locally, syncs stale state files and notes chat-only decisions, asking only for risky items; compacts in one shot with cc-wrap-up.", + "author": { + "name": "Victor De Simone", + "email": "victorsmoliveira@gmail.com", + "url": "https://github.com/vdsmon" + }, + "homepage": "https://github.com/vdsmon/skills", + "repository": "https://github.com/vdsmon/skills", + "license": "MIT", + "skills": "./skills" +} diff --git a/plugins/prep-compact/.codex-plugin b/plugins/better-compact/.codex-plugin similarity index 100% rename from plugins/prep-compact/.codex-plugin rename to plugins/better-compact/.codex-plugin diff --git a/plugins/prep-compact/skills/prep-compact/SKILL.md b/plugins/better-compact/skills/better-compact/SKILL.md similarity index 60% rename from plugins/prep-compact/skills/prep-compact/SKILL.md rename to plugins/better-compact/skills/better-compact/SKILL.md index 131aa89..cc28e35 100644 --- a/plugins/prep-compact/skills/prep-compact/SKILL.md +++ b/plugins/better-compact/skills/better-compact/SKILL.md @@ -1,7 +1,7 @@ --- -name: prep-compact +name: better-compact disable-model-invocation: true -description: Audits in-flight session state before compaction truncates history, saves what would be lost (local commits, state files, notes), then emits a paste-ready focus message for the next session. +description: A better /compact. Audits in-flight session state, saves what compaction would lose (local commits, state files, notes), then compacts with a focus message and resumes with a follow-up, in one shot when cc-wrap-up is installed. argument-hint: "[--message-only | --propose]" allowed-tools: - Bash(git status *) @@ -12,20 +12,20 @@ allowed-tools: - Bash(git stash push *) --- -# Prep-Compact +# Better-Compact -Compaction drops the conversation history and keeps only the short summary you write. Anything not saved outside the chat (a half-written plan, an unsaved snippet, a background task name) is gone. This skill audits the session and saves what would be lost, so the next session does not miss it. +Compaction drops the conversation history and keeps only a summary. Anything not saved outside the chat (a half-written plan, an unsaved snippet, a background task name) is gone. This skill audits the session, saves what would be lost, then compacts with a message that steers the summary and a follow-up that resumes the work. ## When to use -Use it when the user is about to compact, or asks whether they should. This skill acts (commits, writes files), so it runs only when the user asks. +Use it when the user wants to compact, or asks whether they should. This skill acts (commits, writes files, compacts), so it runs only when the user asks. To save state and stop without compacting, the user runs `/handoff` instead. ## Modes Raw input: `$ARGUMENTS` - `$ARGUMENTS` contains `--message-only` (or `-m`, `message only`, `just the message`, `skip audit`) -> **message-only mode**: skip steps 1-2, jump straight to step 3. Still run `scripts/baseline.sh` (or the two git commands it wraps) so the message can cite branch + recent commits accurately, but no audit summary, no saves. -- `$ARGUMENTS` contains `--propose` (or `-p`, `propose only`, `don't act`, `just list`) -> **propose mode**: step 2 lists the save-actions under **Action needed** instead of doing them, then the message as usual. Do nothing until the user answers; on a go-ahead, run the approved saves, re-run the baseline, reissue the message if what it cites changed. +- `$ARGUMENTS` contains `--propose` (or `-p`, `propose only`, `don't act`, `just list`) -> **propose mode**: step 2 lists the save-actions under **Action needed** instead of doing them, then the message, handed over without `now`. Do nothing until the user answers; on a go-ahead, run the approved saves, re-run the baseline, hand over again. - Otherwise -> **full mode**: all three steps in order, saves carried out. Also honour natural language overrides mid-conversation: "skip audit, just give me the message" -> message-only without re-running; "don't do anything, just list it" before the saves ran -> propose. @@ -90,40 +90,28 @@ Commits: - uncommitted changes where real work vs. experimental cruft is unclear; - paid, long-running or background jobs. -Ask once: one question batching every held-back item, recommended choice first for each. Do everything else in the meantime; the saves never wait on the answer. On a yes, act, re-run the baseline, reissue the message only if what it cites changed. +Ask once: one question batching every held-back item, recommended choice first for each. Do everything else in the meantime; the saves never wait on the answer. On a yes, act, re-run the baseline, hand over again (step 3). After the saves, re-run `scripts/baseline.sh` so the compact message cites the state after them: new commit hashes, updated paths. **Nothing needs saving -> emit nothing here. Go straight to the compact message.** No "state is clean" line, no recap (that's noise). -### 3. Propose: compact message + follow-up +### 3. Compact: message, follow-up, hand-over -Output **two** code blocks: +The summarizer that `/compact` runs reads the whole conversation; the message only steers it. The follow-up is sent verbatim as the first prompt after compaction. Split them with no overlap: -1. **Compact message:** paste and send. Must start with the literal `/compact ` prefix, then the focus message, so paste fires the command directly, no editing. 3-6 sentences, optimized for next-session cold-start. -2. **Follow-up:** the user queues this *while compact runs*. The host fires queued input the moment compact finishes, so work resumes hands-free: no second prompt, no waiting. It's the literal next action, written as an imperative to your post-compact self. +- **Compact message**, 1-3 sentences: what the summary must keep and what it can drop. Keep the live thread, decisions with their why, exact paths and hashes of what step 2 saved (local, unpushed), every held-back item still awaiting a yes, what not to redo. Drop finished side threads and settled dead ends. Don't retell what was done, and don't name the next step. +- **Follow-up**, one imperative sentence: the first action on resume, naming the file or command to touch first. No context; the summary carries it. -Compact message holds **context**: +Hand over: -- **Where we are:** current task / branch / stage if applicable -- **What's done:** key milestones, test counts, decisions locked in, commits made in step 2 (local, unpushed) -- **Any gotchas:** open debug threads, things to skip or redo, non-obvious state, every held-back item still awaiting a yes (so the question survives an unanswered compact) -- **Pointers to persisted state:** "plan at X, state file at Y, branch Z" - -Follow-up holds **the next move**: - -- One or two imperative sentences: the exact first action on resume. -- Self-contained: assume the summary plus file access are the only context. Name the file / command / function to touch first. - -Keep both terse and non-overlapping: context in the compact message, the next action only in the follow-up. Model reading has full file access: breadcrumbs, not paragraphs. - -Tell the user plainly: send the compact block, then immediately paste the follow-up so it queues and chains. - -If a tool named `mcp__cc-wrap-up__ready` is listed (load it first if it is deferred), call it with `kind: compact`, the focus message without the `/compact ` prefix as `message`, the follow-up as `followUp`, and `openQuestion: true` when **Needs your yes** holds a question. Print both blocks as usual. Call it again whenever you reissue the message. +- If a tool named `mcp__cc-wrap-up__ready` is listed (load it first if it is deferred), call it with `kind: compact`, the message without the `/compact ` prefix as `message`, the follow-up as `followUp`, `openQuestion: true` when **Needs your yes** holds a question, and `now: true` unless a question is held back, propose mode waits for a go-ahead, or you advise against compacting (see Notes). With `now`, the mod compacts as soon as your turn ends, then sends the follow-up: print what the tool result asks and end the turn. Without `now`, it offers the user a band; print the blocks. +- Without the tool, or when it refuses: print the two blocks below. Tell the user to send the compact block, then paste the follow-up at once so it queues and fires when compaction finishes. +- After a yes to a held-back item: act, re-run the baseline, hand over again (with `now: true` once nothing waits). ## Format -No audit recap. Saves done -> lead with **Saved before compacting**, one line per action with its evidence (commit hash and subject, file path, what the note holds). Something held back -> **Needs your yes** with the single batched question. Neither when nothing needed saving (the common case). +No audit recap, no step narration. Saves done -> lead with **Saved before compacting**, one line per action with its evidence (commit hash and subject, file path, what the note holds). Something held back -> **Needs your yes** with the single batched question. Neither when nothing needed saving (the common case). Then the blocks, unless the mod compacts now and asks for its one line instead. ``` **Saved before compacting** @@ -137,40 +125,22 @@ No audit recap. Saves done -> lead with **Saved before compacting**, one line pe /compact [the focus message] ``` -**Follow-up** — queue this while compact runs; it fires when compact finishes and chains the work: +**Follow-up** — queue this while compact runs; it fires when compact finishes: ```text -[the next-action kickoff] +[the next action] ``` ``` -Propose mode: **Action needed** (one line per save-action, with rationale) in place of **Saved before compacting**, then the message. - -Message-only mode: same two blocks, no Saved or Needs-your-yes. - -No "Step 1 / Step 2 / Step 3" narration, no audit bullets (ceremony). +Propose mode: **Action needed** (one line per save-action, with rationale) in place of **Saved before compacting**, then the blocks. Message-only mode: no Saved or Needs-your-yes. ## Example -**Real work at risk** (saves done, then the message citing the new state): +Saved `4e1a9c2` wip(limiter): jittered backoff, regression test pending; summarized the finished `bash_3` traces under Findings in `.flow/hotfix-503/plan.md`; synced that plan to "fix in progress". -``` -**Saved before compacting** -- Committed `4e1a9c2` wip(limiter): jittered backoff on token refresh, regression test pending. -- Read `bash_3` (repro harness, finished): 12 traces, every 503 within 40 ms of a token refresh. Summary under Findings in `.flow/hotfix-503/plan.md`. -- Synced `.flow/hotfix-503/plan.md`: root cause done, fix in progress, regression test next. - -**Compact message** — paste and send: -```text -/compact Hotfix for rate-limit 503 on hotfix/rate-limit-503. Root cause: thundering-herd on token refresh (traces summarized in .flow/hotfix-503/plan.md, Findings). Fix committed locally as 4e1a9c2 (jittered backoff in src/limiter.ts), not pushed. Skip rerunning the repro; the traces are enough. -``` - -**Follow-up** — queue while compact runs; fires on finish, chains the work: -```text -Resume the 503 hotfix: add a regression test for the jittered backoff in src/limiter.ts, run the limiter suite, then commit. -``` -``` +- Message: `Keep the 503 hotfix on hotfix/rate-limit-503: root cause is a thundering herd on token refresh (Findings in .flow/hotfix-503/plan.md); the fix is local commit 4e1a9c2, not pushed. Drop the repro runs; the traces are enough.` +- Follow-up: `Add a regression test for the jittered backoff in src/limiter.ts, run the limiter suite, then commit.` ## Notes - Compact message is *yours*, so don't parrot user in-session. They compact because they trust you preserve what matters. -- State genuinely chaotic (many unfinished threads, half-implementations): say so, recommend *against* compacting until sorted, even in message-only mode. Save what's clearly real work anyway. Losing one session context cheap; losing track of in-flight work not. +- State genuinely chaotic (many unfinished threads, half-implementations): say so, recommend *against* compacting until sorted, even in message-only mode, and hand over without `now`. Save what's clearly real work anyway. Losing one session context cheap; losing track of in-flight work not. diff --git a/plugins/prep-compact/skills/prep-compact/agents/openai.yaml b/plugins/better-compact/skills/better-compact/agents/openai.yaml similarity index 100% rename from plugins/prep-compact/skills/prep-compact/agents/openai.yaml rename to plugins/better-compact/skills/better-compact/agents/openai.yaml diff --git a/plugins/prep-exit/skills/prep-exit/scripts/baseline.sh b/plugins/better-compact/skills/better-compact/scripts/baseline.sh old mode 100755 new mode 100644 similarity index 95% rename from plugins/prep-exit/skills/prep-exit/scripts/baseline.sh rename to plugins/better-compact/skills/better-compact/scripts/baseline.sh index abc421a..7e399c2 --- a/plugins/prep-exit/skills/prep-exit/scripts/baseline.sh +++ b/plugins/better-compact/skills/better-compact/scripts/baseline.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# baseline.sh [dir]: the mechanical half of the prep-compact audit, in one call. +# baseline.sh [dir]: the mechanical half of the better-compact and handoff audit, in one call. # # What git knows (branch and upstream, status with counts, the last five commits, # the stash), plus the one thing `git status` never shows and a compact message @@ -51,7 +51,7 @@ stashes=$(git stash list 2>/dev/null | grep -c .) # (portable across BSD and GNU find, which disagree on -newermt). head_epoch=$(git log -1 --format=%ct 2>/dev/null) if [ -n "$head_epoch" ]; then - ref=$(mktemp "${TMPDIR:-/tmp}/prep-compact-ref.XXXXXX") || ref="" + ref=$(mktemp "${TMPDIR:-/tmp}/baseline-ref.XXXXXX") || ref="" if [ -n "$ref" ]; then stamp=$(date -r "$head_epoch" '+%Y%m%d%H%M.%S' 2>/dev/null || date -d "@$head_epoch" '+%Y%m%d%H%M.%S' 2>/dev/null) [ -n "$stamp" ] && touch -t "$stamp" "$ref" 2>/dev/null diff --git a/plugins/cc-wrap-up/.claude-plugin/plugin.json b/plugins/cc-wrap-up/.claude-plugin/plugin.json index 12ec05d..f2f9de0 100644 --- a/plugins/cc-wrap-up/.claude-plugin/plugin.json +++ b/plugins/cc-wrap-up/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "cc-wrap-up", - "version": "1.0.0", - "description": "Cues a wrap-up at a natural break in a long session, then compacts with prep-compact's message or hands off with prep-exit's resume prompt, with no copy-paste. Reads the context size at no cost and acts only when you press.", + "version": "1.1.0", + "description": "Cues a wrap-up at a natural break in a long session, then compacts with better-compact's message or hands off with handoff's resume prompt, with no copy-paste. Reads the context size at no cost and acts only when you press or run a skill.", "author": { "name": "Victor De Simone", "email": "victorsmoliveira@gmail.com", diff --git a/plugins/cc-wrap-up/README.md b/plugins/cc-wrap-up/README.md index 32778b8..ba33903 100644 --- a/plugins/cc-wrap-up/README.md +++ b/plugins/cc-wrap-up/README.md @@ -1,19 +1,19 @@ # cc-wrap-up -Offers to wrap up a long Claude Code session at a natural break, then does the last step for you: it compacts with `prep-compact`'s message, or it hands the work off with `prep-exit`'s resume prompt. You do not copy or paste anything. +Offers to wrap up a long Claude Code session at a natural break, then does the last step for you: it compacts with `better-compact`'s message, or it hands the work off with `handoff`'s resume prompt. You do not copy or paste anything. -It is a mod: a hooks module that Claude Code runs inside each session. It reads the context size after each turn at no cost, and it acts only when you press a button or run `/wrap-up`. +It is a mod: a hooks module that Claude Code runs inside each session. It reads the context size after each turn at no cost, and it acts only when you press a button, run `/wrap-up`, or run one of the two skills. ## Install ``` /plugin marketplace add vdsmon/skills /plugin install cc-wrap-up@vdsmon-skills -/plugin install prep-compact@vdsmon-skills -/plugin install prep-exit@vdsmon-skills +/plugin install better-compact@vdsmon-skills +/plugin install handoff@vdsmon-skills ``` -The two skills are optional. Without `prep-compact`, Compact compacts at once with no message. Without `prep-exit`, there is no Hand off button. +The two skills are optional. Without `better-compact`, Compact compacts at once with no message. Without `handoff`, there is no Hand off button. ## When it offers a wrap-up @@ -28,13 +28,15 @@ It never shows during a running turn, in a subagent, or in a `claude -p` run. ## What the buttons do -**Compact** runs `/prep-compact`. The skill saves what would be lost and hands its message to the mod. The band then offers: +**Compact** runs `/better-compact`; typing the command yourself works the same. The skill saves what would be lost and hands its message to the mod, which compacts as soon as the skill's turn ends, then sends the follow-up as your next prompt. A prompt you send before that calls it off. + +When the skill held back a question, or advises against compacting, the band waits instead and offers: - **Compact now**: compacts with the message, then sends the follow-up as your next prompt. - **Edit**: puts `/compact ` in the prompt box for you to change. The follow-up is still sent after compaction. - **Not now**: closes the band. -**Hand off** runs `/prep-exit`. The skill writes `HANDOFF.md`, a memory entry and a resume prompt. The band then offers: +**Hand off** runs `/handoff`. The skill writes `HANDOFF.md`, a memory entry and a resume prompt. The band then offers: - **Fresh session here**: runs `/clear`, then sends the resume prompt as the first message. - **Copy prompt**: copies the resume prompt, for Codex or another host. @@ -63,7 +65,7 @@ Set them in `/config` under the plugin. ## What it costs -Nothing until you press. Reading the context size makes no API call. Past the line, the mod runs `git status --porcelain` once per turn, and only until it finds a break. Compact and Hand off run the skills, which spend tokens the same as when you type them. +Nothing until you press a button or run one of the skills. Reading the context size makes no API call. Past the line, the mod runs `git status --porcelain` once per turn, and only until it finds a break. Compact and Hand off run the skills, which spend tokens the same as when you type them. ## Tests diff --git a/plugins/cc-wrap-up/hooks/register.tsx b/plugins/cc-wrap-up/hooks/register.tsx index a93bdce..e7c8a75 100644 --- a/plugins/cc-wrap-up/hooks/register.tsx +++ b/plugins/cc-wrap-up/hooks/register.tsx @@ -7,7 +7,7 @@ const AUTO_COMPACT_AT = '967k' const COMMAND = 'wrap-up' const TOOL = 'ready' const TOOL_NAME = `mcp__cc-wrap-up__${TOOL}` -const SKILL: Record = { compact: 'prep-compact', handoff: 'prep-exit' } +const SKILL: Record = { compact: 'better-compact', handoff: 'handoff' } const IDLE: Wrap = { phase: 'idle', @@ -214,7 +214,7 @@ async function runCommand($: EngineInterface, args: string) { } if (verb === 'send') { const to = rest.join(' ') - if (w.payload?.kind !== 'handoff') return 'No handoff is ready. Press Hand off, or run /prep-exit, first.' + if (w.payload?.kind !== 'handoff') return 'No handoff is ready. Press Hand off, or run /handoff, first.' if (to === '') return `Name the session: /${COMMAND} send .` const r = await $.session.send({ to, text: w.payload.resumePrompt }) return r.isDelivered ? `Sent the resume prompt to ${to}.` : `Could not send to ${to}: ${r.reason}` @@ -225,13 +225,17 @@ async function runCommand($: EngineInterface, args: string) { const TOOL_SPEC = { name: TOOL, description: - 'Hands the result of prep-compact or prep-exit to the wrap-up band, which offers the user to compact or to deliver the resume prompt.', + 'Hands the result of better-compact or handoff to the wrap-up band, which compacts or offers the user to compact, or to deliver the resume prompt.', inputSchema: { type: 'object', properties: { kind: { enum: ['compact', 'handoff'] }, message: { type: 'string', description: 'Required for compact: the focus message, without the /compact prefix' }, followUp: { type: 'string', description: 'Required for compact: the follow-up, the next action sent as a prompt after compaction' }, + now: { + type: 'boolean', + description: 'Compact only: true to compact as soon as this turn ends, with no press; leave it out when a question is held back or you advise against compacting', + }, resumePrompt: { type: 'string', description: 'Required for handoff: the resume prompt, the first message for the next session' }, handoffPath: { type: 'string', description: 'Required for handoff: the absolute path of HANDOFF.md' }, openQuestion: { type: 'boolean', description: 'true when a question was held back for the user' }, @@ -249,7 +253,13 @@ function payloadOf(e: Record): Payload | string { if (e.kind === 'compact') { const gone = missing(['message', 'followUp']) if (gone.length > 0) return `missing ${gone.join(' and ')}` - return { kind: 'compact', message: text('message').replace(/^\/compact\s+/, ''), followUp: text('followUp'), openQuestion } + return { + kind: 'compact', + message: text('message').replace(/^\/compact\s+/, ''), + followUp: text('followUp'), + openQuestion, + now: e.now === true && !openQuestion, + } } if (e.kind === 'handoff') { const gone = missing(['resumePrompt', 'handoffPath']) @@ -261,6 +271,16 @@ function payloadOf(e: Record): Payload | string { async function onTurnEnd($: EngineInterface, reason: string) { const w = await read($, wrap) + if (reason === 'answer' && w.phase === 'ready-compact' && w.payload?.kind === 'compact' && w.payload.now) { + const { message, followUp } = w.payload + soon($, async () => { + // A prompt the user sent since the turn ended has made the result stale. + const cur = await read($, wrap) + if (cur.phase !== 'ready-compact' || cur.payload?.kind !== 'compact' || cur.payload.message !== message) return + await compactNow($, message, followUp) + }) + return + } if (w.phase === 'prepping') { await update($, wrap, cur => (cur.phase === 'prepping' ? dismissed(cur) : cur)) const skill = SKILL[w.prepKind ?? 'compact'] @@ -296,6 +316,12 @@ export const register: Register = (on, options) => { payload, prepKind: null, })) + if (payload.kind === 'compact' && payload.now) { + return { + result: + 'cc-wrap-up compacts with this message when your turn ends, then sends the follow-up. Print the Saved lines if any, then one line "Compacting now; then: ", and nothing else: not the two blocks, no new remarks. End your turn.', + } + } return { result: 'The wrap-up band now offers this to the user. Print your usual blocks too.' } }).catch(() => ({ deny: 'cc-wrap-up failed to take the result. Print your usual blocks.' })) diff --git a/plugins/cc-wrap-up/hooks/wrap-up.test.ts b/plugins/cc-wrap-up/hooks/wrap-up.test.ts index d4a667a..2163292 100644 --- a/plugins/cc-wrap-up/hooks/wrap-up.test.ts +++ b/plugins/cc-wrap-up/hooks/wrap-up.test.ts @@ -23,7 +23,7 @@ function world(on: On) { const seen = { tokens: 0 as number | undefined, isDirty: false, - commands: ['prep-compact:prep-compact', 'prep-exit:prep-exit', 'compact', 'clear'], + commands: ['better-compact:better-compact', 'handoff:handoff', 'compact', 'clear'], gitRuns: 0, notified: 0, toasts: [] as string[], @@ -376,7 +376,7 @@ test('a prep the user typed shows as preparing, and one that hands nothing over const seen = world(on) await start($) const ui = await band($) - await typed($, 'prep-compact:prep-compact') + await typed($, 'better-compact:better-compact') expect(await lineText(ui)).toContain('Preparing to compact') expect(await buttons(ui)).toEqual([]) await turn($, seen, 300_000) @@ -388,7 +388,7 @@ test('a prep that hands over its result leaves the band ready after its turn', a const seen = world(on) await start($) const ui = await band($) - await typed($, 'prep-exit:prep-exit') + await typed($, 'handoff:handoff') expect(await lineText(ui)).toContain('Preparing the handoff') await ready($, HANDOFF) await turn($, seen, 700_000) @@ -419,17 +419,17 @@ test('with an open question, the answer keeps the payload and the next prompt dr expect(await buttons(ui)).toEqual([]) }) -test('Compact runs prep-compact and shows the prep as running', async ($, on) => { +test('Compact runs better-compact and shows the prep as running', async ($, on) => { const seen = world(on) await start($) const ui = await band($) await turn($, seen, 520_000) await press(ui, 'compact') - expect(seen.ran).toEqual([{ command: 'prep-compact:prep-compact', args: '' }]) + expect(seen.ran).toEqual([{ command: 'better-compact:better-compact', args: '' }]) expect(await lineText(ui)).toContain('Preparing to compact') }) -test('without prep-compact installed, Compact compacts at once with no message', async ($, on) => { +test('without better-compact installed, Compact compacts at once with no message', async ($, on) => { const seen = world(on) seen.commands = ['compact', 'clear'] await start($) @@ -467,6 +467,69 @@ test('a skipped compaction keeps the band and sends nothing', async ($, on) => { expect(await buttons(ui)).toEqual(['compact-now', 'edit', 'not-now']) }) +test('a payload with now compacts when the turn ends, then sends the follow-up, with no press', async ($, on) => { + const seen = world(on) + await start($) + const ui = await band($) + const r = await ready($, { ...COMPACT, now: true }) + expect(String(r.result)).toContain('compacts with this message when your turn ends') + expect(seen.compactions).toEqual([]) + await turn($, seen, 700_000) + await clock.settle() + expect(seen.compactions.map(c => c.instructions)).toEqual([COMPACT.message]) + expect(seen.submits).toEqual([{ text: COMPACT.followUp, asUser: true }]) + expect(await buttons(ui)).toEqual([]) +}) + +test('now with an open question waits for a press', async ($, on) => { + const seen = world(on) + await start($) + const ui = await band($) + const r = await ready($, { ...COMPACT, now: true, openQuestion: true }) + expect(String(r.result)).toContain('Print your usual blocks') + await turn($, seen, 700_000) + await clock.settle() + expect(seen.compactions).toEqual([]) + expect(await buttons(ui)).toEqual(['compact-now', 'edit', 'not-now']) +}) + +test('now on an aborted turn waits for a press', async ($, on) => { + const seen = world(on) + await start($) + const ui = await band($) + await ready($, { ...COMPACT, now: true }) + await turn($, seen, 700_000, { reason: 'aborted' }) + await clock.settle() + expect(seen.compactions).toEqual([]) + expect(await buttons(ui)).toEqual(['compact-now', 'edit', 'not-now']) +}) + +test('a prompt the user sends before the compaction starts calls it off', async ($, on) => { + const seen = world(on) + await start($) + const ui = await band($) + await ready($, { ...COMPACT, now: true }) + await turn($, seen, 700_000) + await said($, 'wait, one more thing') + await clock.settle() + expect(seen.compactions).toEqual([]) + expect(seen.submits.map(s => s.text)).toEqual(['wait, one more thing']) + expect(await buttons(ui)).toEqual([]) +}) + +test('a skipped compaction with now keeps the band and sends nothing', async ($, on) => { + const seen = world(on) + seen.isCompactSkipped = true + await start($) + const ui = await band($) + await ready($, { ...COMPACT, now: true }) + await turn($, seen, 700_000) + await clock.settle() + expect(seen.toasts.join('\n')).toContain('a hook said no') + expect(seen.submits).toEqual([]) + expect(await buttons(ui)).toEqual(['compact-now', 'edit', 'not-now']) +}) + test('Edit puts /compact with the message in the prompt box and keeps the band', async ($, on) => { const seen = world(on) await start($) @@ -499,23 +562,23 @@ test('while a turn runs, the band offers nothing but a running prep', async ($, props: { ...BAND, isWorking: true }, }) expect(await buttons(working)).toEqual([]) - await typed($, 'prep-compact:prep-compact') + await typed($, 'better-compact:better-compact') expect(await lineText(working)).toContain('Preparing to compact') }) -test('Hand off runs prep-exit and shows the prep as running', async ($, on) => { +test('Hand off runs handoff and shows the prep as running', async ($, on) => { const seen = world(on) await start($) const ui = await band($) await turn($, seen, 520_000) await press(ui, 'handoff') - expect(seen.ran).toEqual([{ command: 'prep-exit:prep-exit', args: '' }]) + expect(seen.ran).toEqual([{ command: 'handoff:handoff', args: '' }]) expect(await lineText(ui)).toContain('Preparing the handoff') }) -test('without prep-exit installed, there is no Hand off button', async ($, on) => { +test('without handoff installed, there is no Hand off button', async ($, on) => { const seen = world(on) - seen.commands = ['prep-compact:prep-compact', 'compact', 'clear'] + seen.commands = ['better-compact:better-compact', 'compact', 'clear'] await start($) const ui = await band($) await turn($, seen, 520_000) diff --git a/plugins/cc-wrap-up/types/index.d.ts b/plugins/cc-wrap-up/types/index.d.ts index 211b780..31a5066 100644 --- a/plugins/cc-wrap-up/types/index.d.ts +++ b/plugins/cc-wrap-up/types/index.d.ts @@ -3,9 +3,10 @@ export type Phase = 'idle' | 'armed' | 'cue' | 'urgent' | 'prepping' | 'ready-co export type PrepKind = 'compact' | 'handoff' -/** What prep-compact or prep-exit handed over through the `ready` tool. */ +/** What better-compact or handoff handed over through the `ready` tool. */ export type Payload = - | { kind: 'compact'; message: string; followUp: string; openQuestion: boolean } + /** `now`: compact when the turn ends, with no press. */ + | { kind: 'compact'; message: string; followUp: string; openQuestion: boolean; now: boolean } | { kind: 'handoff'; resumePrompt: string; handoffPath: string; openQuestion: boolean } export type Wrap = { diff --git a/plugins/prep-exit/.claude-plugin/plugin.json b/plugins/handoff/.claude-plugin/plugin.json similarity index 92% rename from plugins/prep-exit/.claude-plugin/plugin.json rename to plugins/handoff/.claude-plugin/plugin.json index 2492ccd..dc6efdb 100644 --- a/plugins/prep-exit/.claude-plugin/plugin.json +++ b/plugins/handoff/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { - "name": "prep-exit", - "version": "0.4.0", + "name": "handoff", + "version": "1.0.0", "description": "Save what only the conversation knows before a session ends, so a fresh session can pick up the work. Commits real work locally, syncs state files, writes a handoff note and a memory pointer, and prints a resume prompt; --close instead ties up loose ends for a clean exit.", "author": { "name": "Victor De Simone", diff --git a/plugins/prep-exit/.codex-plugin b/plugins/handoff/.codex-plugin similarity index 100% rename from plugins/prep-exit/.codex-plugin rename to plugins/handoff/.codex-plugin diff --git a/plugins/prep-exit/skills/prep-exit/SKILL.md b/plugins/handoff/skills/handoff/SKILL.md similarity index 93% rename from plugins/prep-exit/skills/prep-exit/SKILL.md rename to plugins/handoff/skills/handoff/SKILL.md index 9d6a02a..6b79812 100644 --- a/plugins/prep-exit/skills/prep-exit/SKILL.md +++ b/plugins/handoff/skills/handoff/SKILL.md @@ -1,5 +1,5 @@ --- -name: prep-exit +name: handoff disable-model-invocation: true description: Audits in-flight session state before the session ends, saves what only the conversation knows into files and persistent memory, and leaves a handoff note plus a paste-ready resume prompt for the next session. argument-hint: "[--note-only | --close]" @@ -13,11 +13,11 @@ allowed-tools: - Bash(git commit *) --- -# Prep-Exit +# Handoff A fresh session (a new conversation, another machine, another host) starts only from disk and from persistent memory. Nothing of this conversation reaches it: no summary, no background task output, no session-only cron, no task list. Resuming this same conversation (`claude --continue` or `--resume` in Claude Code) brings back the history and the crons that have not expired, but never background Bash or monitor output, and the user may never resume. So save as if the next session starts fresh. This skill audits what the conversation still holds, saves it where the next session will look, and leaves one resume prompt. -`prep-compact` is the sibling for compaction, where a summary survives and carries context. Here plan for nothing surviving, so every finding becomes a file or a memory entry, never a message. +`better-compact` is the sibling for compaction, where a summary survives and carries context. Here plan for nothing surviving, so every finding becomes a file or a memory entry, never a message. ## Modes @@ -86,7 +86,7 @@ Commits: Ask once: one question batching every held-back item, recommended choice first for each, printed with the hand-over in step 4. Nothing waits on the answer: every held-back item goes into the handoff under **Pending** as awaiting a yes, so the question survives an exit with no reply. If the user said they will not be around ("just save everything", "I won't answer"), skip the question; Pending still lists the items. -Exit prep saves state; it does not advance the work. A pending fix, a next step, a question from another session: record where it stands, do not do it now. Work done in the last minute lands unreviewed and the handoff then misreports where things are. Likewise, do not judge pending items closed (a benchmark "dead", a chip "moot"): record what you observed and leave the call to the next session. +This skill saves state; it does not advance the work. A pending fix, a next step, a question from another session: record where it stands, do not do it now. Work done in the last minute lands unreviewed and the handoff then misreports where things are. Likewise, do not judge pending items closed (a benchmark "dead", a chip "moot"): record what you observed and leave the call to the next session. ### 3. Write the handoff and the memory entry @@ -157,7 +157,7 @@ With `--close`, print the same format with **Saved** for **Saved before exit**, | "The background job is still running, I'll leave it" | Its output dies with the session. Capture what it wrote and say it was unfinished. | | "This is scratch, but committing it is safer" | Scratch in history is noise the next session must undo. Name it in the handoff instead. | | "The handoff is enough, memory is redundant" | Nothing opens the handoff unless memory points at it. | -| "I will finish the pending fix while I am here" | Exit prep records state; it does not advance the work. A last-minute change lands unreviewed, and the handoff then says the wrong thing about where the work stands. | +| "I will finish the pending fix while I am here" | This skill records state; it does not advance the work. A last-minute change lands unreviewed, and the handoff then says the wrong thing about where the work stands. | | "The throwaway directory is gitignored, so I will delete it" | Deleting is destructive and nobody asked. Name it in the handoff as safe to delete. | | "One memory file per loose end keeps things tidy" | Memory holds one pointer and what must survive on its own; the handoff holds the detail. Several files per session bloat the index the next session loads. | | "The benchmark is dead, the chip is moot, I will close them" | Judgments about pending items belong to the next session. Record what you saw (the log stopped at size 2 of 3; the README has no badge) and leave the items open. | diff --git a/plugins/prep-exit/skills/prep-exit/agents/openai.yaml b/plugins/handoff/skills/handoff/agents/openai.yaml similarity index 100% rename from plugins/prep-exit/skills/prep-exit/agents/openai.yaml rename to plugins/handoff/skills/handoff/agents/openai.yaml diff --git a/plugins/prep-compact/skills/prep-compact/scripts/baseline.sh b/plugins/handoff/skills/handoff/scripts/baseline.sh old mode 100644 new mode 100755 similarity index 95% rename from plugins/prep-compact/skills/prep-compact/scripts/baseline.sh rename to plugins/handoff/skills/handoff/scripts/baseline.sh index abc421a..7e399c2 --- a/plugins/prep-compact/skills/prep-compact/scripts/baseline.sh +++ b/plugins/handoff/skills/handoff/scripts/baseline.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# baseline.sh [dir]: the mechanical half of the prep-compact audit, in one call. +# baseline.sh [dir]: the mechanical half of the better-compact and handoff audit, in one call. # # What git knows (branch and upstream, status with counts, the last five commits, # the stash), plus the one thing `git status` never shows and a compact message @@ -51,7 +51,7 @@ stashes=$(git stash list 2>/dev/null | grep -c .) # (portable across BSD and GNU find, which disagree on -newermt). head_epoch=$(git log -1 --format=%ct 2>/dev/null) if [ -n "$head_epoch" ]; then - ref=$(mktemp "${TMPDIR:-/tmp}/prep-compact-ref.XXXXXX") || ref="" + ref=$(mktemp "${TMPDIR:-/tmp}/baseline-ref.XXXXXX") || ref="" if [ -n "$ref" ]; then stamp=$(date -r "$head_epoch" '+%Y%m%d%H%M.%S' 2>/dev/null || date -d "@$head_epoch" '+%Y%m%d%H%M.%S' 2>/dev/null) [ -n "$stamp" ] && touch -t "$stamp" "$ref" 2>/dev/null diff --git a/plugins/prep-exit/skills/prep-exit/scripts/handoff.sh b/plugins/handoff/skills/handoff/scripts/handoff.sh similarity index 95% rename from plugins/prep-exit/skills/prep-exit/scripts/handoff.sh rename to plugins/handoff/skills/handoff/scripts/handoff.sh index d2adbe5..3269d8c 100755 --- a/plugins/prep-exit/skills/prep-exit/scripts/handoff.sh +++ b/plugins/handoff/skills/handoff/scripts/handoff.sh @@ -26,7 +26,7 @@ repo_abs=$(cd "$repo_dir" 2>/dev/null && pwd -P || printf '%s' "$repo_dir") { echo "# Handoff, $stamp" echo - echo "Written by prep-exit before the session ended. A fresh session starts from this file" + echo "Written by handoff before the session ended. A fresh session starts from this file" echo "and from persistent memory; nothing else of that session reaches it." echo echo "Repository: \`$repo_abs\`" diff --git a/plugins/prep-exit/tests/test-prep-exit.sh b/plugins/handoff/tests/test-handoff.sh similarity index 91% rename from plugins/prep-exit/tests/test-prep-exit.sh rename to plugins/handoff/tests/test-handoff.sh index 55385c0..b4c8bd9 100755 --- a/plugins/prep-exit/tests/test-prep-exit.sh +++ b/plugins/handoff/tests/test-handoff.sh @@ -1,13 +1,13 @@ #!/usr/bin/env bash -# Offline checks for the prep-exit scripts: handoff.sh writes the note with the baseline +# Offline checks for the handoff scripts: handoff.sh writes the note with the baseline # embedded and every section header, keeps the previous note, and never fails the caller. set -u here=$(cd "$(dirname "$0")" && pwd) -scripts="$here/../skills/prep-exit/scripts" +scripts="$here/../skills/handoff/scripts" fail=0 check() { if eval "$2"; then echo "ok $1"; else echo "FAIL $1"; fail=1; fi; } -tmp=$(mktemp -d "${TMPDIR:-/tmp}/prep-exit-test.XXXXXX") +tmp=$(mktemp -d "${TMPDIR:-/tmp}/handoff-test.XXXXXX") trap 'rm -rf "$tmp"' EXIT repo="$tmp/repo"; mkdir -p "$repo" export GIT_AUTHOR_NAME=t GIT_AUTHOR_EMAIL=t@example.invalid GIT_COMMITTER_NAME=t GIT_COMMITTER_EMAIL=t@example.invalid HOME="$tmp" diff --git a/plugins/prep-compact/.claude-plugin/plugin.json b/plugins/prep-compact/.claude-plugin/plugin.json deleted file mode 100644 index cb4ae59..0000000 --- a/plugins/prep-compact/.claude-plugin/plugin.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "prep-compact", - "version": "1.10.0", - "description": "Audit in-flight session state before compaction truncates history, and save what would be lost. Commits real work locally, syncs stale plan and state files, and notes chat-only decisions, asking first only for risky items, then prints a paste-ready /compact message plus a queued follow-up.", - "author": { - "name": "Victor De Simone", - "email": "victorsmoliveira@gmail.com", - "url": "https://github.com/vdsmon" - }, - "homepage": "https://github.com/vdsmon/skills", - "repository": "https://github.com/vdsmon/skills", - "license": "MIT", - "skills": "./skills" -} diff --git a/scripts/check.py b/scripts/check.py index 268b678..f545701 100755 --- a/scripts/check.py +++ b/scripts/check.py @@ -16,10 +16,10 @@ PLUGIN_DESC_MAX = 300 FIRST_SENTENCE_MAX = 170 SKILL_LINES_AIM = 100 -# prep-exit ships its own copy of prep-compact's audit script; they must not diverge. +# handoff ships its own copy of better-compact's audit script; they must not diverge. IDENTICAL_COPIES = [ - ("plugins/prep-compact/skills/prep-compact/scripts/baseline.sh", - "plugins/prep-exit/skills/prep-exit/scripts/baseline.sh"), + ("plugins/better-compact/skills/better-compact/scripts/baseline.sh", + "plugins/handoff/skills/handoff/scripts/baseline.sh"), ] # Claude-Code-only features that an unprefixed (portable) plugin must not use.