From om-superpowers
Reviews GitHub PRs in isolated worktrees: fetches PR, runs code review, submits approve/request-changes, manages labels, and optionally autofixes via iterative fixes/tests/typechecks until merge-ready.
How this skill is triggered — by the user, by Claude, or both
Slash command
/om-superpowers:om-auto-review-prThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Review a GitHub pull request by number without touching the current worktree. Always fetch the exact PR from GitHub, review it in an isolated worktree, submit the verdict, and if the PR still has blockers offer an explicit autofix flow that keeps resolving conflicts, fixing code, testing, typechecking, and re-reviewing until the PR is actually ready or a non-actionable blocker remains.
Review a GitHub pull request by number without touching the current worktree. Always fetch the exact PR from GitHub, review it in an isolated worktree, submit the verdict, and if the PR still has blockers offer an explicit autofix flow that keeps resolving conflicts, fixing code, testing, typechecking, and re-reviewing until the PR is actually ready or a non-actionable blocker remains.
{prNumber} (required) — the PR number to review or re-review (for example 1234)--force (optional) — bypass the in-progress concurrency check; use when intentionally taking over a PR that another auto-skill or human already claimedAuto-skills MUST NOT clobber each other. Before doing anything else, decide whether you may claim this PR.
CURRENT_USER=$(gh api user --jq '.login')
gh pr view {prNumber} --json assignees,labels,number,title,comments
A PR is considered already in progress when ANY of the following is true:
in-progress label$CURRENT_USER🤖 start marker)Decision tree:
| State | --force set? | Action |
|---|---|---|
| Not in progress | — | Claim and proceed |
| In progress, current user owns the lock | — | Treat as re-entry; proceed without re-claiming |
| In progress, someone else owns the lock | no | STOP. Ask the user via AskUserQuestion: "PR #{prNumber} is in progress (owner: {owner}, signal: {label/assignee/comment}). Override and continue?" Only continue when the user explicitly says yes. |
| In progress, someone else owns the lock | yes | Post a force-override comment naming the previous owner, then claim and proceed |
Stale lock recovery:
in-progress label is older than 60 minutes and the assignee did not push or comment in that window, treat it as expired. Still ask the user before overriding unless --force was set.gh pr edit {prNumber} --add-assignee "$CURRENT_USER"
# Apply the in-progress label via the same GraphQL flow used for pipeline labels
# (kept atomic with the pipeline label transitions in step 8)
gh pr comment {prNumber} --body "🤖 \`auto-review-pr\` started by @${CURRENT_USER} at $(date -u +%Y-%m-%dT%H:%M:%SZ). Other auto-skills will skip this PR until the lock is released."
The release step happens in step 11 — the lock MUST be released even on failure.
Use GitHub as the source of truth. Collect enough data to decide whether this is a first review or a re-review and whether the PR comes from a fork.
gh pr view {prNumber} --json number,title,url,author,baseRefName,baseRefOid,headRefName,headRefOid,headRepository,headRepositoryOwner,isCrossRepository,maintainerCanModify,mergeable,mergeStateStatus,reviewDecision,labels,latestReviews,reviews,commits,files
gh api user --jq '.login'
Capture at least:
isCrossRepository)maintainerCanModify)Treat the run as a re-review when the current reviewer has already submitted a review on the PR. Use reviews first and latestReviews as a fallback.
Rules:
When re-reviewing:
Re-review: {PR title} instead of Code Review: {PR title}.Run these checks before the worktree is created. If either fails, skip the full code review and go straight to the changes-requested flow.
gh pr view {prNumber} --json mergeable,mergeStateStatus,baseRefName
If mergeable is CONFLICTING or mergeStateStatus is DIRTY, do not continue with checkout or review execution on the first pass.
Submit a changes-requested review with a conflict-focused body, apply the changes-requested label, remove merge-queue, and stop the first pass.
Important:
Discover required checks first:
gh api repos/{owner}/{repo}/branches/{baseRefName}/protection/required_status_checks --jq '.contexts[]' 2>/dev/null
If the branch protection API returns 404, treat all reported PR checks as required.
Fetch the actual PR check results:
gh pr checks {prNumber} --json name,state,link
Treat these states as failing:
FAILUREERRORCANCELLEDTIMED_OUTIgnore these as non-failing:
PENDINGSUCCESSSKIPPEDNEUTRALIf any required check is failing, do not continue with checkout or review execution. Submit a changes-requested review listing only the failing required checks, apply changes-requested, remove merge-queue, and stop.
Never review directly in the repository’s primary worktree.
First detect whether you are already inside a linked worktree:
REPO_ROOT=$(git rev-parse --show-toplevel)
GIT_DIR=$(git rev-parse --git-dir)
GIT_COMMON_DIR=$(git rev-parse --git-common-dir)
WORKTREE_PARENT="$REPO_ROOT/.ai/tmp/auto-review-pr"
CREATED_WORKTREE=0
if [ "$GIT_DIR" != "$GIT_COMMON_DIR" ]; then
WORKTREE_DIR="$PWD"
else
WORKTREE_DIR="$WORKTREE_PARENT/pr-{prNumber}-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$WORKTREE_PARENT"
git fetch origin "pull/{prNumber}/head"
PR_HEAD_SHA=$(git rev-parse FETCH_HEAD)
git worktree add --detach "$WORKTREE_DIR" "$PR_HEAD_SHA"
CREATED_WORKTREE=1
cd "$WORKTREE_DIR"
git switch -c "review/pr-{prNumber}"
fi
If you reused an existing linked worktree, repoint it deliberately to the PR branch or a fresh local branch for that PR before continuing. If you created a new worktree, use the GitHub pull ref so the checkout works for both same-repo PRs and fork PRs.
After selecting the worktree, ensure you are on the correct PR branch context:
cd "$WORKTREE_DIR"
git fetch origin "pull/{prNumber}/head"
git checkout -B "review/pr-{prNumber}" FETCH_HEAD
git fetch origin "{baseRefName}"
Rules:
Before running any Yarn-based validation in the new worktree, restore the package-manager install state:
yarn install --mode=skip-build
If --mode=skip-build is unavailable in the current Yarn version, run plain yarn install.
Cleanup sequence:
cd "$REPO_ROOT"
if [ "$CREATED_WORKTREE" = "1" ]; then
git worktree remove --force "$WORKTREE_DIR"
fi
Before proceeding with the full review, verify that the PR does not duplicate work already present in the base branch. This catches cases where:
Steps:
Get the list of changed files from the PR diff:
gh pr diff {prNumber} --name-only
For each changed file, compare the PR version against the base branch version to identify overlap:
git diff origin/{baseRefName} -- <file>
Check recent commits on the base branch that touch the same files:
git log origin/{baseRefName} --oneline -20 -- <files>
Look for semantic duplication — the same logic, function, or fix already present in the base branch even if the code differs slightly.
If the PR's core changes are already present in the base branch:
changes-requested, remove merge-queue, and stop.If partial overlap exists (some changes are new, some are redundant):
Before running the full code-review skill, scan the PR diff for hard-rule violations. Use:
gh pr diff {prNumber}
gh pr diff {prNumber} --name-only
Record findings from the patterns below. These are mandatory findings, not optional heuristics.
| Pattern in diff | Finding |
|---|---|
Removed or renamed event ID in any events.ts | Critical: event ID is a frozen contract surface |
Removed or renamed widget spot ID in injection-table.ts | Critical: spot ID is a frozen contract surface |
| Removed field from an API response schema or zod response type | Critical: response fields are additive-only |
| Renamed or removed a database column or table in a migration | Critical: DB schema is additive-only |
| Removed a public import path without re-export bridge | Critical: import paths require deprecation protocol |
Missing organization_id or tenant_id filter on a tenant-scoped query | Critical: tenant isolation breach |
| Pattern in diff | Finding |
|---|---|
findWithDecryption or findOneWithDecryption replaced with raw em.find or em.findOne | High: encryption helpers must not be downgraded |
New API route file missing export const openApi or export const metadata | High: required exports for auto-discovery |
New subscriber or worker file missing export const metadata | High: required exports for auto-discovery |
Raw fetch( call in UI or backend page code, outside tests | High: must use apiCall or apiCallOrThrow |
New raw em.findOne( or em.find( in non-test production code (grep the diff: gh pr diff {prNumber} | grep "^+" | grep -v "test\." | grep -v "__tests__" | grep "em\.find") | High: must use findOneWithDecryption/findWithDecryption from @open-mercato/shared/lib/encryption/find |
| Behavior change with no corresponding test file in the diff | High: behavior changes must include tests |
| Pattern in diff | Finding |
|---|---|
| Hardcoded user-facing string in API errors or UI labels | Medium: must use i18n |
New any type annotation outside tests | Medium: use zod plus z.infer |
alert( or custom toast instead of flash() | Medium: use flash() |
| Hand-written migration SQL file | Medium: never hand-write migrations |
| Entity schema changed but no migration file in the diff | Medium: run yarn db:generate |
| Missing explicit tenant scoping in sub-entity queries | Medium: defense in depth |
| New or modified i18n locale JSON keys not in alphabetical order | Medium: CI i18n-check-sync requires sorted keys — run yarn i18n:check-sync --fix or sort manually |
| Pattern in diff | Finding |
|---|---|
One-letter variable name outside loop counters i, j, k | Low: use descriptive names |
| Inline comment on self-explanatory code | Low: remove comment |
| Added docstring or comment on unchanged function | Low: do not annotate unchanged code |
Execute skills/om-code-review/SKILL.md in the isolated worktree.
Mandatory scope and gates:
gh pr diff {prNumber} --name-onlyAGENTS.md files, related specs, and .ai/lessons.mdyarn template:syncBACKWARD_COMPATIBILITY.mdMerge findings from step 5 into the final review report. Do not duplicate the same issue twice.
If the PR diff touches any .tsx or .ts files under packages/, apps/, or any module's backend/ / frontend/ / components/ directories, run the DS Guardian gate. The gate has two phases — a deterministic grep pass that runs first, then an LLM-augmented review that consumes the grep findings as input.
Phase 1 — Deterministic grep gate (fast path, ~5s).
# Detect UI-touching files
UI_FILES=$(gh pr diff {prNumber} --name-only \
| grep -E '\.(tsx|ts)$' \
| grep -E '(packages/|apps/).*((backend|frontend|components|widgets|primitives)/|\.tsx$)' \
| grep -v '__tests__' \
| grep -v '\.test\.' \
| grep -v '\.spec\.')
# Run the deterministic linter. Output is one finding per line:
# <file>:<line>:<rule-id>:<matched-text>
# Empty stdout means no greppable violations — the LLM phase still runs for
# judgment cases (decoration vs status, primitive choice, missing empty/
# loading states), but it gets a clean slate instead of having to redetect.
GREP_FINDINGS=""
if [ -n "$UI_FILES" ]; then
GREP_FINDINGS=$(echo "$UI_FILES" | bash skills/om-ds-guardian/scripts/ds-diff-check.sh)
fi
If $UI_FILES is empty, skip the rest of step 6a entirely.
Phase 2 — LLM-augmented REVIEW (judgment + fix language).
Execute skills/om-ds-guardian/SKILL.md Capability 4 (REVIEW) against $UI_FILES in the isolated worktree, passing $GREP_FINDINGS as known-violations input. The LLM's contract changes from "detect everything" to "augment what grep already caught and add what grep can't see":
$GREP_FINDINGS: confirm severity per the mapping below, attach a fix recipe from references/token-mapping.md, and downgrade or drop only with an explicit reason (e.g. "decorative use, not status semantics — DS-SKIP").<Switch> chosen where <Radio> would express the model better, or vice versa?DataTable but no EmptyState/LoadingMessage.aria-label — when the icon is not decorative.<Input> without <Label> / not wrapped in <FormField>.Severity mapping (applies to both phases):
<input>/<select>/<textarea>, missing empty/loading states, wrong selection-color contract (data-[state=checked]:bg-primary)Notice, missing aria-label on non-decorative icon buttons, disabled:opacity-50, hardcoded brand hex, old focus rings (focus:ring-2 ring-offset-2)Merge the combined output into the same report under a "Design System" section. Do not duplicate findings already raised by step 5 or step 6 — DS Guardian's checks are orthogonal to the general code-review checklist (it covers design-system surface; code-review covers architecture/security/conventions).
Why grep-first?
ds-diff-check.shruns in seconds, has zero false positives on its rules, and covers ~80% of recurring DS violations (colors, raw form controls, deprecated imports, focus rings, brand hex, opacity-disabled, selection-color contract). The LLM phase still runs unconditionally — its job is judgment and fix language, not redetection. This split is a Pareto improvement: faster on the common case, more reliable on greppable rules, no coverage loss on judgment cases.
Use the same severity rules as the code-review skill:
| Condition | Decision |
|---|---|
| Any Critical, High, or Medium finding | changes_requested |
| Only Low findings | approved |
| No findings | approved |
If approved, submit an approval review. If there are Critical, High, or Medium findings, submit a changes-requested review.
The review body must contain the full structured report from the code-review skill. For re-reviews, explicitly note that it is a re-review in the title or summary.
Use the GraphQL label mutation flow, not gh pr edit --add-label.
Pipeline labels:
reviewchanges-requestedqaqa-failedmerge-queueblockeddo-not-mergeKeep in-progress separate from the pipeline-state helper. It is a lock, not a workflow state.
Define and reuse a shared helper such as setPipelineLabel(prNumber, newLabel) that:
newLabelbug, feature, refactor, security, dependencies, enterprise, documentation) and meta labels (needs-qa, skip-qa, in-progress)After every pipeline-label change, post a short PR comment explaining why that label was chosen. Keep it to one short sentence.
Label rules:
review before continuing so the state machine is explicit.changes-requested.needs-qa but not skip-qa, set qa.merge-queue.review, changes-requested, qa, qa-failed, and merge-queue on the same PR together.Suggested label comments:
review: Label set to \review` because this PR is ready for code review.`changes-requested: Label set to \changes-requested` because review found actionable issues.`qa: Label set to \qa` because code review passed and manual QA is still required.`merge-queue: Label set to \merge-queue` because the required review gates passed.`blocked: Label set to \blocked` because progress depends on an external blocker.`do-not-merge: Label set to \do-not-merge` because this PR should not merge yet.`After posting a changes_requested review, immediately proceed to fix all actionable findings without asking the user. The auto-review-pr skill must be fully autonomous — it reviews, fixes, re-reviews, and iterates until the PR is merge-ready or a truly critical blocker remains.
Only stop and ask the user in these critical situations:
For everything else — missing tests, code style issues, i18n problems, type errors, lint failures, missing metadata exports, security hardening — fix them autonomously.
Continue inside the isolated worktree.
Do not stop after the first patch. Treat autofix as an iterative loop:
*.test.ts, *.spec.ts, __tests__/*), add appropriate unit tests as the first autofix action. Every behavior change, bug fix, or new feature must have corresponding test coverage — this is non-negotiable in autofix mode.yarn i18n:check-sync which enforces this).approved, orExamples of real blockers:
Conflict-resolution rules for autofix mode:
{baseRefName} before resolving conflicts.For autofix mode, the goal is not "submit one fix commit". The goal is "finish the PR". Keep iterating until the code review is clean and validation passes, unless a real blocker stops progress.
Before any git commit in this skill — including autofix fix commits, conflict-resolution commits, and follow-up commits in §10a / §10b — run this mechanical check on the staged index. Same shape as om-auto-create-pr step 6 and om-auto-continue-pr step 4:
STAGED=$(git diff --cached --name-only)
CODE=$(echo "$STAGED" | grep -E '\.(ts|tsx|js|jsx|mjs|cjs)$' | grep -v -E '(__tests__|\.test\.|\.spec\.)' || true)
TESTS=$(echo "$STAGED" | grep -E '(__tests__|\.test\.|\.spec\.)' || true)
if [ -n "$CODE" ] && [ -z "$TESTS" ]; then
echo "BLOCK: code change without tests in the same commit:"
echo "$CODE"
echo "Add or update tests in this commit, or split the staged set so test-bearing changes land separately."
exit 1
fi
Rationale and exemptions documented in docs/specs/2026-05-06-test-coverage-at-commit.md. Single mechanical check — no retry counter, no Gate log, no needs-human label. If the check fails, fix the staged set (add tests, or split) and re-run.
This gate exists in autofix because autofix commits ARE code-bearing — when you land a fix that touches *.ts source files, the autofix loop's "step 0 unit test audit" requires test coverage anyway. The gate enforces that requirement at commit time rather than relying on the audit being honored.
If the PR head branch is in the main repository and you have push access, implement the fixes on the checked-out PR branch, resolve any base-branch conflicts there if needed, run the autofix loop above, then commit and push to that branch only after the latest re-review is approvable.
Rules:
fix(<area>): <summary>, feat(<area>): <summary>, refactor(<area>): <summary>, etc.For fork PRs, do not wait on the original author and do not push to the contributor’s branch by default.
Instead:
carry/pr-{prNumber}-ready.{baseRefName} on that carry-forward branch.origin.{baseRefName}.Validation requirements for autofix mode:
Replacement PR requirements:
fix(<area>): <summary>, feat(<area>): <summary>, refactor(<area>): <summary>, etc. Where <area> is the primary affected module or package (e.g., auth, catalog, ui, shared)Suggested replacement PR body:
Supersedes #{prNumber}
Credit: original implementation by @{originalAuthor}. This follow-up PR carries that work forward with the requested fixes so it can merge without waiting on the original branch.
## Included work
- Original changes from #{prNumber}
- Follow-up fixes applied during re-review
Suggested original PR closing comment:
Closing in favor of #{newPrNumber} ({newPrUrl}).
Credit to @{originalAuthor} for the original implementation. The replacement PR carries the same work forward with the requested fixes so it can merge without waiting on the fork branch.
Always release before the skill exits — even on failure. Use a trap or equivalent finally-block so a crash or early stop still clears the lock.
# Remove the in-progress label (use the same GraphQL label flow used elsewhere)
gh pr comment {prNumber} --body "🤖 auto-review-pr completed: ${VERDICT}. Lock released."
Rules (v1.12.0+ lean style):
in-progress label.Print a concise summary to the user:
PR #{prNumber}: {title}
Mode: {review | re-review}
Decision: {APPROVED | CHANGES REQUESTED}
Label: {qa | merge-queue | changes-requested}
Findings: {X critical, Y high, Z medium, W low}
Worktree: {path}
Review submitted successfully.
If all findings were auto-fixed, the summary should note that fixes were applied and the PR is ready for merge.
If a critical blocker remains that requires human judgment, the summary must describe the blocker and ask for guidance.
in-progress lock in step 11, even if the run fails or is aborted (use a trap/finally)git commit in this skill (autofix or otherwise), run the tests-with-code gate on the staged index per the block in §10. Code without tests in the same commit blocks the commit. Mirrors the gate in om-auto-create-pr step 6 and om-auto-continue-pr step 4.code-review skillcode-review skill severity modelneeds-qa and without skip-qa must land in qa, not merge-queuemerge-queuereview before continuingProvides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
npx claudepluginhub shgrowth/om-superpowers --plugin om-superpowers