Files
my-pi/pi-worktree
云服务部-叶林立andDevin <158243242+devin-ai-integration[bot]@users.noreply.github.com> 2aa1be0b54 feat: 组合包配置以包内 config/ 为唯一来源,并新增受保护写入人工审批
配置统一:权限策略、硬防御、Tool Search、提示词封面以及新迁入的
RTK、pi-compaction、pi-worktree、pi-hermes-memory 配置全部由扩展
直接读取已安装组合包内的 config/,pi install / pi update 即完成部署;
删除 rebuild-bundle-config.mjs 与 bundle-manifest.json,my-pi/ 只保留
日志与缓存。update.sh 成功升级后将默认 git 来源的已安装副本
reset 回发布版本。AGENTS.md 新增「组合包配置原则」。

脚本整合:search_config.sh / ssh_config.sh 合并为 configure.sh
search|ssh;install.sh / update.sh 共享函数抽到 scripts/lib.sh,远程
安装器改为先下载到临时文件再执行,下载失败可被检测。

权限:hard-guards 新增 protectedWrites,写入 Pi 凭据/设置、已安装
组合包与扩展目录、pi-ssh 凭据库时一律人工确认(跳过 AutoReview、
yolo 与 Session grant,无 UI 时拒绝);放行 ls、LSP 查询、ctx 只读、
web_search、Chrome 只读与 git 只读命令,skill_manage 改为 ask。

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-20 01:05:31 -07:00
..

pi-worktree

本仓库自维护的 Pi 扩展:只用斜杠命令管理 git worktree,提供「一键拆分」(/wt new)与「一键合并」(/wt merge)。不注册任何模型可调用的工具。

所有 git 操作都通过 node:child_process 的 execFile 以 argv 形式调用(不经过 shell),普通命令 30 秒超时,worktree add / remove 为 120 秒。因为这些命令由用户显式触发,它们不经过 Bash 工具的权限策略。

命令

/wt 与别名 /worktree 等价;不带参数时弹出子命令菜单。

命令 说明
/wt new <slug> [--from <ref>] 在 <root>/<仓库名>/<slug> 创建 worktree,签出新分支 <branchPrefix><slug>(默认 wt/<slug>),确认后把当前会话切换进 worktree
/wt merge [--squash] [--keep] [--continue] [--abort] 在当前 worktree 内执行:确认 → rebase 到来源分支 → 快进合并(或 squash)回来源分支 → 切回来源目录并清理 worktree 与分支。--keep 保留 worktree、分支和登记;rebase 冲突保留时可用 --continue/--abort 续跑或放弃
/wt switch [<slug>|main] 切换到某个登记的 worktree;main 切回该 worktree 的来源 checkout。无参数时弹列表
/wt list 列出登记的 worktree:分支、路径、(缺失)、(有未提交改动)、+N 领先数,以及非本扩展管理的其它 git worktree
/wt remove <slug> [--force] 删除指定 worktree 与分支;有未提交改动或未合并提交时要求 --force 并二次确认。不能删除当前所在的 worktree
/wt prune 执行 git worktree prune 并清除目录已不存在的登记项
/wt reopen [<slug>] 重新读取来源目录的 .pi/worktree.json 并补跑环境初始化(已存在的文件/软链自动跳过);默认 slug 为当前所在 worktree

流程与安全规则

  • 拆分:确认对话框显示新分支、基准 ref 和短 SHA、目标路径;创建并登记后才切换会话。会话切换失败不会回滚已创建的 worktree,可用 /wt switch <slug> 重试。
  • 合并:必须先校验来源 checkout 仍在登记的分支上;worktree 未提交改动先经确认后 add -A + commit;随后展示 diffstat 并要求确认——rebase 改写分支的动作只发生在确认之后。确认后若来源目录有未提交改动先 stash push,再 rebase 到来源分支并合并,无论成败都在 finally 中 stash pop(pop 失败时提示 stash 名称)。rebase 冲突时弹出选择:「中止 rebase,保持原状」或「保留冲突,稍后手动解决」;保留后在 worktree 中解决冲突并 git add,再运行 /wt merge --continue 续走合并流程,或用 /wt merge --abort 放弃。默认只允许快进合并,--squash 压缩为单个提交;永远不会 push。
  • 删除:拒绝删除当前所在的 worktree;有未提交改动或领先提交时必须 --force 并明确列出将丢弃的内容。
  • 状态栏:进入登记的 worktree 后 footer 显示 🌳 <slug>,离开后清除。

会话切换机制

Pi 不支持修改进程 cwd。/wt new / merge / switch 通过 SessionManager.forkFrom(或为空会话写入最小 session 文件)生成 header.cwd 指向目标目录的新会话文件,再调用 ctx.switchSession 让 Pi 以新 cwd 重建运行时;切换后的收尾工作在 withSession 回调里用新 ctx 完成。

切换成功后,扩展会先往新会话追加一条 [pi-worktree] 工作目录已从 … 切换到 … 的模型可见提示(customType: pi-worktree、display: true,空闲时直接写入 session 文件,不触发新轮次),告知此前旧 cwd 下的路径可能已不存在。

状态登记与锁

登记文件为 <agentDir>/pi-worktree/state.json(目录 700、文件 600、原子写入)。所有写操作经 updateState 在 <path>.lock 锁文件内「读取-修改-保存」,防止多个 Pi 进程互相覆盖登记;锁文件陈旧 30 秒会被接管,正常操作下最长等待 5 秒。读取(list、补全、findByCwd)不加锁。

环境复刻:.pi/worktree.json

在来源 checkout(读取来源是 entry.sourcePath,不是新 worktree)提交 .pi/worktree.json 后,/wt new 创建 worktree 时会按顺序执行 复制 → 软链 → 环境文件 → postCreate 四个阶段,确认对话框会预先列出完整计划:

{
  "version": 1,                        // 必填
  "copy": [".env.local", ".env.*.local", "config/local.*"],
  "link": ["node_modules", ".gradle"],
  "env": { "PORT": "{{port}}", "DB_NAME": "app_{{slug}}" },
  "envFile": ".env.worktree",          // 默认 .env.worktree
  "portBase": 3000,                    // 默认 3000
  "postCreate": [
    { "command": "npm", "args": ["ci"], "timeoutMs": 600000 }
  ]
}

规则:

  • 复制:只复制未被 git 跟踪的文件(已跟踪文件应通过提交共享);* / ? 通配符只允许出现在路径最后一段,不支持 **。目录暂不复制。
  • 软链:目标必须被 gitignore 忽略,否则拒绝软链以避免改动写回来源目录。
  • 环境文件:env 渲染为 KEY=value 逐行写入 envFile(权限 600);模板变量:{{slug}}、{{pathSlug}}、{{branch}}、{{path}}、{{sourcePath}}、{{sourceBranch}}、{{port}},其中 port = portBase + fnv1a32(slug) % 1000。
  • info/exclude 兜底:未被 .gitignore 覆盖的软链(如 node_modules/ 尾斜杠规则不匹配符号链接)和环境文件会写入 .git/info/exclude(仓库内所有 worktree 共享同一 common dir),确保 /wt merge 的 commitAll 不会把它们提交进来源分支。
  • postCreate:仅当项目受 Pi 信任(ctx.isProjectTrusted())时执行,未受信任的项目只列出步骤并跳过;步骤以 argv 形式调用 execFile(不经 shell),并向步骤注入 PI_WT_PATH / PI_WT_SOURCE_PATH / PI_WT_BRANCH / PI_WT_SOURCE_BRANCH / PI_WT_SLUG / PI_WT_PORT 环境变量;任一步失败即停止。
  • 永不覆盖:所有产物(复制、软链、环境文件)在目标已存在时跳过,因此 /wt reopen 可以安全地增量补跑;初始化结果记录在登记项的 setup 字段,/wt list 会标注 (环境初始化失败:<步骤>)。

状态与配置

  • 登记文件:<agentDir>/pi-worktree/state.json(getAgentDir(),遵循 PI_CODING_AGENT_DIR),目录权限 700、文件权限 600,原子写入;文件损坏或版本未知时命令报错中止,不会被静默覆盖。不放在 <agentDir>/my-pi/ 下,因为该目录只承载可随时丢弃的运行时状态。
  • 配置文件:已安装组合包内的 config/pi-worktree.json(仓库 config/ 是唯一来源,pi update 随组合包刷新;不读取 settings.json)。键:
    • root:worktree 根目录,默认 ~/.pi/worktrees(只展开开头的 ~,展开后必须是绝对路径)。该默认值依赖本机路径,因此组合包附带的 JSON 不写 root,由代码运行时计算。worktree 路径为 <root>/<主 worktree 目录名>/<slug>。
    • branchPrefix:新分支前缀,默认 wt/。
  • slug 规则:A-Za-z0-9._- 与 / 分层,最长 64 字符,不能以 - 开头、不能含 ..、不能以 .lock 结尾,保留名 main / HEAD 不可用。

测试

npm test    # node --test test/*.test.ts
npm run check

测试在临时目录里创建真实 git 仓库;状态与会话测试使用隔离的 PI_CODING_AGENT_DIR。