@vibescope/mcp-server 0.0.1 → 0.2.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 (173) hide show
  1. package/README.md +113 -98
  2. package/dist/api-client.d.ts +1169 -0
  3. package/dist/api-client.js +713 -0
  4. package/dist/cli.d.ts +1 -6
  5. package/dist/cli.js +39 -240
  6. package/dist/config/tool-categories.d.ts +31 -0
  7. package/dist/config/tool-categories.js +253 -0
  8. package/dist/handlers/blockers.js +57 -58
  9. package/dist/handlers/bodies-of-work.d.ts +2 -0
  10. package/dist/handlers/bodies-of-work.js +108 -477
  11. package/dist/handlers/cost.d.ts +1 -0
  12. package/dist/handlers/cost.js +35 -113
  13. package/dist/handlers/decisions.d.ts +2 -0
  14. package/dist/handlers/decisions.js +28 -27
  15. package/dist/handlers/deployment.js +113 -828
  16. package/dist/handlers/discovery.d.ts +3 -0
  17. package/dist/handlers/discovery.js +26 -627
  18. package/dist/handlers/fallback.d.ts +2 -0
  19. package/dist/handlers/fallback.js +56 -142
  20. package/dist/handlers/findings.d.ts +8 -1
  21. package/dist/handlers/findings.js +65 -68
  22. package/dist/handlers/git-issues.d.ts +9 -13
  23. package/dist/handlers/git-issues.js +80 -225
  24. package/dist/handlers/ideas.d.ts +3 -0
  25. package/dist/handlers/ideas.js +53 -134
  26. package/dist/handlers/index.d.ts +2 -0
  27. package/dist/handlers/index.js +6 -0
  28. package/dist/handlers/milestones.d.ts +2 -0
  29. package/dist/handlers/milestones.js +51 -98
  30. package/dist/handlers/organizations.js +79 -275
  31. package/dist/handlers/progress.d.ts +2 -0
  32. package/dist/handlers/progress.js +25 -123
  33. package/dist/handlers/project.js +42 -221
  34. package/dist/handlers/requests.d.ts +2 -0
  35. package/dist/handlers/requests.js +23 -83
  36. package/dist/handlers/session.js +119 -590
  37. package/dist/handlers/sprints.d.ts +32 -0
  38. package/dist/handlers/sprints.js +275 -0
  39. package/dist/handlers/tasks.d.ts +7 -10
  40. package/dist/handlers/tasks.js +245 -894
  41. package/dist/handlers/tool-docs.d.ts +9 -0
  42. package/dist/handlers/tool-docs.js +904 -0
  43. package/dist/handlers/types.d.ts +11 -3
  44. package/dist/handlers/validation.d.ts +1 -1
  45. package/dist/handlers/validation.js +38 -153
  46. package/dist/index.js +493 -162
  47. package/dist/knowledge.js +106 -9
  48. package/dist/tools.js +34 -4
  49. package/dist/validators.d.ts +21 -0
  50. package/dist/validators.js +91 -0
  51. package/package.json +2 -3
  52. package/src/api-client.ts +1822 -0
  53. package/src/cli.test.ts +128 -302
  54. package/src/cli.ts +41 -285
  55. package/src/handlers/__test-setup__.ts +215 -0
  56. package/src/handlers/__test-utils__.ts +4 -134
  57. package/src/handlers/blockers.test.ts +114 -124
  58. package/src/handlers/blockers.ts +68 -70
  59. package/src/handlers/bodies-of-work.test.ts +236 -831
  60. package/src/handlers/bodies-of-work.ts +210 -525
  61. package/src/handlers/cost.test.ts +149 -113
  62. package/src/handlers/cost.ts +44 -132
  63. package/src/handlers/decisions.test.ts +111 -209
  64. package/src/handlers/decisions.ts +35 -27
  65. package/src/handlers/deployment.test.ts +193 -239
  66. package/src/handlers/deployment.ts +143 -896
  67. package/src/handlers/discovery.test.ts +20 -67
  68. package/src/handlers/discovery.ts +29 -714
  69. package/src/handlers/fallback.test.ts +206 -361
  70. package/src/handlers/fallback.ts +81 -156
  71. package/src/handlers/findings.test.ts +229 -320
  72. package/src/handlers/findings.ts +76 -64
  73. package/src/handlers/git-issues.test.ts +623 -0
  74. package/src/handlers/git-issues.ts +174 -0
  75. package/src/handlers/ideas.test.ts +229 -343
  76. package/src/handlers/ideas.ts +69 -143
  77. package/src/handlers/index.ts +6 -0
  78. package/src/handlers/milestones.test.ts +167 -281
  79. package/src/handlers/milestones.ts +54 -93
  80. package/src/handlers/organizations.test.ts +275 -467
  81. package/src/handlers/organizations.ts +84 -294
  82. package/src/handlers/progress.test.ts +112 -218
  83. package/src/handlers/progress.ts +29 -142
  84. package/src/handlers/project.test.ts +203 -226
  85. package/src/handlers/project.ts +48 -238
  86. package/src/handlers/requests.test.ts +74 -342
  87. package/src/handlers/requests.ts +25 -83
  88. package/src/handlers/session.test.ts +276 -206
  89. package/src/handlers/session.ts +136 -662
  90. package/src/handlers/sprints.test.ts +711 -0
  91. package/src/handlers/sprints.ts +510 -0
  92. package/src/handlers/tasks.test.ts +669 -353
  93. package/src/handlers/tasks.ts +263 -1015
  94. package/src/handlers/tool-docs.ts +1024 -0
  95. package/src/handlers/types.ts +12 -4
  96. package/src/handlers/validation.test.ts +237 -568
  97. package/src/handlers/validation.ts +43 -167
  98. package/src/index.ts +493 -186
  99. package/src/tools.ts +2532 -0
  100. package/src/validators.test.ts +223 -223
  101. package/src/validators.ts +127 -0
  102. package/tsconfig.json +1 -1
  103. package/vitest.config.ts +14 -13
  104. package/dist/cli.test.d.ts +0 -1
  105. package/dist/cli.test.js +0 -367
  106. package/dist/handlers/__test-utils__.d.ts +0 -72
  107. package/dist/handlers/__test-utils__.js +0 -176
  108. package/dist/handlers/checkouts.d.ts +0 -37
  109. package/dist/handlers/checkouts.js +0 -377
  110. package/dist/handlers/knowledge-query.d.ts +0 -22
  111. package/dist/handlers/knowledge-query.js +0 -253
  112. package/dist/handlers/knowledge.d.ts +0 -12
  113. package/dist/handlers/knowledge.js +0 -108
  114. package/dist/handlers/roles.d.ts +0 -30
  115. package/dist/handlers/roles.js +0 -281
  116. package/dist/handlers/tasks.test.d.ts +0 -1
  117. package/dist/handlers/tasks.test.js +0 -431
  118. package/dist/utils.test.d.ts +0 -1
  119. package/dist/utils.test.js +0 -532
  120. package/dist/validators.test.d.ts +0 -1
  121. package/dist/validators.test.js +0 -176
  122. package/src/knowledge.ts +0 -132
  123. package/src/tmpclaude-0078-cwd +0 -1
  124. package/src/tmpclaude-0ee1-cwd +0 -1
  125. package/src/tmpclaude-2dd5-cwd +0 -1
  126. package/src/tmpclaude-344c-cwd +0 -1
  127. package/src/tmpclaude-3860-cwd +0 -1
  128. package/src/tmpclaude-4b63-cwd +0 -1
  129. package/src/tmpclaude-5c73-cwd +0 -1
  130. package/src/tmpclaude-5ee3-cwd +0 -1
  131. package/src/tmpclaude-6795-cwd +0 -1
  132. package/src/tmpclaude-709e-cwd +0 -1
  133. package/src/tmpclaude-9839-cwd +0 -1
  134. package/src/tmpclaude-d829-cwd +0 -1
  135. package/src/tmpclaude-e072-cwd +0 -1
  136. package/src/tmpclaude-f6ee-cwd +0 -1
  137. package/tmpclaude-0439-cwd +0 -1
  138. package/tmpclaude-132f-cwd +0 -1
  139. package/tmpclaude-15bb-cwd +0 -1
  140. package/tmpclaude-165a-cwd +0 -1
  141. package/tmpclaude-1ba9-cwd +0 -1
  142. package/tmpclaude-21a3-cwd +0 -1
  143. package/tmpclaude-2a38-cwd +0 -1
  144. package/tmpclaude-2adf-cwd +0 -1
  145. package/tmpclaude-2f56-cwd +0 -1
  146. package/tmpclaude-3626-cwd +0 -1
  147. package/tmpclaude-3727-cwd +0 -1
  148. package/tmpclaude-40bc-cwd +0 -1
  149. package/tmpclaude-436f-cwd +0 -1
  150. package/tmpclaude-4783-cwd +0 -1
  151. package/tmpclaude-4b6d-cwd +0 -1
  152. package/tmpclaude-4ba4-cwd +0 -1
  153. package/tmpclaude-51e6-cwd +0 -1
  154. package/tmpclaude-5ecf-cwd +0 -1
  155. package/tmpclaude-6f97-cwd +0 -1
  156. package/tmpclaude-7fb2-cwd +0 -1
  157. package/tmpclaude-825c-cwd +0 -1
  158. package/tmpclaude-8baf-cwd +0 -1
  159. package/tmpclaude-8d9f-cwd +0 -1
  160. package/tmpclaude-975c-cwd +0 -1
  161. package/tmpclaude-9983-cwd +0 -1
  162. package/tmpclaude-a045-cwd +0 -1
  163. package/tmpclaude-ac4a-cwd +0 -1
  164. package/tmpclaude-b593-cwd +0 -1
  165. package/tmpclaude-b891-cwd +0 -1
  166. package/tmpclaude-c032-cwd +0 -1
  167. package/tmpclaude-cf43-cwd +0 -1
  168. package/tmpclaude-d040-cwd +0 -1
  169. package/tmpclaude-dcdd-cwd +0 -1
  170. package/tmpclaude-dcee-cwd +0 -1
  171. package/tmpclaude-e16b-cwd +0 -1
  172. package/tmpclaude-ecd2-cwd +0 -1
  173. package/tmpclaude-f48d-cwd +0 -1
@@ -1,6 +1,4 @@
1
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import type { SupabaseClient } from '@supabase/supabase-js';
3
- import type { HandlerContext, TokenUsage } from './types.js';
4
2
  import {
5
3
  startFallbackActivity,
6
4
  stopFallbackActivity,
@@ -8,118 +6,8 @@ import {
8
6
  getActivitySchedules,
9
7
  } from './fallback.js';
10
8
  import { ValidationError } from '../validators.js';
11
-
12
- // ============================================================================
13
- // Test Utilities
14
- // ============================================================================
15
-
16
- function createMockSupabase(overrides: {
17
- selectResult?: { data: unknown; error: unknown };
18
- insertResult?: { data: unknown; error: unknown };
19
- updateResult?: { data: unknown; error: unknown };
20
- deleteResult?: { data: unknown; error: unknown };
21
- } = {}) {
22
- const defaultResult = { data: null, error: null };
23
- let currentOperation = 'select';
24
- let insertThenSelect = false;
25
-
26
- const mock = {
27
- from: vi.fn().mockReturnThis(),
28
- select: vi.fn(() => {
29
- if (currentOperation === 'insert') {
30
- insertThenSelect = true;
31
- } else {
32
- currentOperation = 'select';
33
- insertThenSelect = false;
34
- }
35
- return mock;
36
- }),
37
- insert: vi.fn(() => {
38
- currentOperation = 'insert';
39
- insertThenSelect = false;
40
- return mock;
41
- }),
42
- update: vi.fn(() => {
43
- currentOperation = 'update';
44
- insertThenSelect = false;
45
- return mock;
46
- }),
47
- delete: vi.fn(() => {
48
- currentOperation = 'delete';
49
- insertThenSelect = false;
50
- return mock;
51
- }),
52
- eq: vi.fn().mockReturnThis(),
53
- neq: vi.fn().mockReturnThis(),
54
- in: vi.fn().mockReturnThis(),
55
- is: vi.fn().mockReturnThis(),
56
- not: vi.fn().mockReturnThis(),
57
- or: vi.fn().mockReturnThis(),
58
- gt: vi.fn().mockReturnThis(),
59
- gte: vi.fn().mockReturnThis(),
60
- lte: vi.fn().mockReturnThis(),
61
- lt: vi.fn().mockReturnThis(),
62
- order: vi.fn().mockReturnThis(),
63
- limit: vi.fn().mockReturnThis(),
64
- single: vi.fn(() => {
65
- if (currentOperation === 'insert' || insertThenSelect) {
66
- return Promise.resolve(overrides.insertResult ?? defaultResult);
67
- }
68
- if (currentOperation === 'select') {
69
- return Promise.resolve(overrides.selectResult ?? defaultResult);
70
- }
71
- if (currentOperation === 'update') {
72
- return Promise.resolve(overrides.updateResult ?? defaultResult);
73
- }
74
- return Promise.resolve(defaultResult);
75
- }),
76
- maybeSingle: vi.fn(() => {
77
- return Promise.resolve(overrides.selectResult ?? defaultResult);
78
- }),
79
- then: vi.fn((resolve: (value: unknown) => void) => {
80
- if (currentOperation === 'insert' || insertThenSelect) {
81
- return Promise.resolve(overrides.insertResult ?? defaultResult).then(resolve);
82
- }
83
- if (currentOperation === 'select') {
84
- return Promise.resolve(overrides.selectResult ?? defaultResult).then(resolve);
85
- }
86
- if (currentOperation === 'update') {
87
- return Promise.resolve(overrides.updateResult ?? defaultResult).then(resolve);
88
- }
89
- if (currentOperation === 'delete') {
90
- return Promise.resolve(overrides.deleteResult ?? defaultResult).then(resolve);
91
- }
92
- return Promise.resolve(defaultResult).then(resolve);
93
- }),
94
- };
95
-
96
- return mock as unknown as SupabaseClient;
97
- }
98
-
99
- function createMockContext(
100
- supabase: SupabaseClient,
101
- options: { sessionId?: string | null } = {}
102
- ): HandlerContext {
103
- const defaultTokenUsage: TokenUsage = {
104
- callCount: 5,
105
- totalTokens: 2500,
106
- byTool: {},
107
- };
108
-
109
- const sessionId = 'sessionId' in options ? options.sessionId : 'session-123';
110
-
111
- return {
112
- supabase,
113
- auth: { userId: 'user-123', apiKeyId: 'api-key-123' },
114
- session: {
115
- instanceId: 'instance-abc',
116
- currentSessionId: sessionId,
117
- currentPersona: 'Wave',
118
- tokenUsage: defaultTokenUsage,
119
- },
120
- updateSession: vi.fn(),
121
- };
122
- }
9
+ import { createMockContext } from './__test-utils__.js';
10
+ import { mockApiClient } from './__test-setup__.js';
123
11
 
124
12
  // ============================================================================
125
13
  // startFallbackActivity Tests
@@ -129,8 +17,7 @@ describe('startFallbackActivity', () => {
129
17
  beforeEach(() => vi.clearAllMocks());
130
18
 
131
19
  it('should throw error for missing project_id', async () => {
132
- const supabase = createMockSupabase();
133
- const ctx = createMockContext(supabase);
20
+ const ctx = createMockContext();
134
21
 
135
22
  await expect(
136
23
  startFallbackActivity({ activity: 'code_review' }, ctx)
@@ -138,8 +25,7 @@ describe('startFallbackActivity', () => {
138
25
  });
139
26
 
140
27
  it('should throw error for invalid project_id UUID', async () => {
141
- const supabase = createMockSupabase();
142
- const ctx = createMockContext(supabase);
28
+ const ctx = createMockContext();
143
29
 
144
30
  await expect(
145
31
  startFallbackActivity({ project_id: 'invalid', activity: 'code_review' }, ctx)
@@ -147,8 +33,7 @@ describe('startFallbackActivity', () => {
147
33
  });
148
34
 
149
35
  it('should throw error for missing activity', async () => {
150
- const supabase = createMockSupabase();
151
- const ctx = createMockContext(supabase);
36
+ const ctx = createMockContext();
152
37
 
153
38
  await expect(
154
39
  startFallbackActivity({ project_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
@@ -156,8 +41,7 @@ describe('startFallbackActivity', () => {
156
41
  });
157
42
 
158
43
  it('should throw error for invalid activity type', async () => {
159
- const supabase = createMockSupabase();
160
- const ctx = createMockContext(supabase);
44
+ const ctx = createMockContext();
161
45
 
162
46
  await expect(
163
47
  startFallbackActivity({
@@ -168,10 +52,11 @@ describe('startFallbackActivity', () => {
168
52
  });
169
53
 
170
54
  it('should start fallback activity successfully', async () => {
171
- const supabase = createMockSupabase({
172
- updateResult: { data: null, error: null },
55
+ mockApiClient.startFallbackActivity.mockResolvedValue({
56
+ ok: true,
57
+ data: { message: 'Started code_review' },
173
58
  });
174
- const ctx = createMockContext(supabase);
59
+ const ctx = createMockContext();
175
60
 
176
61
  const result = await startFallbackActivity(
177
62
  {
@@ -188,11 +73,12 @@ describe('startFallbackActivity', () => {
188
73
  expect((result.result as { title: string }).title).toBeDefined();
189
74
  });
190
75
 
191
- it('should update agent_sessions with fallback activity', async () => {
192
- const supabase = createMockSupabase({
193
- updateResult: { data: null, error: null },
76
+ it('should call API client with correct parameters', async () => {
77
+ mockApiClient.startFallbackActivity.mockResolvedValue({
78
+ ok: true,
79
+ data: { message: 'Started' },
194
80
  });
195
- const ctx = createMockContext(supabase, { sessionId: 'my-session' });
81
+ const ctx = createMockContext({ sessionId: 'my-session' });
196
82
 
197
83
  await startFallbackActivity(
198
84
  {
@@ -202,15 +88,11 @@ describe('startFallbackActivity', () => {
202
88
  ctx
203
89
  );
204
90
 
205
- expect(supabase.from).toHaveBeenCalledWith('agent_sessions');
206
- expect(supabase.update).toHaveBeenCalledWith(
207
- expect.objectContaining({
208
- current_fallback_activity: 'security_review',
209
- current_task_id: null,
210
- status: 'active',
211
- })
91
+ expect(mockApiClient.startFallbackActivity).toHaveBeenCalledWith(
92
+ '123e4567-e89b-12d3-a456-426614174000',
93
+ 'security_review',
94
+ 'my-session'
212
95
  );
213
- expect(supabase.eq).toHaveBeenCalledWith('id', 'my-session');
214
96
  });
215
97
 
216
98
  it('should accept all valid activity types', async () => {
@@ -228,10 +110,11 @@ describe('startFallbackActivity', () => {
228
110
  ];
229
111
 
230
112
  for (const activity of validActivities) {
231
- const supabase = createMockSupabase({
232
- updateResult: { data: null, error: null },
113
+ mockApiClient.startFallbackActivity.mockResolvedValue({
114
+ ok: true,
115
+ data: { message: `Started ${activity}` },
233
116
  });
234
- const ctx = createMockContext(supabase);
117
+ const ctx = createMockContext();
235
118
 
236
119
  const result = await startFallbackActivity(
237
120
  {
@@ -248,25 +131,97 @@ describe('startFallbackActivity', () => {
248
131
  }
249
132
  });
250
133
 
251
- it('should throw error when update fails', async () => {
252
- const supabase = createMockSupabase();
253
- const ctx = createMockContext(supabase);
254
-
255
- // Override update to return error
256
- vi.mocked(supabase.from('').update).mockReturnValue({
257
- ...supabase,
258
- eq: vi.fn().mockReturnValue({
259
- then: (resolve: (val: unknown) => void) =>
260
- Promise.resolve({ data: null, error: { message: 'Update failed' } }).then(resolve),
261
- }),
262
- } as unknown as ReturnType<SupabaseClient['from']>);
134
+ it('should throw error when API call fails', async () => {
135
+ mockApiClient.startFallbackActivity.mockResolvedValue({
136
+ ok: false,
137
+ error: 'Failed to start activity',
138
+ });
139
+ const ctx = createMockContext();
263
140
 
264
141
  await expect(
265
142
  startFallbackActivity({
266
143
  project_id: '123e4567-e89b-12d3-a456-426614174000',
267
144
  activity: 'code_review',
268
145
  }, ctx)
269
- ).rejects.toThrow();
146
+ ).rejects.toThrow('Failed to start fallback activity');
147
+ });
148
+
149
+ it('should pass through worktree guidance when API returns it', async () => {
150
+ mockApiClient.startFallbackActivity.mockResolvedValue({
151
+ ok: true,
152
+ data: {
153
+ success: true,
154
+ activity: 'code_review',
155
+ message: 'Started code_review',
156
+ git_workflow: {
157
+ workflow: 'git-flow',
158
+ base_branch: 'develop',
159
+ worktree_recommended: true,
160
+ note: 'Fallback activities use the base branch directly (read-only).',
161
+ },
162
+ worktree_setup: {
163
+ message: 'RECOMMENDED: Create a worktree to avoid conflicts.',
164
+ commands: [
165
+ 'git checkout develop',
166
+ 'git pull origin develop',
167
+ 'git worktree add ../Project-code-review develop',
168
+ 'cd ../Project-code-review',
169
+ ],
170
+ worktree_path: '../Project-code-review',
171
+ branch_name: 'develop',
172
+ cleanup_command: 'git worktree remove ../Project-code-review',
173
+ report_worktree: 'heartbeat(current_worktree_path: "../Project-code-review")',
174
+ },
175
+ next_step: 'After setting up worktree: call heartbeat to report your location.',
176
+ },
177
+ });
178
+ const ctx = createMockContext();
179
+
180
+ const result = await startFallbackActivity(
181
+ {
182
+ project_id: '123e4567-e89b-12d3-a456-426614174000',
183
+ activity: 'code_review',
184
+ },
185
+ ctx
186
+ );
187
+
188
+ expect(result.result).toMatchObject({
189
+ success: true,
190
+ activity: 'code_review',
191
+ });
192
+ expect((result.result as { git_workflow?: unknown }).git_workflow).toBeDefined();
193
+ expect((result.result as { git_workflow: { workflow: string } }).git_workflow.workflow).toBe('git-flow');
194
+ expect((result.result as { worktree_setup?: unknown }).worktree_setup).toBeDefined();
195
+ expect((result.result as { worktree_setup: { worktree_path: string } }).worktree_setup.worktree_path).toBe('../Project-code-review');
196
+ expect((result.result as { next_step?: string }).next_step).toContain('heartbeat');
197
+ });
198
+
199
+ it('should not include worktree guidance when API does not return it', async () => {
200
+ mockApiClient.startFallbackActivity.mockResolvedValue({
201
+ ok: true,
202
+ data: {
203
+ success: true,
204
+ activity: 'code_review',
205
+ message: 'Started code_review',
206
+ },
207
+ });
208
+ const ctx = createMockContext();
209
+
210
+ const result = await startFallbackActivity(
211
+ {
212
+ project_id: '123e4567-e89b-12d3-a456-426614174000',
213
+ activity: 'code_review',
214
+ },
215
+ ctx
216
+ );
217
+
218
+ expect(result.result).toMatchObject({
219
+ success: true,
220
+ activity: 'code_review',
221
+ });
222
+ expect((result.result as { git_workflow?: unknown }).git_workflow).toBeUndefined();
223
+ expect((result.result as { worktree_setup?: unknown }).worktree_setup).toBeUndefined();
224
+ expect((result.result as { next_step?: string }).next_step).toBeUndefined();
270
225
  });
271
226
  });
272
227
 
@@ -278,46 +233,25 @@ describe('stopFallbackActivity', () => {
278
233
  beforeEach(() => vi.clearAllMocks());
279
234
 
280
235
  it('should throw error for missing project_id', async () => {
281
- const supabase = createMockSupabase();
282
- const ctx = createMockContext(supabase);
236
+ const ctx = createMockContext();
283
237
 
284
238
  await expect(stopFallbackActivity({}, ctx)).rejects.toThrow(ValidationError);
285
239
  });
286
240
 
287
241
  it('should throw error for invalid project_id UUID', async () => {
288
- const supabase = createMockSupabase();
289
- const ctx = createMockContext(supabase);
242
+ const ctx = createMockContext();
290
243
 
291
244
  await expect(
292
245
  stopFallbackActivity({ project_id: 'invalid' }, ctx)
293
246
  ).rejects.toThrow(ValidationError);
294
247
  });
295
248
 
296
- it('should stop fallback activity successfully when activity exists', async () => {
297
- const supabase = createMockSupabase({
298
- selectResult: { data: { current_fallback_activity: 'code_review' }, error: null },
299
- insertResult: { data: null, error: null },
300
- updateResult: { data: null, error: null },
249
+ it('should stop fallback activity successfully', async () => {
250
+ mockApiClient.stopFallbackActivity.mockResolvedValue({
251
+ ok: true,
252
+ data: { success: true },
301
253
  });
302
- const ctx = createMockContext(supabase);
303
-
304
- const result = await stopFallbackActivity(
305
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
306
- ctx
307
- );
308
-
309
- expect(result.result).toMatchObject({
310
- success: true,
311
- });
312
- expect((result.result as { message: string }).message).toContain('code_review');
313
- });
314
-
315
- it('should stop fallback activity when no activity was active', async () => {
316
- const supabase = createMockSupabase({
317
- selectResult: { data: { current_fallback_activity: null }, error: null },
318
- updateResult: { data: null, error: null },
319
- });
320
- const ctx = createMockContext(supabase);
254
+ const ctx = createMockContext();
321
255
 
322
256
  const result = await stopFallbackActivity(
323
257
  { project_id: '123e4567-e89b-12d3-a456-426614174000' },
@@ -330,13 +264,12 @@ describe('stopFallbackActivity', () => {
330
264
  });
331
265
  });
332
266
 
333
- it('should include summary in history when provided', async () => {
334
- const supabase = createMockSupabase({
335
- selectResult: { data: { current_fallback_activity: 'code_review' }, error: null },
336
- insertResult: { data: null, error: null },
337
- updateResult: { data: null, error: null },
267
+ it('should call API client with summary when provided', async () => {
268
+ mockApiClient.stopFallbackActivity.mockResolvedValue({
269
+ ok: true,
270
+ data: { success: true },
338
271
  });
339
- const ctx = createMockContext(supabase);
272
+ const ctx = createMockContext({ sessionId: 'my-session' });
340
273
 
341
274
  await stopFallbackActivity(
342
275
  {
@@ -346,32 +279,25 @@ describe('stopFallbackActivity', () => {
346
279
  ctx
347
280
  );
348
281
 
349
- expect(supabase.from).toHaveBeenCalledWith('background_activity_history');
350
- expect(supabase.insert).toHaveBeenCalledWith(
351
- expect.objectContaining({
352
- summary: 'Reviewed 5 files, found 3 issues',
353
- })
282
+ expect(mockApiClient.stopFallbackActivity).toHaveBeenCalledWith(
283
+ '123e4567-e89b-12d3-a456-426614174000',
284
+ 'Reviewed 5 files, found 3 issues',
285
+ 'my-session'
354
286
  );
355
287
  });
356
288
 
357
- it('should clear session fallback activity', async () => {
358
- const supabase = createMockSupabase({
359
- selectResult: { data: { current_fallback_activity: null }, error: null },
360
- updateResult: { data: null, error: null },
289
+ it('should throw error when API call fails', async () => {
290
+ mockApiClient.stopFallbackActivity.mockResolvedValue({
291
+ ok: false,
292
+ error: 'Failed to stop activity',
361
293
  });
362
- const ctx = createMockContext(supabase, { sessionId: 'my-session' });
294
+ const ctx = createMockContext();
363
295
 
364
- await stopFallbackActivity(
365
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
366
- ctx
367
- );
368
-
369
- expect(supabase.update).toHaveBeenCalledWith(
370
- expect.objectContaining({
371
- current_fallback_activity: null,
372
- status: 'idle',
373
- })
374
- );
296
+ await expect(
297
+ stopFallbackActivity({
298
+ project_id: '123e4567-e89b-12d3-a456-426614174000',
299
+ }, ctx)
300
+ ).rejects.toThrow('Failed to stop fallback activity');
375
301
  });
376
302
  });
377
303
 
@@ -383,15 +309,13 @@ describe('getActivityHistory', () => {
383
309
  beforeEach(() => vi.clearAllMocks());
384
310
 
385
311
  it('should throw error for missing project_id', async () => {
386
- const supabase = createMockSupabase();
387
- const ctx = createMockContext(supabase);
312
+ const ctx = createMockContext();
388
313
 
389
314
  await expect(getActivityHistory({}, ctx)).rejects.toThrow(ValidationError);
390
315
  });
391
316
 
392
317
  it('should throw error for invalid project_id UUID', async () => {
393
- const supabase = createMockSupabase();
394
- const ctx = createMockContext(supabase);
318
+ const ctx = createMockContext();
395
319
 
396
320
  await expect(
397
321
  getActivityHistory({ project_id: 'invalid' }, ctx)
@@ -399,10 +323,15 @@ describe('getActivityHistory', () => {
399
323
  });
400
324
 
401
325
  it('should return empty history when no activities', async () => {
402
- const supabase = createMockSupabase({
403
- selectResult: { data: [], error: null },
326
+ mockApiClient.proxy.mockResolvedValue({
327
+ ok: true,
328
+ data: {
329
+ history: [],
330
+ latest_by_type: {},
331
+ count: 0,
332
+ },
404
333
  });
405
- const ctx = createMockContext(supabase);
334
+ const ctx = createMockContext();
406
335
 
407
336
  const result = await getActivityHistory(
408
337
  { project_id: '123e4567-e89b-12d3-a456-426614174000' },
@@ -415,28 +344,31 @@ describe('getActivityHistory', () => {
415
344
  });
416
345
  });
417
346
 
418
- it('should return activity history', async () => {
347
+ it('should return activity history from API', async () => {
419
348
  const mockHistory = [
420
349
  {
421
350
  id: 'h1',
422
351
  activity_type: 'code_review',
423
352
  completed_at: '2025-01-14T10:00:00Z',
424
353
  summary: 'Reviewed auth module',
425
- agent_sessions: { agent_name: 'Wave' },
426
354
  },
427
355
  {
428
356
  id: 'h2',
429
357
  activity_type: 'security_review',
430
358
  completed_at: '2025-01-14T09:00:00Z',
431
359
  summary: null,
432
- agent_sessions: { agent_name: 'Glitch' },
433
360
  },
434
361
  ];
435
362
 
436
- const supabase = createMockSupabase({
437
- selectResult: { data: mockHistory, error: null },
363
+ mockApiClient.proxy.mockResolvedValue({
364
+ ok: true,
365
+ data: {
366
+ history: mockHistory,
367
+ latest_by_type: { code_review: mockHistory[0] },
368
+ count: 2,
369
+ },
438
370
  });
439
- const ctx = createMockContext(supabase);
371
+ const ctx = createMockContext();
440
372
 
441
373
  const result = await getActivityHistory(
442
374
  { project_id: '123e4567-e89b-12d3-a456-426614174000' },
@@ -447,83 +379,42 @@ describe('getActivityHistory', () => {
447
379
  expect((result.result as { count: number }).count).toBe(2);
448
380
  });
449
381
 
450
- it('should include latest_by_type in response', async () => {
451
- const mockHistory = [
452
- {
453
- id: 'h1',
454
- activity_type: 'code_review',
455
- completed_at: '2025-01-14T10:00:00Z',
456
- agent_sessions: null,
457
- },
458
- {
459
- id: 'h2',
460
- activity_type: 'code_review',
461
- completed_at: '2025-01-14T09:00:00Z',
462
- agent_sessions: null,
463
- },
464
- ];
465
-
466
- const supabase = createMockSupabase({
467
- selectResult: { data: mockHistory, error: null },
382
+ it('should call API proxy with correct parameters', async () => {
383
+ mockApiClient.proxy.mockResolvedValue({
384
+ ok: true,
385
+ data: { history: [], count: 0 },
468
386
  });
469
- const ctx = createMockContext(supabase);
470
-
471
- const result = await getActivityHistory(
472
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
473
- ctx
474
- );
475
-
476
- const latestByType = (result.result as { latest_by_type: Record<string, unknown> }).latest_by_type;
477
- expect(latestByType['code_review']).toBeDefined();
478
- expect((latestByType['code_review'] as { id: string }).id).toBe('h1');
479
- });
480
-
481
- it('should filter by activity_type when provided', async () => {
482
- const supabase = createMockSupabase({
483
- selectResult: { data: [], error: null },
484
- });
485
- const ctx = createMockContext(supabase);
387
+ const ctx = createMockContext();
486
388
 
487
389
  await getActivityHistory(
488
390
  {
489
391
  project_id: '123e4567-e89b-12d3-a456-426614174000',
490
392
  activity_type: 'security_review',
393
+ limit: 10,
491
394
  },
492
395
  ctx
493
396
  );
494
397
 
495
- expect(supabase.eq).toHaveBeenCalledWith('activity_type', 'security_review');
496
- });
497
-
498
- it('should respect limit parameter', async () => {
499
- const supabase = createMockSupabase({
500
- selectResult: { data: [], error: null },
501
- });
502
- const ctx = createMockContext(supabase);
503
-
504
- await getActivityHistory(
398
+ expect(mockApiClient.proxy).toHaveBeenCalledWith(
399
+ 'get_activity_history',
505
400
  {
506
401
  project_id: '123e4567-e89b-12d3-a456-426614174000',
402
+ activity_type: 'security_review',
507
403
  limit: 10,
508
- },
509
- ctx
404
+ }
510
405
  );
511
-
512
- expect(supabase.limit).toHaveBeenCalledWith(10);
513
406
  });
514
407
 
515
- it('should query background_activity_history table', async () => {
516
- const supabase = createMockSupabase({
517
- selectResult: { data: [], error: null },
408
+ it('should throw error when API call fails', async () => {
409
+ mockApiClient.proxy.mockResolvedValue({
410
+ ok: false,
411
+ error: 'Query failed',
518
412
  });
519
- const ctx = createMockContext(supabase);
520
-
521
- await getActivityHistory(
522
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
523
- ctx
524
- );
413
+ const ctx = createMockContext();
525
414
 
526
- expect(supabase.from).toHaveBeenCalledWith('background_activity_history');
415
+ await expect(
416
+ getActivityHistory({ project_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
417
+ ).rejects.toThrow('Failed to get activity history');
527
418
  });
528
419
  });
529
420
 
@@ -535,15 +426,13 @@ describe('getActivitySchedules', () => {
535
426
  beforeEach(() => vi.clearAllMocks());
536
427
 
537
428
  it('should throw error for missing project_id', async () => {
538
- const supabase = createMockSupabase();
539
- const ctx = createMockContext(supabase);
429
+ const ctx = createMockContext();
540
430
 
541
431
  await expect(getActivitySchedules({}, ctx)).rejects.toThrow(ValidationError);
542
432
  });
543
433
 
544
434
  it('should throw error for invalid project_id UUID', async () => {
545
- const supabase = createMockSupabase();
546
- const ctx = createMockContext(supabase);
435
+ const ctx = createMockContext();
547
436
 
548
437
  await expect(
549
438
  getActivitySchedules({ project_id: 'invalid' }, ctx)
@@ -551,10 +440,15 @@ describe('getActivitySchedules', () => {
551
440
  });
552
441
 
553
442
  it('should return empty schedules when none exist', async () => {
554
- const supabase = createMockSupabase({
555
- selectResult: { data: [], error: null },
443
+ mockApiClient.proxy.mockResolvedValue({
444
+ ok: true,
445
+ data: {
446
+ schedules: [],
447
+ due_activities: [],
448
+ count: 0,
449
+ },
556
450
  });
557
- const ctx = createMockContext(supabase);
451
+ const ctx = createMockContext();
558
452
 
559
453
  const result = await getActivitySchedules(
560
454
  { project_id: '123e4567-e89b-12d3-a456-426614174000' },
@@ -568,7 +462,7 @@ describe('getActivitySchedules', () => {
568
462
  });
569
463
  });
570
464
 
571
- it('should return activity schedules', async () => {
465
+ it('should return activity schedules from API', async () => {
572
466
  const mockSchedules = [
573
467
  {
574
468
  id: 's1',
@@ -586,10 +480,15 @@ describe('getActivitySchedules', () => {
586
480
  },
587
481
  ];
588
482
 
589
- const supabase = createMockSupabase({
590
- selectResult: { data: mockSchedules, error: null },
483
+ mockApiClient.proxy.mockResolvedValue({
484
+ ok: true,
485
+ data: {
486
+ schedules: mockSchedules,
487
+ due_activities: ['code_review'],
488
+ count: 2,
489
+ },
591
490
  });
592
- const ctx = createMockContext(supabase);
491
+ const ctx = createMockContext();
593
492
 
594
493
  const result = await getActivitySchedules(
595
494
  { project_id: '123e4567-e89b-12d3-a456-426614174000' },
@@ -598,90 +497,36 @@ describe('getActivitySchedules', () => {
598
497
 
599
498
  expect((result.result as { schedules: unknown[] }).schedules).toHaveLength(2);
600
499
  expect((result.result as { count: number }).count).toBe(2);
500
+ expect((result.result as { due_activities: string[] }).due_activities).toContain('code_review');
601
501
  });
602
502
 
603
- it('should identify due activities', async () => {
604
- const pastDate = new Date(Date.now() - 3600000).toISOString(); // 1 hour ago
605
- const futureDate = new Date(Date.now() + 3600000).toISOString(); // 1 hour from now
606
-
607
- const mockSchedules = [
608
- {
609
- id: 's1',
610
- activity_type: 'code_review',
611
- enabled: true,
612
- next_run_at: pastDate, // Due
613
- },
614
- {
615
- id: 's2',
616
- activity_type: 'security_review',
617
- enabled: true,
618
- next_run_at: futureDate, // Not due
619
- },
620
- {
621
- id: 's3',
622
- activity_type: 'test_coverage',
623
- enabled: false, // Disabled
624
- next_run_at: pastDate,
625
- },
626
- ];
627
-
628
- const supabase = createMockSupabase({
629
- selectResult: { data: mockSchedules, error: null },
630
- });
631
- const ctx = createMockContext(supabase);
632
-
633
- const result = await getActivitySchedules(
634
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
635
- ctx
636
- );
637
-
638
- const dueActivities = (result.result as { due_activities: string[] }).due_activities;
639
- expect(dueActivities).toContain('code_review');
640
- expect(dueActivities).not.toContain('security_review');
641
- expect(dueActivities).not.toContain('test_coverage');
642
- });
643
-
644
- it('should query background_activity_schedules table', async () => {
645
- const supabase = createMockSupabase({
646
- selectResult: { data: [], error: null },
503
+ it('should call API proxy with correct parameters', async () => {
504
+ mockApiClient.proxy.mockResolvedValue({
505
+ ok: true,
506
+ data: { schedules: [], due_activities: [], count: 0 },
647
507
  });
648
- const ctx = createMockContext(supabase);
508
+ const ctx = createMockContext();
649
509
 
650
510
  await getActivitySchedules(
651
511
  { project_id: '123e4567-e89b-12d3-a456-426614174000' },
652
512
  ctx
653
513
  );
654
514
 
655
- expect(supabase.from).toHaveBeenCalledWith('background_activity_schedules');
656
- });
657
-
658
- it('should order schedules by activity_type', async () => {
659
- const supabase = createMockSupabase({
660
- selectResult: { data: [], error: null },
661
- });
662
- const ctx = createMockContext(supabase);
663
-
664
- await getActivitySchedules(
665
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
666
- ctx
515
+ expect(mockApiClient.proxy).toHaveBeenCalledWith(
516
+ 'get_activity_schedules',
517
+ { project_id: '123e4567-e89b-12d3-a456-426614174000' }
667
518
  );
668
-
669
- expect(supabase.order).toHaveBeenCalledWith('activity_type');
670
519
  });
671
520
 
672
- it('should throw error when query fails', async () => {
673
- const supabase = createMockSupabase();
674
- const ctx = createMockContext(supabase);
675
-
676
- // Override to return error
677
- vi.mocked(supabase.from('').select).mockReturnValue({
678
- ...supabase,
679
- then: (resolve: (val: unknown) => void) =>
680
- Promise.resolve({ data: null, error: { message: 'Query failed' } }).then(resolve),
681
- } as unknown as ReturnType<SupabaseClient['from']>);
521
+ it('should throw error when API call fails', async () => {
522
+ mockApiClient.proxy.mockResolvedValue({
523
+ ok: false,
524
+ error: 'Query failed',
525
+ });
526
+ const ctx = createMockContext();
682
527
 
683
528
  await expect(
684
529
  getActivitySchedules({ project_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
685
- ).rejects.toThrow();
530
+ ).rejects.toThrow('Failed to get activity schedules');
686
531
  });
687
532
  });