---
name: "env_management"
description: "Handle .env files and per-environment configuration: dotenv format rules, loading with python-dotenv or shell, dev/staging/prod config, and secret hygiene. Triggers when an agent must read, write, load, or debug environment variables or secrets."
---

## Purpose

Give an agent a reliable, verified playbook for environment configuration:
write valid `.env` files, load them in Python or shell, separate
dev/staging/prod config, and keep secrets out of repos and logs. Every
format claim below was tested against installed `python-dotenv`.

## Workflow

1. **Inspect before touching.** Find existing config (`.env`, `.env.*`,
   `.env.example`, exported vars). Read the current file first; merge edits,
   never blind-overwrite a `.env` that may hold live secrets.
2. **Write valid dotenv.** One `KEY=value` per line, keys in `UPPER_SNAKE`.
   All of the following are real, tested behavior:
   - Spaces around `=` are tolerated: `SPACED = v` loads as `SPACED=v`,
     but default to no spaces for safety.
   - Quote values that contain spaces: `GREETING="hello world"`.
     Single quotes also work. Unquoted values stop at whitespace.
   - Comments: full-line `# comment`, or trailing `  # comment` after an
     *unquoted* value (a `#` inside quotes is kept: `HASHED="a # b"`).
   - A leading `export` is accepted and stripped:
     `export API_KEY=abc` loads as `API_KEY=abc`.
   - Multiline only via real newlines inside double quotes, or the `\n`
     escape inside double quotes (verified: expands to a real newline).
     Single-quoted `\n` stays literal.
   - Empty values are fine: `OPTIONAL=`.
3. **Load in Python.**
   ```python
   from dotenv import load_dotenv
   load_dotenv()  # reads .env in the current working directory
   ```
   Default `load_dotenv()` does **not** override variables already set in
   the environment (verified). Pass `override=True` to force file values.
4. **Load in shell.** `set -a; source .env; set +a` exports every var, but
   verified: it breaks on *unquoted* values containing spaces
   (`BAZ=hello world` → `world: command not found`). Either quote such
   values in the file or use `export $(grep -v '^#' .env | xargs)` for
   simple value-only files.
5. **Per-environment config (12-factor style).** Keep config in the
   environment, not in code. Two patterns, pick one per project:
   - `.env.development`, `.env.staging`, `.env.production` — load the one
     matching `APP_ENV` (or the deploy target's own env vars).
   - One base `.env` plus per-env overrides applied on top.
   Always commit a `.env.example` with placeholder values and a comment per
   key explaining what it is; real `.env` files are never committed.
6. **Secret hygiene.** Add `.env` (and `.env.*` except `.env.example`) to
   `.gitignore`, then confirm with `git status --porcelain` that no `.env`
   shows as tracked or staged. Prefer env vars over hardcoded secrets
   everywhere — code, prompts, and logs.

## Output Contract

- Any `.env` written or edited must parse before the task is done.
  Validate with:
  `python3 -c "from dotenv import dotenv_values; print(dotenv_values('.env'))"`
- Report back: which file holds the secrets, whether `.gitignore` covers
  it (verified via `git status`), and the exact load call the consumer
  should use.

## Operating Rules

1. Default to reading an existing `.env` before writing — merge, don't clobber.
2. Default to quoting values with spaces; default to no spaces around `=`.
3. Default to `.env.example` committed and real `.env` ignored, never the reverse.
4. Never echo secret *values* into logs, chat, or tool output — reference the
   key name instead.
5. If a secret was ever committed or pasted into a log, rotation is mandatory
   — deleting the line or force-pushing is not enough.
6. Keep secret-manager guidance to env-var discipline; don't name vendor CLIs
   or flags you haven't verified from their primary docs.
