@raidou/pi-notify 0.4.0 → 0.5.1

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,115 +0,0 @@
1
- import type { ExtensionAPI } from '@earendil-works/pi-coding-agent'
2
- import { describe, expect, it } from 'vitest'
3
-
4
- import type { ResolvedNotifyConfig } from './config.js'
5
- import { EventsNotifier } from './events.js'
6
-
7
- type EventsListener = (payload: unknown) => void
8
-
9
- interface FakePi {
10
- eventListeners: Map<string, Set<EventsListener>>
11
- listeners: Map<string, Set<(...args: unknown[]) => void>>
12
- events: { on(event: string, listener: EventsListener): () => void }
13
- on(event: string, listener: (...args: unknown[]) => void): void
14
- }
15
-
16
- function makeFakePi(): FakePi {
17
- const eventListeners = new Map<string, Set<EventsListener>>()
18
- const listeners = new Map<string, Set<(...args: unknown[]) => void>>()
19
- return {
20
- eventListeners,
21
- listeners,
22
- events: {
23
- on(event, listener) {
24
- let set = eventListeners.get(event)
25
- if (!set) {
26
- set = new Set()
27
- eventListeners.set(event, set)
28
- }
29
- set.add(listener)
30
- return () => {
31
- set.delete(listener)
32
- }
33
- },
34
- },
35
- on(event, listener) {
36
- let set = listeners.get(event)
37
- if (!set) {
38
- set = new Set()
39
- listeners.set(event, set)
40
- }
41
- set.add(listener)
42
- },
43
- }
44
- }
45
-
46
- function makeConfig(
47
- events: ResolvedNotifyConfig['events'],
48
- ): ResolvedNotifyConfig {
49
- return {
50
- enabled: true,
51
- notifyTools: new Set<string>(),
52
- events,
53
- finished: true,
54
- finishedThrottleMs: 0,
55
- onlyNotifyWhenUnfocused: true,
56
- unfocusedActivityThresholdMs: 0,
57
- tmuxSymbol: '',
58
- }
59
- }
60
-
61
- function emitEvent(pi: FakePi, event: string, payload?: unknown): void {
62
- const set = pi.eventListeners.get(event)
63
- if (!set) return
64
- for (const listener of [...set]) listener(payload)
65
- }
66
-
67
- describe('EventsNotifier', () => {
68
- it('notifies with the configured message when a custom event fires', () => {
69
- const pi = makeFakePi()
70
- const notifier = new EventsNotifier(
71
- pi as unknown as ExtensionAPI,
72
- makeConfig({
73
- 'my:custom:event': 'Custom event triggered',
74
- }),
75
- )
76
- const bodies: string[] = []
77
- notifier.register((body) => bodies.push(body))
78
-
79
- emitEvent(pi, 'my:custom:event')
80
-
81
- expect(bodies).toEqual(['Custom event triggered'])
82
- })
83
-
84
- it('does not notify for events disabled with false', () => {
85
- const pi = makeFakePi()
86
- const notifier = new EventsNotifier(
87
- pi as unknown as ExtensionAPI,
88
- makeConfig({
89
- 'my:custom:event': false,
90
- }),
91
- )
92
- const bodies: string[] = []
93
- notifier.register((body) => bodies.push(body))
94
-
95
- emitEvent(pi, 'my:custom:event')
96
-
97
- expect(bodies).toEqual([])
98
- })
99
-
100
- it('does not notify for events disabled with an empty string (backward compatibility)', () => {
101
- const pi = makeFakePi()
102
- const notifier = new EventsNotifier(
103
- pi as unknown as ExtensionAPI,
104
- makeConfig({
105
- 'my:custom:event': '',
106
- }),
107
- )
108
- const bodies: string[] = []
109
- notifier.register((body) => bodies.push(body))
110
-
111
- emitEvent(pi, 'my:custom:event')
112
-
113
- expect(bodies).toEqual([])
114
- })
115
- })
package/src/events.ts DELETED
@@ -1,31 +0,0 @@
1
- import type { ExtensionAPI } from '@earendil-works/pi-coding-agent'
2
-
3
- import type { ResolvedNotifyConfig } from './config.js'
4
- import { Registrar } from './shared/registrar.js'
5
- import type { NotifyAction } from './shared/types.js'
6
-
7
- export const PI_NOTIFY_EVENT = 'pi-notify:notify'
8
-
9
- export class EventsNotifier extends Registrar {
10
- private readonly config: ResolvedNotifyConfig
11
-
12
- constructor(pi: ExtensionAPI, config: ResolvedNotifyConfig) {
13
- super(pi)
14
- this.config = config
15
- }
16
-
17
- protected override setup(notify: NotifyAction): void {
18
- for (const [channel, message] of Object.entries(this.config.events)) {
19
- if (typeof message !== 'string' || message === '') continue
20
- const unsubscribe = this.pi.events.on(channel, () => {
21
- notify(message)
22
- })
23
- this.unsubscribes.push(unsubscribe)
24
- }
25
-
26
- const customEventUnsub = this.pi.events.on(PI_NOTIFY_EVENT, (payload) => {
27
- notify(String(payload))
28
- })
29
- this.unsubscribes.push(customEventUnsub)
30
- }
31
- }
package/src/idle.ts DELETED
@@ -1,37 +0,0 @@
1
- import type { ExtensionAPI } from '@earendil-works/pi-coding-agent'
2
-
3
- import type { ResolvedNotifyConfig } from './config.js'
4
- import { Registrar } from './shared/registrar.js'
5
- import type { NotifyAction } from './shared/types.js'
6
- import type { StateTracker } from './state-tracker.js'
7
-
8
- export class IdleNotifier extends Registrar {
9
- private readonly config: ResolvedNotifyConfig
10
- private readonly stateTracker: StateTracker
11
- private hasActivity = false
12
-
13
- constructor(
14
- pi: ExtensionAPI,
15
- config: ResolvedNotifyConfig,
16
- stateTracker: StateTracker,
17
- ) {
18
- super(pi)
19
- this.config = config
20
- this.stateTracker = stateTracker
21
- }
22
-
23
- protected override setup(notify: NotifyAction): void {
24
- if (!this.config.finished) return
25
-
26
- this.pi.on('turn_start', () => {
27
- this.hasActivity = true
28
- })
29
-
30
- this.unsubscribes.push(
31
- this.stateTracker.events.on('idle', () => {
32
- if (!this.hasActivity) return
33
- notify('Idle')
34
- }),
35
- )
36
- }
37
- }
package/src/tool.ts DELETED
@@ -1,21 +0,0 @@
1
- import type { ExtensionAPI } from '@earendil-works/pi-coding-agent'
2
-
3
- import type { ResolvedNotifyConfig } from './config.js'
4
- import { Registrar } from './shared/registrar.js'
5
- import type { NotifyAction } from './shared/types.js'
6
-
7
- export class ToolCallNotifier extends Registrar {
8
- private readonly config: ResolvedNotifyConfig
9
-
10
- constructor(pi: ExtensionAPI, config: ResolvedNotifyConfig) {
11
- super(pi)
12
- this.config = config
13
- }
14
-
15
- protected override setup(notify: NotifyAction): void {
16
- this.pi.on('tool_call', (event) => {
17
- if (!this.config.notifyTools.has(event.toolName)) return
18
- notify(`Tool call: ${event.toolName}`)
19
- })
20
- }
21
- }