Merge
Merge an approved change request once its gates are green — waiting on the pipeline if needed — then clean up the branch. Use when merging or landing a PR/MR.From its SKILL.md
npx -y skills add chris-peterson/anchor --skill mergeAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
13.1 KB, ~3.4k tokens by cl100k_base, as published. Nobody here has run it
Merge
Land an open change request into the default branch. This is the terminal step
of the anchor lifecycle: /anchor:prepare-review opens the CR and
/anchor:resolve-feedback drives its threads to done; /anchor:merge checks
that the CR is actually ready to land, merges it, and cleans up the branch
behind it. The job is a safe merge: never land a CR that a gate says isn't
ready, and never leave the local checkout stranded on a branch that no longer
exists.
CR = change request: a pull request on GitHub, a merge request on GitLab. Pick
the forge tool by the origin remote.
Don't narrate your work. Every step below is an operating instruction, not a
script to read aloud — follow the execute-quietly discipline:
${CLAUDE_PLUGIN_ROOT}/guides/execute-quietly.md. For this skill, the only
things worth surfacing are the resolved repo and CR in one line, any gate that
blocks, the merge-method decision, and the one-line result.
%%{ init: { 'look': 'handDrawn' } }%%
flowchart TD
Start(["/merge"]) --> Repo["Resolve repo + CR"]
subgraph "Step 1: Gates"
Repo --> Ready{Marked ready?}
Ready -->|Draft| StopDraft["Ask to mark ready"]
Ready -->|Ready| Mergeable{Mergeable?}
Mergeable -->|Conflicts| StopConflict["Stop: rebase first"]
Mergeable -->|Clean| Pipe{Pipeline green?}
Pipe -->|Running| Watch["Watch until settled"]
Watch --> Pipe
Pipe -->|Failed| StopPipe["Stop: report jobs"]
Pipe -->|Passed| Appr{Approvals met?}
Appr -->|Missing| StopAppr["Stop: needs approval"]
Appr -->|Met| Threads{Threads resolved?}
Threads -->|Open| ConfirmThreads["Surface + confirm"]
end
subgraph "Step 2: Method"
Threads -->|Resolved| Method["Resolve method: no-ff default + settings"]
ConfirmThreads --> Method
Method --> Ask["Preview + confirm (yes/no)"]
end
subgraph "Step 3-4: Land + clean up"
Ask --> Do["Merge via gh/glab"]
Do --> Post["Checkout default + pull, delete branch, record tack"]
end
Post --> Report([One-line result])
Task tracking when orchestrated
At the very start, call TaskList. If any task is already in_progress, this
skill is running inside an orchestrator (e.g. a release workflow) — run silently
and do not create your own tasks. Otherwise enumerate:
Step 1: Check the merge gatesStep 2: Choose the merge methodStep 3: Merge and clean up
Target repo and CR
Resolve the repo as the other anchor skills do. With a name argument, resolve
it through tack's repo db (scripts/resolve-target.sh <name>, see the cookbook's
"Resolving a named target repo"): TARGET_VIA=tack → use TARGET_LOCAL as the
checkout — this skill runs git post-merge (checkout, pull, branch delete), so it
needs one; if TARGET_LOCAL is empty, ask where the checkout lives rather than
proceeding. ambiguous → prompt with TARGET_CANDIDATES. cwd (no tack, or no
match) → fall back to a substring-match against repos the session has touched.
With no argument, git rev-parse --show-toplevel from the working directory;
ambiguous → ask. Run git with -C <repo> when the working directory isn't the
target.
When the target repo isn't the working directory, the forge commands below also
default to the cwd repo — retarget each (-R <owner/name> for gh/glab
subcommands; substitute the URL-encoded project for :fullpath and add
--hostname <host> for glab api). Derive owner/name and the host once from
git -C <repo> remote get-url origin, or from a CR URL argument. The full
retargeting rules are in ${CLAUDE_PLUGIN_ROOT}/guides/forge-cookbook.md
("Targeting a repo that isn't the working directory").
Resolve the open CR for the branch (when no URL was given):
# GitLab
glab mr view --output json 2>/dev/null | jq '{iid, web_url, draft, sha, source_branch, target_branch}'
# GitHub
gh pr view --json number,url,isDraft,headRefOid,headRefName,baseRefName 2>/dev/null
No open CR → say so and stop; there's nothing to merge. If the CR is already merged or closed, report that and stop.
Confirm local state matches the CR head (same check as the other skills):
git status --porcelain clean, and local HEAD equals the CR head SHA. If they
disagree, surface the mismatch and stop — merging a CR whose head you haven't seen
means landing code you didn't review here.
Step 1: Check the merge gates
Four gates must be green before the merge. Check them in this order — cheapest and most-blocking first — and stop at the first that fails, so the user fixes one thing at a time. Only the pipeline gate resolves itself with time; the skill waits on that one. The other three need a person (mark ready, get an approval, resolve a thread) or a rebase, so they stop and report rather than spin.
1a. Marked ready (not draft)
A draft CR is the author's "not under review yet" flag; merging one skips the review
it's waiting for. If the resolved CR is a draft (isDraft / .draft true), stop and
ask whether to mark it ready and proceed — don't mark it ready silently:
This CR is still a draft. Marking it ready requests review; merging now lands it without that review. Mark ready and merge anyway?
[yes / no]
On yes, mark it ready (gh pr ready <num> / glab mr update <iid> --ready), then
continue. On no, stop.
1b. Mergeable (no conflicts)
Read the forge's mergeable state (cookbook: "Check a CR's mergeable state"). If the
CR conflicts with the target branch or is behind it in a way the forge won't
auto-resolve, stop and route to a rebase — /anchor:prepare-review owns the
rebase-on-default flow. Don't attempt the merge; the forge would reject it anyway.
1c. Pipeline green — wait if it's still running
Resolve the pipeline for the CR head and read its state with the pipeline helper
(the same one /anchor:pipeline uses), so the poll loop, forge normalization, and
failed-job reporting are shared rather than re-derived:
bash "${CLAUDE_PLUGIN_ROOT}/scripts/pipeline-status.sh"
Map PIPELINE_STATE:
-
success— gate passes; continue. -
running/pending— the pipeline hasn't settled. Don't hand control back for the user to re-ask later — watch it here. Re-launch the helper with--watchas a background call (run_in_background: true; a foreground call holds the turn open until the Bash timeout), then read the settled verdict with the BashOutput tool (nottail/$(...), which trip the command-substitution gate):bash "${CLAUDE_PLUGIN_ROOT}/scripts/pipeline-status.sh" --watchWhen it settles, re-map the terminal state below. If
PIPELINE_TIMEOUT=1(the watch ceiling elapsed), report the last state and offer to keep watching with a longer--timeoutrather than merging on an unsettled pipeline. -
failed/canceled— stop. List each job fromPIPELINE_FAILED_JOBS(name linked to its url) and thePIPELINE_URL, exactly as/anchor:pipelinereports. A red pipeline is a blocked merge; offer to look at a failed job's log rather than fetching it unprompted. -
manual— the pipeline is blocked awaiting a manual action; it won't progress on its own. Say so and stop. -
none— no pipeline for this commit (path/branch filters, or the repo has no CI for this ref). Treat as "no pipeline gate", not a failure — note it and continue. -
absent— origin isn't a recognized forge; there's no pipeline to gate on.
1d. Approvals satisfied
Read the CR's approval state (cookbook: "Check a CR's approvals"). If required
approvals are missing — GitHub reviewDecision is REVIEW_REQUIRED or
CHANGES_REQUESTED; GitLab approvals_left > 0 — stop and report who still needs
to approve. This gate needs a reviewer; the skill can't clear it. On
CHANGES_REQUESTED specifically, point the user at /anchor:resolve-feedback.
Where a repo has no approval rules configured, there's nothing to satisfy — don't invent a requirement; continue.
1e. Review threads resolved
Fetch unresolved, human-authored review threads (cookbook: "List unresolved review
threads" — the same query /anchor:resolve-feedback uses). If any remain, surface
them in one line each (<file:line> — @reviewer — <ask>) and confirm before
landing:
<n>review threads are still unresolved. Merge anyway, or resolve them first?[merge / resolve first]
On resolve first, hand off to /anchor:resolve-feedback and stop. On merge,
continue — some threads are intentionally left open (answered questions the asker
never marked resolved), and the author is the one who knows.
Step 2: Choose the merge method
Land the branch's commits as they stand, with a merge commit that preserves
every commit on the branch (git's --no-ff). This is the method — don't read the
commits to second-guess it. Whether the branch is one commit or twenty, tidy or
noisy, is the author's history to keep; collapsing it isn't this skill's call. The
method changes only when the project or CR is configured for a different one.
Read that configuration and let it override the default:
GitLab — the project pins the merge method; the MR pins the squash choice (cookbook: "Merge a CR"):
glab api projects/:fullpath | jq '{merge_method, squash_option}'
glab mr view <iid> --output json | jq '{squash}'
merge_method:merge→ merge commit, the default (no override).ff→ fast-forward, no merge commit (the project mandates a linear history).rebase_merge→ semi-linear.squash_option:always→ squash (the project requires it).never→ don't squash.default_on/default_off→ the author's per-MRsquashcheckbox decides; honor the MR'ssquashfield.
GitHub — the repo pins which strategies are allowed; there's no enforced default beyond that (cookbook: "Merge a CR"):
gh repo view --json mergeCommitAllowed,squashMergeAllowed,rebaseMergeAllowed
Use --merge when merge commits are allowed. Only when the repo disables them
does the method change — fall to the allowed strategy that still keeps the commits
(--rebase) ahead of the one that discards them (--squash).
Then preview and confirm — state what will happen and take a yes/no; don't offer a method menu. When the method is the merge-commit default:
Merging
!42intomainvia merge commit (preserves 3 commits). Proceed?[yes / no]
When a setting moved it off the default, name the setting so the deviation is visible:
The project's merge method is fast-forward — merging
!42intomainfast-forward, no merge commit. Proceed?[yes / no]
On no, stop and don't merge. The method follows the default and the forge's
settings, not an inline menu — so to land it differently the user either adjusts
the project/CR settings (and you re-read them) or names the method to use. On
yes, merge.
Step 3: Merge
Run the merge for the chosen method (cookbook: "Merge a CR"). Delete the source
branch as part of the merge where the forge supports it (gh pr merge --delete-branch
/ GitLab remove_source_branch — anchor sets --remove-source-branch at create time,
but pass it here too in case it wasn't). This is a forge write:
- On a 401/403 or other auth failure, surface it and ask the user to refresh credentials — do not retry or fall back (the fail-fast-on-auth rule).
- If the forge rejects the merge because a gate flipped since Step 1 (a new commit, a fresh unresolved thread, protection rules), re-read the specific gate it names and surface that — don't force past it.
Step 4: Clean up
Once the forge confirms the merge, leave the local checkout on a clean footing:
-
Return to the default branch and pull the merged result:
git -C <repo> checkout <target> && git -C <repo> pull --ff-only -
Delete the merged local branch. The forge deleted the remote branch (Step 3); remove its local counterpart:
git -C <repo> branch -d <head>-drefuses to delete a branch whose commits aren't merged — if it refuses, surface that rather than forcing with-D; it means the merged commit differs (e.g. a squash produced a new SHA). After a squash, the branch's commits are in the target under a new SHA, so-dwill refuse; confirm the merge landed, then delete with-D. -
Record the tack deliverable when a tack route is bound to this session. Mark the CR's tack done and attach the CR as its deliverable so
/wipand/recapreflect the landed work:tack done <route> <tackId> tack deliverable <route> <tackId> "<cr-url>"No tack route bound → skip this; don't create one just to close it.
Step 5: Report
One line: Merged <CR ref> into <target> (<method>) — <merge-sha>, with the CR URL.
Note the branch cleanup only if it needed the user's attention (a -d that refused).
Nothing more — the merge is the outcome, not a status report.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.