Files
dotfiles/pi/.pi/agent/extensions/postpone.ts
2026-03-29 10:01:22 +02:00

157 lines
4.6 KiB
TypeScript

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 <minutes> <prompt>
*
* 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 <minutes> <prompt>", "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<PostponedPromptData>("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");
}
}
});
}