关于 accessing-github-repos
此技能用于在 git clone 受阻的容器化 AI 环境中访问 GitHub 仓库,改用 GitHub REST API 和原始文件 URL。可通过 PAT 访问私有仓库并执行写入操作。
主要功能
- 01通过原始 URL 或 API 获取文件
- 02列出仓库目录树并下载归档
- 03从环境变量和项目文件检测 PAT
- 04创建文件、分支、Issue 和 Pull Request
- 05使用 PAT 访问私有仓库
适用场景
- 01不使用 git clone 阅读公开仓库文件
- 02通过 PAT 访问私有仓库
- 03通过 API 更新仓库文件
- 04在受限环境中管理 Issue 或创建 Pull Request
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ oaustegard/ claude-skills --skill accessing-github-repos安装后在 Claude Code 中输入/accessing-github-repos确认已加载。
附带文件CHANGELOG.md
详细介绍
访问 GitHub 仓库
在容器化 AI 环境中,git clone 会被阻止(出口代理会拒绝 CONNECT 隧道),但仍可通过 GitHub REST API 和原始文件 URL 完整访问仓库。
快速开始
公开仓库(无需设置)
# Individual file via raw URL
curl -sL "https://raw.githubusercontent.com/OWNER/REPO/BRANCH/path/file"
# Directory tree via API
curl -sL "https://api.github.com/repos/OWNER/REPO/git/trees/BRANCH?recursive=1"
私有仓库或写入权限
需要 GitHub Personal Access Token(PAT)。请参阅下方的“设置”部分。
设置
凭据配置
此技能会自动从环境变量或项目文件中检测 PAT:
环境变量(按以下顺序检查):
GITHUB_PATGH_PATGITHUB_TOKENGH_TOKEN
项目文件(Claude.ai):
/mnt/project/.env/mnt/project/github.env
格式:
GITHUB_PAT=github_pat_11AAAAAA...
创建 GitHub PAT
- GitHub → Settings → Developer settings → Fine-grained tokens
- 创建令牌,并将其限定为所需的仓库
- 设置权限:
- Contents: Read - 用于访问私有仓库
- Contents: Write - 用于推送文件
- Issues: Write - 用于管理议题
- Pull requests: Write - 用于创建 PR
网络访问(Claude.ai Projects)
将以下地址添加到网络允许列表:
api.github.comraw.githubusercontent.com
不同认证级别的功能
| 功能 | 无 PAT(仅公开仓库) | PAT(读取) | PAT(写入) |
|---|---|---|---|
| 获取公开文件 | ✅ | ✅ | ✅ |
| 获取私有文件 | ❌ | ✅ | ✅ |
| 下载 tarball | ✅ 公开 | ✅ | ✅ |
| 创建/更新文件 | ❌ | ❌ | ✅ |
| 创建分支 | ❌ | ❌ | ✅ |
| 管理议题 | ❌ | ❌ | ✅ |
| 创建 PR | ❌ | ❌ | ✅ |
Python 辅助函数
凭据检测
import os
def get_github_auth():
"""Returns (token, source) or (None, None)"""
# Check environment variables
for var in ['GITHUB_PAT', 'GH_PAT', 'GITHUB_TOKEN', 'GH_TOKEN']:
if token := os.environ.get(var):
return token, var
# Check project .env files
env_paths = ['/mnt/project/.env', '/mnt/project/github.env']
for path in env_paths:
try:
with open(path) as f:
for line in f:
if '=' in line and not line.startswith('#'):
key, val = line.strip().split('=', 1)
if key in ['GITHUB_PAT', 'GH_PAT', 'GITHUB_TOKEN']:
return val.strip(), f'{path}:{key}'
except FileNotFoundError:
continue
return None, None
获取单个文件
import base64
import urllib.request
import json
def fetch_file(owner: str, repo: str, path: str, ref: str = 'main', token: str = None) -> str:
"""Fetch single file. Uses API if token provided, raw URL otherwise."""
if token:
# Use API (works for private repos)
url = f'https://api.github.com/repos/{owner}/{repo}/contents/{path}?ref={ref}'
req = urllib.request.Request(url, headers={
'Authorization': f'Bearer {token}',
'Accept': 'application/vnd.github+json'
})
with urllib.request.urlopen(req) as resp:
data = json.load(resp)
return base64.b64decode(data['content']).decode()
else:
# Use raw URL (public repos only)
url = f'https://raw.githubusercontent.com/{owner}/{repo}/{ref}/{path}'
with urllib.request.urlopen(url) as resp:
return resp.read().decode()
获取仓库 Tarball
def fetch_repo_tarball(owner: str, repo: str, ref: str = 'main', token: str = None) -> bytes:
"""Download full repo as tarball. Requires token for private repos."""
url = f'https://api.github.com/repos/{owner}/{repo}/tarball/{ref}'
headers = {'Accept': 'application/vnd.github+json'}
if token:
headers['Authorization'] = f'Bearer {token}'
req = urllib.request.Request(url, headers=headers)
with urllib.request.urlopen(req) as resp:
return resp.read()
# Usage:
tarball = fetch_repo_tarball('owner', 'repo', 'main', token)
with open('/tmp/repo.tar.gz', 'wb') as f:
f.write(tarball)
# Extract: tar -xzf /tmp/repo.tar.gz
创建或更新文件
def push_file(owner: str, repo: str, path: str, content: str,
message: str, token: str, sha: str = None) -> dict:
"""Create/update file via API. Returns commit info.
Args:
sha: Required when updating existing file (get via contents API)
"""
url = f'https://api.github.com/repos/{owner}/{repo}/contents/{path}'
payload = {
'message': message,
'content': base64.b64encode(content.encode()).decode()
}
if sha: # Update existing file
payload['sha'] = sha
req = urllib.request.Request(url,
data=json.dumps(payload).encode(),
headers={
'Authorization': f'Bearer {token}',
'Accept': 'application/vnd.github+json',
'Content-Type': 'application/json'
},
method='PUT')
with urllib.request.urlopen(req) as resp:
return json.load(resp)
获取文件 SHA(用于更新)
def get_file_sha(owner: str, repo: str, path: str, token: str, ref: str = 'main') -> str:
"""Get file SHA needed for updates."""
url = f'https://api.github.com/repos/{owner}/{repo}/contents/{path}?ref={ref}'
req = urllib.request.Request(url, headers={
'Authorization': f'Bearer {token}',
'Accept': 'application/vnd.github+json'
})
with urllib.request.urlopen(req) as resp:
data = json.load(resp)
return data['sha']
Bash 示例
获取公开文件
curl -sL "https://raw.githubusercontent.com/owner/repo/main/path/file.py"
获取私有文件(使用 PAT)
curl -H "Authorization: Bearer $GITHUB_PAT" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/owner/repo/contents/path/file.py" | \
python3 -c "import sys,json,base64; print(base64.b64decode(json.load(sys.stdin)['content']).decode())"
下载仓库 Tarball
# Public repo
curl -sL "https://api.github.com/repos/owner/repo/tarball/main" -o repo.tar.gz
# Private repo
curl -sL -H "Authorization: Bearer $GITHUB_PAT" \
"https://api.github.com/repos/owner/repo/tarball/main" -o repo.tar.gz
tar -xzf repo.tar.gz
创建/更新文件
# Encode content
CONTENT=$(cat file.txt | base64 -w0)
# Push (new file)
curl -X PUT \
-H "Authorization: Bearer $GITHUB_PAT" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/owner/repo/contents/path/file.txt" \
-d "{\"message\":\"Add file\",\"content\":\"$CONTENT\"}"
# Push (update existing - need SHA first)
SHA=$(curl -s -H "Authorization: Bearer $GITHUB_PAT" \
"https://api.github.com/repos/owner/repo/contents/path/file.txt" | \
python3 -c "import sys,json; print(json.load(sys.stdin)['sha'])")
curl -X PUT \
-H "Authorization: Bearer $GITHUB_PAT" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/owner/repo/contents/path/file.txt" \
-d "{\"message\":\"Update file\",\"content\":\"$CONTENT\",\"sha\":\"$SHA\"}"
列出目录树
curl -sL "https://api.github.com/repos/owner/repo/git/trees/main?recursive=1" | \
python3 -c "import json, sys; [print(f['path']) for f in json.load(sys.stdin)['tree'] if f['type']=='blob']"
为什么 git clone 无法使用
容器的出口代理会阻止 git 协议操作:
- HTTPS 克隆:代理在 CONNECT 隧道上返回 401
- SSH:容器中没有 ssh 二进制文件
- git:// 协议:DNS 解析受阻
GitHub REST API 使用标准 HTTPS,可正常通过代理路由。
请勿
- 绝不要尝试
git clone(浪费时间,而且总会失败) - 绝不要建议需要使用 git 协议的变通方法
- 绝不要尝试使用不同的 git 标志或 SSH URL 重试
- 绝不要建议使用 git submodules、git archive 或其他 git 协议操作
速率限制
- 已认证:每小时 5,000 次请求
- 未认证:每小时 60 次请求
如果使用量较大,请务必提供 PAT。
此技能不适用的情况
- 原生开发环境:可直接访问 git,请使用标准 git 命令
- 本地计算机:
git clone可正常使用 - 配置了 MCP GitHub 服务的环境:请改用 MCP 工具
评论
支持 Markdown