@timbenniks/gitme
v0.1.4
Published
Multi-account GitHub CLI — manage SSH keys, git identities, and GitHub auth per repo
Maintainers
Readme
▄██████████▄
▄█ ◉ ◉ █▄ gitme
███ ▀▀ ███─────● multi-account github cli
█████████████████ ●
▀█████████████▀ ●
▀███▀ ▀███gitme
Multi-account GitHub CLI — manage SSH keys, git identities, and GitHub auth per repo so you never commit or push as the wrong person.
Meet Gigi the gitmeleon — your identity-switching mascot. Just like a chameleon adapts to its environment, gitme adapts your git identity to each repo automatically.
Why gitme?
If you have both a personal and work GitHub account, every git operation is a landmine. You might commit with the wrong email, push with the wrong SSH key, or clone into a directory configured for the wrong account.
gitme fixes this permanently. It configures git and SSH at the repo level — once. After that, every tool just works: VS Code, JetBrains, GitHub Desktop, terminal, Claude Code, CI scripts. No wrappers, no runtime dependency, no environment variables.
gitme is a setup tool, not a runtime wrapper. It configures two things that every git tool already respects:
- Local
.git/config— setsuser.nameanduser.emailper repo, so commits always use the right identity - SSH host aliases — rewrites remotes to use per-account SSH aliases (e.g.,
[email protected]:org/repo.git), so push/pull always use the right key
Set up once, then forget about it.
Install
npm install -g @timbenniks/gitmeRequires Node.js 18+. Only depends on git and ssh-keygen (universally available).
Tutorial: Getting Started
Step 1: First run
Run gitme with no arguments. Gigi greets you and scans your machine for existing git config, SSH keys, and gh CLI auth.
$ gitme
▄██████████▄
▄█ ◉ ◉ █▄ Hey! I'm Gigi the gitmeleon.
███ ▀▀ ███─────● I help you juggle GitHub identities.
█████████████████ ● Set up once, never think about it again.
▀█████████████▀ ●
▀███▀ ▀███
┌ █◉█─● gitme ──────────────────────────────────────
● Scanning your existing git setup...
◆ Found existing configuration:
Git user: Gigi Meleon <[email protected]>
SSH key: ~/.ssh/id_ed25519
gh CLI auth: gigimeleon
◇ Import this as your first profile?
│ > Yes, import as 'personal'
│ Yes, but let me customize it
│ No, start freshStep 2: Add a work account
After importing your personal profile, gitme asks if you want to add another account. Say yes and enter your work details.
◇ Set up another GitHub account?
│ Yes
◇ Profile name:
│ work
◇ GitHub username:
│ gigi-at-acme
◇ Full name for commits:
│ Gigi Meleon
◇ Email for commits:
│ [email protected]
◇ SSH key for this profile:
│ > Generate a new key
● Generating SSH key...
◆ Created: ~/.ssh/gitme_work
◆ ❐ Public key (copied to clipboard):
ssh-ed25519 AAAAC3Nz... [email protected]
→ Add this key to GitHub: https://github.com/settings/ssh/new
◇ Open browser? Yes
◇ Have you added the key to GitHub? Yes
● Testing ssh -T [email protected]...
◆ Authenticated as gigi-at-acmeStep 3: Map organizations
Tell gitme which GitHub orgs belong to which profile. This enables automatic profile detection when cloning.
◇ Map GitHub orgs to profiles?
│ Yes
◇ Map an org to 'personal':
│ gigimeleon
◇ Map an org to 'work':
│ acme-corpStep 4: Discover existing repos
gitme scans common directories to find and register your existing repos.
● Scanning for git repos...
◆ Found 7 git repos
◆ Discovered repos:
~/work/api-service acme-corp/api-service → work
~/work/frontend acme-corp/frontend → work
~/projects/my-site gigimeleon/my-site → personal
~/projects/dotfiles gigimeleon/dotfiles → personal
◇ Register these repos? Yes
◆ Registered 4 repos
└ Gigi says: you're all set! Happy committing.Step 5: Clone with the right identity
From now on, just use gitme clone instead of git clone. It auto-detects the profile.
$ gitme clone [email protected]:acme-corp/new-service.git
◆ Detected org: acme-corp → profile 'work'
● Cloning acme-corp/new-service...
◆ Cloned as 'work' ([email protected])
◆ Registered in repo registryStep 6: Verify your identity
Inside any managed repo, check who you are:
$ cd ~/work/api-service
$ gitme whoami
◆ Repository: api-service
● Profile: work
✉ Email: [email protected]
⚷ SSH key: ~/.ssh/gitme_work (✓ valid)
⤷ Remote: [email protected]:acme-corp/api-serviceNow every tool — VS Code, terminal, Claude Code, GitHub Desktop — uses the correct identity automatically. No wrappers. No thinking. It just works.
Features
Profiles
A profile is a named GitHub identity: username, name, email, SSH key, and optional API token.
$ gitme profiles
PROFILE USERNAME EMAIL REPOS DEFAULT
personal gigimeleon [email protected] 5 ✓
work gigi-at-acme [email protected] 8Full CRUD:
- Add:
gitme setup→ "Add a new profile" - Edit:
gitme setup→ "Edit an existing profile" (change any field, regenerate SSH key, update token) - Remove:
gitme setup→ "Remove a profile" (cleans up SSH config block and org mappings) - List:
gitme profiles
Smart clone
gitme clone auto-detects the right profile from the org, rewrites the remote, sets local git config, and registers the repo — all in one step.
$ gitme clone [email protected]:acme-corp/api-service.git
◆ Detected org: acme-corp → profile 'work'
● Cloning acme-corp/api-service...
◆ Cloned as 'work' ([email protected])
◆ Registered in repo registryIf the org isn't mapped, gitme asks which profile to use and offers to remember the mapping.
Repo registry
A central registry at ~/.gitme/repos.json — one source of truth for all managed repos. No dotfiles inside repos, no gitignore entries, no pollution.
$ gitme repos
PROFILE REPO PATH CLONED
work acme-corp/api-service ~/work/api-service 2 days ago
work acme-corp/frontend ~/work/frontend 1 day ago
personal gigimeleon/my-site ~/personal/my-site 2 weeks ago
3 repos across 2 profilesFull CRUD:
- Add:
gitme clone <url>orgitme init(bind existing repo) - Remove:
gitme repos remove(interactive picker, orgitme repos remove /path/to/repo) - List:
gitme repos(with--profile <name>filter,--jsonfor scripting) - Health check:
gitme repos --check(verify repos exist on disk) - Clean:
gitme repos --clean(remove stale entries)
Org mappings
Map GitHub organizations to profiles so gitme clone auto-detects which account to use.
$ gitme config org list
ORG PROFILE
acme-corp work
gigimeleon personalFull CRUD:
- Add:
gitme config org add <org> <profile> - Remove:
gitme config org remove <org> - List:
gitme config org list
Identity check
$ gitme whoami
◆ Repository: api-service
● Profile: work
✉ Email: [email protected]
⚷ SSH key: ~/.ssh/gitme_work (✓ valid)
⤷ Remote: [email protected]:acme-corp/api-service
◈ GitHub API: ✓ token configuredPull requests and issues
Manage PRs and issues with the correct GitHub account — no gh CLI required.
gitme pr list # list open PRs
gitme pr create # create a PR (interactive)
gitme pr view 42 # view PR details
gitme pr status # PRs you authored + review requests
gitme issue list # list open issues
gitme issue create # create an issue
gitme issue view 17 # view issue detailsAuthenticated via personal access tokens stored per profile. SSH-based operations (clone, push, pull) always work without a token.
Context-aware default
Running bare gitme adapts to where you are:
| Context | Behavior | | --------------------------- | ------------------------------------------ | | First run (no config) | Gigi greets you + interactive setup wizard | | Inside a registered repo | Dashboard with identity + branch info | | Inside an unregistered repo | Offers to adopt it with a profile | | Outside any repo | Hub menu with all actions |
Adopt existing repos
Enter any git repo that isn't managed by gitme yet, and run gitme or gitme init:
$ cd ~/random/some-project
$ gitme
▲ This repo isn't managed by gitme yet.
⊙ Detected remote: [email protected]:some-org/some-project.git
◇ Set it up now? Yes
◇ Which profile?
│ > work ([email protected])
◆ Rewriting origin to: [email protected]:some-org/some-project.git
◆ Set git user to: Gigi Meleon <[email protected]>
◆ Registered in repo registry.Command Reference
Core commands
| Command | Description |
| ------------------------- | -------------------------------------------------------- |
| gitme | Context-aware default (setup / dashboard / adopt / menu) |
| gitme setup | Interactive setup wizard (add, edit, remove profiles) |
| gitme clone <url> [dir] | Clone with the right identity |
| gitme init [profile] | Bind an existing repo to a profile |
| gitme whoami | Show current repo identity |
| gitme status | Git status with identity info |
Listing
| Command | Description |
| ------------------------------ | ---------------------------------- |
| gitme profiles | List all profiles with repo counts |
| gitme repos | List all registered repos |
| gitme repos --profile <name> | Filter repos by profile |
| gitme repos --check | Verify all repos exist on disk |
| gitme repos --clean | Remove stale entries |
| gitme repos --json | Machine-readable JSON output |
Managing repos
| Command | Description |
| --------------------------- | ------------------------------------------------- |
| gitme repos remove [path] | Unregister a repo (interactive picker if no path) |
Config
| Command | Description |
| -------------------------------------- | ----------------------------- |
| gitme config default <profile> | Set the default profile |
| gitme config org add <org> <profile> | Map a GitHub org to a profile |
| gitme config org remove <org> | Remove an org mapping |
| gitme config org list | List all org mappings |
| gitme config list | Show all settings |
GitHub (requires personal access token)
| Command | Description |
| --------------------------- | --------------------------------------- |
| gitme pr list | List open pull requests |
| gitme pr create | Create a pull request (interactive) |
| gitme pr view [number] | View pull request details |
| gitme pr status | Show PRs you authored + review requests |
| gitme issue list | List open issues |
| gitme issue create | Create an issue (interactive) |
| gitme issue view [number] | View issue details |
How it works under the hood
SSH host aliases
Each profile gets a unique SSH alias in ~/.ssh/config, managed by gitme with marker comments:
# gitme-begin:work
Host github.com-work
HostName github.com
User git
IdentityFile ~/.ssh/gitme_work
IdentitiesOnly yes
# gitme-end:workWhen a repo's remote is [email protected]:org/repo.git, SSH automatically uses the right key. No agent switching, no environment variables. Every tool that calls git push inherits this — VS Code, JetBrains, Claude Code, GitHub Desktop, CI scripts.
Profile resolution
When gitme needs to determine the active profile for the current directory:
- Registry lookup — resolve the repo root, look up the path in
~/.gitme/repos.json - Org mapping — read the remote URL, extract the org, match against configured mappings
- Default profile — fall back to the default profile
- Prompt — if running interactively, ask the user to pick
Config files
All config lives in ~/.gitme/ (permissions: 0700 directory, 0600 files):
~/.gitme/
├── config.json # profiles, default profile, org-to-profile mappings
└── repos.json # central repo registry (path → profile + metadata)Nothing is written inside your repos. Portable, backupable, scriptable.
config.json structure:
{
"version": 1,
"defaultProfile": "personal",
"profiles": {
"personal": {
"githubUsername": "gigimeleon",
"gitName": "Gigi Meleon",
"gitEmail": "[email protected]",
"sshKeyPath": "~/.ssh/gitme_personal",
"sshHost": "github.com-personal",
"githubToken": null
}
},
"orgMappings": {
"acme-corp": "work",
"gigimeleon": "personal"
}
}Tech stack
- TypeScript with strict mode
- Vite+ unified toolchain (tsdown build, Vitest tests, oxlint linting, oxfmt formatting)
- @clack/prompts for beautiful interactive CLI output
- picocolors for terminal colors
- commander for CLI argument parsing
- Zero runtime dependencies beyond
gitandssh-keygen
Development
git clone https://github.com/timbenniks/gitme.git
cd gitme
npm install
npm run dev # run from source (tsx)
npm run build # build to dist/ (vp pack)
npm test # run tests (vp test)
npm run check # format + lint + typecheck (vp check)License
MIT
