PR Review Skill
You review a GitHub PR end-to-end: fetch the diff, get expert analysis from momus, and post every review comment directly on the PR as inline GitHub comments. You do NOT stop after showing the summary — you MUST post the comments on GitHub before you are done.
Workflow
Step 1: Extract PR Information
Parse the PR identifier from the user's message:
- Full URL:
https://github.com/org/repo/pull/123→ extract number123and repoorg/repo - Short form:
PR #123or#123→ use number directly - If just a number with no repo context, infer from
gh repo view --json nameWithOwner -q '.nameWithOwner'
Step 2: Fetch PR Diff and Metadata
Run these commands (use --repo org/repo if the PR is in a different repo):
gh pr view <NUMBER> --json title,body,baseRefName,headRefName,files,additions,deletions,changedFiles,headRefOid
gh pr diff <NUMBER>
gh pr diff <NUMBER> --stat
gh repo view --json nameWithOwner -q '.nameWithOwner'
Save these for later — you need them to post comments:
PR_NUMBER= the PR numberCOMMIT_SHA=headRefOidfrom the metadataOWNER_REPO=nameWithOwner(e.g.juspay/hyperswitch-control-center)
Step 3: Send to Momus for Expert Review
Invoke momus synchronously (do NOT run in background — you need the result):
task(subagent_type="momus", run_in_background=false, load_skills=[], ...)
Prompt momus with:
Review the following GitHub Pull Request.
## PR Metadata
- Title: <title>
- Description: <body>
- Base: <baseRefName> → Head: <headRefName>
- Files changed: <changedFiles>, +<additions> -<deletions>
## Changed Files
<file list with stats>
## Full Diff
<complete diff>
## Instructions
For each issue found, return:
- file_path: exact relative path (e.g. src/components/Button.tsx)
- line_number: line in the NEW file (right side of diff, added/modified lines only)
- severity: critical | warning | suggestion | nitpick
- comment: clear, actionable description
- suggested_code: (optional) corrected snippet
Also return:
- summary: 2-3 sentence assessment
- verdict: approve | request_changes | comment_only
Return as JSON:
{
"summary": "...",
"verdict": "...",
"comments": [
{ "file_path": "...", "line_number": N, "severity": "...", "comment": "...", "suggested_code": "..." }
]
}
Step 4: Validate Momus Output
- Parse the JSON from momus
- Verify each comment has
file_path,line_number,severity,comment - Drop comments referencing files not in the PR's changed files list
- Sort by severity: critical → warning → suggestion → nitpick
Step 5: Show Review Summary
Display to the user:
## PR Review: <title>
**Verdict**: <approve/request_changes/comment_only>
**Summary**: <summary>
### Critical (N)
- `file:line` — comment
### Warnings (N)
- `file:line` — comment
### Suggestions (N)
- `file:line` — comment
### Nitpicks (N)
- `file:line` — comment
Step 6: Post All Comments on GitHub
THIS STEP IS NOT OPTIONAL. After showing the summary, you MUST post every comment on the PR. Do not ask the user for confirmation — just post them.
6a. Validate lines are in the diff
For each comment, check the line is actually in the diff:
gh pr diff <PR_NUMBER> -- <FILE_PATH>
- Line appears as a
+line on the right side → post as inline comment (Step 6b) - Line is NOT in the diff → post as PR-level comment instead (Step 6c)
6b. Post inline comments
Comment bodies often contain backticks, pipes, quotes, newlines, and code fences that WILL break shell parsing if passed as -f body="...". You MUST write the JSON payload to a temp file and use --input to avoid parse errors.
For each comment, write a JSON file and post it:
# 1. Build the JSON payload and write to a temp file
cat > /tmp/pr_comment.json << 'JSONEOF'
{
"body": "<COMMENT_BODY>",
"commit_id": "<COMMIT_SHA>",
"path": "<FILE_PATH>",
"line": <LINE_NUMBER>,
"side": "RIGHT"
}
JSONEOF
# 2. Post using --input to avoid shell escaping issues
gh api repos/<OWNER_REPO>/pulls/<PR_NUMBER>/comments \
--method POST \
--input /tmp/pr_comment.json
Building the body string: Construct <COMMENT_BODY> as a properly JSON-escaped string. Remember to escape:
"→\"- newlines →
\n - backslashes →
\\ - tabs →
\t
For a comment WITH suggested_code, the body should be:
**[<SEVERITY>]** <COMMENT>\n\n```suggestion\n<SUGGESTED_CODE>\n```
For a comment WITHOUT suggested_code, the body is simply:
**[<SEVERITY>]** <COMMENT>
CRITICAL: Always use the heredoc + --input pattern. NEVER pass the body as a -f body="..." argument — it will break on any comment containing backticks, pipes, or quotes.
6c. Post PR-level comments
For comments where the line is NOT in the diff, or that have no file/line, also use a temp file:
cat > /tmp/pr_comment.json << 'JSONEOF'
{
"body": "**[<SEVERITY>]** `<FILE_PATH>:<LINE_NUMBER>` — <COMMENT>"
}
JSONEOF
gh api repos/<OWNER_REPO>/issues/<PR_NUMBER>/comments \
--method POST \
--input /tmp/pr_comment.json
6d. Post the overall review summary
After all inline comments are posted, post the verdict as a PR comment:
cat > /tmp/pr_comment.json << 'JSONEOF'
{
"body": "## Review Summary\n\n**Verdict**: <VERDICT>\n\n<SUMMARY>\n\n---\n_Automated review via momus_"
}
JSONEOF
gh api repos/<OWNER_REPO>/issues/<PR_NUMBER>/comments \
--method POST \
--input /tmp/pr_comment.json
Step 7: Report Results
After posting, show what happened:
## Comments Posted
### Inline Comments
- `src/Button.tsx:42` — "[critical] ..." ✓
- `src/helpers.js:15` — "[warning] ..." ✓
### PR-Level Comments
- Review summary ✓
### Downgraded (line not in diff)
- `src/client.ts:200` — posted as PR comment instead
### Failed
- `src/api.ts:30` — error: <reason>
If a comment fails to post, log the error and continue with the rest.
Error Handling
ghnot authenticated → tell user to rungh auth login- Invalid PR number or repo → report error clearly
- Momus returns unparseable response → show raw output, ask to retry
- Individual comment fails to post → log error, continue with remaining
- All comments fail → suggest checking GitHub permissions
- Very large PR (50+ files / 2000+ lines) → split into batches, tell the user
Important
- Line numbers MUST be from the NEW file (right side of diff) — that's what GitHub expects
commit_idMUST beheadRefOid— not any other commit- Preserve exact file paths from the diff — no normalizing
- Fetch the FULL diff, not just filenames — momus needs actual code to review