with one click
gh-cli
// Reliably drive the GitHub gh CLI for issue, PR, and label operations in automation and subagent environments, with pre-flight verification so you never fabricate a success or a fake issue URL.
// Reliably drive the GitHub gh CLI for issue, PR, and label operations in automation and subagent environments, with pre-flight verification so you never fabricate a success or a fake issue URL.
PM-invocable protocol for Cargo publish and release operations in the trusty-tools Rust monorepo: semver rules, 10-step release sequence, macOS codesign safety, and cross-crate dependency ordering
PM-invocable protocol for running and interpreting Rust quality gates in the trusty-tools monorepo: fmt, clippy, and test in strict sequence before any merge
Six-stage quality-gate pipeline for any code implementation task
Severity-tagged code review checklist (CRITICAL/HIGH/MEDIUM/LOW) used by code-critic agent
MCP (Model Context Protocol) - Build AI-native servers with tools, resources, and prompts. TypeScript/Python SDKs for Claude Desktop integration.
MCP (Model Context Protocol) - Build AI-native servers with tools, resources, and prompts. TypeScript/Python SDKs for Claude Desktop integration.
| name | gh-cli |
| description | Reliably drive the GitHub gh CLI for issue, PR, and label operations in automation and subagent environments, with pre-flight verification so you never fabricate a success or a fake issue URL. |
| user-invocable | false |
| disable-model-invocation | true |
| license | Apache-2.0 |
| compatibility | claude-code |
| metadata | {"updated_at":"2026-05-29T00:00:00.000Z"} |
| tags | ["github","gh","cli","issues","pull-requests","labels","automation","ci","troubleshooting"] |
| progressive_disclosure | {"entry_point":{"summary":"Reliably drive the GitHub gh CLI for issue/PR/label work, with pre-flight auth verification so you never fabricate a success.","when_to_use":"When creating, listing, editing, or closing GitHub issues/PRs/labels via gh, especially inside automation or a subagent shell.","quick_start":"1. Run gh auth status and gh repo view BEFORE any op. 2. If auth fails, STOP and report the exact error. 3. Use --json/--jq to parse and confirm results."}} |
gh CLIReliable patterns for using the GitHub gh CLI for issue, PR, and label operations โ built for automation and subagent environments where silent auth failures are common and fabricating a result is the worst outcome.
When to Use: Any time you create, list, view, edit, or close GitHub issues / PRs / labels through gh, particularly inside a sandboxed shell or a delegated subagent.
When an operation cannot be verified as succeeded, report the failure with the exact error. Never invent an issue number, a URL, or a "created successfully" message. A loud, accurate failure is always better than a fake success โ downstream agents and humans act on what gets reported.
Always confirm authentication and repo access first. These are cheap, read-only, and catch the overwhelming majority of failures before you mutate anything.
# 1. Confirm you are authenticated and see which account/scopes are active
gh auth status
# 2. Confirm you can actually reach the target repo (proves token + SSO + network)
gh repo view <org>/<repo> --json name,url
gh auth status reports "not logged into any GitHub hosts" or any error, STOP. Do not run create/edit/close. Diagnose using the sections below, then report the exact error verbatim.gh repo view fails (404, SAML, Bad credentials), STOP. The token may be valid but lack access to this org/repo. Report the exact error.In a subagent: treat a failed pre-flight as a hard stop. Returning "I created issue #42" when auth failed is the failure mode this skill exists to prevent.
gh's OAuth (keyring) tokens are stored in the OS keychain (macOS Keychain, Linux Secret Service). A sandboxed shell cannot read the keychain, so gh falls back to "no credentials found" and reports "not logged into any GitHub hosts" โ even though valid credentials exist.
Symptom: gh auth status says not logged in, but the same command works in a normal terminal.
Fix: Run gh with the sandbox disabled so it can reach the keychain.
dangerouslyDisableSandbox: true on the Bash tool call that runs gh.gh auth status) with the sandbox disabled to confirm before proceeding.GH_CONFIG_DIRgh reads its config (including which hosts.yml holds credentials) from GH_CONFIG_DIR if that env var is set, otherwise from ~/.config/gh. A stale or wrong GH_CONFIG_DIR pointing at a nonexistent directory makes gh report "not logged in" even when valid keyring credentials exist elsewhere.
Diagnose:
echo "$GH_CONFIG_DIR" # Is it set? To what?
ls "$GH_CONFIG_DIR" # Does the directory exist? Has hosts.yml?
Find the config dir that actually has credentials:
find ~ -maxdepth 5 -name hosts.yml -path '*gh*'
Fix โ point GH_CONFIG_DIR at the real config dir, or unset it to fall back to the default:
# Option A: point at the working config explicitly
GH_CONFIG_DIR=/path/to/real/gh-config gh auth status
# Option B: unset to use ~/.config/gh
unset GH_CONFIG_DIR && gh auth status
gh auth status may list several accounts, with one marked active. gh uses the active account. If the active one lacks access to your target org/repo, switch:
gh auth status # see all accounts; note which is "Active account: true"
gh auth switch -u <username> # make the correct account active
gh repo view <org>/<repo> --json name,url # re-verify after switching
Personal Access Tokens pulled from .env files are frequently rejected by orgs that enforce SAML SSO:
HTTP 403: Resource protected by organization SAML enforcement.
You must grant your Personal Access token access to this organization.
Why: Even a valid PAT must be SSO-authorized for that specific org via a browser grant. Tokens injected from .env in a headless environment usually have never been through that grant.
Preferred fix: Use a keyring OAuth token (gh auth login) that has already been SSO-authorized for the org โ OAuth logins prompt for the SSO grant interactively. To authorize an existing PAT, complete the org's SSO browser grant in GitHub settings (Developer settings โ PATs โ Configure SSO).
Rule of thumb: For SSO-enforced orgs, prefer keyring OAuth over .env PATs.
When local auth is unavailable, unreliable, or you need robust bulk operations, run gh from a server that holds a GitHub App installation token. App installation tokens are SSO-exempt and scoped to the installation, making them the most reliable path for automation.
A common pattern is to dispatch the command to a server via AWS SSM:
aws ssm send-command \
--instance-ids <instance-id> --region <region> \
--document-name "AWS-RunShellScript" \
--parameters '{"commands":["gh issue create --repo <org>/<repo> --title \"...\" --body \"...\""]}'
Treat this as the robust path for automation / CI when keychain-backed local auth cannot be guaranteed.
Use --json <fields> --jq <filter> to get machine-parseable output and to confirm results rather than trusting exit codes alone.
# Create โ capture the returned URL; do not invent it
gh issue create --repo <org>/<repo> --title "Title" --body "Body" --label bug
# List (parseable)
gh issue list --repo <org>/<repo> --state open --json number,title,url --jq '.[]'
# View a specific issue
gh issue view <number> --repo <org>/<repo> --json number,title,state,url
# Close
gh issue close <number> --repo <org>/<repo> --comment "Resolved"
# Edit (also the basis of upsert-by-title โ see below)
gh issue edit <number> --repo <org>/<repo> --add-label triaged --body "Updated body"
Avoid duplicate issues by checking for an existing open issue with the same title before creating:
existing=$(gh issue list --repo <org>/<repo> --state open \
--search "in:title \"My exact title\"" \
--json number,title --jq '.[] | select(.title=="My exact title") | .number' | head -n1)
if [ -n "$existing" ]; then
gh issue edit "$existing" --repo <org>/<repo> --body "Refreshed body"
else
gh issue create --repo <org>/<repo> --title "My exact title" --body "..."
fi
gh pr view <number> --repo <org>/<repo> --json number,title,state,url,mergeable
gh pr list --repo <org>/<repo> --state open --json number,title,headRefName --jq '.[]'
gh label create "code-intelligence" --repo <org>/<repo> --color BFD4F2 --description "..."
gh label list --repo <org>/<repo> --json name,color --jq '.[].name'
This section documents a concrete, worked example of the failure modes above โ diagnosed while ticketing subagents could not create issues in duettoresearch/code-intelligence. The generic instructions above are what you apply; this is the case study showing how they combine in practice.
Ticketing subagents reported "not logged into any GitHub hosts" and "Bad credentials" and could not create issues in duettoresearch/code-intelligence. Some runs would otherwise have been tempted to report a fabricated issue URL โ do not.
GH_CONFIG_DIR โ subagents inherited GH_CONFIG_DIR=/Users/masa/.config/gh-duetto, a directory that does not exist, so gh saw no credentials. (See ยง3.).env.local PAT blocked by SAML SSO โ the PAT in .env.local is rejected by duettoresearch org SAML enforcement. (See ยง5.)bobmatnyc returned HTTP 401 Bad credentials. (See ยง4 โ wrong active account.)GH_CONFIG_DIR=/Users/masa/.config/gh-personal # real config dir with valid hosts.yml
sandbox: disabled # so gh can read the keychain
active account: bob-duetto # scopes: gist, read:org, repo, workflow
result: gh repo view duettoresearch/code-intelligence โ succeeds
# Run gh with the real config dir AND the sandbox disabled
# (in Claude Code: dangerouslyDisableSandbox: true on the Bash call)
GH_CONFIG_DIR=/Users/masa/.config/gh-personal gh auth status
# If the wrong account is active, switch first:
GH_CONFIG_DIR=/Users/masa/.config/gh-personal gh auth switch -u bob-duetto
# Then re-verify before any mutation:
GH_CONFIG_DIR=/Users/masa/.config/gh-personal gh repo view duettoresearch/code-intelligence --json name,url
Only after that pre-flight succeeds should the subagent run gh issue create etc. โ and it must report the real returned URL, never a fabricated one.
gh issue create ... # exit code ignored
# "Created issue #42!" โ fabricated; the command may have failed silently
url=$(gh issue create --repo <org>/<repo> --title "T" --body "B")
echo "Created: $url" # report the actual returned URL, or the actual error
It usually means sandbox/keychain or a stale GH_CONFIG_DIR โ diagnose first (ยง2, ยง3).
.env PATs for SSO-enforced orgsPrefer keyring OAuth or a server-side App installation token (ยง5, ยง6).
gh auth status + gh repo view before any op.GH_CONFIG_DIR, multiple accounts, SAML SSO PAT rejection.