| name | gh-pr-fixup |
| description | Push changes, monitor CI, and address review comments until the PR is clean. Run after creating or updating a PR, or when CI fails or reviewers leave comments. |
| user-invocable | true |
| allowed-tools | Bash(gh pr:*), Bash(gh api:*), Bash(gh run:*), Bash(git push:*), Bash(git fetch:*), Bash(git rebase:*), Bash(git branch:*), Bash(git add:*), Bash(git commit:*), Bash(go run ./cmd/mdsmith:*) |
| argument-hint | [PR number] |
Push changes, monitor CI, and address review comments
until the PR is clean. Run this workflow after creating
or updating a PR, or when CI fails or reviewers leave
comments.
Prerequisites
- Git configured with push access to the remote
gh CLI authenticated with repo access (step 1
installs it if missing)
- Run each code block as its own Bash call — do not
chain with
&& or prefix with inline variable
assignments (VAR=x cmd). Permission patterns match
on the command prefix, so gh api ... must be the
first token in the command
Steps
1. Ensure gh CLI is installed
gh pr --help
If missing, install from
cli.github.com:
brew install gh
If brew is unavailable, download from the
GitHub releases page.
If not authenticated, run gh auth login or set
GITHUB_TOKEN.
2. Identify the PR
Store the PR number, branch, and repo name for later
steps. Run each command separately:
gh pr view --json number -q '.number'
Note the number as $PR. Then:
git branch --show-current
Note the branch as $BRANCH. Then:
gh pr view --json headRepository \
-q '.headRepository.owner.login + "/" + .headRepository.name'
Note the repo as $REPO.
3. Rebase onto the base branch
gh pr view --json baseRefName -q '.baseRefName'
Note the base branch as $BASE. Then:
git fetch origin "$BASE"
git rebase "origin/$BASE"
If the rebase produces conflicts, resolve them, then
continue:
git rebase --continue
After a successful rebase, verify linting still passes:
go run ./cmd/mdsmith check .
go tool -modfile=tools/go.mod golangci-lint run --fix ./...
The --fix flag auto-corrects formatting issues.
Stage any changes in the next commit.
4. Push changes
After a rebase, a force push is required (subsequent
pushes after CI/review fixes can use a regular push):
git push --force-with-lease origin "$BRANCH"
After pushing, request a Copilot code review. The
bot's API username is
copilot-pull-request-reviewer[bot]:
gh api --method POST \
"repos/$REPO/pulls/$PR/requested_reviewers" \
-f 'reviewers[]=copilot-pull-request-reviewer[bot]'
5. Schedule recurring polling
Use the /loop skill to re-invoke /gh-pr-fixup every
minute. This avoids blocking bash loops and long-running
--watch commands:
/loop 1m /gh-pr-fixup
On each invocation, check CI and review threads. If
everything is green and resolved, cancel the loop
(step 10). If action is needed, handle it inline and
let the next loop iteration verify the result.
Check CI status:
gh pr checks "$PR" --json name,state,bucket
If every check has bucket = pass, proceed to
step 7. If any show bucket = fail, proceed to
step 6. If any show bucket = pending, wait for
the next loop iteration.
6. On CI failure — diagnose and fix
List failed checks, then get the run ID:
gh pr checks "$PR" --json name,state,bucket \
-q '.[] | select(.bucket == "fail")'
gh run list --branch "$BRANCH" \
--status failure --limit 1 \
--json databaseId -q '.[0].databaseId'
Note the run ID as $RUN_ID. Then view the log:
gh run view "$RUN_ID" --log-failed
Read the log, identify the root cause, fix the code,
then run linters before committing:
go tool -modfile=tools/go.mod golangci-lint run --fix ./...
git add -A
git commit -m "fix: address CI failure"
git push origin "$BRANCH"
Request Copilot re-review:
gh api --method POST \
"repos/$REPO/pulls/$PR/requested_reviewers" \
-f 'reviewers[]=copilot-pull-request-reviewer[bot]'
The next /loop iteration will re-check CI.
7. Fetch review threads
Fetch all review threads with their comments, paths,
and resolution status in a single GraphQL call:
gh api graphql -f query='
query($owner: String!, $repo: String!, $pr: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pr) {
reviewThreads(first: 100) {
nodes {
id
isResolved
comments(first: 10) {
nodes {
body
author { login }
path
line
}
}
}
}
}
}
}' -f owner="${REPO%%/*}" -f repo="${REPO##*/}" \
-F pr="$PR"
Returns the first 100 threads (10 comments each).
Paginate with pageInfo if the PR exceeds this.
8. Address each comment
For every unresolved review thread:
- Read the comment body and file path.
- Make the requested change (or explain why not).
- Reply to the thread:
gh api "repos/$REPO/pulls/$PR/comments" \
-f body="Fixed — see latest push." \
-F in_reply_to=COMMENT_ID
- Resolve the thread:
gh api graphql -f query='
mutation($threadId: ID!) {
resolveReviewThread(input: {threadId: $threadId}) {
thread { id isResolved }
}
}' -f threadId="THREAD_NODE_ID"
9. Push fixes and repeat
go tool -modfile=tools/go.mod golangci-lint run --fix ./...
git add -A
git commit -m "fix: address review comments"
git push origin "$BRANCH"
Request Copilot re-review:
gh api --method POST \
"repos/$REPO/pulls/$PR/requested_reviewers" \
-f 'reviewers[]=copilot-pull-request-reviewer[bot]'
The next /loop iteration will re-check CI and
threads automatically.
10. Final verification and loop cancellation
gh pr checks "$PR"
Re-run the step 7 query and filter for unresolved
threads. If the count is 0 and CI is green, cancel
the recurring loop. The PR is ready for merge —
use /merge-queue to enqueue it.
Notes
- Works in both local and Claude Code web
environments. Step 1 installs
gh if missing.
- Run
mdsmith check . and
go tool -modfile=tools/go.mod golangci-lint run --fix ./...
before committing.
- Keep fix commits small — one per CI fix, one per
batch of related review comments.
- Use
--force-with-lease only after rebase
(step 3). Regular pushes after that show
incremental progress.
Use /merge-queue to enqueue the PR once it is
ready.