Skip to content

feat: the gh account follows the directory outside interactive zsh too - #4

Merged
softspark merged 2 commits into
mainfrom
feat/gh-wrapper-on-path
Sep 3, 2026
Merged

softspark merged 2 commits into
mainfrom
feat/gh-wrapper-on-path

Conversation

@softspark

Copy link
Copy Markdown
Owner

The chpwd hook only ever reached an interactive zsh, so scripts, editors and agents got whatever account was last made active — global state every terminal shares. A gh wrapper on PATH now hands gh the bound account token for one process and switches nothing.

Wired onto PATH through both ~/.zshenv and ~/.zprofile: .zshenv is the only file a non-interactive zsh reads, and macOS rebuilds PATH in /etc/zprofile afterwards. doctor checks whether gh actually resolves to the wrapper.

Also fixes --sign writing gpg.format = ssh on git < 2.34, which failed every commit in the workspace, and defines the skip helper that was called in three places and defined in none.

This PR was opened through the wrapper, with the global active account left on the other one.

Binding the account on chpwd only ever worked in an interactive zsh. A script,
an editor or a coding agent got whatever account was last made active -- and
because that is global state shared by every terminal, switching accounts to
open a pull request in one window silently repointed the others. Observed, not
theorised: an agent working in one workspace had the account changed under it
by a terminal sitting in another.

~/.config/git/bin/gh decides at the point of use and switches nothing. gh is
handed the bound account's token through GH_TOKEN for the life of one process,
so nothing global changes and a shell that never sourced the plugin still gets
the right identity. gh auth is excluded: switch refuses to run while GH_TOKEN
is set, and status would report the token's account as the active one.

install wires the directory onto PATH through both ~/.zshenv and ~/.zprofile.
.zshenv is the only startup file a non-interactive zsh reads; macOS then
rebuilds PATH in /etc/zprofile, which would leave the wrapper behind the gh it
shadows. doctor checks what actually matters -- whether gh resolves to the
wrapper in this shell.

Resolution moved to lib/resolve.sh. Sourcing guard.sh runs git and exits on a
mismatch, which is right for a hook and useless to anything else.

Two defects surfaced on the way. --sign wrote gpg.format = ssh without checking
that git understands it: on 2.23 every commit in the workspace then died on
"bad config variable" pointing at a file the user never wrote, so add refuses
and doctor reports it. And `skip` was called in three places and defined in
none, which nothing had reached until the signing tests learned to take the
other path.
The lint job carried its own copy of the shellcheck arguments. Adding
lib/resolve.sh to package.json therefore left CI linting guard.sh without the
file it sources, and SC1091 turned a green change red. One list, in package.json.
tests/run.sh joins it, which CI was checking and npm was not.
@softspark
softspark merged commit f088ec1 into main Sep 3, 2026
7 checks passed
@softspark
softspark deleted the feat/gh-wrapper-on-path branch September 3, 2026 12:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant