@narumitw/pi-plan-mode 0.1.34 → 0.1.35
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 +3 -3
- package/package.json +1 -1
- package/src/plan-mode.ts +30 -25
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Pi core intentionally does not ship a built-in plan mode; this package provides
|
|
|
14
14
|
- Disables extension and custom tools by default, with a `/plan tools` selector for explicit user-risk opt-in.
|
|
15
15
|
- Blocks mutating built-in tools and bash commands such as `rm`, `git commit`, dependency installs, redirects, and editor launches.
|
|
16
16
|
- Injects Codex-like Plan mode instructions: explore first, ask only non-discoverable questions, do not mutate files, and finish with `<proposed_plan>`.
|
|
17
|
-
- Detects proposed plan blocks and prompts you to implement,
|
|
17
|
+
- Detects proposed plan blocks and prompts you to implement, stay in Plan mode, or exit and discard the plan.
|
|
18
18
|
- Shows Plan mode state in Pi's statusline as `📝 plan active` or `📝 plan ready`.
|
|
19
19
|
- Persists Plan mode state in the Pi session so resume restores the mode.
|
|
20
20
|
|
|
@@ -72,12 +72,12 @@ A complete Plan mode answer should include exactly one block like this:
|
|
|
72
72
|
</proposed_plan>
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
After a proposed plan is detected, `/plan` lets you choose whether to implement the plan,
|
|
75
|
+
After a proposed plan is detected, `/plan` lets you choose whether to implement the plan, 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. Choosing Stay keeps the plan ready while you decide what to do next; to revise the plan, choose Stay and type your revision feedback in the normal prompt. When that next Plan-mode turn starts, the previous plan is no longer treated as the latest implementable plan unless the agent produces an updated `<proposed_plan>`. Choosing exit/off disables Plan mode and discards the proposed plan so it is not carried into later non-plan turns.
|
|
76
76
|
|
|
77
77
|
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:
|
|
78
78
|
|
|
79
79
|
- `📝 plan active`: Plan mode is enabled and still gathering context or drafting a plan.
|
|
80
|
-
- `📝 plan ready`: A `<proposed_plan>` was detected and
|
|
80
|
+
- `📝 plan ready`: A `<proposed_plan>` was detected and remains ready until you implement it, continue planning, or exit Plan mode.
|
|
81
81
|
|
|
82
82
|
You can also exit directly. Direct exit discards the latest proposed plan instead of treating it as an implementation request:
|
|
83
83
|
|
package/package.json
CHANGED
package/src/plan-mode.ts
CHANGED
|
@@ -159,8 +159,13 @@ export default function planMode(pi: ExtensionAPI) {
|
|
|
159
159
|
};
|
|
160
160
|
});
|
|
161
161
|
|
|
162
|
-
pi.on("before_agent_start", () => {
|
|
162
|
+
pi.on("before_agent_start", (_event, ctx) => {
|
|
163
163
|
if (!state.enabled) return;
|
|
164
|
+
if (state.latestPlan || state.awaitingAction) {
|
|
165
|
+
state = { ...state, latestPlan: undefined, awaitingAction: false };
|
|
166
|
+
persistState();
|
|
167
|
+
updateUi(ctx);
|
|
168
|
+
}
|
|
164
169
|
applyPlanModeTools();
|
|
165
170
|
return {
|
|
166
171
|
message: {
|
|
@@ -186,17 +191,20 @@ export default function planMode(pi: ExtensionAPI) {
|
|
|
186
191
|
persistState();
|
|
187
192
|
updateUi(ctx);
|
|
188
193
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
194
|
+
scheduleAfterCurrentAgentRun(async () => {
|
|
195
|
+
if (!state.enabled || state.latestPlan !== proposedPlan) return;
|
|
196
|
+
if (ctx.hasUI) await showPlanReadyMenu(ctx);
|
|
197
|
+
if (!state.enabled || state.latestPlan !== proposedPlan) return;
|
|
198
|
+
|
|
199
|
+
pi.sendMessage(
|
|
200
|
+
{
|
|
201
|
+
customType: PROPOSED_PLAN_MESSAGE_TYPE,
|
|
202
|
+
content: `**Proposed Plan**\n\n${proposedPlan}`,
|
|
203
|
+
display: true,
|
|
204
|
+
},
|
|
205
|
+
{ triggerTurn: false },
|
|
206
|
+
);
|
|
207
|
+
});
|
|
200
208
|
});
|
|
201
209
|
|
|
202
210
|
function enterPlanMode(ctx: ExtensionContext) {
|
|
@@ -229,6 +237,15 @@ export default function planMode(pi: ExtensionAPI) {
|
|
|
229
237
|
else pi.sendUserMessage(message, { deliverAs: "followUp" });
|
|
230
238
|
}
|
|
231
239
|
|
|
240
|
+
function scheduleAfterCurrentAgentRun(task: () => Promise<void> | void) {
|
|
241
|
+
setTimeout(() => {
|
|
242
|
+
void Promise.resolve(task()).catch((error: unknown) => {
|
|
243
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
244
|
+
console.error(`Plan mode follow-up failed: ${message}`);
|
|
245
|
+
});
|
|
246
|
+
}, 0);
|
|
247
|
+
}
|
|
248
|
+
|
|
232
249
|
function startImplementation(ctx: ExtensionContext) {
|
|
233
250
|
const plan = state.latestPlan?.trim();
|
|
234
251
|
exitPlanMode(ctx);
|
|
@@ -283,8 +300,6 @@ export default function planMode(pi: ExtensionAPI) {
|
|
|
283
300
|
async function showPlanReadyMenu(ctx: ExtensionContext) {
|
|
284
301
|
const choice = await ctx.ui.select("Proposed plan ready. What next?", [
|
|
285
302
|
"Implement this plan",
|
|
286
|
-
"Revise plan",
|
|
287
|
-
"Configure Plan-mode tools",
|
|
288
303
|
"Stay in Plan mode",
|
|
289
304
|
"Exit Plan mode",
|
|
290
305
|
]);
|
|
@@ -292,15 +307,6 @@ export default function planMode(pi: ExtensionAPI) {
|
|
|
292
307
|
startImplementation(ctx);
|
|
293
308
|
return;
|
|
294
309
|
}
|
|
295
|
-
if (choice === "Configure Plan-mode tools") {
|
|
296
|
-
await showToolSelector(ctx);
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
if (choice === "Revise plan") {
|
|
300
|
-
const refinement = await ctx.ui.editor("Revise the plan", "");
|
|
301
|
-
if (refinement?.trim()) sendPlanModeUserMessage(refinement.trim(), ctx);
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
310
|
if (choice === "Exit Plan mode") {
|
|
305
311
|
exitPlanMode(ctx);
|
|
306
312
|
ctx.ui.notify("Plan mode disabled. Proposed plan discarded.", "info");
|
|
@@ -635,8 +641,7 @@ function messageContainsInactivePlanModeArtifact(message: unknown) {
|
|
|
635
641
|
const candidate = unwrapSessionMessage(message);
|
|
636
642
|
return (
|
|
637
643
|
candidate.customType === PLAN_CONTEXT_MESSAGE_TYPE ||
|
|
638
|
-
candidate.customType === PROPOSED_PLAN_MESSAGE_TYPE
|
|
639
|
-
contentText(candidate.content).includes(PLAN_CONTEXT_MARKER)
|
|
644
|
+
candidate.customType === PROPOSED_PLAN_MESSAGE_TYPE
|
|
640
645
|
);
|
|
641
646
|
}
|
|
642
647
|
|