name: check-open-prs description: Personal PR inbox for LI.FI engineers — covers both outgoing PRs (yours) and incoming review queue (others'). Produces a single dry-run dashboard with (a) your OWN open PRs cross-referenced with their #dev-sc-review / #dev-backend-expansion-review Slack threads, split into REMIND-team / YOUR-ACTION / OWN-DRAFTS / STALE, and (b) OTHER developers' PRs posted in those channels that need your attention — unreviewed (potential pick up) or you-reviewed-and-dev-addressed (potential re-review). Always dry-run first with explicit approval gates before any Slack post or code change. Optional parallel sub-agent investigation per PR. Use when the user says "check open PRs", "check my open PRs", "PR triage", "what PRs need me", "review queue", "where do my PRs stand", "/check-open-PRs", or asks for a personal PR dashboard.
Check Open PRs
A personal PR inbox: triage your own open PRs (where you're waiting on the team vs the team's waiting on you) and the team's open PRs in the review channels (where you can pick up an unreviewed one, or re-review one you already commented on). Always dry-run first; act only on explicit approval.
Inputs (defaults)
- Author — current
ghuser (gh api user --jq '.login'). - Lookback — none for your own PRs (all open ones, regardless of age). 6 weeks of channel history for the incoming-review queue.
- State —
open. Drafts go into their own bucket, not "excluded". - Stale threshold — created ≥6 weeks ago AND last activity ≥2 weeks ago. Stale PRs get a separate disposition section.
- Reminder cooldown — 48 hours since your last message in a thread. Under that, the PR is "recently pinged" and skipped from auto-bumps.
Hard invariants
Violations are bugs — stop, fix, re-run.
- Never estimate a Slack timestamp. Every cooldown / "hours ago" figure must come from an actual
Message TSon the actual last reply by the current user. Threads routinely have multi-day-later follow-ups ("ready for re-review" two days after the parent), so parent-post ts is not a substitute. - Always read threads with
response_format="detailed". Concise format strips per-reply timestamps. If a fetch came back concise, re-fetch before classifying. - Scan every reply. Find the most recent message by the current user (for cooldown) and the most recent message overall (for REMIND vs YOUR-ACTION classification). Don't infer from
reply_countor assume reply 1 is the only one.
Surface a one-line audit at the end of Phase 1 with the exact Message TS used per PR, so the user can spot-check:
Audit: #1806 cooldown ts=1779065876.814329 (2026-05-18 07:57:56 +07, 0.4h ago) → RECENTLY-PINGED
Phase 1 — Dry-run (read-only)
1.1 Pull open PRs
gh search prs --author=@me --state=open --limit 200 \
--owner lifinance --owner lifinance-tron \
--json url,title,number,repository,createdAt,updatedAt,isDraft
No --created filter — pull ALL open PRs (keeping a clean slate is part of the job).
Scope to LI.FI orgs. --owner lifinance --owner lifinance-tron restricts results to LI.FI repos; without it, --author=@me returns every open PR on GitHub (personal forks, OSS contributions, etc.). Extend the --owner list if LI.FI publishes PRs under additional orgs.
Drop archived-repo PRs. gh search prs returns PRs in archived repositories; those can't be merged or acted on. For each unique repository.nameWithOwner, check once:
gh api repos/<owner>/<repo> --jq '.archived'
Cache per-repo (one call per repo). Add the dropped count to the Excluded section as Archived repo: N (repos: …) so the scan stays auditable.
Split by age:
- Active —
createdAtwithin last 6w ORupdatedAtwithin last 2w. Goes through the REMIND / YOUR-ACTION / OWN-DRAFTS flow. - Stale —
createdAt≥6w ago ANDupdatedAt≥2w ago. Goes to its own STALE bucket for human disposition.
Route to a Slack channel by repo:
lifinance/contracts,lifinance/contracts-tron→#dev-sc-review(C088UJWC8PR).lifinance/lifi-backend,lifinance/tenderly-sim, other backend services →#dev-backend-expansion-review(resolve viaslack_search_channels).- Other repos (
lifi-claude-plugins,lifi-team-skills, …) → no channel; mark "no review thread expected".
1.2 Classify your own non-draft PRs (REMIND / YOUR-ACTION)
For each non-draft, non-stale active PR:
Find the Slack post. Search the routed channel:
in:#<channel> pull/<NUMBER> from:<@<current-user-slack-id>>If no result, mark "not posted for review yet" and surface in the dashboard so the user can decide whether to post.
Read the thread (
response_format="detailed"). Walk every reply.- Last author is the current user → REMIND. Compute
hours_since = (now - your_last_ts) / 3600.<48h→ REMIND-RECENTLY-PINGED (skip Phase 2A; spam guard).≥48h→ REMIND-DUE (eligible for Phase 2A).
- Last author is anyone else → YOUR-ACTION.
- Zero replies → REMIND, applying the 48h rule to the parent post ts.
- Last author is the current user → REMIND. Compute
Edge cases:
- A re-ping by the user counts as the user's last message; the 48h cooldown resets from there.
- A bot/Linear/automation reply doesn't count — fall back to the previous human message.
1.3 Classify your own draft PRs (OWN-DRAFTS)
Drafts aren't posted for review, but they're still work-in-progress that you owe yourself. Don't dump them into "Excluded".
For each active draft (stale drafts roll into STALE):
gh api repos/<owner>/<repo>/pulls/<n>/commits --jq '.[-1].commit.committer.date'
gh pr view <n> --json statusCheckRollup
Classify into a likely-disposition bucket:
- READY-TO-FLIP — CI green, last commit <2w, looks complete → suggest "mark Ready for Review + run
/post-pr-for-review". - NEEDS-WORK — CI failing, or title tagged
(WIP)→ suggest "finish the WIP". - DORMANT — last commit ≥4w ago → suggest "decide: resume / convert to issue / close".
- SYNC-PR — title matches
chore(claude): sync …orchore(skills): sync …(auto-sync output) → suggest "check if the sync target has since been re-synced (PR may be obsolete) or merge".
1.4 Classify others' PRs in the review channel (incoming inbox)
This is the team-inbox half. Scan the same channel(s) for PRs not posted by the current user.
List PR-post messages in
#dev-sc-reviewfrom the last 6 weeks (paginate as needed). Each post follows the<URL> << <title>convention. Extract(owner, repo, pr_number, parent_ts, posted_at, slack_author).Drop entries where
slack_author == current_user(already covered in 1.2).For each remaining PR, fetch state in parallel:
gh pr view <n> --repo <owner>/<repo> --json \ author,state,isDraft,reviewDecision,reviews,latestReviews,commits,updatedAt,mergeableDrop
state != "OPEN"orisDraft == true.Reduce against the current user (
me):my_last_review= latestreviewsentry whereauthor.login == me(capturestate,submittedAt).any_human_review= any review by a real user (exclude bots:coderabbitai,github-actions,*-bot,app/*).last_commit_ts= latestcommits[].commit.committedDate.Slack thread state — read with
response_format="detailed"(mandatory; re-review signals live in Slack, not GitHub). Capture latest author + ts. Detect a re-review signal in any message authored by the PR author aftermy_last_review.submittedAt, case-insensitive regex:ready (for|to) re-?review | please re.?review | addressed | updated.*PR | PTAL | fixed @ | all comments addressed | rebasedIf the re-review ping mentions someone else (
@<other>) but my prior review is still open (COMMENTED / CHANGES_REQUESTED with no follow-up from me), still classify as INBOX-REREVIEW and flag the primary-reviewer ambiguity in the dashboard row.
Bucket Condition INBOX-UNREVIEWED my_last_reviewis null ANDany_human_reviewis false — no human has reviewed yetINBOX-REREVIEW my_last_review.state ∈ {COMMENTED, CHANGES_REQUESTED}AND (last_commit_ts > my_last_review.submittedAtOR slack re-review signal after my review)WAITING-ON-OTHERS any_human_reviewtrue butmy_last_reviewnull — someone else owns itDONE-BY-ME my_last_review.state == APPROVEDOR my review exists with no new activity sinceOnly INBOX-UNREVIEWED and INBOX-REREVIEW appear in the dashboard. Drop the others; note their counts in the summary so the user knows the scan was exhaustive.
Sort INBOX-UNREVIEWED by
posted_atascending (oldest first = most painful for the author). Sort INBOX-REREVIEW bymy_last_review.submittedAtascending (oldest open feedback first).
1.5 Present the dry-run report
Sections in this order:
🔔 REMIND-DUE — last message from you, ≥48h ago → ready to bump
| PR | Title | Last from you | Age |
|---|
⏳ REMIND-RECENTLY-PINGED — last from you, <48h → skip (cooldown)
| PR | Title | Last from you | Hours ago |
|---|
👀 YOUR-ACTION — team replied last → you need to respond
| PR | Title | Last message (≤120 chars, with author) | Notes |
|---|
📝 OWN-DRAFTS — your WIP, not yet posted for review
| PR | Title | Last commit | CI | Bucket | Suggested next step |
|---|
🧹 STALE — created >6w ago, no activity in 2+ weeks → needs disposition
| PR | Title | Age | Last activity | Bucket | Suggested disposition |
|---|
Stale buckets: WIP/abandoned, posted-but-ignored, reviewed-stuck-on-you, reviewed-stuck-on-team.
📥 INCOMING-UNREVIEWED — others' PRs with no human review yet → potential pick up
| PR | Author | Title | Posted | Age in channel |
|---|
🔁 INCOMING-REREVIEW — you reviewed, dev addressed → potential re-review
| PR | Author | Title | Your last review | New commits since | Re-review signal |
|---|
Excluded — other-repo PRs without a configured channel, plus Archived repo: N (repos: …).
Counts — WAITING-ON-OTHERS: N · DONE-BY-ME: N (silently-dropped buckets, for auditability).
End with the action menu:
Proceed?
(a) Bump REMIND-DUE threads
(b) Investigate YOUR-ACTION PRs with parallel sub-agents
(c) Walk through OWN-DRAFTS (decide: flip / finish / close)
(d) Walk through STALE PRs (decide: close / keep / hand off)
(e) Open INCOMING-UNREVIEWED for review (browser or sub-agent triage)
(f) Open INCOMING-REREVIEW with diff-since-my-review
(g) All of the above
(h) Cancel
STOP. No actions without explicit approval.
Phase 2A — Bump REMIND-DUE threads (only if (a) or (g))
Eligibility is REMIND-DUE only. RECENTLY-PINGED is skipped — Phase 1 already filtered it; do not re-check here.
Post one thread reply per PR via slack_send_message with thread_ts=<parent_ts>. Exact message text — do not paraphrase:
friendly bump <!subteam^S096X6MCB0C>
Renders as friendly bump @smartcontract_core. The <!subteam^…> syntax is mandatory — plain @smartcontract_core does not trigger notifications (verified 2026-05-13). For the backend channel, swap to that channel's subteam ID or skip the tag per channel convention.
Confirm each post with its permalink.
Phase 2B — Walk through OWN-DRAFTS (only if (c) or (g))
For each draft, ask once (or in a small batch):
PR #<n> — <title> (last commit <X>w ago, CI <status>)
Bucket: <READY-TO-FLIP / NEEDS-WORK / DORMANT / SYNC-PR>
What would you like to do?
(1) Flip to Ready for Review + post via /post-pr-for-review
(2) Continue work locally (no action now)
(3) Close (with reason)
(4) Investigate first (defer to Phase 3)
(5) Skip for now
Execute the chosen disposition. For (1): gh pr ready <n> then invoke post-pr-for-review. For (3): gh pr close --comment "<reason>".
Phase 2C — Walk through STALE (only if (d) or (g))
For each STALE PR:
PR #<n> — <title> (age <X>w, last activity <Y>w ago)
Bucket: <bucket>
What would you like to do?
(1) Close (with one-line reason)
(2) Mark "still working" — post "Still active — target date: <D>" comment
(3) Hand off / re-assign (you'll name the assignee)
(4) Convert to draft if not already
(5) Investigate first (defer to Phase 3)
(6) Skip
Execute. (1) gh pr close --comment. (3) gh pr edit --add-assignee. (4) gh pr ready --undo.
Phase 2D — Incoming review queue (only if (e), (f), or (g))
For INCOMING-UNREVIEWED:
- Show
gh pr view <n> --weblink + a 2-line preview (changed-files count, primary language, linked issue/ticket from PR body). - Per-PR offer: (i) open in browser, (ii) spawn sub-agent for a ≤200-word triage summary (what changed, risk level, next step), (iii) skip.
- If (ii), use the Phase 3 sub-agent template with this orientation: "The user has not reviewed this yet. Give them the minimum context to decide whether to review now, defer, or hand off."
For INCOMING-REREVIEW:
Fetch diff since my last review:
gh pr view <n> --json reviews \ --jq '.reviews[] | select(.author.login=="<me>") | .commit.oid' | tail -1 gh pr diff <n> --color=never --commit-range <last_review_sha>..HEADShow filename list + line-count delta; quote the dev's "ready for re-review" message from the Slack thread; list my still-open inline review comments:
gh api repos/<o>/<r>/pulls/<n>/commentsfiltered by
user.login == meANDin_reply_to_id == null.Per-PR offer: (i) open
--weblink to the "Files changed since" view, (ii) spawn sub-agent to verify the dev addressed each of my original comments and report mismatches, (iii) skip.
Never auto-approve, auto-comment, or auto-request-changes. The skill routes; the human reviews.
Phase 3 — Sub-agent investigation (only if (b) or (g), or option (5) from Phase 2B/2C)
Dispatch one sub-agent per PR, in parallel. Each sub-agent:
gh pr view <n> --repo <owner>/<repo> --json reviewDecision,reviews,comments,statusCheckRollup,mergeable,mergeStateStatusgh api repos/<owner>/<repo>/pulls/<n>/comments(inline review comments)gh api repos/<owner>/<repo>/issues/<n>/comments(PR discussion)- Read the Slack thread (orchestrator passes it in — sub-agent has no other context).
- Identify (a) what the team is blocking on, (b) which comments are unresolved, (c) ONE concrete next step.
- Report <300 words in the format below.
Sub-agent prompt template (self-contained):
You are investigating LI.FI PR <URL>. The user is the author. The team posted comments after the user's last reply; the user needs to decide what to do next.
1. Pull GitHub review state with the `gh` commands above.
2. Read this Slack thread: <paste detailed thread text>.
3. Summarise what the team is asking for and propose ONE concrete next step.
Be terse. Report under 300 words:
## PR #<n>
### Findings
- ...
### Suggested action
- ...
### Confidence: high | medium | low
Orchestrator consolidates all reports:
## Sub-agent findings — N PRs investigated
<each PR's findings>
Next?
(1) Implement the suggested actions one PR at a time (interactive)
(2) Implement all in parallel via sub-agents
(3) Just give me the list; I'll act manually
(4) Done
STOP. No code changes without per-PR approval.
Phase 4 — Implementation (only if (1) or (2))
Per PR:
gh pr checkout <n>in the right repo.- Apply changes per the suggested action.
- Run lints/tests locally.
- Push.
- Reply in the Slack thread linking the new commit and re-requesting review.
Do not invoke /pr-ready here — that's the pre-create gate, not the per-iteration gate. Re-request review via gh pr edit --add-reviewer or just push and ping.
What this skill never does
- Touches closed or merged PRs.
- Auto-reminds on STALE PRs (always a human call).
- Re-pings inside the 48h cooldown window.
- Pushes code without per-PR approval in Phase 4.
- Submits a code review on the user's behalf — no auto-approve, auto-comment, auto-request-changes. The incoming queue is read-only routing; the human reviews.