GitHub repository access in containerized environments using REST API and credential detection. Use when git clone fails, or when accessing private repos/writing files via API.
npx skills add https://github.com/oaustegard/claude-skills --skill accessing-github-repos
Git clone is blocked in containerized AI environments (egress proxy rejects CONNECT tunnel), but full repository access is available via GitHub REST API and raw file URLs.
# 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"
Requires GitHub Personal Access Token (PAT). See Setup section below.
The skill automatically detects PATs from environment variables or project files:
Environment Variables (checked in order):
GITHUB_PATGH_PATGITHUB_TOKENGH_TOKENProject Files (Claude.ai):
/mnt/project/.env/mnt/project/github.envFormat:
GITHUB_PAT=github_pat_11AAAAAA...
Add to network allowlist:
api.github.comraw.githubusercontent.com| Capability | No PAT (public only) | PAT (read) | PAT (write) |
|------------|---------------------|------------|-------------|
| Fetch public files | ✅ | ✅ | ✅ |
| Fetch private files | ❌ | ✅ | ✅ |
| Download tarball | ✅ public | ✅ | ✅ |
| Create/update files | ❌ | ❌ | ✅ |
| Create branches | ❌ | ❌ | ✅ |
| Manage issues | ❌ | ❌ | ✅ |
| Create PRs | ❌ | ❌ | ✅ |
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()
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)
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']
curl -sL "https://raw.githubusercontent.com/owner/repo/main/path/file.py"
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())"
# 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']"
The container's egress proxy blocks git protocol operations:
The GitHub REST API uses standard HTTPS and routes through the proxy normally.
git clone (wastes time, always fails)For heavy usage, always provide a PAT.
Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).
Automatically creates user-facing changelogs from git commits by analyzing commit history, categorizing changes, and transforming technical commits into clear, customer-friendly release notes. Turns hours of manual changelog writing into minutes of automated generation.
Use when implementation is complete, all tests pass, and you need to decide how to integrate the work - guides completion of development work by presenting structured options for merge, PR, or cleanup
Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).
React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
Next.js best practices - file conventions, RSC boundaries, data patterns, async APIs, metadata, error handling, route handlers, image/font optimization, bundling
Use when starting feature work that needs isolation from current workspace or before executing implementation plans - creates isolated git worktrees with smart directory selection and safety verification
Take oaustegard/accessing-github-repos from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.