@hank-warren/pi-plan-mode 1.1.1 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/README.md +9 -3
- package/package.json +1 -1
- package/src/plan-action-menus.ts +1 -0
- package/src/plan-launch-menu.ts +1 -1
- package/src/plan-mode.ts +117 -19
- package/src/presentation.ts +11 -2
- package/src/prompt.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# @hank-warren/pi-plan-mode
|
|
2
2
|
|
|
3
|
+
## 1.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 3fbf632: Persist the thinking-level capture taken while restoring plan mode. The two restore paths applied the configured level without writing the captured previous one, and Pi's `setThinkingLevel` writes the new level into the user's settings — so a session that ended without `session_shutdown` lost the only record of the original level and the next restore captured the plan level as "previous", permanently raising the user's default. An unchanged restore still writes nothing.
|
|
8
|
+
|
|
9
|
+
## 1.2.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 358aa1f: re-read `pi-plan-mode.json` when it changes instead of only at session start
|
|
14
|
+
|
|
15
|
+
The settings file was read once per session, so a hand-edit — or a Settings save
|
|
16
|
+
from another session — did nothing until the next restart. That sat badly next to
|
|
17
|
+
the plan file, which has been hand-editable and read from disk since 1.0.
|
|
18
|
+
|
|
19
|
+
Plan mode now watches the settings file's directory (not the file: saves land
|
|
20
|
+
through a temp file and an atomic rename, which replaces the inode) and re-reads
|
|
21
|
+
on a 75 ms debounce, so one save costs one load.
|
|
22
|
+
|
|
23
|
+
An unparseable file is still reported and replaced by the defaults at session
|
|
24
|
+
start. Mid-session it is ignored and the last good settings stay in place: a
|
|
25
|
+
reload sees the file the moment an editor touches it, so an invalid read is
|
|
26
|
+
usually a partial write, and there is no context to report it through anyway.
|
|
27
|
+
Deleting the file mid-session does restore the defaults.
|
|
28
|
+
|
|
29
|
+
Session start now also honours the injected `settingsPath` dependency when
|
|
30
|
+
reading, which previously only the Settings menu used — the two could disagree
|
|
31
|
+
about which file they were looking at.
|
|
32
|
+
|
|
3
33
|
## 1.1.1
|
|
4
34
|
|
|
5
35
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
`@hank-warren/pi-plan-mode` adds a `/plan` mode to Pi for research and design. You gather information, ask questions, and land on a plan — then implement it, either in the same conversation or in a fresh one.
|
|
6
6
|
|
|
7
|
-
**Plan mode is a mode of intent, not a permission system.** It blocks `edit
|
|
7
|
+
**Plan mode is a mode of intent, not a permission system.** It blocks `edit` and `write` while planning and leaves every other tool exactly as you configured it. Command safety stays with your permission extension (for example [`@hank-warren/pi-auto-permissions`](../pi-auto-permissions)), which already reviews Bash. The only tool Plan mode ever removes from the active set is its own `plan_mode_question`, and only when a better questionnaire is installed (see below), so it cannot break other extensions.
|
|
8
8
|
|
|
9
9
|
The plan is written to a **durable file** that survives compaction, survives resume, and can be hand-edited.
|
|
10
10
|
|
|
@@ -45,7 +45,9 @@ pi -e npm:@hank-warren/pi-plan-mode
|
|
|
45
45
|
|
|
46
46
|
`--plan` starts a session directly in Plan mode.
|
|
47
47
|
|
|
48
|
-
While Plan mode is active, ask the agent to design the change. It can read, search, and run commands, but `edit
|
|
48
|
+
While Plan mode is active, ask the agent to design the change. It can read, search, and run commands, but `edit` and `write` are blocked. When the plan is decision-complete, the agent calls `plan_mode_complete` and the plan is written to disk.
|
|
49
|
+
|
|
50
|
+
A completed plan is not final until you act on it: just type feedback to revise — the next planning turn supersedes the proposed plan, and the next `plan_mode_complete` replaces it.
|
|
49
51
|
|
|
50
52
|
From a completed plan you can:
|
|
51
53
|
|
|
@@ -72,6 +74,8 @@ Writes are atomic (temp file plus rename), so a reader never sees a partial plan
|
|
|
72
74
|
|
|
73
75
|
Open **Settings** from the `/plan` menu, or edit `$PI_CODING_AGENT_DIR/pi-plan-mode.json` (normally `~/.pi/agent/pi-plan-mode.json`). The file is optional.
|
|
74
76
|
|
|
77
|
+
The file is read at session start and **re-read whenever it changes**, so a hand-edit — or a save from another session — applies without restarting. Like the plan file itself, it is edited on disk and read from disk.
|
|
78
|
+
|
|
75
79
|
```json
|
|
76
80
|
{
|
|
77
81
|
"thinkingLevel": "inherit",
|
|
@@ -89,9 +93,11 @@ Open **Settings** from the `/plan` menu, or edit `$PI_CODING_AGENT_DIR/pi-plan-m
|
|
|
89
93
|
|
|
90
94
|
Unknown keys are preserved. Settings removed in 1.0 (`defaultPlanTools`, `bashPolicy`, `safeSubcommands`, `implementationPlanRetention`) are ignored rather than treated as errors, so an existing settings file keeps working.
|
|
91
95
|
|
|
96
|
+
A settings file that does not parse is reported at session start and the defaults are used. Mid-session it is ignored instead, leaving the last good settings in place: an edit is seen the moment your editor touches the file, so an unreadable one is usually a half-finished save rather than what you meant.
|
|
97
|
+
|
|
92
98
|
## 🔐 What Plan mode does and does not enforce
|
|
93
99
|
|
|
94
|
-
Plan mode blocks exactly
|
|
100
|
+
Plan mode blocks exactly two tools while planning: `edit` and `write`. That is the whole enforcement surface. Checklist tools (a `todo` extension, for example) are deliberately not blocked — a task list is ephemeral planning scratch, and the planning prompt steers the model away from execution-progress tracking.
|
|
95
101
|
|
|
96
102
|
It deliberately does **not** police Bash, subagents, MCP tools, or any other extension tool. Those decisions belong to your permission layer, which can see the whole session and judge each call. Pair Plan mode with a permission extension such as `@hank-warren/pi-auto-permissions` if you want command review during planning.
|
|
97
103
|
|
package/package.json
CHANGED
package/src/plan-action-menus.ts
CHANGED
|
@@ -10,6 +10,7 @@ interface MenuLifecycle {
|
|
|
10
10
|
const IMPLEMENTATION_CONTEXT_LINES = [
|
|
11
11
|
"Implement here keeps this planning conversation.",
|
|
12
12
|
"Start fresh opens a new session that reads the same plan file.",
|
|
13
|
+
"Or just type feedback to revise — the next completed plan supersedes this one.",
|
|
13
14
|
] as const;
|
|
14
15
|
|
|
15
16
|
interface PlanMenuOptions extends MenuLifecycle {
|
package/src/plan-launch-menu.ts
CHANGED
|
@@ -13,7 +13,7 @@ const HOW_IT_WORKS_LINES = [
|
|
|
13
13
|
"Plan mode is for research and design, not implementation.",
|
|
14
14
|
"Explore the codebase, ask decision questions, then finish with plan_mode_complete.",
|
|
15
15
|
"The plan is written to a durable file that survives compaction and can be hand-edited.",
|
|
16
|
-
"Built-in edit
|
|
16
|
+
"Built-in edit and write are blocked while planning.",
|
|
17
17
|
"All other tools stay exactly as configured; command safety is left to your permission extension.",
|
|
18
18
|
"When the plan is ready: implement here, or start a fresh session that reads the same file.",
|
|
19
19
|
] as const;
|
package/src/plan-mode.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { watch } from "node:fs";
|
|
2
|
+
import { basename, dirname } from "node:path";
|
|
1
3
|
import type {
|
|
2
4
|
ExtensionAPI,
|
|
3
5
|
ExtensionCommandContext,
|
|
@@ -46,6 +48,7 @@ import {
|
|
|
46
48
|
awaitPlanModeSettingsWrites,
|
|
47
49
|
configuredThinkingLevel,
|
|
48
50
|
type PlanModeSettings,
|
|
51
|
+
planModeSettingsPath,
|
|
49
52
|
readPlanModeSettings,
|
|
50
53
|
} from "./settings.js";
|
|
51
54
|
import { type PlanModeState, restorePlanModeState } from "./state.js";
|
|
@@ -56,8 +59,18 @@ const STATE_ENTRY_TYPE = "plan-mode-state";
|
|
|
56
59
|
* MCP, and other extension tools — is left to the session's normal permission
|
|
57
60
|
* layer (for example @hank-warren/pi-auto-permissions), so Plan mode never
|
|
58
61
|
* mutates the active tool set and never fights other extensions for it.
|
|
62
|
+
* Checklist tools (a `todo` extension, for example) are deliberately not
|
|
63
|
+
* blocked: a task list is ephemeral planning scratch, and the planning prompt
|
|
64
|
+
* already steers the model away from execution-progress tooling. (`update_plan`
|
|
65
|
+
* was once listed here; it was a pre-1.0 upstream tool that no longer exists.)
|
|
59
66
|
*/
|
|
60
|
-
const BLOCKED_TOOLS = new Set(["edit", "write"
|
|
67
|
+
const BLOCKED_TOOLS = new Set(["edit", "write"]);
|
|
68
|
+
/**
|
|
69
|
+
* One hand-edit or menu save fans out into several filesystem events (temp file
|
|
70
|
+
* created, renamed into place). Collapsing them into one re-read keeps a save
|
|
71
|
+
* to a single load.
|
|
72
|
+
*/
|
|
73
|
+
const SETTINGS_RELOAD_DEBOUNCE_MS = 75;
|
|
61
74
|
|
|
62
75
|
/**
|
|
63
76
|
* Which question tool the prompt should name this turn.
|
|
@@ -124,6 +137,8 @@ export default function planMode(pi: ExtensionAPI, dependencies: PlanModeDepende
|
|
|
124
137
|
let workflowGeneration = 0;
|
|
125
138
|
let refreshStateBeforeFirstAgentStart = false;
|
|
126
139
|
let menuController = new AbortController();
|
|
140
|
+
let settingsWatch: ReturnType<typeof watch> | undefined;
|
|
141
|
+
let settingsReloadTimer: ReturnType<typeof setTimeout> | undefined;
|
|
127
142
|
const persistState = () => pi.appendEntry<PlanModeState>(STATE_ENTRY_TYPE, state);
|
|
128
143
|
const planExports = createPlanExportController({
|
|
129
144
|
getState: () => state,
|
|
@@ -146,8 +161,14 @@ export default function planMode(pi: ExtensionAPI, dependencies: PlanModeDepende
|
|
|
146
161
|
exportPlan: (ctx, path, signal, isCurrent) => planExports.export(path, ctx, signal, isCurrent),
|
|
147
162
|
stay: updateUi,
|
|
148
163
|
exitReady: (ctx) => {
|
|
164
|
+
// Same had-plan branching as the /plan exit command: the menu must not
|
|
165
|
+
// claim a plan was discarded when none was ever completed.
|
|
166
|
+
const hadPlan = state.planPath !== undefined;
|
|
149
167
|
void exitPlanMode(ctx).then(() => {
|
|
150
|
-
ctx.ui.notify(
|
|
168
|
+
ctx.ui.notify(
|
|
169
|
+
hadPlan ? "Plan mode disabled. Proposed plan discarded." : "Plan mode disabled.",
|
|
170
|
+
"info",
|
|
171
|
+
);
|
|
151
172
|
});
|
|
152
173
|
},
|
|
153
174
|
});
|
|
@@ -295,6 +316,73 @@ export default function planMode(pi: ExtensionAPI, dependencies: PlanModeDepende
|
|
|
295
316
|
},
|
|
296
317
|
});
|
|
297
318
|
|
|
319
|
+
const readRuntimeSettings = () =>
|
|
320
|
+
dependencies.readSettings?.() ?? readPlanModeSettings(dependencies.settingsPath);
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* `ctx` present means this is the session-start load: problems are reported
|
|
324
|
+
* and anything unusable falls back to defaults.
|
|
325
|
+
*
|
|
326
|
+
* `ctx` absent means a watch-triggered reload, which keeps the last good
|
|
327
|
+
* settings when the file no longer parses. A hand-edit is observed the moment
|
|
328
|
+
* the editor touches the file, so an invalid read is usually a half-written
|
|
329
|
+
* save rather than intent — discarding a working export path for it, with no
|
|
330
|
+
* `ctx` to explain why, would be worse than waiting for the next write. A
|
|
331
|
+
* genuinely broken file is still reported at the next session start.
|
|
332
|
+
*/
|
|
333
|
+
const loadPlanModeSettings = async (generation: number, ctx?: ExtensionContext) => {
|
|
334
|
+
const loaded = await readRuntimeSettings();
|
|
335
|
+
if (generation !== menuGeneration || menuController.signal.aborted) return;
|
|
336
|
+
if (loaded.kind === "invalid" && !ctx) return;
|
|
337
|
+
settings = loaded.kind === "loaded" ? loaded.settings : { thinkingLevel: "inherit" };
|
|
338
|
+
if (!ctx) return;
|
|
339
|
+
if (loaded.kind === "invalid") {
|
|
340
|
+
ctx.ui.notify(`pi-plan-mode settings ignored: ${loaded.reason}`, "warning");
|
|
341
|
+
}
|
|
342
|
+
if (loaded.notice) ctx.ui.notify(loaded.notice, "warning");
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
const stopPlanModeSettingsWatch = () => {
|
|
346
|
+
if (settingsReloadTimer) {
|
|
347
|
+
clearTimeout(settingsReloadTimer);
|
|
348
|
+
settingsReloadTimer = undefined;
|
|
349
|
+
}
|
|
350
|
+
settingsWatch?.close();
|
|
351
|
+
settingsWatch = undefined;
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Watches the settings file's directory rather than the file itself: saves go
|
|
356
|
+
* through a temp file and an atomic rename, and a watch bound to the old inode
|
|
357
|
+
* would go deaf after the first one.
|
|
358
|
+
*/
|
|
359
|
+
const startPlanModeSettingsWatch = (generation: number) => {
|
|
360
|
+
stopPlanModeSettingsWatch();
|
|
361
|
+
if (dependencies.readSettings) return;
|
|
362
|
+
const watchedPath = dependencies.settingsPath ?? planModeSettingsPath();
|
|
363
|
+
const watchedFile = basename(watchedPath);
|
|
364
|
+
try {
|
|
365
|
+
const watcher = watch(dirname(watchedPath), { persistent: false }, (event, changedFile) => {
|
|
366
|
+
if (event !== "rename" && event !== "change") return;
|
|
367
|
+
// A null filename means the platform could not name the entry; reload
|
|
368
|
+
// rather than miss the edit. The agent directory holds other churn, so
|
|
369
|
+
// a named entry that is not ours is ignored.
|
|
370
|
+
if (changedFile && changedFile.toString() !== watchedFile) return;
|
|
371
|
+
if (settingsReloadTimer) clearTimeout(settingsReloadTimer);
|
|
372
|
+
settingsReloadTimer = setTimeout(() => {
|
|
373
|
+
settingsReloadTimer = undefined;
|
|
374
|
+
void loadPlanModeSettings(generation);
|
|
375
|
+
}, SETTINGS_RELOAD_DEBOUNCE_MS);
|
|
376
|
+
});
|
|
377
|
+
watcher.on("error", stopPlanModeSettingsWatch);
|
|
378
|
+
settingsWatch = watcher;
|
|
379
|
+
} catch {
|
|
380
|
+
// An unwatchable directory only costs the live reload; settings still
|
|
381
|
+
// load at session start.
|
|
382
|
+
stopPlanModeSettingsWatch();
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
|
|
298
386
|
pi.on("session_start", async (event, ctx) => {
|
|
299
387
|
const generation = ++menuGeneration;
|
|
300
388
|
refreshStateBeforeFirstAgentStart = event.reason === "new";
|
|
@@ -305,19 +393,15 @@ export default function planMode(pi: ExtensionAPI, dependencies: PlanModeDepende
|
|
|
305
393
|
settings = { thinkingLevel: "inherit" };
|
|
306
394
|
sessionPlanPath = resolveSessionPlanPath(ctx);
|
|
307
395
|
restoreState(ctx);
|
|
308
|
-
|
|
396
|
+
await loadPlanModeSettings(generation, ctx);
|
|
309
397
|
if (generation !== menuGeneration || menuController.signal.aborted) return;
|
|
310
|
-
|
|
311
|
-
else if (loadedSettings.kind === "invalid") {
|
|
312
|
-
ctx.ui.notify(`pi-plan-mode settings ignored: ${loadedSettings.reason}`, "warning");
|
|
313
|
-
}
|
|
314
|
-
if (loadedSettings.notice) ctx.ui.notify(loadedSettings.notice, "warning");
|
|
398
|
+
startPlanModeSettingsWatch(generation);
|
|
315
399
|
const persistFlagActivation = pi.getFlag("plan") === true && !state.enabled;
|
|
316
400
|
if (persistFlagActivation) {
|
|
317
401
|
state = { ...state, enabled: true, awaitingAction: state.planPath !== undefined };
|
|
318
402
|
}
|
|
319
|
-
|
|
320
|
-
if (persistFlagActivation) persistState();
|
|
403
|
+
const capturedThinkingLevel = state.enabled ? applyPlanThinkingLevel() : false;
|
|
404
|
+
if (persistFlagActivation || capturedThinkingLevel) persistState();
|
|
321
405
|
updateUi(ctx);
|
|
322
406
|
});
|
|
323
407
|
|
|
@@ -336,6 +420,7 @@ export default function planMode(pi: ExtensionAPI, dependencies: PlanModeDepende
|
|
|
336
420
|
|
|
337
421
|
pi.on("session_shutdown", async (_event, ctx) => {
|
|
338
422
|
menuGeneration += 1;
|
|
423
|
+
stopPlanModeSettingsWatch();
|
|
339
424
|
menuController.abort(new DOMException("Plan-mode session shut down", "AbortError"));
|
|
340
425
|
pendingReadyNonce = undefined;
|
|
341
426
|
latestCommandContext = undefined;
|
|
@@ -348,7 +433,7 @@ export default function planMode(pi: ExtensionAPI, dependencies: PlanModeDepende
|
|
|
348
433
|
});
|
|
349
434
|
|
|
350
435
|
/**
|
|
351
|
-
* The complete enforcement surface:
|
|
436
|
+
* The complete enforcement surface: two static built-in names. Plan mode
|
|
352
437
|
* does not classify, inspect, or filter any other tool.
|
|
353
438
|
*/
|
|
354
439
|
pi.on("tool_call", async (event) => {
|
|
@@ -356,10 +441,7 @@ export default function planMode(pi: ExtensionAPI, dependencies: PlanModeDepende
|
|
|
356
441
|
if (!BLOCKED_TOOLS.has(event.toolName)) return;
|
|
357
442
|
return {
|
|
358
443
|
block: true,
|
|
359
|
-
reason:
|
|
360
|
-
event.toolName === "update_plan"
|
|
361
|
-
? "Plan mode blocks update_plan because it tracks execution progress rather than conversational planning."
|
|
362
|
-
: `Plan mode blocks '${event.toolName}' because planning must not mutate files. Finish the plan with plan_mode_complete, then implement.`,
|
|
444
|
+
reason: `Plan mode blocks '${event.toolName}' because planning must not mutate files. Finish the plan with plan_mode_complete, then implement.`,
|
|
363
445
|
};
|
|
364
446
|
});
|
|
365
447
|
|
|
@@ -367,7 +449,7 @@ export default function planMode(pi: ExtensionAPI, dependencies: PlanModeDepende
|
|
|
367
449
|
if (refreshStateBeforeFirstAgentStart) {
|
|
368
450
|
refreshStateBeforeFirstAgentStart = false;
|
|
369
451
|
restoreState(ctx);
|
|
370
|
-
if (state.enabled
|
|
452
|
+
if (state.enabled && applyPlanThinkingLevel()) persistState();
|
|
371
453
|
updateUi(ctx);
|
|
372
454
|
}
|
|
373
455
|
if (state.enabled && state.awaitingAction) {
|
|
@@ -624,26 +706,42 @@ export default function planMode(pi: ExtensionAPI, dependencies: PlanModeDepende
|
|
|
624
706
|
};
|
|
625
707
|
}
|
|
626
708
|
|
|
627
|
-
|
|
709
|
+
/**
|
|
710
|
+
* Returns true when the captured thinking levels changed, so the restore
|
|
711
|
+
* paths can persist that capture. It is the only record of the user's level:
|
|
712
|
+
* `setPlanThinkingLevel` goes through Pi's `setThinkingLevel`, which writes
|
|
713
|
+
* the new level into the user's settings, so a capture left only in memory is
|
|
714
|
+
* lost if the session ends without `session_shutdown`.
|
|
715
|
+
*/
|
|
716
|
+
function applyPlanThinkingLevel(): boolean {
|
|
628
717
|
if (state.manualThinkingLevel) {
|
|
629
718
|
if (pi.getThinkingLevel() !== state.manualThinkingLevel) {
|
|
630
719
|
setPlanThinkingLevel(pi, state.manualThinkingLevel);
|
|
631
720
|
}
|
|
632
|
-
return;
|
|
721
|
+
return false;
|
|
633
722
|
}
|
|
634
723
|
const configured = configuredThinkingLevel(settings);
|
|
635
724
|
if (!configured) {
|
|
725
|
+
if (state.previousThinkingLevel === undefined && state.appliedThinkingLevel === undefined) {
|
|
726
|
+
return false;
|
|
727
|
+
}
|
|
636
728
|
state = {
|
|
637
729
|
...state,
|
|
638
730
|
previousThinkingLevel: undefined,
|
|
639
731
|
appliedThinkingLevel: undefined,
|
|
640
732
|
};
|
|
641
|
-
return;
|
|
733
|
+
return true;
|
|
642
734
|
}
|
|
643
735
|
const current = pi.getThinkingLevel();
|
|
736
|
+
const capturedPrevious = state.previousThinkingLevel;
|
|
737
|
+
const capturedApplied = state.appliedThinkingLevel;
|
|
644
738
|
if (!state.appliedThinkingLevel) state.previousThinkingLevel = current;
|
|
645
739
|
if (current !== configured) setPlanThinkingLevel(pi, configured);
|
|
646
740
|
state.appliedThinkingLevel = pi.getThinkingLevel();
|
|
741
|
+
return (
|
|
742
|
+
state.previousThinkingLevel !== capturedPrevious ||
|
|
743
|
+
state.appliedThinkingLevel !== capturedApplied
|
|
744
|
+
);
|
|
647
745
|
}
|
|
648
746
|
|
|
649
747
|
function captureManualThinkingLevel() {
|
package/src/presentation.ts
CHANGED
|
@@ -10,7 +10,7 @@ export function updatePlanModeUi(ctx: ExtensionContext, state: PlanModeState) {
|
|
|
10
10
|
if (state.enabled && state.awaitingAction) {
|
|
11
11
|
ctx.ui.setWidget(PLAN_WIDGET_KEY, [
|
|
12
12
|
"Proposed plan ready",
|
|
13
|
-
"Use /plan to implement, export,
|
|
13
|
+
"Use /plan to implement, export, or exit — or type feedback to revise.",
|
|
14
14
|
]);
|
|
15
15
|
} else if (state.enabled) {
|
|
16
16
|
ctx.ui.setWidget(PLAN_WIDGET_KEY, [
|
|
@@ -48,7 +48,13 @@ export async function showStoredPlan(
|
|
|
48
48
|
);
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
|
-
|
|
51
|
+
// enabled without awaitingAction but with a stored plan means revision
|
|
52
|
+
// feedback superseded the completed plan: show it, but never as current.
|
|
53
|
+
const title = state.enabled
|
|
54
|
+
? state.awaitingAction
|
|
55
|
+
? "Proposed Plan"
|
|
56
|
+
: "Superseded Proposed Plan (revision in progress — awaiting a new plan_mode_complete)"
|
|
57
|
+
: "Active Implementation Plan";
|
|
52
58
|
showPlanModePlan(pi, ctx, title, plan);
|
|
53
59
|
}
|
|
54
60
|
|
|
@@ -76,6 +82,9 @@ export function showPlanModePlan(
|
|
|
76
82
|
export function planModeStatusText(state: PlanModeState) {
|
|
77
83
|
if (state.enabled) {
|
|
78
84
|
if (state.awaitingAction) return "Plan mode is active and a proposed plan is ready.";
|
|
85
|
+
if (state.planPath) {
|
|
86
|
+
return "Plan mode is active; revision in progress. The stored plan is superseded until the next plan_mode_complete.";
|
|
87
|
+
}
|
|
79
88
|
return "Plan mode is active. Explore, ask, and finish with plan_mode_complete when decision-ready.";
|
|
80
89
|
}
|
|
81
90
|
if (state.planPath) return "An implementation plan is active.";
|
package/src/prompt.ts
CHANGED
|
@@ -53,7 +53,7 @@ You are in Plan Mode, a collaboration mode for producing a decision-complete imp
|
|
|
53
53
|
|
|
54
54
|
- Stay in Plan Mode until a developer or extension explicitly exits it.
|
|
55
55
|
- Treat requests to implement as requests to plan the implementation; do not edit files or carry out the plan.
|
|
56
|
-
- Do not use
|
|
56
|
+
- Do not use todo/checklist tooling to track execution progress in Plan Mode; Plan Mode is conversational planning, and the plan itself belongs in plan_mode_complete.
|
|
57
57
|
- Do not perform mutating actions: no edit/write tools, no patching, no formatting that rewrites files, no dependency installation, no commits, no migrations.
|
|
58
58
|
- Gather information freely: read files, search, inspect configuration, and run read-only commands.
|
|
59
59
|
|