Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
---
|
||||
title: "Codebase Inspection — Inspect codebases w/ pygount: LOC, languages, ratios"
|
||||
sidebar_label: "Codebase Inspection"
|
||||
description: "Inspect codebases w/ pygount: LOC, languages, ratios"
|
||||
---
|
||||
|
||||
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
|
||||
|
||||
# Codebase Inspection
|
||||
|
||||
Inspect codebases w/ pygount: LOC, languages, ratios.
|
||||
|
||||
## Skill metadata
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Source | Bundled (installed by default) |
|
||||
| Path | `skills/github/codebase-inspection` |
|
||||
| Version | `1.0.0` |
|
||||
| Author | Hermes Agent |
|
||||
| License | MIT |
|
||||
| Platforms | linux, macos, windows |
|
||||
| Tags | `LOC`, `Code Analysis`, `pygount`, `Codebase`, `Metrics`, `Repository` |
|
||||
| Related skills | [`github-repo-management`](/docs/user-guide/skills/bundled/github/github-github-repo-management) |
|
||||
|
||||
## Reference: full SKILL.md
|
||||
|
||||
:::info
|
||||
The following is the complete skill definition that Hermes loads when this skill is triggered. This is what the agent sees as instructions when the skill is active.
|
||||
:::
|
||||
|
||||
# Codebase Inspection with pygount
|
||||
|
||||
Analyze repositories for lines of code, language breakdown, file counts, and code-vs-comment ratios using `pygount`.
|
||||
|
||||
## When to Use
|
||||
|
||||
- User asks for LOC (lines of code) count
|
||||
- User wants a language breakdown of a repo
|
||||
- User asks about codebase size or composition
|
||||
- User wants code-vs-comment ratios
|
||||
- General "how big is this repo" questions
|
||||
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
pip install --break-system-packages pygount 2>/dev/null || pip install pygount
|
||||
```
|
||||
|
||||
## 1. Basic Summary (Most Common)
|
||||
|
||||
Get a full language breakdown with file counts, code lines, and comment lines:
|
||||
|
||||
```bash
|
||||
cd /path/to/repo
|
||||
pygount --format=summary \
|
||||
--folders-to-skip=".git,node_modules,venv,.venv,__pycache__,.cache,dist,build,.next,.tox,.eggs,*.egg-info" \
|
||||
.
|
||||
```
|
||||
|
||||
**IMPORTANT:** Always use `--folders-to-skip` to exclude dependency/build directories, otherwise pygount will crawl them and take a very long time or hang.
|
||||
|
||||
## 2. Common Folder Exclusions
|
||||
|
||||
Adjust based on the project type:
|
||||
|
||||
```bash
|
||||
# Python projects
|
||||
--folders-to-skip=".git,venv,.venv,__pycache__,.cache,dist,build,.tox,.eggs,.mypy_cache"
|
||||
|
||||
# JavaScript/TypeScript projects
|
||||
--folders-to-skip=".git,node_modules,dist,build,.next,.cache,.turbo,coverage"
|
||||
|
||||
# General catch-all
|
||||
--folders-to-skip=".git,node_modules,venv,.venv,__pycache__,.cache,dist,build,.next,.tox,vendor,third_party"
|
||||
```
|
||||
|
||||
## 3. Filter by Specific Language
|
||||
|
||||
```bash
|
||||
# Only count Python files
|
||||
pygount --suffix=py --format=summary .
|
||||
|
||||
# Only count Python and YAML
|
||||
pygount --suffix=py,yaml,yml --format=summary .
|
||||
```
|
||||
|
||||
## 4. Detailed File-by-File Output
|
||||
|
||||
```bash
|
||||
# Default format shows per-file breakdown
|
||||
pygount --folders-to-skip=".git,node_modules,venv" .
|
||||
|
||||
# Sort by code lines (pipe through sort)
|
||||
pygount --folders-to-skip=".git,node_modules,venv" . | sort -t$'\t' -k1 -nr | head -20
|
||||
```
|
||||
|
||||
## 5. Output Formats
|
||||
|
||||
```bash
|
||||
# Summary table (default recommendation)
|
||||
pygount --format=summary .
|
||||
|
||||
# JSON output for programmatic use
|
||||
pygount --format=json .
|
||||
|
||||
# Pipe-friendly: Language, file count, code, docs, empty, string
|
||||
pygount --format=summary . 2>/dev/null
|
||||
```
|
||||
|
||||
## 6. Interpreting Results
|
||||
|
||||
The summary table columns:
|
||||
- **Language** — detected programming language
|
||||
- **Files** — number of files of that language
|
||||
- **Code** — lines of actual code (executable/declarative)
|
||||
- **Comment** — lines that are comments or documentation
|
||||
- **%** — percentage of total
|
||||
|
||||
Special pseudo-languages:
|
||||
- `__empty__` — empty files
|
||||
- `__binary__` — binary files (images, compiled, etc.)
|
||||
- `__generated__` — auto-generated files (detected heuristically)
|
||||
- `__duplicate__` — files with identical content
|
||||
- `__unknown__` — unrecognized file types
|
||||
|
||||
## Pitfalls
|
||||
|
||||
1. **Always exclude .git, node_modules, venv** — without `--folders-to-skip`, pygount will crawl everything and may take minutes or hang on large dependency trees.
|
||||
2. **Markdown shows 0 code lines** — pygount classifies all Markdown content as comments, not code. This is expected behavior.
|
||||
3. **JSON files show low code counts** — pygount may count JSON lines conservatively. For accurate JSON line counts, use `wc -l` directly.
|
||||
4. **Large monorepos** — for very large repos, consider using `--suffix` to target specific languages rather than scanning everything.
|
||||
@@ -0,0 +1,265 @@
|
||||
---
|
||||
title: "Github Auth — GitHub auth setup: HTTPS tokens, SSH keys, gh CLI login"
|
||||
sidebar_label: "Github Auth"
|
||||
description: "GitHub auth setup: HTTPS tokens, SSH keys, gh CLI login"
|
||||
---
|
||||
|
||||
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
|
||||
|
||||
# Github Auth
|
||||
|
||||
GitHub auth setup: HTTPS tokens, SSH keys, gh CLI login.
|
||||
|
||||
## Skill metadata
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Source | Bundled (installed by default) |
|
||||
| Path | `skills/github/github-auth` |
|
||||
| Version | `1.1.0` |
|
||||
| Author | Hermes Agent |
|
||||
| License | MIT |
|
||||
| Platforms | linux, macos, windows |
|
||||
| Tags | `GitHub`, `Authentication`, `Git`, `gh-cli`, `SSH`, `Setup` |
|
||||
| Related skills | [`github-pr-workflow`](/docs/user-guide/skills/bundled/github/github-github-pr-workflow), [`github-code-review`](/docs/user-guide/skills/bundled/github/github-github-code-review), [`github-issues`](/docs/user-guide/skills/bundled/github/github-github-issues), [`github-repo-management`](/docs/user-guide/skills/bundled/github/github-github-repo-management) |
|
||||
|
||||
## Reference: full SKILL.md
|
||||
|
||||
:::info
|
||||
The following is the complete skill definition that Hermes loads when this skill is triggered. This is what the agent sees as instructions when the skill is active.
|
||||
:::
|
||||
|
||||
# GitHub Authentication Setup
|
||||
|
||||
This skill sets up authentication so the agent can work with GitHub repositories, PRs, issues, and CI. It covers two paths:
|
||||
|
||||
- **`git` (always available)** — uses HTTPS personal access tokens or SSH keys
|
||||
- **`gh` CLI (if installed)** — richer GitHub API access with a simpler auth flow
|
||||
|
||||
## Detection Flow
|
||||
|
||||
When a user asks you to work with GitHub, run this check first:
|
||||
|
||||
```bash
|
||||
# Check what's available
|
||||
git --version
|
||||
gh --version 2>/dev/null || echo "gh not installed"
|
||||
|
||||
# Check if already authenticated
|
||||
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 → you're good, use `gh` for everything
|
||||
2. If `gh` is installed but not authenticated → use "gh auth" method below
|
||||
3. If `gh` is not installed → use "git-only" method below (no sudo needed)
|
||||
|
||||
---
|
||||
|
||||
## Method 1: Git-Only Authentication (No gh, No sudo)
|
||||
|
||||
This works on any machine with `git` installed. No root access needed.
|
||||
|
||||
### Option A: HTTPS with Personal Access Token (Recommended)
|
||||
|
||||
This is the most portable method — works everywhere, no SSH config needed.
|
||||
|
||||
**Step 1: Create a personal access token**
|
||||
|
||||
Tell the user to go to: **https://github.com/settings/tokens**
|
||||
|
||||
- Click "Generate new token (classic)"
|
||||
- Give it a name like "hermes-agent"
|
||||
- Select scopes:
|
||||
- `repo` (full repository access — read, write, push, PRs)
|
||||
- `workflow` (trigger and manage GitHub Actions)
|
||||
- `read:org` (if working with organization repos)
|
||||
- Set expiration (90 days is a good default)
|
||||
- Copy the token — it won't be shown again
|
||||
|
||||
**Step 2: Configure git to store the token**
|
||||
|
||||
```bash
|
||||
# Set up the credential helper to cache credentials
|
||||
# "store" saves to ~/.git-credentials in plaintext (simple, persistent)
|
||||
git config --global credential.helper store
|
||||
|
||||
# Now do a test operation that triggers auth — git will prompt for credentials
|
||||
# Username: <their-github-username>
|
||||
# Password: <paste the personal access token, NOT their GitHub password>
|
||||
git ls-remote https://github.com/<their-username>/<any-repo>.git
|
||||
```
|
||||
|
||||
After entering credentials once, they're saved and reused for all future operations.
|
||||
|
||||
**Alternative: cache helper (credentials expire from memory)**
|
||||
|
||||
```bash
|
||||
# Cache in memory for 8 hours (28800 seconds) instead of saving to disk
|
||||
git config --global credential.helper 'cache --timeout=28800'
|
||||
```
|
||||
|
||||
**Alternative: set the token directly in the remote URL (per-repo)**
|
||||
|
||||
```bash
|
||||
# Embed token in the remote URL (avoids credential prompts entirely)
|
||||
git remote set-url origin https://<username>:<token>@github.com/<owner>/<repo>.git
|
||||
```
|
||||
|
||||
**Step 3: Configure git identity**
|
||||
|
||||
```bash
|
||||
# Required for commits — set name and email
|
||||
git config --global user.name "Their Name"
|
||||
git config --global user.email "their-email@example.com"
|
||||
```
|
||||
|
||||
**Step 4: Verify**
|
||||
|
||||
```bash
|
||||
# Test push access (this should work without any prompts now)
|
||||
git ls-remote https://github.com/<their-username>/<any-repo>.git
|
||||
|
||||
# Verify identity
|
||||
git config --global user.name
|
||||
git config --global user.email
|
||||
```
|
||||
|
||||
### Option B: SSH Key Authentication
|
||||
|
||||
Good for users who prefer SSH or already have keys set up.
|
||||
|
||||
**Step 1: Check for existing SSH keys**
|
||||
|
||||
```bash
|
||||
ls -la ~/.ssh/id_*.pub 2>/dev/null || echo "No SSH keys found"
|
||||
```
|
||||
|
||||
**Step 2: Generate a key if needed**
|
||||
|
||||
```bash
|
||||
# Generate an ed25519 key (modern, secure, fast)
|
||||
ssh-keygen -t ed25519 -C "their-email@example.com" -f ~/.ssh/id_ed25519 -N ""
|
||||
|
||||
# Display the public key for them to add to GitHub
|
||||
cat ~/.ssh/id_ed25519.pub
|
||||
```
|
||||
|
||||
Tell the user to add the public key at: **https://github.com/settings/keys**
|
||||
- Click "New SSH key"
|
||||
- Paste the public key content
|
||||
- Give it a title like "hermes-agent-<machine-name>"
|
||||
|
||||
**Step 3: Test the connection**
|
||||
|
||||
```bash
|
||||
ssh -T git@github.com
|
||||
# Expected: "Hi <username>! You've successfully authenticated..."
|
||||
```
|
||||
|
||||
**Step 4: Configure git to use SSH for GitHub**
|
||||
|
||||
```bash
|
||||
# Rewrite HTTPS GitHub URLs to SSH automatically
|
||||
git config --global url."git@github.com:".insteadOf "https://github.com/"
|
||||
```
|
||||
|
||||
**Step 5: Configure git identity**
|
||||
|
||||
```bash
|
||||
git config --global user.name "Their Name"
|
||||
git config --global user.email "their-email@example.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)
|
||||
|
||||
```bash
|
||||
gh auth login
|
||||
# Select: GitHub.com
|
||||
# Select: HTTPS
|
||||
# Authenticate via browser
|
||||
```
|
||||
|
||||
### Token-Based Login (Headless / SSH Servers)
|
||||
|
||||
```bash
|
||||
echo "<THEIR_TOKEN>" | gh auth login --with-token
|
||||
|
||||
# Set up git credentials through gh
|
||||
gh auth setup-git
|
||||
```
|
||||
|
||||
### Verify
|
||||
|
||||
```bash
|
||||
gh auth status
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Using the GitHub API Without gh
|
||||
|
||||
When `gh` is not available, you can still access the full GitHub API using `curl` with a personal access token. This is how the other GitHub skills implement their fallbacks.
|
||||
|
||||
### Setting the Token for API Calls
|
||||
|
||||
```bash
|
||||
# Option 1: Export as env var (preferred — keeps it out of commands)
|
||||
export GITHUB_TOKEN="<token>"
|
||||
|
||||
# Then use in curl calls:
|
||||
curl -s -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/user
|
||||
```
|
||||
|
||||
### Extracting the Token from Git Credentials
|
||||
|
||||
If git credentials are already configured (via credential.helper store), the token can be extracted:
|
||||
|
||||
```bash
|
||||
# Read from git credential store
|
||||
uv run python3 "${HERMES_HOME:-$HOME/.hermes}/skills/github/github-auth/scripts/git-credential-token.py"
|
||||
```
|
||||
|
||||
### Helper: Detect Auth Method
|
||||
|
||||
Use this pattern at the start of any GitHub workflow:
|
||||
|
||||
```bash
|
||||
# Try gh first, fall back to git + curl
|
||||
if command -v gh &>/dev/null && gh auth status &>/dev/null; then
|
||||
echo "AUTH_METHOD=gh"
|
||||
elif [ -n "$GITHUB_TOKEN" ]; then
|
||||
echo "AUTH_METHOD=curl"
|
||||
elif _hermes_env="${HERMES_HOME:-$HOME/.hermes}/.env"; [ -f "$_hermes_env" ] && grep -q "^GITHUB_TOKEN=" "$_hermes_env"; then
|
||||
export GITHUB_TOKEN=$(grep "^GITHUB_TOKEN=" "$_hermes_env" | head -1 | cut -d= -f2 | tr -d '\n\r')
|
||||
echo "AUTH_METHOD=curl"
|
||||
elif grep -q "github.com" ~/.git-credentials 2>/dev/null; then
|
||||
export GITHUB_TOKEN=$(uv run python3 "${HERMES_HOME:-$HOME/.hermes}/skills/github/github-auth/scripts/git-credential-token.py")
|
||||
echo "AUTH_METHOD=curl"
|
||||
else
|
||||
echo "AUTH_METHOD=none"
|
||||
echo "Need to set up authentication first"
|
||||
fi
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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 may be stale — run `git credential reject` then re-authenticate |
|
||||
| `ssh: connect to host github.com port 22: Connection refused` | Try SSH over HTTPS port: add `Host github.com` with `Port 443` and `Hostname ssh.github.com` to `~/.ssh/config` |
|
||||
| Credentials not persisting | Check `git config --global credential.helper` — must be `store` or `cache` |
|
||||
| Multiple GitHub accounts | Use SSH with different keys per host alias in `~/.ssh/config`, or per-repo credential URLs |
|
||||
| `gh: command not found` + no sudo | Use git-only Method 1 above — no installation needed |
|
||||
@@ -0,0 +1,499 @@
|
||||
---
|
||||
title: "Github Code Review — Review PRs: diffs, inline comments via gh or REST"
|
||||
sidebar_label: "Github Code Review"
|
||||
description: "Review PRs: diffs, inline comments via gh or REST"
|
||||
---
|
||||
|
||||
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
|
||||
|
||||
# Github Code Review
|
||||
|
||||
Review PRs: diffs, inline comments via gh or REST.
|
||||
|
||||
## Skill metadata
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Source | Bundled (installed by default) |
|
||||
| Path | `skills/github/github-code-review` |
|
||||
| Version | `1.1.0` |
|
||||
| Author | Hermes Agent |
|
||||
| License | MIT |
|
||||
| Platforms | linux, macos, windows |
|
||||
| Tags | `GitHub`, `Code-Review`, `Pull-Requests`, `Git`, `Quality` |
|
||||
| Related skills | [`github-auth`](/docs/user-guide/skills/bundled/github/github-github-auth), [`github-pr-workflow`](/docs/user-guide/skills/bundled/github/github-github-pr-workflow) |
|
||||
|
||||
## Reference: full SKILL.md
|
||||
|
||||
:::info
|
||||
The following is the complete skill definition that Hermes loads when this skill is triggered. This is what the agent sees as instructions when the skill is active.
|
||||
:::
|
||||
|
||||
# GitHub Code Review
|
||||
|
||||
Perform code reviews on local changes before pushing, or review open PRs on GitHub. Most of this skill uses plain `git` — the `gh`/`curl` split only matters for PR-level interactions.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Authenticated with GitHub (see `github-auth` skill)
|
||||
- Inside a git repository
|
||||
|
||||
### Setup (for PR interactions)
|
||||
|
||||
```bash
|
||||
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=$(uv run python3 "${HERMES_HOME:-$HOME/.hermes}/skills/github/github-auth/scripts/git-credential-token.py")
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
REMOTE_URL=$(git remote get-url origin)
|
||||
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)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. Reviewing Local Changes (Pre-Push)
|
||||
|
||||
This is pure `git` — works everywhere, no API needed.
|
||||
|
||||
### Get the Diff
|
||||
|
||||
```bash
|
||||
# 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 (insertions/deletions per file)
|
||||
git diff main...HEAD --stat
|
||||
```
|
||||
|
||||
### Review Strategy
|
||||
|
||||
1. **Get the big picture first:**
|
||||
|
||||
```bash
|
||||
git diff main...HEAD --stat
|
||||
git log main..HEAD --oneline
|
||||
```
|
||||
|
||||
2. **Review file by file** — use `read_file` on changed files for full context, and the diff to see what changed:
|
||||
|
||||
```bash
|
||||
git diff main...HEAD -- src/auth/login.py
|
||||
```
|
||||
|
||||
3. **Check for common issues:**
|
||||
|
||||
```bash
|
||||
# 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.
|
||||
|
||||
### Review Output Format
|
||||
|
||||
When reviewing local changes, present findings in this structure:
|
||||
|
||||
```
|
||||
## Code Review Summary
|
||||
|
||||
### Critical
|
||||
- **src/auth.py:45** — SQL injection: user input passed directly to query.
|
||||
Suggestion: Use parameterized queries.
|
||||
|
||||
### Warnings
|
||||
- **src/models/user.py:23** — Password stored in plaintext. Use bcrypt or argon2.
|
||||
- **src/api/routes.py:112** — No rate limiting on login endpoint.
|
||||
|
||||
### Suggestions
|
||||
- **src/utils/helpers.py:8** — Duplicates logic in `src/core/utils.py:34`. Consolidate.
|
||||
- **tests/test_auth.py** — Missing edge case: expired token test.
|
||||
|
||||
### Looks Good
|
||||
- Clean separation of concerns in the middleware layer
|
||||
- Good test coverage for the happy path
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Reviewing a Pull Request on GitHub
|
||||
|
||||
### View PR Details
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
gh pr view 123
|
||||
gh pr diff 123
|
||||
gh pr diff 123 --name-only
|
||||
```
|
||||
|
||||
**With git + curl:**
|
||||
|
||||
```bash
|
||||
PR_NUMBER=123
|
||||
|
||||
# Get PR details
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
pr = json.load(sys.stdin)
|
||||
print(f\"Title: {pr['title']}\")
|
||||
print(f\"Author: {pr['user']['login']}\")
|
||||
print(f\"Branch: {pr['head']['ref']} -> {pr['base']['ref']}\")
|
||||
print(f\"State: {pr['state']}\")
|
||||
print(f\"Body:\n{pr['body']}\")"
|
||||
|
||||
# List changed files
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER/files \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for f in json.load(sys.stdin):
|
||||
print(f\"{f['status']:10} +{f['additions']:-4} -{f['deletions']:-4} {f['filename']}\")"
|
||||
```
|
||||
|
||||
### Check Out PR Locally for Full Review
|
||||
|
||||
This works with plain `git` — no `gh` needed:
|
||||
|
||||
```bash
|
||||
# Fetch the PR branch and check it out
|
||||
git fetch origin pull/123/head:pr-123
|
||||
git checkout pr-123
|
||||
|
||||
# Now you can use read_file, search_files, run tests, etc.
|
||||
|
||||
# View diff against the base branch
|
||||
git diff main...pr-123
|
||||
```
|
||||
|
||||
**With gh (shortcut):**
|
||||
|
||||
```bash
|
||||
gh pr checkout 123
|
||||
```
|
||||
|
||||
### Leave Comments on a PR
|
||||
|
||||
**General PR comment — with gh:**
|
||||
|
||||
```bash
|
||||
gh pr comment 123 --body "Overall looks good, a few suggestions below."
|
||||
```
|
||||
|
||||
**General PR comment — with curl:**
|
||||
|
||||
```bash
|
||||
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."}'
|
||||
```
|
||||
|
||||
### Leave Inline Review Comments
|
||||
|
||||
**Single inline comment — with gh (via API):**
|
||||
|
||||
```bash
|
||||
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"
|
||||
```
|
||||
|
||||
**Single inline comment — with curl:**
|
||||
|
||||
```bash
|
||||
# Get the head commit SHA
|
||||
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\"
|
||||
}"
|
||||
```
|
||||
|
||||
### Submit a Formal Review (Approve / Request Changes)
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
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."
|
||||
```
|
||||
|
||||
**With curl — multi-comment review submitted atomically:**
|
||||
|
||||
```bash
|
||||
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 refers to the line number in the *new* version of the file. For deleted lines, use `"side": "LEFT"`.
|
||||
|
||||
---
|
||||
|
||||
## 3. 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
|
||||
|
||||
---
|
||||
|
||||
## 4. Pre-Push Review Workflow
|
||||
|
||||
When the user asks you to "review the code" or "check before pushing":
|
||||
|
||||
1. `git diff main...HEAD --stat` — see scope of changes
|
||||
2. `git diff main...HEAD` — read the full diff
|
||||
3. For each changed file, use `read_file` if you need more context
|
||||
4. Apply the checklist above
|
||||
5. Present findings in the structured format (Critical / Warnings / Suggestions / Looks Good)
|
||||
6. If critical issues found, offer to fix them before the user pushes
|
||||
|
||||
---
|
||||
|
||||
## 5. PR Review Workflow (End-to-End)
|
||||
|
||||
When the user asks you to "review PR #N", "look at this PR", or gives you a PR URL, follow this recipe:
|
||||
|
||||
### Step 1: Set up environment
|
||||
|
||||
```bash
|
||||
source "${HERMES_HOME:-$HOME/.hermes}/skills/github/github-auth/scripts/gh-env.sh"
|
||||
# Or run the inline setup block from the top of this skill
|
||||
```
|
||||
|
||||
### Step 2: Gather PR context
|
||||
|
||||
Get the PR metadata, description, and list of changed files to understand scope before diving into code.
|
||||
|
||||
**With gh:**
|
||||
```bash
|
||||
gh pr view 123
|
||||
gh pr diff 123 --name-only
|
||||
gh pr checks 123
|
||||
```
|
||||
|
||||
**With curl:**
|
||||
```bash
|
||||
PR_NUMBER=123
|
||||
|
||||
# PR details (title, author, description, branch)
|
||||
curl -s -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$GH_OWNER/$GH_REPO/pulls/$PR_NUMBER
|
||||
|
||||
# Changed files with line counts
|
||||
curl -s -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$GH_OWNER/$GH_REPO/pulls/$PR_NUMBER/files
|
||||
```
|
||||
|
||||
### Step 3: Check out the PR locally
|
||||
|
||||
This gives you full access to `read_file`, `search_files`, and the ability to run tests.
|
||||
|
||||
```bash
|
||||
git fetch origin pull/$PR_NUMBER/head:pr-$PR_NUMBER
|
||||
git checkout pr-$PR_NUMBER
|
||||
```
|
||||
|
||||
### Step 4: Read the diff and understand changes
|
||||
|
||||
```bash
|
||||
# Full diff against the base branch
|
||||
git diff main...HEAD
|
||||
|
||||
# Or file-by-file for large PRs
|
||||
git diff main...HEAD --name-only
|
||||
# Then for each file:
|
||||
git diff main...HEAD -- path/to/file.py
|
||||
```
|
||||
|
||||
For each changed file, use `read_file` to see full context around the changes — diffs alone can miss issues visible only with surrounding code.
|
||||
|
||||
### Step 5: Run automated checks locally (if applicable)
|
||||
|
||||
```bash
|
||||
# Run tests if there's a test suite
|
||||
python -m pytest 2>&1 | tail -20
|
||||
# or: npm test, cargo test, go test ./..., etc.
|
||||
|
||||
# Run linter if configured
|
||||
ruff check . 2>&1 | head -30
|
||||
# or: eslint, clippy, etc.
|
||||
```
|
||||
|
||||
### Step 6: Apply the review checklist (Section 3)
|
||||
|
||||
Go through each category: Correctness, Security, Code Quality, Testing, Performance, Documentation.
|
||||
|
||||
### Step 7: Post the review to GitHub
|
||||
|
||||
Collect your findings and submit them as a formal review with inline comments.
|
||||
|
||||
**With gh:**
|
||||
```bash
|
||||
# If no issues — approve
|
||||
gh pr review $PR_NUMBER --approve --body "Reviewed by Hermes Agent. Code looks clean — good test coverage, no security concerns."
|
||||
|
||||
# If issues found — request changes with inline comments
|
||||
gh pr review $PR_NUMBER --request-changes --body "Found a few issues — see inline comments."
|
||||
```
|
||||
|
||||
**With curl — atomic review with multiple inline comments:**
|
||||
```bash
|
||||
HEAD_SHA=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$GH_OWNER/$GH_REPO/pulls/$PR_NUMBER \
|
||||
| python3 -c "import sys,json; print(json.load(sys.stdin)['head']['sha'])")
|
||||
|
||||
# Build the review JSON — event is APPROVE, REQUEST_CHANGES, or COMMENT
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$GH_OWNER/$GH_REPO/pulls/$PR_NUMBER/reviews \
|
||||
-d "{
|
||||
\"commit_id\": \"$HEAD_SHA\",
|
||||
\"event\": \"REQUEST_CHANGES\",
|
||||
\"body\": \"## Hermes Agent Review\n\nFound 2 issues, 1 suggestion. See inline comments.\",
|
||||
\"comments\": [
|
||||
{\"path\": \"src/auth.py\", \"line\": 45, \"body\": \"🔴 **Critical:** User input passed directly to SQL query — use parameterized queries.\"},
|
||||
{\"path\": \"src/models.py\", \"line\": 23, \"body\": \"⚠️ **Warning:** Password stored without hashing.\"},
|
||||
{\"path\": \"src/utils.py\", \"line\": 8, \"body\": \"💡 **Suggestion:** This duplicates logic in core/utils.py:34.\"}
|
||||
]
|
||||
}"
|
||||
```
|
||||
|
||||
### Step 8: Also post a summary comment
|
||||
|
||||
In addition to inline comments, leave a top-level summary so the PR author gets the full picture at a glance. Use the review output format from `references/review-output-template.md`.
|
||||
|
||||
**With gh:**
|
||||
```bash
|
||||
gh pr comment $PR_NUMBER --body "$(cat <<'EOF'
|
||||
## Code Review Summary
|
||||
|
||||
**Verdict: Changes Requested** (2 issues, 1 suggestion)
|
||||
|
||||
### 🔴 Critical
|
||||
- **src/auth.py:45** — SQL injection vulnerability
|
||||
|
||||
### ⚠️ Warnings
|
||||
- **src/models.py:23** — Plaintext password storage
|
||||
|
||||
### 💡 Suggestions
|
||||
- **src/utils.py:8** — Duplicated logic, consider consolidating
|
||||
|
||||
### ✅ Looks Good
|
||||
- Clean API design
|
||||
- Good error handling in the middleware layer
|
||||
|
||||
---
|
||||
*Reviewed by Hermes Agent*
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
### Step 9: Clean up
|
||||
|
||||
```bash
|
||||
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 or all clear
|
||||
- **Request Changes** — any critical or warning-level issue that should be fixed before merge
|
||||
- **Comment** — observations and suggestions, but nothing blocking (use when you're unsure or the PR is a draft)
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
title: "Github Issue To Pr — Carry a GitHub issue to a verified PR with honest CI state"
|
||||
sidebar_label: "Github Issue To Pr"
|
||||
description: "Carry a GitHub issue to a verified PR with honest CI state"
|
||||
---
|
||||
|
||||
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
|
||||
|
||||
# Github Issue To Pr
|
||||
|
||||
Carry a GitHub issue to a verified PR with honest CI state.
|
||||
|
||||
## Skill metadata
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Source | Bundled (installed by default) |
|
||||
| Path | `skills/github/github-issue-to-pr` |
|
||||
| Version | `0.1.0` |
|
||||
| Author | Ben Barclay (benbarclay), Hermes Agent |
|
||||
| License | MIT |
|
||||
| Platforms | linux, macos, windows |
|
||||
| Tags | `GitHub`, `Issues`, `Coding`, `Pull-Requests`, `CI` |
|
||||
| Related skills | [`github-issues`](/docs/user-guide/skills/bundled/github/github-github-issues), [`github-pr-workflow`](/docs/user-guide/skills/bundled/github/github-github-pr-workflow), [`systematic-debugging`](/docs/user-guide/skills/bundled/software-development/software-development-systematic-debugging), [`test-driven-development`](/docs/user-guide/skills/bundled/software-development/software-development-test-driven-development), [`requesting-code-review`](/docs/user-guide/skills/bundled/software-development/software-development-requesting-code-review) |
|
||||
|
||||
## Reference: full SKILL.md
|
||||
|
||||
:::info
|
||||
The following is the complete skill definition that Hermes loads when this skill is triggered. This is what the agent sees as instructions when the skill is active.
|
||||
:::
|
||||
|
||||
# GitHub Issue to Pull Request
|
||||
|
||||
Turn a GitHub issue into a tested, verified PR. This skill owns the end-to-end discipline — premise validation, duplicate sweeps, class-level fixes, and honest CI reporting; the sibling GitHub and development skills own their own mechanics.
|
||||
|
||||
## When to Use
|
||||
|
||||
- "Fix issue #123 and open a PR."
|
||||
- "Implement this GitHub feature request."
|
||||
- "Take this bug from issue to green CI."
|
||||
|
||||
Don't use for: reviewing an existing PR, or answering a code question with no requested change.
|
||||
|
||||
## Procedure
|
||||
|
||||
### 1. Read the live issue — body AND full thread
|
||||
|
||||
Use `terminal` to run `gh issue view <N> --comments`. The body is a snapshot from filing time; the newest comments carry the live state: partial fixes already merged, new root-cause analyses, maintainer decisions, or questions directed at you that change the task. Also read repository instructions (`AGENTS.md`, contribution docs) with `read_file`. Done when the currently requested behavior, non-goals, and any unanswered thread questions are known.
|
||||
|
||||
### 2. Sweep for existing and duplicate work
|
||||
|
||||
Before writing anything, run `gh pr list --search "#<N>" --state all` plus at least two keyword/synonym variants of the symptom (`gh pr list --search "<subsystem> <symptom>" --state open`). Popular issues attract multiple independent fixes; building a duplicate wastes the work and the credit. Also check whether a recent commit already fixed it: `git log --oneline -20 -- <relevant files>`. Done when you know every open PR and recent commit touching this issue, or that none exist.
|
||||
|
||||
### 3. Validate the premise against current code — and against design intent
|
||||
|
||||
Reproduce the bug or demonstrate the missing behavior on the current default branch with a failing test or fixture, using `search_files` and `read_file` to trace the reported path. Then check the second question: is the "bug" actually deliberate design? Run `git log -p -S "<symbol>"` on the code the issue wants changed and read the original commit's intent — a missing link or restriction is often the feature. Challenge stale or flawed issue prose instead of implementing it blindly. Done when the root cause or feature gap is demonstrated in current code AND the change doesn't fight an intentional design.
|
||||
|
||||
### 4. Define acceptance and risk
|
||||
|
||||
List acceptance criteria, interfaces, migrations/state changes, compatibility, security/privacy, rollout, and rollback. Map every criterion to a test or explicit verification. Done when review has a finite contract.
|
||||
|
||||
### 5. Implement the smallest complete change — and fix the class
|
||||
|
||||
Work on an isolated branch or worktree, loading `systematic-debugging` or `test-driven-development` when the bug class calls for them. Add regression tests first, then implement. When the fix is in hand, `search_files` for the same bug shape at sibling call sites and fix the whole class in this PR — an incomplete fix that leaves known siblings broken is worse than none. Every changed line must trace to the issue; no drive-by cleanup. Done when targeted tests pass, the original failure no longer reproduces, and sibling sites are fixed or explicitly ruled out.
|
||||
|
||||
### 6. Prove the regression test bites (sabotage run)
|
||||
|
||||
Temporarily restore the old behavior of the exact function under test, run the new test, and confirm it FAILS; then restore the fix and confirm it passes. A regression test that passes with and without the fix proves nothing. Done when the test demonstrably fails on pre-fix code.
|
||||
|
||||
### 7. Run repository quality gates, then open the PR immediately
|
||||
|
||||
Run the formatter, lint, typecheck, and the repo's canonical test entrypoint on affected areas; use `requesting-code-review` on the diff. Then push and open the PR right away — the PR is what dispatches CI, and CI latency is the long pole; do not sit on finished work. Load `github-pr-workflow` for PR mechanics: conventional branch/commit, body linking the issue with problem, approach, tests, risk, and exclusions. Read the PR back and verify head SHA, base, title, and files. Done when the PR exists with the intended diff and CI is running.
|
||||
|
||||
### 8. Shepherd CI honestly and close the loop
|
||||
|
||||
Inspect live checks and failure logs via `gh pr checks` / `gh run view --log-failed`. Distinguish failures introduced by your diff from pre-existing baseline or infrastructure failures — reproduce on the default branch when unsure, and rerun once only for genuine infra flakes. Never say "green," "merged," or "released" without live evidence of that exact state. When the PR lands, comment on the issue with the PR link and a one-line explanation so the reporter gets a traceable resolution. Done when CI state, remaining blockers, and the issue thread all reflect reality.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- Coding before reading issue comments, sweeping for duplicate PRs, or reading current code.
|
||||
- "Fixing" behavior that the original commit shows is intentional design.
|
||||
- Fixing a symptom at one call site while sibling sites keep the same bug.
|
||||
- Shipping a regression test that also passes without the fix.
|
||||
- Opening a PR with unrun tests or unrelated formatting churn.
|
||||
- Claiming the issue is delivered because a PR exists.
|
||||
|
||||
## Verification
|
||||
|
||||
- [ ] Full issue thread read; newest comment state reflected in the plan.
|
||||
- [ ] Duplicate-PR sweep run with issue number + 2 keyword variants.
|
||||
- [ ] Premise reproduced on current code; design intent checked via git history.
|
||||
- [ ] Regression test proven to fail without the fix.
|
||||
- [ ] Sibling call sites fixed or explicitly ruled out.
|
||||
- [ ] Every changed line traces to the issue.
|
||||
- [ ] CI state reported from live evidence only; issue commented with the PR link.
|
||||
@@ -0,0 +1,388 @@
|
||||
---
|
||||
title: "Github Issues — Create, triage, label, assign GitHub issues via gh or REST"
|
||||
sidebar_label: "Github Issues"
|
||||
description: "Create, triage, label, assign GitHub issues via gh or REST"
|
||||
---
|
||||
|
||||
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
|
||||
|
||||
# Github Issues
|
||||
|
||||
Create, triage, label, assign GitHub issues via gh or REST.
|
||||
|
||||
## Skill metadata
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Source | Bundled (installed by default) |
|
||||
| Path | `skills/github/github-issues` |
|
||||
| Version | `1.1.0` |
|
||||
| Author | Hermes Agent |
|
||||
| License | MIT |
|
||||
| Platforms | linux, macos, windows |
|
||||
| Tags | `GitHub`, `Issues`, `Project-Management`, `Bug-Tracking`, `Triage` |
|
||||
| Related skills | [`github-auth`](/docs/user-guide/skills/bundled/github/github-github-auth), [`github-pr-workflow`](/docs/user-guide/skills/bundled/github/github-github-pr-workflow) |
|
||||
|
||||
## Reference: full SKILL.md
|
||||
|
||||
:::info
|
||||
The following is the complete skill definition that Hermes loads when this skill is triggered. This is what the agent sees as instructions when the skill is active.
|
||||
:::
|
||||
|
||||
# GitHub Issues Management
|
||||
|
||||
Create, search, triage, and manage GitHub issues. Each section shows `gh` first, then the `curl` fallback.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Authenticated with GitHub (see `github-auth` skill)
|
||||
- Inside a git repo with a GitHub remote, or specify the repo explicitly
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
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=$(uv run python3 "${HERMES_HOME:-$HOME/.hermes}/skills/github/github-auth/scripts/git-credential-token.py")
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
REMOTE_URL=$(git remote get-url origin)
|
||||
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)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. Viewing Issues
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
**With curl:**
|
||||
|
||||
```bash
|
||||
# List open issues
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/$OWNER/$REPO/issues?state=open&per_page=20" \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for i in json.load(sys.stdin):
|
||||
if 'pull_request' not in i: # GitHub API returns PRs in /issues too
|
||||
labels = ', '.join(l['name'] for l in i['labels'])
|
||||
print(f\"#{i['number']:5} {i['state']:6} {labels:30} {i['title']}\")"
|
||||
|
||||
# Filter by label
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/$OWNER/$REPO/issues?state=open&labels=bug&per_page=20" \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for i in json.load(sys.stdin):
|
||||
if 'pull_request' not in i:
|
||||
print(f\"#{i['number']} {i['title']}\")"
|
||||
|
||||
# View a specific issue
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/issues/42 \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
i = json.load(sys.stdin)
|
||||
labels = ', '.join(l['name'] for l in i['labels'])
|
||||
assignees = ', '.join(a['login'] for a in i['assignees'])
|
||||
print(f\"#{i['number']}: {i['title']}\")
|
||||
print(f\"State: {i['state']} Labels: {labels} Assignees: {assignees}\")
|
||||
print(f\"Author: {i['user']['login']} Created: {i['created_at']}\")
|
||||
print(f\"\n{i['body']}\")"
|
||||
|
||||
# Search issues
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/search/issues?q=authentication+error+repo:$OWNER/$REPO" \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for i in json.load(sys.stdin)['items']:
|
||||
print(f\"#{i['number']} {i['state']:6} {i['title']}\")"
|
||||
```
|
||||
|
||||
## 2. Creating Issues
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
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"
|
||||
```
|
||||
|
||||
**With curl:**
|
||||
|
||||
```bash
|
||||
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.\n\n## Steps to Reproduce\n1. Navigate to /settings while logged out\n2. Get redirected to /login?next=/settings\n3. Log in\n4. Actual: redirected to /dashboard\n\n## Expected Behavior\nRespect the ?next= query parameter.",
|
||||
"labels": ["bug", "backend"],
|
||||
"assignees": ["username"]
|
||||
}'
|
||||
```
|
||||
|
||||
### Bug Report Template
|
||||
|
||||
```
|
||||
## Bug Description
|
||||
<What's happening>
|
||||
|
||||
## Steps to Reproduce
|
||||
1. <step>
|
||||
2. <step>
|
||||
|
||||
## Expected Behavior
|
||||
<What should happen>
|
||||
|
||||
## Actual Behavior
|
||||
<What actually happens>
|
||||
|
||||
## Environment
|
||||
- OS: <os>
|
||||
- Version: <version>
|
||||
```
|
||||
|
||||
### Feature Request Template
|
||||
|
||||
```
|
||||
## Feature Description
|
||||
<What you want>
|
||||
|
||||
## Motivation
|
||||
<Why this would be useful>
|
||||
|
||||
## Proposed Solution
|
||||
<How it could work>
|
||||
|
||||
## Alternatives Considered
|
||||
<Other approaches>
|
||||
```
|
||||
|
||||
## 3. Managing Issues
|
||||
|
||||
### Add/Remove Labels
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
gh issue edit 42 --add-label "priority:high,bug"
|
||||
gh issue edit 42 --remove-label "needs-triage"
|
||||
```
|
||||
|
||||
**With curl:**
|
||||
|
||||
```bash
|
||||
# Add labels
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/issues/42/labels \
|
||||
-d '{"labels": ["priority:high", "bug"]}'
|
||||
|
||||
# Remove a label
|
||||
curl -s -X DELETE \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/issues/42/labels/needs-triage
|
||||
|
||||
# List available labels in the repo
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/labels \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for l in json.load(sys.stdin):
|
||||
print(f\" {l['name']:30} {l.get('description', '')}\")"
|
||||
```
|
||||
|
||||
### Assignment
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
gh issue edit 42 --add-assignee username
|
||||
gh issue edit 42 --add-assignee @me
|
||||
```
|
||||
|
||||
**With curl:**
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/issues/42/assignees \
|
||||
-d '{"assignees": ["username"]}'
|
||||
```
|
||||
|
||||
### Commenting
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
gh issue comment 42 --body "Investigated — root cause is in auth middleware. Working on a fix."
|
||||
```
|
||||
|
||||
**With curl:**
|
||||
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/issues/42/comments \
|
||||
-d '{"body": "Investigated — root cause is in auth middleware. Working on a fix."}'
|
||||
```
|
||||
|
||||
### Closing and Reopening
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
gh issue close 42
|
||||
gh issue close 42 --reason "not planned"
|
||||
gh issue reopen 42
|
||||
```
|
||||
|
||||
**With curl:**
|
||||
|
||||
```bash
|
||||
# Close
|
||||
curl -s -X PATCH \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/issues/42 \
|
||||
-d '{"state": "closed", "state_reason": "completed"}'
|
||||
|
||||
# Reopen
|
||||
curl -s -X PATCH \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/issues/42 \
|
||||
-d '{"state": "open"}'
|
||||
```
|
||||
|
||||
### Linking Issues to PRs
|
||||
|
||||
Issues are automatically closed when a PR merges with the right keywords in the body:
|
||||
|
||||
```
|
||||
Closes #42
|
||||
Fixes #42
|
||||
Resolves #42
|
||||
```
|
||||
|
||||
To create a branch from an issue:
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
gh issue develop 42 --checkout
|
||||
```
|
||||
|
||||
**With git (manual equivalent):**
|
||||
|
||||
```bash
|
||||
git checkout main && git pull origin main
|
||||
git checkout -b fix/issue-42-login-redirect
|
||||
```
|
||||
|
||||
## 4. Issue Triage Workflow
|
||||
|
||||
When asked to triage issues:
|
||||
|
||||
1. **List untriaged issues:**
|
||||
|
||||
```bash
|
||||
# With gh
|
||||
gh issue list --label "needs-triage" --state open
|
||||
|
||||
# With curl
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/$OWNER/$REPO/issues?labels=needs-triage&state=open" \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for i in json.load(sys.stdin):
|
||||
if 'pull_request' not in i:
|
||||
print(f\"#{i['number']} {i['title']}\")"
|
||||
```
|
||||
|
||||
2. **Read and categorize** each issue (view details, understand the bug/feature)
|
||||
|
||||
3. **Apply labels and priority** (see Managing Issues above)
|
||||
|
||||
4. **Assign** if the owner is clear
|
||||
|
||||
5. **Comment with triage notes** if needed
|
||||
|
||||
## 5. Bulk Operations
|
||||
|
||||
For batch operations, combine API calls with shell scripting:
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
# Close all issues with a specific label
|
||||
gh issue list --label "wontfix" --json number --jq '.[].number' | \
|
||||
xargs -I {} gh issue close {} --reason "not planned"
|
||||
```
|
||||
|
||||
**With curl:**
|
||||
|
||||
```bash
|
||||
# List issue numbers with a label, then close each
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/$OWNER/$REPO/issues?labels=wontfix&state=open" \
|
||||
| python3 -c "import sys,json; [print(i['number']) for i in json.load(sys.stdin)]" \
|
||||
| while read num; do
|
||||
curl -s -X PATCH \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/issues/$num \
|
||||
-d '{"state": "closed", "state_reason": "not_planned"}'
|
||||
echo "Closed #$num"
|
||||
done
|
||||
```
|
||||
|
||||
## Quick Reference Table
|
||||
|
||||
| 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=...` |
|
||||
@@ -0,0 +1,385 @@
|
||||
---
|
||||
title: "Github Pr Workflow — GitHub PR lifecycle: branch, commit, open, CI, merge"
|
||||
sidebar_label: "Github Pr Workflow"
|
||||
description: "GitHub PR lifecycle: branch, commit, open, CI, merge"
|
||||
---
|
||||
|
||||
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
|
||||
|
||||
# Github Pr Workflow
|
||||
|
||||
GitHub PR lifecycle: branch, commit, open, CI, merge.
|
||||
|
||||
## Skill metadata
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Source | Bundled (installed by default) |
|
||||
| Path | `skills/github/github-pr-workflow` |
|
||||
| Version | `1.1.0` |
|
||||
| Author | Hermes Agent |
|
||||
| License | MIT |
|
||||
| Platforms | linux, macos, windows |
|
||||
| Tags | `GitHub`, `Pull-Requests`, `CI/CD`, `Git`, `Automation`, `Merge` |
|
||||
| Related skills | [`github-auth`](/docs/user-guide/skills/bundled/github/github-github-auth), [`github-code-review`](/docs/user-guide/skills/bundled/github/github-github-code-review) |
|
||||
|
||||
## Reference: full SKILL.md
|
||||
|
||||
:::info
|
||||
The following is the complete skill definition that Hermes loads when this skill is triggered. This is what the agent sees as instructions when the skill is active.
|
||||
:::
|
||||
|
||||
# GitHub Pull Request Workflow
|
||||
|
||||
Complete guide for managing the PR lifecycle. Each section shows the `gh` way first, then the `git` + `curl` fallback for machines without `gh`.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Authenticated with GitHub (see `github-auth` skill)
|
||||
- Inside a git repository with a GitHub remote
|
||||
|
||||
### Quick Auth Detection
|
||||
|
||||
```bash
|
||||
# Determine which method to use throughout this workflow
|
||||
if command -v gh &>/dev/null && gh auth status &>/dev/null; then
|
||||
AUTH="gh"
|
||||
else
|
||||
AUTH="git"
|
||||
# Ensure we have a token for API calls
|
||||
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=$(uv run python3 "${HERMES_HOME:-$HOME/.hermes}/skills/github/github-auth/scripts/git-credential-token.py")
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
echo "Using: $AUTH"
|
||||
```
|
||||
|
||||
### Extracting Owner/Repo from the Git Remote
|
||||
|
||||
Many `curl` commands need `owner/repo`. Extract it from the git remote:
|
||||
|
||||
```bash
|
||||
# Works for both HTTPS and SSH remote URLs
|
||||
REMOTE_URL=$(git remote get-url origin)
|
||||
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)
|
||||
echo "Owner: $OWNER, Repo: $REPO"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. Branch Creation
|
||||
|
||||
This part is pure `git` — identical either way:
|
||||
|
||||
```bash
|
||||
# Make sure you're up to date
|
||||
git fetch origin
|
||||
git checkout main && git pull origin main
|
||||
|
||||
# Create and switch to a new branch
|
||||
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
|
||||
|
||||
## 2. Making Commits
|
||||
|
||||
Use the agent's file tools (`write_file`, `patch`) to make changes, then commit:
|
||||
|
||||
```bash
|
||||
# Stage specific files
|
||||
git add src/auth.py src/models/user.py tests/test_auth.py
|
||||
|
||||
# Commit with a conventional commit message
|
||||
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"
|
||||
```
|
||||
|
||||
Commit message format (Conventional Commits):
|
||||
```
|
||||
type(scope): short description
|
||||
|
||||
Longer explanation if needed. Wrap at 72 characters.
|
||||
```
|
||||
|
||||
Types: `feat`, `fix`, `refactor`, `docs`, `test`, `ci`, `chore`, `perf`
|
||||
|
||||
## 3. Pushing and Creating a PR
|
||||
|
||||
### Push the Branch (same either way)
|
||||
|
||||
```bash
|
||||
git push -u origin HEAD
|
||||
```
|
||||
|
||||
### Create the PR
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
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"
|
||||
```
|
||||
|
||||
Options: `--draft`, `--reviewer user1,user2`, `--label "enhancement"`, `--base develop`
|
||||
|
||||
**With git + curl:**
|
||||
|
||||
```bash
|
||||
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\"
|
||||
}"
|
||||
```
|
||||
|
||||
The response JSON includes the PR `number` — save it for later commands.
|
||||
|
||||
To create as a draft, add `"draft": true` to the JSON body.
|
||||
|
||||
## 4. Monitoring CI Status
|
||||
|
||||
### Check CI Status
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
# One-shot check
|
||||
gh pr checks
|
||||
|
||||
# Watch until all checks finish (polls every 10s)
|
||||
gh pr checks --watch
|
||||
```
|
||||
|
||||
**With git + curl:**
|
||||
|
||||
```bash
|
||||
# Get the latest commit SHA on the current branch
|
||||
SHA=$(git rev-parse HEAD)
|
||||
|
||||
# Query the combined status
|
||||
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', '')}\")"
|
||||
|
||||
# Also check GitHub Actions check runs (separate endpoint)
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/commits/$SHA/check-runs \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
data = json.load(sys.stdin)
|
||||
for cr in data.get('check_runs', []):
|
||||
print(f\" {cr['name']}: {cr['status']} / {cr['conclusion'] or 'pending'}\")"
|
||||
```
|
||||
|
||||
### Poll Until Complete (git + curl)
|
||||
|
||||
```bash
|
||||
# Simple polling loop — check every 30 seconds, up to 10 minutes
|
||||
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
|
||||
```
|
||||
|
||||
## 5. Auto-Fixing CI Failures
|
||||
|
||||
When CI fails, diagnose and fix. This loop works with either auth method.
|
||||
|
||||
### Step 1: Get Failure Details
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
# List recent workflow runs on this branch
|
||||
gh run list --branch $(git branch --show-current) --limit 5
|
||||
|
||||
# View failed logs
|
||||
gh run view <RUN_ID> --log-failed
|
||||
```
|
||||
|
||||
**With git + curl:**
|
||||
|
||||
```bash
|
||||
BRANCH=$(git branch --show-current)
|
||||
|
||||
# List workflow runs on this branch
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/$OWNER/$REPO/actions/runs?branch=$BRANCH&per_page=5" \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
runs = json.load(sys.stdin)['workflow_runs']
|
||||
for r in runs:
|
||||
print(f\"Run {r['id']}: {r['name']} - {r['conclusion'] or r['status']}\")"
|
||||
|
||||
# Get failed job logs (download as zip, extract, read)
|
||||
RUN_ID=<run_id>
|
||||
curl -s -L \
|
||||
-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
|
||||
|
||||
After identifying the issue, use file tools (`patch`, `write_file`) to fix it:
|
||||
|
||||
```bash
|
||||
git add <fixed_files>
|
||||
git commit -m "fix: resolve CI failure in <check_name>"
|
||||
git push
|
||||
```
|
||||
|
||||
### Step 3: Verify
|
||||
|
||||
Re-check CI status using the commands from Section 4 above.
|
||||
|
||||
### Auto-Fix Loop Pattern
|
||||
|
||||
When asked to auto-fix CI, follow this loop:
|
||||
|
||||
1. Check CI status → identify failures
|
||||
2. Read failure logs → understand the error
|
||||
3. Use `read_file` + `patch`/`write_file` → fix the code
|
||||
4. `git add . && git commit -m "fix: ..." && git push`
|
||||
5. Wait for CI → re-check status
|
||||
6. Repeat if still failing (up to 3 attempts, then ask the user)
|
||||
|
||||
## 6. Merging
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
# Squash merge + delete branch (cleanest for feature branches)
|
||||
gh pr merge --squash --delete-branch
|
||||
|
||||
# Enable auto-merge (merges when all checks pass)
|
||||
gh pr merge --auto --squash --delete-branch
|
||||
```
|
||||
|
||||
**With git + curl:**
|
||||
|
||||
```bash
|
||||
PR_NUMBER=<number>
|
||||
|
||||
# Merge the PR via API (squash)
|
||||
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)\"
|
||||
}"
|
||||
|
||||
# Delete the remote branch after merge
|
||||
BRANCH=$(git branch --show-current)
|
||||
git push origin --delete $BRANCH
|
||||
|
||||
# Switch back to main locally
|
||||
git checkout main && git pull origin main
|
||||
git branch -d $BRANCH
|
||||
```
|
||||
|
||||
Merge methods: `"merge"` (merge commit), `"squash"`, `"rebase"`
|
||||
|
||||
### Enable Auto-Merge (curl)
|
||||
|
||||
```bash
|
||||
# Auto-merge requires the repo to have it enabled in settings.
|
||||
# This uses the GraphQL API since REST doesn't support auto-merge.
|
||||
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 } }\"}"
|
||||
```
|
||||
|
||||
## 7. Complete Workflow Example
|
||||
|
||||
```bash
|
||||
# 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 (picks gh or curl based on what's available)
|
||||
# ... (see Section 3)
|
||||
|
||||
# 7. Monitor CI (see Section 4)
|
||||
|
||||
# 8. Merge when green (see Section 6)
|
||||
```
|
||||
|
||||
## Useful PR Commands Reference
|
||||
|
||||
| Action | gh | git + curl |
|
||||
|--------|-----|-----------|
|
||||
| List my PRs | `gh pr list --author @me` | `curl -s -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/repos/$OWNER/$REPO/pulls?state=open"` |
|
||||
| View PR diff | `gh pr diff` | `git diff main...HEAD` (local) or `curl -H "Accept: application/vnd.github.diff" ...` |
|
||||
| Add comment | `gh pr comment N --body "..."` | `curl -X POST .../issues/N/comments -d '{"body":"..."}'` |
|
||||
| Request review | `gh pr edit N --add-reviewer user` | `curl -X POST .../pulls/N/requested_reviewers -d '{"reviewers":["user"]}'` |
|
||||
| Close PR | `gh pr close N` | `curl -X PATCH .../pulls/N -d '{"state":"closed"}'` |
|
||||
| Check out someone's PR | `gh pr checkout N` | `git fetch origin pull/N/head:pr-N && git checkout pr-N` |
|
||||
@@ -0,0 +1,534 @@
|
||||
---
|
||||
title: "Github Repo Management — Clone/create/fork repos; manage remotes, releases"
|
||||
sidebar_label: "Github Repo Management"
|
||||
description: "Clone/create/fork repos; manage remotes, releases"
|
||||
---
|
||||
|
||||
{/* This page is auto-generated from the skill's SKILL.md by website/scripts/generate-skill-docs.py. Edit the source SKILL.md, not this page. */}
|
||||
|
||||
# Github Repo Management
|
||||
|
||||
Clone/create/fork repos; manage remotes, releases.
|
||||
|
||||
## Skill metadata
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Source | Bundled (installed by default) |
|
||||
| Path | `skills/github/github-repo-management` |
|
||||
| Version | `1.1.0` |
|
||||
| Author | Hermes Agent |
|
||||
| License | MIT |
|
||||
| Platforms | linux, macos, windows |
|
||||
| Tags | `GitHub`, `Repositories`, `Git`, `Releases`, `Secrets`, `Configuration` |
|
||||
| Related skills | [`github-auth`](/docs/user-guide/skills/bundled/github/github-github-auth), [`github-pr-workflow`](/docs/user-guide/skills/bundled/github/github-github-pr-workflow), [`github-issues`](/docs/user-guide/skills/bundled/github/github-github-issues) |
|
||||
|
||||
## Reference: full SKILL.md
|
||||
|
||||
:::info
|
||||
The following is the complete skill definition that Hermes loads when this skill is triggered. This is what the agent sees as instructions when the skill is active.
|
||||
:::
|
||||
|
||||
# GitHub Repository Management
|
||||
|
||||
Create, clone, fork, configure, and manage GitHub repositories. Each section shows `gh` first, then the `git` + `curl` fallback.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Authenticated with GitHub (see `github-auth` skill)
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
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=$(uv run python3 "${HERMES_HOME:-$HOME/.hermes}/skills/github/github-auth/scripts/git-credential-token.py")
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Get your GitHub username (needed for several operations)
|
||||
if [ "$AUTH" = "gh" ]; then
|
||||
GH_USER=$(gh api user --jq '.login')
|
||||
else
|
||||
GH_USER=$(curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/user | python3 -c "import sys,json; print(json.load(sys.stdin)['login'])")
|
||||
fi
|
||||
```
|
||||
|
||||
If you're inside a repo already:
|
||||
|
||||
```bash
|
||||
REMOTE_URL=$(git remote get-url origin)
|
||||
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)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. Cloning Repositories
|
||||
|
||||
Cloning is pure `git` — works identically either way:
|
||||
|
||||
```bash
|
||||
# Clone via HTTPS (works with credential helper or token-embedded URL)
|
||||
git clone https://github.com/owner/repo-name.git
|
||||
|
||||
# Clone into a specific directory
|
||||
git clone https://github.com/owner/repo-name.git ./my-local-dir
|
||||
|
||||
# Shallow clone (faster for large repos)
|
||||
git clone --depth 1 https://github.com/owner/repo-name.git
|
||||
|
||||
# Clone a specific branch
|
||||
git clone --branch develop https://github.com/owner/repo-name.git
|
||||
|
||||
# Clone via SSH (if SSH is configured)
|
||||
git clone git@github.com:owner/repo-name.git
|
||||
```
|
||||
|
||||
**With gh (shorthand):**
|
||||
|
||||
```bash
|
||||
gh repo clone owner/repo-name
|
||||
gh repo clone owner/repo-name -- --depth 1
|
||||
```
|
||||
|
||||
## 2. Creating Repositories
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
# Create a public repo and clone it
|
||||
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:**
|
||||
|
||||
```bash
|
||||
# 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
|
||||
```
|
||||
|
||||
To create under an organization:
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
gh repo create my-new-app --template owner/template-repo --public --clone
|
||||
```
|
||||
|
||||
**With curl:**
|
||||
|
||||
```bash
|
||||
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}'
|
||||
```
|
||||
|
||||
## 3. Forking Repositories
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
gh repo fork owner/repo-name --clone
|
||||
```
|
||||
|
||||
**With git + curl:**
|
||||
|
||||
```bash
|
||||
# Create the fork via API
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/owner/repo-name/forks
|
||||
|
||||
# Wait a moment for GitHub to create it, then clone
|
||||
sleep 3
|
||||
git clone https://github.com/$GH_USER/repo-name.git
|
||||
cd repo-name
|
||||
|
||||
# Add the original repo as "upstream" remote
|
||||
git remote add upstream https://github.com/owner/repo-name.git
|
||||
```
|
||||
|
||||
### Keeping a Fork in Sync
|
||||
|
||||
```bash
|
||||
# Pure git — works everywhere
|
||||
git fetch upstream
|
||||
git checkout main
|
||||
git merge upstream/main
|
||||
git push origin main
|
||||
```
|
||||
|
||||
**With gh (shortcut):**
|
||||
|
||||
```bash
|
||||
gh repo sync $GH_USER/repo-name
|
||||
```
|
||||
|
||||
## 4. Repository Information
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
gh repo view owner/repo-name
|
||||
gh repo list --limit 20
|
||||
gh search repos "machine learning" --language python --sort stars
|
||||
```
|
||||
|
||||
**With curl:**
|
||||
|
||||
```bash
|
||||
# View repo details
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
r = json.load(sys.stdin)
|
||||
print(f\"Name: {r['full_name']}\")
|
||||
print(f\"Description: {r['description']}\")
|
||||
print(f\"Stars: {r['stargazers_count']} Forks: {r['forks_count']}\")
|
||||
print(f\"Default branch: {r['default_branch']}\")
|
||||
print(f\"Language: {r['language']}\")"
|
||||
|
||||
# List your repos
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/user/repos?per_page=20&sort=updated" \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for r in json.load(sys.stdin):
|
||||
vis = 'private' if r['private'] else 'public'
|
||||
print(f\" {r['full_name']:40} {vis:8} {r.get('language', ''):10} ★{r['stargazers_count']}\")"
|
||||
|
||||
# Search repos
|
||||
curl -s \
|
||||
"https://api.github.com/search/repositories?q=machine+learning+language:python&sort=stars&per_page=10" \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for r in json.load(sys.stdin)['items']:
|
||||
print(f\" {r['full_name']:40} ★{r['stargazers_count']:6} {r['description'][:60] if r['description'] else ''}\")"
|
||||
```
|
||||
|
||||
## 5. Repository Settings
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
**With curl:**
|
||||
|
||||
```bash
|
||||
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"]}'
|
||||
```
|
||||
|
||||
## 6. Branch Protection
|
||||
|
||||
```bash
|
||||
# View current protection
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/branches/main/protection
|
||||
|
||||
# Set up branch 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
|
||||
}'
|
||||
```
|
||||
|
||||
## 7. Secrets Management (GitHub Actions)
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
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 require encryption with the repo's public key — more involved via API:
|
||||
|
||||
```bash
|
||||
# Get the repo's public key for encrypting secrets
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/actions/secrets/public-key
|
||||
|
||||
# Encrypt and set (requires Python with PyNaCl)
|
||||
python3 -c "
|
||||
from base64 import b64encode
|
||||
from nacl import encoding, public
|
||||
import json, sys
|
||||
|
||||
# Get the public key
|
||||
key_id = '<key_id_from_above>'
|
||||
public_key = '<base64_key_from_above>'
|
||||
|
||||
# Encrypt
|
||||
sealed = public.SealedBox(
|
||||
public.PublicKey(public_key.encode('utf-8'), encoding.Base64Encoder)
|
||||
).encrypt('your-secret-value'.encode('utf-8'))
|
||||
print(json.dumps({
|
||||
'encrypted_value': b64encode(sealed).decode('utf-8'),
|
||||
'key_id': key_id
|
||||
}))"
|
||||
|
||||
# Then PUT the encrypted secret
|
||||
curl -s -X PUT \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/actions/secrets/API_KEY \
|
||||
-d '<output from python script above>'
|
||||
|
||||
# List secrets (names only, values hidden)
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/actions/secrets \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for s in json.load(sys.stdin)['secrets']:
|
||||
print(f\" {s['name']:30} updated: {s['updated_at']}\")"
|
||||
```
|
||||
|
||||
Note: 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.
|
||||
|
||||
## 8. Releases
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
**With curl:**
|
||||
|
||||
```bash
|
||||
# Create a release
|
||||
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\n- Bug fix B",
|
||||
"draft": false,
|
||||
"prerelease": false,
|
||||
"generate_release_notes": true
|
||||
}'
|
||||
|
||||
# List releases
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/releases \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for r in json.load(sys.stdin):
|
||||
tag = r.get('tag_name', 'no tag')
|
||||
print(f\" {tag:15} {r['name']:30} {'draft' if r['draft'] else 'published'}\")"
|
||||
|
||||
# Upload a release asset (binary file)
|
||||
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
|
||||
```
|
||||
|
||||
## 9. GitHub Actions Workflows
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
**With curl:**
|
||||
|
||||
```bash
|
||||
# List workflows
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/actions/workflows \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for w in json.load(sys.stdin)['workflows']:
|
||||
print(f\" {w['id']:10} {w['name']:30} {w['state']}\")"
|
||||
|
||||
# List recent runs
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/$OWNER/$REPO/actions/runs?per_page=10" \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for r in json.load(sys.stdin)['workflow_runs']:
|
||||
print(f\" Run {r['id']} {r['name']:30} {r['conclusion'] or r['status']}\")"
|
||||
|
||||
# Download failed run logs
|
||||
RUN_ID=<run_id>
|
||||
curl -s -L \
|
||||
-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
|
||||
|
||||
# Re-run a failed workflow
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/actions/runs/$RUN_ID/rerun
|
||||
|
||||
# Re-run only failed jobs
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/actions/runs/$RUN_ID/rerun-failed-jobs
|
||||
|
||||
# Trigger a workflow manually (workflow_dispatch)
|
||||
WORKFLOW_ID=<workflow_id_or_filename>
|
||||
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"}}'
|
||||
```
|
||||
|
||||
## 10. Gists
|
||||
|
||||
**With gh:**
|
||||
|
||||
```bash
|
||||
gh gist create script.py --public --desc "Useful script"
|
||||
gh gist list
|
||||
```
|
||||
|
||||
**With curl:**
|
||||
|
||||
```bash
|
||||
# Create a gist
|
||||
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\")"}
|
||||
}
|
||||
}'
|
||||
|
||||
# List your gists
|
||||
curl -s \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/gists \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
for g in json.load(sys.stdin):
|
||||
files = ', '.join(g['files'].keys())
|
||||
print(f\" {g['id']} {g['description'] or '(no desc)':40} {files}\")"
|
||||
```
|
||||
|
||||
## Quick Reference Table
|
||||
|
||||
| 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) |
|
||||
Reference in New Issue
Block a user