import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { Box, Text } from "@earendil-works/pi-tui"; export const MESSAGE_TYPE = "machine-report"; export type ReportLevel = "info" | "success" | "warning" | "error"; /** * Mirror reports to stderr while there is no UI (pi -p / --mode json). * Custom messages only render in the TUI, so without this a failed * headless /good or /train command exits silently. */ let mirrorToStderr = false; export function setMachineReportMirror(enabled: boolean): void { mirrorToStderr = enabled; } /** * Register the shared renderer for machine reports and return a helper that * sends one. Both extensions use the same custom message type, so the * renderer implementation must stay identical across them. */ export function registerMachineReporter(pi: ExtensionAPI): (title: string, body: string, level?: ReportLevel) => void { pi.registerMessageRenderer(MESSAGE_TYPE, (message, _options, theme) => { const details = message.details as { level?: ReportLevel } | undefined; const level = details?.level ?? "info"; const color = level === "error" ? "error" : level === "warning" ? "warning" : level === "success" ? "success" : "accent"; const box = new Box(1, 1, (s: string) => theme.bg("customMessageBg", s)); box.addChild(new Text(theme.fg(color, String(message.content)), 0, 0)); return box; }); return (title: string, body: string, level: ReportLevel = "info") => { pi.sendMessage({ customType: MESSAGE_TYPE, content: `# ${title}\n\n${body}`, display: true, details: { level, timestamp: Date.now() }, }); if (mirrorToStderr) process.stderr.write(`# ${title}\n\n${body}\n\n`); }; }