import assert from "node:assert/strict"; import { afterEach, describe, it } from "node:test"; import type { ExtensionContext } from "@earendil-works/pi-coding-agent"; import { getConfig, shouldNotify } from "../src/config.ts"; const envKeys = [ "PI_NOTIFY_ENABLED", "PI_NOTIFY_TITLE", "PI_NOTIFY_BODY", "PI_NOTIFY_MESSAGE_SOURCE", "PI_NOTIFY_MESSAGE_MAX", "PI_NOTIFY_MIN_SECONDS", "PI_NOTIFY_VISIBILITY", "PI_NOTIFY_ACTION", "PI_NOTIFY_SOUND_CMD", ] as const; const savedEnv = new Map(envKeys.map((key) => [key, process.env[key]])); afterEach(() => { for (const key of envKeys) { const value = savedEnv.get(key); if (value === undefined) delete process.env[key]; else process.env[key] = value; } }); function context(): ExtensionContext { return { cwd: "/tmp/pi-notify", model: { provider: "openai", id: "gpt-4.1" }, } as ExtensionContext; } const runtime = { notificationId: "pi-session", toolErrorCount: 0 }; describe("config", () => { it("builds safe Kitty defaults from the settled assistant output", () => { const config = getConfig( context(), 800, [ { role: "user", content: "please fix this" }, { role: "assistant", content: "fixed it" }, ], runtime, 5_000, ); assert.deepEqual(config, { enabled: true, title: "π - pi-notify", body: "fixed it", durationMs: 4_200, minDurationMs: 3_000, notificationId: "pi-session", visibility: "unfocused", action: "focus", soundCommand: undefined, }); assert.equal(shouldNotify(config), true); }); it("supports privacy, threshold, focus, and status template settings", () => { process.env.PI_NOTIFY_ENABLED = "off"; process.env.PI_NOTIFY_TITLE = "{status_icon} {status} {tool_error_count}"; process.env.PI_NOTIFY_BODY = "{message}"; process.env.PI_NOTIFY_MESSAGE_SOURCE = "none"; process.env.PI_NOTIFY_MIN_SECONDS = "10.5"; process.env.PI_NOTIFY_VISIBILITY = "invisible"; process.env.PI_NOTIFY_ACTION = "none"; process.env.PI_NOTIFY_SOUND_CMD = " say done "; const config = getConfig(context(), 1_000, [{ role: "assistant", content: "secret" }], { notificationId: "pi-session", toolErrorCount: 2, }, 2_000); assert.equal(config.title, "⚠ ready · 2 tool errors 2"); assert.equal(config.body, "Ready for input"); assert.equal(config.enabled, false); assert.equal(config.minDurationMs, 10_500); assert.equal(config.visibility, "invisible"); assert.equal(config.action, "none"); assert.equal(config.soundCommand, "say done"); assert.equal(shouldNotify(config), false); assert.equal(shouldNotify(config, true), false, "the minimum duration still applies to command overrides"); }); it("applies selected-message truncation and a session override", () => { process.env.PI_NOTIFY_MESSAGE_SOURCE = "user"; process.env.PI_NOTIFY_MESSAGE_MAX = "5"; process.env.PI_NOTIFY_MIN_SECONDS = "0"; const config = getConfig( context(), 1_000, [{ role: "user", content: "hello world" }], runtime, 2_000, ); assert.equal(config.body, "hell…"); assert.equal(shouldNotify(config, false), false); assert.equal(shouldNotify(config, true), true); }); });