AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Pr Babysit

skill-a1f-agent-templates-pr-babysit · by a1f

Use when the user wants to babysit a PR through the review/CI cycle, or invokes /pr-babysit. Polls for new review comments and CI failures, fixes them, resolves merge conflicts, and loops until the PR is ready to merge.

No reviews yet
0 installs
42 views
0.0% view→install

Install

$ agentstack add skill-a1f-agent-templates-pr-babysit

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-a1f-agent-templates-pr-babysit)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Pr Babysit? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

PR Babysit

Automate the post-PR feedback loop. Polls for new review comments, fixes them, checks CI, resolves merge conflicts, pushes, and repeats until the PR is clean or max rounds are exhausted.

/pr-babysit [--interval 3m] [--max-rounds 5]

Loop:
  1. Fetch PR comments & reviews + CI status + mergeable status (parallel)
  2. Filter for unresolved/new comments since last check, split by human vs bot
  3. Human comments → trade-off assessment + user decision gate (AskUserQuestion or worksheet)
  4. Fix all actionable comments per gate decisions (bot: auto; human: per decision), run quick gates once
  5. Reply to question comments (bot: auto; human: only approved reply, in parallel)
  6. If CI failing → read logs, fix failures
  7. If conflicts → rebase/merge main and resolve conflicts
  8. If changes were made → commit & push, reset idle counter
  9. If nothing left to fix AND CI green AND no conflicts AND no undecided human comments → [READY TO MERGE]
 10. Otherwise → wait interval, repeat

Prerequisites

The gh CLI must be authenticated with repo scope. The jq command must be available.

Arguments

Parse arguments from the user's message. All are optional:

| Arg | Default | Example | |-----|---------|---------| | --interval=DURATION | 3m | --interval=5m | | --max-rounds=N | 5 | --max-rounds=10 |

Phase 0: Setup

0a. Detect Current PR and Repo Info

Fetch PR data and repo info in parallel:

CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)

# Parallel: PR info + repo info
PR_JSON=$(gh pr view --json number,url,baseRefName,mergeable,mergeStateStatus,createdAt)
REPO_INFO=$(gh repo view --json owner,name)

If no PR exists for the current branch, stop and tell the user to create one first (suggest /make-pr or /pr-make).

Extract:

PR_NUMBER=$(echo "$PR_JSON" | jq -r '.number')
PR_URL=$(echo "$PR_JSON" | jq -r '.url')
BASE_BRANCH=$(echo "$PR_JSON" | jq -r '.baseRefName')
PR_CREATED_AT=$(echo "$PR_JSON" | jq -r '.createdAt')
OWNER=$(echo "$REPO_INFO" | jq -r '.owner.login')
REPO=$(echo "$REPO_INFO" | jq -r '.name')

After setup, always print the PR link so the user can click it:

Babysitting PR: 

0b. Fast-Exit Check

Before entering the loop, check if the PR is already ready:

CHECKS=$(gh pr checks "$PR_NUMBER" --json name,state,conclusion 2>/dev/null || echo "[]")
MERGEABLE=$(echo "$PR_JSON" | jq -r '.mergeable')
MERGE_STATE=$(echo "$PR_JSON" | jq -r '.mergeStateStatus')

If ALL of the following are true:

  • MERGEABLE is "MERGEABLE"
  • MERGE_STATE is "CLEAN"
  • At least one check exists (CHECKS is not empty) — OR the PR was created more than 5 minutes ago (checks genuinely absent, not just slow to register)
  • Every check is in a terminal state (SUCCESS, NEUTRAL, or FAILURE) — no PENDING/QUEUED/IN_PROGRESS
  • All required checks pass
  • No review comments exist on the PR

Then print and exit immediately:

[ALREADY READY TO MERGE] 

If CHECKS is empty and the PR was created less than 5 minutes ago, do NOT fast-exit — wait for checks to be reported. Empty checks shortly after creation means GitHub hasn't registered them yet, not that CI is absent.

Otherwise, proceed to the loop.

0c. Initialize State

IDLE_ROUNDS_REMAINING = max-rounds (default 5)
MAX_TOTAL_ITERATIONS = max-rounds * 3    # hard cap to prevent infinite loops
TOTAL_ITERATIONS = 0
INTERVAL = interval (default 3m)
LAST_CHECKED = PR_CREATED_AT             # first pass processes all existing comments
TOTAL_WAIT_MINUTES = 0
CONSECUTIVE_READY_COUNT = 0              # must reach 2 before declaring ready
PUSHED_AT = None                         # set to current time after each push
EXTERNAL_CHECK_PATIENCE = 10             # separate counter for external check waits
DECISIONS = load_decisions(PR_NUMBER)    # comment_id → {decision, drafted_reply?, custom?} — persisted across iterations and skill runs

DECISIONS persists at .claude/pr-babysit//decisions.json. Load on startup (create empty if missing), write after every human gate decision. This survives Ctrl-C, re-invocations, and worksheet round-trips so the user is never asked twice about the same comment.

Three counters prevent infinite loops:

  • IDLE_ROUNDS_REMAINING resets when progress is made (commit & push). Exhaustion means the PR stalled.
  • MAX_TOTAL_ITERATIONS never resets. Exhaustion means the skill has been running too long regardless of progress.
  • EXTERNAL_CHECK_PATIENCE counts down only when external checks are the sole blocker. Does NOT consume IDLE_ROUNDS_REMAINING — the PR isn't stalled, it's waiting for an external system.

Phase 1: The Loop

Each iteration runs these phases in order. Evaluate exit conditions at the end.

At the start of each iteration, print a status line:

Round  — 

1a. Check PR State

Before doing any work, verify the PR is still open:

PR_STATE=$(gh pr view "$PR_NUMBER" --json state -q '.state')

If PR_STATE is MERGED or CLOSED, print the PR URL and exit — no further action needed.

1b. Run Local Gates (first iteration only)

On the first iteration only (TOTAL_ITERATIONS == 0), run all gates from .claude/gates.json locally before checking remote CI. This catches failures immediately instead of waiting for remote CI to report them.

if [ -f .claude/gates.json ] && [ "$TOTAL_ITERATIONS" -eq 0 ]; then
  SETUP=$(jq -r '.setup // empty' .claude/gates.json)
  [ -n "$SETUP" ] && eval "$SETUP"
  for gate in $(jq -c '.gates[]' .claude/gates.json); do
    NAME=$(echo "$gate" | jq -r '.name')
    RUN=$(echo "$gate" | jq -r '.run')
    FIX=$(echo "$gate" | jq -r '.fix // empty')
    [ -n "$FIX" ] && eval "$FIX"
    if ! eval "$RUN"; then
      # Gate failed — fix the errors before proceeding
    fi
  done
fi

If any gate fails, fix the errors, commit, and push before continuing to 1c. This avoids a wasted wait cycle.

Skip on subsequent iterations — the existing gate run in step 1f (after comment fixes) handles those.

1c. Fetch New Data (Parallel)

Kick off all data fetches in parallel at the start of each iteration:

# All four in parallel:

# 1. Review comments (inline code comments on specific lines)
COMMENTS=$(gh api "repos/$OWNER/$REPO/pulls/$PR_NUMBER/comments" \
  --paginate \
  -q '[.[] | select(.created_at > "'"$LAST_CHECKED"'") | {id, path, line, body, user: .user.login}]')

# 2. Review bodies (top-level review comments submitted with a review)
REVIEWS=$(gh api "repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews" \
  --paginate \
  -q '[.[] | select(.submitted_at > "'"$LAST_CHECKED"'" and .body != "") | {id, body, user: .user.login, state}]')

# 3. Issue-level comments (top-level PR comments from bots and reviewers)
# IMPORTANT: GitHub bot comments (e.g., GitHub Actions, Cursor Bugbot) are posted
# to the issues endpoint, NOT the pulls/comments endpoint. Missing this means
# missing bot feedback entirely.
ISSUE_COMMENTS=$(gh api "repos/$OWNER/$REPO/issues/$PR_NUMBER/comments" \
  --paginate \
  -q '[.[] | select(.created_at > "'"$LAST_CHECKED"'") | {id, body, user: .user.login}]')

# 4. CI status + mergeable status
CHECKS=$(gh pr checks "$PR_NUMBER" --json name,state,conclusion,detailsUrl)
MERGEABLE=$(gh pr view "$PR_NUMBER" --json mergeable,mergeStateStatus)

IMPORTANT: You MUST fetch from all three comment endpoints. pulls/N/comments has inline review comments, pulls/N/reviews has review bodies, and issues/N/comments has top-level PR comments including bot feedback. Missing any endpoint means missing feedback.

Update LAST_CHECKED to current UTC timestamp after fetching.

1d. Categorize Comments

For each new comment (from inline comments, review bodies, AND issue-level comments), tag both the author type and the category.

Author type:

  • Botuser.type == "Bot" OR login ends in [bot] OR login is in the known-bot allowlist: github-actions, cursor-bugbot, codecov, coderabbitai, renovate-bot, sonarcloud. Belt-and-suspenders: check all three, not just one.
  • Human — everything else.

Category:

  1. Actionable code change — requests a specific code modification (e.g., "rename this variable", "add error handling here", "this should use X instead of Y")
  2. Question — asks something that needs a reply (e.g., "why did you choose this approach?", "is this tested?")
  3. Informational / FYI — no action needed (e.g., coverage reports, bot status messages, "LGTM", acknowledgements)
  4. Stale bot comment — a bot reviewed an old commit and the referenced lines have changed since

Skip informational comments entirely. Bot comments flow automatically through 1f/1g. Human comments MUST go through the 1e gate first — never auto-fix or auto-reply to a human.

Auto-dismiss stale bot comments: For each bot comment, check if the referenced lines changed since the comment was posted:

# Get the commit SHA the comment refers to
COMMENT_COMMIT=$(echo "$COMMENT" | jq -r '.commit_id // empty')
# Check if the file at that line changed since that commit
git diff "$COMMENT_COMMIT"..HEAD -- "$FILE_PATH" | grep -q "^@@.*$LINE_NUMBER"

If the lines changed, auto-reply: "This was addressed in a subsequent commit." and skip the comment. Only process bot comments on unchanged code as actionable.

1e. Human Comment Gate

Human comments (actionable + question) require an explicit user decision before any code change or reply is posted. Bot comments do not enter this gate.

1. Filter already-decided comments. For each new human comment, look up DECISIONS[comment_id]:

  • Decision apply, push-back, dismiss — skip (terminal, already handled or explicitly waived).
  • Decision defer or no entry — include in the gate batch.
  • If the comment body changed since the decision was recorded — include, re-present.

2. Build a trade-off assessment per batched comment. For each, READ the referenced file AND run git blame on the referenced line BEFORE drafting the assessment. Skipping this step makes the gate empty ceremony. Each assessment contains:

  • What's asked — one-sentence paraphrase of the reviewer's request
  • Current code — the referenced lines as they are now (actual snippet, not a description)
  • Proposed change — what applying the comment would do, concretely
  • Pros / Cons — factual tradeoffs
  • Blast radius — files and call-sites affected
  • Effort — rough time estimate
  • Drafted push-back — a respectful reply the user can post if they disagree, grounded in the actual code

3. Present assessments and collect decisions.

Fast path (≤ 5 human comments): emit a single AskUserQuestion tool call with one question per comment (questions in parallel). Each question's header is @:, question text is a terse 1-2 line summary of the ask, and the full assessment goes in the option descriptions where relevant. Offer these four options per comment:

  • Apply — accept the feedback. For actionable comments: 1f applies the proposed change and posts Fixed — . For questions: 1g posts the drafted answer.
  • Push back — reject the feedback. For actionable: no code change; 1g posts the drafted push-back reply. For questions: 1g posts a counter-argument drafted during the gate.
  • Defer — no action this iteration. The comment will be re-presented next round (blocks readiness).
  • Dismiss — no action, no reply. Treated as resolved locally (does NOT block readiness). Use when the user deems the comment not applicable.

Slow path (> 5 human comments): write all assessments to .claude/pr-babysit//decisions.md with a Decision: field per comment (default defer). Print:

 human comments need decisions — edit .claude/pr-babysit//decisions.md and re-invoke /pr-babysit to continue.

Then exit the skill (do NOT sleep and loop — the user may take hours to review). On the next invocation, Phase 0 detects the worksheet, parses Decision: lines into DECISIONS, archives the file to decisions.md.done, and resumes normal flow.

Worksheet format per comment:

## . @ — :
> 

**Current code:**

**Proposed change:** 
**Pros / Cons:** 
**Blast radius:** 
**Effort:** 
**Drafted push-back:** 

**Decision:** defer    

4. Persist DECISIONS to disk. After every decision (AskUserQuestion response or worksheet parse), write DECISIONS to .claude/pr-babysit//decisions.json. Schema: {: {decision, drafted_reply?, custom?, comment_body_sha256, decided_at}}. The comment_body_sha256 lets step 1 above detect when the reviewer edited the comment.

5. Gate-level readiness rule. If any human comment lacks a terminal decision (apply / push-back / dismiss) after the gate runs, the PR is NOT ready this iteration — even if all other conditions are green.

1f. Fix All Actionable Comments

Process actionable comments, then run gates once.

1. Bot actionable comments (auto-flow, no gate):

  • Read the referenced file at the referenced line
  • Understand the reviewer's request in context
  • Apply the fix
  • Reply on the thread:

``bash gh api "repos/$OWNER/$REPO/pulls/$PR_NUMBER/comments/$COMMENT_ID/replies" \ -f body="Fixed — ." ``

2. Human actionable comments — consult DECISIONS[comment_id].decision:

  • apply — apply the change described in the assessment's "Proposed change", then post Fixed — reply (same API call as above).
  • push-back — do NOT change code. The drafted push-back reply is posted in 1g, not here.
  • defer — skip entirely this iteration. Do not change code, do not reply. Comment will be re-presented next round.
  • dismiss — skip entirely. Do not change code, do not reply. Decision persists so this won't re-appear.

3. After all fixes are applied, run quick gates once: ``bash # Run format + lint gates from gates.json if it exists if [ -f .claude/gates.json ]; then SETUP=$(jq -r '.setup // empty' .claude/gates.json) [ -n "$SETUP" ] && eval "$SETUP" # Run all gates that have a fix command (typically format and lint) for fix_cmd in $(jq -r '.gates[] | select(.fix != null) | .fix' .claude/gates.json); do eval "$fix_cmd" done fi ``

1g. Reply to Questions

Fire replies in parallel.

Bot questions (rare): auto-reply with a concise, code-grounded answer.

Human questions AND human push-backs from 1f: post ONLY the reply approved in the gate. Look up DECISIONS[comment_id].decision:

  • apply / push-back — post the drafted reply stored in DECISIONS[comment_id].drafted_reply. For apply the drafted reply is Fixed — (posted in 1f if the comment was actionable; posted here if it was a question). For push-back it's the rebuttal drafted during the gate.
  • defer / dismiss — no reply this iteration.

Never write a human reply that wasn't approved in 1e.

For inline review comments, reply in the review thread:

gh api "repos/$OWNER/$REPO/pulls/$PR_NUMBER/comments/$COMMENT_ID/replies" \
  -f body="$REPLY_TEXT"

For top-level review bodies, reply as a PR comment:

gh pr comment "$PR_NUMBER" --body "$REPLY_TEXT"

Base replies on actual code and commit history — don't make things up.

1h. Fix CI Failures

From the checks data fetched in 1c, categorize each check:

  • Passing (conclusion: "SUCCESS" or conclusion: "NEUTRAL") — no action, terminal state
  • Failing (conclusion: "FAILURE") — needs fixing, terminal state
  • Still running (state: "PENDING", state: "QUEUED", or state: "IN_PROGRESS") — not terminal, must wait. This includes external tools like Cursor Bugbot, Noa Analysis, etc. — treat them the same as any other check

If any checks are failing:

  1. Extract the run ID from detailsUrl
  2. Fetch failure logs:

``bash gh run view "$RUN_ID" --log-failed 2>/dev/null | tail -100 ``

  1. Analyze the failure and apply fixes
  2. If the failure is a flaky t

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.