Babysit a PR through GitHub Copilot review until merge. Use when the user asks to "babysit PR
How this skill is triggered — by the user, by Claude, or both
Slash command
/mauriciovieira-skills:babysit-with-github-copilotThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Drive a pull request from "just opened" (or "ready for review") to merged + clean
Drive a pull request from "just opened" (or "ready for review") to merged + clean worktree, in tight 5-minute cycles around GitHub Copilot review feedback.
Invocation: always /babysit-with-github-copilot (hyphens, not colons). The
on-disk skill name is babysit-with-github-copilot. Use this exact form in any
ScheduleWakeup prompt that re-enters this skill — a colon form is NOT a valid
command and will fail with "Unknown command".
These are non-negotiable. If any would be violated, stop the turn and schedule the next cycle instead of merging.
git push. Push ends the turn.
Schedule the next cycle (ScheduleWakeup, 300 s) and exit.created_at is after
last_push_at and are not addressed with a code push or a documented
“won’t fix” reply.in_progress or queued.
An active Action is a stronger signal than silence — the review is in
flight, not absent. Merging cancels it mid-scan (the --delete-branch
kills the run), and you ship without seeing comments that were about to
land. Check via gh run list -R <owner>/<repo> --branch <head-branch> --workflow "Running Copilot Code Review" --limit 3 (the workflow is named "Running Copilot Code Review", not "Copilot"; if unsure, gh run list -R <owner>/<repo> --branch <head-branch> --limit 6 and match the run whose name contains "Copilot")
before every merge. Wait for status: completed (any conclusion is fine
— Copilot may produce comments AND a completed run, but a still-running
run means more comments may still appear).last_push_at. This is the master gate. After a re-request, Copilot
always submits a review summary — even "I reviewed your changes and found
no new comments" is an explicit review event with its own submitted_at
(the REST field the gate uses; GraphQL calls the same value submittedAt).
That event, newer than your last push, is what authorises merge. Until it
arrives, the absence of comments is pending review, not clean
review — keep waiting (ScheduleWakeup), never merge on silence. A
completed Copilot Action is necessary but not sufficient: the Action
can finish seconds before the review summary posts (this exact race has
shipped PRs with unaddressed comments). Gate on the review event, not the
Action — and resolve that review's comments through its own endpoint,
not the global comments list (see Hard Stop #8 / Step 3)./repos/<owner>/<repo>/pulls/<N>/comments list to decide a merge. A review
event appears on /repos/<owner>/<repo>/pulls/<N>/reviews a few
seconds before its inline comments propagate to the global comments
list — so the global list can read "0 new comments" while the review you
just detected actually carries several (this race merged a PR with 4
unaddressed comments). Get the post-push review's numeric id, then read
gh api repos/<owner>/<repo>/pulls/<N>/reviews/<id>/comments — the
per-review endpoint is consistent with the review object, so its count is
authoritative the instant the review is visible. Merge only when that
endpoint returns zero comments for the post-push review. (Step 3 has the
full command; the bot's login differs per endpoint — match with
startswith("copilot-pull-request-reviewer") on REST.)If you pushed fixes this turn: commit → push → re-request review (Step 2) →
brief status to user → ScheduleWakeup → stop. Do not call
gh pr merge until a later wake-up completes Step 3.
origin/main (or repo's default).gh is authenticated for the repo.docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md if the
repo follows that convention. Don't fabricate one — if absent
and the change is non-trivial, flag it before opening the PR.If any precondition fails, stop and report. Don't paper over.
Persist across wake-ups (in the wakeup prompt or session notes):
| Field | Meaning |
|---|---|
<N> | PR number |
last_review_at | ISO timestamp of the latest Copilot review submitted_at |
last_push_at | ISO timestamp of the most recent fix push (empty if none yet) |
awaiting_rereview | true after a fix push until Step 3 completes once post-push |
Reset awaiting_rereview to false only after a full Step 3 wake-up that
finds (a) a Copilot review event with submitted_at > last_push_at and (b) no
unaddressed Copilot comments newer than last_push_at. The merge gate is
last_review_at > last_push_at — a fix push always makes last_push_at newer
than last_review_at, so you cannot merge again until Copilot re-reviews.
make lint && make test). Don't push if lint or unit tests fail —
fix first.git fetch origin --prune && git rebase origin/maingit push -u origin <branch>..github/WORKFLOW_TEMPLATES/pull_request.md (or PULL_REQUEST_TEMPLATE.md).
Fill it in fully — no placeholder bullets, no "TBD". Sections expected:
Summary, Context, What changed, Test plan, Risk & rollback, Checklist.gh pr create --title "<conventional title>" --body-file <path>.
Pass the body via --body-file, never inline --body "$(cat ...)".<N>.gh pr edit <N> -R <owner>/<repo> --add-reviewer copilot-pull-request-reviewer
Copilot may auto-review on PR creation in some repos; the explicit add is a no-op if so, and a trigger if not. Don't request other human reviewers unless the user told you to.
You must sleep 5 minutes before deciding to merge. Use ScheduleWakeup with
delaySeconds: 300 and a prompt that re-enters this skill. Do not poll in a
tight loop; do not skip because “CI is empty” or “comments look addressed”.
On wake-up, do all of the following before deciding next move:
gh pr checks <N> -R <owner>/<repo> # CI status
gh pr view <N> -R <owner>/<repo> --json reviews \
--jq '.reviews | map({author: .author.login, state: .state, submitted: .submittedAt})'
# Copilot review Action — separate from CI checks. An in-flight Copilot run
# blocks merge (see Hard Stop #6). Note: `gh pr checks` can lag and report
# `pending` for a job that's already completed — verify any "pending" via
# `gh run view <runId> -R <owner>/<repo> --json status,conclusion` before treating it as live.
gh run list -R <owner>/<repo> --branch <head-branch> --workflow "Running Copilot Code Review" --limit 3 \
--json status,conclusion,createdAt
# Workflow name is "Running Copilot Code Review" (NOT "Copilot"). If --workflow
# returns nothing, run without it and match the run whose name contains Copilot:
# gh run list -R <owner>/<repo> --branch <head-branch> --limit 6 --json status,conclusion,name,createdAt
Compute the merge gate authoritatively. Do NOT filter the global
/repos/<owner>/<repo>/pulls/<N>/comments list by timestamp: a review event
becomes visible on /repos/<owner>/<repo>/pulls/<N>/reviews a few seconds
before its own inline comments propagate
to the global comments list — that race once merged a PR with 4 unaddressed
comments. Instead, find the post-push review's id, then read that review's
own comments (the per-review endpoint is consistent with the review object,
so there is no lag):
# 1) Newest post-push Copilot review. REST /reviews reports the bot as
# "copilot-pull-request-reviewer[bot]" with field submitted_at; startswith()
# also matches a non-[bot] form. `--paginate` emits ONE array per page, so
# `jq -s` (slurp) + `add` concatenates all pages into a single array before
# filtering — a bare `jq 'map(...)'` would run per-page and pick the wrong
# (or multiple) ids. sort_by parsed epoch (fromdateiso8601), not the raw
# string, so "newest" stays correct regardless of timestamp formatting.
review=$(gh api repos/<owner>/<repo>/pulls/<N>/reviews --paginate \
| jq -s --arg lp "<last_push_at>" \
'add
| map(select((.user.login|startswith("copilot-pull-request-reviewer"))
and (.submitted_at|fromdateiso8601? // 0) > ($lp|fromdateiso8601? // 0)))
| sort_by(.submitted_at | fromdateiso8601? // 0) | last')
review_id=$(printf '%s' "$review" | jq -r '.id? // empty')
# Persist into loop state ONLY when a post-push review exists — otherwise keep
# the previously persisted value (overwriting with "" would drift the state and
# could break the documented last_review_at > last_push_at gate):
[ -n "$review_id" ] && last_review_at=$(printf '%s' "$review" | jq -r '.submitted_at? // empty')
# 2) Read THAT review's own comments (authoritative; no propagation lag).
# Always emit an explicit {count, comments} object — including the
# no-review case — so the gate output is unambiguous in transcripts/logs:
if [ -n "$review_id" ]; then
gh api repos/<owner>/<repo>/pulls/<N>/reviews/$review_id/comments \
--jq '{count: length, comments: [.[] | {path, line, body, user: .user.login}]}'
else
printf '%s\n' '{"count":0,"comments":[],"note":"no post-push review yet — gate closed"}'
fi
Interpret:
review_id empty → Copilot has NOT re-reviewed since the push. Gate
closed; you may NOT merge no matter how quiet the PR looks (Step 4 #6).review_id present, zero comments → genuinely clean re-review →
merge-eligible.review_id present, ≥ 1 comment → those are unaddressed until fixed
and pushed (or replied “won’t fix” with reason); go to Step 4 #3.The bot's login differs by endpoint: REST
/reviews→copilot-pull-request-reviewer[bot], REST/comments→Copilot,gh pr view <N> -R <owner>/<repo> --json reviews(GraphQL) →copilot-pull-request-reviewer. Match accordingly. Always compare timestamps numerically withfromdateiso8601? // 0, never as strings.
Decision tree, in order. If the answer sends you to Step 3, do not merge this turn.
last_push_at, awaiting_rereview=true. Re-request review (Step 2).
Stop turn → Step 3. Do NOT merge with red CI.last_push_at, awaiting_rereview=true.
For every addressed comment, complete Step 4a (reply + resolve + echo)
before re-requesting review. Re-request Copilot via Step 2.
Stop turn → Step 3. Do NOT merge.awaiting_rereview is true → you pushed since the last completed
cycle. Even if comments look answered, waited time has not elapsed.
Apply Step 5 on this wake-up only; do not merge on the push turn itself.in_progress or queued → wait one more
cycle. ScheduleWakeup 270s. Do NOT merge — see Hard Stop #6.last_push_at yet → Copilot has
not finished re-reviewing, even if its Action shows completed (the
summary posts seconds after the Action ends). Wait one more cycle
(ScheduleWakeup 270s). Do NOT merge — see Hard Stop #7. If no review event
arrives after ~3 consecutive cycles (~15 min), surface to the user and let
them decide; never auto-merge on silence.review_id non-empty) AND that review's own comments endpoint
(/repos/<owner>/<repo>/pulls/<N>/reviews/<review_id>/comments) returns
zero AND no Copilot Action in flight → merge. Skip to Step 6. (Never
substitute the global comments list here — Hard Stop #8.)Note: branch #7 is the only path to merge. Branches #1, #3, #5, #6, and the push turn of #4 all require another Step 3 cycle first.
After pushing the fix commit(s), for each comment you addressed (whether applied or "won't fix"):
"Done in <sha> — <one-line summary> (file:line).""Skipping — <reason>. (Tracked: <link/issue> if applicable.)"path:line and the
reviewer login. The user must be able to read the verbatim reply text in
the agent transcript without opening GitHub.gh api -X POST \
repos/<owner>/<repo>/pulls/<N>/comments/<comment_id>/replies \
-f body="<reply text>"
databaseId:
gh api graphql -F owner=<owner> -F repo=<repo> -F number=<N> -f query='
query($owner: String!, $repo: String!, $number: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $number) {
reviewThreads(first: 100) {
nodes {
id
isResolved
comments(first: 1) { nodes { databaseId } }
}
}
}
}
}'
Then resolve:
gh api graphql -f threadId="<thread_id>" -f query='
mutation($threadId: ID!) {
resolveReviewThread(input: {threadId: $threadId}) {
thread { id isResolved }
}
}'
isResolved: true in the mutation response.A comment is not "addressed" until reply + resolve + terminal echo all complete. Re-requesting review with unresolved threads is a Step 4 #3 violation.
Applies only when awaiting_rereview is true and Step 3 has completed
once after that push (i.e., you are on a wake-up, not the push turn).
The gate is a Copilot review event, not silence and not a finished Action.
After every fix push you re-request review (Step 2); Copilot responds by
submitting a review summary — and it does so even when it has nothing to
add ("I reviewed your changes and found no new comments"). That summary is
an explicit review with its own submitted_at. Wait for it.
Three mandatory pre-checks before merge:
completed — the Copilot review Action on the head branch must
not be in_progress/queued (Hard Stop #6 / Step 4 #5).submitted_at > last_push_at, i.e. review_id from Step 3 is non-empty
(Hard Stop #7 / Step 4 #6). A completed Action alone does not clear
this; the summary commonly posts a few seconds after the Action ends./repos/<owner>/<repo>/pulls/<N>/reviews/<review_id>/comments, never the
global comments list (Hard Stop #8). The review event can be visible before its inline comments
propagate to the global list, so a global "0 new" is not trustworthy.Then branch on what the per-review endpoint returned:
review_id present, its /comments returns zero → genuinely clean
re-review. Merge (CI green or absent). Go to Step 6.review_id present, its /comments returns ≥ 1 → go to Step 4 #3 (fix
round). Do not merge.review_id empty → no post-push review yet; keep waiting (Step 4 #6). Do
not merge on silence. After ~3 empty cycles (~15 min), surface to the user;
never auto-merge.This gate never overrides the hard stops: it does not permit merging in the same turn as push/re-request, nor skipping Step 3, nor merging with a Copilot Action still in flight.
Use the project's preferred merge style. Default is squash:
gh pr merge <N> -R <owner>/<repo> --squash --delete-branch
--delete-branch removes the remote branch. Confirm with
gh pr view <N> -R <owner>/<repo> that state is MERGED.
git fetch origin --prune
# Drop local branches whose upstream is gone:
git branch -vv | awk '/: gone]/{print ($1 == "*" ? $2 : $1)}' \
| while read -r b; do
git branch -d "$b" || echo "skip $b (not fully merged locally — inspect)"
done
If the work was in a worktree under .worktrees/<name>/:
git worktree remove .worktrees/<name>
Worktree removal will fail if there are uncommitted changes — investigate before forcing.
fix(frontend): apply Copilot round-N feedback on … is a fine pattern.--no-verify to dodge a hook. If a pre-commit hook fails, fix the
underlying issue.--amend a published commit. New commits only.git push
fast-forwards if your local history is a clean superset; otherwise stop.page.route, fixture monkeypatch).-R <owner>/<repo> on every gh pr ... / gh run ... call when the local clone has more than one remote (e.g. an
upstream fork remote). Without -R, gh may resolve pr view <N> /
pr checks <N> against the wrong remote and silently report another
repo's PR (stale reviews, wrong head branch) — which can fake-pass or
fake-fail the merge gate. The gh api repos/<owner>/<repo>/... form is
already explicit and safe.prompt to re-enter this skill verbatim, so the next firing
picks up where this one left off. Include <N>, last_push_at, and
awaiting_rereview in the prompt.completed Action is not the review; Copilot posts
its "no new comments" (or its new comments) a few seconds later. Merging in
that gap ships unaddressed comments. Gate on the review event
(submitted_at > last_push_at), never on a quiet PR (Hard Stop #7).chore: review-feedback commit that contains unrelated
refactors. Keep review-fix commits tightly scoped.in_progress or queued —
gh pr merge --delete-branch cancels the run mid-scan and the comments
it was about to post are lost. Treat an active Action as a stronger
signal than comment silence; wait for the run to reach completed
before invoking Step 5. See Hard Stop #6.| Phase this turn | Allowed actions | Forbidden |
|---|---|---|
| Fix round | edit, commit, push, re-request, reply to comments, status, ScheduleWakeup | gh pr merge |
| Wait wake-up | fetch checks/comments, decide Step 4, maybe merge (Step 5/6) | push unless Step 4 #1 or #3 triggered |
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub mauriciovieira/skills