| name | github-cli |
| description | GitHub CLI (gh) patterns — issues, PRs, releases, actions, gh api (REST + GraphQL), jq filtering, scripting. Use for GitHub operations. |
| when_to_use | TRIGGER when: GitHub issues, PRs, CI, releases, gh api, repo management. DO NOT TRIGGER when: local git ops. |
GitHub CLI Skill
Use gh (GitHub CLI) for all GitHub operations. Prefer gh over raw API calls, curl, or web browser. Every gh command supports --help for flag discovery.
Principles
- JSON first: Add
--json <fields> + --jq <query> for machine-readable output in scripts
- Repo context: Run commands from a git repo directory —
gh auto-detects {owner} and {repo}
- Cross-repo: Use
-R owner/repo flag to target a different repository
- No interactive prompts: Always pass
--title, --body, flags explicitly — never rely on interactive prompts
- Rate limits: Add
sleep 1 between iterations in bulk loops
Issues
Create
gh issue create --title "Bug: login fails" --body "Steps to reproduce..." --label bug --assignee @me
gh issue create --title "Feature request" --body-file description.md --label enhancement --project "Roadmap"
List & Filter
gh issue list
gh issue list --state all --limit 100
gh issue list --label "bug" --assignee @me
gh issue list --search "error no:assignee sort:created-asc"
gh issue list --json number,title,labels --jq '.[].title'
View & Modify
gh issue view 123
gh issue view 123 --json body,comments --jq '.comments[-1].body'
gh issue comment 123 --body "Working on this"
gh issue edit 123 --add-label "in-progress" --remove-label "needs-triage"
gh issue close 123 --reason completed
gh issue reopen 123
Link to PR
Reference issues in PR body with Fixes #123 or Closes #123 — GitHub auto-closes on merge.
Pull Requests
Create
gh pr create --title "feat: add auth" --body "Implements OAuth2 flow" --base main
gh pr create --fill
gh pr create --fill-verbose
gh pr create --draft
gh pr create --reviewer user1,team-name --label feature --assignee @me
gh pr create --body-file pr-description.md
List & Filter
gh pr list
gh pr list --author @me
gh pr list --reviewer @me --state open
gh pr list --label "needs-review" --draft=false
gh pr list --search "is:open review:required"
gh pr list --json number,title,reviewDecision --jq '.[] | select(.reviewDecision == "APPROVED")'
Review
gh pr review 42 --approve
gh pr review 42 --approve --body "LGTM, tested locally"
gh pr review 42 --request-changes --body "See inline comments"
gh pr review 42 --comment --body "Quick question about line 42"
gh pr diff 42
gh pr checks 42
Checkout & Test
gh pr checkout 42
gh pr checkout 42 --detach
Merge
gh pr merge 42 --squash
gh pr merge 42 --merge
gh pr merge 42 --rebase
gh pr merge 42 --auto --squash
gh pr merge 42 --squash --delete-branch
Update & Edit
gh pr edit 42 --title "Updated title" --add-label "reviewed"
gh pr edit 42 --add-reviewer user1 --add-assignee @me
gh pr ready 42
gh pr update-branch 42
Releases
gh release create v1.0.0 --title "v1.0.0" --generate-notes
gh release create v1.0.0 ./dist/*.tar.gz --title "v1.0.0" --notes "Bug fixes"
gh release create v1.0.0 --notes-file CHANGELOG.md --draft
gh release list
gh release view v1.0.0
gh release download v1.0.0 --dir ./downloads
gh release edit v1.0.0 --draft=false
gh release delete v1.0.0 --yes
GitHub Actions
Trigger Workflows
gh workflow run deploy.yml
gh workflow run deploy.yml -f environment=production
gh workflow run deploy.yml -r release/v2
echo '{"env":"staging"}' | gh workflow run deploy.yml --json
Monitor Runs
gh run list
gh run list --workflow=ci.yml --limit 5
gh run view 12345
gh run view 12345 --log
gh run view 12345 --log-failed
gh run watch 12345
gh run rerun 12345
gh run rerun 12345 --failed
gh run cancel 12345
Download Artifacts
gh run download 12345
gh run download 12345 --name coverage-report
gh run download 12345 --dir ./artifacts
Repository
gh repo view
gh repo view owner/repo --json description,stargazerCount
gh repo clone owner/repo
gh repo fork owner/repo --clone
gh repo create my-project --public --clone
gh repo edit --default-branch main
gh repo sync
Labels
gh label list
gh label create "priority:high" --color FF0000 --description "High priority"
gh label edit "bug" --color 00FF00
gh label delete "old-label" --yes
gh label clone source-owner/source-repo
Search (Cross-Repo)
gh search issues "memory leak" --repo owner/repo --state open
gh search prs "auth" --author @me --state merged
gh search repos "cli tool" --language go --stars ">100"
gh search code "handleAuth" --repo owner/repo
gh search commits "fix bug" --repo owner/repo --author user1
Secrets & Variables
gh secret set API_KEY
gh secret set API_KEY --body "sk-..."
gh secret set API_KEY < secret.txt
gh secret list
gh secret delete API_KEY
gh secret set ORG_SECRET --org myorg --visibility all
gh variable set APP_ENV --body "production"
gh variable list
gh variable delete APP_ENV
gh api (REST & GraphQL)
The escape hatch for anything not covered by dedicated commands.
REST
gh api repos/{owner}/{repo}/issues --jq '.[].title'
gh api repos/{owner}/{repo}/issues -f title="Bug" -f body="Details" -f 'labels[]=bug'
gh api repos/{owner}/{repo}/issues/123 -X PATCH -f state=closed
gh api repos/{owner}/{repo}/issues --paginate --jq '.[].title'
gh api repos/{owner}/{repo}/readme -H 'Accept: application/vnd.github.v3.raw'
GraphQL
gh api graphql -F owner='{owner}' -F name='{repo}' -f query='
query($name: String!, $owner: String!) {
repository(owner: $owner, name: $name) {
issues(last: 5, states: OPEN) {
nodes { title number }
}
}
}
'
gh api graphql -f query='
mutation($id: ID!) {
closeIssue(input: {issueId: $id}) {
issue { number state }
}
}
' -F id="I_kwDOxxx"
gh api graphql --paginate -f query='
query($endCursor: String) {
viewer {
repositories(first: 100, after: $endCursor) {
nodes { nameWithOwner }
pageInfo { hasNextPage endCursor }
}
}
}
'
Placeholders
{owner}, {repo}, {branch} auto-resolve from the current git repo context.
JSON Output & jq Patterns
Most list/view commands support --json <fields> + --jq <expression>:
gh pr list --json
gh pr list --json number,title,author --jq '.[] | "\(.number) \(.title) by \(.author.login)"'
gh issue list --json number,labels --jq '[.[] | select(.labels | length > 0)]'
gh issue list --state all --limit 500 --json labels --jq '[.[].labels[].name] | group_by(.) | map({name: .[0], count: length}) | sort_by(-.count)'
Status Dashboard
gh status
gh status -o myorg
gh status -e owner/noisy-repo
Scripting Patterns
Bulk Operations
gh issue list --label "wontfix" --json number --jq '.[].number' | \
while read num; do gh issue close "$num" --reason "not planned"; sleep 1; done
gh pr list --json number --jq '.[].number' | \
while read num; do gh pr edit "$num" --add-label "reviewed"; sleep 1; done
Conditional Logic
if gh pr checks 42 --json state --jq 'all(.state == "SUCCESS")' | grep -q true; then
gh pr merge 42 --squash
fi
PR Comments from API
gh api repos/{owner}/{repo}/pulls/42/comments --jq '.[] | "\(.user.login): \(.body)"'
gh api repos/{owner}/{repo}/issues/42/comments -f body="Automated comment from script"
Common Mistakes
- Missing
--json fields: Run gh <cmd> --json with no value to see available fields
- Interactive prompts in scripts: Always pass
--title, --body, --yes explicitly
- Rate limiting in loops: Add
sleep 1 between API calls in bulk operations
- Search exclusion syntax: Use
-- before query on Unix: gh search issues -- "-label:bug"
- Draft filter:
--draft without value means "only drafts", use --draft=false for non-drafts
- PR vs Issue numbers: GitHub shares the number space — PR #42 and Issue #42 cannot coexist