upload-gist
Upload markdown/text content to GitHub Gist via API
Files1
View on GitHubSKILL.mdupload-gist/SKILL.md
GitHubupload-gist
Upload markdown/text content to GitHub Gist using the raw GitHub API or the gh CLI, reading the token from ~/.hermes/.env.
Prerequisites
~/.hermes/.envmust containGITHUB_TOKEN=<token>withgistscopeghCLI must be installed (skill auto-installs if missing)
Workflow
Step 1: Read the token from .env
grep '^GITHUB_TOKEN=' ~/.hermes/.env | cut -d'=' -f2-
If GITHUB_TOKEN is missing from the file, ask the user for a GitHub token with gist scope: https://github.com/settings/tokens
Step 2: Install gh CLI if not present
command -v gh >/dev/null 2>&1 || {
(
type -p wget >/dev/null || (sudo apt update && sudo apt-get install wget -y)
sudo mkdir -p -m 755 /etc/apt/keyrings
wget -qO- https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg >/dev/null
sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list >/dev/null
sudo apt update
sudo apt install gh -y
)
}
Step 3: Test the token
Fine-grained tokens don't work with gh auth login --with-token. Instead, test via API:
GH_TOKEN="$(grep '^GITHUB_TOKEN=' ~/.hermes/.env | cut -d'=' -f2-)"
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" https://api.github.com/user \
-H "Authorization: Bearer $GH_TOKEN" \
-H "Accept: application/vnd.github.v3+json")
if [ "$RESPONSE" = "200" ]; then
echo "Token is valid"
else
echo "Token validation failed with HTTP $RESPONSE"
exit 1
fi
Step 4: Create gist (Always use the Raw API approach)
The raw API approach works reliably with both fine-grained and classic tokens. Avoid gh — it requires interactive auth or classic tokens that don't work well with --with-token.
CRITICAL: To avoid JSONDecodeError or API Error HTTP 422 caused by control characters or improper escaping in the content, do not rely on shell interpolation. Use a Python wrapper (via execute_code) to generate the JSON payload.
Recommended Implementation (Python):
- Read token from
~/.hermes/.env. - Use
json.dumps()to create a valid JSON string of the payload. - Use
requestsorcurlto POST tohttps://api.github.com/gists.
Example payload structure:
{
"description": "Description",
"public": true,
"files": {
"filename.md": { "content": "Actual content here" }
}
}
Step 5: Verify
GH_TOKEN="$(grep '^GITHUB_TOKEN=' ~/.hermes/.env | cut -d'=' -f2-)"
FILEID=$(basename "$(echo $GIST_URL | rev | cut -d/ -f1 | rev)")
curl -s -o /dev/null -w "HTTP %{http_code}" https://gist.github.com/$FILEID \
-H "Authorization: Bearer $GH_TOKEN"
Error Handling
| Error | Fix |
|---|---|
gh: command not found |
Run the install step (Step 2) |
Token returns 404 on /user |
Token expired or invalid — ask for new token from https://github.com/settings/tokens |
GITHUB_TOKEN not found in .env |
Ask the user to add it to ~/.hermes/.env |
API Error HTTP 422 |
Content might contain unescaped characters — use Python json.dumps() for the payload |
Common Pitfalls
- Fine-grained tokens +
gh: Fine-grained tokens do NOT work withgh auth login --with-token. Use the raw API approach instead. - Control Characters: Raw text content often contains characters that break shell-interpolated JSON. Always use
json.dumps()in Python. GITHUB_TOKEN=contains trailing whitespace: Usecut -d'=' -f2-and quote the variable.- Python
requestsandcurlmay timeout in sandbox: Ifcurlfails, fall back to Pythonurlliborrequests.
Tips
- Always use the raw GitHub API (
/gists) instead ofgh gist create— it works with any token type and avoids auth complexity. - Minimum scope for gist upload:
gist - For private gists: change
'public': Trueto'public': False - After creating, always return the gist URL to the user.