文章
并行 Vibe Coding 的密钥管理:一个 .envrc 搞定所有 Worktree#
用 Claude Code 等 Vibe Coding 工具做开发时,经常需要同时开多个 Agent 并行工作。最常见的做法是通过 Git Worktree 为每个 Agent 创建独立的工作目录——每个 worktree 有自己的文件和分支,互不干扰。
问题在于:每个新创建的 worktree 都是一个「干净」的工作目录,项目运行所需的密钥文件(API keys、OAuth tokens、各种 credentials)通常都不存在。于是每次创建 worktree 之后,都得手动把 .env、.env.local 或其他密钥文件复制过去。手动维护这些密钥会严重阻塞工作流程,也容易出错。
目前有两条比较实用的路线:
- 用 direnv 在 worktree 的公共父目录自动加载环境变量。
- 用 Claude Code 官方支持的
.worktreeinclude自动复制 gitignored 文件。
这篇文章重点介绍 direnv 方案,同时解释它和 .worktreeinclude 的取舍。
direnv 如何工作#
direnv 是一个 shell 扩展,用 Go 编写,支持 bash、zsh、fish、nushell、PowerShell 等多种 shell。它的工作方式很简单:
- 在每次 shell prompt 刷新前,direnv 检查当前目录及 父目录 中是否存在
.envrc文件。 - 如果存在且已授权,就在一个 bash 子进程中执行
.envrc,把导出的环境变量注入当前 shell。 - 离开该目录时,自动卸载这些环境变量。
「父目录继承」这个特性正好适用于 worktree 场景:只要在 worktree 的公共父目录放一个 .envrc,所有子目录中的 worktree 都能自动获取到密钥配置。
安装和配置:
# macOS
brew install direnv
# 在 ~/.zshrc 末尾添加 hook(zsh 为例)
eval "$(direnv hook zsh)"
安装完成后重启 shell 即可。direnv 有一个安全机制:新的或修改过的 .envrc 文件需要执行 direnv allow 才能生效,防止恶意目录注入环境变量。
路径一:VS Code GitLens Worktree#
使用 VS Code 的 GitLens 插件时,通过 Create Worktree 命令可以从当前仓库创建 worktree。假设当前仓库目录是 foo,新创建的分支是 feature/bar,GitLens 通常会将 worktree 放在与 foo 同级的 foo.worktree/feature-bar/ 下(分支名中的 / 会被替换成 -)。
目录结构如下:
foo/ # 主仓库
foo.worktree/ # GitLens worktree 根目录
├── .envrc # ← direnv 配置,所有 worktree 自动继承
├── feature-bar/ # feature/bar 分支的 worktree
├── feature-baz/ # feature/baz 分支的 worktree
└── bugfix-123/ # bugfix/123 分支的 worktree
只需要在 foo.worktree/ 目录下创建一个 .envrc 文件:
# foo.worktree/.envrc
export OPENAI_API_KEY="sk-..."
export ANTHROPIC_API_KEY="sk-ant-..."
export DATABASE_URL="postgres://..."
然后授权一次:
cd foo.worktree
direnv allow
之后无论创建多少个 worktree,进入任何一个子目录时 direnv 都会自动加载这些环境变量。不需要在每个 worktree 里再复制 .env 文件。
路径二:Claude Code 的 --worktree 参数#
Claude Code 提供了内置 worktree 支持。通过 --worktree(或 -w)参数可以直接创建一个隔离的 Git worktree 并在其中启动 Claude:
# 指定名称创建 worktree
claude --worktree feature-auth
# 简写形式
claude -w feature-auth
# 不指定名称,自动生成随机名(如 bright-running-fox)
claude --worktree
默认情况下,worktree 会创建在 <repo>/.claude/worktrees/<name>/ 下,分支命名为 worktree-<name>。Claude Code 官方文档建议把 .claude/worktrees/ 加入 .gitignore,避免 worktree 内容作为主仓库的 untracked files 出现。
同样的思路,可以在 .claude/worktrees/ 目录下放一个 .envrc:
my-project/
├── .claude/
│ └── worktrees/
│ ├── .envrc # ← direnv 配置
│ ├── feature-auth/ # claude --worktree feature-auth 创建
│ ├── bugfix-login/ # claude --worktree bugfix-login 创建
│ └── bright-running-fox/# claude --worktree 自动生成
├── src/
└── ...
cd .claude/worktrees
cat > .envrc << 'EOF'
export OPENAI_API_KEY="sk-..."
export ANTHROPIC_API_KEY="sk-ant-..."
EOF
direnv allow
之后所有通过 claude --worktree 创建的 worktree 都会自动继承这些环境变量。
Claude Code 的 subagent 也支持 worktree 隔离。可以在对话里要求 Claude “use worktrees for your agents”,也可以在自定义 subagent 的 frontmatter 中设置:
isolation: worktree
这样 subagent 会在独立 worktree 中工作。只要这些 worktree 仍然位于 .claude/worktrees/ 下,它们也能享受到父目录 .envrc 的自动加载。
官方替代方案:.worktreeinclude#
Claude Code 现在也提供了更直接的方式:在项目根目录创建 .worktreeinclude 文件,让 Claude 创建 worktree 时自动复制指定的 gitignored 文件。
例如:
# .worktreeinclude
.env
.env.local
config/secrets.json
官方设计里,.worktreeinclude 使用类似 .gitignore 的语法;只有同时满足下面两个条件的文件才会被复制:
- 匹配
.worktreeinclude中的规则。 - 本身是 gitignored 文件。
这意味着 tracked 文件不会被意外复制,主要目标就是解决 .env、.env.local、本地 secrets config 这类文件在新 worktree 中缺失的问题。
direnv 和 .worktreeinclude 怎么选#
两种方案解决的是相似问题,但适用场景不完全一样。
适合用 direnv 的情况#
- 你不想在每个 worktree 中生成任何密钥文件。
- 你希望所有 worktree 共享同一组本地环境变量。
- 你的工具链主要从 shell 环境变量读取密钥。
- 你希望在 GitLens、自建 worktree、Claude Code worktree 等多种路径下复用同一套机制。
适合用 .worktreeinclude 的情况#
- 你的项目必须读取
.env或.env.local文件。 - 你希望 worktree 创建后就像一个完整项目副本。
- 你只关心 Claude Code 创建的 worktree。
- 你愿意在每个 worktree 中保留一份本地密钥文件副本。
可以混合使用吗?#
可以,但要小心重复和冲突。
一种比较稳妥的做法是:
- 通用的 API key、模型 key 放在父目录
.envrc中。 - 项目确实依赖的
.env.local、本地配置文件用.worktreeinclude复制。 - 避免同一个变量既在
.envrc里定义,又在.env.local里定义,除非你明确知道加载顺序。
关于 IDE 和非交互环境#
direnv 的自动加载依赖 shell hook。也就是说,它最稳定的使用场景是:你在终端里 cd 到 worktree,然后从这个终端启动开发命令、测试命令或 Claude Code。
如果你直接从 IDE、GUI App、后台任务或非交互脚本启动进程,它们未必会自动继承 direnv 注入的变量。此时可以考虑:
- 使用 IDE 的 direnv 插件或集成。
- 从已经加载 direnv 的终端启动 IDE。
- 在脚本里显式使用
direnv exec <dir> <command>。 - 对 Claude Code worktree 使用
.worktreeinclude复制必要的.env文件。
关于安全性#
需要注意的是:direnv 加载的环境变量会直接出现在 shell 环境中。Agent 执行 env 或 printenv 就能看到所有密钥的值。这个方案 并不能阻止 Agent 访问密钥。
.worktreeinclude 也一样,它只是自动复制密钥文件,并不提供运行时隔离。Agent 如果有读取文件权限,仍然可以读取这些文件。
因此,这两种方案真正解决的问题是:
- 减少重复操作:不需要每次创建 worktree 后手动复制密钥文件。
- 减少误提交风险:密钥文件仍然保持 gitignored;direnv 方案甚至可以不在每个 worktree 中生成
.env文件。 - 降低配置漂移:多个并行 worktree 更容易使用同一组开发密钥。
- 保留基本授权审查:direnv 的
.envrc必须通过direnv allow明确授权才会生效。如果文件内容被修改,需要重新授权。
如果需要更严格的密钥隔离,应该考虑 vault、1Password CLI、AWS/GCP/Azure secret manager、短期 token、CI/CD 注入、容器隔离或最小权限账号,而不是依赖本地环境变量或复制 .env 文件。
对于日常的 Vibe Coding 并行开发场景,direnv 在便利性和安全性之间取得了不错的平衡;而 .worktreeinclude 则是 Claude Code 官方提供的、更直接的“复制本地配置文件”方案。
.gitignore 配置#
使用 Claude Code 的 --worktree 时,建议在 .gitignore 中添加:
.claude/worktrees/
这样 worktree 的内容不会出现在主仓库的 untracked files 中。.envrc 文件如果放在 .claude/worktrees/ 下,也会被一并忽略,不用担心被提交。
对于 GitLens 的方式,foo.worktree/ 目录通常在主仓库外面,天然不会被 Git 追踪。不过如果你自定义了 worktree 目录位置,仍然建议检查一下 git status,确认密钥文件或 worktree 目录没有出现在待提交列表里。
推荐配置#
如果你主要使用 Claude Code,并且项目依赖 .env.local,最简单的配置是:
# .worktreeinclude
.env.local
如果你同时使用 Claude Code、GitLens 和手动 git worktree add,并且密钥主要通过环境变量读取,更通用的配置是:
# 公共 worktree 父目录下的 .envrc
export ANTHROPIC_API_KEY="sk-ant-..."
export OPENAI_API_KEY="sk-..."
然后执行:
direnv allow
如果两者都需要,就把不敏感或项目必须读取的本地配置交给 .worktreeinclude,把跨项目通用的开发密钥交给 direnv。重点不是追求“绝对安全”,而是减少重复复制、降低误提交概率,并让多个 Agent 并行工作时的环境足够一致。
