envswitch

Switch shell environments with one word. No binaries, no hooks to approve, no .env files rotting in your repos.

Zsh Shell Developer tooling Open source
[ 01 — What it does ]

Type the environment name. That's the whole interface.

$ staging
  Loaded staging  (5 vars)

$ production
 ! Load production env? (y/n): y
  Loaded production  (12 vars)

$ unsetenv
  Cleared production environment

Env files live in ~/.envs/, one per environment, outside every repository. Each one automatically becomes a shell command — drop in production.env and production is now a command. No registry to update, no config to edit, nothing to register. The filename is the configuration.

[ 02 — The monorepo problem ]

One repo, many services, one very confused shell.

Here's the actual problem it was built for. In a monorepo, the environment you need depends on where you are in the tree and what branch you're on. Directory alone doesn't cut it — growth/feature-x and payments/new-api live in the same checkout and want completely different credentials.

So env files carry tags in their header, and the shell works it out on cd and on branch switch:

# ~/.envs/growth_staging.env
# dir: ~/monorepo
# branch: growth/*
API_KEY=sk-staging-123
$ cd ~/monorepo                    # on branch growth/feature-x
  Auto-loaded growth_staging  (dir:~/monorepo, branch:growth/feature-x)

$ production                       # context-aware: resolves to growth_production
  Context: growth
  Loaded growth_production  (5 vars)

$ git switch payments/new-api
  Auto-loaded payments_staging  (branch:payments/new-api)

$ cd ~/Desktop
  Left mapped directory, clearing payments_staging

That last line is the one that matters. Leaving the directory unloads the environment instead of leaving it lying around — so a terminal you opened three days ago and forgot about isn't still holding credentials for a repo you've since wandered out of.

[ 03 — The design rule ]

Only tag the stuff that's safe to get wrong.

Tagged environments auto-load. Production never gets tagged. You reach it on purpose — by name, or through context resolution — and you can mark it protected so loading it needs a typed confirmation.

That's the entire safety model, and it's deliberately not clever. Anything that can happen implicitly — on a cd, on a branch switch, without you asking for it — only ever reaches environments where being wrong is cheap. Anything expensive needs you to actually do something.

Every alternative design fails the same way. If production can auto-load, then some sequence of git switch and cd silently arms a shell with production credentials — and you will discover which sequence at the worst imaginable moment, probably while running something with the word "delete" in it.

[ 04 — Cost ]

It runs on every prompt, so it had better be free.

Auto-loading means hooking directory and branch changes, which means this code runs constantly. So resolution is tiered, and the common cases cost nothing:

No directory tags

Zero overhead. Nothing to match, nothing to check.

Directory match, no branch tags

Resolved without invoking git at all.

Branch disambiguation needed

Git gets read once (~1ms), then cached.

Shelling out to git rev-parse on every prompt is the classic way a tool like this becomes the reason your terminal feels sluggish and you can't work out why. Reading git only when the answer genuinely depends on the branch keeps it off the hot path for everyone not using that feature.

[ 05 — Against the alternatives ]

Yes, I know direnv exists.

envswitch direnv dotenv aws-vault
Scope Whole shell Per directory Per app (runtime) AWS creds only
staging → prod production Edit .envrc + reload Restart app aws-vault exec prod
Monorepo + branches Auto-load by dir + branch pattern One file per dir, no branch awareness N/A N/A
Files in repo No — everything in ~/.envs Yes — .envrc in each project Yes — .env in project root No
Install Source one file Binary + shell hook + allow per dir Language-specific package Binary + keychain setup
Prod protection Built-in confirmation None None Session-based
Config to add one None — add a file, get a command Edit + direnv allow Edit app config Edit ~/.aws/config

direnv is the closest thing and it's genuinely good — but it's directory-scoped and repo-resident by design. It answers "what does this folder need", and it drops a file in the folder to say so. In a monorepo those are precisely the two properties you don't want: one folder, many answers, and nothing secret going anywhere near a commit.

[ 06 — Built with ]
Zsh ~1,000 lines of shell No runtime dependencies No compiled binary