Environment setup, handled.
Copy the values you've already stored in ~/.env into every project's .env — automatically, accurately, and without touching secrets you haven't defined.
yarn dlx @allons-y/envoy
# ✨ Created /your/project/.envEvery project starts the same way: copy .env.example → .env, then hunt through Notion, 1Password, Slack history, or your own memory for the actual values. In a monorepo it's worse — five packages, five .env.example files, the same ritual repeated for each one.
If you keep a root ~/.env with your real values (and you should), envoy bridges the gap. It reads your .env.example as a template, pulls matching keys from ~/.env, and writes a complete .env alongside it — preserving every comment and blank line in the process.
- Template-driven —
.env.exampledefines the shape;~/.envsupplies the values - Safe by default — skips any
.envthat already exists; use--forceto overwrite - Security-first — will not copy secrets if
.envis currently tracked by git; warns the user if it isn't included in.gitignore - Monorepo-aware — recursively finds every
.env.exampleunder your project root, skippingnode_modules - Non-destructive — keys absent from
~/.envfall back to the example value, so nothing is lost - Comment-preserving — blank lines and
# commentsin.env.exampleare written as-is - Preview before you commit —
--dry-runshows exactly what would be written without touching the filesystem - MCP tool — expose
copy_envto any MCP-compatible host (Claude Desktop, Claude Code, etc.) - No magic — the source is small, readable, and fully tested
Envoy reads from a root ~/.env file on your machine. If you don't have one yet, create it and add any values you want shared across projects:
# ~/.env
DATABASE_URL=postgres://localhost:5432/mydb
STRIPE_SECRET_KEY=sk_test_...
OPENAI_API_KEY=sk-...Any key that isn't in ~/.env will fall back to the value in your .env.example, so you can add keys incrementally — you don't need to migrate everything up front.
Run envoy once in any project directory without adding it as a dependency:
yarn dlx @allons-y/envoy
npx @allons-y/envoy
pnpm dlx @allons-y/envoy
bunx @allons-y/envoyInstall as a dev dependency to use envoy in scripts, hooks, or CI:
yarn add --dev @allons-y/envoy # Yarn Berry
npm install --save-dev @allons-y/envoy
pnpm add --save-dev @allons-y/envoy
bun add --dev @allons-y/envoyRun in any project root. Envoy will find every .env.example recursively and create the corresponding .env.
envoy| Flag | Alias | Description |
|---|---|---|
--force |
-f |
Overwrite existing .env files |
--dry-run |
-n |
Preview changes without writing any files |
--root <path> |
-r |
Use a custom root env file (default: ~/.env) |
--dir <path> |
-d |
Directory to scan (default: current directory) |
--skip-audit |
-s |
Skip the git safety checks |
--help |
-h |
Show help |
Examples:
# Preview what would be created, without writing anything
envoy --dry-run
# Regenerate .env files from scratch
envoy --force
# Use a team-shared env file instead of ~/.env
envoy --root ./secrets/.env.shared
# Scan a specific directory
envoy --dir packages/apiEvery time envoy writes a .env file it runs two git safety checks automatically:
1. Git tracking check — if .env is already committed to the repository, envoy refuses to overwrite it and exits with a non-zero code:
🚨 Blocked /your/project/.env — this file is tracked by git. Remove it from
version control before proceeding:
git rm --cached /your/project/.env
Writing secrets into a tracked file would put them one git push away from exposure. Envoy will not do this under any circumstances without --skip-audit.
2. Gitignore check — if .env is not covered by any .gitignore rule, envoy writes the file but prints a warning:
⚠️ /your/project/.env is not covered by .gitignore — add it to prevent
accidentally committing secrets
Both checks use git's own plumbing (git check-ignore and git ls-files) so nested .gitignore files, global ignores, and .git/info/exclude are all respected. In directories that aren't git repositories the checks are skipped silently.
Use --skip-audit to bypass both checks — for example, in a non-git environment where git isn't available:
envoy --skip-auditThe highest-value place to run envoy is in your project's postinstall script. Every contributor who clones the repo and runs their package manager gets a fully populated .env automatically — no onboarding doc to follow, no values to track down.
{
"scripts": {
"postinstall": "envoy"
}
}Because envoy skips any .env that already exists, running it repeatedly is completely safe. Contributors who already have a .env won't have their values touched.
npm vs Yarn
Use postinstall for this hook regardless of which package manager your project uses. While npm supports a prepare lifecycle script that only runs during local development, Yarn Berry does not support prepare — postinstall is the correct choice for both.
Library authors
If your package is published to npm, a bare postinstall will run for every consumer who installs your package as a dependency — which is not what you want. Use pinst to strip the hook from your published tarball:
yarn add --dev pinst{
"scripts": {
"postinstall": "envoy",
"prepack": "pinst --disable",
"postpack": "pinst --enable"
}
}pinst --disable removes postinstall from package.json before packing, so the published tarball consumers receive contains no hook. pinst --enable restores it locally afterward.
Envoy ships an MCP server so AI tools can call copy_env directly. Add it to your host's config:
Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"envoy": {
"command": "npx",
"args": ["-y", "@allons-y/envoy/mcp"]
}
}
}Claude Code (.claude/settings.json):
{
"mcpServers": {
"envoy": {
"command": "npx",
"args": ["-y", "@allons-y/envoy/mcp"]
}
}
}The copy_env tool accepts dir, force, dry_run, root_env_path, and skip_audit — the same options as the CLI.
- Scans
dirrecursively for.env.examplefiles (ignoringnode_modules) - For each one, checks whether a
.envalready exists alongside it (skips unless--force) - Runs git safety checks — blocks if
.envis tracked; warns if it isn't gitignored - Reads
~/.env(or--root) into a key → value map - Walks every line in
.env.example:- Comments and blank lines are written through unchanged
KEY=VALUElines whereKEYexists in~/.envget the root value substitutedKEY=VALUElines whereKEYis absent fall back to the example value
- Writes the result to
.envnext to the example file
No network calls. No config files. No global state.
- Node.js >= 24.0.0
