From groundwork
Reviews a GitHub PR using multi-agent verification. Runs specialized agents against PR changes and posts structured feedback to GitHub.
How this skill is triggered — by the user, by Claude, or both
Slash command
/groundwork:review-pr [PR number or URL] [--no-interactive][PR number or URL] [--no-interactive]This skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Multi-agent PR review that runs specialized agents against PR changes and posts structured feedback to GitHub.
Multi-agent PR review that runs specialized agents against PR changes and posts structured feedback to GitHub.
Check for non-interactive mode from either source:
--no-interactive → strip it from the argument before PR number parsing, set non_interactive = trueGROUNDWORK_BATCH_MODE=true → set non_interactive = trueIf either condition is met, all AskUserQuestion prompts below are replaced with their documented auto-decision — except the pre-flight model check, which always prompts.
Your current effort level is {{effort_level}}.
Skip this step silently if effort is high, xhigh, or max (the scale is low < medium < high < xhigh < max, so xhigh and max are already above high) AND you are Sonnet or Opus.
If effort is low or medium (i.e. below high), you MUST show the recommendation prompt — regardless of model.
If you are not Sonnet or Opus, you MUST show the recommendation prompt - regardless of effort level.
Otherwise → always prompt (even in non-interactive mode) using AskUserQuestion:
{
"questions": [{
"question": "Do you want to switch? Multi-agent orchestration and deduplication judgment across 6-8 agents benefits from consistent reasoning.\n\nTo switch: cancel, run `/effort high` (and `/model sonnet` if on Haiku), then re-invoke this skill.",
"header": "Recommended: Sonnet or Opus at high effort",
"options": [
{ "label": "Continue" },
{ "label": "Cancel — I'll switch first" }
],
"multiSelect": false
}]
}
If the user selects "Cancel — I'll switch first": output the switching commands above and stop. Do not proceed with the skill.
Extract the PR number from the user's input. Accept any of these formats:
42#42https://github.com/owner/repo/pull/42If no PR identifier was provided:
AskUserQuestion to ask for one (free-text input, no options).Extract owner/repo by running:
gh repo view --json nameWithOwner --jq '.nameWithOwner'
Store the PR number as pr_number and the owner/repo as repo_slug.
Run all of these gh commands in parallel using the Bash tool:
PR metadata:
gh pr view <pr_number> --json title,body,baseRefName,headRefName,headRefOid,author,state,url,isCrossRepository,additions,deletions,changedFiles
PR diff:
gh pr diff <pr_number>
Store as pr_diff.
Changed file paths:
gh pr view <pr_number> --json files --jq '.files[].path'
Store as changed_file_paths.
Diff stat:
gh pr diff <pr_number> --stat
Store as diff_stat.
Existing reviews:
gh api repos/<repo_slug>/pulls/<pr_number>/reviews
Store as existing_reviews.
Existing review comments:
gh api repos/<repo_slug>/pulls/<pr_number>/comments
Store as existing_comments.
Validate state:
OPEN:
AskUserQuestion:
{
"questions": [{
"question": "This PR is **[state]**. Continue reviewing anyway?",
"header": "PR is not open",
"options": [
{ "label": "Yes, continue" },
{ "label": "Cancel" }
],
"multiSelect": false
}]
}
If "Cancel" → stop.isCrossRepository is true, run gh pr checkout <pr_number> to fetch the fork's branch.gh pr checkout <pr_number>.Search existing_reviews for reviews whose body contains the marker <!-- groundwork-review -->.
If groundwork reviews exist:
AskUserQuestion:
{
"questions": [{
"question": "Previous groundwork review(s) found on this PR. How would you like to proceed?",
"header": "Existing groundwork reviews detected",
"options": [
{ "label": "Fresh review", "description": "Post a fresh review (dismisses previous)" },
{ "label": "Incremental review", "description": "Only new findings since last groundwork review" },
{ "label": "Cancel" }
],
"multiSelect": false
}]
}
If "Fresh review" → proceed normally. If "Incremental review" → store previous groundwork findings to exclude duplicates in Step 6. If "Cancel" → stop.
If non-groundwork reviews or review comments exist, produce a brief summary as existing_human_feedback. Include:
This summary will be passed to agents so they avoid restating already-raised points.
If existing_comments contain replies to previous groundwork-authored comments (detected by <!-- groundwork-review --> marker or matching author):
Parse changed_file_paths to determine which conditional agents to enable:
| Agent | Active | Skip when |
|---|---|---|
code-quality-reviewer | Always | -- |
test-quality-reviewer | Always | -- |
security-reviewer | Always | -- |
performance-reviewer | Always | -- |
code-simplifier | Always | -- |
housekeeper | Always | -- |
architecture-alignment-checker | Conditional | No architecture docs found at {{specs_dir}}/ |
design-consistency-checker | Conditional | No design system docs AND no CSS/styling files in changed_file_paths |
cloud-infrastructure-reviewer | Conditional | No IaC/config files in changed_file_paths |
conventions-reviewer | Conditional | No CLAUDE.md files found in the repo |
Skip spec-alignment-checker — it performs internal EARS requirement tracing which is not relevant to PR review.
Check for architecture and design docs:
{{specs_dir}}/architecture.md or {{specs_dir}}/architecture/{{specs_dir}}/design_system.md or {{specs_dir}}/design_system/Record skipped agents with reason in the aggregation table (verdict: skipped).
CRITICAL — Context budget: Do NOT read file contents, full diffs, or docs into this orchestrating context. Collect only file paths and metadata. Agents have Read/Grep/Glob tools and will read files in their own context windows.
Spawn all active agents in parallel using the Agent tool with subagent_type. Each agent receives:
Agent (subagent_type) | Context to Provide |
|---|---|
groundwork:code-quality-reviewer:code-quality-reviewer | changed_file_paths, diff_stat, pr_description, existing_human_feedback |
groundwork:test-quality-reviewer:test-quality-reviewer | changed_file_paths, diff_stat, pr_description, existing_human_feedback |
groundwork:security-reviewer:security-reviewer | changed_file_paths, diff_stat, pr_description, existing_human_feedback |
groundwork:performance-reviewer:performance-reviewer | changed_file_paths, diff_stat, pr_description, existing_human_feedback |
groundwork:code-simplifier:code-simplifier | changed_file_paths, diff_stat, pr_description, existing_human_feedback |
groundwork:housekeeper:housekeeper | changed_file_paths, diff_stat, pr_description, existing_human_feedback |
groundwork:architecture-alignment-checker:architecture-alignment-checker | changed_file_paths, diff_stat, pr_description, architecture_path, existing_human_feedback |
groundwork:design-consistency-checker:design-consistency-checker | changed_file_paths, diff_stat, pr_description, design_system_path, existing_human_feedback |
groundwork:cloud-infrastructure-reviewer:cloud-infrastructure-reviewer | changed_file_paths, diff_stat, pr_description, architecture_path, existing_human_feedback |
groundwork:conventions-reviewer:conventions-reviewer | changed_file_paths, diff_stat, pr_description, existing_human_feedback |
In each agent prompt, include these instructions:
Use the Read tool to examine these files. Do NOT expect file contents in this prompt — read them yourself.
PR Review context: You are reviewing a pull request, not a local task implementation. The
pr_descriptionbelow replacestask_definition. Focus ONLY on code within the PR diff. Do not flag issues in unchanged code unless the change directly impacts it.Existing feedback: Other reviewers have already raised the points summarized below. Do not restate these — focus on issues not yet covered.
Where pr_description is the PR title + body concatenated.
Each agent returns standard JSON:
{
"summary": "One-sentence assessment",
"score": 0-100,
"findings": [{"severity": "critical|major|minor", "category": "...", "file": "...", "line": N, "finding": "...", "recommendation": "..."}],
"verdict": "approve|request-changes"
}
## PR Review — Multi-Agent Report
| Agent | Score | Verdict | Critical | Major | Minor |
|-------|-------|---------|----------|-------|-------|
| Code Quality | 85 | approve | 0 | 1 | 2 |
| Test Quality | 88 | approve | 0 | 1 | 0 |
| Security | 95 | approve | 0 | 0 | 1 |
| Performance | 82 | approve | 0 | 1 | 1 |
| Code Simplifier | 92 | approve | 0 | 0 | 2 |
| Housekeeper | 90 | approve | 0 | 1 | 0 |
| Architecture | -- | skipped | -- | -- | -- |
| Design | -- | skipped | -- | -- | -- |
| Conventions | 95 | approve | 0 | 0 | 1 |
Group all findings by file. For findings within 3 lines of each other with semantically similar text:
If running in incremental mode (from Step 3a), also exclude findings that match previous groundwork review comments.
Parse pr_diff to determine which finding lines fall within diff hunks. Use the @@ hunk headers to map original/new line numbers.
line falls within a diff hunk on the RIGHT side → inline_findings[] (will be posted as inline review comments)line falls outside any diff hunk → general_findings[] (will be included in review body)AskUserQuestion:{
"questions": [{
"question": "- **Inline comments**: [N]\n- **General findings**: [N]\n- **Verdict**: [APPROVE / REQUEST_CHANGES / COMMENT]\n- **Agents run**: [N] ([N] skipped)",
"header": "PR Review Summary for [PR title] (#[pr_number])",
"options": [
{ "label": "Post review" },
{ "label": "Show full preview", "description": "Display all findings before posting" },
{ "label": "Cancel" }
],
"multiSelect": false
}]
}
If "Show full preview" → display the full aggregation table plus all findings with their file/line/severity, then re-ask:
{
"questions": [{
"question": "Review the findings above. Ready to post?",
"header": "Post review?",
"options": [
{ "label": "Post review" },
{ "label": "Cancel" }
],
"multiSelect": false
}]
}
If "Cancel" at either prompt → stop.
event: APPROVErequest-changes OR any critical/major findings exist → event: REQUEST_CHANGESevent: COMMENTEach inline finding becomes a review comment:
**[severity]** [category] *(from: agent1, agent2)*
[finding]
**Recommendation:** [recommendation]
Map each to the GitHub review comment format:
path: file path relative to repo rootline: line number on the RIGHT side of the diffside: RIGHTBuild the review body:
## Groundwork PR Review
[Aggregation table from 6a]
### General Findings
[List of general_findings not attached to inline comments]
---
*Reviewed by [N] agents | [N] findings ([N] inline, [N] general)*
<!-- groundwork-review -->
The <!-- groundwork-review --> marker at the end enables future identification of groundwork-authored reviews.
Post a single review using the GitHub API to avoid multiple notifications. Use --input - with a single-quoted heredoc to pass the JSON payload via stdin — this avoids shell interpolation issues with complex markdown bodies.
Do NOT generate Python, Node, or any helper scripts to post the review. Use the gh api --input - approach below directly.
cat <<'REVIEW_PAYLOAD' | gh api repos/<repo_slug>/pulls/<pr_number>/reviews --method POST --input -
{
"event": "<EVENT>",
"body": "<review_body>",
"comments": [
{
"path": "src/example.ts",
"line": 42,
"side": "RIGHT",
"body": "This condition doesn't handle the edge case where `input` is empty.\n\n```suggestion\nif (!input || input.length === 0) {\n return defaultValue;\n}\n```"
}
]
}
REVIEW_PAYLOAD
JSON escaping rules — inside the heredoc, the JSON must be valid:
\"\n\\<<'REVIEW_PAYLOAD') prevents shell expansion, so $, `, and \ are passed literally to ghFallback: If the API call fails due to an invalid line position, remove the offending comment from comments[], append it to the review body under a "Could not attach inline" heading, and retry using the same cat <<'REVIEW_PAYLOAD' | gh api --input - approach.
git checkout -
git branch -D pr-<pr_number>-review 2>/dev/null || true
## PR Review Posted
**[PR title]** (#[pr_number]) — [url]
- Event: [APPROVE / REQUEST_CHANGES / COMMENT]
- Inline comments: [N]
- General findings: [N]
- Agents: [N] run, [N] skipped
pr_description (PR title + body) instead of task_definition. They are prompted to focus on diff scope only.line + side=RIGHT API. Parses diff hunk headers (@@ -a,b +c,d @@) to determine which lines are in scope.<!-- groundwork-review --> HTML comment in review body enables identifying previous groundwork reviews for incremental mode.| Level | Meaning |
|---|---|
| critical | Serious defect — security vulnerability, data loss, crash |
| major | Significant issue — logic error, missing edge case, poor design |
| minor | Improvement opportunity — style, naming, minor optimization |
npx claudepluginhub etr/groundworkPerforms thorough pull request reviews with parallel agents for bugs, security issues, guideline compliance, and error handling. Provides confidence-scored feedback and batched GitHub comments.
Reviews a pull request through multiple quality lenses (security, architecture, test coverage, etc.) and presents a compiled analysis with inline comments. Use when the user wants a thorough PR review.