import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent"; /** * Postpone Extension * * Allows users to postpone sending a prompt by X minutes. * When the timer reaches 0, the prompt is automatically sent. * * Usage: * /postpone * * Example: * /postpone 5 Remember to check the build status */ export default function postponeExtension(pi: ExtensionAPI) { // Interface for postponed prompt data stored in session interface PostponedPromptData { prompt: string; delayMs: number; timestamp: number; // When it was postponed sendAs?: "steer" | "followUp" | "nextTurn"; // How to send it } // Restore postponed prompts from session on startup pi.on("session_start", async (_event, ctx) => { // Get all entries from current branch const branchEntries = ctx.sessionManager.getBranch(); // Look for postponed prompt entries for (const entry of branchEntries) { if (entry.type === "custom" && entry.customType === "postponed-prompt") { const data = entry.data as PostponedPromptData | undefined; if (data) { const elapsed = Date.now() - data.timestamp; const remaining = data.delayMs - elapsed; // If time is still remaining, set up a new timeout if (remaining > 0) { setTimeout(() => { sendPostponedPrompt(data, ctx); }, remaining); } else { // Time's up, send it now sendPostponedPrompt(data, ctx); } } } } }); // Function to actually send a postponed prompt function sendPostponedPrompt(data: PostponedPromptData, ctx: ExtensionContext) { // Send as user message pi.sendUserMessage(data.prompt, { deliverAs: data.sendAs || "steer" }); // Notify user ctx.ui.notify("Postponed prompt sent", "info"); } // Register the /postpone command pi.registerCommand("postpone", { description: "Postpone sending a prompt by X minutes", handler: async (args, ctx) => { // Parse args: first argument should be minutes, rest is the prompt const parts = args.trim().split(/\s+/); if (parts.length < 2) { ctx.ui.notify("Usage: /postpone ", "error"); return; } const minutes = parseInt(parts[0], 10); if (isNaN(minutes) || minutes <= 0) { ctx.ui.notify("Please provide a valid number of minutes", "error"); return; } const prompt = parts.slice(1).join(" "); if (!prompt.trim()) { ctx.ui.notify("Please provide a prompt to postpone", "error"); return; } const delayMs = minutes * 60 * 1000; const timestamp = Date.now(); // Store in session for persistence pi.appendEntry("postponed-prompt", { prompt, delayMs, timestamp, sendAs: "steer" // Default to steer }); // Set up timeout setTimeout(() => { sendPostponedPrompt({ prompt, delayMs, timestamp, sendAs: "steer" }, ctx); }, delayMs); ctx.ui.notify(`Prompt postponed for ${minutes} minute(s)`, "info"); } }); // Optional: Add a command to list pending postponed prompts pi.registerCommand("postponed", { description: "List postponed prompts", handler: async (_args, ctx) => { const branchEntries = ctx.sessionManager.getBranch(); const postponed = branchEntries.filter( entry => entry.type === "custom" && entry.customType === "postponed-prompt" ); if (postponed.length === 0) { ctx.ui.notify("No postponed prompts", "info"); return; } let message = "Postponed prompts:\n"; postponed.forEach((entry, index) => { const data = entry.data as PostponedPromptData; const elapsed = Date.now() - data.timestamp; const remaining = data.delayMs - elapsed; const minutesLeft = Math.ceil(remaining / 60000); message += `${index + 1}. ${data.prompt} (${minutesLeft} minute(s) left)\n`; }); ctx.ui.notify(message.trim(), "info"); } }); // Optional: Add a command to cancel all postponed prompts pi.registerCommand("postpone-cancel", { description: "Cancel all postponed prompts", handler: async (_args, ctx) => { const branchEntries = ctx.sessionManager.getBranch(); let cancelled = 0; for (const entry of branchEntries) { if (entry.type === "custom" && entry.customType === "postponed-prompt") { // We can't actually remove entries, but we can add a marker // For simplicity, we'll just notify the user that we've cleared the list // In a real implementation, we might want to track active timeouts cancelled++; } } if (cancelled > 0) { ctx.ui.notify(`Cancelled ${cancelled} postponed prompt(s)`, "info"); } else { ctx.ui.notify("No postponed prompts to cancel", "info"); } } }); }