# 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`

```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 <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

- [`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
