| name | gh |
| description | Invoke whenever the user is working with GitHub. Trigger on any of these signals: a URL containing "github.com" or a self-hosted GitHub Enterprise host, a git remote pointing to GitHub (git@github.com:... or https://github.com/...), the #N issue or PR notation (#15, #42), or words like "PR", "pull request", "gh", "GitHub issue", "GitHub Actions", "workflow run", "release", "gist", "review this PR", "merge this PR", "CI status", "checks", "fork", or "clone". Handles issues, pull requests, PR reviews and comments, GitHub Actions and CI runs, releases, gists, search, and reading files from GitHub repos. Always use gh, not WebFetch or curl, for any GitHub URL because gh handles authentication and returns structured data. This skill is the GitHub equivalent of glab for GitLab; if the project is on GitHub use this skill, and do not invoke it for GitLab tasks (use the glab skill instead). |
gh CLI Skill
Use the gh CLI for ALL GitHub-related tasks including working with issues, pull requests, Actions workflows, releases, and repositories. If given a GitHub URL, use gh to get the information needed.
Before you start
- Detect GitHub URLs: if the user provided a URL containing "github.com", this is a GitHub resource. Extract the owner and repo from the URL and proceed with
gh commands using -R owner/repo.
- Confirm it's a GitHub project: check
git remote -v for "github" in the URL. If so, use gh (not glab).
- Verify authentication: run
gh auth status. If not authenticated, stop and ask the user -- do not proceed.
CLI-first principle
Always prefer gh subcommands (gh issue list, gh pr list, gh run list, etc.) over raw gh api calls. The CLI subcommands provide better output formatting, pagination, and are less error-prone. Use gh api only when no subcommand covers the operation (see the gh api section below).
Targeting a repository
Before running any gh command, determine the repository context:
-
User provided a GitHub URL (e.g., https://github.com/owner/repo/issues/42 or https://github.com/owner/repo/pull/15):
Extract the owner/repo from the URL, then use -R:
gh pr view 15 -R acme/webapp
gh issue list -R acme/webapp --label "bug"
-
Inside a git repo with a GitHub remote: no -R needed, gh detects the repository from the remote automatically.
-
No URL provided and not inside a git repo: ask the user for the full GitHub repository URL before proceeding. Do not guess or assume a repository.
Handling auth errors
If any gh command fails with "authentication", "401", "403", or "could not determine", stop immediately. Do NOT work around auth failures with WebFetch, curl, or glab.
Present the user with these options:
Option 1: Browser login (preferred -- more secure, tokens auto-refresh)
gh auth login --web
Option 2: Personal Access Token
Tell the user to generate a token at: https://github.com/settings/tokens?type=beta -- recommend fine-grained tokens with minimal scopes and a short expiry (30-90 days).
gh auth login --with-token < token.txt
Option 3: Skip the operation
Core terminology
| GitLab | GitHub | CLI |
|---|
| Merge Request | Pull Request | gh pr |
| Snippet | Gist | gh gist |
| CI/CD | Actions | gh run, gh workflow |
GROUP/PROJECT (supports nesting) | OWNER/REPO | -- |
MR !15 | PR #15 | -- |
Issues and PRs both use # prefix.
Writing on behalf of the user
Whenever you create or post content on GitHub on behalf of the user -- including PR descriptions (gh pr create), issue descriptions (gh issue create), comments (gh pr comment, gh issue comment), reviews (gh pr review), or gh api body fields -- you must prepend the following header to make it clear the content was authored by an AI agent acting on behalf of the user:
> :robot: _This was written by an AI agent on behalf of @<username>._
Before writing any content, always fetch the username first and embed it in the header. Do not hardcode a username or leave the placeholder unfilled:
GH_USERNAME=$(gh api user --jq '.login')
gh pr create \
--title "feat: add dark mode" \
--body "> :robot: _This was written by an AI agent on behalf of @${GH_USERNAME}._
## Summary
- Adds dark mode toggle to settings page
- ..."
Example -- adding a comment to issue #42:
gh issue comment 42 \
--body "> :robot: _This was written by an AI agent on behalf of @${GH_USERNAME}._
## Triage
Root cause identified: ..."
This applies to every piece of content the agent creates, regardless of length or context. Never skip the header.
Heredoc warning: when using cat <<EOF to build the body, never single-quote the delimiter (<<'EOF'). Single-quoted heredocs suppress variable expansion and produce the literal string $GH_USERNAME instead of the resolved value. Always use an unquoted delimiter:
gh pr create --title "feat: add dark mode" --body "$(cat <<EOF
> :robot: _This was written by an AI agent on behalf of @${GH_USERNAME}._
## Summary
- Adds dark mode toggle to settings page
EOF
)"
Issue auto-linking: GitHub renders bare #N as a clickable link to issue N. Use backticks (`#18`) when referring to issue numbers as text (examples, tables, logs). Leave #N bare only when it should link to an actual issue (e.g., Closes #42).
Authoring rules for descriptions and commit messages
These rules apply to every issue title and description, pull request title and description, comment, review, and commit message you write.
Write in plain, professional prose. Issue and pull request titles and descriptions, comments and reviews, and commit messages must always be written in normal, complete, well-structured English: full sentences, articles, and proper markdown. They are durable, outward-facing documents that other people read and that GitHub renders in its UI; compression hurts readability and looks unprofessional. This rule overrides any active terse output style for the duration of writing these artifacts. A session-level style may inject a per-turn reminder such as CAVEMAN MODE ACTIVE (drop articles, fragments OK, short synonyms); that reminder applies to your conversational replies, not to the artifacts. When you compose an issue/PR title or body, a comment, a review, or a commit message, write full prose regardless of the active style, then resume the terse conversational style for your surrounding chat replies. Do not run any command to toggle the style off; just write the artifact in plain prose regardless of what reminder is present.
Use the full path for cross-project references. When you reference an issue or pull request that lives in a different repository than the one you are writing in, use the full owner/repo#123 form rather than a bare #123. A bare #123 (or a short form) only resolves within the same repository and will not render as a link from another repository. Apply this in prose and in footers alike (Closes:, Refs:). For example, to reference the platform-team board from a code repository, write sparkfabrik-innovation-team/board#4379, never a bare board#4379 or #4379. Within the same repository, a bare #123 is correct and renders as a link.
Avoid AI-slop writing tells. Do not use the em dash (—) or en dash (–) as a sentence connector; rewrite with a period, comma, colon, or parentheses instead. Prefer clear structure over dense run-on paragraphs: use real line breaks, short paragraphs, and lists, and keep sentences plain and direct. Write like a human engineer, not a generated summary.
Issues
Issue title format
Issue titles must be human-readable, short, and concise — a few words that express the goal or scope of the work. Issue titles are NOT commit messages and must NOT use the Conventional Commits format (no feat:, fix(scope):, chore: prefixes, no imperative commit-style phrasing).
The title is read by humans scanning boards, backlogs, and notifications — it should describe what the issue is about, not how the eventual fix will be committed. Conventional Commits belongs on PR titles and commit messages, where it drives changelogs and tooling. Issues sit upstream of that, often before the solution is even known, so a commit-shaped title is both premature and harder to scan.
Style guidelines:
- Sentence case, no trailing period.
- Aim for under ~60 characters.
- Prefer noun phrases ("Slow dashboard load on Safari") or short problem statements ("Users locked out after password reset") over imperative verbs.
- Do not prefix with
Bug:, Feature:, Task:, etc. — use labels for categorization instead.
Examples:
| Bad (commit-shaped) | Good (human-readable) |
|---|
feat(auth): add JWT token refresh | JWT token refresh |
fix: prevent crash on empty password | Login crash with empty password |
docs(api): update rate limiting section | Rate limiting docs out of date |
refactor(parser): simplify config validation | Simplify config parser |
chore: bump dependencies | Update dependencies |
Bug: login broken on Safari | Login broken on Safari |
Issue commands
gh issue create --title "Login broken on Safari" --body "..." --label bug --label "high-priority" --assignee "@me"
gh issue list --assignee @me
gh issue list --label "bug" --search "login"
gh issue view 42 --comments
gh issue comment 42 --body "Root cause found."
gh issue edit 42 --add-label "confirmed"
gh issue close 42
gh issue reopen 42
Issue template selection
Many GitHub projects define issue templates (stored in .github/ISSUE_TEMPLATE/) that encode the team's expected structure -- sections to fill, checklists, labels. Skipping these creates issues that don't match the project's conventions and forces manual cleanup.
Use --template to select a template by name:
ls .github/ISSUE_TEMPLATE/
gh api repos/{owner}/{repo}/contents/.github/ISSUE_TEMPLATE --jq '.[].name'
gh issue create --template "Bug Report" --title "Login crash on empty password"
If no --template is specified and templates exist, gh issue create will prompt interactively. When running non-interactively, check for templates first and ask the user which one to use.
Label selection process
If the user specified exact labels, use them. Otherwise:
- Discover available labels:
gh label list (or gh label list --json name,description for more detail)
- Propose labels that fit the issue context.
- Ask the user to confirm or adjust before creating the issue.
Do NOT invent label names. Only propose labels that actually exist in the repository.
Create a branch from an issue
gh issue develop 42 --checkout
gh issue develop 42 --name fix/issue-42
gh issue develop 42 --base develop
Pull Requests
Creating PRs
gh pr create --title "Fix login crash" --body "Closes #42" \
--base develop --reviewer "marco" --assignee "@me"
gh pr create --fill
gh pr create --fill-verbose
gh pr create --draft --fill
Include Closes #42 or Fixes #42 in the body to auto-close issues on merge.
PR title format
PR titles must follow the Conventional Commits format:
<type>[(optional scope)]: <description>
Common types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert.
Use a scope when the change is clearly scoped to a module, component, or area of the codebase. Keep the description lowercase, concise, and in imperative mood.
Examples:
feat(auth): add JWT token refresh
fix: prevent crash on empty password submission
docs(api): update rate limiting section
refactor(parser): simplify config validation logic
ci: add deploy stage for staging environment
chore: bump dependencies
Breaking changes append ! before the colon: feat(api)!: change response format for /users endpoint.
PR creation checklist (follow this carefully):
- Inspect branch state:
git status, git log <base>...HEAD --oneline, git diff <base>...HEAD
- Draft title/description from the actual diff -- reference specific files, functions, behaviors. Do not just restate the user's request.
- Push:
git push -u origin HEAD if not yet pushed.
- Create:
gh pr create with all relevant flags.
- Return the PR URL to the user.
Reviewing and managing PRs
gh pr view 15 --comments
gh pr diff 15
gh pr diff 15 --name-only
gh pr list --author @me
gh pr checkout 15
gh pr comment 15 --body "LGTM"
gh pr review 15 --approve --body "Looks good"
gh pr review 15 --request-changes --body "Please fix..."
gh pr merge 15 --squash --delete-branch
gh pr update-branch 15
gh pr edit 15 --add-label "reviewed"
gh pr ready 15
gh pr ready 15 --undo
gh pr close 15
Code review workflow: view PR -> read diff -> check CI (gh pr checks) -> read comments -> leave feedback -> approve or request changes.
Approval and comments are separate actions -- gh pr review --approve handles GitHub's formal review system, gh pr comment posts a timeline comment.
PR checks (CI status per PR)
gh pr checks 15
gh pr checks 15 --watch
gh pr checks 15 --watch --interval 5
gh pr checks 15 --required
gh pr checks 15 --fail-fast
PR Comments and Reviews
GitHub PRs have three distinct comment types. Using the wrong one is a common mistake -- a top-level comment when the user wanted a threaded reply to an inline review creates noise and loses context.
Comment types
| Type | What it is | How to post |
|---|
| Top-level comment | Timeline comment on the PR (like an issue comment) | gh pr comment 15 --body "..." |
| Review | Formal review: approve, request changes, or comment with a body | gh pr review 15 --approve --body "..." |
| Inline review comment | Comment on a specific line of code, starting a thread | gh api (no CLI subcommand) |
| Reply to inline comment | Threaded reply to an existing inline code comment | gh api (no CLI subcommand) |
Replying to review comments
There is no gh pr subcommand for replying to inline code review comments. You must use gh api.
Step 1: List review comments to find the comment ID
gh api repos/{owner}/{repo}/pulls/15/comments \
--jq '.[] | {id, user: .user.login, path, line, body: (.body | .[0:80])}'
Each comment has an id field. Top-level review comments have no in_reply_to_id; replies do.
Step 2: Reply to a specific comment
Use the dedicated replies endpoint:
gh api -X POST repos/{owner}/{repo}/pulls/15/comments/<comment_id>/replies \
-f body="> :robot: _This was written by an AI agent on behalf of @${GH_USERNAME}._
The null check is needed because..."
Constraint: replies to replies are not supported by the API -- you can only reply to top-level review comments (those without in_reply_to_id). If the comment you want to reply to is itself a reply, reply to the parent comment instead.
Step 3 (alternative): Reply using in_reply_to
You can also reply via the create comment endpoint with the in_reply_to parameter. When in_reply_to is set, all other parameters except body are ignored:
gh api -X POST repos/{owner}/{repo}/pulls/15/comments \
-f body="Agreed, good catch." \
-F in_reply_to=<comment_id>
Creating a new inline review comment
To start a new thread on a specific line of code:
COMMIT_SHA=$(gh pr view 15 --json headRefOid --jq '.headRefOid')
gh api -X POST repos/{owner}/{repo}/pulls/15/comments \
-f body="This null check should handle empty strings too." \
-f commit_id="$COMMIT_SHA" \
-f path="src/auth.ts" \
-F line=42 \
-f side="RIGHT"
Parameters:
commit_id (required): use the PR's HEAD commit SHA. Using an older commit may render the comment outdated.
path (required): relative file path in the repo.
line (required for line comments): line number in the diff.
side: RIGHT for additions (green), LEFT for deletions (red).
start_line + start_side: for multi-line comments, the first line of the range.
Responding to all open review comments
When the user asks to respond to review comments on a PR, follow this workflow:
- List all review comments:
gh api repos/{owner}/{repo}/pulls/15/comments --jq '.[] | {id, in_reply_to_id, user: .user.login, path, line, body}'
- Filter to top-level comments (those without
in_reply_to_id) that haven't been addressed yet.
- Present a summary to the user: who commented, on which file/line, what they said.
- Draft replies and get user confirmation before posting.
- Post replies using the replies endpoint for each comment.
Never post replies without showing them to the user first.
Editing and deleting comments
gh pr comment and gh issue comment only support editing/deleting your last comment:
gh pr comment 15 --edit-last --body "Updated comment"
gh pr comment 15 --delete-last
To edit or delete an arbitrary comment by ID, use the API:
gh api -X PATCH repos/{owner}/{repo}/issues/comments/<comment_id> -f body="Updated"
gh api -X PATCH repos/{owner}/{repo}/pulls/comments/<comment_id> -f body="Updated"
gh api -X DELETE repos/{owner}/{repo}/pulls/comments/<comment_id>
GitHub Actions / CI
gh run list
gh run list --branch main
gh run list --workflow "ci.yml"
gh run list --status failure
gh run view <run-id>
gh run view <run-id> --verbose
gh run view <run-id> --log
gh run view <run-id> --log-failed
gh run view <run-id> --job <job-id>
gh run watch <run-id>
gh run watch <run-id> --exit-status
gh run rerun <run-id>
gh run rerun <run-id> --job <job-id>
gh run cancel <run-id>
gh run download <run-id>
gh run download <run-id> --name build
Workflows
gh workflow list
gh workflow view ci.yml
gh workflow view ci.yml --yaml
gh workflow run ci.yml
gh workflow run ci.yml --ref develop
gh workflow run ci.yml -f version="1.0" -f env="prod"
gh workflow enable ci.yml
gh workflow disable ci.yml
Monitoring a run
To watch a specific run, first find its ID, then watch it:
gh run list --branch main --limit 1 --json databaseId --jq '.[0].databaseId'
gh run watch <run-id>
gh run watch <run-id> --compact
gh run watch takes a run ID, not a branch name. To monitor a run on a specific branch, always look up the run ID first via gh run list --branch <branch>.
For PR-specific checks, use gh pr checks instead -- it shows all status checks on a PR and supports --watch:
gh pr checks 15 --watch
Viewing failed logs
When a run fails, --log-failed is the most efficient way to see what went wrong:
gh run view <run-id> --log-failed
This shows only the output from failed steps, avoiding hundreds of lines of passing step output.
Actions caches
gh cache list
gh cache list --limit 50
gh cache delete <cache-id>
Releases
gh release list
gh release view v1.0.0
gh release create v1.0.0 --notes "Release notes" --title "Version 1.0.0"
gh release create v1.0.0 --generate-notes
gh release create v1.0.0 --notes-file notes.md
gh release create v1.0.0 --draft
gh release create v1.0.0 --prerelease
gh release create v1.0.0 ./dist/*.tar.gz
gh release upload v1.0.0 ./file.tar.gz
gh release download v1.0.0
gh release download v1.0.0 --pattern "*.tar.gz"
gh release edit v1.0.0 --notes "Updated notes"
gh release delete v1.0.0
Search
gh search lets you search across all of GitHub, not just the current repository:
gh search issues "label:bug state:open" --repo owner/repo
gh search prs "is:open review:required" --repo owner/repo
gh search code "TODO" --repo owner/repo
gh search commits "fix auth" --repo owner/repo
gh search repos "stars:>1000 language:python"
All search commands support --json, --jq, and --limit for structured output.
Safety Protocol
Your default role is read-only. Do NOT proactively suggest, offer, or execute state-changing operations. Only perform write or mutating operations when the user explicitly asks for them.
Tier 1 -- FORBIDDEN (never execute these)
These commands are never executed, regardless of what the user asks. If the user needs one of these, explain the consequences and tell them how to run it manually.
| Action | Command |
|---|
| Delete repo | gh repo delete |
| Delete release | gh release delete |
| Delete workflow runs | gh run delete |
| Destructive API calls | gh api -X DELETE on critical resources |
| Force push to default branch | git push --force to main/master/default |
| Hard reset | git reset --hard |
Tier 2 -- EXPLICIT REQUEST ONLY (never suggest, never offer)
These commands are executed only when the user explicitly requests them with clear intent (e.g., "merge the PR", "close issue #42"). Never propose them, never include them in automated workflows, never ask "should I merge/close this?".
Before executing, always explain what will happen and ask for confirmation.
| Action | Command |
|---|
| Merge PR | gh pr merge |
| Merge bypassing checks | gh pr merge --admin (bypasses branch protection -- extra caution) |
| Close issue or PR | gh issue close, gh pr close |
| Delete issue | gh issue delete |
| Cancel workflow run | gh run cancel |
| Disable workflow | gh workflow disable |
| Modify secrets | gh secret set, gh secret delete |
| Modify variables | gh variable set, gh variable delete |
| Delete release assets | gh release delete-asset |
| Delete all caches | gh cache delete --all |
| Force push (non-default branch) | git push --force |
| Skip hooks | --no-verify |
Tier 3 -- SAFE WITH CONFIRMATION
These operations can be proposed when relevant, but require a brief confirmation before execution.
| Action | Command |
|---|
| Update PR branch | gh pr update-branch |
| Update metadata (labels, assignees, etc.) | gh pr edit, gh issue edit |
| Change draft status | gh pr ready, gh pr ready --undo |
| Rerun workflow | gh run rerun |
| Trigger workflow | gh workflow run |
Git safety
- Prefer
gh pr update-branch over manual rebase + force push
- When in doubt about target branch, check the repo default:
gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name'
gh api -- last resort for advanced operations
Do NOT use gh api when a CLI subcommand can do the job. For issues, PRs, runs, labels -- always use the dedicated subcommands first. Use gh api only for operations not covered by any subcommand (e.g., review comment replies, team membership, GraphQL queries).
gh api repos/{owner}/{repo}/collaborators
gh api -X POST repos/{owner}/{repo}/issues -f title="New" -f body="..."
gh api -X PATCH repos/{owner}/{repo}/pulls/15 -f title="Updated"
gh api repos/{owner}/{repo}/issues --paginate
Placeholder variables
Inside a git repo, {owner}, {repo}, and {branch} are auto-resolved:
gh api repos/{owner}/{repo}/pulls
Native --jq filtering
Unlike glab (which requires piping to external jq), gh api has built-in --jq support:
gh api repos/{owner}/{repo}/pulls --jq '.[].title'
gh api user --jq '.login'
gh api repos/{owner}/{repo}/contributors --jq '.[] | {login: .login, contributions: .contributions}'
Use --jq instead of piping to jq whenever possible -- it's faster and avoids a dependency.
GraphQL
gh api graphql -f query='
query {
repository(owner: "owner", name: "repo") {
pullRequests(states: OPEN, first: 10) {
nodes {
number
title
author { login }
reviewDecision
}
}
}
}
'
For comprehensive API patterns (GraphQL, pagination, advanced queries), read references/api-patterns.md.