name: cli-gh description: > Covers effective use of the GitHub CLI (gh) for managing pull requests, issues, releases, Actions workflows, repositories, and raw API calls. Activates when the user asks about gh, GitHub CLI, automating GitHub workflows, or interacting with GitHub from the terminal.
gh — GitHub CLI
Repo: https://github.com/cli/cli
Official GitHub CLI. Manage every GitHub resource — PRs, issues, releases, Actions runs, repos, and the full REST/GraphQL API — without leaving the terminal. Composes naturally with jq, fzf, and lazygit.
When to Activate
Manual triggers:
- "How do I use gh?"
- "Create a PR from the terminal"
- "Check CI status in the terminal"
- "Automate GitHub with the CLI"
Auto-detect triggers:
- User wants to open/review/merge a pull request from the terminal
- User wants to list or triage issues without opening a browser
- User wants to monitor GitHub Actions runs
- User is scripting GitHub workflows (release automation, issue triage)
- User wants to call the GitHub REST or GraphQL API
Key Commands
Authentication
gh auth login # Authenticate (browser or token)
gh auth status # Show current auth state
gh auth token # Print the active token (for curl/API scripts)
gh auth refresh -s write:packages # Add an OAuth scope
Pull Requests
gh pr list # List open PRs
gh pr list --state merged # Merged PRs
gh pr list --author "@me" # Your PRs
gh pr view 123 # View PR #123
gh pr view 123 --web # Open in browser
gh pr create # Interactive PR creation
gh pr create --title "Fix bug" --body "Details" --base main
gh pr checkout 123 # Check out PR branch locally
gh pr diff 123 # Show PR diff in terminal
gh pr review 123 --approve # Approve a PR
gh pr review 123 --request-changes --body "Needs tests"
gh pr merge 123 --squash --delete-branch
gh pr merge 123 --auto --squash # Merge automatically when CI passes
gh pr close 123
gh pr reopen 123
gh pr ready 123 # Convert draft → ready for review
gh pr checks 123 # Show CI check status
Issues
gh issue list # List open issues
gh issue list --label bug # Filter by label
gh issue list --assignee "@me"
gh issue view 42
gh issue create --title "Bug" --body "Steps to reproduce..."
gh issue create --label bug --assignee octocat
gh issue edit 42 --add-label "priority:high"
gh issue close 42 --comment "Fixed in #123"
gh issue transfer 42 owner/other-repo
GitHub Actions / Runs
gh run list # List recent workflow runs
gh run list --workflow ci.yml # Filter by workflow file
gh run view 12345 # View a specific run
gh run view 12345 --log # Stream logs
gh run view 12345 --log-failed # Logs for failed steps only
gh run watch 12345 # Watch run until it completes
gh run rerun 12345 # Re-run a failed run
gh run rerun 12345 --failed # Re-run only failed jobs
gh workflow list
gh workflow run deploy.yml # Trigger a workflow dispatch
gh workflow run deploy.yml --ref feature-branch -f env=staging
Releases
gh release list
gh release view v1.2.3
gh release create v1.2.3 --title "v1.2.3" --notes "Changelog..."
gh release create v1.2.3 ./dist/binary-linux ./dist/binary-darwin
gh release create v1.2.3 --generate-notes # Auto-generate notes from PRs
gh release upload v1.2.3 ./dist/app.tar.gz # Upload additional asset
gh release download v1.2.3 # Download all assets
gh release delete v1.2.3
Repositories
gh repo list # Your repos
gh repo list org --limit 100
gh repo view owner/repo
gh repo clone owner/repo
gh repo create my-project --public --clone
gh repo fork owner/repo --clone # Fork and clone
gh repo archive owner/repo
gh repo delete owner/repo # Requires confirmation
gh repo set-default owner/repo # Set default repo for current directory
Gists
gh gist list
gh gist create file.py # Create public gist
gh gist create file.py --secret
gh gist view <id> --raw
gh gist edit <id>
gh gist clone <id>
Raw API Calls
# REST API (GET is default)
gh api repos/{owner}/{repo}/topics
# POST/PATCH/DELETE
gh api -X POST repos/{owner}/{repo}/issues \
-f title="New issue" -f body="Body text"
# Pagination
gh api repos/{owner}/{repo}/issues --paginate
# GraphQL
gh api graphql -f query='
query($owner:String!, $repo:String!) {
repository(owner:$owner, name:$repo) {
stargazerCount
forkCount
}
}
' -f owner=cli -f repo=cli
Search
gh search repos "language:go stars:>1000"
gh search issues "is:open label:bug assignee:@me"
gh search prs "is:open review-requested:@me"
gh search commits "fix authentication" --repo owner/repo
Advanced Patterns
--json + jq Piping
# List PR titles and numbers as JSON, filter with jq
gh pr list --json number,title,author \
| jq '.[] | "\(.number) \(.author.login): \(.title)"'
# Find all open PRs with failing checks
gh pr list --json number,title,statusCheckRollup \
| jq '.[] | select(.statusCheckRollup != null) |
select(.statusCheckRollup[] | .conclusion == "FAILURE") |
{number, title}'
# Get the URL of the latest release
gh release list --json tagName,publishedAt,url \
| jq 'sort_by(.publishedAt) | last | .url'
# Summarize workflow run durations
gh run list --json databaseId,displayTitle,createdAt,updatedAt \
| jq '.[] | {title: .displayTitle, duration: ((.updatedAt | fromdateiso8601) - (.createdAt | fromdateiso8601))}'
Auto-merge + CI Monitoring
# Open PR and auto-merge when checks pass
gh pr create --title "feat: add caching" --body "" \
&& gh pr merge --auto --squash
# Watch CI for the current branch's PR
gh pr checks --watch
# Re-run failed jobs and watch until done
gh run rerun --failed && gh run watch
GraphQL Queries
# Get all labels in a repo
gh api graphql -f query='
query {
repository(owner:"owner", name:"repo") {
labels(first:100) {
nodes { name color }
}
}
}
' | jq '.data.repository.labels.nodes'
# Get PR review decisions
gh api graphql -f query='
query($pr:Int!) {
repository(owner:"owner", name:"repo") {
pullRequest(number:$pr) {
reviewDecision
reviews(last:5) {
nodes { author { login } state }
}
}
}
}
' -F pr=123 | jq '.data.repository.pullRequest'
Scripting and Automation
# Label all issues mentioning "crash" as a bug
gh issue list --json number,title --limit 200 \
| jq -r '.[] | select(.title | test("crash";"i")) | .number' \
| xargs -I{} gh issue edit {} --add-label bug
# Close stale issues (not updated in 90 days)
gh issue list --json number,updatedAt --limit 500 \
| jq -r --argjson cutoff $(date -v-90d +%s) \
'.[] | select((.updatedAt | fromdateiso8601) < $cutoff) | .number' \
| xargs -I{} gh issue close {}
# Bulk-approve renovate PRs
gh pr list --author app/renovate --json number -q '.[].number' \
| xargs -I{} gh pr review {} --approve
Environment and Config
gh config set editor nvim
gh config set git_protocol ssh
gh config set prompt disabled # Disable interactive prompts in scripts
GH_TOKEN=ghp_xxx gh pr list # Override token for one command
GH_REPO=owner/repo gh issue list # Override repo for one command
GH_NO_UPDATE_NOTIFIER=1 # Suppress update nag in CI
Practical Examples
Daily PR Workflow
# Create branch, commit, push, open PR, set to auto-merge
git checkout -b feat/my-feature
# ... make changes ...
git add -A && git commit -m "feat: my feature"
git push -u origin feat/my-feature
gh pr create --fill --web # --fill uses commit message as title/body
gh pr merge --auto --squash
Release Automation
# Tag, create release with generated notes, upload assets
VERSION=v2.3.0
git tag $VERSION && git push origin $VERSION
gh release create $VERSION \
--generate-notes \
--title "$VERSION" \
dist/*.tar.gz dist/*.zip
Monitor CI Across Repos
# Watch all failing workflows in an org
for repo in $(gh repo list myorg --json name -q '.[].name'); do
gh run list --repo myorg/$repo --status failure --json displayTitle,url \
| jq -r ".[] | \"$repo: \(.displayTitle) \(.url)\""
done
Interactive PR Review with fzf
# Fuzzy-select a PR and check it out
gh pr list --json number,title \
| jq -r '.[] | "\(.number)\t\(.title)"' \
| fzf \
| awk '{print $1}' \
| xargs gh pr checkout
Chaining with Other Skills
- jq (cli-jq):
--jsonoutput + jq is the coreghpower combo — filter, reshape, and script any GitHub data - fzf (cli-fzf): Pipe
gh pr list,gh issue list,gh run listinto fzf for interactive selection workflows - lazygit: Use
gh pr checkoutfor PR branches, then lazygit for interactive rebasing before merge - httpie / curl: When gh's API shorthand isn't enough, use
gh auth tokento grab a token for raw HTTP calls