plugins/github-app/skills/github-app-session-env/SKILL.md
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>
npx skillsauth add nsheaps/ai-mktpl github-app-session-envInstall 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 reproduces, by hand, the session wiring the github-app plugin's
SessionStart hook (hooks/scripts/github-token-init.sh) performs. The end state
is: a valid installation token on disk, an isolated gh config, a runtime env
file, and CLAUDE_ENV_FILE sourcing it so GH_TOKEN/GITHUB_TOKEN are present
in every subsequent Bash command.
For token generation/refresh/status mechanics see the github-app-token
skill. For bot git identity (commit attribution) see the
github-app-git-identity skill. This skill is the env/session plumbing that
sits between them.
GH_TOKEN / GITHUB_TOKEN are missing even though the three credential env
vars are present.gh is resolving the wrong account because GH_CONFIG_DIR is not isolated.All runtime files live under $CLAUDE_PLUGIN_DATA/ (per-agent isolated). Outside
a hook, CLAUDE_PLUGIN_DATA may be unset — fall back to the deterministic path:
CLAUDE_PLUGIN_DATA="${CLAUDE_PLUGIN_DATA:-$HOME/.claude/plugins/data/github-app-ai-mktpl}"
mkdir -p "$CLAUDE_PLUGIN_DATA"
| File | Purpose |
| ------------------- | ---------------------------------------------- |
| github-app.pem | Materialized private key (mode 600) |
| github-token | Raw installation token (mode 600) |
| github-token.meta | JSON: expires_at, app_slug, bot_id, … |
| github-app-env | Runtime env file sourced via CLAUDE_ENV_FILE |
| gh/ | Isolated GH_CONFIG_DIR |
Confirm the three credential env vars are present (length-only — never print secret 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
If any are missing, the token cannot be generated — inject them first (the 1pass plugin is the recommended mechanism; see the plugin README).
The plugin writes the key from the env var to disk on every session start (never cached across sessions):
printf '%s\n' "$GITHUB_APP_PRIVATE_KEY" > "$CLAUDE_PLUGIN_DATA/github-app.pem"
chmod 600 "$CLAUDE_PLUGIN_DATA/github-app.pem"
Prefer the shipped refresh script — it handles generation, retries, cooldown, and rewrites the runtime env file:
"$CLAUDE_PLUGIN_ROOT/bin/token-check.sh" --sync
Exit codes: 0 valid/refreshed · 1 failed after retries · 2 not configured
· 3 in cooldown. If CLAUDE_PLUGIN_ROOT is unset (outside a hook), call
bin/generate-token.sh directly as documented in the github-app-token skill.
On success github-token and github-token.meta exist under
$CLAUDE_PLUGIN_DATA/.
GH_CONFIG_DIRGive gh an empty per-agent config dir so it never falls back to the handler's
keyring when the App token expires:
export GH_CONFIG_DIR="$CLAUDE_PLUGIN_DATA/gh"
mkdir -p "$GH_CONFIG_DIR"
chmod 700 "$GH_CONFIG_DIR"
Reuse the plugin's write_runtime_env_file helper so the file matches exactly
what the hook produces (token vars + GITHUB_TOKEN_FILE, GITHUB_APP_ENV_FILE,
GH_CONFIG_DIR, and any already-exported GIT_* identity vars), with proper
shell-escaping:
export TOKEN_FILE="$CLAUDE_PLUGIN_DATA/github-token"
export ENV_RUNTIME_FILE="$CLAUDE_PLUGIN_DATA/github-app-env"
source "$CLAUDE_PLUGIN_ROOT/lib/env-file.sh"
write_runtime_env_file "$(cat "$TOKEN_FILE")"
write_runtime_env_file requires TOKEN_FILE and ENV_RUNTIME_FILE to be set
before it is called, and reads optional GITHUB_APP_ID, GITHUB_INSTALLATION_ID,
GH_CONFIG_DIR, and GIT_* vars from the environment.
CLAUDE_ENV_FILESo every later Bash command picks up the token (and re-reads it after a refresh):
if [[ -n "${CLAUDE_ENV_FILE:-}" ]]; then
echo "source \"$CLAUDE_PLUGIN_DATA/github-app-env\"" >> "$CLAUDE_ENV_FILE"
fi
Without CLAUDE_ENV_FILE (a plain shell), source the env file directly instead:
source "$CLAUDE_PLUGIN_DATA/github-app-env".
# Token present and reports the bot identity (never print the raw token)
GH_TOKEN="$(cat "$CLAUDE_PLUGIN_DATA/github-token")" gh api /user --jq '.login'
# Expected: <app-slug>[bot]
# Status JSON (expiry, minutes remaining, app/installation)
"$CLAUDE_PLUGIN_ROOT/bin/token-status.sh"
In a fresh Bash call (after Step 5), echo "${GH_TOKEN:+set} ${GITHUB_TOKEN:+set}"
should print set set.
| Symptom | Likely cause / fix |
| ---------------------------------------- | -------------------------------------------------------------------------------------------------- |
| GH_TOKEN still unset in new Bash calls | Step 5 skipped, or CLAUDE_ENV_FILE unset — source github-app-env directly |
| token-check.sh exits 2 | Credential env vars missing — re-check prerequisites |
| token-check.sh exits 3 | Cooldown after repeated failures — rm "$CLAUDE_PLUGIN_DATA/github-token.cooldown" then retry |
| gh uses the wrong account | GH_CONFIG_DIR not isolated/exported — redo Step 3 and re-source the env file |
| env file has empty GIT_* vars | Identity not configured yet — run the github-app-git-identity skill, which rewrites the env file |
github-app-token — token generation, refresh, status, and failure tablegithub-app-git-identity — bot commit attribution + credential helpergithub-auth — general GitHub authentication methodsbin/token-check.sh, bin/generate-token.sh, lib/env-file.sh,
hooks/scripts/github-token-init.shtools
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
tools
Reference material for Claude Code internals — the on-disk layout under ~/.claude and project-scope .claude, the plugin cache, session-env propagation, and the full hook lifecycle. Auto-recall when working on Claude-Code-related tasks: writing or debugging hooks, authoring plugins, inspecting session state, troubleshooting why an env var is or isn't visible to a Bash tool call, or when paths under ~/.claude or ~/.claude/plugins/ come up.