@narumitw/pi-plan-mode 0.28.0 → 0.31.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/completion-tool.ts +1 -3
- package/src/question-tool.ts +41 -10
- package/src/state.ts +9 -22
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@narumitw/pi-plan-mode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.0",
|
|
4
4
|
"description": "Pi extension that adds a Codex-like read-only /plan collaboration mode.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"typecheck": "tsc --noEmit"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@biomejs/biome": "2.5.
|
|
32
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
31
|
+
"@biomejs/biome": "2.5.5",
|
|
32
|
+
"@earendil-works/pi-coding-agent": "0.82.1",
|
|
33
33
|
"typescript": "7.0.2"
|
|
34
34
|
},
|
|
35
35
|
"repository": {
|
package/src/completion-tool.ts
CHANGED
|
@@ -22,9 +22,7 @@ export const PLAN_MODE_COMPLETE_PARAMS = {
|
|
|
22
22
|
},
|
|
23
23
|
} as const;
|
|
24
24
|
|
|
25
|
-
type NormalizePlanModeCompletionResult =
|
|
26
|
-
| { ok: true; plan: string }
|
|
27
|
-
| { ok: false; error: string };
|
|
25
|
+
type NormalizePlanModeCompletionResult = { ok: true; plan: string } | { ok: false; error: string };
|
|
28
26
|
|
|
29
27
|
export function normalizePlanModeCompletion(input: unknown): NormalizePlanModeCompletionResult {
|
|
30
28
|
if (!isRecord(input) || typeof input.plan !== "string") {
|
package/src/question-tool.ts
CHANGED
|
@@ -23,7 +23,11 @@ type PlanModeQuestionAnswer = {
|
|
|
23
23
|
optionIndex?: number;
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
type PlanModeQuestionReason =
|
|
26
|
+
type PlanModeQuestionReason =
|
|
27
|
+
| "cancelled"
|
|
28
|
+
| "ui_unavailable"
|
|
29
|
+
| "plan_mode_inactive"
|
|
30
|
+
| "invalid_input";
|
|
27
31
|
|
|
28
32
|
type PlanModeQuestionDetails = {
|
|
29
33
|
cancelled: boolean;
|
|
@@ -47,8 +51,14 @@ export const PLAN_MODE_QUESTION_PARAMS = {
|
|
|
47
51
|
additionalProperties: false,
|
|
48
52
|
required: ["id", "header", "question", "options"],
|
|
49
53
|
properties: {
|
|
50
|
-
id: {
|
|
51
|
-
|
|
54
|
+
id: {
|
|
55
|
+
type: "string",
|
|
56
|
+
description: "Stable identifier for mapping answers (snake_case).",
|
|
57
|
+
},
|
|
58
|
+
header: {
|
|
59
|
+
type: "string",
|
|
60
|
+
description: "Short header label shown in the UI (12 or fewer chars).",
|
|
61
|
+
},
|
|
52
62
|
question: { type: "string", description: "Single-sentence prompt shown to the user." },
|
|
53
63
|
options: {
|
|
54
64
|
type: "array",
|
|
@@ -79,7 +89,9 @@ type NormalizePlanModeQuestionParamsResult =
|
|
|
79
89
|
| { ok: true; questions: PlanModeQuestion[] }
|
|
80
90
|
| { ok: false; error: string };
|
|
81
91
|
|
|
82
|
-
export function normalizePlanModeQuestionParams(
|
|
92
|
+
export function normalizePlanModeQuestionParams(
|
|
93
|
+
input: unknown,
|
|
94
|
+
): NormalizePlanModeQuestionParamsResult {
|
|
83
95
|
if (!isRecord(input) || !Array.isArray(input.questions)) {
|
|
84
96
|
return { ok: false, error: "questions must be an array" };
|
|
85
97
|
}
|
|
@@ -96,7 +108,10 @@ export function normalizePlanModeQuestionParams(input: unknown): NormalizePlanMo
|
|
|
96
108
|
const header = stringField(rawQuestion.header);
|
|
97
109
|
const question = stringField(rawQuestion.question);
|
|
98
110
|
if (!id || !header || !question) {
|
|
99
|
-
return {
|
|
111
|
+
return {
|
|
112
|
+
ok: false,
|
|
113
|
+
error: `question ${questionIndex + 1} requires non-empty id, header, and question`,
|
|
114
|
+
};
|
|
100
115
|
}
|
|
101
116
|
if (!Array.isArray(rawQuestion.options)) {
|
|
102
117
|
return { ok: false, error: `question ${questionIndex + 1} options must be an array` };
|
|
@@ -114,7 +129,10 @@ export function normalizePlanModeQuestionParams(input: unknown): NormalizePlanMo
|
|
|
114
129
|
}
|
|
115
130
|
const label = stringField(rawOption.label);
|
|
116
131
|
if (!label) {
|
|
117
|
-
return {
|
|
132
|
+
return {
|
|
133
|
+
ok: false,
|
|
134
|
+
error: `question ${questionIndex + 1} option ${optionIndex + 1} requires a label`,
|
|
135
|
+
};
|
|
118
136
|
}
|
|
119
137
|
const description = stringField(rawOption.description);
|
|
120
138
|
if (!description) {
|
|
@@ -138,7 +156,10 @@ export async function askPlanModeQuestions(
|
|
|
138
156
|
for (const question of questions) {
|
|
139
157
|
const choices = question.options.map(formatPlanModeQuestionChoice);
|
|
140
158
|
const otherChoice = `${question.options.length + 1}. Other (free-form)`;
|
|
141
|
-
const choice = await ctx.ui.select(`${question.header}: ${question.question}`, [
|
|
159
|
+
const choice = await ctx.ui.select(`${question.header}: ${question.question}`, [
|
|
160
|
+
...choices,
|
|
161
|
+
otherChoice,
|
|
162
|
+
]);
|
|
142
163
|
if (!choice) return undefined;
|
|
143
164
|
if (choice === otherChoice) {
|
|
144
165
|
const customAnswer = (await ctx.ui.editor(question.question, ""))?.trim();
|
|
@@ -171,9 +192,14 @@ function formatPlanModeQuestionChoice(option: PlanModeQuestionOption, index: num
|
|
|
171
192
|
return `${index + 1}. ${option.label}${option.description ? ` — ${option.description}` : ""}`;
|
|
172
193
|
}
|
|
173
194
|
|
|
174
|
-
export function planModeQuestionAnswered(
|
|
195
|
+
export function planModeQuestionAnswered(
|
|
196
|
+
questions: PlanModeQuestion[],
|
|
197
|
+
answers: PlanModeQuestionAnswer[],
|
|
198
|
+
) {
|
|
175
199
|
return {
|
|
176
|
-
content: [
|
|
200
|
+
content: [
|
|
201
|
+
{ type: "text" as const, text: formatPlanModeQuestionPayload({ cancelled: false, answers }) },
|
|
202
|
+
],
|
|
177
203
|
details: { cancelled: false, questions, answers } satisfies PlanModeQuestionDetails,
|
|
178
204
|
};
|
|
179
205
|
}
|
|
@@ -184,7 +210,12 @@ export function planModeQuestionCancelled(
|
|
|
184
210
|
message: string,
|
|
185
211
|
) {
|
|
186
212
|
return {
|
|
187
|
-
content: [
|
|
213
|
+
content: [
|
|
214
|
+
{
|
|
215
|
+
type: "text" as const,
|
|
216
|
+
text: formatPlanModeQuestionPayload({ cancelled: true, reason, message }),
|
|
217
|
+
},
|
|
218
|
+
],
|
|
188
219
|
details: { cancelled: true, reason, questions } satisfies PlanModeQuestionDetails,
|
|
189
220
|
};
|
|
190
221
|
}
|
package/src/state.ts
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
PLAN_MODE_COMPLETE_TOOL_NAME,
|
|
3
2
|
normalizePlanModeCompletion,
|
|
3
|
+
PLAN_MODE_COMPLETE_TOOL_NAME,
|
|
4
4
|
planFromCompletionDetails,
|
|
5
5
|
} from "./completion-tool.js";
|
|
6
|
-
import {
|
|
7
|
-
PLAN_MODE_THINKING_LEVELS,
|
|
8
|
-
type PlanModeFixedThinkingLevel,
|
|
9
|
-
} from "./settings.js";
|
|
6
|
+
import { PLAN_MODE_THINKING_LEVELS, type PlanModeFixedThinkingLevel } from "./settings.js";
|
|
10
7
|
|
|
11
|
-
export type PlanCompletionSource =
|
|
12
|
-
| typeof PLAN_MODE_COMPLETE_TOOL_NAME
|
|
13
|
-
| "legacy_proposed_plan";
|
|
8
|
+
export type PlanCompletionSource = typeof PLAN_MODE_COMPLETE_TOOL_NAME | "legacy_proposed_plan";
|
|
14
9
|
|
|
15
10
|
export interface PlanModeState {
|
|
16
11
|
enabled: boolean;
|
|
@@ -49,23 +44,19 @@ export function restorePlanModeState(entries: unknown[], stateEntryType: string)
|
|
|
49
44
|
if (!isRecord(entry?.data)) return { enabled: false, awaitingAction: false };
|
|
50
45
|
|
|
51
46
|
const enabled = entry.data.enabled === true;
|
|
52
|
-
const persistedSource = enabled
|
|
53
|
-
? planCompletionSource(entry.data.latestPlanSource)
|
|
54
|
-
: undefined;
|
|
47
|
+
const persistedSource = enabled ? planCompletionSource(entry.data.latestPlanSource) : undefined;
|
|
55
48
|
const persistedPlan = enabled
|
|
56
49
|
? normalizePersistedPlan(entry.data.latestPlan, persistedSource)
|
|
57
50
|
: undefined;
|
|
58
51
|
const recoveredPlan =
|
|
59
|
-
enabled && !persistedPlan
|
|
60
|
-
? latestCompletionPlan(branch.slice(stateEntryIndex + 1))
|
|
61
|
-
: undefined;
|
|
52
|
+
enabled && !persistedPlan ? latestCompletionPlan(branch.slice(stateEntryIndex + 1)) : undefined;
|
|
62
53
|
const latestPlan = persistedPlan ?? recoveredPlan;
|
|
63
54
|
return {
|
|
64
55
|
enabled,
|
|
65
56
|
latestPlan,
|
|
66
57
|
latestPlanSource: enabled
|
|
67
|
-
? (persistedPlan ? persistedSource : undefined) ??
|
|
68
|
-
(recoveredPlan ? PLAN_MODE_COMPLETE_TOOL_NAME : undefined)
|
|
58
|
+
? ((persistedPlan ? persistedSource : undefined) ??
|
|
59
|
+
(recoveredPlan ? PLAN_MODE_COMPLETE_TOOL_NAME : undefined))
|
|
69
60
|
: undefined,
|
|
70
61
|
awaitingAction: enabled && latestPlan !== undefined,
|
|
71
62
|
selectedToolNames: stringArray(entry.data.selectedToolNames),
|
|
@@ -73,12 +64,8 @@ export function restorePlanModeState(entries: unknown[], stateEntryType: string)
|
|
|
73
64
|
previousThinkingLevel: enabled
|
|
74
65
|
? fixedThinkingLevel(entry.data.previousThinkingLevel)
|
|
75
66
|
: undefined,
|
|
76
|
-
appliedThinkingLevel: enabled
|
|
77
|
-
|
|
78
|
-
: undefined,
|
|
79
|
-
manualThinkingLevel: enabled
|
|
80
|
-
? fixedThinkingLevel(entry.data.manualThinkingLevel)
|
|
81
|
-
: undefined,
|
|
67
|
+
appliedThinkingLevel: enabled ? fixedThinkingLevel(entry.data.appliedThinkingLevel) : undefined,
|
|
68
|
+
manualThinkingLevel: enabled ? fixedThinkingLevel(entry.data.manualThinkingLevel) : undefined,
|
|
82
69
|
};
|
|
83
70
|
}
|
|
84
71
|
|