Compare commits

...

52 Commits

Author SHA1 Message Date
Drew Ritter
b8d9f06cfb fix: close sibling-worktree loophole in already-inside path
Drill scenario worktree-already-inside-spec-aware revealed a latent
skill loophole: agents detecting existing isolation would correctly
refuse to nest a worktree, but then offer options — including exiting
to the main repo and creating a sibling worktree from there — and
execute the sibling option when the actor accepted.

The prior wording "Do NOT create another worktree" got read as
"don't NEST another worktree," leaving sibling creation as an
apparently-legal workaround that satisfied the agent's interpretation
of the directive while violating the skill's actual intent (use the
current workspace, period).

Fix: tighten the already-inside block to be explicit:
- Current workspace is the working environment, use it
- No alternatives, no creation — not nested, not sibling, not anywhere
- If the user genuinely needs different isolation for unrelated work,
  they have to exit the current workspace themselves

Validation pending via drill reruns of worktree-already-inside and
worktree-already-inside-spec-aware on this branch state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:31:11 -07:00
Drew Ritter
884b1a5960 fix: treat direct worktree skill invocation as consent (PRI-974)
The skill previously required an explicit reply to its "do you want a
worktree?" dialogue, which produced obtuse UX when the user invoked the
skill by name — agents had to stop and ask "do you want a worktree?"
even though the user just asked for the skill whose purpose is worktrees.

Loosen Step 2 to recognize the invoking turn as consent: if the user's
most recent message named the skill, asked for a worktree, or asked for
an isolated workspace, proceed directly to Step 3 without re-prompting.
The gate still fires for the transitive case (agent infers isolation
from a feature description) — that remains the #991 failure mode.

Also trim "or skill invocation" from the anti-inference Red Flag and
destale the Integration section now that SDD/executing-plans no longer
require a worktree.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:31:10 -07:00
Drew Ritter
018f3675e5 fix: update stale references and restore silence safety net (PRI-974)
Post-inversion cleanup:

- executing-plans, subagent-driven-development: update Integration
  description from "Ensures isolated workspace" to "Detects workspace
  environment and offers worktree isolation on request"
- codex-tools.md: update step references (Step 0→1, Step 1→2)
- using-git-worktrees Step 2: restore "silence → ask once more" instead
  of "silence → work in place" to preserve safety net for confused users

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:31:10 -07:00
Drew Ritter
7c4597af34 fix: invert worktree skill default to work-in-place, eliminating Step 0.5 (PRI-974)
Agents consistently skipped Step 0.5 (consent gate) because fractional
numbering signals "optional afterthought" and the prose-only step was
invisible to code-block anchoring. The fix inverts the structural
gravity: the default path now works in place, and worktree creation
is an off-ramp requiring explicit user request.

- Renumber to clean integers: Step 1 (detect) → 2 (offer) → 3 (create) → 4 (setup) → 5 (verify)
- Step 2 defaults to Step 4 (in-place); Step 3 only on explicit user ask
- Step 2 includes a code block so agents register it during execution
- Add "creating without being asked" to Common Mistakes
- Add anti-inference red flag: consent from task/plan/skill invocation doesn't count

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:31:10 -07:00
Drew Ritter
b0e08a497f fix: promote consent to own Step 0.5 with structural enforcement (PRI-974)
Drill benchmark showed 0/4 consent compliance across both Claude Code
and Codex. Root cause: consent was buried inline in Step 0's conditional
branch. Agents anchor on the next bash command and skip prose.

Fix: promote consent to its own numbered section with imperative framing
("REQUIRED STOP", "Do NOT proceed without an answer") and exact output
template. Also adds explicit "no" path — users who want to work directly
on their current branch skip to Step 3 with no worktree creation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:31:10 -07:00
Drew Ritter
ddbba8e469 docs: drop brittle step-number chain from multi-repo row
Addresses review feedback on #1123. Replaces "(same Step 0→1a→1b flow,
matching branch names)" with plain-language instruction that doesn't
forward-reference section numbers that could rot under future edits.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:30:00 -07:00
Drew Ritter
f0728841d8 feat: add multi-repo worktree guidance (#710) 2026-04-13 16:29:59 -07:00
Drew Ritter
e3dd3b4c5a fix: replace hardcoded /Users/jesse with generic placeholders (#858) 2026-04-13 16:29:59 -07:00
Drew Ritter
e4a15b6d52 docs: drop instruction file enumeration per PR #1121 review
Jesse flagged that the verbose CLAUDE.md/AGENTS.md/GEMINI.md/.cursorrules
enumeration (a) chews tokens, (b) confuses models that anchor on exact
strings, and (c) is repeated DRY-violatingly across 3+ locations.

Replace with abstract "your instructions" framing in four spots:
- skills/using-git-worktrees/SKILL.md Step 0 → Step 1 transition
- skills/using-git-worktrees/SKILL.md Step 1b Directory Selection
- docs/superpowers/plans/2026-04-06-worktree-rototill.md (both mirror locations)

Same intent, harness-agnostic phrasing, ~half the tokens.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:29:44 -07:00
Drew Ritter
998c40be29 docs: soften Step 1a native-tool framing per PR #1121 review
Address obra's comment on explicit step numbers / prescriptive tone.
Drops "STOP HERE if available", the "If YES:" gate, and the "even if /
even if / NO EXCEPTIONS" reinforcement paragraph. Keeps the specific
tool-name anchors (EnterWorktree, WorktreeCreate, /worktree, --worktree),
which the original TDD data showed are load-bearing.

A/B verified against drill harness on the 3 creation/consent scenarios
(consent-flow, creation-from-main, creation-from-main-spec-aware):
baseline explicit wording scored 12/12 criteria, softened wording also
scored 12/12. The "agent used the most appropriate tool" criterion
passed in all 3 softened runs — agents still picked EnterWorktree via
ToolSearch without the imperative framing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 11:43:45 -07:00
Drew Ritter
98263ce179 docs: address PR #1121 review — respect user preference, drop y/n
- Consent prompt: drop "(y/n)" and add escape valve for users who
  have already declared their worktree preference in global or
  project agent instruction files.
- Directory selection: reorder to put declared user preference
  ahead of observed filesystem state, and reframe the default as
  "if no other guidance available".
- Sandbox fallback: require explicitly informing the user that
  the sandbox blocked creation, not just "report accordingly".
- writing-plans: fully qualify the superpowers:using-git-worktrees
  reference.
- Plan doc: mirror the consent-prompt change.

Step 1a native-tool framing and the helper-scripts suggestion are
still outstanding — the first needs a benchmark re-run before softer
phrasing can be adopted without regressing compliance; the second is
exploratory and will get a thread reply.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 09:53:08 -07:00
Drew Ritter
4c49406d22 fix: remove incorrect hooks symlink step from worktree skill
Git worktrees inherit hooks from the main repo automatically via
$GIT_COMMON_DIR — this has been the case since git 2.5 (2015).
The symlink step was based on an incorrect premise from PR #965
and also fails in practice (.git is a file in worktrees, not a dir).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 10:48:24 -07:00
Drew Ritter
238167f291 docs: cross-platform validation on 5 harnesses (PRI-974)
Tested on Gemini CLI (gemini -p) and Cursor Agent (cursor-agent -p):
- Gemini: Step 0 detection 1/1, Step 1b fallback 1/1
- Cursor: Step 0 detection 1/1, Step 1b fallback 1/1

Both correctly identified no native agent-callable worktree tool,
fell through to git worktree add, and performed safety verification.
Both correctly detected existing worktrees and skipped creation.

5 of 6 harnesses now tested. Only OpenCode untested (no CLI access).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:13:19 -07:00
Drew Ritter
118d85b7e7 docs: honest cross-platform validation table in spec (PRI-974)
Research confirmed Claude Code is currently the only harness with an
agent-callable mid-session worktree tool. All others either create
worktrees before the agent starts (Codex App, Gemini, Cursor) or have
no native support (Codex CLI, OpenCode).

Table now shows: what was actually tested (Claude Code 50/50, Codex CLI
6/6), what was simulated (Codex App 1/1), and what's untested (Gemini,
Cursor, OpenCode). Step 1a is forward-compatible for when other
harnesses add agent-callable tools.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:13:19 -07:00
Drew Ritter
0f4d7d67c1 docs: update spec with TDD findings on Step 1a (PRI-974)
Step 1a's original "deliberately short, abstract" design was disproven
by TDD (2/6 pass rate). Spec now documents the validated approach:
explicit tool naming + consent bridge + red flag (50/50 pass rate).

- Design Principles: updated to reflect explicit naming over abstraction
- Step 1a: replaced abstract text with validated approach, added design
  note explaining the TDD revision and why file splitting was unnecessary
- Risks: Step 1a risk marked RESOLVED with cross-platform validation table
  and residual risk note about upstream tool description dependency

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:13:19 -07:00
Drew Ritter
61ad4821da fix: Step 1a validated through TDD — explicit naming + consent bridge (PRI-974)
Step 1a failed at 2/6 with the spec's original abstract text ("use your
native tool"). Three REFACTOR iterations found what works (50/50 runs):

1. Explicit tool naming — "do you have EnterWorktree, WorktreeCreate..."
   transforms interpretation into factual toolkit check
2. Consent bridge — "user's consent is your authorization" directly
   addresses EnterWorktree's "ONLY when user explicitly asks" guardrail
3. Red Flag entry naming the specific anti-pattern

File split was tested but proven unnecessary — the fix is the Step 1a
text quality, not physical separation of git commands. Control test
with full 240-line skill (all git commands visible) passed 20/20.

Test script updated: supports batch runs (./test.sh green 20), "all"
phase, and checks absence of git worktree add (reliable signal) rather
than presence of EnterWorktree text (agent sometimes omits tool name).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:13:19 -07:00
Drew Ritter
9dd13e534f fix: include worktrees/ (non-hidden) in finishing provenance check (PRI-974)
The creation skill supports both .worktrees/ and worktrees/ directories,
but the finishing skill's cleanup only checked .worktrees/. Worktrees
under the non-hidden path would be orphaned on merge or discard.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:13:19 -07:00
Drew Ritter
77f98c5805 fix: update worktree integration references across skills (PRI-974)
Remove REQUIRED language from executing-plans and subagent-driven-development.
Consent and detection now live inside using-git-worktrees itself.
Fix stale 'created by brainstorming' claim in writing-plans.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:13:19 -07:00
Drew Ritter
c62b835a0b fix: address spec review findings in both skill rewrites (PRI-974)
using-git-worktrees: submodule guard now says "treat as normal repo"
instead of "proceed to Step 1" (preserves consent flow)
using-git-worktrees: directory priority summaries include global legacy

finishing-a-development-branch: move git branch -d after Step 6 cleanup
to make Bug #999 ordering unambiguous (merge -> worktree remove -> branch delete)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:13:19 -07:00
Drew Ritter
5dade17572 feat: rewrite finishing-a-development-branch with detect-and-defer (PRI-974)
Step 2: environment detection (GIT_DIR != GIT_COMMON) before presenting menu
Detached HEAD: reduced 3-option menu (no merge from detached HEAD)
Provenance-based cleanup: .worktrees/ = ours, anything else = hands off
Bug #940: Option 2 no longer cleans up worktree
Bug #999: merge -> verify -> remove worktree -> delete branch
Bug #238: cd to main repo root before git worktree remove
Stale worktree pruning after removal (git worktree prune)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:13:19 -07:00
Drew Ritter
4652e65ec8 feat: rewrite using-git-worktrees with detect-and-defer (PRI-974)
Step 0: GIT_DIR != GIT_COMMON detection (skip if already isolated)
Step 0 consent: opt-in prompt before creating worktree (#991)
Step 1a: native tool preference (short, first, declarative)
Step 1b: git worktree fallback with hooks symlink and legacy path compat
Submodule guard prevents false detection
Platform-neutral instruction file references (#1049)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:13:19 -07:00
Drew Ritter
abaaf8a6e6 test: add RED/GREEN validation for native worktree preference (PRI-974)
Gate test for Step 1a — validates agents prefer EnterWorktree over
git worktree add on Claude Code. Must pass before skill rewrite.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:13:19 -07:00
Drew Ritter
c6d66a0bc7 docs: add worktree rototill implementation plan (PRI-974)
5 tasks: TDD gate for Step 1a, using-git-worktrees rewrite,
finishing-a-development-branch rewrite, integration updates,
end-to-end validation. Task 1 is a hard gate — if native tool
preference fails RED/GREEN, stop and redesign.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 14:22:07 -07:00
Drew Ritter
7ebda5c81b docs: honest spec revisions after issue/PR deep dive
- Step 1a is the load-bearing assumption, not just a risk — if it fails,
  the entire design needs rework. TDD validation must be first impl task.
- #1009 resolution depends on Step 1a working, stated explicitly
- #574 honestly deferred, not "partially addressed"
- Add hooks symlink to Step 1b (PR #965 idea, prevents silent hook loss)
- Add stale worktree pruning to Step 5 (PR #1072 idea, one-line self-heal)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 14:13:54 -07:00
Drew Ritter
2e53549478 docs: address SWE review feedback on worktree rototill spec
- Fix Bug #999 order: merge → verify → remove worktree → delete branch
  (avoids losing work if merge fails after worktree removal)
- Add submodule guard to Step 0 detection (GIT_DIR != GIT_COMMON is also
  true in submodules)
- Preserve global path (~/.config/superpowers/worktrees/) in detection for
  backward compatibility, just stop offering it to new users
- Add step numbering note and implementation notes section
- Expand provenance heuristic to cover global path and manual creation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 14:07:56 -07:00
Drew Ritter
79fee93c4e docs: add worktree rototill design spec (PRI-974)
Design for detect-and-defer worktree support. Superpowers defers to
native harness worktree systems when available, falls back to manual
git worktree creation when not. Covers Phases 0-2: detection, consent,
native tool preference, finishing state detection, and three bug fixes
(#940, #999, #238).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 14:01:48 -07:00
Jesse Vincent
8b9a5da90b docs: update release notes with OpenCode bootstrap change 2026-03-25 17:16:55 -07:00
Jesse Vincent
04ff6660e8 fix(opencode): inject bootstrap as user message instead of system message
Move bootstrap injection from experimental.chat.system.transform to
experimental.chat.messages.transform, prepending to the first user
message instead of adding a system message.

This avoids two issues:
- System messages repeated every turn inflate token usage (#750)
- Multiple system messages break Qwen and other models (#894)

Tested on OpenCode 1.3.2 with Claude Sonnet 4.5 — brainstorming skill
fires correctly on "Let's make a React to do list" prompt.
2026-03-25 17:09:09 -07:00
Jesse Vincent
471aa93a4c docs: add OpenCode path fix to release notes 2026-03-25 14:34:33 -07:00
Jesse Vincent
872172870d fix(opencode): align skills path across bootstrap, runtime, and tests
The bootstrap text advertised a configDir-based skills path that didn't
match the runtime path (resolved relative to the plugin file). Tests
used yet another hardcoded path and referenced a nonexistent lib/ dir.

- Remove misleading skills path from bootstrap text; the agent should
  use the native skill tool, not read files by path
- Fix test setup to create a consistent layout matching the plugin's
  ../../skills resolution
- Export SUPERPOWERS_SKILLS_DIR from setup.sh so tests use a single
  source of truth
- Add regression test that bootstrap doesn't advertise the old path
- Remove broken cp of nonexistent lib/ directory

Fixes #847
2026-03-25 14:29:45 -07:00
Jesse Vincent
ed06287a8a feat: add Copilot CLI tool mapping, docs, and install instructions
- Add references/copilot-tools.md with full tool equivalence table
- Add Copilot CLI to using-superpowers skill platform instructions
- Add marketplace install instructions to README
- Add changelog entry crediting @culinablaz for the hook fix
2026-03-25 14:06:04 -07:00
Blaž Čulina
5406747197 fix: add Copilot CLI platform detection for sessionStart context injection
Copilot CLI v1.0.11 reads `additionalContext` from sessionStart hook
output, but the session-start script only emits the Claude Code-specific
nested format. Add COPILOT_CLI env var detection so Copilot CLI gets the
SDK-standard top-level `additionalContext` while Claude Code continues
getting `hookSpecificOutput`.

Based on PR #910 by @culinablaz.
2026-03-25 14:05:56 -07:00
Jesse Vincent
879940ba5e Release v5.0.6: inline self-review, brainstorm server restructure, owner-PID fixes 2026-03-25 13:11:03 -07:00
Jesse Vincent
0f306f0d18 Merge branch 'fix/owner-pid-lifecycle' into dev 2026-03-24 16:13:30 -07:00
Jesse Vincent
af025aa35b Fix owner-PID lifecycle monitoring for cross-platform reliability
Two bugs caused the brainstorm server to self-terminate within 60s:

1. ownerAlive() treated EPERM (permission denied) as "process dead".
   When the owner PID belongs to a different user (Tailscale SSH,
   system daemons), process.kill(pid, 0) throws EPERM — but the
   process IS alive. Fixed: return e.code === 'EPERM'.

2. On WSL, the grandparent PID resolves to a short-lived subprocess
   that exits before the first 60s lifecycle check. The PID is
   genuinely dead (ESRCH), so the EPERM fix alone doesn't help.
   Fixed: validate the owner PID at server startup — if it's already
   dead, it was a bad resolution, so disable monitoring and rely on
   the 30-minute idle timeout.

This also removes the Windows/MSYS2-specific OWNER_PID="" carve-out
from start-server.sh, since the server now handles invalid PIDs
generically at startup regardless of platform.

Tested on Linux (magic-kingdom) via Tailscale SSH:
- Root-owned owner PID (EPERM): server survives ✓
- Dead owner PID at startup (WSL sim): monitoring disabled, survives ✓
- Valid owner that dies: server shuts down within 60s ✓

Fixes #879
2026-03-24 14:39:20 -07:00
Jesse Vincent
738a18d6ff Fix owner-PID false positive when owner runs as different user
ownerAlive() treated EPERM (permission denied) the same as ESRCH
(process not found), causing the server to self-terminate within 60s
whenever the owner process ran as a different user. This affected WSL
(owner is a Windows process), Tailscale SSH, and any cross-user
scenario.

The fix: `return e.code === 'EPERM'` — if we get permission denied,
the process is alive; we just can't signal it.

Tested on Linux via Tailscale SSH with a root-owned grandparent PID:
- Server survives past the 60s lifecycle check (EPERM = alive)
- Server still shuts down when owner genuinely dies (ESRCH = dead)

Fixes #879
2026-03-24 11:46:29 -07:00
Jesse Vincent
94b2bcbb24 Separate brainstorm server content and state into peer directories
The session directory now contains two peers: content/ (HTML served to
the browser) and state/ (events, server-info, pid, log). Previously
all files shared a single directory, making server state and user
interaction data accessible over the /files/ HTTP route.

Also fixes stale test assertion ("Waiting for Claude" → "Waiting for
the agent").

Reported-By: 吉田仁
2026-03-24 11:07:59 -07:00
Jesse Vincent
ed4103ab91 Revert "Move brainstorm server metadata to .meta/ subdirectory"
This reverts commit ab500dade6.
2026-03-24 10:58:33 -07:00
Jesse Vincent
ab500dade6 Move brainstorm server metadata to .meta/ subdirectory
Metadata files (.server-info, .events, .server.pid, .server.log,
.server-stopped) were stored in the same directory served over HTTP,
making them accessible via the /files/ route. They now live in a .meta/
subdirectory that is not web-accessible.

Also fixes a stale test assertion ("Waiting for Claude" → "Waiting for
the agent").

Reported-By: 吉田仁
2026-03-24 10:56:12 -07:00
Jesse Vincent
a22122d57f Add v5.0.6 release notes 2026-03-24 10:50:38 -07:00
Jesse Vincent
218c3ed93e Merge branch 'main' into dev 2026-03-24 10:44:19 -07:00
Jesse Vincent
9fa8ceb101 Reapply "Replace subagent review loops with lightweight inline self-review"
This reverts commit b045fa3950.
2026-03-24 10:44:09 -07:00
Jesse Vincent
b045fa3950 Revert "Replace subagent review loops with lightweight inline self-review"
This reverts commit bf8f7572eb.
2026-03-24 10:43:58 -07:00
Jesse Vincent
bf8f7572eb Replace subagent review loops with lightweight inline self-review
The subagent review loop (dispatching a fresh agent to review plans/specs)
doubled execution time (~25 min overhead) without measurably improving plan
quality. Regression testing across 5 versions (v3.6.0 through v5.0.4) with
5 trials each showed identical plan sizes, task counts, and quality scores
regardless of whether the review loop ran.

Changes:
- writing-plans: Replace subagent Plan Review Loop with inline Self-Review
  checklist (spec coverage, placeholder scan, type consistency)
- writing-plans: Add explicit "No Placeholders" section listing plan failures
  (TBD, vague descriptions, undefined references, "similar to Task N")
- brainstorming: Replace subagent Spec Review Loop with inline Spec Self-Review
  (placeholder scan, internal consistency, scope check, ambiguity check)
- Both skills now use "look at it with fresh eyes" framing

Testing: 5 trials with the new skill show self-review catches 3-5 real bugs
per run (spawn positions, API mismatches, seed bugs, grid indexing) in ~30s
instead of ~25 min. Remaining defects are comparable to the subagent approach.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 18:50:44 -07:00
Drew Ritter
c141508f36 fix(writing-skills): correct false 'only two fields' frontmatter claim (#882) 2026-03-23 18:20:37 -07:00
Drew Ritter
7820adcde7 docs(codex-tools): add named agent dispatch mapping for Codex (#647) 2026-03-23 17:37:54 -07:00
Drew Ritter
250dea46fd docs: add implementation plan for Codex App compatibility (PRI-823)
8 tasks covering: environment detection in using-git-worktrees,
Step 1.5 + cleanup guard in finishing-a-development-branch,
Integration line updates, codex-tools.md docs, automated tests,
and final verification.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 17:37:54 -07:00
Drew Ritter
477c55386a docs: add cleanup guard test (#5) and sandbox fallback test (#10) to spec
Both tests address real risk scenarios:
- #5: cleanup guard bug would delete Codex App's own worktree (data loss)
- #10: Local thread sandbox fallback needs manual Codex App validation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 17:37:54 -07:00
Drew Ritter
cb4745eeb5 docs: clarify executing-plans in What Does NOT Change section
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 17:37:54 -07:00
Drew Ritter
872ec69f4c docs: address team review feedback for PRI-823 spec
- Add commit SHA + data loss warning to handoff payload (HIGH)
- Add explicit commit step before handoff (HIGH)
- Remove misleading "mark as externally managed" from Path B
- Add executing-plans 1-line edit (was missing)
- Add branch name derivation rules
- Add conditional UI language for non-App environments
- Add sandbox fallback for permission errors
- Add STOP directive after Step 0 reporting

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 17:37:54 -07:00
Drew Ritter
e0fcfaf838 docs: address spec review feedback for PRI-823
Fix three Important issues from spec review:
- Clarify Step 1.5 placement relative to existing Steps 2/3
- Re-derive environment state at cleanup time instead of relying on
  earlier skill output
- Acknowledge pre-existing Step 5 cleanup inconsistency

Also: precise step references, exact codex-tools.md content, clearer
Integration section update instructions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 17:37:54 -07:00
Drew Ritter
5bf3f77483 docs: add Codex App compatibility design spec (PRI-823)
Design for making using-git-worktrees, finishing-a-development-branch,
and subagent-driven-development skills work in the Codex App's sandboxed
worktree environment. Read-only environment detection via git-dir vs
git-common-dir comparison, ~48 lines across 4 files, zero breaking changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 17:37:54 -07:00
36 changed files with 2858 additions and 299 deletions

View File

@@ -9,7 +9,7 @@
{
"name": "superpowers",
"description": "Core skills library for Claude Code: TDD, debugging, collaboration patterns, and proven techniques",
"version": "5.0.5",
"version": "5.0.6",
"source": "./",
"author": {
"name": "Jesse Vincent",

View File

@@ -1,7 +1,7 @@
{
"name": "superpowers",
"description": "Core skills library for Claude Code: TDD, debugging, collaboration patterns, and proven techniques",
"version": "5.0.5",
"version": "5.0.6",
"author": {
"name": "Jesse Vincent",
"email": "jesse@fsck.com"
@@ -9,5 +9,12 @@
"homepage": "https://github.com/obra/superpowers",
"repository": "https://github.com/obra/superpowers",
"license": "MIT",
"keywords": ["skills", "tdd", "debugging", "collaboration", "best-practices", "workflows"]
"keywords": [
"skills",
"tdd",
"debugging",
"collaboration",
"best-practices",
"workflows"
]
}

View File

@@ -2,7 +2,7 @@
"name": "superpowers",
"displayName": "Superpowers",
"description": "Core skills library: TDD, debugging, collaboration patterns, and proven techniques",
"version": "5.0.5",
"version": "5.0.6",
"author": {
"name": "Jesse Vincent",
"email": "jesse@fsck.com"
@@ -10,7 +10,14 @@
"homepage": "https://github.com/obra/superpowers",
"repository": "https://github.com/obra/superpowers",
"license": "MIT",
"keywords": ["skills", "tdd", "debugging", "collaboration", "best-practices", "workflows"],
"keywords": [
"skills",
"tdd",
"debugging",
"collaboration",
"best-practices",
"workflows"
],
"skills": "./skills/",
"agents": "./agents/",
"commands": "./commands/",

View File

@@ -68,8 +68,6 @@ When skills reference tools you don't have, substitute OpenCode equivalents:
- \`Skill\` tool → OpenCode's native \`skill\` tool
- \`Read\`, \`Write\`, \`Edit\`, \`Bash\` → Your native tools
**Skills location:**
Superpowers skills are in \`${configDir}/skills/superpowers/\`
Use OpenCode's native \`skill\` tool to list and load skills.`;
return `<EXTREMELY_IMPORTANT>
@@ -96,12 +94,19 @@ ${toolMapping}
}
},
// Use system prompt transform to inject bootstrap (fixes #226 agent reset bug)
'experimental.chat.system.transform': async (_input, output) => {
// Inject bootstrap into the first user message of each session.
// Using a user message instead of a system message avoids:
// 1. Token bloat from system messages repeated every turn (#750)
// 2. Multiple system messages breaking Qwen and other models (#894)
'experimental.chat.messages.transform': async (_input, output) => {
const bootstrap = getBootstrapContent();
if (bootstrap) {
(output.system ||= []).push(bootstrap);
}
if (!bootstrap || !output.messages.length) return;
const firstUser = output.messages.find(m => m.info.role === 'user');
if (!firstUser || !firstUser.parts.length) return;
// Only inject once
if (firstUser.parts.some(p => p.type === 'text' && p.text.includes('EXTREMELY_IMPORTANT'))) return;
const ref = firstUser.parts[0];
firstUser.parts.unshift({ ...ref, type: 'text', text: bootstrap });
}
};
};

View File

@@ -82,6 +82,13 @@ Fetch and follow instructions from https://raw.githubusercontent.com/obra/superp
**Detailed docs:** [docs/README.opencode.md](docs/README.opencode.md)
### GitHub Copilot CLI
```bash
copilot plugin marketplace add obra/superpowers-marketplace
copilot plugin install superpowers@superpowers-marketplace
```
### Gemini CLI
```bash

View File

@@ -1,5 +1,44 @@
# Superpowers Release Notes
## Unreleased
### GitHub Copilot CLI Support
- **SessionStart context injection** — Copilot CLI v1.0.11 added support for `additionalContext` in sessionStart hook output. The session-start hook now detects the `COPILOT_CLI` environment variable and emits the SDK-standard `{ "additionalContext": "..." }` format, giving Copilot CLI users the full superpowers bootstrap at session start. (Original fix by @culinablaz in PR #910)
- **Tool mapping** — added `references/copilot-tools.md` with the full Claude Code to Copilot CLI tool equivalence table
- **Skill and README updates** — added Copilot CLI to the `using-superpowers` skill's platform instructions and README installation section
### OpenCode Fixes
- **Skills path consistency** — the bootstrap text no longer advertises a misleading `configDir/skills/superpowers/` path that didn't match the runtime path. The agent should use the native `skill` tool, not navigate to files by path. Tests now use consistent paths derived from a single source of truth. (#847, #916)
- **Bootstrap as user message** — moved bootstrap injection from `experimental.chat.system.transform` to `experimental.chat.messages.transform`, prepending to the first user message instead of adding a system message. Avoids token bloat from system messages repeated every turn (#750) and fixes compatibility with Qwen and other models that break on multiple system messages (#894).
## v5.0.6 (2026-03-24)
### Inline Self-Review Replaces Subagent Review Loops
The subagent review loop (dispatching a fresh agent to review plans/specs) doubled execution time (~25 min overhead) without measurably improving plan quality. Regression testing across 5 versions with 5 trials each showed identical quality scores regardless of whether the review loop ran.
- **brainstorming** — replaced Spec Review Loop (subagent dispatch + 3-iteration cap) with inline Spec Self-Review checklist: placeholder scan, internal consistency, scope check, ambiguity check
- **writing-plans** — replaced Plan Review Loop (subagent dispatch + 3-iteration cap) with inline Self-Review checklist: spec coverage, placeholder scan, type consistency
- **writing-plans** — added explicit "No Placeholders" section defining plan failures (TBD, vague descriptions, undefined references, "similar to Task N")
- Self-review catches 3-5 real bugs per run in ~30s instead of ~25 min, with comparable defect rates to the subagent approach
### Brainstorm Server
- **Session directory restructured** — the brainstorm server session directory now contains two peer subdirectories: `content/` (HTML files served to the browser) and `state/` (events, server-info, pid, log). Previously, server state and user interaction data were stored alongside served content, making them accessible over HTTP. The `screen_dir` and `state_dir` paths are both included in the server-started JSON. (Reported by 吉田仁)
### Bug Fixes
- **Owner-PID lifecycle fixes** — the brainstorm server's owner-PID monitoring had two bugs causing false shutdowns within 60 seconds: (1) EPERM from cross-user PIDs (Tailscale SSH, etc.) was treated as "process dead", and (2) on WSL the grandparent PID resolves to a short-lived subprocess that exits before the first lifecycle check. Fixed by treating EPERM as "alive" and validating the owner PID at startup — if it's already dead, monitoring is disabled and the server relies on the 30-minute idle timeout. This also removes the Windows/MSYS2-specific carve-out from `start-server.sh` since the server now handles it generically. (#879)
- **writing-skills** — corrected false claim that SKILL.md frontmatter supports "only two fields"; now says "two required fields" and links to the agentskills.io specification for all supported fields (PR #882 by @arittr)
### Codex App Compatibility
- **codex-tools** — added named agent dispatch mapping documenting how to translate Claude Code's named agent types to Codex's `spawn_agent` with worker roles (PR #647 by @arittr)
- **codex-tools** — added environment detection and Codex App finishing sections for worktree-aware skills (by @arittr)
- **Design spec** — added Codex App compatibility design spec (PRI-823) covering read-only environment detection, worktree-safe skill behavior, and sandbox fallback patterns (by @arittr)
## v5.0.5 (2026-03-17)
### Bug Fixes

View File

@@ -0,0 +1,564 @@
# Codex App Compatibility Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Make `using-git-worktrees`, `finishing-a-development-branch`, and related skills work in the Codex App's sandboxed worktree environment without breaking existing behavior.
**Architecture:** Read-only environment detection (`git-dir` vs `git-common-dir`) at the start of two skills. If already in a linked worktree, skip creation. If on detached HEAD, emit a handoff payload instead of the 4-option menu. Sandbox fallback catches permission errors during worktree creation.
**Tech Stack:** Git, Markdown (skill files are instruction documents, not executable code)
**Spec:** `docs/superpowers/specs/2026-03-23-codex-app-compatibility-design.md`
---
## File Structure
| File | Responsibility | Action |
|---|---|---|
| `skills/using-git-worktrees/SKILL.md` | Worktree creation + isolation | Add Step 0 detection + sandbox fallback |
| `skills/finishing-a-development-branch/SKILL.md` | Branch finishing workflow | Add Step 1.5 detection + cleanup guard |
| `skills/subagent-driven-development/SKILL.md` | Plan execution with subagents | Update Integration description |
| `skills/executing-plans/SKILL.md` | Plan execution inline | Update Integration description |
| `skills/using-superpowers/references/codex-tools.md` | Codex platform reference | Add detection + finishing docs |
---
### Task 1: Add Step 0 to `using-git-worktrees`
**Files:**
- Modify: `skills/using-git-worktrees/SKILL.md:14-15` (insert after Overview, before Directory Selection Process)
- [ ] **Step 1: Read the current skill file**
Read `skills/using-git-worktrees/SKILL.md` in full. Identify the exact insertion point: after the "Announce at start" line (line 14) and before "## Directory Selection Process" (line 16).
- [ ] **Step 2: Insert Step 0 section**
Insert the following between the Overview section and "## Directory Selection Process":
```markdown
## Step 0: Check if Already in an Isolated Workspace
Before creating a worktree, check if one already exists:
```bash
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
BRANCH=$(git branch --show-current)
```
**If `GIT_DIR` differs from `GIT_COMMON`:** You are already inside a linked worktree (created by the Codex App, Claude Code's Agent tool, a previous skill run, or the user). Do NOT create another worktree. Instead:
1. Run project setup (auto-detect package manager as in "Run Project Setup" below)
2. Verify clean baseline (run tests as in "Verify Clean Baseline" below)
3. Report with branch state:
- On a branch: "Already in an isolated workspace at `<path>` on branch `<name>`. Tests passing. Ready to implement."
- Detached HEAD: "Already in an isolated workspace at `<path>` (detached HEAD, externally managed). Tests passing. Note: branch creation needed at finish time. Ready to implement."
After reporting, STOP. Do not continue to Directory Selection or Creation Steps.
**If `GIT_DIR` equals `GIT_COMMON`:** Proceed with the full worktree creation flow below.
**Sandbox fallback:** If you proceed to Creation Steps but `git worktree add -b` fails with a permission error (e.g., "Operation not permitted"), treat this as a late-detected restricted environment. Fall back to the behavior above — run setup and baseline tests in the current directory, report accordingly, and STOP.
```
- [ ] **Step 3: Verify the insertion**
Read the file again. Confirm:
- Step 0 appears between Overview and Directory Selection Process
- The rest of the file (Directory Selection, Safety Verification, Creation Steps, etc.) is unchanged
- No duplicate sections or broken markdown
- [ ] **Step 4: Commit**
```bash
git add skills/using-git-worktrees/SKILL.md
git commit -m "feat(using-git-worktrees): add Step 0 environment detection (PRI-823)
Skip worktree creation when already in a linked worktree. Includes
sandbox fallback for permission errors on git worktree add."
```
---
### Task 2: Update `using-git-worktrees` Integration section
**Files:**
- Modify: `skills/using-git-worktrees/SKILL.md:211-215` (Integration > Called by)
- [ ] **Step 1: Update the three "Called by" entries**
Change lines 212-214 from:
```markdown
- **brainstorming** (Phase 4) - REQUIRED when design is approved and implementation follows
- **subagent-driven-development** - REQUIRED before executing any tasks
- **executing-plans** - REQUIRED before executing any tasks
```
To:
```markdown
- **brainstorming** - REQUIRED: Ensures isolated workspace (creates one or verifies existing)
- **subagent-driven-development** - REQUIRED: Ensures isolated workspace (creates one or verifies existing)
- **executing-plans** - REQUIRED: Ensures isolated workspace (creates one or verifies existing)
```
- [ ] **Step 2: Verify the Integration section**
Read the Integration section. Confirm all three entries are updated, "Pairs with" is unchanged.
- [ ] **Step 3: Commit**
```bash
git add skills/using-git-worktrees/SKILL.md
git commit -m "docs(using-git-worktrees): update Integration descriptions (PRI-823)
Clarify that skill ensures a workspace exists, not that it always creates one."
```
---
### Task 3: Add Step 1.5 to `finishing-a-development-branch`
**Files:**
- Modify: `skills/finishing-a-development-branch/SKILL.md:38` (insert after Step 1, before Step 2)
- [ ] **Step 1: Read the current skill file**
Read `skills/finishing-a-development-branch/SKILL.md` in full. Identify the insertion point: after "**If tests pass:** Continue to Step 2." (line 38) and before "### Step 2: Determine Base Branch" (line 40).
- [ ] **Step 2: Insert Step 1.5 section**
Insert the following between Step 1 and Step 2:
```markdown
### Step 1.5: Detect Environment
```bash
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
BRANCH=$(git branch --show-current)
```
**Path A — `GIT_DIR` differs from `GIT_COMMON` AND `BRANCH` is empty (externally managed worktree, detached HEAD):**
First, ensure all work is staged and committed (`git add` + `git commit`).
Then present this to the user (do NOT present the 4-option menu):
```
Implementation complete. All tests passing.
Current HEAD: <full-commit-sha>
This workspace is externally managed (detached HEAD).
I cannot create branches, push, or open PRs from here.
⚠ These commits are on a detached HEAD. If you do not create a branch,
they may be lost when this workspace is cleaned up.
If your host application provides these controls:
- "Create branch" — to name a branch, then commit/push/PR
- "Hand off to local" — to move changes to your local checkout
Suggested branch name: <ticket-id/short-description>
Suggested commit message: <summary-of-work>
```
Branch name: use ticket ID if available (e.g., `pri-823/codex-compat`), otherwise slugify the first 5 words of the plan title, otherwise omit. Avoid sensitive content in branch names.
Skip to Step 5 (cleanup is a no-op — see guard below).
**Path B — `GIT_DIR` differs from `GIT_COMMON` AND `BRANCH` exists (externally managed worktree, named branch):**
Proceed to Step 2 and present the 4-option menu as normal.
**Path C — `GIT_DIR` equals `GIT_COMMON` (normal environment):**
Proceed to Step 2 and present the 4-option menu as normal.
```
- [ ] **Step 3: Verify the insertion**
Read the file again. Confirm:
- Step 1.5 appears between Step 1 and Step 2
- Steps 2-5 are unchanged
- Path A handoff includes commit SHA and data loss warning
- Paths B and C proceed to Step 2 normally
- [ ] **Step 4: Commit**
```bash
git add skills/finishing-a-development-branch/SKILL.md
git commit -m "feat(finishing-a-development-branch): add Step 1.5 environment detection (PRI-823)
Detect externally managed worktrees with detached HEAD and emit handoff
payload instead of 4-option menu. Includes commit SHA and data loss warning."
```
---
### Task 4: Add Step 5 cleanup guard to `finishing-a-development-branch`
**Files:**
- Modify: `skills/finishing-a-development-branch/SKILL.md` (Step 5: Cleanup Worktree — find by section heading, line numbers will have shifted after Task 3)
- [ ] **Step 1: Read the current Step 5 section**
Find the "### Step 5: Cleanup Worktree" section in `skills/finishing-a-development-branch/SKILL.md` (line numbers will have shifted after Task 3's insertion). The current Step 5 is:
```markdown
### Step 5: Cleanup Worktree
**For Options 1, 2, 4:**
Check if in worktree:
```bash
git worktree list | grep $(git branch --show-current)
```
If yes:
```bash
git worktree remove <worktree-path>
```
**For Option 3:** Keep worktree.
```
- [ ] **Step 2: Add the cleanup guard before existing logic**
Replace the Step 5 section with:
```markdown
### Step 5: Cleanup Worktree
**First, check if worktree is externally managed:**
```bash
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
```
If `GIT_DIR` differs from `GIT_COMMON`: skip worktree removal — the host environment owns this workspace.
**Otherwise, for Options 1 and 4:**
Check if in worktree:
```bash
git worktree list | grep $(git branch --show-current)
```
If yes:
```bash
git worktree remove <worktree-path>
```
**For Option 3:** Keep worktree.
```
Note: the original text said "For Options 1, 2, 4" but the Quick Reference table and Common Mistakes section say "Options 1 & 4 only." This edit aligns Step 5 with those sections.
- [ ] **Step 3: Verify the replacement**
Read Step 5. Confirm:
- Cleanup guard (re-detection) appears first
- Existing removal logic preserved for non-externally-managed worktrees
- "Options 1 and 4" (not "1, 2, 4") matches Quick Reference and Common Mistakes
- [ ] **Step 4: Commit**
```bash
git add skills/finishing-a-development-branch/SKILL.md
git commit -m "feat(finishing-a-development-branch): add Step 5 cleanup guard (PRI-823)
Re-detect externally managed worktree at cleanup time and skip removal.
Also fixes pre-existing inconsistency: cleanup now correctly says
Options 1 and 4 only, matching Quick Reference and Common Mistakes."
```
---
### Task 5: Update Integration lines in `subagent-driven-development` and `executing-plans`
**Files:**
- Modify: `skills/subagent-driven-development/SKILL.md:268`
- Modify: `skills/executing-plans/SKILL.md:68`
- [ ] **Step 1: Update `subagent-driven-development`**
Change line 268 from:
```
- **superpowers:using-git-worktrees** - REQUIRED: Set up isolated workspace before starting
```
To:
```
- **superpowers:using-git-worktrees** - REQUIRED: Ensures isolated workspace (creates one or verifies existing)
```
- [ ] **Step 2: Update `executing-plans`**
Change line 68 from:
```
- **superpowers:using-git-worktrees** - REQUIRED: Set up isolated workspace before starting
```
To:
```
- **superpowers:using-git-worktrees** - REQUIRED: Ensures isolated workspace (creates one or verifies existing)
```
- [ ] **Step 3: Verify both files**
Read line 268 of `skills/subagent-driven-development/SKILL.md` and line 68 of `skills/executing-plans/SKILL.md`. Confirm both say "Ensures isolated workspace (creates one or verifies existing)".
- [ ] **Step 4: Commit**
```bash
git add skills/subagent-driven-development/SKILL.md skills/executing-plans/SKILL.md
git commit -m "docs(sdd, executing-plans): update worktree Integration descriptions (PRI-823)
Clarify that using-git-worktrees ensures a workspace exists rather than
always creating one."
```
---
### Task 6: Add environment detection docs to `codex-tools.md`
**Files:**
- Modify: `skills/using-superpowers/references/codex-tools.md:25` (append at end)
- [ ] **Step 1: Read the current file**
Read `skills/using-superpowers/references/codex-tools.md` in full. Confirm it ends at line 25-26 after the multi_agent section.
- [ ] **Step 2: Append two new sections**
Add at the end of the file:
```markdown
## Environment Detection
Skills that create worktrees or finish branches should detect their
environment with read-only git commands before proceeding:
```bash
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
BRANCH=$(git branch --show-current)
```
- `GIT_DIR != GIT_COMMON` → already in a linked worktree (skip creation)
- `BRANCH` empty → detached HEAD (cannot branch/push/PR from sandbox)
See `using-git-worktrees` Step 0 and `finishing-a-development-branch`
Step 1.5 for how each skill uses these signals.
## Codex App Finishing
When the sandbox blocks branch/push operations (detached HEAD in an
externally managed worktree), the agent commits all work and informs
the user to use the App's native controls:
- **"Create branch"** — names the branch, then commit/push/PR via App UI
- **"Hand off to local"** — transfers work to the user's local checkout
The agent can still run tests, stage files, and output suggested branch
names, commit messages, and PR descriptions for the user to copy.
```
- [ ] **Step 3: Verify the additions**
Read the full file. Confirm:
- Two new sections appear after the existing content
- Bash code block renders correctly (not escaped)
- Cross-references to Step 0 and Step 1.5 are present
- [ ] **Step 4: Commit**
```bash
git add skills/using-superpowers/references/codex-tools.md
git commit -m "docs(codex-tools): add environment detection and App finishing docs (PRI-823)
Document the git-dir vs git-common-dir detection pattern and the Codex
App's native finishing flow for skills that need to adapt."
```
---
### Task 7: Automated test — environment detection
**Files:**
- Create: `tests/codex-app-compat/test-environment-detection.sh`
- [ ] **Step 1: Create test directory**
```bash
mkdir -p tests/codex-app-compat
```
- [ ] **Step 2: Write the detection test script**
Create `tests/codex-app-compat/test-environment-detection.sh`:
```bash
#!/usr/bin/env bash
set -euo pipefail
# Test environment detection logic from PRI-823
# Tests the git-dir vs git-common-dir comparison used by
# using-git-worktrees Step 0 and finishing-a-development-branch Step 1.5
PASS=0
FAIL=0
TEMP_DIR=$(mktemp -d)
trap "rm -rf $TEMP_DIR" EXIT
log_pass() { echo " PASS: $1"; PASS=$((PASS + 1)); }
log_fail() { echo " FAIL: $1"; FAIL=$((FAIL + 1)); }
# Helper: run detection and return "linked" or "normal"
detect_worktree() {
local git_dir git_common
git_dir=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
git_common=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
if [ "$git_dir" != "$git_common" ]; then
echo "linked"
else
echo "normal"
fi
}
echo "=== Test 1: Normal repo detection ==="
cd "$TEMP_DIR"
git init test-repo > /dev/null 2>&1
cd test-repo
git commit --allow-empty -m "init" > /dev/null 2>&1
result=$(detect_worktree)
if [ "$result" = "normal" ]; then
log_pass "Normal repo detected as normal"
else
log_fail "Normal repo detected as '$result' (expected 'normal')"
fi
echo "=== Test 2: Linked worktree detection ==="
git worktree add "$TEMP_DIR/test-wt" -b test-branch > /dev/null 2>&1
cd "$TEMP_DIR/test-wt"
result=$(detect_worktree)
if [ "$result" = "linked" ]; then
log_pass "Linked worktree detected as linked"
else
log_fail "Linked worktree detected as '$result' (expected 'linked')"
fi
echo "=== Test 3: Detached HEAD detection ==="
git checkout --detach HEAD > /dev/null 2>&1
branch=$(git branch --show-current)
if [ -z "$branch" ]; then
log_pass "Detached HEAD: branch is empty"
else
log_fail "Detached HEAD: branch is '$branch' (expected empty)"
fi
echo "=== Test 4: Linked worktree + detached HEAD (Codex App simulation) ==="
result=$(detect_worktree)
branch=$(git branch --show-current)
if [ "$result" = "linked" ] && [ -z "$branch" ]; then
log_pass "Codex App simulation: linked + detached HEAD"
else
log_fail "Codex App simulation: result='$result', branch='$branch'"
fi
echo "=== Test 5: Cleanup guard — linked worktree should NOT remove ==="
cd "$TEMP_DIR/test-wt"
result=$(detect_worktree)
if [ "$result" = "linked" ]; then
log_pass "Cleanup guard: linked worktree correctly detected (would skip removal)"
else
log_fail "Cleanup guard: expected 'linked', got '$result'"
fi
echo "=== Test 6: Cleanup guard — main repo SHOULD remove ==="
cd "$TEMP_DIR/test-repo"
result=$(detect_worktree)
if [ "$result" = "normal" ]; then
log_pass "Cleanup guard: main repo correctly detected (would proceed with removal)"
else
log_fail "Cleanup guard: expected 'normal', got '$result'"
fi
# Cleanup worktree before temp dir removal
cd "$TEMP_DIR/test-repo"
git worktree remove "$TEMP_DIR/test-wt" > /dev/null 2>&1 || true
echo ""
echo "=== Results: $PASS passed, $FAIL failed ==="
if [ "$FAIL" -gt 0 ]; then
exit 1
fi
```
- [ ] **Step 3: Make it executable and run it**
```bash
chmod +x tests/codex-app-compat/test-environment-detection.sh
./tests/codex-app-compat/test-environment-detection.sh
```
Expected output: 6 passed, 0 failed.
- [ ] **Step 4: Commit**
```bash
git add tests/codex-app-compat/test-environment-detection.sh
git commit -m "test: add environment detection tests for Codex App compat (PRI-823)
Tests git-dir vs git-common-dir comparison in normal repo, linked
worktree, detached HEAD, and cleanup guard scenarios."
```
---
### Task 8: Final verification
**Files:**
- Read: all 5 modified skill files
- [ ] **Step 1: Run the automated detection tests**
```bash
./tests/codex-app-compat/test-environment-detection.sh
```
Expected: 6 passed, 0 failed.
- [ ] **Step 2: Read each modified file and verify changes**
Read each file end-to-end:
- `skills/using-git-worktrees/SKILL.md` — Step 0 present, rest unchanged
- `skills/finishing-a-development-branch/SKILL.md` — Step 1.5 present, cleanup guard present, rest unchanged
- `skills/subagent-driven-development/SKILL.md` — line 268 updated
- `skills/executing-plans/SKILL.md` — line 68 updated
- `skills/using-superpowers/references/codex-tools.md` — two new sections at end
- [ ] **Step 3: Verify no unintended changes**
```bash
git diff --stat HEAD~7
```
Should show exactly 6 files changed (5 skill files + 1 test file). No other files modified.
- [ ] **Step 4: Run existing test suite**
If test runner exists:
```bash
# Run skill-triggering tests
./tests/skill-triggering/run-all.sh 2>/dev/null || echo "Skill triggering tests not available in this environment"
# Run SDD integration test
./tests/claude-code/test-subagent-driven-development-integration.sh 2>/dev/null || echo "SDD integration test not available in this environment"
```
Note: these tests require Claude Code with `--dangerously-skip-permissions`. If not available, document that regression tests should be run manually.

View File

@@ -0,0 +1,879 @@
# Worktree Rototill Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Make superpowers defer to native harness worktree systems when available, fall back to manual git worktrees when not, and fix three known finishing bugs.
**Architecture:** Two skill files are rewritten (`using-git-worktrees`, `finishing-a-development-branch`), three files get one-line integration updates (`executing-plans`, `subagent-driven-development`, `writing-plans`). The core change is adding detection (`GIT_DIR != GIT_COMMON`) and a native-tool-first creation path. These are markdown skill instruction files, not application code — "tests" are agent behavior tests using the testing-skills-with-subagents TDD framework.
**Tech Stack:** Markdown (skill files), bash (test scripts), Claude Code CLI (`claude -p` for headless testing)
**Spec:** `docs/superpowers/specs/2026-04-06-worktree-rototill-design.md`
---
### Task 1: GATE — TDD Validation of Step 1a (Native Tool Preference)
Step 1a is the load-bearing assumption of the entire design. If agents don't prefer native worktree tools over `git worktree add`, the spec fails. Validate this FIRST, before touching any skill files.
**Files:**
- Create: `tests/claude-code/test-worktree-native-preference.sh`
- Read: `skills/using-git-worktrees/SKILL.md` (current version, for RED baseline)
- Read: `tests/claude-code/test-helpers.sh` (for `run_claude`, `assert_contains`, etc.)
- Read: `skills/writing-skills/testing-skills-with-subagents.md` (TDD framework)
**This task is a gate.** If the GREEN phase fails after 2 REFACTOR iterations, STOP. Do not proceed to Task 2. Report back — the creation approach needs redesign.
- [ ] **Step 1: Write the RED baseline test script**
Create the test script that will run scenarios both WITHOUT and WITH the updated skill text. The RED phase runs against the current skill (which has no Step 1a).
```bash
#!/usr/bin/env bash
# Test: Does the agent prefer native worktree tools (EnterWorktree) over git worktree add?
# Framework: RED-GREEN-REFACTOR per testing-skills-with-subagents.md
#
# RED: Current skill has no native tool preference. Agent should use git worktree add.
# GREEN: Updated skill has Step 1a. Agent should use EnterWorktree on Claude Code.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "$SCRIPT_DIR/test-helpers.sh"
# Pressure scenario: realistic implementation task where agent needs isolation
SCENARIO='IMPORTANT: This is a real task. Choose and act.
You need to implement a small feature (add a "version" field to package.json).
This should be done in an isolated workspace to protect the main branch.
You have the using-git-worktrees skill available. Set up the isolated workspace now.
Do NOT actually implement the feature — just set up the workspace and report what you did.
Respond with EXACTLY what tool/command you used to create the workspace.'
echo "=== Worktree Native Preference Test ==="
echo ""
# Phase selection
PHASE="${1:-red}"
if [ "$PHASE" = "red" ]; then
echo "--- RED PHASE: Running WITHOUT Step 1a (current skill) ---"
echo "Expected: Agent uses 'git worktree add' (no native tool awareness)"
echo ""
test_dir=$(create_test_project)
cd "$test_dir"
git init && git commit --allow-empty -m "init"
mkdir -p .worktrees
output=$(run_claude "$SCENARIO" 120)
echo "Agent output:"
echo "$output"
echo ""
# RED expectation: agent uses git worktree add (current behavior)
if echo "$output" | grep -qi "EnterWorktree"; then
echo "[UNEXPECTED] Agent used EnterWorktree WITHOUT Step 1a — skill may not be needed"
echo "Investigate: is Claude Code's default behavior already correct?"
else
echo "[RED CONFIRMED] Agent did NOT use EnterWorktree"
assert_contains "$output" "git worktree" "Agent used git worktree (expected in RED)"
fi
cleanup_test_project "$test_dir"
elif [ "$PHASE" = "green" ]; then
echo "--- GREEN PHASE: Running WITH Step 1a (updated skill) ---"
echo "Expected: Agent uses EnterWorktree instead of git worktree add"
echo ""
test_dir=$(create_test_project)
cd "$test_dir"
git init && git commit --allow-empty -m "init"
output=$(run_claude "$SCENARIO" 120)
echo "Agent output:"
echo "$output"
echo ""
# GREEN expectation: agent uses EnterWorktree
assert_contains "$output" "EnterWorktree" "Agent used native EnterWorktree tool"
assert_not_contains "$output" "git worktree add" "Agent did NOT fall back to git worktree add"
cleanup_test_project "$test_dir"
elif [ "$PHASE" = "pressure" ]; then
echo "--- PRESSURE PHASE: Multiple pressures to skip native tools ---"
echo ""
PRESSURE_SCENARIO='IMPORTANT: This is a real task. Choose and act.
You need to set up an isolated workspace URGENTLY. Production is impacted and
the fix needs to go in a worktree. You know git worktree add works reliably.
The .worktrees/ directory already exists and is gitignored.
You have the using-git-worktrees skill. Set up the workspace NOW.
Speed matters. Report EXACTLY what tool/command you used.'
test_dir=$(create_test_project)
cd "$test_dir"
git init && git commit --allow-empty -m "init"
mkdir -p .worktrees
echo ".worktrees/" >> .gitignore
output=$(run_claude "$PRESSURE_SCENARIO" 120)
echo "Agent output:"
echo "$output"
echo ""
# Should STILL use EnterWorktree even under pressure
assert_contains "$output" "EnterWorktree" "Agent used native tool even under time pressure"
assert_not_contains "$output" "git worktree add" "Agent resisted falling back to git despite pressure"
cleanup_test_project "$test_dir"
fi
echo ""
echo "=== Test Complete ==="
```
- [ ] **Step 2: Run RED phase — confirm agent uses git worktree add today**
Run: `cd tests/claude-code && bash test-worktree-native-preference.sh red`
Expected: `[RED CONFIRMED] Agent did NOT use EnterWorktree` — agent uses `git worktree add` because current skill has no native tool preference.
Document the agent's exact output and any rationalizations verbatim. This is the baseline failure the skill must fix.
- [ ] **Step 3: If RED confirmed, proceed. Write the Step 1a skill text.**
Create a temporary test version of the skill with ONLY the Step 1a addition (minimal change to isolate the variable). Add this section to the top of the skill's creation instructions, BEFORE the existing directory selection process:
```markdown
## Step 1: Create Isolated Workspace
**You have two mechanisms. Try them in this order.**
### 1a. Native Worktree Tools (preferred)
If your platform provides a worktree or workspace-isolation tool, use it. You know your own toolkit — the skill does not need to name specific tools. Native tools handle directory placement, branch creation, and cleanup automatically.
After using a native tool, skip to Step 3 (Project Setup).
### 1b. Git Worktree Fallback
If no native tool is available, create a worktree manually using git.
```
- [ ] **Step 4: Run GREEN phase — confirm agent now uses EnterWorktree**
Run: `cd tests/claude-code && bash test-worktree-native-preference.sh green`
Expected: `[PASS] Agent used native EnterWorktree tool`
If FAIL: Document the agent's exact output and rationalizations. This is a REFACTOR signal — the Step 1a text needs revision. Try up to 2 REFACTOR iterations. If still failing after 2 iterations, STOP and report back.
- [ ] **Step 5: Run PRESSURE phase — confirm agent resists fallback under pressure**
Run: `cd tests/claude-code && bash test-worktree-native-preference.sh pressure`
Expected: `[PASS] Agent used native tool even under time pressure`
If FAIL: Document rationalizations verbatim. Add explicit counters to Step 1a text (e.g., a Red Flag entry: "Never use git worktree add when your platform provides a native worktree tool"). Re-run.
- [ ] **Step 6: Commit test script**
```bash
git add tests/claude-code/test-worktree-native-preference.sh
git commit -m "test: add RED/GREEN validation for native worktree preference (PRI-974)
Gate test for Step 1a — validates agents prefer EnterWorktree over
git worktree add on Claude Code. Must pass before skill rewrite."
```
---
### Task 2: Rewrite `using-git-worktrees` SKILL.md
Full rewrite of the creation skill. Replaces the existing file entirely.
**Files:**
- Modify: `skills/using-git-worktrees/SKILL.md` (full rewrite, 219 lines → ~210 lines)
**Depends on:** Task 1 GREEN passing.
- [ ] **Step 1: Write the complete new SKILL.md**
Replace the entire contents of `skills/using-git-worktrees/SKILL.md` with:
```markdown
---
name: using-git-worktrees
description: Use when starting feature work that needs isolation from current workspace or before executing implementation plans - ensures an isolated workspace exists via native tools or git worktree fallback
---
# Using Git Worktrees
## Overview
Ensure work happens in an isolated workspace. Prefer your platform's native worktree tools. Fall back to manual git worktrees only when no native tool is available.
**Core principle:** Detect existing isolation first. Then use native tools. Then fall back to git. Never fight the harness.
**Announce at start:** "I'm using the using-git-worktrees skill to set up an isolated workspace."
## Step 0: Detect Existing Isolation
**Before creating anything, check if you are already in an isolated workspace.**
```bash
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
BRANCH=$(git branch --show-current)
```
**Submodule guard:** `GIT_DIR != GIT_COMMON` is also true inside git submodules. Before concluding "already in a worktree," verify you are not in a submodule:
```bash
# If this returns a path, you're in a submodule, not a worktree — proceed to Step 1
git rev-parse --show-superproject-working-tree 2>/dev/null
```
**If `GIT_DIR != GIT_COMMON` (and not a submodule):** You are already in a linked worktree. Skip to Step 3 (Project Setup). Do NOT create another worktree.
Report with branch state:
- On a branch: "Already in isolated workspace at `<path>` on branch `<name>`."
- Detached HEAD: "Already in isolated workspace at `<path>` (detached HEAD, externally managed). Branch creation needed at finish time."
**If `GIT_DIR == GIT_COMMON` (or in a submodule):** You are in a normal repo checkout.
Has the user already indicated their worktree preference in your instructions? If not, ask for consent before creating a worktree:
> "Would you like me to set up an isolated worktree? It protects your current branch from changes."
Honor any existing declared preference without asking. If the user declines consent, work in place and skip to Step 3.
## Step 1: Create Isolated Workspace
**You have two mechanisms. Try them in this order.**
### 1a. Native Worktree Tools (preferred)
If your platform provides a worktree or workspace-isolation tool, use it. You know your own toolkit — the skill does not need to name specific tools. Native tools handle directory placement, branch creation, and cleanup automatically.
After using a native tool, skip to Step 3 (Project Setup).
### 1b. Git Worktree Fallback
If no native tool is available, create a worktree manually using git.
#### Directory Selection
Follow this priority order:
1. **Check existing directories:**
```bash
ls -d .worktrees 2>/dev/null # Preferred (hidden)
ls -d worktrees 2>/dev/null # Alternative
```
If found, use that directory. If both exist, `.worktrees` wins.
2. **Check for existing global directory:**
```bash
project=$(basename "$(git rev-parse --show-toplevel)")
ls -d ~/.config/superpowers/worktrees/$project 2>/dev/null
```
If found, use it (backward compatibility with legacy global path).
3. **Check your instructions for a worktree directory preference.** If specified, use it without asking.
4. **Default to `.worktrees/`.**
#### Safety Verification (project-local directories only)
**MUST verify directory is ignored before creating worktree:**
```bash
git check-ignore -q .worktrees 2>/dev/null || git check-ignore -q worktrees 2>/dev/null
```
**If NOT ignored:** Add to .gitignore, commit the change, then proceed.
**Why critical:** Prevents accidentally committing worktree contents to repository.
Global directories (`~/.config/superpowers/worktrees/`) need no verification.
#### Create the Worktree
```bash
project=$(basename "$(git rev-parse --show-toplevel)")
# Determine path based on chosen location
# For project-local: path="$LOCATION/$BRANCH_NAME"
# For global: path="~/.config/superpowers/worktrees/$project/$BRANCH_NAME"
git worktree add "$path" -b "$BRANCH_NAME"
cd "$path"
```
#### Hooks Awareness
Git worktrees do not inherit the parent repo's hooks directory. After creating the worktree, symlink hooks from the main repo if they exist:
```bash
MAIN_ROOT=$(git -C "$(git rev-parse --git-common-dir)/.." rev-parse --show-toplevel)
if [ -d "$MAIN_ROOT/.git/hooks" ]; then
ln -sf "$MAIN_ROOT/.git/hooks" "$path/.git/hooks"
fi
```
This prevents pre-commit checks, linters, and other hooks from silently stopping when work moves to a worktree.
**Sandbox fallback:** If `git worktree add` fails with a permission error (sandbox denial), treat this as a restricted environment. Skip creation, run setup and baseline tests in the current directory, report accordingly.
## Step 3: Project Setup
Auto-detect and run appropriate setup:
```bash
# Node.js
if [ -f package.json ]; then npm install; fi
# Rust
if [ -f Cargo.toml ]; then cargo build; fi
# Python
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
if [ -f pyproject.toml ]; then poetry install; fi
# Go
if [ -f go.mod ]; then go mod download; fi
```
## Step 4: Verify Clean Baseline
Run tests to ensure workspace starts clean:
```bash
# Use project-appropriate command
npm test / cargo test / pytest / go test ./...
```
**If tests fail:** Report failures, ask whether to proceed or investigate.
**If tests pass:** Report ready.
### Report
```
Worktree ready at <full-path>
Tests passing (<N> tests, 0 failures)
Ready to implement <feature-name>
```
## Quick Reference
| Situation | Action |
|-----------|--------|
| Already in linked worktree | Skip creation (Step 0) |
| In a submodule | Treat as normal repo (Step 0 guard) |
| Native worktree tool available | Use it (Step 1a) |
| No native tool | Git worktree fallback (Step 1b) |
| `.worktrees/` exists | Use it (verify ignored) |
| `worktrees/` exists | Use it (verify ignored) |
| Both exist | Use `.worktrees/` |
| Neither exists | Check instruction file, then default `.worktrees/` |
| Global path exists | Use it (backward compat) |
| Directory not ignored | Add to .gitignore + commit |
| Permission error on create | Sandbox fallback, work in place |
| Tests fail during baseline | Report failures + ask |
| No package.json/Cargo.toml | Skip dependency install |
## Common Mistakes
### Fighting the harness
- **Problem:** Using `git worktree add` when the platform already provides isolation
- **Fix:** Step 0 detects existing isolation. Step 1a defers to native tools.
### Skipping detection
- **Problem:** Creating a nested worktree inside an existing one
- **Fix:** Always run Step 0 before creating anything
### Skipping ignore verification
- **Problem:** Worktree contents get tracked, pollute git status
- **Fix:** Always use `git check-ignore` before creating project-local worktree
### Assuming directory location
- **Problem:** Creates inconsistency, violates project conventions
- **Fix:** Follow priority: existing > instruction file > default
### Proceeding with failing tests
- **Problem:** Can't distinguish new bugs from pre-existing issues
- **Fix:** Report failures, get explicit permission to proceed
## Red Flags
**Never:**
- Create a worktree when Step 0 detects existing isolation
- Use git commands when a native worktree tool is available
- Create worktree without verifying it's ignored (project-local)
- Skip baseline test verification
- Proceed with failing tests without asking
**Always:**
- Run Step 0 detection first
- Prefer native tools over git fallback
- Follow directory priority: existing > instruction file > default
- Verify directory is ignored for project-local
- Auto-detect and run project setup
- Verify clean test baseline
- Symlink hooks after creating worktree via 1b
## Integration
**Called by:**
- **subagent-driven-development** - Ensures isolated workspace (creates one or verifies existing)
- **executing-plans** - Ensures isolated workspace (creates one or verifies existing)
- Any skill needing isolated workspace
**Pairs with:**
- **finishing-a-development-branch** - REQUIRED for cleanup after work complete
```
- [ ] **Step 2: Verify the file reads correctly**
Run: `wc -l skills/using-git-worktrees/SKILL.md`
Expected: Approximately 200-220 lines. Scan for any markdown formatting issues.
- [ ] **Step 3: Commit**
```bash
git add skills/using-git-worktrees/SKILL.md
git commit -m "feat: rewrite using-git-worktrees with detect-and-defer (PRI-974)
Step 0: GIT_DIR != GIT_COMMON detection (skip if already isolated)
Step 0 consent: opt-in prompt before creating worktree (#991)
Step 1a: native tool preference (short, first, declarative)
Step 1b: git worktree fallback with hooks symlink and legacy path compat
Submodule guard prevents false detection
Platform-neutral instruction file references (#1049)"
```
---
### Task 3: Rewrite `finishing-a-development-branch` SKILL.md
Full rewrite of the finishing skill. Adds environment detection, fixes three bugs, adds provenance-based cleanup.
**Files:**
- Modify: `skills/finishing-a-development-branch/SKILL.md` (full rewrite, 201 lines → ~220 lines)
- [ ] **Step 1: Write the complete new SKILL.md**
Replace the entire contents of `skills/finishing-a-development-branch/SKILL.md` with:
```markdown
---
name: finishing-a-development-branch
description: Use when implementation is complete, all tests pass, and you need to decide how to integrate the work - guides completion of development work by presenting structured options for merge, PR, or cleanup
---
# Finishing a Development Branch
## Overview
Guide completion of development work by presenting clear options and handling chosen workflow.
**Core principle:** Verify tests → Detect environment → Present options → Execute choice → Clean up.
**Announce at start:** "I'm using the finishing-a-development-branch skill to complete this work."
## The Process
### Step 1: Verify Tests
**Before presenting options, verify tests pass:**
```bash
# Run project's test suite
npm test / cargo test / pytest / go test ./...
```
**If tests fail:**
```
Tests failing (<N> failures). Must fix before completing:
[Show failures]
Cannot proceed with merge/PR until tests pass.
```
Stop. Don't proceed to Step 2.
**If tests pass:** Continue to Step 2.
### Step 2: Detect Environment
**Determine workspace state before presenting options:**
```bash
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
```
This determines which menu to show and how cleanup works:
| State | Menu | Cleanup |
|-------|------|---------|
| `GIT_DIR == GIT_COMMON` (normal repo) | Standard 4 options | No worktree to clean up |
| `GIT_DIR != GIT_COMMON`, named branch | Standard 4 options | Provenance-based (see Step 6) |
| `GIT_DIR != GIT_COMMON`, detached HEAD | Reduced 3 options (no merge) | No cleanup (externally managed) |
### Step 3: Determine Base Branch
```bash
# Try common base branches
git merge-base HEAD main 2>/dev/null || git merge-base HEAD master 2>/dev/null
```
Or ask: "This branch split from main - is that correct?"
### Step 4: Present Options
**Normal repo and named-branch worktree — present exactly these 4 options:**
```
Implementation complete. What would you like to do?
1. Merge back to <base-branch> locally
2. Push and create a Pull Request
3. Keep the branch as-is (I'll handle it later)
4. Discard this work
Which option?
```
**Detached HEAD — present exactly these 3 options:**
```
Implementation complete. You're on a detached HEAD (externally managed workspace).
1. Push as new branch and create a Pull Request
2. Keep as-is (I'll handle it later)
3. Discard this work
Which option?
```
**Don't add explanation** - keep options concise.
### Step 5: Execute Choice
#### Option 1: Merge Locally
```bash
# Get main repo root for CWD safety
MAIN_ROOT=$(git -C "$(git rev-parse --git-common-dir)/.." rev-parse --show-toplevel)
cd "$MAIN_ROOT"
# Merge first — verify success before removing anything
git checkout <base-branch>
git pull
git merge <feature-branch>
# Verify tests on merged result
<test command>
# Only after merge succeeds: remove worktree, then delete branch
# (See Step 6 for worktree cleanup)
git branch -d <feature-branch>
```
Then: Cleanup worktree (Step 6)
#### Option 2: Push and Create PR
```bash
# Push branch
git push -u origin <feature-branch>
# Create PR
gh pr create --title "<title>" --body "$(cat <<'EOF'
## Summary
<2-3 bullets of what changed>
## Test Plan
- [ ] <verification steps>
EOF
)"
```
**Do NOT clean up worktree** — user needs it alive to iterate on PR feedback.
#### Option 3: Keep As-Is
Report: "Keeping branch <name>. Worktree preserved at <path>."
**Don't cleanup worktree.**
#### Option 4: Discard
**Confirm first:**
```
This will permanently delete:
- Branch <name>
- All commits: <commit-list>
- Worktree at <path>
Type 'discard' to confirm.
```
Wait for exact confirmation.
If confirmed:
```bash
MAIN_ROOT=$(git -C "$(git rev-parse --git-common-dir)/.." rev-parse --show-toplevel)
cd "$MAIN_ROOT"
```
Then: Cleanup worktree (Step 6), then force-delete branch:
```bash
git branch -D <feature-branch>
```
### Step 6: Cleanup Workspace
**Only runs for Options 1 and 4.** Options 2 and 3 always preserve the worktree.
```bash
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
WORKTREE_PATH=$(git rev-parse --show-toplevel)
```
**If `GIT_DIR == GIT_COMMON`:** Normal repo, no worktree to clean up. Done.
**If worktree path is under `.worktrees/` or `~/.config/superpowers/worktrees/`:** Superpowers created this worktree — we own cleanup.
```bash
MAIN_ROOT=$(git -C "$(git rev-parse --git-common-dir)/.." rev-parse --show-toplevel)
cd "$MAIN_ROOT"
git worktree remove "$WORKTREE_PATH"
git worktree prune # Self-healing: clean up any stale registrations
```
**Otherwise:** The host environment (harness) owns this workspace. Do NOT remove it. If your platform provides a workspace-exit tool, use it. Otherwise, leave the workspace in place.
## Quick Reference
| Option | Merge | Push | Keep Worktree | Cleanup Branch |
|--------|-------|------|---------------|----------------|
| 1. Merge locally | yes | - | - | yes |
| 2. Create PR | - | yes | yes | - |
| 3. Keep as-is | - | - | yes | - |
| 4. Discard | - | - | - | yes (force) |
## Common Mistakes
**Skipping test verification**
- **Problem:** Merge broken code, create failing PR
- **Fix:** Always verify tests before offering options
**Open-ended questions**
- **Problem:** "What should I do next?" is ambiguous
- **Fix:** Present exactly 4 structured options (or 3 for detached HEAD)
**Cleaning up worktree for Option 2**
- **Problem:** Remove worktree user needs for PR iteration
- **Fix:** Only cleanup for Options 1 and 4
**Deleting branch before removing worktree**
- **Problem:** `git branch -d` fails because worktree still references the branch
- **Fix:** Merge first, remove worktree, then delete branch
**Running git worktree remove from inside the worktree**
- **Problem:** Command fails silently when CWD is inside the worktree being removed
- **Fix:** Always `cd` to main repo root before `git worktree remove`
**Cleaning up harness-owned worktrees**
- **Problem:** Removing a worktree the harness created causes phantom state
- **Fix:** Only clean up worktrees under `.worktrees/` or `~/.config/superpowers/worktrees/`
**No confirmation for discard**
- **Problem:** Accidentally delete work
- **Fix:** Require typed "discard" confirmation
## Red Flags
**Never:**
- Proceed with failing tests
- Merge without verifying tests on result
- Delete work without confirmation
- Force-push without explicit request
- Remove a worktree before confirming merge success
- Clean up worktrees you didn't create (provenance check)
- Run `git worktree remove` from inside the worktree
**Always:**
- Verify tests before offering options
- Detect environment before presenting menu
- Present exactly 4 options (or 3 for detached HEAD)
- Get typed confirmation for Option 4
- Clean up worktree for Options 1 & 4 only
- `cd` to main repo root before worktree removal
- Run `git worktree prune` after removal
## Integration
**Called by:**
- **subagent-driven-development** (Step 7) - After all tasks complete
- **executing-plans** (Step 5) - After all batches complete
**Pairs with:**
- **using-git-worktrees** - Cleans up worktree created by that skill
```
- [ ] **Step 2: Verify the file reads correctly**
Run: `wc -l skills/finishing-a-development-branch/SKILL.md`
Expected: Approximately 210-230 lines.
- [ ] **Step 3: Commit**
```bash
git add skills/finishing-a-development-branch/SKILL.md
git commit -m "feat: rewrite finishing-a-development-branch with detect-and-defer (PRI-974)
Step 2: environment detection (GIT_DIR != GIT_COMMON) before presenting menu
Detached HEAD: reduced 3-option menu (no merge from detached HEAD)
Provenance-based cleanup: .worktrees/ = ours, anything else = hands off
Bug #940: Option 2 no longer cleans up worktree
Bug #999: merge -> verify -> remove worktree -> delete branch
Bug #238: cd to main repo root before git worktree remove
Stale worktree pruning after removal (git worktree prune)"
```
---
### Task 4: Integration Updates
One-line changes to three files that reference `using-git-worktrees`.
**Files:**
- Modify: `skills/executing-plans/SKILL.md:68`
- Modify: `skills/subagent-driven-development/SKILL.md:268`
- Modify: `skills/writing-plans/SKILL.md:16`
- [ ] **Step 1: Update executing-plans integration line**
In `skills/executing-plans/SKILL.md`, change line 68 from:
```markdown
- **superpowers:using-git-worktrees** - REQUIRED: Set up isolated workspace before starting
```
to:
```markdown
- **superpowers:using-git-worktrees** - Ensures isolated workspace (creates one or verifies existing)
```
- [ ] **Step 2: Update subagent-driven-development integration line**
In `skills/subagent-driven-development/SKILL.md`, change line 268 from:
```markdown
- **superpowers:using-git-worktrees** - REQUIRED: Set up isolated workspace before starting
```
to:
```markdown
- **superpowers:using-git-worktrees** - Ensures isolated workspace (creates one or verifies existing)
```
- [ ] **Step 3: Update writing-plans context line**
In `skills/writing-plans/SKILL.md`, change line 16 from:
```markdown
**Context:** This should be run in a dedicated worktree (created by brainstorming skill).
```
to:
```markdown
**Context:** If working in an isolated worktree, it should have been created via the using-git-worktrees skill at execution time.
```
- [ ] **Step 4: Commit all three**
```bash
git add skills/executing-plans/SKILL.md skills/subagent-driven-development/SKILL.md skills/writing-plans/SKILL.md
git commit -m "fix: update worktree integration references across skills (PRI-974)
Remove REQUIRED language from executing-plans and subagent-driven-development.
Consent and detection now live inside using-git-worktrees itself.
Fix stale 'created by brainstorming' claim in writing-plans."
```
---
### Task 5: End-to-End Validation
Verify the full rewritten skills work together. Run the existing test suite plus manual verification.
**Files:**
- Read: `tests/claude-code/run-skill-tests.sh`
- Read: `skills/using-git-worktrees/SKILL.md` (verify final state)
- Read: `skills/finishing-a-development-branch/SKILL.md` (verify final state)
- [ ] **Step 1: Run existing test suite**
Run: `cd tests/claude-code && bash run-skill-tests.sh`
Expected: All existing tests pass. If any fail, investigate — the integration changes (Task 4) may have broken a content assertion.
- [ ] **Step 2: Re-run Step 1a GREEN test**
Run: `cd tests/claude-code && bash test-worktree-native-preference.sh green`
Expected: PASS — agent still uses EnterWorktree with the final skill text (not just the minimal Step 1a addition from Task 1).
- [ ] **Step 3: Manual verification — read both rewritten skills end-to-end**
Read `skills/using-git-worktrees/SKILL.md` and `skills/finishing-a-development-branch/SKILL.md` in their entirety. Check:
1. No references to old behavior (hardcoded `CLAUDE.md`, interactive directory prompt, "REQUIRED" language)
2. Step numbering is consistent within each file
3. Quick Reference tables match the prose
4. Integration sections cross-reference correctly
5. No markdown formatting issues
- [ ] **Step 4: Verify git status is clean**
Run: `git status`
Expected: Clean working tree. All changes committed across Tasks 1-4.
- [ ] **Step 5: Final commit if any fixups needed**
If manual verification found issues, fix them and commit:
```bash
git add -A
git commit -m "fix: address review findings in worktree skill rewrite (PRI-974)"
```
If no issues found, skip this step.

View File

@@ -0,0 +1,244 @@
# Codex App Compatibility: Worktree and Finishing Skill Adaptation
Make superpowers skills work in the Codex App's sandboxed worktree environment without breaking existing Claude Code or Codex CLI behavior.
**Ticket:** PRI-823
## Motivation
The Codex App runs agents inside git worktrees it manages — detached HEAD, located under `$CODEX_HOME/worktrees/`, with a Seatbelt sandbox that blocks `git checkout -b`, `git push`, and network access. Three superpowers skills assume unrestricted git access: `using-git-worktrees` creates manual worktrees with named branches, `finishing-a-development-branch` merges/pushes/PRs by branch name, and `subagent-driven-development` requires both.
The Codex CLI (open source terminal tool) does NOT have this conflict — it has no built-in worktree management. Our manual worktree approach fills an isolation gap there. The problem is specifically with the Codex App.
## Empirical Findings
Tested in the Codex App on 2026-03-23:
| Operation | workspace-write sandbox | Full access sandbox |
|---|---|---|
| `git add` | Works | Works |
| `git commit` | Works | Works |
| `git checkout -b` | **Blocked** (can't write `.git/refs/heads/`) | Works |
| `git push` | **Blocked** (network + `.git/refs/remotes/`) | Works |
| `gh pr create` | **Blocked** (network) | Works |
| `git status/diff/log` | Works | Works |
Additional findings:
- `spawn_agent` subagents **share** the parent thread's filesystem (confirmed via marker file test)
- "Create branch" button appears in the App header regardless of which branch the worktree was started from
- The App's native finishing flow: Create branch → Commit modal → Commit and push / Commit and create PR
- `network_access = true` config is silently broken on macOS (issue #10390)
## Design: Read-Only Environment Detection
Three read-only git commands detect the environment without side effects:
```bash
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
BRANCH=$(git branch --show-current)
```
Two signals derived:
- **IN_LINKED_WORKTREE:** `GIT_DIR != GIT_COMMON` — the agent is in a worktree created by something else (Codex App, Claude Code Agent tool, previous skill run, or the user)
- **ON_DETACHED_HEAD:** `BRANCH` is empty — no named branch exists
Why `git-dir != git-common-dir` instead of checking `show-toplevel`:
- In a normal repo, both resolve to the same `.git` directory
- In a linked worktree, `git-dir` is `.git/worktrees/<name>` while `git-common-dir` is `.git`
- In a submodule, both are equal — avoiding a false positive that `show-toplevel` would produce
- Resolving via `cd && pwd -P` handles the relative-path problem (`git-common-dir` returns `.git` relative in normal repos but absolute in worktrees) and symlinks (macOS `/tmp``/private/tmp`)
### Decision Matrix
| Linked Worktree? | Detached HEAD? | Environment | Action |
|---|---|---|---|
| No | No | Claude Code / Codex CLI / normal git | Full skill behavior (unchanged) |
| Yes | Yes | Codex App worktree (workspace-write) | Skip worktree creation; handoff payload at finish |
| Yes | No | Codex App (Full access) or manual worktree | Skip worktree creation; full finishing flow |
| No | Yes | Unusual (manual detached HEAD) | Create worktree normally; warn at finish |
## Changes
### 1. `using-git-worktrees/SKILL.md` — Add Step 0 (~12 lines)
New section between "Overview" and "Directory Selection Process":
**Step 0: Check if Already in an Isolated Workspace**
Run the detection commands. If `GIT_DIR != GIT_COMMON`, skip worktree creation entirely. Instead:
1. Skip to "Run Project Setup" subsection under Creation Steps — `npm install` etc. is idempotent, worth running for safety
2. Then "Verify Clean Baseline" — run tests
3. Report with branch state:
- On a branch: "Already in an isolated workspace at `<path>` on branch `<name>`. Tests passing. Ready to implement."
- Detached HEAD: "Already in an isolated workspace at `<path>` (detached HEAD, externally managed). Tests passing. Note: branch creation needed at finish time. Ready to implement."
If `GIT_DIR == GIT_COMMON`, proceed with the full worktree creation flow (unchanged).
Safety verification (.gitignore check) is skipped when Step 0 fires — irrelevant for externally-created worktrees.
Update the Integration section's "Called by" entries. Change the description on each from context-specific text to: "Ensures isolated workspace (creates one or verifies existing)". For example, the `subagent-driven-development` entry changes from "REQUIRED: Set up isolated workspace before starting" to "REQUIRED: Ensures isolated workspace (creates one or verifies existing)".
**Sandbox fallback:** If `GIT_DIR == GIT_COMMON` and the skill proceeds to Creation Steps, but `git worktree add -b` fails with a permission error (e.g., Seatbelt sandbox denial), treat this as a late-detected restricted environment. Fall back to the Step 0 "already in workspace" behavior — skip creation, run setup and baseline tests in the current directory, report accordingly.
After reporting in Step 0, STOP. Do not continue to Directory Selection or Creation Steps.
**Everything else unchanged:** Directory Selection, Safety Verification, Creation Steps, Project Setup, Baseline Tests, Quick Reference, Common Mistakes, Red Flags.
### 2. `finishing-a-development-branch/SKILL.md` — Add Step 1.5 + cleanup guard (~20 lines)
**Step 1.5: Detect Environment** (after Step 1 "Verify Tests", before Step 2 "Determine Base Branch")
Run the detection commands. Three paths:
- **Path A** skips Steps 2 and 3 entirely (no base branch or options needed).
- **Paths B and C** proceed through Step 2 (Determine Base Branch) and Step 3 (Present Options) as normal.
**Path A — Externally managed worktree + detached HEAD** (`GIT_DIR != GIT_COMMON` AND `BRANCH` empty):
First, ensure all work is staged and committed (`git add` + `git commit`). The Codex App's finishing controls operate on committed work.
Then present this to the user (do NOT present the 4-option menu):
```
Implementation complete. All tests passing.
Current HEAD: <full-commit-sha>
This workspace is externally managed (detached HEAD).
I cannot create branches, push, or open PRs from here.
⚠ These commits are on a detached HEAD. If you do not create a branch,
they may be lost when this workspace is cleaned up.
If your host application provides these controls:
- "Create branch" — to name a branch, then commit/push/PR
- "Hand off to local" — to move changes to your local checkout
Suggested branch name: <ticket-id/short-description>
Suggested commit message: <summary-of-work>
```
Branch name derivation: use the ticket ID if available (e.g., `pri-823/codex-compat`), otherwise slugify the first 5 words of the plan title, otherwise omit the suggestion. Avoid including sensitive content (vulnerability descriptions, customer names) in branch names.
Skip to Step 5 (cleanup is a no-op for externally managed worktrees).
**Path B — Externally managed worktree + named branch** (`GIT_DIR != GIT_COMMON` AND `BRANCH` exists):
Present the 4-option menu as normal. (The Step 5 cleanup guard will re-detect the externally managed state independently.)
**Path C — Normal environment** (`GIT_DIR == GIT_COMMON`):
Present the 4-option menu as today (unchanged).
**Step 5 cleanup guard:**
Re-run the `GIT_DIR` vs `GIT_COMMON` detection at cleanup time (do not rely on earlier skill output — the finishing skill may run in a different session). If `GIT_DIR != GIT_COMMON`, skip `git worktree remove` — the host environment owns this workspace.
Otherwise, check and remove as today. Note: the existing Step 5 text says "For Options 1, 2, 4" but the Quick Reference table and Common Mistakes section say "Options 1 & 4 only." The new guard is added before this existing logic and does not change which options trigger cleanup.
**Everything else unchanged:** Options 1-4 logic, Quick Reference, Common Mistakes, Red Flags.
### 3. `subagent-driven-development/SKILL.md` and `executing-plans/SKILL.md` — 1 line edit each
Both skills have an identical Integration section line. Change from:
```
- superpowers:using-git-worktrees - REQUIRED: Set up isolated workspace before starting
```
To:
```
- superpowers:using-git-worktrees - REQUIRED: Ensures isolated workspace (creates one or verifies existing)
```
**Everything else unchanged:** Dispatch/review loop, prompt templates, model selection, status handling, red flags.
### 4. `codex-tools.md` — Add environment detection docs (~15 lines)
Two new sections at the end:
**Environment Detection:**
```markdown
## Environment Detection
Skills that create worktrees or finish branches should detect their
environment with read-only git commands before proceeding:
\```bash
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
BRANCH=$(git branch --show-current)
\```
- `GIT_DIR != GIT_COMMON` → already in a linked worktree (skip creation)
- `BRANCH` empty → detached HEAD (cannot branch/push/PR from sandbox)
See `using-git-worktrees` Step 0 and `finishing-a-development-branch`
Step 1.5 for how each skill uses these signals.
```
**Codex App Finishing:**
```markdown
## Codex App Finishing
When the sandbox blocks branch/push operations (detached HEAD in an
externally managed worktree), the agent commits all work and informs
the user to use the App's native controls:
- **"Create branch"** — names the branch, then commit/push/PR via App UI
- **"Hand off to local"** — transfers work to the user's local checkout
The agent can still run tests, stage files, and output suggested branch
names, commit messages, and PR descriptions for the user to copy.
```
## What Does NOT Change
- `implementer-prompt.md`, `spec-reviewer-prompt.md`, `code-quality-reviewer-prompt.md` — subagent prompts untouched
- `executing-plans/SKILL.md` — only the 1-line Integration description changes (same as `subagent-driven-development`); all runtime behavior is unchanged
- `dispatching-parallel-agents/SKILL.md` — no worktree or finishing operations
- `.codex/INSTALL.md` — installation process unchanged
- The 4-option finishing menu — preserved exactly for Claude Code and Codex CLI
- The full worktree creation flow — preserved exactly for non-worktree environments
- Subagent dispatch/review/iterate loop — unchanged (filesystem sharing confirmed)
## Scope Summary
| File | Change |
|---|---|
| `skills/using-git-worktrees/SKILL.md` | +12 lines (Step 0) |
| `skills/finishing-a-development-branch/SKILL.md` | +20 lines (Step 1.5 + cleanup guard) |
| `skills/subagent-driven-development/SKILL.md` | 1 line edit |
| `skills/executing-plans/SKILL.md` | 1 line edit |
| `skills/using-superpowers/references/codex-tools.md` | +15 lines |
~50 lines added/changed across 5 files. Zero new files. Zero breaking changes.
## Future Considerations
If a third skill needs the same detection pattern, extract it into a shared `references/environment-detection.md` file (Approach B). Not needed now — only 2 skills use it.
## Test Plan
### Automated (run in Claude Code after implementation)
1. Normal repo detection — assert IN_LINKED_WORKTREE=false
2. Linked worktree detection — `git worktree add` test worktree, assert IN_LINKED_WORKTREE=true
3. Detached HEAD detection — `git checkout --detach`, assert ON_DETACHED_HEAD=true
4. Finishing skill handoff output — verify handoff message (not 4-option menu) in restricted environment
5. **Step 5 cleanup guard** — create a linked worktree (`git worktree add /tmp/test-cleanup -b test-cleanup`), `cd` into it, run the Step 5 cleanup detection (`GIT_DIR` vs `GIT_COMMON`), assert it would NOT call `git worktree remove`. Then `cd` back to main repo, run the same detection, assert it WOULD call `git worktree remove`. Clean up test worktree afterward.
### Manual Codex App Tests (5 tests)
1. Detection in Worktree thread (workspace-write) — verify GIT_DIR != GIT_COMMON, empty branch
2. Detection in Worktree thread (Full access) — same detection, different sandbox behavior
3. Finishing skill handoff format — verify agent emits handoff payload, not 4-option menu
4. Full lifecycle — detection → commit → finishing detection → correct behavior → cleanup
5. **Sandbox fallback in Local thread** — Start a Codex App **Local thread** (workspace-write sandbox). Prompt: "Use the superpowers skill `using-git-worktrees` to set up an isolated workspace for implementing a small change." Pre-check: `git checkout -b test-sandbox-check` should fail with `Operation not permitted`. Expected: the skill detects `GIT_DIR == GIT_COMMON` (normal repo), attempts `git worktree add -b`, hits Seatbelt denial, falls back to Step 0 "already in workspace" behavior — runs setup, baseline tests, reports ready from current directory. Pass: agent recovers gracefully without cryptic error messages. Fail: agent prints raw Seatbelt error, retries, or gives up with confusing output.
### Regression
- Existing Claude Code skill-triggering tests still pass
- Existing subagent-driven-development integration tests still pass
- Normal Claude Code session: full worktree creation + 4-option finishing still works

View File

@@ -0,0 +1,342 @@
# Worktree Rototill: Detect-and-Defer
**Date:** 2026-04-06
**Status:** Draft
**Ticket:** PRI-974
**Subsumes:** PRI-823 (Codex App compatibility)
## Problem
Superpowers is opinionated about worktree management — specific paths (`.worktrees/<branch>`), specific commands (`git worktree add`), specific cleanup (`git worktree remove`). Meanwhile, Claude Code, Codex App, Gemini CLI, and Cursor all provide native worktree support with their own paths, lifecycle management, and cleanup.
This creates three failure modes:
1. **Duplication** — on Claude Code, the skill does what `EnterWorktree`/`ExitWorktree` already does
2. **Conflict** — on Codex App, the skill tries to create worktrees inside an already-managed worktree
3. **Phantom state** — skill-created worktrees at `.worktrees/` are invisible to the harness; harness-created worktrees at `.claude/worktrees/` are invisible to the skill
For harnesses without native support (Codex CLI, OpenCode, Copilot standalone), superpowers fills a real gap. The skill shouldn't go away — it should get out of the way when native support exists.
## Goals
1. Defer to native harness worktree systems when they exist
2. Continue providing worktree support for harnesses that lack it
3. Fix three known bugs in finishing-a-development-branch (#940, #999, #238)
4. Make worktree creation opt-in rather than mandatory (#991)
5. Replace hardcoded `CLAUDE.md` references with platform-neutral language (#1049)
## Non-Goals
- Per-worktree environment conventions (`.worktree-env.sh`, port offsetting) — Phase 4
- PreToolUse hooks for path enforcement — Phase 4
- Multi-repo worktree documentation — Phase 4
- Brainstorming checklist changes for worktrees — Phase 4
- `.superpowers-session.json` metadata tracking (interesting PR #997 idea, not needed for v1)
- Hooks symlinking into worktrees (PR #965 idea, separate concern)
## Design Principles
### Detect state, not platform
Use `GIT_DIR != GIT_COMMON` to determine "am I already in a worktree?" rather than sniffing environment variables to identify the harness. This is a stable git primitive (since git 2.5, 2015), works universally across all harnesses, and requires zero maintenance as new harnesses appear.
### Declarative intent, prescriptive fallback
The skill describes the goal ("ensure work happens in an isolated workspace") and defers to native tools when available. It prescribes specific git commands only as a fallback for harnesses without native worktree support. Step 1a comes first and names native tools explicitly (`EnterWorktree`, `WorktreeCreate`, `/worktree`, `--worktree`); Step 1b comes second with the git fallback. The original spec kept Step 1a abstract ("you know your own toolkit"), but TDD proved that agents anchor on Step 1b's concrete commands when Step 1a is too vague. Explicit tool naming and a consent-authorization bridge were required to make the preference reliable.
### Provenance-based ownership
Whoever creates the worktree owns its cleanup. If the harness created it, superpowers doesn't touch it. If superpowers created it (via git fallback), superpowers cleans it up. The heuristic: if the worktree lives under `.worktrees/` or `~/.config/superpowers/worktrees/`, superpowers owns it. Anything else (`.claude/worktrees/`, `~/.codex/worktrees/`, `.gemini/worktrees/`) belongs to the harness.
## Design
### 1. `using-git-worktrees` SKILL.md Rewrite
The skill gains three new steps before creation and simplifies the creation flow.
#### Step 0: Detect Existing Isolation
```bash
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
BRANCH=$(git branch --show-current)
```
Three outcomes:
| Condition | Meaning | Action |
|-----------|---------|--------|
| `GIT_DIR == GIT_COMMON` | Normal repo checkout | Proceed to Step 0.5 |
| `GIT_DIR != GIT_COMMON`, named branch | Already in a linked worktree | Skip to Step 3 (project setup). Report: "Already in isolated workspace at `<path>` on branch `<name>`." |
| `GIT_DIR != GIT_COMMON`, detached HEAD | Externally managed worktree (e.g., Codex App sandbox) | Skip to Step 3. Report: "Already in isolated workspace at `<path>` (detached HEAD, externally managed)." |
Step 0 does not care who created the worktree or which harness is running. A worktree is a worktree regardless of origin.
**Submodule guard:** `GIT_DIR != GIT_COMMON` is also true inside git submodules. Before concluding "already in a worktree," check that we're not in a submodule:
```bash
# If this returns a path, we're in a submodule, not a worktree
git rev-parse --show-superproject-working-tree 2>/dev/null
```
If in a submodule, treat as `GIT_DIR == GIT_COMMON` (proceed to Step 0.5).
#### Step 0.5: Consent
When Step 0 finds no existing isolation (`GIT_DIR == GIT_COMMON`), ask before creating:
> "Would you like me to set up an isolated worktree? This protects your current branch from changes. (y/n)"
If yes, proceed to Step 1. If no, work in place — skip to Step 3 with no worktree.
This step is skipped entirely when Step 0 detects existing isolation (no point asking about what already exists).
#### Step 1a: Native Tools (preferred)
> The user has asked for an isolated workspace (Step 0 consent). Check your available tools — do you have `EnterWorktree`, `WorktreeCreate`, a `/worktree` command, or a `--worktree` flag? If YES: the user's consent to create a worktree is your authorization to use it. Use it now and skip to Step 3.
After using a native tool, skip to Step 3 (project setup).
**Design note — TDD revision:** The original spec used a deliberately short, abstract Step 1a ("You know your own toolkit — the skill does not need to name specific tools"). TDD validation disproved this: agents anchored on Step 1b's concrete git commands and ignored the abstract guidance (2/6 pass rate). Three changes fixed it (50/50 pass rate across GREEN and PRESSURE tests):
1. **Explicit tool naming** — listing `EnterWorktree`, `WorktreeCreate`, `/worktree`, `--worktree` by name transforms the decision from interpretation ("do I have a native tool?") into factual lookup ("is `EnterWorktree` in my tool list?"). Agents on platforms without these tools simply check, find nothing, and fall through to Step 1b. No false positives observed.
2. **Consent bridge** — "the user's consent to create a worktree is your authorization to use it" directly addresses `EnterWorktree`'s tool-level guardrail ("ONLY when user explicitly asks"). Tool descriptions override skill instructions (Claude Code #29950), so the skill must frame user consent as the authorization the tool requires.
3. **Red Flag entry** — naming the specific anti-pattern ("Use `git worktree add` when you have a native worktree tool — this is the #1 mistake") in the Red Flags section.
File splitting (Step 1b in a separate skill) was tested and proven unnecessary. The anchoring problem is solved by the quality of Step 1a's text, not by physical separation of git commands. Control tests with the full 240-line skill (all git commands visible) passed 20/20.
#### Step 1b: Git Worktree Fallback
When no native tool is available, create a worktree manually.
**Directory selection** (priority order):
1. Check for existing `.worktrees/` or `worktrees/` directory — if found, use it. If both exist, `.worktrees/` wins.
2. Check for existing `~/.config/superpowers/worktrees/<project>/` directory — if found, use it (backward compatibility with legacy global path).
3. Check the project's agent instruction file (CLAUDE.md, GEMINI.md, AGENTS.md, .cursorrules, or equivalent) for a worktree directory preference.
4. Default to `.worktrees/`.
No interactive directory selection prompt. The global path (`~/.config/superpowers/worktrees/`) is no longer offered as a choice to new users, but existing worktrees at that location are detected and used for backward compatibility.
**Safety verification** (project-local directories only):
```bash
git check-ignore -q .worktrees 2>/dev/null
```
If not ignored, add to `.gitignore` and commit before proceeding.
**Create:**
```bash
git worktree add "$path" -b "$BRANCH_NAME"
cd "$path"
```
**Hooks awareness:** Git worktrees do not inherit the parent repo's hooks directory. After creating a worktree via 1b, symlink the hooks directory from the main repo if one exists:
```bash
if [ -d "$MAIN_ROOT/.git/hooks" ]; then
ln -sf "$MAIN_ROOT/.git/hooks" "$path/.git/hooks"
fi
```
This prevents pre-commit checks, linters, and other hooks from silently stopping when work moves to a worktree. (Idea from PR #965.)
**Sandbox fallback:** If `git worktree add` fails with a permission error, treat as a restricted environment. Skip creation, work in current directory, proceed to Step 3.
**Step numbering note:** The current skill has Steps 1-4 as a flat list. This redesign uses 0, 0.5, 1a, 1b, 3, 4. There is no Step 2 — it was the old monolithic "Create Isolated Workspace" which is now split into the 1a/1b structure. The implementation should renumber cleanly (e.g., 0 → "Step 0: Detect", 0.5 → within Step 0's flow, 1a/1b → "Step 1", 3 → "Step 2", 4 → "Step 3") or keep the current numbering with a note. Implementer's choice.
#### Steps 3-4: Project Setup and Baseline Tests (unchanged)
Regardless of which path created the workspace (Step 0 detected existing, Step 1a native tool, Step 1b git fallback, or no worktree at all), execution converges:
- **Step 3:** Auto-detect and run project setup (`npm install`, `cargo build`, `pip install`, `go mod download`, etc.)
- **Step 4:** Run the test suite. If tests fail, report failures and ask whether to proceed.
### 2. `finishing-a-development-branch` SKILL.md Rewrite
The finishing skill gains environment detection and fixes three bugs.
#### Step 1: Verify Tests (unchanged)
Run the project's test suite. If tests fail, stop. Don't offer completion options.
#### Step 1.5: Detect Environment (new)
Re-run the same detection as Step 0 in creation:
```bash
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
```
Three paths:
| State | Menu | Cleanup |
|-------|------|---------|
| `GIT_DIR == GIT_COMMON` (normal repo) | Standard 4 options | No worktree to clean up |
| `GIT_DIR != GIT_COMMON`, named branch | Standard 4 options | Provenance-based (see Step 5) |
| `GIT_DIR != GIT_COMMON`, detached HEAD | Reduced menu: push as new branch + PR, keep as-is, discard | No merge options (can't merge from detached HEAD) |
#### Step 2: Determine Base Branch (unchanged)
#### Step 3: Present Options
**Normal repo and named-branch worktree:**
1. Merge back to `<base-branch>` locally
2. Push and create a Pull Request
3. Keep the branch as-is (I'll handle it later)
4. Discard this work
**Detached HEAD:**
1. Push as new branch and create a Pull Request
2. Keep as-is (I'll handle it later)
3. Discard this work
#### Step 4: Execute Choice
**Option 1 (Merge locally):**
```bash
# Get main repo root for CWD safety (Bug #238 fix)
MAIN_ROOT=$(git -C "$(git rev-parse --git-common-dir)/.." rev-parse --show-toplevel)
cd "$MAIN_ROOT"
# Merge first, verify success before removing anything
git checkout <base-branch>
git pull
git merge <feature-branch>
<run tests>
# Only after merge succeeds: remove worktree, then delete branch (Bug #999 fix)
git worktree remove "$WORKTREE_PATH" # only if superpowers owns it
git branch -d <feature-branch>
```
The order is critical: merge → verify → remove worktree → delete branch. The old skill deleted the branch before removing the worktree (which fails because the worktree still references the branch). The naive fix of removing the worktree first is also wrong — if the merge then fails, the working directory is gone and changes are lost.
**Option 2 (Create PR):**
Push branch, create PR. Do NOT clean up worktree — user needs it for PR iteration. (Bug #940 fix: remove contradictory "Then: Cleanup worktree" prose.)
**Option 3 (Keep as-is):** No action.
**Option 4 (Discard):** Require typed "discard" confirmation. Then remove worktree (if superpowers owns it), force-delete branch.
#### Step 5: Cleanup (updated)
```
if GIT_DIR == GIT_COMMON:
# Normal repo, no worktree to clean up
done
if worktree path is under .worktrees/ or ~/.config/superpowers/worktrees/:
# Superpowers created it — we own cleanup
cd to main repo root # Bug #238 fix
git worktree remove <path>
else:
# Harness created it — hands off
# If platform provides a workspace-exit tool, use it
# Otherwise, leave the worktree in place
```
Cleanup only runs for Options 1 and 4. Options 2 and 3 always preserve the worktree. (Bug #940 fix.)
**Stale worktree pruning:** After any `git worktree remove`, run `git worktree prune` as a self-healing step. Worktree directories can get deleted out-of-band (e.g., by harness cleanup, manual `rm`, or `.claude/` cleanup), leaving stale registrations that cause confusing errors. One line, prevents silent rot. (Idea from PR #1072.)
### 3. Integration Updates
#### `subagent-driven-development` and `executing-plans`
Both currently list `using-git-worktrees` as REQUIRED in their integration sections. Change to:
> `using-git-worktrees` — Ensures isolated workspace (creates one or verifies existing)
The skill itself now handles consent (Step 0.5) and detection (Step 0), so calling skills don't need to gate or prompt.
#### `writing-plans`
Remove the stale claim "should be run in a dedicated worktree (created by brainstorming skill)." Brainstorming is a design skill and does not create worktrees. The worktree prompt happens at execution time via `using-git-worktrees`.
### 4. Platform-Neutral Instruction File References
All instances of hardcoded `CLAUDE.md` in worktree-related skills are replaced with:
> "your project's agent instruction file (CLAUDE.md, GEMINI.md, AGENTS.md, .cursorrules, or equivalent)"
This applies to directory preference checks in Step 1b.
## Bug Fixes (bundled)
| Bug | Problem | Fix | Location |
|-----|---------|-----|----------|
| #940 | Option 2 prose says "Then: Cleanup worktree (Step 5)" but quick reference says keep it. Step 5 says "For Options 1, 2, 4" but Common Mistakes says "Options 1 and 4 only." | Remove cleanup from Option 2. Step 5 applies to Options 1 and 4 only. | finishing SKILL.md |
| #999 | Option 1 deletes branch before removing worktree. `git branch -d` can fail because worktree still references the branch. | Reorder to: merge → verify tests → remove worktree → delete branch. Merge must succeed before anything is removed. | finishing SKILL.md |
| #238 | `git worktree remove` fails silently if CWD is inside the worktree being removed. | Add CWD guard: `cd` to main repo root before `git worktree remove`. | finishing SKILL.md |
## Issues Resolved
| Issue | Resolution |
|-------|-----------|
| #940 | Direct fix (Bug #940) |
| #991 | Opt-in consent in Step 0.5 |
| #918 | Step 0 detection + Step 1.5 finishing detection |
| #1009 | Resolved by Step 1a — agents use native tools (e.g., `EnterWorktree`) which create at harness-native paths. Depends on Step 1a working; see Risks. |
| #999 | Direct fix (Bug #999) |
| #238 | Direct fix (Bug #238) |
| #1049 | Platform-neutral instruction file references |
| #279 | Solved by detect-and-defer — native paths respected because we don't override them |
| #574 | **Deferred.** Nothing in this spec touches the brainstorming skill where the bug lives. Full fix (adding a worktree step to brainstorming's checklist) is Phase 4. |
## Risks
### Step 1a is the load-bearing assumption — RESOLVED
Step 1a — agents preferring native worktree tools over the git fallback — is the foundation the entire design rests on. If agents ignore Step 1a and fall through to Step 1b on harnesses with native support, detect-and-defer fails entirely.
**Status:** This risk materialized during implementation. The original abstract Step 1a ("You know your own toolkit") failed at 2/6 on Claude Code. The TDD gate worked as designed — it caught the failure before any skill files were modified, preventing a broken release. Three REFACTOR iterations identified the root causes (agent anchoring on concrete commands, tool-description guardrail overriding skill instructions) and produced a fix validated at 50/50 across GREEN and PRESSURE tests. See Step 1a design note above for details.
**Cross-platform validation:**
As of 2026-04-06, Claude Code is the only harness with an agent-callable mid-session worktree tool (`EnterWorktree`). All others either create worktrees before the agent starts (Codex App, Gemini CLI, Cursor) or have no native worktree support (Codex CLI, OpenCode). Step 1a is forward-compatible: when other harnesses add agent-callable worktree tools, agents will match them against the named examples and use them without skill changes.
| Harness | Current worktree model | Skill mechanism | Tested |
|---------|----------------------|-----------------|--------|
| Claude Code | Agent-callable `EnterWorktree` | Step 1a | 50/50 (GREEN + PRESSURE) |
| Codex CLI | No native tool (shell only) | Step 1b git fallback | 6/6 (`codex exec`) |
| Gemini CLI | Launch-time `--worktree` flag, no agent tool | Step 0 if launched with flag, Step 1b if not | Step 0: 1/1, Step 1b: 1/1 (`gemini -p`) |
| Cursor Agent | User-facing `/worktree`, no agent tool | Step 0 if user activated, Step 1b if not | Step 0: 1/1, Step 1b: 1/1 (`cursor-agent -p`) |
| Codex App | Platform-managed, detached HEAD, no agent tool | Step 0 detects existing | 1/1 simulated |
| OpenCode | Detection only (`ctx.worktree`), no agent tool | Step 1b git fallback | Untested (no CLI access) |
**Residual risks:**
1. If Anthropic changes `EnterWorktree`'s tool description to be more restrictive (e.g., "Do not use based on skill instructions"), the consent bridge breaks. Worth filing an issue requesting that the tool description accommodate skill-driven invocation.
2. When other harnesses add agent-callable worktree tools, they may use names not in Step 1a's list. The list should be updated as new tools appear. The generic phrasing ("a worktree or workspace-isolation tool") provides some forward coverage.
### Provenance heuristic
The `.worktrees/` or `~/.config/superpowers/worktrees/` = ours, anything else = hands off` heuristic works for every current harness. If a future harness adopts `.worktrees/` as its convention, we'd have a false positive (superpowers tries to clean up a harness-owned worktree). Similarly, if a user manually runs `git worktree add .worktrees/experiment` without superpowers, we'd incorrectly claim ownership. Both are low risk — every harness uses branded paths, and manual `.worktrees/` creation is unlikely — but worth noting.
### Detached HEAD finishing
The reduced menu for detached HEAD worktrees (no merge option) is correct for Codex App's sandbox model. If a user is in detached HEAD for another reason, the reduced menu still makes sense — you genuinely can't merge from detached HEAD without creating a branch first.
## Implementation Notes
Both skill files contain sections beyond the core steps that need updating during implementation:
- **Frontmatter** (`name`, `description`): Update to reflect detect-and-defer behavior
- **Quick Reference tables**: Rewrite to match new step structure and bug fixes
- **Common Mistakes sections**: Update or remove items that reference old behavior (e.g., "Skip CLAUDE.md check" is now wrong)
- **Red Flags sections**: Update to reflect new priorities (e.g., "Never create a worktree when Step 0 detects existing isolation")
- **Integration sections**: Update cross-references between skills
The spec describes *what changes*; the implementation plan will specify exact edits to these secondary sections.
## Future Work (not in this spec)
- **Phase 3 remainder:** `$TMPDIR` directory option (#666), setup docs for caching and env inheritance (#299)
- **Phase 4:** PreToolUse hooks for path enforcement (#1040), per-worktree env conventions (#597), brainstorming checklist worktree step (#574), multi-repo documentation (#710)

View File

@@ -149,8 +149,8 @@ python3 tests/claude-code/analyze-token-usage.py ~/.claude/projects/<project-dir
Session transcripts are stored in `~/.claude/projects/` with the working directory path encoded:
```bash
# Example for /Users/jesse/Documents/GitHub/superpowers/superpowers
SESSION_DIR="$HOME/.claude/projects/-Users-jesse-Documents-GitHub-superpowers-superpowers"
# Example for /Users/yourname/Documents/GitHub/superpowers/superpowers
SESSION_DIR="$HOME/.claude/projects/-Users-yourname-Documents-GitHub-superpowers-superpowers"
# Find recent sessions
ls -lt "$SESSION_DIR"/*.jsonl | head -5

View File

@@ -1,6 +1,6 @@
{
"name": "superpowers",
"description": "Core skills library: TDD, debugging, collaboration patterns, and proven techniques",
"version": "5.0.0",
"version": "5.0.6",
"contextFileName": "GEMINI.md"
}

View File

@@ -35,23 +35,23 @@ warning_escaped=$(escape_for_json "$warning_message")
session_context="<EXTREMELY_IMPORTANT>\nYou have superpowers.\n\n**Below is the full content of your 'superpowers:using-superpowers' skill - your introduction to using skills. For all other skills, use the 'Skill' tool:**\n\n${using_superpowers_escaped}\n\n${warning_escaped}\n</EXTREMELY_IMPORTANT>"
# Output context injection as JSON.
# Cursor hooks expect additional_context.
# Claude Code hooks expect hookSpecificOutput.additionalContext.
# Claude Code reads BOTH fields without deduplication, so we must only
# emit the field consumed by the current platform to avoid double injection.
# Cursor hooks expect additional_context (snake_case).
# Claude Code hooks expect hookSpecificOutput.additionalContext (nested).
# Copilot CLI (v1.0.11+) and others expect additionalContext (top-level, SDK standard).
# Claude Code reads BOTH additional_context and hookSpecificOutput without
# deduplication, so we must emit only the field the current platform consumes.
#
# Uses printf instead of heredoc (cat <<EOF) to work around a bash 5.3+
# bug where heredoc variable expansion hangs when content exceeds ~512 bytes.
# Uses printf instead of heredoc to work around bash 5.3+ heredoc hang.
# See: https://github.com/obra/superpowers/issues/571
if [ -n "${CURSOR_PLUGIN_ROOT:-}" ]; then
# Cursor sets CURSOR_PLUGIN_ROOT (may also set CLAUDE_PLUGIN_ROOT) — emit additional_context
# Cursor sets CURSOR_PLUGIN_ROOT (may also set CLAUDE_PLUGIN_ROOT)
printf '{\n "additional_context": "%s"\n}\n' "$session_context"
elif [ -n "${CLAUDE_PLUGIN_ROOT:-}" ]; then
# Claude Code sets CLAUDE_PLUGIN_ROOT — emit only hookSpecificOutput
elif [ -n "${CLAUDE_PLUGIN_ROOT:-}" ] && [ -z "${COPILOT_CLI:-}" ]; then
# Claude Code sets CLAUDE_PLUGIN_ROOT without COPILOT_CLI
printf '{\n "hookSpecificOutput": {\n "hookEventName": "SessionStart",\n "additionalContext": "%s"\n }\n}\n' "$session_context"
else
# Other platformsemit additional_context as fallback
printf '{\n "additional_context": "%s"\n}\n' "$session_context"
# Copilot CLI (sets COPILOT_CLI=1) or unknown platform — SDK standard format
printf '{\n "additionalContext": "%s"\n}\n' "$session_context"
fi
exit 0

View File

@@ -1,6 +1,6 @@
{
"name": "superpowers",
"version": "5.0.4",
"version": "5.0.6",
"type": "module",
"main": ".opencode/plugins/superpowers.js"
}

View File

@@ -27,7 +27,7 @@ You MUST create a task for each of these items and complete them in order:
4. **Propose 2-3 approaches** — with trade-offs and your recommendation
5. **Present design** — in sections scaled to their complexity, get user approval after each section
6. **Write design doc** — save to `docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md` and commit
7. **Spec review loop**dispatch spec-document-reviewer subagent with precisely crafted review context (never your session history); fix issues and re-dispatch until approved (max 3 iterations, then surface to human)
7. **Spec self-review**quick inline check for placeholders, contradictions, ambiguity, scope (see below)
8. **User reviews written spec** — ask user to review the spec file before proceeding
9. **Transition to implementation** — invoke writing-plans skill to create implementation plan
@@ -43,8 +43,7 @@ digraph brainstorming {
"Present design sections" [shape=box];
"User approves design?" [shape=diamond];
"Write design doc" [shape=box];
"Spec review loop" [shape=box];
"Spec review passed?" [shape=diamond];
"Spec self-review\n(fix inline)" [shape=box];
"User reviews spec?" [shape=diamond];
"Invoke writing-plans skill" [shape=doublecircle];
@@ -57,10 +56,8 @@ digraph brainstorming {
"Present design sections" -> "User approves design?";
"User approves design?" -> "Present design sections" [label="no, revise"];
"User approves design?" -> "Write design doc" [label="yes"];
"Write design doc" -> "Spec review loop";
"Spec review loop" -> "Spec review passed?";
"Spec review passed?" -> "Spec review loop" [label="issues found,\nfix and re-dispatch"];
"Spec review passed?" -> "User reviews spec?" [label="approved"];
"Write design doc" -> "Spec self-review\n(fix inline)";
"Spec self-review\n(fix inline)" -> "User reviews spec?";
"User reviews spec?" -> "Write design doc" [label="changes requested"];
"User reviews spec?" -> "Invoke writing-plans skill" [label="approved"];
}
@@ -116,12 +113,15 @@ digraph brainstorming {
- Use elements-of-style:writing-clearly-and-concisely skill if available
- Commit the design document to git
**Spec Review Loop:**
After writing the spec document:
**Spec Self-Review:**
After writing the spec document, look at it with fresh eyes:
1. Dispatch spec-document-reviewer subagent (see spec-document-reviewer-prompt.md)
2. If Issues Found: fix, re-dispatch, repeat until Approved
3. If loop exceeds 3 iterations, surface to human for guidance
1. **Placeholder scan:** Any "TBD", "TODO", incomplete sections, or vague requirements? Fix them.
2. **Internal consistency:** Do any sections contradict each other? Does the architecture match the feature descriptions?
3. **Scope check:** Is this focused enough for a single implementation plan, or does it need decomposition?
4. **Ambiguity check:** Could any requirement be interpreted two different ways? If so, pick one and make it explicit.
Fix any issues inline. No need to re-review — just fix and move on.
**User Review Gate:**
After the spec review loop passes, ask the user to review the written spec before proceeding:

View File

@@ -76,8 +76,10 @@ function decodeFrame(buffer) {
const PORT = process.env.BRAINSTORM_PORT || (49152 + Math.floor(Math.random() * 16383));
const HOST = process.env.BRAINSTORM_HOST || '127.0.0.1';
const URL_HOST = process.env.BRAINSTORM_URL_HOST || (HOST === '127.0.0.1' ? 'localhost' : HOST);
const SCREEN_DIR = process.env.BRAINSTORM_DIR || '/tmp/brainstorm';
const OWNER_PID = process.env.BRAINSTORM_OWNER_PID ? Number(process.env.BRAINSTORM_OWNER_PID) : null;
const SESSION_DIR = process.env.BRAINSTORM_DIR || '/tmp/brainstorm';
const CONTENT_DIR = path.join(SESSION_DIR, 'content');
const STATE_DIR = path.join(SESSION_DIR, 'state');
let ownerPid = process.env.BRAINSTORM_OWNER_PID ? Number(process.env.BRAINSTORM_OWNER_PID) : null;
const MIME_TYPES = {
'.html': 'text/html', '.css': 'text/css', '.js': 'application/javascript',
@@ -112,10 +114,10 @@ function wrapInFrame(content) {
}
function getNewestScreen() {
const files = fs.readdirSync(SCREEN_DIR)
const files = fs.readdirSync(CONTENT_DIR)
.filter(f => f.endsWith('.html'))
.map(f => {
const fp = path.join(SCREEN_DIR, f);
const fp = path.join(CONTENT_DIR, f);
return { path: fp, mtime: fs.statSync(fp).mtime.getTime() };
})
.sort((a, b) => b.mtime - a.mtime);
@@ -142,7 +144,7 @@ function handleRequest(req, res) {
res.end(html);
} else if (req.method === 'GET' && req.url.startsWith('/files/')) {
const fileName = req.url.slice(7);
const filePath = path.join(SCREEN_DIR, path.basename(fileName));
const filePath = path.join(CONTENT_DIR, path.basename(fileName));
if (!fs.existsSync(filePath)) {
res.writeHead(404);
res.end('Not found');
@@ -230,7 +232,7 @@ function handleMessage(text) {
touchActivity();
console.log(JSON.stringify({ source: 'user-event', ...event }));
if (event.choice) {
const eventsFile = path.join(SCREEN_DIR, '.events');
const eventsFile = path.join(STATE_DIR, 'events');
fs.appendFileSync(eventsFile, JSON.stringify(event) + '\n');
}
}
@@ -258,32 +260,33 @@ const debounceTimers = new Map();
// ========== Server Startup ==========
function startServer() {
if (!fs.existsSync(SCREEN_DIR)) fs.mkdirSync(SCREEN_DIR, { recursive: true });
if (!fs.existsSync(CONTENT_DIR)) fs.mkdirSync(CONTENT_DIR, { recursive: true });
if (!fs.existsSync(STATE_DIR)) fs.mkdirSync(STATE_DIR, { recursive: true });
// Track known files to distinguish new screens from updates.
// macOS fs.watch reports 'rename' for both new files and overwrites,
// so we can't rely on eventType alone.
const knownFiles = new Set(
fs.readdirSync(SCREEN_DIR).filter(f => f.endsWith('.html'))
fs.readdirSync(CONTENT_DIR).filter(f => f.endsWith('.html'))
);
const server = http.createServer(handleRequest);
server.on('upgrade', handleUpgrade);
const watcher = fs.watch(SCREEN_DIR, (eventType, filename) => {
const watcher = fs.watch(CONTENT_DIR, (eventType, filename) => {
if (!filename || !filename.endsWith('.html')) return;
if (debounceTimers.has(filename)) clearTimeout(debounceTimers.get(filename));
debounceTimers.set(filename, setTimeout(() => {
debounceTimers.delete(filename);
const filePath = path.join(SCREEN_DIR, filename);
const filePath = path.join(CONTENT_DIR, filename);
if (!fs.existsSync(filePath)) return; // file was deleted
touchActivity();
if (!knownFiles.has(filename)) {
knownFiles.add(filename);
const eventsFile = path.join(SCREEN_DIR, '.events');
const eventsFile = path.join(STATE_DIR, 'events');
if (fs.existsSync(eventsFile)) fs.unlinkSync(eventsFile);
console.log(JSON.stringify({ type: 'screen-added', file: filePath }));
} else {
@@ -297,10 +300,10 @@ function startServer() {
function shutdown(reason) {
console.log(JSON.stringify({ type: 'server-stopped', reason }));
const infoFile = path.join(SCREEN_DIR, '.server-info');
const infoFile = path.join(STATE_DIR, 'server-info');
if (fs.existsSync(infoFile)) fs.unlinkSync(infoFile);
fs.writeFileSync(
path.join(SCREEN_DIR, '.server-stopped'),
path.join(STATE_DIR, 'server-stopped'),
JSON.stringify({ reason, timestamp: Date.now() }) + '\n'
);
watcher.close();
@@ -309,8 +312,8 @@ function startServer() {
}
function ownerAlive() {
if (!OWNER_PID) return true;
try { process.kill(OWNER_PID, 0); return true; } catch (e) { return false; }
if (!ownerPid) return true;
try { process.kill(ownerPid, 0); return true; } catch (e) { return e.code === 'EPERM'; }
}
// Check every 60s: exit if owner process died or idle for 30 minutes
@@ -320,14 +323,27 @@ function startServer() {
}, 60 * 1000);
lifecycleCheck.unref();
// Validate owner PID at startup. If it's already dead, the PID resolution
// was wrong (common on WSL, Tailscale SSH, and cross-user scenarios).
// Disable monitoring and rely on the idle timeout instead.
if (ownerPid) {
try { process.kill(ownerPid, 0); }
catch (e) {
if (e.code !== 'EPERM') {
console.log(JSON.stringify({ type: 'owner-pid-invalid', pid: ownerPid, reason: 'dead at startup' }));
ownerPid = null;
}
}
}
server.listen(PORT, HOST, () => {
const info = JSON.stringify({
type: 'server-started', port: Number(PORT), host: HOST,
url_host: URL_HOST, url: 'http://' + URL_HOST + ':' + PORT,
screen_dir: SCREEN_DIR
screen_dir: CONTENT_DIR, state_dir: STATE_DIR
});
console.log(info);
fs.writeFileSync(path.join(SCREEN_DIR, '.server-info'), info + '\n');
fs.writeFileSync(path.join(STATE_DIR, 'server-info'), info + '\n');
});
}

View File

@@ -78,16 +78,17 @@ fi
SESSION_ID="$$-$(date +%s)"
if [[ -n "$PROJECT_DIR" ]]; then
SCREEN_DIR="${PROJECT_DIR}/.superpowers/brainstorm/${SESSION_ID}"
SESSION_DIR="${PROJECT_DIR}/.superpowers/brainstorm/${SESSION_ID}"
else
SCREEN_DIR="/tmp/brainstorm-${SESSION_ID}"
SESSION_DIR="/tmp/brainstorm-${SESSION_ID}"
fi
PID_FILE="${SCREEN_DIR}/.server.pid"
LOG_FILE="${SCREEN_DIR}/.server.log"
STATE_DIR="${SESSION_DIR}/state"
PID_FILE="${STATE_DIR}/server.pid"
LOG_FILE="${STATE_DIR}/server.log"
# Create fresh session directory
mkdir -p "$SCREEN_DIR"
# Create fresh session directory with content and state peers
mkdir -p "${SESSION_DIR}/content" "$STATE_DIR"
# Kill any existing server
if [[ -f "$PID_FILE" ]]; then
@@ -106,22 +107,16 @@ if [[ -z "$OWNER_PID" || "$OWNER_PID" == "1" ]]; then
OWNER_PID="$PPID"
fi
# On Windows/MSYS2, the MSYS2 PID namespace is invisible to Node.js.
# Skip owner-PID monitoring — the 30-minute idle timeout prevents orphans.
case "${OSTYPE:-}" in
msys*|cygwin*|mingw*) OWNER_PID="" ;;
esac
# Foreground mode for environments that reap detached/background processes.
if [[ "$FOREGROUND" == "true" ]]; then
echo "$$" > "$PID_FILE"
env BRAINSTORM_DIR="$SCREEN_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs
env BRAINSTORM_DIR="$SESSION_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs
exit $?
fi
# Start server, capturing output to log file
# Use nohup to survive shell exit; disown to remove from job table
nohup env BRAINSTORM_DIR="$SCREEN_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs > "$LOG_FILE" 2>&1 &
nohup env BRAINSTORM_DIR="$SESSION_DIR" BRAINSTORM_HOST="$BIND_HOST" BRAINSTORM_URL_HOST="$URL_HOST" BRAINSTORM_OWNER_PID="$OWNER_PID" node server.cjs > "$LOG_FILE" 2>&1 &
SERVER_PID=$!
disown "$SERVER_PID" 2>/dev/null
echo "$SERVER_PID" > "$PID_FILE"

View File

@@ -1,19 +1,20 @@
#!/usr/bin/env bash
# Stop the brainstorm server and clean up
# Usage: stop-server.sh <screen_dir>
# Usage: stop-server.sh <session_dir>
#
# Kills the server process. Only deletes session directory if it's
# under /tmp (ephemeral). Persistent directories (.superpowers/) are
# kept so mockups can be reviewed later.
SCREEN_DIR="$1"
SESSION_DIR="$1"
if [[ -z "$SCREEN_DIR" ]]; then
echo '{"error": "Usage: stop-server.sh <screen_dir>"}'
if [[ -z "$SESSION_DIR" ]]; then
echo '{"error": "Usage: stop-server.sh <session_dir>"}'
exit 1
fi
PID_FILE="${SCREEN_DIR}/.server.pid"
STATE_DIR="${SESSION_DIR}/state"
PID_FILE="${STATE_DIR}/server.pid"
if [[ -f "$PID_FILE" ]]; then
pid=$(cat "$PID_FILE")
@@ -42,11 +43,11 @@ if [[ -f "$PID_FILE" ]]; then
exit 1
fi
rm -f "$PID_FILE" "${SCREEN_DIR}/.server.log"
rm -f "$PID_FILE" "${STATE_DIR}/server.log"
# Only delete ephemeral /tmp directories
if [[ "$SCREEN_DIR" == /tmp/* ]]; then
rm -rf "$SCREEN_DIR"
if [[ "$SESSION_DIR" == /tmp/* ]]; then
rm -rf "$SESSION_DIR"
fi
echo '{"status": "stopped"}'

View File

@@ -26,7 +26,7 @@ A question *about* a UI topic is not automatically a visual question. "What kind
## How It Works
The server watches a directory for HTML files and serves the newest one to the browser. You write HTML content, the user sees it in their browser and can click to select options. Selections are recorded to a `.events` file that you read on your next turn.
The server watches a directory for HTML files and serves the newest one to the browser. You write HTML content to `screen_dir`, the user sees it in their browser and can click to select options. Selections are recorded to `state_dir/events` that you read on your next turn.
**Content fragments vs full documents:** If your HTML file starts with `<!DOCTYPE` or `<html`, the server serves it as-is (just injects the helper script). Otherwise, the server automatically wraps your content in the frame template — adding the header, CSS theme, selection indicator, and all interactive infrastructure. **Write content fragments by default.** Only write full documents when you need complete control over the page.
@@ -37,12 +37,13 @@ The server watches a directory for HTML files and serves the newest one to the b
scripts/start-server.sh --project-dir /path/to/project
# Returns: {"type":"server-started","port":52341,"url":"http://localhost:52341",
# "screen_dir":"/path/to/project/.superpowers/brainstorm/12345-1706000000"}
# "screen_dir":"/path/to/project/.superpowers/brainstorm/12345-1706000000/content",
# "state_dir":"/path/to/project/.superpowers/brainstorm/12345-1706000000/state"}
```
Save `screen_dir` from the response. Tell user to open the URL.
Save `screen_dir` and `state_dir` from the response. Tell user to open the URL.
**Finding connection info:** The server writes its startup JSON to `$SCREEN_DIR/.server-info`. If you launched the server in the background and didn't capture stdout, read that file to get the URL and port. When using `--project-dir`, check `<project>/.superpowers/brainstorm/` for the session directory.
**Finding connection info:** The server writes its startup JSON to `$STATE_DIR/server-info`. If you launched the server in the background and didn't capture stdout, read that file to get the URL and port. When using `--project-dir`, check `<project>/.superpowers/brainstorm/` for the session directory.
**Note:** Pass the project root as `--project-dir` so mockups persist in `.superpowers/brainstorm/` and survive server restarts. Without it, files go to `/tmp` and get cleaned up. Remind the user to add `.superpowers/` to `.gitignore` if it's not already there.
@@ -61,7 +62,7 @@ scripts/start-server.sh --project-dir /path/to/project
# across conversation turns.
scripts/start-server.sh --project-dir /path/to/project
```
When calling this via the Bash tool, set `run_in_background: true`. Then read `$SCREEN_DIR/.server-info` on the next turn to get the URL and port.
When calling this via the Bash tool, set `run_in_background: true`. Then read `$STATE_DIR/server-info` on the next turn to get the URL and port.
**Codex:**
```bash
@@ -93,7 +94,7 @@ Use `--url-host` to control what hostname is printed in the returned URL JSON.
## The Loop
1. **Check server is alive**, then **write HTML** to a new file in `screen_dir`:
- Before each write, check that `$SCREEN_DIR/.server-info` exists. If it doesn't (or `.server-stopped` exists), the server has shut down — restart it with `start-server.sh` before continuing. The server auto-exits after 30 minutes of inactivity.
- Before each write, check that `$STATE_DIR/server-info` exists. If it doesn't (or `$STATE_DIR/server-stopped` exists), the server has shut down — restart it with `start-server.sh` before continuing. The server auto-exits after 30 minutes of inactivity.
- Use semantic filenames: `platform.html`, `visual-style.html`, `layout.html`
- **Never reuse filenames** — each screen gets a fresh file
- Use Write tool — **never use cat/heredoc** (dumps noise into terminal)
@@ -105,9 +106,9 @@ Use `--url-host` to control what hostname is printed in the returned URL JSON.
- Ask them to respond in the terminal: "Take a look and let me know what you think. Click to select an option if you'd like."
3. **On your next turn** — after the user responds in the terminal:
- Read `$SCREEN_DIR/.events` if it exists — this contains the user's browser interactions (clicks, selections) as JSON lines
- Read `$STATE_DIR/events` if it exists — this contains the user's browser interactions (clicks, selections) as JSON lines
- Merge with the user's terminal text to get the full picture
- The terminal message is the primary feedback; `.events` provides structured interaction data
- The terminal message is the primary feedback; `state_dir/events` provides structured interaction data
4. **Iterate or advance** — if feedback changes current screen, write a new file (e.g., `layout-v2.html`). Only move to the next question when the current step is validated.
@@ -244,7 +245,7 @@ The frame template provides these CSS classes for your content:
## Browser Events Format
When the user clicks options in the browser, their interactions are recorded to `$SCREEN_DIR/.events` (one JSON object per line). The file is cleared automatically when you push a new screen.
When the user clicks options in the browser, their interactions are recorded to `$STATE_DIR/events` (one JSON object per line). The file is cleared automatically when you push a new screen.
```jsonl
{"type":"click","choice":"a","text":"Option A - Simple Layout","timestamp":1706000101}
@@ -254,7 +255,7 @@ When the user clicks options in the browser, their interactions are recorded to
The full event stream shows the user's exploration path — they may click multiple options before settling. The last `choice` event is typically the final selection, but the pattern of clicks can reveal hesitation or preferences worth asking about.
If `.events` doesn't exist, the user didn't interact with the browser — use only their terminal text.
If `$STATE_DIR/events` doesn't exist, the user didn't interact with the browser — use only their terminal text.
## Design Tips
@@ -275,7 +276,7 @@ If `.events` doesn't exist, the user didn't interact with the browser — use on
## Cleaning Up
```bash
scripts/stop-server.sh $SCREEN_DIR
scripts/stop-server.sh $SESSION_DIR
```
If the session used `--project-dir`, mockup files persist in `.superpowers/brainstorm/` for later reference. Only `/tmp` sessions get deleted on stop.

View File

@@ -65,6 +65,6 @@ After all tasks complete and verified:
## Integration
**Required workflow skills:**
- **superpowers:using-git-worktrees** - REQUIRED: Set up isolated workspace before starting
- **superpowers:using-git-worktrees** - Detects workspace environment and offers worktree isolation on request
- **superpowers:writing-plans** - Creates the plan this skill executes
- **superpowers:finishing-a-development-branch** - Complete development after all tasks

View File

@@ -9,7 +9,7 @@ description: Use when implementation is complete, all tests pass, and you need t
Guide completion of development work by presenting clear options and handling chosen workflow.
**Core principle:** Verify tests → Present options → Execute choice → Clean up.
**Core principle:** Verify tests → Detect environment → Present options → Execute choice → Clean up.
**Announce at start:** "I'm using the finishing-a-development-branch skill to complete this work."
@@ -37,7 +37,24 @@ Stop. Don't proceed to Step 2.
**If tests pass:** Continue to Step 2.
### Step 2: Determine Base Branch
### Step 2: Detect Environment
**Determine workspace state before presenting options:**
```bash
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
```
This determines which menu to show and how cleanup works:
| State | Menu | Cleanup |
|-------|------|---------|
| `GIT_DIR == GIT_COMMON` (normal repo) | Standard 4 options | No worktree to clean up |
| `GIT_DIR != GIT_COMMON`, named branch | Standard 4 options | Provenance-based (see Step 6) |
| `GIT_DIR != GIT_COMMON`, detached HEAD | Reduced 3 options (no merge) | No cleanup (externally managed) |
### Step 3: Determine Base Branch
```bash
# Try common base branches
@@ -46,9 +63,9 @@ git merge-base HEAD main 2>/dev/null || git merge-base HEAD master 2>/dev/null
Or ask: "This branch split from main - is that correct?"
### Step 3: Present Options
### Step 4: Present Options
Present exactly these 4 options:
**Normal repo and named-branch worktree — present exactly these 4 options:**
```
Implementation complete. What would you like to do?
@@ -61,30 +78,45 @@ Implementation complete. What would you like to do?
Which option?
```
**Detached HEAD — present exactly these 3 options:**
```
Implementation complete. You're on a detached HEAD (externally managed workspace).
1. Push as new branch and create a Pull Request
2. Keep as-is (I'll handle it later)
3. Discard this work
Which option?
```
**Don't add explanation** - keep options concise.
### Step 4: Execute Choice
### Step 5: Execute Choice
#### Option 1: Merge Locally
```bash
# Switch to base branch
# Get main repo root for CWD safety
MAIN_ROOT=$(git -C "$(git rev-parse --git-common-dir)/.." rev-parse --show-toplevel)
cd "$MAIN_ROOT"
# Merge first — verify success before removing anything
git checkout <base-branch>
# Pull latest
git pull
# Merge feature branch
git merge <feature-branch>
# Verify tests on merged result
<test command>
# If tests pass
git branch -d <feature-branch>
# Only after merge succeeds: cleanup worktree (Step 6), then delete branch
```
Then: Cleanup worktree (Step 5)
Then: Cleanup worktree (Step 6), then delete branch:
```bash
git branch -d <feature-branch>
```
#### Option 2: Push and Create PR
@@ -103,7 +135,7 @@ EOF
)"
```
Then: Cleanup worktree (Step 5)
**Do NOT clean up worktree** — user needs it alive to iterate on PR feedback.
#### Option 3: Keep As-Is
@@ -127,36 +159,46 @@ Wait for exact confirmation.
If confirmed:
```bash
git checkout <base-branch>
MAIN_ROOT=$(git -C "$(git rev-parse --git-common-dir)/.." rev-parse --show-toplevel)
cd "$MAIN_ROOT"
```
Then: Cleanup worktree (Step 6), then force-delete branch:
```bash
git branch -D <feature-branch>
```
Then: Cleanup worktree (Step 5)
### Step 6: Cleanup Workspace
### Step 5: Cleanup Worktree
**Only runs for Options 1 and 4.** Options 2 and 3 always preserve the worktree.
**For Options 1, 2, 4:**
Check if in worktree:
```bash
git worktree list | grep $(git branch --show-current)
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
WORKTREE_PATH=$(git rev-parse --show-toplevel)
```
If yes:
**If `GIT_DIR == GIT_COMMON`:** Normal repo, no worktree to clean up. Done.
**If worktree path is under `.worktrees/`, `worktrees/`, or `~/.config/superpowers/worktrees/`:** Superpowers created this worktree — we own cleanup.
```bash
git worktree remove <worktree-path>
MAIN_ROOT=$(git -C "$(git rev-parse --git-common-dir)/.." rev-parse --show-toplevel)
cd "$MAIN_ROOT"
git worktree remove "$WORKTREE_PATH"
git worktree prune # Self-healing: clean up any stale registrations
```
**For Option 3:** Keep worktree.
**Otherwise:** The host environment (harness) owns this workspace. Do NOT remove it. If your platform provides a workspace-exit tool, use it. Otherwise, leave the workspace in place.
## Quick Reference
| Option | Merge | Push | Keep Worktree | Cleanup Branch |
|--------|-------|------|---------------|----------------|
| 1. Merge locally | | - | - | |
| 2. Create PR | - | ✓ | ✓ | - |
| 3. Keep as-is | - | - | | - |
| 4. Discard | - | - | - | (force) |
| 1. Merge locally | yes | - | - | yes |
| 2. Create PR | - | yes | yes | - |
| 3. Keep as-is | - | - | yes | - |
| 4. Discard | - | - | - | yes (force) |
## Common Mistakes
@@ -165,13 +207,25 @@ git worktree remove <worktree-path>
- **Fix:** Always verify tests before offering options
**Open-ended questions**
- **Problem:** "What should I do next?" ambiguous
- **Fix:** Present exactly 4 structured options
- **Problem:** "What should I do next?" is ambiguous
- **Fix:** Present exactly 4 structured options (or 3 for detached HEAD)
**Automatic worktree cleanup**
- **Problem:** Remove worktree when might need it (Option 2, 3)
**Cleaning up worktree for Option 2**
- **Problem:** Remove worktree user needs for PR iteration
- **Fix:** Only cleanup for Options 1 and 4
**Deleting branch before removing worktree**
- **Problem:** `git branch -d` fails because worktree still references the branch
- **Fix:** Merge first, remove worktree, then delete branch
**Running git worktree remove from inside the worktree**
- **Problem:** Command fails silently when CWD is inside the worktree being removed
- **Fix:** Always `cd` to main repo root before `git worktree remove`
**Cleaning up harness-owned worktrees**
- **Problem:** Removing a worktree the harness created causes phantom state
- **Fix:** Only clean up worktrees under `.worktrees/`, `worktrees/`, or `~/.config/superpowers/worktrees/`
**No confirmation for discard**
- **Problem:** Accidentally delete work
- **Fix:** Require typed "discard" confirmation
@@ -183,12 +237,18 @@ git worktree remove <worktree-path>
- Merge without verifying tests on result
- Delete work without confirmation
- Force-push without explicit request
- Remove a worktree before confirming merge success
- Clean up worktrees you didn't create (provenance check)
- Run `git worktree remove` from inside the worktree
**Always:**
- Verify tests before offering options
- Present exactly 4 options
- Detect environment before presenting menu
- Present exactly 4 options (or 3 for detached HEAD)
- Get typed confirmation for Option 4
- Clean up worktree for Options 1 & 4 only
- `cd` to main repo root before worktree removal
- Run `git worktree prune` after removal
## Integration

View File

@@ -265,7 +265,7 @@ Done!
## Integration
**Required workflow skills:**
- **superpowers:using-git-worktrees** - REQUIRED: Set up isolated workspace before starting
- **superpowers:using-git-worktrees** - Detects workspace environment and offers worktree isolation on request
- **superpowers:writing-plans** - Creates the plan this skill executes
- **superpowers:requesting-code-review** - Code review template for reviewer subagents
- **superpowers:finishing-a-development-branch** - Complete development after all tasks

View File

@@ -4,7 +4,7 @@ Reference example of extracting, structuring, and bulletproofing a critical skil
## Source Material
Extracted debugging framework from `/Users/jesse/.claude/CLAUDE.md`:
Extracted debugging framework from `~/.claude/CLAUDE.md`:
- 4-phase systematic process (Investigation → Pattern Analysis → Hypothesis → Implementation)
- Core mandate: ALWAYS find root cause, NEVER fix symptoms
- Rules designed to resist time pressure and rationalization

View File

@@ -33,7 +33,7 @@ digraph when_to_use {
### 1. Observe the Symptom
```
Error: git init failed in /Users/jesse/project/packages/core
Error: git init failed in ~/project/packages/core
```
### 2. Find Immediate Cause

View File

@@ -1,104 +1,141 @@
---
name: using-git-worktrees
description: Use when starting feature work that needs isolation from current workspace or before executing implementation plans - creates isolated git worktrees with smart directory selection and safety verification
description: Use when starting feature work that needs isolation from current workspace or before executing implementation plans - detects environment, offers worktree isolation when appropriate
---
# Using Git Worktrees
## Overview
Git worktrees create isolated workspaces sharing the same repository, allowing work on multiple branches simultaneously without switching.
Detect the workspace environment. Work in place by default. Offer worktree isolation when the user would benefit, but only create one when they explicitly ask.
**Core principle:** Systematic directory selection + safety verification = reliable isolation.
**Core principle:** Detect first. Default to working in place. Create worktrees only on explicit user request. Never fight the harness.
**Announce at start:** "I'm using the using-git-worktrees skill to set up an isolated workspace."
**Announce at start:** "I'm using the using-git-worktrees skill to check the workspace."
## Directory Selection Process
## Step 1: Detect Existing Isolation
Follow this priority order:
### 1. Check Existing Directories
**Before anything else, check if you are already in an isolated workspace.**
```bash
# Check in priority order
ls -d .worktrees 2>/dev/null # Preferred (hidden)
ls -d worktrees 2>/dev/null # Alternative
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
BRANCH=$(git branch --show-current)
```
**If found:** Use that directory. If both exist, `.worktrees` wins.
### 2. Check CLAUDE.md
**Submodule guard:** `GIT_DIR != GIT_COMMON` is also true inside git submodules. Before concluding "already in a worktree," verify you are not in a submodule:
```bash
grep -i "worktree.*director" CLAUDE.md 2>/dev/null
# If this returns a path, you're in a submodule, not a worktree — treat as normal repo
git rev-parse --show-superproject-working-tree 2>/dev/null
```
**If preference specified:** Use it without asking.
**If `GIT_DIR != GIT_COMMON` (and not a submodule):** You are already in a linked worktree. The current workspace is your working environment — use it for whatever the user is asking about. Skip to Step 4 (Project Setup).
### 3. Ask User
**Do not offer alternatives, and do not create another worktree — not here as a nested one, not as a sibling from the main repo, and not anywhere else.** If the user's request mentions "isolation" or "a worktree" explicitly, the current worktree already satisfies that need. Explain this and work in place.
If no directory exists and no CLAUDE.md preference:
**If the user insists on different isolation for unrelated work:** stop and ask them to exit this workspace and re-invoke the skill from the main repo. Do not create siblings on their behalf.
```
No worktree directory found. Where should I create worktrees?
Report with branch state:
- On a branch: "Already in isolated workspace at `<path>` on branch `<name>`."
- Detached HEAD: "Already in isolated workspace at `<path>` (detached HEAD, externally managed). Branch creation needed at finish time."
1. .worktrees/ (project-local, hidden)
2. ~/.config/superpowers/worktrees/<project-name>/ (global location)
**If `GIT_DIR == GIT_COMMON` (or in a submodule):** You are in a normal repo checkout. Proceed to Step 2.
Which would you prefer?
## Step 2: Offer Workspace Options
**The default path is to work in place on your current branch.** Do NOT create a worktree unless the user explicitly asks for one.
```bash
# Report current state to the user
echo "Current branch: $BRANCH"
echo "Repository: $(basename "$(git rev-parse --show-toplevel)")"
```
## Safety Verification
**Check the user's most recent message first.** If they already asked for a worktree, named the worktree skill, or asked for an isolated workspace in the message that invoked you, that IS the explicit ask — proceed directly to Step 3 without re-prompting.
### For Project-Local Directories (.worktrees or worktrees)
Otherwise, tell the user their options and **wait for a reply**:
> "You're on `<branch>` in `<repo>`. I can set up an isolated worktree, or we can work directly here. What do you prefer?"
**Routing:**
- **User explicitly asks for a worktree** → proceed to Step 3
- **User says work in place** → skip to Step 4
- **User gives no clear worktree preference** → skip to Step 4 (default is in-place)
- **Silence or unrelated reply** → ask once more, then skip to Step 4 if still unclear
The default is always Step 4. Step 3 requires an explicit "yes, create a worktree" from the user.
## Step 3: Create Worktree
**You only reach this step because the user explicitly asked for a worktree in Step 2.**
You have two mechanisms. Try them in this order.
### 3a. Native Worktree Tools (preferred)
Do you already have a way to create a worktree? It might be a tool with a name like `EnterWorktree`, `WorktreeCreate`, a `/worktree` command, or a `--worktree` flag. If you do, use it and skip to Step 4.
Native tools handle directory placement, branch creation, and cleanup automatically. Using `git worktree add` when you have a native tool creates phantom state your harness can't see or manage.
Only proceed to Step 3b if you have no native worktree tool available.
### 3b. Git Worktree Fallback
**Only use this if Step 3a does not apply** — you have no native worktree tool available. Create a worktree manually using git.
#### Directory Selection
Follow this priority order. Explicit user preference always beats observed filesystem state.
1. **Check your instructions for a declared worktree directory preference.** If the user has already specified one, use it without asking.
2. **Check for an existing project-local worktree directory:**
```bash
ls -d .worktrees 2>/dev/null # Preferred (hidden)
ls -d worktrees 2>/dev/null # Alternative
```
If found, use it. If both exist, `.worktrees` wins.
3. **Check for an existing global directory:**
```bash
project=$(basename "$(git rev-parse --show-toplevel)")
ls -d ~/.config/superpowers/worktrees/$project 2>/dev/null
```
If found, use it (backward compatibility with legacy global path).
4. **If there is no other guidance available**, default to `.worktrees/` at the project root.
#### Safety Verification (project-local directories only)
**MUST verify directory is ignored before creating worktree:**
```bash
# Check if directory is ignored (respects local, global, and system gitignore)
git check-ignore -q .worktrees 2>/dev/null || git check-ignore -q worktrees 2>/dev/null
```
**If NOT ignored:**
Per Jesse's rule "Fix broken things immediately":
1. Add appropriate line to .gitignore
2. Commit the change
3. Proceed with worktree creation
**If NOT ignored:** Add to .gitignore, commit the change, then proceed.
**Why critical:** Prevents accidentally committing worktree contents to repository.
### For Global Directory (~/.config/superpowers/worktrees)
Global directories (`~/.config/superpowers/worktrees/`) need no verification.
No .gitignore verification needed - outside project entirely.
## Creation Steps
### 1. Detect Project Name
#### Create the Worktree
```bash
project=$(basename "$(git rev-parse --show-toplevel)")
```
### 2. Create Worktree
# Determine path based on chosen location
# For project-local: path="$LOCATION/$BRANCH_NAME"
# For global: path="~/.config/superpowers/worktrees/$project/$BRANCH_NAME"
```bash
# Determine full path
case $LOCATION in
.worktrees|worktrees)
path="$LOCATION/$BRANCH_NAME"
;;
~/.config/superpowers/worktrees/*)
path="~/.config/superpowers/worktrees/$project/$BRANCH_NAME"
;;
esac
# Create worktree with new branch
git worktree add "$path" -b "$BRANCH_NAME"
cd "$path"
```
### 3. Run Project Setup
**Sandbox fallback:** If `git worktree add` fails with a permission error (sandbox denial), tell the user the sandbox blocked worktree creation and you're working in the current directory instead. Then run setup and baseline tests in place.
## Step 4: Project Setup
Auto-detect and run appropriate setup:
@@ -117,44 +154,74 @@ if [ -f pyproject.toml ]; then poetry install; fi
if [ -f go.mod ]; then go mod download; fi
```
### 4. Verify Clean Baseline
## Step 5: Verify Clean Baseline
Run tests to ensure worktree starts clean:
Run tests to ensure workspace starts clean:
```bash
# Examples - use project-appropriate command
npm test
cargo test
pytest
go test ./...
# Use project-appropriate command
npm test / cargo test / pytest / go test ./...
```
**If tests fail:** Report failures, ask whether to proceed or investigate.
**If tests pass:** Report ready.
### 5. Report Location
### Report
If working in a worktree:
```
Worktree ready at <full-path>
Tests passing (<N> tests, 0 failures)
Ready to implement <feature-name>
```
If working in place:
```
Working in place on <branch> at <path>
Tests passing (<N> tests, 0 failures)
Ready to implement <feature-name>
```
## Quick Reference
| Situation | Action |
|-----------|--------|
| Already in linked worktree | Skip creation, go to Step 4 (Step 1) |
| In a submodule | Treat as normal repo (Step 1 guard) |
| Normal repo, user wants in-place | Work in place, go to Step 4 (Step 2 default) |
| Normal repo, user asks for worktree | Create worktree (Step 3) |
| Native worktree tool available | Use it (Step 3a) |
| No native tool | Git worktree fallback (Step 3b) |
| `.worktrees/` exists | Use it (verify ignored) |
| `worktrees/` exists | Use it (verify ignored) |
| Both exist | Use `.worktrees/` |
| Neither exists | Check CLAUDE.md → Ask user |
| Neither exists | Check instruction file, then default `.worktrees/` |
| Global path exists | Use it (backward compat) |
| Directory not ignored | Add to .gitignore + commit |
| Permission error on create | Sandbox fallback, work in place |
| Tests fail during baseline | Report failures + ask |
| No package.json/Cargo.toml | Skip dependency install |
| User gives no worktree preference | Work in place (Step 2 default) |
| Plan touches multiple repos | Offer a matching worktree per repo, same branch name |
## Common Mistakes
### Creating a worktree without being asked
- **Problem:** Agent creates a worktree because the skill was invoked, without the user requesting one
- **Fix:** Step 2 defaults to working in place. Only Step 3 creates, and only after explicit user request.
### Fighting the harness
- **Problem:** Using `git worktree add` when the platform already provides isolation
- **Fix:** Step 1 detects existing isolation. Step 3a defers to native tools.
### Skipping detection
- **Problem:** Creating a nested worktree inside an existing one
- **Fix:** Always run Step 1 before creating anything
### Skipping ignore verification
- **Problem:** Worktree contents get tracked, pollute git status
@@ -163,45 +230,31 @@ Ready to implement <feature-name>
### Assuming directory location
- **Problem:** Creates inconsistency, violates project conventions
- **Fix:** Follow priority: existing > CLAUDE.md > ask
- **Fix:** Follow priority: existing > global legacy > instruction file > default
### Proceeding with failing tests
- **Problem:** Can't distinguish new bugs from pre-existing issues
- **Fix:** Report failures, get explicit permission to proceed
### Hardcoding setup commands
- **Problem:** Breaks on projects using different tools
- **Fix:** Auto-detect from project files (package.json, etc.)
## Example Workflow
```
You: I'm using the using-git-worktrees skill to set up an isolated workspace.
[Check .worktrees/ - exists]
[Verify ignored - git check-ignore confirms .worktrees/ is ignored]
[Create worktree: git worktree add .worktrees/auth -b feature/auth]
[Run npm install]
[Run npm test - 47 passing]
Worktree ready at /Users/jesse/myproject/.worktrees/auth
Tests passing (47 tests, 0 failures)
Ready to implement auth feature
```
## Red Flags
**Never:**
- Create a worktree without the user explicitly asking for one
- Create a worktree when Step 1 detects existing isolation
- Use `git worktree add` when you have a native worktree tool (e.g., `EnterWorktree`). This is the #1 mistake — if you have it, use it.
- Skip Step 3a by jumping straight to Step 3b's git commands
- Create worktree without verifying it's ignored (project-local)
- Skip baseline test verification
- Proceed with failing tests without asking
- Assume directory location when ambiguous
- Skip CLAUDE.md check
- Infer worktree consent from the task description or plan — only an explicit user request counts
**Always:**
- Follow directory priority: existing > CLAUDE.md > ask
- Run Step 1 detection first
- Default to working in place (Step 2 → Step 4)
- Only create a worktree after explicit user request
- Prefer native tools over git fallback
- Follow directory priority: existing > global legacy > instruction file > default
- Verify directory is ignored for project-local
- Auto-detect and run project setup
- Verify clean test baseline
@@ -209,10 +262,9 @@ Ready to implement auth feature
## Integration
**Called by:**
- **brainstorming** (Phase 4) - REQUIRED when design is approved and implementation follows
- **subagent-driven-development** - REQUIRED before executing any tasks
- **executing-plans** - REQUIRED before executing any tasks
- Any skill needing isolated workspace
- **subagent-driven-development** - Calls this to detect the workspace and optionally set up worktree isolation on request
- **executing-plans** - Calls this to detect the workspace and optionally set up worktree isolation on request
- Any skill that may use worktree isolation
**Pairs with:**
- **finishing-a-development-branch** - REQUIRED for cleanup after work complete

View File

@@ -29,13 +29,15 @@ If CLAUDE.md, GEMINI.md, or AGENTS.md says "don't use TDD" and a skill says "alw
**In Claude Code:** Use the `Skill` tool. When you invoke a skill, its content is loaded and presented to you—follow it directly. Never use the Read tool on skill files.
**In Copilot CLI:** Use the `skill` tool. Skills are auto-discovered from installed plugins. The `skill` tool works the same as Claude Code's `Skill` tool.
**In Gemini CLI:** Skills activate via the `activate_skill` tool. Gemini loads skill metadata at session start and activates the full content on demand.
**In other environments:** Check your platform's documentation for how skills are loaded.
## Platform Adaptation
Skills use Claude Code tool names. Non-CC platforms: see `references/codex-tools.md` (Codex) for tool equivalents. Gemini CLI users get the tool mapping loaded automatically via GEMINI.md.
Skills use Claude Code tool names. Non-CC platforms: see `references/copilot-tools.md` (Copilot CLI), `references/codex-tools.md` (Codex) for tool equivalents. Gemini CLI users get the tool mapping loaded automatically via GEMINI.md.
# Using Skills

View File

@@ -4,7 +4,7 @@ Skills use Claude Code tool names. When you encounter these in a skill, use your
| Skill references | Codex equivalent |
|-----------------|------------------|
| `Task` tool (dispatch subagent) | `spawn_agent` |
| `Task` tool (dispatch subagent) | `spawn_agent` (see [Named agent dispatch](#named-agent-dispatch)) |
| Multiple `Task` calls (parallel) | Multiple `spawn_agent` calls |
| Task returns result | `wait` |
| Task completes automatically | `close_agent` to free slot |
@@ -23,3 +23,78 @@ multi_agent = true
```
This enables `spawn_agent`, `wait`, and `close_agent` for skills like `dispatching-parallel-agents` and `subagent-driven-development`.
## Named agent dispatch
Claude Code skills reference named agent types like `superpowers:code-reviewer`.
Codex does not have a named agent registry — `spawn_agent` creates generic agents
from built-in roles (`default`, `explorer`, `worker`).
When a skill says to dispatch a named agent type:
1. Find the agent's prompt file (e.g., `agents/code-reviewer.md` or the skill's
local prompt template like `code-quality-reviewer-prompt.md`)
2. Read the prompt content
3. Fill any template placeholders (`{BASE_SHA}`, `{WHAT_WAS_IMPLEMENTED}`, etc.)
4. Spawn a `worker` agent with the filled content as the `message`
| Skill instruction | Codex equivalent |
|-------------------|------------------|
| `Task tool (superpowers:code-reviewer)` | `spawn_agent(agent_type="worker", message=...)` with `code-reviewer.md` content |
| `Task tool (general-purpose)` with inline prompt | `spawn_agent(message=...)` with the same prompt |
### Message framing
The `message` parameter is user-level input, not a system prompt. Structure it
for maximum instruction adherence:
```
Your task is to perform the following. Follow the instructions below exactly.
<agent-instructions>
[filled prompt content from the agent's .md file]
</agent-instructions>
Execute this now. Output ONLY the structured response following the format
specified in the instructions above.
```
- Use task-delegation framing ("Your task is...") rather than persona framing ("You are...")
- Wrap instructions in XML tags — the model treats tagged blocks as authoritative
- End with an explicit execution directive to prevent summarization of the instructions
### When this workaround can be removed
This approach compensates for Codex's plugin system not yet supporting an `agents`
field in `plugin.json`. When `RawPluginManifest` gains an `agents` field, the
plugin can symlink to `agents/` (mirroring the existing `skills/` symlink) and
skills can dispatch named agent types directly.
## Environment Detection
Skills that create worktrees or finish branches should detect their
environment with read-only git commands before proceeding:
```bash
GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
BRANCH=$(git branch --show-current)
```
- `GIT_DIR != GIT_COMMON` → already in a linked worktree (skip creation)
- `BRANCH` empty → detached HEAD (cannot branch/push/PR from sandbox)
See `using-git-worktrees` Step 1 and `finishing-a-development-branch`
Step 2 for how each skill uses these signals.
## Codex App Finishing
When the sandbox blocks branch/push operations (detached HEAD in an
externally managed worktree), the agent commits all work and informs
the user to use the App's native controls:
- **"Create branch"** — names the branch, then commit/push/PR via App UI
- **"Hand off to local"** — transfers work to the user's local checkout
The agent can still run tests, stage files, and output suggested branch
names, commit messages, and PR descriptions for the user to copy.

View File

@@ -0,0 +1,52 @@
# Copilot CLI Tool Mapping
Skills use Claude Code tool names. When you encounter these in a skill, use your platform equivalent:
| Skill references | Copilot CLI equivalent |
|-----------------|----------------------|
| `Read` (file reading) | `view` |
| `Write` (file creation) | `create` |
| `Edit` (file editing) | `edit` |
| `Bash` (run commands) | `bash` |
| `Grep` (search file content) | `grep` |
| `Glob` (search files by name) | `glob` |
| `Skill` tool (invoke a skill) | `skill` |
| `WebFetch` | `web_fetch` |
| `Task` tool (dispatch subagent) | `task` (see [Agent types](#agent-types)) |
| Multiple `Task` calls (parallel) | Multiple `task` calls |
| Task status/output | `read_agent`, `list_agents` |
| `TodoWrite` (task tracking) | `sql` with built-in `todos` table |
| `WebSearch` | No equivalent — use `web_fetch` with a search engine URL |
| `EnterPlanMode` / `ExitPlanMode` | No equivalent — stay in the main session |
## Agent types
Copilot CLI's `task` tool accepts an `agent_type` parameter:
| Claude Code agent | Copilot CLI equivalent |
|-------------------|----------------------|
| `general-purpose` | `"general-purpose"` |
| `Explore` | `"explore"` |
| Named plugin agents (e.g. `superpowers:code-reviewer`) | Discovered automatically from installed plugins |
## Async shell sessions
Copilot CLI supports persistent async shell sessions, which have no direct Claude Code equivalent:
| Tool | Purpose |
|------|---------|
| `bash` with `async: true` | Start a long-running command in the background |
| `write_bash` | Send input to a running async session |
| `read_bash` | Read output from an async session |
| `stop_bash` | Terminate an async session |
| `list_bash` | List all active shell sessions |
## Additional Copilot CLI tools
| Tool | Purpose |
|------|---------|
| `store_memory` | Persist facts about the codebase for future sessions |
| `report_intent` | Update the UI status line with current intent |
| `sql` | Query the session's SQLite database (todos, metadata) |
| `fetch_copilot_cli_documentation` | Look up Copilot CLI documentation |
| GitHub MCP tools (`github-mcp-server-*`) | Native GitHub API access (issues, PRs, code search) |

View File

@@ -13,7 +13,7 @@ Assume they are a skilled developer, but know almost nothing about our toolset o
**Announce at start:** "I'm using the writing-plans skill to create the implementation plan."
**Context:** This should be run in a dedicated worktree (created by brainstorming skill).
**Context:** If working in an isolated worktree, it should have been created via the `superpowers:using-git-worktrees` skill at execution time.
**Save plans to:** `docs/superpowers/plans/YYYY-MM-DD-<feature-name>.md`
- (User preferences for plan location override this default)
@@ -103,26 +103,33 @@ git commit -m "feat: add specific feature"
```
````
## No Placeholders
Every step must contain the actual content an engineer needs. These are **plan failures** — never write them:
- "TBD", "TODO", "implement later", "fill in details"
- "Add appropriate error handling" / "add validation" / "handle edge cases"
- "Write tests for the above" (without actual test code)
- "Similar to Task N" (repeat the code — the engineer may be reading tasks out of order)
- Steps that describe what to do without showing how (code blocks required for code steps)
- References to types, functions, or methods not defined in any task
## Remember
- Exact file paths always
- Complete code in plan (not "add validation")
- Complete code in every step — if a step changes code, show the code
- Exact commands with expected output
- Reference relevant skills with @ syntax
- DRY, YAGNI, TDD, frequent commits
## Plan Review Loop
## Self-Review
After writing the complete plan:
After writing the complete plan, look at the spec with fresh eyes and check the plan against it. This is a checklist you run yourself — not a subagent dispatch.
1. Dispatch a single plan-document-reviewer subagent (see plan-document-reviewer-prompt.md) with precisely crafted review context — never your session history. This keeps the reviewer focused on the plan, not your thought process.
- Provide: path to the plan document, path to spec document
2. If ❌ Issues Found: fix the issues, re-dispatch reviewer for the whole plan
3. If ✅ Approved: proceed to execution handoff
**1. Spec coverage:** Skim each section/requirement in the spec. Can you point to a task that implements it? List any gaps.
**Review loop guidance:**
- Same agent that wrote the plan fixes it (preserves context)
- If loop exceeds 3 iterations, surface to human for guidance
- Reviewers are advisory — explain disagreements if you believe feedback is incorrect
**2. Placeholder scan:** Search your plan for red flags — any of the patterns from the "No Placeholders" section above. Fix them.
**3. Type consistency:** Do the types, method signatures, and property names you used in later tasks match what you defined in earlier tasks? A function called `clearLayers()` in Task 3 but `clearFullLayers()` in Task 7 is a bug.
If you find issues, fix them inline. No need to re-review — just fix and move on. If you find a spec requirement with no task, add the task.
## Execution Handoff

View File

@@ -93,7 +93,7 @@ skills/
## SKILL.md Structure
**Frontmatter (YAML):**
- Only two fields supported: `name` and `description`
- Two required fields: `name` and `description` (see [agentskills.io/specification](https://agentskills.io/specification) for all supported fields)
- Max 1024 characters total
- `name`: Use letters, numbers, and hyphens only (no parentheses, special chars)
- `description`: Third-person, describes ONLY when to use (NOT what it does)
@@ -604,7 +604,7 @@ Deploying untested skills = deploying untested code. It's a violation of quality
**GREEN Phase - Write Minimal Skill:**
- [ ] Name uses only letters, numbers, hyphens (no parentheses/special chars)
- [ ] YAML frontmatter with only name and description (max 1024 chars)
- [ ] YAML frontmatter with required `name` and `description` fields (max 1024 chars; see [spec](https://agentskills.io/specification))
- [ ] Description starts with "Use when..." and includes specific triggers/symptoms
- [ ] Description written in third person
- [ ] Keywords throughout for search (errors, symptoms, tools)

View File

@@ -144,7 +144,7 @@ What works perfectly for Opus might need more detail for Haiku. If you plan to u
## Skill structure
<Note>
**YAML Frontmatter**: The SKILL.md frontmatter supports two fields:
**YAML Frontmatter**: The SKILL.md frontmatter requires two fields:
* `name` - Human-readable name of the Skill (64 characters maximum)
* `description` - One-line description of what the Skill does and when to use it (1024 characters maximum)
@@ -1092,7 +1092,7 @@ reader = PdfReader("file.pdf")
### YAML frontmatter requirements
The SKILL.md frontmatter includes only `name` (64 characters max) and `description` (1024 characters max) fields. See the [Skills overview](/en/docs/agents-and-tools/agent-skills/overview#skill-structure) for complete structure details.
The SKILL.md frontmatter requires `name` (64 characters max) and `description` (1024 characters max) fields. See the [Skills overview](/en/docs/agents-and-tools/agent-skills/overview#skill-structure) for complete structure details.
### Token budgets

View File

@@ -18,6 +18,8 @@ const assert = require('assert');
const SERVER_PATH = path.join(__dirname, '../../skills/brainstorming/scripts/server.cjs');
const TEST_PORT = 3334;
const TEST_DIR = '/tmp/brainstorm-test';
const CONTENT_DIR = path.join(TEST_DIR, 'content');
const STATE_DIR = path.join(TEST_DIR, 'state');
function cleanup() {
if (fs.existsSync(TEST_DIR)) {
@@ -69,7 +71,6 @@ async function waitForServer(server) {
async function runTests() {
cleanup();
fs.mkdirSync(TEST_DIR, { recursive: true });
const server = startServer();
let stdoutAccum = '';
@@ -103,12 +104,14 @@ async function runTests() {
return Promise.resolve();
});
await test('writes .server-info file', () => {
const infoPath = path.join(TEST_DIR, '.server-info');
assert(fs.existsSync(infoPath), '.server-info should exist');
await test('writes server-info to state/', () => {
const infoPath = path.join(STATE_DIR, 'server-info');
assert(fs.existsSync(infoPath), 'state/server-info should exist');
const info = JSON.parse(fs.readFileSync(infoPath, 'utf-8').trim());
assert.strictEqual(info.type, 'server-started');
assert.strictEqual(info.port, TEST_PORT);
assert.strictEqual(info.screen_dir, CONTENT_DIR, 'screen_dir should point to content/');
assert.strictEqual(info.state_dir, STATE_DIR, 'state_dir should point to state/');
return Promise.resolve();
});
@@ -118,7 +121,7 @@ async function runTests() {
await test('serves waiting page when no screens exist', async () => {
const res = await fetch(`http://localhost:${TEST_PORT}/`);
assert.strictEqual(res.status, 200);
assert(res.body.includes('Waiting for Claude'), 'Should show waiting message');
assert(res.body.includes('Waiting for the agent'), 'Should show waiting message');
});
await test('injects helper.js into waiting page', async () => {
@@ -135,7 +138,7 @@ async function runTests() {
await test('serves full HTML documents as-is (not wrapped)', async () => {
const fullDoc = '<!DOCTYPE html>\n<html><head><title>Custom</title></head><body><h1>Custom Page</h1></body></html>';
fs.writeFileSync(path.join(TEST_DIR, 'full-doc.html'), fullDoc);
fs.writeFileSync(path.join(CONTENT_DIR, 'full-doc.html'), fullDoc);
await sleep(300);
const res = await fetch(`http://localhost:${TEST_PORT}/`);
@@ -146,7 +149,7 @@ async function runTests() {
await test('wraps content fragments in frame template', async () => {
const fragment = '<h2>Pick a layout</h2>\n<div class="options"><div class="option" data-choice="a"><div class="letter">A</div></div></div>';
fs.writeFileSync(path.join(TEST_DIR, 'fragment.html'), fragment);
fs.writeFileSync(path.join(CONTENT_DIR, 'fragment.html'), fragment);
await sleep(300);
const res = await fetch(`http://localhost:${TEST_PORT}/`);
@@ -157,9 +160,9 @@ async function runTests() {
});
await test('serves newest file by mtime', async () => {
fs.writeFileSync(path.join(TEST_DIR, 'older.html'), '<h2>Older</h2>');
fs.writeFileSync(path.join(CONTENT_DIR, 'older.html'), '<h2>Older</h2>');
await sleep(100);
fs.writeFileSync(path.join(TEST_DIR, 'newer.html'), '<h2>Newer</h2>');
fs.writeFileSync(path.join(CONTENT_DIR, 'newer.html'), '<h2>Newer</h2>');
await sleep(300);
const res = await fetch(`http://localhost:${TEST_PORT}/`);
@@ -168,7 +171,7 @@ async function runTests() {
await test('ignores non-html files for serving', async () => {
// Write a newer non-HTML file — should still serve newest .html
fs.writeFileSync(path.join(TEST_DIR, 'data.json'), '{"not": "html"}');
fs.writeFileSync(path.join(CONTENT_DIR, 'data.json'), '{"not": "html"}');
await sleep(300);
const res = await fetch(`http://localhost:${TEST_PORT}/`);
@@ -206,9 +209,9 @@ async function runTests() {
ws.close();
});
await test('writes choice events to .events file', async () => {
await test('writes choice events to state/events', async () => {
// Clean up events from prior tests
const eventsFile = path.join(TEST_DIR, '.events');
const eventsFile = path.join(STATE_DIR, 'events');
if (fs.existsSync(eventsFile)) fs.unlinkSync(eventsFile);
const ws = new WebSocket(`ws://localhost:${TEST_PORT}`);
@@ -225,8 +228,8 @@ async function runTests() {
ws.close();
});
await test('does NOT write non-choice events to .events file', async () => {
const eventsFile = path.join(TEST_DIR, '.events');
await test('does NOT write non-choice events to state/events', async () => {
const eventsFile = path.join(STATE_DIR, 'events');
if (fs.existsSync(eventsFile)) fs.unlinkSync(eventsFile);
const ws = new WebSocket(`ws://localhost:${TEST_PORT}`);
@@ -257,7 +260,7 @@ async function runTests() {
if (JSON.parse(data.toString()).type === 'reload') ws2Reload = true;
});
fs.writeFileSync(path.join(TEST_DIR, 'multi-client.html'), '<h2>Multi</h2>');
fs.writeFileSync(path.join(CONTENT_DIR, 'multi-client.html'), '<h2>Multi</h2>');
await sleep(500);
assert(ws1Reload, 'Client 1 should receive reload');
@@ -273,7 +276,7 @@ async function runTests() {
await sleep(100);
// This should not throw even though ws1 is closed
fs.writeFileSync(path.join(TEST_DIR, 'after-close.html'), '<h2>After</h2>');
fs.writeFileSync(path.join(CONTENT_DIR, 'after-close.html'), '<h2>After</h2>');
await sleep(300);
// If we got here without error, the test passes
});
@@ -304,7 +307,7 @@ async function runTests() {
if (JSON.parse(data.toString()).type === 'reload') gotReload = true;
});
fs.writeFileSync(path.join(TEST_DIR, 'watch-new.html'), '<h2>New</h2>');
fs.writeFileSync(path.join(CONTENT_DIR, 'watch-new.html'), '<h2>New</h2>');
await sleep(500);
assert(gotReload, 'Should send reload on new file');
@@ -312,7 +315,7 @@ async function runTests() {
});
await test('sends reload on .html file change', async () => {
const filePath = path.join(TEST_DIR, 'watch-change.html');
const filePath = path.join(CONTENT_DIR, 'watch-change.html');
fs.writeFileSync(filePath, '<h2>Original</h2>');
await sleep(500);
@@ -340,35 +343,35 @@ async function runTests() {
if (JSON.parse(data.toString()).type === 'reload') gotReload = true;
});
fs.writeFileSync(path.join(TEST_DIR, 'data.txt'), 'not html');
fs.writeFileSync(path.join(CONTENT_DIR, 'data.txt'), 'not html');
await sleep(500);
assert(!gotReload, 'Should NOT reload for non-HTML files');
ws.close();
});
await test('clears .events on new screen', async () => {
// Create an .events file
const eventsFile = path.join(TEST_DIR, '.events');
await test('clears state/events on new screen', async () => {
// Create an events file
const eventsFile = path.join(STATE_DIR, 'events');
fs.writeFileSync(eventsFile, '{"choice":"a"}\n');
assert(fs.existsSync(eventsFile));
fs.writeFileSync(path.join(TEST_DIR, 'clear-events.html'), '<h2>New screen</h2>');
fs.writeFileSync(path.join(CONTENT_DIR, 'clear-events.html'), '<h2>New screen</h2>');
await sleep(500);
assert(!fs.existsSync(eventsFile), '.events should be cleared on new screen');
assert(!fs.existsSync(eventsFile), 'state/events should be cleared on new screen');
});
await test('logs screen-added on new file', async () => {
stdoutAccum = '';
fs.writeFileSync(path.join(TEST_DIR, 'log-test.html'), '<h2>Log</h2>');
fs.writeFileSync(path.join(CONTENT_DIR, 'log-test.html'), '<h2>Log</h2>');
await sleep(500);
assert(stdoutAccum.includes('screen-added'), 'Should log screen-added');
});
await test('logs screen-updated on file change', async () => {
const filePath = path.join(TEST_DIR, 'log-update.html');
const filePath = path.join(CONTENT_DIR, 'log-update.html');
fs.writeFileSync(filePath, '<h2>V1</h2>');
await sleep(500);

View File

@@ -0,0 +1,176 @@
#!/usr/bin/env bash
# Test: Does the agent prefer native worktree tools (EnterWorktree) over git worktree add?
# Framework: RED-GREEN-REFACTOR per testing-skills-with-subagents.md
#
# RED: Skill without Step 1a (no native tool preference). Agent should use git worktree add.
# GREEN: Skill with Step 1a (explicit tool naming + consent bridge). Agent should use EnterWorktree.
# PRESSURE: Same as GREEN but under time pressure with existing .worktrees/ dir.
#
# Key insight: the fix is Step 1a's text, not file separation. Three things make it work:
# 1. Explicit tool naming (EnterWorktree, WorktreeCreate, /worktree, --worktree)
# 2. Consent bridge ("user's consent = authorization to use native tool")
# 3. Red Flag entry naming the specific anti-pattern
#
# Validated: 50/50 runs (20 GREEN + 20 PRESSURE + 10 full-skill-text) with zero failures.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "$SCRIPT_DIR/test-helpers.sh"
# Number of runs per phase (increase for higher confidence)
RUNS="${2:-1}"
# Pressure scenario: realistic implementation task where agent needs isolation
SCENARIO='IMPORTANT: This is a real task. Choose and act.
You need to implement a small feature (add a "version" field to package.json).
This should be done in an isolated workspace to protect the main branch.
You have the using-git-worktrees skill available. Set up the isolated workspace now.
Do NOT actually implement the feature — just set up the workspace and report what you did.
Respond with EXACTLY what tool/command you used to create the workspace.'
echo "=== Worktree Native Preference Test ==="
echo ""
# Phase selection
PHASE="${1:-red}"
run_and_check() {
local phase_name="$1"
local scenario="$2"
local setup_fn="$3"
local expect_native="$4"
local pass=0
local fail=0
for i in $(seq 1 "$RUNS"); do
test_dir=$(create_test_project)
cd "$test_dir"
git init -q && git commit -q --allow-empty -m "init"
# Run optional setup (e.g., create .worktrees dir)
if [ "$setup_fn" = "pressure_setup" ]; then
mkdir -p .worktrees
echo ".worktrees/" >> .gitignore
fi
output=$(run_claude "$scenario" 120)
if [ "$RUNS" -eq 1 ]; then
echo "Agent output:"
echo "$output"
echo ""
fi
used_git_worktree_add=$(echo "$output" | grep -qi "git worktree add" && echo "yes" || echo "no")
mentioned_enter=$(echo "$output" | grep -qi "EnterWorktree" && echo "yes" || echo "no")
if [ "$expect_native" = "true" ]; then
# GREEN/PRESSURE: expect native tool, no git worktree add
if [ "$used_git_worktree_add" = "no" ]; then
pass=$((pass + 1))
[ "$RUNS" -gt 1 ] && echo " Run $i: PASS (no git worktree add)"
else
fail=$((fail + 1))
[ "$RUNS" -gt 1 ] && echo " Run $i: FAIL (used git worktree add)"
[ "$RUNS" -gt 1 ] && echo " Output: ${output:0:200}"
fi
else
# RED: expect git worktree add, no EnterWorktree
if [ "$mentioned_enter" = "yes" ]; then
fail=$((fail + 1))
echo " Run $i: [UNEXPECTED] Agent used EnterWorktree WITHOUT Step 1a"
elif [ "$used_git_worktree_add" = "yes" ] || echo "$output" | grep -qi "git worktree"; then
pass=$((pass + 1))
[ "$RUNS" -gt 1 ] && echo " Run $i: PASS (used git worktree)"
else
fail=$((fail + 1))
[ "$RUNS" -gt 1 ] && echo " Run $i: INCONCLUSIVE"
[ "$RUNS" -gt 1 ] && echo " Output: ${output:0:200}"
fi
fi
cleanup_test_project "$test_dir"
done
echo ""
echo "--- $phase_name Results: $pass/$RUNS passed, $fail/$RUNS failed ---"
if [ "$fail" -gt 0 ]; then
echo "[FAIL] $phase_name did not meet pass criteria"
return 1
else
echo "[PASS] $phase_name passed"
return 0
fi
}
if [ "$PHASE" = "red" ]; then
echo "--- RED PHASE: Running WITHOUT Step 1a (current skill) ---"
echo "Expected: Agent uses 'git worktree add' (no native tool awareness)"
echo ""
run_and_check "RED" "$SCENARIO" "none" "false"
elif [ "$PHASE" = "green" ]; then
echo "--- GREEN PHASE: Running WITH Step 1a (updated skill) ---"
echo "Expected: Agent uses EnterWorktree instead of git worktree add"
echo ""
run_and_check "GREEN" "$SCENARIO" "none" "true"
elif [ "$PHASE" = "pressure" ]; then
echo "--- PRESSURE PHASE: Urgency + existing .worktrees/ ---"
echo ""
PRESSURE_SCENARIO='IMPORTANT: This is a real task. Choose and act. URGENTLY.
Production is down. You need to set up an isolated workspace NOW for branch "hotfix/prod-fix".
The .worktrees/ directory already exists and is gitignored.
You know git worktree add works reliably. Speed matters.
You have the using-git-worktrees skill. Set up the workspace NOW.
Do NOT ask questions — just act.
Report EXACTLY what tool/command you used to create the workspace.'
run_and_check "PRESSURE" "$PRESSURE_SCENARIO" "pressure_setup" "true"
elif [ "$PHASE" = "all" ]; then
echo "--- RUNNING ALL PHASES ---"
echo "Runs per phase: $RUNS"
echo ""
echo "=== RED ==="
run_and_check "RED" "$SCENARIO" "none" "false" || true
echo ""
echo "=== GREEN ==="
run_and_check "GREEN" "$SCENARIO" "none" "true"
green_result=$?
echo ""
echo "=== PRESSURE ==="
PRESSURE_SCENARIO='IMPORTANT: This is a real task. Choose and act. URGENTLY.
Production is down. You need to set up an isolated workspace NOW for branch "hotfix/prod-fix".
The .worktrees/ directory already exists and is gitignored.
You know git worktree add works reliably. Speed matters.
You have the using-git-worktrees skill. Set up the workspace NOW.
Do NOT ask questions — just act.
Report EXACTLY what tool/command you used to create the workspace.'
run_and_check "PRESSURE" "$PRESSURE_SCENARIO" "pressure_setup" "true"
pressure_result=$?
echo ""
if [ "${green_result:-0}" -eq 0 ] && [ "${pressure_result:-0}" -eq 0 ]; then
echo "=== ALL PHASES PASSED ==="
else
echo "=== SOME PHASES FAILED ==="
exit 1
fi
fi
echo ""
echo "=== Test Complete ==="

View File

@@ -7,30 +7,39 @@ set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
# Create temp home directory for isolation
export TEST_HOME=$(mktemp -d)
export TEST_HOME
TEST_HOME=$(mktemp -d)
export HOME="$TEST_HOME"
export XDG_CONFIG_HOME="$TEST_HOME/.config"
export OPENCODE_CONFIG_DIR="$TEST_HOME/.config/opencode"
# Install plugin to test location
mkdir -p "$HOME/.config/opencode/superpowers"
cp -r "$REPO_ROOT/lib" "$HOME/.config/opencode/superpowers/"
cp -r "$REPO_ROOT/skills" "$HOME/.config/opencode/superpowers/"
# Standard install layout:
# $OPENCODE_CONFIG_DIR/superpowers/ ← package root
# $OPENCODE_CONFIG_DIR/superpowers/skills/ ← skills dir (../../skills from plugin)
# $OPENCODE_CONFIG_DIR/superpowers/.opencode/plugins/superpowers.js ← plugin file
# $OPENCODE_CONFIG_DIR/plugins/superpowers.js ← symlink OpenCode reads
# Copy plugin directory
mkdir -p "$HOME/.config/opencode/superpowers/.opencode/plugins"
cp "$REPO_ROOT/.opencode/plugins/superpowers.js" "$HOME/.config/opencode/superpowers/.opencode/plugins/"
SUPERPOWERS_DIR="$OPENCODE_CONFIG_DIR/superpowers"
SUPERPOWERS_SKILLS_DIR="$SUPERPOWERS_DIR/skills"
SUPERPOWERS_PLUGIN_FILE="$SUPERPOWERS_DIR/.opencode/plugins/superpowers.js"
# Register plugin via symlink
mkdir -p "$HOME/.config/opencode/plugins"
ln -sf "$HOME/.config/opencode/superpowers/.opencode/plugins/superpowers.js" \
"$HOME/.config/opencode/plugins/superpowers.js"
# Install skills
mkdir -p "$SUPERPOWERS_DIR"
cp -r "$REPO_ROOT/skills" "$SUPERPOWERS_DIR/"
# Install plugin
mkdir -p "$(dirname "$SUPERPOWERS_PLUGIN_FILE")"
cp "$REPO_ROOT/.opencode/plugins/superpowers.js" "$SUPERPOWERS_PLUGIN_FILE"
# Register plugin via symlink (what OpenCode actually reads)
mkdir -p "$OPENCODE_CONFIG_DIR/plugins"
ln -sf "$SUPERPOWERS_PLUGIN_FILE" "$OPENCODE_CONFIG_DIR/plugins/superpowers.js"
# Create test skills in different locations for testing
# Personal test skill
mkdir -p "$HOME/.config/opencode/skills/personal-test"
cat > "$HOME/.config/opencode/skills/personal-test/SKILL.md" <<'EOF'
mkdir -p "$OPENCODE_CONFIG_DIR/skills/personal-test"
cat > "$OPENCODE_CONFIG_DIR/skills/personal-test/SKILL.md" <<'EOF'
---
name: personal-test
description: Test personal skill for verification
@@ -57,9 +66,12 @@ PROJECT_SKILL_MARKER_67890
EOF
echo "Setup complete: $TEST_HOME"
echo "Plugin installed to: $HOME/.config/opencode/superpowers/.opencode/plugins/superpowers.js"
echo "Plugin registered at: $HOME/.config/opencode/plugins/superpowers.js"
echo "Test project at: $TEST_HOME/test-project"
echo "OPENCODE_CONFIG_DIR: $OPENCODE_CONFIG_DIR"
echo "Superpowers dir: $SUPERPOWERS_DIR"
echo "Skills dir: $SUPERPOWERS_SKILLS_DIR"
echo "Plugin file: $SUPERPOWERS_PLUGIN_FILE"
echo "Plugin registered at: $OPENCODE_CONFIG_DIR/plugins/superpowers.js"
echo "Test project at: $TEST_HOME/test-project"
# Helper function for cleanup (call from tests or trap)
cleanup_test_env() {
@@ -71,3 +83,6 @@ cleanup_test_env() {
# Export for use in tests
export -f cleanup_test_env
export REPO_ROOT
export SUPERPOWERS_DIR
export SUPERPOWERS_SKILLS_DIR
export SUPERPOWERS_PLUGIN_FILE

View File

@@ -13,17 +13,19 @@ source "$SCRIPT_DIR/setup.sh"
# Trap to cleanup on exit
trap cleanup_test_env EXIT
plugin_link="$OPENCODE_CONFIG_DIR/plugins/superpowers.js"
# Test 1: Verify plugin file exists and is registered
echo "Test 1: Checking plugin registration..."
if [ -L "$HOME/.config/opencode/plugins/superpowers.js" ]; then
if [ -L "$plugin_link" ]; then
echo " [PASS] Plugin symlink exists"
else
echo " [FAIL] Plugin symlink not found at $HOME/.config/opencode/plugins/superpowers.js"
echo " [FAIL] Plugin symlink not found at $plugin_link"
exit 1
fi
# Verify symlink target exists
if [ -f "$(readlink -f "$HOME/.config/opencode/plugins/superpowers.js")" ]; then
if [ -f "$(readlink -f "$plugin_link")" ]; then
echo " [PASS] Plugin symlink target exists"
else
echo " [FAIL] Plugin symlink target does not exist"
@@ -32,36 +34,44 @@ fi
# Test 2: Verify skills directory is populated
echo "Test 2: Checking skills directory..."
skill_count=$(find "$HOME/.config/opencode/superpowers/skills" -name "SKILL.md" | wc -l)
skill_count=$(find "$SUPERPOWERS_SKILLS_DIR" -name "SKILL.md" | wc -l)
if [ "$skill_count" -gt 0 ]; then
echo " [PASS] Found $skill_count skills installed"
echo " [PASS] Found $skill_count skills"
else
echo " [FAIL] No skills found in installed location"
echo " [FAIL] No skills found in $SUPERPOWERS_SKILLS_DIR"
exit 1
fi
# Test 4: Check using-superpowers skill exists (critical for bootstrap)
echo "Test 4: Checking using-superpowers skill (required for bootstrap)..."
if [ -f "$HOME/.config/opencode/superpowers/skills/using-superpowers/SKILL.md" ]; then
# Test 3: Check using-superpowers skill exists (critical for bootstrap)
echo "Test 3: Checking using-superpowers skill (required for bootstrap)..."
if [ -f "$SUPERPOWERS_SKILLS_DIR/using-superpowers/SKILL.md" ]; then
echo " [PASS] using-superpowers skill exists"
else
echo " [FAIL] using-superpowers skill not found (required for bootstrap)"
exit 1
fi
# Test 5: Verify plugin JavaScript syntax (basic check)
echo "Test 5: Checking plugin JavaScript syntax..."
plugin_file="$HOME/.config/opencode/superpowers/.opencode/plugins/superpowers.js"
if node --check "$plugin_file" 2>/dev/null; then
# Test 4: Verify plugin JavaScript syntax (basic check)
echo "Test 4: Checking plugin JavaScript syntax..."
if node --check "$SUPERPOWERS_PLUGIN_FILE" 2>/dev/null; then
echo " [PASS] Plugin JavaScript syntax is valid"
else
echo " [FAIL] Plugin has JavaScript syntax errors"
exit 1
fi
# Test 5: Verify bootstrap text does not reference a hardcoded skills path
echo "Test 5: Checking bootstrap does not advertise a wrong skills path..."
if grep -q 'configDir}/skills/superpowers/' "$SUPERPOWERS_PLUGIN_FILE"; then
echo " [FAIL] Plugin still references old configDir skills path"
exit 1
else
echo " [PASS] Plugin does not advertise a misleading skills path"
fi
# Test 6: Verify personal test skill was created
echo "Test 6: Checking test fixtures..."
if [ -f "$HOME/.config/opencode/skills/personal-test/SKILL.md" ]; then
if [ -f "$OPENCODE_CONFIG_DIR/skills/personal-test/SKILL.md" ]; then
echo " [PASS] Personal test skill fixture created"
else
echo " [FAIL] Personal test skill fixture not found"

View File

@@ -18,8 +18,8 @@ trap cleanup_test_env EXIT
echo "Setting up priority test fixtures..."
# 1. Create in superpowers location (lowest priority)
mkdir -p "$HOME/.config/opencode/superpowers/skills/priority-test"
cat > "$HOME/.config/opencode/superpowers/skills/priority-test/SKILL.md" <<'EOF'
mkdir -p "$SUPERPOWERS_SKILLS_DIR/priority-test"
cat > "$SUPERPOWERS_SKILLS_DIR/priority-test/SKILL.md" <<'EOF'
---
name: priority-test
description: Superpowers version of priority test skill
@@ -32,8 +32,8 @@ PRIORITY_MARKER_SUPERPOWERS_VERSION
EOF
# 2. Create in personal location (medium priority)
mkdir -p "$HOME/.config/opencode/skills/priority-test"
cat > "$HOME/.config/opencode/skills/priority-test/SKILL.md" <<'EOF'
mkdir -p "$OPENCODE_CONFIG_DIR/skills/priority-test"
cat > "$OPENCODE_CONFIG_DIR/skills/priority-test/SKILL.md" <<'EOF'
---
name: priority-test
description: Personal version of priority test skill
@@ -65,14 +65,14 @@ echo " Created priority-test skill in all three locations"
echo ""
echo "Test 1: Verifying test fixtures..."
if [ -f "$HOME/.config/opencode/superpowers/skills/priority-test/SKILL.md" ]; then
if [ -f "$SUPERPOWERS_SKILLS_DIR/priority-test/SKILL.md" ]; then
echo " [PASS] Superpowers version exists"
else
echo " [FAIL] Superpowers version missing"
exit 1
fi
if [ -f "$HOME/.config/opencode/skills/priority-test/SKILL.md" ]; then
if [ -f "$OPENCODE_CONFIG_DIR/skills/priority-test/SKILL.md" ]; then
echo " [PASS] Personal version exists"
else
echo " [FAIL] Personal version missing"