Skip to content
Skills Library
Programming · Tool integration

github

/github

Explicit-use only — invoke when the user explicitly names this skill, or when an active explicitly invoked workflow calls it. Provides reference guidance for GitHub CLI, pull-request, review-thread, and GitHub API operations.

Author
fx ↗
Repository
cc ↗
Licence
MIT
Last repository change
08.10.2026

Assessment and security

95/ 100Class: good
Format25 / 25
Frontmatter with name and description, valid name, description up to 1,024 characters, name matching the folder.
Discoverability25 / 25
Description explains when to use the skill, has a reasonable length and uses third person.
Content30 / 35
At least 150 words and at most 500 lines, headings, step lists, examples or code.
Hygiene15 / 15
No placeholders such as TODO, no Windows paths, with a licence.
Riskno signals detected in the scan
Files in this package
3
Scripts
0
HTML comments
0
URLs in SKILL.md
4
Deprecation

No signals detected. Repository changed on 08.10.2026, without references to retired models or APIs.

Licence

MIT, open: use and modify while retaining author attribution.

What it does and when to use it

Explicit-use only — invoke when the user explicitly names this skill, or when an active explicitly invoked workflow calls it. Provides reference guidance for GitHub CLI, pull-request, review-thread, and GitHub API operations.

Original author description

Explicit-use only — invoke when the user explicitly names this skill, or when an active explicitly invoked workflow calls it. Provides reference guidance for GitHub CLI, pull-request, review-thread, and GitHub API operations.

How it works

  1. Purpose
  2. Invocation Boundary
  3. Prerequisites
  4. ⛔ PR Comments Prohibition (CRITICAL)
  5. ⛔ PR Merge Requirements (CRITICAL — BLOCKING)
  6. ⛔ Release PR Prohibition (CRITICAL)
  7. Core Principles
  8. Recognizing Repository References
  9. Git Operations via gh CLI
  10. Common Operations
Original SKILL.md headings · 764 lines, 5,318 words
SKILL.md preview
---
name: github
description: "Explicit-use only — invoke when the user explicitly names this skill, or when an active explicitly invoked workflow calls it. Provides reference guidance for GitHub CLI, pull-request, review-thread, and GitHub API operations."
---

# GitHub CLI Expert

Comprehensive guidance for working with the GitHub CLI (`gh`) including common pitfalls, GraphQL patterns, and self-improvement workflows.

## Purpose

To provide reliable, tested patterns for GitHub operations and prevent repeating known mistakes with the `gh` CLI.

## Invocation Boundary

Use this skill only when the user explicitly invokes or names it, or when an active explicitly invoked workflow calls `fx-dev:github` by name. Do not auto-load it merely because a standalone request mentions GitHub, a pull request, `gh`, GraphQL, review threads, or merging. Handle ordinary GitHub operations directly unless the user selected this guidance or an active workflow requires it.

## Prerequisites

### GitHub CLI Version

**CRITICAL**: Many features require a recent `gh` CLI version. Before using this skill:

1. **Check current version:**
   ```bash
   gh --version
   ```

2. **Compare with latest release:**
   - Check https://github.com/cli/cli/releases for the current stable version
   - If your version is >6 months old, upgrade

3. **Upgrade `gh` CLI:**

   **Preferred method (mise):**
   ```bash
   mise use -g gh@latest
   ```

   **Alternative (apt):**
   ```bash
   sudo apt update && sudo apt install -y gh
   ```

   **Why mise is preferred:**
   - Always gets the latest version (apt repos lag behind)
   - No sudo required
   - Consistent across environments

4. **Verify upgrade:**
   ```bash
   gh --version
   # Should show version 2.80+ (as of Dec 2025)
   ```

**Known version issues:**
- `gh < 2.20`: Limited GraphQL mutation support
- `gh < 2.40`: Missing `--body-file` flag on `gh pr edit`
- `gh < 2.50`: Incomplete review thread APIs

## ⛔ PR Comments Prohibition (CRITICAL)

**NEVER leave comments directly on GitHub PRs.** This is strictly forbidden:

- ❌ `gh pr review --comment` - FORBIDDEN
- ❌ `gh pr comment` - FORBIDDEN
- ❌ `gh api` mutations that create new reviews or PR-level comments - FORBIDDEN
- ❌ Responding to human review comments - FORBIDDEN

**The ONLY permitted interaction with review threads:**
- ✅ Reply to an EXISTING thread created by an **automated reviewer** using `addPullRequestReviewThreadReply`
- ✅ Resolve an automated reviewer's thread using `resolveReviewThread`

**The line is automated versus human, not Copilot versus everything else.** Copilot and CodeRabbit are the two with their own resolver skills, and the merge gates below require CodeRabbit's delivered threads to be resolved — so a Copilot-only reading of this rule contradicts them. Any other bot a repo has configured is settled the same way, by hand, since no skill adapts it (`fx-dev/skills/dev/references/scope-contract.md` § Injecting the brief into reviews).

**Never respond to or interact with human reviewer comments.** Human threads are out of scope and must never be touched, replied to, or resolved — only automated feedback is addressed.

## ⛔ PR Merge Requirements (CRITICAL — BLOCKING)

**NEVER run `gh pr merge` without verifying ALL of the following gates. No exceptions for PR size, urgency, or any other reason.**

| Gate | Verification | Blocking? |
|------|-------------|-----------|
| CI checks ALL green | `gh pr checks <NUMBER>` — every check must show `pass` | ⛔ YES |
| Copilot review RECEIVED **for the commit being merged** | A Copilot review whose `commit_id` equals the PR's current `headRefOid` — see the scoped command below. A review of ANY older commit does NOT satisfy this gate | ⛔ YES |
| Copilot verdict READ | The reviewed body's verdict headline. *Approval recommended* and *Needs a closer look* both pass when no threads are open; *Changes recommended* must be worked through. A suppressed-comments block is ignored by default and gates nothing | ⛔ YES |
| Copilot comments RESOLVED | All **Copilot-authored** review threads resolved (0 unresolved). Filter on the Copilot login — human threads are out of scope and must never be touched | ⛔ YES |
| CodeRabbit review attempted (if GitHub App configured) | Prefer a received review; explicit `skipped (rate-limited)` is acceptable | Optional when rate-limited |
| CodeRabbit comments resolved (if received) | Resolve **every** thread CodeRabbit already posted, rate limit or not. Throttling waives the review passes that never ran, never a thread already on the PR | ⛔ YES for delivered threads |
| Codecov passing | `codecov/patch` and `codecov/project` checks pass | ⛔ YES |

> **CodeRabbit is PR-level only** — see `fx-dev:coderabbit-review`. There is no local CodeRabbit pass; the `cr` CLI is not used, and Codex is the only local pre-PR reviewer (`fx-dev:dev` Step 4.5). The PR-level review applies only when the GitHub App auto-reviews PRs; its waiter reports `STATUS=NOT_CONFIGURED` otherwise, which is terminal and expected for most repos. **CodeRabbit is optional when it reports a rate/quota limit or cooldown:** report once, resolve findings already received **and settle every thread it already posted**, record `skipped (rate-limited)`, and continue without waiting or retrying. The degradation waives the passes that never ran, never work already on the PR. Other merge gates remain mandatory.

Verify the Copilot gate with a **head-SHA-scoped** query — an unscoped one accepts a
review of a superseded commit as coverage for the code you are about to merge:

```bash
PR_NUMBER=$(gh pr view --json number --jq '.number')          # or set it explicitly: PR_NUMBER=123
REPO_NWO=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
HEAD_SHA=$(gh pr view "$PR_NUMBER" --json headRefOid --jq '.headRefOid')
gh api "/repos/${REPO_NWO}/pulls/${PR_NUMBER}/reviews" \
  --jq "[.[] | select(.user.login | startswith(\"copilot-pull-request-reviewer\")) | select(.commit_id == \"${HEAD_SHA}\")] | length"
```

A result of `0` means **the commit you are about to merge is UNREVIEWED**, whatever
older reviews the PR carries.

**If a Copilot review of the current head has NOT been received:** WAIT — using
`fx-dev:copilot-review`, which owns the head-SHA-aware waiter. **Do not hand-roll a
polling loop here.** Hand-rolled loops reliably accept a review of a superseded
commit and re-derive the broken `requested_reviewers` readiness check. Do NOT merge
without it.

**Incident context:** A "small follow-up" PR was merged without waiting for Copilot review. Copilot found 5 real bugs (timing drift, race conditions, missing tests) that shipped to main. PR size is NEVER a reason to skip review gates.

## ⛔ Release PR Prohibition (CRITICAL)

**NEVER merge release PRs.** This includes PRs created by:

- ❌ release-please (`chore(main): release X.Y.Z`)
- ❌ semantic-release
- ❌ changesets (`Version Packages`)
- ❌ Any automated versioning/release bot

Release PRs control package versioning. Merging them autonomously can publish unintended major/minor versions, which is irreversible. **The user must always merge release PRs manually.**

If a workflow requires a new version to be published (e.g., updating a dependency after an upstream PR merges), STOP and inform the user:

> A release PR exists. Please merge it manually when ready, then confirm so I can proceed.

## Core Principles

### 1. Verify All Operations

Always verify that `gh` commands produced the expected result:

```bash
# After editing PR description
gh pr edit 13 --body-file /tmp/pr-body.md
gh pr view 13 --json body -q .body | head -20  # Verify it worked

# After resolving threads
gh api graphql -f query='mutation { ... }'
gh api graphql -f query='query { ... }' --jq '.data'  # Verify resolution
```

### 2. Prefer GitHub API for Complex Operations

For multi-step operations or data transformations, use `gh api graphql` directly:

```bash
# More reliable than chaining CLI commands
gh api graphql -f query='...' --jq '.data.repository.pullRequest'
```

### 3. Use Correct Methods for Each Task

Check `references/known-issues.md` before attempting operations that have failed before. Common issues include:

- PR description updates with heredocs
- Review thread resolution vs. PR comments
- Command substitution in heredoc strings

### 4. Follow Messaging Conventions

**Be Direct and Concise:**
- All PR descriptions, commit messages, and comments must be direct and to the point
- Eliminate unnecessary prose and filler content
- Focus on what changed and why, not how the work was organized

**⛔ Never hard-wrap anything GitHub renders as markdown:**

PR descriptions, PR/issue comments, and issue bodies MUST NOT be hard-wrapped at 80 columns — or any column. GitHub reflows markdown to the reader's viewport, so manual line breaks only produce ragged text that re-wraps badly on narrow screens. **Write each paragraph as ONE long line** and let it soft-wrap.

Commit messages are the **opposite**: git renders them as plain text, so wrap commit bodies at ~72 columns as usual. The rule follows the renderer, not the content.

| Target | Wrap? |
|---|---|
| Commit message body | Yes, ~72 columns |
| PR description / PR comment / issue body | **No — one line per paragraph** |

```markdown
❌ WRONG — hard-wrapped PR body, renders ragged on GitHub
## Summary
Standardizes every skill on two canonical instruction files, with a
pointer for each tool that cannot read them natively.

✅ RIGHT — one line per paragraph, GitHub reflows it
## Summary
Standardizes every skill on two canonical instruction files, with a pointer for each tool that cannot read them natively.
```

This applies however the body is authored — heredoc, `--body-file`, or `gh api -F body=@file`. Tables, lists, and fenced code blocks keep their own line structure; the rule is about prose paragraphs.

### PR conventions block (paste verbatim into any agent prompt that may open or edit a PR)

A convention that lives only in this skill does not survive delegation: an agent spawned with an ad-hoc prompt never loads it. Whenever you delegate PR creation — a `/team` PR preparer opening a PR, a fix agent editing a body, any sub-agent running `gh pr create` — paste this block into that agent's prompt verbatim. Quote it by name ("the github skill's PR conventions block") when referring to it from another skill.

~~~markdown
### PR conventions (mandatory)
- TITLE: a conventional-commit subject. The **mechanical floor** is `^(feat|fix|docs|refactor|chore|test|perf|build|ci|style|revert)(\(.+\))?!?: .+` — that regex checks the type prefix and nothing else, so passing it is necessary, not sufficient.
- TITLE: three more rules the floor regex cannot express. Two are checkable against the title (`grep -Eq '^[a-z]+(\(.+\))?!?: [a-z]'` must match — lowercase after the colon; `grep -Eq '\.$'` must NOT match — no trailing period). The third, **imperative mood** ("add", not "adds" or "added"), has no mechanical check: read the title and confirm it by eye.
- TITLE: no `#<number>` unless it references a real existing PR/issue, and no wave/phase/step/batch or change-doc number. Squash-merge bakes the title into the default branch, where `#N` auto-links permanently.
- BODY: **never hard-wrapped.** GitHub reflows markdown to the reader's viewport, so write each paragraph as ONE long line and let it soft-wrap. Lists, tables and fenced code blocks keep their own line structure. This applies however the body is authored — heredoc, `--body-file`, or `gh api -F body=@file`.
- COMMIT MESSAGE: the opposite — wrap the body at ~72 columns, because git renders it as plain text. The rule follows the renderer, not the content.
- Verify before AND after creating: the title against every rule above, and the body with the command below. It judges only prose — fenced code, headings, blockquotes, tables, list items and their continuation lines are all skipped — and **exits 1 printing `HARD-WRAPPED`** when prose clusters in the 60-100 column band. Read its output; do not assume it passed. Fix with `gh pr edit <N> --body-file <file>` and re-run.

```bash
gh pr view <N> --json body -q .body | awk '
  # Fenced code, CommonMark rules: a fence opens on ``` or ~~~ and closes only
  # on the SAME character, at least as long as the fence that opened it — so a
  # ```` block may legally contain a ``` line without closing.
  {
    t = $0; sub(/^[[:space:]]+/, "", t)
    if (t ~ /^```/ || t ~ /^~~~/) {
      ch = substr(t, 1, 1); len = 0
      while (substr(t, len + 1, 1) == ch) len++
      if (!fence)                          { fence = ch; flen = len; next }
      else if (ch == fence && len >= flen) { fence = "";  flen = 0;  next }
    }
  }
  fence                { next }
  /^[[:space:]]*$/     { next }                   # blank: does not end a list
  /^(    |\t)/         { next }                   # indented code block
  /^[[:space:]]*#/     { list = 0; next }         # heading
  /^[[:space:]]*>/     { list = 0; next }         # blockquote
  /^[[:space:]]*\|/    { list = 0; next }         # table row
  /^[[:space:]]*([-*+]|[0-9]+[.)])[[:space:]]/ { list = 1; next }   # list marker line
  /^[[:space:]]/       { if (list) next }         # continuation, ONLY inside a list
  { list = 0; n++; if (length($0) >= 60 && length($0) <= 100) w++ }
  END {
    if (n == 0) { print "no prose lines to check"; exit 0 }
    printf "prose lines: %d; in the 60-100 col hard-wrap band: %d\n", n, w
    if (w * 2 > n) { print "HARD-WRAPPED - rewrite each paragraph as ONE long line"; exit 1 }
    print "OK - prose is not hard-wrapped"
  }'
```
~~~

**That command is the canonical "Mechanical body check"**, and `fx-dev:team` merge gate 5c refers to it by that name. Run it yourself after creating or editing any PR body, not only when delegating.

**It is a heuristic, not a Markdown parser, and that is deliberate.** It follows CommonMark on the two things that actually bite — a fence closes only on the same character at a length at least its opening, and an indented line is a continuation only inside a list — and it guesses at the rest. Treat a `HARD-WRAPPED` verdict as a prompt to *read* the body, not as proof. If you have read it and the prose genuinely is one line per paragraph, say so in your report and move on: **do not rewrite correct prose to satisfy the checker**, and do not extend the awk to chase a further Markdown construct. The supply of constructs does not run out, and the rule the gate enforces is the one in prose above it.

Do not substitute `awk '{print length}' | sort -rn | head -3`. The three longest lines in a body are usually a table row or a code line, both exempt, so it reports a healthy number for a body whose prose is entirely hard-wrapped.

**Use Conventional Formats:**
- **Commit messages**: Follow conventional commit format (`feat:`, `fix:`, `refactor:`, `docs:`, etc.)
- **PR titles**: MUST use conventional commit format — `type(scope): description` (e.g., `feat: add user authentication`, `fix(api): handle null token`). **BLOCKING**: on squash-merge the PR title becomes the commit subject, so a plain prose title (no `type:` prefix) permanently pollutes a conventional-commit history. **Canonical check** — every PR title, no matter who creates it (pr-preparer, the `/dev` workflow, or a `/team` coordinator running `gh pr create` directly), MUST match this regex; verify before creating AND before merging:
  ```bash
  gh pr view <N> --json title -q .title | grep -Eq '^(feat|fix|docs|refactor|chore|test|perf|build|ci|style|revert)(\(.+\))?!?: .+' \
    && echo "OK: conventional" || echo "⛔ NOT conventional — gh pr edit <N> --title \"type(scope): …\""
  ```
  A prose title like `Add anti-fabrication grounding rules` is FORBIDDEN — reform it (`feat(scope): add anti-fabrication grounding rules`). Creating the PR directly (not via pr-preparer) does NOT exempt you from this.
- **Branch names**: Use conventional naming (e.g., `feat/user-auth`, `fix/login-bug`)
- **Comments**: Use conventional comment markers where applicable

**Content Rules:**
- Describe the work being done and changes being made
- **Never mention** in the title: implementation phases, waves, steps of a process, project management terminology, workflow stages, or change-doc numbers
- **Never include** in the title: "Phase 1", "Step 2", "Part 3", "Wave 4", "First iteration", "Initial implementation", "0004-..."
- These belong in the PR **body** (description) if anywhere — never the title

### ⛔ The `#<number>` PR-Title Rule (CRITICAL — BLOCKING)

**A `#` immediately followed by a number — `#4`, `(#4)`, `#123` — in a PR title is a latent reference to PR/issue #N in the target repo.** The title bar itself renders it as plain text, so it looks harmless — but on **squash merge with GitHub's default commit-message setting, the PR title becomes the merge commit's subject line**, and `#N` in a *commit message* DOES auto-link and create a hard cross-reference to PR/issue #N. So a title saying `(#4)` to mean "implementation wave 4" ends up permanently cross-linking your merged commit (and the PR) to whatever PR/issue #4 happens to be. This has repeatedly created messy, wrong cross-links on `main`.

**Rules — no exceptions:**

1. **NEVER put `#<number>` in a PR title to mean anything other than a real PR/issue reference.** Implementation waves, phases, steps, parts, iterations, change-doc numbers (`0004`), and task numbers are FORBIDDEN as `#N` in titles.
2. **A `#<number>` is allowed in a title ONLY if N is a genuine, existing PR or issue in the target repo that this PR is actually about** — and even then, prefer putting the reference in the body (`Closes #123`). If you're not certain the number maps to a real PR/issue on this exact repo, do NOT write it.
3. **Do NOT pre-add a `(#N)` suffix.** When squash-merging with the default commit-message setting, GitHub appends `(#<real-PR-number>)` to the commit subject for you — a hand-added `(#4)` either duplicates or contradicts it. Leave your title clean and let GitHub add the real number at merge time.
4. To reference a change document or wave in the body, write the **path** (`docs/changes/0004-add-oauth.md`) or plain words ("the second batch of tasks") — never `#0004`, `#4`, or `(#4)`.

**Examples:**

✅ **Good PR Title** (no `#N`, no wave/phase):
```
feat: add user authentication with JWT tokens
```

❌ **Bad PR Title** (`(#4)` means "wave 4" — becomes the squash-merge commit subject and cross-links to PR/issue #4):
```
feat: add user authentication (#4)
```

❌ **Bad PR Title** (phase/wave in title):
```
feat: add user authentication - Phase 1: Initial Implementation
```

✅ **Allowed** only when #123 is a real issue this PR resolves on this repo (prefer doing this in the body instead):
```
fix: resolve login timeout reported in #123
```

✅ **Good Commit Message:**
```
fix: resolve login timeout issue

- Increase session timeout to 30 minutes
- Add retry logic for failed auth requests

Fixes #456
```

❌ **Bad Commit Message:**
```
fix: resolve login timeout issue - Step 2 of authentication refactor

This is the second phase of our authentication improvements...
```

✅ **Good Branch Name:**
```
feat/jwt-authentication
fix/login-timeout
```

❌ **Bad Branch Name:**
```
feat/authentication-phase-1
fix/login-step-2
```

## Recognizing Repository References

When users refer to repositories, recognize the `owner/repo` shorthand format and expand it appropriately.

### Shorthand Format

The pattern `owner/repo` (e.g., `fx/dotfiles`, `anthropics/claude-code`) refers to a GitHub repository. Always expand this to a full URL.

### Examples

| User says | Interpretation |
|-----------|----------------|
| "clone fx/dotfiles" | Clone `git@github.com:fx/dotfiles.git` |
| "look at anthropics/claude-code" | Repository at `github.com/anthropics/claude-code` |
| "fork vercel/next.js" | Fork from `github.com/vercel/next.js` |

### Clone Priority

When cloning, **always try SSH first**, then fall back to `gh` CLI:

```bash
# User: "clone fx/dotfiles"

# 1. Try SSH first (preferred)
git clone git@github.com:fx/dotfiles.git

# 2. If SSH fails, use gh CLI (handles auth automatically)
gh repo clone fx/dotfiles
```

### URL Expansion Rules

| Shorthand | SSH URL | HTTPS URL |
|-----------|---------|-----------|
| `owner/repo` | `git@github.com:owner/repo.git` | `https://github.com/owner/repo.git` |
| `fx/dotfiles` | `git@github.com:fx/dotfiles.git` | `https://github.com/fx/dotfiles.git` |

**IMPORTANT:** Never prompt the user to clarify `owner/repo` references - assume GitHub and proceed with cloning.

## Git Operations via `gh` CLI

When SSH keys aren't configured or `GIT_SSH_COMMAND` proxying fails, use `gh` CLI for git operations. The `gh` CLI handles authentication automatically when logged in.

### Check Authentication Status

Before using `gh` for git operations, verify authentication:

```bash
gh auth status
```

If authenticated, `gh` can handle cloning, pushing, and other git operations without SSH keys.

### Clone Repositories

**Preferred approach when SSH works:**
```bash
git clone git@github.com:owner/repo.git
```

**Alternative via `gh` (no SSH required):**
```bash
gh repo clone owner/repo
```

This uses HTTPS with automatic token authentication - no SSH key needed.

### Configure Git to Use `gh` for Authentication

Set up git to use `gh` as a credential helper for HTTPS:

```bash
gh auth setup-git
```

This configures git to use `gh` for HTTPS authentication, allowing standard git commands to work:

```bash
git clone https://github.com/owner/repo.git
git push origin main
```

### When to Use `gh` vs SSH

| Scenario | Use |
|----------|-----|
| SSH key configured and working | `git clone git@github.com:...` |
| No SSH key, but `gh auth status` shows logged in | `gh repo clone ...` or HTTPS with `gh auth setup-git` |
| Coder workspace with broken `GIT_SSH_COMMAND` | `gh repo clone ...` |
| CI/CD with `GITHUB_TOKEN` | HTTPS with token auth |

### Common `gh` Git Operations

```bash
# Clone
gh repo clone owner/repo
gh repo clone owner/repo -- --depth 1  # Shallow clone

# Fork and clone
gh repo fork owner/repo --clone

# View repo info
gh repo view owner/repo

# Create repo
gh repo create my-repo --private --clone
```

## Common Operations

### Create Pull Requests

**CRITICAL — NEVER create draft PRs:**

ALL pull requests MUST be created READY FOR REVIEW. **Never use `--draft`.** Never include "draft" / "WIP" / "for review" language in the title or body. The full review/CI cycle (CI checks, Copilot, CodeRabbit, codecov) runs from the moment the PR opens — drafting it has been used repeatedly as an excuse to skip those steps.

**Workflow:**
1. Create the PR ready for review (no `--draft` flag)
2. Run the full review/CI cycle (handled by `fx-dev:dev` Steps 6 and 7)
3. Merge once all gates pass and the user approves

**Correct approach:**
```bash
gh pr create --title "feat: add feature" --body "$(cat <<'EOF'
## Summary
...
EOF
)"
```

**Never use `--draft`. Never run `gh pr ready` as a workaround for having opened a draft.** If the work isn't ready for review, don't open the PR yet — finish it first.

### Update PR Description

**Recommended approach** (most reliable):

```bash
# Write description to file first
cat > /tmp/pr-body.md <<'EOF'
## Summary
...
EOF

# Update via GitHub API
gh api repos/owner/repo/pulls/13 -X PATCH -F body=@/tmp/pr-body.md
```

See `references/known-issues.md` for failed approaches and why they don't work.

### Resolve Copilot Review Threads

**ONLY resolve threads created by an automated reviewer** (see the PR Comments Prohibition above). Never interact with human review threads.

Use GraphQL mutations to resolve an automated reviewer's threads:

```bash
# Get thread ID (must be an automated reviewer's thread, never a human's)
THREAD_ID="RT_kwDOQipvu86RqL7d"

# Resolve it
gh api graphql -f query='
  mutation($threadId: ID!) {
    resolveReviewThread(input: {threadId: $threadId}) {
      thread { id isResolved }
    }
  }' -f threadId="$THREAD_ID"
```

**Reminder:** `gh pr review --comment` is FORBIDDEN. See the PR Comments Prohibition section above.

### Get PR Information

```bash
# Simple PR view
gh pr view 13

# Get specific fields as JSON
gh pr view 13 --json title,body,state,reviewThreads

# Filter with jq
gh pr view 13 --json reviewThreads --jq '.reviewThreads[] | select(.isResolved == false)'
```

## Copilot Review Management

GitHub Copilot can automatically review pull requests. This section covers how to check review status and manage Copilot reviews.

### Key Facts

- **Copilot username**: `copilot-pull-request-reviewer` (GraphQL) or `copilot-pull-request-reviewer[bot]` (REST API)
- **Review state**: Copilot only leaves `COMMENTED` state reviews, never `APPROVED` or `CHANGES_REQUESTED`
- **Reviews are per-commit**: each review carries a `commit_id`. A review of an earlier commit says nothing about the current head.
- **Pushing does NOT re-trigger a review** unless the repo's ruleset enables "Review new pushes". Assume it does not.

### Request Copilot to Review a PR

**A Copilot review CAN be requested via the API** — add the bot as a requested reviewer:

```bash
PR_NUMBER=$(gh pr view --json number --jq '.number')          # or set it explicitly: PR_NUMBER=123
REPO_NWO=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
gh api --method POST "/repos/${REPO_NWO}/pulls/${PR_NUMBER}/requested_reviewers" \
  --input - <<'EOF'
{"reviewers":["copilot-pull-request-reviewer[bot]"]}
EOF
```

**The response to this POST is not evidence of anything.** It returns 200 with an
empty `requested_reviewers` array regardless, and a `422` is equally
uninformative — neither tells you whether a review is coming. Issue it and ignore
the result. The only sound signal is a review whose `commit_id` equals the PR's
current `headRefOid`; see `fx-dev:copilot-review` ("Known GitHub API Behaviour",
D1–D3) and use that skill, which wraps this together with a head-SHA-aware waiter.

**Do this again after every push to the PR branch.** Copilot will not reliably look at new commits on its own, and a stale review must never be read as coverage for the current head.

Other ways a review can be triggered, none of which replace the request above:

1. **Automatic reviews via repository rulesets**
   - Configure in repo Settings → Rules → Rulesets
   - Enable "Automatically request Copilot code review" (first review on PR open only)
   - Optionally enable "Review new pushes" for re-reviews on each commit — **off by default**
2. **GitHub UI** — Open PR → Reviewers → "Copilot"; the re-request button (🔄) forces a fresh pass

### ⛔ There Is No Way to Check Whether a Copilot Review Is "Pending"

**Do not try.** `reviewRequests` / `requested_reviewers` is **empirically always
empty** for the Copilot bot — 200 with `requested_reviewers: []` every time, and the
GraphQL `reviewRequests` node is empty too, including while a review is genuinely on
its way and after it has landed. See `fx-dev:copilot-review` (**D1**/**D3**).

Because that field is always empty, an "if non-empty → pending" check has only one
reachable branch: the negative one. Wired into a workflow it concludes "nobody asked
Copilot to review" on every PR, forever — which is exactly the false conclusion
**D3** forbids, and it made an earlier waiter report "no review requested" against
reviews that had already been delivered.

**The only sound signal is the review itself, scoped to the commit** — nothing has to
be "requested" for that to become true, and nothing being there yet does not mean
nothing was requested. Absence is never a verdict.

### Check Whether Copilot Has Reviewed the Current Head

This is the real check. Note the `commit_id` scoping — without it you learn only that
Copilot reviewed *something*, which says nothing about the code in front of you:

```bash
PR_NUMBER=$(gh pr view --json number --jq '.number')          # or set it explicitly: PR_NUMBER=123
REPO_NWO=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
HEAD_SHA=$(gh pr view "$PR_NUMBER" --json headRefOid --jq '.headRefOid')

# Reviews of the CURRENT head — this is coverage.
gh api "/repos/${REPO_NWO}/pulls/${PR_NUMBER}/reviews" \
  --jq "[.[] | select(.user.login | startswith(\"copilot-pull-request-reviewer\")) | select(.commit_id == \"${HEAD_SHA}\") | {state, submitted_at}]"

# Every Copilot review with its commit — diagnostic, to see whether a review exists
# but covers an older commit. NOT a pass signal.
gh api "/repos/${REPO_NWO}/pulls/${PR_NUMBER}/reviews" \
  --jq '[.[] | select(.user.login | startswith("copilot-pull-request-reviewer")) | {commit_id, state, submitted_at}]'
```

Or via GraphQL — include `commit { oid }`, otherwise the result is unscoped and
cannot answer the question:

```bash
# Replace OWNER, REPO, PR_NUMBER with actual values. `-f query='...'` is single-quoted,
# so nothing inside it is shell-expanded — these are literal substitutions, not shell
# variables. The `$head` in the --jq below is a *jq* variable and stays as written.
gh api graphql -f query='
query {
  repository(owner: "OWNER", name: "REPO") {
    pullRequest(number: PR_NUMBER) {
      headRefOid
      reviews(first: 20) {
        nodes {
          author { login }
          state
          submittedAt
          commit { oid }
        }
      }
    }
  }
}' --jq '.data.repository.pullRequest | .headRefOid as $head | [.reviews.nodes[] | select(.author.login == "copilot-pull-request-reviewer") | {state, submittedAt, oid: .commit.oid, covers_head: (.commit.oid == $head)}]'
```

**Prefer `fx-dev:copilot-review` over any of the above.** It wraps the request and a
head-SHA-aware wait, and it prints the review body so the verdict headline — which
no thread query surfaces — can be read.

### Full Copilot Review Status Summary

Query all Copilot-related information in one call. Note there is deliberately **no
`reviewRequests` node**: it is always empty for the Copilot bot (**D1**), so including
it only invites the "no request → nobody asked" misreading. `headRefOid` and
`commit { oid }` are what make the result answerable.

```bash
# Replace OWNER, REPO, PR_NUMBER with actual values (GraphQL body — no shell expansion here)
gh api graphql -f query='
query {
  repository(owner: "OWNER", name: "REPO") {
    pullRequest(number: PR_NUMBER) {
      headRefOid
      reviews(first: 20) {
        nodes {
          author { login }
          state
          submittedAt
          commit { oid }
        }
      }
      reviewThreads(first: 100) {
        totalCount
        nodes {
          id
          isResolved
          comments(first: 1) {
            nodes {
              author { login }
            }
          }
        }
      }
    }
  }
}'
```

Then filter for Copilot status:

```bash
# Reviews covering the CURRENT head — the only pass signal
jq '.data.repository.pullRequest | .headRefOid as $head | [.reviews.nodes[] | select(.author.login == "copilot-pull-request-reviewer" and .commit.oid == $head)]'

# All Copilot reviews with their commits — diagnostic only, NOT a pass signal
jq '.data.repository.pullRequest | .headRefOid as $head | [.reviews.nodes[] | select(.author.login == "copilot-pull-request-reviewer") | {state, oid: .commit.oid, covers_head: (.commit.oid == $head)}]'

# Unresolved Copilot threads
jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false and .comments.nodes[0].author.login == "copilot-pull-request-reviewer")] | length'
```

### Status Interpretation

Every row keys off a **delivered review and its commit**. No row keys off a review
*request*: that field is always empty (**D1**), so any rule reading it fires its
negative branch on every PR (**D3**).

| Condition | Meaning |
|-----------|---------|
| Review whose `commit_id` == the PR's `headRefOid` | Review completed **for the code you are about to merge**. Read its verdict headline (**D4**) |
| Review exists, but its `commit_id` is an older commit | **Current head is UNREVIEWED.** Nudge, then wait via `fx-dev:copilot-review` — do not treat this as reviewed |
| No Copilot review at all | **Nothing has reviewed this PR yet.** Wait via `fx-dev:copilot-review`. This is not "clean", and it is *not* evidence that no review was requested — you cannot determine that at all (**D1**) |
| Unresolved threads with Copilot author | Feedback needs attention |
| Zero unresolved threads | Clean **once** a review covers the current head — on an older commit it says nothing about the code being merged |
| `reviewRequests` / `requested_reviewers` empty | **Means nothing.** It is always empty. Do not derive any status from it (**D1**) |

## Bundled References

### references/known-issues.md

Documents solutions to issues encountered during development:

- PR description update methods (what works, what doesn't)
- Heredoc escaping problems
- Review thread vs PR comment distinction
- Self-improvement template for new issues

**When to read:** Encountering errors with `gh` commands, before attempting complex operations.

### references/graphql-patterns.md

Common GraphQL query and mutation patterns:

- PR operations (get details, review threads)
- Thread management (resolve, unresolve, reply)
- Copilot review workflows
- Batch operations and pagination
- Error handling patterns

**When to read:** Need to query GitHub data, work with review threads, perform batch operations.

## Self-Improvement Workflow

When encountering a new `gh` CLI issue:

1. **Document the problem**
   - What command was run?
   - What was the error or unexpected behavior?
   - What was the intended outcome?

2. **Find the solution**
   - Try alternative approaches
   - Check GitHub CLI documentation
   - Use GraphQL API directly if needed

3. **Update this skill**
   - Read `references/known-issues.md`
   - Add the new issue using the provided template
   - Include both the failed approach and working solution
   - Explain the root cause

4. **Update SKILL.md if needed**
   - If it's a common pattern, add brief guidance to SKILL.md
   - Link to the detailed documentation in references files

### Self-Improvement Example

**Problem encountered:**
```bash
gh pr edit 13 --body "$(cat <<'EOF'
$(cat /tmp/pr-body.md)
EOF
)"
# Result: Literal string "$(cat /tmp/pr-body.md)" in PR description
```

**Solution found:**
```bash
gh api repos/owner/repo/pulls/13 -X PATCH -F body=@/tmp/pr-body.md
# Result: PR description correctly updated
```

**Documentation added to references/known-issues.md:**
- Failed approach with explanation
- Working approach with example
- Root cause analysis
- Alternative solutions

This ensures the same mistake is never repeated.

## Best Practices

1. **Read references before complex operations** - Check if the pattern is already documented
2. **Verify all changes** - Always confirm `gh` commands had the intended effect
3. **Use GraphQL for data queries** - More powerful than chaining CLI commands
4. **Document new solutions** - Update `references/known-issues.md` when encountering new problems
5. **Prefer `-F` over `-f` for file inputs** - Use `@filename` syntax for reliable file reading

## Integration with Other Skills

- **copilot-feedback-resolver**: For complete Copilot review thread workflows
- **fx-dev:pr-***: For PR creation, review, and management workflows
Files in this package (3)
  • SKILL.md 35 KB
  • references/graphql-patterns.md 6 KB
  • references/known-issues.md 3 KB

Versions

RepositoryScoreLicence
This page · fx/cc95MIT
rjmurillo/ai-agents100MIT
rjmurillo/ai-agents100MIT
VincenzoImp/pitcrew100MIT
AceDataCloud/Skills100Apache-2.0
the-open-agent/openagent100Apache-2.0
OpenHands/extensions100MIT
TerminalSkills/skills100Apache-2.0
atomicbot-ai/atomic-agent100MIT

Database snapshot: 13.11.2026 · package version 458687f5bf4e (SHA-256 digest of the entire package)

Downloads in the last 30 days: 0 · ranking

Report a problem with this skill: harmful content, incorrect descriptions or an author’s removal request.

Similar skills

  • 100resolve-ci-failures
    Explicit-use only — invoke when the user explicitly names this skill, or when an active explicitly invoked workflow calls it. Analyzes and fixes CI failures within an active explicitly requested PR workflow.
  • 92pr-check-monitor
    Explicit-use only — invoke when the user explicitly names this skill, or when an active explicitly invoked workflow calls it. Monitors pull-request checks and coordinates explicitly requested CI remediation.
  • 96code-review
    Use when reviewing GitHub CLI (gh) pull requests.
  • 100create-github-pr
    Create GitHub pull requests using the gh CLI. Use when the user wants to create a new PR, submit code for review, or open a pull request. Trigger keywords - create PR, pull request, new PR, submit for review, code review.
  • 100gh-cli
    Use when working with GitHub from the command line using the gh CLI tool. Trigger on "create PR", "open pull request", "list issues", "check CI", "merge PR", "review PR", "create issue", "release", "gh auth", "clone repo", "fork repo", "close issue", "view workflow run", or any…
  • 100github-pr
    Use when opening, formatting, and standardizing Pull Requests with GitHub CLI gh commands, PR templates, and review checklists.

Feedback and reports

Your message goes to Bartosz Sokoliński. An email address is needed only for a reply. See the privacy policy.