import { existsSync, readFileSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import type { ExtensionAPI, ExtensionContext, } from "@earendil-works/pi-coding-agent"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const PHRASES_PATH = resolve(__dirname, "PHRASES.md"); function loadPhrases(): string[] { if (!existsSync(PHRASES_PATH)) return ["Working..."]; const raw = readFileSync(PHRASES_PATH, "utf-8"); return raw .split("\n") .map((l) => l.trim()) .filter((l) => l.length > 0 && !l.startsWith("#")); } function pickPhrase(phrases: string[]): string { return phrases[Math.floor(Math.random() * phrases.length)]!; } const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; export default function (pi: ExtensionAPI) { let phrases = loadPhrases(); let interval: ReturnType | null = null; function clearTimer() { if (interval !== null) { clearInterval(interval); interval = null; } } function rotateMessage(ctx: ExtensionContext) { ctx.ui.setWorkingMessage(pickPhrase(phrases)); } pi.on("session_start", async (_event, ctx) => { phrases = loadPhrases(); ctx.ui.setWorkingIndicator({ frames: [...SPINNER], intervalMs: 80, }); ctx.ui.notify(`Loaded ${phrases.length} phrases`, "info"); }); pi.on("agent_start", async (_event, ctx) => { clearTimer(); rotateMessage(ctx); interval = setInterval(() => rotateMessage(ctx), 3_000); }); pi.on("agent_end", async (_event, ctx) => { clearTimer(); }); pi.on("session_shutdown", async () => { clearTimer(); }); pi.registerCommand("phrases", { description: "Reload phrases from PHRASES.md or list them", handler: async (args, ctx) => { if (args.trim() === "reload") { phrases = loadPhrases(); ctx.ui.notify(`Reloaded ${phrases.length} phrases`, "info"); return; } if (args.trim() === "list") { const samples = phrases .sort(() => Math.random() - 0.5) .slice(0, 8) .join("\n"); ctx.ui.setEditorText(`${phrases.length} phrases loaded:\n\n${samples}`); return; } ctx.ui.notify("Usage: /phrases [reload|list]", "info"); }, }); }