Shadowenv
reversible directory-local environment variable manipulations
Install / Use
/learn @Shopify/ShadowenvREADME
Shadowenv
Shadowenv provides a way to perform a set of manipulations to the process environment upon entering a directory in a shell. These manipulations are reversed when leaving the directory, and there is some limited ability to make the manipulations dynamic.

In order to use shadowenv, add a line to your shell profile (.zshrc, .bash_profile,
or config.fish) reading:
eval "$(shadowenv init bash)" # for bash
eval "$(shadowenv init zsh)" # for zsh
shadowenv init fish | source # for fish
For nushell, run shadowenv init nushell which will install a hook into nushell's autoload directory.
With this code loaded, upon entering a directory containing a .shadowenv.d directory,
any *.lisp files in that directory will be executed and you will see "activated shadowenv." in your
shell.
The syntax for the .shadowenv.d/*.lisp files is Shadowlisp,
a minimal Scheme-like language.
Unlike other tools like direnv, this has the interesting property of allowing
us to do things like simulate chruby reset upon entry into a directory without the user having
chruby installed (and undo these changes to the environment when cd'ing back out):
(provide "ruby")
(when-let ((ruby-root (env/get "RUBY_ROOT")))
(env/remove-from-pathlist "PATH" (path-concat ruby-root "bin"))
(when-let ((gem-root (env/get "GEM_ROOT")))
(env/remove-from-pathlist "PATH" (path-concat gem-root "bin"))
(env/remove-from-pathlist "GEM_PATH" gem-root))
(when-let ((gem-home (env/get "GEM_HOME")))
(env/remove-from-pathlist "PATH" (path-concat gem-home "bin"))
(env/remove-from-pathlist "GEM_PATH" gem-home)))
The intention isn't really for users to write these files directly, nor to commit them to repositories , but for other tool authors to generate configuration on the user's machine. Here's an example of a generated Shadowlisp file for activating ruby 2.7.1:
(provide "ruby" "2.7.1")
(when-let ((ruby-root (env/get "RUBY_ROOT")))
(env/remove-from-pathlist "PATH" (path-concat ruby-root "bin"))
(when-let ((gem-root (env/get "GEM_ROOT")))
(env/remove-from-pathlist "PATH" (path-concat gem-root "bin")))
(when-let ((gem-home (env/get "GEM_HOME")))
(env/remove-from-pathlist "PATH" (path-concat gem-home "bin"))))
(env/set "GEM_PATH" ())
(env/set "GEM_HOME" ())
(env/set "RUBYOPT" ())
(env/set "RUBY_ROOT" "/opt/rubies/2.7.1")
(env/prepend-to-pathlist "PATH" "/opt/rubies/2.7.1/bin")
(env/set "RUBY_ENGINE" "ruby")
(env/set "RUBY_VERSION" "2.7.1")
(env/set "GEM_ROOT" "/opt/rubies/2.7.1/lib/ruby/gems/2.7.0")
(when-let ((gem-root (env/get "GEM_ROOT")))
(env/prepend-to-pathlist "GEM_PATH" gem-root)
(env/prepend-to-pathlist "PATH" (path-concat gem-root "bin")))
(let ((gem-home
(path-concat (env/get "HOME") ".gem" (env/get "RUBY_ENGINE") (env/get "RUBY_VERSION"))))
(do
(env/set "GEM_HOME" gem-home)
(env/prepend-to-pathlist "GEM_PATH" gem-home)
(env/prepend-to-pathlist "PATH" (path-concat gem-home "bin"))))
.shadowenv.d
The .shadowenv.d directory will generally exist at the root of your repository (in the same
directory as .git.
We strongly recommend creating gitignore'ing everything under .shadowenv.d (echo '*' > .shadowenv.d/.gitignore).
A .shadowenv.d will contain any number of *.lisp files. These are evaluated in the order in which
the OS returns when reading the directory: generally alphabetically. We strongly recommend using
a prefix like 090_something.lisp to make it easy to maintain ordering.
.shadowenv.d will also contain a .trust-<fingerprint> file if it has been marked as trusted. (see
the trust section).
Language
See https://shopify.github.io/shadowenv/ for Shadowlisp documentation.
Integrations
Shadowenv has plugins for multiple editors and/or IDEs:
Trust
If you cd into a directory containing .shadowenv.d/*.lisp files, they will not be run and you
will see a message indicating so. This is for security reasons: we don't want to enable random
stuff downloaded from the internet to modify your PATH, for example.
You can run shadowenv trust to mark a directory as trusted.
Technically, running shadowenv trust will create a file at .shadowenv.d/.trust-<fingerprint>,
indicating that it's okay for shadowenv to run this code. The .shadowenv.d/.trust-* file contains
a cryptographic signature of the directory path. The key is generated the first time shadowenv is
run, and the fingerprint is an identifier for the key.
Related Skills
node-connect
345.4kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
104.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
345.4kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
345.4kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
