@astrofoundry/pi-astro 0.13.0 → 0.13.1
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.
|
@@ -43,6 +43,7 @@ interface FakeCtx {
|
|
|
43
43
|
ui: {
|
|
44
44
|
notify: ReturnType<typeof vi.fn>;
|
|
45
45
|
setFooter: ReturnType<typeof vi.fn>;
|
|
46
|
+
setWidget: ReturnType<typeof vi.fn>;
|
|
46
47
|
};
|
|
47
48
|
cwd: string;
|
|
48
49
|
model: { id: string } | undefined;
|
|
@@ -56,6 +57,7 @@ function makeCtx(overrides: Partial<FakeCtx> = {}, oauth = false): FakeCtx {
|
|
|
56
57
|
ui: {
|
|
57
58
|
notify: vi.fn(),
|
|
58
59
|
setFooter: vi.fn(),
|
|
60
|
+
setWidget: vi.fn(),
|
|
59
61
|
},
|
|
60
62
|
cwd: "/Users/astro/proj",
|
|
61
63
|
model: { id: "anthropic/claude-sonnet-4-6" },
|
|
@@ -123,6 +125,55 @@ describe("astro-footer extension", () => {
|
|
|
123
125
|
expect(pi.commands.has("footer")).toBe(true);
|
|
124
126
|
expect(pi.handlers.session_start).toBeDefined();
|
|
125
127
|
expect(pi.handlers.tool_result).toBeDefined();
|
|
128
|
+
expect(pi.handlers.before_agent_start).toBeDefined();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("registers a last-prompt widget below the editor on activate", async () => {
|
|
132
|
+
const { pi } = await install();
|
|
133
|
+
const ctx = makeCtx();
|
|
134
|
+
await pi.handlers.session_start({}, ctx);
|
|
135
|
+
const calls = ctx.ui.setWidget.mock.calls;
|
|
136
|
+
const lastPromptCall = calls.find((c) => c[0] === "astro-footer-last-prompt");
|
|
137
|
+
expect(lastPromptCall).toBeDefined();
|
|
138
|
+
expect(lastPromptCall![2]).toEqual({ placement: "belowEditor" });
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("last-prompt widget renders nothing before the first prompt", async () => {
|
|
142
|
+
const { pi } = await install();
|
|
143
|
+
const ctx = makeCtx();
|
|
144
|
+
await pi.handlers.session_start({}, ctx);
|
|
145
|
+
const widgetCall = ctx.ui.setWidget.mock.calls.find((c) => c[0] === "astro-footer-last-prompt");
|
|
146
|
+
const factory = widgetCall![1] as (
|
|
147
|
+
tui: unknown,
|
|
148
|
+
theme: unknown,
|
|
149
|
+
) => { render: (w: number) => string[] };
|
|
150
|
+
const lines = factory({}, fakeTheme).render(120);
|
|
151
|
+
expect(lines).toEqual([]);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("last-prompt widget renders the last submitted prompt with arrow prefix", async () => {
|
|
155
|
+
const { pi } = await install();
|
|
156
|
+
const ctx = makeCtx();
|
|
157
|
+
await pi.handlers.session_start({}, ctx);
|
|
158
|
+
await pi.handlers.before_agent_start({ prompt: "fix the auth middleware token expiry check" }, ctx);
|
|
159
|
+
const widgetCall = ctx.ui.setWidget.mock.calls.find((c) => c[0] === "astro-footer-last-prompt");
|
|
160
|
+
const factory = widgetCall![1] as (
|
|
161
|
+
tui: unknown,
|
|
162
|
+
theme: unknown,
|
|
163
|
+
) => { render: (w: number) => string[] };
|
|
164
|
+
const lines = factory({}, fakeTheme).render(120);
|
|
165
|
+
expect(lines).toHaveLength(1);
|
|
166
|
+
expect(lines[0]).toContain("↳");
|
|
167
|
+
expect(lines[0]).toContain("fix the auth middleware");
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("/footer off clears the last-prompt widget too", async () => {
|
|
171
|
+
const { pi } = await install();
|
|
172
|
+
const ctx = makeCtx();
|
|
173
|
+
await pi.commands.get("footer")!.handler("off", ctx);
|
|
174
|
+
const widgetCalls = ctx.ui.setWidget.mock.calls;
|
|
175
|
+
const cleared = widgetCalls.find((c) => c[0] === "astro-footer-last-prompt" && c[1] === undefined);
|
|
176
|
+
expect(cleared).toBeDefined();
|
|
126
177
|
});
|
|
127
178
|
|
|
128
179
|
it("session_start activates the footer via ctx.ui.setFooter", async () => {
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
ReadonlyFooterDataProvider,
|
|
5
5
|
} from "@mariozechner/pi-coding-agent";
|
|
6
6
|
import type { AssistantMessage } from "@mariozechner/pi-ai";
|
|
7
|
+
import type { Theme } from "@mariozechner/pi-coding-agent";
|
|
7
8
|
import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
|
|
8
9
|
import { resolvePathMode, type PathMode } from "./format.ts";
|
|
9
10
|
import { GitStatusTracker } from "./git-status.ts";
|
|
@@ -27,6 +28,17 @@ import {
|
|
|
27
28
|
} from "./segments.ts";
|
|
28
29
|
|
|
29
30
|
const SUBAGENT_STATUS_KEY = "subagents";
|
|
31
|
+
const LAST_PROMPT_WIDGET_KEY = "astro-footer-last-prompt";
|
|
32
|
+
|
|
33
|
+
function renderLastPromptLine(theme: Theme, lastPrompt: string, width: number): string[] {
|
|
34
|
+
const prefix = ` ${theme.fg("dim", "↳")} `;
|
|
35
|
+
const available = width - visibleWidth(prefix);
|
|
36
|
+
if (available < 10) return [];
|
|
37
|
+
const collapsed = lastPrompt.replace(/\s+/g, " ").trim();
|
|
38
|
+
if (collapsed.length === 0) return [];
|
|
39
|
+
const truncated = truncateToWidth(collapsed, available, "…");
|
|
40
|
+
return [truncateToWidth(`${prefix}${theme.fg("dim", truncated)}`, width, "…")];
|
|
41
|
+
}
|
|
30
42
|
|
|
31
43
|
interface UsageTotals {
|
|
32
44
|
input: number;
|
|
@@ -162,6 +174,7 @@ export default function astroFooterExtension(pi: ExtensionAPI): void {
|
|
|
162
174
|
let autoCompact = true;
|
|
163
175
|
let activeRequestRender: (() => void) | null = null;
|
|
164
176
|
let timeTickHandle: ReturnType<typeof setInterval> | null = null;
|
|
177
|
+
let lastPrompt = "";
|
|
165
178
|
|
|
166
179
|
const gitTracker = new GitStatusTracker(() => {
|
|
167
180
|
activeRequestRender?.();
|
|
@@ -184,6 +197,17 @@ export default function astroFooterExtension(pi: ExtensionAPI): void {
|
|
|
184
197
|
sessionStartedAt = Date.now();
|
|
185
198
|
autoCompact = isAutoCompactEnabled(ctx.cwd);
|
|
186
199
|
gitTracker.refresh(ctx.cwd, true);
|
|
200
|
+
ctx.ui.setWidget(
|
|
201
|
+
LAST_PROMPT_WIDGET_KEY,
|
|
202
|
+
(_tui, theme) => ({
|
|
203
|
+
invalidate() {},
|
|
204
|
+
render(width: number): string[] {
|
|
205
|
+
if (!lastPrompt) return [];
|
|
206
|
+
return renderLastPromptLine(theme, lastPrompt, width);
|
|
207
|
+
},
|
|
208
|
+
}),
|
|
209
|
+
{ placement: "belowEditor" },
|
|
210
|
+
);
|
|
187
211
|
ctx.ui.setFooter((tui, theme, footerData) => {
|
|
188
212
|
activeRequestRender = () => tui.requestRender();
|
|
189
213
|
startTimeTicker();
|
|
@@ -215,9 +239,15 @@ export default function astroFooterExtension(pi: ExtensionAPI): void {
|
|
|
215
239
|
}
|
|
216
240
|
|
|
217
241
|
pi.on("session_start", async (_event, ctx) => {
|
|
242
|
+
lastPrompt = "";
|
|
218
243
|
if (enabled) activate(ctx);
|
|
219
244
|
});
|
|
220
245
|
|
|
246
|
+
pi.on("before_agent_start", async (event) => {
|
|
247
|
+
lastPrompt = event.prompt ?? "";
|
|
248
|
+
activeRequestRender?.();
|
|
249
|
+
});
|
|
250
|
+
|
|
221
251
|
pi.on("tool_result", async (event, ctx) => {
|
|
222
252
|
if (!enabled) return;
|
|
223
253
|
if (event.toolName === "edit" || event.toolName === "write" || event.toolName === "bash") {
|
|
@@ -256,6 +286,7 @@ export default function astroFooterExtension(pi: ExtensionAPI): void {
|
|
|
256
286
|
enabled = false;
|
|
257
287
|
stopTimeTicker();
|
|
258
288
|
ctx.ui.setFooter(undefined);
|
|
289
|
+
ctx.ui.setWidget(LAST_PROMPT_WIDGET_KEY, undefined);
|
|
259
290
|
ctx.ui.notify("astro-footer: off (default footer restored)", "info");
|
|
260
291
|
return;
|
|
261
292
|
}
|