This skill should be used when merging a feature branch to main with automatic conflict resolution and cleanup. It automates merging main into the feature branch, resolving conflicts, pushing, creating a PR, waiting for CI, merging, and cleaning up the worktree.
From soleurnpx claudepluginhub jikig-ai/soleur --plugin soleurThis skill uses the workspace's default tool permissions.
Purpose: Automate the merge pipeline for a single PR -- replacing the manual execution of /ship Phases 3.5-8. Runs lights-out: merge main, resolve conflicts, push, create PR, wait for CI, merge, and cleanup.
Relationship to /ship: Both skills are independent user-invoked entry points. /ship handles artifact validation, compound, and documentation (Phases 0-3). This skill handles the merge-through-cleanup pipeline. They do NOT invoke each other.
Arguments: Optional branch name. If omitted, auto-detects from current branch.
Detect the current environment and record the starting state for rollback. Run these commands separately and store the results:
git rev-parse --abbrev-ref HEAD
git rev-parse HEAD
pwd
git worktree list
Store these four values as BRANCH, STARTING_SHA, WORKTREE_PATH, and REPO_ROOT for use throughout the pipeline.
Load project conventions:
if [[ -f "CLAUDE.md" ]]; then
cat CLAUDE.md
fi
If an argument was provided, verify that branch exists and check it out. Otherwise use the current branch.
Announce:
merge-pr: Starting pipeline for branch: <branch-name>
Starting SHA: <starting-sha> (rollback point)
Replace <branch-name> with the actual branch name and <starting-sha> with the current HEAD SHA.
Validate all pre-conditions before proceeding. On any failure, stop immediately and report.
git rev-parse --abbrev-ref HEAD
If the branch is main or master, stop:
STOPPED: Cannot run merge-pr on the default branch.
Switch to a feature branch or provide a branch name as argument.
git status --porcelain
If output is non-empty, stop:
STOPPED: Uncommitted changes detected. Commit changes before running merge-pr.
Extract the feature name from the branch (strip feat-, feature/, fix-, fix/ prefix). Search for unarchived KB artifacts matching the feature name in:
knowledge-base/project/brainstorms/ (excluding archive/ paths)knowledge-base/project/plans/ (excluding archive/ paths)knowledge-base/project/specs/feat-<feature>/If any unarchived artifacts are found, stop:
STOPPED: Unarchived KB artifacts found for this feature:
<list of files>
Use `skill: soleur:compound` to consolidate and archive these artifacts, then re-run `skill: soleur:merge-pr`.
If no unarchived artifacts exist (either compound already archived them, or no artifacts were created), proceed.
Fetch the latest main and merge into the feature branch:
git fetch origin main
git merge origin/main
If merge is clean (exit code 0): Proceed to Phase 4 (skip Phase 3).
If merge conflicts (exit code non-zero): Proceed to Phase 3.
Note: Version bumping is handled automatically by CI at merge time. This skill does not bump versions.
Identify conflicted files:
git diff --name-only --diff-filter=U
For each conflicted file, apply the appropriate resolution strategy:
| File Pattern | Strategy |
|---|---|
plugins/soleur/CHANGELOG.md | Merge both sides -- see 3.2 |
plugins/soleur/README.md | Accept feature branch component counts |
| Everything else | Claude-assisted resolution -- see 3.3 |
For README.md (accept feature branch):
git checkout --ours plugins/soleur/README.md
git add plugins/soleur/README.md
CHANGELOG requires special handling to preserve entries from both sides without truncation.
Read both sides using git stage numbers (NOT git show HEAD: which only gives one side):
git show :2:plugins/soleur/CHANGELOG.md > /tmp/changelog-ours.md
git show :3:plugins/soleur/CHANGELOG.md > /tmp/changelog-theirs.md
:2: is "ours" (feature branch):3: is "theirs" (main)Read both files. Reconstruct the complete CHANGELOG:
Write the complete reconstructed file. Then verify integrity:
wc -l plugins/soleur/CHANGELOG.md
The line count should be roughly the sum of unique lines from both sides. If the result is suspiciously short (less than 80% of the larger input file), something was truncated -- stop and report.
git add plugins/soleur/CHANGELOG.md
For non-version-file conflicts, read the conflicted file and resolve based on intent:
If resolution confidence is low (ambiguous intent, large conflict spanning many lines, or the changes are contradictory), abort the entire merge:
git merge --abort
Then stop:
STOPPED: Could not confidently resolve conflict in: <file>
The merge has been aborted. Working tree is clean at the starting SHA.
Conflicted files:
<list>
Resolve manually, then re-run /soleur:merge-pr.
After all conflicts are resolved:
git commit -m "merge: resolve conflicts with origin/main"
Push the branch to remote:
git push -u origin <branch-name>
Replace <branch-name> with the actual branch name.
Check for an existing PR:
gh pr list --head <branch-name> --json number,state | jq '.[] | select(.state == "OPEN") | .number'
If a PR exists: Announce the PR number and proceed.
If no PR exists: Before creating, detect associated issue numbers from the branch name (e.g., fix/123-desc) and commit messages (git log origin/main..HEAD --oneline). Then create the PR:
gh pr create --title "<type>: <description>" --body "
## Summary
<bullet points summarizing changes>
Closes #ISSUE_NUMBER
## Test plan
- [ ] CI passes
- [ ] Manual verification of merge pipeline
Generated with [Claude Code](https://claude.com/claude-code)
"
If an issue number was detected, include the Closes #N line. If none, omit it. Derive the title from the branch name and changes. Use feat: for features, fix: for bug fixes.
Announce the PR URL.
gh pr merge <number> --squash --auto
This queues the merge. GitHub waits for all branch protection requirements (CI checks, CLA) to pass, then merges automatically. Do NOT use gh pr checks --watch -- it exits immediately with "no checks reported" when CI hasn't registered yet.
NEVER use --delete-branch. The guardrails hook blocks it when any worktree exists. Branch cleanup is handled by cleanup-merged in Phase 6.
Poll until the PR state is MERGED:
gh pr view <number> --json state --jq .state
If the state is CLOSED (not MERGED), auto-merge was cancelled -- check for CI failures:
gh pr checks --json name,state,description | jq '.[] | select(.state != "SUCCESS")'
Stop and report:
STOPPED: CI check failed.
Failed checks:
<check name>: <description>
Starting SHA for rollback: <starting-sha>
To rollback: git reset --hard <starting-sha> && git push --force-with-lease origin <branch-name>
The cleanup-merged script skips the current working directory's worktree. Navigate to the main repo root first:
Navigate to the main repository root directory (the parent of .worktrees/). Run cd to the repo root path, then verify with pwd.
bash ./plugins/soleur/skills/git-worktree/scripts/worktree-manager.sh cleanup-merged
This detects [gone] branches (remote deleted after merge), removes worktrees, archives spec directories, deletes local branches, and pulls latest main so the next worktree branches from the current state.
Print a summary:
merge-pr complete!
PR: #<number> (<URL>)
Merge SHA: <sha>
Cleanup: <worktrees cleaned or "no cleanup needed">
Rollback (if needed): git reset --hard <starting-sha>
Replace <starting-sha> with the SHA recorded at the start of the pipeline.
If the pipeline fails partway through and the branch has unwanted commits (e.g., merge commit), rollback to the starting state:
git reset --hard <starting-sha>
git push --force-with-lease origin <branch-name>
Replace <starting-sha> and <branch-name> with the actual values recorded at pipeline start.
The starting SHA is recorded in Phase 0 and printed in the end-of-run report.
--delete-branch with gh pr merge. Use cleanup-merged for branch deletion.:2: and :3: stage numbers. Verify line count after writing. Never truncate.