Apache-2.0updated 28d ago
One iteration of the babysit loop. Runs autonomously except for fix confirmations.
What can you do with Dependabot Babysit Cycle?
name: dependabot-babysit-cycle model: sonnet description: > One iteration of the dependabot-babysit loop. Reads session state, runs verify → main health check → review/update → fix → stop-condition evaluation. Not intended to be invoked directly — use /dependabot-babysit instead. Invoke with: /dependabot-babysit-cycle [scope]
/dependabot-babysit-cycle
One iteration of the babysit loop. Runs autonomously except for fix confirmations.
All GitHub I/O is performed through the dependabot-reviewer MCP server tools. Do not
call gh CLI for any GitHub operations — only for token acquisition. If the MCP server
is not present, stop and report an error.
Model: this cycle runs on a fast model (sonnet) — its logic is deterministic and
mechanical. Fixes are the exception: whenever a fix is required (Steps 3c and 5b), invoke
/dependabot-fix through the Skill tool rather than executing its steps inline, so the
fix runs under its own model: opus frontmatter. Analysis-heavy repair logic must always
use Opus, even when triggered from within babysit.
Step 0: Parse arguments
Parse ARGUMENTS (the text after the skill name) before doing anything else. Produce three variables used throughout the rest of the workflow:
| Variable | Type | Description |
|---|---|---|
filter_hosts |
[string] | null |
hosts to process; null = all authenticated hosts |
filter_repo |
"org/repo" | null |
exact repo to scope to |
filter_pr |
int | null |
single PR number; requires filter_repo |
Also derive:
filter_org— the part before/infilter_repo, or the standalone<org>argument, ornull
Parsing rules (first match wins)
Strip a leading https:// prefix first (do not pass the protocol to any tool).
| Input format | filter_hosts |
filter_repo |
filter_pr |
|---|---|---|---|
<host>/<org>/<repo>/pull/<PR> (after stripping https://) |
[host] |
org/repo |
PR |
<host>/<org>/<repo> |
[host] |
org/repo |
null |
<host>/<org> |
[host] |
null | null |
<host> (contains .) |
[host] |
null | null |
<org>/<repo>:<PR> |
null | org/repo |
PR |
<org>/<repo>#<PR> |
null | org/repo |
PR |
<org>/<repo> |
null | org/repo |
null |
<org> (no .) |
null | null | null |
| (empty) | null | null | null |
Host detection: a path segment is a host if it contains .; otherwise it is an org.
Errors
filter_hostscontains a host not found ingh auth statusoutput → stop:"Error: not logged in to <host>. Run 'gh auth login --hostname <host>'."filter_prset butfilter_repois null → stop:"Error: PR number requires a repo (use <org>/<repo>:<PR>)."
Step 1: Load state
Read ~/.claude/dependabot-babysit-state.json.
If the file does not exist, initialise:
{
"blocked_prs": [],
"blocked_repos": [],
"iteration": 0,
"start_time": "<now>",
"scope": null
}
Increment iteration by 1 (first real iteration = 1).
Keep in memory:
blocked_prs— list of strings like"org/repo#123". These PRs are never processed.blocked_repos— list of strings like"org/repo". Main branch of these repos is never checked.iteration— current cycle number (after increment).start_time— ISO timestamp of first invocation (used in final report).
Step 1.5: Load knowledge base
Read the knowledge base as described in the agent's Knowledge Base section. Keep loaded entries available for PR classification in Step 2c and correlation in Step 3a.
Step 1.9: Early exit check
On every re-invocation (iteration ≥ 1 after increment in Step 1), check whether the stop condition is already satisfied before running the full discovery:
- Read
all_open_prsby callinglist_dependabot_prsfor each host (same scope as Step 2b). - If the result is empty AND there were no repos with failing main CI in the last iteration (i.e., the previous Step 6 evaluated to "stop condition met"), print the silent exit message and stop:
Babysit — nothing to do. All eligible PRs merged and all mains passing.
Stop the /loop manually when ready.
If open PRs are found (GitHub has opened new PRs since the last cycle), continue to Step 2 normally — do not exit early.
Note: This check is a lightweight guard — it calls list_dependabot_prs but not
get_pr_details or any CI status call. Its purpose is to avoid a full verification round
when the work is already done.
Step 2: Verify — snapshot all PRs and main branches
Step 2a: Discover hosts and acquire tokens
gh auth status --show-token
Parse output to build {host, token} pairs. If filter_hosts is non-null (set in Step 0), keep only pairs where the host appears in filter_hosts.
Step 2b: Discover open PRs
For each {host, token}:
if filter_repo is not null:
prs = list_dependabot_prs(host, token, repo=filter_repo)
elif filter_org is not null:
prs = list_dependabot_prs(host, token, org=filter_org)
else:
prs = list_dependabot_prs(host, token)
Step 2c: Classify each PR
Step 2c-i: Re-check blocked PRs
Before classifying open PRs, check whether any PR in blocked_prs has been fixed
(e.g. someone manually resolved a merge conflict or repaired a failing CI check).
For each PR key in blocked_prs (format "org/repo#123"):
- Parse
repoandpr_numberfrom the key. - Determine
hostandtokenby matchingrepoagainst the repos discovered in Step 2b across all{host, token}pairs from Step 2a. Use the host whererepowas found. - Call
get_pr_details(host, token, repo=repo, pr_number=pr_number). - Apply the classification priority table (same table as in Step 2c-ii).
- If the resulting status is anything other than
⚠️ ACTION REQUIRED:- Remove the key from
blocked_prs. - Record the already-fetched
PRDetailsfor this PR — it will be reused in Step 2c-ii.
- Remove the key from
- If the status is still
⚠️ ACTION REQUIRED— leave it inblocked_prs, but retain the already-fetchedPRDetailsin memory (keyed by PR). It will be reused in Step 5.5 to keep the blocked PR's branch current. Take no fix action here.
Step 2c-ii: Classify open PRs
For each PR from Step 2b not in blocked_prs, plus any PRs unblocked in Step 2c-i:
- If the PR was unblocked in Step 2c-i, reuse the
PRDetailsalready fetched there — do not callget_pr_detailsagain. - Otherwise, call:
get_pr_details(host, token, repo=pr.repo, pr_number=pr.number)
Apply the classification priority table from /dependabot-verify (8 states):
| Priority | Status | Condition |
|---|---|---|
| 0 | ⚠️ ACTION REQUIRED | KB proactive match |
| 1 | ⚠️ ACTION REQUIRED | ci_status == "failing" OR comment contains "requires manual action ⚠️" |
| 2 | 🔄 NEEDS BRANCH UPDATE | merge_state == "behind" |
| 3 | 🔐 WAITING FOR ENV APPROVAL | ci_status == "waiting_for_env_approval" |
| 4 | ⏳ WAITING FOR CI | ci_status == "pending" |
| 5 | 👀 NEEDS REVIEW | No APPROVED review from current user AND no comment contains "Dependabot PR reviewed ✅" or "requires manual action ⚠️" |
| 6 | ✅ READY | Approved + automerge + CI passing + branch up to date |
| 7 | 👀 NEEDS REVIEW | catch-all |
PRs remaining in blocked_prs are excluded entirely from this classification.
Step 2d: Collect main branch CI status
For each unique (host, repo) across all open PRs, plus repos from
list_recently_merged_dependabot_prs(host, token, since=<14 days ago>):
get_branch_ci_status(host, token, repo, branch="main")
If 404 → retry with branch="master". If both fail → record as ❌ ERROR.
Exclude repos in blocked_repos from collection.
Store results as main_health: map of "org/repo" → {branch, ci_status, failing_checks}.
Step 3: Main branch health check (PRIORITY — before review/fix)
For each repo in main_health where ci_status == "failing" AND repo NOT in blocked_repos:
Step 3a: Determine if failure is dependency-related
- Call
list_recently_merged_dependabot_prs(host, token, since=<14 days ago>)filtered to this repo. - For each failing check, call
get_check_logs(host, token, repo, check_run_id=<id>)and read the log file. - Correlate: find which merged Dependabot PR most likely introduced the failure by comparing
merge timestamps to the first CI failure timestamp. Use the same logic as
/dependabot-fixStep 4b. - If no recently merged Dependabot/Renovate PRs in the past 14 days → skip (not in scope).
Do NOT add to
blocked_repos. Do NOT gate review for this repo. - If correlation is found → proceed to Step 3b.
Step 3b: Ask for confirmation
Present:
Main branch CI failing in <repo>
Likely caused by: <library> <old_version> → <new_version> (merged <date>, PR <url>)
Failing checks: <check names>
Proceed with /dependabot-fix? (yes / no)
Step 3c: Execute or block
tak,yes, or empty reply → run the fix by invoking the fix skill through the Skill tool:Skill(skill="dependabot-fix", args="--yes <repo>"). Do not execute the fix steps inline — invoking via the Skill tool ensures the fix runs underdependabot-fix's own model (Opus), which is required even when a fix is triggered from within babysit (this cycle otherwise runs on a faster model).- If fix succeeds → call
get_branch_ci_status(host, token, repo, branch)to confirm main is now passing. Updatemain_health[repo].ci_statusto"passing"in memory so the stop condition in Step 6 and theunhealthy_reposgate use fresh state. - If fix fails (diagnostic comment posted, or unexpected situation hit) → add
"org/repo"toblocked_repos.
- If fix succeeds → call
nieorno→ add"org/repo"toblocked_repos.- Any other input → re-present the prompt from Step 3b and wait for a valid response.
Step 3d: Build unhealthy_repos gate
After processing all failing repos, collect:
unhealthy_repos = { repo | main_health[repo].ci_status == "failing" AND repo NOT in blocked_repos }
PRs in unhealthy_repos are excluded from Steps 4 and 5. This prevents reviewing or
approving PRs into a broken pipeline.
Step 4: Review / Update
For each open PR where:
- PR key (
"org/repo#123") NOT inblocked_prs - PR repo NOT in
unhealthy_repos
Route by current status:
| Status | Action |
|---|---|
👀 NEEDS REVIEW |
Execute full /dependabot-review Path B analysis for this PR. Use prepare_merge to approve and set automerge. |
🔄 NEEDS BRANCH UPDATE |
Call update_branch(host, token, repo, pr_number). If needs_manual_rebase → attempt conflict resolution as per /dependabot-update Step 3.5. |
🔐 WAITING FOR ENV APPROVAL |
Call prepare_merge(host, token, repo, pr_number, comment) to approve pending environment deployments. |
⏳ WAITING FOR CI |
No action — wait for GitHub. |
✅ READY |
No action — GitHub automerge will handle it. |
⚠️ ACTION REQUIRED |
Skip here — handled in Step 5. |
After routing, record each PR whose repo is in unhealthy_repos with status 🔒 skipped
and action repo main failing in the iteration report. These PRs are not processed in
Step 5 either — they are deferred to the next iteration.
Step 5: Fix PRs with ACTION REQUIRED
For each open PR where:
- Status is
⚠️ ACTION REQUIRED - PR key NOT in
blocked_prs - PR repo NOT in
unhealthy_repos
Step 5a: Present diagnosis and ask for confirmation
Collect diagnosis from get_pr_details:
failing_checks[].name— names of failing CI checksdiff_classification— library, old_version, new_version, semver
Present:
PR <repo>#<number> — <title>
Problem: <merge conflict | CI failing: <check names> | both>
Library: <library> <old_version> → <new_version> (<semver>)
Attempt fix? (yes / no)
Step 5b: Execute or block
tak,yes, or empty reply → run the fix by invoking the fix skill through the Skill tool:Skill(skill="dependabot-fix", args="--yes <repo>:<pr_number>"). Do not execute the fix steps inline — invoking via the Skill tool ensures the fix runs underdependabot-fix's own model (Opus), which is required even when a fix is triggered from within babysit (this cycle otherwise runs on a faster model).- Fix succeeds → record action
🔧 fixed <commit_url>. - Fix fails or posts diagnostic comment → add
"org/repo#<number>"toblocked_prs, record action⏭️ blocked (fix failed).
- Fix succeeds → record action
nieorno→ add"org/repo#<number>"toblocked_prs, record action⏭️ blocked (user declined).- Any other input → re-present the prompt from Step 5a and wait for a valid response.
Step 5.5: Keep blocked PRs current
Blocked PRs are parked from fixing (no re-fix, no new approval, no automerge), but their branches should not be allowed to drift behind base while they wait for manual attention. Keeping them current prevents a stale-branch pile-up and means that when the underlying problem is finally resolved (and the PR auto-unblocks in Step 2c-i), it is already mergeable.
For each PR key still in blocked_prs after Step 5 (i.e. those that were not unblocked
in Step 2c-i), where the PR repo is NOT in unhealthy_repos:
-
Use the
PRDetailsretained for this PR in Step 2c-i (fetched during the blocked re-check). Do not callget_pr_detailsagain. -
Route on
merge_state:
merge_state |
Action |
|---|---|
"behind" |
Call update_branch(host, token, repo, pr_number). See Step 5.5a. |
"dirty" |
Attempt dependency-file conflict resolution as per /dependabot-update Step 3.5. On success record action 🔄 branch updated (conflicts resolved); if non-dependency conflicts or unresolvable → leave blocked, record action ⏭️ still blocked. |
"clean" or "unknown" |
No update needed. Record action ⏭️ still blocked. |
Step 5.5a: Handle "behind" state
Call update_branch(host, token, repo, pr_number).
status == "needs_manual_rebase"→ attempt conflict resolution as per/dependabot-updateStep 3.5. On success record🔄 branch updated (conflicts resolved); otherwise leave blocked and record⏭️ still blocked.status == "done"→ the branch is now current. If the PR has an APPROVED review from any user in itsreviewslist, call:
to approve any pending environment deployments. (prepare_merge(host, token, repo=pr.repo, pr_number=pr.number, comment="Blocked PR branch updated by babysit — re-triggering env approvals.")prepare_mergeis idempotent.)- If
envs_approved > 0→ record action🔄 branch updated; <N> env(s) approved. - Otherwise → record action
🔄 branch updated. - On exception → record action
🔄 branch updated (env approval failed).
- If
Important: this step never removes the PR from blocked_prs, never re-runs /dependabot-fix,
and never sets a new approval or automerge beyond what prepare_merge re-triggers for an
already-approved PR. The PR stays blocked; only its branch is kept current.
Step 6: Save state
Write updated state back to ~/.claude/dependabot-babysit-state.json using the Write tool:
{
"blocked_prs": ["<updated list>"],
"blocked_repos": ["<updated list>"],
"iteration": <current iteration number>,
"start_time": "<preserved from initial write>",
"scope": "<preserved from initial write>"
}
Always save before printing reports so that if the skill is interrupted mid-report, state is not lost.
Step 7: Evaluate stop condition
Collect current state after Steps 3–5:
open_eligible_prs = [pr for pr in all_open_prs if "org/repo#<number>" not in blocked_prs]
unhealthy_eligible_repos = [
repo for repo, health in main_health.items()
if health.ci_status == "failing" AND repo not in blocked_repos
]
Stop condition met when BOTH are true:
open_eligible_prsis empty (all non-blocked PRs have been merged by GitHub)unhealthy_eligible_reposis empty (all non-blocked repos have passing main)
If stop condition met:
Print the final report (see Output Format → Final report below), then exit.
The /loop scheduler will invoke this skill again — on that next invocation,
the same check will detect "nothing to do" and exit silently. The user stops
the loop manually.
If stop condition NOT met:
Print the iteration report (see Output Format → Iteration report below) and exit normally.
The /loop scheduler will invoke the next cycle after the configured interval.
Output Format
Iteration report
## Babysit — iteration #<N> [YYYY-MM-DD HH:MM]
### PRs
| Repo | PR | Status | Action |
|------|----|--------|--------|
| org/repo | [#123](url) | ✅ merged | — |
| org/repo | [#456](url) | ✅ READY | ⏳ waiting for GitHub automerge |
| org/repo | [#789](url) | ⚠️ ACTION REQUIRED | 🔧 fixed <commit_url> |
| org/repo | [#101](url) | ⚠️ ACTION REQUIRED | ⏭️ blocked (user declined) |
| org/repo | [#105](url) | ⚠️ ACTION REQUIRED | 🔄 branch updated (still blocked) |
| org/repo | [#102](url) | ⏳ WAITING FOR CI | ⏳ waiting |
| org/repo | [#104](url) | 🔐 WAITING FOR ENV APPROVAL | 🔐 envs approved (1) |
| org/repo | [#103](url) | 🔒 skipped | repo main failing |
### Main branch health
| Repo | Branch | Status | Action |
|------|--------|--------|--------|
| org/repo | main | ✅ passing | — |
| org/repo2 | main | ❌ failing | 🔧 fixed (patch PR #42 opened) |
| org/repo3 | main | ❌ failing | ⏭️ blocked (user declined) |
Next iteration in <interval>.
Action values for PRs:
—— no action taken (already merged, READY, or WAITING)⏳ waiting for GitHub automerge— READY, automerge set, waiting for GitHub🔐 envs approved (<N>)— N environment deployments approved; CI now running🔧 fixed <commit_url>— fix committed successfully⏭️ blocked (user declined)— user said no to fix prompt⏭️ blocked (fix failed)— fix attempted but could not be completed⏭️ still blocked— remains inblocked_prs; branch already current, no update needed🔄 branch updated— blocked PR's branch was behind and has been updated (still blocked)🔄 branch updated; <N> env(s) approved— blocked PR branch updated and N env deployments approved (still blocked)🔄 branch updated (conflicts resolved)— blocked PR branch updated, dependency-file conflicts resolved (still blocked)🔒 skipped— PR's repo has a failing main branch; deferred to next iteration
Action values for main branches:
—— passing, no action needed🔧 fixed (patch PR #N opened)— fix PR created against main🔧 fixed (commit <url>)— fix committed directly to PR branch⏭️ blocked (user declined)— user said no⏭️ blocked (fix failed)— fix attempted but failedℹ️ not dependency-related— CI failing but not caused by a Dependabot merge; out of scope
Final report
## Babysit — done [YYYY-MM-DD HH:MM]
All eligible PRs merged and all main branches passing.
### Blocked PRs (require manual attention)
| Repo | PR | Title | Blocked reason | Iteration |
|------|----|-------|----------------|-----------|
| org/repo | [#789](url) | Bump foo 1.0→2.0 | user declined fix | #2 |
| org/repo | [#999](url) | Bump bar 3.0→4.0 | fix failed | #3 |
### Blocked repos (main branch still failing)
| Repo | Reason | Iteration blocked |
|------|--------|-------------------|
| org/repo3 | user declined fix | #3 |
Total iterations: 5 | Elapsed: ~50 minutes
Omit "Blocked PRs" section if blocked_prs is empty.
Omit "Blocked repos" section if blocked_repos is empty.
If both are empty, print instead: No manual action required. Everything merged and green.
Silent exit (stop condition already met on re-entry)
When the stop condition is detected at the top of a fresh invocation (all eligible PRs merged, all mains passing) and the iteration report was already printed in the previous run, exit silently:
Babysit — nothing to do. All eligible PRs merged and all mains passing.
Stop the /loop manually when ready.
Install
Add Dependabot Babysit Cycle to your client. Pick the one you use.
npx skills add pPrecel/dependabot-reviewerInstalls every skill in the repository, then prompts for which to keep.
/plugin marketplace add pPrecel/dependabot-reviewerAdds the repository as a plugin marketplace; install individual plugins with `/plugin install`.
git clone https://github.com/pPrecel/dependabot-reviewer
cp -r skills/dependabot-babysit-cycle ~/.claude/skills/A skill is a plain directory. Copy it into `.claude/skills/` in a project or in your home directory.
Score
79 / 100
Good