-
Notifications
You must be signed in to change notification settings - Fork 207
Description
Background
Software development often requires project-specific tools be added to the PATH. Often, there are toolchains which generate such PATH entries dynamically -- Nix uses flake.nix files (or, following older releases, shell.nix files) to specify which software to download and install if necessary and then place in the PATH; uv, conda, and many other similar tools exist for other ecosystems.
One generic way to use any/all of these tools to update one's shell environment based on the current working directory is to use direnv, which allows a .envrc file to specify how to set up an environment for a given project. Used in an interactive shell, direnv sets up related variables/tools when the user cds into a directory, and reverts to the prior state when that directory is exited.
When Claude Code executes shell commands, it spawns fresh subprocesses that don't benefit from the interactive shell hooks that makes direnv seamless for human developers. Each Bash() tool invocation runs in isolation, without the PROMPT_COMMAND or precmd hooks that normally trigger direnv export.
This means that even when a developer has carefully configured their project's .envrc to set up the correct toolchain, the AI assistant operates in a "bare" environment — potentially using system-default versions of tools, missing project-specific binaries entirely, or lacking environment variables that affect build behavior.
Why This Matters
- Correctness: Commands that work in the developer's terminal may fail or behave differently when run by the AI agent
- Reproducibility: The AI operates in a different environment than the human, leading to "works for me" situations in reverse
- Security: Without proper environment isolation, the AI might inadvertently use or modify global toolchains rather than project-scoped ones
Opportunity
Claude Code already provides the CLAUDE_ENV_FILE mechanism — a script sourced before each Bash command execution. This offers a clean integration point: Maestro could detect .envrc presence and configure CLAUDE_ENV_FILE to invoke direnv export bash, ensuring the AI's shell environment matches what the developer sees interactively.
Proposed Behavior
When spawning an AI agent session, Maestro could:
- Detect whether the project directory contains a
.envrcfile - Verify that direnv is installed and the
.envrcis allowed (the user has previously rundirenv allow) - Configure CLAUDE_ENV_FILE to source the direnv-exported environment before each command
This should be automatic and require no per-project configuration from the user — if they've already set up direnv for their workflow, Maestro should respect that setup.
User-Facing Considerations
Warnings: When Maestro detects a .envrc but cannot activate it (direnv not installed, or .envrc not yet allowed), a non-blocking notification could inform the user rather than silently falling back to the bare environment.
No action when not applicable: Projects without .envrc would see no change in behavior — this feature should be invisible to users who don't use direnv.
Scope Limitations
This proposal intentionally limits scope to direnv-based workflows. While it would be possible to also detect flake.nix or shell.nix files and invoke Nix directly, direnv already serves as a shared integration layer for these tools (with basic support built in, and extended support available via https://github.com/nix-community/nix-direnv), and supporting direnv alone covers not just Nix but also many other ecosystems (node, opam, julia, php, pip, pyenv -- search for layout in https://direnv.net/man/direnv-stdlib.1.html for a current list) without Maestro needing ecosystem-specific knowledge.
Implementation Notes
The integration point is src/main/ipc/handlers/process.ts, where process:spawn resolves environment variables before invoking ProcessManager.spawn(). The existing effectiveCustomEnvVars flow (lines 172-175) provides a pattern for injecting CLAUDE_ENV_FILE without disrupting other environment configuration.
Detection logic would fit naturally in a new utility module (e.g., src/main/utils/direnv.ts), following the pattern established by src/main/utils/cliDetection.ts for detecting external tools like gh and cloudflared.
Key considerations:
- Efficiency:
direnv export bashis fast (~10-50ms after the environment is cached), so running it per-command viaCLAUDE_ENV_FILEadds negligible overhead - GC roots (Nix-specific): For Nix users, nix-direnv automatically maintains garbage collection roots in
.direnv/, so builds won't be collected while the project exists — no additional handling needed from Maestro - Allowed check:
direnv statusoutput indicates whether the.envrcis allowed; this avoids attempting activation that would fail - SSH sessions: For remote execution, the local
.envrcpath isn't meaningful — detection could be skipped whensessionSshRemoteConfigis active, or deferred to a future enhancement for remote direnv support