पढ़ने का समय : 13 minutes

आप अपने टर्मिनल में OpenCode लॉन्च करते हैं, सब काम कर रहा है। एजेंट एक प्रयास कर रहा है`gh`— अप्राप्य. एक`java -version`— अनुपस्थित। एक`node`— कहीं नहीं. हालांकि, ये उपकरण सही जगह पर हैं आपके votre shell में। समस्या? OpenCode शेल्स लॉन्च करता है।गैर-इंटरैक्टिवजो कभी आपके नहीं पढ़ते`.zshrc`. यहाँ बताया गया है कि इसे सही तरीके से कैसे निदान और सुधार करें।

दृश्य: उपकरणों की दुनिया में एक अंधा एजेंट

यह मंगलवार की शाम थी। मैंneas स्थापित कर चुका थाhttps://opencode.ai[OpenCode], वह AI एजेंट जो मेरे कोडिंग के तरीके को बदलने का वादा करता था। पहला परीक्षण: उसे मेरे GitHub रिपॉज़िटरीज़ की सूची देने के लिए कहना।

$ 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

Java, Gradle, Node,`gh`सब कुछ एजेंट के लिए अदृश्य था। मेरा टर्मिनल, हालांकि, सब कुछ देखता था। जैसे कि एजेंट और मैं दो समानांतर दुनिया में रह रहे होते।

मुझे समझने में घंटे लग गए। घंटों`echo $PATH`, de which java, बढ़ती निराशा के कारण। मैं अंततः समझ गया कि समस्या उपकरण नहीं थी — यह थाशेल. OpenCode, किसी भी एजेंट की तरह जो सबप्रोसेस लॉन्च करता है, शेल्स में काम करता हैगैर-इंटरेक्टिव. Zsh, इन शेल्स में, आपके को पूरी तरह से अनदेखा करता है`.zshrc`.

जो आगे आता है, इस निदान, समाधान, और मेरे द्वारा सीखा गया सबक का पूरा विवरण है। यदि आप एक AI एजेंट — OpenCode, Aider, Cursor, या यहां तक कि cron स्क्रिप्ट — का उपयोग करते हैं, तो यह समस्या एक दिन आपको प्रभावित करेगी।

बग की अनाटॉमी : दो शेल, दो दुनिया

जब आप टर्मिनल खोलते हैं, Zsh इसे एक शेल के रूप में व्यवहार करता हैइंटरैक्टिव. वह चार्ज करता है`.zshrc`, सब कुछ शुरू करता है : SDKMAN, NVM, pnpm, उपनाम, आपका सुंदर प्रॉम्प्ट. आपका वातावरण पूरा है।

लेकिन जब OpenCode एक कमांड चलाता है, तो यह एक इंटरैक्टिव टर्मिनल नहीं लॉन्च करता। यह एक शेल लॉन्च करता है।गैर-इंटरैक्टिव— एक कार्य शेल, स्क्रीन के पीछे कोई मानव नहीं। और इस संदर्भ में Zsh, कूदता है।.zshrc. वह केवल पढ़ता है`.zshenv`.

इस अंतर क्यों मौजूद है? क्योंकि एक गैर-इंटरैक्टिव शेल स्क्रिप्ट निष्पादित करने के लिए डिज़ाइन किया गया है, मानव उपयोगकर्ता की सेवा करने के लिए नहीं। बैकग्राउंड में चलने वाले स्क्रिप्ट में एलियास, प्रॉम्प्ट और पूर्णता को लोड करना बर्बादी होगी। समस्या यह है कि आपका PATH — निष्पादन योग्य खोजने के लिए सबसे महत्वपूर्ण जानकारी — अक्सर इस प्रकार इनिशियलाइज़ किया जाता है`.zshrc`, नहीं में`.zshenv`.

@startuml
skinparam backgroundColor #FEFEFE

actor "आप" as user
actor "OpenCode\n(agent)" as agent
participant "इंटरैक्टिव\nShell\n(.zshrc sourcé)" as ishell
participant "नॉन-इंटरैक्टिव\nShell\n(.zshrc ignored)" as nshell

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

note right of ishell
स्रोत .zshrc :
- SDKMAN init
- NVM init
- PATH में ~/apps
- PATH में pnpm
end note

note right of nshell
.zshrc को अनदेखा किया गया!
PATH = /usr/bin:/bin
+ कुछ डिफ़ॉल्ट निर्देशिकाएँ
end note

@enduml

समस्या की जड़:Zsh .zshrc को केवल इंटरैक्टिव shells के लिए source करता है. OpenCode, किसी भी IA एजेंट की तरह जो सबप्रोसेस लॉन्च करता है, गैर-इंटरैक्टिव शैल का उपयोग करता है। ये शैल केवल पढ़ते हैं`.zshenv`.

दोषी: .zshrc vs .zshenv

Zsh के पास हैचार प्रारंभ फ़ाइलें, प्रत्येक में एक विशिष्ट भूमिका है। यह एक शालीन डिज़ाइन है—लेकिन यह समस्या का नोड भी है :

फ़ाइल स्रोत प्राप्त जब भूमिका पाथ बदलें?

.zshenv

हमेशा(इंटरैक्टिव + नॉन-इंटरैक्टिव + लॉगिन)

आवश्यक पर्यावरण चर, PATH

✅ हाँ — यह उसकी जगह है

.zprofile

केवल लॉगिन शेल्स

धीमी कमांड (प्रति सत्र एक बार)

संभव

.zshrc

केवल इंटरैक्टिव शेल्स

एलियास, प्रॉम्प्ट, पूर्णता, इंटरैक्टिव टूल्स

❌ आलोच्य चर के लिए नहीं

.zlogin

लॉगिन शेल्स (.zshrc के बाद)

स्वागत संदेश, अंतिमीकरण

दुर्लभतापूर्वक

तालिका एक संकेत देती है, लेकिन इसे गहराई से समझना आवश्यक है। इसे घर के कमरों की तरह सोचें:

  • `.zshenv`है

प्रवेश हॉल— हर कोई यहाँ से गुज़रता है, चाहे वह आगंतुक हो या स्थायी निवासी। यदि आप यहाँ कुछ रखते हैं, तो सभी शेल इसे देख सकते हैं।

  • `.zshrc`हैड्राइंग रूम— केवल निवासी (shells interactifs) इसमें प्रवेश करते हैं। आगंतुक (shells non-interactifs) हॉल में रहते हैं।

  • .zprofile et `.zlogin`ये लॉगिन शेल के लिए विशेषीकृत टुकड़े हैं (जैसे जब आप SSH से जुड़ते हैं).

@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

समस्या को एक आरेख में:

@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 पुनरावृत्तीय. SDKMAN के एलीयास, रंगीन प्रॉम्प्ट और भारी प्रारम्भीकरण को cron स्क्रिप्ट या AI एजेंट में लोड करने से धीमा और भंगुर हो जाएगा। इसलिए अलगाव तार्किक है :`.zshenv`मुख्य रूप से (variables, PATH)`.zshrc`सुविधा के लिए (aliases, prompt, complétons)। समस्या तब होती है जब हम सार को सुविधा में रखते हैं।

चरणबद्ध निदान

सुधारने से पहले, यह समझना आवश्यक है कि ठीक क्या कमी है। यहाँ एक पुनरावृत्ति योग्य निदान विधि है — यदि आप IA एजेंटों के साथ काम करते हैं, तो इसे बुकमार्क में रखें।

1. एजेंट का PATH जाँचें

इंटरैक्टिव टर्मिनल द्वारा देखे गए की तुलना गैर-इंटरैक्टिव शेल द्वारा देखे गए से की जाती है — यानी 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

छह में से पाँच मार्ग लुप्त हो गए हैं।एजेंट अपने पर्यावरण का 83% खो चुका है। यह ऐसा है जैसे आपको अपने बर्तनों की आधी संख्या के बिना खाना बनाना पड़े — आप पानी उबाल सकते हैं, लेकिन कुछ और नहीं।

आदेश`zsh -c 'echo $PATH'`आपका हैनिदान उपकरण नंबर 1. यदि कोई उपकरण जिसे आप रोज़ाना उपयोग करते हैं परिणाम में अनुपस्थित है, आपका AI एजेंट इसे भी नहीं देख सकेगा। इसका परीक्षण करें प्रत्येक संशोधन से पहले और बाद`.zshenv`.

2. पहचानें कि .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 स्रोत करने की गारंटी देता हैसभीles contextes. यहाँ 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"

समस्याएँ :

@startuml
skinparam backgroundColor #FEFEFE

rectangle "धीमी विधि\n(स्रोत sdkman-init.sh)" as slow {
  card ".zshenv source sdkman-init.sh\nप्रति शेल 2-3 सेकंड\nनेटवर्क जाँच\nविफलता का जोखिम" as s1 #FDEDEC
}

rectangle "तेज़ विधि\n(सीधे symlinks)" as fast {
  card ".zshenv export PATH=...current/bin\n0 ms\nकोई नेटवर्क नहीं\nकोई इनिशियलाइज़ेशन नहीं" as s2 #E8F8E8
}

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

@enduml

सही तरीका: SDKMAN की इनिशियलाइज़ेशन को बायपास करना और सीधे सिंबोलिक लिंक्स को इंगित करना`current`. यह समाधान की पूरी कुंजी है :फ़ाइल संरचना को अनुबंध के रूप में उपयोग करें, प्रारम्भिक कोड के बजाय.

@startuml
skinparam backgroundColor #FEFEFE

rectangle "धीमा दृष्टिकोण\n(source sdkman-init.sh)" as slow {
  card ".zshenv source sdkman-init.sh\n2-3 सेकंड प्रति शेल\nनेटवर्क जाँचें\nअसफलता का जोखिम" as s1 #FDEDEC
}

rectangle "तेज़ पद्धति\n(symlinks सीधे)" as fast {
  card ".zshenv export PATH=...current/bin\n0 ms\nनेटवर्क नहीं\nप्रारंभ नहीं" 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`, यह सिमलिंक बनाता है :

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

यह सिंबलिक लिंक हैहमेशा अपडेटेड. इशारा करें ऊपर में`.zshenv`और आप कवर किए गए हैं, शेल जो भी हो। Beautiful.

NVM, उसे, नहीं बनाताकुछ नहींऐसा का। सिमलिंक नहीं`current`. कोई स्थायी एंकर बिंदु नहीं। हमें एक हार्डकोडेड संस्करणित पथ पर इशारा करना चाहिए, जो इस तरह दिखता है:

~/.nvm/versions/node/v22.19.0/bin/node
@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
कोई current symlink नहीं है!
.zshenv को हार्ड-पॉइंट (hard point) करना होगा
अगले 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 ऐसा क्यों करता है ?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 के लिए सिमलिंक current बनाएँ

क्योंकि NVM ऐसा नहीं करता, हम इसे खुद करते हैं। सिद्धांत SDKMAN के समान है — एक सिम्लिंक`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

सिमलिंक के अपडेट को स्वचालित करें

एक सिमलिंक कुछ भी मूल्यवान नहीं है यदि यह अपडेट नहीं होता। सिमलिंक`current`इसे अपडेट करना चाहिए जब हम करते हैं`nvm use` ou nvm install. समाधान: एकरैपर— एक फ़ंक्शन जो असली कमांड को लपेटता है`nvm`और हर कॉल के बाद सिमलिंक को अपडेट करता है।

@startuml
skinparam backgroundColor #FEFEFE

actor Développeur
participant "nvm रैपर\n(_nvm)" as wrapper
participant "वास्तविक nvm\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
हमेशा अपडेटेड!
`.zshenv` द्वारा उपयोग किया जाता है
→ OpenCode द्वारा सुलभ
end note

@enduml

यह कैसे काम करता है, विस्तार से :

@startuml
skinparam backgroundColor #FEFEFE

actor Développeur
participant "nvm रैपर\n(_nvm)" as wrapper
participant "nvm वास्तविक\n(आदेश 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 को अपडेट करता है. एलियास`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

✅

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 use --lts`में .zshrc

$HOME/.nvm/current/bin.zshenv (symlink) में

✅

pnpm

`$PNPM_HOME`में .zshrc

$PNPM_HOME.zshenv में

✅

JetBrains टूलबॉक्स

Toolbox द्वारा .zshrc में स्वचालित रूप से जोड़ा गया

`$HOME/.local/share/JetBrains/Toolbox/scripts`में .zshenv

✅

पायथन 3

`/usr/bin/python3`PATH .zshrc में

.zshenv में स्पष्ट रूप से

✅

जाल और निवारण

इस दृष्टिकोण से सब कुछ सही नहीं है। ये समस्याएँ हैं जिन्हें मैंने सामना किया, और उन्हें कैसे टाला जा सकता है।

जाल विवरण न्यूनीकरण

दुप्लीकेट पाथ

यदि .zshenv और .zshrc एक ही पथ जोड़ते हैं, तो यह दो बार दिखाई देता है

`.zshenv`स्रोतित हैपहले.zshrc. दोनों इंटरैक्टिव शेल में पढ़े जाते हैं। PATH दोहराया जा सकता है। यह कॉस्मेटिक है, कार्यात्मक नहीं। इससे बचने के लिए: केवल .zshenv में उन पथों को रखें जो डिफ़ॉल्ट PATH में absents हैं।

NVM : .zshenv में हार्डकोडेड संस्करण

रखना`~/.nvm/versions/node/v22.19.0/bin`पक्का झूठा हो जाता है अगले पर`nvm install`

सिम्लिंक का उपयोग करना`$HOME/.nvm/current/bin`और रैपर`_nvm`.zshrc में

SDKMAN : source sdkman-init.sh में .zshenv

धीमा, नाजुक, गैर-इंटरैक्टिव शेल में उपयोगी नहीं

सिमलिंक का उपयोग करें`candidates/<tool>/current/bin`सीधे

संशोधन के बाद उपनाम गायब

रैपर`nvm='_nvm'`.zshrc में केवल पुनः लोड करने के बाद प्रभाव पड़ता है

शेल को पुनः आरंभ करना या`source ~/.zshrc`

OpenCode परिवर्तन नहीं देखता

एजेंट ने अपने पुराने .zshenv के साथ अपने सब-शेल्स पहले ही लॉन्च कर दिए हैं

OpenCode को .zshenv में संशोधन करने के बाद रीस्टार्ट करें

.zshenv बहुत भरा

.zshenv में भारी फ़ंक्शन या इंटरेक्टिव प्रारम्भीकरण रखें

.zshenv = पर्यावरण चर + PATH केवल. नहीं`source`, कोई भारी फ़ंक्शन नहीं, कोई प्रॉम्प्ट नहीं

@startuml
skinparam backgroundColor #FEFEFE

rectangle "पहले" as avant {
  card "इंटरैक्टिव शेल : ✅\nनॉन-इंटरैक्टिव शेल : ❌\nOpenCode : ❌\nCron : ❌\nस्क्रिप्ट्स : ❌" as av1 #FDEDEC
}

rectangle "बाद में" as apres {
  card "इंटरैक्टिव शेल : ✅\nनॉन-इंटरैक्टिव शेल : ✅\nOpenCode : ✅\nCron : ✅\nस्क्रिप्ट्स : ✅" as ap1 #E8F8E8
}

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

@enduml
  1. .zshrc` इंटरैक्टिव है, .zshenv सार्वभौमिक हैयदि एक चर को सभी शेल्स में मौजूद होना चाहिए (AI एजेंट्स, क्रॉन, स्क्रिप्ट्स, आईडीई), इसमें रखा जाता है`.zshenv`. लिविंग रूम आरामदायक है, लेकिन प्रवेश हॉल ही एकमात्र स्थान है जहाँ हर कोई गुज़रता है।

  2. वर्जन प्रबंधक समान नहीं हैं— SDKMAN सिम्लिंक्स बनाता है`current`डिज़ाइन के अनुसार। NVM नहीं। इस कमी को हाथ से पूरा करना होगा। यह एक महत्वपूर्ण सबक है: अपने PATH को कॉन्फ़िगर करने से पहले, जाँचें कि आपका प्रबंधक एक स्थिर आधार बिंदु प्रदान करता है या नहीं।

  3. .zshenv में init स्क्रिप्ट्स को सोर्स न करें—sdkman-init.sh et `nvm.sh`अंतःक्रियात्मक शेल के लिए डिज़ाइन किए गए हैं। वे गैर-इंटरैक्टिव संदर्भ में धीमे और भंगुर होते हैं। सिमलिंक`current`वे पर्याप्त हैं और तत्काल हैं।

  4. हमेशा गैर-इंटरैक्टिव शेल में परीक्षण करें—`zsh -c 'echo $PATH'`एक एजेंट द्वारा देखे गए को सटीक रूप से सिम्युलेट करता है। यह मान्यता परीक्षण है। इस परीक्षण के बिना, आपको नहीं पता होगा कि आपकी एजेंट्स के लिए कॉन्फ़िगरेशन काम करता है या नहीं।

  5. व्रैपर पैटर्न पुनः उपयोग योग्य है— वही पैटर्न`_nvm`+`alias nvm='_nvm'`यह किसी भी उपकरण पर लागू होता है जो PATH को डायनामिक रूप से संशोधित करता है बिना किसी स्थायी निशान छोड़े। यह आपके विचार बॉक्स में एक और उपकरण है।

@startuml
skinparam backgroundColor #FEFEFE

rectangle "पहले" as avant {
  card "इंटरैक्टिव शेल : ✅\nगैर-इंटरैक्टिव शेल : ❌\nOpenCode : ❌\nCron : ❌\nस्क्रिप्ट्स : ❌" as av1 #FDEDEC
}

rectangle "के बाद" as apres {
  card "इंटरैक्टिव शेल : ✅\nगैर-इंटरैक्टिव शेल : ✅\nOpenCode : ✅\nCron : ✅\nस्क्रिप्ट्स : ✅" as ap1 #E8F8E8
}

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

@enduml

अंतिम सत्यापन

बनाने के बाद`.zshenv`और सिमलिंक NVM, सुनिश्चित करें कि सब कुछ सही काम कर रहा है — दोनों संदर्भों में:

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

दोनों को सफल होना चाहिए। यदि हाँ, तो आपका AI एजेंट वही उपकरण देखेगा जो आप देखते हैं। यदि नहीं, तो कृपया चरण-दर-चरण निदान पर वापस आएँ — आप शायद एक पथ भूल गए हैं या NVM सिमलिंक अपडेट नहीं है।

इस परीक्षण को स्वचालित करेंइस जाँच को अपने टूल्स के हर अपडेट के बाद चलाए जाने वाले healthcheck स्क्रिप्ट में जोड़ें। एक`zsh -c 'which java && which node && which gh'`CI, यह आश्चर्य के खिलाफ एक बीमा है।

_ गैर-इंटरैक्टिव शेल एक चुपचाप बैठे मेहमान की तरह होता है: वह केवल वही पढ़ता है जो प्रवेश द्वार पर प्रदर्शित होता है। यदि PATH ड्राइंग रूम में है, तो वह इसे कभी नहीं देख पाएगा। _

लिंक

आधिकारिक दस्तावेज़

उल्लेखित उपकरण

संबंधित लेख