Post Draft Review
agent-substrate/substrate
Posts pull request review findings as GitHub draft (pending) inline comments for a human to edit and submit, instead of publishing them straight to the PR author.
Review open PRs two ways - default is a per-PR deep review with severity-tagged findings, inline comments, and a verdict; --survey runs a risk-tiered triage digest of what's safe to merge first
$ npx skills add aeonfun/aeon --skill pr-review -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install aeonfun/aeon pr-review --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/aeonfun/aeon.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/pr-review .claude/skills/pr-review && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "pr-review" agent skill from https://github.com/aeonfun/aeon/tree/main/skills/pr-review into .claude/skills/pr-review/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "pr-review", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/aeonfun/aeon/tree/main/skills/pr-reviewType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add aeonfun/aeon --skill pr-review -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install aeonfun/aeon pr-review --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/aeonfun/aeon.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/pr-review .agents/skills/pr-review && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "pr-review" agent skill from https://github.com/aeonfun/aeon/tree/main/skills/pr-review into .agents/skills/pr-review/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "pr-review", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add aeonfun/aeon --skill pr-review -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install aeonfun/aeon pr-review --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/aeonfun/aeon.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/pr-review .cursor/skills/pr-review && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "pr-review" agent skill from https://github.com/aeonfun/aeon/tree/main/skills/pr-review into .cursor/skills/pr-review/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "pr-review", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/aeonfun/aeon.git --path skills/pr-review--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add aeonfun/aeon --skill pr-review -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install aeonfun/aeon pr-review --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/aeonfun/aeon.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/pr-review .gemini/skills/pr-review && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "pr-review" agent skill from https://github.com/aeonfun/aeon/tree/main/skills/pr-review into .gemini/skills/pr-review/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "pr-review", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install aeonfun/aeon pr-reviewInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add aeonfun/aeon --skill pr-review -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/aeonfun/aeon.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/pr-review .github/skills/pr-review && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "pr-review" agent skill from https://github.com/aeonfun/aeon/tree/main/skills/pr-review into .github/skills/pr-review/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "pr-review", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add aeonfun/aeon --skill pr-review -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install aeonfun/aeon pr-review --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/aeonfun/aeon.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/pr-review .opencode/skills/pr-review && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "pr-review" agent skill from https://github.com/aeonfun/aeon/tree/main/skills/pr-review into .opencode/skills/pr-review/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "pr-review", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
pr-reviewReview open PRs two ways - default is a per-PR deep review with severity-tagged findings, inline comments, and a verdict; --survey runs a risk-tiered triage digest of what's safe to merge first
PR Review is an agent skill from aeonfun/aeon. Review open PRs two ways - default is a per-PR deep review with severity-tagged findings, inline comments, and a verdict; --survey runs a risk-tiered triage digest of what's safe to merge first
Its SKILL.md is about 8k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Development, covering Pull requests and Technical documentation. The repository describes itself as: The most autonomous AI agent framework: runs unattended on GitHub Actions, self-healing skills, drives Claude Code, Grok, Codex & more. No approval loops. Configure once, forget… The licence is MIT.
11 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit c0cb7c4. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
ghbashjqFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use gh, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
GH_TOKENGITHUB_TOKENFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
PR Review loads about 8k tokens when it runs. Until then it costs about 51 tokens; SKILL.md has 3,511 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from aeonfun/aeon at commit c0cb7c4, republished under its MIT licence (© aeonfun). 3,511 words, ~7,954 tokens.
.claude/skills/pr-review/SKILL.md (or your agent's skills folder).<!-- autoresearch: variation B — sharper output: severity-tagged & capped findings, inline comments on exact lines, one-line verdict; folds in skip rules (A) and SHA dedup + large-diff fallback (C). Absorbs pr-merge as the `--survey` risk-tiered triage-digest branch (no capability lost). -->
${var} — Selects the branch and scopes it.
- Default (no
--survey) → per-PR deep review.${var}empty reviews every repo inmemory/watched-repos.md;${var}=owner/reposcopes to one repo;${var}=owner/repo#Nscopes to that exact PR.--survey(aliassurvey) → risk-tiered triage digest (the formerpr-merge). In this branch the remaining tokens follow pr-merge's grammar: passdry-runto skip notify (article + state still write), passowner/repoto override the target repo, combine with a space (--survey dry-run owner/repo). Empty target =aeonfun/aeon.Examples: `` (review every watched repo) ·
owner/repo(review one repo) ·owner/repo#42(review exactly PR 42) ·--survey(triage digest of aeonfun/aeon) ·--survey dry-run(refresh digest, no notify) ·--survey owner/repo(triage a specific repo).
Read memory/MEMORY.md for high-level context. Scan the last ~3 days of memory/logs/ for recent activity and to avoid re-reporting the same signal.
Parse ${var} → branch: split ${var} on whitespace.
--survey or survey token is present → SURVEY branch (jump to "Survey branch"). Remove that token; the remaining tokens are parsed by the survey branch (dry-run, owner/repo override, unknown → BAD_VAR).${var} is an optional owner/repo or owner/repo#N scope (empty = every watched repo).The two branches never share mutation logic: the REVIEW branch posts PR comments/reviews via gh; the SURVEY branch writes the digest article + state file and (gated) notifies — neither performs an actual gh pr merge. Dispatch to exactly one branch per run.
Read memory/MEMORY.md and memory/watched-repos.md.
Read the last 2 days of memory/logs/ for context on recent reviews. Whether a commit was already reviewed is decided by GitHub, not the logs (see the skip rules): a run can post its review and then lose its log entry.
If ${var} names owner/repo#N, fetch and review only that exact open PR; do not list or comment on any other PR. If ${var} names owner/repo, review that repo's open PRs. Otherwise review every repo listed in memory/watched-repos.md.
If memory/watched-repos.md is empty or missing (and no owner/repo was passed), log PR_REVIEW_NO_REPOS and end.
Noise is the documented failure mode of automated PR review. Every finding emitted must be severity-tagged, line-specific, and justified with a one-sentence "why it matters". If there is nothing worth saying, say so in one line and move on.
gh pr list -R owner/repo --state open --limit 20 \
--json number,title,author,isDraft,labels,headRefOid,updatedAtSkip a PR if any of the following hold (record the skip reason for the run summary):
isDraft: true^(WIP|\[WIP\]|Draft:) (case-insensitive)no-review, do-not-merge, wip, or blocked[bot] (dependabot, renovate, etc.) or equals aeonframeworkheadRefOid. Ask GitHub:./scripts/dev-loop-review.sh reviewed owner/repo#NUMBER <headRefOid>0 → skip as dup-SHA; exit 2 (the reviews could not be read) → skip as review-check-unavailable and let the next run decide; exit 1 → not yet reviewed at this commit. It counts exactly what the dev-loop gate counts, so a second review can never make the gate see two receipts. It works on either token: with GH_GLOBAL it counts that account's reviews, and on the GITHUB_TOKEN fallback (which cannot read /user) it counts github-actions[bot], the login that token reviews as.coderabbitai, copilot-pull-request-reviewer, claude) posted a review in the last 30 min — skip to avoid piling on. Check via:gh api repos/owner/repo/pulls/NUMBER/reviews --jq '.[] | {user: .user.login, submitted_at}'Fetch context:
gh pr view NUMBER -R owner/repo \
--json title,body,headRefOid,baseRefName,files,additions,deletionsIf the body contains Fixes #N or Closes #N, fetch the linked issue for context:
gh issue view N -R owner/repo --json title,body,labelsFetch the diff:
gh pr diff NUMBER -R owner/repoadditions + deletions > 3000, review only the top-5 largest-delta files from the files array (not the full diff).gh pr diff fails, fall back to per-file patches:gh api repos/owner/repo/pulls/NUMBER/files --jq '.[] | {path, patch}'empty-diff.Early-exit for trivial PRs: if the diff is docs-only (.md/.rst/docs/**), lockfile-only, or test-only, skip deep review and post the 1-line ack form in step 6.
Review with severity tagging. Every finding must carry exactly one tag:
[CRITICAL] — correctness break, security hole, data loss, API break, regression[ISSUE] — likely bug, missing edge case, wrong behavior under a realistic input[NIT] — naming, style, minor cleanup (dropped by default)Rules:
path/to/file:LINE and include a one-sentence "why it matters" — the consequence, not just "this is wrong".Determine a verdict:
approve-ready — no CRITICAL, no ISSUEblocked: <one-phrase reason> — at least one CRITICALdiscussion-needed — ISSUE findings but no CRITICALPost the review. Send both a consolidated summary comment and inline line-specific comments — inline for precision, summary for consumers that parse review bodies.
For each line-specific finding:
gh api repos/owner/repo/pulls/NUMBER/comments \
-f body="[SEVERITY] finding text — why it matters" \
-f path="path/to/file" \
-f commit_id="$HEAD_SHA" \
-F line=LINE_NUMBER \
-f side="RIGHT"Then the consolidated summary as a review — include the verdict and a bulleted recap of every inline finding (severity + file:line + one-sentence rationale), so downstream body-parsers don't miss them:
gh pr review NUMBER -R owner/repo --comment --body "**Verdict**: <verdict>
<one-line rationale if blocked or discussion-needed; omit if approve-ready>Findings (mirrored as inline comments):
<!-- aeon-review:{"schema":1,"target":"owner/repo#N","sha":"<full-headRefOid>","verdict":"blocked|discussion-needed","critical":N,"issues":M} -->"
If there are no CRITICAL/ISSUE findings, skip inline comments and post the
approve-ready line followed by its receipt:
```text
**Verdict**: approve-ready — no blockers.
<!-- aeon-review:{"schema":1,"target":"owner/repo#N","sha":"<full-headRefOid>","verdict":"approve-ready","critical":0,"issues":0} --> Use exactly one receipt in every completed review, including trivial-PR
early-exits. The target and full headRefOid must match the PR fetched in
step 1, and the counts must equal the [CRITICAL] and [ISSUE] bullets in
the consolidated body. This is routing input for a later chain step. Do not
emit it only when the PR itself was skipped (draft, bot, or duplicate SHA),
and keep the human-readable blocked reason outside the receipt.
For trivial-PR early-exits (step 3), post the category line — Docs-only change — no blockers. / Dependency-bump — no review needed. / Test-only change — no production code touched. — followed by the same approve-ready receipt from above. A trivial PR is reviewed, not skipped; omitting its receipt makes the dev-loop fail closed because it cannot distinguish an approval from an incomplete review.
Fallback: if inline-comment creation fails (missing permissions, commit_id mismatch), consolidate all findings into the review body, preserving the severity tags and file:line refs. Do not silently drop findings.
The captured final output must include the same **Verdict** line and exact
<!-- aeon-review:{...} --> receipt posted to GitHub for every reviewed PR.
Do not replace them with a generic "review posted" summary. The chain consumes
the receipt, and the notification gate uses the verdict line as its signal.
Send one combined message per run via ./notify:
*PR Review — ${today}*
Reviewed N, skipped K (drafts: x, bots: y, dup-SHA: z, bot-reviewed-recently: w).
- owner/repo#123: [verdict] — N critical, M issuesTelegram summary. When the run was scoped to a single repo (${var}=owner/repo), send a Telegram summary of the review with ./notify -f review.md so the operator sees the verdict at a glance. Skip it on an all-repos run.
If every PR was skipped, do not notify — just log.
Log to memory/logs/${today}.md under the shared ### pr-review heading with a mode discriminator:
### pr-review
- **Mode**: review (per-PR deep review)
- owner/repo#123 (SHA abc1234): [verdict] — N critical, M issues
- Skipped: owner/repo#124 (draft), owner/repo#125 (bot-reviewed-recently)If no open PRs across all repos, log PR_REVIEW_OK and end.
--survey) — risk-tiered triage digest(This is the former pr-merge skill, folded in verbatim. It surveys the queue and buckets by blast radius; it does not merge anything — auto-merge owns the actual-merge action behind its own author-allowlist + size-cap + branch-protection policy. This branch is the decision-support layer that lives before auto-merge, sized for the much larger pool of PRs auto-merge's safety policy intentionally skips.)
Today is ${today}. The open-PR queue on aeonfun/aeon has crossed the threshold where a human reviewer working alone falls behind: yesterday (June 1) eighteen PRs were merged in a single 37-minute Monday catch-up window, but on every prior weekend day they stacked up untouched. As community skill packs become the primary contribution model and external contributors keep landing skill PRs every other day, the queue's steady-state size will keep climbing — skill-scan evaluates one inbound skill PR at a time, but no skill answers the operator's actual morning question: "of the N open PRs right now, which N1 can I merge in one click and which N2 need real review?"
This branch is that answer. It surveys every open PR on a target repo, categorises each by the files it touches, runs scripts/skill-scan.sh against every changed SKILL.md (the same scanner install-skill runs before installing a skill), and emits one structured Telegram digest with four risk buckets sorted by PR age. The operator can fire-and-forget the FAST_TRACK bucket, glance at SKILL_PASS, and budget real attention for INFRA_REVIEW + SKILL_WARN_OR_BLOCK + CORE_REVIEW.
Read memory/MEMORY.md for context.
Read the last 8 days of memory/logs/ for prior-run context (skip if dispatched).
Read soul/SOUL.md + soul/STYLE.md if populated to match voice in the notification.
| Skill | Scope | Action |
|---|---|---|
pr-triage | Per external PR, first-touch | Welcomes + labels + leaves a verdict comment |
install-skill | One skill being installed from another repo | Runs scripts/skill-scan.sh first; a HIGH finding blocks the install |
auto-merge | All bot-authored PRs that pass a strict safety policy | Merges if CLEAN |
pr-review --survey | All open PRs across the watched-repos queue | One operator-facing digest sorted by risk + age — no per-PR comment, no merge action |
The four compose. pr-triage runs once per PR open; install-skill scans each skill before it lands; auto-merge runs against the bot subset; this survey branch is the morning brief over everything else — the open backlog the operator still has to think about. Building a fifth verdict layer into any of the existing three would either bloat their per-PR cost or skip the operator-overview question entirely.
| Source | Purpose | Auth |
|---|---|---|
gh api repos/{repo}/pulls?state=open&per_page=100 --paginate | Open PR list with author, draft state, base ref, age, mergeable state, head SHA, statusCheckRollup summary, labels | GH_TOKEN |
gh api repos/{repo}/pulls/{N}/files?per_page=100 --paginate | Per-PR list of changed file paths + status (added/modified/removed) — the only signal we trust for bucketing | GH_TOKEN |
gh api repos/{repo}/contents/{path}?ref={head_sha} | Each changed SKILL.md body — fed to scan.sh for PASS/WARN/BLOCK verdict | GH_TOKEN |
scripts/skill-scan.sh (local) | Scanner reused verbatim (no fork, no shadow copy) — same source install-skill runs | Local script |
memory/watched-repos.md (local) | Read only the ## Trusted Authors section — those authors' PRs surface in a separate TRUSTED_AUTHOR row that bypasses the FAST_TRACK / CORE_REVIEW buckets | Local file |
No new secrets. GitHub access via gh CLI (GH_TOKEN) per CLAUDE.md.
Writes:
output/articles/pr-merge-${today}.md — full digest with one row per open PR, sortable by bucket + age (every non-error run, including QUIET)memory/topics/pr-merge-state.json — prior-run snapshot (per-PR bucket + first_seen date + last_head_sha, used to suppress re-notification on the same head SHA)memory/logs/${today}.md — one log block per run./notify — only when ≥1 new PR appeared in a non-FAST_TRACK bucket since the last run, or a SKILL_BLOCK / CORE_REVIEW PR is present and operator has not been notified about it on this head SHA yet, or it's the first (baseline) run (see step 8)mkdir -p memory/topics output/articles
[ -f memory/topics/pr-merge-state.json ] || cat > memory/topics/pr-merge-state.json <<'EOF'
{"last_run":null,"last_status":null,"last_repo":null,"prs":{}}
EOFIf jq empty fails on the state file (corrupt JSON from an aborted write), back it up to .bak, reset to the empty template, and tag the run STATE_CORRUPT. Continue — a fresh state file means re-notifying every currently-open PR as "new" on this one run, which is the safer post-corruption outcome than silently skipping a SKILL_BLOCK PR the operator hadn't seen yet.
prs is a map keyed by <owner>/<repo>#<number>: {bucket, scan_verdict, head_sha, first_seen, last_notified_head_sha, age_days, author, draft}. Cap to 200 most-recent entries (LRU by first_seen) so a long-lived state file can't grow unbounded.
The --survey/survey token has already been consumed by the shared preamble. Parse the remaining tokens:
dry-run, anything matching ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ (treated as REPO_OVERRIDE), anything else.PR_MERGE_QUEUE_BAD_VAR: ${var} and exit (no writes, no notify).DRY_RUN=yes if the dry-run token is present, else no (execute).TARGET_REPO=${REPO_OVERRIDE:-aeonfun/aeon}.gh api "repos/${TARGET_REPO}/pulls?state=open&per_page=100" --paginate \
--jq '[.[] | {number, title, user_login: .user.login, draft, base_ref: .base.ref, head_sha: .head.sha, created_at, updated_at, mergeable_state, labels: [.labels[].name]}]' \
> /tmp/pr-merge-prs.jsonIf gh api fails (non-zero exit) → log PR_MERGE_QUEUE_API_FAIL: pulls list, write a one-line notification (pr-review --survey: GitHub API failed listing open PRs for ${TARGET_REPO}), exit API_FAIL. The pulls endpoint is the floor — if it can't be read, every downstream step is meaningless.
Empty list → write the article + state with all buckets at zero, log PR_MERGE_QUEUE_EMPTY, skip notify (an empty queue is not news), exit EMPTY.
Bot/trusted-author short-circuit: build TRUSTED_LOGINS from the ## Trusted Authors section of memory/watched-repos.md (one login per - bullet). Always include the bot logins dependabot[bot], renovate[bot], github-actions[bot], plus aeonframework and aeonfun. PRs whose user_login is in this set get categorised TRUSTED_AUTHOR and bypass the file-bucket logic in step 4 (auto-merge handles them; surfacing them in the operator's risk view would only add noise).
For each PR (skip drafts and skip TRUSTED_AUTHOR PRs in this step — drafts are signals the contributor isn't done; trusted-author PRs route through auto-merge):
gh api "repos/${TARGET_REPO}/pulls/${PR_NUMBER}/files?per_page=100" --paginate \
--jq '[.[] | {filename, status}]' \
> "/tmp/pr-merge-files-${PR_NUMBER}.json"If the files endpoint fails for a single PR → tag the PR bucket=UNKNOWN, scan_verdict=files_api_fail, skip its scan but keep it in the digest as an UNKNOWN row at the bottom (silently dropping a PR from a triage digest is the failure mode this skill exists to prevent). Continue to the next PR.
Apply this rubric in order (first match wins). The rubric is conservative — when in doubt, escalate, never the other way.
| Bucket | Match condition | Rationale |
|---|---|---|
| CORE_REVIEW | Any changed path matches bin/install-skill-pack, scripts/lib/skill-install.sh, bin/add-skill, bin/add-mcp, aeon, scripts/notify.sh, scripts/notify-jsonrender.sh, aeon.yml, bin/generate-skills-json, scripts/check-capabilities-parity.sh, .github/workflows/aeon.yml, .github/workflows/chain-runner.yml, chain-runner.yml, CLAUDE.md | The runtime executor + the things every skill depends on. A bug here ships to every fork. |
| INFRA_REVIEW | Any changed path matches .github/workflows/*.yml (excluding aeon.yml already in CORE_REVIEW), .github/actions/*, Dockerfile*, package.json at repo root, package-lock.json at repo root, apps/dashboard/package.json, mcp-server/package.json | Build + CI + dependency surface. Not the executor itself but adjacent enough that the operator should look. |
| SKILL_WARN_OR_BLOCK | Touches any skills/*/SKILL.md AND scripts/skill-scan.sh returned WARN or BLOCK on at least one of them (step 5) | A skill PR with a HIGH (BLOCK) or MEDIUM (WARN) security finding — surface explicitly. |
| SKILL_PASS | Touches any skills/*/SKILL.md AND every scanned SKILL.md returned PASS | A clean skill PR. The category most likely to be safely merged once a human has read the description. |
| FAST_TRACK | All changed paths match *.md, *.txt, LICENSE*, docs/**, README*, _data/**, _layouts/**, _posts/**, _config.yml, output/.chains/** | Docs/asset/data-only PR. No code path. Operator can merge on the title + a glance. |
| UNKNOWN | Files endpoint failed, OR no rule matched (shouldn't happen — UNKNOWN is the catch-all so a future contributor's PR with a brand-new path doesn't silently vanish from the digest) | Surface and ask the operator to look. Never silently bucket as FAST_TRACK. |
Note: CORE_REVIEW takes precedence over INFRA_REVIEW takes precedence over SKILL_*. A PR that touches both aeon.yml AND a skills/*/SKILL.md is CORE_REVIEW, not SKILL_PASS — the executor-config change is the higher-blast-radius signal and the digest should reflect that. The "first match wins" ordering in the rubric encodes this.
skills/*/SKILL.md)For each SKILL.md path changed in such a PR:
gh api "repos/${TARGET_REPO}/contents/${PATH}?ref=${HEAD_SHA}" \
--jq '.content' | base64 -d > "/tmp/pr-merge-scan-${PR_NUMBER}-$(echo ${PATH} | tr '/' '_').md"
bash scripts/skill-scan.sh "/tmp/pr-merge-scan-${PR_NUMBER}-$(echo ${PATH} | tr '/' '_').md" > "/tmp/pr-merge-scan-${PR_NUMBER}.out" 2>&1The scan output's first HIGH/MEDIUM/PASS verdict line is taken as the per-file verdict. PR-level verdict is the worst across all scanned files: any HIGH → BLOCK; otherwise any MEDIUM → WARN; otherwise PASS.
If scan.sh itself errors (missing, non-executable, non-zero exit without a verdict line) → tag the PR scan_verdict=scan_error, bucket UNKNOWN, do not infer PASS from silence. The scanner is the same path install-skill relies on; a broken scanner is a fleet-wide problem the operator must know about.
A PR can touch skills/*/SKILL.md AND skills/*/scan.sh AND aeon.yml at the same time. The bucket precedence in step 4 routes it to CORE_REVIEW regardless of the scan verdict — but still run the scan and record the verdict in the state file. A CORE_REVIEW PR with a BLOCK scan verdict deserves a second escalation line in the notification.
For each PR:
age_days = floor((now - created_at) / 86400)updated_age_days = floor((now - updated_at) / 86400) (used as a tie-breaker when sorting — a PR last updated 8 days ago beats one updated yesterday for review urgency)has_changes_requested = mergeable_state == "blocked" AND any label in {changes-requested, needs-revision}labels = the labels array from the PR list responseWithin each bucket, sort by age_days DESC, then updated_age_days DESC, then number ASC. The oldest unreviewed PR in each bucket is the operator's highest-leverage merge click.
Overwrite output/articles/pr-merge-${today}.md:
# PR Merge Queue — ${TARGET_REPO} — ${today}
*Open PRs surveyed: N · Drafts skipped: D · Trusted-author PRs (auto-merge handles): T*
## FAST_TRACK ({fast_track_count})
Docs / asset / data-only. Safe to merge on title + a glance.
| # | Title | Author | Age | Labels |
|---|-------|--------|-----|--------|
| ... |
## SKILL_PASS ({skill_pass_count})
Skill PR; security scan PASS on every changed `SKILL.md`.
| # | Title | Author | Age | Scan |
|---|-------|--------|-----|------|
## INFRA_REVIEW ({infra_review_count})
Build / CI / dependency-surface change. Requires operator eyes.
| # | Title | Author | Age | Files touched |
|---|-------|--------|-----|---------------|
## SKILL_WARN_OR_BLOCK ({skill_warn_count})
Skill PR with security scan WARN or BLOCK. **Do not merge without resolving the finding.**
| # | Title | Author | Age | Scan | Verdict |
|---|-------|--------|-----|------|---------|
## CORE_REVIEW ({core_review_count})
Touches the runtime executor or `aeon.yml`. **Highest blast radius — review carefully.**
| # | Title | Author | Age | Files touched | Scan (if any) |
|---|-------|--------|-----|---------------|---------------|
## UNKNOWN ({unknown_count})
Files endpoint failed OR matched no rule. Operator: glance and re-run.
| # | Title | Author | Age | Reason |
|---|-------|--------|-----|--------|
## TRUSTED_AUTHOR ({trusted_count})
Routed to `auto-merge`. Listed here for visibility only.
| # | Title | Author | Age |
|---|-------|--------|-----|
---
*Generated by `pr-review --survey`. Bucket precedence: CORE_REVIEW > INFRA_REVIEW > SKILL_WARN_OR_BLOCK > SKILL_PASS > FAST_TRACK > UNKNOWN. Run again with `--survey dry-run` to refresh without sending a notification.*PR rows: | #N | [title] | @author | Nd | [labels-or-files-or-scan-cell] | — every column ≤80 chars, truncate with … if needed so the markdown table stays narrow enough to render in the dashboard.
Skip notify entirely on BAD_VAR, API_FAIL, EMPTY, DRY_RUN, STATE_CORRUPT.
Otherwise notify only if any of:
state.prs was empty before this run.bucket != FAST_TRACK and bucket != TRUSTED_AUTHOR (a new FAST_TRACK or trusted-author PR is not news; surfacing one would re-create the dependabot-noise pattern other skills work hard to suppress).state.prs[k].last_notified_head_sha != head_sha OR state.prs[k] did not exist. (A force-push or rebase reopens the review surface; we re-notify once per head SHA, not per run.)When notifying, set state.prs[k].last_notified_head_sha = head_sha for every PR cited in the notification body, so a noisy queue doesn't re-fire the same line every morning. PRs that drop out (closed/merged) are removed from prs (no retention beyond active queue).
*PR Merge Queue — ${TARGET_REPO} — ${today}*
{open_count} open · {new_count} new since last run · {trusted_count} trusted (auto-merge)
FAST_TRACK ({n}): #A · #B · #C ← safe to merge
INFRA_REVIEW ({n}): #D · #E ← needs eyes
SKILL_PASS ({n}): #F · #G ← clean scan
SKILL_WARN_OR_BLOCK ({n}): #H ⚠️ HIGH security finding · #I ⚠️ MEDIUM
CORE_REVIEW ({n}): #J ⚠️ touches aeon.yml · #K
{If unknown_count > 0:} UNKNOWN ({n}): #L — files API failed
Oldest in queue: #M (Nd, {bucket})
Full digest: output/articles/pr-merge-${today}.mdKeep under 900 chars. Drop any bucket row whose count is zero. Drop the "oldest in queue" line if open_count == 0. The ⚠️ HIGH security finding / ⚠️ touches aeon.yml annotations are appended only to PRs the operator has not been notified about on this head SHA — repeat-rendering them would dilute the alert signal.
Write the message to a scratch file (e.g. .pending-notify-temp/pr-review-survey-${today}.md) and send it with ./notify -f <file>. It is multi-line, so never pass it as a positional ./notify "$MSG" argument (long multi-line argv trips the sandbox; see CLAUDE.md Tools).
Append to memory/logs/${today}.md under the shared ### pr-review heading with a mode discriminator:
### pr-review
- **Mode**: survey (risk-tiered triage digest)
- **Target repo**: ${TARGET_REPO}
- **Open PRs**: N (drafts skipped: D · trusted-author: T)
- **Buckets**: FAST_TRACK=A · SKILL_PASS=B · INFRA_REVIEW=C · SKILL_WARN_OR_BLOCK=D · CORE_REVIEW=E · UNKNOWN=F
- **New since last run**: G (excluding FAST_TRACK and TRUSTED_AUTHOR)
- **Oldest open PR**: #N (Mt days, bucket={bucket})
- **Scan results**: PASS=P · WARN=W · BLOCK=B · scan_error=E
- **Article**: output/articles/pr-merge-${today}.md
- **Notification**: sent / skipped (gated)
- **Status**: PR_MERGE_QUEUE_OK| Status | Meaning | Notify? |
|---|---|---|
PR_MERGE_QUEUE_OK | Digest written; baseline or a delta fired | Yes |
PR_MERGE_QUEUE_QUIET | Digest written; no new non-FAST_TRACK PR, no fresh-head-SHA escalation since last run | No (article + state still write) |
PR_MERGE_QUEUE_EMPTY | Queue is empty; article still writes with zero counts | No |
PR_MERGE_QUEUE_API_FAIL | pulls endpoint failed | Yes (one-line failure notify) |
PR_MERGE_QUEUE_DRY_RUN | DRY_RUN=yes; article + state wrote, notify skipped | No |
PR_MERGE_QUEUE_STATE_CORRUPT | State JSON unreadable, recreated; silent recovery this run | No |
PR_MERGE_QUEUE_BAD_VAR | ${var} parse failed | No |
PR_MERGE_QUEUE_OK and PR_MERGE_QUEUE_QUIET are the two success states. The split exists so the dashboard can show "ran clean, nothing changed" without overloading the OK row.
aeon.yml and skills/x/SKILL.md is CORE_REVIEW. Routing it to SKILL_PASS because the scan passed would understate the blast radius — the executor-config change is the dominant signal. First-match-wins in step 4 encodes this; do not "improve" by softening to per-file majority voting.scan_error → bucket UNKNOWN. The whole point of the scanner is to catch HIGH findings before merge; a silent PASS on a broken scanner is the failure mode the skill exists to prevent. Same rule applies wherever the scanner runs.aeon.yml is still a auto-merge-handled PR — listing it under CORE_REVIEW would suggest the operator should look, but auto-merge's policy already gates this. The split keeps the operator's mental model: this digest is the queue you have to think about, not everything that's open.last_notified_head_sha per PR; a force-push or rebase reopens the review surface and is the only thing that re-notifies. Same dedup pattern skill-scan uses.pr-triage territory and should grow there.memory/watched-repos.md is read for ## Trusted Authors only (in this branch). The branch does not iterate every watched repo by default because the digest format only renders cleanly for one repo at a time and the operator's morning question ("what's safe to merge on aeon today") is per-repo. Multi-repo support is the ${var} override path.gh CLI handles GitHub auth internally — use it over raw curl, which would put a bare $SECRET on the command line for the Bash permission layer to refuse. Both branches route all outbound GitHub calls through gh / gh api (GH_TOKEN, or GITHUB_TOKEN in CI — provided by the runner, no new secret to provision). No postprocess wrapper required. The only other outbound call is ./notify, which stages for re-delivery after the run.
gh fails at the repo level, log the error and continue to the next repo. As a last-resort fallback, use WebFetch on the raw PR URL to read the diff.pulls list endpoint is the floor (see step 2) — on failure, one-line failure notify + exit API_FAIL. A single PR's files-endpoint failure degrades that PR to UNKNOWN but keeps it in the digest.No third-party API keys. No on-chain reads. No file writes outside memory/, output/articles/, and /tmp/.
© aeonfun, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in skills/pr-review of aeonfun/aeon.
Open the folder on GitHubat commit c0cb7c4
PR Review next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| PR Review this skillaeonfun/aeon | 767 | — | ~8k | Automated safety check: Pass | MIT | |
| Post Draft Reviewagent-substrate/substrate | 4.5k | — | ~2.8k | Automated safety check: Pass | Apache-2.0 | |
| Review Kedro PRkedro-org/kedro | 11k | — | ~2.8k | Automated safety check: Pass | Custom licence | |
| Opik Documentation Patternscomet-ml/opik | 22k | — | ~1.3k | Automated safety check: Pass | Apache-2.0 | |
| Eli5coldteadotai/pr-lens | 1.9k | — | ~2.3k | Automated safety check: Pass | MIT | |
| Review PRjavierbrea/eslint-plugin-boundaries | 997 | — | ~2.9k | Automated safety check: Pass | MIT |
agent-substrate/substrate
Posts pull request review findings as GitHub draft (pending) inline comments for a human to edit and submit, instead of publishing them straight to the PR author.
kedro-org/kedro
Review a Kedro PR for checklist compliance, architecture, correctness, and clarity.
comet-ml/opik
Rules for writing PR descriptions, changelog entries and feature documentation in the Opik repository, including the exact headings that CI requires.
coldteadotai/pr-lens
WHAT: Explains a codebase, a folder, a feature, a command or a pull request to someone who knows nothing about it, as a PR Lens canvas whose walkthrough builds the picture one part at a time.
javierbrea/eslint-plugin-boundaries
Review a GitHub pull request from three perspectives — functional fit against its linked issue, code correctness/quality, and architectural boundaries — then post a single GitHub review: one general…
docwriter-org/plain-writing-skill
Writes and edits prose in a plain and boring style: simple everyday words, complete sentences, no dashes, no jargon, and no analogies.
aeonfun/aeon
Browses open tasks on the TaskMarket agent-worker market and, with explicit operator approval, creates tasks, tracks submissions and submits finished work.
aeonfun/aeon
Sets up and manages an Aeon agent instance that runs skills on a schedule through GitHub Actions: starting, rescheduling, debugging, editing skills and mining chat history.
aeonfun/aeon
Reads a Base Account's address, portfolio and transaction history through the Base MCP server, and stays strictly read-only in unattended Aeon runs, reporting only changes.
aeonfun/aeon
Audits every page of a site each day from its sitemap, scores on-page and technical SEO, checks duplicates across pages and reports what changed since the last run.
aeonfun/aeon
5 concrete real-life actions, leverage-scored against open loops with specificity and anti-fluff gates
aeonfun/aeon
Static linter for an Aeon instance's configuration that catches silent failures such as unquoted schedules, duplicate keys, unconfigured skills and broken MCP references.
Categories
Review open PRs two ways - default is a per-PR deep review with severity-tagged findings, inline comments, and a verdict; --survey runs a risk-tiered triage digest of what's safe to merge first. PR Review is an agent skill from aeonfun/aeon.
PR Review fits situations like: tasks that involve Pull requests; tasks that involve Technical documentation.
Run `npx skills add aeonfun/aeon --skill pr-review -a claude-code`. Or copy the skill folder (skills/pr-review in aeonfun/aeon) into .claude/skills/pr-review in your project. Claude Code loads it when a task matches its description.
Run `npx skills add aeonfun/aeon --skill pr-review -a codex`. Or copy the skill folder (skills/pr-review in aeonfun/aeon) into .agents/skills/pr-review in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add aeonfun/aeon --skill pr-review -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/pr-review, .gemini/skills/pr-review, .github/skills/pr-review and .opencode/skills/pr-review in your project.
Going by SKILL.md and its folder, PR Review needs the command-line tools its instructions call (gh, bash and jq) and credentials named GH_TOKEN and GITHUB_TOKEN. Our summary lists: A credential in GITHUB_TOKEN.
SKILL.md contains no URLs. Its commands use gh, which can reach the network depending on how they are called. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
PR Review is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 8k tokens (SKILL.md is roughly 32k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with PR Review: Post Draft Review (agent-substrate/substrate, 4.5k stars), Review Kedro PR (kedro-org/kedro, 11k stars), Opik Documentation Patterns (comet-ml/opik, 22k stars) and Eli5 (coldteadotai/pr-lens, 1.9k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
aeonfun (a GitHub organization) maintains it in aeonfun/aeon, which has 767 GitHub stars. The repository holds 82 skills in this directory. The repository was last updated on October 8, 2026.
Source: aeonfun/aeon on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.