@janvitos/pi-plan-build 0.1.33 → 0.1.34

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 CHANGED
@@ -19,7 +19,7 @@ A global [Pi coding agent](https://github.com/badlogic/pi-mono) extension that a
19
19
  - **Switch to Build and implement here**
20
20
  - **Start fresh and implement**
21
21
  - **Stay in Plan mode**
22
- - **Experimental:** In fullscreen TUI, valid checklist plans also offer **Implement step by step**: a passive, non-overlapping docked right panel keeps the plan visible while natural-language prompts gate steps and return completed work for review. The panel is visual-only and never captures keyboard input. This feature is still under active development.
22
+ - **Experimental:** In fullscreen TUI, valid checklist plans also offer **Implement step by step**: a passive, non-overlapping docked right panel keeps the plan visible while natural-language prompts gate steps and report completed work. The panel is visual-only and never captures keyboard input. This feature is still under active development.
23
23
  - Staying in Plan mode—or pressing Escape in the approval dialog—produces a durable acknowledgement and stops the run until the user responds.
24
24
  - Mode state survives reloads, resumes, and forks.
25
25
  - When Pi recreates the custom editor, the latest 100 user prompts from the active session branch are restored for Up/Down history navigation.
@@ -96,11 +96,10 @@ The passive 64-column right panel reserves terminal columns, so the transcript a
96
96
  - “Implement the next step” or “Start step 2.”
97
97
  - “Step 1 is complete,” “I verified that one,” or “I already handled this.”
98
98
  - “Change step 3 to …” or “Skip this step.”
99
- - “Accept this result” or “Correct it by …”
100
99
  - “Pause the plan,” “hide the plan,” or “show the plan.”
101
100
  - “Cancel this plan” at any point to end step-by-step execution immediately.
102
101
 
103
- 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. A reviewed implementation may be accepted, while ambiguous feedback prompts clarification. 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. 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. Accepting a result makes the next step ready but never starts it automatically. 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.
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 confirmation with a concise plan summary. 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.
104
103
 
105
104
  Enable fullscreen in `~/.pi/agent/settings.json` and restart Pi:
106
105
 
package/index.ts CHANGED
@@ -15,17 +15,15 @@ import {
15
15
  PLAN_TO_BUILD_REMINDER,
16
16
  } from "./prompts.ts";
17
17
  import {
18
- acceptPlanStep,
19
18
  activePlanStep,
20
19
  completePlanStep,
21
20
  createPlanExecution,
22
21
  decodePlanExecution,
22
+ formatPlanCompletionSummary,
23
23
  pausePlanExecution,
24
- requestPlanStepCorrections,
25
24
  revisePlanStep,
26
25
  skipPlanStep,
27
26
  startPlanStep,
28
- submitPlanStepForReview,
29
27
  updatePlanChecklistStep,
30
28
  type PlanExecutionState,
31
29
  } from "./plan-execution.ts";
@@ -155,6 +153,19 @@ export default function planBuildModes(pi: ExtensionAPI): void {
155
153
  applyTools("build");
156
154
  }
157
155
 
156
+ function applyExecutionTransition(next: PlanExecutionState): string | undefined {
157
+ if (next.status !== "completed") {
158
+ updateExecution(next);
159
+ return undefined;
160
+ }
161
+ const summary = formatPlanCompletionSummary(next);
162
+ execution = undefined;
163
+ removePanelLayout();
164
+ persist();
165
+ applyTools("build");
166
+ return summary;
167
+ }
168
+
158
169
  function ensurePanelLayout(): boolean {
159
170
  if (!execution || !fullscreenPanelCapable || !panelTui || !originalLayoutRoot || !currentContext) return false;
160
171
  if (!panel) panel = new PlanPanel(execution, currentContext.ui.theme);
@@ -199,7 +210,7 @@ export default function planBuildModes(pi: ExtensionAPI): void {
199
210
  ...base,
200
211
  "question",
201
212
  "plan_enter",
202
- ...(execution ? ["plan_step_control"] : []),
213
+ ...(execution && execution.status !== "completed" ? ["plan_step_control"] : []),
203
214
  ...(activePlanStep(execution) ? ["plan_step_complete"] : []),
204
215
  ]));
205
216
  }
@@ -379,13 +390,11 @@ export default function planBuildModes(pi: ExtensionAPI): void {
379
390
  pi.registerTool({
380
391
  name: "plan_step_control",
381
392
  label: "Control Plan Execution",
382
- description: `Use this tool to translate the user's natural-language instructions into one step-by-step plan action. Available actions: start a ready step, complete a clearly finished ready or reviewed step, accept a reviewed step, request corrections, skip a ready step, revise an unimplemented instruction, pause/resume or cancel execution, or hide/show the visual plan panel. Interpret clear user intent semantically, including direct completion statements, but do not advance based on hypothetical, uncertain, or unrelated conversation.`,
393
+ description: `Use this tool to translate the user's natural-language instructions into one step-by-step plan action. Available actions: start a ready step, complete a clearly finished ready step, skip a ready step, revise an unimplemented instruction, pause/resume or cancel execution, or hide/show the visual plan panel. Interpret clear user intent semantically, including direct completion statements, but do not advance based on hypothetical, uncertain, or unrelated conversation.`,
383
394
  parameters: Type.Object({
384
395
  action: Type.Union([
385
396
  Type.Literal("start"),
386
397
  Type.Literal("complete"),
387
- Type.Literal("accept"),
388
- Type.Literal("correct"),
389
398
  Type.Literal("skip"),
390
399
  Type.Literal("revise"),
391
400
  Type.Literal("pause"),
@@ -394,14 +403,14 @@ export default function planBuildModes(pi: ExtensionAPI): void {
394
403
  Type.Literal("hide"),
395
404
  Type.Literal("show"),
396
405
  ]),
397
- step: Type.Optional(Type.Number({ description: "One-based step number; defaults to the current ready/review step", minimum: 1 })),
406
+ step: Type.Optional(Type.Number({ description: "One-based step number; defaults to the current ready step", minimum: 1 })),
398
407
  instruction: Type.Optional(Type.String({ description: "Replacement instruction required for revise" })),
399
408
  }),
400
409
  executionMode: "sequential",
401
410
  async execute(_toolCallId, params) {
402
411
  if (!execution) throw new Error("No step-by-step plan is active");
403
412
  const target = params.step === undefined
404
- ? execution.steps.find((step) => step.status === "ready" || step.status === "review")
413
+ ? execution.steps.find((step) => step.status === "ready")
405
414
  : execution.steps[Math.floor(params.step) - 1];
406
415
  const finish = (message: string) => ({
407
416
  content: [{ type: "text" as const, text: message }],
@@ -418,7 +427,7 @@ export default function planBuildModes(pi: ExtensionAPI): void {
418
427
  if (params.action === "show") ensurePanelLayout();
419
428
  return finish(`The visual plan panel is now ${params.action === "show" ? "visible" : "hidden"}. Progress is unchanged.`);
420
429
  }
421
- if (execution.status === "completed") throw new Error("The plan is complete; only hide or show actions remain available");
430
+ if (execution.status === "completed") throw new Error("The plan is already complete");
422
431
  if (params.action === "pause" || params.action === "resume") {
423
432
  if ((params.action === "pause") === (execution.status === "paused")) return finish(`Plan execution is already ${params.action === "pause" ? "paused" : "running"}.`);
424
433
  updateExecution(pausePlanExecution(execution));
@@ -433,22 +442,12 @@ export default function planBuildModes(pi: ExtensionAPI): void {
433
442
  return finish("The requested step is approved. Its implementation is starting in a follow-up turn.");
434
443
  }
435
444
  if (params.action === "complete") {
436
- updateExecution(completePlanStep(execution, target.id));
437
- return finish(execution.status === "completed" ? "The step was marked complete and the plan is complete." : "The step was marked complete. The next step is ready and awaits user instruction.");
438
- }
439
- if (params.action === "accept") {
440
- updateExecution(acceptPlanStep(execution, target.id));
441
- return finish(execution.status === "completed" ? "The reviewed step was accepted and the plan is complete." : "The reviewed step was accepted. The next step is ready and awaits user instruction.");
442
- }
443
- if (params.action === "correct") {
444
- updateExecution(requestPlanStepCorrections(execution, target.id));
445
- applyTools("build");
446
- pi.sendUserMessage(`Apply the corrections requested in the user's preceding message to plan step ${execution.steps.findIndex((step) => step.id === target.id) + 1}.`, { deliverAs: "followUp" });
447
- return finish("The reviewed step was reopened. The requested corrections are starting in a follow-up turn.");
445
+ const completion = applyExecutionTransition(completePlanStep(execution, target.id));
446
+ return finish(completion ?? "The step was marked complete. The next step is ready and awaits user instruction.");
448
447
  }
449
448
  if (params.action === "skip") {
450
- updateExecution(skipPlanStep(execution, target.id));
451
- return finish(execution.status === "completed" ? "The step was skipped and the plan is complete." : "The step was skipped. The next step awaits user instruction.");
449
+ const completion = applyExecutionTransition(skipPlanStep(execution, target.id));
450
+ return finish(completion ?? "The step was skipped. The next step awaits user instruction.");
452
451
  }
453
452
  if (!params.instruction?.trim()) throw new Error("Revising a step requires a replacement instruction");
454
453
  const plan = await fs.promises.readFile(planPath, "utf8");
@@ -460,7 +459,7 @@ export default function planBuildModes(pi: ExtensionAPI): void {
460
459
  renderCall(args, theme) {
461
460
  const requestedStep = typeof args.step === "number" && Number.isFinite(args.step) ? Math.max(1, Math.floor(args.step)) : undefined;
462
461
  const inferredStep = requestedStep ?? (execution
463
- ? execution.steps.findIndex((step) => step.status === "ready" || step.status === "review") + 1
462
+ ? execution.steps.findIndex((step) => step.status === "ready") + 1
464
463
  : 0);
465
464
  const label = inferredStep > 0 ? `Step ${inferredStep}: ${args.action}` : `Plan: ${args.action}`;
466
465
  return new Text(theme.fg("toolTitle", theme.bold(label)), 0, 0);
@@ -482,19 +481,19 @@ export default function planBuildModes(pi: ExtensionAPI): void {
482
481
  async execute(_toolCallId, params) {
483
482
  const step = activePlanStep(execution);
484
483
  if (!execution || !step) throw new Error("No plan step is currently active");
485
- updateExecution(submitPlanStepForReview(execution, step.id, params.summary));
486
- applyTools("build");
484
+ const completion = applyExecutionTransition(completePlanStep(execution, step.id, params.summary));
487
485
  return {
488
- content: [{ type: "text", text: "The active plan step is awaiting user review. Stop now and do not begin another step." }],
489
- details: { stepId: step.id, review: true },
486
+ 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 },
490
488
  terminate: true,
491
489
  };
492
490
  },
493
491
  renderCall(_args, theme) {
494
- return new Text(theme.fg("toolTitle", theme.bold("Submit plan step for review")), 0, 0);
492
+ return new Text(theme.fg("toolTitle", theme.bold("Complete plan step")), 0, 0);
495
493
  },
496
- renderResult(_result, _options, theme, context) {
497
- return new Text(theme.fg(context.isError ? "error" : "success", context.isError ? "Plan step submission failed" : "Plan step ready for review"), 0, 0);
494
+ renderResult(result, _options, theme, context) {
495
+ const text = result.content.find((item) => item.type === "text")?.text ?? "Plan step completed";
496
+ return new Text(theme.fg(context.isError ? "error" : "success", text), 0, 0);
498
497
  },
499
498
  });
500
499
 
@@ -647,7 +646,7 @@ export default function planBuildModes(pi: ExtensionAPI): void {
647
646
  runMode = undefined;
648
647
  applyTools(selectedMode);
649
648
  updateModeIndicator(ctx);
650
- if (execution) ensurePanelLayout();
649
+ if (execution && execution.status !== "completed") ensurePanelLayout();
651
650
  });
652
651
 
653
652
  pi.on("session_start", async (event, ctx) => {
@@ -826,7 +825,7 @@ export default function planBuildModes(pi: ExtensionAPI): void {
826
825
  originalLayoutRoot = (tui as TUI & { layoutRoot?: Component }).layoutRoot;
827
826
  fullscreenPanelCapable = originalLayoutRoot !== undefined;
828
827
  }
829
- if (execution) ensurePanelLayout();
828
+ if (execution && execution.status !== "completed") ensurePanelLayout();
830
829
  editor.onCycle = () => {
831
830
  if (currentContext) void selectMode(nextMode(selectedMode), currentContext, "manual");
832
831
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@janvitos/pi-plan-build",
3
- "version": "0.1.33",
3
+ "version": "0.1.34",
4
4
  "description": "Plan safely, approve explicitly, then implement here or in a clean session.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/plan-execution.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type PlanStepStatus = "pending" | "ready" | "active" | "review" | "completed" | "skipped";
1
+ export type PlanStepStatus = "pending" | "ready" | "active" | "completed" | "skipped";
2
2
 
3
3
  export interface PlanStep {
4
4
  id: string;
@@ -55,21 +55,36 @@ export function decodePlanExecution(value: unknown): PlanExecutionState | undefi
55
55
  const candidate = value as Partial<PlanExecutionState>;
56
56
  if (candidate.version !== 1 || !["running", "paused", "completed"].includes(candidate.status ?? "")) return undefined;
57
57
  if (!Array.isArray(candidate.steps) || candidate.steps.length === 0 || typeof candidate.planMarkdown !== "string") return undefined;
58
- const validStatuses = new Set<PlanStepStatus>(["pending", "ready", "active", "review", "completed", "skipped"]);
58
+ const validStatuses = new Set<PlanStepStatus>(["pending", "ready", "active", "completed", "skipped"]);
59
59
  const steps: PlanStep[] = [];
60
+ let legacyReviewIndex = -1;
60
61
  for (const raw of candidate.steps) {
61
62
  if (!raw || typeof raw !== "object") return undefined;
62
- const step = raw as Partial<PlanStep>;
63
- if (typeof step.id !== "string" || typeof step.text !== "string" || !validStatuses.has(step.status as PlanStepStatus)) return undefined;
63
+ const step = raw as Partial<PlanStep> & { status?: string };
64
+ const status = step.status === "review" ? "completed" : step.status;
65
+ if (step.status === "review") legacyReviewIndex = steps.length;
66
+ if (typeof step.id !== "string" || typeof step.text !== "string" || !validStatuses.has(status as PlanStepStatus)) return undefined;
64
67
  if (!Number.isInteger(step.sourceLine) || (step.sourceLine ?? -1) < 0) return undefined;
65
- steps.push({ id: step.id, text: step.text, status: step.status as PlanStepStatus, sourceLine: step.sourceLine!, ...(typeof step.summary === "string" ? { summary: step.summary } : {}) });
68
+ steps.push({ id: step.id, text: step.text, status: status as PlanStepStatus, sourceLine: step.sourceLine!, ...(typeof step.summary === "string" ? { summary: step.summary } : {}) });
69
+ }
70
+ let selectedStepId = typeof candidate.selectedStepId === "string" ? candidate.selectedStepId : undefined;
71
+ let status = candidate.status!;
72
+ if (legacyReviewIndex >= 0) {
73
+ const next = steps.slice(legacyReviewIndex + 1).find((step) => step.status === "pending");
74
+ if (next) {
75
+ next.status = "ready";
76
+ selectedStepId = next.id;
77
+ } else if (steps.every((step) => step.status === "completed" || step.status === "skipped")) {
78
+ status = "completed";
79
+ selectedStepId = undefined;
80
+ }
66
81
  }
67
82
  return {
68
83
  version: 1,
69
- status: candidate.status!,
84
+ status,
70
85
  steps,
71
86
  planMarkdown: candidate.planMarkdown,
72
- selectedStepId: typeof candidate.selectedStepId === "string" ? candidate.selectedStepId : undefined,
87
+ selectedStepId,
73
88
  panelVisible: candidate.panelVisible !== false,
74
89
  };
75
90
  }
@@ -98,6 +113,16 @@ function makeNextReady(state: PlanExecutionState, afterId: string): void {
98
113
  }
99
114
  }
100
115
 
116
+ export function formatPlanCompletionSummary(state: PlanExecutionState): string {
117
+ const lines = ["Plan complete.", "", "Summary:"];
118
+ for (const [index, step] of state.steps.entries()) {
119
+ const outcome = step.status === "skipped" ? "Skipped" : "Completed";
120
+ lines.push(`${index + 1}. ${outcome}: ${step.text}`);
121
+ if (step.summary?.trim()) lines.push(` ${step.summary.trim()}`);
122
+ }
123
+ return lines.join("\n");
124
+ }
125
+
101
126
  export function startPlanStep(state: PlanExecutionState, id: string): PlanExecutionState {
102
127
  const next = clone(state);
103
128
  if (next.status === "completed") throw new Error("The plan is already complete");
@@ -109,42 +134,18 @@ export function startPlanStep(state: PlanExecutionState, id: string): PlanExecut
109
134
  return next;
110
135
  }
111
136
 
112
- export function submitPlanStepForReview(state: PlanExecutionState, id: string, summary: string): PlanExecutionState {
113
- const next = clone(state);
114
- const step = findStep(next, id);
115
- if (step.status !== "active") throw new Error("Only the active step can be submitted for review");
116
- step.status = "review";
117
- step.summary = summary.trim();
118
- next.selectedStepId = id;
119
- return next;
120
- }
121
-
122
- export function completePlanStep(state: PlanExecutionState, id: string): PlanExecutionState {
137
+ export function completePlanStep(state: PlanExecutionState, id: string, summary?: string): PlanExecutionState {
123
138
  const next = clone(state);
124
139
  const step = findStep(next, id);
125
- if (step.status !== "ready" && step.status !== "review") {
126
- throw new Error("Only a ready or reviewed step can be marked complete");
140
+ if (step.status !== "ready" && step.status !== "active") {
141
+ throw new Error("Only a ready or active step can be marked complete");
127
142
  }
143
+ if (summary?.trim()) step.summary = summary.trim();
128
144
  step.status = "completed";
129
145
  makeNextReady(next, id);
130
146
  return next;
131
147
  }
132
148
 
133
- export function acceptPlanStep(state: PlanExecutionState, id: string): PlanExecutionState {
134
- const step = findStep(state, id);
135
- if (step.status !== "review") throw new Error("Only a step awaiting review can be accepted");
136
- return completePlanStep(state, id);
137
- }
138
-
139
- export function requestPlanStepCorrections(state: PlanExecutionState, id: string): PlanExecutionState {
140
- const next = clone(state);
141
- const step = findStep(next, id);
142
- if (step.status !== "review") throw new Error("Only a step awaiting review can receive corrections");
143
- step.status = "active";
144
- next.selectedStepId = id;
145
- return next;
146
- }
147
-
148
149
  export function skipPlanStep(state: PlanExecutionState, id: string): PlanExecutionState {
149
150
  const next = clone(state);
150
151
  const step = findStep(next, id);
package/plan-panel.ts CHANGED
@@ -6,7 +6,6 @@ const GLYPHS = {
6
6
  pending: "○",
7
7
  ready: "▷",
8
8
  active: "▶",
9
- review: "◆",
10
9
  completed: "✓",
11
10
  skipped: "–",
12
11
  } as const;
@@ -46,7 +45,7 @@ export class PlanPanel implements Component {
46
45
  ];
47
46
  for (let index = 0; index < this.state.steps.length; index++) {
48
47
  const step = this.state.steps[index]!;
49
- const glyphColor = step.status === "completed" ? "success" : step.status === "review" ? "warning" : step.status === "active" || step.status === "ready" ? "accent" : "muted";
48
+ const glyphColor = step.status === "completed" ? "success" : step.status === "active" || step.status === "ready" ? "accent" : "muted";
50
49
  const prefix = `${this.theme.fg(glyphColor, GLYPHS[step.status])} ${index + 1}. `;
51
50
  const text = step.status === "completed" || step.status === "skipped" ? this.theme.fg("muted", step.text) : step.text;
52
51
  const wrapped = wrapTextWithAnsi(text, Math.max(1, contentWidth - visibleWidth(prefix)));
@@ -68,7 +67,6 @@ export class PlanPanel implements Component {
68
67
  if (this.state.status === "completed") lines.push(pad(this.theme.fg("success", "Plan complete")));
69
68
  else if (this.state.status === "paused") lines.push(pad("Tell the agent to resume, cancel, or hide the plan."));
70
69
  else if (current?.status === "ready") lines.push(pad("Tell the agent to implement, complete, edit, skip, cancel, or hide this step."));
71
- else if (current?.status === "review") lines.push(pad("Tell the agent to accept, correct, or cancel the plan."));
72
70
  else lines.push(pad("Plan progress updates automatically from your prompts."));
73
71
  lines.push(border(`╰${"─".repeat(inner)}╯`));
74
72
  return lines.map((line) => truncateToWidth(line, safeWidth, ""));
package/prompts.ts CHANGED
@@ -60,7 +60,7 @@ The approved plan is at ${planPath}. Implement only step ${stepNumber} of ${tota
60
60
 
61
61
  ${step}
62
62
 
63
- Do not begin any later plan step. Complete and verify this step, then call plan_step_complete with a concise result summary. If the user requests corrections, continue working only on this same step and submit it for review again.
63
+ Do not begin any later plan step. Complete and verify this step, then call plan_step_complete with a concise result summary. The step will be marked completed immediately; do not ask the user to review or accept it.
64
64
  </system-reminder>`;
65
65
  }
66
66
 
@@ -71,11 +71,11 @@ Step-by-step execution is waiting for the user's natural-language instruction. N
71
71
  Current progress:
72
72
  ${progress}
73
73
 
74
- Interpret the user's intent contextually rather than requiring exact phrases. Clear statements, confirmations, and feedback may request a state transition even when phrased non-imperatively. For example, a statement that a ready step is finished can use the complete action, while a positive review of agent work can use accept. If multiple materially different actions are plausible, ask a brief clarification. Cancellation is always available when the user clearly wants to stop. Do not advance based on hypothetical, uncertain, or unrelated discussion. The sidebar is a passive visual aid and cannot receive input.
74
+ Interpret the user's intent contextually rather than requiring exact phrases. Clear statements, confirmations, and feedback may request a state transition even when phrased non-imperatively. For example, a statement that a ready step is finished can use the complete action. If multiple materially different actions are plausible, ask a brief clarification. Cancellation is always available when the user clearly wants to stop. Do not advance based on hypothetical, uncertain, or unrelated discussion. The sidebar is a passive visual aid and cannot receive input.
75
75
  </system-reminder>`;
76
76
  }
77
77
 
78
- export const PLAN_STEP_COMPLETE_DESCRIPTION = `Call this tool after implementing and verifying the currently active plan step. It stops execution and returns control to the user for review. Do not call it before the active step is complete, and never begin the next step yourself.`;
78
+ export const PLAN_STEP_COMPLETE_DESCRIPTION = `Call this tool after implementing and verifying the currently active plan step. It marks the step completed immediately and returns control to the user before any next step begins. Do not call it before the active step is complete, and never begin the next step yourself.`;
79
79
 
80
80
  export const PLAN_EXIT_DESCRIPTION = `Use this tool when you have completed the planning phase and are ready to exit plan agent.
81
81