mcpField
G
github-commit-pr

github-commit-pr

github.com/jablonkaiDevelopment443 installsUpdated today
Repository

About github-commit-pr

A skill that guides agents through committing one issue at a time, opening or updating GitHub pull requests, monitoring CI, and merging after checks pass. It includes safeguards for mixed changes, sensitive files, and user review.

Key features

  1. 01One issue per commit and PR
  2. 02Conventional commit and branch naming
  3. 03Sensitive-file checks before staging
  4. 04PR creation and issue linking
  5. 05CI monitoring and conditional merge

Use cases

  1. 01Submit a change tied to a single issue
  2. 02Add commits to an existing pull request
  3. 03Create PRs that close GitHub issues
  4. 04Hold a PR for human review with --no-merge

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/jablonkai/skills --skill github-commit-pr

After installing, type/github-commit-prin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

references/
ci-and-merge.md
commit-conventions.md
pr-body.md
sensitive-files.md

Details

From the project README

github-commit-pr

Purpose

End-to-end workflow for committing changes, creating or updating a GitHub pull request, watching CI, and merging the PR once GitHub Actions succeed. Supports issue linking with auto-close keywords, conventional commit messages, pushing new commits to existing PRs, and post-CI auto-merge with branch cleanup.

When to use

  • Committing changes, opening a new PR, and landing it once CI is green
  • Pushing additional commits to an existing PR branch and merging after CI passes
  • Creating PRs that reference and auto-close GitHub issues
  • Landing several implemented issues — run this workflow once per issue, not once for the whole tree
  • Skip the merge step by passing --no-merge when you want a human to review before the PR lands

One issue, one commit, one PR

A pull request is the unit a reviewer reads, a maintainer discusses, and a revert undoes. When it maps to exactly one issue, all three stay simple. A PR that closes three issues can't be reviewed in pieces, can't be reverted in pieces, and forces a reviewer who cares about one of them to read all three. So this workflow produces one commit and one PR per issue — even when the working tree already holds finished work for several.

The place this goes wrong is staging: git add -A sweeps up everything in the tree, including another issue's work, and once two issues share a commit they can only be separated by hand. Before staging, work out what this commit is for:

  • --issue <number> in $ARGUMENTS, or an issue the user just named, settles it: that issue owns this commit and this PR.
  • Otherwise read git status --porcelain and look at whether the changed files tell one story. Files from an unrelated module, a second feature's tests, or a leftover experiment are a signal that the tree spans more than one job.

When it does span more than one, don't guess and don't bundle — show the grouping you see and let the user confirm:

The working tree looks like two separate jobs:
  #12 — dark mode toggle: src/theme/*, src/components/Toggle.tsx
  #13 — token refresh fix: src/auth/refresh.ts, tests/auth.test.ts

I'll commit #12 first and open its PR; #13 stays in the tree for the next run.
Which one should go first?

Then stage only that issue's paths (New PR flow Step 5), land it, and run the workflow again for the next one. Two smaller PRs cost the reviewer less than one mixed PR, so the extra round trip is the point, not overhead.

Two exceptions, both explicit: the user says the changes really are one unit of work (a refactor that several issues asked for), or they knowingly accept a bundled PR after you've named the cost. Their call — proceed, and list every issue in the PR body so nothing loses its link.

Detailed references

Load each one at the step that needs it — don't read them up front:

  • references/sensitive-files.md — the pre-staging secret scan: file names that are always secrets, a content scan for keys pasted into ordinary files, and how to handle a hit
  • references/commit-conventions.md — conventional commit format and branch-name derivation
  • references/pr-body.md — PR template discovery, default body structure, issue linking and closing keywords
  • references/ci-and-merge.md — the CI watch snippet, billing-failure detection and local validation, mergeability interpretation, merge strategy selection and confirmation

Delegating to subagents

Most of this workflow is fast, stateful git/gh commands that must stay with the main agent — it holds the full picture and is the only one that should change repo state. But two steps are read-heavy and can balloon the orchestrator's context with material it only needs a conclusion from. Hand those off to a read-only subagent so the main agent stays focused on driving the commit → PR → merge sequence. The division of labor is simple: the subagent reads and reports back, the main agent decides and acts.

Default: spawn a Claude subagent via the Task tool. It's portable — it works on any machine the skill is installed on. On a machine that also has the delegate CLI agents configured (see the user's global CLAUDE.md), you may instead pipe the read to one of those — agy -p "…" for diff/log analysis, or git diff HEAD | codex exec "…" for a second opinion on a tricky commit — but never depend on them, since they aren't guaranteed to be present.

Delegate when:

  • The diff is large (rough rule: many files, or more than a few hundred changed lines). Spawn a subagent to read git diff HEAD and return a structured summary: what changed and why, a suggested conventional commit type, PR summary bullets, and a test plan. Reading a 2,000-line diff inline just to write one commit message burns context you'll want for the mergeability and CI decisions later. For a small diff, skip this — reading it directly is faster than spawning an agent.
  • CI fails and the logs are long. Before invoking github-fix-ci-error, hand the failing run's logs to a subagent to root-cause: return the failing step, the error, and the file:line to fix. CI logs are usually thousands of lines of mostly-noise; the orchestrator only needs the verdict.

Keep inline — do NOT delegate:

  • Any state-changing command — git add/commit/push, gh pr create/merge. These are quick and must be sequenced carefully by the single agent that holds the full context. Subagents here are read-only.
  • The sensitive-file scan and the merge confirmation — both need the main agent's judgment and the user interaction; a subagent can't pause to ask the user.

Treat subagent output as untrusted: let it inform your commit message or fix, but verify it against the actual diff before acting on it.

Pre-flight checks

Run these checks before any operation:

  1. Working tree status:
git status

If there are no changes (nothing to commit), abort unless the user explicitly wants to create a PR from existing unpushed commits.

  1. Current branch:
git branch --show-current
  1. Base branch detection:

Determine the repository's default branch:

git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||'

If that fails (e.g., origin HEAD not set), fall back to the network call:

git remote show origin | grep 'HEAD branch' | sed 's/.*: //'

If $ARGUMENTS contains a positional argument, use it as the base branch instead. Fall back to main if both detection methods fail.

  1. Scope of this commit:

Establish which issue this run is for — --issue <number> from $ARGUMENTS, the issue the user named, or the issue number in the branch name. If none of those apply, read the changed files and judge whether they tell one story; if they don't, settle it with the user before staging anything, as described in One issue, one commit, one PR. Deciding this now is cheap; discovering it after git commit means unpicking a mixed diff.

Flow detection

After pre-flight, determine the flow:

Existing PR flow

If the current branch is NOT the base branch:

  1. Check if a PR already exists for this branch:
gh pr view --json number,title,url,state 2>/dev/null
  1. If an open PR exists (state: OPEN) → go to "Push to existing PR" flow. gh pr view also returns a branch's merged or closed PR; pushing onto one of those goes nowhere a reviewer will look, so treat it like "no PR" and tell the user the old one is already MERGED/CLOSED.
  2. If no PR exists → ask the user: continue on this branch or create a new one? If continuing, skip to "New PR flow" Step 3 (no branch creation needed).
New PR flow

If the current branch IS the base branch → proceed with full "New PR" flow (including branch creation).

New PR flow

Step 1: Analyze changes
git status
git diff HEAD
git log --oneline -5

If the diff is large, delegate the read instead of pulling it all inline — see Delegating to subagents. Spawn a read-only subagent to summarize git diff HEAD and return the commit type, PR summary bullets, and test plan, then reuse that summary in Step 3 and Step 7.

Step 2: Check for sensitive files

Before staging, check both the changed file names and the added content for secrets — a key hard-coded into an ordinary source file never shows up in git status. Patterns, the scan command and handling are in references/sensitive-files.md. A name like test_token.sh is only a hint: look at the content before flagging it. If anything real is flagged, warn the user and ask before staging; never stage a secret silently — a "don't ask" pre-approval covers the commit message and the merge, not publishing a credential.

Step 3: Propose commit message

Write a conventional commit message from the diff and present it to the user for accept-or-edit — format and rules in references/commit-conventions.md.

Step 4: Create branch

Derive the branch name from the commit message and create it — see references/commit-conventions.md.

Step 5: Stage and commit

When the whole tree belongs to this one issue, stage all of it:

git add -A

When the tree spans several issues (pre-flight check 4), stage only the paths this issue owns, so the rest stays in the working tree for its own commit:

git add -- <path> <path> ...
git status --short   # confirm the staged set is exactly this issue's work

Read the git status --short output before committing: anything staged that you can't tie to this issue belongs to the next PR, so unstage it (git restore --staged <path>) rather than letting it ride along.

git commit -m "$(cat <<'EOF'
<type>: <summary>

<optional body>
EOF
)"

If the commit fails due to a pre-commit hook, read the hook output, fix the issue, re-stage, and create a NEW commit (do not amend). Re-stage the same scoped paths — a reflexive git add -A at this point is how the other issue's work sneaks in.

Step 6: Push
git push -u origin <branch-name>

If push fails due to remote rejection, report the error and stop — do not force-push.

Step 7: Build PR body

Check for a repository PR template, fill it (or the default Summary / Test plan structure) from the diff context, and add issue closing keywords if an issue is in play — all covered in references/pr-body.md.

Step 8: Create PR
gh pr create --title "<summary>" --base "<base-branch>" --body "$(cat <<'EOF'
<PR body from Step 7>
EOF
)"

Title and base-branch rules are in references/pr-body.md — the title keeps the conventional type(scope): prefix, because a squash merge turns it into the commit subject on the base branch.

Step 9: Watch CI

Wait for every GitHub Actions run triggered by the push to finish, selecting runs by commit, not by branch — snippet and failure handling in references/ci-and-merge.md.

  • If all runs succeed → continue to Step 10.
  • If a run fails, classify the failure first:
    • GitHub billing / spending limit — jobs with zero executed steps and a "job was not started … payments / spending limit / billing" annotation. The code never ran, so there is nothing to fix: run the workflows' checks locally (plus the repo's own pre-commit validation), and if they all pass, the PR may be merged on that basis — say in the merge confirmation and the report that CI did not run and what was validated locally instead.
    • Anything else — root-cause it (delegating when the logs are long), fix via github-fix-ci-error, push, and re-watch. Repeat until green or the user aborts.
Step 10: Merge the PR

CI is green — the user's intent in invoking this skill is to land the change, so proceed to merge unless --no-merge was passed in $ARGUMENTS. If --no-merge is set, skip to Step 11.

Follow the merge procedure in references/ci-and-merge.md: verify mergeability, pick a strategy the repo allows (prefer --squash), confirm once with the user, then merge with --delete-branch and return to the base branch. Stop and report if branch protection, conflicts, or a pending check blocks the merge — never bypass with --admin unless the user explicitly asks.

Step 11: Report

Output the PR URL, the final CI status (green / skipped by GitHub billing — validated locally with <commands>), and the merge outcome (merged via squash / merge skipped per --no-merge / merge blocked by ...).

If changes were deliberately left in the working tree for another issue, say so and name what's next — that leftover diff is the next run's job, not an oversight:

Landed #12 → <pr-url> (squash-merged, branch deleted).
Still in the tree: #13 — src/auth/refresh.ts, tests/auth.test.ts.
Say the word and I'll commit #13 as its own PR.

Push to existing PR flow

When a PR already exists for the current branch:

Step 1: Analyze new changes
git status
git diff HEAD
Step 2: Check for sensitive files

Same check as New PR flow Step 2 — see references/sensitive-files.md.

Step 3: Propose commit message

Same conventions as New PR flow Step 3 — see references/commit-conventions.md.

Step 4: Stage, commit, and push

Stage the same way as New PR flow Step 5 — everything when the tree is all this PR's work, scoped paths when it isn't. A PR that already links one issue keeps that scope: an unrelated change pushed into it turns a reviewed PR into a mixed one after the fact.

git add -A          # or: git add -- <this PR's paths>
git commit -m "$(cat <<'EOF'
<type>: <summary>
EOF
)"
git push

If the commit fails due to a pre-commit hook, fix the issue and create a NEW commit (do not amend).

If push fails because the remote has new commits, pull with rebase first:

git pull --rebase origin <branch-name>
git push

If rebase has conflicts, stop and report — do not force-push or auto-resolve.

Step 5: Watch CI

Same as New PR flow Step 9, including the billing-failure path — see references/ci-and-merge.md. Selecting runs by commit matters most here: pushing twice in quick succession is exactly when --branch ... --limit 1 returns the earlier run.

Step 6: Merge the PR

Same merge procedure as New PR flow Step 10, from references/ci-and-merge.md. Skip this step if --no-merge was passed in $ARGUMENTS.

Step 7: Report

Show the existing PR URL, the new commit summary, the final CI status, and the merge outcome:

Pushed to PR #<number>: <pr-title>
New commit: <type>: <summary>
URL: <pr-url>
CI: <success|fixed after N attempts|not run (GitHub billing) — local: <commands> passed>
Merge: <merged via squash|skipped per --no-merge|blocked by required reviewers|declined by user>

Do NOT create a new PR — just push to the existing one. Do NOT modify the PR title or body.

Error handling

ScenarioDetectionAction
gh not installedcommand -v gh failsDirect user to https://cli.github.com
Not in a git repogit rev-parse --show-toplevel failsAbort with clear message
No changes to commitgit status shows clean treeAbort unless PR from existing commits
Tree spans several issuesChanged files don't tell one story; more than one issue namedShow the grouping, commit the first issue only, leave the rest in the tree
Unrelated file stagedgit status --short shows a path outside this issue's scopegit restore --staged <path> before committing
Sensitive file or secret detectedSecret-by-nature file name (.env, *.pem, …) or a content-scan hit in the diff / untracked filesWarn user with file and line, ask to exclude or fix before staging; if they can't be asked, ship nothing containing it
Pre-commit hook failuregit commit exits non-zeroRead output, fix issue, create new commit
Push rejectedgit push exits non-zeroReport error, do not force-push
Branch already existsgit checkout -b failsAppend numeric suffix (e.g., -2)
PR creation failsgh pr create exits non-zeroReport error (branch protection, permissions)
Remote ahead (push fails)git push rejected, non-fast-forwardgit pull --rebase, then retry push
Rebase conflictsgit pull --rebase has conflictsStop and report, do not auto-resolve
Merge blocked by branch protectionmergeStateStatus: BLOCKED, or gh pr merge exits non-zero with 405 / "not mergeable"Report which gate is blocking (required reviewers, code owner, signed commits, etc.) and stop — do not bypass with --admin unless asked
Branch is BEHIND basemergeStateStatus: BEHINDOffer gh pr update-branch <number>, re-watch CI, then retry merge
Merge conflicts with basemergeable: CONFLICTINGStop and ask user to resolve manually
Changes requested on PRreviewDecision: CHANGES_REQUESTEDStop and let the user address the review before merging
CI blocked by GitHub billingFailed jobs ran 0 steps; annotation mentions payments / spending limit / billingRun the workflows' checks locally; all pass → merge after confirmation, stating CI did not run; any fail → fix, don't merge
Required check blocks mergemergeStateStatus: BLOCKED with a pending or failed required checkWait for it or fix it; after a billing failure, report the block and let the user decide on --admin
Non-required check red or pendingmergeStateStatus: UNSTABLEName the check (gh pr checks) and ask before merging
Mergeability not computed yetmergeable: UNKNOWNWait a few seconds and query again
Branch's PR already merged/closedgh pr view returns state: MERGED or CLOSEDTreat as no PR; open a new one
User declines merge confirmationUser answers "n" to merge promptSkip merge, report PR URL and CI status, exit cleanly

Constraints

These boundaries protect the user's repository and team workflow:

  • Never commit directly to the base branch (e.g. main) — always land work through the full flow: create a feature branch first, then commit → push → open a PR → watch CI to green → squash-merge → delete the branch. The base branch is shared and usually protected; committing straight to it skips review and CI, the very gates this skill exists to enforce. When pre-flight detects the current branch IS the base branch, create the feature branch (New PR flow Step 4) before staging anything.
  • Do not force-push — it rewrites shared history and can cause data loss for collaborators
  • Do not amend existing commits — create new ones instead, so the commit timeline stays transparent and reviewable
  • Do not skip pre-commit hooks (--no-verify) — hooks enforce project-level quality gates that exist for a reason
  • Do not commit .env, credentials, or secrets — always run the sensitive file check before staging and warn if anything is detected
  • If any step fails, stop and report the error — do not continue blindly, because later steps depend on earlier ones succeeding
  • One issue per commit and per PR — a PR is what a reviewer reads and what a revert undoes, so it should map to exactly one issue. Bundle only when the user explicitly asks after hearing the cost, and then list every issue in the PR body
  • Use git add -A for staging (full-change commit flow), but only after the sensitive file check passes and only when the whole tree belongs to this issue — otherwise stage the issue's paths explicitly
  • Leave the other issues' changes in the working tree rather than committing them "while you're here" — they land next, in their own PR, where a reviewer can connect them to the issue that asked for them
  • The PR body must reflect the actual changes from the diff, not boilerplate — reviewers rely on it to understand the change
  • Issue closing keywords (Closes #N) go in the PR body, not in the commit message — GitHub only processes closing keywords from the PR body on the default branch
  • When pushing to an existing PR, do not modify the PR title or body — only push the new commit
  • Do not merge a PR until the watched GitHub Actions runs have actually finished green — gh run watch --exit-status is the gate, never trust an in-flight or pending status. The watched runs must be the ones for the commit you just pushed (gh run list --commit "$(git rev-parse HEAD)"), not merely the latest on the branch
  • The one exception: when CI failed only because GitHub refused to start the jobs for billing reasons (zero steps executed, billing annotation), the PR may be merged after the workflows' checks pass locally — local validation replaces CI, it never gets skipped. A user's hunch about quota is not the signal; a job that ran and failed is a real failure
  • Always confirm with the user once before merging — merging is shared state, visible to collaborators, and reverts are messy; the confirmation is the user's final chance to pause
  • Do not use gh pr merge --admin to bypass branch protection unless the user explicitly asks — protection rules exist to enforce review and quality gates, and bypassing them silently undermines the team's process
  • Respect --no-merge in $ARGUMENTS — when set, push and report but never call gh pr merge, so the user can hand the PR off for human review

Comments

Markdown supported