hanamizuki/solopreneur/merge-pr

Claude Code and agent skill by hanamizuki/solopreneur

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Merge an open PR for the current branch. Refuses to merge when the worktree has uncommitted changes, consolidates the branch's plan file (renaming the latest Handoff Context section to Final Progress and moving the file to done/ in state-machine mode), merges via GitHub CLI, and reports status. Also cleans up stale merged-but-not-removed worktrees from prior sessions on every Claude Code run. Codex V1 uses a merge-only profile that performs no pre-merge plan or worktree mutation. Use when the user says "merge", "merge PR", "merge this", or wants to land the current branch.

Install

npx vskill@latest install hanamizuki/solopreneur/merge-pr

SKILL.md

Merge PR

Host profiles

Before any other action, check whether CODEX_THREAD_ID is set.

  • Unset — Claude Code: run Steps 0–6 unchanged.
  • Set — Codex V1: use the merge-only profile. Skip Steps 0 and 3 entirely;

run Steps 1, 2, 4, 5, and 6. Do not clean another worktree, consolidate or move a plan, create a commit, or push before the CI gate. This deliberately omits Claude's plan-state housekeeping so an Autopilot call after Greenlight cannot mutate the reviewed head before merge.

The Codex profile keeps the current worktree and uses GitHub's atomic --match-head-commit precondition. After GitHub reports the PR merged, it may delete the remote feature branch; failure to delete is reported as cleanup debt, never misreported as a failed merge.

Core Design: Why the Current Session's Own Worktree Cannot Be Deleted

When a Claude Code session starts, it locks a primary working directory. After each Bash tool call completes, the harness resets CWD back to that path. If the current session's worktree is deleted mid-session (e.g. via git worktree remove), the next Bash call fails its cd before any command runs — the entire session becomes non-functional.

Solution: leave your own worktree for the next session to clean up. Every /merge-pr run scans all worktrees and removes stale merged ones, except the current session's own worktree. This caps the long-term residue at 1 leftover worktree, which gets cleaned on the next /merge-pr run from any other session.


Flow

Step 0: Scan and clean up stale worktrees from prior sessions (Claude only)

On Codex, skip this step. On Claude Code, regardless of whether there is a new PR to merge, start by cleaning up any worktrees left over from previous sessions whose branches have already been merged. Skip only the current session's own worktree.

bash
CURRENT_WORKTREE=$(git rev-parse --show-toplevel)
MAIN_REPO=$(dirname "$(git rev-parse --git-common-dir)")
# List all worktrees with their branch names, excluding current and main repo.
# Parsed in bash rather than awk: a skill body is not a safe place for `$N` of
# any kind (the harnesses disagree about substituting it — see
# ../../shared/config.sh). Prefix-stripping also keeps paths containing spaces
# intact, and a detached-HEAD worktree emits no `branch` line so it is skipped.
git worktree list --porcelain | while IFS= read -r line; do
case "$line" in
"worktree "*) wt_path="${line#worktree }" ;;
"branch refs/heads/"*) printf '%s\t%s\n' "$wt_path" "${line#branch refs/heads/}" ;;
esac
done | while IFS=$'\t' read -r wt br; do
[ "$wt" = "$CURRENT_WORKTREE" ] && continue # Never delete own worktree (would break session CWD)
[ "$wt" = "$MAIN_REPO" ] && continue # Never delete main repo
[[ "$br" = "main" || "$br" = "master" ]] && continue
# Only remove if the branch has a merged PR and no currently open PR
# (open PR check guards against branch-name reuse: an older merged PR
# with the same name must not cause deletion of an active worktree)
OPEN_PR=$(gh pr list --state open --head "$br" --json number --jq '.[0].number' 2>/dev/null)
[ -n "$OPEN_PR" ] && continue
PR_NUM=$(gh pr list --state merged --head "$br" --json number --jq '.[0].number' 2>/dev/null)
if [ -n "$PR_NUM" ]; then
echo "Cleaning up stale worktree: $wt (branch: $br, PR #$PR_NUM merged)"
git worktree remove --force "$wt" 2>/dev/null || true
git branch -D "$br" 2>/dev/null || true
git push origin --delete "$br" 2>/dev/null || true
fi
done
# Prune stale entries in the worktree registry (directories deleted manually)
git worktree prune -v
echo "=== Step 0 complete ==="
git worktree list
Step 1: Confirm the current PR to merge

Determine the branch name from the current working context. If there is no open PR for this branch (e.g. the user only wanted stale-worktree cleanup), stop after Step 0.

bash
BRANCH=$(git branch --show-current)
PR_NUMBER=$(gh pr view --json number --jq '.number' 2>/dev/null)
if [ -z "$PR_NUMBER" ]; then
if [ -n "${CODEX_THREAD_ID:-}" ]; then
echo "No open PR for the current branch — stopping without changes."
else
echo "No open PR for the current branch — cleanup only, not merging."
fi
exit 0
fi
echo "Ready to merge: branch=$BRANCH, PR=#$PR_NUMBER"
Step 2: Pre-merge uncommitted-changes check (only runs in a worktree)

Refuse to proceed if the worktree has any uncommitted changes. The new flow commits everything intentionally, so anything uncommitted here is a mistake that must be surfaced before merging.

bash
IS_WORKTREE=$([ "$(git rev-parse --git-common-dir)" != "$(git rev-parse --git-dir)" ] && echo yes || echo no)
if [ "$IS_WORKTREE" = "yes" ]; then
# Refusal: the new flow commits everything intentionally, so anything
# uncommitted at this point is unintentional and must be surfaced.
if ! git diff --quiet HEAD; then
echo "Worktree has uncommitted changes — refusing to merge."
echo "Commit or stash first, then re-run /merge-pr."
exit 1
fi
fi
Step 3: Consolidate the plan file for this branch (Claude only)

On Codex, skip this step. On Claude Code, resolve the plan file associated with the current branch, rename the latest Handoff Context section to Final Progress, delete older same-branch Handoff Context sections, update the status line, optionally move the file to done/, and commit each change.

Step 3a: Resolve plan file path
bash
# --- solopreneur config helpers (sourced from shared/config.sh) ---
# One real shell file, so no harness rewrites the helpers on the way to the
# shell. Claude Code replaces the ${CLAUDE_SKILL_DIR} token below when it loads
# this body; Codex does not. It is SINGLE-quoted on purpose — it is a load-time
# token, not an environment variable, and letting the shell expand the name
# would source whatever an inherited value happened to point at. Unreplaced, it
# is not a directory, so substitute the absolute path of the directory holding
# THIS SKILL.md — every harness states that path to the model.
SOLO_SKILL_DIR='${CLAUDE_SKILL_DIR}'
[ -d "$SOLO_SKILL_DIR" ] || SOLO_SKILL_DIR="<absolute path of the directory holding this SKILL.md>"
SOLO_CONFIG_SH="$SOLO_SKILL_DIR/../../shared/config.sh"
# Three candidates, one contract. Inside the plugin the helpers sit at ../../shared/;
# authoring against this repo reaches them under src/solopreneur/shared/; and a
# skill republished on its own — any flattened skills directory — carries them
# at scripts/config.sh instead, because shared/ is a sibling of skills/ and does
# not travel with a per-skill copy. Try each in order, then STOP. Sourcing a
# file that is not there does not halt the shell: every helper stays undefined,
# every config read returns empty, and the 2026-08-11 A2 run showed where that
# leads — the model "rescued" it with a repo-relative path, which resolves only
# when the repo under review happens to be this plugin's own source repo.
# Canonical authoring keeps non-skill source under src/.
[ -f "$SOLO_CONFIG_SH" ] || SOLO_CONFIG_SH="$SOLO_SKILL_DIR/../../../src/solopreneur/shared/config.sh"
[ -f "$SOLO_CONFIG_SH" ] || SOLO_CONFIG_SH="$SOLO_SKILL_DIR/scripts/config.sh"
[ -f "$SOLO_CONFIG_SH" ] || { echo "HALT: solopreneur config helpers not found under $SOLO_SKILL_DIR — stop here, do not improvise a path"; exit 1; }
source "$SOLO_CONFIG_SH"
# --- end solopreneur config helpers ---
TODOS_CONFIG=$(read_solopreneur_config todos)
PLANS_CONFIG=$(read_solopreneur_config plans)
BACKLOG=$(echo "${TODOS_CONFIG:-{}}" | jq -r '.backlog // empty')
DOING=$(echo "${TODOS_CONFIG:-{}}" | jq -r '.doing // empty')
DONE_DIR=$(echo "${TODOS_CONFIG:-{}}" | jq -r '.done // empty')
PLANS_DIR=$(echo "${PLANS_CONFIG:-{}}" | jq -r '.dir // empty')
PLANS_DIR="${PLANS_DIR:-docs/solopreneur/plans}"
BRANCH=$(git branch --show-current)

Build the list of plan roots — union of $BACKLOG, $DOING, $DONE_DIR, and $PLANS_DIR (whichever are set and exist on disk).

Resolution order (first match wins):

  1. Plan-Branch marker (primary): search for *.md files containing

Plan-Branch: <exact branch> in any plan root.

``bash PLAN_FILE="" for root in "$BACKLOG" "$DOING" "$DONE_DIR" "$PLANS_DIR"; do [ -z "$root" ] && continue [ -d "$root" ] || continue match=$(grep -lF "Plan-Branch: ${BRANCH}" "$root"/*.md 2>/dev/null | head -1) if [ -n "$match" ]; then PLAN_FILE="$match" break fi done ``

  1. Commit-history grep (fallback): only if PLAN_FILE is still empty.

Find the most recent commit matching the handoff pattern and extract its .md file path.

```bash if [ -z "$PLAN_FILE" ]; then HANDOFF_SHA=$(git log --pretty=format:'%H %s' main..HEAD \

if [ -n "$HANDOFF_SHA" ]; then PLAN_FILE=$(git show --name-only --format='' "$HANDOFF_SHA" \

grep -E '\.md$' \
head -1)

fi fi ```

  1. No match → skip consolidation. Not every branch has a plan file.

Step 3 still succeeds; consolidation is simply not performed.

Step 3b: Rule-based consolidation

Only runs if PLAN_FILE is set and the file exists on disk.

Changes to make in `PLAN_FILE`:

  1. Find all ## Handoff Context (<date>, branch: <BRANCH>) sections

(matched by branch name in the heading):

``bash ESC_BRANCH=$(printf '%s' "$BRANCH" | sed 's/[]\.[*^$(){}?+|]/\\&/g') grep -n "^## Handoff Context (.*branch: ${ESC_BRANCH})" "$PLAN_FILE" ``

  1. Rename the latest matching section to

## Final Progress (merged <MERGE_DATE>, branch: <BRANCH>) where <MERGE_DATE> = $(date +%Y-%m-%d). Do NOT modify any [ ] or [x] checkboxes inside the section.

  1. Delete older same-branch Handoff Context sections (all but the latest).

"Older" means earlier in the file. Sections from other branches are left untouched.

  1. Update the top-of-file status line if present: find a line matching

Status: <something> in the first 20 lines and replace it with Status: merged <MERGE_DATE>.

Implementation — use Python to process the file (sed is too fragile for multi-line section deletion). The key invariant: never touch [ ] or [x] checkboxes.

Write the following script to a temp file and run it:

python
import re, sys, os
from datetime import date
def consolidate(path, branch):
merge_date = date.today().isoformat()
with open(path) as f:
content = f.read()
lines = content.split('\n')
# 1. Update status line in first 20 lines
for i in range(min(20, len(lines))):
if re.match(r'^Status:', lines[i]):
lines[i] = f'Status: merged {merge_date}'
break
content = '\n'.join(lines)
# 2. Find all Handoff Context sections for this branch
pattern = rf'^## Handoff Context \([^)]*branch: {re.escape(branch)}\)'
matches = [(m.start(), m.group()) for m in re.finditer(pattern, content, re.MULTILINE)]
if not matches:
print(f'No Handoff Context sections found for branch {branch}')
return content
# 3. Rename the latest (last in file) to Final Progress
latest_start, latest_heading = matches[-1]
new_heading = f'## Final Progress (merged {merge_date}, branch: {branch})'
content = content[:latest_start] + new_heading + content[latest_start + len(latest_heading):]

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
144
Repository forks
9
7-day trend score
25

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/hanamizuki/solopreneur/merge-pr/badge)](https://verified-skill.com/skills/hanamizuki/solopreneur/merge-pr)

Something wrong with this skill or its page? Tell us.