CtrlK
BlogDocsLog inGet started
Tessl Logo

monitor-pr

Monitor one or more GitHub PRs and maintain a live status table showing title, link, check status, resolved/total comments, and reviewer approval. Shows a Windows dialog when a PR is ready to merge. Input: a comma-separated list of PR numbers, "mine", or "all".

58

Quality

68%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

Fix and improve this skill with Tessl

tessl review fix ./.github/skills/monitor-pr/SKILL.md
SKILL.md
Quality
Evals
Security

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:

ValueMeaning
Comma-separated numbers (e.g. 1234,5678)Monitor those specific PRs
mineAll open PRs authored by the current user
allAll 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

  1. Verify gh is installed and authenticated (gh auth status). If not, link to https://github.com/cli/cli#installation and stop.
  2. 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):

ColumnSource
PR#<number> linked to the PR URL
Titlegh pr view --json "title"
Checksgh pr view --json "statusCheckRollup" — ✅ all pass / ❌ N fail, M pending (ETA ~Xm) / ⏳ N pending (ETA ~Xm). See ETA note below.
CommentsGraphQL reviewThreads query — ✅ all resolved / ❌ N/M resolved
Approvedgh pr view --json "reviewDecision" — ✅ approved / ❌ not approved (the field is an enum, not a count)
Readyready 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 with pageInfo { 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.

  1. For each in-progress check (status != COMPLETED):
    • elapsed = now - startedAt (from statusCheckRollup).
    • 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, use elapsed as a lower bound and mark ETA as ~Xm+.
  2. For each queued / not-started check (status == QUEUED or missing startedAt): remaining = historical (full duration).
  3. Checks run in parallel, so the overall PR ETA is max(remaining) across all pending checks — not the sum.
  4. 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:

PRTitleChecksCommentsApprovedReady
#1234Fix foo⏳ 2 pending (ETA ~5m)✅ all resolved (4/4)❌ not approved❌ not ready
#5678Add 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 (detailsUrl on github.com) — use GitHub MCP get_job_logs, or gh run view <run-id> --log-failed.

  • Azure DevOps Pipelines (detailsUrl on dev.azure.com) — the Babylon.js ADO org (babylonjs) allows anonymous API access; no auth needed. The detailsUrl looks like https://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.records to the failing entries (result == "failed") and read each .log.url. If a jobId is present in detailsUrl, 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

  1. After printing the initial status table, sleep for 5 minutes:
    sleep 300
  2. After sleeping, re-fetch ALL PR data from scratch using the same gh and 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.
  3. Print the fully refreshed status table to the main chat.
  4. 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.
  • 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.
Repository
BabylonJS/Babylon.js
Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.