@vibescope/mcp-server 0.0.1 → 0.1.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 (170) hide show
  1. package/README.md +113 -98
  2. package/dist/api-client.d.ts +1114 -0
  3. package/dist/api-client.js +698 -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 +106 -476
  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 +112 -828
  16. package/dist/handlers/discovery.js +31 -0
  17. package/dist/handlers/fallback.d.ts +2 -0
  18. package/dist/handlers/fallback.js +39 -134
  19. package/dist/handlers/findings.js +43 -67
  20. package/dist/handlers/git-issues.d.ts +9 -13
  21. package/dist/handlers/git-issues.js +80 -225
  22. package/dist/handlers/ideas.d.ts +3 -0
  23. package/dist/handlers/ideas.js +53 -134
  24. package/dist/handlers/index.d.ts +2 -0
  25. package/dist/handlers/index.js +6 -0
  26. package/dist/handlers/milestones.d.ts +2 -0
  27. package/dist/handlers/milestones.js +51 -98
  28. package/dist/handlers/organizations.js +79 -275
  29. package/dist/handlers/progress.d.ts +2 -0
  30. package/dist/handlers/progress.js +25 -123
  31. package/dist/handlers/project.js +42 -221
  32. package/dist/handlers/requests.d.ts +2 -0
  33. package/dist/handlers/requests.js +23 -83
  34. package/dist/handlers/session.js +99 -585
  35. package/dist/handlers/sprints.d.ts +32 -0
  36. package/dist/handlers/sprints.js +274 -0
  37. package/dist/handlers/tasks.d.ts +7 -10
  38. package/dist/handlers/tasks.js +230 -900
  39. package/dist/handlers/tool-docs.d.ts +8 -0
  40. package/dist/handlers/tool-docs.js +657 -0
  41. package/dist/handlers/types.d.ts +11 -3
  42. package/dist/handlers/validation.d.ts +1 -1
  43. package/dist/handlers/validation.js +26 -153
  44. package/dist/index.js +473 -160
  45. package/dist/knowledge.js +106 -9
  46. package/dist/tools.js +4 -0
  47. package/dist/validators.d.ts +21 -0
  48. package/dist/validators.js +91 -0
  49. package/package.json +2 -3
  50. package/src/api-client.ts +1752 -0
  51. package/src/cli.test.ts +128 -302
  52. package/src/cli.ts +41 -285
  53. package/src/handlers/__test-setup__.ts +210 -0
  54. package/src/handlers/__test-utils__.ts +4 -134
  55. package/src/handlers/blockers.test.ts +114 -124
  56. package/src/handlers/blockers.ts +68 -70
  57. package/src/handlers/bodies-of-work.test.ts +236 -831
  58. package/src/handlers/bodies-of-work.ts +194 -525
  59. package/src/handlers/cost.test.ts +149 -113
  60. package/src/handlers/cost.ts +44 -132
  61. package/src/handlers/decisions.test.ts +111 -209
  62. package/src/handlers/decisions.ts +35 -27
  63. package/src/handlers/deployment.test.ts +193 -239
  64. package/src/handlers/deployment.ts +140 -895
  65. package/src/handlers/discovery.test.ts +20 -67
  66. package/src/handlers/discovery.ts +32 -0
  67. package/src/handlers/fallback.test.ts +128 -361
  68. package/src/handlers/fallback.ts +62 -148
  69. package/src/handlers/findings.test.ts +127 -345
  70. package/src/handlers/findings.ts +49 -66
  71. package/src/handlers/git-issues.test.ts +623 -0
  72. package/src/handlers/git-issues.ts +174 -0
  73. package/src/handlers/ideas.test.ts +229 -343
  74. package/src/handlers/ideas.ts +69 -143
  75. package/src/handlers/index.ts +6 -0
  76. package/src/handlers/milestones.test.ts +167 -281
  77. package/src/handlers/milestones.ts +54 -93
  78. package/src/handlers/organizations.test.ts +275 -467
  79. package/src/handlers/organizations.ts +84 -294
  80. package/src/handlers/progress.test.ts +112 -218
  81. package/src/handlers/progress.ts +29 -142
  82. package/src/handlers/project.test.ts +203 -226
  83. package/src/handlers/project.ts +48 -238
  84. package/src/handlers/requests.test.ts +74 -342
  85. package/src/handlers/requests.ts +25 -83
  86. package/src/handlers/session.test.ts +241 -206
  87. package/src/handlers/session.ts +110 -657
  88. package/src/handlers/sprints.test.ts +711 -0
  89. package/src/handlers/sprints.ts +497 -0
  90. package/src/handlers/tasks.test.ts +608 -353
  91. package/src/handlers/tasks.ts +248 -1025
  92. package/src/handlers/types.ts +12 -4
  93. package/src/handlers/validation.test.ts +189 -572
  94. package/src/handlers/validation.ts +29 -166
  95. package/src/index.ts +473 -184
  96. package/src/knowledge.ts +107 -9
  97. package/src/tools.ts +2506 -0
  98. package/src/validators.test.ts +223 -223
  99. package/src/validators.ts +127 -0
  100. package/tsconfig.json +1 -1
  101. package/vitest.config.ts +14 -13
  102. package/dist/cli.test.d.ts +0 -1
  103. package/dist/cli.test.js +0 -367
  104. package/dist/handlers/__test-utils__.d.ts +0 -72
  105. package/dist/handlers/__test-utils__.js +0 -176
  106. package/dist/handlers/checkouts.d.ts +0 -37
  107. package/dist/handlers/checkouts.js +0 -377
  108. package/dist/handlers/knowledge-query.d.ts +0 -22
  109. package/dist/handlers/knowledge-query.js +0 -253
  110. package/dist/handlers/knowledge.d.ts +0 -12
  111. package/dist/handlers/knowledge.js +0 -108
  112. package/dist/handlers/roles.d.ts +0 -30
  113. package/dist/handlers/roles.js +0 -281
  114. package/dist/handlers/tasks.test.d.ts +0 -1
  115. package/dist/handlers/tasks.test.js +0 -431
  116. package/dist/utils.test.d.ts +0 -1
  117. package/dist/utils.test.js +0 -532
  118. package/dist/validators.test.d.ts +0 -1
  119. package/dist/validators.test.js +0 -176
  120. package/src/tmpclaude-0078-cwd +0 -1
  121. package/src/tmpclaude-0ee1-cwd +0 -1
  122. package/src/tmpclaude-2dd5-cwd +0 -1
  123. package/src/tmpclaude-344c-cwd +0 -1
  124. package/src/tmpclaude-3860-cwd +0 -1
  125. package/src/tmpclaude-4b63-cwd +0 -1
  126. package/src/tmpclaude-5c73-cwd +0 -1
  127. package/src/tmpclaude-5ee3-cwd +0 -1
  128. package/src/tmpclaude-6795-cwd +0 -1
  129. package/src/tmpclaude-709e-cwd +0 -1
  130. package/src/tmpclaude-9839-cwd +0 -1
  131. package/src/tmpclaude-d829-cwd +0 -1
  132. package/src/tmpclaude-e072-cwd +0 -1
  133. package/src/tmpclaude-f6ee-cwd +0 -1
  134. package/tmpclaude-0439-cwd +0 -1
  135. package/tmpclaude-132f-cwd +0 -1
  136. package/tmpclaude-15bb-cwd +0 -1
  137. package/tmpclaude-165a-cwd +0 -1
  138. package/tmpclaude-1ba9-cwd +0 -1
  139. package/tmpclaude-21a3-cwd +0 -1
  140. package/tmpclaude-2a38-cwd +0 -1
  141. package/tmpclaude-2adf-cwd +0 -1
  142. package/tmpclaude-2f56-cwd +0 -1
  143. package/tmpclaude-3626-cwd +0 -1
  144. package/tmpclaude-3727-cwd +0 -1
  145. package/tmpclaude-40bc-cwd +0 -1
  146. package/tmpclaude-436f-cwd +0 -1
  147. package/tmpclaude-4783-cwd +0 -1
  148. package/tmpclaude-4b6d-cwd +0 -1
  149. package/tmpclaude-4ba4-cwd +0 -1
  150. package/tmpclaude-51e6-cwd +0 -1
  151. package/tmpclaude-5ecf-cwd +0 -1
  152. package/tmpclaude-6f97-cwd +0 -1
  153. package/tmpclaude-7fb2-cwd +0 -1
  154. package/tmpclaude-825c-cwd +0 -1
  155. package/tmpclaude-8baf-cwd +0 -1
  156. package/tmpclaude-8d9f-cwd +0 -1
  157. package/tmpclaude-975c-cwd +0 -1
  158. package/tmpclaude-9983-cwd +0 -1
  159. package/tmpclaude-a045-cwd +0 -1
  160. package/tmpclaude-ac4a-cwd +0 -1
  161. package/tmpclaude-b593-cwd +0 -1
  162. package/tmpclaude-b891-cwd +0 -1
  163. package/tmpclaude-c032-cwd +0 -1
  164. package/tmpclaude-cf43-cwd +0 -1
  165. package/tmpclaude-d040-cwd +0 -1
  166. package/tmpclaude-dcdd-cwd +0 -1
  167. package/tmpclaude-dcee-cwd +0 -1
  168. package/tmpclaude-e16b-cwd +0 -1
  169. package/tmpclaude-ecd2-cwd +0 -1
  170. package/tmpclaude-f48d-cwd +0 -1
@@ -1,95 +1,8 @@
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 { logDecision, getDecisions, deleteDecision } from './decisions.js';
5
3
  import { ValidationError } from '../validators.js';
6
-
7
- // ============================================================================
8
- // Test Utilities
9
- // ============================================================================
10
-
11
- function createMockSupabase(overrides: {
12
- selectResult?: { data: unknown; error: unknown };
13
- insertResult?: { data: unknown; error: unknown };
14
- deleteResult?: { data: unknown; error: unknown };
15
- } = {}) {
16
- const defaultResult = { data: null, error: null };
17
- let currentOperation = 'select';
18
- let insertThenSelect = false;
19
-
20
- const mock = {
21
- from: vi.fn().mockReturnThis(),
22
- select: vi.fn(() => {
23
- if (currentOperation === 'insert') {
24
- insertThenSelect = true;
25
- } else {
26
- currentOperation = 'select';
27
- insertThenSelect = false;
28
- }
29
- return mock;
30
- }),
31
- insert: vi.fn(() => {
32
- currentOperation = 'insert';
33
- insertThenSelect = false;
34
- return mock;
35
- }),
36
- delete: vi.fn(() => {
37
- currentOperation = 'delete';
38
- insertThenSelect = false;
39
- return mock;
40
- }),
41
- eq: vi.fn().mockReturnThis(),
42
- order: vi.fn().mockReturnThis(),
43
- single: vi.fn(() => {
44
- if (currentOperation === 'insert' || insertThenSelect) {
45
- return Promise.resolve(overrides.insertResult ?? defaultResult);
46
- }
47
- if (currentOperation === 'select') {
48
- return Promise.resolve(overrides.selectResult ?? defaultResult);
49
- }
50
- return Promise.resolve(defaultResult);
51
- }),
52
- then: vi.fn((resolve: (value: unknown) => void) => {
53
- if (currentOperation === 'insert' || insertThenSelect) {
54
- return Promise.resolve(overrides.insertResult ?? defaultResult).then(resolve);
55
- }
56
- if (currentOperation === 'select') {
57
- return Promise.resolve(overrides.selectResult ?? defaultResult).then(resolve);
58
- }
59
- if (currentOperation === 'delete') {
60
- return Promise.resolve(overrides.deleteResult ?? defaultResult).then(resolve);
61
- }
62
- return Promise.resolve(defaultResult).then(resolve);
63
- }),
64
- };
65
-
66
- return mock as unknown as SupabaseClient;
67
- }
68
-
69
- function createMockContext(
70
- supabase: SupabaseClient,
71
- options: { sessionId?: string | null } = {}
72
- ): HandlerContext {
73
- const defaultTokenUsage: TokenUsage = {
74
- callCount: 5,
75
- totalTokens: 2500,
76
- byTool: {},
77
- };
78
-
79
- const sessionId = 'sessionId' in options ? options.sessionId : 'session-123';
80
-
81
- return {
82
- supabase,
83
- auth: { userId: 'user-123', apiKeyId: 'api-key-123' },
84
- session: {
85
- instanceId: 'instance-abc',
86
- currentSessionId: sessionId,
87
- currentPersona: 'Wave',
88
- tokenUsage: defaultTokenUsage,
89
- },
90
- updateSession: vi.fn(),
91
- };
92
- }
4
+ import { createMockContext } from './__test-utils__.js';
5
+ import { mockApiClient } from './__test-setup__.js';
93
6
 
94
7
  // ============================================================================
95
8
  // logDecision Tests
@@ -99,126 +12,129 @@ describe('logDecision', () => {
99
12
  beforeEach(() => vi.clearAllMocks());
100
13
 
101
14
  it('should throw error for missing project_id', async () => {
102
- const supabase = createMockSupabase();
103
- const ctx = createMockContext(supabase);
15
+ const ctx = createMockContext();
104
16
 
105
17
  await expect(
106
- logDecision({ title: 'Decision', description: 'Description' }, ctx)
18
+ logDecision({ title: 'Test', description: 'Desc' }, ctx)
107
19
  ).rejects.toThrow(ValidationError);
108
20
  });
109
21
 
110
22
  it('should throw error for invalid project_id UUID', async () => {
111
- const supabase = createMockSupabase();
112
- const ctx = createMockContext(supabase);
23
+ const ctx = createMockContext();
113
24
 
114
25
  await expect(
115
- logDecision({ project_id: 'invalid', title: 'Decision', description: 'Description' }, ctx)
26
+ logDecision({ project_id: 'invalid', title: 'Test', description: 'Desc' }, ctx)
116
27
  ).rejects.toThrow(ValidationError);
117
28
  });
118
29
 
119
30
  it('should throw error for missing title', async () => {
120
- const supabase = createMockSupabase();
121
- const ctx = createMockContext(supabase);
31
+ const ctx = createMockContext();
122
32
 
123
33
  await expect(
124
- logDecision({ project_id: '123e4567-e89b-12d3-a456-426614174000', description: 'Description' }, ctx)
34
+ logDecision({ project_id: '123e4567-e89b-12d3-a456-426614174000', description: 'Desc' }, ctx)
125
35
  ).rejects.toThrow(ValidationError);
126
36
  });
127
37
 
128
38
  it('should throw error for missing description', async () => {
129
- const supabase = createMockSupabase();
130
- const ctx = createMockContext(supabase);
39
+ const ctx = createMockContext();
131
40
 
132
41
  await expect(
133
- logDecision({ project_id: '123e4567-e89b-12d3-a456-426614174000', title: 'Decision' }, ctx)
42
+ logDecision({ project_id: '123e4567-e89b-12d3-a456-426614174000', title: 'Test' }, ctx)
134
43
  ).rejects.toThrow(ValidationError);
135
44
  });
136
45
 
137
46
  it('should log decision successfully', async () => {
138
- const supabase = createMockSupabase({
139
- insertResult: { data: null, error: null },
47
+ mockApiClient.logDecision.mockResolvedValue({
48
+ ok: true,
49
+ data: { decision_id: 'dec-123' },
140
50
  });
141
- const ctx = createMockContext(supabase);
51
+ const ctx = createMockContext();
142
52
 
143
53
  const result = await logDecision(
144
54
  {
145
55
  project_id: '123e4567-e89b-12d3-a456-426614174000',
146
- title: 'Use PostgreSQL',
147
- description: 'Decided to use PostgreSQL for the database',
56
+ title: 'Use TypeScript',
57
+ description: 'We will use TypeScript for type safety',
148
58
  },
149
59
  ctx
150
60
  );
151
61
 
152
62
  expect(result.result).toMatchObject({
153
63
  success: true,
154
- title: 'Use PostgreSQL',
64
+ title: 'Use TypeScript',
65
+ decision_id: 'dec-123',
155
66
  });
156
67
  });
157
68
 
158
69
  it('should log decision with rationale and alternatives', async () => {
159
- const supabase = createMockSupabase({
160
- insertResult: { data: null, error: null },
70
+ mockApiClient.logDecision.mockResolvedValue({
71
+ ok: true,
72
+ data: { decision_id: 'dec-456' },
161
73
  });
162
- const ctx = createMockContext(supabase);
74
+ const ctx = createMockContext();
163
75
 
164
76
  await logDecision(
165
77
  {
166
78
  project_id: '123e4567-e89b-12d3-a456-426614174000',
167
79
  title: 'Use PostgreSQL',
168
- description: 'Decided to use PostgreSQL for the database',
169
- rationale: 'Better JSON support and reliability',
80
+ description: 'We will use PostgreSQL as our database',
81
+ rationale: 'Better JSON support and performance',
170
82
  alternatives_considered: ['MySQL', 'MongoDB'],
171
83
  },
172
84
  ctx
173
85
  );
174
86
 
175
- expect(supabase.insert).toHaveBeenCalledWith(
176
- expect.objectContaining({
87
+ expect(mockApiClient.logDecision).toHaveBeenCalledWith(
88
+ '123e4567-e89b-12d3-a456-426614174000',
89
+ {
177
90
  title: 'Use PostgreSQL',
178
- description: 'Decided to use PostgreSQL for the database',
179
- rationale: 'Better JSON support and reliability',
91
+ description: 'We will use PostgreSQL as our database',
92
+ rationale: 'Better JSON support and performance',
180
93
  alternatives_considered: ['MySQL', 'MongoDB'],
181
- created_by: 'agent',
182
- created_by_session_id: 'session-123',
183
- })
94
+ },
95
+ 'session-123'
184
96
  );
185
97
  });
186
98
 
187
- it('should include session_id in insert', async () => {
188
- const supabase = createMockSupabase({
189
- insertResult: { data: null, error: null },
99
+ it('should include session_id in API call', async () => {
100
+ mockApiClient.logDecision.mockResolvedValue({
101
+ ok: true,
102
+ data: { decision_id: 'dec-789' },
190
103
  });
191
- const ctx = createMockContext(supabase, { sessionId: 'my-session' });
104
+ const ctx = createMockContext({ sessionId: 'my-session' });
192
105
 
193
106
  await logDecision(
194
107
  {
195
108
  project_id: '123e4567-e89b-12d3-a456-426614174000',
196
- title: 'Decision',
197
- description: 'Description',
109
+ title: 'Test Decision',
110
+ description: 'Test description',
198
111
  },
199
112
  ctx
200
113
  );
201
114
 
202
- expect(supabase.insert).toHaveBeenCalledWith(
203
- expect.objectContaining({
204
- created_by: 'agent',
205
- created_by_session_id: 'my-session',
206
- })
115
+ expect(mockApiClient.logDecision).toHaveBeenCalledWith(
116
+ '123e4567-e89b-12d3-a456-426614174000',
117
+ expect.any(Object),
118
+ 'my-session'
207
119
  );
208
120
  });
209
121
 
210
- it('should throw error when insert fails', async () => {
211
- const supabase = createMockSupabase({
212
- insertResult: { data: null, error: { message: 'Insert failed' } },
122
+ it('should throw error when API call fails', async () => {
123
+ mockApiClient.logDecision.mockResolvedValue({
124
+ ok: false,
125
+ error: 'Insert failed',
213
126
  });
214
- const ctx = createMockContext(supabase);
127
+ const ctx = createMockContext();
215
128
 
216
129
  await expect(
217
- logDecision({
218
- project_id: '123e4567-e89b-12d3-a456-426614174000',
219
- title: 'Decision',
220
- description: 'Description',
221
- }, ctx)
130
+ logDecision(
131
+ {
132
+ project_id: '123e4567-e89b-12d3-a456-426614174000',
133
+ title: 'Test',
134
+ description: 'Test',
135
+ },
136
+ ctx
137
+ )
222
138
  ).rejects.toThrow('Failed to log decision: Insert failed');
223
139
  });
224
140
  });
@@ -231,15 +147,13 @@ describe('getDecisions', () => {
231
147
  beforeEach(() => vi.clearAllMocks());
232
148
 
233
149
  it('should throw error for missing project_id', async () => {
234
- const supabase = createMockSupabase();
235
- const ctx = createMockContext(supabase);
150
+ const ctx = createMockContext();
236
151
 
237
152
  await expect(getDecisions({}, ctx)).rejects.toThrow(ValidationError);
238
153
  });
239
154
 
240
155
  it('should throw error for invalid project_id UUID', async () => {
241
- const supabase = createMockSupabase();
242
- const ctx = createMockContext(supabase);
156
+ const ctx = createMockContext();
243
157
 
244
158
  await expect(
245
159
  getDecisions({ project_id: 'invalid' }, ctx)
@@ -247,10 +161,11 @@ describe('getDecisions', () => {
247
161
  });
248
162
 
249
163
  it('should return empty list when no decisions', async () => {
250
- const supabase = createMockSupabase({
251
- selectResult: { data: [], error: null },
164
+ mockApiClient.getDecisions.mockResolvedValue({
165
+ ok: true,
166
+ data: { decisions: [] },
252
167
  });
253
- const ctx = createMockContext(supabase);
168
+ const ctx = createMockContext();
254
169
 
255
170
  const result = await getDecisions(
256
171
  { project_id: '123e4567-e89b-12d3-a456-426614174000' },
@@ -264,65 +179,55 @@ describe('getDecisions', () => {
264
179
 
265
180
  it('should return decisions list', async () => {
266
181
  const mockDecisions = [
267
- { id: 'd1', title: 'Decision 1', description: 'Desc 1', created_at: '2025-01-14T10:00:00Z' },
268
- { id: 'd2', title: 'Decision 2', description: 'Desc 2', created_at: '2025-01-14T11:00:00Z' },
182
+ { id: 'd1', title: 'Decision 1', description: 'Desc 1' },
183
+ { id: 'd2', title: 'Decision 2', description: 'Desc 2' },
269
184
  ];
270
-
271
- const supabase = createMockSupabase({
272
- selectResult: { data: mockDecisions, error: null },
185
+ mockApiClient.getDecisions.mockResolvedValue({
186
+ ok: true,
187
+ data: { decisions: mockDecisions },
273
188
  });
274
- const ctx = createMockContext(supabase);
189
+ const ctx = createMockContext();
275
190
 
276
191
  const result = await getDecisions(
277
192
  { project_id: '123e4567-e89b-12d3-a456-426614174000' },
278
193
  ctx
279
194
  );
280
195
 
281
- expect((result.result as { decisions: unknown[] }).decisions).toHaveLength(2);
282
- });
283
-
284
- it('should query decisions table', async () => {
285
- const supabase = createMockSupabase({
286
- selectResult: { data: [], error: null },
196
+ expect(result.result).toMatchObject({
197
+ decisions: mockDecisions,
287
198
  });
288
- const ctx = createMockContext(supabase);
289
-
290
- await getDecisions(
291
- { project_id: '123e4567-e89b-12d3-a456-426614174000' },
292
- ctx
293
- );
294
-
295
- expect(supabase.from).toHaveBeenCalledWith('decisions');
296
199
  });
297
200
 
298
- it('should order by created_at descending', async () => {
299
- const supabase = createMockSupabase({
300
- selectResult: { data: [], error: null },
201
+ it('should call API client getDecisions', async () => {
202
+ mockApiClient.getDecisions.mockResolvedValue({
203
+ ok: true,
204
+ data: { decisions: [] },
301
205
  });
302
- const ctx = createMockContext(supabase);
206
+ const ctx = createMockContext();
303
207
 
304
208
  await getDecisions(
305
209
  { project_id: '123e4567-e89b-12d3-a456-426614174000' },
306
210
  ctx
307
211
  );
308
212
 
309
- expect(supabase.order).toHaveBeenCalledWith('created_at', { ascending: false });
213
+ expect(mockApiClient.getDecisions).toHaveBeenCalledWith(
214
+ '123e4567-e89b-12d3-a456-426614174000'
215
+ );
310
216
  });
311
217
 
312
218
  it('should throw error when query fails', async () => {
313
- const supabase = createMockSupabase();
314
- const ctx = createMockContext(supabase);
315
-
316
- // Override to return error
317
- vi.mocked(supabase.from('').select).mockReturnValue({
318
- ...supabase,
319
- then: (resolve: (val: unknown) => void) =>
320
- Promise.resolve({ data: null, error: { message: 'Query failed' } }).then(resolve),
321
- } as unknown as ReturnType<SupabaseClient['from']>);
219
+ mockApiClient.getDecisions.mockResolvedValue({
220
+ ok: false,
221
+ error: 'Query failed',
222
+ });
223
+ const ctx = createMockContext();
322
224
 
323
225
  await expect(
324
- getDecisions({ project_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
325
- ).rejects.toThrow('Failed to fetch decisions');
226
+ getDecisions(
227
+ { project_id: '123e4567-e89b-12d3-a456-426614174000' },
228
+ ctx
229
+ )
230
+ ).rejects.toThrow('Failed to fetch decisions: Query failed');
326
231
  });
327
232
  });
328
233
 
@@ -334,15 +239,13 @@ describe('deleteDecision', () => {
334
239
  beforeEach(() => vi.clearAllMocks());
335
240
 
336
241
  it('should throw error for missing decision_id', async () => {
337
- const supabase = createMockSupabase();
338
- const ctx = createMockContext(supabase);
242
+ const ctx = createMockContext();
339
243
 
340
244
  await expect(deleteDecision({}, ctx)).rejects.toThrow(ValidationError);
341
245
  });
342
246
 
343
247
  it('should throw error for invalid decision_id UUID', async () => {
344
- const supabase = createMockSupabase();
345
- const ctx = createMockContext(supabase);
248
+ const ctx = createMockContext();
346
249
 
347
250
  await expect(
348
251
  deleteDecision({ decision_id: 'invalid' }, ctx)
@@ -350,10 +253,11 @@ describe('deleteDecision', () => {
350
253
  });
351
254
 
352
255
  it('should delete decision successfully', async () => {
353
- const supabase = createMockSupabase({
354
- deleteResult: { data: null, error: null },
256
+ mockApiClient.deleteDecision.mockResolvedValue({
257
+ ok: true,
258
+ data: { success: true },
355
259
  });
356
- const ctx = createMockContext(supabase);
260
+ const ctx = createMockContext();
357
261
 
358
262
  const result = await deleteDecision(
359
263
  { decision_id: '123e4567-e89b-12d3-a456-426614174000' },
@@ -365,37 +269,35 @@ describe('deleteDecision', () => {
365
269
  });
366
270
  });
367
271
 
368
- it('should call delete on decisions table', async () => {
369
- const supabase = createMockSupabase({
370
- deleteResult: { data: null, error: null },
272
+ it('should call API client deleteDecision', async () => {
273
+ mockApiClient.deleteDecision.mockResolvedValue({
274
+ ok: true,
275
+ data: { success: true },
371
276
  });
372
- const ctx = createMockContext(supabase);
277
+ const ctx = createMockContext();
373
278
 
374
279
  await deleteDecision(
375
280
  { decision_id: '123e4567-e89b-12d3-a456-426614174000' },
376
281
  ctx
377
282
  );
378
283
 
379
- expect(supabase.from).toHaveBeenCalledWith('decisions');
380
- expect(supabase.delete).toHaveBeenCalled();
381
- expect(supabase.eq).toHaveBeenCalledWith('id', '123e4567-e89b-12d3-a456-426614174000');
284
+ expect(mockApiClient.deleteDecision).toHaveBeenCalledWith(
285
+ '123e4567-e89b-12d3-a456-426614174000'
286
+ );
382
287
  });
383
288
 
384
289
  it('should throw error when delete fails', async () => {
385
- const supabase = createMockSupabase();
386
- const ctx = createMockContext(supabase);
387
-
388
- // Override delete to return error
389
- vi.mocked(supabase.from('').delete).mockReturnValue({
390
- ...supabase,
391
- eq: vi.fn().mockReturnValue({
392
- then: (resolve: (val: unknown) => void) =>
393
- Promise.resolve({ data: null, error: { message: 'Delete failed' } }).then(resolve),
394
- }),
395
- } as unknown as ReturnType<SupabaseClient['from']>);
290
+ mockApiClient.deleteDecision.mockResolvedValue({
291
+ ok: false,
292
+ error: 'Delete failed',
293
+ });
294
+ const ctx = createMockContext();
396
295
 
397
296
  await expect(
398
- deleteDecision({ decision_id: '123e4567-e89b-12d3-a456-426614174000' }, ctx)
399
- ).rejects.toThrow('Failed to delete decision');
297
+ deleteDecision(
298
+ { decision_id: '123e4567-e89b-12d3-a456-426614174000' },
299
+ ctx
300
+ )
301
+ ).rejects.toThrow('Failed to delete decision: Delete failed');
400
302
  });
401
303
  });
@@ -5,10 +5,13 @@
5
5
  * - log_decision
6
6
  * - get_decisions
7
7
  * - delete_decision
8
+ *
9
+ * MIGRATED: Uses Vibescope API client instead of direct Supabase
8
10
  */
9
11
 
10
12
  import type { Handler, HandlerRegistry } from './types.js';
11
13
  import { validateRequired, validateUUID } from '../validators.js';
14
+ import { getApiClient } from '../api-client.js';
12
15
 
13
16
  export const logDecision: Handler = async (args, ctx) => {
14
17
  const { project_id, title, description, rationale, alternatives_considered } = args as {
@@ -24,40 +27,44 @@ export const logDecision: Handler = async (args, ctx) => {
24
27
  validateRequired(title, 'title');
25
28
  validateRequired(description, 'description');
26
29
 
27
- const { supabase, session } = ctx;
30
+ const { session } = ctx;
31
+ const apiClient = getApiClient();
28
32
 
29
- const { error } = await supabase
30
- .from('decisions')
31
- .insert({
32
- project_id,
33
- title,
34
- description,
35
- rationale: rationale || null,
36
- alternatives_considered: alternatives_considered || null,
37
- created_by: 'agent',
38
- created_by_session_id: session.currentSessionId,
39
- });
33
+ const response = await apiClient.logDecision(project_id, {
34
+ title,
35
+ description,
36
+ rationale,
37
+ alternatives_considered
38
+ }, session.currentSessionId || undefined);
40
39
 
41
- if (error) throw new Error(`Failed to log decision: ${error.message}`);
40
+ if (!response.ok) {
41
+ throw new Error(`Failed to log decision: ${response.error}`);
42
+ }
42
43
 
43
- return { result: { success: true, title } };
44
+ return { result: { success: true, title, decision_id: response.data?.decision_id } };
44
45
  };
45
46
 
46
47
  export const getDecisions: Handler = async (args, ctx) => {
47
- const { project_id } = args as { project_id: string };
48
+ const { project_id } = args as {
49
+ project_id: string;
50
+ };
48
51
 
49
52
  validateRequired(project_id, 'project_id');
50
53
  validateUUID(project_id, 'project_id');
51
54
 
52
- const { data, error } = await ctx.supabase
53
- .from('decisions')
54
- .select('id, title, description, rationale, created_at')
55
- .eq('project_id', project_id)
56
- .order('created_at', { ascending: false });
55
+ const apiClient = getApiClient();
57
56
 
58
- if (error) throw new Error(`Failed to fetch decisions: ${error.message}`);
57
+ const response = await apiClient.getDecisions(project_id);
59
58
 
60
- return { result: { decisions: data || [] } };
59
+ if (!response.ok) {
60
+ throw new Error(`Failed to fetch decisions: ${response.error}`);
61
+ }
62
+
63
+ return {
64
+ result: {
65
+ decisions: response.data?.decisions || [],
66
+ },
67
+ };
61
68
  };
62
69
 
63
70
  export const deleteDecision: Handler = async (args, ctx) => {
@@ -66,12 +73,13 @@ export const deleteDecision: Handler = async (args, ctx) => {
66
73
  validateRequired(decision_id, 'decision_id');
67
74
  validateUUID(decision_id, 'decision_id');
68
75
 
69
- const { error } = await ctx.supabase
70
- .from('decisions')
71
- .delete()
72
- .eq('id', decision_id);
76
+ const apiClient = getApiClient();
77
+
78
+ const response = await apiClient.deleteDecision(decision_id);
73
79
 
74
- if (error) throw new Error(`Failed to delete decision: ${error.message}`);
80
+ if (!response.ok) {
81
+ throw new Error(`Failed to delete decision: ${response.error}`);
82
+ }
75
83
 
76
84
  return { result: { success: true } };
77
85
  };