Skillquality 0.45

gh

GitHub CLI — source control, CI, code review, and issues for OSS and personal repos

Price
free
Protocol
skill
Verified
no

What it does

Skill: gh

GitHub CLI (gh) gotchas that --help won't tell you.

Reading PR/issue comments — prefer gh pr view --json

For plain comment reads, use gh pr view N --json comments,reviews --jq ... (or gh issue view) instead of gh api repos/.../pulls/N/comments. The gh pr view form is allow-listed as read-only; gh api is on ask because it accepts -X POST/PATCH/DELETE.

# Top-level PR comments
gh pr view "$PR" --json comments --jq '.comments[] | {user: .author.login, body: .body}'

# Inline review comments
gh pr view "$PR" --json reviews --jq '.reviews[] | {user: .author.login, state, body: .body}'

Field names differ from REST: .author.login (not .user.login), no commit_id or in_reply_to_id. Drop down to gh api .../comments only when you need those REST-only fields (e.g. the automated-review flow below).

What needs action across repos

Bucket cross-repo PR work on latestReviews[].state and mergeStateStatus:

gh api graphql -f query='{
  viewer { pullRequests(first: 50, states: OPEN) { nodes {
    number title url isDraft mergeStateStatus
    repository { nameWithOwner }
    reviewRequests(first: 20) { nodes { requestedReviewer { ... on User { login } ... on Bot { login } } } }
    latestReviews(first: 20) { nodes { state author { login } submittedAt } }
    commits(last: 1) { nodes { commit { committedDate } } }
  } } }
  search(query: "is:pr is:open review-requested:@me", type: ISSUE, first: 50) { nodes { ... on PullRequest {
    number title url repository { nameWithOwner }
  } } }
}'

Bucket your authored PRs (closest-to-done first). lastCommit = commits[-1].commit.committedDate:

BucketRule
conflictmergeStateStatus == DIRTY — fix first
ci-failingmergeStateStatus == UNSTABLE
review-to-addressany latestReviews[].state in (CHANGES_REQUESTED, COMMENTED) with submittedAt > lastCommit — bots count
ready-to-mergemergeStateStatus == CLEAN AND any APPROVED in latestReviews[].state AND no CHANGES_REQUESTED/COMMENTED newer than lastCommit
waiting-for-reviewnone of the above; reviewRequests non-empty or no review yet

The search block returns others' PRs requesting your review (review-requested). For mentions and assigned issues without PRs, use gh status (--org <org> to scope, -e <owner/repo> to exclude).

mergeStateStatus values

ValueMeans
CLEANNo conflicts, branch protection satisfied
DIRTYMerge conflicts
UNSTABLECI failing
BLOCKEDBranch protection unsatisfied (e.g. required approval missing) — not a conflict
BEHINDBehind base branch
HAS_HOOKSMergeable with passing status and pre-receive hooks
UNKNOWNRetry

Known silent failures

  • gh pr list --reviewer @me returns empty even when review requests exist — use the GraphQL search block above (or gh search prs --review-requested=@me).
  • gh search prs --reviewed-by @me returns empty — for PRs you've reviewed, query per-repo with gh pr list --json author,reviews and filter reviews[].author.login == @me AND author.login != @me.

Automated review (Copilot, Codex, etc.)

gh is the transport for GitHub-hosted automated reviewers. Per-org config lives in ~/.local/share/chezmoi/.chezmoidata/local.yaml under orgs.<org>.automated_review (see local.yaml.example in the dotfiles repo root for schema). Orgs without that block have no automated review available.

Is automated review available for this repo?

Run this exact command. Do not infer availability from collaborator lists, repo settings, prior PRs, or anything else.

ORG=$(gh repo view --json owner -q '.owner.login')
chezmoi data --format json | jq -r ".orgs[\"$ORG\"].automated_review // empty"
  • Non-empty output = available. The returned object has bot_login, auto_runs, and trigger. Proceed with the trigger/wait/fetch flow below.
  • Empty output = not configured for this org. Only then fall back to the no-automation pipeline.

Common wrong checks that will give you a false negative:

  • Listing repo collaborators and looking for the bot login — the bot is not added as a collaborator
  • Checking org-level installed apps via gh api — the bot may be installed at GitHub-account level rather than per-org
  • Looking at prior PRs for bot reviews — a repo with no prior bot reviews may still be configured

Fetch the latest automated review for a PR

Pull all reviews authored by the configured bot_login. Most recent first; take [0] for the latest:

gh api "repos/$OWNER/$REPO/pulls/$PR/reviews" --paginate \
  --jq "[.[] | select(.user.login == \"$BOT_LOGIN\")] | sort_by(.submitted_at) | reverse"

Each review has commit_id (the head SHA at review time — needed for staleness checks) and submitted_at. Inline comments live separately:

gh api "repos/$OWNER/$REPO/pulls/$PR/comments" --paginate \
  --jq "[.[] | select(.user.login == \"$BOT_LOGIN\") | {path, line, body, id, in_reply_to_id, commit_id}]"

Reply threads on a comment use in_reply_to_id chains.

Trigger automated review

Use only the commands below. Do not invent alternatives. The REST endpoint POST /repos/{o}/{r}/pulls/{n}/requested_reviewers returns 422 "Reviews may only be requested from collaborators" when given copilot-pull-request-reviewer — the bot is not a collaborator. Only gh pr edit --add-reviewer "@copilot" works, because @copilot is a special handle that gh resolves client-side (see gh pr edit --help).

The trigger field returned by the availability check tells you which form. Parse it and run the corresponding command — do not hardcode the handle or text:

  • add_reviewer @handlegh pr edit $PR --add-reviewer "@handle"
  • comment @handle <text>gh pr comment $PR --body "@handle <text>"
TRIGGER=$(chezmoi data --format json | jq -r ".orgs[\"$ORG\"].automated_review.trigger")
FORM="${TRIGGER%% *}"          # "add_reviewer" or "comment"
ARGS="${TRIGGER#* }"           # everything after the first word

if [[ "$FORM" == "add_reviewer" ]]; then
  gh pr edit "$PR" --add-reviewer "$ARGS"
else
  gh pr comment "$PR" --body "$ARGS"
fi

Triggering is a public action — it appears in the timeline. Announce it before doing it.

After triggering, verify the bot was actually added as a reviewer before waiting:

gh api "repos/$OWNER/$REPO/pulls/$PR/requested_reviewers" --jq '.users[].login, (.users[] | select(.type=="Bot") | .login)'

Note: Copilot appears in requested_reviewers as login Copilot (a Bot user, app slug copilot-pull-request-reviewer), but its review submissions use login copilot-pull-request-reviewer[bot] — the bot_login from the availability check matches the review author, not the requested-reviewer login.

Wait for a fresh review

After triggering (or to wait for an auto-running bot), poll until a review exists with a commit_id matching the current HEAD (or just newer than since_sha). Reasonable cadence: every 5s, timeout 120s.

TARGET_SHA=$(gh pr view "$PR" --json headRefOid -q .headRefOid)
for i in {1..24}; do
  LATEST=$(gh api "repos/$OWNER/$REPO/pulls/$PR/reviews" --paginate \
    --jq "[.[] | select(.user.login == \"$BOT_LOGIN\")] | sort_by(.submitted_at) | reverse | .[0].commit_id")
  [[ "$LATEST" == "$TARGET_SHA" ]] && break
  sleep 5
done

Was a prior review already requested-and-dismissed?

Don't re-trigger if the human author dismissed the bot without a fix and the diff hasn't materially changed. Check review timeline events:

gh api "repos/$OWNER/$REPO/pulls/$PR/reviews" --paginate \
  --jq "[.[] | select(.user.login == \"$BOT_LOGIN\") | {state, dismissed: .state == \"DISMISSED\", submitted_at, commit_id}]"

state == "DISMISSED" with no follow-up review means the author chose not to act on it.

Checking repo visibility

gh repo view --json visibility -q '.visibility'

CI status

# Latest run on a branch
gh run list --branch <branch> --limit 1

# Failed step logs
gh run view <run-id> --log-failed

Inline Review Comments

gh pr review doesn't support inline comments on specific lines. Use gh api instead.

Posting Inline Comments

Use --input - with heredoc JSON (not -f 'comments=[...]' which gets stringified):

gh api repos/{owner}/{repo}/pulls/{pr_number}/reviews \
  --method POST \
  --input - << 'EOF'
{
  "event": "REQUEST_CHANGES",
  "comments": [
    {"path":"file.rb","line":10,"body":"Comment text"},
    {"path":"other.rb","line":25,"body":"Another comment"}
  ]
}
EOF

Event types: COMMENT, APPROVE, REQUEST_CHANGES.

Approving with Nits

When approving with non-blocking feedback (nits), disable auto-merge so the author has a chance to address them before the merge lands:

gh pr merge --disable-auto {pr_number}

Do this before posting the review. If auto-merge wasn't enabled, the command is a no-op that exits cleanly.

Multi-line Comments

{"path":"file.rb","start_line":5,"line":10,"body":"This block..."}

Responding to Review Feedback

When addressing reviewer comments after making fixes:

  1. Fixed it? Resolve the conversation thread (no reply needed).
  2. Not addressing it? Reply explaining why.

Push before resolving. GitHub ties thread resolution to the commit that addressed it.

Listing Review Threads

gh api graphql -f query='
  query($owner:String!,$repo:String!,$pr:Int!) {
    repository(owner:$owner,name:$repo) {
      pullRequest(number:$pr) {
        reviewThreads(first:100) {
          nodes {
            id isResolved path line
            comments(first:5) {
              nodes { id body author { login } }
            }
          }
        }
      }
    }
  }' -f owner=OWNER -f repo=REPO -F pr=NUMBER

Resolving a Thread

gh api graphql -f query='
  mutation($id:ID!) {
    resolveReviewThread(input:{threadId:$id}) {
      thread { isResolved }
    }
  }' -f id=PRRT_kwDOxxxxxxx

Replying to a Thread

Use the REST API with the top comment's numeric ID:

gh api repos/{owner}/{repo}/pulls/{pr_number}/comments/{comment_id}/replies \
  --method POST \
  -f body="Intentionally left as-is because..."

Line Number Gotchas

  • Line numbers are file lines in HEAD commit (new file version)
  • GitHub's diff display can be off-by-one from what you expect
  • To delete a misplaced comment: gh api repos/{owner}/{repo}/pulls/comments/{id} --method DELETE

Notes

  • Omit top-level body field to skip summary comment
  • Each comment needs: path, line, body

GitHub Issues

gh issue handles GitHub Issues for OSS and personal repos (non-Linear trackers). Use gh issue create, gh issue list, gh issue view, and gh issue edit. GitHub issue templates live in .github/ISSUE_TEMPLATE/*.mdgh issue create --template only works interactively; read the template file directly and pass content via --body-file.

Capabilities

skillsource-athal7skill-ghtopic-agent-skills

Install

Installnpx skills add athal7/dotfiles
Transportskills-sh
Protocolskill

Quality

0.45/ 1.00

deterministic score 0.45 from registry signals: · indexed on github topic:agent-skills · 6 github stars · SKILL.md body (11,111 chars)

Provenance

Indexed fromgithub
Enriched2026-05-18 19:14:34Z · deterministic:skill-github:v1 · v1
First seen2026-05-18
Last seen2026-05-18

Agent access