From github
GitHub operations using the octokit NPM package. Use when creating issues, managing pull requests, reading repository files, searching code, or interacting with GitHub Actions workflows.
How this skill is triggered — by the user, by Claude, or both
Slash command
/github:githubThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
```!
# === GitHub Skill Environment Check ===
BLOCKED=""
# Package manager detection
if [ -f "yarn.lock" ]; then
PKG_MGR="yarn"
PKG_ADD="yarn add"
PKG_GLOBAL="yarn global add"
elif [ -f "pnpm-lock.yaml" ]; then
PKG_MGR="pnpm"
PKG_ADD="pnpm add"
PKG_GLOBAL="pnpm add -g"
else
PKG_MGR="npm"
PKG_ADD="npm install"
PKG_GLOBAL="npm install -g"
fi
# 1. Token check (GITHUB_TOKEN only)
if [ -z "$GITHUB_TOKEN" ]; then
BLOCKED="yes"
echo "BLOCKED: GITHUB_TOKEN not set"
echo ""
echo "STOP. Do not attempt GitHub operations."
echo "Ask user to set up their token:"
echo " export GITHUB_TOKEN=ghp_..."
echo ""
echo "See advanced/token-setup.md for setup instructions."
else
TOKEN_PREFIX=$(echo "$GITHUB_TOKEN" | cut -c1-4)
echo "GITHUB_TOKEN set (${TOKEN_PREFIX}...)"
fi
# 2. Runtime check
if command -v tsx >/dev/null 2>&1; then
TSX_VER=$(tsx --version 2>&1 | head -1)
echo "tsx $TSX_VER"
else
BLOCKED="yes"
echo "BLOCKED: tsx not installed"
echo " Install with: $PKG_GLOBAL tsx"
fi
# 3. Package check
if [ -d "node_modules/octokit" ]; then
VER=$(node -e "console.log(require('./node_modules/octokit/package.json').version)" 2>/dev/null || echo "?")
echo "octokit@$VER"
else
BLOCKED="yes"
echo "BLOCKED: octokit not installed"
echo " Install with: $PKG_ADD octokit"
fi
# Final status
if [ -z "$BLOCKED" ]; then
echo ""
echo "Ready to execute GitHub operations."
fi
Uses octokit with tsx. Every script starts with:
import { Octokit } from "octokit";
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
Run scripts using heredoc syntax:
tsx << 'EOF'
import { Octokit } from "octokit";
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
// Your code here with top-level await
const { data: user } = await octokit.rest.users.getAuthenticated();
console.log("Logged in as:", user.login);
EOF
These complete examples work immediately after environment check passes.
tsx << 'EOF'
import { Octokit } from "octokit";
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
const { data: issue } = await octokit.rest.issues.create({
owner: "OWNER", // Replace with repo owner
repo: "REPO", // Replace with repo name
title: "Issue title",
body: "Issue description",
});
console.log("Created issue #" + issue.number + ":", issue.html_url);
EOF
tsx << 'EOF'
import { Octokit } from "octokit";
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
const { data } = await octokit.rest.repos.getContent({
owner: "OWNER", repo: "REPO", path: "README.md",
});
if ("content" in data) {
// CRITICAL: File content is base64 encoded - always decode!
const content = Buffer.from(data.content, "base64").toString("utf8");
console.log(content);
}
EOF
tsx << 'EOF'
import { Octokit } from "octokit";
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
const { data: branches } = await octokit.rest.repos.listBranches({
owner: "OWNER", repo: "REPO",
});
for (const b of branches) console.log(b.name);
EOF
tsx << 'EOF'
import { Octokit } from "octokit";
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
// Query syntax: qualifiers separated by spaces are AND-ed together
const { data } = await octokit.rest.search.issuesAndPullRequests({
q: "repo:OWNER/REPO is:issue state:open",
});
console.log("Found:", data.total_count);
for (const issue of data.items) console.log("#" + issue.number + ":", issue.title);
EOF
tsx << 'EOF'
import { Octokit } from "octokit";
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
const { data } = await octokit.rest.actions.listWorkflowRunsForRepo({
owner: "OWNER", repo: "REPO", per_page: 5,
});
console.log("Total runs:", data.total_count);
for (const run of data.workflow_runs) {
console.log(run.name + ":", run.conclusion ?? run.status);
}
EOF
Need >30 results? Most endpoints return max 30 items per page. See Pagination for fetching all results.
All API calls follow: octokit.rest.<category>.<method>({ owner, repo, ... })
| Parameter | Description | Example |
|---|---|---|
owner | Repository owner (user or org) | "octocat" |
repo | Repository name | "Hello-World" |
issue_number | Issue number (integer) | 123 |
pull_number | PR number (integer) | 45 |
path | File path in repo | "src/index.ts" |
ref | Branch/tag/commit reference | "main" or "refs/heads/feature" |
| Task | Reference |
|---|---|
| Create an issue | sdk/issues.md#create |
| Get issue details | sdk/issues.md#get |
| Comment on an issue | sdk/issues.md#comments |
| Update/close an issue | sdk/issues.md#update |
| Add labels | sdk/issues.md#labels |
| List issues | sdk/issues.md#list |
| Task | Reference |
|---|---|
| Create a PR | sdk/pull-requests.md#create |
| Get PR details | sdk/pull-requests.md#get |
| Get PR diff | sdk/pull-requests.md#diff |
| List changed files | sdk/pull-requests.md#files |
| Create a review | sdk/pull-requests.md#reviews |
| Merge a PR | sdk/pull-requests.md#merge |
| Update PR branch | sdk/pull-requests.md#update-branch |
| Task | Reference |
|---|---|
| Read a file | sdk/files.md#read |
| Create a file | sdk/files.md#create |
| Update a file | sdk/files.md#update |
| Delete a file | sdk/files.md#delete |
| List directory contents | sdk/files.md#list |
| Task | Reference |
|---|---|
| Create a branch | sdk/git.md#create-branch |
| List branches | sdk/git.md#list-branches |
| Get commit details | sdk/git.md#get-commit |
| List commits | sdk/git.md#list-commits |
| Task | Reference |
|---|---|
| Search code | sdk/search.md#code |
| Search issues/PRs | sdk/search.md#issues |
| Search repositories | sdk/search.md#repos |
| Task | Reference |
|---|---|
| List workflow runs | sdk/actions.md#runs |
| Get run details | sdk/actions.md#get-run |
| Get job logs | sdk/actions.md#logs |
| Trigger a workflow | sdk/actions.md#trigger |
| Task | Reference |
|---|---|
| Set up GITHUB_TOKEN | advanced/token-setup.md |
| Use GraphQL API | advanced/graphql.md |
| Diagnose issues | #troubleshooting |
| Common errors | #troubleshooting |
REST vs GraphQL Decision:
Need simple CRUD on one resource? -> Use REST (this skill's main docs)
Need nested/related data efficiently? -> Use GraphQL (advanced/graphql.md)
This requires multiple steps in sequence:
branch param)Warning:
getRef()usesheads/mainbutcreateRef()needsrefs/heads/main. See sdk/git.md for details.
File updates require the current SHA:
sha from response)sha)const { data: user } = await octokit.rest.users.getAuthenticated();
console.log("User:", user.login);
console.log("Name:", user.name ?? "(not set)");
const { data } = await octokit.rest.rateLimit.get();
console.log("Remaining:", data.rate.remaining, "/", data.rate.limit);
console.log("Resets:", new Date(data.rate.reset * 1000).toISOString());
Use paginate() to automatically fetch all pages:
const allIssues = await octokit.paginate(octokit.rest.issues.listForRepo, {
owner: "owner",
repo: "repo",
state: "all",
per_page: 100,
});
console.log("Total:", allIssues.length);
| Issue | What Happens | Fix |
|---|---|---|
| File content is base64 | Get garbled text | Buffer.from(data.content, 'base64').toString('utf8') |
| File updates need SHA | 409 Conflict error | Get SHA from getContent(), pass to update |
| PR mergeable is null | Can't check if mergeable | Wait 2 seconds or poll until non-null |
| Missing resource | 404 error thrown | Use try/catch, check error.status === 404 |
| Rate limit exceeded | 403 error | Check rateLimit.get(), wait for reset |
| Branch ref format | Wrong ref error | createRef() needs refs/heads/name, getRef() needs heads/name |
try {
const { data } = await octokit.rest.issues.get({ owner, repo, issue_number: 999 });
} catch (error: any) {
if (error.status === 404) {
console.log("Issue not found");
} else if (error.status === 403) {
console.log("Rate limited or no permission");
} else if (error.status === 401) {
console.log("Bad credentials - check GITHUB_TOKEN");
} else {
throw error;
}
}
tsx << 'EOF'
import { Octokit } from "octokit";
// Check 1: Token exists
if (!process.env.GITHUB_TOKEN) {
console.log("GITHUB_TOKEN not set");
process.exit(1);
}
console.log("Token:", process.env.GITHUB_TOKEN.substring(0, 4) + "...");
// Check 2: Authentication works
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
try {
const { data: user } = await octokit.rest.users.getAuthenticated();
console.log("Authenticated as:", user.login);
} catch (e: any) {
console.log("Auth failed:", e.status, e.message);
process.exit(1);
}
// Check 3: Rate limits
const { data: rate } = await octokit.rest.rateLimit.get();
console.log("Rate limit:", rate.rate.remaining + "/" + rate.rate.limit);
EOF
| Error | Likely Cause | Solution |
|---|---|---|
401 Bad credentials | Token invalid or expired | Generate new token at github.com/settings/tokens |
403 Resource not accessible | Token lacks required permissions | Add permissions or use fine-grained token with repo access |
404 Not Found | Repo doesn't exist OR token can't access private repo | Verify repo name; ensure token has repo scope |
409 Conflict (file update) | SHA mismatch - file changed | Re-read file to get current SHA, then update |
422 Unprocessable | Invalid parameters or state | Check API docs for required fields |
rate.remaining = 0 | Hit 5000 req/hour limit | Wait until rate.reset time or reduce requests |
Search queries use AND logic between qualifiers:
repo:owner/repo is:issue state:open = issues in repo AND openAlways decode base64:
// Wrong: console.log(data.content)
// Right:
const text = Buffer.from(data.content, "base64").toString("utf8");
Check ref format - different methods need different formats:
git.getRef() needs heads/main (no refs/ prefix)git.createRef() needs refs/heads/main (with prefix)Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub goodfoot-io/marketplace --plugin github