157 lines
4.6 KiB
TypeScript
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");
|
|
}
|
|
}
|
|
});
|
|
} |