accessing-github-repos
Scannednpx machina-cli add skill oaustegard/claude-skills/accessing-github-repos --openclawAccessing GitHub Repositories
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.
Quick Start
Public Repos (no setup needed)
# 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"
Private Repos or Write Access
Requires GitHub Personal Access Token (PAT). See Setup section below.
Setup
Credential Configuration
The skill automatically detects PATs from environment variables or project files:
Environment Variables (checked in order):
GITHUB_PATGH_PATGITHUB_TOKENGH_TOKEN
Project Files (Claude.ai):
/mnt/project/.env/mnt/project/github.env
Format:
GITHUB_PAT=github_pat_11AAAAAA...
Creating a GitHub PAT
- GitHub → Settings → Developer settings → Fine-grained tokens
- Create token scoped to needed repositories
- Set permissions:
- Contents: Read - for private repo access
- Contents: Write - for pushing files
- Issues: Write - for issue management
- Pull requests: Write - for creating PRs
Network Access (Claude.ai Projects)
Add to network allowlist:
api.github.comraw.githubusercontent.com
Capabilities by Auth Level
| 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 | ❌ | ❌ | ✅ |
Python Helper Functions
Credential Detection
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
Fetch Single File
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()
Fetch Repository 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
Create or Update File
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)
Get File SHA (for updates)
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 Examples
Fetch Public File
curl -sL "https://raw.githubusercontent.com/owner/repo/main/path/file.py"
Fetch Private File (with 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())"
Download Repo 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
Create/Update File
# 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\"}"
List Directory Tree
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']"
Why git clone Doesn't Work
The container's egress proxy blocks git protocol operations:
- HTTPS clone: Proxy returns 401 on CONNECT tunnel
- SSH: No ssh binary in container
- git:// protocol: DNS resolution blocked
The GitHub REST API uses standard HTTPS and routes through the proxy normally.
Do Not
- Never attempt
git clone(wastes time, always fails) - Never suggest workarounds requiring git protocol
- Never retry with different git flags or SSH URLs
- Never recommend git submodules, git archive, or other git-protocol operations
Rate Limits
- Authenticated: 5,000 requests/hour
- Unauthenticated: 60 requests/hour
For heavy usage, always provide a PAT.
When This Skill Does Not Apply
- Native development environments: Have direct git access, use standard git commands
- Local machines: git clone works normally
- Environments with MCP GitHub server: Use MCP tools instead
Source
git clone https://github.com/oaustegard/claude-skills/blob/main/accessing-github-repos/SKILL.mdView on GitHub Overview
Git clone is blocked in containerized AI environments, but you can access GitHub repos through the REST API and raw file URLs. It supports public repos without setup and private repos or write access via a Personal Access Token (PAT), with credential detection from environment variables or project files. This enables reading files, listing repo trees, and performing write operations without cloning.
How This Skill Works
Public files are fetched via raw.githubusercontent.com, while directory structures can be retrieved with the GitHub API. For private repos or write access, the skill detects a PAT from env vars or project files and uses authorized API calls to read, write, create issues/PRs, or download tarballs as needed.
When to Use It
- Git clone is blocked in a containerized environment and you still need repository access
- You need to read files from a private repo without cloning it
- You want to list or fetch a full repository tree without cloning
- You need to write or update files in a private repo (or create issues/PRs) with proper permissions
- You require access to raw files or tarballs from public/private repos via API
Quick Start
- Step 1: Detect a GitHub PAT automatically from environment variables or project files
- Step 2: For public repos, fetch files with raw URLs or list the tree using the API; for private repos, use the API with your token
- Step 3: When writing is needed, ensure the PAT has Contents: Write and related scopes before making API calls
Best Practices
- Use a dedicated PAT with the minimum required scopes (e.g., Contents: Read for reads, Contents: Write for writes; add Issues/PRs only if needed)
- Prefer raw URLs for public files to avoid unnecessary API calls when possible
- Let the credential detector scan environment variables (GITHUB_PAT, GH_PAT, GITHUB_TOKEN, GH_TOKEN) and project files (/mnt/project/.env, /mnt/project/github.env)
- Add api.github.com and raw.githubusercontent.com to your network allowlist for reliable access
- Respect rate limits and paginate when listing large trees or histories; cache frequently accessed files when feasible
Example Use Cases
- Fetch a public config file via raw URL without any setup
- Read a private file from a repo using the API with a PAT
- List all files in a repo by requesting the recursive tree via the API
- Update a file in a private repo using the contents API with a PAT
- Create a pull request in a private repo using a PAT with appropriate scopes