monitor-pr
GitHub监控GitHub PR状态,轮询检查、评论及审批情况,生成实时状态表。支持指定PR、本人或全部PR,并在PR满足合并条件时发送桌面弹窗通知,辅助代码审查流程。
Trigger Scenarios
Install
npx skills add BabylonJS/Babylon.js --skill monitor-pr -g -y
SKILL.md
Frontmatter
{
"name": "monitor-pr",
"description": "Monitor one or more GitHub PRs and maintain a live status table showing\ntitle, link, check status, resolved\/total comments, and reviewer approval.\nShows a Windows dialog when a PR is ready to merge.\nInput: a comma-separated list of PR numbers, \"mine\", or \"all\".\n",
"argument-hint": "<pr-numbers | mine | all>"
}
Monitor PR
Poll GitHub PRs and print a live status table to the main chat. Alert when a PR is ready to merge.
Input
Parse $ARGUMENTS:
| Value | Meaning |
|---|---|
Comma-separated numbers (e.g. 1234,5678) |
Monitor those specific PRs |
mine |
All open PRs authored by the current user |
all |
All open PRs in the repo |
If $ARGUMENTS is empty, use the ask_user tool (not plain chat text)
to prompt the user with choices: mine, all, or a freeform list of PR
numbers. Do not proceed until the user has answered.
Prerequisites
- Verify
ghis installed and authenticated (gh auth status). If not, link to https://github.com/cli/cli#installation and stop. - Check if PowerShell is available (
powershell -Command "echo ok"). If unavailable, skip dialog notifications and use text-only alerts.
Step 1: Resolve the PR list
# Specific PRs — validate each exists
gh pr view <number> --repo "BabylonJS/Babylon.js" --json "number"
# "mine"
gh pr list --repo "BabylonJS/Babylon.js" -A "@me" --json "number,title,url"
# "all" (defaults to open)
gh pr list --repo "BabylonJS/Babylon.js" --json "number,title,url"
Step 2: Build and display the status table
For each PR, gather the following data (this table describes the columns — it is not the output format):
| Column | Source |
|---|---|
| PR | #<number> linked to the PR URL |
| Title | gh pr view --json "title" |
| Checks | gh pr view --json "statusCheckRollup" — ✅ all pass / ❌ N fail, M pending (ETA ~Xm) / ⏳ N pending (ETA ~Xm). See ETA note below. |
| Comments | GraphQL reviewThreads query — ✅ all resolved / ❌ N/M resolved |
| Approved | gh pr view --json "reviewDecision" — ✅ approved / ❌ not approved (the field is an enum, not a count) |
| Ready | ✅ ready if all checks pass AND approved AND all comments resolved, ❌ not ready otherwise |
Review threads require the GraphQL API since gh pr view --json does not
expose them:
gh api graphql -f query='
query {
repository(owner: "BabylonJS", name: "Babylon.js") {
pullRequest(number: <NUMBER>) {
reviewThreads(first: 100) {
totalCount
nodes { isResolved }
}
}
}
}' --jq '.data.repository.pullRequest.reviewThreads | {total: .totalCount, resolved: ([.nodes[] | select(.isResolved)] | length)}'
If
totalCount > 100, the single-page query above will undercount resolved threads. Page through withpageInfo { hasNextPage endCursor }
after:until all threads are fetched, then sum the resolved counts.
Checks ETA
When any checks are still running, compute a rough ETA before rendering the table. The Checks cell is incomplete without it.
- For each in-progress check (
status != COMPLETED):elapsed = now - startedAt(fromstatusCheckRollup).- Look up a historical duration for the same check name from
the most recent merged PR that ran it:
gh pr list --repo "<owner>/<repo>" -s merged -L 5 --json "number" gh pr view <recent-pr> --repo "<owner>/<repo>" --json "statusCheckRollup" \ --jq '.statusCheckRollup[] | select(.name == "<name>" and .status == "COMPLETED") | {startedAt, completedAt}'historical = completedAt - startedAt. remaining = max(historical - elapsed, 1m). If no history, useelapsedas a lower bound and mark ETA as~Xm+.
- For each queued / not-started check (
status == QUEUEDor missingstartedAt):remaining = historical(full duration). - Checks run in parallel, so the overall PR ETA is
max(remaining)across all pending checks — not the sum. - Round to whole minutes. If the computed ETA is negative or < 1m,
show
<1m.
Cache historical durations per check name across PRs within a single
poll iteration to avoid redundant gh calls. Do not cache across
polls (stale data risk — see the polling rule below).
Output format
Only render the table after all per-PR data (including ETA) is
gathered. Every pending-checks cell MUST end with (ETA ~Xm) (or
~Xm+ / <1m) — no ETA means the data isn't ready yet.
Render as a markdown table, one row per PR, with the columns above
as headers in the same order. Do not transpose or split into per-PR
tables. Prefix with a header line showing the OS's local time, e.g.
PR Status — 2026-04-17 05:17 PM PDT (date or Get-Date).
Example:
| PR | Title | Checks | Comments | Approved | Ready |
|---|---|---|---|---|---|
| #1234 | Fix foo | ⏳ 2 pending (ETA ~5m) | ✅ all resolved (4/4) | ❌ not approved | ❌ not ready |
| #5678 | Add bar | ✅ all pass | ✅ all resolved | ✅ 1 approval | ✅ ready |
Step 3: Distinguish real failures from flakes
When checks fail, read the CI logs. Source depends on which CI the
check is on — look at statusCheckRollup[].detailsUrl to tell:
-
GitHub Actions (
detailsUrlongithub.com) — use GitHub MCPget_job_logs, orgh run view <run-id> --log-failed. -
Azure DevOps Pipelines (
detailsUrlondev.azure.com) — the Babylon.js ADO org (babylonjs) allows anonymous API access; no auth needed. ThedetailsUrllooks likehttps://dev.azure.com/<org>/<project-guid>/_build/results?buildId=<id>&view=logs&jobId=<job-guid>. Fetch the build timeline and then the failing record's log:# List all records (jobs/tasks) and their log URLs curl -s "https://dev.azure.com/<org>/<project-guid>/_apis/build/builds/<buildId>/timeline?api-version=7.0" # Fetch a specific record's log curl -s "<record.log.url>"Filter
timeline.recordsto the failing entries (result == "failed") and read each.log.url. If ajobIdis present indetailsUrl, scope directly to that record's children.
Classification:
- Real failure — caused by changes in the PR. Show ❌ with a brief error summary.
- Flake — a test that failed on some iterations and passed on others, and is not a new test added in the PR. Show ⚠️ and note it as a suspected flake.
Step 4: Poll loop
MANDATORY: You MUST implement a continuous polling loop. Do not display the status once and stop. Do not suggest the user re-invoke the skill to refresh. You must keep running and re-checking every ~5 minutes until every monitored PR is merged or closed. Use
sleep 300(or equivalent) between polls to wait 5 minutes, then re-fetch and print the updated table. This is the core purpose of this skill.
How to poll
- After printing the initial status table, sleep for 5 minutes:
sleep 300 - After sleeping, re-fetch ALL PR data from scratch using the same
ghand GraphQL commands as Step 2. Every column — checks, comments, approval, state — must be queried fresh from the API. Do not reuse or cache any data from a previous polling iteration. New commits can restart all checks, so data from a previous poll may be stale. - Print the fully refreshed status table to the main chat.
- Repeat from step 1.
On each poll, also check
- If a PR becomes ready to merge (all checks pass, approved, all
comments resolved):
- Show a Windows dialog (if PowerShell is available):
powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.MessageBox]::Show('PR #<number> — <title> — is ready to merge.', 'PR Ready', 'OK', 'Information')" - Always also print a prominent message in the chat.
- Show a Windows dialog (if PowerShell is available):
- If a PR is merged or closed, remove it from the table and note it.
When to stop
Stop polling only when:
- Every monitored PR has been merged or closed, OR
- The user explicitly tells you to stop.
Do NOT stop for any other reason. Do not stop because "polling isn't practical in a chat session." Do not stop because "the user can re-invoke the skill." The entire point of this skill is continuous, autonomous monitoring.
Retriggering CI
If the user asks to retrigger CI (e.g. for flakes), push an empty commit:
git commit --allow-empty -m "retrigger CI"
git push
Never force push. Always use new commits.
Guidelines
- Always read CI logs on failures — don't guess at the cause.
- Print the status table to the main chat so the user can follow along.
- You MUST keep polling until all PRs are merged/closed or the user tells you to stop. Do not exit early. Do not suggest the user re-invoke the skill. This skill runs continuously.
Version History
- cf656ae Current 2026-08-20 15:17


