Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/Morrison-Lab/ai-config --skill pr-status-all명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
| name | pr-status-all |
| description | Summarize all open PR statuses. |
| user-invocable | true |
| allowed-tools | ["Bash","Agent"] |
Produce a one-row-per-PR status table for all open PRs. This is the
whole-queue version of pr-status: apply the same
"read the latest review and parse it for findings" discipline to every
open PR, then lay the results out as a table. It is read-only --- it reports
status, it does not push, merge, or run review loops (use
ardia for that, or
sync-pr-branch to update a branch).
Because the per-PR signals are independent and read-only, gather them concurrently --- one subagent per PR --- then assemble the table. See Why fan-out is safe here for why this loop parallelizes and the write-loops don't.
gh pr checks <N> going green is about CI state, not the review verdict. A
PR can have every check passing and still carry unaddressed review findings.
Report CI state and review verdict as separate columns --- never collapse
them into one "OK".
GitHub's mergeable / mergeStateStatus: CLEAN field is conflict existence, not a merge-readiness verdict --- don't report it as one in the status table.
A PR without a clean review verdict on the latest commit is not merge-ready.
Do not describe it as merge-ready.
gh pr list --state open --json number,title,headRefName,isDraft,author \
--jq '.[] | "\(.number)\t\(.headRefName)\t\(.isDraft)\t\(.author.login)\t\(.title)"' # LIST_PRS
This is fast and sequential --- a single call to get the work units.
Spawn one subagent per open PR, all in a single batch (multiple Agent
calls in one message) so they run at once. The fan-out is read-only, so it
needs no worktrees --- each subagent only reads PR signals, nothing mutates,
and there is nothing to collide on.
Safety Cap: If Step 1 returns more than 10 open PRs, do not fan out per-PR subagents.
Unbounded concurrent subagents would exhaust the session's token quota.
Instead, build a condensed table straight from Step 1's own fields (title, headRefName, isDraft, author) plus one cheap orchestrator-level gh pr checks pass per PR for CI state --- skip the seven-signal subagent depth (review currency, external-reviewer check, thread counts, behind-main) entirely.
Label the table's heading "Condensed --- queue too large for full per-PR review (N open PRs)" so it reads as lower-fidelity rather than as the standard dashboard, and note that a full audit is available on a smaller subset or via ardia.
Give each subagent its PR number, headRefName, and isDraft, and have it gather the seven independent signals below and return one structured row.
Carry the disciplines into the prompt --- a subagent that doesn't follow Read the LATEST review will silently misreport:
A subagent starts fresh --- it sees only this prompt, not this skill file ---
so inline the exact commands; don't point it at a section it can't read.
Fill in <N>, <headRefName>, <isDraft>, <owner>, <repo> for each PR (resolve owner/repo once with gh repo view --json owner,name --jq '"\(.owner.login)/\(.name)"'):
Gather the status of PR # (branch
<headRefName>, draft:<isDraft>) in this repo and return a single structured row. Do not push, merge, or modify anything.
- Latest review verdict, checked for currency against the head, with hyperlinked comment URL. Read the most recent review comment (including its URL for hyperlinking), author, requested reviewers, and the timestamp of the latest commit:
This fetches more thangh pr view "<N>" --json comments,commits,headRefOid,author,reviewRequests \ --jq '{ author: .author.login, reviewRequests: [.reviewRequests[].login], review: ([.comments[] | select(.author.login | startswith("claude"))] | last | {url: .url, body: .body, createdAt: .createdAt}), lastCommitDate: (.commits[-1].committedDate), headRefOid: .headRefOid }'READ_PR_COMMENTSmaps to --tool-mappings.md's entry for that token is a comments-only MCP call, which returns neithercommitsnorheadRefOid. In a remote/MCP session withoutgh, fetch those fields with separate calls rather than assuming the token mapping covers this expanded query. The reviewer login varies by setup:gh pr viewreportsclaude; the REST API reportsclaude[bot].startswith("claude")matches both. If.reviewisnull, the reviewer may post asgithub-actions[bot]or another login -- never report "clean"; broaden the filter or say no review was found. If.review.createdAtis earlier than.lastCommitDate, the review predates the latest push -- report[⏳ In-Flight / Stale](url)(orin-flight), not the review body's verdict, regardless of what it says (both are ISO 8601 UTC timestamps, so a plain string comparison works). When the review body names the commit it reviewed (the@claudebot commonly writes "commit<sha>"), cross-check that mentioned SHA's prefix against.headRefOid(part of the same call above) as a corroborating signal. Treat a mismatch as[⏳ In-Flight](url)even if the timing check alone would have said . -- report (not ) instead, since is the commit's local committer timestamp, not when GitHub received the push, and a commit authored earlier but pushed later can pass the timing check while still being newer than the review. Only once the review postdates the last commit a named SHA matches -- unconditionally, with no SHA named meaning , not , full stop -- apply the bar for : "Looks good" / "no findings" / "approved" with zero follow-on bullets under any heading, hyperlinked as or . A rebuttal the reviewer still disputes is (), not clean.
Collect the rows the subagents return and pair each with the title, headRefName, and isDraft the orchestrator already has from step 1 (the subagent doesn't re-fetch these), then render the table + per-PR findings list (see Output) --- marking draft PRs clearly (e.g. [#<N>](url) (Draft)).
If subagent fan-out is unavailable (no Agent tool in the session), fall back to gathering the seven signals in series -- loop the exact same per-PR gather (items 1-7 above, including the currency check, thread resolution, behind-main check, and the human CHANGES_REQUESTED check) over each PR from step 1.
The output is the same; it is just sequential.
Don't substitute a simplified comments-only query here -- that would silently drop the current-head, thread-resolution, and human-review guarantees the rest of this skill relies on.
A Markdown table, one row per open PR, with these columns:
| PR | Author | AI Review Verdict | CI State | Reviewers Requested | Next Step |
|---|---|---|---|---|---|
| #101 | the repository owner | ✅ Approved (Round 3) | 🟢 All Green | Self-authored (GitHub prevents requesting review from author) | Ready for self-merge |
| #102 | external-dev | ✅ Clean (Round 2) | 🟢 All Green | the repository owner | Ready for human review |
| #103 | external-dev | ✅ Clean (Round 1) | 🟢 All Green | ⚠️ None (Request human review) | Request human review |
| #104 | external-dev | ❌ Needs Work (Round 1) | 🟢 All Green | - (AI review in progress) | Drive to clean (ARDI) |
| #105 (Draft) | external-dev | - | ⏳ Pending (build) | - | Draft (Work in progress) |
[#<N>](https://github.com/<owner>/<repo>/pull/<N>), appended with (Draft) if isDraft is true.[✅ Clean (Round N)](https://github.com/...#issuecomment-...)).
Verified current with the latest commit (.createdAt >= .lastCommitDate and matching commit SHA).
If the review predates the latest push, display [⏳ In-Flight / Stale](url).
If no SHA is named, display [⚠️ Unverified](url).🟢 All Green / ❌ Failing (<name>) / ⏳ Pending (<name>).copilot-review-before-human.md.
If human review has requested changes, flag ❌ Changes requested by <login>.
For self-authored PRs, note *Self-authored*.
When AI review is clean and CI is green, list requested reviewers (e.g. the repository owner) or flag ⚠️ None (Request human review).
When AI review is clean but CI is failing or pending, display - (CI in progress / failing).
When AI review is in-flight or unclean, display - (AI review in progress).isDraft: Draft (Work in progress).CHANGES_REQUESTED is pending: Blocked on human changes (<login>) (overrides everything below).Resolve conflicts / Sync main (<N> commits behind).Fix CI (<failing-check>).Resolve inline threads (<N> open).Drive to clean (ARDI).In-flight AI review.Wait for CI (<pending-check>).When detailed git/thread metrics are needed, include the extended columns:
| PR | Title | Branch | CI | Review | External | Human | Threads | Behind main | Next Step |
|---|
Below the table, list each PR's open findings briefly (or "none"), and call out anything needing action: branches behind main, failing CI, drafts, reviews that returned null, or a pending human review.
Do not label a PR "ready to merge" or "merge-ready" unless it is fully clean --- Human is none (a blocking human review overrides everything below) and at least one of Review or External is clean at the current head and neither one has open findings and all CI workflows are green and it's not behind main and every inline review thread is resolved.
Never hedge with "ready except for one nit."
This loop parallelizes because its units are independent and side-effect-free --- each PR's signals are read-only and don't depend on any other PR. The whole-queue write loops are different, and deliberately stay (mostly) series:
ardia / iterate-all --- share one working directory, compete for CI
runner capacity, and have human checkpoints. Parallelize only opt-in, with
worktree isolation + bounded concurrency --- not by default.gii / gia --- intentionally sequential: a later issue's base branch
depends on whether the prior MR merged, and same-file issues conflict.
gip is the opt-in exception --- it fans out only the provably
independent subset (no stacking dependency, no file overlap), each subagent
in its own worktree, and sends everything else back through gii.Rule of thumb: fan out a whole-queue loop only when its units are provably independent and don't mutate shared state --- like this one.
pr-status-all.rationale.md.pr-status --- the single-PR version; this applies its latest-review-only /
null-not-clean discipline across the whole open-PR queue. (pr-status :
pr-status-all :: ardi : ardia.)ardia / iterate-all --- the write counterpart: actually drive every
open PR to clean. This skill only reports; see Why fan-out is safe here for
why those loops stay series.sync-pr-branch --- offered for any PR the table flags as behind main.scripts/pr-sweep.py -- the cheap deterministic sweep that says which
PRs this dashboard should be pointed at.
It answers one narrower question ("which open PRs are stalled right now")
across several repos in one GraphQL call, with a wall-clock staleness
threshold this skill has no equivalent of.
This skill then supplies the per-PR depth it deliberately omits.
See derive-dont-enumerate.scripts/pr-overlap.py -- the same sweep over pairs rather than over PRs: which open PRs share a file, and which share none.
Reach for it whenever this dashboard's rows are about to be merged, since "collides" is a property of the pair and no per-PR column can carry it.
It separates an identical file set (a duplicate to close) from a partial overlap (an order to pick), and reports pairs examined alongside pairs colliding.
See batch-merge-and-resolve.clean[⚠️ Unverified](url)cleancommittedDate[⚠️ Unverified](url)cleanclean[✅ Clean (Round N)](url)[✅ Approved](url)[❌ Needs Work (Round N)](url)@claude bot only;
a formal review (Copilot's or a human's) is a separate object it won't show.
This step only inspects existing reviews (Copilot's or a human's) -- it never POSTs a review request.
Requesting a review is a mutation (triggers a review job, consumes quota, can collide with a concurrent ardi loop), which breaks this skill's whole justification for fanning out subagents concurrently (read-only, side-effect-free).
If no genuine verdict already exists at the current head, report that fact -- don't try to produce one; that's ardi's job.
set -o pipefail
head="$(gh pr view "<N>" --json headRefOid -q .headRefOid)"
review_id="$(gh api "repos/<owner>/<repo>/pulls/<N>/reviews" --paginate \
| jq -s --arg h "$head" \
'[.[][] | select(.user.login=="copilot-pull-request-reviewer[bot]" and .commit_id==$h)] | last | .id')"
if [ -n "$review_id" ] && [ "$review_id" != "null" ]; then
gh api "repos/<owner>/<repo>/pulls/<N>/reviews/$review_id" --jq '{state, body}'
gh api "repos/<owner>/<repo>/pulls/<N>/comments" --paginate \
| jq -s --arg rid "$review_id" \
'[.[][] | select(.pull_request_review_id == ($rid | tonumber))] | .[] | {line: (.line // .original_line), body}'
else
echo "no Copilot review exists at the current head"
fi
Clean requires three things: an affirmative zero-new-findings overview (e.g. "generated no new comments" -- never a literally empty body), zero matched inline comments, and no suppression block in the body.
Match inside the <summary> heading, case-insensitively on suppressed -- not on either exact phrase, and not anywhere in the body.
A stub-like non-answer ("ineligible", "reached their quota limit") is not a verdict either.
A human's formal review at the current head counts as an external verdict too -- check for one whenever the Copilot half found no clean verdict, before settling on no verdict at head:
set -o pipefail
head="$(gh pr view "<N>" --json headRefOid -q .headRefOid)"
gh api "repos/<owner>/<repo>/pulls/<N>/reviews" --paginate \
| jq -s --arg h "$head" \
'[.[][] | select(.user.type == "User" and .commit_id == $h
and .state != "DISMISSED")]
| group_by(.user.login)
| map(sort_by(.submitted_at) | last
| {id, login: .user.login, state, submitted_at})'
The head= line is repeated deliberately so this block is self-contained: shell state does not persist across separate Bash invocations, and a subagent that runs each fence as its own call would otherwise pass an empty $h that matches no review's commit_id -- a silent [] every time.
The .state != "DISMISSED" exclusion is load-bearing: GitHub's dismiss action flips the review's own state in place rather than adding a new review, and retracts neither its body nor its inline threads.
The group_by(.user.login) reduces per reviewer before taking each one's latest: two humans can review the same head, and a bare | last over the combined list would let a later clean "LGTM" from one reviewer silently drop an earlier reviewer's body-only findings.
Filter on .user.type == "User", not on a login list -- a bot's REST user object carries type: "Bot", so the type field needs no bot-login blocklist (measured 2026-08-15).
Judge each matched review by substance, not state: 106 of 106 formal reviews across 60 merged PRs on this repo are COMMENTED, zero APPROVED (measured 2026-07-30 on #668).
Fetch each matched review's body and inline comments.
An affirmative zero-findings read across every matched review means a genuine external verdict at the head.
Findings in any of them mean N open.gh pr checks <N> (PR_CHECKS); report 🟢 All Green or ❌ Failing (<check-name>) or ⏳ Pending (<check-name>)..author.login, .reviewRequests, and human review status.
❌ Changes requested by <login>..author.login is the current user / repo owner (the repository owner), report *Self-authored* (GitHub prevents requesting review from author).the repository owner), report the repository owner.reviewRequests is empty, report ⚠️ None (Request human review).- (CI in progress / failing).- (AI review in progress).READ_PR_REVIEW_COMMENTS).
gh api graphql -f query='query {
repository(owner:"<owner>", name:"<repo>") {
pullRequest(number:<N>) {
reviewThreads(first:100) {
totalCount
nodes { isResolved }
}
}
}
}' --jq '.data.repository.pullRequest.reviewThreads as $rt |
($rt.nodes | map(select(.isResolved | not)) | length) as $open |
if $rt.totalCount > ($rt.nodes | length)
then "\($open)+ open (cap)"
else if $open == 0 then "resolved" else "\($open) open" end
end'
git fetch origin main <headRefName> -q && git rev-list --count origin/<headRefName>..origin/main.CHANGES_REQUESTED (READ_PR_REVIEWS):
gh pr view "<N>" --json reviews \
--jq '[.reviews[] | select(.author.login != null and (.state == "APPROVED" or .state == "CHANGES_REQUESTED" or .state == "DISMISSED"))] | group_by(.author.login) | map(sort_by(.submittedAt) | last) | [.[] | select(.state == "CHANGES_REQUESTED") | .author.login]'
Filter to only APPROVED/CHANGES_REQUESTED/DISMISSED states before reducing to each author's latest review -- reducing over all states first lets a later COMMENTED round hide an earlier CHANGES_REQUESTED (verified with synthetic fixtures).
Keep DISMISSED in the filter so an explicit dismissal clears an older CHANGES_REQUESTED.
Any non-empty result blocks regardless of what any bot says -- report changes requested by <login>.Return: PR number, Author, isDraft, AI Review ([✅ Clean (Round N)](url) / [⏳ In-Flight](url) / [⚠️ Unverified](url) / [❌ Needs Work](url) / none found), External Review (clean / N open / no verdict at head), Human Blocked (none / changes requested by <login>), CI State (🟢 All Green / ❌ Failing (<name>) / ⏳ Pending (<name>)), Reviewers Requested (the repository owner / *Self-authored* / ⚠️ None / ❌ Changes requested by <login> / - (CI in progress / failing) / - (AI review in progress)), Threads (resolved / N open), Behind-main (up to date / N commits), Next Step (computed per the deterministic transition rules).
Confirm review (no verified verdict at head)Author is the repository owner (self-authored): Ready for self-merge.Author is external and human review is requested (the repository owner): Ready for human review.Author is external and human review is not yet requested: Request human review.