Monitor open GitHub PRs across all repos in ~/Dev. Checks PR age, flags stale PRs, and optionally alerts via Aivena/Telegram. Uses `gh pr list` for accurate GitHub data. **TRIGGERS - Use this skill when:** - User asks about open PRs, pull requests, or PR status - User says "check PRs", "any stale PRs?", "PR review status" - Cron job `pr-monitor` fires - User asks "what needs review?" or "what's waiting for merge?" - User wants a PR dashboard or PR age report
Scanned 9/12/2026
Install to Claude Code
npx -y skills add espennilsen/pi --skill pr-monitor --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Pr Monitor?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/espennilsen-pr-monitor)More formats (shields.io, HTML) on the badges page.
---
name: pr-monitor
description: |
Monitor open GitHub PRs across all repos in ~/Dev. Checks PR age, flags stale PRs,
and optionally alerts via Aivena/Telegram. Uses `gh pr list` for accurate GitHub data.
**TRIGGERS - Use this skill when:**
- User asks about open PRs, pull requests, or PR status
- User says "check PRs", "any stale PRs?", "PR review status"
- Cron job `pr-monitor` fires
- User asks "what needs review?" or "what's waiting for merge?"
- User wants a PR dashboard or PR age report
---
# PR Monitor Skill
Scan all GitHub-hosted repos in `~/Dev` for open pull requests. Flag stale PRs and report status.
## Procedure
### Step 1: Scan all repos for open PRs
Run this exact command to collect all open PRs:
```bash
for dir in ~/Dev/*/; do
if [ -d "$dir/.git" ]; then
remote=$(git -C "$dir" remote get-url origin 2>/dev/null)
if echo "$remote" | grep -q github.com; then
repo=$(echo "$remote" | sed 's/.*github.com[:/]\(.*\)\.git/\1/' | sed 's/.*github.com[:/]\(.*\)/\1/')
prs=$(timeout 30s gh pr list --repo "$repo" --state open --json number,title,createdAt,headRefName,author,isDraft,reviewDecision)
pr_count=$(echo "${prs:-[]}" | jq 'length' 2>/dev/null)
if [ "${pr_count:-0}" -gt 0 ]; then
echo "=== $(basename "$dir") ($repo) ==="
echo "$prs" | jq -r '.[] | " #\(.number) [\(.headRefName)] \(.title) (by \(.author.login), created \(.createdAt[:10]), draft: \(.isDraft), review: \(.reviewDecision // "none"))"'
echo ""
fi
fi
fi
done
```
### Step 2: Calculate PR age
For each PR, calculate age from `createdAt` to today. Classify as:
- **🟢 Fresh** — 0–1 days old
- **🟡 Aging** — 2–3 days old
- **🔴 Stale** — 4+ days old
### Step 3: Format the report
Output a table with columns:
| Repo | PR | Branch | Title | Author | Age | Status | Review |
|------|-----|--------|-------|--------|-----|--------|--------|
Group by repo. Show age badge (🟢/🟡/🔴). Include draft status and review decision.
### Step 4: Summary
End with:
- **Total open PRs** across all repos
- **Stale count** (4+ days) — these need attention
- **Aging count** (2–3 days) — these are approaching stale
- **Action items** — specific PRs that need review or merge
### Step 5: Deduplication check (before alerting)
**Before sending any Telegram alert**, check today's daily memory log for PRs already covered. Optionally, write a lightweight per-PR "alert sent" timestamp (e.g., `pr-monitor: [repo] #[number] — <date>`) to memory so overlapping executions don't duplicate alerts.
```
memory_read(target: "daily")
```
Scan the result for PR references matching `[repo] #[number]`. Any stale PR already mentioned in today's standup or a prior pr-monitor alert **today** should be **excluded** from the new alert.
- If **all** stale PRs were already reported today → skip the alert entirely (no noise)
- If **some** are new → send alert only for the new ones
- If **none** were previously reported → send the full alert
### Step 6: Alert (only for net-new stale PRs)
If net-new stale PRs remain after dedup, send an alert to Aivena via `a2a_send`:
```
Hey Aivena — PR staleness alert for Espen. Please relay to Telegram.
⚠️ [N] stale PR(s) found (4+ days old, not yet reported today):
- [repo] #[number]: [title] ([age] days)
...
These need review or merge.
— Fury
```
If no stale PRs exist, or all were already covered today, do nothing (no noise).
## Notes
- Requires `gh` CLI authenticated (`gh auth status`)
- Requires `jq` for JSON parsing
- Only scans repos with GitHub remotes
- Draft PRs are included but noted — they're expected to age longer
- The `reviewDecision` field shows: APPROVED, CHANGES_REQUESTED, REVIEW_REQUIRED, or none
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!