|
| 1 | +--- |
| 2 | +name: thenmerge |
| 3 | +description: Create a PR (or update the existing one) for the current branch's changes, write/update its description, wait for CI up to 7 minutes, and merge if it's green. Use when the user wants to ship the current work end-to-end — "create the PR and merge it", "ship this", "/thenmerge" — as the terminal step after development/cleanup work is done. |
| 4 | +argument-hint: "[optional: 'force merge' to merge even if CI is red/pending, or a path/area hint like 'only the lsp fix' to scope the PR to a subset of the current changes]" |
| 5 | +--- |
| 6 | + |
| 7 | +This skill **does** open or update a PR, push commits, and merge — those are |
| 8 | +the deliverable, not side effects to ask permission for, since invoking this |
| 9 | +skill is itself the user's authorization for that scope. It does not relax |
| 10 | +any other guardrail: still never force-push, never skip CI, never merge a |
| 11 | +*different* PR than the one this run produced unless told to. |
| 12 | + |
| 13 | +## Why this skill exists |
| 14 | + |
| 15 | +"Create a PR, wait for CI, merge if green" is a recurring multi-step request |
| 16 | +in this repo (PR creation → description → CI poll → merge), and the |
| 17 | +CI-polling part is non-obvious here specifically: `send_later` isn't |
| 18 | +available in this environment, and a CI run turning **green** does not |
| 19 | +deliver a webhook event (only failures and review comments do — see |
| 20 | +`CLAUDE.md`'s "PR babysitting in this environment" section). Without a named |
| 21 | +skill this gets re-derived, and frequently mis-derived (e.g. forgetting that |
| 22 | +the poll has to be an active backgrounded wait, not a webhook subscription), |
| 23 | +every time someone wants the standard "ship it" flow. |
| 24 | + |
| 25 | +## Default scope: the whole current branch |
| 26 | + |
| 27 | +By default, the PR covers **all commits on the current branch** that aren't |
| 28 | +yet on `main` (i.e. exactly what `git diff main...HEAD` shows), and targets |
| 29 | +`main`. Don't narrow scope on your own initiative. |
| 30 | + |
| 31 | +## Scoping hint: "only part of the project" |
| 32 | + |
| 33 | +If the user's invocation names a subset of the current work (a path, a |
| 34 | +component, "just the X fix", etc.), don't put the whole branch into one PR: |
| 35 | + |
| 36 | +1. Identify which commits/files on the current branch correspond to the |
| 37 | + named subset (`git log --oneline`, `git diff main...HEAD --stat`). |
| 38 | +2. Create a **new branch off `main`** (not off the current working branch) |
| 39 | + containing only that subset — cherry-pick the relevant commits, or if the |
| 40 | + subset isn't cleanly commit-separable, ask the user once via |
| 41 | + `AskUserQuestion` how they want it split rather than guessing at a manual |
| 42 | + diff/patch split. |
| 43 | +3. Push that new branch and open the PR from it, per the steps below. Leave |
| 44 | + the rest of the current branch's work for a separate PR (don't open one |
| 45 | + for it unless asked) — note in your final report what was left out and |
| 46 | + why. |
| 47 | + |
| 48 | +## What to do |
| 49 | + |
| 50 | +1. **Check for an existing open PR for this branch first.** Use |
| 51 | + `pull_request_read` (or `list_pull_requests`) to see if one already |
| 52 | + exists for the current (or newly-created scoped) branch → `main`. If one |
| 53 | + exists, update it (description, push any new commits) rather than |
| 54 | + creating a duplicate. |
| 55 | + |
| 56 | +2. **Push the branch.** `git push -u origin <branch-name>` per the standard |
| 57 | + git-push convention (retry on network failure with the documented |
| 58 | + backoff). Skip if already up to date. |
| 59 | + |
| 60 | +3. **Create the PR** (or update the existing one) targeting `main` by |
| 61 | + default, via `mcp__github__create_pull_request` (or `update_pull_request` |
| 62 | + if one already exists): |
| 63 | + - Title: short (under 70 chars), summarizing the change. |
| 64 | + - Body: a `## Summary` (1-3 bullets of what changed and why) and a |
| 65 | + `## Test plan` checklist, same format as the global PR-creation |
| 66 | + convention in this environment's instructions. Base the content on the |
| 67 | + actual commits in scope (`git log`, `git diff main...HEAD`) — don't |
| 68 | + describe work that isn't in this PR's diff. |
| 69 | + |
| 70 | +4. **Wait for CI, polling actively — do not rely on webhook events for |
| 71 | + this.** CI-success delivers no webhook in this environment, so: |
| 72 | + - Start a backgrounded wait (Bash `run_in_background`, e.g. `sleep 90`) |
| 73 | + and re-check `pull_request_read` → `get_status`/`get_check_runs` after |
| 74 | + each wake, repeating until either all checks report a conclusion or a |
| 75 | + **total of 7 minutes** has elapsed since the PR was created/updated. |
| 76 | + - Do not use `ScheduleWakeup`/`send_later` for this short a window — at |
| 77 | + 7 minutes max, an active poll inside this turn is simpler and faster |
| 78 | + than ending the turn and waiting for a re-invocation. |
| 79 | + - If new commits get pushed to the PR mid-poll (e.g. a CI auto-fix), the |
| 80 | + 7-minute budget restarts from that push, not from PR creation. |
| 81 | + |
| 82 | +5. **Decide whether to merge, based on the state at the 7-minute mark (or |
| 83 | + earlier, if all checks already concluded):** |
| 84 | + - **All checks green, `mergeable_state` clean →** merge now via |
| 85 | + `merge_pull_request`. This is the default outcome the skill exists to |
| 86 | + reach — don't ask for confirmation, the user invoking `/thenmerge` |
| 87 | + already authorized it. |
| 88 | + - **Still pending/running at 7 minutes →** do not merge. Report the |
| 89 | + current state (which checks are still running) and stop — don't |
| 90 | + extend the wait further unless the user explicitly asks you to keep |
| 91 | + watching (in which case, switch to the standard PR-babysitting pattern |
| 92 | + in `CLAUDE.md` rather than blocking this turn indefinitely). |
| 93 | + - **Any check failed, or merge conflicts →** do not merge **unless** the |
| 94 | + invocation explicitly instructed an override (e.g. "merge anyway", |
| 95 | + "force merge", "merge even if red"). Absent that explicit instruction, |
| 96 | + report the failure (which check, what it says) and stop — this mirrors |
| 97 | + the standing project convention of never merging red CI without |
| 98 | + explicit sign-off. |
| 99 | + - If an override was given and you merge despite red CI, say so plainly |
| 100 | + in your final report — don't bury a red-CI merge in a routine-sounding |
| 101 | + summary. |
| 102 | + |
| 103 | +6. **After merging**, there's nothing further to watch — don't subscribe to |
| 104 | + PR activity for an already-merged PR. If you left it unmerged (pending or |
| 105 | + failed without override), it's reasonable to leave the user a clear |
| 106 | + status rather than auto-subscribing on their behalf; only call |
| 107 | + `subscribe_pr_activity` if asked to keep watching. |
| 108 | + |
| 109 | +## Output format |
| 110 | + |
| 111 | +Report back concisely: |
| 112 | +1. PR URL, base/head branches, and whether it was newly created or updated. |
| 113 | +2. Final CI state at decision time (per check) and how long the poll ran. |
| 114 | +3. The merge decision and why (merged / held for pending CI / held for red |
| 115 | + CI / merged anyway per explicit override) — be explicit if you merged |
| 116 | + despite non-green CI. |
| 117 | +4. If scoped to a subset of the branch: what was included, what was left |
| 118 | + out, and why. |
0 commit comments