AgentSkillsCN

Github Api

GitHub API

SKILL.md

GitHub API Skill

Part of Cardo — "Spawn many. Swarm the objective."

Use the GitHub API via curl for all GitHub operations. This skill provides standardized access to issues, PRs, reviews, and CI status.

Prerequisites

bash
# Token must be set in environment
export GITHUB_API_TOKEN=ghp_...

Configuration

bash
REPO_OWNER="{{GITHUB_OWNER}}"
REPO_NAME="{{GITHUB_REPO}}"
BASE_URL="https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}"

Helper Function

Use this wrapper for all API calls:

bash
gh_api() {
  local method="${1:-GET}"
  local endpoint="$2"
  local data="$3"

  if [ -n "$data" ]; then
    curl -s -X "$method" \
      -H "Authorization: token $GITHUB_TOKEN" \
      -H "Content-Type: application/json" \
      -d "$data" \
      "${BASE_URL}${endpoint}"
  else
    curl -s -X "$method" \
      -H "Authorization: token $GITHUB_TOKEN" \
      "${BASE_URL}${endpoint}"
  fi
}

Issues

Read Issue

bash
gh_api GET "/issues/{number}"

# Example
gh_api GET "/issues/13"

Get Issue Comments

bash
gh_api GET "/issues/{number}/comments"

Post Issue Comment

bash
gh_api POST "/issues/{number}/comments" '{"body":"Comment text"}'

# With multiline/markdown
gh_api POST "/issues/13/comments" '{
  "body": "## Status Update\n\n- Phase: Implementation\n- Progress: 50%"
}'

Update Issue

bash
# Update labels
gh_api PATCH "/issues/{number}" '{"labels":["in-progress","cardo"]}'

# Update state
gh_api PATCH "/issues/{number}" '{"state":"closed"}'

# Update assignees
gh_api PATCH "/issues/{number}" '{"assignees":["username"]}'

# Multiple updates
gh_api PATCH "/issues/13" '{
  "labels": ["in-progress", "cardo"],
  "assignees": ["username"]
}'

Create Issue

bash
gh_api POST "/issues" '{
  "title": "Child task: Implement auth",
  "body": "Parent: #13\n\n## Description\n...",
  "labels": ["agent-ready", "cardo"]
}'

Close Issue

bash
gh_api PATCH "/issues/{number}" '{"state":"closed"}'

Add Labels

bash
gh_api POST "/issues/{number}/labels" '{"labels":["blocked","needs-review"]}'

Remove Label

bash
curl -s -X DELETE \
  -H "Authorization: token $GITHUB_TOKEN" \
  "${BASE_URL}/issues/{number}/labels/{label_name}"

Pull Requests

Create PR

bash
gh_api POST "/pulls" '{
  "title": "feat: implement subscribe service",
  "body": "Closes #13\n\n## Changes\n- Added subscription API\n- Unit tests\n\n## Screenshots\n...",
  "head": "feature/13-subscribe-service",
  "base": "main"
}'

Get PR

bash
gh_api GET "/pulls/{number}"

List PR Files

bash
gh_api GET "/pulls/{number}/files"

Update PR

bash
gh_api PATCH "/pulls/{number}" '{
  "title": "Updated title",
  "body": "Updated description"
}'

Merge PR

bash
# Squash merge (recommended)
gh_api PUT "/pulls/{number}/merge" '{"merge_method":"squash"}'

# Regular merge
gh_api PUT "/pulls/{number}/merge" '{"merge_method":"merge"}'

# Rebase merge
gh_api PUT "/pulls/{number}/merge" '{"merge_method":"rebase"}'

# With custom commit message
gh_api PUT "/pulls/15/merge" '{
  "merge_method": "squash",
  "commit_title": "feat: implement subscribe service (#15)",
  "commit_message": "Closes #13"
}'

Close PR (without merging)

bash
gh_api PATCH "/pulls/{number}" '{"state":"closed"}'

PR Reviews

Get Reviews

bash
gh_api GET "/pulls/{number}/reviews"

Get Review Comments (line-level)

bash
gh_api GET "/pulls/{number}/comments"

Reply to Review Comment

bash
gh_api POST "/pulls/{number}/comments/{comment_id}/replies" '{"body":"Reply text"}'

Post General PR Comment

bash
# Uses issues endpoint (PRs are issues)
gh_api POST "/issues/{number}/comments" '{"body":"General comment"}'

Request Reviewers

bash
gh_api POST "/pulls/{number}/requested_reviewers" '{"reviewers":["username"]}'

Remove Reviewer Request

bash
curl -s -X DELETE \
  -H "Authorization: token $GITHUB_TOKEN" \
  -d '{"reviewers":["username"]}' \
  "${BASE_URL}/pulls/{number}/requested_reviewers"

CI / Checks

Get Check Runs for Commit

bash
gh_api GET "/commits/{sha}/check-runs"

# Parse status
gh_api GET "/commits/abc123/check-runs" | jq '.check_runs[] | {name, status, conclusion}'

Get Combined Status

bash
gh_api GET "/commits/{sha}/status"

Get Workflow Runs

bash
gh_api GET "/actions/runs"

# Filter by branch
gh_api GET "/actions/runs?branch=feature/13-subscribe-service"

# Latest run
gh_api GET "/actions/runs?per_page=1"

Get Workflow Run Details

bash
gh_api GET "/actions/runs/{run_id}"

Get Workflow Run Jobs

bash
gh_api GET "/actions/runs/{run_id}/jobs"

Re-run Failed Workflow

bash
gh_api POST "/actions/runs/{run_id}/rerun"

Branches

List Branches

bash
gh_api GET "/branches"

Get Branch

bash
gh_api GET "/branches/{branch}"

# Get latest commit SHA
gh_api GET "/branches/main" | jq -r '.commit.sha'

Delete Branch

bash
curl -s -X DELETE \
  -H "Authorization: token $GITHUB_TOKEN" \
  "${BASE_URL}/git/refs/heads/{branch}"

Commits

List Commits

bash
gh_api GET "/commits"

# On specific branch
gh_api GET "/commits?sha=feature/13-subscribe-service"

# With pagination
gh_api GET "/commits?per_page=10&page=1"

Get Commit

bash
gh_api GET "/commits/{sha}"

Compare Commits

bash
gh_api GET "/compare/{base}...{head}"

# Example: compare main to feature branch
gh_api GET "/compare/main...feature/13-subscribe-service"

Repository

Get Repo Info

bash
gh_api GET ""

Get README

bash
gh_api GET "/readme"

Get File Contents

bash
gh_api GET "/contents/{path}"

# Example
gh_api GET "/contents/package.json"

# Decode content (base64)
gh_api GET "/contents/package.json" | jq -r '.content' | base64 -d

Error Handling

Check for Errors

bash
response=$(gh_api GET "/issues/999")
if echo "$response" | jq -e '.message' > /dev/null 2>&1; then
  echo "Error: $(echo "$response" | jq -r '.message')"
  exit 1
fi

Common Error Responses

StatusMeaningAction
401Bad tokenCheck GITHUB_TOKEN
403Rate limited or forbiddenWait or check permissions
404Not foundCheck resource exists
422Validation failedCheck request body

Rate Limit Check

bash
curl -s -H "Authorization: token $GITHUB_TOKEN" \
  https://api.github.com/rate_limit | jq '.rate'

Pagination

GitHub API returns max 100 items per page. For lists:

bash
# First page
gh_api GET "/issues?per_page=100&page=1"

# Check for more pages in response headers
curl -s -I -H "Authorization: token $GITHUB_TOKEN" \
  "${BASE_URL}/issues?per_page=100" | grep -i "link:"

Cardo-Specific Patterns

Post Orchestration Log Update

bash
gh_api POST "/issues/13/comments" '{
  "body": "## Cardo Update\n\n| Phase | Status |\n|-------|--------|\n| Intake | Done |\n| Discovery | In Progress |\n\n**Current:** Clarifying requirements with Product Owner"
}'

Mark Issue as In-Progress

bash
gh_api PATCH "/issues/13" '{
  "labels": ["in-progress", "cardo"],
  "assignees": ["username"]
}'

Mark Issue as Blocked

bash
gh_api PATCH "/issues/13" '{
  "labels": ["blocked", "cardo"]
}'
gh_api POST "/issues/13/comments" '{
  "body": "**Blocked**\n\nPhase: Implementation\nAttempts: 3/3\nError: Test failure in auth module\n\n_Escalating to team lead._"
}'

Create PR with Full Context

bash
gh_api POST "/pulls" '{
  "title": "feat(subscribe): implement subscribe service",
  "body": "## Summary\nImplements subscription service.\n\nCloses #13\n\n## Changes\n- Added SubscribeService class\n- Unit tests with 95% coverage\n- E2E tests\n\n## Checklist\n- [x] Tests passing\n- [x] E2E passing\n- [x] Internal review complete",
  "head": "feature/13-subscribe-service",
  "base": "main"
}'

Check if PR Ready to Merge

bash
pr_number=15
sha=$(gh_api GET "/pulls/$pr_number" | jq -r '.head.sha')

# Check CI status
ci_status=$(gh_api GET "/commits/$sha/status" | jq -r '.state')

# Check reviews
reviews=$(gh_api GET "/pulls/$pr_number/reviews" | jq '[.[] | select(.state == "APPROVED")] | length')

if [ "$ci_status" = "success" ] && [ "$reviews" -gt 0 ]; then
  echo "PR ready to merge"
else
  echo "CI: $ci_status, Approvals: $reviews"
fi

Branch Protection

Get Current Protection

bash
# Via gh CLI (preferred)
gh api repos/{owner}/{repo}/branches/main/protection

# Parse specific settings
gh api repos/{owner}/{repo}/branches/main/protection \
  --jq '{
    required_status_checks: .required_status_checks,
    required_reviews: .required_pull_request_reviews,
    enforce_admins: .enforce_admins.enabled
  }'

Set Branch Protection

bash
# Minimal protection for small team (1-2 devs)
gh api repos/{owner}/{repo}/branches/main/protection \
  -X PUT \
  -H "Accept: application/vnd.github+json" \
  -f required_status_checks='{"strict":true,"contexts":["lint","test"]}' \
  -F enforce_admins=true \
  -f required_pull_request_reviews='{"required_approving_review_count":0,"dismiss_stale_reviews":true}' \
  -f restrictions=null \
  -F required_linear_history=true \
  -F allow_force_pushes=false \
  -F allow_deletions=false

Full Protection JSON (for complex setups)

bash
gh api repos/{owner}/{repo}/branches/main/protection \
  -X PUT \
  --input - <<'EOF'
{
  "required_status_checks": {
    "strict": true,
    "contexts": ["lint", "test", "typecheck"]
  },
  "enforce_admins": true,
  "required_pull_request_reviews": {
    "required_approving_review_count": 1,
    "dismiss_stale_reviews": true,
    "require_code_owner_reviews": false,
    "require_last_push_approval": false
  },
  "restrictions": null,
  "required_linear_history": true,
  "allow_force_pushes": false,
  "allow_deletions": false,
  "required_signatures": false
}
EOF

Protection Settings Reference

SettingTypeDescription
required_status_checks.strictbooleanRequire branch to be up-to-date with base before merging
required_status_checks.contextsstring[]CI job names that must pass (match jobs.<id>.name in workflow)
enforce_adminsbooleanApply rules to admins too
required_pull_request_reviews.required_approving_review_count0-6Number of approvals needed (0 = no review required, PR still required)
required_pull_request_reviews.dismiss_stale_reviewsbooleanDismiss approvals when new commits pushed
required_linear_historybooleanRequire squash or rebase (no merge commits)
allow_force_pushesbooleanAllow force push to protected branch
allow_deletionsbooleanAllow branch deletion
required_signaturesbooleanRequire signed commits
restrictionsobject|nullLimit who can push (null = no restriction beyond PR rules)

Remove Branch Protection

bash
# Remove all protection (requires admin)
gh api repos/{owner}/{repo}/branches/main/protection -X DELETE

Check if Branch is Protected

bash
gh api repos/{owner}/{repo}/branches/main --jq '.protected'

Note: Repository Rulesets (Modern Alternative)

GitHub also offers Repository Rulesets — a newer, more flexible system that can replace or complement branch protection rules. Key differences:

FactorBranch ProtectionRulesets
ScopeOne rule per branchMultiple branches/tags via patterns
StackingOne rule per branchMultiple rulesets aggregate (most restrictive wins)
ToggleMust delete and recreateToggle enforcement on/off
Extra rulesNoCommit message patterns, file restrictions, code scanning

For basic needs (PR gate + CI checks), classic branch protection is simpler. Consider rulesets for future expansion.

bash
# List rulesets
gh api repos/{owner}/{repo}/rulesets --jq '.[] | {id, name, enforcement}'

# Get effective rules on a branch
gh api repos/{owner}/{repo}/rules/branches/main

GitHub Secrets

List Secrets

bash
# Repository secrets
gh secret list

# Environment secrets
gh secret list --env production

Set a Secret

bash
# From value
gh secret set MY_TOKEN --body "your-token-value"

# From file
gh secret set SERVICE_ACCOUNT_KEY < service-account.json

# From stdin (pipe)
echo "token-value" | gh secret set MY_TOKEN

# Environment-scoped
gh secret set DEPLOY_KEY --env production --body "value"

# Batch from .env file (loads all KEY=value pairs)
gh secret set -f .env

# Dependabot secrets (separate from Actions secrets)
gh secret set REGISTRY_TOKEN --app dependabot --body "value"

Delete a Secret

bash
gh secret delete MY_TOKEN
gh secret delete DEPLOY_KEY --env production

Variables (Non-Secret Config)

bash
# Repository variables (visible in logs, not encrypted)
gh variable set NODE_ENV --body "production"
gh variable list

# Environment-scoped variables
gh variable set API_URL --env production --body "https://api.example.com"
gh variable list --env production
gh variable delete API_URL --env production

Secrets via API (for automation)

bash
# Step 1: Get the repository public key (needed for encryption)
gh api repos/{owner}/{repo}/actions/secrets/public-key \
  --jq '{key_id, key}'

# Step 2: Encrypt the secret value with the public key
# (Requires libsodium — complex. Prefer `gh secret set` instead.)

# List secrets via API
gh api repos/{owner}/{repo}/actions/secrets --jq '.secrets[].name'

GitHub Environments

Create Environment

bash
gh api repos/{owner}/{repo}/environments/production \
  -X PUT \
  --input - <<'EOF'
{
  "wait_timer": 0,
  "reviewers": [],
  "deployment_branch_policy": {
    "protected_branches": true,
    "custom_branch_policies": false
  }
}
EOF

Environment with Required Reviewers

bash
gh api repos/{owner}/{repo}/environments/production \
  -X PUT \
  --input - <<'EOF'
{
  "reviewers": [
    {
      "type": "User",
      "id": USER_ID
    }
  ],
  "deployment_branch_policy": {
    "protected_branches": true,
    "custom_branch_policies": false
  }
}
EOF

List Environments

bash
gh api repos/{owner}/{repo}/environments --jq '.environments[].name'

Use Environment in Workflow

yaml
jobs:
  deploy:
    environment: production    # Triggers environment protection rules
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploying with ${{ secrets.DEPLOY_KEY }}"

GitHub Actions API

List Workflow Runs

bash
# All runs
gh run list --limit 10

# Filter by workflow
gh run list --workflow pr-gate.yml --limit 5

# Filter by branch
gh run list --branch feature/155-branch-protection --limit 5

# Filter by status
gh run list --status failure --limit 5

View Run Details

bash
# Summary
gh run view {run-id}

# Full logs
gh run view {run-id} --log

# Failed jobs only
gh run view {run-id} --log-failed

Re-Run

bash
# Re-run all jobs
gh run rerun {run-id}

# Re-run only failed jobs
gh run rerun {run-id} --failed

Cancel a Run

bash
gh run cancel {run-id}

Manage Workflows

bash
# List workflows
gh workflow list

# View workflow details
gh workflow view pr-gate.yml

# Disable/enable a workflow
gh workflow disable pr-gate.yml
gh workflow enable pr-gate.yml

# Trigger a workflow_dispatch
gh workflow run e2e.yml -f run_unit_tests=true -f run_e2e=true

Milestones

List Milestones

bash
gh api repos/{owner}/{repo}/milestones --jq '.[] | {number, title, state, open_issues, closed_issues}'

Create Milestone

bash
gh api repos/{owner}/{repo}/milestones \
  -X POST \
  -f title="Sprint 2 (Feb 13 -- Feb 15)" \
  -f description="Sprint 2 goal" \
  -f due_on="2026-02-15T23:59:59Z" \
  -f state="open"

Close Milestone

bash
gh api repos/{owner}/{repo}/milestones/{number} \
  -X PATCH \
  -f state="closed"

Security Notes

  • Never hardcode GITHUB_TOKEN in files
  • Use fine-grained PAT with minimal scopes
  • Token visible in cloud session logs — rotate periodically
  • For production, consider GitHub App instead of PAT
  • Use environment secrets for production-only credentials
  • Never echo secrets in CI logs — they're masked but don't test limits