GitHub CLI (gh)
Multi-step workflows (pre-merge checklist, debug CI, release, fetch and reply to PR comments): references/workflows.md.
Pull Request Review Comments
Before drafting or posting new PR review findings, read and follow references/review-comments.md. It defines the finding format, classification vocabulary, human-approval and attribution rules, and posting completion criteria.
Scripts
PR comments: use the CLI helper (PEP 723 + Typer + PyGithub + Pydantic). Run from repo root or from this skill directory. No gh binary required for fetch/reply (uses GitHub API with GITHUB_TOKEN or --token; optional fallback: gh auth token). If GITHUB_TOKEN is not set, run export GITHUB_TOKEN=$(gh auth token) before invoking the helper (or pass --token). GitHub operations require network access; request it through the active agent or harness when needed.
Path from repo root: .agents/skills/github-cli/scripts/gh_pr_helper.py (or scripts/gh_pr_helper.py if symlinked).
| Command | Description |
|---|---|
uv run --script .agents/skills/github-cli/scripts/gh_pr_helper.py -- comments [PR_NUMBER] |
Fetch all PR discussion (inline comments + submitted review bodies + top-level comments). Single JSON object to stdout: { "pr_number", "repo", "inline": [...], "reviews": [...], "top_level": [...] }. Omit PR to use current branch's open PR. |
uv run --script ... -- submit-review <PR_NUMBER> --review-file path/to/review.json |
Submit one approved inline review, abort if the PR head changed, and return the review URL plus each inline comment URL. |
uv run --script .agents/skills/github-cli/scripts/gh_pr_helper.py -- reply <COMMENT_ID> "Reply body" |
Post a reply to an inline review comment. |
uv run --script ... -- reply <COMMENT_ID> --reply-file - |
Reply body from stdin. |
uv run --script ... -- reply <COMMENT_ID> --reply-file path/to/body.md |
Reply body from file. |
Draft the reply with the user in a file, then run with --reply-file path to post (avoids long inline strings).
Options (all commands): --repo OWNER/REPO (default: from git remote), --token / -t (default: GITHUB_TOKEN or gh auth token). Full workflow: references/workflows.md § Fetch and Address Review Comments.
Network Access
Sandboxed environments may block GitHub access. Request network permission through the current client or harness; do not assume a client-specific permission field.
Pre-flight
Before any gh operation, verify the binary is available and authenticated:
export PATH="$HOME/.local/bin:$PATH" && gh auth status
Pull Requests
# List open PRs
gh pr list
gh pr list --author=@me
# View PR details -- use --json to avoid GraphQL errors with deprecated fields
gh pr view <number> --json number,title,state,url,headRefName,baseRefName,isDraft,mergeable,reviewDecision,statusCheckRollup,additions,deletions,changedFiles,reviews,labels,assignees,author \
--jq '{number, title, state, url, branch: .headRefName, base: .baseRefName, draft: .isDraft, mergeable, reviewDecision, author: .author.login, additions, deletions, changedFiles, labels: [.labels[].name], checks: [.statusCheckRollup[] | {name: .name, status: .status, conclusion: .conclusion}], reviews: [.reviews[] | {author: .author.login, state: .state}]}'
# Create PR
gh pr create --title "Title" --body "Description"
# Check out a PR locally
gh pr checkout <number>
# Create a draft PR (WIP)
gh pr create --draft --title "Title" --body "Description"
# View PR diff
gh pr diff <number>
# Check if a PR already exists before creating -- use gh pr edit if it does
gh pr view --json number,url 2>/dev/null && echo "PR exists -- use gh pr edit" || echo "No PR yet"
# Edit an existing PR's title/body
gh pr edit <number> --title "New title" --body "New body"
# Commit, push, and create PR in one call
git add -A && git commit -s -S -m "feat: short title" \
&& git push -u origin HEAD \
&& gh pr create --draft --title "feat: short title" --body "$(cat <<'EOF'
## Summary
- ...
EOF
)"
Review Comments
gh pr view --json comments returns top-level PR conversation comments only. Inline code review comments require the REST API:
# Inline code review comments on a PR
gh api repos/NVIDIA-NeMo/Safe-Synthesizer/pulls/<number>/comments
# Submitted reviews and their summary bodies
gh api repos/NVIDIA-NeMo/Safe-Synthesizer/pulls/<number>/reviews
# Issue discussion thread
gh api repos/NVIDIA-NeMo/Safe-Synthesizer/issues/<number>/comments
# Get a PR's base commit SHA (useful for lockfile diff diagnosis)
gh pr view <number> --json baseRefOid -q .baseRefOid
CI / Actions
The project has 7 GitHub Actions workflows:
| Workflow | File | Trigger | Purpose |
|---|---|---|---|
| CI Checks | ci-checks.yml |
push to main, PRs, manual | format, lint, typecheck, unit-test |
| GPU jobs | gpu-tests.yml |
push to main, push to pull-request/<N>, manual |
e2e on A100 (copy-pr-bot; see below) |
| Conventional Commit | conventional-commit.yml |
PR title changes | Validates PR title format |
| DCO Assistant | dco-assistant.yml |
PR events, comments | Developer Certificate of Origin |
| Release | release.yml |
manual dispatch only | Build and publish to PyPI |
| Secrets Detector | secrets-detector.yml |
PRs to main | Scans for leaked secrets |
GPU tests use the copy-pr-bot pattern: they do NOT fire on pull_request events. Instead, copy-pr-bot pushes PR content to a pull-request/<N> branch, which triggers the workflow via a push event. To manually trigger GPU tests on a PR, comment /sync on the PR. See the copy-pr-bot docs.
# Check CI status + recent runs in one call
gh pr checks && gh run list --limit 5
# When you have a run ID (e.g. from a URL like .../actions/runs/<run-id>/job/<job-id>):
gh run view <run-id> --log-failed
# Find latest failure on current branch (when no run ID given)
RUN_ID=$(gh run list --branch="$(git branch --show-current)" --status=failure --limit=1 --json databaseId -q '.[0].databaseId') \
&& [ "$RUN_ID" != "null" ] && gh run view "$RUN_ID" --log-failed
# Re-run failed jobs only
gh run rerun <run-id> --failed
CI jobs map to local commands:
| CI Job | Local Command |
|---|---|
| Format | mise run format (fix) or mise run check:format ::: check:lint ::: check:license:headers (check) |
| Format (lock) | mise run check:lock |
| Typecheck | mise run check:type |
| Unit Tests | mise run test:ci |
Format and type checks run on every pull request. The local pre-PR gate is mise run check ::: test; specialized CI and GPU suites remain separate.
Issues
gh issue list
gh issue view <number>
gh issue create --title "Title" --body "Description"
gh issue edit <number> --body "Updated body"
Issue templates: bug-report, feature-request, development-task (use gh issue create for interactive prompt).
Code Review
# List reviews on a PR
gh pr view <number> --json reviews
# Approve
gh pr review <number> --approve
# Request changes
gh pr review <number> --request-changes --body "feedback"
Releases
gh release list
gh release view <tag>
# Trigger release workflow (dry run first)
gh workflow run release.yml \
-f release-ref=<full-sha-or-tag> \
-f dry-run=true \
-f create-gh-release=true \
-f version-bump-branch=<branch>
CODEOWNERS
Defined in .github/CODEOWNERS:
- All files:
@NVIDIA-NeMo/safe-synthesizer-maintainers src/andtests/:@NVIDIA-NeMo/safe-synthesizer-reviewers- Critical files (
pyproject.toml,uv.lock,SECURITY.md,LICENSE,.github/):@NVIDIA-NeMo/safe-synthesizer-maintainers
gh pr edit <number> --add-reviewer NVIDIA-NeMo/safe-synthesizer-reviewers
Writing PR and Issue Bodies
Always use HEREDOC for multiline bodies:
gh pr create --title "feat: short title" --body "$(cat <<'EOF'
## Summary
- 2-4 bullet points, not a full audit
## Test plan
- [ ] Verify X
- [ ] Check Y
EOF
)"
Keep bodies concise -- 2-4 bullet summary for PRs, problem + options for issues. Don't generate long audit dumps or parameter inventories. When the user asks for "succinct" -- 1-3 sentences, no lists. For issues meant for team discussion, keep options human-readable and decision-focused, not implementation inventories.
No decorative bold (**text**) in PR or issue bodies. No per-file change inventories. No ## Why sections that restate the commit message. Default to the shortest body that answers "what changed and how to verify it."
Common Mistakes
- Don't run
gh run view <id>andgh run view <id> --log-failedas separate calls -- go straight to--log-failedwhen you already have the run ID - Don't WebFetch GitHub Actions URLs for private repos -- returns 404; use
gh run view <run-id> --log-failedinstead - Don't propose
gh pr createwithout checking if a PR already exists first -- usegh pr editif it does - Don't generate long PR/issue bodies -- users consistently ask agents to cut them down; 2-4 bullets max, no audit dumps
- Don't use separate shell calls for
git pushthengh pr create-- chain them with&& - Don't manually write
Signed-off-byin commit messages -- always usegit commit --signoff --gpg-sign(-s -S) so the trailer matchesgit config user.name/user.emailand the commit is cryptographically signed; DCO probot and signature verification both require exact identity match - Don't use plain
gh pr view <number>-- use--jsonform to avoid GraphQL errors about deprecated Projects (classic)