{"id":47793498,"url":"https://github.com/adametherzlab/env-diff","last_synced_at":"2026-04-03T16:00:17.924Z","repository":{"id":346084858,"uuid":"1188397886","full_name":"AdametherzLab/env-diff","owner":"AdametherzLab","description":"Compare .env files across environments, branches, and runtimes. Type-aware diffing, secret masking, git branch comparison, MCP server for AI agents. Zero dependencies.","archived":false,"fork":false,"pushed_at":"2026-03-22T06:13:30.000Z","size":63,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-22T20:24:59.114Z","etag":null,"topics":["ai-tools","bun","ci-cd","cli","configuration","developer-tools","devops","diff","dotenv","env","environment-drift","environment-variables","mcp","secrets","typescript"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AdametherzLab.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-22T02:38:52.000Z","updated_at":"2026-03-22T06:13:33.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/AdametherzLab/env-diff","commit_stats":null,"previous_names":["adametherzlab/env-diff"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/AdametherzLab/env-diff","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdametherzLab%2Fenv-diff","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdametherzLab%2Fenv-diff/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdametherzLab%2Fenv-diff/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdametherzLab%2Fenv-diff/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AdametherzLab","download_url":"https://codeload.github.com/AdametherzLab/env-diff/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AdametherzLab%2Fenv-diff/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31362658,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-03T15:19:21.178Z","status":"ssl_error","status_checked_at":"2026-04-03T15:19:20.670Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-tools","bun","ci-cd","cli","configuration","developer-tools","devops","diff","dotenv","env","environment-drift","environment-variables","mcp","secrets","typescript"],"created_at":"2026-04-03T16:00:13.452Z","updated_at":"2026-04-03T16:00:17.876Z","avatar_url":"https://github.com/AdametherzLab.png","language":"TypeScript","readme":"[![CI](https://github.com/AdametherzLab/env-diff/actions/workflows/ci.yml/badge.svg)](https://github.com/AdametherzLab/env-diff/actions)\n[![npm](https://img.shields.io/npm/v/@adametherzlab/env-diff)](https://www.npmjs.com/package/@adametherzlab/env-diff)\n[![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue)](https://www.typescriptlang.org/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![Zero Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)]()\n\n# env-diff\n\n\u003e Compare .env files across environments, branches, and runtimes. Catch missing variables, type mismatches, and configuration drift before deployment.\n\nBuilt by [AdametherzLab](https://github.com/AdametherzLab) with [Claude](https://claude.ai).\n\n## Why env-diff?\n\nEnvironment variable mismatches are one of the most common causes of deployment failures. A missing `DATABASE_URL`, a `PORT` that changed from number to string, a secret that exists in development but not production — these bugs are silent until they break production.\n\n**env-diff catches them all.**\n\n## Features\n\n- **Type-aware diffing** — Catches when `PORT=3000` (number) becomes `PORT=\"3000\"` (string)\n- **Git branch comparison** — `env-diff .env:main .env:staging` compares across branches\n- **Secret masking** — Automatically detects and masks API keys, tokens, and passwords\n- **Multi-file matrix** — Compare dev, staging, and prod simultaneously\n- **Codebase scanning** — Auto-generate `.env.example` from your source code\n- **Sync mode** — Generate patches to fix environment drift\n- **Watch mode** — Live drift detection during development\n- **CI/CD ready** — GitHub Action, strict mode, JSON output for automation\n- **MCP server** — Native integration for AI coding agents (Claude Code, Cursor)\n- **Zero dependencies** — Pure TypeScript, runs on Bun or Node.js 20+\n- **Config file** — Persistent settings via `.envdiffrc.json`\n\n## How env-diff compares\n\n| Feature | env-diff | dotenv-linter | dotenv-diff | dotenvx |\n|---------|:--------:|:-------------:|:-----------:|:-------:|\n| Type-aware diffing | **yes** | no | no | no |\n| Git branch comparison | **yes** | no | no | no |\n| Secret masking | **yes** | no | no | yes |\n| Multi-file matrix | **yes** | no | no | no |\n| Codebase scanning | **yes** | no | no | no |\n| MCP server (AI agents) | **yes** | no | no | no |\n| GitHub Action | **yes** | no | no | no |\n| Watch mode | **yes** | no | no | no |\n| Sync mode | **yes** | no | no | no |\n| Zero dependencies | **yes** | yes | no | no |\n| Config file | **yes** | yes | no | yes |\n| CI/CD strict mode | **yes** | yes | no | yes |\n\n## Quick Start\n\n### Installation\n\n```bash\nnpm install -g @adametherzlab/env-diff\n# or\nbun add -g @adametherzlab/env-diff\n# or use directly\nnpx @adametherzlab/env-diff .env.example .env.production\n```\n\n### Compare two files\n\n```bash\nenv-diff .env.example .env.production\n```\n\n### Compare across git branches\n\n```bash\nenv-diff .env:main .env:staging\nenv-diff .env@abc123 .env@HEAD\n```\n\n### Strict mode for CI\n\n```bash\nenv-diff .env.example .env.production --strict\n# Exits with code 1 if errors detected\n```\n\n## CLI Reference\n\n```\nenv-diff \u003cleft\u003e \u003cright\u003e [options]\n\nOptions:\n  --strict           Exit with code 1 if errors detected\n  --ignore \u003ckey\u003e     Ignore specific keys (repeatable)\n  --no-value-diff    Only check key existence and types\n  --format \u003ctype\u003e    Output: table (default), json, markdown, summary\n  --mask             Mask detected secret values\n  --watch            Re-diff on file changes\n  --sync             Show patch to fix missing keys\n  --sync-write       Apply sync patch to target file\n\nSubcommands:\n  --scan [dir]       Scan codebase for env var references\n  --scan-write [dir] Generate .env.example from scan\n  --matrix \u003cfiles\u003e   Compare multiple files simultaneously\n  --install-hook     Install pre-commit git hook\n```\n\n## Output Formats\n\n### Table (default)\n```\n+──────────────+──────────────+──────────────+───────────────+\n│ Key          │ .env.example │ .env.prod    │ Status        │\n+──────────────+──────────────+──────────────+───────────────+\n│ DATABASE_URL │ postgres://… │ (undefined)  │ removed       │\n│ PORT         │ 3000         │ \"3000\"       │ type-mismatch │\n│ API_KEY      │ (undefined)  │ sk-l****     │ added         │\n+──────────────+──────────────+──────────────+───────────────+\n\n✖ Environment check failed with errors\n```\n\n### JSON\n```bash\nenv-diff .env.example .env.prod --format json\n```\n\n### Markdown\n```bash\nenv-diff .env.example .env.prod --format markdown\n```\n\n## Programmatic API\n\n```typescript\nimport { parseEnvFile, diffEnvMaps, parseProcessEnv } from \"@adametherzlab/env-diff\";\n\n// Compare .env files\nconst template = parseEnvFile(\".env.example\");\nconst runtime = parseProcessEnv();\nconst result = diffEnvMaps(template, runtime, \"Template\", \"Runtime\");\n\nif (result.hasErrors) {\n  console.error(\"Missing required environment variables!\");\n  result.entries\n    .filter(e =\u003e e.severity === \"error\")\n    .forEach(e =\u003e console.error(`  - ${e.key}: ${e.status}`));\n  process.exit(1);\n}\n```\n\n## Git Branch Comparison\n\nCompare .env files across any git ref (branch, tag, commit):\n\n```bash\n# Compare main vs staging\nenv-diff .env:main .env:staging\n\n# Compare current vs specific commit\nenv-diff .env@abc123 .env\n\n# PR review: compare base branch vs HEAD\nenv-diff .env:origin/main .env:HEAD\n```\n\n## Secret Masking\n\nenv-diff automatically detects sensitive keys and masks their values:\n\n```bash\nenv-diff .env.example .env.production --mask\n# API_KEY: sk-l**** | SECRET_TOKEN: rea****\n```\n\nDefault patterns: `*KEY*`, `*SECRET*`, `*TOKEN*`, `*PASSWORD*`, `*CREDENTIAL*`, `*AUTH*`, `*PRIVATE*`\n\nConfigure custom patterns in `.envdiffrc.json`:\n```json\n{\n  \"secretPatterns\": [\"*KEY*\", \"*SECRET*\", \"STRIPE_*\", \"AWS_*\"]\n}\n```\n\n## Codebase Scanning\n\nAuto-generate `.env.example` from your source code:\n\n```bash\n# Preview what variables your code uses\nenv-diff --scan ./src\n\n# Generate .env.example\nenv-diff --scan-write ./src\n```\n\nOutput:\n```bash\n# Referenced in: src/db.ts, src/config.ts\nDATABASE_URL=\n\n# Referenced in: src/server.ts\nPORT=3000\n```\n\n## Multi-File Matrix\n\nCompare all environments simultaneously:\n\n```bash\nenv-diff --matrix .env.dev .env.staging .env.prod\n```\n\n## Sync Mode\n\nFix environment drift automatically:\n\n```bash\n# Preview missing keys\nenv-diff .env.example .env.local --sync\n\n# Apply patch\nenv-diff .env.example .env.local --sync-write\n```\n\n## Configuration\n\nCreate `.envdiffrc.json` in your project root:\n\n```json\n{\n  \"ignoreKeys\": [\"NODE_ENV\", \"CI\", \"PATH\"],\n  \"caseSensitive\": true,\n  \"strict\": true,\n  \"format\": \"table\",\n  \"secretPatterns\": [\"*KEY*\", \"*SECRET*\", \"*TOKEN*\"],\n  \"compareValues\": true\n}\n```\n\nOr add to `package.json`:\n```json\n{\n  \"envdiff\": {\n    \"ignoreKeys\": [\"NODE_ENV\"],\n    \"strict\": true\n  }\n}\n```\n\n## CI/CD Integration\n\n### GitHub Actions (Recommended)\n\n```yaml\n- uses: AdametherzLab/env-diff@v1\n  with:\n    left: '.env.example'\n    right: '.env.production'\n    strict: 'true'\n    ignore: 'NODE_ENV,CI'\n    mask-secrets: 'true'\n```\n\n### Generic CI\n\n```yaml\nsteps:\n  - run: npx @adametherzlab/env-diff .env.example .env.production --strict --mask\n```\n\n### Pre-commit Hook\n\n```bash\nenv-diff --install-hook\n```\n\n## AI Agent Integration (MCP)\n\nenv-diff includes an MCP (Model Context Protocol) server for AI coding agents:\n\n```bash\n# Add to Claude Code, Cursor, or any MCP-compatible agent\nenv-diff-mcp\n```\n\nAvailable tools:\n- `env_diff_compare` — Compare .env files or content\n- `env_diff_scan` — Scan codebase for env references\n- `env_diff_sync` — Generate sync patches\n\n## Watch Mode\n\nLive drift detection during development:\n\n```bash\nenv-diff .env.example .env --watch\n# Re-diffs automatically when either file changes\n```\n\n## API Reference\n\n| Function | Description |\n|----------|-------------|\n| `parseEnvString(content, options?)` | Parse raw .env content into typed EnvMap |\n| `parseEnvFile(filePath, options?)` | Read and parse .env file |\n| `parseProcessEnv()` | Wrap `process.env` into typed EnvMap |\n| `diffEnvMaps(left, right, leftLabel, rightLabel, options?)` | Compare two EnvMaps |\n| `loadGitEnv(source, options?)` | Load .env from git ref |\n| `scanForEnvVars(directory, extensions?)` | Scan code for env references |\n| `syncEnvFiles(source, target, options?)` | Generate sync patch |\n| `compareMatrix(filePaths, options?)` | Multi-file comparison |\n| `watchEnvFiles(left, right, options?)` | Live file watching |\n| `isSecretKey(key, patterns?)` | Check if key is a secret |\n| `maskValue(value)` | Mask a sensitive value |\n| `formatTable(result)` | Format as colored table |\n| `formatJson(result)` | Format as JSON |\n| `formatMarkdown(result)` | Format as markdown table |\n\n## Status \u0026 Severity\n\n| Status | Severity | Meaning |\n|--------|----------|---------|\n| `removed` | error | Key missing in target |\n| `type-mismatch` | error | Same key, different types |\n| `added` | warning | New key in target only |\n| `modified` | warning | Same key \u0026 type, different value |\n| `unchanged` | info | Identical |\n\n## Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md) for version history.\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md)\n\n## License\n\nMIT (c) [AdametherzLab](https://github.com/AdametherzLab) — Built with [Claude](https://claude.ai)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadametherzlab%2Fenv-diff","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadametherzlab%2Fenv-diff","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadametherzlab%2Fenv-diff/lists"}