跳转到内容

.vibe.toml

.vibe.toml 文件包含共享配置,通常会提交到 git 并与团队成员共享。

将源仓库中的单个文件复制到 worktree:

[copy]
files = [".env", "config.json"]

files 数组支持 glob 模式,可以灵活地选择文件:

[copy]
files = [
"*.env", # 根目录下的所有 .env 文件
"**/*.json", # 所有 JSON 文件(递归)
"config/*.txt", # config/ 下的所有 .txt 文件
".env.production" # 精确路径依然可用
]

支持的模式:

模式 说明
* 匹配除 / 以外的任意字符
** 匹配包含 / 的任意字符(递归)
? 匹配任意单个字符
[abc] 匹配方括号内的任意字符

files_prependfiles_append(以及 dirscopy.symlink 和所有 [hooks] 数组对应的 _prepend / _append)不仅可以用在 .vibe.local.toml 中,也可以直接用在 .vibe.toml 里。在单个文件内,实际生效的数组为 prepend + 字段 + append

[copy]
files = [".env"]
files_append = [".env.local"]
# 实际生效: [".env", ".env.local"]

关于这些字段在两个文件之间如何相互作用,请参阅 .vibe.local.toml

递归复制整个目录:

[copy]
dirs = [
"node_modules", # 精确的目录路径
".cache", # 隐藏目录
"packages/*" # 匹配多个目录的 Glob 模式
]
Section titled “共享目录(symlink) ”

除了把目录复制到每个工作树,也可以用符号链接指向源仓库中的目录来共享它:

[copy]
dirs = ["node_modules"]
symlink = [".cache", ".turbo"]

原因: 在 APFS/Btrfs/XFS 上写时复制(CoW)克隆让 dirs 很快,但在不支持 reflink 的文件系统(以及 Windows)上,完整复制庞大的依赖或缓存目录既慢又浪费 磁盘空间。有些目录本来也不需要按工作树隔离——共享的构建缓存或下载缓存反而更好。

规则:

  • 条目是相对于仓库根目录的精确目录路径。不支持 Glob 模式(一个符号链接只指向 一个要共享的目录)。
  • 实际创建了链接symlink 条目优先于覆盖同一路径的任何其他复制来源—— 既包括 files/dirs 条目,也包括由 untrackedmodified 收集到的文件。 该路径会被链接而不是被复制。该判定在 glob 展开之后进行,因此即使 symlink = [".cache"]dirs = [".*"] 同时存在,.cache 仍然会被链接, 只有其他匹配项会被复制。共享目录下层的路径(及其父目录)同样会被排除, 因此复制绝不会穿过链接写入源仓库。在不区分大小写的文件系统(APFS、NTFS)上, .Cache.cache 是同一个目录条目,因此排除判定也会忽略大小写。
  • 目标必须存在于源仓库中且不能越出仓库范围。目标不存在、路径逃逸出仓库,或者操作 系统拒绝创建链接(未开启开发者模式的 Windows)时会打印警告并继续执行 vibe start——工作树仍然可用。此时并没有创建链接,因此指向同一路径的 files/dirs 条目仍会照常复制。
  • 工作树中该路径上已存在的真实文件或目录不会被替换;只有过期的符号链接会被刷新。
  • vibe clean 只删除链接,绝不删除它指向的目录。

控制并行执行的目录复制操作数量:

[copy]
concurrency = 8
  • 默认值4
  • 取值范围132
  • 在存储速度较快的系统(NVMe、SSD)上,提高该值可以加快复制
  • 降低该值可以减少系统资源占用

通过环境变量覆盖:

Terminal window
VIBE_COPY_CONCURRENCY=16 vibe start feat/my-feature

环境变量的优先级高于配置文件中的设置。

vibe 会根据你的系统自动选择最佳的复制策略:

策略 使用场景 平台
Clone (CoW) APFS 上的目录复制 macOS
Clone (reflink) Btrfs/XFS 上的目录复制 Linux
rsync 无法使用 clone 时的目录复制 macOS/Linux
robocopy (/MT) 目录复制 Windows
Standard 文件复制,或作为兜底方案 全部

工作原理:

  • 文件复制:始终使用原生的 copyFile(),以获得最佳的单文件性能
  • 目录复制:自动使用当前可用的最快方式

优势:

  • Copy-on-Write 只复制元数据而非实际数据,因此速度极快
  • 无需配置 - 最佳策略会被自动检测
  • 自动回退机制确保复制始终可用

关于 pre/post 钩子的配置细节,请参阅钩子

在 worktree 创建之后、父仓库的 pre_start 钩子执行之前,加载所选直接子模块中已信任的 .vibe.toml 文件:

[submodules]
configs = ["libs/foo", "vendor/bar"]
  • 默认值[]
  • 每一项都必须与 .gitmodules 中某个直接子模块的路径完全匹配
  • 会在新的 worktree 中执行 git submodule update --init -- <paths>
  • 使用各子模块自身的 trust entry 加载其 .vibe.toml / .vibe.local.toml
  • 子模块配置中的钩子和 copy 规则会以子模块根目录为基准解析路径并执行
  • --no-hooks 会跳过子模块的钩子,但不会跳过子模块的初始化
  • --no-copy 会跳过子模块的 copy 规则
  • 子模块更新、trust、路径校验或 setup 失败都会中断 vibe start,已创建的 worktree 会保留下来以便排查

vibe list 添加一个 SUMMARY 列,其内容由你提供的命令生成:

[summary]
command = "./examples/summary/last-commit.sh"
timeout_seconds = 30
  • command — 一行 shell 命令。只有设置了它,该列才会出现
  • timeout_seconds — 命令被强制终止前允许运行的时长。默认值30范围13600

只要设置了 command,该列就会存在,无论命令是否为某个 worktree 给出了答案。命令没有回答的 worktree 只会显示一个空单元格。

命令每次 vibe list 只运行一次,在主 worktree 中执行,待处理的 worktree 批次通过 stdin 传入:

{
"worktrees": [
{
"name": "feat/login",
"path": "/abs/path/to/wt",
"base": "develop",
"head": "0f1e2d3c…"
}
]
}

namepath 始终是字符串;basehead 在未知时为 null。这里只会出现摘要尚未被缓存的 worktree。

命令必须在 stdout 上打印一个 JSON 对象,将 name 映射到摘要文本:

{ "feat/login": "添加登录表单" }

命令未提及的名称不会获得摘要,也不会被缓存,因此下次运行会再次询问。

一次批处理,而非每个 worktree 一次调用:真正有价值的摘要命令是 LLM 调用和全仓库查询,N 次调用意味着 N 倍的延迟。单次调用还能让命令在通观全局后给出对比性的回答。

名称重复的 worktree 会被排除:答案以 name 为键,而两个处于 detached HEAD 的 worktree 可能共用同一个目录 basename。当批次中出现重复名称时,这些 worktree 会被完全排除在请求之外(在 --verbose 下会报告)——缺少摘要比把摘要显示在错误的行上更安全。

命令的 stdout 被视为不可信输入,在存储或显示之前都会施加边界:

限制 行为
stdout 1 MiB 读取在上限处停止,过长的回答会被拒绝
stderr 64 KiB 读取在上限处停止(只有首行会被引用)
每个 worktree 4 个条目 远超请求规模的回答会被拒绝
字符串值的 JSON 对象 数组、数字或嵌套对象属于违反契约
仅第一行 多行摘要会在第一个换行处被截断
500 个字符 更长的文本会以 截断
终端控制字符 与分支名一样,在显示前被无害化

如果命令以非零状态退出、无法启动、超时,或打印了违反契约的内容,vibe list发出警告并继续执行。此前缓存的摘要会代替空单元格显示,因为略微陈旧的答案也比什么都没有更有价值。在 --json 模式下,警告会被抑制以保持 payload 可解析。

摘要按仓库缓存在 $XDG_CACHE_HOME/vibe/summaries/(当 XDG_CACHE_HOME 未设置或格式不正确时,为 $HOME/.cache/vibe/summaries/)。对未发生变化的仓库第二次运行 vibe list 时,命令完全不会被执行

缓存的摘要会在以下情况失效:

  • worktree 的 HEAD 发生变化(新提交、切换检出),
  • worktree 的未提交改动发生变化(git status 报告了不同的内容),
  • worktree 的分支名发生变化(vibe rename),
  • worktree 的 base 发生变化(git branch --set-upstream-to),
  • [summary] command 本身发生变化——此时会丢弃全部条目,因为旧命令生成的摘要无法说明新命令的含义

timeout_seconds 不参与失效判断:它只改变等待时长,不改变答案本身。已不存在的 worktree 对应的条目会在每次运行时被清理;缓存文件损坏或无法读取时会直接重新生成。

开箱即用的脚本位于 examples/summary/(面向 Unix;每个脚本的文件头都写有用法和 [summary] 配置片段):

脚本 该列显示的内容 依赖
last-commit.sh 每个 worktree 最新提交的 subject jqgit
note-file.sh 每个 worktree 的 .vibe/note.txt 首行 jq
claude.sh 由 LLM 撰写的、说明每个 worktree 在做什么 jqgitclaude

使用外部脚本自定义 worktree 的目录路径。

指定一个输出 worktree 路径的脚本:

[worktree]
path_script = "~/.config/vibe/worktree-path.sh"

脚本会接收到以下环境变量:

变量 说明 示例
VIBE_REPO_NAME 仓库名称 my-project
VIBE_BRANCH_NAME 分支名称 feat/new-feature
VIBE_SANITIZED_BRANCH 净化后的分支名称(/- feat-new-feature
VIBE_REPO_ROOT 仓库根目录路径 /path/to/repo

脚本示例:

~/.config/vibe/worktree-path.sh
#!/bin/bash
echo "${HOME}/worktrees/${VIBE_REPO_NAME}-${VIBE_SANITIZED_BRANCH}"
[copy]
files = [
".env",
".env.local",
"**/*.secret"
]
dirs = [
"node_modules",
".cache",
"vendor"
]
concurrency = 8
[hooks]
pre_start = ["echo '正在准备 worktree...'"]
post_start = [
"pnpm install",
"pnpm db:migrate",
"pnpm build"
]
pre_clean = ["git stash"]
post_clean = ["echo '清理完成'"]
[submodules]
configs = ["libs/foo"]
[summary]
command = "./examples/summary/last-commit.sh"
timeout_seconds = 30
[worktree]
path_script = "~/.config/vibe/worktree-path.sh"