@pennyfarthing/cyclist 10.2.0 → 10.3.0

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 (48) hide show
  1. package/dist/api/agent-load.js +1 -1
  2. package/dist/api/agent-load.js.map +1 -1
  3. package/dist/api/theme-agents.js +2 -2
  4. package/dist/api/theme-agents.js.map +1 -1
  5. package/dist/bikerack.d.ts +2 -0
  6. package/dist/bikerack.d.ts.map +1 -0
  7. package/dist/bikerack.js +43 -0
  8. package/dist/bikerack.js.map +1 -0
  9. package/dist/hooks/cyclist-pretooluse-hook.d.ts +60 -0
  10. package/dist/hooks/cyclist-pretooluse-hook.d.ts.map +1 -0
  11. package/dist/hooks/cyclist-pretooluse-hook.js +57 -0
  12. package/dist/hooks/cyclist-pretooluse-hook.js.map +1 -0
  13. package/dist/hooks/pretooluse-hook.d.ts +89 -0
  14. package/dist/hooks/pretooluse-hook.d.ts.map +1 -0
  15. package/dist/hooks/pretooluse-hook.js +235 -0
  16. package/dist/hooks/pretooluse-hook.js.map +1 -0
  17. package/dist/notification-sound.d.ts +59 -0
  18. package/dist/notification-sound.d.ts.map +1 -0
  19. package/dist/notification-sound.js +219 -0
  20. package/dist/notification-sound.js.map +1 -0
  21. package/dist/plugin-loader.test.d.ts +17 -0
  22. package/dist/plugin-loader.test.d.ts.map +1 -0
  23. package/dist/plugin-loader.test.js +407 -0
  24. package/dist/plugin-loader.test.js.map +1 -0
  25. package/dist/public/css/react.css +1 -1
  26. package/dist/public/js/react/react.js +32 -32
  27. package/dist/server.d.ts +1 -0
  28. package/dist/server.d.ts.map +1 -1
  29. package/dist/server.js +9 -0
  30. package/dist/server.js.map +1 -1
  31. package/dist/sprint-data.d.ts +21 -0
  32. package/dist/sprint-data.d.ts.map +1 -1
  33. package/dist/sprint-data.js +26 -1
  34. package/dist/sprint-data.js.map +1 -1
  35. package/dist/theme-metadata.js +1 -1
  36. package/dist/theme-metadata.js.map +1 -1
  37. package/dist/websocket.js +2 -2
  38. package/dist/websocket.js.map +1 -1
  39. package/package.json +1 -1
  40. package/src/public/App.tsx +34 -0
  41. package/src/public/components/BikeRackIndex.tsx +54 -0
  42. package/src/public/components/BikeRackWorkspace.tsx +140 -0
  43. package/src/public/components/DockviewWorkspace.tsx +5 -6
  44. package/src/public/components/StandalonePanel.tsx +84 -0
  45. package/src/public/components/panel-registry.ts +11 -0
  46. package/src/public/styles/dockview-theme.css +1 -1
  47. package/src/public/types/electron.d.ts +18 -0
  48. package/src/public/utils/slash-commands.ts +4 -0
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Notification Sound Service (Story MSSCI-14191)
3
+ *
4
+ * Provides audio notifications for workflow events using Web Audio API.
5
+ * Different tones for different event types:
6
+ * - HANDOFF: Low tone (agent transition)
7
+ * - QUESTION: Higher tone (user input needed)
8
+ * - COMPLETION: Chord (workflow complete)
9
+ *
10
+ * State is managed in-memory but can sync with settings via loadNotificationSoundSetting().
11
+ */
12
+ /**
13
+ * Notification event types that trigger sounds
14
+ */
15
+ export declare const NotificationEvent: {
16
+ readonly HANDOFF: "handoff";
17
+ readonly QUESTION: "question";
18
+ readonly COMPLETION: "completion";
19
+ };
20
+ export type NotificationEventType = (typeof NotificationEvent)[keyof typeof NotificationEvent];
21
+ /**
22
+ * Service class for notification sounds (exported for type checking)
23
+ */
24
+ export declare class NotificationSound {
25
+ static isEnabled(): boolean;
26
+ static setEnabled(enabled: boolean): void;
27
+ static play(event: NotificationEventType): Promise<void>;
28
+ }
29
+ /**
30
+ * Check if notification sounds are enabled
31
+ */
32
+ export declare function isNotificationSoundEnabled(): boolean;
33
+ /**
34
+ * Enable or disable notification sounds
35
+ */
36
+ export declare function setNotificationSoundEnabled(enabled: boolean): void;
37
+ /**
38
+ * Load notification sound setting from REST API
39
+ */
40
+ export declare function loadNotificationSoundSetting(): Promise<void>;
41
+ /**
42
+ * Check if audio playback is permitted (handles autoplay restrictions)
43
+ * Returns true if audio can play, false if user gesture is required
44
+ */
45
+ export declare function checkAudioPermission(): Promise<boolean>;
46
+ /**
47
+ * Play a notification sound for the given event type
48
+ */
49
+ export declare function playNotificationSound(event: NotificationEventType): Promise<void>;
50
+ /**
51
+ * Reset state (for testing)
52
+ */
53
+ export declare function resetNotificationSoundState(): void;
54
+ /**
55
+ * Inject a custom AudioContext class (for testing)
56
+ * Pass null to use the default detection
57
+ */
58
+ export declare function setAudioContextClass(cls: (new () => AudioContext) | null): void;
59
+ //# sourceMappingURL=notification-sound.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-sound.d.ts","sourceRoot":"","sources":["../src/notification-sound.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;CAIpB,CAAC;AAEX,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,OAAO,iBAAiB,CAAC,CAAC;AA0B/F;;GAEG;AACH,qBAAa,iBAAiB;IAC5B,MAAM,CAAC,SAAS,IAAI,OAAO;IAI3B,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;WAI5B,IAAI,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;CAG/D;AAED;;GAEG;AACH,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAElE;AAED;;GAEG;AACH,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,IAAI,CAAC,CAUlE;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC,CAa7D;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BvF;AA6ED;;GAEG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAUlD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,CAAC,UAAU,YAAY,CAAC,GAAG,IAAI,GAAG,IAAI,CAI/E"}
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Notification Sound Service (Story MSSCI-14191)
3
+ *
4
+ * Provides audio notifications for workflow events using Web Audio API.
5
+ * Different tones for different event types:
6
+ * - HANDOFF: Low tone (agent transition)
7
+ * - QUESTION: Higher tone (user input needed)
8
+ * - COMPLETION: Chord (workflow complete)
9
+ *
10
+ * State is managed in-memory but can sync with settings via loadNotificationSoundSetting().
11
+ */
12
+ // =============================================================================
13
+ // Types
14
+ // =============================================================================
15
+ /**
16
+ * Notification event types that trigger sounds
17
+ */
18
+ export const NotificationEvent = {
19
+ HANDOFF: 'handoff', // Agent handoff / phase transition
20
+ QUESTION: 'question', // User input required
21
+ COMPLETION: 'completion', // Workflow completion
22
+ };
23
+ /**
24
+ * Frequency configurations for each event type (in Hz)
25
+ */
26
+ const EVENT_FREQUENCIES = {
27
+ [NotificationEvent.HANDOFF]: [330], // E4 - single low tone
28
+ [NotificationEvent.QUESTION]: [523], // C5 - higher, attention-grabbing
29
+ [NotificationEvent.COMPLETION]: [523, 659, 784], // C5-E5-G5 major chord
30
+ };
31
+ // =============================================================================
32
+ // State
33
+ // =============================================================================
34
+ let soundEnabled = false;
35
+ let audioContext = null;
36
+ // For testing: injectable AudioContext constructor
37
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
+ let _audioContextClass = null;
39
+ // =============================================================================
40
+ // Public API
41
+ // =============================================================================
42
+ /**
43
+ * Service class for notification sounds (exported for type checking)
44
+ */
45
+ export class NotificationSound {
46
+ static isEnabled() {
47
+ return isNotificationSoundEnabled();
48
+ }
49
+ static setEnabled(enabled) {
50
+ setNotificationSoundEnabled(enabled);
51
+ }
52
+ static async play(event) {
53
+ return playNotificationSound(event);
54
+ }
55
+ }
56
+ /**
57
+ * Check if notification sounds are enabled
58
+ */
59
+ export function isNotificationSoundEnabled() {
60
+ return soundEnabled;
61
+ }
62
+ /**
63
+ * Enable or disable notification sounds
64
+ */
65
+ export function setNotificationSoundEnabled(enabled) {
66
+ soundEnabled = enabled;
67
+ }
68
+ /**
69
+ * Load notification sound setting from REST API
70
+ */
71
+ export async function loadNotificationSoundSetting() {
72
+ try {
73
+ const response = await fetch('/api/settings');
74
+ if (response.ok) {
75
+ const settings = await response.json();
76
+ soundEnabled = settings?.notifications?.sound ?? false;
77
+ }
78
+ }
79
+ catch {
80
+ // Silently fail - keep current state
81
+ }
82
+ }
83
+ /**
84
+ * Check if audio playback is permitted (handles autoplay restrictions)
85
+ * Returns true if audio can play, false if user gesture is required
86
+ */
87
+ export async function checkAudioPermission() {
88
+ try {
89
+ const ctx = getOrCreateAudioContext();
90
+ if (!ctx)
91
+ return false;
92
+ if (ctx.state === 'suspended') {
93
+ await ctx.resume();
94
+ }
95
+ return ctx.state === 'running';
96
+ }
97
+ catch {
98
+ return false;
99
+ }
100
+ }
101
+ /**
102
+ * Play a notification sound for the given event type
103
+ */
104
+ export async function playNotificationSound(event) {
105
+ // Exit early if disabled
106
+ if (!soundEnabled) {
107
+ return;
108
+ }
109
+ try {
110
+ const ctx = getOrCreateAudioContext();
111
+ if (!ctx) {
112
+ return;
113
+ }
114
+ // Handle suspended state (autoplay restrictions)
115
+ if (ctx.state === 'suspended') {
116
+ await ctx.resume();
117
+ }
118
+ const frequencies = EVENT_FREQUENCIES[event] || EVENT_FREQUENCIES[NotificationEvent.HANDOFF];
119
+ // Play each frequency in the pattern
120
+ for (let i = 0; i < frequencies.length; i++) {
121
+ const freq = frequencies[i];
122
+ const startTime = ctx.currentTime + i * 0.1; // Slight offset for chord/arpeggio effect
123
+ playTone(ctx, freq, startTime, 0.2);
124
+ }
125
+ }
126
+ catch {
127
+ // Silently fail - audio is optional
128
+ }
129
+ }
130
+ // =============================================================================
131
+ // Internal Helpers
132
+ // =============================================================================
133
+ /**
134
+ * Get the AudioContext constructor (allows for mocking in tests)
135
+ * Checks multiple locations to support different environments and test mocking
136
+ */
137
+ function getAudioContextClass() {
138
+ // Check if a test-injected class is set
139
+ if (_audioContextClass !== null) {
140
+ return _audioContextClass;
141
+ }
142
+ // Check various locations where AudioContext might be defined
143
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
144
+ const g = globalThis;
145
+ if (g.AudioContext) {
146
+ return g.AudioContext;
147
+ }
148
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
149
+ if (typeof window !== 'undefined' && window.AudioContext) {
150
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
151
+ return window.AudioContext;
152
+ }
153
+ // webkitAudioContext for Safari
154
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
155
+ if (typeof window !== 'undefined' && window.webkitAudioContext) {
156
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
157
+ return window.webkitAudioContext;
158
+ }
159
+ return null;
160
+ }
161
+ /**
162
+ * Get or create the shared AudioContext
163
+ */
164
+ function getOrCreateAudioContext() {
165
+ const AudioContextClass = getAudioContextClass();
166
+ if (!AudioContextClass) {
167
+ return null;
168
+ }
169
+ if (!audioContext) {
170
+ try {
171
+ audioContext = new AudioContextClass();
172
+ }
173
+ catch {
174
+ return null;
175
+ }
176
+ }
177
+ return audioContext;
178
+ }
179
+ /**
180
+ * Play a single tone using Web Audio API
181
+ */
182
+ function playTone(ctx, frequency, startTime, duration) {
183
+ const oscillator = ctx.createOscillator();
184
+ const gainNode = ctx.createGain();
185
+ oscillator.connect(gainNode);
186
+ gainNode.connect(ctx.destination);
187
+ oscillator.type = 'sine';
188
+ oscillator.frequency.setValueAtTime(frequency, startTime);
189
+ // Envelope: quick attack, gradual decay
190
+ gainNode.gain.setValueAtTime(0.3, startTime);
191
+ gainNode.gain.exponentialRampToValueAtTime(0.01, startTime + duration);
192
+ oscillator.start(startTime);
193
+ oscillator.stop(startTime + duration);
194
+ }
195
+ /**
196
+ * Reset state (for testing)
197
+ */
198
+ export function resetNotificationSoundState() {
199
+ soundEnabled = false;
200
+ if (audioContext) {
201
+ try {
202
+ audioContext.close().catch(() => { });
203
+ }
204
+ catch {
205
+ // Ignore errors closing mock audio context
206
+ }
207
+ audioContext = null;
208
+ }
209
+ }
210
+ /**
211
+ * Inject a custom AudioContext class (for testing)
212
+ * Pass null to use the default detection
213
+ */
214
+ export function setAudioContextClass(cls) {
215
+ _audioContextClass = cls;
216
+ // Reset the existing context so the new class is used
217
+ audioContext = null;
218
+ }
219
+ //# sourceMappingURL=notification-sound.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-sound.js","sourceRoot":"","sources":["../src/notification-sound.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Tests for Story 93-6: Plugin Router Loader for Cyclist
3
+ *
4
+ * Tests the dynamic discovery and mounting of API routers from installed
5
+ * @pennyfarthing/* plugin packages. The loader uses the plugin discovery
6
+ * system (93-3) to find plugins and mount their Express routers.
7
+ *
8
+ * Test categories:
9
+ * 1. initPluginRouters() - Core loading behavior
10
+ * 2. Plugin with API router - Benchmark plugin integration
11
+ * 3. Graceful degradation - Missing packages, failed imports
12
+ * 4. Cyclist starts without plugins - Empty state
13
+ *
14
+ * Run with: cd packages/cyclist && pnpm test
15
+ */
16
+ export {};
17
+ //# sourceMappingURL=plugin-loader.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-loader.test.d.ts","sourceRoot":"","sources":["../src/plugin-loader.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}