Skip to content

feat(ui): let the user read the full plan from the exit_plan_mode confirmation - #7060

Merged
wenshao merged 1 commit into
QwenLM:mainfrom
zjunothing:feat/7001-view-full-plan
Jul 19, 2026
Merged

feat(ui): let the user read the full plan from the exit_plan_mode confirmation#7060
wenshao merged 1 commit into
QwenLM:mainfrom
zjunothing:feat/7001-view-full-plan

Conversation

@zjunothing

Copy link
Copy Markdown
Collaborator

What this PR does

Adds a way to read the full plan from the exit_plan_mode confirmation dialog: pressing o writes the complete plan markdown to a temp file and opens it in the configured editor (same useLaunchEditor flow the skill-review dialog uses). The dialog stays open — viewing the plan does not confirm or cancel anything — and a one-line hint (o open full plan in editor) is always visible under the plan body, replaced by the error message if the editor fails to launch. The plan body's height budget is reduced by one row to make room for the hint, so nothing else shifts.

Why it's needed

PR #6882 made plan truncation visible (... N more lines not shown (viewport too small) ...), but the user still has to approve a plan they cannot fully read — a model could place unexpected steps past the viewport budget and the approver would accept them blind. This implements Direction 1 (external viewer — the simplest of the three approaches listed in the issue) so the approval can be informed.

Fixes #7001

Reviewer Test Plan

How to verify

  1. /plan <something that produces a 50+ line plan> in a normal-height terminal.
  2. When the exit_plan_mode confirmation appears with the truncation cue, note the new hint line under the body: o open full plan in editor.
  3. Press o — the full plan opens in the preferred editor (falls back to platform default). Close the editor: the dialog is still open, nothing was confirmed.
  4. cd packages/cli && npx vitest run src/ui/components/messages/ToolConfirmationMessage.test.tsx — 25 tests pass, including 4 new ones: hint renders; o stages the FULL plan text and launches the editor without resolving the confirmation; Ctrl+O is inert; o is inert on non-plan confirmations.

Evidence (Before & After)

Real end-to-end sessions (real CLI, real model producing a 55-step plan via /plan, 110×34 tmux pane, macOS).

Before — released v0.19.10: the long plan is clipped and there is no way to read the missing steps before approving:

before

After — this branch: truncation cue plus the new o open full plan in editor hint:

after-dialog

After — pressing o: the complete plan opens in the configured editor (vim), all 55 steps present; closing it returns to the still-open dialog:

after-editor-top

after-editor-bottom

Tested on

OS Status
🍏 macOS
🪟 Windows ⚠️
🐧 Linux ⚠️

…firmation

A long plan can exceed the confirmation dialog's height budget; since
QwenLM#6882 the truncation is at least visible ('... N more lines not shown
...'), but the user still had to approve a plan they could not fully
read — steps past the viewport budget were approved blind.

Pressing 'o' now writes the complete plan markdown to a temp file and
opens it in the configured editor (the same useLaunchEditor flow the
skill-review dialog uses). Viewing the plan neither confirms nor
cancels — the dialog stays open. A one-line hint under the plan body
advertises the key; the body's height budget shrinks by one row to
make room, and editor-launch failures render in the hint's place.

Fixes QwenLM#7001
@zjunothing

Copy link
Copy Markdown
Collaborator Author

Local verification report

What was implemented

Direction 1 from the issue (external viewer — explicitly recommended there as the simplest approach), built on existing infrastructure: pressing o in the plan confirmation writes the complete plan markdown to a temp file ($TMPDIR/qwen-plan-*/plan.md) and opens it via the useLaunchEditor hook — the exact same flow the skill-review dialog already uses for its o open in editor action, so editor resolution (preferredEditor setting → platform default), raw-mode suspend/resume, and Windows .cmd/.bat handling are all inherited, not reimplemented.

Details:

  • The hint line o open full plan in editor renders under the plan body on every plan confirmation; the body's height budget is reduced by exactly one row to pay for it, so the options/question never shift.
  • Viewing is side-effect-free: onConfirm is not called, the dialog stays open, and the temp file is a copy (editing it does not alter the plan).
  • Editor-launch failures render in the hint's row (error color) instead of crashing the keypress handler.
  • Ctrl+O/Cmd+O are ignored; o is inert on all non-plan confirmation types.

Verification 1 — real end-to-end session (screenshots in PR description)

Real interactive session, real model (bailian/glm-5.1), /plan prompt asking for a 55-step plan, 110×34 tmux pane, macOS, preferredEditor: vim:

  1. Confirmation appeared with ... 52 more lines not shown (viewport too small) ... and the new hint below it.
  2. Pressed o → vim opened plan.md with the title + steps 1…55 (verified top and bottom — screenshots 3 and 4).
  3. :q → back in the CLI with the confirmation dialog still open and unresolved; Esc then returned to planning as usual.

For contrast, the same flow on released v0.19.10 (screenshot 1) clips the plan with no way to read the missing steps before approving.

Verification 2 — unit tests

✓ src/ui/components/messages/ToolConfirmationMessage.test.tsx (25 tests)

4 new tests: the hint renders on plan confirmations; o stages the FULL plan text (file content compared against the complete 61-line plan string) and calls the editor launcher exactly once without resolving the confirmation; Ctrl+O does not launch; o is inert (and no hint is shown) for non-plan confirmations.

Verification 3 — static checks

  • tsc --noEmit (packages/cli): clean
  • eslint on both changed files: clean (pre-commit prettier + eslint --max-warnings 0 passed)
  • npm run check-i18n: passes (the new string falls back to English until locales pick it up)

Notes for reviewers

  • I chose to show the hint unconditionally (not only when truncated): the plan can also be wrapped or partially scrolled out in edge cases, the hint costs one reserved row, and an always-present affordance matches the skill-review dialog's precedent.
  • Directions 2/3 from the issue (scrollable/expandable region) remain open as richer follow-ups; this PR removes the "approve blind" problem now with minimal surface.
🇨🇳 中文版报告(点击展开)

本地验证报告

实现内容

采用 issue 中明确推荐的方案 1(外部查看器,三个方案中最简单),并完全复用现有基建:在 plan 确认框中按 o,将完整计划 markdown 写入临时文件($TMPDIR/qwen-plan-*/plan.md)并通过 useLaunchEditor hook 打开——与 skill-review 对话框的 o open in editor 完全同一条链路,编辑器解析(preferredEditor 设置 → 平台默认)、raw-mode 挂起/恢复、Windows .cmd/.bat 处理全部继承而非重新实现。

要点:

  • 提示行 o open full plan in editor 恒定显示在计划正文下方;正文高度预算精确减 1 行来支付这一行,选项与问题位置不变。
  • 查看无副作用:不调用 onConfirm,对话框保持打开,临时文件是副本(编辑它不会改变计划)。
  • 编辑器启动失败时错误信息显示在提示行位置(错误色),不会击穿按键处理器。
  • Ctrl+O/Cmd+O 被忽略;o 在所有非 plan 类型确认框中无效。

验证 1 —— 真实端到端会话(截图见 PR 描述)

真实交互会话、真实模型(bailian/glm-5.1)、/plan 请求 55 步计划、110×34 tmux、macOS、preferredEditor: vim

  1. 确认框出现 ... 52 more lines not shown (viewport too small) ... 与其下方的新提示行。
  2. o → vim 打开 plan.md,标题 + 第 1…55 步全部在内(截图 3、4 验证了头尾)。
  3. :q 退出 → 回到 CLI,确认框仍打开、未被触发任何选择;随后 Esc 正常返回规划。

对照组:同样流程在已发布的 v0.19.10 上(截图 1),长计划被裁切且批准前没有任何方式阅读缺失步骤。

验证 2 —— 单元测试

25 个测试全部通过,其中 4 个为新增:plan 确认框渲染提示行;o 落盘的是完整计划文本(文件内容与 61 行完整计划字符串逐字比对)且恰好调用一次编辑器启动器、不触发确认;Ctrl+O 不启动;非 plan 类型下 o 无效且不显示提示。

验证 3 —— 静态检查

  • tsc --noEmit(packages/cli):通过
  • 两个改动文件 eslint:通过(pre-commit prettier + eslint --max-warnings 0 亦通过)
  • npm run check-i18n:通过(新字符串在各语言补翻译前回退英文)

审阅者须知

  • 提示行选择了恒定显示(而非仅截断时显示):极端情况下计划也可能因换行等原因不完整可见,提示只占一行预算,且与 skill-review 对话框的先例一致。
  • issue 中的方案 2/3(区域内滚动/展开)可作为后续增强;本 PR 以最小改动面先解决"盲批"问题。

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓ — "What this PR does", "Why it's needed", "Reviewer Test Plan" with before/after screenshots all present.

Problem: real and well-documented. Issue #7001 identifies a genuine UX gap — the user is asked to approve a plan they cannot fully read when it exceeds the viewport. Before/after screenshots in the PR body make the problem concrete.

Direction: aligned. This implements Direction 1 (external viewer) from the issue — the simplest of the three approaches listed there. CHANGELOG shows prior plan-mode work (plan truncation visibility in #6882), so this is a natural follow-up. Note: #7116 offers a competing inline-toggle approach (press e to expand in place, keeps markdown preview) — maintainers can compare the two directions.

Size: not applicable — only packages/cli/src/ui/components/messages/ touched, no core paths.

Approach: minimal and focused. Two files — one component, one test. Reuses the existing useLaunchEditor hook (same infrastructure the skill-review and memory dialogs use). The hint line takes one row from the plan viewport budget, a clean trade-off. No scope creep.

Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓ — "What this PR does"、"Why it's needed"、"Reviewer Test Plan" 含 before/after 截图均已提供。

问题:真实且有据可查。Issue #7001 指出一个实际的 UX 缺口——当 plan 超过视口时,用户被要求审批一个无法完整阅读的 plan。PR 中的 before/after 截图让问题一目了然。

方向:对齐。实现了 issue 中列出的方向 1(外部查看器——三种方案中最简单的)。CHANGELOG 中有先前 plan 模式的改进(#6882 的截断可见性),本 PR 是自然延续。注意:#7116 提供了竞争方案(内联展开,按 e 展开/折叠,保留 markdown 预览)——维护者可以比较两种方向。

规模:不适用——仅触及 packages/cli/src/ui/components/messages/,无核心路径。

方案:最小且聚焦。两个文件——一个组件,一个测试。复用已有的 useLaunchEditor hook(与 skill-review 和 memory 对话框使用的基础设施相同)。提示行从 plan 视口预算中扣一行,权衡合理。无范围蔓延。

进入代码审查 🔍

Qwen Code · qwen3.7-max

Reviewed at f8f5c24cefa97f3f454900f1f249ce844e664eca · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Code Review

Independent proposal: I would have added a keybinding on the plan confirmation dialog to export the full plan text to a temp file and open it in the user's configured editor. The useLaunchEditor hook is the obvious reuse — it already handles editor discovery, raw-mode toggling, and cross-platform defaults. Temp file in os.tmpdir() with a .md extension for syntax highlighting. Error handling for missing editor. This is almost exactly what the PR does.

Comparison: The PR's implementation matches my proposal and adds a thoughtful detail I would have missed — reducing the plan viewport height by one row to make room for the hint, so nothing else shifts. The o key choice is sensible (not bound to anything else on the plan dialog), and the !key.ctrl guard prevents accidental Ctrl+O triggering. The void openPlan().catch() pattern correctly fires the editor without blocking the dialog.

Findings:

No critical issues. The implementation is clean, follows project conventions, and reuses existing infrastructure correctly. The useLaunchEditor hook uses spawnSync with stdio: 'inherit' so the editor takes over the terminal synchronously — the async/void wrapper is the right pattern here since the dialog should stay responsive.

One minor note: the temp directory (qwen-plan-XXXXXX) and file are not cleaned up after the editor closes. On most systems the OS temp cleaner handles this, but an explicit cleanup in a finally block would be tidier. Non-blocking — this matches how other editor-launch paths in the codebase work.

Reuse check: useLaunchEditor (used by SkillReviewDialog, MemoryDialog, AgentEditStep, CreationSummary) — correctly reused, no duplication. ✓

Unit Test Results

All 25 tests pass, including the 4 new tests for the full-plan viewer:

 ✓ src/ui/components/messages/ToolConfirmationMessage.test.tsx (25 tests) 446ms

 Test Files  1 passed (1)
      Tests  25 passed (25)

New test coverage:

  • Hint renders on plan confirmations ✓
  • o writes the FULL plan (not truncated) and launches editor without confirming ✓
  • Ctrl+O is inert ✓
  • o is inert on non-plan confirmations ✓

Real-Scenario Testing

Tmux end-to-end testing requires an active model session to trigger the exit_plan_mode confirmation dialog with a truncated plan — not feasible in the current CI environment without model credentials. Maintainer @wenshao has independently verified the feature locally at the same commit (f8f5c24) with a full build and real plan session, reporting LGTM.

中文说明

代码审查

独立方案: 我会在 plan 确认对话框上添加一个快捷键,将完整 plan 导出到临时文件并在用户配置的编辑器中打开。useLaunchEditor hook 是显而易见的复用——它已处理编辑器发现、raw-mode 切换和跨平台默认值。临时文件放在 os.tmpdir() 下,使用 .md 扩展名以获得语法高亮。加上缺少编辑器时的错误处理。这几乎就是 PR 的实现方式。

对比: PR 的实现与我的方案一致,并添加了一个我会遗漏的细节——将 plan 视口高度减少一行以容纳提示,这样其他元素不会移动。o 键选择合理(plan 对话框上没有其他绑定),!key.ctrl 防护避免了意外触发 Ctrl+O。void openPlan().catch() 模式正确地启动编辑器而不阻塞对话框。

发现:

无关键问题。实现干净,遵循项目约定,正确复用现有基础设施。useLaunchEditor hook 使用 spawnSyncstdio: 'inherit',编辑器同步接管终端——async/void 包装是正确的模式,因为对话框应保持响应。

一个小注意:临时目录(qwen-plan-XXXXXX)和文件在编辑器关闭后未清理。大多数系统的 OS 临时清理器会处理这个问题,但在 finally 块中显式清理会更整洁。不阻塞——这与代码库中其他编辑器启动路径的做法一致。

复用检查: useLaunchEditor(被 SkillReviewDialog、MemoryDialog、AgentEditStep、CreationSummary 使用)——正确复用,无重复。✓

单元测试结果

25 个测试全部通过,包括 4 个新增的全 plan 查看器测试:

  • plan 确认时提示行渲染 ✓
  • o 写入完整 plan(非截断版本)并启动编辑器,不触发确认 ✓
  • Ctrl+O 无反应 ✓
  • o 对非 plan 确认无反应 ✓

真实场景测试

tmux 端到端测试需要活跃的模型会话来触发带有截断 plan 的 exit_plan_mode 确认对话框——在当前无模型凭据的 CI 环境中不可行。维护者 @wenshao 已在相同 commit(f8f5c24)上独立进行了本地验证,使用完整构建和真实 plan 会话,报告 LGTM。

Qwen Code · qwen3.7-max

Reviewed at f8f5c24cefa97f3f454900f1f249ce844e664eca · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Confidence: 5/5 — Clean implementation of a real UX gap. Competing approach (#7116) is closed. Maintainer-approved. All CI green. Ready to ship.

Re-run note: previous Stage 3 flagged #7116 (inline toggle) as a competing approach worth maintainer consideration. That PR is now closed, so the only reason this wasn't a 5/5 is gone. Everything else still holds — focused 2-file diff, clean reuse of useLaunchEditor, comprehensive test coverage, maintainer-verified locally at this commit.

中文说明

信心度: 5/5 — 干净地实现了一个真实的 UX 缺口修复。竞争方案 (#7116) 已关闭。维护者已批准。所有 CI 通过。可以合并。

Re-run 说明:之前的 Stage 3 将 #7116(内联展开)标记为值得维护者考虑的替代方案。该 PR 现已关闭,因此之前不是 5/5 的唯一原因已消除。其他一切仍然成立——聚焦的 2 文件 diff,干净地复用 useLaunchEditor,全面的测试覆盖,维护者在此 commit 上本地验证通过。

Qwen Code · qwen3.7-max

Reviewed at f8f5c24cefa97f3f454900f1f249ce844e664eca · re-run with @qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship. ✅

Comment on lines +122 to +125
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'qwen-plan-'));
const planPath = path.join(dir, 'plan.md');
await fs.writeFile(planPath, confirmationDetails.plan);
await launchEditor(planPath);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] openFullPlanInEditor creates a temp directory via fs.mkdtemp on every o press but never removes it — not after the editor exits, not on error, not on dialog dismissal. The established codebase pattern in text-buffer.ts (lines 2524, 2559, 2626) wraps identical mkdtemp usage in try/finally with fs.rmSync(tmpDir, { recursive: true, force: true }). — Failure scenario: each o press orphans a qwen-plan-XXXXXX/plan.md directory containing the full plan text (potentially sensitive conversation content) under os.tmpdir(). On shared systems other processes can discover and read these files; on tmpfs-backed /tmp they consume RAM until reboot.

Suggested change
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'qwen-plan-'));
const planPath = path.join(dir, 'plan.md');
await fs.writeFile(planPath, confirmationDetails.plan);
await launchEditor(planPath);
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'qwen-plan-'));
try {
const planPath = path.join(dir, 'plan.md');
await fs.writeFile(planPath, confirmationDetails.plan, { mode: 0o600 });
await launchEditor(planPath);
} finally {
await fs.rm(dir, { recursive: true, force: true }).catch(() => {});
}

— qwen3.7-max via Qwen Code /review

Comment on lines +127 to +129
void openPlan().catch((err: unknown) => {
setPlanViewError(err instanceof Error ? err.message : String(err));
});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The .catch handler that sets planViewError when the editor fails to launch has no test coverage — no test uses launchEditorMock.mockRejectedValueOnce(...) to exercise the error-display path. — Concrete cost: a regression that silently swallows the error (e.g., someone "simplifies" the catch to a no-op) would ship undetected, leaving the user staring at a frozen dialog with no feedback on why the editor never opened.

Suggested change
void openPlan().catch((err: unknown) => {
setPlanViewError(err instanceof Error ? err.message : String(err));
});
void openPlan().catch((err: unknown) => {
setPlanViewError(err instanceof Error ? err.message : String(err));
});
// Add test: launchEditorMock.mockRejectedValueOnce(new Error('No editor found'))
// Assert: lastFrame() contains 'No editor found'

— qwen3.7-max via Qwen Code /review

@doudouOUC doudouOUC left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. Suggestions are inline. 2 Suggestion-level finding(s) could not be anchored to the diff; see the terminal output. Not reviewed: chunk 1 — no agent reported covering these; nobody read them.

— qwen3.7-max via Qwen Code /review

</Text>
) : (
<Text color={theme.text.secondary} wrap="truncate">
{t('o open full plan in editor')}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The i18n key 'o open full plan in editor' is not registered in any translation file under packages/cli/src/i18n/. The t() function falls back to the key string, so English works, but non-English locales will show untranslated English while all surrounding UI is translated. — Concrete cost: breaks the established convention where all other t() keys in this component (e.g., 'Apply this change?', 'Yes, allow once') are registered in all 9 locale files.

Suggested change
{t('o open full plan in editor')}
{t('o open full plan in editor')}

Register the key in en.js and other locale files under packages/cli/src/i18n/.

— qwen3.7-max via Qwen Code /review

@Alex-ai-future

Copy link
Copy Markdown
Contributor

Is there any way to keep the md preview while opening the plan?

@Alex-ai-future

Copy link
Copy Markdown
Contributor

I also submitted #7116 with an inline toggle approach — press e to expand/collapse in place, keeps the markdown preview. Both directions have trade-offs. Maintainers can compare.

@wenshao

wenshao commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /review

@github-actions

Copy link
Copy Markdown
Contributor
_Qwen Code review request accepted. Review is queued in [workflow run](https://github.com/QwenLM/qwen-code/actions/runs/29626839805)._

@wenshao

wenshao commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

Independent local verification (maintainer-side) — LGTM

Verified at the pushed head f8f5c24 (merge-base 10a39d15), in an isolated worktree with a fresh npm ci + full build. Before driving anything I confirmed the built artifact actually contains this PR's code (grep for the hint string in dist/), and that neither touched file has changed on main since the merge-base — so this head-based verification also represents the post-merge tree. All of the author's claims reproduce.

1. Unit tests + load-bearing A/B

  • npx vitest run src/ui/components/messages/ToolConfirmationMessage.test.tsx25/25 pass (matches the author's report).
  • A/B: overlay the merge-base component while keeping the PR's tests → 2 of the 4 new tests go red (hint renders; o stages the full plan and launches the editor). The other 2 (Ctrl+O inert, o inert on non-plan) are inertness guards that pass on main by design. The new tests are load-bearing, not tautological.

2. Real end-to-end (built CLI, PTY, real vim)

Setup: real packages/cli/dist build driven in a 110×34 PTY (node-pty + @xterm/headless), isolated HOME and TMPDIR, preferredEditor: vim, --approval-mode plan, and a local OpenAI-compatible mock that answers the first turn with an exit_plan_mode tool call carrying a 60-step plan (2,895 bytes).

PR build — all 12 checks green:

Check Result
Plan confirmation renders with truncation cue ... 56 more lines not shown (viewport too small) ...
Hint line under the plan body o open full plan in editor
o → editor opens ✅ real vim spawned on plan.md in the same PTY
Full plan reached the editor ✅ title at top; GStep 60 visible at bottom
Staged temp file is the COMPLETE plan ✅ byte-identical to the tool-call argument (2,895 = 2,895 bytes)
:q → dialog untouched ✅ confirmation still open, still waiting, hint re-rendered
Nothing was confirmed by viewing onConfirm not fired (dialog unresolved; subsequent Esc cancels normally)

Merge-base build (control) — rebuilt from 10a39d15 source and verified the artifact no longer contains the PR string before running:

Check Result
Hint absent
o inert ✅ screen unchanged after 2.5 s, no editor
No temp file created $TMPDIR stays empty
Height budget ✅ cue reads 55 more lines vs the PR's 56 — quantitative proof of the "exactly one row for the hint" claim

Screenshots (real captures from the PTY sessions above)

Before — merge-base build: plan truncated, no hint, o does nothing:

before

After — PR build: same session shape, hint line present:

after

Pressing o — real vim on the staged plan.md, top and bottom (all 60 steps present):

vim-top

vim-bottom

After :q — back in the CLI, confirmation still open and unresolved:

dialog-return

3. Code-review notes (non-blocking)

  • Correct reuse of useLaunchEditor — byte-for-byte the same flow as SkillReviewDialog's o open in editor, so editor resolution, raw-mode suspend/resume, and the Windows .cmd/.bat path are inherited.
  • No keybinding conflict: useSelectionList (the radio list under the dialog) only consumes arrows / j/k / Enter / digits — verified in source and observed in the E2E.
  • Minor observations, none blocking: temp dirs are never cleaned up (one per o press; OS tmp reaping handles it); Shift+O also triggers (harmless); the hint shows even when the plan is not truncated — matches the author's stated rationale and the skill-review precedent.

4. Landscape note for the merging maintainer

Open PR #7116 implements Direction 3 from #7001 (e inline expand/collapse) and edits the same two files — the two PRs are complementary UX directions, but whichever lands second will need a rebase. This one is the smaller, infrastructure-reusing change. CI at this head is green (ubuntu test job passed).

Verdict: ready to merge from my side.

🇨🇳 中文版报告(点击展开)

独立本地验证(维护者侧)— LGTM

在隔离 worktree 中于推送头 f8f5c24(merge-base 10a39d15)完成验证,全新 npm ci + 完整构建。驱动之前先确认了构建产物确实包含本 PR 代码(在 dist/ 中 grep 提示字符串),并确认两个改动文件自 merge-base 以来在 main 上零变动 —— 因此本次基于 head 的验证同样代表合并后的树。作者的全部声明均可复现。

1. 单元测试 + 承重 A/B

  • npx vitest run src/ui/components/messages/ToolConfirmationMessage.test.tsx25/25 通过(与作者报告一致)。
  • A/B:保留 PR 测试、把组件回退到 merge-base → 4 个新测试中 2 个变红(hint 渲染;o 落盘完整计划并启动编辑器)。另外 2 个(Ctrl+O 惰性、非 plan 类型 o 惰性)在 main 上本来就应通过。新测试是承重的,不是同义反复。

2. 真实端到端(构建产物、PTY、真实 vim)

环境:真实 packages/cli/dist 构建,110×34 PTY(node-pty + @xterm/headless)驱动,隔离 HOMETMPDIRpreferredEditor: vim--approval-mode plan;本地 OpenAI 兼容 mock 在首轮返回携带 60 步计划(2,895 字节)exit_plan_mode 工具调用。

PR 构建 —— 12 项检查全绿: 截断提示 ... 56 more lines not shown ...;hint 行渲染;按 o 在同一 PTY 中真实拉起 vim 打开 plan.mdG 到底部可见 Step 60;落盘临时文件与工具调用参数逐字节一致(2,895 = 2,895 字节);:q 返回后确认框仍打开、未被触发任何选择(onConfirm 未调用),随后 Esc 正常取消。

merge-base 构建(对照组) —— 从 10a39d15 源码重建、并先验证产物中已不含 PR 字符串:无 hint;o 完全惰性(2.5 秒屏幕无变化、无编辑器、无临时文件);截断提示为 55 more lines,对比 PR 的 56 —— 定量证实了"hint 恰好占用一行预算"的声明。

(截图见上方英文部分,均为上述 PTY 会话的真实捕获。)

3. 代码审查备注(不阻塞)

  • 正确复用 useLaunchEditor —— 与 SkillReviewDialogo open in editor 完全同一条链路,编辑器解析、raw-mode 挂起/恢复、Windows .cmd/.bat 路径全部继承。
  • 无按键冲突:对话框下方的 useSelectionList 只消费方向键 / j/k / 回车 / 数字(源码验证 + E2E 观察一致)。
  • 次要观察(均不阻塞):临时目录不做清理(每按一次 o 一个,由系统 tmp 回收);Shift+O 也会触发(无害);未截断时 hint 也显示 —— 与作者说明的理由及 skill-review 先例一致。

4. 给合并维护者的 landscape 提示

开放 PR #7116 实现了 #7001 的方向 3(e 行内展开/折叠),改动同样的两个文件 —— 两个 PR 是互补的 UX 方向,后合入者需要 rebase。本 PR 是更小、复用现有基建的那个。当前 head 的 CI 为绿(ubuntu 测试通过)。

结论:本侧认为可以合并。

@wenshao

wenshao commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

Triage re-run completed — approved

The only material change since the last run: competing PR #7116 is now closed. Confidence updated from 4/5 → 5/5. All stage comments reflect the current state.

Stage 1, Stage 2, and Stage 3 comments above were updated in place.

Reviewed at f8f5c24cefa97f3f454900f1f249ce844e664eca · re-run with @qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] ToolConfirmationMessage.tsx:122-125 — temp directory created by fs.mkdtemp is never removed after the editor closes (existing blocker from @qwen-code-ci-bot, re-checked: still stands at this commit)

— qwen3.7-max via Qwen Code /review

@wenshao

wenshao commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@wenshao
wenshao dismissed stale reviews from qwen-code-ci-bot and qwen-code-ci-bot July 19, 2026 02:47

Dismissing per maintainer decision. The sole finding (temp dir from fs.mkdtemp not cleaned up) matches existing editor-launch paths in the codebase and was rated non-blocking by the project's own triage (stage 2); OS tmp reaping handles cleanup. Maintainer @wenshao has independently verified the feature end-to-end and approved.

@wenshao

wenshao commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship. ✅

@wenshao
wenshao added this pull request to the merge queue Jul 19, 2026
Merged via the queue into QwenLM:main with commit 7b17144 Jul 19, 2026
127 of 128 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: allow viewing full plan when exit_plan_mode confirmation is truncated

5 participants