mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-10-26 10:45:22 +00:00
配置统一:权限策略、硬防御、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>
81 lines
7.5 KiB
Markdown
81 lines
7.5 KiB
Markdown
# 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 四个阶段,确认对话框会预先列出完整计划:
|
||
|
||
```jsonc
|
||
{
|
||
"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` 不可用。
|
||
|
||
## 测试
|
||
|
||
```bash
|
||
npm test # node --test test/*.test.ts
|
||
npm run check
|
||
```
|
||
|
||
测试在临时目录里创建真实 git 仓库;状态与会话测试使用隔离的 `PI_CODING_AGENT_DIR`。
|