skills/github-pr-reviewer/SKILL.md
Create an automation that reviews GitHub pull requests when a configurable trigger label is applied. Polls GitHub deterministically, starts one OpenHands review conversation per label event, inspects full repository and PR context, and posts the final review comment back to GitHub.
npx skillsauth add openhands/extensions github-pr-reviewerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Create a cron automation that watches a GitHub repository for pull requests
with a review trigger label, starts an OpenHands review conversation once per
label event, and posts the AI review as a GitHub comment.
Windows PowerShell equivalents for the setup, packaging, upload, and API-check shell snippets are in references/windows.md.
The automation script is deterministic: PR discovery, label-event tracking, state persistence, stale-result suppression, and GitHub comment posting are handled in Python. The LLM is invoked only for the review itself.
Verify that the following secret is set in OpenHands Settings -> Secrets:
| Secret name | Token type | Minimum permissions |
|---|---|---|
| GITHUB_PERSONAL_ACCESS_TOKEN | Classic PAT | repo for private repos or public_repo for public repos |
| GITHUB_PERSONAL_ACCESS_TOKEN | Fine-grained PAT | Contents: Read, Metadata: Read, Pull requests: Read, Issues: Read and Write |
Check with:
curl -s https://api.github.com/user \
-H "Authorization: Bearer $GITHUB_PERSONAL_ACCESS_TOKEN" \
| python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('login') or d.get('message'))"
If the token is missing or invalid, inform the user and stop.
Follow these steps in order.
GITHUB_PERSONAL_ACCESS_TOKENRun the curl check above.
{"message": "Bad credentials"}: tell the user the
token is invalid and ask them to update it. Stop.Ask: "Which GitHub repository should be monitored?
(Format: owner/repo, e.g. myorg/backend)"
Validate access:
curl -s "https://api.github.com/repos/{owner}/{repo}" \
-H "Authorization: Bearer $GITHUB_PERSONAL_ACCESS_TOKEN" \
| python3 -c "
import json, sys
d = json.load(sys.stdin)
if 'message' in d:
print('ERROR:', d['message'])
else:
print(f\"Accessible. Private: {d.get('private')}. Permissions: {d.get('permissions')}\")
"
Record REPO = "{owner}/{repo}".
Ask: "Which PR label should trigger a review?
(Press Enter for the default: openhands-review.)"
Record the answer as TRIGGER_LABEL. If the label does not exist yet, tell the
user that GitHub will still record the event once the label is created and
applied to a PR.
The automation reviews a PR when it sees the latest matching labeled event for
that label. To request another review later, remove and re-apply the label.
Ask: *"What review tone should the reviewer use?
Map the choice to REVIEW_TONE:
| Answer | REVIEW_TONE | REVIEW_STYLE_INSTRUCTIONS |
|---|---|---|
| 1 / Enter | "thorough" | "" |
| 2 | "concise" | "" |
| 3 | "friendly" | "" |
| Custom text, e.g. strict but kind | "thorough" | the custom text verbatim |
Ask: "How often should the automation poll for labeled PRs?
(Press Enter for the default: every 5 minutes.
Use a cron expression for a different interval, e.g. 0 * * * * = hourly)"
Default: */5 * * * *.
Record as CRON_SCHEDULE.
Read scripts/main.py from this skill's directory. Apply exactly five constant
substitutions near the top of the file:
| Placeholder | Replace with |
|---|---|
| REPO = "owner/repo" | REPO = "{owner_repo}" |
| TRIGGER_LABEL = "openhands-review" | TRIGGER_LABEL = "{trigger_label}" |
| REVIEW_TONE = "thorough" | REVIEW_TONE = "{review_tone}" |
| REVIEW_STYLE_INSTRUCTIONS = "" | REVIEW_STYLE_INSTRUCTIONS = "{style_instructions}" |
| DEFAULT_OPENHANDS_URL = "http://localhost:8000" | leave unchanged unless the user has a preference |
Use a safe string writer such as json.dumps(value) when inserting user-provided
repository names, labels, or style instructions into Python string literals.
Write the customized script to a temporary build directory:
mkdir -p /tmp/pr-reviewer-build
# write the customized main.py to /tmp/pr-reviewer-build/main.py
Validate syntax before packaging:
python3 -m py_compile /tmp/pr-reviewer-build/main.py && echo "Syntax OK"
Fix any syntax errors before proceeding.
Determine the Automation backend URL and auth from the <RUNTIME_SERVICES>
block in your system context:
url_from_agentX-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEYtar -czf /tmp/pr-reviewer.tar.gz -C /tmp/pr-reviewer-build .
TARBALL_PATH=$(curl -s -X POST \
"${OPENHANDS_HOST}/api/automation/v1/uploads?name=github-pr-reviewer" \
-H "X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY" \
-H "Content-Type: application/gzip" \
--data-binary @/tmp/pr-reviewer.tar.gz \
| python3 -c "import json,sys; print(json.load(sys.stdin)['tarball_path'])")
echo "Uploaded: $TARBALL_PATH"
curl -s -X POST "${OPENHANDS_HOST}/api/automation/v1" \
-H "X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"GitHub PR Reviewer: {owner}/{repo} label {trigger_label}\",
\"trigger\": {\"type\": \"cron\", \"schedule\": \"{cron_schedule}\"},
\"tarball_path\": \"$TARBALL_PATH\",
\"entrypoint\": \"python3 main.py\",
\"timeout\": 300
}" | python3 -m json.tool
Record the returned id.
Tell the user:
✅ GitHub PR Reviewer is running!
- Automation ID:
{id}- Repository:
{owner}/{repo}- Trigger label:
{trigger_label}- Review tone:
{tone}- Polling schedule:
{cron_schedule}- State file:
~/.openhands/workspaces/automation-state/github_pr_reviewer_label_event_{id}.jsonApply the
{trigger_label}label to a pull request to queue a review. Each label event is processed once. To request another review, remove and re-apply the label.
Each cron run executes main.py, which:
references/state-schema.md).GITHUB_PERSONAL_ACCESS_TOKEN and repository access.TRIGGER_LABEL:
labeled issue event.status: "active".idle, finished, error, or stuck,
posts the agent's final response as a GitHub comment and marks the review
closed.references/state-schema.md - State JSON schema, field definitions, and
review lifecycle diagram.scripts/main.py - The complete automation script. Customize the five
constants at the top before packaging.| Symptom | Likely cause | Fix |
|---|---|---|
| Bot never queues reviews | Trigger label not present or no matching labeled event | Apply the configured label to the PR |
| "Bad credentials" in run logs | Token expired | Rotate and update GITHUB_PERSONAL_ACCESS_TOKEN |
| 404 on repo access | Repo name wrong or no access | Re-check owner/repo and token permissions |
| Same PR not reviewed after new commits | Label event was already processed | Remove and re-apply the trigger label |
| Review result never posts | Conversation still running or stuck | Open the conversation link from the acknowledgement comment |
| Stale review suppressed | PR head SHA changed while the agent was reviewing | Re-apply the trigger label after the latest commit |
tools
Iterate on a GitHub pull request — drive it through CI, code review, and QA until it is merge-ready. Poll verification layers with `gh` CLI, diagnose and fix CI failures, address review feedback, retry flaky checks, push fixes, and repeat. The agent is the orchestration loop.
development
Guides technical explanations toward flowing, direct, conversational prose. This skill should be used for engineering chat, design discussion, architecture analysis, code-review explanations, and technical recommendations that should be concise without becoming fragmented or vague.
tools
This skill should be used when the user asks to "set up a Jira automation to create pull requests", "poll Jira for create-pr issues", "automatically create GitHub PRs from Jira tickets", "deploy a Jira issue-to-PR automation", "create a Jira to GitHub PR workflow", or mentions automating GitHub PR creation from a Jira label. Deploys a cron-based OpenHands automation that watches a Jira Cloud project for issues labeled with a configurable label (default: "create-pr") and spawns an agent conversation to create a GitHub pull request for each new issue found. The target GitHub repository is read from the body of the Jira ticket - no repo parameter is required at deploy time.
tools
This skill should be used when the user asks to "create an automation", "schedule a task", "set up a cron job", "webhook integration", "event-triggered automation", or mentions automations, scheduled tasks, cron scheduling, or webhook events in OpenHands Cloud.