@plannotator/pi-extension 0.15.0 → 0.15.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/README.md +1 -1
- package/generated/ai/base-session.ts +95 -0
- package/generated/ai/context.ts +212 -0
- package/generated/ai/endpoints.ts +309 -0
- package/generated/ai/index.ts +106 -0
- package/generated/ai/provider.ts +104 -0
- package/generated/ai/providers/claude-agent-sdk.ts +441 -0
- package/generated/ai/providers/codex-sdk.ts +430 -0
- package/generated/ai/providers/opencode-sdk.ts +491 -0
- package/generated/ai/providers/pi-events.ts +111 -0
- package/generated/ai/providers/pi-sdk-node.ts +377 -0
- package/generated/ai/providers/pi-sdk.ts +442 -0
- package/generated/ai/session-manager.ts +196 -0
- package/generated/ai/types.ts +370 -0
- package/generated/resolve-file.ts +28 -0
- package/index.ts +74 -45
- package/package.json +2 -2
- package/plannotator.html +70 -70
- package/review-editor.html +2 -2
- package/server/serverAnnotate.ts +2 -1
- package/server/serverReview.ts +5 -5
package/index.ts
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Plannotator Pi Extension — File-based plan mode with visual browser review.
|
|
3
3
|
*
|
|
4
4
|
* Plans are written to PLAN.md on disk (git-trackable, editor-visible).
|
|
5
|
-
* The agent calls
|
|
6
|
-
* the plan in the Plannotator browser UI and can approve, deny
|
|
7
|
-
* annotations, or request changes.
|
|
5
|
+
* The agent calls plannotator_submit_plan to request approval; the user
|
|
6
|
+
* reviews the plan in the Plannotator browser UI and can approve, deny
|
|
7
|
+
* with annotations, or request changes.
|
|
8
8
|
*
|
|
9
9
|
* Features:
|
|
10
10
|
* - /plannotator command or Ctrl+Alt+P to toggle
|
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
* - --plan-file flag to customize the plan file path
|
|
13
13
|
* - Bash unrestricted during planning (prompt-guided)
|
|
14
14
|
* - Write restricted to plan file only during planning
|
|
15
|
-
* -
|
|
15
|
+
* - plannotator_submit_plan tool with browser-based visual approval
|
|
16
16
|
* - [DONE:n] markers for execution progress tracking
|
|
17
17
|
* - /plannotator-review command for code review
|
|
18
18
|
* - /plannotator-annotate command for markdown annotation
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
21
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
22
22
|
import { dirname, resolve } from "node:path";
|
|
23
23
|
import { fileURLToPath } from "node:url";
|
|
24
24
|
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
|
@@ -35,6 +35,8 @@ import {
|
|
|
35
35
|
parseChecklist,
|
|
36
36
|
} from "./generated/checklist.js";
|
|
37
37
|
import { planDenyFeedback } from "./generated/feedback-templates.js";
|
|
38
|
+
import { hasMarkdownFiles } from "./generated/resolve-file.js";
|
|
39
|
+
import { FILE_BROWSER_EXCLUDED } from "./generated/reference-common.js";
|
|
38
40
|
import { openBrowser } from "./server/network.js";
|
|
39
41
|
import {
|
|
40
42
|
type AnnotateServerResult,
|
|
@@ -46,6 +48,12 @@ import {
|
|
|
46
48
|
startPlanReviewServer,
|
|
47
49
|
startReviewServer,
|
|
48
50
|
} from "./server.js";
|
|
51
|
+
import {
|
|
52
|
+
getToolsForPhase,
|
|
53
|
+
PLAN_SUBMIT_TOOL,
|
|
54
|
+
type Phase,
|
|
55
|
+
stripPlanningOnlyTools,
|
|
56
|
+
} from "./tool-scope.js";
|
|
49
57
|
|
|
50
58
|
// ── Types ──────────────────────────────────────────────────────────────
|
|
51
59
|
|
|
@@ -77,11 +85,6 @@ try {
|
|
|
77
85
|
// HTML not built yet — review feature will be unavailable
|
|
78
86
|
}
|
|
79
87
|
|
|
80
|
-
/** Extra tools to ensure are available during planning (on top of whatever is already active). */
|
|
81
|
-
const PLANNING_EXTRA_TOOLS = ["grep", "find", "ls", "exit_plan_mode"];
|
|
82
|
-
|
|
83
|
-
type Phase = "idle" | "planning" | "executing";
|
|
84
|
-
|
|
85
88
|
function isAssistantMessage(m: AgentMessage): m is AssistantMessage {
|
|
86
89
|
return m.role === "assistant" && Array.isArray(m.content);
|
|
87
90
|
}
|
|
@@ -177,28 +180,19 @@ export default function plannotator(pi: ExtensionAPI): void {
|
|
|
177
180
|
}
|
|
178
181
|
|
|
179
182
|
function persistState(): void {
|
|
180
|
-
pi.appendEntry("plannotator", { phase, planFilePath });
|
|
183
|
+
pi.appendEntry("plannotator", { phase, planFilePath, preplanTools });
|
|
181
184
|
}
|
|
182
185
|
|
|
183
186
|
/** Apply tool visibility for the current phase, preserving tools from other extensions. */
|
|
184
187
|
function applyToolsForPhase(): void {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const toolSet = new Set(base);
|
|
188
|
-
for (const t of PLANNING_EXTRA_TOOLS) toolSet.add(t);
|
|
189
|
-
pi.setActiveTools([...toolSet]);
|
|
190
|
-
} else if (preplanTools) {
|
|
191
|
-
// Restore pre-plan tool set (removes exit_plan_mode, etc.)
|
|
192
|
-
pi.setActiveTools(preplanTools);
|
|
193
|
-
preplanTools = null;
|
|
194
|
-
}
|
|
195
|
-
// If no preplanTools (e.g. session restore to executing/idle), leave tools as-is
|
|
188
|
+
const baseTools = stripPlanningOnlyTools(preplanTools ?? pi.getActiveTools());
|
|
189
|
+
pi.setActiveTools(getToolsForPhase(baseTools, phase));
|
|
196
190
|
}
|
|
197
191
|
|
|
198
192
|
function enterPlanning(ctx: ExtensionContext): void {
|
|
199
193
|
phase = "planning";
|
|
200
194
|
checklistItems = [];
|
|
201
|
-
preplanTools = pi.getActiveTools();
|
|
195
|
+
preplanTools = stripPlanningOnlyTools(pi.getActiveTools());
|
|
202
196
|
applyToolsForPhase();
|
|
203
197
|
updateStatus(ctx);
|
|
204
198
|
updateWidget(ctx);
|
|
@@ -212,6 +206,7 @@ export default function plannotator(pi: ExtensionAPI): void {
|
|
|
212
206
|
phase = "idle";
|
|
213
207
|
checklistItems = [];
|
|
214
208
|
applyToolsForPhase();
|
|
209
|
+
preplanTools = null;
|
|
215
210
|
updateStatus(ctx);
|
|
216
211
|
updateWidget(ctx);
|
|
217
212
|
persistState();
|
|
@@ -343,11 +338,11 @@ export default function plannotator(pi: ExtensionAPI): void {
|
|
|
343
338
|
});
|
|
344
339
|
|
|
345
340
|
pi.registerCommand("plannotator-annotate", {
|
|
346
|
-
description: "Open markdown file in annotation UI",
|
|
341
|
+
description: "Open markdown file or folder in annotation UI",
|
|
347
342
|
handler: async (args, ctx) => {
|
|
348
343
|
const filePath = args?.trim();
|
|
349
344
|
if (!filePath) {
|
|
350
|
-
ctx.ui.notify("Usage: /plannotator-annotate <file.md
|
|
345
|
+
ctx.ui.notify("Usage: /plannotator-annotate <file.md | folder/>", "error");
|
|
351
346
|
return;
|
|
352
347
|
}
|
|
353
348
|
if (!planHtmlContent) {
|
|
@@ -364,15 +359,41 @@ export default function plannotator(pi: ExtensionAPI): void {
|
|
|
364
359
|
return;
|
|
365
360
|
}
|
|
366
361
|
|
|
367
|
-
|
|
362
|
+
// Check if the argument is a directory (folder annotation mode)
|
|
363
|
+
let isFolder = false;
|
|
364
|
+
try {
|
|
365
|
+
isFolder = statSync(absolutePath).isDirectory();
|
|
366
|
+
} catch {
|
|
367
|
+
ctx.ui.notify(`Cannot access: ${absolutePath}`, "error");
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
let markdown: string;
|
|
372
|
+
let folderPath: string | undefined;
|
|
373
|
+
let mode: string | undefined;
|
|
374
|
+
|
|
375
|
+
if (isFolder) {
|
|
376
|
+
if (!hasMarkdownFiles(absolutePath, FILE_BROWSER_EXCLUDED)) {
|
|
377
|
+
ctx.ui.notify(`No markdown files found in ${absolutePath}`, "error");
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
markdown = "";
|
|
381
|
+
folderPath = absolutePath;
|
|
382
|
+
mode = "annotate-folder";
|
|
383
|
+
ctx.ui.notify(`Opening annotation UI for folder ${filePath}...`, "info");
|
|
384
|
+
} else {
|
|
385
|
+
markdown = readFileSync(absolutePath, "utf-8");
|
|
386
|
+
ctx.ui.notify(`Opening annotation UI for ${filePath}...`, "info");
|
|
387
|
+
}
|
|
368
388
|
|
|
369
|
-
const markdown = readFileSync(absolutePath, "utf-8");
|
|
370
389
|
let server: AnnotateServerResult;
|
|
371
390
|
try {
|
|
372
391
|
server = await startAnnotateServer({
|
|
373
392
|
markdown,
|
|
374
393
|
filePath: absolutePath,
|
|
375
394
|
origin: "pi",
|
|
395
|
+
mode,
|
|
396
|
+
folderPath,
|
|
376
397
|
htmlContent: planHtmlContent,
|
|
377
398
|
sharingEnabled: process.env.PLANNOTATOR_SHARE !== "disabled",
|
|
378
399
|
shareBaseUrl: process.env.PLANNOTATOR_SHARE_URL || undefined,
|
|
@@ -389,8 +410,11 @@ export default function plannotator(pi: ExtensionAPI): void {
|
|
|
389
410
|
const result = await runBrowserReview(server, ctx);
|
|
390
411
|
|
|
391
412
|
if (result.feedback) {
|
|
413
|
+
const header = isFolder
|
|
414
|
+
? `# Markdown Annotations\n\nFolder: ${absolutePath}\n\n`
|
|
415
|
+
: `# Markdown Annotations\n\nFile: ${absolutePath}\n\n`;
|
|
392
416
|
pi.sendUserMessage(
|
|
393
|
-
|
|
417
|
+
`${header}${result.feedback}\n\nPlease address the annotation feedback above.`,
|
|
394
418
|
);
|
|
395
419
|
} else {
|
|
396
420
|
ctx.ui.notify("Annotation closed (no feedback).", "info");
|
|
@@ -519,14 +543,14 @@ export default function plannotator(pi: ExtensionAPI): void {
|
|
|
519
543
|
handler: async (ctx) => togglePlanMode(ctx),
|
|
520
544
|
});
|
|
521
545
|
|
|
522
|
-
// ──
|
|
546
|
+
// ── plannotator_submit_plan Tool ────────────────────────────────────
|
|
523
547
|
|
|
524
548
|
pi.registerTool({
|
|
525
|
-
name:
|
|
526
|
-
label: "
|
|
549
|
+
name: PLAN_SUBMIT_TOOL,
|
|
550
|
+
label: "Submit Plan",
|
|
527
551
|
description:
|
|
528
|
-
"Submit your plan for user review. " +
|
|
529
|
-
"Call this after drafting or revising your plan file. " +
|
|
552
|
+
"Submit your Plannotator plan for user review. " +
|
|
553
|
+
"Call this only while Plannotator planning mode is active, after drafting or revising your plan file. " +
|
|
530
554
|
"The user will review the plan in a visual browser UI and can approve, deny with feedback, or annotate it. " +
|
|
531
555
|
"If denied, use the edit tool to make targeted revisions (not write), then call this again.",
|
|
532
556
|
parameters: Type.Object({
|
|
@@ -561,7 +585,7 @@ export default function plannotator(pi: ExtensionAPI): void {
|
|
|
561
585
|
content: [
|
|
562
586
|
{
|
|
563
587
|
type: "text",
|
|
564
|
-
text: `Error: ${planFilePath} does not exist. Write your plan using the write tool first, then call
|
|
588
|
+
text: `Error: ${planFilePath} does not exist. Write your plan using the write tool first, then call ${PLAN_SUBMIT_TOOL} again.`,
|
|
565
589
|
},
|
|
566
590
|
],
|
|
567
591
|
details: { approved: false },
|
|
@@ -573,7 +597,7 @@ export default function plannotator(pi: ExtensionAPI): void {
|
|
|
573
597
|
content: [
|
|
574
598
|
{
|
|
575
599
|
type: "text",
|
|
576
|
-
text: `Error: ${planFilePath} is empty. Write your plan first, then call
|
|
600
|
+
text: `Error: ${planFilePath} is empty. Write your plan first, then call ${PLAN_SUBMIT_TOOL} again.`,
|
|
577
601
|
},
|
|
578
602
|
],
|
|
579
603
|
details: { approved: false },
|
|
@@ -587,6 +611,7 @@ export default function plannotator(pi: ExtensionAPI): void {
|
|
|
587
611
|
if (!ctx.hasUI || !planHtmlContent) {
|
|
588
612
|
phase = "executing";
|
|
589
613
|
applyToolsForPhase();
|
|
614
|
+
preplanTools = null;
|
|
590
615
|
persistState();
|
|
591
616
|
return {
|
|
592
617
|
content: [
|
|
@@ -624,6 +649,7 @@ export default function plannotator(pi: ExtensionAPI): void {
|
|
|
624
649
|
if (result.approved) {
|
|
625
650
|
phase = "executing";
|
|
626
651
|
applyToolsForPhase();
|
|
652
|
+
preplanTools = null;
|
|
627
653
|
updateStatus(ctx);
|
|
628
654
|
updateWidget(ctx);
|
|
629
655
|
persistState();
|
|
@@ -664,7 +690,7 @@ export default function plannotator(pi: ExtensionAPI): void {
|
|
|
664
690
|
content: [
|
|
665
691
|
{
|
|
666
692
|
type: "text",
|
|
667
|
-
text: planDenyFeedback(feedbackText,
|
|
693
|
+
text: planDenyFeedback(feedbackText, PLAN_SUBMIT_TOOL, {
|
|
668
694
|
planFilePath,
|
|
669
695
|
}),
|
|
670
696
|
},
|
|
@@ -712,7 +738,7 @@ export default function plannotator(pi: ExtensionAPI): void {
|
|
|
712
738
|
content: `[PLANNOTATOR - PLANNING PHASE]
|
|
713
739
|
You are in plan mode. You MUST NOT make any changes to the codebase — no edits, no commits, no installs, no destructive commands. The ONLY file you may write to or edit is the plan file: ${planFilePath}.
|
|
714
740
|
|
|
715
|
-
Available tools: read, bash, grep, find, ls, write (${planFilePath} only), edit (${planFilePath} only),
|
|
741
|
+
Available tools: read, bash, grep, find, ls, write (${planFilePath} only), edit (${planFilePath} only), ${PLAN_SUBMIT_TOOL}
|
|
716
742
|
|
|
717
743
|
Do not run destructive bash commands (rm, git push, npm install, etc.) — focus on reading and exploring the codebase. Web fetching (curl, wget) is fine.
|
|
718
744
|
|
|
@@ -755,20 +781,20 @@ Keep the plan concise enough to scan quickly, but detailed enough to execute eff
|
|
|
755
781
|
|
|
756
782
|
### When to Submit
|
|
757
783
|
|
|
758
|
-
Your plan is ready when you've addressed all ambiguities and it covers: what to change, which files to modify, what existing code to reuse, and how to verify. Call
|
|
784
|
+
Your plan is ready when you've addressed all ambiguities and it covers: what to change, which files to modify, what existing code to reuse, and how to verify. Call ${PLAN_SUBMIT_TOOL} to submit for review.
|
|
759
785
|
|
|
760
786
|
### Revising After Feedback
|
|
761
787
|
|
|
762
788
|
When the user denies a plan with feedback:
|
|
763
789
|
1. Read ${planFilePath} to see the current plan.
|
|
764
790
|
2. Use the edit tool to make targeted changes addressing the feedback — do NOT rewrite the entire file.
|
|
765
|
-
3. Call
|
|
791
|
+
3. Call ${PLAN_SUBMIT_TOOL} again to resubmit.
|
|
766
792
|
|
|
767
793
|
### Ending Your Turn
|
|
768
794
|
|
|
769
795
|
Your turn should only end by either:
|
|
770
796
|
- Asking the user a question to gather more information.
|
|
771
|
-
- Calling
|
|
797
|
+
- Calling ${PLAN_SUBMIT_TOOL} when the plan is ready for review.
|
|
772
798
|
|
|
773
799
|
Do not end your turn without doing one of these two things.`,
|
|
774
800
|
display: false,
|
|
@@ -867,6 +893,7 @@ Execute each step in order. After completing a step, include [DONE:n] in your re
|
|
|
867
893
|
phase = "idle";
|
|
868
894
|
checklistItems = [];
|
|
869
895
|
applyToolsForPhase();
|
|
896
|
+
preplanTools = null;
|
|
870
897
|
updateStatus(ctx);
|
|
871
898
|
updateWidget(ctx);
|
|
872
899
|
persistState();
|
|
@@ -893,11 +920,14 @@ Execute each step in order. After completing a step, include [DONE:n] in your re
|
|
|
893
920
|
(e: { type: string; customType?: string }) =>
|
|
894
921
|
e.type === "custom" && e.customType === "plannotator",
|
|
895
922
|
)
|
|
896
|
-
.pop() as {
|
|
923
|
+
.pop() as {
|
|
924
|
+
data?: { phase: Phase; planFilePath?: string; preplanTools?: string[] | null };
|
|
925
|
+
} | undefined;
|
|
897
926
|
|
|
898
927
|
if (stateEntry?.data) {
|
|
899
928
|
phase = stateEntry.data.phase ?? phase;
|
|
900
929
|
planFilePath = stateEntry.data.planFilePath ?? planFilePath;
|
|
930
|
+
preplanTools = stateEntry.data.preplanTools ?? preplanTools;
|
|
901
931
|
}
|
|
902
932
|
|
|
903
933
|
// Rebuild execution state from disk + session messages
|
|
@@ -934,10 +964,9 @@ Execute each step in order. After completing a step, include [DONE:n] in your re
|
|
|
934
964
|
}
|
|
935
965
|
}
|
|
936
966
|
|
|
937
|
-
//
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
}
|
|
967
|
+
// Re-apply tool visibility on startup/resume so planning-only tools stay hidden
|
|
968
|
+
// outside planning and pre-plan tool state is restored after approvals.
|
|
969
|
+
applyToolsForPhase();
|
|
941
970
|
|
|
942
971
|
updateStatus(ctx);
|
|
943
972
|
updateWidget(ctx);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plannotator/pi-extension",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Plannotator extension for Pi coding agent - interactive plan review with visual annotation",
|
|
6
6
|
"author": "backnotprop",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"review-editor.html"
|
|
29
29
|
],
|
|
30
30
|
"scripts": {
|
|
31
|
-
"build": "cp ../hook/dist/index.html plannotator.html && cp ../hook/dist/review.html review-editor.html && mkdir -p generated && for f in feedback-templates review-core storage draft project pr-provider pr-github pr-gitlab checklist integrations-common repo reference-common favicon resolve-file; do src=\"../../packages/shared/$f.ts\"; printf '// @generated — DO NOT EDIT. Source: packages/shared/%s.ts\\n' \"$f\" | cat - \"$src\" > \"generated/$f.ts\"; done",
|
|
31
|
+
"build": "cp ../hook/dist/index.html plannotator.html && cp ../hook/dist/review.html review-editor.html && mkdir -p generated && for f in feedback-templates review-core storage draft project pr-provider pr-github pr-gitlab checklist integrations-common repo reference-common favicon resolve-file; do src=\"../../packages/shared/$f.ts\"; printf '// @generated — DO NOT EDIT. Source: packages/shared/%s.ts\\n' \"$f\" | cat - \"$src\" > \"generated/$f.ts\"; done && mkdir -p generated/ai/providers && for f in index types provider session-manager endpoints context base-session; do src=\"../../packages/ai/$f.ts\"; printf '// @generated — DO NOT EDIT. Source: packages/ai/%s.ts\\n' \"$f\" | cat - \"$src\" > \"generated/ai/$f.ts\"; done && for f in claude-agent-sdk codex-sdk opencode-sdk pi-sdk pi-sdk-node pi-events; do src=\"../../packages/ai/providers/$f.ts\"; printf '// @generated — DO NOT EDIT. Source: packages/ai/providers/%s.ts\\n' \"$f\" | cat - \"$src\" > \"generated/ai/providers/$f.ts\"; done",
|
|
32
32
|
"prepublishOnly": "cd ../.. && bun run build:pi"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|