ghs switches your active GitHub account (via gh), keeps your git commit
identity in sync, always shows which account is active, and lets you pin
directory trees to a profile so the right identity and the right push
token apply there — statelessly, in every terminal, no matter what's
globally active.
$ ghs list
-> personal octocat <octocat@users.noreply.github.com>
work octo-work <dev@company.com>
pin: /Users/you/work
$ ghs use work
Now using work (octo-work <dev@company.com>)
gh extension install OSSMafia/gh-ghs
gh ghs link # adds `ghs` and `git-ghs` symlinks -> bare `ghs` and `git ghs` workAll three spellings are the same binary: gh ghs status, git ghs status, ghs status.
ghs add personal # interactive: maps a gh account to a git identity
ghs add work # runs `gh auth login` if the account isn't logged in yet
ghs use personal # switch account + commit identity
ghs pin work ~/work # everything under ~/work commits AND pushes as work, forever
ghs status # what would happen if I commit/push right here?Most "account switcher" tools mutate one global active account — which means two terminals in different projects fight over it. ghs pins are stateless: a pinned tree gets
- its profile's
user.name/user.emailvia a native gitincludeIf "gitdir:..."include, and - its profile's push token via
credential.username, which gh's credential helper honors per-account.
So pushes from a pinned directory can't use the wrong account, even when the global active account is something else. Nothing switches; nothing races.
Honesty note: switching accounts never touches branches, remotes, or uncommitted work. The risk ghs manages is identity/auth mismatch — committing or pushing as the wrong account.
ghs init zsh --install # prompt segment: ⎇ octocat (red + "≠work" on mismatch)ghs prompt does no subprocess work (two file reads) and is safe to call on
every prompt render.
- Pinned dirs (automatic): wrong-token pushes are impossible — see above.
- Visible:
ghs status/ the prompt segment go red on mismatch. - Enforced (opt-in):
ghs guard installadds a pre-push hook to the current repo only;ghs init claude --installadds a Claude Code PreToolUse hook that blocks agent-initiatedgit push/git commiton mismatch;ghs init cursor --installwrites a Cursor rules file.
- Every command supports
--jsonwith stable schemas. - Exit codes:
0ok ·1mismatch/check failure ·2usage ·3environment. - Fully non-interactive when not on a TTY (
--yes,--username/--name/--email). ghs contextemits a snippet for CLAUDE.md describing the directory's account rules.
ghs doctor diagnoses the whole chain — gh version, account/profile mapping,
include ordering, whether git push actually authenticates through gh
(a fresh macOS setup usually uses osxkeychain instead: gh auth switch alone
would silently NOT change push auth), stale pins, broken symlinks. --fix
repairs what it safely can and asks before anything that changes auth.
ghs uninstall # reverts every change ghs made, in order, and says so
gh extension remove gh-ghsghs never edits your [user] block: it adds exactly one include.path line
to your global gitconfig, and all its config lives in ~/.config/ghs/.
Uninstall removes that one line — your original identity is live again
instantly — then unwinds credential wiring (only if ghs added it), guard
hooks, agent hooks, the zshrc snippet, and symlinks. gh accounts and tokens
are never touched. A verbatim backup of your original gitconfig is kept at
~/.config/ghs/backup/gitconfig.orig (use --keep-backup to retain it).
| Concern | Mechanism |
|---|---|
| Account/token switching | gh auth switch (gh ≥ 2.40 multi-account) |
| Commit identity | one include.path → ghs-owned ghs.gitconfig (shadows, never edits, your [user]) |
| Folder pinning | includeIf "gitdir:/path/" blocks, regenerated from ~/.config/ghs/config.toml |
| Pinned push auth | credential."https://github.com".username — gh's helper returns that user's token |
git ghs |
git's native git-<name> subcommand convention (a symlink) |
Nested pins: the deepest pin wins (blocks are ordered shallow→deep;
git's last include wins). Worktrees: a pin covers all worktrees of repos
under it; ghs status warns about the one trap (a worktree checked out
inside a pin whose main repo is outside).