| name | pull-request:babysit |
| description | Monitor a PR's CI, fix trivial failures, and self-cancel when green; --merge drives to merged, --reviews hands off to AI-review triage. |
| argument-hint | [pr-url] [--merge] [--reviews] |
| allowed-tools | ["Monitor","TaskStop","Agent","Bash(git:*)","Bash(bun:*)","Bash(bunx:*)","Bash(gh:*)","Skill(pull-request:follow-up)","Skill(gitlab:merge-request)","Skill(git:conflicts)","mcp__github"] |
Babysit PR
Delegate CI monitoring to a provider-specific watcher and react to its events with fix-and-push behavior, stopping when CI turns green.
Context
- Branch: !
git branch --show-current
- Remote URL: !
git remote get-url origin
- Start SHA: !
git rev-parse HEAD
- Session:
${CLAUDE_SESSION_ID}
Workflow
Inspect the remote URL. For a github.com remote, invoke the github:actions-monitor skill. For a gitlab.com remote, invoke the gitlab:ci-monitor skill. Each provider skill owns the watcher process (via the Monitor tool) and emits a structured JSON event stream describing CI state changes.
Babysit consumes that event stream and reacts with the handlers below. The watcher handles polling, deduping by (sha, state), rate limits, timeouts, and session-scoped lifecycle. Babysit handles fixes, pushes, and reporting. Remember the start SHA above so the success handler can summarize work done in the session.
Parse $ARGUMENTS for an optional PR positional and two optional flags, both flags off by default so plain babysit stays CI-only:
$0 (pr-url): the PR to babysit, given as a URL or number. Pass it through to follow-up and the merge commands below. Default: resolve the PR from the branch in Context.
--reviews: after the first green, triage AI-reviewer threads. See Reviews Hand-off.
--merge: don't stop at green; drive the PR to merged. See Merge Mode.
Bounds
Every wait babysit performs runs through a watcher, including the post-submit merge wait (Merge Mode); babysit has no poll loop of its own. The watcher enforces the wall clock (--max-minutes, default 60), poll interval, and dedup, so pass --max-minutes through when the user supplies one. After a watcher emits max-time-reached it has exited: report and stop, never re-arm a fresh watcher. If babysit runs under /loop, the loop owns repetition, not babysit.
Event Handlers
status: running
Do nothing. The watcher emits a new status event when the situation changes.
status: failing
Compare git rev-parse HEAD against the event's sha. If HEAD is newer, a fix was already pushed; ignore this event and wait for the new run.
The monitor skill's flow has already invoked the provider's logs agent (github:logs or gitlab:logs) and produced a summary plus a log-file path. Read the summary to decide triviality.
Check whether the start SHA's CI run had the same failure. If so, it's pre-existing, not a regression from this branch. Report it and call TaskStop.
For trivial failures (lint, type, format, lockfile), attempt a fix. Reproduce the CI step locally to verify (skip reproduction for lockfile-only changes). Commit, push. The watcher picks up the new SHA on its next poll.
For non-trivial failures (logic bugs, design issues, flaky tests, environment-dependent behavior), report the logs agent's summary and log-file path, then call TaskStop.
status: success
Green on a conflicting PR is stale: if a conflicts or unresolved mergeable-unknown event arrived for the current SHA, address it (per conflicts or mergeable-unknown) before treating green as done.
Summarize the session: run git log ${start-sha}..HEAD --oneline for the commits pushed while babysitting.
Then branch on the $ARGUMENTS flags:
--reviews: hand off to AI-review triage before finishing. See Reviews Hand-off.
--merge: don't stop here. Drive the PR to merged. See Merge Mode.
- neither: report the summary and call
TaskStop.
conflicts
Reproduce the conflict locally to identify the conflicting files:
git merge origin/<base> --no-commit --no-ff
git diff --name-only --diff-filter=U
git merge --abort
Lockfiles or generated files (bun.lock, etc.): regenerate per project convention (e.g. rm bun.lock && bun install), commit, push.
Real source conflicts: rebase on origin/<base> and delegate to the git:conflicts skill. Resolve, commit, and push where mechanically clear. Where ambiguous or semantic, report the conflicting hunks and call TaskStop (this runs unattended, so never guess a merge).
In Merge Mode, after any push here, re-arm and count it as a submit attempt.
mergeable-unknown
The platform could not determine mergeability after its own bounded re-polling, so run the authoritative local check with the same dry-run as conflicts:
git fetch origin <base>
git merge origin/<base> --no-commit --no-ff
git diff --name-only --diff-filter=U
git merge --abort
If the dry-run surfaces conflicting paths, route them through the conflicts handler: lockfiles and generated files regenerate, commit, and push; real source conflicts go to git:conflicts, resolving where mechanical and stopping where ambiguous. If the merge is clean, report that the PR is mergeable and keep watching.
queued-timeout
Report the event (include minutes) and wait. The watcher continues polling.
api-error
Report the event (include consecutive). If consecutive errors continue past a second threshold event, call TaskStop.
rate-limited
Report retry_after and wait. The watcher resumes polling once the window elapses.
pr-closed
The PR closed without merging, or its source branch no longer exists. Report and stop. The watcher has already exited.
merged
The PR landed. In Merge Mode this is the success terminal: report the merge and the work done since the start SHA, then stop. The watcher has already exited.
max-time-reached
Report the event (include minutes) and the work done since the start SHA, then stop. The watcher has already exited; do not re-arm (see Bounds).
Reviews Hand-off
With --reviews, after the first green invoke pull-request:follow-up --auto <pr-url> to triage AI-reviewer threads (fix, reply, resolve, loop until the reviewer is satisfied). follow-up calls back into babysit for each post-push CI wait, so let it own the review loop.
When it returns satisfied, re-request the human reviewers whose approval a push (follow-up's fixes or babysit's own) invalidated. Don't re-request bots; follow-up owns the @bot re-trigger.
- GitHub:
gh pr edit <pr-url> --add-reviewer <user>.
- GitLab: delegate to
gitlab:merge-request (Re-request reviewers).
Then branch:
- If
--merge is also set, proceed to Merge Mode.
- Otherwise report what was addressed and call
TaskStop.
This human re-request happens only in the --reviews flow. Review threads stay out of scope: follow-up lists them and leaves them for you.
Merge Mode
With --merge, don't stop at green; drive the PR to merged. CI green is the entry condition; from here, submit to the repo's merge mechanism and recover from kickouts until it lands. GitHub merges run through gh directly; delegate all GitLab merge behavior (trains, endpoint, squash) to gitlab:merge-request.
First confirm the PR can merge on its own. Don't bypass blocks you can't resolve: missing human approval (you can't self-approve; if a bot was the blocker and --reviews ran, it's already handled), branch protection, draft state, or requested changes; report and TaskStop. Read state via gh pr view --json mergeable,mergeStateStatus,reviewDecision,state (GitLab: gitlab:merge-request).
Submit by the most automated path the repo allows (merge queue/train, else auto-merge, else direct, valid since CI is green):
- GitHub:
gh pr merge <pr-url> --auto --squash enables auto-merge or queues the PR. If --auto is rejected, merge directly: gh pr merge <pr-url> --squash. Prefer squash → merge → rebase per gh repo view --json squashMergeAllowed,mergeCommitAllowed,rebaseMergeAllowed.
- GitLab: delegate to the
gitlab:merge-request skill.
Re-arm
A push drops the PR from the merge mechanism (GitLab: off the train; GitHub: clears queued auto-merge) and fires no monitor event. So in Merge Mode, after every push, re-submit by the same path as the initial submit above instead of waiting. Each re-arm counts toward the 3-attempt oscillation guard below.
Then watch the merge through the monitor rather than polling by hand: invoke the provider's monitor skill again on the PR and react to its events. The watcher enforces the interval and the wall clock, so this phase stays bounded like the CI wait (see Bounds) and babysit owns no loop here. React to:
merged: the PR landed. Report success and TaskStop.
conflicts: route through the conflicts handler, then re-arm. Counts as a submit attempt.
status: failing: route through the status: failing handler; the pushed fix produces a new SHA, then re-arm. Counts as a submit attempt.
pr-closed: the PR closed without merging. Report and TaskStop.
max-time-reached: report and TaskStop; do not re-arm.
Stop re-submitting after 3 attempts (re-submits included, an oscillation guard) or an unrecoverable block (missing human approval, non-trivial CI failure, non-lockfile conflict, permissions).
Gotchas
The monitor script delivers structured JSON events. Do not pipe CLI output to python3 -c, bun -e, node -e, or any inline interpreter for parsing.
CI may run on synthetic merge commits whose SHA never matches the branch tip. The watcher reports the source branch SHA in each event; compare against git rev-parse HEAD.
The Bash tool escapes ! to \!. Use | not in jq filters (e.g. select(.x == null | not)), or pass filters via heredoc.
The watcher dedupes by (sha, state). A failing event for a SHA older than git rev-parse HEAD means a fix was already pushed; ignore it.
Babysit is session-scoped. If the session ends, the watcher process ends with it. Re-invoke this skill from a new session to resume.