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
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 compose:merge skill to complete this work."
Before presenting options, verify tests pass:
# 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.
Determine workspace state before presenting options:
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) |
# Try common base branches
git merge-base HEAD main 2>/dev/null || git merge-base HEAD master 2>/dev/nullIf the base branch is ambiguous, confirm through compose:ask:
Base BranchThis branch appears to have split from <detected>. Correct?Yes, <detected>, description: Use <detected> as merge targetDifferent branch, description: I'll specify the correct base branchIf no user is available, use the detected base branch and proceed.
Normal repo / named-branch worktree — use compose:ask:
Complete WorkImplementation complete. What would you like to do?Merge locally, description: Merge back to <base-branch>Create PR, description: Push branch and open a Pull RequestKeep as-is, description: Leave the branch — I'll handle it laterDiscard, description: Delete branch and all commitsIf no user is available, merge locally and proceed.
Detached HEAD — use compose:ask:
Complete WorkImplementation complete (detached HEAD, externally managed). What would you like to do?Create PR, description: Push as new branch and open a PRKeep as-is, description: Leave as-is — I'll handle it laterDiscard, description: Delete this work permanentlyIf no user is available, create a PR and proceed.
# 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: cleanup worktree (Step 6), then delete branchThen: Cleanup worktree (Step 6), then delete branch:
git branch -d <feature-branch># 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.
Report: "Keeping branch . Worktree preserved at ."
Don't cleanup worktree.
Confirm through compose:ask:
Confirm DiscardThis will permanently delete branch <name>, all commits, and worktree at <path>. Are you sure?Cancel, description: Keep branch and worktree intactDiscard permanently, description: Cannot be undoneIf no user is available, do NOT discard — keep the branch as-is. (Destructive actions never auto-approve; see compose:ask.)
Only proceed if user selected "Discard permanently". If "Cancel", return to Step 4.
If confirmed:
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:
git branch -D <feature-branch>Only runs for Options 1 and 4. Options 2 and 3 always preserve the worktree.
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/, worktrees/, or ~/.config/compose/worktrees/: Compose created this worktree — we own cleanup.
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 registrationsOtherwise: 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.
| 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) |
Skipping test verification
Open-ended questions
Cleaning up worktree for Option 2
Deleting branch before removing worktree
git branch -d fails because worktree still references the branchRunning git worktree remove from inside the worktree
cd to main repo root before git worktree removeCleaning up harness-owned worktrees
.worktrees/, worktrees/, or ~/.config/compose/worktrees/No confirmation for discard
Never:
git worktree remove from inside the worktreeAlways:
cd to main repo root before worktree removalgit worktree prune after removalff87d47
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.