feat(pi): record session-start make test in test-balls via event bus
Emit test-balls:record on the shared pi.events bus after the session-start make test run completes, so test-balls shows a ball for it like any other run. The run is now fire-and-forget: session start no longer blocks on make test, and the emit always lands after every extension's session_start handler has run (project extensions load before global ones, so emitting synchronously raced test-balls' runs reset). Exactly one ball is recorded per run, even if UI reporting fails.
This commit is contained in:
@@ -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<void> {
|
||||
// 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" });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user