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]
Recommended by author
This prompt takes no variables — just pick a model and run.
# 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 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: `git remote get-url origin` Extract owner/repo from the URL (handles both HTTPS and SSH formats). - HTTPS: https://github.com/owner/repo.git → owner/repo - SSH: [email protected]: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, `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 ~/.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. `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: 1. **Dirty working tree check:** ``` git status --porcelain ``` If output is non-empty, stop and ask the user to commit, stash, or discard the local changes before spawning sub-agents: > "Working tree has uncommitted changes. Worktree-isolated sub-agents require a clean source checkout so each task starts from a reproducible HEAD. Please commit, stash, or discard the changes first." 2. **Record base branch:** ``` git rev-parse --abbrev-ref HEAD ``` Store as BASE_BRANCH. 3. **Verify remote access:** If FORK_MODE: - Verify the fork remote exists. Check if a git remote named `fork` exists: ``` git remote get-url fork ``` If it doesn't exist, add it: ``` git remote add fork https://x-access-token:[email protected]/{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 HEAD ``` If this fails, stop with: "Cannot reach remote origin. Check your network and git config." 4. **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 OpenClaw dashboard or in ~/.openclaw/openclaw.json under skills.entries.gh-issues." 5. **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). 6. **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): ``` 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). If all issues are skipped after this check, report and stop (or loop back if in watch mode). 7. **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: ``` 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: 1. Mark it as in_progress in the cursor file 2. Spawn a single sub-agent for that one issue with `isolation: "worktree"` and `timeout_seconds: 3600` Clean worktrees are removed automatically; changed worktrees are preserved, so no cleanup input is needed. 3. If `--model` was provided, include `model: "{MODEL}"` in the spawn config 4. If `--notify-channel` was provided, include the channel in the task so the sub-agent can notify 5. Do NOT await the sub-agent result — fire and forget 6. **Write claim:** After spawning, read the claims file, add `{SOURCE_REPO}#{N}` with the current ISO timestamp, and write it back 7. Immediately report: "Spawned fix agent for #{N} — will create PR when complete" 8. 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 subagents_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. ### Sub-agent Task Prompt For each issue, construct the following prompt and pass it to subagents_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 ~/.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 PR branch inside the runtime-managed isolated worktree: git checkout -b fix/issue-{number} 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:[email protected]/{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> — [truncated; see full source: https://github.com/trpc-group/trpc-agent-go]
Running prompts needs a free account.
Sign in and we'll stream the response from Claude Opus 4.7 right here — no config needed for the platform models.
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]
# 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 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: `git remote get-url origin` Extract owner/repo from the URL (handles both HTTPS and SSH formats). - HTTPS: https://github.com/owner/repo.git → owner/repo - SSH: [email protected]: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, `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 ~/.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. `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: 1. **Dirty working tree check:** ``` git status --porcelain ``` If output is non-empty, stop and ask the user to commit, stash, or discard the local changes before spawning sub-agents: > "Working tree has uncommitted changes. Worktree-isolated sub-agents require a clean source checkout so each task starts from a reproducible HEAD. Please commit, stash, or discard the changes first." 2. **Record base branch:** ``` git rev-parse --abbrev-ref HEAD ``` Store as BASE_BRANCH. 3. **Verify remote access:** If FORK_MODE: - Verify the fork remote exists. Check if a git remote named `fork` exists: ``` git remote get-url fork ``` If it doesn't exist, add it: ``` git remote add fork https://x-access-token:[email protected]/{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 HEAD ``` If this fails, stop with: "Cannot reach remote origin. Check your network and git config." 4. **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 OpenClaw dashboard or in ~/.openclaw/openclaw.json under skills.entries.gh-issues." 5. **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). 6. **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): ``` 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). If all issues are skipped after this check, report and stop (or loop back if in watch mode). 7. **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: ``` 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: 1. Mark it as in_progress in the cursor file 2. Spawn a single sub-agent for that one issue with `isolation: "worktree"` and `timeout_seconds: 3600` Clean worktrees are removed automatically; changed worktrees are preserved, so no cleanup input is needed. 3. If `--model` was provided, include `model: "{MODEL}"` in the spawn config 4. If `--notify-channel` was provided, include the channel in the task so the sub-agent can notify 5. Do NOT await the sub-agent result — fire and forget 6. **Write claim:** After spawning, read the claims file, add `{SOURCE_REPO}#{N}` with the current ISO timestamp, and write it back 7. Immediately report: "Spawned fix agent for #{N} — will create PR when complete" 8. 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 subagents_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. ### Sub-agent Task Prompt For each issue, construct the following prompt and pass it to subagents_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 ~/.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 PR branch inside the runtime-managed isolated worktree: git checkout -b fix/issue-{number} 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:[email protected]/{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> — [truncated; see full source: https://github.com/trpc-group/trpc-agent-go]