Essay

并行 Vibe Coding 的密钥管理:一个 .envrc 搞定所有 Worktree#

By Asuka

用 Claude Code 等 Vibe Coding 工具做开发时,经常需要同时开多个 Agent 并行工作。最常见的做法是通过 Git Worktree 为每个 Agent 创建独立的工作目录——每个 worktree 有自己的文件和分支,互不干扰。

问题在于:每个新创建的 worktree 都是一个「干净」的工作目录,项目运行所需的密钥文件(API keys、OAuth tokens、各种 credentials)通常都不存在。于是每次创建 worktree 之后,都得手动把 .env.env.local 或其他密钥文件复制过去。手动维护这些密钥会严重阻塞工作流程,也容易出错。

目前有两条比较实用的路线:

  1. direnv 在 worktree 的公共父目录自动加载环境变量。
  2. 用 Claude Code 官方支持的 .worktreeinclude 自动复制 gitignored 文件。

这篇文章重点介绍 direnv 方案,同时解释它和 .worktreeinclude 的取舍。

direnv 如何工作#

direnv 是一个 shell 扩展,用 Go 编写,支持 bash、zsh、fish、nushell、PowerShell 等多种 shell。它的工作方式很简单:

  1. 在每次 shell prompt 刷新前,direnv 检查当前目录及 父目录 中是否存在 .envrc 文件。
  2. 如果存在且已授权,就在一个 bash 子进程中执行 .envrc,把导出的环境变量注入当前 shell。
  3. 离开该目录时,自动卸载这些环境变量。

「父目录继承」这个特性正好适用于 worktree 场景:只要在 worktree 的公共父目录放一个 .envrc,所有子目录中的 worktree 都能自动获取到密钥配置。

安装和配置

Bash
# 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/ 下(分支名中的 / 会被替换成 -)。

目录结构如下:

TEXT
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 文件:

Bash
# foo.worktree/.envrc
export OPENAI_API_KEY="sk-..."
export ANTHROPIC_API_KEY="sk-ant-..."
export DATABASE_URL="postgres://..."

然后授权一次:

Bash
cd foo.worktree
direnv allow

之后无论创建多少个 worktree,进入任何一个子目录时 direnv 都会自动加载这些环境变量。不需要在每个 worktree 里再复制 .env 文件。

路径二:Claude Code 的 --worktree 参数#

Claude Code 提供了内置 worktree 支持。通过 --worktree(或 -w)参数可以直接创建一个隔离的 Git worktree 并在其中启动 Claude:

Bash
# 指定名称创建 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

TEXT
my-project/
├── .claude/
│   └── worktrees/
│       ├── .envrc             # ← direnv 配置
│       ├── feature-auth/      # claude --worktree feature-auth 创建
│       ├── bugfix-login/      # claude --worktree bugfix-login 创建
│       └── bright-running-fox/# claude --worktree 自动生成
├── src/
└── ...
Bash
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 中设置:

YAML
isolation: worktree

这样 subagent 会在独立 worktree 中工作。只要这些 worktree 仍然位于 .claude/worktrees/ 下,它们也能享受到父目录 .envrc 的自动加载。

官方替代方案:.worktreeinclude#

Claude Code 现在也提供了更直接的方式:在项目根目录创建 .worktreeinclude 文件,让 Claude 创建 worktree 时自动复制指定的 gitignored 文件。

例如:

TEXT
# .worktreeinclude
.env
.env.local
config/secrets.json

官方设计里,.worktreeinclude 使用类似 .gitignore 的语法;只有同时满足下面两个条件的文件才会被复制:

  1. 匹配 .worktreeinclude 中的规则。
  2. 本身是 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 执行 envprintenv 就能看到所有密钥的值。这个方案 并不能阻止 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 中添加:

GITIGNORE
.claude/worktrees/

这样 worktree 的内容不会出现在主仓库的 untracked files 中。.envrc 文件如果放在 .claude/worktrees/ 下,也会被一并忽略,不用担心被提交。

对于 GitLens 的方式,foo.worktree/ 目录通常在主仓库外面,天然不会被 Git 追踪。不过如果你自定义了 worktree 目录位置,仍然建议检查一下 git status,确认密钥文件或 worktree 目录没有出现在待提交列表里。

推荐配置#

如果你主要使用 Claude Code,并且项目依赖 .env.local,最简单的配置是:

TEXT
# .worktreeinclude
.env.local

如果你同时使用 Claude Code、GitLens 和手动 git worktree add,并且密钥主要通过环境变量读取,更通用的配置是:

Bash
# 公共 worktree 父目录下的 .envrc
export ANTHROPIC_API_KEY="sk-ant-..."
export OPENAI_API_KEY="sk-..."

然后执行:

Bash
direnv allow

如果两者都需要,就把不敏感或项目必须读取的本地配置交给 .worktreeinclude,把跨项目通用的开发密钥交给 direnv。重点不是追求“绝对安全”,而是减少重复复制、降低误提交概率,并让多个 Agent 并行工作时的环境足够一致。

参考#