Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

extensions/prompt-autopsy/__tests__/harness.test.ts

Raw
import {
	lstatSync,
	mkdtempSync,
	readdirSync,
	readFileSync,
	rmSync,
} from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { createTestSession, type TestSession } from "../../../test/harness";
import { groupSystemPrompt } from "../index.ts";

describe("prompt-autopsy pi-test-harness", () => {
	let t: TestSession | undefined;

	afterEach(() => {
		t?.dispose();
		t = undefined;
	});

	it("groups Pi 0.86 top-level prompt sections without mistaking nested XML", () => {
		const grouped = groupSystemPrompt(`Custom identity

<tools>
- read: Read files
</tools>

<rules>
- Be concise
</rules>

<general_guidelines>
General guidance
</general_guidelines>

<tool_guidelines>
<tool name="read">
Read carefully
</tool>
</tool_guidelines>

<project_context>
Project-specific instructions and guidelines:

<project_instructions path="/repo/AGENTS.md">
PROJECT RULE
</project_instructions>
</project_context>

<skills>
<available_skills>
</available_skills>
</skills>

<cwd>
/repo
</cwd>

<custom_context>
CUSTOM
</custom_context>`);

		expect(grouped).toContain("[PREAMBLE]\nCustom identity");
		expect(grouped).toContain("[TOOLS]\n<tools>");
		expect(grouped).toContain("[RULES]\n<rules>");
		expect(grouped).toContain("[GENERAL GUIDELINES]\n<general_guidelines>");
		expect(grouped).toContain("[TOOL GUIDELINES]\n<tool_guidelines>");
		expect(grouped).toContain("[CTX]\n<project_context>");
		expect(grouped).toContain("[SKILLS]\n<skills>");
		expect(grouped).toContain("[CWD]\n<cwd>");
		expect(grouped).toContain("[SECTION custom_context]\n<custom_context>");
		expect(grouped).not.toContain("[SECTION project_instructions]");
	});

	it("keeps nested same-name sections intact", () => {
		const grouped = groupSystemPrompt(`<addendum>
outer
<addendum>
inner
</addendum>
outer tail
</addendum>

<cwd>
/repo
</cwd>`);

		expect(grouped).toBe(`[APPEND]
<addendum>
outer
<addendum>
inner
</addendum>
outer tail
</addendum>

[CWD]
<cwd>
/repo
</cwd>`);
	});

	it("writes a private output file and opens it", async () => {
		const extensionPath = path.resolve(import.meta.dirname, "../index.ts");
		t = await createTestSession({
			extensions: [extensionPath],
			env: { VISUAL: `${process.execPath} -e "process.exit(0)"` },
		});

		await t.session.extensionRunner.emitBeforeAgentStart("prompt", undefined, {
			cwd: t.cwd,
			forceSystemPrompt: "<tools>\n- read\n</tools>",
		});
		await t.session.extensionRunner.emit({ type: "agent_start" } as never);

		const command =
			t.session.extensionRunner.extensions[0].commands.get("prompt-autopsy");
		await command.handler("", t.session.extensionRunner.createCommandContext());

		const notification = t.events.uiCallsFor("notify").at(-1)?.args;
		expect(notification?.[1]).toBe("info");
		const outputPath = String(notification?.[0]).replace(/^wrote /, "");
		expect(outputPath).not.toBe("/tmp/sysprompt-autopsy.md");
		expect(lstatSync(path.dirname(outputPath)).mode & 0o777).toBe(0o700);
		expect(lstatSync(outputPath).mode & 0o777).toBe(0o600);
		expect(readFileSync(outputPath, "utf8")).toContain("[TOOLS]");
		rmSync(path.dirname(outputPath), { recursive: true, force: true });
	});

	it("writes its opt-in lifecycle trace inside the harness sandbox", async () => {
		const root = mkdtempSync(
			path.join(tmpdir(), "pi-ext-prompt-autopsy-debug-"),
		);
		try {
			const extensionPath = path.resolve(import.meta.dirname, "../index.ts");
			t = await createTestSession({
				cwd: root,
				extensions: [extensionPath],
				env: { PI_PROMPT_AUTOPSY_DEBUG: "1" },
			});
			const runner = t.session.extensionRunner;
			await runner.emit({ type: "session_start", reason: "startup" });
			await runner.extensions[0].commands
				.get("prompt-autopsy")
				?.handler("", runner.createCommandContext());
			await runner.emit({ type: "session_shutdown", reason: "quit" });
			const stateHome = t.env.XDG_STATE_HOME;
			if (!stateHome) throw new Error("missing sandbox state home");
			const directory = path.join(
				stateHome,
				"pi-ext",
				"debug",
				"prompt-autopsy",
			);
			const [file] = readdirSync(directory);
			const events = readFileSync(path.join(directory, file), "utf8")
				.trim()
				.split("\n")
				.map((line) => JSON.parse(line).event);
			expect(events).toEqual(
				expect.arrayContaining([
					"session.start",
					"session.shutdown",
					"prompt.skip",
				]),
			);
		} finally {
			t?.dispose();
			t = undefined;
			rmSync(root, { recursive: true, force: true });
		}
	});

	it("loads in real Pi extension runtime", async () => {
		const extensionPath = path.resolve(import.meta.dirname, "../index.ts");
		t = await createTestSession({ extensions: [extensionPath] });

		expect(t.session.extensionRunner.extensions).toHaveLength(1);
		expect(t.session.extensionRunner.extensions[0].path).toBe(extensionPath);

		const command =
			t.session.extensionRunner.extensions[0].commands.get("prompt-autopsy");
		await command.handler("", t.session.extensionRunner.createCommandContext());
		expect(t.events.uiCallsFor("notify").at(-1)?.args).toEqual([
			"No provider-effective prompt captured since session start or reload. Send a prompt, then retry.",
			"warning",
		]);
	});
});