Files
云服务部-叶林立andDevin <158243242+devin-ai-integration[bot]@users.noreply.github.com> f9a7f743dd feat: 接入 Pi 内置 codemode,并修复全面审查发现的问题
- codemode:Tool Search 常驻(alwaysEnabled)、权限 allow(嵌套调用仍逐个过权限门)、
  tools.jsonc 登记使用规则;RTK 跳过 parentToolCallId 嵌套调用,避免丢失 bash
  structuredContent;Tool Search 目录过滤 hidden / codemode(script-only)exposure;
  扩展加载测试加载 builtin:codemode 并断言其常驻激活
- 搜索 key 不再经命令行参数传递:search-auth.mjs 改为 --set-stdin(先读校验再加锁),
  configure.sh 交互输入经管道、非交互用 --<provider> -
- CodeGraph 客户端监听子进程 stdin error,避免 EPIPE 未处理导致崩溃
- 移除 PI_TOOL_SEARCH/HERMES_MEMORY/RTK_CONFIG_PATH 环境变量入口,测试改用工厂 configPath 注入
- pi-subagents 固定 vitest / coverage-istanbul 4.1.10,修复 npm ls 版本冲突
- 补 pi-web-search / pi-worktree UPSTREAM.md 与 pi-minimal-footer AGENTS.md,
  files 白名单补齐并加测试;.codegraph/.gitignore 移出跟踪
- detect_kitty 优先识别官方安装目录

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-21 23:18:34 -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。