/** * plan-mode 的纯逻辑层:三态状态机 + plan 阶段的 bash 写操作判定。 * * 不 import pi / pi-tui,所以 `node --test clients/pi/extensions/plan-mode/plan.test.ts` * 能直接跑到每个分支。 * * ## 状态机 * * normal ──shift+tab / enter_plan_mode──▶ plan ──exit_plan_mode + 用户批准──▶ execute * ▲ │ │ * └───────── 用户拒绝 / shift+tab ───────┘◀──── 步骤全部 [DONE:n] ────────────┘ * * plan 阶段进入时对 `pi.getActiveTools()` 做一次快照,退出时**原样还原**:本机 pi 的 * 工具表里有二十多个扩展动态注册的工具(mcp / ask_user_question / task_set / task_update …), * 硬编码白名单会把它们全吃掉(官方 plan-mode 示例就是那么写的,所以这里没照抄)。 * * ## bash 判定 * * 判定「这条命令会不会改工作区」,粒度是**简单命令** —— 用 `;` `&` `|` `&&` `||` `(` `)` * 和换行切开,所以 `cat a.txt && rm -rf b` 会被 rm 那一段拦住,而不是被 `cat` 那一段放过。 * 识别四类写操作: * * 1. 写重定向:`>` / `>>`(`2> f` 的 fd 前缀不算参数、`2>&1` 这种 fd 复制不算写入) * 2. 写命令:rm / mv / cp / sed -i / tee / dd / git commit / npm install / sudo … * 3. 全局危险参数:`--fix` / `--write` / `--in-place`(eslint --fix、prettier --write …) * 4. heredoc 正文先剥掉再判,免得「读一个 heredoc」里顺带出现的一句写命令被误判 * * **这是给配合的模型用的护栏,不是沙箱。** 模型被明确告知 plan 阶段不能改动代码,这里 * 只负责拦下它顺手打出的写操作并把原因回给它(错误结果就是模型的反馈)。要真正防住 * 恶意写入得靠操作系统级沙箱,不在这个扩展的范围内。两个已知的漏网形状:双引号内的 * `$(...)` 命令替换、以及 `npm run