git clone https://github.com/ComeOnOliver/skillshub
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/openclaw/openclaw/gh-issues" ~/.claude/skills/comeonoliver-skillshub-gh-issues && rm -rf "$T"
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.openclaw/skills && cp -r "$T/skills/openclaw/openclaw/gh-issues" ~/.openclaw/skills/comeonoliver-skillshub-gh-issues && rm -rf "$T"
skills/openclaw/openclaw/gh-issues/SKILL.mdgh-issues — Auto-fix GitHub Issues with Parallel Sub-agents
You are an orchestrator. Follow these 6 phases exactly. Do not skip phases.
IMPORTANT — No
gh CLI dependency. This skill uses curl + the GitHub REST API exclusively. The GH_TOKEN env var is already injected by OpenClaw. Pass it as a Bearer token in all API calls:
curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" ...
Phase 1 — Parse Arguments
Parse the arguments string provided after /gh-issues.
Positional:
- owner/repo — optional. This is the source repo to fetch issues from. If omitted, detect from the current git remote:
Extract owner/repo from the URL (handles both HTTPS and SSH formats).git remote get-url origin- HTTPS: https://github.com/owner/repo.git → owner/repo
- SSH: git@github.com:owner/repo.git → owner/repo If not in a git repo or no remote found, stop with an error asking the user to specify owner/repo.
Flags (all optional):
| Flag | Default | Description |
|---|---|---|
| --label | (none) | Filter by label (e.g. bug, ) |
| --limit | 10 | Max issues to fetch per poll |
| --milestone | (none) | Filter by milestone title |
| --assignee | (none) | Filter by assignee ( for self) |
| --state | open | Issue state: open, closed, all |
| --fork | (none) | Your fork () to push branches and open PRs from. Issues are fetched from the source repo; code is pushed to the fork; PRs are opened from the fork to the source repo. |
| --watch | false | Keep polling for new issues and PR reviews after each batch |
| --interval | 5 | Minutes between polls (only with ) |
| --dry-run | false | Fetch and display only — no sub-agents |
| --yes | false | Skip confirmation and auto-process all filtered issues |
| --reviews-only | false | Skip issue processing (Phases 2-5). Only run Phase 6 — check open PRs for review comments and address them. |
| --cron | false | Cron-safe mode: fetch issues and spawn sub-agents, exit without waiting for results. |
| --model | (none) | Model to use for sub-agents (e.g. , ). If not specified, uses the agent's default model. |
| --notify-channel | (none) | Telegram channel ID to send final PR summary to (e.g. -1002381931352). Only the final result with PR links is sent, not status updates. |
Store parsed values for use in subsequent phases.
Derived values:
- SOURCE_REPO = the positional owner/repo (where issues live)
- PUSH_REPO = --fork value if provided, otherwise same as SOURCE_REPO
- FORK_MODE = true if --fork was provided, false otherwise
If
is set: Skip directly to Phase 6. Run token resolution (from Phase 2) first, then jump to Phase 6.--reviews-only
If
is set:--cron
- Force
(skip confirmation)--yes - If
is also set, run token resolution then jump to Phase 6 (cron review mode)--reviews-only - Otherwise, proceed normally through Phases 2-5 with cron-mode behavior active
Phase 2 — Fetch Issues
Token Resolution: First, ensure GH_TOKEN is available. Check environment:
echo $GH_TOKEN
If empty, read from config:
cat ~/.openclaw/openclaw.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
If still empty, check
/data/.clawdbot/openclaw.json:
cat /data/.clawdbot/openclaw.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
Export as GH_TOKEN for subsequent commands:
export GH_TOKEN="<token>"
Build and run a curl request to the GitHub Issues API via exec:
curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \ "https://api.github.com/repos/{SOURCE_REPO}/issues?per_page={limit}&state={state}&{query_params}"
Where {query_params} is built from:
- labels={label} if --label was provided
- milestone={milestone} if --milestone was provided (note: API expects milestone number, so if user provides a title, first resolve it via GET /repos/{SOURCE_REPO}/milestones and match by title)
- assignee={assignee} if --assignee was provided (if @me, first resolve your username via
)GET /user
IMPORTANT: The GitHub Issues API also returns pull requests. Filter them out — exclude any item where pull_request key exists in the response object.
If in watch mode: Also filter out any issue numbers already in the PROCESSED_ISSUES set from previous batches.
Error handling:
- If curl returns an HTTP 401 or 403 → stop and tell the user:
"GitHub authentication failed. Please check your apiKey in the OpenClaw dashboard or in ~/.openclaw/openclaw.json under skills.entries.gh-issues."
- If the response is an empty array (after filtering) → report "No issues found matching filters" and stop (or loop back if in watch mode).
- If curl fails or returns any other error → report the error verbatim and stop.
Parse the JSON response. For each issue, extract: number, title, body, labels (array of label names), assignees, html_url.
Phase 3 — Present & Confirm
Display a markdown table of fetched issues:
| # | Title | Labels |
|---|---|---|
| 42 | Fix null pointer in parser | bug, critical |
| 37 | Add retry logic for API calls | enhancement |
If FORK_MODE is active, also display:
"Fork mode: branches will be pushed to {PUSH_REPO}, PRs will target
"{SOURCE_REPO}
If
--dry-run is active:
- Display the table and stop. Do not proceed to Phase 4.
If
--yes is active:
- Display the table for visibility
- Auto-process ALL listed issues without asking for confirmation
- Proceed directly to Phase 4
Otherwise: Ask the user to confirm which issues to process:
- "all" — process every listed issue
- Comma-separated numbers (e.g.
) — process only those42, 37 - "cancel" — abort entirely
Wait for user response before proceeding.
Watch mode note: On the first poll, always confirm with the user (unless --yes is set). On subsequent polls, auto-process all new issues without re-confirming (the user already opted in). Still display the table so they can see what's being processed.
Phase 4 — Pre-flight Checks
Run these checks sequentially via exec:
-
Dirty working tree check:
git status --porcelainIf output is non-empty, warn the user:
"Working tree has uncommitted changes. Sub-agents will create branches from HEAD — uncommitted changes will NOT be included. Continue?" Wait for confirmation. If declined, stop.
-
Record base branch:
git rev-parse --abbrev-ref HEADStore as BASE_BRANCH.
-
Verify remote access: If FORK_MODE:
- Verify the fork remote exists. Check if a git remote named
exists:fork
If it doesn't exist, add it:git remote get-url forkgit remote add fork https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git - Also verify origin (the source repo) is reachable:
git ls-remote --exit-code origin HEAD
If not FORK_MODE:
git ls-remote --exit-code origin HEADIf this fails, stop with: "Cannot reach remote origin. Check your network and git config."
- Verify the fork remote exists. Check if a git remote named
-
Verify GH_TOKEN validity:
curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $GH_TOKEN" https://api.github.com/userIf HTTP status is not 200, stop with:
"GitHub authentication failed. Please check your apiKey in the OpenClaw dashboard or in ~/.openclaw/openclaw.json under skills.entries.gh-issues."
-
Check for existing PRs: For each confirmed issue number N, run:
curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \ "https://api.github.com/repos/{SOURCE_REPO}/pulls?head={PUSH_REPO_OWNER}:fix/issue-{N}&state=open&per_page=1"(Where PUSH_REPO_OWNER is the owner portion of
) If the response array is non-empty, remove that issue from the processing list and report:PUSH_REPO"Skipping #{N} — PR already exists: {html_url}"
If all issues are skipped, report and stop (or loop back if in watch mode).
-
Check for in-progress branches (no PR yet = sub-agent still working): For each remaining issue number N (not already skipped by the PR check above), check if a
branch exists on the push repo (which may be a fork, not origin):fix/issue-{N}curl -s -o /dev/null -w "%{http_code}" \ -H "Authorization: Bearer $GH_TOKEN" \ "https://api.github.com/repos/{PUSH_REPO}/branches/fix/issue-{N}"If HTTP 200 → the branch exists on the push repo but no open PR was found for it in step 5. Skip that issue:
"Skipping #{N} — branch fix/issue-{N} exists on {PUSH_REPO}, fix likely in progress"
This check uses the GitHub API instead of
so it works correctly in fork mode (where branches are pushed to the fork, not origin).git ls-remoteIf all issues are skipped after this check, report and stop (or loop back if in watch mode).
-
Check claim-based in-progress tracking: This prevents duplicate processing when a sub-agent from a previous cron run is still working but hasn't pushed a branch or opened a PR yet.
Read the claims file (create empty
if missing):{}CLAIMS_FILE="/data/.clawdbot/gh-issues-claims.json" if [ ! -f "$CLAIMS_FILE" ]; then mkdir -p /data/.clawdbot echo '{}' > "$CLAIMS_FILE" fiParse the claims file. For each entry, check if the claim timestamp is older than 2 hours. If so, remove it (expired — the sub-agent likely finished or failed silently). Write back the cleaned file:
CLAIMS=$(cat "$CLAIMS_FILE") CUTOFF=$(date -u -d '2 hours ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-2H +%Y-%m-%dT%H:%M:%SZ) CLAIMS=$(echo "$CLAIMS" | jq --arg cutoff "$CUTOFF" 'to_entries | map(select(.value > $cutoff)) | from_entries') echo "$CLAIMS" > "$CLAIMS_FILE"For each remaining issue number N (not already skipped by steps 5 or 6), check if
exists as a key in the claims file.{SOURCE_REPO}#{N}If claimed and not expired → skip:
"Skipping #{N} — sub-agent claimed this issue {minutes}m ago, still within timeout window"
Where
is calculated from the claim timestamp to now.{minutes}If all issues are skipped after this check, report and stop (or loop back if in watch mode).
Phase 5 — Spawn Sub-agents (Parallel)
Cron mode (
is active):--cron
-
Sequential cursor tracking: Use a cursor file to track which issue to process next:
CURSOR_FILE="/data/.clawdbot/gh-issues-cursor-{SOURCE_REPO_SLUG}.json" # SOURCE_REPO_SLUG = owner-repo with slashes replaced by hyphens (e.g., openclaw-openclaw)Read the cursor file (create if missing):
if [ ! -f "$CURSOR_FILE" ]; then echo '{"last_processed": null, "in_progress": null}' > "$CURSOR_FILE" fi
: issue number of the last completed issue (or null if none)last_processed
: issue number currently being processed (or null)in_progress
-
Select next issue: Filter the fetched issues list to find the first issue where:
- Issue number > last_processed (if last_processed is set)
- AND issue is not in the claims file (not already in progress)
- AND no PR exists for the issue (checked in Phase 4 step 5)
- AND no branch exists on the push repo (checked in Phase 4 step 6)
-
If no eligible issue is found after the last_processed cursor, wrap around to the beginning (start from the oldest eligible issue).
-
If an eligible issue is found:
- Mark it as in_progress in the cursor file
- Spawn a single sub-agent for that one issue with
andcleanup: "keep"runTimeoutSeconds: 3600 - If
was provided, include--model
in the spawn configmodel: "{MODEL}" - If
was provided, include the channel in the task so the sub-agent can notify--notify-channel - Do NOT await the sub-agent result — fire and forget
- Write claim: After spawning, read the claims file, add
with the current ISO timestamp, and write it back{SOURCE_REPO}#{N} - Immediately report: "Spawned fix agent for #{N} — will create PR when complete"
- Exit the skill. Do not proceed to Results Collection or Phase 6.
-
If no eligible issue is found (all issues either have PRs, have branches, or are in progress), report "No eligible issues to process — all issues have PRs/branches or are in progress" and exit.
Normal mode (
is NOT active):
For each confirmed issue, spawn a sub-agent using sessions_spawn. Launch up to 8 concurrently (matching --cron
subagents.maxConcurrent: 8). If more than 8 issues, batch them — launch the next agent as each completes.
Write claims: After spawning each sub-agent, read the claims file, add
{SOURCE_REPO}#{N} with the current ISO timestamp, and write it back (same procedure as cron mode above). This covers interactive usage where watch mode might overlap with cron runs.
Sub-agent Task Prompt
For each issue, construct the following prompt and pass it to sessions_spawn. Variables to inject into the template:
- {SOURCE_REPO} — upstream repo where the issue lives
- {PUSH_REPO} — repo to push branches to (same as SOURCE_REPO unless fork mode)
- {FORK_MODE} — true/false
- {PUSH_REMOTE} —
if FORK_MODE, otherwiseforkorigin - {number}, {title}, {url}, {labels}, {body} — from the issue
- {BASE_BRANCH} — from Phase 4
- {notify_channel} — Telegram channel ID for notifications (empty if not set). Replace {notify_channel} in the template below with the value of
flag (or leave as empty string if not provided).--notify-channel
When constructing the task, replace all template variables including {notify_channel} with actual values.
You are a focused code-fix agent. Your task is to fix a single GitHub issue and open a PR. IMPORTANT: Do NOT use the gh CLI — it is not installed. Use curl with the GitHub REST API for all GitHub operations. First, ensure GH_TOKEN is set. Check: `echo $GH_TOKEN`. If empty, read from config: GH_TOKEN=$(cat ~/.openclaw/openclaw.json 2>/dev/null | jq -r '.skills.entries["gh-issues"].apiKey // empty') || GH_TOKEN=$(cat /data/.clawdbot/openclaw.json 2>/dev/null | jq -r '.skills.entries["gh-issues"].apiKey // empty') Use the token in all GitHub API calls: curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" ... <config> Source repo (issues): {SOURCE_REPO} Push repo (branches + PRs): {PUSH_REPO} Fork mode: {FORK_MODE} Push remote name: {PUSH_REMOTE} Base branch: {BASE_BRANCH} Notify channel: {notify_channel} </config> <issue> Repository: {SOURCE_REPO} Issue: #{number} Title: {title} URL: {url} Labels: {labels} Body: {body} </issue> <instructions> Follow these steps in order. If any step fails, report the failure and stop. 0. SETUP — Ensure GH_TOKEN is available:
export GH_TOKEN=$(node -e "const fs=require('fs'); const c=JSON.parse(fs.readFileSync('/data/.clawdbot/openclaw.json','utf8')); console.log(c.skills?.entries?.['gh-issues']?.apiKey || '')")
If that fails, also try:
export GH_TOKEN=$(cat ~/.openclaw/openclaw.json 2>/dev/null | node -e "const fs=require('fs');const d=JSON.parse(fs.readFileSync(0,'utf8'));console.log(d.skills?.entries?.['gh-issues']?.apiKey||'')")
Verify: echo "Token: ${GH_TOKEN:0:10}..." 1. CONFIDENCE CHECK — Before implementing, assess whether this issue is actionable: - Read the issue body carefully. Is the problem clearly described? - Search the codebase (grep/find) for the relevant code. Can you locate it? - Is the scope reasonable? (single file/function = good, whole subsystem = bad) - Is a specific fix suggested or is it a vague complaint? Rate your confidence (1-10). If confidence < 7, STOP and report: > "Skipping #{number}: Low confidence (score: N/10) — [reason: vague requirements | cannot locate code | scope too large | no clear fix suggested]" Only proceed if confidence >= 7. 1. UNDERSTAND — Read the issue carefully. Identify what needs to change and where. 2. BRANCH — Create a feature branch from the base branch: git checkout -b fix/issue-{number} {BASE_BRANCH} 3. ANALYZE — Search the codebase to find relevant files: - Use grep/find via exec to locate code related to the issue - Read the relevant files to understand the current behavior - Identify the root cause 4. IMPLEMENT — Make the minimal, focused fix: - Follow existing code style and conventions - Change only what is necessary to fix the issue - Do not add unrelated changes or new dependencies without justification 5. TEST — Discover and run the existing test suite if one exists: - Look for package.json scripts, Makefile targets, pytest, cargo test, etc. - Run the relevant tests - If tests fail after your fix, attempt ONE retry with a corrected approach - If tests still fail, report the failure 6. COMMIT — Stage and commit your changes: git add {changed_files} git commit -m "fix: {short_description} Fixes {SOURCE_REPO}#{number}" 7. PUSH — Push the branch: First, ensure the push remote uses token auth and disable credential helpers: git config --global credential.helper "" git remote set-url {PUSH_REMOTE} https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git Then push: GIT_ASKPASS=true git push -u {PUSH_REMOTE} fix/issue-{number} 8. PR — Create a pull request using the GitHub API: If FORK_MODE is true, the PR goes from your fork to the source repo: - head = "{PUSH_REPO_OWNER}:fix/issue-{number}" - base = "{BASE_BRANCH}" - PR is created on {SOURCE_REPO} If FORK_MODE is false: - head = "fix/issue-{number}" - base = "{BASE_BRANCH}" - PR is created on {SOURCE_REPO} curl -s -X POST \ -H "Authorization: Bearer $GH_TOKEN" \ -H "Accept: application/vnd.github+json" \ https://api.github.com/repos/{SOURCE_REPO}/pulls \ -d '{ "title": "fix: {title}", "head": "{head_value}", "base": "{BASE_BRANCH}", "body": "## Summary\n\n{one_paragraph_description_of_fix}\n\n## Changes\n\n{bullet_list_of_changes}\n\n## Testing\n\n{what_was_tested_and_results}\n\nFixes {SOURCE_REPO}#{number}" }' Extract the `html_url` from the response — this is the PR link. 9. REPORT — Send back a summary: - PR URL (the html_url from step 8) - Files changed (list) - Fix summary (1-2 sentences) - Any caveats or concerns 10. NOTIFY (if notify_channel is set) — If {notify_channel} is not empty, send a notification to the Telegram channel:
Use the message tool with:
- action: "send"
- channel: "telegram"
- target: "{notify_channel}"
- message: "✅ PR Created: {SOURCE_REPO}#{number}
{title}
{pr_url}
Files changed: {files_changed_list}"
</instructions> <constraints> - No force-push, no modifying the base branch - No unrelated changes or gratuitous refactoring - No new dependencies without strong justification - If the issue is unclear or too complex to fix confidently, report your analysis instead of guessing - Do NOT use the gh CLI — it is not available. Use curl + GitHub REST API for all GitHub operations. - GH_TOKEN is already in the environment — do NOT prompt for auth - Time limit: you have 60 minutes max. Be thorough — analyze properly, test your fix, don't rush. </constraints>
Spawn configuration per sub-agent:
- runTimeoutSeconds: 3600 (60 minutes)
- cleanup: "keep" (preserve transcripts for review)
- If
was provided, include--model
in the spawn configmodel: "{MODEL}"
Timeout Handling
If a sub-agent exceeds 60 minutes, record it as:
"#{N} — Timed out (issue may be too complex for auto-fix)"
Results Collection
If
is active: Skip this section entirely — the orchestrator already exited after spawning in Phase 5.--cron
After ALL sub-agents complete (or timeout), collect their results. Store the list of successfully opened PRs in
OPEN_PRS (PR number, branch name, issue number, PR URL) for use in Phase 6.
Present a summary table:
| Issue | Status | PR | Notes |
|---|---|---|---|
| #42 Fix null pointer | PR opened | https://github.com/.../pull/99 | 3 files changed |
| #37 Add retry logic | Failed | -- | Could not identify target code |
| #15 Update docs | Timed out | -- | Too complex for auto-fix |
| #8 Fix race condition | Skipped | -- | PR already exists |
Status values:
- PR opened — success, link to PR
- Failed — sub-agent could not complete (include reason in Notes)
- Timed out — exceeded 60-minute limit
- Skipped — existing PR detected in pre-flight
End with a one-line summary:
"Processed {N} issues: {success} PRs opened, {failed} failed, {skipped} skipped."
Send notification to channel (if --notify-channel is set): If
--notify-channel was provided, send the final summary to that Telegram channel using the message tool:
Use the message tool with: - action: "send" - channel: "telegram" - target: "{notify-channel}" - message: "✅ GitHub Issues Processed Processed {N} issues: {success} PRs opened, {failed} failed, {skipped} skipped. {PR_LIST}" Where PR_LIST includes only successfully opened PRs in format: • #{issue_number}: {PR_url} ({notes})
Then proceed to Phase 6.
Phase 6 — PR Review Handler
This phase monitors open PRs (created by this skill or pre-existing
fix/issue-* PRs) for review comments and spawns sub-agents to address them.
When this phase runs:
- After Results Collection (Phases 2-5 completed) — checks PRs that were just opened
- When
flag is set — skips Phases 2-5 entirely, runs only this phase--reviews-only - In watch mode — runs every poll cycle after checking for new issues
Cron review mode (
):
When both --cron --reviews-only
--cron and --reviews-only are set:
- Run token resolution (Phase 2 token section)
- Discover open
PRs (Step 6.1)fix/issue-* - Fetch review comments (Step 6.2)
- Analyze comment content for actionability (Step 6.3)
- If actionable comments are found, spawn ONE review-fix sub-agent for the first PR with unaddressed comments — fire-and-forget (do NOT await result)
- Use
andcleanup: "keep"runTimeoutSeconds: 3600 - If
was provided, include--model
in the spawn configmodel: "{MODEL}"
- Use
- Report: "Spawned review handler for PR #{N} — will push fixes when complete"
- Exit the skill immediately. Do not proceed to Step 6.5 (Review Results).
If no actionable comments found, report "No actionable review comments found" and exit.
Normal mode (non-cron) continues below:
Step 6.1 — Discover PRs to Monitor
Collect PRs to check for review comments:
If coming from Phase 5: Use the
OPEN_PRS list from Results Collection.
If
or subsequent watch cycle: Fetch all open PRs with --reviews-only
fix/issue- branch pattern:
curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \ "https://api.github.com/repos/{SOURCE_REPO}/pulls?state=open&per_page=100"
Filter to only PRs where
head.ref starts with fix/issue-.
For each PR, extract:
number (PR number), head.ref (branch name), html_url, title, body.
If no PRs found, report "No open fix/ PRs to monitor" and stop (or loop back if in watch mode).
Step 6.2 — Fetch All Review Sources
For each PR, fetch reviews from multiple sources:
Fetch PR reviews:
curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \ "https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/reviews"
Fetch PR review comments (inline/file-level):
curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \ "https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/comments"
Fetch PR issue comments (general conversation):
curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \ "https://api.github.com/repos/{SOURCE_REPO}/issues/{pr_number}/comments"
Fetch PR body for embedded reviews: Some review tools (like Greptile) embed their feedback directly in the PR body. Check for:
markers<!-- greptile_comment -->- Other structured review sections in the PR body
curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \ "https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}"
Extract the
body field and parse for embedded review content.
Step 6.3 — Analyze Comments for Actionability
Determine the bot's own username for filtering:
curl -s -H "Authorization: Bearer $GH_TOKEN" https://api.github.com/user | jq -r '.login'
Store as
BOT_USERNAME. Exclude any comment where user.login equals BOT_USERNAME.
For each comment/review, analyze the content to determine if it requires action:
NOT actionable (skip):
- Pure approvals or "LGTM" without suggestions
- Bot comments that are informational only (CI status, auto-generated summaries without specific requests)
- Comments already addressed (check if bot replied with "Addressed in commit...")
- Reviews with state
and no inline comments requesting changesAPPROVED
IS actionable (requires attention):
- Reviews with state
CHANGES_REQUESTED - Reviews with state
that contain specific requests:COMMENTED- "this test needs to be updated"
- "please fix", "change this", "update", "can you", "should be", "needs to"
- "will fail", "will break", "causes an error"
- Mentions of specific code issues (bugs, missing error handling, edge cases)
- Inline review comments pointing out issues in the code
- Embedded reviews in PR body that identify:
- Critical issues or breaking changes
- Test failures expected
- Specific code that needs attention
- Confidence scores with concerns
Parse embedded review content (e.g., Greptile): Look for sections marked with
<!-- greptile_comment --> or similar. Extract:
- Summary text
- Any mentions of "Critical issue", "needs attention", "will fail", "test needs to be updated"
- Confidence scores below 4/5 (indicates concerns)
Build actionable_comments list with:
- Source (review, inline comment, PR body, etc.)
- Author
- Body text
- For inline: file path and line number
- Specific action items identified
If no actionable comments found across any PR, report "No actionable review comments found" and stop (or loop back if in watch mode).
Step 6.4 — Present Review Comments
Display a table of PRs with pending actionable comments:
| PR | Branch | Actionable Comments | Sources | |----|--------|---------------------|---------| | #99 | fix/issue-42 | 2 comments | @reviewer1, greptile | | #101 | fix/issue-37 | 1 comment | @reviewer2 |
If
--yes is NOT set and this is not a subsequent watch poll: ask the user to confirm which PRs to address ("all", comma-separated PR numbers, or "skip").
Step 6.5 — Spawn Review Fix Sub-agents (Parallel)
For each PR with actionable comments, spawn a sub-agent. Launch up to 8 concurrently.
Review fix sub-agent prompt:
You are a PR review handler agent. Your task is to address review comments on a pull request by making the requested changes, pushing updates, and replying to each comment. IMPORTANT: Do NOT use the gh CLI — it is not installed. Use curl with the GitHub REST API for all GitHub operations. First, ensure GH_TOKEN is set. Check: echo $GH_TOKEN. If empty, read from config: GH_TOKEN=$(cat ~/.openclaw/openclaw.json 2>/dev/null | jq -r '.skills.entries["gh-issues"].apiKey // empty') || GH_TOKEN=$(cat /data/.clawdbot/openclaw.json 2>/dev/null | jq -r '.skills.entries["gh-issues"].apiKey // empty') <config> Repository: {SOURCE_REPO} Push repo: {PUSH_REPO} Fork mode: {FORK_MODE} Push remote: {PUSH_REMOTE} PR number: {pr_number} PR URL: {pr_url} Branch: {branch_name} </config> <review_comments> {json_array_of_actionable_comments} Each comment has: - id: comment ID (for replying) - user: who left it - body: the comment text - path: file path (for inline comments) - line: line number (for inline comments) - diff_hunk: surrounding diff context (for inline comments) - source: where the comment came from (review, inline, pr_body, greptile, etc.) </review_comments> <instructions> Follow these steps in order: 0. SETUP — Ensure GH_TOKEN is available:
export GH_TOKEN=$(node -e "const fs=require('fs'); const c=JSON.parse(fs.readFileSync('/data/.clawdbot/openclaw.json','utf8')); console.log(c.skills?.entries?.['gh-issues']?.apiKey || '')")
Verify: echo "Token: ${GH_TOKEN:0:10}..." 1. CHECKOUT — Switch to the PR branch: git fetch {PUSH_REMOTE} {branch_name} git checkout {branch_name} git pull {PUSH_REMOTE} {branch_name} 2. UNDERSTAND — Read ALL review comments carefully. Group them by file. Understand what each reviewer is asking for. 3. IMPLEMENT — For each comment, make the requested change: - Read the file and locate the relevant code - Make the change the reviewer requested - If the comment is vague or you disagree, still attempt a reasonable fix but note your concern - If the comment asks for something impossible or contradictory, skip it and explain why in your reply 4. TEST — Run existing tests to make sure your changes don't break anything: - If tests fail, fix the issue or revert the problematic change - Note any test failures in your replies 5. COMMIT — Stage and commit all changes in a single commit: git add {changed_files} git commit -m "fix: address review comments on PR #{pr_number} Addresses review feedback from {reviewer_names}" 6. PUSH — Push the updated branch: git config --global credential.helper "" git remote set-url {PUSH_REMOTE} https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git GIT_ASKPASS=true git push {PUSH_REMOTE} {branch_name} 7. REPLY — For each addressed comment, post a reply: For inline review comments (have a path/line), reply to the comment thread: curl -s -X POST \ -H "Authorization: Bearer $GH_TOKEN" \ -H "Accept: application/vnd.github+json" \ https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/comments/{comment_id}/replies \ -d '{"body": "Addressed in commit {short_sha} — {brief_description_of_change}"}' For general PR comments (issue comments), reply on the PR: curl -s -X POST \ -H "Authorization: Bearer $GH_TOKEN" \ -H "Accept: application/vnd.github+json" \ https://api.github.com/repos/{SOURCE_REPO}/issues/{pr_number}/comments \ -d '{"body": "Addressed feedback from @{reviewer}:\n\n{summary_of_changes_made}\n\nUpdated in commit {short_sha}"}' For comments you could NOT address, reply explaining why: "Unable to address this comment: {reason}. This may need manual review." 8. REPORT — Send back a summary: - PR URL - Number of comments addressed vs skipped - Commit SHA - Files changed - Any comments that need manual attention </instructions> <constraints> - Only modify files relevant to the review comments - Do not make unrelated changes - Do not force-push — always regular push - If a comment contradicts another comment, address the most recent one and flag the conflict - Do NOT use the gh CLI — use curl + GitHub REST API - GH_TOKEN is already in the environment — do not prompt for auth - Time limit: 60 minutes max </constraints>
Spawn configuration per sub-agent:
- runTimeoutSeconds: 3600 (60 minutes)
- cleanup: "keep" (preserve transcripts for review)
- If
was provided, include--model
in the spawn configmodel: "{MODEL}"
Step 6.6 — Review Results
After all review sub-agents complete, present a summary:
| PR | Comments Addressed | Comments Skipped | Commit | Status | |----|-------------------|-----------------|--------|--------| | #99 fix/issue-42 | 3 | 0 | abc123f | All addressed | | #101 fix/issue-37 | 1 | 1 | def456a | 1 needs manual review |
Add comment IDs from this batch to
ADDRESSED_COMMENTS set to prevent re-processing.
Watch Mode (if --watch is active)
After presenting results from the current batch:
- Add all issue numbers from this batch to the running set PROCESSED_ISSUES.
- Add all addressed comment IDs to ADDRESSED_COMMENTS.
- Tell the user:
"Next poll in {interval} minutes... (say 'stop' to end watch mode)"
- Sleep for {interval} minutes.
- Go back to Phase 2 — Fetch Issues. The fetch will automatically filter out:
- Issues already in PROCESSED_ISSUES
- Issues that have existing fix/issue-{N} PRs (caught in Phase 4 pre-flight)
- After Phases 2-5 (or if no new issues), run Phase 6 to check for new review comments on ALL tracked PRs (both newly created and previously opened).
- If no new issues AND no new actionable review comments → report "No new activity. Polling again in {interval} minutes..." and loop back to step 4.
- The user can say "stop" at any time to exit watch mode. When stopping, present a final cumulative summary of ALL batches — issues processed AND review comments addressed.
Context hygiene between polls — IMPORTANT: Only retain between poll cycles:
- PROCESSED_ISSUES (set of issue numbers)
- ADDRESSED_COMMENTS (set of comment IDs)
- OPEN_PRS (list of tracked PRs: number, branch, URL)
- Cumulative results (one line per issue + one line per review batch)
- Parsed arguments from Phase 1
- BASE_BRANCH, SOURCE_REPO, PUSH_REPO, FORK_MODE, BOT_USERNAME Do NOT retain issue bodies, comment bodies, sub-agent transcripts, or codebase analysis between polls.