plugins/github-app/skills/github-app-token/SKILL.md
Manage GitHub App installation tokens in Claude Code sessions. Use when tokens expire, auth errors occur in long-running sessions, or when setting up GitHub App credentials for agent teams. <example>my github token expired</example> <example>refresh the github app token</example> <example>check token status</example> <example>set up github app authentication for this session</example>
npx skillsauth add nsheaps/ai-mktpl github-app-tokenInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill covers managing GitHub App installation tokens in Claude Code sessions, including setup, refresh, troubleshooting, and agent team distribution.
Session Start
│
├─ SessionStart Hook (github-token-init.sh)
│ ├─ Reads GITHUB_APP_ID, GITHUB_INSTALLATION_ID, GITHUB_APP_PRIVATE_KEY
│ ├─ Materializes PEM to $CLAUDE_PLUGIN_DATA/github-app.pem
│ ├─ Generates JWT from PEM key
│ ├─ Exchanges JWT for installation token (1 hour validity)
│ ├─ Writes token to $CLAUDE_PLUGIN_DATA/github-token
│ ├─ Creates runtime env file ($CLAUDE_PLUGIN_DATA/github-app-env)
│ ├─ Sources env file via CLAUDE_ENV_FILE
│ ├─ Configures git identity (bot user)
│ └─ Prints: app name, expiry time, env var names
│
└─ PreToolUse Hook (github-token-check.sh)
├─ Debounced: checks at most every 300 seconds (5 minutes)
├─ For gh/git commands: synchronous check
│ ├─ Valid + >45min: silent allow
│ ├─ Valid + <45min: allow + background refresh
│ └─ Expired: synchronous refresh, then allow
├─ For other tools: async background check
├─ Retries up to 3x with exponential backoff
└─ 5-minute cooldown after all retries fail
$CLAUDE_PLUGIN_DATA/github-token (permissions 600)https://github.com/settings/appsSet these three env vars before the session starts:
GITHUB_APP_IDGITHUB_INSTALLATION_IDGITHUB_APP_PRIVATE_KEY (PEM content, not a file path)Recommended: Use the 1pass plugin to inject from 1Password by adding the three vars to your agent's 1pass.secrets list in plugins.settings.yaml:
1pass:
secrets:
- envVar: GITHUB_APP_ID
reference: "op://vault/github-app--repo--my-repo/GITHUB_APP_ID"
- envVar: GITHUB_INSTALLATION_ID
reference: "op://vault/github-app--repo--my-repo/GITHUB_INSTALLATION_ID"
- envVar: GITHUB_APP_PRIVATE_KEY
reference: "op://vault/github-app--repo--my-repo/GITHUB_APP_PRIVATE_KEY"
github-app:
enabled: true
autoGitConfig: true
Any other mechanism that exports these vars into the session env works (direct shell export, .env file sourced before launch, etc.).
The plugin materializes the PEM to $CLAUDE_PLUGIN_DATA/github-app.pem on every session start. No pre-existing PEM file is needed.
Run the token status script directly:
$CLAUDE_PLUGIN_ROOT/bin/token-status.sh
Or check the metadata file (length-only — never print the raw token):
jq '.expires_at' "$CLAUDE_PLUGIN_DATA/github-token.meta"
$CLAUDE_PLUGIN_ROOT/bin/token-check.sh --sync
Exit codes: 0 = valid/refreshed, 1 = failed after retries, 2 = not configured, 3 = cooldown.
Step 1 — Verify env vars (length-only, never print values)
for v in GITHUB_APP_ID GITHUB_INSTALLATION_ID GITHUB_APP_PRIVATE_KEY; do
val="${!v:-}"
[[ -n "$val" ]] && echo "$v is set (${#val} chars)" || echo "$v is NOT set"
done
Step 2 — Run generate-token.sh
The PEM is already at $CLAUDE_PLUGIN_DATA/github-app.pem (materialized by SessionStart).
$CLAUDE_PLUGIN_ROOT/bin/generate-token.sh \
"$GITHUB_APP_ID" \
"$CLAUDE_PLUGIN_DATA/github-app.pem" \
"$GITHUB_INSTALLATION_ID" \
"$CLAUDE_PLUGIN_DATA/github-token"
Step 3 — Verify
GH_TOKEN=$(cat "$CLAUDE_PLUGIN_DATA/github-token") gh api /user --jq '.login'
# Expected: <app-slug>[bot]
| Symptom | Likely cause |
| ------------------------------------ | --------------------------------------------------------- |
| HTTP 401 during JWT exchange | PEM key mismatch or clock skew > 60s |
| HTTP 404 on /app/installations/… | Wrong GITHUB_INSTALLATION_ID |
| Failed to sign JWT | PEM content malformed or GITHUB_APP_PRIVATE_KEY missing |
| exit 2 from token-check.sh | Credential env vars missing |
| exit 3 from token-check.sh | 5-min cooldown — wait or clear .cooldown file |
The SessionStart hook configures git to use gh auth git-credential directly. The gitconfig entry written is:
[credential "https://github.com"]
helper =
helper = !gh auth git-credential
Missing env vars. Check lengths (never print values):
for v in GITHUB_APP_ID GITHUB_INSTALLATION_ID GITHUB_APP_PRIVATE_KEY; do
val="${!v:-}"; [[ -n "$val" ]] && echo "$v set (${#val} chars)" || echo "$v NOT SET"
done
Clear the cooldown:
rm "$CLAUDE_PLUGIN_DATA/github-token.cooldown"
v0.4.0 writes everything under $CLAUDE_PLUGIN_DATA/. Orphaned files at ~/.agents/<name>/.config/ can be removed:
rm -rf ~/.agents/<name>/.config/github-token* ~/.agents/<name>/.config/github-app-env \
~/.agents/<name>/.config/github-app.pem ~/.agents/<name>/.config/github-git-identity
github-app-session-env skill — manually reproduce the SessionStart env
wiring (PEM, runtime env file, CLAUDE_ENV_FILE, GH_CONFIG_DIR isolation)github-app-git-identity skill — manually configure the bot git identity
and the gh auth git-credential helpergithub-auth skill covers all auth methodstools
Manually reproduce what the github-app plugin's SessionStart hook does to make a GitHub App installation token usable in the current session — materialize the PEM, generate the token, isolate GH_CONFIG_DIR, write the runtime env file, and wire CLAUDE_ENV_FILE so every Bash call sees GH_TOKEN/GITHUB_TOKEN. Use when the hook did not run, the token is missing from the environment, or a shell/teammate needs the token wired up by hand. <example>GH_TOKEN isn't set even though github-app is configured</example> <example>the github-app SessionStart hook didn't run, set up the token manually</example> <example>wire the github app token into CLAUDE_ENV_FILE</example> <example>gh keeps falling back to the wrong account, isolate GH_CONFIG_DIR</example>
tools
Manually configure the GitHub App bot git identity the way the github-app plugin's SessionStart hook does — resolve the app slug and bot user ID, build the <slug>[bot] name and noreply email, set GIT_AUTHOR_*/GIT_COMMITTER_* env vars, and write an isolated GIT_CONFIG_GLOBAL with the gh auth git-credential helper. Use when commits are attributed to the wrong account, "Author identity unknown" appears, or git identity must be set up by hand. <example>my commits are showing up as the handler, not the bot</example> <example>git says Author identity unknown after the github-app hook ran</example> <example>configure the github app bot git identity manually</example> <example>set up the gh credential helper for git push</example>
tools
Manages spec files for requirements capture and validation
tools
# Bash Chaining Alternatives This skill teaches you how to work around the bash command chaining restriction enforced by this plugin. ## Why Chaining is Blocked The `bash-command-rejection` plugin blocks these operators: | Operator | Name | Why Blocked | | -------- | ---------- | ----------------------------------------------------------------------------------- | | `&&` | AND chain | Runs cmd2 only if cmd1 su