زمان خواندن: 13 minutes

شما OpenCode را در ترمینال خود اجرا می‌کنید؛ همه ​​چیز به درستی کار می‌کند. عامل سعی می‌کند یک`gh`— یافت نشد. یک`java -version`— غایب. یک`node`— هیچ‌جا. اما، این ابزارها واقعاً در votre شل موجود هستند. مشکل ؟ OpenCode 쉘‌ها را اجرا می‌کندغیر تعاملیکه هرگز شما را نمی‌خوانند`.zshrc`در اینجا روش تشخیص و رفع آن به‌صورت مناسب را نشان می‌دهیم.

فهرست محتويات

[]

صحنه : یک عامل کور در یک عالم ابزار

این یک شب سه‌شنبه بود. من تازه نصب کرده بودمhttps://opencode.ai[کد باز], الوكيل الذكي الاصطناعي الذي كان يعدّ بتحويل طريقة برمجتي. الاختبار الأول: أن أطلب منه أن يسرد مستودعاتي على جيت هاب.

$ opencode
> Utilise gh pour lister mes repos
❌ bash: gh: command not found

عجیب.`gh`در ترمینال من به خوبی کار می‌کرد. من چیز دیگری امتحان می‌کنم :

> Vérifie la version de Java
❌ bash: java: command not found

سپس:

> Lance le build Gradle
❌ bash: gradle: command not found

جاوا، گردل، نود،gh— همه چیز برای مامور نامرئی بود. ترمینال من، او هم، همه چیز را می‌دید. گویی مامور و من در دو جهان موازی زندگی می‌کردند.

من ساعت‌ها را صرف فهمیدن کردم. ساعت‌های`echo $PATH`, de which java, از narahati-ye afzayesh. من در نهایت متوجه شدم که مشکل ابزارها نبود — کهشل. OpenCode، مانند هر عاملی که زیرفرآیند‌ها را راه‌اندازی می‌کند، در پوسته‌ها کار می‌کندغیر تعاملی. و Zsh، در این شل‌ها، به سادگی صرفاً`.zshrc`.

این متن زیر گزارش کامل از این تشخیص، حل، و درس است که از آن به دست آوردم. اگر از یک عامل هوش مصنوعی — OpenCode, Aider, Cursor یا حتی اسکریپت‌های cron — استفاده می‌کنید، این مشکل يوماً به شما مربوط خواهد شد.

تشریح باگ: دو شل، دو جهان

وقتی یک ترمینال باز می‌کنید، Zsh آن را به عنوان یک شل محسوب می‌کندتعاملی. او بار می‌گیرد`.zshrc`، که همه چیز را مقداردهی می‌کند: SDKMAN, NVM, pnpm, الیاها، پرامپت زیبای شما. محیط شما کامل است.

اما وقتی OpenCode یک دستور را اجرا می‌کند، یک ترمینال تعاملی را راه‌اندازی نمی‌کند. یک شل را راه‌اندازی می‌کند.غیر تعاملی— یک شل وظیفه‌ای، بدون انسان پشت صفحه. و Zsh، در این زمینه، می‌پرد`.zshrc`.او فقط می‌خواند`.zshenv`.

چرا این وجود دارد؟ چون شل غیرتعاملی برای اجرای اسکریپت‌ها طراحی شده، نه برای خدمت به یک کاربر بشر. بارگذاری aliasها، prompt و تکمیل‌ها در یک اسکریپتی که در پس‌زمینه اجرا می‌شود، هدر رفتن است. مشکل این است که PATH شما — اطلاعاتی که مهم‌ترین برای یافتن اجراهای قابل اجرا است — غالبًا در`.zshrc`, نه در`.zshenv`.

@startuml
skinparam backgroundColor #FEFEFE

actor "شما" as user
actor "OpenCode\n(عامل)" as agent
participant "Shell
تعاملی
(.zshrc منبع‌ شده)" as ishell
participant "Shell\nغیر تعاملی\n(.zshrc نادیده)" as nshell

user -> ishell : gh, java, node ✅
agent -> nshell : gh, java, node ❌

note right of ishell
  Source .zshrc :
  - SDKMAN init
  - NVM init
  - ~/apps dans PATH
  - pnpm dans PATH
end note

note right of nshell
  .zshrc ignoré !
  PATH = /usr/bin:/bin
  + quelques répertoires défaut
end note

@enduml

ریشه مسئله :Zsh فقط برای شل‌های تعاملی .zshrc را بارگذاری می‌کند. OpenCode، همانند هر عامل هوش مصنوعی که زیرفرآیندها را راه‌اندازی می‌کند، از شل‌های غیر تعاملی استفاده می‌کند. این شل‌ها فقط`.zshenv`.

متهمان: .zshrc در مقابل .zshenv

Zsh داردچهار فایل راه‌اندازی, هر کس با نقش دقیقی. این یک طراحی ظریف است — اما همین مشکل اصلی است:

فایل

منبع شده وقتی

نقش

PATH را تغییر می‌دهی؟

.zshenv

همیشه(تعاملی + غیرتعاملی + login)

متغیرهای محیطی ضروری، PATH

✅ بله — این جای اوست

.zprofile

شل‌های ورود فقط

دستورات آهسته (یک بار در هر جلسه)

ممکن

.zshrc

فقط پوسته‌های تعاونی

Alias, prompt, تکمیل، ابزارهای تعاملی

❌ برای متغیرهای بحرانی نیست

.zlogin

شل‌های ورود (پس از .zshrc)

پیام‌های خوش‌آمدگویی, نهایی‌سازی

به ندرت

جدول یک نشانه می‌دهد، اما باید آن را به عمق درک کرد. ان را همانند اتاق‌های یک منزل در نظر بگیرید :

  • `.zshenv`استراهرو ورود— همه از اینجا عبور می‌کنند، مهمان یا مقیم. اگر چیزی اینجا بگذارید، هر شل می‌تواند آن را ببیند.

  • `.zshrc`استسالن— فقط ساکنان (shell‌های تعاملی) وارد می‌شوند. مهمانان (shell‌های غیرتعاملی) در بهو می‌مانند.

  • .zprofile et `.zlogin`هستند قطعات مخصوص برای شل‌های ورود (مثل زمانی که از طریق SSH وارد می‌شوید).

مشكله این است که اکثر ما PATH را در اتاق نشیمن می‌گذاریم. و facteur IA، او هرگز اجازه ورود به آنجا را ندارد.

مشکل در یک نمودار:

@startuml
skinparam backgroundColor #FEFEFE

start

if (Shell interactif ?) then (Oui)
  :Source .zshenv;
  :Source .zprofile;
  :Source .zshrc;
  :Source .zlogin;
  note right
    SDKMAN ✅
    NVM ✅
    ~/apps ✅
    pnpm ✅
  end note
else (Non — shell non-interactif)
  :Source .zshenv uniquement;
  note right
    SDKMAN ❌
    NVM ❌
    ~/apps ❌
    pnpm ❌
  end note
endif

stop

@enduml

وقتی که یک ترمینال باز می‌کنید، Zsh تعاملی است: آن را می‌خواند`.zshrc`, وقتی OpenCode یک شل را برای اجرای یک دستور اجرا می‌کند، Zsh غیرتعاملی است: او می‌پرد`.zshrc`, فقط می‌خواند`.zshenv`. Et si `.zshenv`موجود نیست یا PATH را حاوی نیست — این یک صحراست.

چرا Zsh این کار را انجام می‌دهد؟این یک گزینش طراحی به ارث رسیده از یونیکس است. یک شل غیر تعاملی باید باشدسریع et قابل بازتولید. بارگذاری aliasها، پرامپت‌های رنگ‌dar و مقداردهی‌های سنگین SDKMAN در یک اسکریپت cron یا یک عامل AI کند و شکننده خواهد بود؛ بنابراین جدایی منطقی :`.zshenv`به‌طور کلی (variables, PATH),`.zshrc`برای راحتی (aliases, prompt, کامل می‌کنیم). مشکل پیش می‌آید وقتی که مورد ضروری را در راحتی قرار می‌دهیم.

تشخیص گام به گام

قبل از اصلاح، باید دقیقاً متوجه شویم چه چیزی کم است. یک روش تشخیص قابل تکرار — اگر با عوامل هوش مصنوعی کار می‌کنید، آن را در bookmarks/ favorites خود نگهدارید.

1. مسیر عامل را بررسی کنید

ما مقایسه می‌کنیم که ترمینال تعاملی شما چه می‌بیند، با اینکه چه می‌بیند یک شل غیر تعاملی — یعنی چه می‌بیند OpenCode.

# Dans votre terminal interactif (tout fonctionne)
echo $PATH | tr ':' '\n' | grep -v '^/usr' | sort

نتیجهٔ معمول :

/home/cheroliv/apps
/home/cheroliv/.nvm/versions/node/v22.19.0/bin
/home/cheroliv/.sdkman/candidates/java/current/bin
/home/cheroliv/.sdkman/candidates/gradle/current/bin
/home/cheroliv/.local/share/pnpm
/home/cheroliv/.local/bin

اکنون، بیایید شبیه‌سازی کنیم که چه چیزی عامل می‌بیند — یک شل غیر تعاملی:

# Shell non-interactif : pas de .zshrc
zsh -c 'echo $PATH' | tr ':' '\n' | grep -v '^/usr' | sort

نتیجه :

/home/cheroliv/.local/bin

پنج مسیر از شش مسیر غایب شده‌اند.عامل به ۸۳٪ از محیط خود قطع شده است. دقیقاً همان‌طور که اگر از شما بخواهند بدون نیم ابزار آشپزی‌تان غذا پز کنید، فقط می‌توانید آب را جوش دهید اما تقریباً چیزی دیگر نمی‌توانید.

دستور`zsh -c 'echo $PATH'`است شماابزار تشخیص شماره 1. اگر ابزاری که روزانه استفاده می‌کنید در نتایج غایب باشد، عامل هوش مصنوعی شما نیز نمی‌تواند آن را ببیند. آن را قبل و پس از هر تغییر تست کنید..zshenv.

شناسایی آنچه در .zshrc است ولی در .zshenv نیست

اکنون ما به دنبال مسبب در`.zshrc`. ما خطوطی که ابزارهای ما را ذکر می‌کنند را فیلتر می‌کنیم:

grep -n 'apps\|SDKMAN\|NVM\|PNPM\|PATH' ~/.zshrc

به طور معمول مشاهده می‌شود:

119: PATH="/usr/bin/python3:$HOME/apps:$PATH"           # ← pas exporté !
171: export NVM_DIR="$HOME/.nvm"
172: [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"   # ← NVM init
176: nvm use --lts --silent                              # ← active une version
179: export PNPM_HOME="/home/cheroliv/.local/share/pnpm"
187: export SDKMAN_DIR="$HOME/.sdkman"
188: [[ -s "$HOME/.sdkman/bin/sdkman-init.sh" ]] && source "$HOME/.sdkman/bin/sdkman-init.sh"

همه این استناتوان دیده می‌شودبرای OpenCode. و`PATH`حتی خط 119 نیست`export`é — او هرگز از شل جاری خارج نمی‌شود. این یک جزئی فنی حیاتی است : یک متغیر بدون`export`در شالی که آن را تعریف می‌کند، محلی باقی می‌ماند. زیرشِل‌ها — همان‌گونه که توسط OpenCode اجرا می‌شوند — هرگز آن را به ارث نمی‌برند.

3. بررسی کنید که آیا .zshenv وجود دارد

cat ~/.zshenv 2>/dev/null || echo "FICHIER ABSENT"

اگر پاسخ « فایل گمشده » باشد، همانجا است که همه چیز تعیین می‌شود.

حل : .zshenv + مسیرهای صحیح

استراتژی ساده است، اما نیاز به دقت دارد: قرار دادن در`.zshenv` فقطمسارات ضروری، بدون بارگذاری اسکریپت‌های راه‌اندازی سنگین. ما آن را جابجا نمی‌کنیم..zshrc`در.zshenv`— ما جوهر را استخراج می‌کنیم.

ایجاد .zshenv با تمام مسیرهای ضروری

`.zshenv`استفایل تنهاکه Zsh تضمین می‌کند که source کند درهمهمحیط‌ها. اینجاست که PATH باید برود.

export SDKMAN_DIR="$HOME/.sdkman"
export NVM_DIR="$HOME/.nvm"
export PNPM_HOME="$HOME/.local/share/pnpm"
export PATH="$HOME/apps:$HOME/.sdkman/candidates/java/current/bin:$HOME/.sdkman/candidates/gradle/current/bin:$HOME/.nvm/current/bin:$PNPM_HOME:$HOME/.local/bin:$HOME/.local/share/JetBrains/Toolbox/scripts:/usr/bin/python3:$PATH"

استفاده می‌شود`$HOME/.nvm/current/bin`و نه`$HOME/.nvm/versions/node/v22.19.0/bin`. دلیل: نسخه‌های Node تغییر می‌کنند. یک مسیر ثابت در نسخه بعدی اشتباه می‌شود.nvm install. جزئیات بیشتری در بخش بعدی.

چرا نه source sdkman-init.sh در .zshenv ؟

راهبرد بیشترین ترغیب این است که به سادگی آن را در`.zshenv`ما که در آن انجام می‌دهیم`.zshrc`— منبع کردن اسکریپت‌های راه‌اندازی. مامی‌توانستبه وسواس افتاده برای انجام :

# ❌ MAUVAISE IDÉE
[[ -s "$HOME/.sdkman/bin/sdkman-init.sh" ]] && source "$HOME/.sdkman/bin/sdkman-init.sh"

مشکلات :

  • آواژ:`sdkman-init.sh`او در هر شل، حل شبکه و بررسی‌های لازم را انجام می‌دهد. در یک شل غیر تعاملی، این هزینه بی‌ضروری است.

  • شکستنیSDKMAN انتظار دارد که در یک محیط تعاملی باشد. مقداردهی اولیه آن ممکن است به‌صورت ساکت در یک لول (pipe) یا زیرشل (subshell) شکست بخورد.

  • بی‌فایده: SDKMAN نامزدهای خود را در`~/.sdkman/candidates/<tool>/current/bin`— symlinks ثابت که به نسخه فعال اشاره دارند. می‌توانیم از آنها استفاده کنیممستقیم.

روش مناسب: دور زدن مقداردهی اولیه SDKMAN و اشاره مستقیم به پیوندهای سمبولیک`current`. این کلید تمام راه‌حل است :استفاده از ساختار فایل به‌عنوان قرارداد، به جای کد راه‌اندازی.

@startuml
skinparam backgroundColor #FEFEFE

rectangle "روشی آهسته\n(source sdkman-init.sh)" as slow {
  card ".zshenv source sdkman-init.sh
2-3 ثانیه برای هر پوسته
بررسی‌های شبکه
خطر شکست" as s1 #FDEDEC
}

rectangle "رویکرد سریع
(پیوندهای سمبولیک مستقیم)" as fast {
  card ".zshenv export PATH=...current/bin
0 ms
بدون شبکه
بدون مقداردهی اولیه" as s2 #E8F8E8
}

slow --> fast : Même résultat final\nLe PATH pointe sur current/bin\ndans les deux cas

@enduml

مورد NVM: زمانی که مدیر نسخ‌ها فراموش می‌کند یک اثر بگذار

مشکل NVM

این‌جا است که تحقیق مرا تا بیشترین مسافت برد. SDKMAN یک طراحی شیک دارد : وقتی که`sdk install java 25.0.2-tem`, او یک symlink ایجاد می‌کند :

~/.sdkman/candidates/java/current -> ~/.sdkman/candidates/java/25.0.2-tem

این symlink استهمیشه به‌روز. señalدهیدrouille annotations در`.zshenv`و شما محافظت شده‌اید، چه شل که باشد. عالی.

NVM، او، نه می‌سازدهیچ چیزچنین. هیچ symlink`current`. نقطهٔ پایداری ثابتی وجود ندارد. باید به یک مسیر نسخه‌بندی‌شده‌ی سخت‑کد اشاره کرد، که شبیه به این است:

~/.nvm/versions/node/v22.19.0/bin/node

به بعدی`nvm install 24`, این مسیر مرده است. شما`.zshenv`به یک نسخه که دیگر نسخة فعال نیست، اشاره خواهد کرد. این یک بمب موقوت است.

چرا NVM این کار را می‌کند؟NVM با تغییر به‌صورت دینامیک PATH در هر`nvm use`. این برای توسعه‌دهنده‌هایی که نسخ را به‌صورت منظم تغییر می‌دهند، در یک شل تعاملی طراحی شده است. لینک سمبولی`current`در مشخصات اولیه وجود نداشته بود — یک اشتباه طراحی که خودمان آن را برطرف می‌کنیم.

@startuml
skinparam backgroundColor #FEFEFE

package "SDKMAN ✅" {
  [~/.sdkman/candidates/java/current] as sdk_current
  [~/.sdkman/candidates/java/25.0.2-tem/] as java_25
  [~/.sdkman/candidates/java/21.0.7-tem/] as java_21

  sdk_current --> java_25 : symlink
}

package "NVM ❌ (قبل از اصلاح)" as nvm_before {
  [~/.nvm/versions/node/v22.19.0/] as node_22
  [~/.nvm/versions/node/v20.16.0/] as node_20
  note right of node_22
    Pas de symlink current !
    .zshenv doit pointer en dur
    Cassé au prochain nvm install
  end note
}

package "NVM ✅ (پس از اصلاح)" as nvm_after {
  [~/.nvm/current] as nvm_current
  [~/.nvm/versions/node/v22.19.0/] as node_22b
  [~/.nvm/versions/node/v20.16.0/] as node_20b

  nvm_current --> node_22b : symlink\n(mis à jour auto)
}

@enduml

از آنجا که NVM این کار را انجام نمی‌دهد، ما خودمان این کار را انجام می‌دهیم. اصل آن همان SDKMAN است — یک symlink`current`همیشه به نسخه فعال اشاره دارد. ما آن را یک بار می‌سازیم و سپس آن را خودکار می‌کنیم تا خود به خود به‌روز شود.

# Créer le symlink initial
ln -sfn "$HOME/.nvm/versions/node/v22.19.0" "$HOME/.nvm/current"

اکنون، در`.zshenv`، از استفاده می‌کنیم :

$HOME/.nvm/current/bin

به جای :

# ❌ Chemin en dur — cassé au prochain changement de version
$HOME/.nvm/versions/node/v22.19.0/bin

یک symlink ارزش ندارد اگر به‌روزرسانی نشود. symlink`current`باید به‌روز شود وقتی که انجام می‌دهد`nvm use` ou nvm install. راه‌حل: یکپوشش— یک تابع که دستور واقعی را در بر می‌گیرد`nvm`و سمبلینک را پس از هر فراخوانی به‌روزرسانی می‌کند.

# À la fin de .zshrc, APRÈS le chargement de NVM
nvm use --lts --silent
ln -sfn "$(nvm_version_path "$(nvm current)")" "$NVM_DIR/current"

_nvm() {
  command nvm "$@"
  local rc=$?
  ln -sfn "$(nvm_version_path "$(nvm current)")" "$NVM_DIR/current"
  return $rc
}
alias nvm='_nvm'

چگونه کار می‌کند، به صورت جزئی:

@startuml
skinparam backgroundColor #FEFEFE

actor Développeur
participant "nvm wrapper
(_nvm)" as wrapper
participant "nvm réel\n(command nvm)" as realnvm
participant "~/.nvm/current\n(symlink)" as symlink

Développeur -> wrapper : nvm use 20
wrapper -> realnvm : command nvm use 20
realnvm --> wrapper : version activée
wrapper -> symlink : ln -sfn .../v20.16.0 ~/.nvm/current
wrapper --> Développeur : retour

note right of symlink
  Toujours à jour !
  Utilisé par .zshenv
  → accessible par OpenCode
end note

@enduml

بسته‌بندی`_nvm`دستور واقعی فراخوانی می‌کند`nvm`, سپس symlink را به‌روزرسانی می-conduct. alias`nvm='_nvm'`به گونه‌ای که هنگام تایپ`nvm`, از طریق wrapper می‌رویم. و هنگام مقداردهی اولیه شل، نیز همین کار را بعد انجام می‌دهیم.nvm use --lts.

`nvm_version_path`یک تابع داخلی NVM است که مسیر کامل یک نسخه را حل می‌کند. این کار نیاز به بازسازی دستی مسیر را از بین می‌برد.

جدول خلاصه مسیرها

قبل از رفتن به دام‌ها، یک خلاصه بصری از تبدیل. در سمت چپ، چه چیزی که داشتید (همه در`.zshrc`, برای عامل نامرئی). در سمت راست، چه چیزی الآن دارید (مسارات ضروری در`.zshenv`, در هم�؟

ابزار قبل (.zshrc فقط) بعد از (.zshenv + symlink) قابل مشاهده توسط OpenCode؟

~/apps(gh, vscode)

PATH صادر نشده در .zshrc

`$HOME/apps`در .zshenv

(Empty)

SDKMAN Java

`sdkman-init.sh`منبع در .zshrc

`$HOME/.sdkman/candidates/java/current/bin`در .zshenv

✅

SDKMAN Gradle

`sdkman-init.sh`منبع در .zshrc

`$HOME/.sdkman/candidates/gradle/current/bin`در .zshenv

✅

NVM نود

`nvm use --lts`در .zshrc

`$HOME/.nvm/current/bin`در .zshenv (symlink)

(Empty output)

pnpm

`$PNPM_HOME`در .zshrc

`$PNPM_HOME`در .zshenv

✅

JetBrains Toolbox

به‌صورت خودکار توسط Toolbox در .zshrc افزوده شد

`$HOME/.local/share/JetBrains/Toolbox/scripts`در .zshenv

✅

پایتون ۳

`/usr/bin/python3`در PATH .zshrc

به‌طور صریح در .zshenv

✅

موانع و تدابیر کاهش

همه چیز با این رویکرد کامل نیست. این مشکلاتی هستند که با آن‌ها مواجه شدم و چگونگی رفع کردنشان.

فخ توضیحات کاهش

PATH تکراری

اگر .zshenv و .zshrc همان مسیر را اضافه کنند، دو بار نمایش داده می‌شود

`.zshenv`منبع‌گذاری شدهقبل.zshrc. هر دو در یک shell تعاملی خوانده می‌شوند. PATH می‌تواند تکراری شود. این فقط ظاهری است، نه عملکردی. برای جلوگیری از این: فقط مسیرهایی که در PATH پیش‌فرض غایب هستند را در .zshenv قرار دهید.

NVM : نسخه‌ی ثابت در .zshenv

گذاشتن`~/.nvm/versions/node/v22.19.0/bin`در سخت، در بعدی نادرست می‌شود`nvm install`

از symlink استفاده کن`$HOME/.nvm/current/bin`+ بسته`_nvm`در .zshrc

SDKMAN : source sdkman-init.sh در .zshenv

avaş، شکننده، بی‌فایده در یک شل غیر تعاملی

استفاده از لینک‌های نمادین`candidates/<tool>/current/bin`مستقیم

Alias گمشده پس از تغییر

بسته`nvm='_nvm'`در .zshrc فقط پس از بارگذاری مجدد اثر می‌گیرد

شِل را دوباره راه‌اندازی کنید یا`source ~/.zshrc`

OpenCode تغییرات را نمی‌بیند.

عامل قبلاً شل‌های فرعی خود را با .zshenv قدیمی راه‌اندازی کرده

OpenCode را پس از تغییر .zshenv دوباره راه‌اندازی کنید

.zshenv بسیار بارگذاری شده

قرار دادن توابع سنگین یا اولیه‌سازی‌های تعاملی در .zshenv

.zshenv = متغیرهای محیط + PATH فقط. ندارد`source`, بدون توابع سنگین، بدون پرامپت.

دروس آموختگی

  1. .zshrc` تعاملی است، .zshenv سراسری است— اگر یک متغیر باید در تمام شل‌ها (aktoren هوش مصنوعی، cron، اسکریپت‌ها، IDE) وجود داشته باشد، آن در`.zshenv`.سالن راحتمند است، اما ورودی تنها مکانی است که همه از آن می‌گذرند.

  2. مدیران نسخه‌ها برابر نیستند— SDKMAN لینک‌های sembولی ایجاد می‌کند`current`به صورت طراحی. NVM نه. باید این نقص را به صورت دستی جبران کرد. این یک درس مهم است: قبل از تنظیم PATH خود، بررسی کنید که آیا مدیریت شما یک نقطهٔ تثبيت ثابت ارائه می‌دهد؟

  3. اسکریپت‌های init را در .zshenv سورس نکنید—sdkman-init.sh et `nvm.sh`برای یک شل تعاملی طراحی شده‌اند. در زمینه غیرتعاملی، آهسته و شکننده‌اند. ارتباطات نمادین`current`کافی هستند و فوری هستند.

  4. همیشه در شل غیر تعاملی تست کنید—`zsh -c 'echo $PATH'`دقیقاً همان چیزی که یک نماینده می‌بیند را شبیه‌سازی می‌کند. این تست اعتبارسنجی است. بدون این تست، نمی‌دانید آیا پیکربندی شما برای نمایندگان کار می‌کند یا نه.

  5. الگوی wrapper قابل استفاده دوباره است— الگو یکسان`_nvm`+`alias nvm='_nvm'`این به هر ابزارهایی که PATH را به‌صورت دینامیک تغییر می‌دهند و اثر پایداری باقی نمی‌گذارند، اعمال می‌شود. این یک ابزار دیگر در جعبه ایده‌های شما است.

@startuml
skinparam backgroundColor #FEFEFE

rectangle "قبل" as avant {
  card "Shell تعاملی : ✅
Shell غیر تعاملی : ❌
OpenCode : ❌
Cron : ❌
Scripts : ❌" as av1 #FDEDEC
}

rectangle "پس از" as apres {
  card "Shell تعاملی : ✅
Shell غیرتعاملی : ✅
OpenCode : ✅
Cron : ✅
اسکریپت‌ها : ✅" as ap1 #E8F8E8
}

avant --> apres : .zshenv +\nsymlink ~/.nvm/current

@enduml

بررسی نهایی

پس از ایجاد`.zshenv`و symlink NVM، اطمینان حاصل کنید که همه چیز کار می‌کند — در هر دو زمینه :

# Shell interactif (votre terminal)
gh --version && java -version && node --version
# Shell non-interactif (simulation OpenCode)
zsh -c 'gh --version && java -version && node --version'

هر دو باید موفق شوند. اگر بله، عامل هوش مصنوعی شما همان ابزارهایی را که شما دارید، می‌بیند. اگر خیر، به تشخیص قدم به قدم بازگردید — احتمالاً یک مسیر را فراموش کرده‌اید یا symlink NVM به‌روز نیست.

این تست را خودکار کنید.این بررسی را در یک اسکریپت healthcheck که پس از هر بروز‌رسانی ابزارهای خود را اجرا می‌کنید، اضافه کنید. یک`zsh -c 'which java && which node && which gh'`در CI، یک ضمانت در برابر شگفتی‌ها است.

_ یک شل غیر تعاملی مثل یک مهمان ساکت است: او فقط چیزی را می‌خواند که در در ورودی نمایش داده شود. اگر PATH در اتاق نشیمن باشد، او آن را هرگز نخواهد دید. _

لینک‌ها

مستندات رسمی

ابزارهای ذکر شده

برای پیش رفتن بیشتر

مقالات مرتبط