giving claude code a flake-defined bash tool
slightly more than direnv export bash on session start in practice
April 2026
I use Nix flakes and direnv to ergonomically get per-project dev shells. This keeps things clean and lets me avoid a big pile of globally-available dev toolchains on my path. Unfortunately, this also made it annoying to use desktop Claude Code’s worktrees feature, because Claude’s Bash tool would be missing all the expected shell context.
These are my preferred settings for giving Claude’s Bash tool a dev shell on my macOS system.
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"sandbox": {
"enabled": true,
"network": {
// `use flake` -> direnv invokes `nix print-dev-env`
"allowUnixSockets": ["/nix/var/nix/daemon-socket/socket"]
},
"filesystem": {
"allowWrite": [
"~/.cache/nix/",
"~/.local/share/direnv/allow/",
"/dev/stderr",
]
}
},
"hooks": {
"SessionStart": [
{
"matcher": "startup|resume|compact|clear",
"hooks": [
{
"type": "command",
// `direnv allow` for worktrees; also assumes the `.envrc` will be copied,
// either via `.worktreeinclude` or something else.
"command": "direnv allow >/dev/null 2>&1; OUT=$(direnv export bash 2>/dev/null); if [ -n \"${CLAUDE_ENV_FILE:-}\" ]; then printf '%s' \"$OUT\" >> \"$CLAUDE_ENV_FILE\"; fi"
}
]
}
]
}
}
The unconditional direnv allow also means that you should be wary of
setting these values in the global settings. I prefer to just set this per-project,
whitelisting projects that auto-export direnv environments.
I hope this saves someone the bit of time it took me to converge on this.