diff --git a/.pi/extensions/run_make_test.ts b/.pi/extensions/run_make_test.ts index 7983ba6..ee0cdf9 100644 --- a/.pi/extensions/run_make_test.ts +++ b/.pi/extensions/run_make_test.ts @@ -1,37 +1,57 @@ -import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; +import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent"; export default function (pi: ExtensionAPI) { pi.on("session_start", async (_event, ctx) => { - try { - const { stdout, stderr, code } = await pi.exec("make", ["test"]); - const output = stdout + stderr; - - if (code === 0) { - ctx.ui.notify("make test passed", "info"); - } else { - ctx.ui.notify(`make test failed (exit ${code})`, "error"); - } - - // Also send the output as a message so it's visible in the session - pi.sendMessage({ - customType: "run-make-test", - content: [ - { - type: "text", - text: `Here's the result of running \`make test\`:\n\n\`\`\`\n${output}\`\`\``, - }, - ], - display: true, - }, { deliverAs: "followUp" }); - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - pi.sendMessage({ - customType: "run-make-test", - content: [ - { type: "text", text: `Error running make test: ${message}` }, - ], - display: true, - }, { deliverAs: "followUp" }); - } + // Fire-and-forget: do not block session start on the test run, and let the + // "test-balls:record" event land after every extension's session_start handler + // has run (project extensions load before global ones, so emitting synchronously + // from this handler would race test-balls' runs reset). + void runMakeTest(pi, ctx).catch((error) => { + console.error("run_make_test:", error); + }); }); } + +async function runMakeTest(pi: ExtensionAPI, ctx: ExtensionContext): Promise { + // Record exactly one ball per run, even if UI reporting below fails. + let recorded = false; + const record = (pass: boolean) => { + if (recorded) return; + recorded = true; + pi.events.emit("test-balls:record", { pass, command: "make test", ts: Date.now() }); + }; + + try { + const { stdout, stderr, code } = await pi.exec("make", ["test"]); + record(code === 0); + const output = stdout + stderr; + + if (code === 0) { + ctx.ui.notify("make test passed", "info"); + } else { + ctx.ui.notify(`make test failed (exit ${code})`, "error"); + } + + // Also send the output as a message so it's visible in the session + pi.sendMessage({ + customType: "run-make-test", + content: [ + { + type: "text", + text: `Here's the result of running \`make test\`:\n\n\`\`\`\n${output}\`\`\``, + }, + ], + display: true, + }, { deliverAs: "followUp" }); + } catch (error) { + record(false); + const message = error instanceof Error ? error.message : String(error); + pi.sendMessage({ + customType: "run-make-test", + content: [ + { type: "text", text: `Error running make test: ${message}` }, + ], + display: true, + }, { deliverAs: "followUp" }); + } +}