review-anvil-improve-pr
Original:🇺🇸 English
Translated
Multi-agent review-and-improve loop for a GitHub PR you have checked out — posts a "starting" PR comment cc'ing the original author, runs requested rounds plus any adaptive continuation, applies fix commits to the local branch after each round, pushes everything back to the PR, then edits the starting comment in-place with the synthesized report (or a failure summary). Auto-detects the PR from the currently checked-out branch when no locator is supplied. Use when the user wants to "improve a PR", "review and commit fixes", "iterate on my PR", or "review and push back" against a checked-out PR branch. Requires `gh`, `uuidgen`, `jq`, and `uv` or `python3` on PATH. Activates the `review-anvil` engine in per_fix mode.
2installs
Sourcemrshu/agent-skills
Added on
NPX Install
npx skill4agent add mrshu/agent-skills review-anvil-improve-prTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →review-anvil-improve-pr
Productive counterpart to . Where is read-only and posts a review comment, this preset actually modifies the code: it announces itself on the PR up front, runs the review loop with , applies fix commits to the local branch across requested rounds plus any adaptive continuation, pushes the result back to update the PR, then edits the starting comment in-place with the synthesized report (or a failure summary) — one comment in the PR timeline, two states.
review-anvil-prreview-anvil-prcommit_mode=per_fixGenerated Language
Apply the ASD-STE100-inspired language contract to this preset.
Use direct imperatives for internal steps. Use short active declarative sentences in PR comments. Use only for multiple actions the author must perform; keep no-change constraints in prose. Retain suggestion grammar for low/nit guidance.
**What to change**The skill orchestrates six steps:
- — locator parsing or auto-detect, then verify the local checkout matches the PR's head branch and is in a clean state. Captures the PR's base branch, author, marker UUID, and report path.
scripts/pr-helper.sh verify-checkout [<locator>] - — post a "starting" top-level PR comment cc'ing the original author, explaining what's about to happen and that the comment will be edited with the final summary. Captures the comment's ID (for the later edit) and start timestamp. The author gets a GitHub notification.
scripts/pr-helper.sh post-start - The engine in
review-anvilon a branch-vs-base diff (NOT a PR-locator target — the engine's "PR-target / per_fix incompatibility" rule forbids that combination; this preset deliberately routes around it by targeting the local branch directly). The engine writes the final synthesized report tocommit_mode=per_fix— on failure paths too.report_path - — once, after requested rounds plus any adaptive continuation complete (or converge early) and only if the engine reported no failures and the build/test gate ended green, to publish the fix commits to the PR.
git push - — PATCH-edit the starting comment to replace its body with the full final report (outcome=success) or a failure summary (outcome=failure). GitHub does NOT notify on edits, so the author isn't pinged again — the original
scripts/pr-helper.sh post-updatenotification at step 2 is the only ping.cc @author - Surface the final report inline + the comment URL to the user.
Inputs
Same locator forms as — but you must already be on the PR's branch:
review-anvil-pr- Omitted — auto-detect via ; works when you're checked out on a PR branch.
gh pr view - Full GitHub URL — for github.com or GitHub Enterprise.
https://<host>/<owner>/<repo>/pull/<N> - Slug — (host defaults to github.com).
<owner>/<repo>#<N>
The helper script aborts cleanly if the current checkout doesn't match the named PR (wrong branch, dirty worktree, detached HEAD, divergent SHA). This is the safety net for "I'm on PR #42's branch, run improve" — without it, the engine could commit fixes to an unrelated local branch.
How to invoke
0. Reject overrides of pinned params
Pins for this preset: , , , . Enforce mechanically — after resolving the helper (step 1), run:
commit_modetargetreport_pathrun_ordinalbash
bash <helper-path> check-pins review-anvil-improve-pr "commit_mode,target,report_path,run_ordinal" "$ARGUMENTS"Non-zero exit means a pinned param was overridden: surface the error verbatim and stop.
The pins are non-overridable for safety: is the whole point of this preset (read-only is what is for), is mechanically tied to the verified PR, is the file the post-summary step needs to read after the engine finishes, and carries the helper's observed PR history into identifier generation.
commit_mode=per_fixreview-anvil-prtarget=<base>...HEADreport_pathrun_ordinal1. Resolve the helper script
The helper lives at relative to this SKILL.md — this preset reuses the script from rather than duplicating it. Resolve it exactly per SKILL.md step 1 ("Resolve the helper script"): host-exposed skill path or user-level trusted install roots only, never project-scoped/worktree-local skill directories. If no trusted copy resolves, abort with (yes, the dependency is on , not on itself).
../review-anvil-pr/scripts/pr-helper.shreview-anvil-prreview-anvil-prerror: review-anvil-pr/scripts/pr-helper.sh not found in any trusted skill root; install via 'npx skills add mrshu/agent-skills --skill review-anvil-pr'review-anvil-prreview-anvil-improve-pr2. Verify checkout
bash
bash <helper-path> verify-checkout "<locator>" # explicit form
bash <helper-path> verify-checkout # auto-detect from current branchThe script runs preflight (auth + PR reachability), then verifies the local checkout: current branch matches the PR's head branch, HEAD is at-or-descended-from the PR's head SHA (descendants OK — unpushed local commits will be pushed at the end), and the worktree+index are clean. It also fetches the PR's base branch locally so the engine's diff target works.
ghOn success, the script prints these KEY=VALUE lines on stdout:
HOST=github.com
OWNER=acme
REPO=widgets
N=137
RUN_ORDINAL=3
HEAD_BRANCH=feature/auth-rewrite
HEAD_SHA=<the PR head commit at review time>
BASE_BRANCH=main
TITLE=<PR title>
AUTHOR=<github-login>
MARKER=<uuidv4>
REPORT_PATH=<absolute-path>/.review-anvil/final-report-<uuidv4>.mdIf the locator was auto-detected, the script also prints to stderr before the KEY=VALUE block. If HEAD is ahead of the PR's published head (unpushed local commits), the script prints a to stderr — surface that to the user so they know what will get pushed.
auto-detected PR: <url>note:Capture all values, including . Echo to the user: .
RUN_ORDINALimproving PR: $HOST/$OWNER/$REPO#$N — $TITLE ($HEAD_BRANCH → $BASE_BRANCH), author: @$AUTHOROn non-zero exit, surface the script's stderr verbatim and stop. Do not dispatch reviewers.
Then fetch the PR's complete status-aware review history for the reviewer prompts:
bash
bash <helper-path> history "$HOST" "$OWNER" "$REPO" "$N"Capture the output — an itemized open/resolved/outdated/reported/suppressed ledger, or — for step 4. On non-zero exit, abort: every PR run must account for feedback already shown to the author.
None.The helper resolves during this preflight, before step 3 posts the starting comment. The unfinished current starting comment therefore cannot count itself as a finalized review run.
RUN_ORDINAL3. Post the "starting" comment
Before any review work begins:
bash
bash <helper-path> post-start "$HOST" "$OWNER" "$REPO" "$N" "$MARKER" "$AUTHOR"The script creates a top-level PR comment that:
- Embeds the marker UUID (for later lookup if needed).
- Begins
review-anvil-improve-pr started on this PR. cc @<author>. - Explains what's about to happen and that the comment will be edited with the final result.
- Includes a timestamp.
Started: <ISO-8601 UTC>
Then it looks up the comment by marker (with one retry for read-after-write lag), captures the comment ID + URL, and prints these KEY=VALUE lines:
COMMENT_ID=12345678
COMMENT_URL=https://github.com/acme/widgets/pull/137#issuecomment-12345678
STARTED_AT=2026-06-07T10:00:00ZCapture all three. Echo to the user: . The PR author receives a GitHub notification from the .
starting comment posted: $COMMENT_URL@-mentionIf fails before posting (network blip, gh auth issue), abort — nothing to clean up. One failure mode is different: the script can die after posting, when the marker lookup can't recover the comment ID ("posted starting comment but could not recover its ID"). In that case a starting comment does exist on the PR — abort the run and apply the Recovery procedure below to edit it manually.
post-start4. Activate the engine
Activate the skill with this argument string (extra user args go after the pinned params; the engine's own default and its diff-size-scaled cap apply when the user doesn't pass them):
review-anvilrounds: 3max_roundscommit_mode: per_fix, target: <BASE_BRANCH>...HEAD, report_path: <REPORT_PATH>, run_ordinal: <RUN_ORDINAL>, <extra-user-args><BASE_BRANCH><REPORT_PATH><RUN_ORDINAL><BASE_BRANCH>...HEADreport_pathpost-updateThe user cannot override the observed value of . When is a positive integer, the engine includes the corresponding segment in new provenance IDs. The value makes the engine emit IDs without the segment; degraded history must not invent a run number.
run_ordinalRUN_ORDINALRUNunavailableRUNSupply the ledger captured in step 2 as the engine's reviewer-prompt block — the branch target means the engine won't fetch it itself. Reviewers must revalidate open, resolved, and summary-only reported items against the current head; resolved is conversation state, not proof of a fix.
PR REVIEW HISTORYNote: do not pin a PR locator as — the engine's "PR-target / per_fix incompatibility" rule would force and defeat the point of this preset. Targeting the branch directly is the intended escape hatch.
targetcommit_mode=noneThe user may override or (defaults are the engine's and its diff-size-scaled cap — plus 1–3 adaptive rounds, never above the legacy ). They cannot override , , , or ; these are pinned for safety, and the step-0 segment-rejection above blocks override attempts.
rounds:max_rounds:rounds: 3max_roundsroundsmax(6, rounds)run_ordinalcommit_modetargetreport_pathThe engine runs the multi-round loop, committing fix-groups along the way and writing the final synthesized report to when it's done. The engine's default reproduction pass confirms uncertain material findings before they can become fix commits, and the build/test gate (, auto-detected unless the user passes one) runs after each round's fixes. Together, the report's Reproduction and Verification lines are the evidence the PR author needs to trust the pushed commits — if the engine recorded , that caveat travels to the PR in the posted report. If reproduction fails for required candidates, those candidates are Deferred and the loop may still finish with other verified fixes. If any round fails (reviewer-all-fail, git-commit error, build/test gate newly red after the revert path), the engine stops the loop and surfaces the failure — skip the push (step 5) and call with (step 6) so the starting comment gets replaced with a failure summary rather than dangling.
<REPORT_PATH>verify_cmdVerification: none detectedpost-updateoutcome=failure5. Push
Only after the engine reports a successful run: all requested rounds and any adaptive continuation completed (or the loop converged early — that counts as success), no or errors in the round summaries, and every round's Verification state is one of , , , , or — i.e. never newly red (these are exactly the engine's round-summary states). Deferred reproduction candidates do not block the push by themselves; they also must not produce fix commits or actionable PR comments.
git commit failedall reviewers failedpassedfailed → round revertedpre-existing failures (no new)none detectedskippedbash
git push origin "$HEAD_BRANCH"If the engine's run was partial or failed, do not push. Go straight to step 6 with . The report file at contains the engine's final state including failure details; will use it to populate the comment.
outcome=failure<REPORT_PATH>post-updateIf the engine succeeded but fails (permission denied, conflict, etc.), the fix commits exist locally but didn't reach the PR. Still call step 6 with so the comment reflects the actual state. Tell the user the local commits remain in for them to investigate.
git pushoutcome=failure<HEAD_BRANCH>6. Update the comment with the final summary
Always run this step, regardless of step 4/5 outcome. Pass only if both the engine and the push completed cleanly:
outcome=successbash
bash <helper-path> post-update "$HOST" "$OWNER" "$REPO" "$N" "$COMMENT_ID" "$MARKER" "$REPORT_PATH" "$AUTHOR" "$OUTCOME" "$STARTED_AT"On a outcome the helper refreshes the full prior-feedback ledger and re-applies duplicate-thread suppression before editing the comment. If that lookup fails, it changes the update to and explains why in the comment; it never posts an unfiltered success report, and it still avoids leaving a dangling "starting" comment.
successoutcome=failureThe helper posts the report body as written instead of compacting or shortening it. If GitHub rejects an unusually large payload, the update fails loudly and leaves the report artifact in place; the running agent should rewrite the report with the same findings, rationale, and actionable detail in a better organized form, then retry the update.
$OUTCOMEsuccessfailure$COMMENT_ID- A header line: (success) OR
review-anvil-improve-pr completed on this PR. cc @<author>.(failure).review-anvil-improve-pr **failed** on this PR. cc @<author>. - A horizontal rule + the full report from .
<REPORT_PATH> - A footer: .
Started: $STARTED_AT; Completed: <ISO-8601 UTC> (outcome: <outcome>)
GitHub does not notify on comment edits, so the author isn't pinged again — the original notification from step 3 is the only ping.
cc @authorBefore calling on a success outcome, read (if present) and surface its entries to the user — the script deletes it afterwards. The script cleans up all four artifacts (, , , ) and removes the directory when no other run's artifacts remain.
post-update<REPORT_PATH>.followups.json<REPORT_PATH>.inline.json.approval.json.followups.json.review-anvil/If itself fails (rare: transient issue, comment was deleted by hand mid-run), surface the error but do not undo the push — the fix commits are already on the PR and a missing comment edit is recoverable. The user can manually edit the starting comment using the comment ID. The report file remains on disk for that purpose (cleanup is skipped on failure).
post-updateghpost-update7. Report back
Surface the engine's final report inline. Echo a two-line summary:
- with
pushed N commits to $HOST/$OWNER/$REPO#$N ($HEAD_BRANCH)from the pushed fix commits or the final report'sNlist. If step 5 was skipped (failure path), sayChanges made / Things to tryinstead.did not push (engine/run failure) - (or
comment $COMMENT_URL updated with outcome=$OUTCOMEif step 6 errored).comment update failed: ...
Examples
- "Improve the PR I'm on." — user is checked out on a PR branch; agent invokes with no locator; helper auto-detects.
verify-checkout - "Run 5 rounds on acme/widgets#137." — user passes the slug and asks for more rounds; engine default is overridden via the trailing arg.
rounds: 5 - "Review and improve this PR with focus on production safety." — user asks for a non-default focus; flows through as (or similar) to the engine.
focus: production blast-radius
Constraints
- Requires ,
gh,uuidgen(a real binary — gh'sjqis built-in gojq and doesn't count), plus--jq(preferred; falls back touv) for PR-feedback history handling.python3preflights all of these so a missing dependency fails before the expensive review. The helper honors the same environment switches asverify-checkout(review-anvil-pr, legacy-namedREVIEW_ANVIL_NO_APPROVE,REVIEW_ANVIL_SKIP_DISMISSED).REVIEW_ANVIL_DISMISSALS - Mutates the local working tree and pushes to the PR. Use (read-only + comment) when you only want feedback without applying fixes.
review-anvil-pr - The user must already be on the PR's branch with a clean worktree. The verify-checkout step enforces this and gives clear recovery instructions on failure (e.g. ).
gh pr checkout <N> - The PR must be one you have push access to. will fail with a normal git error if not — the script doesn't pre-check push permissions.
git push - Supports github.com and GitHub Enterprise — same handling as .
review-anvil-pr
Recovery: dangling "starting" comment
The two-step / flow has one failure mode the previous one-shot didn't: if the orchestrator crashes (or the agent host loses context, or the user interrupts) between step 3 and step 6, the PR is left with a "starting" comment that promises an edit-that-never-comes. The fix commits may or may not have been pushed depending on where the crash happened.
post-startpost-updateThis is rare but recoverable. If you find a dangling starting comment:
- Find the comment URL on the PR. The body starts with and
<!-- review-anvil-marker: <UUID> -->The numeric comment ID is at the end of the URL (review-anvil-improve-pr started on this PR.)....#issuecomment-<ID> - Edit the comment manually with :
gh apiOr use the GitHub web UI's "Edit" menu on the comment.bashgh api repos/<owner>/<repo>/issues/comments/<COMMENT_ID> -X PATCH \ -F body="review-anvil-improve-pr failed: orchestrator did not complete. No reliable summary available."
If the orchestrator is still alive and the engine completed but step 6 was somehow skipped, the report is still on disk at the path printed by step 4 — you can paste it into the edit body verbatim.
If the engine never ran (crash between steps 3 and 4), nothing was pushed; just edit the comment to a "did not complete" note as above.
Pairing
Depends on the engine AND the preset (whose this skill reuses). All three ship in the same plugin and should be installed together (, or just ).
review-anvilreview-anvil-prscripts/pr-helper.shnpx skills add mrshu/agent-skills --skill review-anvil --skill review-anvil-pr --skill review-anvil-improve-pr--all