| name | github |
| description | MUST BE USED when working with GitHub: updating PRs, editing PR descriptions/titles, creating PRs, merging, review threads, `gh` CLI commands, GitHub API, or any pull request operations. Load this skill BEFORE running gh commands or modifying PRs. (plugin:fx-dev@fx-cc) |
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. This skill automatically loads when using gh commands and continuously improves by documenting solutions to new issues.
When to Use
This skill triggers automatically when:
- Running any
gh command (pr, api, issue, repo, etc.)
- Working with pull requests, reviews, or issues
- Encountering
gh CLI errors or unexpected behavior
- Needing GraphQL queries for GitHub operations
Prerequisites
GitHub CLI Version
CRITICAL: Many features require a recent gh CLI version. Before using this skill:
-
Check current version:
gh --version
-
Compare with latest release:
-
Upgrade gh CLI:
Preferred method (mise):
mise use -g gh@latest
Alternative (apt):
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
-
Verify upgrade:
gh --version
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 EXISTING threads created by GitHub Copilot only using
addPullRequestReviewThreadReply
- ✅ Resolve Copilot threads using
resolveReviewThread
Never respond to or interact with human reviewer comments. Only automated Copilot feedback should be 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 | gh api repos/{owner}/{repo}/pulls/<NUMBER>/reviews --jq '.[] | select(.user.login == "copilot-pull-request-reviewer[bot]")' — must return a review | ⛔ YES |
| Copilot comments RESOLVED | All Copilot review threads resolved (0 unresolved) | ⛔ 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 all threads received before any rate limit | Optional when rate-limited |
| Codecov passing | codecov/patch and codecov/project checks pass | ⛔ YES |
CodeRabbit is run primarily LOCALLY (via the cr CLI) BEFORE the PR is opened — see fx-dev:coderabbit-review (Mode 1) and fx-dev:dev Step 4.5. The PR-level review applies only when the GitHub App auto-reviews PRs. CodeRabbit is optional when it reports a rate/quota limit or cooldown: report once, resolve findings already received, record skipped (rate-limited), and continue without waiting or retrying. Other merge gates remain mandatory.
If Copilot review has NOT been received: WAIT. Poll every 60 seconds for up to 15 minutes. 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:
gh pr edit 13 --body-file /tmp/pr-body.md
gh pr view 13 --json body -q .body | head -20
gh api graphql -f query='mutation { ... }'
gh api graphql -f query='query { ... }' --jq '.data'
2. Prefer GitHub API for Complex Operations
For multi-step operations or data transformations, use gh api graphql directly:
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 |
❌ 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.
Use Conventional Formats:
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:
- 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.
- 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.
- 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.
- 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:
git clone git@github.com:fx/dotfiles.git
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:
gh auth status
If authenticated, gh can handle cloning, pushing, and other git operations without SSH keys.
Clone Repositories
Preferred approach when SSH works:
git clone git@github.com:owner/repo.git
Alternative via gh (no SSH required):
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:
gh auth setup-git
This configures git to use gh for HTTPS authentication, allowing standard git commands to work:
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
gh repo clone owner/repo
gh repo clone owner/repo -- --depth 1
gh repo fork owner/repo --clone
gh repo view owner/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:
- Create the PR ready for review (no
--draft flag)
- Run the full review/CI cycle (handled by
fx-dev:dev Steps 6 and 7)
- Merge once all gates pass and the user approves
Correct approach:
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):
cat > /tmp/pr-body.md <<'EOF'
...
EOF
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 GitHub Copilot. Never interact with human review threads.
Use GraphQL mutations to resolve Copilot threads:
THREAD_ID="RT_kwDOQipvu86RqL7d"
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
gh pr view 13
gh pr view 13 --json title,body,state,reviewThreads
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
- API limitation: No direct API endpoint to request Copilot reviews; must use UI or automatic triggers
Request Copilot to Review a PR
There is no API endpoint to programmatically request a Copilot review. Reviews are triggered by:
-
Automatic reviews via repository rulesets (recommended)
- Configure in repo Settings → Rules → Rulesets
- Enable "Automatically request Copilot code review"
- Optionally enable "Review new pushes" for re-reviews on each commit
-
GitHub UI
- Open PR → Reviewers menu → Select "Copilot"
- To re-request: Click the re-request button (🔄) next to Copilot's name
-
Push new commits (if "Review new pushes" ruleset is enabled)
- Simply push to the PR branch to trigger a new review
Check if Copilot Review is Pending
Query review requests for Bot reviewers:
gh api graphql -f query='
query {
repository(owner: "OWNER", name: "REPO") {
pullRequest(number: PR_NUMBER) {
reviewRequests(first: 10) {
nodes {
requestedReviewer {
... on Bot { login }
}
}
}
}
}
}' --jq '.data.repository.pullRequest.reviewRequests.nodes[] | select(.requestedReviewer.login == "copilot-pull-request-reviewer")'
If output is non-empty, Copilot review is pending (in progress).
Check if Copilot Has Finished Reviewing
Query completed reviews via REST API:
gh api repos/OWNER/REPO/pulls/PR_NUMBER/reviews \
--jq '.[] | select(.user.login == "copilot-pull-request-reviewer[bot]") | {state, submitted_at}'
Or via GraphQL:
gh api graphql -f query='
query {
repository(owner: "OWNER", name: "REPO") {
pullRequest(number: PR_NUMBER) {
reviews(first: 20) {
nodes {
author { login }
state
submittedAt
}
}
}
}
}' --jq '.data.repository.pullRequest.reviews.nodes[] | select(.author.login == "copilot-pull-request-reviewer")'
Full Copilot Review Status Summary
Query all Copilot-related information in one call:
gh api graphql -f query='
query {
repository(owner: "OWNER", name: "REPO") {
pullRequest(number: PR_NUMBER) {
reviewRequests(first: 10) {
nodes {
requestedReviewer {
... on Bot { login }
}
}
}
reviews(first: 20) {
nodes {
author { login }
state
submittedAt
}
}
reviewThreads(first: 100) {
totalCount
nodes {
id
isResolved
comments(first: 1) {
nodes {
author { login }
}
}
}
}
}
}
}'
Then filter for Copilot status:
jq '.data.repository.pullRequest.reviewRequests.nodes[] | select(.requestedReviewer.login == "copilot-pull-request-reviewer")'
jq '.data.repository.pullRequest.reviews.nodes[] | select(.author.login == "copilot-pull-request-reviewer")'
jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false and .comments.nodes[0].author.login == "copilot-pull-request-reviewer")] | length'
Status Interpretation
| Condition | Meaning |
|---|
Review request exists for copilot-pull-request-reviewer | Review in progress |
Review with submittedAt exists, no pending request | Review completed |
| Unresolved threads with Copilot author | Feedback needs attention |
| No request, no reviews | Copilot not configured or not triggered |
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:
-
Document the problem
- What command was run?
- What was the error or unexpected behavior?
- What was the intended outcome?
-
Find the solution
- Try alternative approaches
- Check GitHub CLI documentation
- Use GraphQL API directly if needed
-
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
-
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:
gh pr edit 13 --body "$(cat <<'EOF'
$(cat /tmp/pr-body.md)
EOF
)"
Solution found:
gh api repos/owner/repo/pulls/13 -X PATCH -F body=@/tmp/pr-body.md
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
- Read references before complex operations - Check if the pattern is already documented
- Verify all changes - Always confirm
gh commands had the intended effect
- Use GraphQL for data queries - More powerful than chaining CLI commands
- Document new solutions - Update
references/known-issues.md when encountering new problems
- 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