repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/prompt-autopsy/__tests__/harness.test.ts
Rawimport {
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",
]);
});
});