@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,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
  };