killeros 2.1.30 → 2.1.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,135 +1,135 @@
1
- import { basename } from "node:path";
2
- import type { StopReason } from "@earendil-works/pi-ai";
3
- import {
4
- type ExtensionAPI,
5
- type ExtensionContext,
6
- } from "@earendil-works/pi-coding-agent";
7
- import { errorMessage } from "./errors.ts";
8
- import { safeTerminalText } from "./safe-terminal-text.ts";
9
- import { createKillerosSettingsStore } from "./settings.ts";
10
-
11
- export interface NotificationPreferenceStore {
12
- load(): boolean;
13
- save(enabled: boolean): void;
14
- }
15
-
16
- export interface CompletionNotificationDependencies {
17
- store: NotificationPreferenceStore;
18
- ring(): void;
19
- }
20
-
21
- export const COMPLETION_BELL_GLYPH = "󰂚";
22
-
23
- export function createNotificationPreferenceStore(
24
- settingsPath?: string,
25
- ): NotificationPreferenceStore {
26
- const settings = createKillerosSettingsStore(settingsPath);
27
- return {
28
- load: () => settings.load().completionSound === true,
29
- save: (enabled) => {
30
- settings.update({ completionSound: enabled });
31
- },
32
- };
33
- }
34
-
35
- export function formatNotificationTitle(
36
- cwd: string,
37
- sessionName: string | undefined,
38
- enabled: boolean,
39
- ): string {
40
- const directory = basename(cwd) || cwd;
41
- const base = sessionName ? `π - ${sessionName} - ${directory}` : `π - ${directory}`;
42
- return safeTerminalText(enabled ? `${base} ${COMPLETION_BELL_GLYPH}` : base).replaceAll("\n", "");
43
- }
44
-
45
- export function registerCompletionNotifications(
46
- pi: ExtensionAPI,
47
- dependencies?: CompletionNotificationDependencies,
48
- ): void {
49
- const runtime = dependencies ?? {
50
- store: createNotificationPreferenceStore(),
51
- ring: () => { process.stdout.write("\x07"); },
52
- };
53
- let enabled = false;
54
- let requestPending = false;
55
- let lastStopReason: StopReason | undefined;
56
-
57
- const applyTitle = (ctx: ExtensionContext): void => {
58
- if (ctx.mode !== "tui") return;
59
- ctx.ui.setTitle(formatNotificationTitle(ctx.cwd, pi.getSessionName(), enabled));
60
- };
61
-
62
- pi.registerCommand("notification", {
63
- description: "Configure the completion sound",
64
- handler: async (_args, ctx) => {
65
- if (ctx.mode !== "tui") {
66
- ctx.ui.notify("/notification requires TUI mode", "error");
67
- return;
68
- }
69
- const selected = await ctx.ui.select("Completion sound", [
70
- enabled ? "On ← current" : "On",
71
- enabled ? "Off" : "Off ← current",
72
- ]);
73
- if (!selected) return;
74
- const nextEnabled = selected.startsWith("On");
75
- try {
76
- runtime.store.save(nextEnabled);
77
- } catch (error) {
78
- ctx.ui.notify(`Completion sound setting could not be saved: ${errorMessage(error)}`, "error");
79
- return;
80
- }
81
- enabled = nextEnabled;
82
- applyTitle(ctx);
83
- ctx.ui.notify(`Completion sound: ${enabled ? "On" : "Off"}`, "info");
84
- },
85
- });
86
-
87
- pi.on("session_start", (_event, ctx) => {
88
- requestPending = false;
89
- lastStopReason = undefined;
90
- if (ctx.mode !== "tui") return;
91
- try {
92
- enabled = runtime.store.load();
93
- } catch (error) {
94
- enabled = false;
95
- ctx.ui.notify(`Completion sound settings could not be read: ${errorMessage(error)}`, "error");
96
- }
97
- applyTitle(ctx);
98
- });
99
-
100
- pi.on("session_info_changed", (_event, ctx) => applyTitle(ctx));
101
-
102
- pi.on("agent_start", (_event, ctx) => {
103
- if (ctx.mode !== "tui") return;
104
- requestPending = true;
105
- lastStopReason = undefined;
106
- });
107
-
108
- pi.on("agent_end", (event, ctx) => {
109
- if (ctx.mode !== "tui" || !requestPending) return;
110
- lastStopReason = undefined;
111
- for (let index = event.messages.length - 1; index >= 0; index -= 1) {
112
- const message = event.messages[index];
113
- if (message?.role !== "assistant") continue;
114
- lastStopReason = message.stopReason;
115
- break;
116
- }
117
- });
118
-
119
- pi.on("agent_settled", (_event, ctx) => {
120
- if (ctx.mode !== "tui" || !requestPending) return;
121
- if (!ctx.isIdle() || ctx.hasPendingMessages()) return;
122
- requestPending = false;
123
- if (!enabled || lastStopReason === "aborted") return;
124
- try {
125
- runtime.ring();
126
- } catch (error) {
127
- ctx.ui.notify(`Completion sound failed: ${errorMessage(error)}`, "error");
128
- }
129
- });
130
-
131
- pi.on("session_shutdown", () => {
132
- requestPending = false;
133
- lastStopReason = undefined;
134
- });
135
- }
1
+ import { basename } from "node:path";
2
+ import type { StopReason } from "@earendil-works/pi-ai";
3
+ import {
4
+ type ExtensionAPI,
5
+ type ExtensionContext,
6
+ } from "@earendil-works/pi-coding-agent";
7
+ import { errorMessage } from "./errors.ts";
8
+ import { safeTerminalText } from "./safe-terminal-text.ts";
9
+ import { createKillerosSettingsStore } from "./settings.ts";
10
+
11
+ export interface NotificationPreferenceStore {
12
+ load(): boolean;
13
+ save(enabled: boolean): void;
14
+ }
15
+
16
+ export interface CompletionNotificationDependencies {
17
+ store: NotificationPreferenceStore;
18
+ ring(): void;
19
+ }
20
+
21
+ export const COMPLETION_BELL_GLYPH = "󰂚";
22
+
23
+ export function createNotificationPreferenceStore(
24
+ settingsPath?: string,
25
+ ): NotificationPreferenceStore {
26
+ const settings = createKillerosSettingsStore(settingsPath);
27
+ return {
28
+ load: () => settings.load().completionSound === true,
29
+ save: (enabled) => {
30
+ settings.update({ completionSound: enabled });
31
+ },
32
+ };
33
+ }
34
+
35
+ export function formatNotificationTitle(
36
+ cwd: string,
37
+ sessionName: string | undefined,
38
+ enabled: boolean,
39
+ ): string {
40
+ const directory = basename(cwd) || cwd;
41
+ const base = sessionName ? `π - ${sessionName} - ${directory}` : `π - ${directory}`;
42
+ return safeTerminalText(enabled ? `${base} ${COMPLETION_BELL_GLYPH}` : base).replaceAll("\n", "");
43
+ }
44
+
45
+ export function registerCompletionNotifications(
46
+ pi: ExtensionAPI,
47
+ dependencies?: CompletionNotificationDependencies,
48
+ ): void {
49
+ const runtime = dependencies ?? {
50
+ store: createNotificationPreferenceStore(),
51
+ ring: () => { process.stdout.write("\x07"); },
52
+ };
53
+ let enabled = false;
54
+ let requestPending = false;
55
+ let lastStopReason: StopReason | undefined;
56
+
57
+ const applyTitle = (ctx: ExtensionContext): void => {
58
+ if (ctx.mode !== "tui") return;
59
+ ctx.ui.setTitle(formatNotificationTitle(ctx.cwd, pi.getSessionName(), enabled));
60
+ };
61
+
62
+ pi.registerCommand("notification", {
63
+ description: "Configure the completion sound",
64
+ handler: async (_args, ctx) => {
65
+ if (ctx.mode !== "tui") {
66
+ ctx.ui.notify("/notification requires TUI mode", "error");
67
+ return;
68
+ }
69
+ const selected = await ctx.ui.select("Completion sound", [
70
+ enabled ? "On ← current" : "On",
71
+ enabled ? "Off" : "Off ← current",
72
+ ]);
73
+ if (!selected) return;
74
+ const nextEnabled = selected.startsWith("On");
75
+ try {
76
+ runtime.store.save(nextEnabled);
77
+ } catch (error) {
78
+ ctx.ui.notify(`Completion sound setting could not be saved: ${errorMessage(error)}`, "error");
79
+ return;
80
+ }
81
+ enabled = nextEnabled;
82
+ applyTitle(ctx);
83
+ ctx.ui.notify(`Completion sound: ${enabled ? "On" : "Off"}`, "info");
84
+ },
85
+ });
86
+
87
+ pi.on("session_start", (_event, ctx) => {
88
+ requestPending = false;
89
+ lastStopReason = undefined;
90
+ if (ctx.mode !== "tui") return;
91
+ try {
92
+ enabled = runtime.store.load();
93
+ } catch (error) {
94
+ enabled = false;
95
+ ctx.ui.notify(`Completion sound settings could not be read: ${errorMessage(error)}`, "error");
96
+ }
97
+ applyTitle(ctx);
98
+ });
99
+
100
+ pi.on("session_info_changed", (_event, ctx) => applyTitle(ctx));
101
+
102
+ pi.on("agent_start", (_event, ctx) => {
103
+ if (ctx.mode !== "tui") return;
104
+ requestPending = true;
105
+ lastStopReason = undefined;
106
+ });
107
+
108
+ pi.on("agent_end", (event, ctx) => {
109
+ if (ctx.mode !== "tui" || !requestPending) return;
110
+ lastStopReason = undefined;
111
+ for (let index = event.messages.length - 1; index >= 0; index -= 1) {
112
+ const message = event.messages[index];
113
+ if (message?.role !== "assistant") continue;
114
+ lastStopReason = message.stopReason;
115
+ break;
116
+ }
117
+ });
118
+
119
+ pi.on("agent_settled", (_event, ctx) => {
120
+ if (ctx.mode !== "tui" || !requestPending) return;
121
+ if (!ctx.isIdle() || ctx.hasPendingMessages()) return;
122
+ requestPending = false;
123
+ if (!enabled || lastStopReason === "aborted") return;
124
+ try {
125
+ runtime.ring();
126
+ } catch (error) {
127
+ ctx.ui.notify(`Completion sound failed: ${errorMessage(error)}`, "error");
128
+ }
129
+ });
130
+
131
+ pi.on("session_shutdown", () => {
132
+ requestPending = false;
133
+ lastStopReason = undefined;
134
+ });
135
+ }