whyenvچرا این مقدار
GitHubگیت‌هاب

for developers and AI coding agentsبرای برنامه‌نویس‌ها و ایجنت‌های کدنویسی · v0.1.0

Changed the variable. Nothing changed?متغیر را عوض کردی. چیزی عوض نشد؟

A value exported once in ~/.bashrc silently beats .env.local. NEXT_PUBLIC_ values are frozen at build time. A container keeps the environment it was created with. whyenv NAME lists every place that can set the variable in the order your framework reads them (Vite, Next.js, CRA, dotenv, Docker Compose), marks the winner, says which of your edits is ignored and where the old value probably comes from.مقداری که یک بار در ~/.bashrc export شده بی‌صدا از .env.local جلو می‌افتد. مقدارهای NEXT_PUBLIC_ هنگام بیلد ثابت می‌شوند. کانتینر همان محیطی را نگه می‌دارد که با آن ساخته شده. ‎whyenv NAME‎ همهٔ جاهایی را که می‌توانند متغیر را بسازند به ترتیب خواندن فریم‌ورک شما (Vite، Next.js، CRA، dotenv، Docker Compose) فهرست می‌کند، برنده را علامت می‌زند، می‌گوید کدام ویرایش شما نادیده است و مقدار قدیمی احتمالاً از کجا می‌آید.

  • Python
  • MIT
  • 0 deps
  • v0.1.0
~/.bashrcshell exportexport شل
export VITE_API_URL=https://old.shop.devWINSبرنده
.env.localyour editویرایش شما
VITE_API_URL=http://localhost:4000IGNOREDنادیده
.envshared defaultپیش‌فرض مشترک
VITE_API_URL=https://staging.shop.devSHADOWEDپوشیده

A Vite app, three places that set VITE_API_URL.یک برنامهٔ Vite و سه جا که VITE_API_URL را تعریف می‌کنند.

Same variable, three sheets, in the order the framework reads them.یک متغیر، سه فایل، به ترتیبی که فریم‌ورک می‌خواند.

whyenv looks the value up the way Vite does…whyenv مقدار را همان‌طور که Vite پیدا می‌کند جستجو می‌کند…

…and the value exported in the shell wins. That is what the app sees.…و مقداری که در شل export شده برنده است. برنامه همین را می‌بیند.

You edit .env.local and restart….env.local را ویرایش می‌کنید و دوباره راه می‌اندازید…

…still the same winner. The edit is ignored, so whyenv names the file that exports it.…باز هم همان برنده. ویرایش نادیده است و whyenv فایلی را که export می‌کند نشان می‌دهد.

Click a sheet to see why it wins or loses. (Illustrative example.)روی یک فایل کلیک کنید تا ببینید چرا برنده یا بازنده است. (نمونهٔ توضیحی)

The problemمسئله

You changed the variable and nothing changed? whyenv shows which source wins and which edit is ignored.متغیر را عوض کردید و چیزی عوض نشد؟ whyenv نشان می‌دهد کدام منبع برنده است و کدام ویرایش نادیده گرفته می‌شود.

The problemمسئله

You edited .env.local, restarted, and the app still calls the old server. A variable exported once in ~/.bashrc, or in Windows Environment Variables, silently beats .env.local in Vite, Next.js, CRA and dotenv; VITE_ and NEXT_PUBLIC_ values are frozen into the build; a container keeps the environment it was created with; environment: beats env_file:. People and AI agents answer "restart the server" and edit the same file again.ویرایش .env.local را کردید، سرور را دوباره راه انداختید و برنامه هنوز سرور قدیمی را صدا می‌زند. متغیری که یک بار در ~/.bashrc یا در Environment Variables ویندوز export شده در Vite، Next.js، CRA و dotenv بی‌صدا از .env.local جلو می‌افتد؛ مقدارهای VITE_ و NEXT_PUBLIC_ داخل بیلد ثابت می‌شوند؛ کانتینر همان محیطی را نگه می‌دارد که با آن ساخته شده؛ environment: از env_file: قوی‌تر است. آدم‌ها و ایجنت‌ها می‌گویند «سرور را ری‌استارت کن» و همان فایل را دوباره ویرایش می‌کنند.

What it doesچه می‌کند

whyenv NAME lists every place that can define the variable (your shell, .env.local, .env, docker-compose.yml and more) in the order of your framework, marks the one that wins, and says which of your edits is being ignored, including the shell startup file or the Windows registry entry that probably exports the old value. It also flags traps around the value: a missing client prefix, a build older than the .env file, an empty value that wins, BOM, CRLF, a container that was not recreated. Names that look secret are shown as hidden. It only reads files; it never runs your project.دستور whyenv NAME همهٔ جاهایی را که می‌توانند متغیر را تعریف کنند (شل شما، .env.local، .env، docker-compose.yml و…) به ترتیب فریم‌ورک شما فهرست می‌کند، برنده را علامت می‌زند و می‌گوید کدام ویرایش شما نادیده گرفته می‌شود، از جمله فایل راه‌اندازی شل یا ورودی رجیستری ویندوز که احتمالاً مقدار قدیمی را export می‌کند. تله‌های اطراف مقدار را هم نشان می‌دهد: پیشوند کلاینت جاافتاده، بیلد قدیمی‌تر از فایل .env، مقدار خالیِ برنده، BOM، CRLF، کانتینری که دوباره ساخته نشده. نام‌های شبیه رمز پنهان نشان داده می‌شوند. فقط فایل می‌خواند و پروژه را اجرا نمی‌کند.

  • 5۵stacks: Vite, Next.js, CRA, dotenv, Docker Composeپشته: Vite، Next.js، CRA، dotenv، Docker Compose
  • 25۲۵tests · 0 dependenciesآزمون · بدون وابستگی
  • 0۰secrets shown by default · never writes filesرمزِ نمایش‌داده‌شده به‌صورت پیش‌فرض · هیچ فایلی نمی‌نویسد

Demoنمایش

Real output, three projects that are broken on purposeخروجی واقعی، سه پروژه که عمداً خراب‌اند

Pick a scenario. whyenv --demo builds three small projects in a temp directory, a Vite app with a value exported in the shell, a Next.js build older than its .env, and a Compose service whose environment: beats env_file:, then runs whyenv on each. The output is unedited (docs/demo.txt).یک سناریو را انتخاب کنید. ‎whyenv --demo‎ در یک پوشهٔ موقت سه پروژهٔ کوچک می‌سازد: برنامهٔ Vite با مقداری که در شل export شده، بیلد Next.js قدیمی‌تر از .env و سرویس Compose که environment: آن از env_file: قوی‌تر است؛ سپس whyenv را روی هرکدام اجرا می‌کند. خروجی ویرایش‌نشده است (docs/demo.txt).

$ whyenvwhyenv --demo

Full unedited output of the demoخروجی کامل و ویرایش‌نشدهٔ نمایش
### 1. 'I changed API_URL in .env.local but the app still calls the old server' (Vite)

whyenv 0.1.0 · API_URL · /tmp/tmpgpidaubx/shop-vite

== Vite (mode=development)
effective value: 'http://old.example.test'   <- from: process environment (your shell)
priority, highest first:
  1. process environment (your shell)             'http://old.example.test'    WINS
       probably set at: ~/.bashrc:3   export API_URL=http://old.example.test
       (whyenv can only see files, not the process that exported it)
  2. .env.development.local                       (no such file)
  3. .env.development                             (no such file)
  4. .env.local:1                                 'http://localhost:3000'      shadowed
  5. .env:2                                       'https://prod.example.test'  shadowed

✗ SHADOWED: edits to .env.local, .env are ignored: the shell already exports API_URL
    why:  vite never overrides a variable that already exists in the process environment; different value(s) in: .env.local (line 1), .env (line 2).
    fix:  remove it from this shell (bash/zsh: `unset API_URL`; PowerShell: `Remove-Item Env:API_URL`; cmd: `set API_URL=`) and restart the dev server; or change the shell value instead. Probably set in: ~/.bashrc:3.

· NOTE: API_URL does not start with VITE_
    why:  vite only exposes VITE_* variables to browser code; reading it there gives undefined (server code and config files can read it)
    fix:  rename to VITE_API_URL if the browser needs it. Never put secrets in VITE_* variables: they end up in the public bundle.

1 confirmed shadowed value(s).


### 2. 'next build was fine, but the site shows the old API URL' (Next.js, mode=production)

whyenv 0.1.0 · NEXT_PUBLIC_API_URL · /tmp/tmpgpidaubx/blog-next

== Next.js (NODE_ENV=production)
effective value: 'https://api.example.test'   <- from: .env.production:1
priority, highest first:
  1. process environment (your shell)             -
  2. .env.production.local                        (no such file)
  3. .env.local                                   (no such file)
  4. .env.production:1                            'https://api.example.test'   WINS
  5. .env                                         (no such file)

· NOTE: .env.production:1: CRLF line ending (Windows): most dotenv loaders strip it, but bash `source` and some other tools keep a trailing \r in the value

? CHECK: .next/ is older than the .env file that defines NEXT_PUBLIC_API_URL
    why:  NEXT_PUBLIC_* values are inlined into the bundle when you build; the existing build still contains the old value
    fix:  rebuild (and restart `next start` / re-deploy); editing .env after a build changes nothing

no shadowed values.


### 3. 'I set LOG_LEVEL=warn in web.env and nothing changed' (Docker Compose)

whyenv 0.1.0 · LOG_LEVEL · /tmp/tmpgpidaubx/stack

== Docker Compose: service 'web' container environment
effective value: 'debug'   <- from: environment: LOG_LEVEL in docker-compose.yml
priority, highest first:
  1. environment: LOG_LEVEL in docker-compose.yml 'debug'                      WINS
  2. web.env:1                                    'warn'                       shadowed

✗ SHADOWED: web.env is ignored: environment: LOG_LEVEL in docker-compose.yml wins
    why:  environment: LOG_LEVEL in docker-compose.yml is read first and has a different value than: web.env (line 1).
    fix:  change the value in environment: LOG_LEVEL in docker-compose.yml, or delete that line if you want the others to apply.

· NOTE: a container keeps the environment it was created with
    why:  after editing any of these sources, run `docker compose up -d --force-recreate web` (a running container does not re-read them)

1 confirmed shadowed value(s).

Featuresامکانات

What it does, precisely.دقیقاً چه می‌کند.

01

Precedence per frameworkاولویت برای هر فریم‌ورک

From each vendor's documentation: Vite (process > .env.[mode].local > .env.[mode] > .env.local > .env), Next.js and CRA, plain dotenv (process wins unless override) and Docker Compose (environment: beats env_file:).از مستندات هر فروشنده: Vite (‏process > .env.[mode].local > .env.[mode] > .env.local > .env)، Next.js و CRA، dotenv ساده (پروسه برنده است مگر override) و Docker Compose (‏environment: از env_file: قوی‌تر است).

02

The edit that is ignoredویرایشی که نادیده گرفته می‌شود

SHADOWED is a confirmed case: your edit to .env.local is ignored because the shell already exports the variable, with the exact unset command for bash, PowerShell or cmd.SHADOWED یعنی مورد تأییدشده: ویرایش شما در .env.local نادیده گرفته می‌شود چون شل از قبل متغیر را export کرده، با دستور دقیق unset برای bash، PowerShell یا cmd.

03

Who exported the old valueچه کسی مقدار قدیمی را export کرده

Best effort: ~/.bashrc, ~/.profile, /etc/profile.d and .envrc in the project and its parents; on Windows the user and machine registry keys. Reported as "probably set at" because whyenv cannot see the process that did it.تا حد ممکن: ‏~/.bashrc، ‏~/.profile، ‏/etc/profile.d و ‏.envrc در پروژه و پوشه‌های بالاتر؛ در ویندوز کلیدهای رجیستری کاربر و ماشین. با عبارت «احتمالاً در اینجا تنظیم شده» گزارش می‌شود چون whyenv فرایندی را که این کار را کرده نمی‌بیند.

04

Traps around the valueتله‌های اطراف مقدار

A missing VITE_ or NEXT_PUBLIC_ prefix, a build directory older than .env, an empty value that wins, a similarly named variable, BOM, CRLF, spaces around =, inline #, duplicate keys, unterminated quotes, a container that was not recreated.پیشوند VITE_ یا NEXT_PUBLIC_ جاافتاده، پوشهٔ بیلد قدیمی‌تر از .env، مقدار خالیِ برنده، متغیری با نام مشابه، BOM، CRLF، فاصله دور =، علامت # وسط خط، کلیدهای تکراری، نقل‌قول بسته‌نشده، کانتینری که دوباره ساخته نشده.

05

Secrets stay hiddenرمزها پنهان می‌مانند

Names containing KEY, SECRET, TOKEN, PASS and similar are shown as hidden with only a length; --show-values is explicit. It reads files and the Windows registry, never sends anything over the network and never writes.نام‌های شامل KEY، SECRET، TOKEN، PASS و مانند آن فقط با طول و به‌صورت پنهان نشان داده می‌شوند؛ ‎--show-values صریح است. فایل‌ها و رجیستری ویندوز را می‌خواند، چیزی را روی شبکه نمی‌فرستد و چیزی نمی‌نویسد.

06

For humans and agentsبرای آدم‌ها و ایجنت‌ها

--json for scripts, exit code 1 on a confirmed shadowed value and 2 when the variable is defined nowhere, and a snippet for AGENTS.md / CLAUDE.md so an assistant runs whyenv instead of editing .env in circles.خروجی --json برای اسکریپت، کد خروج ۱ برای مقدار تأییدشدهٔ shadowed و ۲ وقتی متغیر هیچ‌جا تعریف نشده، و یک snippet برای AGENTS.md / CLAUDE.md تا دستیار به‌جای چرخیدن دور .env از whyenv استفاده کند.

  • Python
  • .env
  • Vite
  • Next.js
  • Docker Compose

Install & quick startنصب و شروع سریع

Up and running in a minute.در یک دقیقه راه بیندازید.

  1. Install (Python 3.9+, no dependencies)نصب (پایتون ۳٫۹ به بالا، بدون وابستگی)

    pipx install git+https://github.com/mrzroot/whyenv
    # or run without installing:
    uvx --from git+https://github.com/mrzroot/whyenv whyenv --demo
  2. Ask about a variableپرسیدن دربارهٔ یک متغیر

    whyenv API_URL                          # in the current project (framework auto-detected)
    whyenv API_URL -d ../shop               # another directory
    whyenv NEXT_PUBLIC_X -f next -m production
    whyenv DB_PASSWORD                      # shown as hidden; add --show-values only when alone
  3. Offline demo, or for scriptsنمایش آفلاین، یا برای اسکریپت

    whyenv --demo       # three broken projects built in a temp dir
    whyenv API_URL --json # exit 1 when an edit is shadowed, 2 when undefined

How it worksچطور کار می‌کند

Four steps, no magic.چهار گام، بدون جادو.

Detect the stackتشخیص پشته

Looks at package.json, config files and docker-compose.yml to pick Vite, Next.js, CRA, plain dotenv or Compose; -f and -m force the framework and mode.package.json، فایل‌های پیکربندی و docker-compose.yml را می‌بیند تا Vite، Next.js، CRA، dotenv ساده یا Compose را انتخاب کند؛ ‎-f‎ و ‎-m‎ فریم‌ورک و مود را اجباری می‌کنند.

Apply the real precedenceاعمال اولویت واقعی

Reads your actual .env files, the process environment of the shell it runs in and the Compose file, and orders them the way the framework documentation says: highest first, one winner..envهای واقعی شما، محیط پروسهٔ شلی که در آن اجرا می‌شود و فایل Compose را می‌خواند و به ترتیب مستندات فریم‌ورک می‌چیند: قوی‌ترین اول، یک برنده.

Find who exported itیافتن چه کسی export کرده

If the winner is the shell, it greps ~/.bashrc, ~/.profile, /etc/profile.d and .envrc (and on Windows the registry) and reports the file and line as "probably set at".اگر برنده شل باشد، ‏~/.bashrc، ‏~/.profile، ‏/etc/profile.d و ‏.envrc (و در ویندوز رجیستری) را می‌گردد و فایل و خط را با عبارت «احتمالاً در اینجا تنظیم شده» گزارش می‌کند.

Three levels, one fixسه سطح، یک راه‌حل

SHADOWED (confirmed, exit 1), CHECK (could be the cause), NOTE (a trap nearby). Each says why and gives the command; secrets are hidden unless --show-values.SHADOWED (تأییدشده، کد ۱)، CHECK (می‌تواند علت باشد)، NOTE (تله‌ای نزدیک). هرکدام دلیل و دستور را می‌گوید؛ رمزها پنهان‌اند مگر با ‎--show-values‎.

Honest limitsمحدودیت‌های صادقانه

What it does not do. Please read.چه کارهایی نمی‌کند. لطفاً بخوانید.

  • It sees files and the Windows registry, not other processes. An IDE, docker -e, a CI runner or a parent shell can set a value that whyenv reports only as "exported by hand, by your IDE or by a parent process".فایل‌ها و رجیستری ویندوز را می‌بیند، نه فرایندهای دیگر. IDE، ‏docker -e، اجراکنندهٔ CI یا شل والد می‌توانند مقداری بسازند که whyenv فقط با عبارت «با دست، توسط IDE یا فرایند والد export شده» گزارش می‌کند.
  • It reads the environment of its own process: run it from the same shell you start the dev server from.محیط پروسهٔ خودش را می‌خواند: از همان شلی اجرا کنید که سرور توسعه را از آن راه می‌اندازید.
  • The Compose reader is a minimal YAML subset (services, environment: as map or list, env_file:). Anchors, extends, profiles and ${VAR:-default} inside values are not resolved: verify with docker compose config.خواننده‌ٔ Compose زیرمجموعهٔ کوچکی از YAML است (services، ‏environment: به‌صورت map یا list، ‏env_file:). anchor، ‏extends، ‏profiles و ‎${VAR:-default}‎ داخل مقدارها حل نمی‌شوند: با docker compose config تأیید کنید.
  • Rules follow each framework's documentation as of October 2026 and can go stale. NODE_ENV is not read from your shell (development is assumed unless -m). The Windows registry reader and PowerShell hints are implemented but untested on Windows.قواعد از مستندات هر فریم‌ورک تا اکتبر ۲۰۲۶ پیروی می‌کنند و ممکن است کهنه شوند. NODE_ENV از شل شما خوانده نمی‌شود (مگر با -m، development فرض می‌شود). خواننده‌ٔ رجیستری ویندوز و راهنمای PowerShell پیاده شده‌اند ولی روی ویندوز آزموده نشده‌اند.

How it comparesمقایسه با ابزارهای دیگر

Closest things, and the difference.نزدیک‌ترین‌ها و تفاوت‌شان.

No existing tool found as of Oct 2026تا مهر ۱۴۰۵ (اکتبر ۲۰۲۶) ابزار مشابهی پیدا نشد that applies each framework's environment-variable precedence to your actual files and names the ignored edit and its probable source.که اولویت متغیر محیطی هر فریم‌ورک را روی فایل‌های واقعی شما اعمال کند و ویرایش نادیده‌گرفته‌شده و منبع احتمالی آن را نام ببرد.

This is the outcome of searches of GitHub, npm, PyPI and the web on 24 Oct 2026, not a guarantee: search indexes are incomplete and this space changes weekly. If you know one, please open an issue and it will be linked here.این نتیجهٔ جست‌وجو در گیت‌هاب، npm، PyPI و وب در ۲ آبان ۱۴۰۵ است، نه تضمین: نمایه‌های جست‌وجو ناقص‌اند و این حوزه هر هفته عوض می‌شود. اگر موردی می‌شناسید لطفاً یک issue باز کنید تا اینجا پیوند شود.

ToolابزارWhat it doesچه می‌کندHow it differsتفاوت
Framework docs (Vite, Next.js, CRA, Compose)Each documents its own precedenceهرکدام اولویت خودش را مستند کردهYou still apply it by hand to your files; whyenv does it for all of them and names the ignored editاعمالش روی فایل‌های شما دستی است؛ whyenv برای همه این کار را می‌کند و ویرایش نادیده‌گرفته‌شده را نام می‌برد
dotenv (debug, override)Loads .env files; debug logs what it loadedفایل‌های .env را بار می‌کند؛ حالت debug آنچه بار شده را ثبت می‌کندOne loader, inside the running process; it does not compare frameworks, the shell source or Composeیک loader داخل فرایند در حال اجرا؛ فریم‌ورک‌ها، منبع شل یا Compose را مقایسه نمی‌کند
direnvLoads and unloads environment per directoryمحیط را برای هر پوشه بار و خالی می‌کندSets variables; it does not explain which source winsمتغیر می‌سازد؛ توضیح نمی‌دهد کدام منبع برنده است
dotenv-precedence-drillTraces interpolation and the final merge of configدرون‌یابی و ادغام نهایی پیکربندی را ردیابی می‌کندTraces one merge model; whyenv applies each framework's own rules and looks for the shell originیک مدل ادغام را ردیابی می‌کند؛ whyenv قواعد خود هر فریم‌ورک را اعمال می‌کند و منبع شل را می‌جوید
docker compose configPrints the resolved Compose fileفایل Compose حل‌شده را چاپ می‌کندAuthoritative for Compose only; whyenv gives a quick "very likely" across stacks, then you verify with itفقط برای Compose مرجع است؛ whyenv یک «بسیار محتمل» سریع روی همهٔ پشته‌ها می‌دهد و بعد با آن تأیید می‌کنید

FAQپرسش‌های متداول

Questions people ask.پرسش‌هایی که می‌پرسند.

Will it print my secrets?آیا رمزهای من را چاپ می‌کند؟

No. Names containing KEY, SECRET, TOKEN, PASS and similar are shown as hidden with only a length. --show-values is explicit, and --json follows the same rule.نه. نام‌های شامل KEY، SECRET، TOKEN، PASS و مانند آن فقط با طول و به‌صورت پنهان نشان داده می‌شوند. ‎--show-values‎ صریح است و ‎--json‎ هم همین قاعده را دارد.

Does it run my project or change files?آیا پروژه را اجرا یا فایل‌ها را تغییر می‌دهد؟

No. It reads files (and the Windows registry), never executes your project, never writes and never uses the network.نه. فایل‌ها (و رجیستری ویندوز) را می‌خواند، پروژه را اجرا نمی‌کند، چیزی نمی‌نویسد و از شبکه استفاده نمی‌کند.

Why does it say "probably set at"?چرا می‌گوید «احتمالاً تنظیم شده در»؟

Because it can only see files, not the process that actually exported the variable. A matching line in ~/.bashrc is strong evidence, not proof.چون فقط فایل‌ها را می‌بیند، نه فرایندی که واقعاً متغیر را export کرده. خط مطابق در ~/.bashrc شاهد قوی است، نه اثبات.

How does an AI agent use it?ایجنت هوش مصنوعی چطور از آن استفاده کند؟

Paste docs/AGENTS.snippet.md into AGENTS.md or CLAUDE.md so it runs whyenv instead of editing .env in circles. --json is machine-readable; exit 1 means a confirmed shadowed value, 2 means undefined.فایل docs/AGENTS.snippet.md را در AGENTS.md یا CLAUDE.md بگذارید تا به‌جای چرخیدن دور .env از whyenv استفاده کند. ‎--json‎ ماشین‌خوان است؛ کد ۱ یعنی مقدار shadowed تأییدشده و ۲ یعنی تعریف‌نشده.