-
Notifications
You must be signed in to change notification settings - Fork 0
github backend primitives
Status: Draft, 2026-05-25. Updated 2026-06-03 — PR-level primitives added; gh auth status conditionalization documented (BILL-60).
The 4 lifecycle skills (:start, :document, :archive, :merge) currently dispatch between Linear (mcp__linear-server__*) and JIRA (mcp__atlassian__*) at three points: Step 1 (MCP detection / $SYSTEM resolution), each skill's call sites for issue read/write operations, and :merge's "advance one state" logic. For github-backed projects (those with system = "github" in .project-conf.toml), each skill currently stops at Step 1 with "No ticket-system MCP found."
This document defines the github backend that closes that gap. It enumerates the github-specific implementation of every primitive the 4 skills need, picks one canonical dispatch shape (MCP-preferred, gh CLI fallback — symmetric with how :pr already works), and specifies the exact snippets each skill should embed at its call sites. The 4 skills consume this doc — each adds a **GitHub:** block alongside its existing **JIRA:** / **Linear:** blocks, copying the relevant snippet verbatim.
The doc exists so the four skills stay consistent and the dispatch decisions are made once, not re-litigated per skill.
- Concrete, verbatim-copyable snippets for every github operation a lifecycle skill performs.
- One canonical Step 1 detection block (3-way ToolSearch +
$SYSTEM+$GH_BACKENDresolution). - Symmetric with
:pr's existing MCP-preferred / CLI-fallback shape — no new dispatch pattern invented. - Tolerant of github MCP namespace variance (canonical
mcp__github__*vs Anthropic-managedmcp__plugin_github_github__*). - Idempotency notes per primitive so
:document's skip-when-current and divergence detection extend cleanly.
- A general GitHub Issues API reference. This doc covers only what the 4 lifecycle skills consume.
- GitHub Projects v2 / status fields / native state machine support. Github's state machine is intentionally shallow here — label-based via
[status_labels], with binary OPEN/CLOSED as the only intrinsic state. - Cross-skill shared helper file. Each
SKILL.mdis self-contained; the snippets in this doc get copied (not included) into each skill that needs them. If duplication becomes painful later, factor then. - Migration of existing Linear/JIRA logic to use the github model. Different systems, different shapes — leave them be.
These are settled here so the 4 skills don't have to re-decide each.
Each lifecycle skill's Step 1 runs a third ToolSearch for github MCP tools alongside the existing JIRA + Linear searches. If github MCP tools are present, set $GH_BACKEND = "MCP" and use MCP tool calls at each call site. If not, set $GH_BACKEND = "CLI", resolve $GH via the trial-path logic from :pr Step 4a, and use gh CLI invocations at each call site.
Rationale: :pr already established this shape. Keeping the lifecycle skills symmetric means a future github MCP installation is picked up automatically without further skill changes.
Github MCPs may be installed under either of two namespaces:
-
mcp__github__*— canonical (e.g., open-source GitHub MCP server). -
mcp__plugin_github_github__*— Anthropic's managedgithub@claude-plugins-officialplugin's namespacing.
The Step 1 ToolSearch tries the canonical names first. On empty result, tries the plugin-namespaced variant. If both empty, fall through to $GH_BACKEND = "CLI". The Step 1 detection block (below) encodes this fallback.
Each SKILL.md is self-contained — there's no include mechanism, and a cross-skill shared file would need a new convention. Github logic gets copied verbatim from this design doc into each of the 4 consumer skills, the same way JIRA and Linear blocks are duplicated today. If maintenance pain accumulates, factor into a shared skills/_shared/github.md (or similar) then.
.project-conf.toml is parsed inline by each skill (no shared parser exists today). The github backend needs to read the nested [status_labels] table — slightly trickier than the flat top-level system / key / prefix. A reusable Bash snippet is specified once (below) and embedded at each skill's call site where a label name is needed.
Embed this verbatim into each lifecycle skill's Step 1, alongside the existing JIRA + Linear ToolSearch calls. Run the three searches in parallel (single message, three tool calls).
ToolSearch(query="select:mcp__atlassian__getJiraIssue,mcp__atlassian__getAccessibleAtlassianResources,mcp__atlassian__getTransitionsForJiraIssue,mcp__atlassian__transitionJiraIssue", max_results=8)
ToolSearch(query="select:mcp__linear-server__get_issue,mcp__linear-server__save_issue,mcp__linear-server__list_issue_statuses", max_results=8)
# New for github — try canonical names first
ToolSearch(query="select:mcp__github__get_issue,mcp__github__add_issue_comment,mcp__github__update_issue,mcp__github__list_issue_comments", max_results=8)
Set $SYSTEM:
- JIRA tools only (and
.project-conf.tomlsayssystem = "jira") →JIRA - Linear tools only (and
system = "linear") →Linear - Github MCP tools resolved above (and
system = "github") →GitHub;$GH_BACKEND = "MCP" - Github MCP search returned empty AND
system = "github"→ run the fallback ToolSearch:If non-empty →ToolSearch(query="select:mcp__plugin_github_github__get_me,mcp__plugin_github_github__add_issue_comment,mcp__plugin_github_github__issue_write", max_results=8)$SYSTEM = "GitHub",$GH_BACKEND = "MCP"(the actual tool names aremcp__plugin_github_github__*; record the namespace prefix as$GH_MCP_NS = "mcp__plugin_github_github__"so call sites can construct the right tool name). If still empty →$SYSTEM = "GitHub",$GH_BACKEND = "CLI"(resolve$GHvia the snippet below). - Multiple systems detected ambiguously (the user's
.project-conf.tomlsays one thing but tools for another are present) → trust.project-conf.toml'ssystemvalue. The other systems' tools are coincidentally available; they're not the active backend. - Neither MCP nor matching
systemvalue → stop with"No ticket-system MCP found for system='<value>' in .project-conf.toml. Configure the matching MCP and retry."
The key rule: .project-conf.toml's system field is authoritative. The ToolSearches are about resolving the backend implementation (MCP vs CLI for github; MCP for JIRA/Linear), not about choosing the system. This avoids the ambiguous case where the user has both Linear and github MCPs installed and the skills can't tell which project they're in.
Used when $GH_BACKEND = "CLI". Lifted from :pr Step 4a verbatim.
For the CLI path, find the
ghbinary. Try each in order; use the first one where<path> --versionsucceeds:
/usr/local/bin/gh$HOME/.local/bin/gh/opt/homebrew/bin/ghcommand -v gh(i.e. whatever$PATHresolves)Save as
$GH. If none resolve, stop:Neither GitHub MCP nor `gh` CLI found. Install one of: - gh CLI: https://cli.github.com/ - GitHub plugin: /plugin install github@claude-plugins-official
Verify auth: $GH auth status succeeds.
Embed this snippet verbatim into each lifecycle skill's Step 1 immediately after $GH_BACKEND is set to CLI.
Several lifecycle skills guard their pre-flight with gh auth status. When $GH_BACKEND = "MCP", this check is irrelevant — the MCP uses Claude Code's own auth context — and running it would require gh on PATH, the dependency we're making optional.
Rule: only run gh auth status when $GH_BACKEND = "CLI". Embed the conditionalized form in each skill that currently runs it unconditionally (:merge, :doc-sync):
if [ "$GH_BACKEND" = "CLI" ]; then
$GH auth status || { echo "Not authenticated — run 'gh auth login' first."; exit 1; }
fiWhen $GH_BACKEND = "MCP", skip the check; if an MCP call later fails on auth, surface that error verbatim — the MCP layer owns its own authentication.
Used when $SYSTEM = "GitHub" and the skill needs the in-progress or in-review label name. Reads the [status_labels] table from .project-conf.toml in cwd.
Bash one-liner per key (no TOML parser dependency — minimal grep/sed):
# Read [status_labels].in_progress (required for github projects).
IN_PROGRESS_LABEL=$(awk '
/^\[status_labels\]/ { in_section=1; next }
/^\[/ && !/^\[status_labels\]/ { in_section=0 }
in_section && /^[[:space:]]*in_progress[[:space:]]*=/ {
sub(/^[^=]*=[[:space:]]*"/, "")
sub(/".*$/, "")
print
exit
}
' .project-conf.toml)
# Same shape for in_review (optional — empty if not present).
IN_REVIEW_LABEL=$(awk '
/^\[status_labels\]/ { in_section=1; next }
/^\[/ && !/^\[status_labels\]/ { in_section=0 }
in_section && /^[[:space:]]*in_review[[:space:]]*=/ {
sub(/^[^=]*=[[:space:]]*"/, "")
sub(/".*$/, "")
print
exit
}
' .project-conf.toml)If $IN_PROGRESS_LABEL is empty (github project but no [status_labels].in_progress): stop with "system='github' requires [status_labels].in_progress in .project-conf.toml. Run /slopstop:gh-init or add it manually."
$IN_REVIEW_LABEL empty is fine — that's the signal for 3-state workflow (used by :merge).
Github's workflow is binary by default (OPEN / CLOSED). [status_labels] adds intermediate states. Two supported workflows:
in_progress |
in_review |
Workflow shape |
:merge behavior |
|---|---|---|---|
| Set | Unset | 3-state (todo → in-progress → done) |
gh issue close $N + remove in_progress label |
| Set | Set | 4-state (todo → in-progress → in-review → done) | Swap: remove in_progress, add in_review. Issue stays open. :archive closes it later. |
:merge reads $IN_PROGRESS_LABEL and $IN_REVIEW_LABEL (per the snippet above) and dispatches based on whether $IN_REVIEW_LABEL is empty.
No introspection of label history or comments needed — the workflow shape is declared in .project-conf.toml.
Each primitive lists both backends (MCP and CLI) plus the consumer(s). MCP names assume canonical namespace; if $GH_MCP_NS = "mcp__plugin_github_github__" was recorded in Step 1, substitute that prefix.
Consumer: :start Step 2, :document Step 2, :archive Step 2, :merge Step 2.
| Backend | Invocation |
|---|---|
| MCP | mcp__github__get_issue(owner=$OWNER, repo=$REPO, issueNumber=$N) |
| CLI | $GH issue view $N --json number,state,body,labels,assignees,milestone,url |
$OWNER and $REPO come from .project-conf.toml's key field, which is owner/repo for github projects.
$N is the numeric part of $TICKET (e.g. $TICKET = BILL-8 → $N = 8).
Returns JSON. Consumer parses fields as needed:
-
state:"OPEN"or"CLOSED"(use for terminal-state gate) -
body: the description markdown (use for divergence detection) -
labels: array of{name, color, description}— find$IN_PROGRESS_LABEL/$IN_REVIEW_LABELmembership -
assignees,milestone: fortask_plan.mdmetadata at:starttime
Consumer: :document Step 2 (find the existing DoD and findings comments by leading marker).
| Backend | Invocation |
|---|---|
| MCP | mcp__github__list_issue_comments(owner=$OWNER, repo=$REPO, issueNumber=$N) |
| CLI | $GH api repos/$OWNER/$REPO/issues/$N/comments |
Returns array of {id, body, user.login, created_at, updated_at}. Consumer matches by leading marker (e.g. ## Definition of Done or ## Findings (from local tracking)) to find the comment to update vs. create.
Consumer: :document Step 6 (description update).
| Backend | Invocation |
|---|---|
| MCP | mcp__github__update_issue(owner=$OWNER, repo=$REPO, issueNumber=$N, body=$BODY) |
| CLI |
$GH issue edit $N --body "$BODY" (or HEREDOC to preserve markdown) |
CLI HEREDOC form for multi-line bodies (recommended):
$GH issue edit $N --body "$(cat <<'EOF'
<body content>
EOF
)"Idempotency: caller should compare the local $BODY (whitespace-trimmed) to the gh-fetched body (also trimmed) and skip the call if equal. Github normalizes \r\n to \n on its end, so the trim is necessary. Existing JIRA/Linear logic already does this; the github path follows the same pattern.
Consumer: :document Step 6 (DoD comment create, findings comment create), :archive Step 4 (delegates to :document).
| Backend | Invocation |
|---|---|
| MCP | mcp__github__add_issue_comment(owner=$OWNER, repo=$REPO, issueNumber=$N, body=$BODY) |
| CLI |
$GH issue comment $N --body "$BODY" (HEREDOC for multi-line) |
Idempotency: the caller distinguishes "create new" from "update existing" using the read-comments primitive. If a comment with the expected leading marker exists, use edit comment instead of add comment.
Consumer: :document Step 6 (DoD/findings comment update when content diverged).
| Backend | Invocation |
|---|---|
| MCP | mcp__github__update_issue_comment(owner=$OWNER, repo=$REPO, commentId=$ID, body=$BODY) |
| CLI | $GH api -X PATCH "repos/$OWNER/$REPO/issues/comments/$ID" -f body="$BODY" |
$ID is the numeric comment id from the read-comments primitive.
Idempotency: same whitespace-trimmed equality check as for body. Skip if the gh-fetched comment body matches local.
Consumer: :start Step 3 (transition to In Progress), :merge Step 5 (4-state: add in_review).
| Backend | Invocation |
|---|---|
| MCP | mcp__github__add_issue_labels(owner=$OWNER, repo=$REPO, issueNumber=$N, labels=[$LABEL]) |
| CLI | $GH issue edit $N --add-label "$LABEL" |
Github silently accepts adding a label that's already on the issue (idempotent by default). The skill doesn't need to pre-check.
Pre-condition: $LABEL must already exist on the repo. The label status:in-progress is created by /slopstop:gh-init (when implemented; see design/ticket-gh-init.md). For the bootstrap on slopstop itself, the label was created manually before BILL-8 started.
Consumer: :merge Step 5 (3-state: remove in_progress; 4-state: remove in_progress while adding in_review).
| Backend | Invocation |
|---|---|
| MCP | mcp__github__remove_issue_label(owner=$OWNER, repo=$REPO, issueNumber=$N, label=$LABEL) |
| CLI | $GH issue edit $N --remove-label "$LABEL" |
Github silently accepts removing a label that wasn't on the issue (idempotent by default).
Consumer: :merge Step 5 (3-state only; 4-state leaves it open for :archive to close).
| Backend | Invocation |
|---|---|
| MCP | mcp__github__update_issue(owner=$OWNER, repo=$REPO, issueNumber=$N, state="closed") |
| CLI | $GH issue close $N |
gh issue close is idempotent (closing a closed issue succeeds quietly). No pre-check needed.
These primitives cover pull request operations consumed by :pr (PR creation, CodeRabbit trigger, polling) and :merge (PR resolution, pre-merge gating, merge, post-merge verification). The same $GH_BACKEND dispatch applies. MCP tool names assume the mcp__plugin_github_github__ namespace; substitute $GH_MCP_NS as recorded in Step 1. $PR is always the numeric PR number (integer).
Consumer: :merge Step 1 (identify which PR to merge).
| Backend | Invocation |
|---|---|
| MCP | ${GH_MCP_NS}list_pull_requests(owner=$OWNER, repo=$REPO, head="$OWNER:$BRANCH", state="open", perPage=5) |
| CLI | $GH pr list --head $BRANCH --state open --json number,title,state,isDraft,mergeable,mergeStateStatus,reviewDecision,statusCheckRollup --limit 5 |
The head parameter requires owner:branch format for the MCP (e.g. iansmith:feat/BILL-60). Returns an array. Zero items → no open PR; more than one → surface the list and ask for --pr <N>; exactly one → $PR.
Consumer: :merge Step 1 (pre-merge gates), Step 4 (post-merge verification).
| Backend | Invocation |
|---|---|
| MCP | ${GH_MCP_NS}pull_request_read(method="get", owner=$OWNER, repo=$REPO, pullNumber=$PR) |
| CLI | $GH pr view $PR --json number,title,headRefName,baseRefName,state,isDraft,mergeable,mergeStateStatus,reviewDecision,statusCheckRollup,url |
For pre-merge gating, consumer checks: state == "OPEN", isDraft == false, mergeable != "CONFLICTING", headRefName == $BRANCH. For post-merge verification (:merge Step 4), re-call and assert state == "MERGED"; capture the merge commit SHA for the Step 7 summary.
Note: the same MCP tool's get_comments / get_review_comments / get_reviews methods drive the CodeRabbit polling fallback described below.
Consumer: :merge Step 4.
| Backend | Invocation |
|---|---|
| MCP | ${GH_MCP_NS}merge_pull_request(owner=$OWNER, repo=$REPO, pullNumber=$PR, merge_method=$STRATEGY) |
| CLI | $GH pr merge $PR --$STRATEGY --delete-branch --auto=false |
$STRATEGY ∈ {"merge", "squash", "rebase"}. Explicitly NOT --auto; the merge happens now or fails now.
Remote branch deletion gap: The CLI path's --delete-branch atomically removes the remote feature branch. The MCP merge_pull_request tool has no equivalent parameter — the remote branch must be deleted separately after a successful MCP merge:
- If
ghis also installed (even when$GH_BACKEND = "MCP"):$GH api -X DELETE "repos/$OWNER/$REPO/git/refs/heads/$BRANCH". - If
ghis absent: skip remote deletion and surface it explicitly:"Remote branch '$BRANCH' was NOT deleted — merge_pull_request does not support delete-branch. Delete it from the GitHub UI."This is a known gap (see Open questions).
Consumer: :pr Step 5b.
| Backend | Invocation |
|---|---|
| MCP | ${GH_MCP_NS}create_pull_request(owner=$OWNER, repo=$REPO, title=$TITLE, body=$BODY, head=$BRANCH, base=$BASE) |
| CLI | $GH pr create --title "$TITLE" --body "$(cat <<'EOF'\n<body>\nEOF\n)" --base "$BASE" --head "$BRANCH" |
Use the HEREDOC form for CLI to preserve markdown in $BODY. Both backends return the PR number and URL; capture as $PR and $PR_URL.
Known limitation — create_pull_request 403 on the Anthropic-managed plugin: mcp__plugin_github_github__create_pull_request may return 403 Resource not accessible by personal access token even when other calls on the same repo (list_pull_requests, pull_request_read, issue_write) succeed. This is a PAT scope limitation specific to the Anthropic-managed plugin's token — not a general MCP or GitHub API issue. Confirmed in BILL-60 against iansmith/slopstop.
Therefore: the CLI fallback is mandatory for PR creation regardless of $GH_PR_BACKEND. :pr Step 5b should either always use gh pr create for the actual create call, or treat a 403 from create_pull_request as a hard signal to retry via CLI rather than stopping. Option 2 (MCP-first, fallback on 403) is preferred — it preserves the MCP-first design intent and picks up automatically if the plugin's scope is ever widened.
Consumer: :pr Pre-flight ($DEFAULT_BRANCH), :merge Step 6a (switch-and-pull base ref).
gh repo view --json defaultBranchRef has no direct MCP equivalent. Use the following fallback chain — steps 1–3 are pure git and require neither gh nor MCP:
-
git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@'— works for any standardgit clone; empty if the remote HEAD wasn't tracked. -
git ls-remote --heads origin mainnon-empty →"main". -
git ls-remote --heads origin masternon-empty →"master". - If
$GH_BACKEND = "CLI":$GH repo view --json defaultBranchRef --jq .defaultBranchRef.name. - Prompt:
"Couldn't auto-detect the default branch. Enter it (e.g. main, master, trunk):".
Consumer: :pr Step 5c (CodeRabbit trigger), :pr Step 6 CodeRabbit polling fallback (issue-style comments).
| Backend | Invocation |
|---|---|
| MCP | ${GH_MCP_NS}add_issue_comment(owner=$OWNER, repo=$REPO, issue_number=$PR, body=$BODY) |
| CLI | $GH pr comment $PR --body "$BODY" |
GitHub's REST API exposes PR issue-comments (the non-review threaded kind) via /issues/:number/comments — identical to issue comments. So add_issue_comment works for PRs: pass the PR number as issue_number.
Consumer: :pr Step 6 (when $GH / gh api is not available).
:pr Step 6 normally polls via gh api. When gh is absent and $GH_BACKEND = "MCP", substitute these MCP reads at each poll iteration:
gh api call |
MCP equivalent |
|---|---|
gh api repos/$OWNER/$REPO/issues/$PR/comments (walkthrough comment) |
${GH_MCP_NS}pull_request_read(method="get_comments", owner=$OWNER, repo=$REPO, pullNumber=$PR) |
gh api repos/$OWNER/$REPO/pulls/$PR/comments (inline review comments) |
${GH_MCP_NS}pull_request_read(method="get_review_comments", owner=$OWNER, repo=$REPO, pullNumber=$PR) |
gh api repos/$OWNER/$REPO/pulls/$PR/reviews (finalized reviews) |
${GH_MCP_NS}pull_request_read(method="get_reviews", owner=$OWNER, repo=$REPO, pullNumber=$PR) |
Limitations vs. gh api:
-
In-place-edit on re-review — do not gate on comment existence (critical): When a new commit is pushed to an existing PR, CodeRabbit does NOT create a new walkthrough comment. It edits the existing one in-place, rewriting its body and updating the
📥 Commits … between X and <new-HEAD>line to name the new HEAD SHA. Consequence: after the first review, a walkthrough comment always exists — so "does a walkthrough comment exist?" fires immediately on every subsequent poll, before CodeRabbit has even started reviewing the new head. That's a false positive that presents stale findings as current.The correct completion gate — for both first reviews and all subsequent re-reviews — is: a walkthrough comment by
coderabbitai[bot]whose body (a) matches the walkthrough marker (<!-- walkthrough_start -->or## Walkthrough) AND (b) contains$HEAD_SHAAND (c) does NOT match[Cc]urrently processing. This is false until CodeRabbit finishes the in-place edit for the current head, at which point it becomes true. The MCP fallback uses the identical check —get_commentsreturns the comment body, apply the same three-condition test. -
commit_idfiltering (secondary signal only): Thegh apipath additionally filters inline review comments and finalized reviews bycommit_id == $HEAD_SHAto separate current-head findings from prior-review leftovers. The MCPpull_request_readresponse may not expose per-commentcommit_id. This is a secondary signal — skip thecommit_idfilter when on the MCP path, and treat the walkthrough body gate as the sole completion indicator. This means on a re-review with new inline comments, the MCP path may briefly show prior-review inline comments before CodeRabbit posts new ones; acceptable given that the walkthrough gate correctly serializes completion. -
Polling loop: The
gh apipath runs a Bashsleep 60loop (20 iterations, 20 min max). The MCP-fallback path cannot sleep in Bash; the poll must be driven as iterated Claude tool calls with instruction pauses between them. This is slower and more token-intensive but functionally equivalent. -
Preferred backend: When
ghis available alongside$GH_BACKEND = "MCP", always usegh apifor CodeRabbit polling (as:prStep 4a already specifies). MCP polling is graceful degradation, not the canonical path.
Which skill needs which primitives, for quick reference when adding the **GitHub:** block:
| Skill | Primitives used |
|---|---|
:start |
Read issue (Step 2); Add label (Step 3) |
:document |
Read issue (Step 2); Read comments (Step 2); Set issue body (Step 6); Add comment (Step 6); Edit comment (Step 6) |
:archive |
Read issue (Step 2 terminal gate); delegates to :document for Step 4 push |
:merge |
Read issue (Step 2); List open PRs (Step 1); Read PR details (Step 1 + Step 4); Merge PR (Step 4); Add label + Remove label + Close issue (Step 5, dispatched on workflow shape) |
:pr |
Create PR (Step 5b); Add PR comment (Step 5c, Step 6 fallback); Get default branch (Pre-flight); CodeRabbit polling — MCP fallback (Step 6) |
-
Github MCP tool names are not stable yet. The canonical tool list assumed above (
mcp__github__get_issue,mcp__github__add_issue_comment,mcp__github__update_issue, etc.) is based on what's commonly installed. If a particular install exposes different names, the ToolSearch in Step 1 may need adjustment. The plugin-namespaced fallback (mcp__plugin_github_github__*) at least covers the Anthropic-managed install; other MCPs may need additional fallbacks added later. -
Edit-comment MCP availability. Some github MCP installs may not expose an edit-comment tool. If
$GH_BACKEND = "MCP"but the edit-comment tool is missing, fall through to$GH_BACKEND = "CLI"just for that operation (and use$GH api -X PATCH …). Document this as a per-op fallback if it comes up in practice. -
Multi-repo projects. This doc assumes
key = "owner/repo"is a single repo. Future work (cross-repo tickets, monorepos with multiple GH issue trackers) would need a richerkeyshape. Out of scope for this ticket. -
Race on label add-then-remove (4-state
:merge). When:mergeswaps labels in 4-state mode (--remove-label in_progress --add-label in_review), gh CLI does both in one invocation (atomic from the user's perspective). The MCP equivalent may require two separate calls; if the first succeeds and the second fails, the issue ends up label-less which is a confusing intermediate state. Caller should detect partial failure and either retry or surface clearly. -
Remote branch deletion after MCP merge.
merge_pull_requesthas nodelete_branchparameter (unlikegh pr merge --delete-branch). The CLI fallback (gh api -X DELETE …) works whenghis installed alongside MCP. Whenghis fully absent, the remote branch is left for the user to delete manually. This is a known limitation of the current MCP surface; if the upstream MCP adds adeleteBranchparameter, adopt it and update the Merge PR primitive above.
- skills/start/SKILL.md — Step 1 detection, Step 2 fetch (read issue), Step 3 transition (add label).
- skills/document/SKILL.md — Step 1 detection, Step 2 fetch (read issue + read comments), Step 6 push (set body, add/edit comment).
-
skills/archive/SKILL.md — Step 1 detection, Step 2 terminal gate (read issue →
state == "CLOSED"). -
skills/merge/SKILL.md — Step 1 detection, Step 1 PR resolution (list PRs, read PR details), Step 2 compute next state (workflow shape from
.project-conf.toml), Step 4 merge PR + post-merge verification, Step 5 apply (add label / remove label / close issue). - skills/pr/SKILL.md — Step 4a backend detection (PR-level MCP tools), Step 5b create PR, Step 5c add PR comment (CodeRabbit trigger), Step 6 CodeRabbit polling MCP fallback.
-
project-conf-toml.md— definessystem = "github",key = "owner/repo",prefix, and the[status_labels]table that this backend consumes. -
multi-ticket.md— defines the lifecycle skill shape (:start/:document/:archive/:merge) that this backend slots into. -
ticket-gh-init.md— bootstrap skill that writes.project-conf.toml+ creates the labels this backend reads.
- BILL-8 — the ticket that implements this design across the 4 consumer skills (issue-level).
- BILL-60 — the ticket that adds PR-level MCP primitives to
:mergeand:prand makesghoptional. - BILL-2 (closed) — the precedent; surfaced the gap when its archived documentation never landed on the github issue.