@janvitos/pi-plan-build 0.1.37 → 0.1.39
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 +2 -2
- package/index.ts +22 -6
- package/package.json +1 -1
- package/plan-execution.ts +4 -3
- package/plan-panel.ts +5 -2
- package/utils.ts +1 -0
package/README.md
CHANGED
|
@@ -91,7 +91,7 @@ Both actions leave Plan mode active, stop the agent, and wait for the next user
|
|
|
91
91
|
|
|
92
92
|
When Pi uses `"tuiMode": "fullscreen"` and the saved plan contains top-level `- [ ]` items under `## Implementation Steps`, `plan_exit` offers the additional **Implement step by step** approval action. This is opt-in per plan; it does not replace either one-shot implementation option or **Stay in Plan mode**.
|
|
93
93
|
|
|
94
|
-
The passive 64-column right panel reserves terminal columns, so the transcript and editor reflow instead of being covered. Long step instructions
|
|
94
|
+
The passive 64-column right panel reserves terminal columns, so the transcript and editor reflow instead of being covered. Long step instructions and panel guidance wrap instead of being clipped. It never accepts focus or keyboard input and collapses below 132 terminal columns. The panel is a visual status aid only; all control happens through ordinary prompts. Its guidance remains visible, and the main transcript says, “Awaiting your instructions.” The agent interprets intent contextually, so these are examples rather than required commands:
|
|
95
95
|
|
|
96
96
|
- “Implement the next step,” “Start step 2,” or simply “Approved” / “Go ahead.”
|
|
97
97
|
- “Step 1 is complete,” “I verified that one,” or “I already handled this.”
|
|
@@ -99,7 +99,7 @@ The passive 64-column right panel reserves terminal columns, so the transcript a
|
|
|
99
99
|
- “Pause the plan,” “hide the plan,” or “show the plan.”
|
|
100
100
|
- “Cancel this plan” at any point to end step-by-step execution immediately.
|
|
101
101
|
|
|
102
|
-
The extension exposes these actions to the agent through `plan_step_control`; project mutations remain blocked until the user clearly approves a ready step or explicitly indicates that it is already complete. A ready step may be marked complete without implementation when the user says they already handled or verified it. An implemented step is marked completed immediately after verification, and the next step becomes ready without a review or acceptance phase. The agent interprets intent contextually rather than requiring exact phrases, while the extension validates every resulting state transition. Step tool results identify the affected step, for example `Step 2: complete`, rather than presenting a plan-wide completion label. When the final step is completed or skipped, the panel and execution guards are removed immediately and the main window receives a completion
|
|
102
|
+
The extension exposes these actions to the agent through `plan_step_control`; project mutations remain blocked until the user clearly approves a ready step or explicitly indicates that it is already complete. A ready step may be marked complete without implementation when the user says they already handled or verified it. An implemented step is marked completed immediately after verification, and the next step becomes ready without a review or acceptance phase. The agent interprets intent contextually rather than requiring exact phrases, while the extension validates every resulting state transition. Step tool results identify the affected step, for example `Step 2: complete`, rather than presenting a plan-wide completion label. When the final step is completed or skipped, the panel and execution guards are removed immediately and the main window receives a Markdown-formatted completion summary with plan-style headings, spacing, and colors. Cancelling removes the panel and execution guards immediately, restores the full-width layout, and preserves the saved plan file for reference. The agent implements only that step, calls `plan_step_complete`, and waits for the user's next prompt. Progress, revisions, summaries, and panel visibility survive reload/resume. If such a session is opened in regular mode, progress is retained but cannot advance until fullscreen mode is restored; no overlay fallback is used.
|
|
103
103
|
|
|
104
104
|
Enable fullscreen in `~/.pi/agent/settings.json` and restart Pi:
|
|
105
105
|
|
package/index.ts
CHANGED
|
@@ -53,6 +53,7 @@ import {
|
|
|
53
53
|
PLAN_EXIT_FRESH_CHOICE,
|
|
54
54
|
PLAN_EXIT_STAY_ACKNOWLEDGEMENT,
|
|
55
55
|
PLAN_EXIT_STAY_CHOICE,
|
|
56
|
+
PLAN_STEP_READY_ACKNOWLEDGEMENT,
|
|
56
57
|
renderModeComposer,
|
|
57
58
|
type Mode,
|
|
58
59
|
unique,
|
|
@@ -64,6 +65,7 @@ const PLAN_REVIEW_ENTRY_TYPE = "pi-plan-build-review";
|
|
|
64
65
|
const LEGACY_PLAN_REVIEW_ENTRY_TYPE = "opencode-plan-review";
|
|
65
66
|
const MODE_NOTICE_ENTRY_TYPE = "pi-plan-build-notice";
|
|
66
67
|
const LEGACY_MODE_NOTICE_ENTRY_TYPE = "opencode-mode-notice";
|
|
68
|
+
const PLAN_STEP_GUIDANCE_ENTRY_TYPE = "pi-plan-build-step-guidance";
|
|
67
69
|
const STATUS_KEY = "pi-plan-build-mode";
|
|
68
70
|
const PLAN_STEP_CHOICE = "Implement step by step";
|
|
69
71
|
const PANEL_WIDTH = 64;
|
|
@@ -119,10 +121,13 @@ export default function planBuildModes(pi: ExtensionAPI): void {
|
|
|
119
121
|
const message = typeof entry.data?.message === "string" ? entry.data.message : "Plan mode unchanged.";
|
|
120
122
|
return new Text(theme.fg("warning", message), 0, 0);
|
|
121
123
|
};
|
|
124
|
+
const renderPlanStepGuidance: EntryRenderer = (_entry, _options, theme) =>
|
|
125
|
+
new Text(theme.fg("success", PLAN_STEP_READY_ACKNOWLEDGEMENT), 0, 0);
|
|
122
126
|
pi.registerEntryRenderer<{ plan: string }>(PLAN_REVIEW_ENTRY_TYPE, renderPlanReview);
|
|
123
127
|
pi.registerEntryRenderer<{ plan: string }>(LEGACY_PLAN_REVIEW_ENTRY_TYPE, renderPlanReview);
|
|
124
128
|
pi.registerEntryRenderer<{ message: string }>(MODE_NOTICE_ENTRY_TYPE, renderModeNotice);
|
|
125
129
|
pi.registerEntryRenderer<{ message: string }>(LEGACY_MODE_NOTICE_ENTRY_TYPE, renderModeNotice);
|
|
130
|
+
pi.registerEntryRenderer(PLAN_STEP_GUIDANCE_ENTRY_TYPE, renderPlanStepGuidance);
|
|
126
131
|
|
|
127
132
|
function stateData(): StoredState {
|
|
128
133
|
return { version: 1, selectedMode, pendingReminder, toolsBeforeModes, ...(execution ? { execution } : {}) };
|
|
@@ -412,9 +417,9 @@ export default function planBuildModes(pi: ExtensionAPI): void {
|
|
|
412
417
|
const target = params.step === undefined
|
|
413
418
|
? execution.steps.find((step) => step.status === "ready")
|
|
414
419
|
: execution.steps[Math.floor(params.step) - 1];
|
|
415
|
-
const finish = (message: string) => ({
|
|
420
|
+
const finish = (message: string, extraDetails?: { planCompleted?: boolean }) => ({
|
|
416
421
|
content: [{ type: "text" as const, text: message }],
|
|
417
|
-
details: { action: params.action, stepId: target?.id },
|
|
422
|
+
details: { action: params.action, stepId: target?.id, ...extraDetails },
|
|
418
423
|
terminate: true,
|
|
419
424
|
});
|
|
420
425
|
|
|
@@ -443,11 +448,17 @@ export default function planBuildModes(pi: ExtensionAPI): void {
|
|
|
443
448
|
}
|
|
444
449
|
if (params.action === "complete") {
|
|
445
450
|
const completion = applyExecutionTransition(completePlanStep(execution, target.id));
|
|
446
|
-
return finish(
|
|
451
|
+
return finish(
|
|
452
|
+
completion ?? "The step was marked complete. The next step is ready and awaits user instruction.",
|
|
453
|
+
{ planCompleted: completion !== undefined },
|
|
454
|
+
);
|
|
447
455
|
}
|
|
448
456
|
if (params.action === "skip") {
|
|
449
457
|
const completion = applyExecutionTransition(skipPlanStep(execution, target.id));
|
|
450
|
-
return finish(
|
|
458
|
+
return finish(
|
|
459
|
+
completion ?? "The step was skipped. The next step awaits user instruction.",
|
|
460
|
+
{ planCompleted: completion !== undefined },
|
|
461
|
+
);
|
|
451
462
|
}
|
|
452
463
|
if (!params.instruction?.trim()) throw new Error("Revising a step requires a replacement instruction");
|
|
453
464
|
const plan = await fs.promises.readFile(planPath, "utf8");
|
|
@@ -466,6 +477,8 @@ export default function planBuildModes(pi: ExtensionAPI): void {
|
|
|
466
477
|
},
|
|
467
478
|
renderResult(result, _options, theme, context) {
|
|
468
479
|
const text = result.content.find((item) => item.type === "text")?.text ?? "Plan state updated";
|
|
480
|
+
const details = result.details as { planCompleted?: boolean } | undefined;
|
|
481
|
+
if (details?.planCompleted && !context.isError) return new Markdown(text, 0, 0, getMarkdownTheme());
|
|
469
482
|
return new Text(theme.fg(context.isError ? "error" : "success", text), 0, 0);
|
|
470
483
|
},
|
|
471
484
|
});
|
|
@@ -484,7 +497,7 @@ export default function planBuildModes(pi: ExtensionAPI): void {
|
|
|
484
497
|
const completion = applyExecutionTransition(completePlanStep(execution, step.id, params.summary));
|
|
485
498
|
return {
|
|
486
499
|
content: [{ type: "text", text: completion ?? "The step was completed. The next step is ready and awaits user instruction." }],
|
|
487
|
-
details: { stepId: step.id, completed: true },
|
|
500
|
+
details: { stepId: step.id, completed: true, planCompleted: completion !== undefined },
|
|
488
501
|
terminate: true,
|
|
489
502
|
};
|
|
490
503
|
},
|
|
@@ -493,6 +506,8 @@ export default function planBuildModes(pi: ExtensionAPI): void {
|
|
|
493
506
|
},
|
|
494
507
|
renderResult(result, _options, theme, context) {
|
|
495
508
|
const text = result.content.find((item) => item.type === "text")?.text ?? "Plan step completed";
|
|
509
|
+
const details = result.details as { planCompleted?: boolean } | undefined;
|
|
510
|
+
if (details?.planCompleted && !context.isError) return new Markdown(text, 0, 0, getMarkdownTheme());
|
|
496
511
|
return new Text(theme.fg(context.isError ? "error" : "success", text), 0, 0);
|
|
497
512
|
},
|
|
498
513
|
});
|
|
@@ -541,6 +556,7 @@ export default function planBuildModes(pi: ExtensionAPI): void {
|
|
|
541
556
|
await selectMode("build", ctx, "tool");
|
|
542
557
|
updateExecution(stepExecution);
|
|
543
558
|
ensurePanelLayout();
|
|
559
|
+
pi.appendEntry(PLAN_STEP_GUIDANCE_ENTRY_TYPE);
|
|
544
560
|
return {
|
|
545
561
|
content: [{ type: "text", text: "Step-by-step execution is ready. Stop now and wait for the user's natural-language instruction in the composer; the plan panel is visual-only." }],
|
|
546
562
|
details: { approved: true, action: "step-by-step", mode: "build", planPath },
|
|
@@ -588,7 +604,7 @@ export default function planBuildModes(pi: ExtensionAPI): void {
|
|
|
588
604
|
renderResult(result, _options, theme, context) {
|
|
589
605
|
const details = result.details as { approved?: boolean; action?: string } | undefined;
|
|
590
606
|
if (details?.action === "step-by-step" && !context.isError) {
|
|
591
|
-
return new Text(theme.fg("success", "Step-by-step execution ready
|
|
607
|
+
return new Text(theme.fg("success", "Step-by-step execution ready"), 0, 0);
|
|
592
608
|
}
|
|
593
609
|
if (details?.action === "implement-fresh" && !context.isError) {
|
|
594
610
|
return new Text(
|
package/package.json
CHANGED
package/plan-execution.ts
CHANGED
|
@@ -114,11 +114,12 @@ function makeNextReady(state: PlanExecutionState, afterId: string): void {
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
export function formatPlanCompletionSummary(state: PlanExecutionState): string {
|
|
117
|
-
const lines = ["Plan complete
|
|
117
|
+
const lines = ["# Plan complete", "", "## Summary", ""];
|
|
118
118
|
for (const [index, step] of state.steps.entries()) {
|
|
119
119
|
const outcome = step.status === "skipped" ? "Skipped" : "Completed";
|
|
120
|
-
lines.push(`${index + 1}.
|
|
121
|
-
if (step.summary?.trim()) lines.push(` ${step.summary.trim()}`);
|
|
120
|
+
lines.push(`${index + 1}. **${outcome}:** ${step.text}`);
|
|
121
|
+
if (step.summary?.trim()) lines.push("", ` ${step.summary.trim()}`);
|
|
122
|
+
if (index < state.steps.length - 1) lines.push("");
|
|
122
123
|
}
|
|
123
124
|
return lines.join("\n");
|
|
124
125
|
}
|
package/plan-panel.ts
CHANGED
|
@@ -34,6 +34,9 @@ export class PlanPanel implements Component {
|
|
|
34
34
|
const clipped = truncateToWidth(content, contentWidth, "");
|
|
35
35
|
return `${border("│")} ${clipped}${" ".repeat(Math.max(0, contentWidth - visibleWidth(clipped)))} ${border("│")}`;
|
|
36
36
|
};
|
|
37
|
+
const pushWrapped = (lines: string[], content: string) => {
|
|
38
|
+
for (const line of wrapTextWithAnsi(content, contentWidth)) lines.push(pad(line));
|
|
39
|
+
};
|
|
37
40
|
const done = this.state.steps.filter((step) => step.status === "completed" || step.status === "skipped").length;
|
|
38
41
|
const status = this.state.status === "completed" ? "complete" : this.state.status;
|
|
39
42
|
const currentIndex = this.state.steps.findIndex((step) => step.id === this.state.selectedStepId);
|
|
@@ -65,8 +68,8 @@ export class PlanPanel implements Component {
|
|
|
65
68
|
}
|
|
66
69
|
lines.push(border(`├${"─".repeat(inner)}┤`));
|
|
67
70
|
if (this.state.status === "completed") lines.push(pad(this.theme.fg("success", "Plan complete")));
|
|
68
|
-
else if (this.state.status === "paused") lines
|
|
69
|
-
else if (current?.status === "ready") lines
|
|
71
|
+
else if (this.state.status === "paused") pushWrapped(lines, "Tell the agent to resume, cancel, or hide the plan.");
|
|
72
|
+
else if (current?.status === "ready") pushWrapped(lines, "Tell the agent to implement, complete, edit, or skip steps. You can also cancel or hide the plan.");
|
|
70
73
|
lines.push(border(`╰${"─".repeat(inner)}╯`));
|
|
71
74
|
return lines.map((line) => truncateToWidth(line, safeWidth, ""));
|
|
72
75
|
}
|
package/utils.ts
CHANGED
|
@@ -136,6 +136,7 @@ export const PLAN_EXIT_FRESH_CHOICE = "Start fresh and implement";
|
|
|
136
136
|
export const PLAN_EXIT_STAY_CHOICE = "Stay in Plan mode";
|
|
137
137
|
export const PLAN_EXIT_STAY_ACKNOWLEDGEMENT =
|
|
138
138
|
"Staying in Plan mode. Let me know when you’re ready to revise or implement the plan.";
|
|
139
|
+
export const PLAN_STEP_READY_ACKNOWLEDGEMENT = "Awaiting your instructions.";
|
|
139
140
|
|
|
140
141
|
export type PlanExitDecision = "implement-here" | "implement-fresh" | "stay";
|
|
141
142
|
|