---
url: /wave-agent/specs/core/plan-mode.md
description: >-
  Shift+Tab、`/plan`、`EnterPlanMode` 进入的只读计划模式，`ExitPlanMode` 审批与三端计划面板预览（远控 Web
  页例外：计划正文渲染在确认对话框内）
---

# 功能规格说明：支持计划模式

**创建日期**：2026-01-19

## 用户场景与测试 *（必填）*

### 用户故事：切换到计划模式（优先级：P1）

作为用户，我希望将系统切换到"计划模式"，以便我可以让 LLM 分析代码库并提出计划，而不会意外修改任何文件。

**为什么是这个优先级**：这是功能的核心，允许用户安全地探索和规划复杂更改。

**独立测试**：可以通过按 Shift+Tab 并验证系统进入计划模式且新计划文件被创建来测试。

**验收场景**：

1. **假设**系统处于 "default" 模式，**当**用户按下 Shift+Tab 时，**则**系统切换到 "acceptEdits" 模式。
2. **假设**系统处于 "acceptEdits" 模式，**当**用户按下 Shift+Tab 时，**则**系统切换到 "plan" 模式。
3. **假设**系统处于 "plan" 模式，**当**用户按下 Shift+Tab 时，**则**系统切换到 "bypassPermissions" 模式。
4. **假设**系统处于 "bypassPermissions" 模式，**当**用户按下 Shift+Tab 时，**则**系统切换回 "default" 模式（完整循环为 `default` → `acceptEdits` → `plan` → `bypassPermissions` → `default`，与 CLI 循环及 GUI 模式菜单的选项顺序一致）。
5. **假设**系统切换到 "plan" 模式，**当**模式活动时，**则**在 `~/.wave/plans/` 中确定带有随机英文名称的计划文件路径。
6. **假设**系统处于 "plan" 模式，**当** LLM 在指定计划文件上使用 `Write` 或 `Edit` 工具时，**则**操作被允许。
7. **假设**系统处于 "plan" 模式，**当**用户查看 UI 时，**则**有清晰的视觉指示器表明计划模式已激活。

***

### 用户故事：计划模式中的规划和限制（优先级：P1）

作为用户，我希望 LLM 在计划模式下只能编辑计划文件，以便我的代码库在规划阶段保持不变。该限制只对**未获得会话级绕过授权**的会话生效；启动时已处于 `bypassPermissions` 的会话在 plan 模式下不经过权限层（见「计划模式继承会话的绕过授权」）。

**为什么是这个优先级**：这确保规划过程中代码库的安全性和完整性。

**独立测试**：在未获得绕过授权的会话中进入计划模式，尝试编辑非计划文件并验证它被阻止。

**验收场景**：

1. **假设**系统处于 "plan" 模式且该会话未获得绕过授权，**当** LLM 尝试读取文件时，**则**操作被允许。
2. **假设**系统处于 "plan" 模式且该会话未获得绕过授权，**当** LLM 尝试编辑指定计划文件以外的文件时，**则**操作被阻止。
3. **假设**系统处于 "plan" 模式且该会话未获得绕过授权，**当** LLM 尝试执行 bash 命令时，**则**该命令不被 plan 模式拒绝，而是走常规权限流程（命中 allow 规则或安全区自动放行则静默执行，否则弹出确认）。
4. **假设**系统处于 "plan" 模式，**当** LLM 编辑计划文件时，**则**操作被允许（与该会话是否获得绕过授权无关）。

***

### 用户故事：计划模式继承会话的绕过授权（优先级：P1）

作为启动时已授权绕过权限的用户（`permissions.defaultMode: "bypassPermissions"`，或 CLI `--dangerously-skip-permissions` / `--permission-mode bypassPermissions`），我希望切到计划模式做探索时不再被逐条权限确认打断，以便计划模式的行为与我在该会话中已授权的"不要问我"一致（对齐 Claude Code 的 `isBypassPermissionsModeAvailable`：plan 模式下该位为真时权限检查直接放行）。

**为什么是这个优先级**：用户已在会话级明确授权，plan 模式不应把它变成"恢复逐条询问"；实测中 plan 模式下的 bash 调用有 69/76 次弹窗，其中 50 次是只读命令仅因路径落在安全区之外被拦。

**独立测试**：以 `permissions.defaultMode: "bypassPermissions"` 启动会话，切到计划模式并让模型执行一条未命中任何 allow 规则、且不在安全区内的 bash 命令，验证不出现确认提示；再以 `defaultMode: "default"` 启动同样会话，验证出现确认提示。

**验收场景**：

1. **假设**会话创建时解析出的有效权限模式为 `bypassPermissions`（来自 `permissions.defaultMode` 或 CLI 指定），**当**该会话创建时，**则**获得"绕过授权"，且该状态在会话生命周期内保持不变。
2. **假设**会话创建时解析出的有效权限模式不是 `bypassPermissions`（如 `default`/`acceptEdits`/`plan`/`dontAsk`），**当**该会话创建时，**则**不具有绕过授权；之后用户通过 Shift+Tab 或模式菜单切到 `bypassPermissions` 也不会获得（授权只在创建时定格）。
3. **假设**会话具有绕过授权且系统处于 "plan" 模式，**当** LLM 执行一条未命中任何 allow 规则、且不在安全区内的 `Bash` 命令时，**则**不出现确认提示。
4. **假设**会话具有绕过授权且系统处于 "plan" 模式，**当** LLM 尝试 `Edit` 或 `Write` 计划文件以外的文件时，**则**不出现确认提示（该会话不再受「只能编辑计划文件」限制）。
5. **假设**会话不具有绕过授权且系统处于 "plan" 模式，**当** LLM 执行未命中 allow 规则的 `Bash` 命令或编辑计划文件以外的文件时，**则**行为与现状一致（bash 弹确认、非计划文件编辑被阻止）。
6. **假设**会话具有绕过授权且系统处于 "plan" 模式，**当** LLM 调用的工具命中 `permissions.deny` 或实例 deny 规则时，**则**仍然被拒绝（deny 优先于绕过授权，对齐 Claude Code 的判定步骤顺序）。
7. **假设**会话具有绕过授权、系统处于 "plan" 模式且会话位于某 worktree 内，**当** LLM 尝试修改主仓库（worktree 之外）的文件时，**则**仍被拒绝，绕过授权不豁免 worktree 越界保护。
8. **假设**会话具有绕过授权且系统处于 "plan" 模式，**当** LLM 调用 `AskUserQuestion` 时，**则**仍然向用户提问（该工具需要用户交互，任何模式下都不被绕过）。
9. **假设**会话具有绕过授权，**当**系统处于 `default`/`acceptEdits`/`dontAsk` 模式时，**则**行为与现状一致：绕过授权只改变 "plan" 模式的判定，不改变其他模式。
10. **假设**具有绕过授权的会话处于 "plan" 模式，**当**它派生子 agent 时，**则**子 agent 继承该会话的绕过授权（子 agent 的权限模式取自父会话的当前模式，若在子 agent 创建时重新推导会得到错误结果）。
11. **假设**会话具有绕过授权且系统处于 "plan" 模式，**当** LLM 调用 `ExitPlanMode` 时，**则**仍然向用户请求计划批准（展示计划内容与确认选项），用户批准后才退出计划模式；绕过授权不豁免该确认（该工具需要用户交互，与 `AskUserQuestion` 同属对齐 Claude Code `requiresUserInteraction` 的例外，也是此类会话中计划模式仅剩的约束之一）。

***

### 用户故事：系统提示引导（优先级：P2）

作为用户，我希望 LLM 被明确告知在计划模式下如何行为，以便它有效地使用计划文件。

**为什么是这个优先级**：确保 LLM 理解其约束和预期工作流。

**独立测试**：可以通过检查计划模式活动时发送给 LLM 的系统提示来测试。

**验收场景**：

1. **假设**系统处于 "plan" 模式，**当**消息发送到主 agent 时，**则**提醒包含计划文件信息并指示 agent 通过写入或编辑计划文件来增量构建计划。
2. **假设**系统处于 "plan" 模式，**当**消息发送到子 agent 时，**则**提醒告诉子 agent 以文本输出形式返回发现，并且不指示它写入或编辑计划文件（因为子 agent 缺少 Write/Edit 工具）。

***

### 用户故事：通过 ExitPlanMode 批准计划（优先级：P1）

作为计划模式中的 agent，我希望在完成将计划写入指定计划文件后使用 `ExitPlanMode` 工具，以便用户可以审查该文件中的计划内容并提供批准或反馈。

**为什么是这个优先级**：这是请求的核心功能。它基于实际计划文件内容，在用户监督下实现从规划到执行的过渡。

**独立测试**：可以通过将 agent 置于计划模式、将计划写入文件、调用 `ExitPlanMode` 并验证用户看到计划文件内容并被提示确认来测试。

**验收场景**：

1. **假设** agent 处于计划模式并已写入计划到指定文件，**当** agent 调用 `ExitPlanMode` 时，**则**用户看到计划文件的内容，并通过标准 `canUseTool` 机制被提示确认，提供三个选项（可通过方向键导航）。
2. **假设**用户正在审查文件中的计划，**当**用户选择 "Default" 时，**则**工具成功，agent 退出计划模式进入默认执行状态。
3. **假设**用户正在审查文件中的计划，**当**用户选择 "Accept Edits" 时，**则**工具成功，agent 退出计划模式进入后续编辑自动接受的状态。
4. **假设**用户正在审查文件中的计划，**当**用户选择 "Tell agent what to do" 时，**则**用户提供反馈，工具将此反馈返回给 agent，agent 保持在计划模式中以优化计划。

***

### 用户故事：计划内容在编辑器区域预览（三端统一，优先级：P2）

作为 Wave 用户，我希望 `ExitPlanMode` 的完整计划内容显示在编辑器区域（而非确认对话框内部），以便我在查看确认对话框的同时并排对照计划与代码，确认对话框本身保持简洁（对齐 CC 的 claudeVSCodePanel + claudePlanPreview 相邻两列）。

**为什么是这个优先级**：计划内容是批准流程的核心信息。三端将计划移出对话框、放入编辑器区域的承载位（JB 独立计划标签页 / VSCE `createWebviewPanel` 计划预览面板 / 桌面端 Plan 面板）后，用户可在对话框确认的同时对照代码审查计划，对话框更紧凑友好。共享 webview 统一移除对话框内联的计划全文渲染，各端自行承载计划内容。**例外（远控 Web 页）**：手机浏览器里既没有编辑器区域、也没有面板，页面**没有**任何可放计划正文的承载位，而计划正文恰是批准与否的唯一依据 ⇒ 远控页**刻意**把计划全文渲染回确认对话框内（见验收场景 13）。这不是「共享 webview 又内联渲染了」——它只是不套用这条故事的三端承载方案。

**独立测试**：在任一前端触发 `ExitPlanMode` 权限确认，验证计划内容渲染在编辑器区域的承载位（markdown），且确认对话框不再显示计划全文。**远控 Web 页反向验证**：在页面上触发 `ExitPlanMode`，验证计划全文**就在确认对话框内**渲染。

**验收场景**：

1. **假设**任一前端中 agent 调用 `ExitPlanMode` 触发权限确认，**当**确认对话框显示时，**则**对话框内不显示计划全文，仅保留确认选项，对话框更小、更友好（三端统一，共享 webview 不再内联渲染 planContent）。
2. **假设** JetBrains 插件中 `ExitPlanMode` 确认触发，**当**确认对话框显示时，**则**完整计划内容以 markdown 渲染在独立的计划标签页中（编辑器区域标签页，对齐 VS Code 的 `createWebviewPanel` 计划预览面板），聊天界面保持侧边栏位置不变。
3. **假设** JetBrains 插件同一会话中 agent 再次调用 `ExitPlanMode`（计划更新后重新请求确认），**当**新的确认请求到达时，**则**复用该会话已有的计划标签页：内容更新为最新计划，而非新建标签页。
4. **假设**计划标签页已显示（JB），**当**用户在确认对话框中选择批准或拒绝时，**则**计划标签页保留，由用户手动关闭。
5. **假设** VS Code 扩展中 `ExitPlanMode` 确认触发，**当**确认对话框显示时，**则**完整计划内容以 markdown 渲染在聊天 webview 面板相邻的计划预览面板中（`createWebviewPanel`，对齐 CC 的 `claudePlanPreview`）。
6. **假设** VS Code 扩展同一会话中 agent 再次调用 `ExitPlanMode`，**当**新的确认请求到达时，**则**复用已有的计划预览面板：内容更新为最新计划，而非新建面板。
7. **假设** VS Code 扩展计划预览面板已显示，**当**用户在确认对话框中选择批准或拒绝时，**则**预览面板保留，由用户手动关闭。
8. **假设**桌面应用中 `ExitPlanMode` 确认触发，**当**确认对话框显示时，**则**完整计划内容以 markdown 渲染在新增的 Plan 面板中（现有面板系统新增 `plan` 面板类型），面板自动打开。
9. **假设**桌面应用同一会话中 agent 再次调用 `ExitPlanMode`，**当**新的确认请求到达时，**则**复用已有的 Plan 面板：内容更新为最新计划。
10. **假设**桌面应用 Plan 面板已显示，**当**用户在确认对话框中选择批准或拒绝时，**则** Plan 面板保留（内容不自动清空），由用户手动关闭。
11. **假设**多个会话并行，**当**各会话分别触发 `ExitPlanMode` 时，**则**每个会话拥有独立的计划承载位，互不覆盖。
12. **假设**非 ExitPlanMode 工具（如 Bash）触发权限确认，**当**确认对话框显示时，**则**行为与现状一致，不受计划承载改动影响。
13. **假设**远控 Web 页（手机浏览器连到本机 daemon）中 agent 调用 `ExitPlanMode`，**当**确认对话框显示时，**则**完整计划内容以 markdown 渲染**在这个确认对话框内部**（独立滚动区、不截断）——页面既无编辑器区域也无面板，此处**不套用**上述三端承载方案（详见 `remote/remote-web.md` 的同名故事与场景 19）；冷启动或断线重连后补读到的同一张待审批卡片同样带计划正文。
14. **假设**桌面应用中某会话在后台（其聊天不在当前前台 pane）调用 `ExitPlanMode`，**当**用户切回该会话时，**则**完整计划内容渲染在 Plan 面板中并自动打开，确认对话框**仍只显示确认选项**（不重复渲染计划全文）——与前台触发的场景 8 同一口径；切走期间宿主给「会话需要确认」提示，不抢前台弹卡片。

***

### 用户故事：聊天会话在侧边栏工具窗口（JB 端，优先级：P2）

作为 JetBrains IDE 中的用户，我希望 Wave 聊天会话渲染在侧边栏工具窗口（而非编辑器区域标签页），以便聊天界面固定可见、不占用代码编辑区，与代码标签页清晰分离（对齐 VS Code 扩展的侧边栏 webview 面板形态；仅 ExitPlanMode 的计划内容在独立标签页预览）。

**为什么是这个优先级**：聊天主界面在侧边栏是跨端一致的形态（VS Code 面板 / 桌面分屏 / JB 工具窗口）；编辑器区域仅用于承载计划预览（见「计划内容在编辑器区域预览」用户故事），聊天不随之挪动。

**独立测试**：在 JB IDE 中打开 Wave 聊天，验证其出现在侧边栏工具窗口；新建多个会话，验证每个会话独立、可切换。

**验收场景**：

1. **假设**用户触发打开 Wave（Tools 菜单 / 编辑器右键 "New Wave Chat"），**当**操作执行时，**则**聊天会话在侧边栏工具窗口打开（而非编辑器区域标签页）。
2. **假设**聊天会话已打开，**当**用户发送第一条消息后，**则**会话标题更新为该消息摘要（首个用户消息，30 字截断）。
3. **假设**多个聊天会话已打开，**当**用户切换会话时，**则**"Add to Wave" 等 IDE 动作作用于当前选中的会话。
4. **假设**聊天会话已打开，**当**用户关闭该工具窗口时，**则**会话被销毁（agent 终止、stdio 资源释放），不影响其他会话。
5. **假设**聊天会话已关闭，**当**用户再次打开 Wave 时，**则**打开一个新的聊天会话。
6. **假设**聊天会话已打开，**当**用户在 webview 内按 Ctrl/Cmd+R（刷新）时，**则**快捷操作转发到 webview（现有行为保持），IDE 的 Refresh 动作不被误触发。
7. **假设**聊天会话在侧边栏打开且 `ExitPlanMode` 确认触发，**当**确认对话框显示时，**则**聊天界面保持侧边栏位置不变，计划内容在独立编辑器标签页渲染（见「计划内容在编辑器区域预览」验收场景 2）。

***

### 边界情况

* **目录创建**：如果 `~/.wave/plans` 不存在，系统应该自动创建。
* **名称冲突**：随机英文名称生成器应该最小化冲突的可能性，但如果文件已存在，应该处理（如生成新名称）。
* **会话持久化**：如果会话重启或消息被压缩，系统必须重用现有的计划文件路径。这通过使用 `rootSessionId`（链中第一个会话的 ID）作为确定性名称生成的种子来实现。
* **`ExitPlanMode` 在计划模式外被调用怎么办？** `ExitPlanMode` 工具始终可用。当 agent 不在计划模式时，工具通过运行时守卫返回错误信息。注意它在工具列表里的位置随 Exec 通道而变：这一对（`EnterPlanMode`/`ExitPlanMode`）是延迟工具（与 Claude Code 的 `shouldDefer: true` 对齐，见 `core/exec-tool.md`），Exec 通道打开时只声明在沙箱内、不在 `tools[]` 里平铺，通道关闭时才平铺——两处不得同时出现，否则模型会看到同名工具两次。
* **系统如何处理多次调用 `ExitPlanMode`？** 如果已在退出中或第一次调用待处理，后续调用应该被优雅地处理（如忽略或返回为待处理）。
* **获得绕过授权的会话里，plan 的约束由谁执行？** 权限层不再拦截（Bash 与 Edit/Write 都不弹窗），约束只剩两处：注入的系统提示词要求模型只规划、不改代码；以及 `ExitPlanMode` 需要用户批准才离开计划模式。这是该设计的已知代价，与 Claude Code 在 `isBypassPermissionsModeAvailable` 为真时的行为一致。
* **绕过授权与 `dontAsk` 的优先级**：绕过授权只作用于 "plan" 模式的判定，不改变 `dontAsk` 的自动拒绝行为。
* **计划承载位刷新的驱动范围**：承载位刷新只由 agent 的 `Write`/`Edit` 工具调用命中计划文件时触发（不引入文件系统监听）；用户直接编辑 `~/.wave/plans/` 下的计划文件、或 agent 退出计划模式后的改动都不刷新承载位。
* **计划文件被写空**：agent 把计划文件写为空内容时，已打开的承载位同步清空（回落空态），不保留上一次的计划内容。
* **CLI 无常驻承载位**：CLI 的 `/plan` 是一次性 Ink 弹层，不存在常驻计划面板，故不受本机制影响。

### 用户故事：计划模式重新进入引导（优先级：P1）

作为之前已退出计划模式的用户，我希望系统在我重新进入计划模式时能够识别，以便 agent 知道现有计划文件并可以决定继续还是重新开始。

**为什么是这个优先级**：没有重新进入引导，agent 可能忽略现有计划文件或假设它仍然相关，导致工作浪费或计划不正确。

**独立测试**：进入计划模式，写入计划，批准 ExitPlanMode，重新进入计划模式，验证 agent 收到关于现有计划文件的重新进入提醒。

**验收场景**：

1. **假设** agent 已退出计划模式且计划文件存在，**当**用户重新进入计划模式时，**则**注入重新进入 `<system-reminder>`，指示模型读取现有计划、评估任务是否相同或不同，并在 ExitPlanMode 之前始终编辑计划文件。
2. **假设** agent 已退出计划模式但没有计划文件存在，**当**重新进入计划模式时，**则**不注入重新进入提醒（视为首次进入）。
3. **假设**重新进入提醒已注入一次，**当**计划模式中后续轮次发生时，**则**重新进入提醒不再被注入（仅一次）。

***

### 用户故事：模式转换意识（优先级：P1）

作为在对话中途从 default/acceptEdits 模式切换到计划模式的用户，我希望 agent 立即理解它必须停止编辑并切换到规划，即使对话历史包含最近的 Edit/Write 工具调用。

**为什么是这个优先级**：没有模式边界意识，模型可能基于最近的工具调用历史继续编辑，忽略计划模式约束。

**独立测试**：进行包含 Edit/Write 调用的对话，然后进入计划模式，验证计划模式提醒作为最后一条指令出现，带有明确的覆盖语言。

**验收场景**：

1. **假设**对话包含最近的 Edit/Write 工具调用且用户进入计划模式，**当**下一次 API 调用发生时，**则**计划模式 `<system-reminder>` 作为模型看到的最后一条指令注入（在所有先前的工具调用之后），明确声明 "This supercedes any other instructions you have received."
2. **假设** agent 处于计划模式且该会话未获得绕过授权，**当** agent 尝试在计划文件以外的任何文件上使用 Edit 或 Write 时，**则**权限系统在运行时阻止该操作。

***

### 用户故事：计划模式退出通知（优先级：P2）

作为刚刚批准计划的用户，我希望 agent 被明确告知已退出计划模式并可以执行操作，以便对模式转换没有混淆。

**为什么是这个优先级**：防止 agent 在批准后继续表现得像仍在计划模式中。

**独立测试**：批准 ExitPlanMode 并验证"已退出计划模式"的 system-reminder 出现在下一个轮次。

**验收场景**：

1. **假设** ExitPlanMode 被批准，**当**下一个 API 轮次开始时，**则**注入"已退出计划模式"的 `<system-reminder>` 作为一次性消息。
2. **假设**退出通知已注入，**当**后续轮次开始时，**则**退出通知不再被注入（仅一次）。

***

### 用户故事：一次性计划进入提醒（优先级：P2）

作为进入计划模式的用户，我希望 agent 在进入计划模式并发送消息时恰好收到一次计划模式指令，以便 token 不会浪费在重复提醒上。

**为什么是这个优先级**：之前的节流机制（扫描消息中的元提醒）已损坏——提醒是临时的，从未存储，所以节流从未触发，每次 AI 调用都注入完整的约 90 行提醒。简化方法在每次计划模式进入时恰好触发一次提醒。

**独立测试**：进入计划模式，发送消息，验证提醒出现。发送另一条消息，验证没有提醒。退出并重新进入计划模式，验证提醒再次出现。

**验收场景**：

1. **假设**用户进入计划模式，**当**第一条消息发送时，**则**注入完整的计划模式 `<system-reminder>`（包括 5 阶段工作流）。
2. **假设**计划进入提醒已注入，**当**同一计划模式会话中发送后续消息时，**则**不注入计划模式提醒。
3. **假设**用户退出计划模式并重新进入，**当**下一条消息发送时，**则**注入重新进入提醒（关于读取现有计划文件的小提醒）。

### 用户故事：通过 EnterPlanMode 工具进入计划模式（优先级：P1）

作为 AI agent，我希望在判断任务较为复杂时主动请求进入计划模式，以便在修改多文件、开发新功能或修复复杂 bug 之前先制定计划。

**为什么是这个优先级**：允许 agent 自主判断何时需要规划，提升复杂任务的处理质量，同时通过用户确认保持人类监督。

**独立测试**：在对话中让 agent 判断任务复杂性，验证其调用 `EnterPlanMode` 工具后触发用户确认，批准后进入计划模式。

**验收场景**：

1. **假设** agent 判断当前任务较为复杂（多文件更改、新功能、复杂 bug 修复），**当** agent 调用 `EnterPlanMode` 工具时，**则**系统通过 `canUseTool` 机制向用户显示确认请求，用户批准后系统进入计划模式。
2. **假设** agent 调用 `EnterPlanMode`，**当**用户拒绝确认请求时，**则**agent 收到 "User declined to enter plan mode. Proceed in current mode." 的返回信息，继续在当前模式中执行。
3. **假设** agent 已在计划模式中，**当** agent 调用 `EnterPlanMode` 时，**则**工具返回错误信息 "Already in plan mode"，不重复进入。
4. **假设** agent 调用 `EnterPlanMode` 触发用户确认，**当**确认对话框显示时，**则**不得显示"始终允许"选项（`hidePersistentOption = true`）。

***

### 用户故事：通过 /plan 斜杠命令进入计划模式（优先级：P1）

作为用户，我希望输入 `/plan` 斜杠命令直接进入计划模式，以便无需通过 Shift+Tab 循环多个模式即可开始规划（对齐 Claude Code 的 `/plan` 命令入口）。

**为什么是这个优先级**：`/plan` 是与 Shift+Tab、`--permission-mode plan` 并列的第三入口，用户显式输入即授权，无需确认对话框；`/plan <描述>` 更进一步把「进入计划模式」和「立即开始规划」合并为一条命令，是用户发起规划的最短路径。命令注册在各宿主侧（CLI 内置命令列表 + 三端 GUI 的本地命令列表），不进入 SDK 的 `SlashCommandManager`——/plan 的动作依赖宿主各自的展示能力（编辑器标签页 / 面板 / Ink 弹层），由宿主处理而非 SDK。

**独立测试**：在非 plan 模式下输入 `/plan` 验证直接进入计划模式（无确认对话框）；输入 `/plan <描述>` 验证进入计划模式且描述作为用户消息触发一次 AI 查询，模型收到计划模式提醒并开始规划；验证命令出现在命令选择器与帮助视图中。

**验收场景**：

1. **假设**系统处于非 plan 模式（default/acceptEdits/bypassPermissions），**当**用户输入 `/plan` 时，**则**系统直接切换到 plan 模式，不显示确认对话框（用户显式输入即授权），并完成完整模式过渡（plan 文件路径生成、`onPermissionModeChange` 回调、状态栏模式指示更新）。
2. **假设**系统处于非 plan 模式，**当**用户输入 `/plan <描述>` 时，**则**系统切换到 plan 模式，并将 `<描述>` 作为用户消息立即触发一次 AI 查询；模型在计划模式提醒（含 plan 文件路径）下开始规划。
3. **假设** `/plan <描述>` 已触发模式切换，**当** AI 查询发起时，**则**plan 文件路径已生成完毕并注入提醒（路径生成必须先于查询触发，避免模型收不到 plan 文件位置的竞态）。
4. **假设**系统已处于 plan 模式，**当**用户输入 `/plan <描述>` 时，**则**系统不重复进入 plan 模式，也不触发新查询（按「查看当前计划」处理，忽略描述参数）。
5. **假设**用户输入 `/plan`（无参数），**当**系统切换成功时，**则**仅进入 plan 模式，不触发 AI 查询。
6. **假设**用户输入 `/` 打开命令选择器，**当**浏览命令列表时，**则**`/plan` 命令可见（描述：启用计划模式或查看当前计划）；CLI `/help` 帮助视图、GUI 命令弹窗中同样可见。
7. **假设**用户输入 `/plan open`，**当**命令被执行时，**则**不打开任何外部编辑器（三端均不支持该子命令），`open` 被视为普通描述或忽略，行为与 `/plan` 一致。

***

### 用户故事：通过 /plan 查看当前计划（优先级：P2）

作为处于计划模式中的用户，我希望输入 `/plan` 查看当前计划的完整内容，以便在不通过 ExitPlanMode 确认流程的情况下审查计划（对齐 Claude Code 的 `/plan` 查看入口）。

**为什么是这个优先级**：查看是计划流程的便利能力，核心路径（写入 → ExitPlanMode → 确认）不依赖它；但缺少它会迫使审查只能走确认对话框。三端展示动作复用 ExitPlanMode 已有的计划承载位，不新增展示形态。

**独立测试**：进入计划模式并写入计划后输入 `/plan`，验证在宿主各自的计划承载位显示当前计划内容与文件路径；无内容时验证给出提示。

**验收场景**：

1. **假设**系统处于 plan 模式且 plan 文件已写入内容，**当**用户输入 `/plan` 时，**则** CLI 在 Ink 弹层中显示当前计划（"Current Plan"：计划文件路径 + 完整内容），弹层高度固定为终端约 50-60%，内容超出高度时支持 PgUp/PgDn/Ctrl+u/Ctrl+d 滚动并显示 ↑↓ more 指示器，Esc 关闭。
2. **假设**系统处于 plan 模式且 plan 文件已写入内容（JetBrains 插件），**当**用户输入 `/plan` 时，**则**完整计划内容以 markdown 渲染在该会话已有的计划标签页中（复用 ExitPlanMode 的编辑器区域标签页，不新建）。
3. **假设**系统处于 plan 模式且 plan 文件已写入内容（VS Code 扩展），**当**用户输入 `/plan` 时，**则**完整计划内容渲染在该会话已有的计划预览面板中（`createWebviewPanel`，与 ExitPlanMode 共用同一面板）。
4. **假设**系统处于 plan 模式且 plan 文件已写入内容（桌面应用），**当**用户输入 `/plan` 时，**则**完整计划内容以 markdown 渲染在 Plan 面板中（复用 ExitPlanMode 的面板，未打开时自动打开）。
5. **假设**系统处于 plan 模式但 plan 文件尚无内容，**当**用户输入 `/plan` 时，**则**提示 "No plan written yet."（对齐 Claude Code 文案），不打开空承载位。
6. **假设** CLI 中 plan 内容超过弹层可视行数，**当**用户按下 PgDn / Ctrl+d 时，**则**内容向下翻页并显示剩余行数指示；滚动到底后 PgDn 不再移动。
7. **假设** CLI 的 `/plan` 弹层已打开，**当**用户按下 Esc 时，**则**弹层关闭，返回输入框，模式不变。

***

### 用户故事：计划文件更新后刷新计划面板（优先级：P2）

作为在计划模式下查看计划面板的用户，我希望模型每次修改计划文件后计划面板就显示最新内容，以便我在规划过程中看到的是当前计划，而不是停留在上一次 `ExitPlanMode` 或 `/plan` 时的旧快照。

**为什么是这个优先级**：模型以增量方式构建计划（先 `Write` 创建、再多次 `Edit`/`Write` 补充），而计划承载位此前只在 `ExitPlanMode` 确认与 `/plan` 查看两个时点刷新，规划过程中展示的内容会持续滞后。承载位应始终反映计划文件的当前内容。

**独立测试**：进入计划模式并打开计划承载位（桌面 Plan 面板 / VS Code 计划预览面板 / JetBrains 计划标签页），让模型写入计划文件，再让它修改一次，验证承载位内容随之更新为最新计划；关闭承载位后再修改文件，验证承载位不会被自动重新打开。

**验收场景**：

1. **假设** agent 处于计划模式且计划承载位已打开，**当** agent 用 `Write` 或 `Edit` 工具修改指定的计划文件后，**则**承载位内容更新为该文件的最新完整内容（等效于此刻执行一次「查看当前计划」）。
2. **假设** agent 在同一计划模式会话中连续多次修改计划文件（`Write` 整体覆盖或 `Edit` 增量修改），**当**每次修改完成时，**则**承载位每次都更新为最新内容，无需等待下一次 `ExitPlanMode` 或 `/plan`。
3. **假设**桌面应用中计划文件被 agent 修改且 Plan 面板已打开，**当**修改完成时，**则** Plan 面板内的 markdown 更新为最新计划，面板的打开状态与位置不变。
4. **假设** VS Code 扩展中计划文件被 agent 修改且计划预览面板已打开，**当**修改完成时，**则**该面板内容更新为最新计划，不新建面板。
5. **假设** JetBrains 插件中计划文件被 agent 修改且计划标签页已打开，**当**修改完成时，**则**该标签页内容更新为最新计划，不新建标签页。
6. **假设**计划承载位尚未打开（本会话还未通过 `ExitPlanMode` 或 `/plan` 触发过），**当** agent 修改计划文件时，**则**不自动打开承载位；下一次由 `ExitPlanMode` 或 `/plan` 打开时展示该文件的最新内容。
7. **假设** agent 修改的文件不是当前计划文件（代码或其它文件），**当**工具执行完成时，**则**计划承载位不受影响。
8. **假设** agent 修改计划文件后随即调用 `ExitPlanMode`，**当**确认对话框显示时，**则**承载位展示的内容与计划文件的当前内容一致（刷新不产生重复内容或过期快照）。
9. **假设**多个会话并行、各自持有独立的计划承载位，**当**其中一个会话修改自己的计划文件时，**则**只有该会话的承载位更新，其它会话不受影响。
10. **假设** agent 已退出计划模式，或用户直接编辑 `~/.wave/plans/` 下的计划文件，**当**计划文件内容变化时，**则**承载位不刷新（本期刷新只由 agent 的 `Write`/`Edit` 工具调用驱动）。
