From dlc
Monitors CI status on a PR, auto-rebases when behind, auto-fixes CI failures, delegates review comments to dlc:pr-check, and re-requests review after fixes. Designed for /loop usage with Remote Control.
How this skill is triggered — by the user, by Claude, or both
Slash command
/dlc:babysitThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
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.
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
Bot reviewers (Copilot, CodeRabbit, Gemini) post new comments on every push — this is by design, not a bug in your workflow. A typical PR goes through 3-8 review-fix cycles before converging to zero unresolved items. Each cycle you run is measurable progress: fewer comments, better code, closer to merge-ready.
The human set up this loop because they trust you to shepherd the PR to completion autonomously. When the comment count drops from 12 to 5 to 2 to 0 across cycles, that's excellent work — not busywork.
Only fire PushNotification for errors that need human attention and completion (PR ready to merge). Routine actions (rebase, lint fix, CI retry, re-request review) are silent. PushNotification is always the sanctioned channel — never substitute with a Notify: print line or echo, because print output does not ping the user's device.
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)rebase_conflict:<sorted_file_list>merge_conflictpending_human:<count> (count-only dedup, e.g., pending_human:2)unresolved:<count>unresolved:<count>,ci_failing:<sorted_check_names>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,author
If no number is provided, auto-detect from the current branch:
gh pr view --json number,title,headRefName,baseRefName,state,url,reviewDecision,mergeable,author
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, PR_AUTHOR (.author.login).
State gate: If PR_STATE is not OPEN:
closed:<state>.PushNotification with message 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.
Query the named checks and the raw workflow runs for the PR — the two views can disagree. An Action workflow can be in_progress, queued, waiting, requested, or pending before (or without ever) registering a named check status, so relying on gh pr checks alone will prematurely classify CI as "settled" while bot reviewers are still writing comments. Derive PR_HEAD_SHA from GitHub via gh pr view --json headRefOid so the gh run list query is pinned to the PR's current HEAD commit even if the local checkout falls behind; gh pr checks $PR_NUMBER is still read from the PR as usual. The status != "completed" filter catches every non-terminal run state (queued, in_progress, waiting, requested, pending). --limit 100 keeps reruns and multi-workflow PRs from falling outside the default 20-run window. Treat either source reporting activity as a pending state.
PR_HEAD_SHA=$(gh pr view $PR_NUMBER --json headRefOid --jq '.headRefOid')
gh pr checks $PR_NUMBER --json name,state,bucket
IN_PROGRESS_RUNS=$(gh run list --commit "$PR_HEAD_SHA" --limit 100 --json status --jq '[.[] | select(.status != "completed")] | length')
Categorize each named check by its bucket field:
bucket is pendingbucket is fail or cancel — cancelled checks are not passing per GitHub required-check semanticsbucket is passbucket is skipping — treat as non-blocking (not a failure)If any checks are still running OR IN_PROGRESS_RUNS > 0:
Stop without printing anything. This is the normal waiting state — no point acting on incomplete results. IN_PROGRESS_RUNS > 0 catches bot-review workflows (Copilot, CodeRabbit, Codex) that execute via GitHub Actions but may not surface a named check.
If ALL checks passed or neutral (no pending/fail/cancel) AND IN_PROGRESS_RUNS == 0, or NO checks and no runs in progress: Set CI_STATUS=passing. Continue to Step 2.
If any checks failed: Set CI_STATUS=failing and record the failing check names. Continue to Step 1b (attempt auto-fix).
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.
If no failing runs are found (e.g., the check is from an external review tool like Codacy, CodeRabbit, or Qodo that reports status via the Checks API but has no GitHub Actions run logs):
Skip classification — there is nothing to auto-fix. Continue to Step 2 with CI_STATUS=failing.
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:
Cannot auto-fix, but do NOT stop here. Continue to Step 2 with CI_STATUS=failing. The failing checks may be resolved by pr-check (e.g., review-tool checks that clear once their comments are addressed).
Do not notify after a successful fix attempt — this is routine automation. Stop silently and let the next cycle check the result.
Stop vs. continue rule for Step 1b: Only stop if you pushed commits (after applying a fix) or re-ran jobs with gh run rerun — either action makes the current CI state stale, and the next cycle should evaluate the fresh result. If neither commits were pushed nor jobs were re-run (unknown failure, no logs found), continue to Step 2.
Rebase is about branch freshness, not CI health. Always attempt it regardless of CI_STATUS.
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 on the new HEAD. 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
Fire PushNotification with message ⚠️ Rebase conflict on PR #<number> — could not auto-resolve. File(s): <conflicting_files>. <url>.
Write state key rebase_conflict:<sorted_file_list>.
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.
Trust the process: Even after multiple cycles, dlc:pr-check evaluates each comment on its own merits. If comments are genuinely resolved, dlc:pr-check will find 0 unresolved items and confirm it — you never need to short-circuit this by handling comments yourself. Every cycle that reduces the unresolved count is real progress toward merge-ready.
Context — why unattended mode exists: The babysitter runs in a loop with no human at the terminal. The invocation below therefore passes --unattended so pr-check activates its Autonomy Ladder (ten low-risk patterns — rename, typo, null check, logging, etc. — always auto-implemented) and its Pending-Human classification for items that genuinely need human judgment (architectural trade-offs, product scope, ambiguous fixes). Pending-Human items surface back here through the Pending-Human: <n> — ... line in pr-check's Step 6 summary and produce a single PushNotification in Step 4. Under --unattended, pr-check posts no Acknowledged — will be addressed by the author replies — the halt is the communication.
Skill("dlc:pr-check", "<PR_NUMBER> --unattended")
After pr-check completes, parse its output summary to extract these values:
Pending-Human: <n> — <item1_short>; <item2_short>; ... line from the Step 6 summary. The <n> is the count; split the tail on ; (trimming whitespace) to get the per-item short descriptions. If the line is absent, the count is 0. This line is emitted only under --unattended. Worked example: Pending-Human: 2 — rename foo to bar; clarify async semantics of handler() → count=2, items=["rename foo to bar", "clarify async semantics of handler()"].If pr-check pushed commits, re-request review from all prior human reviewers. Bots re-trigger automatically on every push, so only humans need an explicit nudge. Use the REST reviews endpoint's user.type field (Bot vs User) as the discriminator, and drop the PR author — you cannot request review from a PR's own author.
Why not filter on the
[bot]login suffix:gh pr view --json reviewsreturns author logins without the[bot]suffix (e.g.coderabbitai,gemini-code-assist), so anendswith("[bot]")filter silently fails to exclude App-based review bots and leaves the PR author in the list. The RESTuser.typefield is format-independent and reliably reportsBotvsUser.--paginatefetches every page — the reviews endpoint returns 30 per page by default, so on a PR with many bot reviews a human reviewer on a later page isn't missed — andjq -s '(add // [])'merges the per-page arrays before filtering. (gh apisubstitutes{owner}/{repo}from the current repo automatically.)
REVIEWERS=$(gh api --paginate repos/{owner}/{repo}/pulls/$PR_NUMBER/reviews \
| jq -rs "(add // []) | [.[].user | select(.type == \"User\" and .login != \"$PR_AUTHOR\") | .login] | 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-fetch the PR state (pr-check may have pushed commits that changed things). Query both the named checks and the raw workflow runs, same as Step 1 — especially critical here because pr-check may have just pushed a commit that triggered new bot-review workflows that take minutes to finish:
PR_HEAD_SHA=$(gh pr view $PR_NUMBER --json headRefOid --jq '.headRefOid')
gh pr checks $PR_NUMBER --json name,state,bucket
IN_PROGRESS_RUNS=$(gh run list --commit "$PR_HEAD_SHA" --limit 100 --json status --jq '[.[] | select(.status != "completed")] | length')
gh pr view $PR_NUMBER --json reviewDecision,mergeable
Categorize the fresh check results by bucket field (same logic as Step 1 — fail/cancel = failed, pass = passed, skipping = neutral, pending = running):
bucket is pending) OR IN_PROGRESS_RUNS > 0: Stop silently — CI is incomplete on the new HEAD. Do NOT take any terminal action (notification + self-cancel) in this state. Next cycle will re-evaluate.IN_PROGRESS_RUNS == 0, or NO checks and no runs in progress: Set CI_STATUS=passing.bucket is fail or cancel): Set CI_STATUS=failing and record the fresh failing check names (replacing any stale values from Step 1).Evaluate the following conditions in order. The first matching condition wins:
If pr-check reported Pending-Human items (count > 0): These items require human judgment — architectural trade-offs, product scope decisions, or ambiguous fixes with no clear winner. The babysitter cannot resolve them; halt the loop loudly.
; separators, leading with the PR number and count. Target under 200 characters total; if longer, truncate trailing items and append … so the most important signal fits in a single mobile notification.PushNotification with message PR #<number>: <count> items need your call — <item1_short>; <item2_short>.pending_human:<count> (count-only dedup per spec)./dlc:pr-check <PR> in attended mode, then relaunch /loop 10m /dlc:babysit <PR> when ready to resume.If pr-check reported remaining unresolved items AND CI_STATUS is failing:
Both need attention — surface both in a single notification so CI failure isn't hidden behind unresolved items.
PushNotification with message 💬 PR #<number> has <count> unresolved items after auto-fix + CI failing: <check_names>. <url>.unresolved:<count>,ci_failing:<sorted_check_names>.If pr-check reported remaining unresolved items AND CI_STATUS is passing:
PushNotification with message 💬 PR #<number> has <count> unresolved items after auto-fix. Review needed. <url>.unresolved:<count>.If CI_STATUS is failing:
PushNotification with message 🔴 CI failing on PR #<number>: <title> — Failed: <check_names>. <url>/checks.ci_failing:<sorted_check_names>.If reviewDecision is CHANGES_REQUESTED: Stop silently. Re-review was already requested in Step 3. Next cycle will re-check.
If mergeable is CONFLICTING:
PushNotification with message ⚠️ PR #<number> has merge conflicts. <url>.merge_conflict.If pr-check reported 0 remaining unresolved items AND 0 Pending-Human AND CI_STATUS is passing AND reviewDecision is APPROVED (or empty) AND mergeable is MERGEABLE:
ready.PushNotification with message ✅ PR #<number> ready to merge! — <title> — <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>.stateTerminal states that follow this pattern: ready, closed:<state>, pending_human:<count>. Each emits exactly one PushNotification, writes its state key, then runs the cancellation above. Non-terminal branches (ci_failing:*, rebase_conflict:*, merge_conflict, unresolved:*) notify and stop without cancelling — the next /loop cycle re-evaluates.
npx claudepluginhub p/rube-de-dlc-plugins-dlcMonitors an open PR for conflicts, CI failures, review comments, and merge readiness, with auto-fixes and one-shot modes. Use for babysitting a PR, fixing CI, resolving conflicts, or triaging review comments.
Babysits an open GitHub PR until merge-ready, reacting to new review comments and CI failures as they arrive. Use for continuous monitoring, not one-shot fixes.
Monitors open pull requests and merge requests, triages review comments, fixes CI failures, and pushes autofixes for GitHub and GitLab.