@narumitw/pi-plan-mode 0.1.17 → 0.1.19
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/README.md +12 -2
- package/package.json +1 -1
- package/src/plan-mode.ts +48 -5
package/README.md
CHANGED
|
@@ -14,6 +14,7 @@ Pi core intentionally does not ship a built-in plan mode; this package provides
|
|
|
14
14
|
- Blocks mutating bash commands such as `rm`, `git commit`, dependency installs, redirects, and editor launches.
|
|
15
15
|
- Injects Codex-like Plan mode instructions: explore first, ask only non-discoverable questions, do not mutate files, and finish with `<proposed_plan>`.
|
|
16
16
|
- Detects proposed plan blocks and prompts you to implement, revise, or stay in Plan mode.
|
|
17
|
+
- Shows Plan mode state in Pi's statusline as `📝 plan active` or `📝 plan ready`.
|
|
17
18
|
- Persists Plan mode state in the Pi session so resume restores the mode.
|
|
18
19
|
|
|
19
20
|
## 📦 Install
|
|
@@ -38,8 +39,11 @@ pi -e ./extensions/pi-plan-mode
|
|
|
38
39
|
|
|
39
40
|
```text
|
|
40
41
|
/plan
|
|
42
|
+
/plan <prompt>
|
|
41
43
|
```
|
|
42
44
|
|
|
45
|
+
Use `/plan` to enter Plan mode before writing your planning prompt. Use `/plan <prompt>` to enter Plan mode and immediately submit `<prompt>` as the first Plan-mode user message.
|
|
46
|
+
|
|
43
47
|
When Plan mode is active, ask the agent to design the change. The agent may inspect files and run read-only commands, but it should not edit files or execute the implementation.
|
|
44
48
|
|
|
45
49
|
A complete Plan mode answer should include exactly one block like this:
|
|
@@ -62,7 +66,12 @@ A complete Plan mode answer should include exactly one block like this:
|
|
|
62
66
|
</proposed_plan>
|
|
63
67
|
```
|
|
64
68
|
|
|
65
|
-
After a proposed plan is detected, `/plan` lets you choose whether to implement the plan, revise it, stay in Plan mode, or exit Plan mode.
|
|
69
|
+
After a proposed plan is detected, `/plan` lets you choose whether to implement the plan, revise it, stay in Plan mode, or exit Plan mode. Choosing implementation disables Plan mode, restores full tool access, and immediately starts an implementation turn with the proposed plan.
|
|
70
|
+
|
|
71
|
+
While Plan mode is enabled, the extension also publishes a compact status for Pi statuslines. With `@narumitw/pi-statusline`, this appears in the extension status area:
|
|
72
|
+
|
|
73
|
+
- `📝 plan active`: Plan mode is enabled and still gathering context or drafting a plan.
|
|
74
|
+
- `📝 plan ready`: A `<proposed_plan>` was detected and is waiting for your next `/plan` action.
|
|
66
75
|
|
|
67
76
|
You can also exit directly:
|
|
68
77
|
|
|
@@ -75,8 +84,9 @@ You can also exit directly:
|
|
|
75
84
|
This extension maps Codex's `ModeKind::Plan` behavior onto Pi's extension API:
|
|
76
85
|
|
|
77
86
|
- Plan mode is a conversational collaboration mode, not TODO/progress tracking.
|
|
87
|
+
- `/plan <prompt>` follows Codex behavior by switching to Plan mode before submitting the inline prompt.
|
|
78
88
|
- `update_plan`-style checklist use is discouraged while Plan mode is active.
|
|
79
|
-
- The implementation boundary is explicit: Plan mode restores tools
|
|
89
|
+
- The implementation boundary is explicit: Plan mode restores tools before starting implementation, and choosing implementation immediately triggers a normal agent turn with full tool access.
|
|
80
90
|
- Pi extension safety is approximated with active-tool restriction plus bash filtering, so it may be stricter or looser than Codex core in edge cases.
|
|
81
91
|
|
|
82
92
|
## 🗂️ Package layout
|
package/package.json
CHANGED
package/src/plan-mode.ts
CHANGED
|
@@ -88,12 +88,17 @@ export default function planMode(pi: ExtensionAPI) {
|
|
|
88
88
|
pi.registerCommand("plan", {
|
|
89
89
|
description: "Enter or manage Codex-like Plan mode",
|
|
90
90
|
handler: async (args, ctx) => {
|
|
91
|
-
const
|
|
91
|
+
const prompt = args.trim();
|
|
92
|
+
const command = prompt.toLowerCase();
|
|
92
93
|
if (command === "exit" || command === "off") {
|
|
93
94
|
exitPlanMode(ctx);
|
|
94
95
|
ctx.ui.notify("Plan mode disabled. Full tool access restored.", "info");
|
|
95
96
|
return;
|
|
96
97
|
}
|
|
98
|
+
if (prompt) {
|
|
99
|
+
enterPlanModeWithPrompt(prompt, ctx);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
97
102
|
if (!state.enabled) {
|
|
98
103
|
enterPlanMode(ctx);
|
|
99
104
|
ctx.ui.notify("Plan mode enabled. I will explore and plan, but not modify files.", "info");
|
|
@@ -186,6 +191,16 @@ export default function planMode(pi: ExtensionAPI) {
|
|
|
186
191
|
updateUi(ctx);
|
|
187
192
|
}
|
|
188
193
|
|
|
194
|
+
function enterPlanModeWithPrompt(prompt: string, ctx: ExtensionContext) {
|
|
195
|
+
const wasEnabled = state.enabled;
|
|
196
|
+
enterPlanMode(ctx);
|
|
197
|
+
if (!wasEnabled) {
|
|
198
|
+
ctx.ui.notify("Plan mode enabled. I will explore and plan, but not modify files.", "info");
|
|
199
|
+
}
|
|
200
|
+
if (ctx.isIdle()) pi.sendUserMessage(prompt);
|
|
201
|
+
else pi.sendUserMessage(prompt, { deliverAs: "followUp" });
|
|
202
|
+
}
|
|
203
|
+
|
|
189
204
|
function exitPlanMode(ctx: ExtensionContext) {
|
|
190
205
|
state = { ...state, enabled: false, awaitingAction: false };
|
|
191
206
|
restoreTools();
|
|
@@ -193,6 +208,25 @@ export default function planMode(pi: ExtensionAPI) {
|
|
|
193
208
|
updateUi(ctx);
|
|
194
209
|
}
|
|
195
210
|
|
|
211
|
+
function startImplementation(ctx: ExtensionContext) {
|
|
212
|
+
const plan = state.latestPlan?.trim();
|
|
213
|
+
exitPlanMode(ctx);
|
|
214
|
+
|
|
215
|
+
if (!plan) {
|
|
216
|
+
ctx.ui.notify("Plan mode disabled. No proposed plan is available to implement.", "warning");
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
pi.sendMessage(
|
|
221
|
+
{
|
|
222
|
+
customType: "plan-mode-implementation",
|
|
223
|
+
content: `Plan mode is now disabled. Full tool access is restored. Implement this proposed plan now:\n\n${plan}`,
|
|
224
|
+
display: true,
|
|
225
|
+
},
|
|
226
|
+
{ triggerTurn: true },
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
196
230
|
async function showPlanMenu(ctx: ExtensionContext) {
|
|
197
231
|
if (!ctx.hasUI) {
|
|
198
232
|
ctx.ui.notify(planStatusText(), "info");
|
|
@@ -207,7 +241,11 @@ export default function planMode(pi: ExtensionAPI) {
|
|
|
207
241
|
ctx.ui.notify(state.latestPlan ?? "No proposed plan yet.", "info");
|
|
208
242
|
return;
|
|
209
243
|
}
|
|
210
|
-
if (choice === "Implement this plan"
|
|
244
|
+
if (choice === "Implement this plan") {
|
|
245
|
+
startImplementation(ctx);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (choice === "Exit Plan mode") {
|
|
211
249
|
exitPlanMode(ctx);
|
|
212
250
|
ctx.ui.notify("Plan mode disabled. Full tool access restored.", "info");
|
|
213
251
|
return;
|
|
@@ -222,8 +260,7 @@ export default function planMode(pi: ExtensionAPI) {
|
|
|
222
260
|
"Stay in Plan mode",
|
|
223
261
|
]);
|
|
224
262
|
if (choice === "Implement this plan") {
|
|
225
|
-
|
|
226
|
-
ctx.ui.notify("Plan mode disabled. Ask me to implement the proposed plan when ready.", "info");
|
|
263
|
+
startImplementation(ctx);
|
|
227
264
|
return;
|
|
228
265
|
}
|
|
229
266
|
if (choice === "Revise plan") {
|
|
@@ -268,7 +305,7 @@ export default function planMode(pi: ExtensionAPI) {
|
|
|
268
305
|
}
|
|
269
306
|
|
|
270
307
|
function updateUi(ctx: ExtensionContext) {
|
|
271
|
-
ctx.ui.setStatus(STATUS_KEY,
|
|
308
|
+
ctx.ui.setStatus(STATUS_KEY, formatStatus());
|
|
272
309
|
if (state.enabled && state.latestPlan) {
|
|
273
310
|
ctx.ui.setWidget(PLAN_WIDGET_KEY, [
|
|
274
311
|
"Proposed plan ready",
|
|
@@ -281,6 +318,12 @@ export default function planMode(pi: ExtensionAPI) {
|
|
|
281
318
|
}
|
|
282
319
|
}
|
|
283
320
|
|
|
321
|
+
function formatStatus() {
|
|
322
|
+
if (!state.enabled) return undefined;
|
|
323
|
+
if (state.awaitingAction || state.latestPlan) return "📝 plan ready";
|
|
324
|
+
return "📝 plan active";
|
|
325
|
+
}
|
|
326
|
+
|
|
284
327
|
function clearUi(ctx: ExtensionContext) {
|
|
285
328
|
ctx.ui.setStatus(STATUS_KEY, undefined);
|
|
286
329
|
ctx.ui.setWidget(PLAN_WIDGET_KEY, undefined);
|