Directory-aware git/gh CLI identity guard: prevent account mix-ups across personal and work GitHub profiles
Project description
gospelo-identity — Directory-Aware git/gh CLI Identity Guard
A small CLI that prevents git / gh account mix-ups when you maintain multiple GitHub profiles (personal OSS, employer, client). It maps the current working directory to an expected profile and verifies that local git config and the active gh CLI account match — and switches them if they do not.
Why gospelo-identity?
If you contribute to OSS as you@example.com from ~/projects/oss/** and to your employer as you@company.com from ~/projects/work/**, a single forgotten gh auth switch can leak the wrong commit author or release into the wrong organisation. gospelo-identity:
- Declares intent up front in
~/.config/gospelo-identity/config.yml— one entry per profile, with directory globs - Detects mismatches before you commit or release (
gospelo-identity check) - Switches both at once — local
git config user.name/user.emailandgh auth switch -u <account>(gospelo-identity switch <profile>) - No fallbacks: if there is no config file or the directory does not match any profile, you get a clear error — not a silent default
How it works
The current working directory resolves to a profile (via path globs in your config). From that one decision, three operations act on your git and gh identity: check reads and compares, switch applies, and the optional guard shim blocks wrong-identity writes.
flowchart TB
CWD["current directory"]
Config[("config.yml<br/>profiles + path globs")]
subgraph Core["gospelo-identity"]
Matcher["resolve profile<br/>(dir → profile)"]
Check["check<br/>compare expected vs actual"]
Switch["switch<br/>apply git + gh"]
Guard["guard (PATH shim)<br/>block wrong-identity writes"]
end
subgraph Ext["external CLIs"]
Git["git config"]
Gh["gh CLI"]
end
CWD --> Matcher
Config -.->|read| Matcher
Matcher --> Check
Matcher --> Switch
Matcher --> Guard
Check -.->|read| Git
Check -.->|read| Gh
Switch -->|write| Git
Switch -->|write| Gh
Guard -->|gated write| Git
Guard -->|gated write| Gh
classDef node fill:#FFFFFF,stroke:#666666,stroke-width:1.5px,color:#2C2C2C
class CWD,Config,Matcher,Check,Switch,Guard,Git,Gh node
style Core fill:#F0FDFA,stroke:#0D9488,color:#2C2C2C
style Ext fill:#F8FAFC,stroke:#94A3B8,color:#2C2C2C
linkStyle 0,2,3,4,7,8,9,10 stroke:#0D9488,stroke-width:2px
linkStyle 1,5,6 stroke:#9CA3AF,stroke-width:1.5px,stroke-dasharray:4 4
Dashed arrows are read-only (config load, check's comparison); solid arrows write or gate writes.
Installation
pip install gospelo-identity
Requires Python 3.11+. The git and gh CLI binaries must be available on PATH.
Quick Start
# 1. Create the config interactively
gospelo-identity init
# 2. Verify the current directory matches the expected profile
gospelo-identity check
# 3. Switch git + gh to the profile that matches the current directory
gospelo-identity switch oss
# 4. Show profiles
gospelo-identity list
Optional: surface the active profile in your shell prompt:
PS1='$(gospelo-identity prompt --format=ps1 --show-mismatch) \w \$ '
CLI Commands
| Command | Description |
|---|---|
init |
Interactively scaffold ~/.config/gospelo-identity/config.yml |
list |
List registered profiles in a table |
detect |
Print the profile name matched by the current directory |
check |
Compare expected vs actual git config and gh CLI account |
switch <profile> |
Apply git config + gh auth switch for the profile |
prompt |
Shell-prompt helper (--format=ps1 / plain / color) |
install-guard / uninstall-guard |
Shadow gh/git on PATH to block wrong-identity writes |
install-commit-hook / uninstall-commit-hook |
Global commit-msg hook that strips Co-Authored-By |
See the CLI Reference for full options and exit codes.
Configuration
Example configs are available in examples/:
config.yml— basic 2-profile setup with commentsconfig.minimal.yml— 1-profile minimal exampleconfig.advanced.yml— 3+ profile setup for freelancers / multi-client work
To create your config:
# Interactive setup
gospelo-identity init
# Copy bundled template + open in $EDITOR (default: vi)
gospelo-identity init --from-template
# Print bundled template to stdout (for piping)
gospelo-identity init --show-example > ~/.config/gospelo-identity/config.yml
~/.config/gospelo-identity/config.yml:
version: "1"
profiles:
oss:
description: "Personal OSS work"
git:
user.name: your-oss-login
user.email: you@example.com
gh:
account: your-oss-login
paths:
- ~/projects/gospelo-dev/**
- ~/projects/personal/**
work:
description: "Company work"
git:
user.name: your-oss-login
user.email: you@company.com
gh:
account: your-work-login
paths:
- ~/projects/work/**
# Used only when no profile path matches the current directory (optional)
default_profile: oss
paths are glob patterns. ~ is expanded, and ** matches any number of intermediate directories. The longest matching prefix wins when multiple profiles match.
Enforcement (optional)
check / switch are advisory. To actively block wrong-identity writes — useful during automation or autonomous agent runs — install the guard:
# Shadow `gh` (add `git` with --tools gh,git) with a PATH shim that runs the
# identity check before every write (gh release/pr/repo create, git push, ...).
gospelo-identity install-guard --tools gh
export PATH="$HOME/.gospelo-identity/bin:$PATH" # add to ~/.zshrc / ~/.bashrc
Now a gh release create / git push under a matched profile with the wrong active account is blocked (exit 1, real binary never runs); read-only commands pass through untouched. Outside any profile, or with no config, the real command always runs — the guard never breaks unrelated work. Per-write status is printed to stderr (silence with GOSPELO_IDENTITY_QUIET=1; bypass once with GOSPELO_IDENTITY_SKIP=1).
A PATH shim only catches name-based calls;
/usr/bin/git pushbypasses it. It stops accidental wrong-identity writes, not an adversarial process — layer an OS sandbox for that.
Separately, strip Co-Authored-By trailers from every commit message via a global commit-msg hook (it chains to your existing repo hooks):
gospelo-identity install-commit-hook
See the CLI Reference for details and uninstall-* commands.
Troubleshooting
switch says OK, but pushes/releases still go to the wrong account (stale keyring credential)
Symptom. gospelo-identity switch <profile> (or gh auth switch) reports success and gh auth status shows the expected account as active, yet git push / gh release / gh pr act as a different account.
Root cause. gh stores each account's token in the OS keyring. The credential saved under one account name can become stale or cross-wired to another account's token (e.g. after re-running gh auth login across multiple profiles). gh auth switch only flips the active label in ~/.config/gh/hosts.yml; it does not re-validate the token. So the active account says you-personal while the token still authenticates as you-work.
Why labels lie. The authoritative identity is gh api user (the account the token actually belongs to), not the active label printed by gh auth status.
Detection (built in). gospelo-identity check already resolves the real identity via gh api user, and gospelo-identity switch now verifies the switch took effect at the token level — if the active token authenticates as someone other than the target after switching, it reports NG ... keyring mismatch and exits non-zero instead of falsely claiming success.
Fix (re-login the affected account):
gh auth logout --hostname github.com --user <account>
gh auth login --hostname github.com # authenticate as <account> (browser)
gospelo-identity check # [gh CLI] should now be OK
gh api user --jq .login # confirms the real identity
no profile matched
The current directory is outside every profile's paths and no default_profile is set. Add a matching glob or a default_profile to your config.
Exit Codes
| Code | Meaning |
|---|---|
0 |
Success / match |
1 |
Expected condition not met (mismatch / no profile matched / not found) |
2 |
Tool error (config missing, invalid YAML, external tool failure) |
Documentation
Japanese documentation is available under docs/manual/ja/ (see README_ja.md).
Agent Skills
Auto-protective skills for AI coding agents that run gospelo-identity check
before any write-to-remote operation (push / PR / release / package
publish) and stop the operation on mismatch. See
skills/README.md for an overview.
- Claude Code skill — drop into
.claude/skills/gospelo-identity-check/ - GitHub Copilot skill — drop into
.github/copilot/skills/gospelo-identity-check/(path subject to change as Copilot's skill spec evolves)
License
MIT — free for commercial use. The config.yml you author is yours. See LICENSE.md for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gospelo_identity-0.1.0.tar.gz.
File metadata
- Download URL: gospelo_identity-0.1.0.tar.gz
- Upload date:
- Size: 38.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
42034c954504e5474a0bfb62b53593c8b21f9c2d56baf6f09472675163e26d25
|
|
| MD5 |
a13a0a0e95ccbdb47b77c95a74f46e86
|
|
| BLAKE2b-256 |
73303ff4d8ac5e08d81b6b33fab97ce31760d5e2a29bc1abbb4a10c21be6dc12
|
File details
Details for the file gospelo_identity-0.1.0-py3-none-any.whl.
File metadata
- Download URL: gospelo_identity-0.1.0-py3-none-any.whl
- Upload date:
- Size: 38.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b1bc49e942d8acbe297ee892f685884502545c5714f996dcdac7348b315a8568
|
|
| MD5 |
6278216bd4972af4f17dd276ede6b426
|
|
| BLAKE2b-256 |
b3f4693f52e93242ef01243304b7d619fcd25ca0e1a814775b0fd6fc17783aa4
|