Russ534

github-workflow

"GitHub end-to-end workflow: auth, repos, issues, code review, PRs, CI, releases. Use the `gh` CLI when available, fall back to `git` + `curl` for headless / no-sudo machines."

Russ534 0 Updated 4d ago

Resources

3
GitHub

Install

npx skillscat add russ534/myopenclawbot/github-workflow

Install via the SkillsCat registry.

SKILL.md

GitHub Workflow

End-to-end guide for working with GitHub from the agent — covers the full lifecycle from authentication through merge, plus repo setup, issue triage, code review, and release management. Every section shows the gh CLI way first, then a git + curl fallback for machines without gh (no sudo, headless servers, minimal containers).

Sections:

  1. Authentication & Environment — start here
  2. Repository Management — clone, create, fork, configure, secrets, releases, gists, Actions
  3. Issues Management — view, create, label, assign, triage
  4. Code Review — local pre-push review, PR review with inline comments
  5. Pull Request Workflow — branch, commit, push, open PR, monitor CI, merge

0. Authentication & Environment

Every GitHub workflow starts with the same auth detection. Get this right once, then every section below "just works."

Detection Flow

# Check what's available
git --version
gh --version 2>/dev/null || echo "gh not installed"
gh auth status 2>/dev/null || echo "gh not authenticated"
git config --global credential.helper 2>/dev/null || echo "no git credential helper"

Decision tree:

  1. If gh auth status shows authenticated → use gh for everything
  2. If gh is installed but not authenticated → use gh auth login (Method 2 below)
  3. If gh is not installed → use git-only Method 1 below (no sudo needed)

Method 1: Git-Only Authentication (No gh, No sudo)

Works on any machine with git installed. No root access needed.

Option A: HTTPS with Personal Access Token (recommended)

  1. Tell the user to go to https://github.com/settings/tokens

    • Click "Generate new token (classic)"
    • Name it (e.g. "hermes-agent")
    • Select scopes: repo, workflow, read:org (if working with org repos)
    • Set expiration (90 days is a good default)
    • Copy the token — it won't be shown again
  2. Configure git to store the token:

    # "store" saves to ~/.git-credentials in plaintext (simple, persistent)
    git config --global credential.helper store
    
    # Trigger auth — git prompts for credentials
    # Username: <github-username>
    # Password: <paste the personal access token, NOT their password>
    git ls-remote https://github.com/<username>/<any-repo>.git

    After entering once, they're saved and reused forever. Alternatives:

    # In-memory only, expires after 8 hours
    git config --global credential.helper 'cache --timeout=28800'
    
    # Embed token in the remote URL (per-repo, no prompts)
    git remote set-url origin https://<username>:<token>@github.com/<owner>/<repo>.git
  3. Configure git identity (required for commits):

    git config --global user.name "Their Name"
    git config --global user.email "their-email@example.com"
  4. Verify:

    git config --global user.name
    git config --global user.email

Option B: SSH Key Authentication

# Check for existing keys
ls -la ~/.ssh/id_*.pub 2>/dev/null || echo "No SSH keys found"

# Generate ed25519 key if needed
ssh-keygen -t ed25519 -C "their-email@example.com" -f ~/.ssh/id_ed25519 -N ""
cat ~/.ssh/id_ed25519.pub   # user pastes this at https://github.com/settings/keys

# Test
ssh -T git@github.com   # "Hi <username>! You've successfully authenticated..."

# Auto-rewrite HTTPS → SSH for GitHub
git config --global url."git@github.com:".insteadOf "https://github.com/"

Method 2: gh CLI Authentication

If gh is installed, it handles both API access and git credentials in one step.

# Interactive browser login (desktop)
gh auth login

# Token-based login (headless / SSH servers)
echo "<THEIR_TOKEN>" | gh auth login --with-token
gh auth setup-git

# Verify
gh auth status

Reusable Auth Detection Block

Drop this at the top of any GitHub script — it sets AUTH (gh or git), GITHUB_TOKEN, GH_USER, GH_OWNER, GH_REPO, GH_OWNER_REPO.

# Source the helper script (preferred):
source "${HERMES_HOME:-$HOME/.hermes}/skills/github/github-workflow/scripts/gh-env.sh"
# Sets: GH_AUTH_METHOD, GITHUB_TOKEN, GH_USER, GH_OWNER, GH_REPO, GH_OWNER_REPO

# Or inline:
if command -v gh &>/dev/null && gh auth status &>/dev/null; then
  AUTH="gh"
else
  AUTH="git"
  if [ -z "$GITHUB_TOKEN" ]; then
    if _hermes_env="${HERMES_HOME:-$HOME/.hermes}/.env"; [ -f "$_hermes_env" ] && grep -q "^GITHUB_TOKEN=" "$_hermes_env"; then
      GITHUB_TOKEN=$(grep "^GITHUB_TOKEN=" "$_hermes_env" | head -1 | cut -d= -f2 | tr -d '\n\r')
    elif grep -q "github.com" ~/.git-credentials 2>/dev/null; then
      GITHUB_TOKEN=$(grep "github.com" ~/.git-credentials 2>/dev/null | head -1 | sed 's|https://[^:]*:\([^@]*\)@.*|\1|')
    fi
  fi
fi

REMOTE_URL=$(git remote get-url origin 2>/dev/null)
if [ -n "$REMOTE_URL" ] && echo "$REMOTE_URL" | grep -q "github.com"; then
  OWNER_REPO=$(echo "$REMOTE_URL" | sed -E 's|.*github\.com[:/]||; s|\.git$||')
  OWNER=$(echo "$OWNER_REPO" | cut -d/ -f1)
  REPO=$(echo "$OWNER_REPO" | cut -d/ -f2)
fi

Auth Troubleshooting

Problem Solution
git push asks for password GitHub disabled password auth. Use a personal access token as the password, or switch to SSH
remote: Permission to X denied Token may lack repo scope — regenerate with correct scopes
fatal: Authentication failed Cached credentials stale — run git credential reject then re-authenticate
ssh: connect to host github.com port 22: Connection refused Use SSH over HTTPS port: add Host github.com / Port 443 / Hostname ssh.github.com to ~/.ssh/config
Credentials not persisting Check git config --global credential.helper — must be store or cache
Multiple GitHub accounts SSH with different keys per host alias, or per-repo credential URLs
gh: command not found + no sudo Use git-only Method 1 above — no installation needed

1. Repository Management

Create, clone, fork, configure, and manage GitHub repositories.

1.1 Cloning

Cloning is pure git — works identically either way:

# HTTPS (works with credential helper or token-embedded URL)
git clone https://github.com/owner/repo-name.git

# Into a specific directory
git clone https://github.com/owner/repo-name.git ./my-local-dir

# Shallow (faster for large repos)
git clone --depth 1 https://github.com/owner/repo-name.git

# Specific branch
git clone --branch develop https://github.com/owner/repo-name.git

# SSH (if configured)
git clone git@github.com:owner/repo-name.git

With gh shorthand:

gh repo clone owner/repo-name
gh repo clone owner/repo-name -- --depth 1

1.2 Creating Repositories

With gh:

# Public, cloned
gh repo create my-new-project --public --clone

# Private, with description and license
gh repo create my-new-project --private --description "A useful tool" --license MIT --clone

# Under an organization
gh repo create my-org/my-new-project --public --clone

# From existing local directory
cd /path/to/existing/project
gh repo create my-project --source . --public --push

With git + curl:

# Create the remote repo via API
curl -s -X POST \
  -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/user/repos \
  -d '{
    "name": "my-new-project",
    "description": "A useful tool",
    "private": false,
    "auto_init": true,
    "license_template": "mit"
  }'

# Clone it
git clone https://github.com/$GH_USER/my-new-project.git
cd my-new-project

# -- OR -- push an existing local directory to the new repo
cd /path/to/existing/project
git init && git add . && git commit -m "Initial commit"
git remote add origin https://github.com/$GH_USER/my-new-project.git
git push -u origin main

Under an organization:

curl -s -X POST \
  -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/orgs/my-org/repos \
  -d '{"name": "my-new-project", "private": false}'

From a template:

# gh
gh repo create my-new-app --template owner/template-repo --public --clone

# curl
curl -s -X POST \
  -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/owner/template-repo/generate \
  -d '{"owner": "'"$GH_USER"'", "name": "my-new-app", "private": false}'

1.3 Forking

# gh — does the API call, clone, and remote setup in one step
gh repo fork owner/repo-name --clone

# curl + manual
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/owner/repo-name/forks
sleep 3
git clone https://github.com/$GH_USER/repo-name.git
cd repo-name
git remote add upstream https://github.com/owner/repo-name.git

Keep a fork in sync:

git fetch upstream
git checkout main
git merge upstream/main
git push origin main

# gh shortcut
gh repo sync $GH_USER/repo-name

1.4 Repository Information

# gh
gh repo view owner/repo-name
gh repo list --limit 20
gh search repos "machine learning" --language python --sort stars

# curl
curl -s -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO
curl -s -H "Authorization: token $GITHUB_TOKEN" \
  "https://api.github.com/user/repos?per_page=20&sort=updated"

1.5 Repository Settings

# gh
gh repo edit --description "Updated description" --visibility public
gh repo edit --enable-wiki=false --enable-issues=true
gh repo edit --default-branch main
gh repo edit --add-topic "machine-learning,python"
gh repo edit --enable-auto-merge

# curl
curl -s -X PATCH \
  -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO \
  -d '{"description": "Updated description", "has_wiki": false, "has_issues": true, "allow_auto_merge": true}'

# Update topics
curl -s -X PUT \
  -H "Authorization: token $GITHUB_TOKEN" \
  -H "Accept: application/vnd.github.mercy-preview+json" \
  https://api.github.com/repos/$OWNER/$REPO/topics \
  -d '{"names": ["machine-learning", "python", "automation"]}'

1.6 Branch Protection

# View current protection
curl -s -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/branches/main/protection

# Set protection
curl -s -X PUT \
  -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/branches/main/protection \
  -d '{
    "required_status_checks": {"strict": true, "contexts": ["ci/test", "ci/lint"]},
    "enforce_admins": false,
    "required_pull_request_reviews": {"required_approving_review_count": 1},
    "restrictions": null
  }'

1.7 Secrets Management (GitHub Actions)

# gh — much simpler
gh secret set API_KEY --body "your-secret-value"
gh secret set SSH_KEY < ~/.ssh/id_rsa
gh secret list
gh secret delete API_KEY

With curl, secrets need encryption with the repo's public key (requires PyNaCl):

KEY_ID=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/actions/secrets/public-key \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['key_id'])")
PUBKEY=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/actions/secrets/public-key \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['key'])")

ENCRYPTED=$(python3 -c "
from base64 import b64encode
from nacl import encoding, public
sealed = public.SealedBox(public.PublicKey('$PUBKEY'.encode(), encoding.Base64Encoder)).encrypt(b'your-secret-value')
print(b64encode(sealed).decode())")

curl -s -X PUT \
  -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/actions/secrets/API_KEY \
  -d "{\"encrypted_value\": \"$ENCRYPTED\", \"key_id\": \"$KEY_ID\"}"

For secrets, gh secret set is dramatically simpler. If setting secrets is needed and gh isn't available, recommend installing it for just that operation.

1.8 Releases

# gh
gh release create v1.0.0 --title "v1.0.0" --generate-notes
gh release create v2.0.0-rc1 --draft --prerelease --generate-notes
gh release create v1.0.0 ./dist/binary --title "v1.0.0" --notes "Release notes"
gh release list
gh release download v1.0.0 --dir ./downloads

# curl
curl -s -X POST \
  -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/releases \
  -d '{"tag_name": "v1.0.0", "name": "v1.0.0", "body": "## Changelog\n- Feature A", "generate_release_notes": true}'

# Upload a release asset
RELEASE_ID=<id_from_create_response>
curl -s -X POST \
  -H "Authorization: token $GITHUB_TOKEN" \
  -H "Content-Type: application/octet-stream" \
  "https://uploads.github.com/repos/$OWNER/$REPO/releases/$RELEASE_ID/assets?name=binary-amd64" \
  --data-binary @./dist/binary-amd64

1.9 GitHub Actions Workflows

# gh
gh workflow list
gh run list --limit 10
gh run view <RUN_ID>
gh run view <RUN_ID> --log-failed
gh run rerun <RUN_ID>
gh run rerun <RUN_ID> --failed
gh workflow run ci.yml --ref main
gh workflow run deploy.yml -f environment=staging

# curl
curl -s -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/actions/workflows
curl -s -H "Authorization: token $GITHUB_TOKEN" \
  "https://api.github.com/repos/$OWNER/$REPO/actions/runs?per_page=10"

# Download failed run logs
RUN_ID=<run_id>
curl -sL -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/actions/runs/$RUN_ID/logs \
  -o /tmp/ci-logs.zip
cd /tmp && unzip -o ci-logs.zip -d ci-logs

# Rerun
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/actions/runs/$RUN_ID/rerun
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/actions/runs/$RUN_ID/rerun-failed-jobs

# Manually trigger
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/actions/workflows/$WORKFLOW_ID/dispatches \
  -d '{"ref": "main", "inputs": {"environment": "staging"}}'

1.10 Gists

# gh
gh gist create script.py --public --desc "Useful script"
gh gist list

# curl
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/gists \
  -d '{"description": "Useful script", "public": true, "files": {"script.py": {"content": "print(\"hello\")"}}}'

1.11 Repo Quick Reference

Action gh git + curl
Clone gh repo clone o/r git clone https://github.com/o/r.git
Create repo gh repo create name --public curl POST /user/repos
Fork gh repo fork o/r --clone curl POST /repos/o/r/forks + git clone
Repo info gh repo view o/r curl GET /repos/o/r
Edit settings gh repo edit --... curl PATCH /repos/o/r
Create release gh release create v1.0 curl POST /repos/o/r/releases
List workflows gh workflow list curl GET /repos/o/r/actions/workflows
Rerun CI gh run rerun ID curl POST /repos/o/r/actions/runs/ID/rerun
Set secret gh secret set KEY curl PUT /repos/o/r/actions/secrets/KEY (+ encryption)

2. Issues Management

Create, search, triage, and manage GitHub issues. Every command shows gh first, then curl.

2.1 Viewing Issues

# gh
gh issue list
gh issue list --state open --label "bug"
gh issue list --assignee @me
gh issue list --search "authentication error" --state all
gh issue view 42

# curl
curl -s -H "Authorization: token $GITHUB_TOKEN" \
  "https://api.github.com/repos/$OWNER/$REPO/issues?state=open&per_page=20"
curl -s -H "Authorization: token $GITHUB_TOKEN" \
  "https://api.github.com/repos/$OWNER/$REPO/issues?state=open&labels=bug&per_page=20"
curl -s -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/issues/42
curl -s -H "Authorization: token $GITHUB_TOKEN" \
  "https://api.github.com/search/issues?q=authentication+error+repo:$OWNER/$REPO"

GitHub's /issues endpoint returns PRs too — filter with if 'pull_request' not in i: when iterating.

2.2 Creating Issues

# gh
gh issue create \
  --title "Login redirect ignores ?next= parameter" \
  --body "## Description
After logging in, users always land on /dashboard.

## Steps to Reproduce
1. Navigate to /settings while logged out
2. Get redirected to /login?next=/settings
3. Log in
4. Actual: redirected to /dashboard (should go to /settings)

## Expected Behavior
Respect the ?next= query parameter." \
  --label "bug,backend" \
  --assignee "username"

# curl
curl -s -X POST \
  -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/issues \
  -d '{
    "title": "Login redirect ignores ?next= parameter",
    "body": "## Description\nAfter logging in, users always land on /dashboard.",
    "labels": ["bug", "backend"],
    "assignees": ["username"]
  }'

Use the templates/bug-report.md and templates/feature-request.md files in this skill for full structured issue templates.

2.3 Managing Issues

# Labels
gh issue edit 42 --add-label "priority:high,bug"
gh issue edit 42 --remove-label "needs-triage"

# Assignment
gh issue edit 42 --add-assignee username
gh issue edit 42 --add-assignee @me

# Commenting
gh issue comment 42 --body "Investigated — root cause is in auth middleware."

# Closing and reopening
gh issue close 42
gh issue close 42 --reason "not planned"
gh issue reopen 42

curl equivalents: POST to /issues/N/labels, /issues/N/assignees, /issues/N/comments; PATCH /issues/N with state: closed / open.

2.4 Linking Issues to PRs

Issues close automatically when a PR merges with the right keywords in the body:

Closes #42
Fixes #42
Resolves #42

To create a branch from an issue:

gh issue develop 42 --checkout
# Manual equivalent
git checkout main && git pull origin main
git checkout -b fix/issue-42-login-redirect

2.5 Triage Workflow

When asked to triage issues:

  1. List untriaged: gh issue list --label "needs-triage" --state open
  2. Read and categorize each issue (view details, understand the bug/feature)
  3. Apply labels and priority (Section 2.3)
  4. Assign if the owner is clear
  5. Comment with triage notes if needed

2.6 Bulk Operations

# Close all issues with a specific label
gh issue list --label "wontfix" --json number --jq '.[].number' | \
  xargs -I {} gh issue close {} --reason "not planned"

2.7 Issues Quick Reference

Action gh curl endpoint
List issues gh issue list GET /repos/{o}/{r}/issues
View issue gh issue view N GET /repos/{o}/{r}/issues/N
Create issue gh issue create ... POST /repos/{o}/{r}/issues
Add labels gh issue edit N --add-label ... POST /repos/{o}/{r}/issues/N/labels
Assign gh issue edit N --add-assignee ... POST /repos/{o}/{r}/issues/N/assignees
Comment gh issue comment N --body ... POST /repos/{o}/{r}/issues/N/comments
Close gh issue close N PATCH /repos/{o}/{r}/issues/N
Search gh issue list --search "..." GET /search/issues?q=...

3. Code Review

Perform code reviews on local changes before pushing, or review open PRs on GitHub. Most of this section uses plain git — the gh/curl split only matters for PR-level interactions.

3.1 Reviewing Local Changes (Pre-Push)

Pure git — works everywhere, no API needed.

# Staged changes (what would be committed)
git diff --staged

# All changes vs main (what a PR would contain)
git diff main...HEAD

# File names only
git diff main...HEAD --name-only

# Stat summary
git diff main...HEAD --stat

Review strategy:

  1. Big picture first:

    git diff main...HEAD --stat
    git log main..HEAD --oneline
  2. Review file by file — use read_file on changed files for full context, plus the diff:

    git diff main...HEAD -- src/auth/login.py
  3. Check for common issues:

    # Debug statements, TODOs, console.logs left behind
    git diff main...HEAD | grep -n "print(\|console\.log\|TODO\|FIXME\|HACK\|XXX\|debugger"
    
    # Large files accidentally staged
    git diff main...HEAD --stat | sort -t'|' -k2 -rn | head -10
    
    # Secrets or credential patterns
    git diff main...HEAD | grep -in "password\|secret\|api_key\|token.*=\|private_key"
    
    # Merge conflict markers
    git diff main...HEAD | grep -n "<<<<<<\|>>>>>>\|======="
  4. Present structured feedback to the user using the format in references/review-output-template.md.

3.2 Reviewing a Pull Request on GitHub

# gh
gh pr view 123
gh pr diff 123
gh pr diff 123 --name-only

# curl
curl -s -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER
curl -s -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER/files

Check out the PR locally for full review (works with plain git):

git fetch origin pull/123/head:pr-123
git checkout pr-123
# Now read_file, search_files, run tests, etc. are all available
git diff main...pr-123

# gh shortcut
gh pr checkout 123

3.3 Leaving Comments

General PR comment:

# gh
gh pr comment 123 --body "Overall looks good, a few suggestions below."

# curl — note: PRs and issues share the comments endpoint
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/issues/$PR_NUMBER/comments \
  -d '{"body": "Overall looks good, a few suggestions below."}'

Single inline comment:

# gh
HEAD_SHA=$(gh pr view 123 --json headRefOid --jq '.headRefOid')
gh api repos/$OWNER/$REPO/pulls/123/comments --method POST \
  -f body="This could be simplified with a list comprehension." \
  -f path="src/auth/login.py" \
  -f commit_id="$HEAD_SHA" \
  -f line=45 \
  -f side="RIGHT"

# curl
HEAD_SHA=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['head']['sha'])")
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER/comments \
  -d "{\"body\": \"This could be simplified with a list comprehension.\", \"path\": \"src/auth/login.py\", \"commit_id\": \"$HEAD_SHA\", \"line\": 45, \"side\": \"RIGHT\"}"

3.4 Submitting a Formal Review

# gh
gh pr review 123 --approve --body "LGTM!"
gh pr review 123 --request-changes --body "See inline comments."
gh pr review 123 --comment --body "Some suggestions, nothing blocking."

# curl — multi-comment review submitted atomically
HEAD_SHA=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['head']['sha'])")
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews \
  -d "{
    \"commit_id\": \"$HEAD_SHA\",
    \"event\": \"COMMENT\",
    \"body\": \"Code review from Hermes Agent\",
    \"comments\": [
      {\"path\": \"src/auth.py\", \"line\": 45, \"body\": \"Use parameterized queries to prevent SQL injection.\"},
      {\"path\": \"src/models/user.py\", \"line\": 23, \"body\": \"Hash passwords with bcrypt before storing.\"},
      {\"path\": \"tests/test_auth.py\", \"line\": 1, \"body\": \"Add test for expired token edge case.\"}
    ]
  }"

Event values: "APPROVE", "REQUEST_CHANGES", "COMMENT". The line field is the line number in the new version. For deleted lines, use "side": "LEFT".

3.5 Review Checklist

When performing a code review (local or PR), systematically check:

Correctness

  • Does the code do what it claims?
  • Edge cases handled (empty inputs, nulls, large data, concurrent access)?
  • Error paths handled gracefully?

Security

  • No hardcoded secrets, credentials, or API keys
  • Input validation on user-facing inputs
  • No SQL injection, XSS, or path traversal
  • Auth/authz checks where needed

Code Quality

  • Clear naming (variables, functions, classes)
  • No unnecessary complexity or premature abstraction
  • DRY — no duplicated logic that should be extracted
  • Functions are focused (single responsibility)

Testing

  • New code paths tested?
  • Happy path and error cases covered?
  • Tests readable and maintainable?

Performance

  • No N+1 queries or unnecessary loops
  • Appropriate caching where beneficial
  • No blocking operations in async code paths

Documentation

  • Public APIs documented
  • Non-obvious logic has comments explaining "why"
  • README updated if behavior changed

3.6 PR Review Workflow (End-to-End)

When asked to "review PR #N", "look at this PR", or given a PR URL:

  1. Set up environment — source scripts/gh-env.sh or run the inline setup block
  2. Gather PR context — gh pr view 123, gh pr diff 123 --name-only, gh pr checks 123
  3. Check out the PR locally — git fetch origin pull/$PR_NUMBER/head:pr-$PR_NUMBER && git checkout pr-$PR_NUMBER
  4. Read the diff — git diff main...HEAD (or per-file for large PRs); use read_file for surrounding context
  5. Run automated checks — pytest, ruff check ., language-specific linter
  6. Apply the review checklist (Section 3.5)
  7. Post the review to GitHub — gh pr review $PR_NUMBER --approve/--request-changes, or atomic curl POST to /reviews
  8. Post a summary comment — use the format from references/review-output-template.md
  9. Clean up — git checkout main && git branch -D pr-$PR_NUMBER

Decision: Approve vs Request Changes vs Comment

  • Approve — no critical or warning-level issues, only minor suggestions
  • Request Changes — any critical or warning-level issue that should be fixed before merge
  • Comment — observations and suggestions, but nothing blocking (draft PRs, uncertain findings)

4. Pull Request Workflow

Complete guide for managing the PR lifecycle — branch, commit, push, open PR, monitor CI, auto-fix, merge.

4.1 Branch Creation

This part is pure git — identical either way:

git fetch origin
git checkout main && git pull origin main
git checkout -b feat/add-user-authentication

Branch naming conventions:

  • feat/description — new features
  • fix/description — bug fixes
  • refactor/description — code restructuring
  • docs/description — documentation
  • ci/description — CI/CD changes

4.2 Making Commits

Use the agent's file tools (write_file, patch) to make changes, then commit:

git add src/auth.py src/models/user.py tests/test_auth.py
git commit -m "feat: add JWT-based user authentication

- Add login/register endpoints
- Add User model with password hashing
- Add auth middleware for protected routes
- Add unit tests for auth flow"

Conventional Commits format: type(scope): short description, body wrapped at 72 chars. Types: feat, fix, refactor, docs, test, ci, chore, perf, style, build, revert. See references/conventional-commits.md for the full reference and breaking-change conventions.

4.3 Pushing and Creating a PR

Push the branch:

git push -u origin HEAD

Create the PR:

# gh
gh pr create \
  --title "feat: add JWT-based user authentication" \
  --body "## Summary
- Adds login and register API endpoints
- JWT token generation and validation

## Test Plan
- [ ] Unit tests pass

Closes #42"

# curl
BRANCH=$(git branch --show-current)
curl -s -X POST \
  -H "Authorization: token $GITHUB_TOKEN" \
  -H "Accept: application/vnd.github.v3+json" \
  https://api.github.com/repos/$OWNER/$REPO/pulls \
  -d "{
    \"title\": \"feat: add JWT-based user authentication\",
    \"body\": \"## Summary\nAdds login and register API endpoints.\n\nCloses #42\",
    \"head\": \"$BRANCH\",
    \"base\": \"main\"
  }"

Options: --draft, --reviewer user1,user2, --label "enhancement", --base develop. For draft via curl, add "draft": true to the JSON. The response JSON includes the PR number — save it for later commands. Use templates/pr-body-bugfix.md and templates/pr-body-feature.md for full PR body templates.

4.4 Monitoring CI Status

# gh
gh pr checks                          # one-shot
gh pr checks --watch                  # poll every 10s

# curl — combined status
SHA=$(git rev-parse HEAD)
curl -s -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/commits/$SHA/status | python3 -c "
import sys, json
data = json.load(sys.stdin)
print(f\"Overall: {data['state']}\")
for s in data.get('statuses', []):
    print(f\"  {s['context']}: {s['state']} - {s.get('description', '')}\")"

# curl — Actions check runs
curl -s -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/commits/$SHA/check-runs | python3 -c "
import sys, json
for cr in json.load(sys.stdin).get('check_runs', []):
    print(f\"  {cr['name']}: {cr['status']} / {cr['conclusion'] or 'pending'}\")"

Poll until complete (curl):

SHA=$(git rev-parse HEAD)
for i in $(seq 1 20); do
  STATUS=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
    https://api.github.com/repos/$OWNER/$REPO/commits/$SHA/status \
    | python3 -c "import sys,json; print(json.load(sys.stdin)['state'])")
  echo "Check $i: $STATUS"
  if [ "$STATUS" = "success" ] || [ "$STATUS" = "failure" ] || [ "$STATUS" = "error" ]; then
    break
  fi
  sleep 30
done

4.5 Auto-Fixing CI Failures

When CI fails, diagnose and fix. This loop works with either auth method.

Step 1: Get failure details

# gh
gh run list --branch $(git branch --show-current) --limit 5
gh run view <RUN_ID> --log-failed

# curl
BRANCH=$(git branch --show-current)
curl -s -H "Authorization: token $GITHUB_TOKEN" \
  "https://api.github.com/repos/$OWNER/$REPO/actions/runs?branch=$BRANCH&per_page=5"
RUN_ID=<run_id>
curl -sL -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/actions/runs/$RUN_ID/logs \
  -o /tmp/ci-logs.zip
cd /tmp && unzip -o ci-logs.zip -d ci-logs && cat ci-logs/*.txt

Step 2: Fix and push

# Use read_file + patch/write_file to fix
git add <fixed_files>
git commit -m "fix: resolve CI failure in <check_name>"
git push

Step 3: Verify — re-check CI status using Section 4.4.

Auto-fix loop pattern: check CI → identify failures → read logs → fix code → push → re-check. Repeat up to 3 times, then ask the user. See references/ci-troubleshooting.md for the full failure-pattern reference (test/lint/type/build/permission/timeout/docker signatures and fixes).

4.6 Merging

# gh
gh pr merge --squash --delete-branch        # squash merge + delete branch
gh pr merge --auto --squash --delete-branch # auto-merge when checks pass

# curl
PR_NUMBER=<number>
curl -s -X PUT -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER/merge \
  -d "{\"merge_method\": \"squash\", \"commit_title\": \"feat: add user authentication (#$PR_NUMBER)\"}"

BRANCH=$(git branch --show-current)
git push origin --delete $BRANCH
git checkout main && git pull origin main
git branch -d $BRANCH

Merge methods: "merge" (merge commit), "squash", "rebase".

Enable auto-merge via GraphQL (REST doesn't support it):

PR_NODE_ID=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['node_id'])")
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/graphql \
  -d "{\"query\": \"mutation { enablePullRequestAutoMerge(input: {pullRequestId: \\\"$PR_NODE_ID\\\", mergeMethod: SQUASH}) { clientMutationId } }\"}"

4.7 Complete Workflow Example

# 1. Start from clean main
git checkout main && git pull origin main

# 2. Branch
git checkout -b fix/login-redirect-bug

# 3. (Agent makes code changes with file tools)

# 4. Commit
git add src/auth/login.py tests/test_login.py
git commit -m "fix: correct redirect URL after login

Preserves the ?next= parameter instead of always redirecting to /dashboard."

# 5. Push
git push -u origin HEAD

# 6. Create PR (Section 4.3)
# 7. Monitor CI (Section 4.4)
# 8. Auto-fix if needed (Section 4.5)
# 9. Merge when green (Section 4.6)

4.8 PR Quick Reference

Action gh git + curl
List my PRs gh pr list --author @me curl "https://api.github.com/repos/$OWNER/$REPO/pulls?state=open"
View PR diff gh pr diff git diff main...HEAD (local)
Add comment gh pr comment N --body "..." POST /issues/N/comments
Request review gh pr edit N --add-reviewer user POST /pulls/N/requested_reviewers
Close PR gh pr close N PATCH /pulls/N {"state":"closed"}
Check out someone's PR gh pr checkout N git fetch origin pull/N/head:pr-N && git checkout pr-N

Support Files

  • scripts/gh-env.sh — Sourceable bash helper that sets GH_AUTH_METHOD, GITHUB_TOKEN, GH_USER, GH_OWNER, GH_REPO, GH_OWNER_REPO from your environment
  • references/ci-troubleshooting.md — CI failure pattern signatures and fixes (test/lint/type/build/perm/timeout/docker)
  • references/conventional-commits.md — Conventional Commits format reference with breaking-change and issue-link conventions
  • references/review-output-template.md — Standard PR review summary format (Critical/Warnings/Suggestions/Looks Good) with severity guide
  • references/github-api-cheatsheet.md — REST API endpoint reference for repos, PRs, issues, reviews, comments, secrets, releases
  • templates/bug-report.md — Issue body template for bug reports
  • templates/feature-request.md — Issue body template for feature requests
  • templates/pr-body-bugfix.md — PR body template for bug fixes
  • templates/pr-body-feature.md — PR body template for new features