@vibescope/mcp-server 0.0.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.
Files changed (170) hide show
  1. package/README.md +98 -0
  2. package/dist/cli.d.ts +34 -0
  3. package/dist/cli.js +356 -0
  4. package/dist/cli.test.d.ts +1 -0
  5. package/dist/cli.test.js +367 -0
  6. package/dist/handlers/__test-utils__.d.ts +72 -0
  7. package/dist/handlers/__test-utils__.js +176 -0
  8. package/dist/handlers/blockers.d.ts +18 -0
  9. package/dist/handlers/blockers.js +81 -0
  10. package/dist/handlers/bodies-of-work.d.ts +34 -0
  11. package/dist/handlers/bodies-of-work.js +614 -0
  12. package/dist/handlers/checkouts.d.ts +37 -0
  13. package/dist/handlers/checkouts.js +377 -0
  14. package/dist/handlers/cost.d.ts +39 -0
  15. package/dist/handlers/cost.js +247 -0
  16. package/dist/handlers/decisions.d.ts +16 -0
  17. package/dist/handlers/decisions.js +64 -0
  18. package/dist/handlers/deployment.d.ts +36 -0
  19. package/dist/handlers/deployment.js +1062 -0
  20. package/dist/handlers/discovery.d.ts +14 -0
  21. package/dist/handlers/discovery.js +870 -0
  22. package/dist/handlers/fallback.d.ts +18 -0
  23. package/dist/handlers/fallback.js +216 -0
  24. package/dist/handlers/findings.d.ts +18 -0
  25. package/dist/handlers/findings.js +110 -0
  26. package/dist/handlers/git-issues.d.ts +22 -0
  27. package/dist/handlers/git-issues.js +247 -0
  28. package/dist/handlers/ideas.d.ts +19 -0
  29. package/dist/handlers/ideas.js +188 -0
  30. package/dist/handlers/index.d.ts +29 -0
  31. package/dist/handlers/index.js +65 -0
  32. package/dist/handlers/knowledge-query.d.ts +22 -0
  33. package/dist/handlers/knowledge-query.js +253 -0
  34. package/dist/handlers/knowledge.d.ts +12 -0
  35. package/dist/handlers/knowledge.js +108 -0
  36. package/dist/handlers/milestones.d.ts +20 -0
  37. package/dist/handlers/milestones.js +179 -0
  38. package/dist/handlers/organizations.d.ts +36 -0
  39. package/dist/handlers/organizations.js +428 -0
  40. package/dist/handlers/progress.d.ts +14 -0
  41. package/dist/handlers/progress.js +149 -0
  42. package/dist/handlers/project.d.ts +20 -0
  43. package/dist/handlers/project.js +278 -0
  44. package/dist/handlers/requests.d.ts +16 -0
  45. package/dist/handlers/requests.js +131 -0
  46. package/dist/handlers/roles.d.ts +30 -0
  47. package/dist/handlers/roles.js +281 -0
  48. package/dist/handlers/session.d.ts +20 -0
  49. package/dist/handlers/session.js +791 -0
  50. package/dist/handlers/tasks.d.ts +52 -0
  51. package/dist/handlers/tasks.js +1111 -0
  52. package/dist/handlers/tasks.test.d.ts +1 -0
  53. package/dist/handlers/tasks.test.js +431 -0
  54. package/dist/handlers/types.d.ts +94 -0
  55. package/dist/handlers/types.js +1 -0
  56. package/dist/handlers/validation.d.ts +16 -0
  57. package/dist/handlers/validation.js +188 -0
  58. package/dist/index.d.ts +2 -0
  59. package/dist/index.js +2707 -0
  60. package/dist/knowledge.d.ts +6 -0
  61. package/dist/knowledge.js +121 -0
  62. package/dist/tools.d.ts +2 -0
  63. package/dist/tools.js +2498 -0
  64. package/dist/utils.d.ts +149 -0
  65. package/dist/utils.js +317 -0
  66. package/dist/utils.test.d.ts +1 -0
  67. package/dist/utils.test.js +532 -0
  68. package/dist/validators.d.ts +35 -0
  69. package/dist/validators.js +111 -0
  70. package/dist/validators.test.d.ts +1 -0
  71. package/dist/validators.test.js +176 -0
  72. package/package.json +44 -0
  73. package/src/cli.test.ts +442 -0
  74. package/src/cli.ts +439 -0
  75. package/src/handlers/__test-utils__.ts +217 -0
  76. package/src/handlers/blockers.test.ts +390 -0
  77. package/src/handlers/blockers.ts +110 -0
  78. package/src/handlers/bodies-of-work.test.ts +1276 -0
  79. package/src/handlers/bodies-of-work.ts +783 -0
  80. package/src/handlers/cost.test.ts +436 -0
  81. package/src/handlers/cost.ts +322 -0
  82. package/src/handlers/decisions.test.ts +401 -0
  83. package/src/handlers/decisions.ts +86 -0
  84. package/src/handlers/deployment.test.ts +516 -0
  85. package/src/handlers/deployment.ts +1289 -0
  86. package/src/handlers/discovery.test.ts +254 -0
  87. package/src/handlers/discovery.ts +969 -0
  88. package/src/handlers/fallback.test.ts +687 -0
  89. package/src/handlers/fallback.ts +260 -0
  90. package/src/handlers/findings.test.ts +565 -0
  91. package/src/handlers/findings.ts +153 -0
  92. package/src/handlers/ideas.test.ts +753 -0
  93. package/src/handlers/ideas.ts +247 -0
  94. package/src/handlers/index.ts +69 -0
  95. package/src/handlers/milestones.test.ts +584 -0
  96. package/src/handlers/milestones.ts +217 -0
  97. package/src/handlers/organizations.test.ts +997 -0
  98. package/src/handlers/organizations.ts +550 -0
  99. package/src/handlers/progress.test.ts +369 -0
  100. package/src/handlers/progress.ts +188 -0
  101. package/src/handlers/project.test.ts +562 -0
  102. package/src/handlers/project.ts +352 -0
  103. package/src/handlers/requests.test.ts +531 -0
  104. package/src/handlers/requests.ts +150 -0
  105. package/src/handlers/session.test.ts +459 -0
  106. package/src/handlers/session.ts +912 -0
  107. package/src/handlers/tasks.test.ts +602 -0
  108. package/src/handlers/tasks.ts +1393 -0
  109. package/src/handlers/types.ts +88 -0
  110. package/src/handlers/validation.test.ts +880 -0
  111. package/src/handlers/validation.ts +223 -0
  112. package/src/index.ts +3205 -0
  113. package/src/knowledge.ts +132 -0
  114. package/src/tmpclaude-0078-cwd +1 -0
  115. package/src/tmpclaude-0ee1-cwd +1 -0
  116. package/src/tmpclaude-2dd5-cwd +1 -0
  117. package/src/tmpclaude-344c-cwd +1 -0
  118. package/src/tmpclaude-3860-cwd +1 -0
  119. package/src/tmpclaude-4b63-cwd +1 -0
  120. package/src/tmpclaude-5c73-cwd +1 -0
  121. package/src/tmpclaude-5ee3-cwd +1 -0
  122. package/src/tmpclaude-6795-cwd +1 -0
  123. package/src/tmpclaude-709e-cwd +1 -0
  124. package/src/tmpclaude-9839-cwd +1 -0
  125. package/src/tmpclaude-d829-cwd +1 -0
  126. package/src/tmpclaude-e072-cwd +1 -0
  127. package/src/tmpclaude-f6ee-cwd +1 -0
  128. package/src/utils.test.ts +681 -0
  129. package/src/utils.ts +375 -0
  130. package/src/validators.test.ts +223 -0
  131. package/src/validators.ts +122 -0
  132. package/tmpclaude-0439-cwd +1 -0
  133. package/tmpclaude-132f-cwd +1 -0
  134. package/tmpclaude-15bb-cwd +1 -0
  135. package/tmpclaude-165a-cwd +1 -0
  136. package/tmpclaude-1ba9-cwd +1 -0
  137. package/tmpclaude-21a3-cwd +1 -0
  138. package/tmpclaude-2a38-cwd +1 -0
  139. package/tmpclaude-2adf-cwd +1 -0
  140. package/tmpclaude-2f56-cwd +1 -0
  141. package/tmpclaude-3626-cwd +1 -0
  142. package/tmpclaude-3727-cwd +1 -0
  143. package/tmpclaude-40bc-cwd +1 -0
  144. package/tmpclaude-436f-cwd +1 -0
  145. package/tmpclaude-4783-cwd +1 -0
  146. package/tmpclaude-4b6d-cwd +1 -0
  147. package/tmpclaude-4ba4-cwd +1 -0
  148. package/tmpclaude-51e6-cwd +1 -0
  149. package/tmpclaude-5ecf-cwd +1 -0
  150. package/tmpclaude-6f97-cwd +1 -0
  151. package/tmpclaude-7fb2-cwd +1 -0
  152. package/tmpclaude-825c-cwd +1 -0
  153. package/tmpclaude-8baf-cwd +1 -0
  154. package/tmpclaude-8d9f-cwd +1 -0
  155. package/tmpclaude-975c-cwd +1 -0
  156. package/tmpclaude-9983-cwd +1 -0
  157. package/tmpclaude-a045-cwd +1 -0
  158. package/tmpclaude-ac4a-cwd +1 -0
  159. package/tmpclaude-b593-cwd +1 -0
  160. package/tmpclaude-b891-cwd +1 -0
  161. package/tmpclaude-c032-cwd +1 -0
  162. package/tmpclaude-cf43-cwd +1 -0
  163. package/tmpclaude-d040-cwd +1 -0
  164. package/tmpclaude-dcdd-cwd +1 -0
  165. package/tmpclaude-dcee-cwd +1 -0
  166. package/tmpclaude-e16b-cwd +1 -0
  167. package/tmpclaude-ecd2-cwd +1 -0
  168. package/tmpclaude-f48d-cwd +1 -0
  169. package/tsconfig.json +16 -0
  170. package/vitest.config.ts +13 -0
@@ -0,0 +1,436 @@
1
+ /**
2
+ * Cost Handlers Unit Tests
3
+ */
4
+
5
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
6
+ import {
7
+ getCostSummary,
8
+ getCostAlerts,
9
+ addCostAlert,
10
+ updateCostAlert,
11
+ deleteCostAlert,
12
+ getTaskCosts,
13
+ } from './cost.js';
14
+ import { createMockSupabase, createMockContext, testUUID } from './__test-utils__.js';
15
+
16
+ describe('Cost Handlers', () => {
17
+ beforeEach(() => {
18
+ vi.clearAllMocks();
19
+ });
20
+
21
+ // ============================================================================
22
+ // getCostSummary
23
+ // ============================================================================
24
+
25
+ describe('getCostSummary', () => {
26
+ it('should return error when project_id is missing', async () => {
27
+ const supabase = createMockSupabase();
28
+ const ctx = createMockContext(supabase);
29
+
30
+ const result = await getCostSummary({}, ctx);
31
+
32
+ expect(result.isError).toBe(true);
33
+ expect(result.result).toEqual({ error: 'project_id is required' });
34
+ });
35
+
36
+ it('should return daily cost summary with totals', async () => {
37
+ const mockData = [
38
+ {
39
+ date: '2025-01-14',
40
+ session_count: 5,
41
+ total_tokens: 10000,
42
+ total_calls: 50,
43
+ estimated_cost_usd: '0.50',
44
+ },
45
+ {
46
+ date: '2025-01-13',
47
+ session_count: 3,
48
+ total_tokens: 6000,
49
+ total_calls: 30,
50
+ estimated_cost_usd: '0.30',
51
+ },
52
+ ];
53
+
54
+ const supabase = createMockSupabase({
55
+ selectResult: { data: mockData, error: null },
56
+ });
57
+ const ctx = createMockContext(supabase);
58
+
59
+ const result = await getCostSummary({ project_id: testUUID() }, ctx);
60
+
61
+ expect(result.isError).toBeUndefined();
62
+ expect(result.result.period).toBe('daily');
63
+ expect(result.result.summary).toEqual(mockData);
64
+ expect(result.result.totals).toEqual({
65
+ sessions: 8,
66
+ tokens: 16000,
67
+ calls: 80,
68
+ cost: 0.8,
69
+ });
70
+ });
71
+
72
+ it('should handle weekly period', async () => {
73
+ const supabase = createMockSupabase({
74
+ selectResult: { data: [], error: null },
75
+ });
76
+ const ctx = createMockContext(supabase);
77
+
78
+ const result = await getCostSummary(
79
+ { project_id: testUUID(), period: 'weekly' },
80
+ ctx
81
+ );
82
+
83
+ expect(result.result.period).toBe('weekly');
84
+ expect(supabase.from).toHaveBeenCalledWith('weekly_cost_summary');
85
+ });
86
+
87
+ it('should handle monthly period', async () => {
88
+ const supabase = createMockSupabase({
89
+ selectResult: { data: [], error: null },
90
+ });
91
+ const ctx = createMockContext(supabase);
92
+
93
+ const result = await getCostSummary(
94
+ { project_id: testUUID(), period: 'monthly' },
95
+ ctx
96
+ );
97
+
98
+ expect(result.result.period).toBe('monthly');
99
+ expect(supabase.from).toHaveBeenCalledWith('monthly_cost_summary');
100
+ });
101
+
102
+ it('should return error on database failure', async () => {
103
+ const supabase = createMockSupabase({
104
+ selectResult: { data: null, error: { message: 'Database error' } },
105
+ });
106
+ const ctx = createMockContext(supabase);
107
+
108
+ const result = await getCostSummary({ project_id: testUUID() }, ctx);
109
+
110
+ expect(result.isError).toBe(true);
111
+ expect(result.result.error).toContain('Failed to get cost summary');
112
+ });
113
+ });
114
+
115
+ // ============================================================================
116
+ // getCostAlerts
117
+ // ============================================================================
118
+
119
+ describe('getCostAlerts', () => {
120
+ it('should return all alerts for user', async () => {
121
+ const mockAlerts = [
122
+ { id: 'alert-1', threshold_amount: 10, threshold_period: 'daily' },
123
+ { id: 'alert-2', threshold_amount: 50, threshold_period: 'weekly' },
124
+ ];
125
+
126
+ const supabase = createMockSupabase({
127
+ selectResult: { data: mockAlerts, error: null },
128
+ });
129
+ const ctx = createMockContext(supabase);
130
+
131
+ const result = await getCostAlerts({}, ctx);
132
+
133
+ expect(result.result.alerts).toEqual(mockAlerts);
134
+ expect(result.result.count).toBe(2);
135
+ });
136
+
137
+ it('should filter by project_id when provided', async () => {
138
+ const supabase = createMockSupabase({
139
+ selectResult: { data: [], error: null },
140
+ });
141
+ const ctx = createMockContext(supabase);
142
+
143
+ await getCostAlerts({ project_id: testUUID() }, ctx);
144
+
145
+ expect(supabase.eq).toHaveBeenCalledWith('project_id', testUUID());
146
+ });
147
+
148
+ it('should return error on database failure', async () => {
149
+ const supabase = createMockSupabase({
150
+ selectResult: { data: null, error: { message: 'Query failed' } },
151
+ });
152
+ const ctx = createMockContext(supabase);
153
+
154
+ const result = await getCostAlerts({}, ctx);
155
+
156
+ expect(result.isError).toBe(true);
157
+ expect(result.result.error).toContain('Failed to get cost alerts');
158
+ });
159
+ });
160
+
161
+ // ============================================================================
162
+ // addCostAlert
163
+ // ============================================================================
164
+
165
+ describe('addCostAlert', () => {
166
+ it('should return error when threshold_amount is missing', async () => {
167
+ const supabase = createMockSupabase();
168
+ const ctx = createMockContext(supabase);
169
+
170
+ const result = await addCostAlert(
171
+ { threshold_period: 'daily' },
172
+ ctx
173
+ );
174
+
175
+ expect(result.isError).toBe(true);
176
+ expect(result.result.error).toContain('threshold_amount must be a positive number');
177
+ });
178
+
179
+ it('should return error when threshold_amount is not positive', async () => {
180
+ const supabase = createMockSupabase();
181
+ const ctx = createMockContext(supabase);
182
+
183
+ const result = await addCostAlert(
184
+ { threshold_amount: -5, threshold_period: 'daily' },
185
+ ctx
186
+ );
187
+
188
+ expect(result.isError).toBe(true);
189
+ expect(result.result.error).toContain('threshold_amount must be a positive number');
190
+ });
191
+
192
+ it('should return error when threshold_period is invalid', async () => {
193
+ const supabase = createMockSupabase();
194
+ const ctx = createMockContext(supabase);
195
+
196
+ const result = await addCostAlert(
197
+ { threshold_amount: 10, threshold_period: 'yearly' },
198
+ ctx
199
+ );
200
+
201
+ expect(result.isError).toBe(true);
202
+ expect(result.result.error).toContain('threshold_period must be');
203
+ });
204
+
205
+ it('should create alert successfully', async () => {
206
+ const mockAlert = {
207
+ id: 'new-alert',
208
+ threshold_amount: 10,
209
+ threshold_period: 'daily',
210
+ alert_type: 'warning',
211
+ };
212
+
213
+ const supabase = createMockSupabase({
214
+ insertResult: { data: mockAlert, error: null },
215
+ });
216
+ const ctx = createMockContext(supabase);
217
+
218
+ const result = await addCostAlert(
219
+ { threshold_amount: 10, threshold_period: 'daily' },
220
+ ctx
221
+ );
222
+
223
+ expect(result.result.success).toBe(true);
224
+ expect(result.result.alert).toEqual(mockAlert);
225
+ expect(result.result.message).toContain('Alert created');
226
+ });
227
+
228
+ it('should create alert with project_id', async () => {
229
+ const supabase = createMockSupabase({
230
+ insertResult: { data: { id: 'new-alert' }, error: null },
231
+ });
232
+ const ctx = createMockContext(supabase);
233
+
234
+ await addCostAlert(
235
+ {
236
+ project_id: testUUID(),
237
+ threshold_amount: 10,
238
+ threshold_period: 'daily',
239
+ },
240
+ ctx
241
+ );
242
+
243
+ expect(supabase.insert).toHaveBeenCalledWith(
244
+ expect.objectContaining({
245
+ project_id: testUUID(),
246
+ })
247
+ );
248
+ });
249
+
250
+ it('should return error on database failure', async () => {
251
+ const supabase = createMockSupabase({
252
+ insertResult: { data: null, error: { message: 'Insert failed' } },
253
+ });
254
+ const ctx = createMockContext(supabase);
255
+
256
+ const result = await addCostAlert(
257
+ { threshold_amount: 10, threshold_period: 'daily' },
258
+ ctx
259
+ );
260
+
261
+ expect(result.isError).toBe(true);
262
+ expect(result.result.error).toContain('Failed to create cost alert');
263
+ });
264
+ });
265
+
266
+ // ============================================================================
267
+ // updateCostAlert
268
+ // ============================================================================
269
+
270
+ describe('updateCostAlert', () => {
271
+ it('should return error when alert_id is missing', async () => {
272
+ const supabase = createMockSupabase();
273
+ const ctx = createMockContext(supabase);
274
+
275
+ const result = await updateCostAlert({}, ctx);
276
+
277
+ expect(result.isError).toBe(true);
278
+ expect(result.result.error).toBe('alert_id is required');
279
+ });
280
+
281
+ it('should return error when no updates provided', async () => {
282
+ const supabase = createMockSupabase();
283
+ const ctx = createMockContext(supabase);
284
+
285
+ const result = await updateCostAlert({ alert_id: testUUID() }, ctx);
286
+
287
+ expect(result.isError).toBe(true);
288
+ expect(result.result.error).toBe('No updates provided');
289
+ });
290
+
291
+ it('should update alert successfully', async () => {
292
+ const mockUpdatedAlert = {
293
+ id: testUUID(),
294
+ threshold_amount: 20,
295
+ threshold_period: 'weekly',
296
+ };
297
+
298
+ const supabase = createMockSupabase({
299
+ updateResult: { data: mockUpdatedAlert, error: null },
300
+ });
301
+ const ctx = createMockContext(supabase);
302
+
303
+ const result = await updateCostAlert(
304
+ { alert_id: testUUID(), threshold_amount: 20 },
305
+ ctx
306
+ );
307
+
308
+ expect(result.result.success).toBe(true);
309
+ expect(result.result.alert).toEqual(mockUpdatedAlert);
310
+ });
311
+
312
+ it('should update enabled status', async () => {
313
+ const supabase = createMockSupabase({
314
+ updateResult: { data: { id: testUUID(), enabled: false }, error: null },
315
+ });
316
+ const ctx = createMockContext(supabase);
317
+
318
+ await updateCostAlert({ alert_id: testUUID(), enabled: false }, ctx);
319
+
320
+ expect(supabase.update).toHaveBeenCalledWith({ enabled: false });
321
+ });
322
+
323
+ it('should return error on database failure', async () => {
324
+ const supabase = createMockSupabase({
325
+ updateResult: { data: null, error: { message: 'Update failed' } },
326
+ });
327
+ const ctx = createMockContext(supabase);
328
+
329
+ const result = await updateCostAlert(
330
+ { alert_id: testUUID(), threshold_amount: 20 },
331
+ ctx
332
+ );
333
+
334
+ expect(result.isError).toBe(true);
335
+ expect(result.result.error).toContain('Failed to update cost alert');
336
+ });
337
+ });
338
+
339
+ // ============================================================================
340
+ // deleteCostAlert
341
+ // ============================================================================
342
+
343
+ describe('deleteCostAlert', () => {
344
+ it('should return error when alert_id is missing', async () => {
345
+ const supabase = createMockSupabase();
346
+ const ctx = createMockContext(supabase);
347
+
348
+ const result = await deleteCostAlert({}, ctx);
349
+
350
+ expect(result.isError).toBe(true);
351
+ expect(result.result.error).toBe('alert_id is required');
352
+ });
353
+
354
+ it('should delete alert successfully', async () => {
355
+ const supabase = createMockSupabase({
356
+ deleteResult: { data: null, error: null },
357
+ });
358
+ const ctx = createMockContext(supabase);
359
+
360
+ const result = await deleteCostAlert({ alert_id: testUUID() }, ctx);
361
+
362
+ expect(result.result.success).toBe(true);
363
+ expect(result.result.deleted_alert_id).toBe(testUUID());
364
+ });
365
+
366
+ it('should return error on database failure', async () => {
367
+ const supabase = createMockSupabase({
368
+ deleteResult: { data: null, error: { message: 'Delete failed' } },
369
+ });
370
+ const ctx = createMockContext(supabase);
371
+
372
+ const result = await deleteCostAlert({ alert_id: testUUID() }, ctx);
373
+
374
+ expect(result.isError).toBe(true);
375
+ expect(result.result.error).toContain('Failed to delete cost alert');
376
+ });
377
+ });
378
+
379
+ // ============================================================================
380
+ // getTaskCosts
381
+ // ============================================================================
382
+
383
+ describe('getTaskCosts', () => {
384
+ it('should return error when project_id is missing', async () => {
385
+ const supabase = createMockSupabase();
386
+ const ctx = createMockContext(supabase);
387
+
388
+ const result = await getTaskCosts({}, ctx);
389
+
390
+ expect(result.isError).toBe(true);
391
+ expect(result.result.error).toBe('project_id is required');
392
+ });
393
+
394
+ it('should return task costs with total', async () => {
395
+ const mockTasks = [
396
+ { id: 'task-1', title: 'Task 1', estimated_cost_usd: '0.50' },
397
+ { id: 'task-2', title: 'Task 2', estimated_cost_usd: '0.30' },
398
+ ];
399
+
400
+ const supabase = createMockSupabase({
401
+ selectResult: { data: mockTasks, error: null },
402
+ });
403
+ const ctx = createMockContext(supabase);
404
+
405
+ const result = await getTaskCosts({ project_id: testUUID() }, ctx);
406
+
407
+ expect(result.result.project_id).toBe(testUUID());
408
+ expect(result.result.tasks).toEqual(mockTasks);
409
+ expect(result.result.total_cost_usd).toBe(0.8);
410
+ });
411
+
412
+ it('should handle empty task list', async () => {
413
+ const supabase = createMockSupabase({
414
+ selectResult: { data: [], error: null },
415
+ });
416
+ const ctx = createMockContext(supabase);
417
+
418
+ const result = await getTaskCosts({ project_id: testUUID() }, ctx);
419
+
420
+ expect(result.result.tasks).toEqual([]);
421
+ expect(result.result.total_cost_usd).toBe(0);
422
+ });
423
+
424
+ it('should return error on database failure', async () => {
425
+ const supabase = createMockSupabase({
426
+ selectResult: { data: null, error: { message: 'Query failed' } },
427
+ });
428
+ const ctx = createMockContext(supabase);
429
+
430
+ const result = await getTaskCosts({ project_id: testUUID() }, ctx);
431
+
432
+ expect(result.isError).toBe(true);
433
+ expect(result.result.error).toContain('Failed to get task costs');
434
+ });
435
+ });
436
+ });