import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import assert from "node:assert/strict"; import { mkdtemp, readFile, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import test from "node:test"; import toolRoutingExtension, { appendToolRouting, buildToolRoutingSection, getSystemPromptDumpPath, } from "../extensions/tool-routing.ts"; test("search routing narrows files before requesting matching line numbers", () => { const section = buildToolRoutingSection(["find", "grep", "read"]); assert.match(section, /Use find to identify likely files or directories/); assert.match(section, /Use grep or multi_grep only inside the narrowed path or file/); assert.match(section, /obtain matching line numbers/); assert.match(section, /narrow the path, glob, or pattern instead of increasing the limit/); assert.match(section, /Use read with offset\/limit only for the exact matching region/); }); test("ctx_execute_file routing warns about its project-root boundary", () => { const section = buildToolRoutingSection(["ctx_execute_file"]); assert.match(section, /ctx_execute_file is confined to the current project root/); assert.match(section, /host permission approval does not bypass this Context Mode boundary/); }); test("routing includes only guidance for active optional tools", () => { const section = buildToolRoutingSection(["read"]); assert.doesNotMatch(section, /codegraph_explore first/); assert.doesNotMatch(section, /For literal search/); assert.doesNotMatch(section, /Use Context Mode/); assert.doesNotMatch(section, /ctx_execute_file is confined/); assert.match(section, /Use read directly/); }); test("routing is appended once", () => { const once = appendToolRouting("base prompt", ["find", "grep"]); const twice = appendToolRouting(once, ["find", "grep"]); assert.equal(twice, once); assert.equal((once.match(//g) ?? []).length, 1); }); test("dump path stays under the current project", () => { assert.equal( getSystemPromptDumpPath("/workspace/project"), "/workspace/project/.pi-debug/effective-system-prompt.md", ); }); test("dump-system-prompt writes the last routed prompt seen by the extension", async () => { let beforeAgentStart: ((event: any) => { systemPrompt: string }) | undefined; let command: { handler: (args: string, ctx: any) => Promise } | undefined; const pi = { on(name: string, handler: typeof beforeAgentStart) { if (name === "before_agent_start") beforeAgentStart = handler; }, registerCommand(_name: string, definition: typeof command) { command = definition; }, } as unknown as ExtensionAPI; toolRoutingExtension(pi); assert.ok(beforeAgentStart); assert.ok(command); beforeAgentStart({ systemPrompt: "prompt already modified by an earlier extension", systemPromptOptions: { selectedTools: ["find", "grep"] }, }); const cwd = await mkdtemp(join(tmpdir(), "my-pi-tool-routing-")); try { let notification = ""; await command.handler("", { cwd, getSystemPrompt: () => "base prompt", getSystemPromptOptions: () => ({ selectedTools: ["find", "grep"] }), ui: { notify(message: string) { notification = message; }, }, }); const outputPath = getSystemPromptDumpPath(cwd); const dumped = await readFile(outputPath, "utf8"); assert.match(dumped, /^prompt already modified by an earlier extension/); assert.match(dumped, //); assert.match(notification, new RegExp(outputPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))); } finally { await rm(cwd, { recursive: true, force: true }); } });