Merge
bridge.ai/anchor: skills for consistent and effective source control
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.
What its author says it does
Copied from the file, not written here
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.
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.