feat(cli): implement /plan command for plan mode#2921
Conversation
- Add /plan command to switch to plan mode or execute the current plan - Update BuiltinCommandLoader to include planCommand - Allow planCommand in non-interactive environments - Add corresponding unit tests - Update i18n locales to include new translations - Document /plan command in features/commands.md
📋 Review SummaryThis PR implements the 🔍 General Feedback
🎯 Specific Feedback🟡 High
🟢 Medium
🔵 Low
✅ Highlights
|
…atrix PR#2921 implements /plan command for plan mode with ACP/Headless support, unit tests, and 6-language i18n. Maps to p2-tools-ui #11. Total tracked PRs: 20 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
- Fix /plan execute running without checking if currently in plan mode - Remove hallucinated i18n keys (e.g. "Coding Plan API keys") added by prior AI-generated code - Remove /plan from non-interactive command allowlist (plan mode is interactive) - Revert unrelated package-lock.json and unused-keys-only-in-locales.json changes - Add test case for /plan execute when not in plan mode Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a new /plan slash command to the CLI to support entering “plan mode” and exiting it, aiming to enable a safer planning-first workflow before executing changes.
Changes:
- Introduces
planCommandand registers it inBuiltinCommandLoader. - Adds unit tests for
/planstate transitions and argument handling. - Updates docs and locale files with new
/plan-related strings.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/cli/src/ui/commands/planCommand.ts | Implements /plan behavior (enter plan mode, submit task, and “execute” flow). |
| packages/cli/src/ui/commands/planCommand.test.ts | Adds vitest coverage for mode transitions and argument handling. |
| packages/cli/src/services/BuiltinCommandLoader.ts | Registers the new built-in planCommand. |
| packages/cli/src/i18n/locales/en.js | Adds English strings for /plan. |
| packages/cli/src/i18n/locales/zh.js | Adds Chinese translations for /plan. |
| packages/cli/src/i18n/locales/ru.js | Adds /plan strings (currently untranslated). |
| packages/cli/src/i18n/locales/pt.js | Adds /plan strings (currently untranslated). |
| packages/cli/src/i18n/locales/ja.js | Adds /plan strings (currently untranslated). |
| packages/cli/src/i18n/locales/de.js | Adds /plan strings (currently untranslated). |
| docs/users/features/commands.md | Documents /plan in the slash-commands table. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Addressed review feedback in commit 007fff6: Already fixed:
Won't change (by design):
Docs fix (Copilot suggestion accepted): aligned wording "execute current plan" → "execute the current plan" |
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
When entering plan mode, Config now saves the previous approval mode (e.g. AUTO_EDIT, YOLO) so it can be restored when exiting. Previously, /plan execute and ExitPlanModeTool both hardcoded a return to DEFAULT, losing the user's prior mode. Changes: - Config: add prePlanMode field, getPrePlanMode(), auto-track in setApprovalMode() - planCommand: /plan execute restores prePlanMode instead of DEFAULT - ExitPlanModeTool: ProceedOnce restores prePlanMode instead of DEFAULT - Tests: 4 new Config tests, 1 new planCommand test, 1 new ExitPlanModeTool test Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Save approved plans to ~/.qwen/plans/{sessionId}.md so they can be
referenced later in the session or reviewed outside the CLI.
Changes:
- Storage: add getPlansDir() and getPlanFilePath()
- Config: add savePlan(), loadPlan(), getPlanFilePath() methods
- ExitPlanModeTool: persist plan to disk when user approves
- Tests: 4 new Config tests, 2 new ExitPlanModeTool assertions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
# Conflicts: # packages/cli/src/i18n/locales/de.js # packages/cli/src/i18n/locales/en.js # packages/cli/src/i18n/locales/ja.js # packages/cli/src/i18n/locales/pt.js # packages/cli/src/i18n/locales/ru.js # packages/cli/src/i18n/locales/zh.js
PR #2921 Review - Updated NotesThanks for the contribution! The
What PR #2921 Actually Adds
Suggestions for This PR
Future Iterations (Non-blocking)These are longer-term goals for Claude Code alignment, suggested as separate PRs:
|
回复修改清单感谢详细的 review,已逐项实现。以下是每项修改的落地情况和审计中额外发现的问题。 修改 1:Plan 存储从"先传参后存盘"改为"先存盘后读取" ✅
审计修复:
修改 2:去掉
|
zhangxy-zju
left a comment
There was a problem hiding this comment.
/plan 命令和 prePlanMode 恢复实现得很好,plan 文件存档基础设施也为后续迭代铺路。几点建议:
1. /plan execute 命名不准确
"execute" 暗示执行计划,实际只是退出 plan mode。建议移除或改名为 /plan exit。详见行内评论。
2. PR description 与实际代码不一致
PR description 描述了 disk-based plan workflow、isPlanFileWrite 白名单、4 个审批选项等能力,但实际改动范围是:/plan 命令 + prePlanMode 跟踪 + 审批后存档。建议更新 description 以准确反映实际范围。
3. ProceedOnce label 与实际行为不匹配(minor)
PR 将 ProceedOnce 从硬编码 DEFAULT 改为恢复 prePlanMode,逻辑正确。但 UI label 仍是 "Yes, and manually approve edits"——如果用户之前在 AUTO_EDIT 或 YOLO 模式,label 就不准确了。后续可考虑增加 "Yes, restore previous mode" 选项。
wenshao
left a comment
There was a problem hiding this comment.
Review Summary
This PR adds the /plan command with disk-based plan file workflow, enhanced approval options, and verbose mode UI improvements. The core plan-mode implementation is solid, well-tested, and follows existing patterns. However, several issues were found:
Critical
packages/vscode-ide-companion/package.json:@vscode/vscewas removed from devDependencies but the"package"script still referencesvsce package --no-dependencies. Runningnpm run packagewill fail. This change was merged from main but is currently in this PR branch. Restore the dependency or change the script to usenpx @vscode/vsce.
Suggestions (not in PR diff, cannot post as inline)
packages/core/src/core/prompts.ts:getActionsSection()(~500 words) is injected unconditionally into every system prompt. If plan-mode-specific, gate it; if general, document intent.AppContainer.tsx:drainQueue()exported fromuseMessageQueuebut never used — AppContainer reimplements drain viamidTurnDrainRef. Consider using the hook's export directly.packages/webui/src/hooks/useFollowupSuggestions.ts:createFollowupControllerwas copy-pasted from core to remove peer dependency, creating ~130 lines of duplicated logic to maintain in lockstep.packages/cli/src/ui/components/Footer.tsx: "verbose" footer indicator shown by default (verbose defaults to true), making it permanent noise. Consider showing "compact" when verbose is OFF instead.
Reviewed by glm-5.1 via Qwen Code /review
wenshao
left a comment
There was a problem hiding this comment.
Request changes — see inline comments.
Additional findings that could not be posted inline because they are not part of the current GitHub PR diff:
- [Critical]
packages/webui/package.json/packages/webui/src/index.ts: this branch removes the public@qwen-code/webui/followupsubpath export, which breaks existing consumers importing that entrypoint. At the same time, the new root re-export path currently fails typecheck because the new relative exports inpackages/webui/src/index.tsomit the required.jsESM extensions (TS2835). - [Nice to have]
packages/core/src/utils/schemaValidator.test.ts: the test comments now say coercion should not happen, but the assertions expect coercion to succeed, so the comments no longer describe the actual behavior.
Reviewed by gpt-5.4 via Qwen Code /review
Three issues found from real /review output on PR #2921: 1. Critical findings but verdict submitted as --comment instead of --request-changes. Added explicit: "Do NOT use --comment when verdict is Request changes — this loses the blocking status." 2. Nice to have findings appeared in PR summary. Added: "Do NOT include Nice to have findings" to all summary rules. 3. Clarified that failed-inline summary should only contain Critical/Suggestion, never Nice to have. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Review comments addressed:
The first and last items required code changes. The middle two were resolved as design-as-intended. |
Rename the subcommand to accurately reflect its behavior (exits plan mode and restores previous approval mode, does not trigger execution). Update source, tests, i18n keys (6 locales), and docs.
Review follow-up summaryAll issues raised in this PR have been addressed:
Re: vsce and webui "Critical" findingsThese two issues were not introduced by this PR — they pre-exist on
Both should be tracked/discussed in their respective PRs, not block this one. |
zhangxy-zju
left a comment
There was a problem hiding this comment.
Issue 1: /plan exit cannot interrupt active planning / /plan exit 无法中断正在进行的 plan 生成
Manual test result / 手动测试结果
During plan generation, typing /plan exit does not immediately exit plan mode. The message stays in "unsent" state until the model finishes generating. By that time, the plan is already complete and the exit_plan_mode tool has presented its confirmation dialog, making /plan exit redundant.
在 plan 生成过程中输入 /plan exit, 无法立即退出 plan 模式。消息处于"未发送"状态, 需等模型生成完毕后才能提交。此时 plan 已生成完毕, exit_plan_mode 工具已展示确认对话框, /plan exit 变得多余。
Root cause / 根因
/plan exit is a slash command (planCommand.ts:39) that can only be processed when user input is submitted. During model generation, user input cannot be submitted, so the command is effectively blocked until the model completes.
/plan exit 是一个 slash command(planCommand.ts:39), 只能在用户提交输入时处理。模型生成期间无法提交输入, 因此该命令被阻塞直到模型完成。
Recommendation / 建议
Since /plan exit cannot interrupt active planning (which is the main scenario where users would want to use it), consider removing the exit subcommand. Users can still exit plan mode via:
- The confirmation dialog options after plan generation
Shift+Tabto cycle approval modes/approval-modecommand
既然 /plan exit 无法在 plan 生成中中断(这是用户最想使用该命令的场景), 建议移除 exit 子命令。用户仍可通过以下方式退出:
- plan 生成后的确认对话框选项
Shift+Tab切换审批模式/approval-mode命令
zhangxy-zju
left a comment
There was a problem hiding this comment.
Issue: /plan exit cannot interrupt active planning
Manual test result
During plan generation, typing /plan exit does not immediately exit plan mode. The message stays in "unsent" state until the model finishes generating. By that time, the plan is already complete and the exit_plan_mode tool has presented its confirmation dialog, making /plan exit redundant.
Root cause
/plan exit is a slash command (planCommand.ts:39) that can only be processed when user input is submitted. During model generation, user input cannot be submitted, so the command is effectively blocked until the model completes.
Recommendation
Since /plan exit cannot interrupt active planning (which is the main scenario where users would want to use it), consider removing the exit subcommand. Users can still exit plan mode via:
- The confirmation dialog options after plan generation
Shift+Tabto cycle approval modes/approval-modecommand
问题: /plan exit 无法中断正在进行的 plan 生成
手动测试结果
在 plan 生成过程中输入 /plan exit, 无法立即退出 plan 模式。消息处于"未发送"状态, 需等模型生成完毕后才能提交。此时 plan 已生成完毕, exit_plan_mode 工具已展示确认对话框, /plan exit 变得多余。
根因
/plan exit 是一个 slash command(planCommand.ts:39), 只能在用户提交输入时处理。模型生成期间无法提交输入, 因此该命令被阻塞直到模型完成。
建议
既然 /plan exit 无法在 plan 生成中中断(这是用户最想使用它的场景), 建议移除 exit 子命令。用户仍可通过以下方式退出:
- plan 生成后的确认对话框选项
Shift+Tab切换审批模式/approval-mode命令
"Yes, and manually approve edits" was restoring getPrePlanMode() which could be YOLO, contradicting the label. Now hardcodes DEFAULT to match the "manually approve" semantics.
|
@zhangxy-zju Fixed the semantic mismatch bug in
|
planmode.mp4 |
|
Thanks for the contribution! This PR adds the The YOLO mode restoration issue when exiting plan mode is not introduced by this PR (it exists in the current codebase). Filed #3002 to track it separately. Recommend merging. |
zhangxy-zju
left a comment
There was a problem hiding this comment.
This PR adds the /plan subcommand to match Claude Code's plan mode shortcuts — looks good overall.
The YOLO mode restoration issue when exiting plan mode is not introduced by this PR (it exists in the current codebase). Filed #3002 to track it separately.
Recommend merging.
Summary
Add
/planslash command with disk-based plan file workflow, structured system prompts, and enhanced approval options.Plan mode (
ApprovalMode.PLAN) already exists in the codebase and can be activated viashift+tabcycling or/approval-mode plan. This PR adds a dedicated/plancommand for quicker access, implements a disk-based plan file workflow where the model writes plans to a file incrementally, and enhances the exit-plan-mode approval flow with 4 user-facing options.Changes
Core architecture: disk-based plan workflow
plans.ts(new): Plan file utilities —getPlanFilePath(sessionId),getPlan(sessionId),getPlansDirectory()exitPlanMode.ts: Removeplan: stringparameter; tool reads plan from disk viagetPlan(sessionId). Add fallback disk read inexecute()for YOLO mode (where scheduler skipsgetConfirmationDetails)coreToolScheduler.ts: AddisPlanFileWrite()whitelist — plan file writes are auto-approved in plan mode (no user confirmation needed)Session.ts(ACP): MirrorisPlanFileWrite()whitelist withplanFileAutoApprovedflag to skip hooks and permission requestclient.ts: PassplanFilePathto system reminder so the model knows where to writeEnhanced system prompt
prompts.ts:getPlanModeSystemReminder(planFilePath?, planOnly?)— withplanFilePath: structured 4-step workflow (Understand → Design → Write Plan → Submit for Approval) + plan file info; without: compact format for tool-blocking error messagesEnhanced approval options
ToolConfirmationMessage.tsx+permissionUtils.ts: 4 options (was 3):prePlanModeAUTO_EDITDEFAULTPLAN/plancommand simplifiedplanCommand.ts: Remove/plan executesubcommand — exit only throughexit_plan_modetool,Shift+Tab, or/approval-modeconfig.ts:prePlanModetracking onsetApprovalMode(). Removed deadsavePlan()/loadPlan()/getPlanFilePath()methods (replaced byplans.ts)Other
/planentry in commands tableBefore / After
Before: Model passes plan as
exit_plan_modetool parameter. No file written during planning. Only 3 approval options./plan executeexits manually.After: Model writes plan to
~/.qwen/plans/{sessionId}.mdincrementally during plan mode (auto-approved).exit_plan_modereads from disk. 4 approval options with "restore previous mode" as default. Exit only through the tool.Comparison with Claude Code
/plancommand to enter plan mode/plan <task>enter + submit promptTest plan
/planenters plan mode when not in plan mode/planshows "already in plan mode" when already active/plan <text>enters plan mode and submits prompt/plan <text>submits prompt when already in plan mode