mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 08:35:57 +00:00
feat: add Kitty-aware completion notifications
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user