# 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 ` 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` ```bash 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`: ```ini [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): ```ini [includeIf "gitdir:/Users/$USER/code/mygh/.path"] path = ~/.gitconfig-personal ``` `~/.git-credential-personal.sh` (chmod +x): ```bash #!/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`: ```zsh # === 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 ```bash 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 ```bash # 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 `~/.zshrc` — `source ~/.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 ` 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 - [`dual-network-setup.md`](./dual-network-setup.md) — corporate proxy + Gradle truststore (related: dual-environment routing) - [`local-development.md`](./local-development.md) — running services locally - [`agent-behavior-guidelines.md`](./agent-behavior-guidelines.md) — canonical agent rules