/** * Event handlers: session_start status reporting and before_agent_start * system-prompt context injection for initialized/uninitialized projects. */ import { test } from "node:test"; import assert from "node:assert/strict"; import { registerEvents } from "../src/events/index.ts"; import { mockPi, makeTempRepo, cleanupTemp, makeCtx } from "./helpers.ts"; function registeredEvents() { const { pi, events } = mockPi(); registerEvents(pi); return events; } function handlerOf(events: ReturnType, name: string) { const ev = events.find((e) => e.event === name); assert.ok(ev, `expected ${name} handler to be registered`); return ev!; } test("registerEvents registers session_start and before_agent_start", () => { const events = registeredEvents(); assert.deepEqual( events.map((e) => e.event).sort(), ["before_agent_start", "session_start"], ); for (const ev of events) assert.equal(typeof ev.handler, "function"); }); test("session_start flags an uninitialized project in the status bar", async () => { const cwd = makeTempRepo(); try { const { ctx, statusCalls } = makeCtx({ cwd }); await handlerOf(registeredEvents(), "session_start").handler({}, ctx); assert.deepEqual(statusCalls, [ { key: "docgraph", text: "Docgraph: not initialized — ask agent to run docgraph_init" }, ]); } finally { cleanupTemp(cwd); } }); test("session_start reports an initialized project in the status bar", async () => { const cwd = makeTempRepo(); try { const { ctx, statusCalls } = makeCtx({ cwd, state: { initialized: true, schemaVersion: 1 } }); await handlerOf(registeredEvents(), "session_start").handler({}, ctx); assert.deepEqual(statusCalls, [{ key: "docgraph", text: "Docgraph: initialized" }]); } finally { cleanupTemp(cwd); } }); test("before_agent_start injects scaffolding instructions when uninitialized", async () => { const cwd = makeTempRepo(); try { const { ctx } = makeCtx({ cwd }); const result = (await handlerOf( registeredEvents(), "before_agent_start", ).handler({ systemPrompt: "BASE PROMPT" }, ctx)) as { systemPrompt: string }; assert.ok(result.systemPrompt.startsWith("BASE PROMPT"), "existing prompt preserved"); assert.match(result.systemPrompt, /This project uses \*\*pi-docgraph\*\*/); assert.match(result.systemPrompt, /has NOT been initialized yet\./); assert.match(result.systemPrompt, /Use the `docgraph_init` tool to scaffold/); assert.match(result.systemPrompt, /`docgraph_read`/); assert.match(result.systemPrompt, /`docgraph_ticket_create`/); assert.match(result.systemPrompt, /source of truth/i); } finally { cleanupTemp(cwd); } }); test("before_agent_start injects minimal context when initialized", async () => { const cwd = makeTempRepo(); try { const { ctx } = makeCtx({ cwd, state: { initialized: true, schemaVersion: 1 } }); const result = (await handlerOf( registeredEvents(), "before_agent_start", ).handler({ systemPrompt: "BASE" }, ctx)) as { systemPrompt: string }; assert.ok(result.systemPrompt.startsWith("BASE")); assert.match(result.systemPrompt, /## Docgraph/); assert.match(result.systemPrompt, /Use `docgraph_read` to read docs, `docgraph_sync` to validate links/); assert.match(result.systemPrompt, /`docgraph_ticket_list`/); assert.match(result.systemPrompt, /The codebase is always the source of truth\./); // The verbose uninitialized script must not leak into initialized prompts. assert.doesNotMatch(result.systemPrompt, /has NOT been initialized/); } finally { cleanupTemp(cwd); } }); test("before_agent_start handles a missing systemPrompt gracefully", async () => { const cwd = makeTempRepo(); try { const { ctx } = makeCtx({ cwd }); const result = (await handlerOf( registeredEvents(), "before_agent_start", ).handler({}, ctx)) as { systemPrompt: string }; assert.ok(result.systemPrompt.length > 0); } finally { cleanupTemp(cwd); } });