@janvitos/pi-plan-build 0.1.5 → 0.1.7
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 +2 -1
- package/index.ts +6 -2
- package/package.json +1 -1
- package/utils.ts +30 -3
package/README.md
CHANGED
|
@@ -21,6 +21,7 @@ A global [Pi coding agent](https://github.com/badlogic/pi-mono) extension that a
|
|
|
21
21
|
- **Stay in Plan mode**
|
|
22
22
|
- Staying in Plan mode produces a durable acknowledgement and stops the run until the user responds.
|
|
23
23
|
- Mode state survives reloads, resumes, and forks.
|
|
24
|
+
- When Pi recreates the custom editor, the latest 100 user prompts from the active session branch are restored for Up/Down history navigation.
|
|
24
25
|
|
|
25
26
|
## Requirements
|
|
26
27
|
|
|
@@ -110,7 +111,7 @@ npm test
|
|
|
110
111
|
npm pack --dry-run
|
|
111
112
|
```
|
|
112
113
|
|
|
113
|
-
The tests cover state decoding, safe plan paths, mutation restrictions, deferred transitions, complete plan rendering, approval decisions, stop behavior, fresh-session handoff content, and question formatting.
|
|
114
|
+
The tests cover state decoding, safe plan paths, mutation restrictions, deferred transitions, mode rendering, session-based prompt history restoration, complete plan rendering, approval decisions, stop behavior, fresh-session handoff content, and question formatting.
|
|
114
115
|
|
|
115
116
|
### Publishing
|
|
116
117
|
|
package/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
buildPlanReviewMessage,
|
|
20
20
|
classifyPlanExitChoice,
|
|
21
21
|
decodeModeState,
|
|
22
|
+
extractPromptHistory,
|
|
22
23
|
formatModeStatus,
|
|
23
24
|
isAllowedPlanMutation,
|
|
24
25
|
makePlanPath,
|
|
@@ -366,7 +367,7 @@ export default function planBuildModes(pi: ExtensionAPI): void {
|
|
|
366
367
|
updateModeIndicator(ctx);
|
|
367
368
|
});
|
|
368
369
|
|
|
369
|
-
pi.on("session_start", async (
|
|
370
|
+
pi.on("session_start", async (event, ctx) => {
|
|
370
371
|
currentContext = ctx;
|
|
371
372
|
const entries = ctx.sessionManager.getEntries();
|
|
372
373
|
const latest = entries
|
|
@@ -389,6 +390,8 @@ export default function planBuildModes(pi: ExtensionAPI): void {
|
|
|
389
390
|
updateModeIndicator(ctx);
|
|
390
391
|
|
|
391
392
|
if (ctx.mode === "tui") {
|
|
393
|
+
// Startup history is populated after session_start; replacement flows recreate the editor after that step.
|
|
394
|
+
const promptHistory = event.reason === "startup" ? [] : extractPromptHistory(ctx.sessionManager.getBranch());
|
|
392
395
|
class ModeEditor extends CustomEditor {
|
|
393
396
|
onCycle?: () => void;
|
|
394
397
|
|
|
@@ -397,7 +400,7 @@ export default function planBuildModes(pi: ExtensionAPI): void {
|
|
|
397
400
|
}
|
|
398
401
|
|
|
399
402
|
override render(width: number): string[] {
|
|
400
|
-
const badge = formatModeStatus(selectedMode, ctx.ui.theme);
|
|
403
|
+
const badge = formatModeStatus(selectedMode, ctx.ui.theme, this.borderColor);
|
|
401
404
|
const prompt = `${badge} `;
|
|
402
405
|
const promptWidth = visibleWidth(prompt);
|
|
403
406
|
const paddingWidth = Math.min(promptWidth, Math.max(0, Math.floor((width - 1) / 2)));
|
|
@@ -421,6 +424,7 @@ export default function planBuildModes(pi: ExtensionAPI): void {
|
|
|
421
424
|
}
|
|
422
425
|
ctx.ui.setEditorComponent((tui, theme, keybindings) => {
|
|
423
426
|
const editor = new ModeEditor(tui, theme, keybindings);
|
|
427
|
+
for (const prompt of promptHistory) editor.addToHistory(prompt);
|
|
424
428
|
requestEditorRender = () => editor.requestModeRender();
|
|
425
429
|
editor.onCycle = () => {
|
|
426
430
|
if (currentContext) void selectMode(nextMode(selectedMode), currentContext, "manual");
|
package/package.json
CHANGED
package/utils.ts
CHANGED
|
@@ -9,14 +9,13 @@ const MODE_LABELS: Record<Mode, { color: string; text: string }> = {
|
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
export interface ModeStatusTheme {
|
|
12
|
-
fg(color: "dim", text: string): string;
|
|
13
12
|
bold(text: string): string;
|
|
14
13
|
}
|
|
15
14
|
|
|
16
|
-
export function formatModeStatus(mode: Mode, theme: ModeStatusTheme): string {
|
|
15
|
+
export function formatModeStatus(mode: Mode, theme: ModeStatusTheme, borderColor: (text: string) => string): string {
|
|
17
16
|
const label = MODE_LABELS[mode];
|
|
18
17
|
const modeText = `\x1b[${label.color}m${theme.bold(label.text)}${ANSI_RESET}`;
|
|
19
|
-
return
|
|
18
|
+
return borderColor("[") + modeText + borderColor("]");
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
export const PLAN_EXIT_APPROVE_CHOICE = "Switch to Build and implement here";
|
|
@@ -122,6 +121,34 @@ export function formatQuestionAnswers(answers: QuestionAnswerData[]): string {
|
|
|
122
121
|
return answers.map((answer) => `"${answer.question}"="${answer.answers.length ? answer.answers.join(", ") : "Unanswered"}"`).join(", ");
|
|
123
122
|
}
|
|
124
123
|
|
|
124
|
+
export function extractPromptHistory(entries: readonly unknown[], limit = 100): string[] {
|
|
125
|
+
const prompts: string[] = [];
|
|
126
|
+
for (const entry of entries) {
|
|
127
|
+
if (!entry || typeof entry !== "object") continue;
|
|
128
|
+
const candidate = entry as {
|
|
129
|
+
type?: unknown;
|
|
130
|
+
message?: { role?: unknown; content?: unknown };
|
|
131
|
+
};
|
|
132
|
+
if (candidate.type !== "message" || candidate.message?.role !== "user") continue;
|
|
133
|
+
|
|
134
|
+
const content = candidate.message.content;
|
|
135
|
+
const text = typeof content === "string"
|
|
136
|
+
? content
|
|
137
|
+
: Array.isArray(content)
|
|
138
|
+
? content
|
|
139
|
+
.filter((block): block is { type: "text"; text: string } =>
|
|
140
|
+
!!block && typeof block === "object" && (block as { type?: unknown }).type === "text" && typeof (block as { text?: unknown }).text === "string")
|
|
141
|
+
.map((block) => block.text)
|
|
142
|
+
.join("")
|
|
143
|
+
: "";
|
|
144
|
+
const trimmed = text.trim();
|
|
145
|
+
if (!trimmed || prompts.at(-1) === trimmed) continue;
|
|
146
|
+
prompts.push(trimmed);
|
|
147
|
+
}
|
|
148
|
+
const maxEntries = Math.max(0, Math.floor(limit));
|
|
149
|
+
return maxEntries === 0 ? [] : prompts.slice(-maxEntries);
|
|
150
|
+
}
|
|
151
|
+
|
|
125
152
|
export function nextMode(mode: Mode): Mode {
|
|
126
153
|
return mode === "build" ? "plan" : "build";
|
|
127
154
|
}
|