PR babysitter: monitors CI status, auto-rebases when behind, auto-fixes CI where possible, delegates review comment handling to dlc:pr-check, and re-requests review after fixes. Designed for /loop usage with Remote Control.
From dlcnpx claudepluginhub rube-de/cc-skills --plugin dlcThis skill is limited to using the following tools:
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Sorts ECC skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using repo evidence like file extensions and configs. Creates trimmed install plan for project-specific needs.
Monitor a PR on a loop: check CI, auto-rebase, auto-fix CI failures, and delegate review comment handling to dlc:pr-check. Use with Remote Control to monitor from your phone.
Usage: /loop 10m /dlc:babysit (auto-detect PR) or /loop 10m /dlc:babysit 253
Only print status messages for errors that need human attention and completion (PR ready to merge). Routine actions (rebase, lint fix, CI retry, re-request review) are silent.
Notifications are deduplicated via a state file at .dev/dlc/babysit-<PR_NUMBER>.state. The file contains a single-line status key:
ci_failing:<sorted_check_names> (e.g., ci_failing:build,lint)ci_unfixable:<sorted_check_names>rebase_conflict:<sorted_file_list>needs_reviewneeds_decision:<count> (e.g., needs_decision:2)needs_decision:<count>,unresolved:<count> (e.g., needs_decision:2,unresolved:3)unresolved:<count>readyclosed:<state>Same key across cycles = no output. Write the new key after notifying. Delete the state file when self-cancelling.
Create .dev/dlc/ if it does not exist. Read the state file if it exists.
If $ARGUMENTS contains a number, use it as PR_NUMBER and fetch that PR explicitly:
gh pr view $PR_NUMBER --json number,title,headRefName,baseRefName,state,url,reviewDecision,mergeable
If no number is provided, auto-detect from the current branch:
gh pr view --json number,title,headRefName,baseRefName,state,url,reviewDecision,mergeable
If no PR exists for the current branch, stop silently.
Extract and store: PR_NUMBER, PR_TITLE, PR_BRANCH, BASE_BRANCH, PR_STATE, PR_URL, REVIEW_DECISION, MERGEABLE.
State gate: If PR_STATE is not OPEN:
PR #<number> is <state>. Babysit cancelled.Before any git operations, verify you are on PR_BRANCH:
CURRENT=$(git branch --show-current)
if [ "$CURRENT" != "$PR_BRANCH" ]; then
if [ -n "$(git status --porcelain)" ]; then
echo "ERROR: Not on PR branch ($PR_BRANCH) and worktree is dirty. Stash or commit first."
exit 1
fi
gh pr checkout $PR_NUMBER
fi
If checkout fails, stop. Do not proceed with git operations on the wrong branch.
gh pr checks $PR_NUMBER --json name,state,bucket
Categorize each check by its bucket field:
bucket is pendingbucket is failbucket is passIf any checks are still running: Stop without printing anything. This is the normal waiting state.
If any checks failed: Continue to Step 1b (attempt auto-fix).
If NO checks exist: Continue to Step 2. The repo may not have CI configured.
If ALL checks passed: Continue to Step 2.
Do not just notify and stop when CI fails. Attempt to diagnose and fix the failure first.
Get the HEAD SHA, then fetch ALL non-success runs scoped to that specific commit:
HEAD_SHA=$(git rev-parse HEAD)
gh run list --commit $HEAD_SHA --json databaseId,conclusion --jq '[.[] | select(.conclusion != "success" and .conclusion != "skipped" and .conclusion != "neutral" and .conclusion != "")] | .[].databaseId'
For each failing run, read its logs:
gh run view <RUN_ID> --log-failed 2>&1 | tail -200
Collect and combine log output from all failing runs before classifying.
Analyze the log output and classify the failure:
Lint / format errors (eslint, prettier, ruff, clippy, etc.):
package.json scripts, Makefile, justfile, or CI config)fix: auto-fix lint errors, and pushType errors (tsc, mypy, pyright, etc.):
fix: resolve type errors, and pushTest failures:
fix: resolve test failures, and pushInfrastructure / flaky failures (timeout, network, OOM, rate limit):
gh run rerun <RUN_ID> --failed
Unknown / cannot diagnose:
Notify: 🔴 CI failing on PR #<number>: <title> — Failed: <check_names> — could not auto-fix. <url>/checks
Stop.
Do not notify after a successful fix attempt — this is routine automation. Stop silently and let the next cycle check the result.
Check if the branch is behind the base branch:
git fetch origin $BASE_BRANCH > /dev/null
BEHIND=$(git rev-list --count HEAD..origin/$BASE_BRANCH)
If BEHIND is 0: Branch is up to date. Continue to Step 3.
If BEHIND > 0:
Attempt rebase:
git rebase origin/$BASE_BRANCH
If rebase succeeds cleanly:
git push --force-with-lease
Stop silently — CI needs to re-run. Next cycle will check the results.
If rebase hits conflicts — resolve them:
Do NOT abort immediately. For each conflicting commit in the rebase sequence:
List conflicting files:
git diff --name-only --diff-filter=U
For each conflicting file, read the full file content and examine the conflict markers (<<<<<<<, =======, >>>>>>>). Understand both sides:
=======): the base branch you are rebasing onto=======): the PR commit being replayedResolve the conflict by editing the file to integrate both sides correctly. Preserve the intent of both changes. Remove all conflict markers.
Stage the resolved file:
git add <file>
After all files in the current commit are resolved:
git rebase --continue
Repeat for any subsequent conflicting commits in the rebase.
After all conflicts resolved and rebase completes:
git push --force-with-lease
Stop silently — CI needs to re-run.
If a conflict is genuinely ambiguous (architectural clash, both sides rewrote the same logic differently, or semantic conflict where the correct resolution is unclear):
git rebase --abort
Notify: ⚠️ Rebase conflict on PR #<number> — could not auto-resolve. File(s): <conflicting_files>. <url>
Stop.
Always run dlc:pr-check — never skip this step. Every push triggers new bot reviews (Copilot, CodeRabbit, Gemini), so prior-cycle state is unreliable. Even if all threads were resolved in a previous cycle, new unresolved comments may have appeared since.
Delegate all review comment handling to dlc:pr-check. It handles: fetching comments, categorizing, fixing what it can, replying inline, committing, and pushing.
Unattended mode: The babysitter runs in a loop with no human at the terminal. When executing pr-check, do NOT use AskUserQuestion — auto-defer only genuinely ambiguous human-judgment items (Design Decisions, items with no clear recommended approach). Auto-implementable fixes per pr-check's Step 3.5c criteria should still be implemented, not deferred. The babysitter will surface deferred items to the human as a notification instead of silently posting "Acknowledged" replies.
Skill("dlc:pr-check", "<PR_NUMBER>")
After pr-check completes, parse its output summary to extract these values:
{deferred} value from the Discussion: {n} ({deferred} deferred, {tracked} tracked) line in the summary.If pr-check pushed commits, re-request review from all prior reviewers. Filter out bot accounts (logins ending in [bot]):
REVIEWERS=$(gh pr view $PR_NUMBER --json reviews --jq '[.reviews[].author.login | select(endswith("[bot]") | not)] | unique | join(",")')
if [ -n "$REVIEWERS" ]; then
gh pr edit $PR_NUMBER --add-reviewer "$REVIEWERS"
fi
If pr-check did NOT push commits, skip the re-request — there's nothing new to review.
After pr-check completes, re-check the PR state:
gh pr checks $PR_NUMBER --json name,state,bucket
gh pr view $PR_NUMBER --json reviewDecision,mergeable
If CI is not fully passing (any check running or failed): Stop silently — next cycle will handle it in Step 1.
If pr-check reported Discussion-Deferred items (count > 0) AND remaining unresolved items: Both need human attention — combine into a single notification so nothing is suppressed.
🧑⚖️ PR #<number> has <deferred_count> discussion items needing your input + <unresolved_count> unresolved. <url>needs_decision:<deferred_count>,unresolved:<unresolved_count>.If pr-check reported Discussion-Deferred items (count > 0) AND 0 remaining unresolved: These are discussion items that need human judgment — design decisions, architectural trade-offs, or ambiguous suggestions. The babysitter cannot resolve them.
🧑⚖️ PR #<number> has <deferred_count> discussion items needing your input. <url>needs_decision:<deferred_count>.If pr-check reported 0 remaining unresolved items AND 0 Discussion-Deferred AND reviewDecision is APPROVED (or empty) AND mergeable is MERGEABLE:
✅ PR #<number> ready to merge! — <title> — <url>If pr-check reported remaining unresolved items (and 0 Discussion-Deferred):
💬 PR #<number> has <count> unresolved items after auto-fix. Review needed. <url>If reviewDecision is CHANGES_REQUESTED: Stop silently. Re-review was already requested in Step 3. Next cycle will re-check.
If mergeable is CONFLICTING:
⚠️ PR #<number> has merge conflicts. <url>To self-cancel the babysit loop:
CronList to list all scheduled tasks.dlc:babysit without a number argument.CronDelete with that task's ID..dev/dlc/babysit-<PR_NUMBER>.state