| name | gh-cli |
| description | Manage GitHub Issues and Pull Request review threads from the command line using the gh CLI. Use when the user asks about GitHub issues, labels, milestones, PR review comments, resolving review threads, or needs to manage project work items via the command line. |
GitHub Issues & PR Review CLI
Interact with GitHub Issues from the command line using gh.
When to Use
- User asks to create, view, edit, or search GitHub Issues
- User needs to manage labels, milestones, or assignees
- User wants to close, reopen, or comment on issues
- User needs to resolve or reply to PR review comments/threads
- User needs to submit a PR review (approve, request changes, comment)
- MCP tools (
mcp_github_*) are unavailable or failing
- User needs scripted/batch issue operations
Prerequisites
- Install gh CLI:
brew install gh (macOS) or see install instructions
- Authenticate:
gh auth login
- Verify:
gh auth status
Issue Commands
List Issues
gh issue list
gh issue list --state open --label "bug"
gh issue list --assignee @me
gh issue list --milestone "v1.0"
gh issue list --search "is:open label:epic sort:created-desc"
gh issue list --limit 20
gh issue list --json number,title,state,labels --limit 50
Create Issues
gh issue create
gh issue create --title "Bug: Login fails" --body "Description here" --label "bug" --assignee @me
gh issue create --title "Add feature" --body "Details" --label "enhancement" --milestone "v1.0"
gh issue create --title "Feature request" --body-file ./description.md
gh issue create --title "Review needed" --assignee user1,user2
View Issues
gh issue view 123
gh issue view 123 --comments
gh issue view 123 --json title,body,state,labels,assignees,milestone
gh issue view 123 --web
Edit Issues
gh issue edit 123 --title "Updated title"
gh issue edit 123 --add-label "priority:high,needs-review"
gh issue edit 123 --remove-label "needs-triage"
gh issue edit 123 --milestone "v1.0"
gh issue edit 123 --add-assignee @me,teammate
gh issue edit 123 --remove-assignee old-owner
Close & Reopen Issues
gh issue close 123
gh issue close 123 --comment "Fixed in PR #456"
gh issue close 123 --reason "not planned"
gh issue reopen 123
gh issue reopen 123 --comment "Reopening — needs more work"
Comments
gh issue comment 123 --body "Working on this"
gh issue comment 123 --body-file ./comment.md
gh issue comment 123 --edit-last
Pin & Lock Issues
gh issue pin 123
gh issue unpin 123
gh issue lock 123 --reason "resolved"
gh issue unlock 123
Search
gh search issues "authentication bug" --repo owner/repo
gh search issues "label:bug state:open" --repo owner/repo --limit 20
gh issue list --search "assignee:@me is:open"
Labels
gh label list
gh label create "priority:high" --color FF0000 --description "High priority"
gh label edit "priority:high" --new-name "priority:critical" --color CC0000
gh label delete "old-label" --yes
Milestones
gh api repos/{{REPO_OWNER}}/{{PROJECT_NAME}}/milestones --jq '.[].title'
gh api repos/{{REPO_OWNER}}/{{PROJECT_NAME}}/milestones -f title="v1.0" -f description="First release" -f due_on="2025-06-01T00:00:00Z"
Common Workflows
Triage New Issues
gh issue list --search "is:open no:label no:assignee sort:created-asc"
gh issue edit 123 --add-label "bug,priority:high" --add-assignee @me
Epic Pattern (using labels)
gh issue list --label "epic" --state open
cat > /tmp/epic-body.md <<'EOF'
- Login
- Registration
- Password reset
EOF
gh issue create --title "Epic: User Authentication" --label "epic" --body-file /tmp/epic-body.md
gh issue list --search "is:open label:story \"Authentication\""
Close with PR Reference
gh issue close 123 --comment "Fixed in #456"
Batch Operations
for id in 10 11 12; do gh issue close "$id" --comment "Superseded by #20"; done
for id in 10 11 12; do gh issue edit "$id" --add-label "backlog"; done
Common Flags
| Flag | Description |
|---|
--json | JSON output with field selection |
--jq | Filter JSON output with jq expressions |
--web | Open in browser |
--limit | Max results to return |
--search | Search query with GitHub qualifiers |
--state | Filter by state (open, closed, all) |
--label | Filter by label |
--assignee | Filter by assignee |
--milestone | Filter by milestone |
Output Formatting
| Command | Use Case |
|---|
gh issue list --json number,title | JSON for scripting |
gh issue list --json number,title --jq '.[].title' | Extract specific fields |
gh issue view 123 --json body --jq '.body' | Get issue body only |
Pull Request Review Threads
Manage PR review comments and resolve threads.
List Review Threads
gh api graphql -f query='
{
repository(owner: "OWNER", name: "REPO") {
pullRequest(number: 123) {
reviewThreads(first: 50) {
nodes {
id
isResolved
comments(first: 1) {
nodes {
id
body
path
line
}
}
}
}
}
}
}'
gh api graphql -f query='...' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | {id, file: .comments.nodes[0].path, body: .comments.nodes[0].body}'
Reply to a Review Comment
gh api repos/OWNER/REPO/pulls/PR_NUMBER/comments/COMMENT_ID/replies -X POST -f body="Fixed in commit abc123."
Resolve a Review Thread
Threads are resolved via the GraphQL API. You need the thread node ID (starts with PRRT_).
gh api graphql -f query='
mutation {
resolveReviewThread(input: {threadId: "PRRT_kwDOQlyQTc6IWPxG"}) {
thread { id isResolved }
}
}'
for thread_id in PRRT_xxx PRRT_yyy PRRT_zzz; do
gh api graphql -f query="mutation { resolveReviewThread(input: {threadId: \"$thread_id\"}) { thread { id isResolved } } }"
done
Unresolve a Review Thread
gh api graphql -f query='
mutation {
unresolveReviewThread(input: {threadId: "PRRT_kwDOQlyQTc6IWPxG"}) {
thread { id isResolved }
}
}'
Submit a PR Review
gh api repos/OWNER/REPO/pulls/123/reviews -X POST \
-f body="All comments addressed in commit abc123." \
-f event="COMMENT"
gh api repos/OWNER/REPO/pulls/123/reviews -X POST \
-f body="LGTM" \
-f event="APPROVE"
gh api repos/OWNER/REPO/pulls/123/reviews -X POST \
-f body="Needs fixes" \
-f event="REQUEST_CHANGES"
Full Workflow: Fix Comments and Resolve Threads
gh api graphql -f query='{
repository(owner: "OWNER", name: "REPO") {
pullRequest(number: 123) {
reviewThreads(first: 50) {
nodes { id isResolved comments(first: 1) { nodes { body path } } }
}
}
}
}' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | {thread_id: .id, file: .comments.nodes[0].path}'
git add -A && git commit -m "fix: address review comments" && git push
gh api repos/OWNER/REPO/pulls/PR_NUMBER/comments/COMMENT_ID/replies -X POST -f body="Fixed in commit abc123."
gh api graphql -f query='mutation { resolveReviewThread(input: {threadId: "THREAD_ID"}) { thread { id isResolved } } }'
gh api repos/OWNER/REPO/pulls/123/reviews -X POST \
-f body="All review comments addressed." \
-f event="COMMENT"
Troubleshooting
Permission Errors (FORBIDDEN)
Symptom: does not have the correct permissions to execute ResolveReviewThread
Cause: Multiple GitHub accounts configured; the active account lacks write access.
Diagnosis:
gh auth status
gh auth status 2>&1 | grep -A2 "Active account: true"
Fix — switch to the correct account:
gh auth switch
gh auth switch -u <CORRECT_ACCOUNT>
gh auth status
Fix — if the active account genuinely lacks permissions:
gh api repos/OWNER/REPO --jq '.permissions'
gh auth refresh -s repo,read:org
Prevention: Always verify gh auth status shows the correct active account before batch operations. The resolveReviewThread GraphQL mutation requires write access to the repository.
Thread Resolution Fails Silently
Symptom: resolveReviewThread returns {"data":{"resolveReviewThread":null}} with no error.
Cause: Thread ID is invalid or already resolved.
Fix:
gh api graphql -f query='...' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | {id, isResolved}'
echo "$THREAD_ID" | grep -q "^PRRT_" || echo "Invalid thread ID format"
Rate Limiting on Batch Operations
Symptom: 403 rate limit exceeded or 429 errors during bulk thread resolution.
Fix:
gh api rate_limit --jq '.rate.remaining'
for thread_id in $(thread_ids); do
gh api graphql -f query="mutation { resolveReviewThread(input: {threadId: \"$thread_id\"}) { thread { id isResolved } } }"
sleep 0.5
done
Limitations
- Requires
gh auth login before first use
- GitHub Projects (v2) require separate
gh project commands
- Rate limits: 5000 requests/hour for authenticated users
- Sub-issues require GitHub's sub-issues feature (beta)
- Epic patterns rely on label conventions, not native epics
- PR thread resolution requires the GraphQL API (REST does not support it)
- Thread node IDs start with
PRRT_, comment IDs start with PRRC_