Add new extensions and updates to the Dev sandbox

This commit is contained in:
Alex Selimov 2026-05-26 09:51:57 -04:00
parent 5f053ec81d
commit 19f3c6a050
5 changed files with 211 additions and 1 deletions

View file

@ -32,3 +32,10 @@ Additional notes:
- If I send the outputs of failing commands, you should provide recommendations instead of addressing configuration/environment issues yourself.
- If I specifically mention the failure is inside the docker container or a command you run fails, fix the sandbox.
## Scope
Limit your scope to the task at hand. Do touch unrelated code or attempt to fix existing issues.
If you see something just raise it if it is confusing.
Examples:
- prompt: Write a test for fun1() -> Only write the test for fun1(), don't fix any bugs/issues

View file

@ -0,0 +1,40 @@
import type { ExtensionAPI, SessionEntry } from "@earendil-works/pi-coding-agent";
const QUICK_MARKER_TYPE = "quick-question-session";
type QuickSessionMarker = {
returnSession: string;
};
function getQuickSessionMarker(entries: SessionEntry[]): QuickSessionMarker | undefined {
for (let i = entries.length - 1; i >= 0; i--) {
const entry = entries[i];
if (entry.type !== "custom" || entry.customType !== QUICK_MARKER_TYPE) continue;
const data = entry.data as Partial<QuickSessionMarker> | undefined;
if (typeof data?.returnSession === "string") {
return { returnSession: data.returnSession };
}
}
return undefined;
}
export default function (pi: ExtensionAPI) {
pi.on("input", async (event, ctx) => {
if (event.source === "extension") return { action: "continue" };
if (event.text.trim() !== "exit") return { action: "continue" };
const marker = getQuickSessionMarker(ctx.sessionManager.getBranch());
if (marker) {
(ctx.sessionManager as unknown as { setSessionFile(path: string): void }).setSessionFile(marker.returnSession);
ctx.ui.setStatus("quick-question", undefined);
ctx.ui.setWidget("quick-question", undefined);
ctx.ui.notify("Returned from quick session.", "info");
return { action: "handled" };
}
ctx.shutdown();
return { action: "handled" };
});
}

View file

@ -0,0 +1,143 @@
import type { ExtensionAPI, ExtensionCommandContext, SessionEntry } from "@earendil-works/pi-coding-agent";
const MARKER_TYPE = "quick-question-session";
type QuickSessionMarker = {
returnSession: string;
createdAt: string;
};
function getQuickSessionMarker(entries: SessionEntry[]): QuickSessionMarker | undefined {
for (let i = entries.length - 1; i >= 0; i--) {
const entry = entries[i];
if (entry.type !== "custom" || entry.customType !== MARKER_TYPE) continue;
const data = entry.data as Partial<QuickSessionMarker> | undefined;
if (typeof data?.returnSession === "string") {
return {
returnSession: data.returnSession,
createdAt: typeof data.createdAt === "string" ? data.createdAt : "",
};
}
}
return undefined;
}
export default function (pi: ExtensionAPI) {
pi.on("input", async (event, ctx) => {
if (event.source === "extension") return { action: "continue" };
const aliases: Record<string, string> = {
"/quick-close": "/quickclose",
"/quick-done": "/quickdone",
"/quick-back": "/quickback",
};
const replacement = aliases[event.text.trim()];
if (!replacement) return { action: "continue" };
const marker = getQuickSessionMarker(ctx.sessionManager.getBranch());
if (!marker) {
ctx.ui.notify("This is not a quick question session.", "error");
return { action: "handled" };
}
(ctx.sessionManager as unknown as { setSessionFile(path: string): void }).setSessionFile(marker.returnSession);
ctx.ui.setStatus("quick-question", undefined);
ctx.ui.setWidget("quick-question", undefined);
ctx.ui.notify("Returned from quick session.", "info");
return { action: "handled" };
});
pi.on("session_start", async (_event, ctx) => {
const marker = getQuickSessionMarker(ctx.sessionManager.getBranch());
if (!marker) {
ctx.ui.setStatus("quick-question", undefined);
ctx.ui.setWidget("quick-question", undefined);
return;
}
ctx.ui.setStatus("quick-question", ctx.ui.theme.fg("accent", "quick"));
ctx.ui.setWidget("quick-question", [
ctx.ui.theme.fg(
"dim",
"Quick question session. Use /quickclose, /quickdone, or /quickback to return to the original session.",
),
]);
});
pi.registerCommand("quick", {
description: "Start a temporary one-off question session; pass a question to ask immediately",
handler: async (args, ctx) => {
if (!ctx.hasUI) {
ctx.ui.notify("quick requires interactive mode", "error");
return;
}
await ctx.waitForIdle();
const returnSession = ctx.sessionManager.getSessionFile();
if (!returnSession) {
ctx.ui.notify("Current session is ephemeral; cannot return to it later.", "error");
return;
}
const question = args.trim();
const result = await ctx.newSession({
parentSession: returnSession,
setup: async (session) => {
session.appendCustomEntry(MARKER_TYPE, {
returnSession,
createdAt: new Date().toISOString(),
} satisfies QuickSessionMarker);
session.appendSessionInfo("Quick question");
},
withSession: async (replacementCtx) => {
replacementCtx.ui.notify(
"Quick session started. Use /quickclose, /quickdone, or /quickback to return.",
"info",
);
if (question) {
await replacementCtx.sendUserMessage(question);
} else {
replacementCtx.ui.setEditorText("");
}
},
});
if (result.cancelled) {
ctx.ui.notify("Quick session cancelled", "info");
}
},
});
async function closeQuickSession(ctx: ExtensionCommandContext) {
await ctx.waitForIdle();
const marker = getQuickSessionMarker(ctx.sessionManager.getBranch());
if (!marker) {
ctx.ui.notify("This is not a quick question session.", "error");
return;
}
const result = await ctx.switchSession(marker.returnSession, {
withSession: async (replacementCtx) => {
replacementCtx.ui.notify("Returned from quick session.", "info");
},
});
if (result.cancelled) {
ctx.ui.notify("Return to original session cancelled", "info");
}
}
for (const name of ["quickclose", "quickdone", "quickback", "quick-close", "quick-done", "quick-back"] as const) {
pi.registerCommand(name, {
description: "Close the quick question session and return to the original session",
handler: async (_args, ctx) => closeQuickSession(ctx),
});
}
}