From rune
Automated PR guardian loop — schedules a recurring cron (every 5 min) that checks review comments, CI/CD status, branch freshness, runs browser tests, and auto-merges when everything is green. Use when you want hands-off PR shepherding to merge. Triggers on "pr guardian", "auto merge loop", "watch my PR", "shepherd PR", "auto merge", "monitor PR", "drive PR to merge". <example> user: "/rune:pr-guardian" assistant: "Detecting current PR from branch, scheduling guardian loop..." </example> <example> user: "/rune:pr-guardian 42" assistant: "PR Guardian activated for PR #42. Checking every 5 minutes..." </example> <example> user: "/rune:pr-guardian --disable-auto-merge" assistant: "PR Guardian activated for PR #N (monitor-only mode). Auto-merge disabled..." </example> <example> user: "/rune:pr-guardian 42 --disable-auto-merge" assistant: "PR Guardian activated for PR #42 (monitor-only mode). Auto-merge disabled..." </example>
npx claudepluginhub vinhnxv/rune --plugin runeThis skill uses the workspace's default tool permissions.
An autonomous cron-based loop that watches your current PR and drives it to merge.
Monitors PR health on recurring schedules: merge conflicts, CI/CD failures in GitHub Actions/Buildkite/Vercel/Fly.io, review comment triage/resolution, merge readiness. One-shot triage mode.
Monitors pull requests through to merge by automatically handling CI failures, review comments, and thread resolution until all checks pass. Invoke after PR creation or via /pr-shepherd.
Babysits GitHub PRs by polling CI checks, workflow runs, reviews, and mergeability until merge-ready; diagnoses failures, retries flakies up to 3x, pushes fixes when appropriate, stops for user help.
Share bugs, ideas, or general feedback.
An autonomous cron-based loop that watches your current PR and drives it to merge.
┌─────────────────────────────────────────────────────────────┐
│ PR Guardian Loop │
├─────────────────────────────────────────────────────────────┤
│ 1. Check PR review comments │
│ └─ If unresolved → /rune:resolve-all-gh-pr-comments │
│ 1.5 Local quality gate (lint + typecheck) │
│ └─ Auto-detect stack → run lint/format/typecheck │
│ └─ Runs BEFORE every git push │
│ 2. Check CI/CD status │
│ └─ If red → diagnose & fix → lint gate → push │
│ 3. Check branch freshness vs main │
│ └─ If behind → rebase → lint gate → push │
│ 3.5 Local services & migration health │
│ └─ A: Start Docker Compose / verify dev servers │
│ └─ B: Detect & resolve migration conflicts │
│ (Alembic, Django, Rails, Prisma, Sequelize, etc.) │
│ └─ C: Apply pending migrations + round-trip verify │
│ 4. Run browser tests (if applicable) │
│ └─ Frontend/API changes → test related routes │
│ 5. All green + no comments? │
│ └─ Merge PR (squash + delete branch) │
│ └─ Delete this cron job │
└─────────────────────────────────────────────────────────────┘
/rune:pr-guardian # Start the guardian on current branch's PR
/rune:pr-guardian <PR_NUMBER> # Watch a specific PR
/rune:pr-guardian --disable-auto-merge # Watch PR but skip auto-merge (monitor + fix only)
/rune:pr-guardian <PR_NUMBER> --disable-auto-merge # Watch specific PR, skip auto-merge
When this skill is invoked, perform these steps in order:
# Parse arguments — extract PR number and flags
PR_NUMBER=""
DISABLE_AUTO_MERGE=false
for arg in $ARGUMENTS; do
case "$arg" in
--disable-auto-merge) DISABLE_AUTO_MERGE=true ;;
[0-9]*) PR_NUMBER="$arg" ;;
esac
done
# If no PR number provided, detect from current branch
if [ -z "$PR_NUMBER" ]; then
gh pr view --json number -q '.number'
fi
If no open PR is found on the current branch, STOP and tell the user:
"No open PR found on this branch. Push your branch and create a PR first."
Use CronCreate with:
*/5 * * * * (every 5 minutes)trueSave the returned job ID — you will need it for self-deletion in Step 5.
Tell the user:
DISABLE_AUTO_MERGE=false:
"PR Guardian activated for PR #. Checking every 5 minutes. I'll auto-merge when everything is green. The cron auto-expires after 7 days if not completed sooner."
DISABLE_AUTO_MERGE=true:
"PR Guardian activated for PR # (monitor-only mode). Checking every 5 minutes. Auto-merge is disabled — I'll fix issues but won't merge. The cron auto-expires after 7 days."
Each cron tick should execute this prompt (adapt the PR number):
You are the PR Guardian. Execute these steps for PR #<PR_NUMBER> in order. Stop at the first step that requires action and re-check on the next tick. Auto-merge is <ENABLED|DISABLED based on DISABLE_AUTO_MERGE flag>.
gh pr view <PR_NUMBER> --json reviewDecision,reviews,comments --jq '{
decision: .reviewDecision,
pending_reviews: [.reviews[] | select(.state == "CHANGES_REQUESTED" or .state == "COMMENTED") | {author: .author.login, state: .state}],
comment_count: (.comments | length)
}'
If there are unresolved review comments or CHANGES_REQUESTED reviews:
/rune:resolve-all-gh-pr-comments skill to resolve all commentsIf no actionable comments → proceed to Phase 1.5 (if code changed since last tick) or Phase 2
Before every git push, run the project's lint and typecheck commands to catch errors
locally instead of waiting for a full CI cycle.
See local-quality-gate.md for full framework-specific commands.
Summary:
.tsx/.jsx/.vue, backend: .py/.rb/.go/.rs/.php)$PKG run lint / eslint → tsc --noEmit → prettier auto-fixmake check-all / make lint / task lint convenience targetsstyle: auto-fix lint and formatting| Stack | Lint | Typecheck | Format |
|---|---|---|---|
| JS/TS | eslint / $PKG run lint | tsc --noEmit | prettier |
| Python | ruff → flake8 | mypy / pyright | ruff format |
| Ruby | rubocop | — | rubocop |
| Go | golangci-lint → go vet | — | gofmt |
| Rust | cargo clippy | built-in | cargo fmt |
| PHP | phpstan | phpstan | pint / php-cs-fixer |
If lint/typecheck fails with unfixable errors:
If lint/typecheck passes → proceed to push (or Phase 2 if no push needed)
Key rule: This phase runs BEFORE every git push in the guardian loop — that includes
after Phase 1 (comment fixes), Phase 2 (CI fixes), Phase 3 (rebase), and Phase 4 (browser test fixes).
gh pr checks <PR_NUMBER> --json name,state,conclusion --jq '.[] | select(.state != "COMPLETED" or .conclusion != "SUCCESS")'
If any checks are pending: STOP this tick — wait for CI to finish
If any checks are failed/red:
gh run view <RUN_ID> --log-failedfix: resolve CI failure — <brief description>If all checks green → proceed to Phase 3
git fetch origin main
BEHIND_COUNT=$(git rev-list --count HEAD..origin/main)
echo "Behind main by: $BEHIND_COUNT commits"
If BEHIND_COUNT > 0:
git rebase origin/mainalembic heads → if multiple, alembic merge headsdb/migrate/ for conflicting timestampsnpx prisma migrate resolvegit rebase --continue after resolvinggit push --force-with-lease (safe force push after rebase)If already up-to-date → proceed to Phase 3.5
Before running browser tests, ensure local services are up and the database schema is current. See services-and-migrations.md for full framework-specific commands.
docker-compose.yml / compose.yml → docker compose up -d --wait → wait for health checks (max 60s) → rebuild if Dockerfile / dependency lockfiles changednc / curlAuto-detect migration framework and resolve conflicts before applying:
| Framework | Conflict Detection | Auto-Resolution |
|---|---|---|
| Alembic | alembic heads → multiple heads? | alembic merge heads + commit |
| Django | showmigrations --plan → CONFLICT? | makemigrations --merge per app + commit |
| Rails | Duplicate timestamps in db/migrate/ | Flag for manual resolution |
| Prisma | prisma migrate status + drift check | Flag drift for review |
| Sequelize | db:migrate:status | — |
| Knex | migrate:status | — |
| TypeORM | migration:show | — |
Command resolution (Python): uv run alembic → .venv/bin/alembic → alembic (same pattern for Django manage.py).
downgrade -1 → upgrade head to ensure reversibilitydb/schema.rb if changed after migrationnpx prisma generate after deploypg_isready (PostgreSQL) / mysqladmin ping (MySQL) via DockerIf migrations fail or conflicts cannot be resolved:
If migrations pass → proceed to Phase 4
Determine if the PR touches frontend or API-facing code:
# Get changed files
gh pr diff <PR_NUMBER> --name-only
/rune:test-browser skill for the PRIf browser tests fail:
If browser tests pass → proceed to Phase 5
All conditions met:
If DISABLE_AUTO_MERGE=true (monitor-only mode):
"PR #<PR_NUMBER> is fully green — all checks pass, no unresolved comments, branch is up-to-date. Auto-merge is disabled. Merge manually when ready."
If DISABLE_AUTO_MERGE=false (default):
Execute the merge:
# 1. Verify gh CLI
command -v gh && gh auth status
# 2. Switch to correct GitHub account
source "${RUNE_PLUGIN_ROOT}/scripts/lib/gh-account-resolver.sh" && rune_gh_ensure_correct_account
# 3. Verify PR is still open and mergeable
gh pr view <PR_NUMBER> --json state,mergeable --jq '{state, mergeable}'
# 4. Squash merge + delete branch
GH_PROMPT_DISABLED=1 gh pr merge <PR_NUMBER> --squash --delete-branch
# 5. Sync local
git checkout main && git pull origin main
After successful merge:
"PR #<PR_NUMBER> has been merged successfully! Guardian loop terminated."
--force-with-lease after rebase (never --force)docker compose down — only up -d. Guardian should add services, not destroy themDROP, TRUNCATE). Only migrate forwardgh-account-resolver.sh to ensure correct account before mergeTo stop the guardian manually, the user can say:
"Stop the PR guardian" / "Cancel the loop"
This should trigger CronDelete with the saved job ID.