$npx -y skills add Codagent-AI/agent-skills --skill wait-ciPolls CI check status for the current branch's pull request and reports pass/fail/pending/comments, surfacing PR review comments even when CI is green. Use when the user says "wait for CI", "check CI", "poll CI", or invokes "codagent:wait-ci".
| 1 | # codagent:wait-ci |
| 2 | |
| 3 | Poll CI check status for the current branch's PR and report the result, enriching failures with log output and checking for blocking reviews. |
| 4 | |
| 5 | Use the bundled scripts for all API calls — they encode the correct field names, jq patterns, and GraphQL queries. Do not rewrite API calls inline. |
| 6 | |
| 7 | > **NEVER pause to ask the user for permission to wait.** Always run the full polling duration silently. Asking mid-execution breaks automation and is never needed — the caller already decided to invoke this skill. |
| 8 | |
| 9 | ## How polling works |
| 10 | |
| 11 | `check-ci.sh` is a **single-invocation** script. It polls every 10 seconds for up to 90 seconds, then exits. Claude calls it in a loop to cover the full max wait time. This keeps each Bash call bounded to ~2 minutes rather than blocking the agent for the full duration. |
| 12 | |
| 13 | Each Bash call to `check-ci.sh` **must** use `timeout: 120000` (2 minutes). |
| 14 | |
| 15 | ## Steps |
| 16 | |
| 17 | ### 1. Run the polling loop |
| 18 | |
| 19 | Compute `max_runs = ceil(max_minutes * 60 / 90)`, defaulting to `max_minutes = 15` → 10 runs. |
| 20 | |
| 21 | The skill accepts an optional `--max-minutes N` argument; pass it through to adjust `max_runs`. |
| 22 | |
| 23 | Track `ever_had_checks = false`, `run = 0`, and the overall deadline across iterations. The deadline is `max_minutes` after polling starts and also bounds any review-bot waiting in Step 3. |
| 24 | |
| 25 | For each run: |
| 26 | |
| 27 | ```bash |
| 28 | bash skills/wait-ci/scripts/check-ci.sh |
| 29 | ``` |
| 30 | |
| 31 | Use **`timeout: 120000`** on the Bash call. Check the exit code: |
| 32 | |
| 33 | | Exit code | Meaning | Action | |
| 34 | |---|---|---| |
| 35 | | `0` | Terminal (`passed` or `failed`) | Break out of loop, proceed to Step 2 | |
| 36 | | `2` | Not yet terminal (`pending` or `no_checks`) | If `run < max_runs`, re-run; else report timeout | |
| 37 | | `1` | Fatal error | Report error and stop | |
| 38 | |
| 39 | After each exit-2 result, set `ever_had_checks = ever_had_checks OR result.had_checks`. |
| 40 | |
| 41 | **Timeout handling:** When all runs are exhausted (exit code 2 on the last run): |
| 42 | - If `ever_had_checks` is false → treat CI as non-blocking, then still run Step 3 before reporting a final status |
| 43 | - Otherwise → report `pending` with the list of still-running checks |
| 44 | |
| 45 | The script outputs a JSON object with these fields: |
| 46 | |
| 47 | | Field | Type | Description | |
| 48 | |---|---|---| |
| 49 | | `status` | string | `passed`, `failed`, `pending`, `no_checks`, or `comments` (set by caller) | |
| 50 | | `pr_url` | string | PR URL | |
| 51 | | `pr_number` | number | PR number | |
| 52 | | `head_sha` | string | PR head commit queried for this invocation; preserve it when upgrading the status to `comments` | |
| 53 | | `owner` / `repo` | string | Repo coordinates for subsequent calls | |
| 54 | | `had_checks` | bool | Whether any checks were seen on this invocation | |
| 55 | | `failed_checks` | array | Checks with `bucket == "fail"` | |
| 56 | | `passed_checks` | array | Checks with `bucket == "pass"` | |
| 57 | | `pending_checks` | array | Checks still running | |
| 58 | | `blocking_reviews` | array | Reviews with `CHANGES_REQUESTED` (latest per reviewer) | |
| 59 | | `failed_run_ids` | array | GitHub Actions run IDs extracted from failed check links | |
| 60 | |
| 61 | ### 2. Fetch failure logs (if `status == "failed"`) |
| 62 | |
| 63 | For each run ID in `failed_run_ids`: |
| 64 | |
| 65 | ```bash |
| 66 | gh run view <run-id> --log-failed |
| 67 | ``` |
| 68 | |
| 69 | Keep the last 100 lines if output is longer. External checks (no run ID) get no logs. |
| 70 | |
| 71 | ### 3. Gather PR comments (when checks are terminal or no checks exist) |
| 72 | |
| 73 | ```bash |
| 74 | bash skills/wait-ci/scripts/get-pr-comments.sh <owner> <repo> <pr-number> [<pr-author-login>] |
| 75 | ``` |
| 76 | |
| 77 | Uses GraphQL to check `isResolved` on review threads directly — no jq `!=` workarounds needed. |
| 78 | |
| 79 | Output fields: |
| 80 | |
| 81 | | Field | Type | Description | |
| 82 | |---|---|---| |
| 83 | | `has_comments` | bool | True if any unaddressed comments exist | |
| 84 | | `unresolved_threads` | array | `{file, line, author, body}` per unresolved review thread | |
| 85 | | `issue_comments` | array | `{author, body}` blocking top-level human comments (excluding PR creator) | |
| 86 | | `informational_bot_comments` | array | `{author, body}` non-blocking top-level bot comments retained as evidence | |
| 87 | |
| 88 | Unresolved review threads are blocking regardless of author. Top-level bot comments do not set `has_comments`; only unresolved threads and human top-level comments do. |
| 89 | |
| 90 | After checks are terminal, inspect `informational_bot_comments`. If a bot explicitly reports that its review is pending or in progress, or asks the caller to check back, wait 10 seconds and call `get-pr-comments.sh` again while time remains before the overall deadline. Re-evaluate the latest evidence on every poll. Do not hard-code bot vendors or exact phrases; use the comment's meaning. Completed summaries and ordinary status notices remain informational and do not delay completion. |
| 91 | |
| 92 | If a bot still explicitly reports unfinished review work when the overall deadline is reached, report `pending`. Preserve its comment in the |