@narumitw/pi-plan-mode 0.18.0 → 0.24.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/package.json +3 -3
- package/src/message-transform.ts +16 -9
- package/src/plan-mode.ts +27 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@narumitw/pi-plan-mode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "Pi extension that adds a Codex-like read-only /plan collaboration mode.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@biomejs/biome": "2.5.3",
|
|
32
|
-
"@earendil-works/pi-coding-agent": "0.80.
|
|
33
|
-
"typescript": "
|
|
32
|
+
"@earendil-works/pi-coding-agent": "0.80.10",
|
|
33
|
+
"typescript": "7.0.2"
|
|
34
34
|
},
|
|
35
35
|
"repository": {
|
|
36
36
|
"type": "git",
|
package/src/message-transform.ts
CHANGED
|
@@ -2,8 +2,12 @@ import { PLAN_MODE_COMPLETE_TOOL_NAME } from "./completion-tool.js";
|
|
|
2
2
|
|
|
3
3
|
const PLAN_CONTEXT_MESSAGE_TYPE = "plan-mode-context";
|
|
4
4
|
const PROPOSED_PLAN_MESSAGE_TYPE = "proposed-plan";
|
|
5
|
-
const
|
|
6
|
-
|
|
5
|
+
const PLAN_IMPLEMENTATION_HANDOFF_PREFIX =
|
|
6
|
+
"Plan mode is now disabled. Full tool access is restored. Implement this proposed plan now:";
|
|
7
|
+
const PROPOSED_PLAN_PATTERN =
|
|
8
|
+
/^<proposed_plan>[\t ]*\r?\n([\s\S]*?)\r?\n<\/proposed_plan>[\t ]*$/gm;
|
|
9
|
+
const PROPOSED_PLAN_BLOCK_PATTERN =
|
|
10
|
+
/^<proposed_plan>[\t ]*\r?\n[\s\S]*?\r?\n<\/proposed_plan>[\t ]*$/gm;
|
|
7
11
|
|
|
8
12
|
export type ProposedPlanParseResult =
|
|
9
13
|
| { kind: "absent" }
|
|
@@ -65,6 +69,14 @@ export function messageContainsInactivePlanModeArtifact(message: unknown) {
|
|
|
65
69
|
);
|
|
66
70
|
}
|
|
67
71
|
|
|
72
|
+
export function messageContainsPlanModeImplementationHandoff(message: unknown) {
|
|
73
|
+
const candidate = unwrapSessionMessage(message);
|
|
74
|
+
return (
|
|
75
|
+
candidate.role === "user" &&
|
|
76
|
+
contentText(candidate.content).trimStart().startsWith(PLAN_IMPLEMENTATION_HANDOFF_PREFIX)
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
68
80
|
export function stripProposedPlanBlocksFromMessage<T>(message: T): T {
|
|
69
81
|
return replaceAssistantContent(message, stripProposedPlanBlocksFromContent);
|
|
70
82
|
}
|
|
@@ -74,9 +86,7 @@ export function stripPlanModeCompletionCallsFromMessage<T>(message: T): T {
|
|
|
74
86
|
if (!Array.isArray(content)) return content;
|
|
75
87
|
const nextContent = content.filter((block) => {
|
|
76
88
|
const candidate = block as { type?: string; name?: string };
|
|
77
|
-
return !(
|
|
78
|
-
candidate.type === "toolCall" && candidate.name === PLAN_MODE_COMPLETE_TOOL_NAME
|
|
79
|
-
);
|
|
89
|
+
return !(candidate.type === "toolCall" && candidate.name === PLAN_MODE_COMPLETE_TOOL_NAME);
|
|
80
90
|
});
|
|
81
91
|
return nextContent.length === content.length ? content : nextContent;
|
|
82
92
|
});
|
|
@@ -91,10 +101,7 @@ export function isEmptyAssistantMessage(message: unknown) {
|
|
|
91
101
|
);
|
|
92
102
|
}
|
|
93
103
|
|
|
94
|
-
function replaceAssistantContent<T>(
|
|
95
|
-
message: T,
|
|
96
|
-
transform: (content: unknown) => unknown,
|
|
97
|
-
): T {
|
|
104
|
+
function replaceAssistantContent<T>(message: T, transform: (content: unknown) => unknown): T {
|
|
98
105
|
const candidate = unwrapSessionMessage(message);
|
|
99
106
|
if (candidate.role !== "assistant") return message;
|
|
100
107
|
|
package/src/plan-mode.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
latestAssistantText,
|
|
11
11
|
messageContainsInactivePlanModeArtifact,
|
|
12
12
|
messageContainsLegacyPlanModeContextArtifact,
|
|
13
|
+
messageContainsPlanModeImplementationHandoff,
|
|
13
14
|
parseProposedPlan,
|
|
14
15
|
stripPlanModeCompletionCallsFromMessage,
|
|
15
16
|
stripProposedPlanBlocksFromMessage,
|
|
@@ -26,6 +27,7 @@ import {
|
|
|
26
27
|
import { showPersistentSelector } from "./selector-ui.js";
|
|
27
28
|
import {
|
|
28
29
|
configuredThinkingLevel,
|
|
30
|
+
type PlanModeFixedThinkingLevel,
|
|
29
31
|
type PlanModeSettings,
|
|
30
32
|
readPlanModeSettings,
|
|
31
33
|
} from "./settings.js";
|
|
@@ -47,6 +49,20 @@ const BLOCKED_BUILTIN_TOOLS = new Set(["edit", "write"]);
|
|
|
47
49
|
const DEFAULT_TOOLS = ["read", "bash", "edit", "write"];
|
|
48
50
|
const TOOL_SELECTOR_PAGE_SIZE = 10;
|
|
49
51
|
|
|
52
|
+
type AgentSettledHandler = (event: unknown, ctx: ExtensionContext) => unknown;
|
|
53
|
+
|
|
54
|
+
function onAgentSettled(pi: ExtensionAPI, handler: AgentSettledHandler) {
|
|
55
|
+
(
|
|
56
|
+
pi as unknown as {
|
|
57
|
+
on(event: "agent_settled", callback: AgentSettledHandler): void;
|
|
58
|
+
}
|
|
59
|
+
).on("agent_settled", handler);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function setPlanThinkingLevel(pi: ExtensionAPI, level: PlanModeFixedThinkingLevel) {
|
|
63
|
+
(pi.setThinkingLevel as unknown as (level: PlanModeFixedThinkingLevel) => void)(level);
|
|
64
|
+
}
|
|
65
|
+
|
|
50
66
|
interface CommandArgumentCompletion {
|
|
51
67
|
value: string;
|
|
52
68
|
label: string;
|
|
@@ -277,7 +293,13 @@ export default function planMode(pi: ExtensionAPI) {
|
|
|
277
293
|
const messagesWithoutLegacyPlanContext = event.messages.filter(
|
|
278
294
|
(message: unknown) => !messageContainsLegacyPlanModeContextArtifact(message),
|
|
279
295
|
);
|
|
280
|
-
if (state.enabled)
|
|
296
|
+
if (state.enabled) {
|
|
297
|
+
return {
|
|
298
|
+
messages: messagesWithoutLegacyPlanContext.filter(
|
|
299
|
+
(message: unknown) => !messageContainsPlanModeImplementationHandoff(message),
|
|
300
|
+
),
|
|
301
|
+
};
|
|
302
|
+
}
|
|
281
303
|
return {
|
|
282
304
|
messages: messagesWithoutLegacyPlanContext
|
|
283
305
|
.filter((message: unknown) => !messageContainsInactivePlanModeArtifact(message))
|
|
@@ -322,7 +344,7 @@ export default function planMode(pi: ExtensionAPI) {
|
|
|
322
344
|
acceptCompletedPlan(parsedPlan.plan, "legacy_proposed_plan", ctx);
|
|
323
345
|
});
|
|
324
346
|
|
|
325
|
-
pi
|
|
347
|
+
onAgentSettled(pi, async (_event, ctx) => {
|
|
326
348
|
const intent = readyPresentationIntent;
|
|
327
349
|
if (!intent || !readyPresentationIsCurrent(intent)) return;
|
|
328
350
|
if (!ctx.isIdle() || ctx.hasPendingMessages()) return;
|
|
@@ -749,7 +771,7 @@ export default function planMode(pi: ExtensionAPI) {
|
|
|
749
771
|
function applyPlanThinkingLevel() {
|
|
750
772
|
if (state.manualThinkingLevel) {
|
|
751
773
|
if (pi.getThinkingLevel() !== state.manualThinkingLevel) {
|
|
752
|
-
pi
|
|
774
|
+
setPlanThinkingLevel(pi, state.manualThinkingLevel);
|
|
753
775
|
}
|
|
754
776
|
return;
|
|
755
777
|
}
|
|
@@ -764,7 +786,7 @@ export default function planMode(pi: ExtensionAPI) {
|
|
|
764
786
|
}
|
|
765
787
|
const current = pi.getThinkingLevel();
|
|
766
788
|
if (!state.appliedThinkingLevel) state.previousThinkingLevel = current;
|
|
767
|
-
if (current !== configured) pi
|
|
789
|
+
if (current !== configured) setPlanThinkingLevel(pi, configured);
|
|
768
790
|
state.appliedThinkingLevel = pi.getThinkingLevel();
|
|
769
791
|
}
|
|
770
792
|
|
|
@@ -788,7 +810,7 @@ export default function planMode(pi: ExtensionAPI) {
|
|
|
788
810
|
previousThinkingLevel &&
|
|
789
811
|
pi.getThinkingLevel() === appliedThinkingLevel
|
|
790
812
|
) {
|
|
791
|
-
pi
|
|
813
|
+
setPlanThinkingLevel(pi, previousThinkingLevel);
|
|
792
814
|
}
|
|
793
815
|
state = { ...state, appliedThinkingLevel: undefined, previousThinkingLevel: undefined };
|
|
794
816
|
}
|