@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,4 +1,4 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
2
  import {
3
3
  addIdea,
4
4
  updateIdea,
@@ -7,18 +7,18 @@ import {
7
7
  convertIdeaToTask,
8
8
  } from './ideas.js';
9
9
  import { ValidationError } from '../validators.js';
10
- import { createMockSupabase, createMockContext } from './__test-utils__.js';
10
+ import { createMockContext } from './__test-utils__.js';
11
+ import { mockApiClient } from './__test-setup__.js';
11
12
 
12
13
  // ============================================================================
13
14
  // addIdea Tests
14
15
  // ============================================================================
15
16
 
16
17
  describe('addIdea', () => {
17
- beforeEach(() => vi.clearAllMocks());
18
+ beforeEach(() => {});
18
19
 
19
20
  it('should throw error for missing project_id', async () => {
20
- const supabase = createMockSupabase();
21
- const ctx = createMockContext(supabase);
21
+ const ctx = createMockContext();
22
22
 
23
23
  await expect(
24
24
  addIdea({ title: 'New Feature' }, ctx)
@@ -26,8 +26,7 @@ describe('addIdea', () => {
26
26
  });
27
27
 
28
28
  it('should throw error for invalid project_id UUID', async () => {
29
- const supabase = createMockSupabase();
30
- const ctx = createMockContext(supabase);
29
+ const ctx = createMockContext();
31
30
 
32
31
  await expect(
33
32
  addIdea({ project_id: 'invalid', title: 'New Feature' }, ctx)
@@ -35,8 +34,7 @@ describe('addIdea', () => {
35
34
  });
36
35
 
37
36
  it('should throw error for missing title', async () => {
38
- const supabase = createMockSupabase();
39
- const ctx = createMockContext(supabase);
37
+ const ctx = createMockContext();
40
38
 
41
39
  await expect(
42
40
  addIdea({ project_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
@@ -44,10 +42,11 @@ describe('addIdea', () => {
44
42
  });
45
43
 
46
44
  it('should add idea successfully', async () => {
47
- const supabase = createMockSupabase({
48
- insertResult: { data: { id: 'idea-1' }, error: null },
45
+ mockApiClient.addIdea.mockResolvedValue({
46
+ ok: true,
47
+ data: { idea_id: 'idea-1' },
49
48
  });
50
- const ctx = createMockContext(supabase);
49
+ const ctx = createMockContext();
51
50
 
52
51
  const result = await addIdea(
53
52
  {
@@ -64,11 +63,12 @@ describe('addIdea', () => {
64
63
  });
65
64
  });
66
65
 
67
- it('should insert with default status "raw"', async () => {
68
- const supabase = createMockSupabase({
69
- insertResult: { data: { id: 'idea-1' }, error: null },
66
+ it('should call API client with default status "raw"', async () => {
67
+ mockApiClient.addIdea.mockResolvedValue({
68
+ ok: true,
69
+ data: { idea_id: 'idea-1' },
70
70
  });
71
- const ctx = createMockContext(supabase);
71
+ const ctx = createMockContext();
72
72
 
73
73
  await addIdea(
74
74
  {
@@ -78,18 +78,23 @@ describe('addIdea', () => {
78
78
  ctx
79
79
  );
80
80
 
81
- expect(supabase.insert).toHaveBeenCalledWith(
82
- expect.objectContaining({
83
- status: 'raw',
84
- })
81
+ expect(mockApiClient.addIdea).toHaveBeenCalledWith(
82
+ '123e4567-e89b-12d3-a456-426614174000',
83
+ {
84
+ title: 'New Feature',
85
+ description: undefined,
86
+ status: undefined, // Default status applied server-side
87
+ },
88
+ 'session-123'
85
89
  );
86
90
  });
87
91
 
88
- it('should insert with custom status', async () => {
89
- const supabase = createMockSupabase({
90
- insertResult: { data: { id: 'idea-1' }, error: null },
92
+ it('should call API client with custom status', async () => {
93
+ mockApiClient.addIdea.mockResolvedValue({
94
+ ok: true,
95
+ data: { idea_id: 'idea-1' },
91
96
  });
92
- const ctx = createMockContext(supabase);
97
+ const ctx = createMockContext();
93
98
 
94
99
  await addIdea(
95
100
  {
@@ -100,18 +105,23 @@ describe('addIdea', () => {
100
105
  ctx
101
106
  );
102
107
 
103
- expect(supabase.insert).toHaveBeenCalledWith(
104
- expect.objectContaining({
108
+ expect(mockApiClient.addIdea).toHaveBeenCalledWith(
109
+ '123e4567-e89b-12d3-a456-426614174000',
110
+ {
111
+ title: 'New Feature',
112
+ description: undefined,
105
113
  status: 'exploring',
106
- })
114
+ },
115
+ 'session-123'
107
116
  );
108
117
  });
109
118
 
110
- it('should include session_id in insert', async () => {
111
- const supabase = createMockSupabase({
112
- insertResult: { data: { id: 'idea-1' }, error: null },
119
+ it('should include session_id in API call', async () => {
120
+ mockApiClient.addIdea.mockResolvedValue({
121
+ ok: true,
122
+ data: { idea_id: 'idea-1' },
113
123
  });
114
- const ctx = createMockContext(supabase, { sessionId: 'my-session' });
124
+ const ctx = createMockContext({ sessionId: 'my-session' });
115
125
 
116
126
  await addIdea(
117
127
  {
@@ -121,19 +131,19 @@ describe('addIdea', () => {
121
131
  ctx
122
132
  );
123
133
 
124
- expect(supabase.insert).toHaveBeenCalledWith(
125
- expect.objectContaining({
126
- created_by: 'agent',
127
- created_by_session_id: 'my-session',
128
- })
134
+ expect(mockApiClient.addIdea).toHaveBeenCalledWith(
135
+ '123e4567-e89b-12d3-a456-426614174000',
136
+ expect.any(Object),
137
+ 'my-session'
129
138
  );
130
139
  });
131
140
 
132
- it('should throw error when insert fails', async () => {
133
- const supabase = createMockSupabase({
134
- insertResult: { data: null, error: { message: 'Insert failed' } },
141
+ it('should throw error when API call fails', async () => {
142
+ mockApiClient.addIdea.mockResolvedValue({
143
+ ok: false,
144
+ error: 'Insert failed',
135
145
  });
136
- const ctx = createMockContext(supabase);
146
+ const ctx = createMockContext();
137
147
 
138
148
  await expect(
139
149
  addIdea({
@@ -149,41 +159,28 @@ describe('addIdea', () => {
149
159
  // ============================================================================
150
160
 
151
161
  describe('updateIdea', () => {
152
- beforeEach(() => vi.clearAllMocks());
162
+ beforeEach(() => {});
153
163
 
154
164
  it('should throw error for missing idea_id', async () => {
155
- const supabase = createMockSupabase();
156
- const ctx = createMockContext(supabase);
165
+ const ctx = createMockContext();
157
166
 
158
167
  await expect(updateIdea({}, ctx)).rejects.toThrow(ValidationError);
159
168
  });
160
169
 
161
170
  it('should throw error for invalid idea_id UUID', async () => {
162
- const supabase = createMockSupabase();
163
- const ctx = createMockContext(supabase);
171
+ const ctx = createMockContext();
164
172
 
165
173
  await expect(
166
174
  updateIdea({ idea_id: 'invalid' }, ctx)
167
175
  ).rejects.toThrow(ValidationError);
168
176
  });
169
177
 
170
- it('should throw error when idea not found', async () => {
171
- const supabase = createMockSupabase({
172
- selectResult: { data: null, error: null },
173
- });
174
- const ctx = createMockContext(supabase);
175
-
176
- await expect(
177
- updateIdea({ idea_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
178
- ).rejects.toThrow('Idea not found');
179
- });
180
-
181
178
  it('should update idea title', async () => {
182
- const supabase = createMockSupabase({
183
- selectResult: { data: { status: 'raw' }, error: null },
184
- updateResult: { data: null, error: null },
179
+ mockApiClient.updateIdea.mockResolvedValue({
180
+ ok: true,
181
+ data: { idea: { id: 'idea-1', title: 'Updated Title' } },
185
182
  });
186
- const ctx = createMockContext(supabase);
183
+ const ctx = createMockContext();
187
184
 
188
185
  const result = await updateIdea(
189
186
  {
@@ -197,20 +194,14 @@ describe('updateIdea', () => {
197
194
  success: true,
198
195
  idea_id: '123e4567-e89b-12d3-a456-426614174000',
199
196
  });
200
- expect(supabase.update).toHaveBeenCalledWith(
201
- expect.objectContaining({
202
- title: 'Updated Title',
203
- updated_at: expect.any(String),
204
- })
205
- );
206
197
  });
207
198
 
208
- it('should set planned_at when transitioning to planned status', async () => {
209
- const supabase = createMockSupabase({
210
- selectResult: { data: { status: 'exploring' }, error: null },
211
- updateResult: { data: null, error: null },
199
+ it('should call API client with status update', async () => {
200
+ mockApiClient.updateIdea.mockResolvedValue({
201
+ ok: true,
202
+ data: { idea: { id: 'idea-1', status: 'planned' } },
212
203
  });
213
- const ctx = createMockContext(supabase);
204
+ const ctx = createMockContext();
214
205
 
215
206
  await updateIdea(
216
207
  {
@@ -220,43 +211,23 @@ describe('updateIdea', () => {
220
211
  ctx
221
212
  );
222
213
 
223
- expect(supabase.update).toHaveBeenCalledWith(
224
- expect.objectContaining({
225
- status: 'planned',
226
- planned_at: expect.any(String),
227
- })
228
- );
229
- });
230
-
231
- it('should not set planned_at when already in planned status', async () => {
232
- const supabase = createMockSupabase({
233
- selectResult: { data: { status: 'planned' }, error: null },
234
- updateResult: { data: null, error: null },
235
- });
236
- const ctx = createMockContext(supabase);
237
-
238
- await updateIdea(
214
+ expect(mockApiClient.updateIdea).toHaveBeenCalledWith(
215
+ '123e4567-e89b-12d3-a456-426614174000',
239
216
  {
240
- idea_id: '123e4567-e89b-12d3-a456-426614174000',
217
+ title: undefined,
218
+ description: undefined,
241
219
  status: 'planned',
242
- },
243
- ctx
244
- );
245
-
246
- // Should NOT have planned_at since it's already planned
247
- expect(supabase.update).toHaveBeenCalledWith(
248
- expect.not.objectContaining({
249
- planned_at: expect.any(String),
250
- })
220
+ doc_url: undefined,
221
+ }
251
222
  );
252
223
  });
253
224
 
254
225
  it('should update doc_url', async () => {
255
- const supabase = createMockSupabase({
256
- selectResult: { data: { status: 'exploring' }, error: null },
257
- updateResult: { data: null, error: null },
226
+ mockApiClient.updateIdea.mockResolvedValue({
227
+ ok: true,
228
+ data: { idea: { id: 'idea-1' } },
258
229
  });
259
- const ctx = createMockContext(supabase);
230
+ const ctx = createMockContext();
260
231
 
261
232
  await updateIdea(
262
233
  {
@@ -266,27 +237,23 @@ describe('updateIdea', () => {
266
237
  ctx
267
238
  );
268
239
 
269
- expect(supabase.update).toHaveBeenCalledWith(
270
- expect.objectContaining({
240
+ expect(mockApiClient.updateIdea).toHaveBeenCalledWith(
241
+ '123e4567-e89b-12d3-a456-426614174000',
242
+ {
243
+ title: undefined,
244
+ description: undefined,
245
+ status: undefined,
271
246
  doc_url: 'https://docs.example.com/feature',
272
- })
247
+ }
273
248
  );
274
249
  });
275
250
 
276
- it('should throw error when update fails', async () => {
277
- const supabase = createMockSupabase({
278
- selectResult: { data: { status: 'raw' }, error: null },
251
+ it('should throw error when API call fails', async () => {
252
+ mockApiClient.updateIdea.mockResolvedValue({
253
+ ok: false,
254
+ error: 'Idea not found',
279
255
  });
280
- const ctx = createMockContext(supabase);
281
-
282
- // Override update to return error
283
- vi.mocked(supabase.from('').update).mockReturnValue({
284
- ...supabase,
285
- eq: vi.fn().mockReturnValue({
286
- then: (resolve: (val: unknown) => void) =>
287
- Promise.resolve({ data: null, error: { message: 'Update failed' } }).then(resolve),
288
- }),
289
- } as unknown as ReturnType<SupabaseClient['from']>);
256
+ const ctx = createMockContext();
290
257
 
291
258
  await expect(
292
259
  updateIdea({ idea_id: '123e4567-e89b-12d3-a456-426614174000', title: 'New' }, ctx)
@@ -299,18 +266,16 @@ describe('updateIdea', () => {
299
266
  // ============================================================================
300
267
 
301
268
  describe('getIdeas', () => {
302
- beforeEach(() => vi.clearAllMocks());
269
+ beforeEach(() => {});
303
270
 
304
271
  it('should throw error for missing project_id', async () => {
305
- const supabase = createMockSupabase();
306
- const ctx = createMockContext(supabase);
272
+ const ctx = createMockContext();
307
273
 
308
274
  await expect(getIdeas({}, ctx)).rejects.toThrow(ValidationError);
309
275
  });
310
276
 
311
277
  it('should throw error for invalid project_id UUID', async () => {
312
- const supabase = createMockSupabase();
313
- const ctx = createMockContext(supabase);
278
+ const ctx = createMockContext();
314
279
 
315
280
  await expect(
316
281
  getIdeas({ project_id: 'invalid' }, ctx)
@@ -318,17 +283,11 @@ describe('getIdeas', () => {
318
283
  });
319
284
 
320
285
  it('should return empty list when no ideas', async () => {
321
- const supabase = createMockSupabase({
322
- selectResult: { data: [], error: null },
286
+ mockApiClient.getIdeas.mockResolvedValue({
287
+ ok: true,
288
+ data: { ideas: [] },
323
289
  });
324
- const ctx = createMockContext(supabase);
325
-
326
- // Override to return array result
327
- vi.mocked(supabase.from('').select).mockReturnValue({
328
- ...supabase,
329
- then: (resolve: (val: unknown) => void) =>
330
- Promise.resolve({ data: [], error: null }).then(resolve),
331
- } as unknown as ReturnType<SupabaseClient['from']>);
290
+ const ctx = createMockContext();
332
291
 
333
292
  const result = await getIdeas(
334
293
  { project_id: '123e4567-e89b-12d3-a456-426614174000' },
@@ -344,17 +303,11 @@ describe('getIdeas', () => {
344
303
  { id: 'i2', title: 'Idea 2', description: 'Some desc', status: 'exploring', doc_url: null },
345
304
  ];
346
305
 
347
- const supabase = createMockSupabase({
348
- selectResult: { data: mockIdeas, error: null },
306
+ mockApiClient.getIdeas.mockResolvedValue({
307
+ ok: true,
308
+ data: { ideas: mockIdeas },
349
309
  });
350
- const ctx = createMockContext(supabase);
351
-
352
- // Override to return array result
353
- vi.mocked(supabase.from('').select).mockReturnValue({
354
- ...supabase,
355
- then: (resolve: (val: unknown) => void) =>
356
- Promise.resolve({ data: mockIdeas, error: null }).then(resolve),
357
- } as unknown as ReturnType<SupabaseClient['from']>);
310
+ const ctx = createMockContext();
358
311
 
359
312
  const result = await getIdeas(
360
313
  { project_id: '123e4567-e89b-12d3-a456-426614174000' },
@@ -364,58 +317,58 @@ describe('getIdeas', () => {
364
317
  expect((result.result as { ideas: unknown[] }).ideas).toHaveLength(2);
365
318
  });
366
319
 
367
- it('should filter by status when provided', async () => {
368
- const supabase = createMockSupabase({
369
- selectResult: { data: [], error: null },
320
+ it('should pass status filter to API', async () => {
321
+ mockApiClient.getIdeas.mockResolvedValue({
322
+ ok: true,
323
+ data: { ideas: [] },
370
324
  });
371
- const ctx = createMockContext(supabase);
325
+ const ctx = createMockContext();
372
326
 
373
327
  await getIdeas(
374
328
  { project_id: '123e4567-e89b-12d3-a456-426614174000', status: 'planned' },
375
329
  ctx
376
330
  );
377
331
 
378
- expect(supabase.eq).toHaveBeenCalledWith('status', 'planned');
379
- });
380
-
381
- it('should query ideas table', async () => {
382
- const supabase = createMockSupabase({
383
- selectResult: { data: [], error: null },
384
- });
385
- const ctx = createMockContext(supabase);
386
-
387
- await getIdeas(
388
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
389
- ctx
332
+ expect(mockApiClient.getIdeas).toHaveBeenCalledWith(
333
+ '123e4567-e89b-12d3-a456-426614174000',
334
+ {
335
+ status: 'planned',
336
+ limit: 50,
337
+ offset: 0,
338
+ search_query: undefined,
339
+ }
390
340
  );
391
-
392
- expect(supabase.from).toHaveBeenCalledWith('ideas');
393
341
  });
394
342
 
395
- it('should order by created_at descending', async () => {
396
- const supabase = createMockSupabase({
397
- selectResult: { data: [], error: null },
343
+ it('should pass limit and offset to API', async () => {
344
+ mockApiClient.getIdeas.mockResolvedValue({
345
+ ok: true,
346
+ data: { ideas: [] },
398
347
  });
399
- const ctx = createMockContext(supabase);
348
+ const ctx = createMockContext();
400
349
 
401
350
  await getIdeas(
402
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
351
+ { project_id: '123e4567-e89b-12d3-a456-426614174000', limit: 10, offset: 5 },
403
352
  ctx
404
353
  );
405
354
 
406
- expect(supabase.order).toHaveBeenCalledWith('created_at', { ascending: false });
355
+ expect(mockApiClient.getIdeas).toHaveBeenCalledWith(
356
+ '123e4567-e89b-12d3-a456-426614174000',
357
+ {
358
+ status: undefined,
359
+ limit: 10,
360
+ offset: 5,
361
+ search_query: undefined,
362
+ }
363
+ );
407
364
  });
408
365
 
409
- it('should throw error when query fails', async () => {
410
- const supabase = createMockSupabase();
411
- const ctx = createMockContext(supabase);
412
-
413
- // Override to return error
414
- vi.mocked(supabase.from('').select).mockReturnValue({
415
- ...supabase,
416
- then: (resolve: (val: unknown) => void) =>
417
- Promise.resolve({ data: null, error: { message: 'Query failed' } }).then(resolve),
418
- } as unknown as ReturnType<SupabaseClient['from']>);
366
+ it('should throw error when API call fails', async () => {
367
+ mockApiClient.getIdeas.mockResolvedValue({
368
+ ok: false,
369
+ error: 'Query failed',
370
+ });
371
+ const ctx = createMockContext();
419
372
 
420
373
  await expect(
421
374
  getIdeas({ project_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
@@ -428,18 +381,16 @@ describe('getIdeas', () => {
428
381
  // ============================================================================
429
382
 
430
383
  describe('deleteIdea', () => {
431
- beforeEach(() => vi.clearAllMocks());
384
+ beforeEach(() => {});
432
385
 
433
386
  it('should throw error for missing idea_id', async () => {
434
- const supabase = createMockSupabase();
435
- const ctx = createMockContext(supabase);
387
+ const ctx = createMockContext();
436
388
 
437
389
  await expect(deleteIdea({}, ctx)).rejects.toThrow(ValidationError);
438
390
  });
439
391
 
440
392
  it('should throw error for invalid idea_id UUID', async () => {
441
- const supabase = createMockSupabase();
442
- const ctx = createMockContext(supabase);
393
+ const ctx = createMockContext();
443
394
 
444
395
  await expect(
445
396
  deleteIdea({ idea_id: 'invalid' }, ctx)
@@ -447,19 +398,11 @@ describe('deleteIdea', () => {
447
398
  });
448
399
 
449
400
  it('should delete idea successfully', async () => {
450
- const supabase = createMockSupabase({
451
- deleteResult: { data: null, error: null },
401
+ mockApiClient.deleteIdea.mockResolvedValue({
402
+ ok: true,
403
+ data: { success: true },
452
404
  });
453
- const ctx = createMockContext(supabase);
454
-
455
- // Override delete to return success
456
- vi.mocked(supabase.from('').delete).mockReturnValue({
457
- ...supabase,
458
- eq: vi.fn().mockReturnValue({
459
- then: (resolve: (val: unknown) => void) =>
460
- Promise.resolve({ data: null, error: null }).then(resolve),
461
- }),
462
- } as unknown as ReturnType<SupabaseClient['from']>);
405
+ const ctx = createMockContext();
463
406
 
464
407
  const result = await deleteIdea(
465
408
  { idea_id: '123e4567-e89b-12d3-a456-426614174000' },
@@ -469,42 +412,29 @@ describe('deleteIdea', () => {
469
412
  expect(result.result).toMatchObject({ success: true });
470
413
  });
471
414
 
472
- it('should call delete on ideas table', async () => {
473
- const supabase = createMockSupabase();
474
- const ctx = createMockContext(supabase);
475
-
476
- const mockEq = vi.fn().mockReturnValue({
477
- then: (resolve: (val: unknown) => void) =>
478
- Promise.resolve({ data: null, error: null }).then(resolve),
415
+ it('should call API client deleteIdea', async () => {
416
+ mockApiClient.deleteIdea.mockResolvedValue({
417
+ ok: true,
418
+ data: { success: true },
479
419
  });
480
-
481
- vi.mocked(supabase.from('').delete).mockReturnValue({
482
- ...supabase,
483
- eq: mockEq,
484
- } as unknown as ReturnType<SupabaseClient['from']>);
420
+ const ctx = createMockContext();
485
421
 
486
422
  await deleteIdea(
487
423
  { idea_id: '123e4567-e89b-12d3-a456-426614174000' },
488
424
  ctx
489
425
  );
490
426
 
491
- expect(supabase.from).toHaveBeenCalledWith('ideas');
492
- expect(supabase.delete).toHaveBeenCalled();
493
- expect(mockEq).toHaveBeenCalledWith('id', '123e4567-e89b-12d3-a456-426614174000');
427
+ expect(mockApiClient.deleteIdea).toHaveBeenCalledWith(
428
+ '123e4567-e89b-12d3-a456-426614174000'
429
+ );
494
430
  });
495
431
 
496
- it('should throw error when delete fails', async () => {
497
- const supabase = createMockSupabase();
498
- const ctx = createMockContext(supabase);
499
-
500
- // Override delete to return error
501
- vi.mocked(supabase.from('').delete).mockReturnValue({
502
- ...supabase,
503
- eq: vi.fn().mockReturnValue({
504
- then: (resolve: (val: unknown) => void) =>
505
- Promise.resolve({ data: null, error: { message: 'Delete failed' } }).then(resolve),
506
- }),
507
- } as unknown as ReturnType<SupabaseClient['from']>);
432
+ it('should throw error when API call fails', async () => {
433
+ mockApiClient.deleteIdea.mockResolvedValue({
434
+ ok: false,
435
+ error: 'Delete failed',
436
+ });
437
+ const ctx = createMockContext();
508
438
 
509
439
  await expect(
510
440
  deleteIdea({ idea_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
@@ -517,18 +447,16 @@ describe('deleteIdea', () => {
517
447
  // ============================================================================
518
448
 
519
449
  describe('convertIdeaToTask', () => {
520
- beforeEach(() => vi.clearAllMocks());
450
+ beforeEach(() => {});
521
451
 
522
452
  it('should throw error for missing idea_id', async () => {
523
- const supabase = createMockSupabase();
524
- const ctx = createMockContext(supabase);
453
+ const ctx = createMockContext();
525
454
 
526
455
  await expect(convertIdeaToTask({}, ctx)).rejects.toThrow(ValidationError);
527
456
  });
528
457
 
529
458
  it('should throw error for invalid idea_id UUID', async () => {
530
- const supabase = createMockSupabase();
531
- const ctx = createMockContext(supabase);
459
+ const ctx = createMockContext();
532
460
 
533
461
  await expect(
534
462
  convertIdeaToTask({ idea_id: 'invalid' }, ctx)
@@ -536,8 +464,7 @@ describe('convertIdeaToTask', () => {
536
464
  });
537
465
 
538
466
  it('should throw error for invalid priority', async () => {
539
- const supabase = createMockSupabase();
540
- const ctx = createMockContext(supabase);
467
+ const ctx = createMockContext();
541
468
 
542
469
  await expect(
543
470
  convertIdeaToTask({
@@ -547,32 +474,16 @@ describe('convertIdeaToTask', () => {
547
474
  ).rejects.toThrow(ValidationError);
548
475
  });
549
476
 
550
- it('should throw error when idea not found', async () => {
551
- const supabase = createMockSupabase({
552
- selectResult: { data: null, error: { message: 'Not found' } },
553
- });
554
- const ctx = createMockContext(supabase);
555
-
556
- await expect(
557
- convertIdeaToTask({ idea_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
558
- ).rejects.toThrow('Idea not found');
559
- });
560
-
561
477
  it('should return error when idea already converted', async () => {
562
- const supabase = createMockSupabase({
563
- selectResult: {
564
- data: {
565
- id: 'idea-1',
566
- project_id: 'proj-1',
567
- title: 'My Idea',
568
- description: null,
569
- status: 'in_development',
570
- converted_to_task_id: 'existing-task-id',
571
- },
572
- error: null,
478
+ mockApiClient.proxy.mockResolvedValue({
479
+ ok: true,
480
+ data: {
481
+ success: false,
482
+ error: 'Idea has already been converted to a task',
483
+ existing_task_id: 'existing-task-id',
573
484
  },
574
485
  });
575
- const ctx = createMockContext(supabase);
486
+ const ctx = createMockContext();
576
487
 
577
488
  const result = await convertIdeaToTask(
578
489
  { idea_id: '123e4567-e89b-12d3-a456-426614174000' },
@@ -587,21 +498,17 @@ describe('convertIdeaToTask', () => {
587
498
  });
588
499
 
589
500
  it('should convert idea to task successfully', async () => {
590
- const mockIdea = {
591
- id: 'idea-1',
592
- project_id: 'proj-1',
593
- title: 'Feature Request',
594
- description: 'Add dark mode',
595
- status: 'planned',
596
- converted_to_task_id: null,
597
- };
598
-
599
- const supabase = createMockSupabase({
600
- selectResult: { data: mockIdea, error: null },
601
- insertResult: { data: { id: 'task-1', title: 'Feature Request' }, error: null },
602
- updateResult: { data: null, error: null },
501
+ mockApiClient.proxy.mockResolvedValue({
502
+ ok: true,
503
+ data: {
504
+ success: true,
505
+ task_id: 'task-1',
506
+ task_title: 'Feature Request',
507
+ idea_id: 'idea-1',
508
+ idea_status: 'in_development',
509
+ },
603
510
  });
604
- const ctx = createMockContext(supabase);
511
+ const ctx = createMockContext();
605
512
 
606
513
  const result = await convertIdeaToTask(
607
514
  { idea_id: '123e4567-e89b-12d3-a456-426614174000' },
@@ -617,50 +524,34 @@ describe('convertIdeaToTask', () => {
617
524
  });
618
525
 
619
526
  it('should use default priority of 3', async () => {
620
- const mockIdea = {
621
- id: 'idea-1',
622
- project_id: 'proj-1',
623
- title: 'Feature Request',
624
- description: null,
625
- status: 'planned',
626
- converted_to_task_id: null,
627
- };
628
-
629
- const supabase = createMockSupabase({
630
- selectResult: { data: mockIdea, error: null },
631
- insertResult: { data: { id: 'task-1', title: 'Feature Request' }, error: null },
632
- updateResult: { data: null, error: null },
527
+ mockApiClient.proxy.mockResolvedValue({
528
+ ok: true,
529
+ data: { success: true, task_id: 'task-1' },
633
530
  });
634
- const ctx = createMockContext(supabase);
531
+ const ctx = createMockContext();
635
532
 
636
533
  await convertIdeaToTask(
637
534
  { idea_id: '123e4567-e89b-12d3-a456-426614174000' },
638
535
  ctx
639
536
  );
640
537
 
641
- expect(supabase.insert).toHaveBeenCalledWith(
642
- expect.objectContaining({
538
+ expect(mockApiClient.proxy).toHaveBeenCalledWith(
539
+ 'convert_idea_to_task',
540
+ {
541
+ idea_id: '123e4567-e89b-12d3-a456-426614174000',
643
542
  priority: 3,
644
- })
543
+ estimated_minutes: undefined,
544
+ update_status: true,
545
+ }
645
546
  );
646
547
  });
647
548
 
648
549
  it('should use custom priority when provided', async () => {
649
- const mockIdea = {
650
- id: 'idea-1',
651
- project_id: 'proj-1',
652
- title: 'Feature Request',
653
- description: null,
654
- status: 'planned',
655
- converted_to_task_id: null,
656
- };
657
-
658
- const supabase = createMockSupabase({
659
- selectResult: { data: mockIdea, error: null },
660
- insertResult: { data: { id: 'task-1', title: 'Feature Request' }, error: null },
661
- updateResult: { data: null, error: null },
550
+ mockApiClient.proxy.mockResolvedValue({
551
+ ok: true,
552
+ data: { success: true, task_id: 'task-1' },
662
553
  });
663
- const ctx = createMockContext(supabase);
554
+ const ctx = createMockContext();
664
555
 
665
556
  await convertIdeaToTask(
666
557
  {
@@ -670,29 +561,29 @@ describe('convertIdeaToTask', () => {
670
561
  ctx
671
562
  );
672
563
 
673
- expect(supabase.insert).toHaveBeenCalledWith(
674
- expect.objectContaining({
564
+ expect(mockApiClient.proxy).toHaveBeenCalledWith(
565
+ 'convert_idea_to_task',
566
+ {
567
+ idea_id: '123e4567-e89b-12d3-a456-426614174000',
675
568
  priority: 1,
676
- })
569
+ estimated_minutes: undefined,
570
+ update_status: true,
571
+ }
677
572
  );
678
573
  });
679
574
 
680
575
  it('should update idea status to in_development by default', async () => {
681
- const mockIdea = {
682
- id: 'idea-1',
683
- project_id: 'proj-1',
684
- title: 'Feature Request',
685
- description: null,
686
- status: 'planned',
687
- converted_to_task_id: null,
688
- };
689
-
690
- const supabase = createMockSupabase({
691
- selectResult: { data: mockIdea, error: null },
692
- insertResult: { data: { id: 'task-1', title: 'Feature Request' }, error: null },
693
- updateResult: { data: null, error: null },
576
+ mockApiClient.proxy.mockResolvedValue({
577
+ ok: true,
578
+ data: {
579
+ success: true,
580
+ task_id: 'task-1',
581
+ task_title: 'Feature Request',
582
+ idea_id: 'idea-1',
583
+ idea_status: 'in_development',
584
+ },
694
585
  });
695
- const ctx = createMockContext(supabase);
586
+ const ctx = createMockContext();
696
587
 
697
588
  const result = await convertIdeaToTask(
698
589
  { idea_id: '123e4567-e89b-12d3-a456-426614174000' },
@@ -703,23 +594,19 @@ describe('convertIdeaToTask', () => {
703
594
  });
704
595
 
705
596
  it('should not update status when update_status is false', async () => {
706
- const mockIdea = {
707
- id: 'idea-1',
708
- project_id: 'proj-1',
709
- title: 'Feature Request',
710
- description: null,
711
- status: 'planned',
712
- converted_to_task_id: null,
713
- };
714
-
715
- const supabase = createMockSupabase({
716
- selectResult: { data: mockIdea, error: null },
717
- insertResult: { data: { id: 'task-1', title: 'Feature Request' }, error: null },
718
- updateResult: { data: null, error: null },
597
+ mockApiClient.proxy.mockResolvedValue({
598
+ ok: true,
599
+ data: {
600
+ success: true,
601
+ task_id: 'task-1',
602
+ task_title: 'Feature Request',
603
+ idea_id: 'idea-1',
604
+ idea_status: 'planned',
605
+ },
719
606
  });
720
- const ctx = createMockContext(supabase);
607
+ const ctx = createMockContext();
721
608
 
722
- const result = await convertIdeaToTask(
609
+ await convertIdeaToTask(
723
610
  {
724
611
  idea_id: '123e4567-e89b-12d3-a456-426614174000',
725
612
  update_status: false,
@@ -727,27 +614,26 @@ describe('convertIdeaToTask', () => {
727
614
  ctx
728
615
  );
729
616
 
730
- expect((result.result as { idea_status: string }).idea_status).toBe('planned');
617
+ expect(mockApiClient.proxy).toHaveBeenCalledWith(
618
+ 'convert_idea_to_task',
619
+ {
620
+ idea_id: '123e4567-e89b-12d3-a456-426614174000',
621
+ priority: 3,
622
+ estimated_minutes: undefined,
623
+ update_status: false,
624
+ }
625
+ );
731
626
  });
732
627
 
733
- it('should throw error when task creation fails', async () => {
734
- const mockIdea = {
735
- id: 'idea-1',
736
- project_id: 'proj-1',
737
- title: 'Feature Request',
738
- description: null,
739
- status: 'planned',
740
- converted_to_task_id: null,
741
- };
742
-
743
- const supabase = createMockSupabase({
744
- selectResult: { data: mockIdea, error: null },
745
- insertResult: { data: null, error: { message: 'Insert failed' } },
628
+ it('should throw error when API call fails', async () => {
629
+ mockApiClient.proxy.mockResolvedValue({
630
+ ok: false,
631
+ error: 'Idea not found',
746
632
  });
747
- const ctx = createMockContext(supabase);
633
+ const ctx = createMockContext();
748
634
 
749
635
  await expect(
750
636
  convertIdeaToTask({ idea_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
751
- ).rejects.toThrow('Failed to create task');
637
+ ).rejects.toThrow('Failed to convert idea');
752
638
  });
753
639
  });