@dreki-gg/pi-plan-mode 0.6.0 → 0.6.2

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 CHANGED
@@ -1,5 +1,21 @@
1
1
  # @dreki-gg/pi-plan-mode
2
2
 
3
+ ## 0.6.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [`376864c`](https://github.com/dreki-gg/pi-extensions/commit/376864c37cefa47530363b47055311269c1724a8) Thanks [@jalbarrang](https://github.com/jalbarrang)! - fix(plan-mode): queue messages with deliverAs to prevent "agent already processing" errors
8
+
9
+ All `sendMessage` and `sendUserMessage` calls inside the `agent_end` handler now use `deliverAs: 'followUp'` so they are queued until the agent fully settles. Previously, "Follow up", "Refine Plan", and "Execute Plan" would fire while the agent was still in a processing state, causing silent failures or the error: "Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message."
10
+
11
+ ## 0.6.1
12
+
13
+ ### Patch Changes
14
+
15
+ - [`d95dad2`](https://github.com/dreki-gg/pi-extensions/commit/d95dad2ac85c4e5428252ee691152a0db83a0ced) Thanks [@jalbarrang](https://github.com/jalbarrang)! - fix(plan-mode): replace Bun-specific APIs with Node.js `fs/promises`
16
+
17
+ `pi` runs under Node.js (`#!/usr/bin/env node`), so `Bun.file()` and `Bun.write()` are unavailable at runtime. Replaced all usages with `readFile` and `writeFile` from `node:fs/promises`, which work in both runtimes.
18
+
3
19
  ## 0.6.0
4
20
 
5
21
  ### Minor Changes
@@ -22,13 +22,8 @@ import type { AgentMessage } from '@earendil-works/pi-agent-core';
22
22
  import type { AssistantMessage, TextContent } from '@earendil-works/pi-ai';
23
23
  import type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';
24
24
  import { Key } from '@earendil-works/pi-tui';
25
- import { mkdir } from 'node:fs/promises';
26
- import {
27
- extractTodoItems,
28
- isSafeCommand,
29
- markCompletedSteps,
30
- type TodoItem,
31
- } from './utils.js';
25
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
26
+ import { extractTodoItems, isSafeCommand, markCompletedSteps, type TodoItem } from './utils.js';
32
27
  import {
33
28
  extractPlanTitle,
34
29
  readPlansJson,
@@ -125,7 +120,7 @@ export default function planMode(pi: ExtensionAPI): void {
125
120
 
126
121
  await mkdir('.plans', { recursive: true });
127
122
  const content = serializePlansJson(manifest);
128
- await Bun.write('.plans/plans.json', content);
123
+ await writeFile('.plans/plans.json', content, 'utf-8');
129
124
  }
130
125
 
131
126
  // ── UI updates ────────────────────────────────────────────────────────────
@@ -253,9 +248,7 @@ export default function planMode(pi: ExtensionAPI): void {
253
248
  ctx.ui.notify('No plan yet. Use /plan to start planning.', 'info');
254
249
  return;
255
250
  }
256
- const list = todos
257
- .map((t, i) => `${i + 1}. ${t.completed ? '✓' : '○'} ${t.text}`)
258
- .join('\n');
251
+ const list = todos.map((t, i) => `${i + 1}. ${t.completed ? '✓' : '○'} ${t.text}`).join('\n');
259
252
  ctx.ui.notify(`Plan Progress:\n${list}`, 'info');
260
253
  },
261
254
  });
@@ -419,7 +412,7 @@ Execute each step in order. You MUST include [DONE:n] in your response after com
419
412
  let title = 'Untitled plan';
420
413
  if (path.endsWith('PLAN.md')) {
421
414
  try {
422
- const content = await Bun.file(path).text();
415
+ const content = await readFile(path, 'utf-8');
423
416
  title = extractPlanTitle(content);
424
417
  } catch {
425
418
  // Fall through
@@ -430,7 +423,7 @@ Execute each step in order. You MUST include [DONE:n] in your response after com
430
423
  } else if (match && planDir && path.endsWith('PLAN.md')) {
431
424
  // planDir already set but PLAN.md just written — update title
432
425
  try {
433
- const content = await Bun.file(path).text();
426
+ const content = await readFile(path, 'utf-8');
434
427
  const title = extractPlanTitle(content);
435
428
  await updatePlansManifest(match[1], 'in-progress', title);
436
429
  } catch {
@@ -496,7 +489,7 @@ Execute each step in order. You MUST include [DONE:n] in your response after com
496
489
  // Read the plan to extract todos
497
490
  let planContent = '';
498
491
  try {
499
- planContent = await Bun.file(planMdPath).text();
492
+ planContent = await readFile(planMdPath, 'utf-8');
500
493
  } catch {
501
494
  // Fall through — will use empty plan content
502
495
  }
@@ -509,7 +502,7 @@ Execute each step in order. You MUST include [DONE:n] in your response after com
509
502
  // Read the start prompt for clean handoff
510
503
  let startPrompt = '';
511
504
  try {
512
- startPrompt = (await Bun.file(startPromptPath).text()).trim();
505
+ startPrompt = (await readFile(startPromptPath, 'utf-8')).trim();
513
506
  } catch {
514
507
  // Fall through
515
508
  }
@@ -524,7 +517,7 @@ Execute each step in order. You MUST include [DONE:n] in your response after com
524
517
  content: startPrompt,
525
518
  display: true,
526
519
  },
527
- { triggerTurn: true },
520
+ { triggerTurn: true, deliverAs: 'followUp' },
528
521
  );
529
522
  } else {
530
523
  // Fallback: ask executor to read the plan
@@ -534,7 +527,7 @@ Execute each step in order. You MUST include [DONE:n] in your response after com
534
527
  content: `Execute the plan in ${planMdPath}. Read it first, then execute step by step. Mark each step with [DONE:n] before moving to the next.`,
535
528
  display: true,
536
529
  },
537
- { triggerTurn: true },
530
+ { triggerTurn: true, deliverAs: 'followUp' },
538
531
  );
539
532
  }
540
533
  } else if (choice === 'Refine Plan') {
@@ -553,12 +546,12 @@ Execute each step in order. You MUST include [DONE:n] in your response after com
553
546
  After your review, update PLAN.md and START-PROMPT.md with any improvements.`,
554
547
  display: true,
555
548
  },
556
- { triggerTurn: true },
549
+ { triggerTurn: true, deliverAs: 'followUp' },
557
550
  );
558
551
  } else if (choice === 'Follow up') {
559
552
  const followUp = await ctx.ui.editor('Follow-up instructions for the planner:', '');
560
553
  if (followUp?.trim()) {
561
- pi.sendUserMessage(followUp.trim());
554
+ pi.sendUserMessage(followUp.trim(), { deliverAs: 'followUp' });
562
555
  }
563
556
  } else if (choice === 'Exit plan mode') {
564
557
  await exitPlanMode(ctx);
@@ -23,17 +23,16 @@ export interface PlanEntry {
23
23
 
24
24
  export type PlansManifest = Record<string, PlanEntry>;
25
25
 
26
+ import { readFile } from 'node:fs/promises';
27
+
26
28
  const PLANS_JSON = '.plans/plans.json';
27
29
 
28
30
  /** Read plans.json, returning current manifest (empty object if missing). */
29
31
  export async function readPlansJson(): Promise<PlansManifest> {
30
32
  try {
31
- const file = Bun.file(PLANS_JSON);
32
- if (await file.exists()) {
33
- const text = await file.text();
34
- if (text.trim()) {
35
- return JSON.parse(text) as PlansManifest;
36
- }
33
+ const text = await readFile(PLANS_JSON, 'utf-8');
34
+ if (text.trim()) {
35
+ return JSON.parse(text) as PlansManifest;
37
36
  }
38
37
  } catch {
39
38
  // File doesn't exist or isn't valid JSON
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dreki-gg/pi-plan-mode",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "Two-phase planning workflow for pi — plan with claude-opus-4-6:medium, execute with gpt-5.5:low, with .plans/ file-based handoff",
5
5
  "keywords": [
6
6
  "pi-package"