@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
@@ -19,7 +19,7 @@
19
19
 
20
20
  import type { Handler, HandlerRegistry } from './types.js';
21
21
  import { validateRequired, validateUUID } from '../validators.js';
22
- import { randomBytes } from 'crypto';
22
+ import { getApiClient } from '../api-client.js';
23
23
 
24
24
  // Valid roles in order of permission level
25
25
  const ROLE_ORDER = ['viewer', 'member', 'admin', 'owner'] as const;
@@ -29,62 +29,23 @@ type Role = (typeof ROLE_ORDER)[number];
29
29
  const PERMISSION_ORDER = ['read', 'write', 'admin'] as const;
30
30
  type Permission = (typeof PERMISSION_ORDER)[number];
31
31
 
32
- /**
33
- * Generate a URL-friendly slug from a name
34
- */
35
- function generateSlug(name: string): string {
36
- return name
37
- .toLowerCase()
38
- .replace(/[^a-z0-9]+/g, '-')
39
- .replace(/^-|-$/g, '')
40
- .slice(0, 50);
41
- }
42
-
43
- /**
44
- * Generate a secure invite token
45
- */
46
- function generateInviteToken(): string {
47
- return randomBytes(32).toString('base64url');
48
- }
49
-
50
32
  // ============================================================================
51
33
  // Organization Management
52
34
  // ============================================================================
53
35
 
54
36
  export const listOrganizations: Handler = async (_args, ctx) => {
55
- const { supabase, auth } = ctx;
56
-
57
- const { data, error } = await supabase
58
- .from('organization_members')
59
- .select(`
60
- role,
61
- joined_at,
62
- organizations (
63
- id,
64
- name,
65
- slug,
66
- description,
67
- logo_url,
68
- owner_id,
69
- created_at
70
- )
71
- `)
72
- .eq('user_id', auth.userId)
73
- .order('joined_at', { ascending: false });
74
-
75
- if (error) throw new Error(`Failed to list organizations: ${error.message}`);
76
-
77
- const organizations = (data || []).map((m) => ({
78
- ...(m.organizations as unknown as Record<string, unknown>),
79
- role: m.role,
80
- joined_at: m.joined_at,
81
- }));
82
-
83
- return { result: { organizations, count: organizations.length } };
37
+ const apiClient = getApiClient();
38
+ const response = await apiClient.listOrganizations();
39
+
40
+ if (!response.ok) {
41
+ throw new Error(response.error || 'Failed to list organizations');
42
+ }
43
+
44
+ return { result: response.data };
84
45
  };
85
46
 
86
47
  export const createOrganization: Handler = async (args, ctx) => {
87
- const { name, description, slug: customSlug } = args as {
48
+ const { name, description, slug } = args as {
88
49
  name: string;
89
50
  description?: string;
90
51
  slug?: string;
@@ -92,40 +53,18 @@ export const createOrganization: Handler = async (args, ctx) => {
92
53
 
93
54
  validateRequired(name, 'name');
94
55
 
95
- const { supabase, auth } = ctx;
96
- const slug = customSlug || generateSlug(name);
56
+ const apiClient = getApiClient();
57
+ const response = await apiClient.createOrganization({
58
+ name,
59
+ description,
60
+ slug
61
+ });
97
62
 
98
- // Check if slug is available
99
- const { data: existing } = await supabase
100
- .from('organizations')
101
- .select('id')
102
- .eq('slug', slug)
103
- .maybeSingle();
104
-
105
- if (existing) {
106
- throw new Error(`Organization slug "${slug}" is already taken`);
63
+ if (!response.ok) {
64
+ throw new Error(response.error || 'Failed to create organization');
107
65
  }
108
66
 
109
- const { data, error } = await supabase
110
- .from('organizations')
111
- .insert({
112
- name,
113
- slug,
114
- description: description || null,
115
- owner_id: auth.userId,
116
- })
117
- .select()
118
- .single();
119
-
120
- if (error) throw new Error(`Failed to create organization: ${error.message}`);
121
-
122
- return {
123
- result: {
124
- success: true,
125
- organization: data,
126
- message: `Organization "${name}" created. You are the owner.`,
127
- },
128
- };
67
+ return { result: response.data };
129
68
  };
130
69
 
131
70
  export const updateOrganization: Handler = async (args, ctx) => {
@@ -139,8 +78,6 @@ export const updateOrganization: Handler = async (args, ctx) => {
139
78
  validateRequired(organization_id, 'organization_id');
140
79
  validateUUID(organization_id, 'organization_id');
141
80
 
142
- const { supabase } = ctx;
143
-
144
81
  const updates: Record<string, unknown> = {};
145
82
  if (name !== undefined) updates.name = name;
146
83
  if (description !== undefined) updates.description = description;
@@ -150,16 +87,14 @@ export const updateOrganization: Handler = async (args, ctx) => {
150
87
  throw new Error('No updates provided');
151
88
  }
152
89
 
153
- const { data, error } = await supabase
154
- .from('organizations')
155
- .update(updates)
156
- .eq('id', organization_id)
157
- .select()
158
- .single();
90
+ const apiClient = getApiClient();
91
+ const response = await apiClient.updateOrganization(organization_id, updates);
159
92
 
160
- if (error) throw new Error(`Failed to update organization: ${error.message}`);
93
+ if (!response.ok) {
94
+ throw new Error(response.error || 'Failed to update organization');
95
+ }
161
96
 
162
- return { result: { success: true, organization: data } };
97
+ return { result: response.data };
163
98
  };
164
99
 
165
100
  export const deleteOrganization: Handler = async (args, ctx) => {
@@ -168,21 +103,14 @@ export const deleteOrganization: Handler = async (args, ctx) => {
168
103
  validateRequired(organization_id, 'organization_id');
169
104
  validateUUID(organization_id, 'organization_id');
170
105
 
171
- const { supabase } = ctx;
172
-
173
- const { error } = await supabase
174
- .from('organizations')
175
- .delete()
176
- .eq('id', organization_id);
106
+ const apiClient = getApiClient();
107
+ const response = await apiClient.deleteOrganization(organization_id);
177
108
 
178
- if (error) throw new Error(`Failed to delete organization: ${error.message}`);
109
+ if (!response.ok) {
110
+ throw new Error(response.error || 'Failed to delete organization');
111
+ }
179
112
 
180
- return {
181
- result: {
182
- success: true,
183
- message: 'Organization deleted. All shares have been removed.',
184
- },
185
- };
113
+ return { result: response.data };
186
114
  };
187
115
 
188
116
  // ============================================================================
@@ -195,18 +123,14 @@ export const listOrgMembers: Handler = async (args, ctx) => {
195
123
  validateRequired(organization_id, 'organization_id');
196
124
  validateUUID(organization_id, 'organization_id');
197
125
 
198
- const { supabase } = ctx;
126
+ const apiClient = getApiClient();
127
+ const response = await apiClient.listOrgMembers(organization_id);
199
128
 
200
- const { data, error } = await supabase
201
- .from('organization_members')
202
- .select('id, user_id, role, joined_at, invited_by')
203
- .eq('organization_id', organization_id)
204
- .order('role', { ascending: true })
205
- .order('joined_at', { ascending: true });
206
-
207
- if (error) throw new Error(`Failed to list members: ${error.message}`);
129
+ if (!response.ok) {
130
+ throw new Error(response.error || 'Failed to list members');
131
+ }
208
132
 
209
- return { result: { members: data || [], count: data?.length || 0 } };
133
+ return { result: response.data };
210
134
  };
211
135
 
212
136
  export const inviteMember: Handler = async (args, ctx) => {
@@ -224,44 +148,14 @@ export const inviteMember: Handler = async (args, ctx) => {
224
148
  throw new Error('Invalid role. Must be admin, member, or viewer.');
225
149
  }
226
150
 
227
- const { supabase, auth } = ctx;
228
- const token = generateInviteToken();
229
-
230
- // Check for existing pending invite
231
- const { data: existing } = await supabase
232
- .from('organization_invites')
233
- .select('id')
234
- .eq('organization_id', organization_id)
235
- .eq('email', email)
236
- .is('accepted_at', null)
237
- .gt('expires_at', new Date().toISOString())
238
- .maybeSingle();
239
-
240
- if (existing) {
241
- throw new Error(`A pending invite already exists for ${email}`);
151
+ const apiClient = getApiClient();
152
+ const response = await apiClient.inviteMember(organization_id, email, role);
153
+
154
+ if (!response.ok) {
155
+ throw new Error(response.error || 'Failed to create invite');
242
156
  }
243
157
 
244
- const { data, error } = await supabase
245
- .from('organization_invites')
246
- .insert({
247
- organization_id,
248
- email,
249
- role,
250
- token,
251
- invited_by: auth.userId,
252
- })
253
- .select()
254
- .single();
255
-
256
- if (error) throw new Error(`Failed to create invite: ${error.message}`);
257
-
258
- return {
259
- result: {
260
- success: true,
261
- invite: data,
262
- message: `Invite sent to ${email} with role "${role}"`,
263
- },
264
- };
158
+ return { result: response.data };
265
159
  };
266
160
 
267
161
  export const updateMemberRole: Handler = async (args, ctx) => {
@@ -285,25 +179,14 @@ export const updateMemberRole: Handler = async (args, ctx) => {
285
179
  throw new Error('Cannot assign owner role. Use transfer ownership instead.');
286
180
  }
287
181
 
288
- const { supabase, auth } = ctx;
182
+ const apiClient = getApiClient();
183
+ const response = await apiClient.updateMemberRole(organization_id, user_id, role);
289
184
 
290
- // Prevent demoting yourself
291
- if (user_id === auth.userId) {
292
- throw new Error('Cannot change your own role');
185
+ if (!response.ok) {
186
+ throw new Error(response.error || 'Failed to update member role');
293
187
  }
294
188
 
295
- const { data, error } = await supabase
296
- .from('organization_members')
297
- .update({ role })
298
- .eq('organization_id', organization_id)
299
- .eq('user_id', user_id)
300
- .neq('role', 'owner') // Cannot change owner's role
301
- .select()
302
- .single();
303
-
304
- if (error) throw new Error(`Failed to update member role: ${error.message}`);
305
-
306
- return { result: { success: true, member: data } };
189
+ return { result: response.data };
307
190
  };
308
191
 
309
192
  export const removeMember: Handler = async (args, ctx) => {
@@ -317,23 +200,14 @@ export const removeMember: Handler = async (args, ctx) => {
317
200
  validateUUID(organization_id, 'organization_id');
318
201
  validateUUID(user_id, 'user_id');
319
202
 
320
- const { supabase } = ctx;
321
-
322
- const { error } = await supabase
323
- .from('organization_members')
324
- .delete()
325
- .eq('organization_id', organization_id)
326
- .eq('user_id', user_id)
327
- .neq('role', 'owner'); // Cannot remove owner
203
+ const apiClient = getApiClient();
204
+ const response = await apiClient.removeMember(organization_id, user_id);
328
205
 
329
- if (error) throw new Error(`Failed to remove member: ${error.message}`);
206
+ if (!response.ok) {
207
+ throw new Error(response.error || 'Failed to remove member');
208
+ }
330
209
 
331
- return {
332
- result: {
333
- success: true,
334
- message: 'Member removed. Their org-scoped API keys have been invalidated.',
335
- },
336
- };
210
+ return { result: response.data };
337
211
  };
338
212
 
339
213
  export const leaveOrganization: Handler = async (args, ctx) => {
@@ -342,34 +216,14 @@ export const leaveOrganization: Handler = async (args, ctx) => {
342
216
  validateRequired(organization_id, 'organization_id');
343
217
  validateUUID(organization_id, 'organization_id');
344
218
 
345
- const { supabase, auth } = ctx;
346
-
347
- // Check if user is owner
348
- const { data: membership } = await supabase
349
- .from('organization_members')
350
- .select('role')
351
- .eq('organization_id', organization_id)
352
- .eq('user_id', auth.userId)
353
- .single();
219
+ const apiClient = getApiClient();
220
+ const response = await apiClient.leaveOrganization(organization_id);
354
221
 
355
- if (membership?.role === 'owner') {
356
- throw new Error('Owner cannot leave. Transfer ownership first or delete the organization.');
222
+ if (!response.ok) {
223
+ throw new Error(response.error || 'Failed to leave organization');
357
224
  }
358
225
 
359
- const { error } = await supabase
360
- .from('organization_members')
361
- .delete()
362
- .eq('organization_id', organization_id)
363
- .eq('user_id', auth.userId);
364
-
365
- if (error) throw new Error(`Failed to leave organization: ${error.message}`);
366
-
367
- return {
368
- result: {
369
- success: true,
370
- message: 'You have left the organization.',
371
- },
372
- };
226
+ return { result: response.data };
373
227
  };
374
228
 
375
229
  // ============================================================================
@@ -392,52 +246,14 @@ export const shareProjectWithOrg: Handler = async (args, ctx) => {
392
246
  throw new Error(`Invalid permission. Must be one of: ${PERMISSION_ORDER.join(', ')}`);
393
247
  }
394
248
 
395
- const { supabase, auth } = ctx;
396
-
397
- // Verify user owns the project
398
- const { data: project } = await supabase
399
- .from('projects')
400
- .select('id, name')
401
- .eq('id', project_id)
402
- .eq('user_id', auth.userId)
403
- .single();
249
+ const apiClient = getApiClient();
250
+ const response = await apiClient.shareProjectWithOrg(project_id, organization_id, permission);
404
251
 
405
- if (!project) {
406
- throw new Error('Project not found or you are not the owner');
252
+ if (!response.ok) {
253
+ throw new Error(response.error || 'Failed to share project');
407
254
  }
408
255
 
409
- // Check if share already exists
410
- const { data: existing } = await supabase
411
- .from('project_shares')
412
- .select('id')
413
- .eq('project_id', project_id)
414
- .eq('organization_id', organization_id)
415
- .maybeSingle();
416
-
417
- if (existing) {
418
- throw new Error('Project is already shared with this organization');
419
- }
420
-
421
- const { data, error } = await supabase
422
- .from('project_shares')
423
- .insert({
424
- project_id,
425
- organization_id,
426
- permission,
427
- shared_by: auth.userId,
428
- })
429
- .select()
430
- .single();
431
-
432
- if (error) throw new Error(`Failed to share project: ${error.message}`);
433
-
434
- return {
435
- result: {
436
- success: true,
437
- share: data,
438
- message: `Project "${project.name}" shared with organization (${permission} access)`,
439
- },
440
- };
256
+ return { result: response.data };
441
257
  };
442
258
 
443
259
  export const updateProjectShare: Handler = async (args, ctx) => {
@@ -457,19 +273,14 @@ export const updateProjectShare: Handler = async (args, ctx) => {
457
273
  throw new Error(`Invalid permission. Must be one of: ${PERMISSION_ORDER.join(', ')}`);
458
274
  }
459
275
 
460
- const { supabase } = ctx;
461
-
462
- const { data, error } = await supabase
463
- .from('project_shares')
464
- .update({ permission })
465
- .eq('project_id', project_id)
466
- .eq('organization_id', organization_id)
467
- .select()
468
- .single();
276
+ const apiClient = getApiClient();
277
+ const response = await apiClient.updateProjectShare(project_id, organization_id, permission);
469
278
 
470
- if (error) throw new Error(`Failed to update share: ${error.message}`);
279
+ if (!response.ok) {
280
+ throw new Error(response.error || 'Failed to update share');
281
+ }
471
282
 
472
- return { result: { success: true, share: data } };
283
+ return { result: response.data };
473
284
  };
474
285
 
475
286
  export const unshareProject: Handler = async (args, ctx) => {
@@ -483,22 +294,14 @@ export const unshareProject: Handler = async (args, ctx) => {
483
294
  validateUUID(project_id, 'project_id');
484
295
  validateUUID(organization_id, 'organization_id');
485
296
 
486
- const { supabase } = ctx;
487
-
488
- const { error } = await supabase
489
- .from('project_shares')
490
- .delete()
491
- .eq('project_id', project_id)
492
- .eq('organization_id', organization_id);
297
+ const apiClient = getApiClient();
298
+ const response = await apiClient.unshareProject(project_id, organization_id);
493
299
 
494
- if (error) throw new Error(`Failed to unshare project: ${error.message}`);
300
+ if (!response.ok) {
301
+ throw new Error(response.error || 'Failed to unshare project');
302
+ }
495
303
 
496
- return {
497
- result: {
498
- success: true,
499
- message: 'Project share removed. Org members can no longer access this project.',
500
- },
501
- };
304
+ return { result: response.data };
502
305
  };
503
306
 
504
307
  export const listProjectShares: Handler = async (args, ctx) => {
@@ -507,27 +310,14 @@ export const listProjectShares: Handler = async (args, ctx) => {
507
310
  validateRequired(project_id, 'project_id');
508
311
  validateUUID(project_id, 'project_id');
509
312
 
510
- const { supabase } = ctx;
511
-
512
- const { data, error } = await supabase
513
- .from('project_shares')
514
- .select(`
515
- id,
516
- permission,
517
- shared_at,
518
- shared_by,
519
- organizations (
520
- id,
521
- name,
522
- slug
523
- )
524
- `)
525
- .eq('project_id', project_id)
526
- .order('shared_at', { ascending: false });
527
-
528
- if (error) throw new Error(`Failed to list shares: ${error.message}`);
529
-
530
- return { result: { shares: data || [], count: data?.length || 0 } };
313
+ const apiClient = getApiClient();
314
+ const response = await apiClient.listProjectShares(project_id);
315
+
316
+ if (!response.ok) {
317
+ throw new Error(response.error || 'Failed to list shares');
318
+ }
319
+
320
+ return { result: response.data };
531
321
  };
532
322
 
533
323
  /**