@pi-unipi/unipi 2.1.2 → 2.1.3

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.
Files changed (41) hide show
  1. package/package.json +22 -22
  2. package/packages/ask-user/package.json +3 -3
  3. package/packages/autocomplete/package.json +1 -1
  4. package/packages/btw/package.json +2 -2
  5. package/packages/cocoindex/package.json +2 -2
  6. package/packages/compactor/package.json +4 -4
  7. package/packages/core/package.json +1 -1
  8. package/packages/footer/package.json +3 -3
  9. package/packages/info-screen/package.json +2 -2
  10. package/packages/input-shortcuts/package.json +3 -3
  11. package/packages/kanboard/package.json +2 -2
  12. package/packages/mcp/package.json +2 -2
  13. package/packages/memory/package.json +3 -3
  14. package/packages/milestone/package.json +2 -2
  15. package/packages/notify/README.md +2 -1
  16. package/packages/notify/events.ts +119 -81
  17. package/packages/notify/package.json +2 -2
  18. package/packages/notify/settings.ts +1 -0
  19. package/packages/notify/skills/configure-notify/SKILL.md +3 -1
  20. package/packages/notify/src/__tests__/event-bus.test.ts +3 -2
  21. package/packages/ralph/package.json +3 -3
  22. package/packages/subagents/package.json +5 -5
  23. package/packages/updater/package.json +3 -3
  24. package/packages/utility/package.json +2 -2
  25. package/packages/web-api/package.json +2 -2
  26. package/packages/workflow/package.json +2 -2
  27. package/packages/info-screen/tui/info-overlay.ts +0 -504
  28. package/packages/kanboard/tui/kanboard-overlay.ts +0 -300
  29. package/packages/memory/tui/settings-tui.ts +0 -281
  30. package/packages/notify/platforms/focus-win.ts +0 -123
  31. package/packages/notify/platforms/focus.ts +0 -33
  32. package/packages/notify/platforms/gotify.ts +0 -36
  33. package/packages/notify/platforms/native.ts +0 -79
  34. package/packages/notify/platforms/node-notifier.d.ts +0 -20
  35. package/packages/notify/platforms/ntfy.ts +0 -48
  36. package/packages/notify/platforms/telegram.ts +0 -77
  37. package/packages/notify/tui/gotify-setup.ts +0 -528
  38. package/packages/notify/tui/ntfy-setup.ts +0 -652
  39. package/packages/notify/tui/recap-model-selector.ts +0 -301
  40. package/packages/notify/tui/settings-overlay.ts +0 -352
  41. package/packages/notify/tui/telegram-setup.ts +0 -301
@@ -1,33 +0,0 @@
1
- /**
2
- * @pi-unipi/notify — Focus detection abstraction
3
- *
4
- * Unified interface for checking whether the terminal window is the
5
- * foreground (active) window. Platform-specific implementations are
6
- * dispatched based on process.platform.
7
- *
8
- * Currently implemented:
9
- * - Windows (win32): calls focus-win.ts
10
- *
11
- * Unimplemented platforms always return false (no suppression).
12
- */
13
-
14
- import { isWindowFocusedOnWindows } from "./focus-win.js";
15
-
16
- /**
17
- * Check whether the current terminal/console window is the foreground
18
- * (active) window. Used by sendNativeNotification to optionally
19
- * suppress notifications when the user is already looking at the screen.
20
- *
21
- * @returns true if the terminal is the foreground window, false otherwise.
22
- * On unimplemented platforms, always returns false.
23
- */
24
- export async function isWindowFocused(): Promise<boolean> {
25
- switch (process.platform) {
26
- case "win32":
27
- return await isWindowFocusedOnWindows();
28
- // TODO: macOS — use osascript to check frontmost application
29
- // TODO: Linux — use xdotool (X11) or per-compositor tool (Wayland)
30
- default:
31
- return false;
32
- }
33
- }
@@ -1,36 +0,0 @@
1
- /**
2
- * @pi-unipi/notify — Gotify notification platform
3
- *
4
- * Sends push notifications to a Gotify server via HTTP POST.
5
- * Gotify is a self-hosted push notification server.
6
- */
7
-
8
- /** Send a notification to Gotify server */
9
- export async function sendGotifyNotification(
10
- serverUrl: string,
11
- appToken: string,
12
- title: string,
13
- message: string,
14
- priority: number = 5
15
- ): Promise<void> {
16
- const url = serverUrl.replace(/\/$/, "") + "/message";
17
- const response = await fetch(url, {
18
- method: "POST",
19
- headers: {
20
- "Content-Type": "application/json",
21
- "X-Gotify-Key": appToken,
22
- },
23
- body: JSON.stringify({
24
- title,
25
- message,
26
- priority,
27
- }),
28
- });
29
-
30
- if (!response.ok) {
31
- const body = await response.text().catch(() => "<no body>");
32
- throw new Error(
33
- `Gotify API error ${response.status}: ${body}`
34
- );
35
- }
36
- }
@@ -1,79 +0,0 @@
1
- /**
2
- * @pi-unipi/notify — Native OS notification platform
3
- *
4
- * Wraps node-notifier for cross-platform desktop notifications.
5
- * Windows: SnoreToast (no admin required)
6
- * macOS: terminal-notifier
7
- * Linux: notify-send / libnotify
8
- */
9
-
10
- import notifier from "node-notifier";
11
- import { isWindowFocused } from "./focus.js";
12
-
13
- /** Options for native notification */
14
- export interface NativeNotificationOptions {
15
- /** Windows appID to show instead of "SnoreToast" */
16
- windowsAppId?: string;
17
- /**
18
- * When true, suppresses the notification if the terminal window is
19
- * the foreground (active) window. Only effective on platforms where
20
- * `isWindowFocused` is implemented (currently Windows).
21
- */
22
- suppressWhenFocused?: boolean;
23
- }
24
-
25
- /**
26
- * Thrown by sendNativeNotification when the notification was suppressed
27
- * because suppressWhenFocused is set and the terminal window is focused.
28
- *
29
- * Callers should catch this and treat it as intentional suppression,
30
- * NOT as a send failure.
31
- */
32
- export class SuppressedError extends Error {
33
- constructor() {
34
- super("Notification suppressed: terminal window is focused");
35
- this.name = "SuppressedError";
36
- }
37
- }
38
-
39
- /**
40
- * Send a native OS notification.
41
- *
42
- * When `suppressWhenFocused` is true and `isWindowFocused()` returns true
43
- * (i.e. the terminal is the foreground window), the notification is
44
- * suppressed and the promise rejects with SuppressedError.
45
- *
46
- * Resolves when notification is shown, rejects with SuppressedError on
47
- * suppression or with a standard Error on failure.
48
- */
49
- export async function sendNativeNotification(
50
- title: string,
51
- message: string,
52
- options?: NativeNotificationOptions
53
- ): Promise<void> {
54
- // Suppress if the terminal window is currently focused
55
- if (options?.suppressWhenFocused && await isWindowFocused()) {
56
- throw new SuppressedError();
57
- }
58
-
59
- return new Promise((resolve, reject) => {
60
- notifier.notify(
61
- {
62
- title,
63
- message,
64
- appID: options?.windowsAppId,
65
- },
66
- (err: Error | null) => {
67
- if (err) {
68
- reject(
69
- new Error(
70
- `Native notification failed: ${err.message}`
71
- )
72
- );
73
- } else {
74
- resolve();
75
- }
76
- }
77
- );
78
- });
79
- }
@@ -1,20 +0,0 @@
1
- declare module "node-notifier" {
2
- interface Notification {
3
- title?: string;
4
- message?: string;
5
- appID?: string;
6
- sound?: boolean | string;
7
- icon?: string;
8
- wait?: boolean;
9
- }
10
-
11
- interface Notifier {
12
- notify(
13
- notification: Notification,
14
- callback: (err: Error | null, data: any) => void
15
- ): void;
16
- }
17
-
18
- const notifier: Notifier;
19
- export default notifier;
20
- }
@@ -1,48 +0,0 @@
1
- /**
2
- * @pi-unipi/notify — ntfy notification platform
3
- *
4
- * Sends push notifications to an ntfy server via HTTP POST.
5
- * ntfy is a simple HTTP-based pub-sub notification service.
6
- * Supports self-hosted instances and ntfy.sh (public).
7
- */
8
-
9
- /** Send a notification to an ntfy server */
10
- export async function sendNtfyNotification(
11
- serverUrl: string,
12
- topic: string,
13
- title: string,
14
- message: string,
15
- priority: number = 3,
16
- token?: string
17
- ): Promise<void> {
18
- // ntfy supports POSTing to the server root with a JSON body that carries
19
- // topic/title/message/priority. JSON bodies are UTF-8 safe, unlike HTTP
20
- // headers which must be ByteString (Latin-1) and reject characters like
21
- // em dash (U+2014). See https://docs.ntfy.sh/publish/#publish-as-json
22
- const url = serverUrl.replace(/\/$/, "");
23
- const headers: Record<string, string> = {
24
- "Content-Type": "application/json",
25
- };
26
-
27
- if (token) {
28
- headers["Authorization"] = `Bearer ${token}`;
29
- }
30
-
31
- const body = JSON.stringify({
32
- topic,
33
- title,
34
- message,
35
- priority: Math.max(1, Math.min(5, priority)),
36
- });
37
-
38
- const response = await fetch(url, {
39
- method: "POST",
40
- headers,
41
- body,
42
- });
43
-
44
- if (!response.ok) {
45
- const resBody = await response.text().catch(() => "<no body>");
46
- throw new Error(`ntfy API error ${response.status}: ${resBody}`);
47
- }
48
- }
@@ -1,77 +0,0 @@
1
- /**
2
- * @pi-unipi/notify — Telegram notification platform
3
- *
4
- * Sends notifications via Telegram Bot API.
5
- * Supports auto-detection of chat ID via polling getUpdates.
6
- */
7
-
8
- /** Send a notification via Telegram Bot API */
9
- export async function sendTelegramNotification(
10
- botToken: string,
11
- chatId: string,
12
- title: string,
13
- message: string
14
- ): Promise<void> {
15
- const text = `*${escapeMarkdown(title)}*\n${escapeMarkdown(message)}`;
16
- const url = `https://api.telegram.org/bot${botToken}/sendMessage`;
17
-
18
- const response = await fetch(url, {
19
- method: "POST",
20
- headers: { "Content-Type": "application/json" },
21
- body: JSON.stringify({
22
- chat_id: chatId,
23
- text,
24
- parse_mode: "MarkdownV2",
25
- }),
26
- });
27
-
28
- if (!response.ok) {
29
- const body = await response.text().catch(() => "<no body>");
30
- throw new Error(
31
- `Telegram API error ${response.status}: ${body}`
32
- );
33
- }
34
- }
35
-
36
- /**
37
- * Poll Telegram getUpdates to detect the chat ID from a user message.
38
- * Returns the chat ID string, or null if no message found.
39
- */
40
- export async function pollForChatId(
41
- botToken: string,
42
- signal?: AbortSignal
43
- ): Promise<string | null> {
44
- const url = `https://api.telegram.org/bot${botToken}/getUpdates?allowed_updates=["message"]`;
45
-
46
- try {
47
- const response = await fetch(url, { signal });
48
- const data = (await response.json()) as {
49
- ok: boolean;
50
- result: Array<{
51
- message?: { chat?: { id?: number } };
52
- callback_query?: { message?: { chat?: { id?: number } } };
53
- }>;
54
- };
55
-
56
- if (data.ok && data.result.length > 0) {
57
- const lastUpdate = data.result[data.result.length - 1];
58
- const chatId =
59
- lastUpdate.message?.chat?.id ||
60
- lastUpdate.callback_query?.message?.chat?.id;
61
- if (chatId) {
62
- return String(chatId);
63
- }
64
- }
65
- } catch (err) {
66
- if (signal?.aborted) throw err;
67
- // Network error — return null to allow retry
68
- return null;
69
- }
70
-
71
- return null;
72
- }
73
-
74
- /** Escape special MarkdownV2 characters */
75
- function escapeMarkdown(text: string): string {
76
- return text.replace(/[_*\[\]()~`>#+\-=|{}.!\\]/g, "\\$&");
77
- }