Back to list
jchidley

github-api

by jchidley

SDK-based GitHub repository manager - better than gh CLI for automation and LLM integration

0🍴 0📅 Jan 14, 2026

SKILL.md


name: github-api description: Octokit (official GitHub SDK) best practices, common operations, and code examples for GitHub API automation. Use when creating repos, updating files, managing issues/PRs, or any GitHub API interaction.

GitHub API / Octokit Skill

This skill provides guidance, patterns, and examples for using Octokit (the official GitHub SDK) to interact with the GitHub API.

When to Use This Skill

Use this skill when you need to:

  • Create, update, or manage GitHub repositories
  • Update files in repositories programmatically
  • Manage issues, pull requests, or discussions
  • Work with GitHub Actions, releases, or webhooks
  • Automate GitHub operations via the API
  • Implement GitHub integrations or bots

Authentication

const { Octokit } = require('@octokit/rest');

const octokit = new Octokit({
  auth: process.env.GITHUB_TOKEN
});

Personal Access Token Requirements

Generate at: https://github.com/settings/tokens

Minimum scopes needed:

  • repo - Full control of private repositories (includes public repos)
  • workflow - Update GitHub Action workflows (if needed)
  • admin:org - Manage organization settings (if working with orgs)

Token format: Starts with ghp_ (personal access token)

Checking Authentication

// Test if token is valid
const { data: user } = await octokit.users.getAuthenticated();
console.log(`Authenticated as: ${user.login}`);

Common Operations

1. Repository Operations

Create Repository

const { data: repo } = await octokit.repos.createForAuthenticatedUser({
  name: 'my-new-repo',
  description: 'Repository description',
  private: false,
  auto_init: true, // Initialize with README
  license_template: 'mit', // Optional: add license
});

console.log(`Created: ${repo.html_url}`);

Get Repository Info

const { data: repo } = await octokit.repos.get({
  owner: 'username',
  repo: 'repo-name'
});

console.log(`Stars: ${repo.stargazers_count}`);
console.log(`Forks: ${repo.forks_count}`);
console.log(`Default branch: ${repo.default_branch}`);

Update Repository Settings

await octokit.repos.update({
  owner: 'username',
  repo: 'repo-name',
  description: 'Updated description',
  homepage: 'https://example.com',
  has_issues: true,
  has_wiki: false,
  has_projects: true,
});

Delete Repository

await octokit.repos.delete({
  owner: 'username',
  repo: 'repo-name'
});

2. File Operations

Get File Contents

const { data: file } = await octokit.repos.getContent({
  owner: 'username',
  repo: 'repo-name',
  path: 'path/to/file.md',
  ref: 'main' // branch name
});

// Decode base64 content
const content = Buffer.from(file.content, 'base64').toString('utf8');
console.log(content);

Create or Update File

const { data } = await octokit.repos.createOrUpdateFileContents({
  owner: 'username',
  repo: 'repo-name',
  path: 'path/to/file.md',
  message: 'Commit message',
  content: Buffer.from('file content here').toString('base64'),
  sha: file.sha, // Required for updates, omit for new files
  branch: 'main'
});

console.log(`Commit: ${data.commit.html_url}`);

Important: Content must be base64 encoded!

Delete File

await octokit.repos.deleteFile({
  owner: 'username',
  repo: 'repo-name',
  path: 'path/to/file.md',
  message: 'Delete file',
  sha: file.sha, // Required - get from getContent first
  branch: 'main'
});

Get Multiple Files (Directory)

const { data: contents } = await octokit.repos.getContent({
  owner: 'username',
  repo: 'repo-name',
  path: 'src',
  ref: 'main'
});

// contents is an array if path is a directory
contents.forEach(item => {
  console.log(`${item.type}: ${item.path}`);
});

3. Branch Operations

List Branches

const { data: branches } = await octokit.repos.listBranches({
  owner: 'username',
  repo: 'repo-name'
});

branches.forEach(branch => {
  console.log(`${branch.name}: ${branch.commit.sha}`);
});

Create Branch

// First, get the SHA of the commit you want to branch from
const { data: ref } = await octokit.git.getRef({
  owner: 'username',
  repo: 'repo-name',
  ref: 'heads/main'
});

// Create new branch
await octokit.git.createRef({
  owner: 'username',
  repo: 'repo-name',
  ref: 'refs/heads/new-branch-name',
  sha: ref.object.sha
});

Delete Branch

await octokit.git.deleteRef({
  owner: 'username',
  repo: 'repo-name',
  ref: 'heads/branch-to-delete'
});

4. Issues and Pull Requests

Create Issue

const { data: issue } = await octokit.issues.create({
  owner: 'username',
  repo: 'repo-name',
  title: 'Issue title',
  body: 'Issue description',
  labels: ['bug', 'help wanted'],
  assignees: ['username']
});

console.log(`Issue #${issue.number}: ${issue.html_url}`);

Update Issue

await octokit.issues.update({
  owner: 'username',
  repo: 'repo-name',
  issue_number: 123,
  state: 'closed',
  body: 'Updated description'
});

List Issues

const { data: issues } = await octokit.issues.listForRepo({
  owner: 'username',
  repo: 'repo-name',
  state: 'open', // 'open', 'closed', 'all'
  labels: 'bug',
  sort: 'created',
  direction: 'desc',
  per_page: 100
});

Create Pull Request

const { data: pr } = await octokit.pulls.create({
  owner: 'username',
  repo: 'repo-name',
  title: 'PR title',
  body: 'PR description',
  head: 'feature-branch', // branch with changes
  base: 'main' // target branch
});

console.log(`PR #${pr.number}: ${pr.html_url}`);

5. Releases

Create Release

const { data: release } = await octokit.repos.createRelease({
  owner: 'username',
  repo: 'repo-name',
  tag_name: 'v1.0.0',
  name: 'Version 1.0.0',
  body: 'Release notes here',
  draft: false,
  prerelease: false
});

List Releases

const { data: releases } = await octokit.repos.listReleases({
  owner: 'username',
  repo: 'repo-name'
});

6. Organization Operations

List Repositories

const { data: repos } = await octokit.repos.listForOrg({
  org: 'organization-name',
  type: 'all', // 'all', 'public', 'private'
  sort: 'updated',
  per_page: 100
});

Create Organization Repository

const { data: repo } = await octokit.repos.createInOrg({
  org: 'organization-name',
  name: 'repo-name',
  description: 'Description',
  private: false
});

7. User Operations

Get Authenticated User

const { data: user } = await octokit.users.getAuthenticated();
console.log(`Username: ${user.login}`);
console.log(`Name: ${user.name}`);
console.log(`Email: ${user.email}`);

List User Repositories

const { data: repos } = await octokit.repos.listForAuthenticatedUser({
  visibility: 'all', // 'all', 'public', 'private'
  sort: 'updated',
  per_page: 100
});

Pagination

For endpoints that return many results, use pagination:

const iterator = octokit.paginate.iterator(octokit.repos.listForAuthenticatedUser, {
  per_page: 100
});

for await (const { data: repos } of iterator) {
  for (const repo of repos) {
    console.log(repo.name);
  }
}

Or get all at once:

const allRepos = await octokit.paginate(octokit.repos.listForAuthenticatedUser);
console.log(`Total repos: ${allRepos.length}`);

Error Handling

Basic Error Handling

try {
  const { data } = await octokit.repos.get({
    owner: 'username',
    repo: 'repo-name'
  });
} catch (error) {
  console.error('Error:', error.message);
  
  if (error.status === 404) {
    console.error('Repository not found');
  } else if (error.status === 401) {
    console.error('Authentication failed - check your token');
  } else if (error.status === 403) {
    console.error('Forbidden - insufficient permissions');
  }
  
  if (error.response) {
    console.error('Response:', error.response.data);
  }
}

Common Error Codes

  • 401 - Bad credentials / invalid token
  • 403 - Forbidden / rate limited / insufficient permissions
  • 404 - Not found
  • 422 - Validation failed / unprocessable entity
  • 500 - Server error

Rate Limiting

Check rate limit status:

const { data: rateLimit } = await octokit.rateLimit.get();
console.log(`Remaining: ${rateLimit.rate.remaining}/${rateLimit.rate.limit}`);
console.log(`Reset: ${new Date(rateLimit.rate.reset * 1000)}`);

Best Practices

1. Always Check Authentication First

async function verifyAuth(octokit) {
  try {
    const { data: user } = await octokit.users.getAuthenticated();
    console.log(`✅ Authenticated as: ${user.login}`);
    return true;
  } catch (error) {
    console.error('❌ Authentication failed:', error.message);
    return false;
  }
}

2. Get SHA Before Updating Files

async function updateFile(owner, repo, path, content, message) {
  // Get current file to get SHA
  const { data: currentFile } = await octokit.repos.getContent({
    owner,
    repo,
    path
  });
  
  // Update with SHA
  await octokit.repos.createOrUpdateFileContents({
    owner,
    repo,
    path,
    message,
    content: Buffer.from(content).toString('base64'),
    sha: currentFile.sha
  });
}

3. Use Environment Variables for Tokens

Never hardcode tokens!

// ✅ Good
const octokit = new Octokit({
  auth: process.env.GITHUB_TOKEN
});

// ❌ Bad
const octokit = new Octokit({
  auth: 'ghp_hardcoded_token_here' // NEVER DO THIS!
});

4. Handle Pagination for Large Result Sets

async function getAllRepos(octokit) {
  return await octokit.paginate(octokit.repos.listForAuthenticatedUser, {
    per_page: 100
  });
}

5. Batch Operations with Error Recovery

async function updateMultipleFiles(files) {
  const results = [];
  
  for (const file of files) {
    try {
      const result = await updateFile(file.path, file.content);
      results.push({ success: true, file: file.path, result });
    } catch (error) {
      results.push({ success: false, file: file.path, error: error.message });
    }
  }
  
  return results;
}

Complete Example Script

#!/usr/bin/env node

const { Octokit } = require('@octokit/rest');
const fs = require('fs');

// Initialize Octokit
const octokit = new Octokit({
  auth: process.env.GITHUB_TOKEN
});

const owner = 'username';
const repo = 'repo-name';
const filePath = 'README.md';

async function main() {
  try {
    // 1. Verify authentication
    const { data: user } = await octokit.users.getAuthenticated();
    console.log(`✅ Authenticated as: ${user.login}`);
    
    // 2. Get current file
    console.log(`\n📥 Getting ${filePath}...`);
    const { data: currentFile } = await octokit.repos.getContent({
      owner,
      repo,
      path: filePath
    });
    console.log(`Current SHA: ${currentFile.sha}`);
    
    // 3. Read new content from local file
    const newContent = fs.readFileSync('./new-readme.md', 'utf8');
    
    // 4. Update file
    console.log(`\n📤 Updating ${filePath}...`);
    const { data } = await octokit.repos.createOrUpdateFileContents({
      owner,
      repo,
      path: filePath,
      message: 'Update README with new content',
      content: Buffer.from(newContent).toString('base64'),
      sha: currentFile.sha
    });
    
    console.log(`\n✅ Successfully updated ${filePath}`);
    console.log(`Commit: ${data.commit.html_url}`);
    
  } catch (error) {
    console.error('\n❌ Error:', error.message);
    if (error.response) {
      console.error('Response:', error.response.data);
    }
    process.exit(1);
  }
}

main();

Installation

npm install @octokit/rest
# or
yarn add @octokit/rest

TypeScript Support

Octokit has full TypeScript support:

import { Octokit } from '@octokit/rest';

const octokit = new Octokit({
  auth: process.env.GITHUB_TOKEN
});

// Type-safe API calls
const { data: repo } = await octokit.repos.get({
  owner: 'username',
  repo: 'repo-name'
});

// repo is fully typed
console.log(repo.stargazers_count);

Common Patterns

Pattern: Update File in Repository

async function updateRepoFile(owner, repo, path, content, message) {
  const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
  
  // Get current file for SHA
  const { data: currentFile } = await octokit.repos.getContent({
    owner,
    repo,
    path
  });
  
  // Update
  const { data } = await octokit.repos.createOrUpdateFileContents({
    owner,
    repo,
    path,
    message,
    content: Buffer.from(content).toString('base64'),
    sha: currentFile.sha
  });
  
  return data.commit.html_url;
}

Pattern: Clone Repository Settings

async function cloneSettings(sourceRepo, targetRepo) {
  const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
  
  // Get source repo settings
  const { data: source } = await octokit.repos.get({
    owner: 'username',
    repo: sourceRepo
  });
  
  // Apply to target
  await octokit.repos.update({
    owner: 'username',
    repo: targetRepo,
    description: source.description,
    homepage: source.homepage,
    has_issues: source.has_issues,
    has_wiki: source.has_wiki,
    has_projects: source.has_projects
  });
}

Pattern: Bulk File Operations

async function updateMultipleFiles(owner, repo, files) {
  const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
  const results = [];
  
  for (const file of files) {
    try {
      const url = await updateRepoFile(
        owner,
        repo,
        file.path,
        file.content,
        file.message
      );
      results.push({ success: true, path: file.path, url });
    } catch (error) {
      results.push({ success: false, path: file.path, error: error.message });
    }
  }
  
  return results;
}

Troubleshooting

"Bad credentials" Error

  • Check that GITHUB_TOKEN is set: echo $GITHUB_TOKEN
  • Verify token starts with ghp_
  • Generate new token at: https://github.com/settings/tokens
  • Ensure token has correct scopes (usually repo)

"Not Found" Error (404)

  • Verify repository exists
  • Check spelling of owner/repo names
  • Ensure token has access to private repos (if applicable)
  • Check if repository is in an organization you don't have access to

"Validation Failed" Error (422)

  • For file updates: Ensure SHA is provided and correct
  • For branch creation: Check branch name doesn't already exist
  • For PRs: Verify head and base branches exist

Rate Limiting (403)

  • Check rate limit: octokit.rateLimit.get()
  • Authenticated requests: 5,000/hour
  • Unauthenticated: 60/hour
  • Wait for reset time or use multiple tokens

Resources

Score

Total Score

65/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon