Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

extensions/strata/index.ts

Raw
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import type { Strata } from "./implementation.js";
import { closeDebug, dbg, span } from "./src/debug.ts";
import { findExecutable } from "./src/pi-ext-executable.ts";

export default function strata(pi: ExtensionAPI) {
	let runtime: Strata | undefined;
	let generation = 0;
	const openings = new Set<Promise<unknown>>();
	// Shared so repeated invocations evaluate ./implementation.js once.
	// Pi's jiti loader (module cache disabled) can otherwise hand one caller a
	// partially initialized module whose imports are still undefined.
	let implementationModule:
		| Promise<typeof import("./implementation.js")>
		| undefined;
	const loadImplementation = (): Promise<
		typeof import("./implementation.js")
	> => {
		implementationModule ??= import("./implementation.js");
		return implementationModule;
	};
	pi.on("session_start", (_event, ctx) => {
		dbg?.("session.start");
		const version = ++generation;
		void hasGit().then((available) => {
			if (!available && generation === version)
				ctx.ui.notify("Strata requires Git on PATH.", "warning");
		});
	});
	pi.on("session_shutdown", async () => {
		dbg?.("session.shutdown");
		generation++;
		const stopped = runtime?.dispose();
		runtime = undefined;
		await Promise.allSettled([...openings, stopped]);
		closeDebug();
	});
	pi.registerCommand("strata", {
		description: "Review Git changes as semantic layers in a local browser",
		getArgumentCompletions(prefix) {
			return ["--staged", "--base", "--pr", "cancel"]
				.filter((value) => value.startsWith(prefix))
				.map((value) => ({ value, label: value }));
		},
		async handler(args, ctx) {
			const finish = span?.("review.open");
			if (args.trim() === "cancel") {
				generation++;
				await runtime?.close();
				finish?.("finish", { outcome: "cancelled" });
				return;
			}
			try {
				const version = generation;
				const { Strata } = await loadImplementation();
				if (generation !== version) {
					finish?.("finish", { outcome: "superseded" });
					return;
				}
				runtime ??= new Strata(pi);
				const opening = runtime.open(args, ctx);
				openings.add(opening);
				let outcome: Awaited<typeof opening>;
				try {
					outcome = await opening;
				} finally {
					openings.delete(opening);
				}
				finish?.("finish", { outcome });
			} catch (error) {
				finish?.("error");
				throw error;
			}
		},
	});
}

async function hasGit(): Promise<boolean> {
	return Boolean(
		findExecutable(process.platform === "win32" ? ["git.exe"] : ["git"]),
	);
}