| name | gh-issues |
| description | Fetch GitHub issues, spawn sub-agents to implement fixes and open PRs, then monitor and address PR review comments. Usage: /gh-issues [owner/repo] [--label bug] [--limit 5] [--milestone v1.0] [--assignee @me] [--fork user/repo] [--watch] [--interval 5] [--reviews-only] [--cron] [--dry-run] [--model glm-5] [--notify-channel -1002381931352] |
| user-invocable | true |
| metadata | {"openclaw":{"requires":{"bins":["curl","git","gh"]},"primaryEnv":"GH_TOKEN"}} |
gh-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 metiq. 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:
git remote get-url origin
Extract owner/repo from the URL (handles both HTTPS and SSH formats).
Flags (all optional):
| Flag | Default | Description |
|---|
| --label | (none) | Filter by label (e.g. bug, enhancement) |
| --limit | 10 | Max issues to fetch per poll |
| --milestone | (none) | Filter by milestone title |
| --assignee | (none) | Filter by assignee (@me for self) |
| --state | open | Issue state: open, closed, all |
| --fork | (none) | Your fork (user/repo) 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 --watch) |
| --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. glm-5, zai/glm-5). 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 --reviews-only is set: Skip directly to Phase 6. Run token resolution (from Phase 2) first, then jump to Phase 6.
If --cron is set:
- Force
--yes (skip confirmation)
- If
--reviews-only is also set, run token resolution then jump to Phase 6 (cron review mode)
- 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 ~/.metiq/config.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
If still empty, check /data/.clawdbot/metiq.json:
cat /data/.clawdbot/metiq.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 metiq config or in ~/.metiq/config.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.
42, 37) — process only those
- "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:
cat ~/.metiq/config.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
-
Dirty working tree check:
git status --porcelain
If 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.
cat /data/.clawdbot/metiq.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
git rev-parse --abbrev-ref HEAD
Store as BASE_BRANCH.
-
Verify remote access:
If FORK_MODE:
If not FORK_MODE:
git ls-remote --exit-code origin HEAD
If this fails, stop with: "Cannot reach remote origin. Check your network and git config."
-
Verify GH_TOKEN validity:
curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $GH_TOKEN" https://api.github.com/user
If HTTP status is not 200, stop with:
"GitHub authentication failed. Please check your apiKey in the metiq config or in ~/.metiq/config.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 PUSH_REPO)
If the response array is non-empty, remove that issue from the processing list and report:
"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 fix/issue-{N} branch exists on the push repo (which may be a fork, not origin):
"GitHub authentication failed. Please check your apiKey in the metiq config or in ~/.metiq/config.json under skills.entries.gh-issues."
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 git ls-remote so it works correctly in fork mode (where branches are pushed to the fork, not origin).
cat ~/.metiq/config.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
If 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"
fi
Parse 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:
cat /data/.clawdbot/metiq.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
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 `{SOURCE_REPO}#{N}` exists as a key in the claims file.
If claimed and not expired → skip:
> "Skipping #{N} — sub-agent claimed this issue {minutes}m ago, still within timeout window"
Where `{minutes}` is calculated from the claim timestamp to now.
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 (--cron is active):
-
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
last_processed: issue number of the last completed issue (or null if none)
in_progress: issue number currently being processed (or null)
-
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
cleanup: "keep" and runTimeoutSeconds: 3600
- If
--model was provided, include model: "{MODEL}" in the spawn config
- If
--notify-channel was provided, include the channel in the task so the sub-agent can notify
- Do NOT await the sub-agent result — fire and forget
- Write claim: After spawning, read the claims file, add
{SOURCE_REPO}#{N} with the current ISO timestamp, and write it back
- 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 (--cron is NOT active):
For each confirmed issue, spawn a sub-agent using sessions_spawn. Launch up to 8 concurrently (matching 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.
cat ~/.metiq/config.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
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} —
fork if FORK_MODE, otherwise origin
- {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
--notify-channel flag (or leave as empty string if not provided).
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 ~/.metiq/config.json 2>/dev/null | jq -r '.skills.entries["gh-issues"].apiKey // empty') || GH_TOKEN=$(cat /data/.clawdbot/metiq.json 2>/dev/null | jq -r '.skills.entries["gh-issues"].apiKey // empty')
> "GitHub authentication failed. Please check your apiKey in the metiq config or in ~/.metiq/config.json under skills.entries.gh-issues."
cat /data/.clawdbot/metiq.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
<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/metiq.json','utf8')); console.log(c.skills?.entries?.['gh-issues']?.apiKey || '')")
If that fails, also try:
export GH_TOKEN=$(cat ~/.metiq/config.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
cat ~/.metiq/config.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
- 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}
cat /data/.clawdbot/metiq.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
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:
"GitHub authentication failed. Please check your apiKey in the metiq config or in ~/.metiq/config.json under skills.entries.gh-issues."
- 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.
cat ~/.metiq/config.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
Spawn configuration per sub-agent:
- runTimeoutSeconds: 3600 (60 minutes)
- cleanup: "keep" (preserve transcripts for review)
- If
--model was provided, include model: "{MODEL}" in the spawn config
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 --cron is active: Skip this section entirely — the orchestrator already exited after spawning in Phase 5.
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)
cat /data/.clawdbot/metiq.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
- 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
--reviews-only flag is set — skips Phases 2-5 entirely, runs only this phase
- In watch mode — runs every poll cycle after checking for new issues
Cron review mode (--cron --reviews-only):
When both --cron and --reviews-only are set:
- Run token resolution (Phase 2 token section)
- Discover open
fix/issue-* PRs (Step 6.1)
- Fetch review comments (Step 6.2)
- Analyze comment content for actionability (Step 6.3)
cat ~/.metiq/config.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
- Use
cleanup: "keep" and runTimeoutSeconds: 3600
- If
--model was provided, include model: "{MODEL}" in the spawn config
- 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
"GitHub authentication failed. Please check your apiKey in the metiq config or in ~/.metiq/config.json under skills.entries.gh-issues."
Collect PRs to check for review comments:
If coming from Phase 5: Use the OPEN_PRS list from Results Collection.
If --reviews-only or subsequent watch cycle: Fetch all open PRs with 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):
cat /data/.clawdbot/metiq.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
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:
- `<!-- greptile_comment -->` markers
- 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
cat ~/.metiq/config.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
- Comments already addressed (check if bot replied with "Addressed in commit...")
- Reviews with state `APPROVED` and no inline comments requesting changes
**IS actionable (requires attention):**
- Reviews with state `CHANGES_REQUESTED`
- Reviews with state `COMMENTED` that contain specific requests:
- "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 |
GH_TOKEN=$(cat ~/.metiq/config.json 2>/dev/null | jq -r '.skills.entries["gh-issues"].apiKey // empty') || GH_TOKEN=$(cat /data/.clawdbot/metiq.json 2>/dev/null | jq -r '.skills.entries["gh-issues"].apiKey // empty')
| #99 | fix/issue-42 | 2 comments | @reviewer1, greptile |
| #101 | fix/issue-37 | 1 comment | @reviewer2 |
> "GitHub authentication failed. Please check your apiKey in the metiq config or in ~/.metiq/config.json under skills.entries.gh-issues."
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").
cat /data/.clawdbot/metiq.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
### 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 ~/.metiq/config.json 2>/dev/null | jq -r '.skills.entries["gh-issues"].apiKey // empty') || GH_TOKEN=$(cat /data/.clawdbot/metiq.json 2>/dev/null | jq -r '.skills.entries["gh-issues"].apiKey // empty')
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}
<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)
cat ~/.metiq/config.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
- source: where the comment came from (review, inline, pr_body, greptile, etc.)
</review_comments>
Follow these steps in order:
- SETUP — Ensure GH_TOKEN is available:
export GH_TOKEN=$(node -e "const fs=require('fs'); const c=JSON.parse(fs.readFileSync('/data/.clawdbot/metiq.json','utf8')); console.log(c.skills?.entries?.['gh-issues']?.apiKey || '')")
Verify: echo "Token: ${GH_TOKEN:0:10}..."
-
CHECKOUT — Switch to the PR branch:
git fetch {PUSH_REMOTE} {branch_name}
git checkout {branch_name}
export GH_TOKEN=$(node -e "const fs=require('fs'); const c=JSON.parse(fs.readFileSync('/data/.clawdbot/metiq.json','utf8')); console.log(c.skills?.entries?.['gh-issues']?.apiKey || '')")
-
UNDERSTAND — Read ALL review comments carefully. Group them by file. Understand what each reviewer is asking for.
-
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
- TEST — Run existing tests to make sure your changes don't break anything:
- If tests fail, fix the issue or revert the problematic change
export GH_TOKEN=$(cat ~/.metiq/config.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||'')")
- 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}"
-
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}
-
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
cat /data/.clawdbot/metiq.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
-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."
- REPORT — Send back a summary:
- PR URL
- Number of comments addressed vs skipped
- Commit SHA
- Files changed
- Any comments that need manual attention
- 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
```
"GitHub authentication failed. Please check your apiKey in the metiq config or in ~/.metiq/config.json under skills.entries.gh-issues."
cat ~/.metiq/config.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
- runTimeoutSeconds: 3600 (60 minutes)
- cleanup: "keep" (preserve transcripts for review)
- If
--model was provided, include model: "{MODEL}" in the spawn config
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.