Skip to main content

Per-folder GitHub CLI workspace manager

Project description

GH Workspace Manager (ghw)

Per-folder GitHub CLI auth for multi-account workflows.

Why This Exists

You work with multiple GitHub accounts:

  • personal - Your personal account
  • work - Your work/employer account
  • client - Client projects

Example uses: personal + work + freelance clients, or personal + open source + consulting.

GitHub CLI stores auth globally in ~/.config/gh/. Every gh auth login overwrites the previous one. You're constantly switching and losing track.

Solution: Each folder uses mise to set GH_CONFIG_DIR, and ghw validates before running gh. You cd into a project, gh automatically uses the right account.

Install

Required: mise

ghw requires mise — it is the mechanism that sets GH_CONFIG_DIR per project folder. Without it, workspace switching doesn't happen. If you are not using mise, this tool is not for you.

curl https://mise.run | sh

Add to your shell rc (~/.bashrc or ~/.zshrc):

export MISE_TRUSTED_CONFIG_PATHS="$HOME/src"
eval "$(mise activate bash)"   # or: eval "$(mise activate zsh)"

Install ghw

uv tool install ghw      # recommended
pipx install ghw         # fallback if no uv
uvx ghw                  # zero-install try-it

Verify:

ghw --help

Setup

1. Create Workspaces

One per GitHub account you want to use:

ghw workspace init personal
ghw workspace init work
ghw workspace list

Configs live under ~/.config/gh-workspaces/<workspace>/.

2. Add .mise.toml to Each Project

Lazy way (with init-here):

cd ~/src/my-project

# If git remote exists, auto-detect from URL:
git remote add origin git@github.com:personal/myrepo.git
ghw init-here --auto

# Or specify workspace explicitly:
ghw init-here --workspace personal

Manual way:

Create ~/src/PROJECT/.mise.toml:

[env]
# Absolute path required — mise does not expand ~ or $HOME in env values.
# macOS: /Users/<you>/.config/gh-workspaces/personal
# Linux: /home/<you>/.config/gh-workspaces/personal
GH_CONFIG_DIR = "/home/<you>/.config/gh-workspaces/personal"

Replace personal with work or client as needed. Tip: ghw init-here --workspace personal emits the correct absolute path for your OS automatically.

3. Configure mise Auto-Trust

Add to your shell rc (~/.bashrc or ~/.zshrc) so all .mise.toml files under ~/src are trusted without manual mise trust:

# Must be exported BEFORE `mise activate` so activation picks it up.
export MISE_TRUSTED_CONFIG_PATHS="$HOME/src"
eval "$(mise activate bash)"   # or: eval "$(mise activate zsh)"

4. Add gh Wrapper Function

Add to the same shell rc:

gh() {
    if [[ -n "$GH_CONFIG_DIR" ]]; then
        echo "[ghw] Using gh via ghw wrapper (GH_CONFIG_DIR: $GH_CONFIG_DIR)"
        ghw gh "$@"
    else
        command gh "$@"
    fi
}

Reload your shell:

exec $SHELL

5. Authenticate Each Workspace

# In a project using personal workspace
cd ~/src/some-project
gh auth login    # Authenticates personal

# In a project using work workspace
cd ~/src/other-project
gh auth login    # Authenticates work (different from personal)

Folder names don't matter - the .mise.toml in each folder determines the workspace.

Daily Workflow

# In a project using personal workspace
cd ~/src/myapp
gh auth status
# [ghw] Using gh via ghw wrapper (GH_CONFIG_DIR: <your-home>/.config/gh-workspaces/personal)
# github.com
# ✓ Logged in to github.com account your-username (keyring)

# Switch to project using work workspace
cd ~/src/client-portal
gh auth status
# [ghw] Using gh via ghw wrapper (GH_CONFIG_DIR: <your-home>/.config/gh-workspaces/work)
# github.com
# ✓ Logged in to github.com account work-account (keyring)

# Outside any project
cd ~
gh auth status
# (no [ghw] message - regular gh)

Note: Folder names like myapp or client-portal are arbitrary. What matters is the .mise.toml inside each folder.

Safety: Strict Validation

The protection mechanism: If GH_CONFIG_DIR is set but workspace missing, ghw errors hard.

cd ~/src/myapp
export GH_CONFIG_DIR=<your-home>/.config/gh-workspaces/missing
gh auth status
# [ghw] Using gh via ghw wrapper (GH_CONFIG_DIR: <your-home>/.config/gh-workspaces/missing)
# ╭─────────────────────────────────── Error ────────────────────────────────────╮
# │ Workspace config missing at <your-home>/.config/gh-workspaces/missing. Run:  │
# │ ghw workspace init missing                                                   │
# ╰──────────────────────────────────────────────────────────────────────────────╯

No fallback. No silent default. You always know what's happening.

Commands

# Quick setup (lazy way)
ghw init-here --workspace personal    # Create .mise.toml for current project
# OR if git remote is set:
git remote add origin git@github.com:personal/repo.git
ghw init-here --auto            # Auto-detect from git remote

# Workspace management
ghw workspace list              # Show: client, personal, work, test
ghw workspace init new-client   # Create new workspace

# Run gh with validation
ghw gh auth status              # Explicit version
gh auth status                  # Via alias (same thing)

# Diagnostics
ghw doctor                      # Check setup and diagnose issues
ghw --version                   # Show version

How It Works

cd ~/src/myapp
↓
mise reads .mise.toml (contains GH_CONFIG_DIR=.../personal)
↓
exports GH_CONFIG_DIR=<your-home>/.config/gh-workspaces/personal
↓
gh auth status (alias)
↓
sees GH_CONFIG_DIR, calls ghw gh auth status
↓
ghw validates workspace exists
↓
runs the real gh (e.g. /opt/homebrew/bin/gh on macOS, /usr/bin/gh on Linux)

The folder name (myapp) is irrelevant. The .mise.toml inside determines the workspace.

File Locations

~/.config/gh-workspaces/          # Your workspaces
├── personal/
├── work/
└── ...

~/.bashrc or ~/.zshrc             # Your gh() wrapper function lives here

~/src/*/                          # Your projects with .mise.toml

Troubleshooting

Quick Diagnosis

Run ghw doctor to check your entire setup:

ghw doctor

This checks:

  • ghw is installed correctly
  • mise is installed and in shell
  • gh is installed
  • gh alias is loaded
  • Workspaces exist
  • GH_CONFIG_DIR detection

"Workspace config missing"

ghw workspace init <name>

"ghw command not found"

cd ~/src/gh-workspace-manager
uv tool install -e .

Alias not working

# Check if wrapper function loaded
type gh

# Should show "gh is a function", not the raw binary path
# (e.g. /opt/homebrew/bin/gh on macOS, /usr/bin/gh on Linux)

# Reload if needed
exec $SHELL

mise not setting GH_CONFIG_DIR

# Check mise is in shell
echo $SHELL
mise --version

# Check .mise.toml exists in project folder
cat ~/src/myapp/.mise.toml

# Force reload
cd . && cd ~/src/myapp
env | grep GH

Further Reading

Development

cd ~/src/gh-workspace-manager

# Run tests
uv run --group dev pytest tests/ -v

# Reinstall after changes
uv tool install -e .

# Test locally
ghw workspace list

Key Principles

  1. Per-folder only: .mise.toml in exact folder only, no inheritance
  2. Strict validation: Error if workspace missing, never fallback
  3. Reminder message: Always know when using ghw vs regular gh
  4. Cross-platform: pathlib, works on macOS/Linux/Windows

Questions

Why not just use GH_CONFIG_DIR manually? Easy to forget, easy to mess up. ghw adds validation and reminder.

Why mise instead of direnv? Faster (<1ms vs 20-50ms), cleaner config, unified with your other tools.

What if I delete a workspace folder? ghw will error immediately and tell you to recreate it.

Can I use this outside my src folder? Yes, anywhere. Just add .mise.toml with the workspace you want.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ghw-1.0.1.tar.gz (30.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ghw-1.0.1-py3-none-any.whl (11.5 kB view details)

Uploaded Python 3

File details

Details for the file ghw-1.0.1.tar.gz.

File metadata

  • Download URL: ghw-1.0.1.tar.gz
  • Upload date:
  • Size: 30.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for ghw-1.0.1.tar.gz
Algorithm Hash digest
SHA256 bcf12a6a1ae2ade4dc4e9fe67edf0da79dc0a2107f43cb15c11d8d2a558a1881
MD5 112eebefe729dd25849f8981c9a07a3a
BLAKE2b-256 4a8d40c6b3141dd082ea5a5a8a3cc0a5ea82905944a9237a1c19d7e4bf6fe661

See more details on using hashes here.

File details

Details for the file ghw-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: ghw-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 11.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for ghw-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c6e45b70c4d1dbc85b8742615ee02177bdb18ebfbfc14dfba02d3582dcbd3520
MD5 1f806355bdb5b10e912c522644963091
BLAKE2b-256 885d2893ebf1a072c3a2795dd29f27f30eb2e09a8298d8e84a6cbed854563076

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page