| name | github-cli |
| description | Comprehensive GitHub CLI (gh) reference. Covers repos, issues, PRs, Actions, releases, gists, search, projects v2, API, secrets/variables, labels, codespaces, extensions, auth, and advanced GraphQL patterns. |
| metadata | {"openclaw":{"emoji":"🐙","requires":{"bins":["gh"]},"install":[{"id":"brew","kind":"brew","formula":"gh","bins":["gh"],"label":"Install GitHub CLI (brew)"},{"id":"apt","kind":"apt","package":"gh","bins":["gh"],"label":"Install GitHub CLI (apt)"}]}} |
GitHub CLI (gh) — Comprehensive Skill
Version: gh 2.66.1+
Auth: gh auth login or set GH_TOKEN env var
Always use --repo OWNER/REPO (or -R) when not inside a git repo directory.
Table of Contents
- Authentication & Config
- Repositories
- Issues
- Pull Requests
- GitHub Actions (Runs & Workflows)
- Releases
- Gists
- Search
- Labels
- Secrets & Variables
- Caches
- Projects V2
- API (REST & GraphQL)
- Extensions
- Codespaces
- Copilot
- Other Commands
- JSON Output & Formatting
- Environment Variables
- Advanced Patterns
- Tips & Gotchas
1. Authentication & Config
Auth
gh auth login
echo "$MY_TOKEN" | gh auth login --with-token
gh auth login --hostname enterprise.example.com
gh auth refresh -s project
gh auth refresh -s delete_repo
gh auth status
gh auth status --show-token
gh auth switch
gh auth token
gh auth logout
Required scopes by feature:
| Feature | Scope needed |
|---|
| Basic repo/PR/issue ops | repo |
| Gists | gist |
| Read org membership | read:org |
| Projects V2 | project |
| Delete repos | delete_repo |
| Actions workflows | workflow |
| Read user profile | user |
Config
gh config list
gh config get git_protocol
gh config set git_protocol ssh
gh config set editor "code --wait"
gh config set pager "less -R"
gh config set prompt disabled
gh config set browser "firefox"
gh config clear-cache
Git credential setup
gh auth setup-git
2. Repositories
Create
gh repo create
gh repo create my-project --public --clone
gh repo create my-org/my-project --private
gh repo create my-project --private --source=. --remote=upstream --push
gh repo create my-project --template owner/template-repo --clone
gh repo create my-project --public --description "My project" \
--license mit --gitignore Node --add-readme
Clone
gh repo clone owner/repo
gh repo clone owner/repo my-dir
gh repo clone owner/repo -- --depth=1
gh repo clone my-repo
Fork
gh repo fork
gh repo fork owner/repo --clone
gh repo fork owner/repo --org my-org --fork-name new-name
gh repo fork owner/repo --default-branch-only
View
gh repo view
gh repo view owner/repo
gh repo view --web
gh repo view --json name,description,stargazerCount,url
gh repo view --json name,stargazerCount --jq '.stargazerCount'
JSON fields for repo: archivedAt, assignableUsers, codeOfConduct, createdAt, defaultBranchRef, deleteBranchOnMerge, description, diskUsage, forkCount, hasDiscussionsEnabled, hasIssuesEnabled, hasProjectsEnabled, hasWikiEnabled, homepageUrl, id, isArchived, isEmpty, isFork, isPrivate, isTemplate, languages, latestRelease, licenseInfo, name, nameWithOwner, owner, parent, primaryLanguage, pullRequests, pushedAt, sshUrl, stargazerCount, updatedAt, url, visibility, watchers
List
gh repo list
gh repo list --limit 100
gh repo list my-org
gh repo list --language go --visibility public
gh repo list --topic cli --no-archived
gh repo list --fork
gh repo list --source
gh repo list --json name,stargazerCount --jq '.[] | "\(.name): \(.stargazerCount) stars"'
Edit
gh repo edit --description "New description"
gh repo edit --homepage "https://example.com"
gh repo edit --enable-issues --enable-wiki
gh repo edit --enable-projects=false
gh repo edit --default-branch main
gh repo edit --enable-auto-merge
gh repo edit --delete-branch-on-merge
gh repo edit --add-topic "cli,automation"
gh repo edit --remove-topic "old-topic"
gh repo edit --template
gh repo edit --visibility public --accept-visibility-change-consequences
Delete / Archive
gh repo delete owner/repo --yes
gh repo archive owner/repo --yes
gh repo unarchive owner/repo --yes
Rename
gh repo rename new-name
gh repo rename new-name -R owner/repo
Set Default
gh repo set-default owner/repo
gh repo set-default --view
gh repo set-default --unset
Sync (Fork ↔ Upstream)
gh repo sync
gh repo sync --branch v1
gh repo sync owner/my-fork
gh repo sync owner/repo --source owner2/repo2
gh repo sync --force
3. Issues
Create
gh issue create --title "Bug report" --body "Description here"
gh issue create --title "Bug" --label "bug,urgent" --assignee "@me"
gh issue create --title "Feature" --project "Roadmap" --milestone "v2.0"
gh issue create --template "Bug Report"
gh issue create --body-file description.md
gh issue create -R owner/repo --title "Bug"
List
gh issue list
gh issue list --state closed
gh issue list --state all --limit 100
gh issue list --label "bug" --assignee "@me"
gh issue list --author monalisa
gh issue list --milestone "v2.0"
gh issue list --search "error no:assignee sort:created-asc"
gh issue list --json number,title,labels,state --jq '.[] | "#\(.number) \(.title)"'
JSON fields for issues: assignees, author, body, closed, closedAt, comments, createdAt, id, isPinned, labels, milestone, number, projectCards, projectItems, reactionGroups, state, stateReason, title, updatedAt, url
View
gh issue view 123
gh issue view 123 --web
gh issue view 123 --comments
gh issue view 123 --json title,body,labels,assignees
gh issue view https://github.com/owner/repo/issues/123
Edit
gh issue edit 123 --title "New title" --body "New body"
gh issue edit 123 --add-label "bug,help wanted" --remove-label "core"
gh issue edit 123 --add-assignee "@me" --remove-assignee monalisa
gh issue edit 123 --add-project "Roadmap" --remove-project "v1"
gh issue edit 123 --milestone "v2.0"
gh issue edit 123 --remove-milestone
gh issue edit 123 --body-file body.md
gh issue edit 123 456 789 --add-label "help wanted"
Close / Reopen
gh issue close 123
gh issue close 123 --comment "Fixed in PR #456"
gh issue close 123 --reason "not planned"
gh issue reopen 123
Comment
gh issue comment 123 --body "Hello from CLI"
gh issue comment 123 --body-file comment.md
gh issue comment 123 --edit-last
Pin / Unpin
gh issue pin 123
gh issue unpin 123
Transfer
gh issue transfer 123 owner/other-repo
Lock / Unlock
gh issue lock 123
gh issue unlock 123
Develop (linked branches)
gh issue develop 123 --checkout
gh issue develop 123 --name "fix-bug-123" --base develop
gh issue develop 123 --list
Delete
gh issue delete 123 --yes
4. Pull Requests
Create
gh pr create --title "Fix bug" --body "Description"
gh pr create --fill
gh pr create --fill-first
gh pr create --fill-verbose
gh pr create --draft
gh pr create --base develop
gh pr create --head owner:feature-branch
gh pr create --reviewer monalisa,hubot --reviewer myorg/team-name
gh pr create --label "bug" --assignee "@me"
gh pr create --project "Roadmap"
gh pr create --milestone "v2.0"
gh pr create --template "pull_request_template.md"
gh pr create --no-maintainer-edit
gh pr create --dry-run
List
gh pr list
gh pr list --state merged --limit 50
gh pr list --state all
gh pr list --author "@me"
gh pr list --assignee monalisa
gh pr list --label "bug" --label "priority"
gh pr list --base main
gh pr list --head feature-branch
gh pr list --draft
gh pr list --search "status:success review:required"
gh pr list --search "<SHA>" --state merged
gh pr list --json number,title,author,state --jq '.[].title'
JSON fields for PRs: additions, assignees, author, autoMergeRequest, baseRefName, body, changedFiles, closed, closedAt, comments, commits, createdAt, deletions, files, headRefName, headRefOid, id, isDraft, labels, latestReviews, maintainerCanModify, mergeCommit, mergeStateStatus, mergeable, mergedAt, mergedBy, milestone, number, projectItems, reviewDecision, reviewRequests, reviews, state, statusCheckRollup, title, updatedAt, url
View
gh pr view 123
gh pr view 123 --web
gh pr view 123 --comments
gh pr view 123 --json title,body,reviews,mergeable
gh pr view feature-branch
Checkout
gh pr checkout 123
gh pr checkout 123 --branch local-name
gh pr checkout 123 --force
gh pr checkout 123 --recurse-submodules
gh co 123
Diff
gh pr diff 123
gh pr diff 123 --name-only
gh pr diff 123 --patch
Merge
gh pr merge 123 --merge
gh pr merge 123 --squash
gh pr merge 123 --rebase
gh pr merge 123 --squash --delete-branch
gh pr merge 123 --auto --squash
gh pr merge 123 --disable-auto
gh pr merge 123 --admin
gh pr merge 123 --squash --subject "feat: new feature" --body "Details"
Review
gh pr review 123 --approve
gh pr review 123 --comment --body "LGTM"
gh pr review 123 --request-changes --body "Please fix the tests"
gh pr review
Checks (CI Status)
gh pr checks 123
gh pr checks 123 --watch
gh pr checks 123 --watch --fail-fast
gh pr checks 123 --required
gh pr checks 123 --json name,state,bucket
gh pr checks 123 --web
JSON fields for checks: bucket, completedAt, description, event, link, name, startedAt, state, workflow
Edit
gh pr edit 123 --title "New title" --body "New body"
gh pr edit 123 --add-label "bug" --remove-label "wip"
gh pr edit 123 --add-reviewer monalisa --remove-reviewer hubot
gh pr edit 123 --add-assignee "@me"
gh pr edit 123 --add-project "Roadmap"
gh pr edit 123 --base develop
gh pr edit 123 --milestone "v2.0"
Close / Reopen
gh pr close 123
gh pr close 123 --comment "Superseded by #456" --delete-branch
gh pr reopen 123
Ready / Draft
gh pr ready 123
gh pr ready 123 --undo
Update Branch
gh pr update-branch 123
gh pr update-branch 123 --rebase
Comment
gh pr comment 123 --body "Comment text"
gh pr comment 123 --body-file comment.md
Lock / Unlock
gh pr lock 123
gh pr unlock 123
5. GitHub Actions (Runs & Workflows)
Workflow Runs
gh run list
gh run list --limit 50
gh run list --workflow build.yml
gh run list --branch main
gh run list --status failure
gh run list --user monalisa
gh run list --event push
gh run list --commit abc123
gh run list --json name,status,conclusion,url
gh run view 12345
gh run view 12345 --verbose
gh run view 12345 --log
gh run view 12345 --log-failed
gh run view 12345 --job 456789
gh run view 12345 --job 456789 --log
gh run view 12345 --attempt 3
gh run view 12345 --web
gh run watch 12345
gh run watch 12345 --exit-status
gh run rerun 12345
gh run rerun 12345 --failed
gh run rerun 12345 --debug
gh run rerun 12345 --job 456789
gh run view 12345 --json jobs --jq '.jobs[] | {name, databaseId}'
gh run cancel 12345
gh run delete 12345
gh run download 12345
gh run download 12345 --name "build-output"
gh run download 12345 --dir ./artifacts
gh run download --name "coverage" --pattern "*.xml"
JSON fields for runs: attempt, conclusion, createdAt, databaseId, displayTitle, event, headBranch, headSha, name, number, startedAt, status, updatedAt, url, workflowDatabaseId, workflowName
Workflows
gh workflow list
gh workflow list --all
gh workflow view build.yml
gh workflow view build.yml --web
gh workflow run build.yml
gh workflow run build.yml --ref my-branch
gh workflow run build.yml -f name=value -f env=prod
echo '{"name":"value"}' | gh workflow run build.yml --json
gh workflow enable build.yml
gh workflow disable build.yml
6. Releases
Create
gh release create
gh release create v1.2.3 --notes "Bug fix release"
gh release create v1.2.3 --generate-notes
gh release create v1.2.3 --notes-from-tag
gh release create v1.2.3 -F CHANGELOG.md
gh release create v1.2.3 --draft
gh release create v1.2.3 --prerelease
gh release create v1.2.3 --latest=false
gh release create v1.2.3 --target release-branch
gh release create v1.2.3 --verify-tag
gh release create v1.2.3 --discussion-category "General"
gh release create v1.2.3 ./dist/*.tar.gz
gh release create v1.2.3 'binary.zip#Linux Build'
List / View
gh release list
gh release list --limit 50
gh release view v1.2.3
gh release view v1.2.3 --web
gh release view --json tagName,publishedAt,assets
Download
gh release download v1.2.3
gh release download v1.2.3 --pattern '*.deb'
gh release download v1.2.3 -p '*.deb' -p '*.rpm'
gh release download v1.2.3 --archive zip
gh release download v1.2.3 --dir ./downloads
gh release download v1.2.3 --output single-file.tar.gz
gh release download --pattern '*.AppImage'
Edit / Upload / Delete
gh release edit v1.2.3 --title "New Title" --notes "Updated notes"
gh release edit v1.2.3 --draft=false
gh release edit v1.2.3 --prerelease=false
gh release upload v1.2.3 ./new-asset.zip
gh release upload v1.2.3 'asset.tar.gz#Display Label'
gh release delete v1.2.3 --yes
gh release delete-asset v1.2.3 asset-name
7. Gists
gh gist create file.py
gh gist create --public file.py
gh gist create file.py -d "My Python snippet"
gh gist create file1.py file2.js
echo "hello" | gh gist create -
cat data.json | gh gist create --filename data.json
gh gist list
gh gist list --public
gh gist list --secret
gh gist list --limit 50
gh gist view GIST_ID
gh gist view GIST_ID --raw
gh gist view GIST_ID --filename file.py
gh gist edit GIST_ID
gh gist edit GIST_ID --add newfile.txt
gh gist edit GIST_ID --filename file.py
gh gist rename GIST_ID old-name.py new-name.py
gh gist clone GIST_ID
gh gist delete GIST_ID
8. Search
Repos
gh search repos "vim plugin"
gh search repos --owner=microsoft --visibility=public
gh search repos --language=go --stars=">1000"
gh search repos --topic=cli,automation
gh search repos --good-first-issues=">=10"
gh search repos --archived=false
gh search repos cli shell --sort stars --limit 10
gh search repos --json fullName,stargazersCount,description
Issues
gh search issues "memory leak"
gh search issues --assignee=@me --state=open
gh search issues --owner=cli --label="bug"
gh search issues --comments=">100"
gh search issues --repo owner/repo "error"
gh search issues -- -label:bug
gh search issues --json number,title,repository,state
Pull Requests
gh search prs "fix bug"
gh search prs --repo=cli/cli --draft
gh search prs --review-requested=@me --state=open
gh search prs --assignee=@me --merged
gh search prs --checks=success --review=approved
gh search prs --json number,title,repository,state
Commits
gh search commits "bug fix"
gh search commits --author=monalisa
gh search commits --committer-date="<2024-01-01"
gh search commits --repo=cli/cli --hash=abc123
gh search commits --json sha,commit,repository
Code
gh search code "TODO" --repo=owner/repo
gh search code "import React" --language=typescript
gh search code "api_key" --filename=".env"
gh search code panic --owner=cli --extension=go
gh search code --json path,repository,textMatches
9. Labels
gh label list
gh label list -R owner/repo
gh label list --json name,color,description
gh label create "priority:high" --color FF0000 --description "High priority"
gh label edit "bug" --name "bug 🐛" --color 00FF00 --description "Something broken"
gh label delete "old-label" --yes
gh label clone source-owner/source-repo --repo dest-owner/dest-repo
10. Secrets & Variables
Secrets (encrypted)
gh secret set MY_SECRET --body "secret-value"
gh secret set MY_SECRET < secret-file.txt
echo "value" | gh secret set MY_SECRET
gh secret set MY_SECRET --app dependabot --body "value"
gh secret set MY_SECRET --app codespaces --body "value"
gh secret set MY_SECRET --env production --body "value"
gh secret set MY_SECRET --org my-org --visibility all --body "value"
gh secret set MY_SECRET --org my-org --visibility selected --repos repo1,repo2
gh secret set MY_SECRET --user --body "value"
gh secret set -f .env
gh secret list
gh secret list --env production
gh secret list --org my-org
gh secret delete MY_SECRET
gh secret delete MY_SECRET --env production
gh secret delete MY_SECRET --org my-org
Variables (plaintext)
gh variable set MY_VAR --body "value"
gh variable set MY_VAR --env staging --body "value"
gh variable set MY_VAR --org my-org --visibility all --body "value"
gh variable set -f .env
gh variable get MY_VAR
gh variable list
gh variable list --env production
gh variable list --org my-org
gh variable delete MY_VAR
gh variable delete MY_VAR --env production
11. Caches
gh cache list
gh cache list --limit 100
gh cache list --sort size --order desc
gh cache delete CACHE_KEY
gh cache delete --all
12. Projects V2
⚠️ Requires project scope: gh auth refresh -s project
Projects V2 uses the GraphQL-based ProjectsV2 API. The CLI provides commands for most operations, but some advanced field mutations require direct GraphQL via gh api graphql.
List Projects
gh project list
gh project list --owner my-org
gh project list --owner my-org --closed
gh project list --format json
Create
gh project create --owner "@me" --title "My Roadmap"
gh project create --owner my-org --title "Sprint Board"
View
gh project view 1
gh project view 1 --owner my-org
gh project view 1 --web
gh project view 1 --format json
Edit
gh project edit 1 --owner "@me" --title "New Title"
gh project edit 1 --description "Updated description"
gh project edit 1 --readme "Project README content"
gh project edit 1 --visibility PUBLIC
Close / Reopen / Delete
gh project close 1 --owner "@me"
gh project close 1 --owner "@me" --undo
gh project delete 1 --owner "@me"
Copy
gh project copy 1 --source-owner monalisa --target-owner my-org --title "Copied Project"
gh project copy 1 --source-owner monalisa --target-owner my-org --drafts
Link / Unlink to Repository or Team
gh project link 1 --owner monalisa --repo my-repo
gh project link 1 --owner my-org --team my-team
gh project unlink 1 --owner monalisa --repo my-repo
Mark as Template
gh project mark-template 1 --owner my-org
gh project mark-template 1 --owner my-org --undo
Fields
gh project field-list 1 --owner "@me"
gh project field-list 1 --owner "@me" --format json
gh project field-create 1 --owner "@me" --name "Priority" --data-type "SINGLE_SELECT" \
--single-select-options "Low,Medium,High,Critical"
gh project field-create 1 --owner "@me" --name "Points" --data-type "NUMBER"
gh project field-create 1 --owner "@me" --name "Notes" --data-type "TEXT"
gh project field-create 1 --owner "@me" --name "Due Date" --data-type "DATE"
gh project field-delete --id FIELD_NODE_ID
Field data types: TEXT, SINGLE_SELECT, DATE, NUMBER
(Iteration fields must be created via the web UI or GraphQL)
Items
gh project item-list 1 --owner "@me"
gh project item-list 1 --owner "@me" --limit 100
gh project item-list 1 --owner "@me" --format json
gh project item-add 1 --owner "@me" --url https://github.com/owner/repo/issues/123
gh project item-create 1 --owner "@me" --title "Draft task" --body "Details"
gh project item-edit --id ITEM_NODE_ID --title "Updated title" --body "Updated body"
gh project item-edit --id ITEM_NODE_ID --field-id FIELD_ID --project-id PROJECT_ID \
--text "some value"
gh project item-edit --id ITEM_NODE_ID --field-id FIELD_ID --project-id PROJECT_ID \
--number 5
gh project item-edit --id ITEM_NODE_ID --field-id FIELD_ID --project-id PROJECT_ID \
--date "2024-12-31"
gh project item-edit --id ITEM_NODE_ID --field-id FIELD_ID --project-id PROJECT_ID \
--single-select-option-id OPTION_ID
gh project item-edit --id ITEM_NODE_ID --field-id FIELD_ID --project-id PROJECT_ID \
--iteration-id ITERATION_ID
gh project item-edit --id ITEM_NODE_ID --field-id FIELD_ID --project-id PROJECT_ID --clear
gh project item-archive 1 --owner "@me" --id ITEM_NODE_ID
gh project item-archive 1 --owner "@me" --id ITEM_NODE_ID --undo
gh project item-delete 1 --owner "@me" --id ITEM_NODE_ID
Getting Node IDs (Essential for item-edit)
The item-edit command requires node IDs for the item, field, project, and option. Here's how to get them:
gh project item-list 1 --owner "@me" --format json | jq '.'
gh project field-list 1 --owner "@me" --format json | jq '.'
gh api graphql -f query='
query {
user(login: "USERNAME") {
projectV2(number: 1) {
id
fields(first: 50) {
nodes {
... on ProjectV2SingleSelectField {
id
name
options { id name }
}
... on ProjectV2IterationField {
id
name
configuration {
iterations { id title startDate duration }
}
}
... on ProjectV2Field {
id
name
dataType
}
}
}
}
}
}
'
Projects V2 via GraphQL (for what the CLI can't do)
Some operations require direct GraphQL:
gh api graphql -f query='
mutation {
updateProjectV2ItemFieldValue(input: {
projectId: "PVT_xxxx"
itemId: "PVTI_xxxx"
fieldId: "PVTF_xxxx"
value: { singleSelectOptionId: "option_id" }
}) {
projectV2Item { id }
}
}
'
gh api graphql -f query='
mutation {
addProjectV2ItemById(input: {
projectId: "PVT_xxxx"
contentId: "I_xxxx"
}) {
item { id }
}
}
'
gh api graphql -f query='
mutation {
updateProjectV2DraftIssue(input: {
draftIssueId: "DI_xxxx"
title: "New Title"
body: "New body"
}) {
draftIssue { id title }
}
}
'
gh api graphql -f query='
mutation {
convertProjectV2DraftIssueItemToIssue(input: {
projectId: "PVT_xxxx"
itemId: "PVTI_xxxx"
repositoryId: "R_xxxx"
}) {
item {
id
content {
... on Issue { id number url }
}
}
}
}
'
gh api graphql -f query='
query {
user(login: "USERNAME") {
projectV2(number: 1) {
items(first: 100) {
nodes {
id
content {
... on Issue { title number url }
... on PullRequest { title number url }
... on DraftIssue { title body }
}
fieldValues(first: 20) {
nodes {
... on ProjectV2ItemFieldTextValue { text field { ... on ProjectV2Field { name } } }
... on ProjectV2ItemFieldNumberValue { number field { ... on ProjectV2Field { name } } }
... on ProjectV2ItemFieldDateValue { date field { ... on ProjectV2Field { name } } }
... on ProjectV2ItemFieldSingleSelectValue { name field { ... on ProjectV2SingleSelectField { name } } }
... on ProjectV2ItemFieldIterationValue { title field { ... on ProjectV2IterationField { name } } }
}
}
}
}
}
}
}
'
Projects V2 Workflow (Complete Example)
gh project create --owner "@me" --title "Sprint 1"
gh project field-create 1 --owner "@me" --name "Status" \
--data-type SINGLE_SELECT --single-select-options "Todo,In Progress,Done"
gh project field-create 1 --owner "@me" --name "Priority" \
--data-type SINGLE_SELECT --single-select-options "Low,Medium,High"
gh project field-create 1 --owner "@me" --name "Points" --data-type NUMBER
FIELDS=$(gh project field-list 1 --owner "@me" --format json)
echo "$FIELDS" | jq '.'
gh project item-add 1 --owner "@me" --url https://github.com/owner/repo/issues/1
gh project item-add 1 --owner "@me" --url https://github.com/owner/repo/issues/2
gh project item-create 1 --owner "@me" --title "Research task" --body "Investigate X"
gh project item-edit --id ITEM_ID --field-id STATUS_FIELD_ID \
--project-id PROJECT_ID --single-select-option-id TODO_OPTION_ID
gh project link 1 --owner "@me" --repo my-repo
13. API (REST & GraphQL)
REST API
gh api repos/{owner}/{repo}
gh api repos/cli/cli/releases --jq '.[].tag_name'
gh api -X GET search/issues -f q='repo:cli/cli is:open label:bug'
gh api repos/{owner}/{repo}/issues -f title="New Issue" -f body="Description"
gh api repos/{owner}/{repo}/issues/123/comments -f body='Comment text'
gh api -X PATCH repos/{owner}/{repo} -f description="Updated"
gh api -X DELETE repos/{owner}/{repo}/issues/123/labels/bug
gh api repos/{owner}/{repo}/issues -f title="Bug" -F private=true -F number:=42
gh api repos/{owner}/{repo}/issues --input issue.json
gh api -H 'Accept: application/vnd.github.v3.raw+json' repos/{owner}/{repo}/readme
gh api -i repos/{owner}/{repo}
gh api --verbose repos/{owner}/{repo}
gh api --silent repos/{owner}/{repo}/issues/123/labels -f labels[]=bug
gh api --cache 3600s repos/{owner}/{repo}/releases
Placeholders
The special placeholders {owner}, {repo}, and {branch} are auto-populated from the current git directory or GH_REPO.
Pagination
gh api --paginate repos/{owner}/{repo}/issues --jq '.[].title'
gh api --paginate --slurp repos/{owner}/{repo}/issues | jq 'flatten | length'
GraphQL API
gh api graphql -f query='{ viewer { login } }'
gh api graphql -F owner='{owner}' -F name='{repo}' -f query='
query($owner: String!, $name: String!) {
repository(owner: $owner, name: $name) {
releases(last: 5) {
nodes { tagName publishedAt }
}
}
}
'
gh api graphql -f query='
mutation {
addStar(input: {starrableId: "MDEwOlJlcG9zaXRvcnkxMjM="}) {
starrable { stargazerCount }
}
}
'
gh api graphql --paginate -f query='
query($endCursor: String) {
viewer {
repositories(first: 100, after: $endCursor) {
nodes { nameWithOwner }
pageInfo { hasNextPage endCursor }
}
}
}
'
gh api graphql -f query='{ viewer { repositories(first: 10) { nodes { nameWithOwner stargazerCount } } } }' \
--jq '.data.viewer.repositories.nodes[] | "\(.nameWithOwner): \(.stargazerCount) ⭐"'
Common GraphQL Patterns
gh api graphql -f query='query { repository(owner: "OWNER", name: "REPO") { id } }' \
--jq '.data.repository.id'
gh api graphql -f query='
query {
repository(owner: "OWNER", name: "REPO") {
issue(number: 123) { id }
}
}
' --jq '.data.repository.issue.id'
gh api graphql -f query='query { user(login: "USERNAME") { id } }' --jq '.data.user.id'
gh api graphql -f query='{ rateLimit { limit remaining resetAt } }'
gh api rate_limit --jq '.rate'
14. Extensions
gh extension search "copilot"
gh extension search --limit 30
gh extension browse
gh extension install owner/gh-extension-name
gh extension install https://github.com/owner/gh-extension-name
gh extension list
gh extension upgrade extension-name
gh extension upgrade --all
gh extension remove extension-name
gh extension exec extension-name
gh extension create my-extension
gh extension create my-extension --precompiled=go
15. Codespaces
gh codespace list
gh codespace create --repo owner/repo
gh codespace create --repo owner/repo --branch feature --machine largePremiumLinux
gh codespace ssh
gh codespace code
gh codespace cp local-file.txt remote:~/path/
gh codespace cp remote:~/path/file.txt ./local/
gh codespace view
gh codespace ports
gh codespace rebuild
gh codespace stop
gh codespace delete
16. Copilot
(Requires gh-copilot extension)
gh copilot suggest "find large files in current directory"
gh copilot explain "find . -type f -size +100M -exec ls -lh {} +"
gh copilot config
17. Other Commands
Browse (open in browser)
gh browse
gh browse 123
gh browse src/main.go
gh browse src/main.go:42
gh browse --settings
gh browse --projects
gh browse --releases
gh browse --branch feature src/
gh browse --commit abc123
gh browse -n
Status (cross-repo overview)
gh status
gh status --org my-org
gh status -e owner/repo
Aliases
gh alias set pv 'pr view'
gh alias set bugs 'issue list --label=bug'
gh alias set --shell igrep 'gh issue list --label="$1" | grep "$2"'
gh alias list
gh alias delete pv
gh alias import aliases.yml
SSH Keys / GPG Keys
gh ssh-key list
gh ssh-key add ~/.ssh/id_ed25519.pub --title "My Laptop"
gh ssh-key delete KEY_ID
gh gpg-key list
gh gpg-key add pubkey.gpg
gh gpg-key delete KEY_ID
Rulesets
gh ruleset list
gh ruleset view RULESET_ID
gh ruleset check branch-name
Attestations
gh attestation verify artifact.tar.gz --owner owner
gh attestation download --owner owner artifact.tar.gz
Org
gh org list
18. JSON Output & Formatting
Most listing/view commands support --json, --jq, and --template flags.
Basic JSON
gh pr list --json
gh pr list --json number,title,author
gh pr list --json number,title,author --jq '.[].author.login'
gh issue list --json number,title,labels --jq '
map(select(.labels | length > 0))
| map(.labels = (.labels | map(.name)))
| .[:5]
'
Go Template Formatting
gh pr list --json number,title,headRefName,updatedAt --template \
'{{range .}}{{tablerow (printf "#%v" .number | autocolor "green") .title .headRefName (timeago .updatedAt)}}{{end}}'
gh issue list --json title,url --template \
'{{range .}}{{hyperlink .url .title}}{{"\n"}}{{end}}'
gh pr list --json title,state --template \
'{{range .}}{{.title}} ({{.state | color "green"}}){{"\n"}}{{end}}'
Template Functions
| Function | Description |
|---|
autocolor <style> <input> | Color (terminal-aware) |
color <style> <input> | Force color |
join <sep> <list> | Join list values |
pluck <field> <list> | Extract field from list items |
tablerow <fields>... | Aligned table columns |
tablerender | Render accumulated tablerows |
timeago <time> | Relative timestamp |
timefmt <format> <time> | Formatted timestamp |
truncate <length> <input> | Truncate text |
hyperlink <url> <text> | Terminal hyperlink |
19. Environment Variables
| Variable | Purpose |
|---|
GH_TOKEN / GITHUB_TOKEN | Auth token for github.com (takes precedence over stored creds) |
GH_ENTERPRISE_TOKEN / GITHUB_ENTERPRISE_TOKEN | Auth token for GHES |
GH_HOST | Default GitHub hostname |
GH_REPO | Default repository in [HOST/]OWNER/REPO format |
GH_EDITOR | Editor for authoring text |
GH_BROWSER / BROWSER | Web browser for opening links |
GH_PAGER / PAGER | Terminal pager (e.g., less) |
GH_DEBUG | Enable verbose output (1 or api for HTTP traffic) |
GH_FORCE_TTY | Force terminal output (value = column count or percentage) |
GH_PROMPT_DISABLED | Disable interactive prompts |
GH_NO_UPDATE_NOTIFIER | Disable update notifications |
GH_CONFIG_DIR | Custom config directory |
NO_COLOR | Disable colored output |
GLAMOUR_STYLE | Markdown rendering style |
20. Advanced Patterns
Scripting Best Practices
GH_PROMPT_DISABLED=1 gh pr create --fill
GH_TOKEN=${{ github.token }} gh pr list
export GH_REPO=owner/repo
gh issue list
Batch Operations
gh issue list --label "wontfix" --json number --jq '.[].number' | \
xargs -I{} gh issue close {} --reason "not planned"
gh pr list --json number --jq '.[].number' | \
xargs -I{} gh pr edit {} --add-label "needs-review"
gh run list --status failure --json databaseId --jq '.[].databaseId' | \
xargs -I{} gh run download {}
Working with Multiple Accounts
gh auth status
gh auth switch
GH_TOKEN=ghp_xxx gh api user --jq '.login'
gh auth login --hostname github.com
gh auth login --hostname github.enterprise.com
Rate Limiting
gh api rate_limit --jq '.rate | "\(.remaining)/\(.limit) (resets \(.reset | strftime("%H:%M:%S")))"'
gh api graphql -f query='{ rateLimit { limit remaining resetAt } }'
gh api --cache 3600s repos/{owner}/{repo}/releases
Complex API Patterns
gh api gists -F 'files[myfile.txt][content]=@myfile.txt'
gh api -X PATCH /orgs/{org}/properties/schema \
-F 'properties[][property_name]=env' \
-F 'properties[][allowed_values][]=staging' \
-F 'properties[][allowed_values][]=production'
gh api --paginate repos/{owner}/{repo}/issues --jq '[.[] | select(.labels | length > 0)] | length'
gh api graphql --paginate --slurp -f query='
query($endCursor: String) {
viewer {
repositories(first: 100, after: $endCursor) {
nodes { isFork stargazerCount }
pageInfo { hasNextPage endCursor }
}
}
}
' | jq '[.[].data.viewer.repositories.nodes[]] | map(.stargazerCount) | add'
21. Tips & Gotchas
Common Mistakes
-
--json field names differ from API field names. For example, PR files use files (not changed_files), author uses author.login (not user.login). Always run gh <cmd> --json without arguments to see available fields.
-
gh run rerun --job needs databaseId, not the URL number. Get it with:
gh run view RUN_ID --json jobs --jq '.jobs[] | {name, databaseId}'
-
Projects V2 require the project scope. If you get permission errors:
gh auth refresh -s project
-
gh repo delete requires delete_repo scope:
gh auth refresh -s delete_repo
-
Subcommand quoting in shells: PowerShell and some shells need {owner} escaped. Use quotes: "{owner}".
When to Use gh api vs Specific Commands
| Use specific commands when... | Use gh api when... |
|---|
| The command exists and does what you need | No CLI command covers the endpoint |
| You want interactive prompts | You need fine-grained control |
| You want pretty-printed output | You want raw JSON response |
| You're doing simple CRUD | You need GraphQL queries |
| You need to set custom headers |
| You need pagination control |
Performance Tips
- Use
--limit to fetch only what you need
- Use
--json with specific fields (fetches less data)
- Use
--cache with gh api for frequently accessed, slowly changing data
- Use
--paginate --slurp for aggregations across all pages
- Set
GH_PAGER=cat to disable paging in scripts
Exit Codes
| Code | Meaning |
|---|
| 0 | Success |
| 1 | Error / failure |
| 2 | Usage error |
| 4 | Command cancelled |
| 8 | Checks pending (for gh pr checks) |
Authentication Precedence
GH_TOKEN / GITHUB_TOKEN environment variable
GH_ENTERPRISE_TOKEN (for GHES hosts)
- Stored credentials from
gh auth login
.env file in repo (only if configured)
Useful One-Liners
gh search prs --author=@me --state=open --json repository,number,title
gh api --paginate user/starred --jq '.[].full_name' | head -20
gh api --paginate repos/owner/repo/contributors --jq '.[] | "\(.login): \(.contributions)"' | head -10
pbpaste | gh issue create --title "From clipboard" --body-file -
gh release view --json tagName --jq '.tagName'
gh run watch && notify-send "CI done!"
gh issue list --state all --limit 9999 --json number,title,state,labels,assignees > issues.json
gh pr list --search "SHA_HERE" --state merged --json number,title,url