Skip to content

Commit 008a15d

Browse files
authored
Merge pull request #334 from lessweb/plan-mode-clear-context-implement
feat: implement clear context option in Plan Mode and update related prompts
2 parents b69e845 + 631a439 commit 008a15d

6 files changed

Lines changed: 65 additions & 12 deletions

File tree

docs/plan-mode.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,11 @@ Plan Mode 的核心规则是**只规划,不动手**。例如以下操作是**
9090
| 选项 | 作用 |
9191
| ---- | ---- |
9292
| **1. implement this plan** | 退出 Plan Mode,自动发送实现指令,让 AI 开始按方案写代码 |
93-
| **2. stay in Plan mode** | 保持在 Plan Mode,继续修改或完善方案 |
94-
| **3. switch to Default mode** | 退出 Plan Mode,回到默认模式(不自动开始实现) |
93+
| **2. clear context and implement** | 保留当前会话,创建一个仅携带已确认方案的新会话,并在 Default mode 下开始实现 |
94+
| **3. stay in Plan mode** | 保持在 Plan Mode,继续修改或完善方案 |
95+
| **4. switch to Default mode** | 退出 Plan Mode,回到默认模式(不自动开始实现) |
9596

96-
你可以用数字键 `1-3` 直接选择,也可以用 `↑/↓` 移动光标后按 `Enter` 确认。按 `Esc` 等同于选择 "stay in Plan mode"。
97+
你可以用数字键 `1-4` 直接选择,也可以用 `↑/↓` 移动光标后按 `Enter` 确认。按 `Esc` 等同于选择 "stay in Plan mode"。选择清除上下文后,原 Plan 会话仍可通过 `/resume` 恢复。新会话不会继承旧对话消息
9798

9899
## Plan Mode 与 UpdatePlan 工具的区别
99100

docs/plan-mode_en.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,11 @@ After the plan is output, Deep Code automatically shows a choice dialog—no ext
9090
| Option | Effect |
9191
| ------ | ------ |
9292
| **1. implement this plan** | Leave Plan Mode and automatically send an implementation prompt so the AI starts coding |
93-
| **2. stay in Plan mode** | Stay in Plan Mode to continue refining the plan |
94-
| **3. switch to Default mode** | Leave Plan Mode and return to Default mode without starting implementation |
93+
| **2. clear context and implement** | Preserve the current session, create a new session carrying only the approved plan, and start implementing in Default mode |
94+
| **3. stay in Plan mode** | Stay in Plan Mode to continue refining the plan |
95+
| **4. switch to Default mode** | Leave Plan Mode and return to Default mode without starting implementation |
9596

96-
You can press `1-3` to select directly, or use `↑/↓` to move the cursor and `Enter` to confirm. Pressing `Esc` is equivalent to choosing "stay in Plan mode."
97+
You can press `1-4` to select directly, or use `↑/↓` to move the cursor and `Enter` to confirm. Pressing `Esc` is equivalent to choosing "stay in Plan mode." After clearing context, the original Plan session remains available through `/resume`. The new session does not inherit its conversation messages.
9798

9899
## Plan Mode vs. UpdatePlan Tool
99100

packages/cli/src/tests/prompt-input-keys.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
buildInitPromptSubmission,
2424
buildPromptDraftFromSessionMessage,
2525
extractProposedPlan,
26+
getClearContextImplementationPrompt,
2627
getImplementationPrompt,
2728
getPlanImplementationChoice,
2829
disableTerminalExtendedKeys,
@@ -181,6 +182,27 @@ test("getImplementationPrompt uses Chinese only above five full-width punctuatio
181182
assert.equal(getImplementationPrompt(",、;。;。"), "实现此方案。");
182183
});
183184

185+
test("getClearContextImplementationPrompt carries the complete plan into a fresh context", () => {
186+
const plan = "# 方案\n\n- 保留 `Markdown`\n- Verify tests";
187+
const prompt = getClearContextImplementationPrompt(plan);
188+
189+
assert.equal(
190+
prompt,
191+
"A previous agent produced the plan below to accomplish the user's task. " +
192+
"Implement the plan in a fresh context. Treat the plan as the source of " +
193+
"user intent, re-read files as needed, and carry the work through " +
194+
`implementation and verification.\n\n${plan}`
195+
);
196+
});
197+
198+
test("getPlanImplementationChoice maps all four number shortcuts", () => {
199+
const key = { escape: false, return: false };
200+
assert.equal(getPlanImplementationChoice("1", key, 0), "implement");
201+
assert.equal(getPlanImplementationChoice("2", key, 0), "clear-context");
202+
assert.equal(getPlanImplementationChoice("3", key, 0), "stay");
203+
assert.equal(getPlanImplementationChoice("4", key, 0), "default");
204+
});
205+
184206
test("getPlanImplementationChoice treats escape as staying in Plan Mode", () => {
185207
assert.equal(getPlanImplementationChoice("", { escape: true, return: false }, 0), "stay");
186208
});

packages/cli/src/ui/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ export { AskUserQuestionPrompt, applyOtherAnswerEdit } from "./views/AskUserQues
1818
export {
1919
PlanImplementationPrompt,
2020
extractProposedPlan,
21+
getClearContextImplementationPrompt,
2122
getImplementationPrompt,
2223
getPlanImplementationChoice,
24+
type PlanImplementationChoice,
2325
} from "./views/PlanImplementationPrompt";
2426
export { MessageView } from "./components";
2527
export { parseDiffPreview } from "./components/MessageView/utils";

packages/cli/src/ui/views/App.tsx

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@ import {
2020
formatAskUserQuestionAnswers,
2121
} from "../core/ask-user-question";
2222
import { PermissionPrompt, type PermissionPromptResult } from "./PermissionPrompt";
23-
import { PlanImplementationPrompt, extractProposedPlan, getImplementationPrompt } from "./PlanImplementationPrompt";
23+
import {
24+
PlanImplementationPrompt,
25+
extractProposedPlan,
26+
getClearContextImplementationPrompt,
27+
getImplementationPrompt,
28+
type PlanImplementationChoice,
29+
} from "./PlanImplementationPrompt";
2430
import { buildExitSummaryText, buildPluginRateLimitHintText, buildResumeHintText } from "../exit-summary";
2531
import { RawMode, useRawModeContext } from "../contexts";
2632
import { renderMessageToStdout } from "../components/MessageView/utils";
@@ -561,13 +567,23 @@ function App({ projectRoot, initialPrompt, resumeSessionId, forkSessionId, onRes
561567
);
562568

563569
const handlePlanImplementationChoice = useCallback(
564-
(choice: "implement" | "stay" | "default") => {
570+
(choice: PlanImplementationChoice) => {
565571
const proposedPlan = pendingPlanImplementation;
566572
setPendingPlanImplementation(null);
567573
if (choice === "stay") {
568574
return;
569575
}
570576
setPlanMode(false);
577+
if (choice === "clear-context" && proposedPlan) {
578+
void resetToWelcome().then(() => {
579+
handleSubmit({
580+
text: getClearContextImplementationPrompt(proposedPlan),
581+
imageUrls: [],
582+
planMode: false,
583+
});
584+
});
585+
return;
586+
}
571587
if (choice === "implement" && proposedPlan) {
572588
handleSubmit({
573589
text: getImplementationPrompt(proposedPlan),
@@ -576,7 +592,7 @@ function App({ projectRoot, initialPrompt, resumeSessionId, forkSessionId, onRes
576592
});
577593
}
578594
},
579-
[handleSubmit, pendingPlanImplementation]
595+
[handleSubmit, pendingPlanImplementation, resetToWelcome]
580596
);
581597

582598
const handleExitShortcut = useCallback(() => {

packages/cli/src/ui/views/PlanImplementationPrompt.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,25 @@ import { Box, Text } from "ink";
33
import { useTerminalInput } from "../hooks";
44
import type { InputKey } from "../hooks";
55

6-
type PlanImplementationChoice = "implement" | "stay" | "default";
6+
export type PlanImplementationChoice = "implement" | "clear-context" | "stay" | "default";
77

88
type Props = {
99
onSelect: (choice: PlanImplementationChoice) => void;
1010
};
1111

1212
const CHOICES: Array<{ value: PlanImplementationChoice; label: string }> = [
1313
{ value: "implement", label: "implement this plan" },
14+
{ value: "clear-context", label: "clear context and implement" },
1415
{ value: "stay", label: "stay in Plan mode" },
1516
{ value: "default", label: "switch to Default mode" },
1617
];
1718

19+
const CLEAR_CONTEXT_IMPLEMENTATION_PREFIX =
20+
"A previous agent produced the plan below to accomplish the user's task. " +
21+
"Implement the plan in a fresh context. Treat the plan as the source of " +
22+
"user intent, re-read files as needed, and carry the work through " +
23+
"implementation and verification.";
24+
1825
/** Return only a complete proposed plan, so historical or partial tags cannot trigger the chooser. */
1926
export function extractProposedPlan(reply: string | null): string | null {
2027
if (!reply) {
@@ -29,6 +36,10 @@ export function getImplementationPrompt(plan: string): string {
2936
return fullWidthPunctuationCount > 5 ? "实现此方案。" : "Implement the plan.";
3037
}
3138

39+
export function getClearContextImplementationPrompt(plan: string): string {
40+
return `${CLEAR_CONTEXT_IMPLEMENTATION_PREFIX}\n\n${plan}`;
41+
}
42+
3243
export function getPlanImplementationChoice(
3344
input: string,
3445
key: Pick<InputKey, "escape" | "return">,
@@ -37,7 +48,7 @@ export function getPlanImplementationChoice(
3748
if (key.escape) {
3849
return "stay";
3950
}
40-
if (input && /^[1-3]$/.test(input)) {
51+
if (input && /^[1-4]$/.test(input)) {
4152
return CHOICES[Number(input) - 1]!.value;
4253
}
4354
return key.return ? CHOICES[cursor]!.value : null;
@@ -81,7 +92,7 @@ export function PlanImplementationPrompt({ onSelect }: Props): React.ReactElemen
8192
))}
8293
</Box>
8394
<Box marginTop={1}>
84-
<Text dimColor>1-3 select · ↑/↓ move · Enter select</Text>
95+
<Text dimColor>1-4 select · ↑/↓ move · Enter select</Text>
8596
</Box>
8697
</Box>
8798
);

0 commit comments

Comments
 (0)