AgentSkillsCN

github-api

指导 LCDPossible 屏幕保护程序的实现。适用于: - 创建全新屏幕保护面板 - 添加动态视觉效果 - 实现游戏模拟(如吃豆人、小行星等) - 运用 Delta-Time 动画技术 - 制作迷幻场景效果(等离子、火焰、隧道等) - 绘制精灵图、几何图形,或实现逐像素特效

SKILL.md
--- frontmatter
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

Environment Variable (Recommended)

javascript
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

javascript
// 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

javascript
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

javascript
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

javascript
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

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

2. File Operations

Get File Contents

javascript
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

javascript
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

javascript
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)

javascript
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

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

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

Create Branch

javascript
// 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

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

4. Issues and Pull Requests

Create Issue

javascript
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

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

List Issues

javascript
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

javascript
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

javascript
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

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

6. Organization Operations

List Repositories

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

Create Organization Repository

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

7. User Operations

Get Authenticated User

javascript
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

javascript
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:

javascript
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:

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

Error Handling

Basic Error Handling

javascript
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:

javascript
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

javascript
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

javascript
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!

javascript
// ✅ 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

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

5. Batch Operations with Error Recovery

javascript
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

javascript
#!/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

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

TypeScript Support

Octokit has full TypeScript support:

typescript
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);

Useful Links

Common Patterns

Pattern: Update File in Repository

javascript
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

javascript
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

javascript
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