learning_ai_common_plat/AI.dev/SKILLS/gh-account-routing.md
saravanakumardb1 269b4d8784 docs(skills): add gh-account-routing skill
Documents the local zsh chpwd hook + git credential helper that
auto-routes the gh CLI to the personal saravanakumardb1 account
whenever cwd is inside ~/code/mygh/, falling back to the keyring
active account elsewhere.

Captures the setup so it can be reproduced on a new laptop or by
another contributor with similar two-account needs. This is machine-
local config (lives in ~/.zshrc and ~/.gitconfig-personal), so it
intentionally lives in SKILLS/ rather than per-repo AGENTS.md.
2026-05-24 15:15:54 -07:00

6.6 KiB

GH CLI Account Routing — Personal vs Work

Purpose: Document the local zsh setup that auto-routes the gh CLI (and any subprocess that calls gh) to the correct GitHub account based on the current working directory.

Tags

#beginner #setup #dotfiles #local-only

Why this exists

ByteLyst contributors often have two GitHub accounts authenticated via gh auth login:

  • A work account (e.g., corporate, used as default in gh's keyring)
  • A personal account (e.g., saravanakumardb1) that owns the ByteLyst repos under ~/code/mygh/

Without explicit routing, every gh call uses the keyring's "active account" — which leads to confusing 404s when you operate on a personal repo (gh api /repos/saravanakumardb1/foo fails because the active account doesn't have access).

The pattern below makes routing automatic and invisible: if you're inside ~/code/mygh/, gh uses the personal account; outside, it uses the default.

When to use

  • New laptop / fresh shell setup, after running gh auth login for both accounts.
  • Any time you find yourself running gh auth switch -u <user> repeatedly.
  • When repo-local scripts (husky hooks, scripts/*.sh, .windsurf/workflows/*) need to call gh and you want them to "just work" regardless of which terminal launched them.

How it works

gh checks the GH_TOKEN env var before consulting the keyring. So we use a chpwd zsh hook to export GH_TOKEN to the personal token whenever cwd is under ~/code/mygh/, and unset it otherwise. Subprocesses inherit the env var, so any script that shells out to gh from inside a workspace repo gets the right account automatically.

Setup steps

1. Authenticate both accounts with gh

gh auth login                                    # work account first
gh auth login                                    # then personal — gh keeps both in keyring
gh auth status                                   # verify both are listed

2. Add a credential helper for git HTTPS pushes (one-time)

This makes plain git push use the personal token when inside ~/code/mygh/, regardless of which gh account is "active". It's independent of the zsh hook below.

~/.gitconfig-personal:

[user]
    name = saravanakumardb1
    email = saravanakumardb1@users.noreply.github.com

[credential "https://github.com"]
    helper =
    helper = !/Users/$USER/.git-credential-personal.sh

~/.gitconfig (append, replacing $USER with your username):

[includeIf "gitdir:/Users/$USER/code/mygh/.path"]
    path = ~/.gitconfig-personal

~/.git-credential-personal.sh (chmod +x):

#!/bin/bash
# Always returns the personal-account GitHub token regardless of `gh` active account.
if [ "$1" = "get" ]; then
    TOKEN=$(/opt/homebrew/bin/gh auth token --user saravanakumardb1 2>/dev/null)
    if [ -n "$TOKEN" ]; then
        echo "protocol=https"
        echo "host=github.com"
        echo "username=saravanakumardb1"
        echo "password=$TOKEN"
    fi
fi

3. Add the zsh chpwd hook for the gh CLI

Append to ~/.zshrc:

# === BEGIN: bytelyst-personal-gh-token (auto-managed) ===
# Auto-route `gh` CLI + scripts to the personal account whenever cwd is
# inside ~/code/mygh/. Outside that tree, GH_TOKEN is unset so `gh` falls
# back to the keyring active account.
_bytelyst_gh_token=""
_set_personal_gh_token() {
  case "$PWD" in
    "$HOME/code/mygh"/*|"$HOME/code/mygh")
      if [ -z "$_bytelyst_gh_token" ]; then
        _bytelyst_gh_token="$(/opt/homebrew/bin/gh auth token --user saravanakumardb1 2>/dev/null)"
      fi
      [ -n "$_bytelyst_gh_token" ] && export GH_TOKEN="$_bytelyst_gh_token"
      ;;
    *)
      unset GH_TOKEN
      ;;
  esac
}
autoload -U add-zsh-hook
add-zsh-hook chpwd _set_personal_gh_token
_set_personal_gh_token   # run once at shell startup

# One-shot personal-account gh call from anywhere:
#   ghp repo list saravanakumardb1
#   ghp api /user
ghp() {
  GH_TOKEN="$(/opt/homebrew/bin/gh auth token --user saravanakumardb1 2>/dev/null)" \
    /opt/homebrew/bin/gh "$@"
}
# === END: bytelyst-personal-gh-token ===

Replace saravanakumardb1 with your own personal account username if different.

4. Reload and verify

source ~/.zshrc

# Should print: saravanakumardb1
cd ~/code/mygh/learning_ai_common_plat && gh api /user --jq .login

# Should print your work account login
cd /tmp && gh api /user --jq .login

# ghp works from anywhere
cd /tmp && ghp api /user --jq .login   # → saravanakumardb1

How agents and scripts benefit

Once installed, the following all "just work":

  • Manual gh commands (gh repo create, gh pr create, gh api, gh repo delete).
  • Husky hooks (.husky/pre-commit, .husky/pre-push) if any call gh.
  • Repo scripts (scripts/*.sh) that invoke gh.
  • Windsurf workflows (.windsurf/workflows/*.md) that include gh shell steps.
  • Agent automation — when Cascade / Claude Code / Codex shells out to gh from inside a workspace repo, it inherits the env and gets the right account.

Diagnostics

# What account will `gh` use right now?
gh api /user --jq .login

# Is GH_TOKEN currently set?
[ -n "$GH_TOKEN" ] && echo "YES (length: ${#GH_TOKEN})" || echo "NO"

# Force a fresh token fetch (e.g., after `gh auth refresh`):
unset _bytelyst_gh_token; _set_personal_gh_token

Common pitfalls

  • Already-running shells won't pick up changes to ~/.zshrcsource ~/.zshrc or open a new terminal.
  • gh auth status prints "GH_TOKEN env var is set" when you're inside ~/code/mygh/. That is expected — it confirms the hook fired.
  • Don't put the token in ~/.zshrc directly. Always fetch it via gh auth token --user <username> so it stays in the keyring.
  • CI environments typically set GITHUB_TOKEN, not GH_TOKEN. Using GH_TOKEN for this hook avoids interfering with CI.
  • Git HTTPS vs gh CLI are independent paths. The ~/.gitconfig-personal credential helper handles git push; the zsh hook handles gh. You need both for full coverage.

Why this is NOT in per-repo AGENTS.md

This is machine-local config, not repo convention. A second contributor on a fresh laptop or a CI runner doesn't have ~/.zshrc set up this way and shouldn't need to — they'd authenticate however they prefer. AGENTS.md is for code/repo conventions; this skill doc is the right home for environment setup.

See also