Run Claude Code on Vertex in one window, Bedrock in another, and a customer's proxy in a third — at the same time. Secrets live in 1Password and are resolved in memory, never written to disk.
eg use bedrock # switch THIS shell to the bedrock profile
eg use dev-vertex zscaler slack # …or stack several profiles into one env
eg exec vertex -- python app.py # run one command with a profile's env
eg render staging -o .env # write a project .env file from a profile
eg doctor bedrock # send a tiny real request to check it worksWhy? Environment variables are global to a shell, so juggling several
AI/cloud backends means export-ing, un-export-ing, and praying you didn't leave
AWS_PROFILE set in the wrong window. eg makes a backend a named profile you
project onto exactly where you want it — this shell (use), one subprocess (exec),
or a file (render) — and keeps each terminal independent.
Supported: macOS and Linux (Intel & Apple Silicon / arm64), shells zsh and bash. Windows works under WSL. See Requirements for optional dependencies.
# 1. install
brew install stjbrown/tap/eg
# 2. wire it into your shell (writes one line to ~/.zshrc, with a backup)
eg setup
# 3. create a profile from a recipe (see `eg recipes` for the list)
eg add claude-code vertex --param project=my-gcp-project
# 4. make it the default for new shells, then reload
eg default vertex
exec zshThat's it. Every new terminal now starts on vertex. Switch any single window
with eg use bedrock; check the current one with eg status.
Why the
eg setupstep? Changing your current shell's environment is only possible from a function sourced into that shell — a plain binary runs in a child process and can't reach back.eg setupaddseval "$(eg init zsh)"to your rc file, which installs that function. (Same mechanism asdirenv,nvm,zoxide.)
| OS | macOS or Linux, amd64 or arm64. Windows via WSL. |
| Shell | zsh or bash (interactive). fish isn't supported yet. |
| Secrets (optional) | 1Password CLI (op) — only needed for profiles that use op:// references. |
eg doctor (optional) |
aws CLI for Bedrock, gcloud for Vertex — only for live-testing those providers. |
eg itself has no runtime dependencies; the optional tools above are only used by
the features that need them.
| You want to… | Use | Effect |
|---|---|---|
| Switch the shell you're typing in | eg use <profile> |
Sets the vars in this shell (and child processes you launch from it). |
| Run one command on a profile | eg exec <profile> -- <cmd> |
Injects the vars into just that subprocess. Your shell is untouched; secrets never hit disk. |
Give an app a .env file |
eg render <profile> |
Writes a file the app reads (e.g. docker compose, a framework). |
use is for interactive work, exec is great for scripts/agents/CI, render
is for tools that only read files.
Profiles live in ~/.config/env-garden/ as .env.<name> files (honors
$XDG_CONFIG_HOME):
# desc: Claude on Bedrock (dev) ← shown in `eg list`
export CLAUDE_CODE_USE_BEDROCK=1
export AWS_REGION=us-east-1
export AWS_PROFILE=dev$VAR/${VAR}are expanded against earlier lines and the environment.- A value containing
op://Vault/Item/fieldis a 1Password reference — resolved on demand, never stored in plaintext (see Secrets).
Make them with eg add (recommended) or by hand:
eg recipes # list built-in (tool, provider) recipes
eg add claude-code bedrock --param aws_profile=dev # scaffold ~/.config/env-garden/.env.bedrock
eg edit bedrock # open it in $EDITOR
eg list # see all profilesBuilt-in recipes include claude-code/{bedrock,vertex,anthropic},
codex/openai-compat, and cursor/cli. (anthropic covers any
OpenAI/Anthropic-compatible proxy or gateway.)
Put the secret in 1Password and reference it from the profile:
export ANTHROPIC_AUTH_TOKEN=op://Private/my-proxy/credentialuse/execresolve refs in memory via theopCLI — never to disk.- Profiles with no
op://refs never invokeopat all. - If
opisn't installed or signed in, a profile that needs it fails cleanly rather than applying a half-set environment.
Requires the 1Password CLI (brew install --cask 1password-cli), ideally with the desktop-app integration enabled
(Settings → Developer → Integrate with 1Password CLI).
Keep each concern in its own small profile and stack them at use time, rather
than maintaining one big copy per combination. use, exec, export, and
render all accept several profiles and merge them left-to-right:
# one .env with your dev Vertex + a customer's Zscaler proxy + Slack creds
eg render dev-vertex zscaler slack -o .env --resolve
eg use dev-vertex zscaler slack # load the merged set into this shell
eg exec dev-vertex zscaler slack -- python agent.py
eval "$(eg export dev-vertex zscaler slack)"- Order matters: when two profiles set the same key, the later one wins.
Each override is reported to stderr (e.g.
eg: HTTPS_PROXY from "zscaler" overrides "dev-vertex") so a clobbered value is never silent. EG_ACTIVEbecomes the joined name (dev-vertex+zscaler+slack), andeg statusshows the full merged variable set.- With
eg exec, the--separator is required when passing more than one profile, so profile names and the command stay unambiguous.
Each terminal is independent — set a different profile in each:
# terminal 1
eg use vertex && claude # this window talks to Vertex
# terminal 2
eg use bedrock && claude # this one talks to Bedrock, at the same timeNew shells start on whatever eg default is set to; eg use overrides for the
current shell only; eg off clears it.
Bind a provider to a directory with direnv — put this in
the repo's .envrc:
eval "$(eg export vertex)" # (or: eg use vertex)| Command | What it does |
|---|---|
eg setup [zsh|bash] |
Add the integration line to your rc file (idempotent, with backup). |
eg default [profile] |
Get/set the provider new shells start on (auto-applied by the shim). |
eg use <profile>… |
Load one or more profiles into the current shell (merged left-to-right). |
eg off |
Clear the active profile from the current shell. |
eg exec <profile>… -- <cmd> |
Run a command with the profile(s)' env injected (subprocess only). -- required for 2+ profiles. |
eg render <profile>… [-o .env] |
Write a project env file (merging multiple profiles). Default: op:// refs. --resolve: real values (output must be git-ignored). --force-resolve: skip the check. |
eg export <profile>… |
Print the profile(s) as export statements (for scripts / eval "$(eg export x)" in a direnv .envrc). |
eg add <tool> <provider> |
Create a profile (and any tool config) from a recipe. |
eg recipes |
List built-in recipes. |
eg doctor [profile] |
Send a tiny real request to verify a provider works. |
eg list / eg status |
List profiles / show the active profile in this shell. |
eg edit <profile> |
Open a profile in $EDITOR. |
eg init <zsh|bash> |
Print the shell-integration snippet (what setup writes). |
eg use prints export … lines instead of switching.
The shell function isn't loaded — eg is resolving to the bare binary. Check:
whence -w eg # zsh (bash: type eg)If it doesn't say eg: function, run eg setup, then exec zsh. (A binary can
only print the exports; the function is what evals them into your shell.)
A new machine's shells/agents have no provider vars.
Same cause — ~/.zshrc on that machine is missing the integration line. Run
eg setup there, set eg default <profile>, and copy/recreate your profiles
(~/.config/env-garden/ — they hold only op:// refs, no secrets, so they're
safe to sync).
A profile with secrets fails with an op message.
Install the 1Password CLI and enable the app integration (or op signin).
A GUI tool (IDE, cmux, etc.) launches an agent without the env.
GUI-launched agents inherit the environment of the shell that started them.
If that's an interactive shell sourcing ~/.zshrc, eg default is enough; if the
tool execs a binary directly, point its launcher at eg exec <profile> -- <cmd>.
Some tools read a config file instead of env vars. eg add handles those too —
Codex needs a provider block in ~/.codex/config.toml:
eg add codex openai-compat \
--param provider_id=my-proxy \
--param base_url=https://proxy.example.com/v1 \
--param model=claude-sonnet-4-6 \
--param key_ref=op://Private/my-proxy/credential
# then run Codex with: codex --profile my-proxyeg writes the [model_providers.my-proxy] block and a my-proxy.config.toml
overlay atomically — with a timestamped backup, preserving the rest of your
config (comments and other providers included).
skills/eg/SKILL.md is an agent skill
so an agent can run commands against a provider (eg exec <profile> -- <cmd>) or
smoke-test one (eg doctor) without touching your shell or seeing secrets.
Install it with the skills CLI:
# install just the eg skill, globally, for Claude Code
npx skills add stjbrown/env-garden --skill eg -g -a claude-code
# or interactively pick from the repo
npx skills add stjbrown/env-gardenIt also works with any other agent the CLI supports (-a opencode, etc.). To
install by hand instead, copy skills/eg/SKILL.md to ~/.claude/skills/eg/.
The skill is self-bootstrapping: if it's installed before the eg binary, it
tells the agent how to install eg first (brew install stjbrown/tap/eg).
eg is a small Go binary plus a shell shim. The binary prints shell code; the
shim (eval "$(eg init zsh)", installed by eg setup) defines an eg function
that runs the binary and evals its output into your shell. That's the only way
a tool can change your live shell — and it's why switching is naturally
per-shell. eg default is applied automatically by the shim on each new shell.
Security: emitted shell code single-quote-escapes every value and validates
variable names, so crafted values/keys can't inject commands; use/off write
only shell code to stdout (messages go to stderr) so what's eval'd is always
clean; render defaults to op:// references and won't write real secrets to a
non-git-ignored file unless you pass --force-resolve.
# Homebrew (recommended)
brew install stjbrown/tap/eg
# install script (no Homebrew)
curl -fsSL https://raw.githubusercontent.com/stjbrown/env-garden/main/install.sh | sh
# from source
go install github.com/stjbrown/env-garden/cmd/eg@latestMIT