@dreki-gg/pi-plan-mode 0.4.0 → 0.5.0
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,19 @@
|
|
|
1
1
|
# @dreki-gg/pi-plan-mode
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`32797ff`](https://github.com/dreki-gg/pi-extensions/commit/32797ff18d968e22c6c44e95c46e3393d8928cef) Thanks [@jalbarrang](https://github.com/jalbarrang)! - feat(plan-mode): add Windows compatibility — replace Unix shell commands with cross-platform Bun/Node APIs
|
|
8
|
+
|
|
9
|
+
Plan-mode no longer shells out to `cat`, `bash`, or `mkdir` via `pi.exec()`. File I/O now uses `Bun.file()` / `Bun.write()` and `node:fs/promises` `mkdir`, making the extension fully cross-platform. Destructive and safe command pattern lists now include Windows equivalents (`del`, `rd`, `copy`, `move`, `powershell`, `dir`, `where`, `tasklist`, etc.).
|
|
10
|
+
|
|
11
|
+
Also fixes Windows compatibility in three other packages:
|
|
12
|
+
|
|
13
|
+
- **browser-tools**: `spawn` now uses `shell: true` on Windows so `.cmd` wrappers resolve correctly; `shellEscape` uses double-quote style on Windows; install guidance is platform-aware (Homebrew shown only on macOS).
|
|
14
|
+
- **subagent**: `spawn` uses `shell: true` on Windows when the command is bare `pi`, allowing `pi.cmd` resolution.
|
|
15
|
+
- **lsp**: `globalConfigPath()` now uses `os.homedir()` on Windows instead of the unreliable `process.env.HOME`.
|
|
16
|
+
|
|
3
17
|
## 0.4.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
|
@@ -22,6 +22,7 @@ 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';
|
|
25
26
|
import {
|
|
26
27
|
extractTodoItems,
|
|
27
28
|
isSafeCommand,
|
|
@@ -111,7 +112,7 @@ export default function planMode(pi: ExtensionAPI): void {
|
|
|
111
112
|
status: 'in-progress' | 'done',
|
|
112
113
|
title?: string,
|
|
113
114
|
): Promise<void> {
|
|
114
|
-
const manifest = await readPlansJson(
|
|
115
|
+
const manifest = await readPlansJson();
|
|
115
116
|
const existing = manifest[planName];
|
|
116
117
|
const now = new Date().toISOString();
|
|
117
118
|
|
|
@@ -122,10 +123,9 @@ export default function planMode(pi: ExtensionAPI): void {
|
|
|
122
123
|
completed: status === 'done' ? now : null,
|
|
123
124
|
};
|
|
124
125
|
|
|
125
|
-
await
|
|
126
|
+
await mkdir('.plans', { recursive: true });
|
|
126
127
|
const content = serializePlansJson(manifest);
|
|
127
|
-
|
|
128
|
-
await pi.exec('bash', ['-c', `cat > .plans/plans.json << 'PLANS_EOF'\n${content}PLANS_EOF`]);
|
|
128
|
+
await Bun.write('.plans/plans.json', content);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
// ── UI updates ────────────────────────────────────────────────────────────
|
|
@@ -419,10 +419,8 @@ Execute each step in order. You MUST include [DONE:n] in your response after com
|
|
|
419
419
|
let title = 'Untitled plan';
|
|
420
420
|
if (path.endsWith('PLAN.md')) {
|
|
421
421
|
try {
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
title = extractPlanTitle(result.stdout);
|
|
425
|
-
}
|
|
422
|
+
const content = await Bun.file(path).text();
|
|
423
|
+
title = extractPlanTitle(content);
|
|
426
424
|
} catch {
|
|
427
425
|
// Fall through
|
|
428
426
|
}
|
|
@@ -432,11 +430,9 @@ Execute each step in order. You MUST include [DONE:n] in your response after com
|
|
|
432
430
|
} else if (match && planDir && path.endsWith('PLAN.md')) {
|
|
433
431
|
// planDir already set but PLAN.md just written — update title
|
|
434
432
|
try {
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
await updatePlansManifest(match[1], 'in-progress', title);
|
|
439
|
-
}
|
|
433
|
+
const content = await Bun.file(path).text();
|
|
434
|
+
const title = extractPlanTitle(content);
|
|
435
|
+
await updatePlansManifest(match[1], 'in-progress', title);
|
|
440
436
|
} catch {
|
|
441
437
|
// Fall through
|
|
442
438
|
}
|
|
@@ -500,10 +496,7 @@ Execute each step in order. You MUST include [DONE:n] in your response after com
|
|
|
500
496
|
// Read the plan to extract todos
|
|
501
497
|
let planContent = '';
|
|
502
498
|
try {
|
|
503
|
-
|
|
504
|
-
if (result.code === 0) {
|
|
505
|
-
planContent = result.stdout;
|
|
506
|
-
}
|
|
499
|
+
planContent = await Bun.file(planMdPath).text();
|
|
507
500
|
} catch {
|
|
508
501
|
// Fall through — will use empty plan content
|
|
509
502
|
}
|
|
@@ -516,10 +509,7 @@ Execute each step in order. You MUST include [DONE:n] in your response after com
|
|
|
516
509
|
// Read the start prompt for clean handoff
|
|
517
510
|
let startPrompt = '';
|
|
518
511
|
try {
|
|
519
|
-
|
|
520
|
-
if (result.code === 0) {
|
|
521
|
-
startPrompt = result.stdout.trim();
|
|
522
|
-
}
|
|
512
|
+
startPrompt = (await Bun.file(startPromptPath).text()).trim();
|
|
523
513
|
} catch {
|
|
524
514
|
// Fall through
|
|
525
515
|
}
|
|
@@ -25,12 +25,15 @@ export type PlansManifest = Record<string, PlanEntry>;
|
|
|
25
25
|
|
|
26
26
|
const PLANS_JSON = '.plans/plans.json';
|
|
27
27
|
|
|
28
|
-
/** Read plans.json
|
|
29
|
-
export async function readPlansJson(
|
|
28
|
+
/** Read plans.json, returning current manifest (empty object if missing). */
|
|
29
|
+
export async function readPlansJson(): Promise<PlansManifest> {
|
|
30
30
|
try {
|
|
31
|
-
const
|
|
32
|
-
if (
|
|
33
|
-
|
|
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
|
+
}
|
|
34
37
|
}
|
|
35
38
|
} catch {
|
|
36
39
|
// File doesn't exist or isn't valid JSON
|
|
@@ -43,6 +43,17 @@ const DESTRUCTIVE_PATTERNS = [
|
|
|
43
43
|
/\bsystemctl\s+(start|stop|restart|enable|disable)/i,
|
|
44
44
|
/\bservice\s+\S+\s+(start|stop|restart)/i,
|
|
45
45
|
/\b(vim?|nano|emacs|code|subl)\b/i,
|
|
46
|
+
// Windows equivalents
|
|
47
|
+
/\bdel\b/i,
|
|
48
|
+
/\brd\b/i,
|
|
49
|
+
/\bcopy\b/i,
|
|
50
|
+
/\bmove\b/i,
|
|
51
|
+
/\bren\b/i,
|
|
52
|
+
/\brename\b/i,
|
|
53
|
+
/\bicacls\b/i,
|
|
54
|
+
/\battrib\b/i,
|
|
55
|
+
/\bpowershell\b/i,
|
|
56
|
+
/\bpwsh\b/i,
|
|
46
57
|
];
|
|
47
58
|
|
|
48
59
|
// ── Safe read-only bash patterns (allowed in plan mode) ─────────────────────
|
|
@@ -98,6 +109,12 @@ const SAFE_PATTERNS = [
|
|
|
98
109
|
/^\s*fd\b/,
|
|
99
110
|
/^\s*bat\b/,
|
|
100
111
|
/^\s*eza\b/,
|
|
112
|
+
// Windows equivalents
|
|
113
|
+
/^\s*dir\b/,
|
|
114
|
+
/^\s*where\b/,
|
|
115
|
+
/^\s*set\b/,
|
|
116
|
+
/^\s*systeminfo\b/,
|
|
117
|
+
/^\s*tasklist\b/,
|
|
101
118
|
];
|
|
102
119
|
|
|
103
120
|
export function isSafeCommand(command: string): boolean {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dreki-gg/pi-plan-mode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
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"
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "24",
|
|
39
|
+
"bun-types": "latest",
|
|
39
40
|
"oxfmt": "^0.43.0",
|
|
40
41
|
"oxlint": "^1.58.0",
|
|
41
42
|
"typescript": "^6.0.0"
|