@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
@@ -4,141 +4,43 @@
4
4
  * Handles progress logging and activity feed:
5
5
  * - log_progress
6
6
  * - get_activity_feed
7
+ *
8
+ * MIGRATED: Uses Vibescope API client instead of direct Supabase
7
9
  */
8
10
  import { validateRequired, validateUUID } from '../validators.js';
11
+ import { getApiClient } from '../api-client.js';
9
12
  export const logProgress = async (args, ctx) => {
10
13
  const { project_id, task_id, summary, details } = args;
11
- const { supabase, session } = ctx;
12
- const { error } = await supabase
13
- .from('progress_logs')
14
- .insert({
15
- project_id,
16
- task_id: task_id || null,
14
+ validateRequired(project_id, 'project_id');
15
+ validateUUID(project_id, 'project_id');
16
+ validateRequired(summary, 'summary');
17
+ const { session } = ctx;
18
+ const apiClient = getApiClient();
19
+ const response = await apiClient.logProgress(project_id, {
17
20
  summary,
18
- details: details || null,
19
- created_by: 'agent',
20
- created_by_session_id: session.currentSessionId,
21
+ details,
22
+ task_id,
23
+ session_id: session.currentSessionId || undefined
21
24
  });
22
- if (error)
23
- throw new Error(`Failed to log progress: ${error.message}`);
24
- return { result: { success: true } };
25
+ if (!response.ok) {
26
+ throw new Error(`Failed to log progress: ${response.error}`);
27
+ }
28
+ return { result: { success: true, progress_id: response.data?.progress_id } };
25
29
  };
26
30
  export const getActivityFeed = async (args, ctx) => {
27
- const { project_id, types, created_by, since, limit = 50 } = args;
31
+ const { project_id, limit = 50, since } = args;
28
32
  validateRequired(project_id, 'project_id');
29
33
  validateUUID(project_id, 'project_id');
30
- const { supabase } = ctx;
34
+ const apiClient = getApiClient();
31
35
  const effectiveLimit = Math.min(limit, 200);
32
- const includeTypes = types || ['task', 'progress', 'blocker', 'decision'];
33
- // Fetch slightly more per type to account for interleaving, but not full limit
34
- const perTypeLimit = Math.min(Math.ceil(effectiveLimit / includeTypes.length) * 2, effectiveLimit);
35
- const activities = [];
36
- // Fetch tasks if included
37
- if (includeTypes.includes('task')) {
38
- let query = supabase
39
- .from('tasks')
40
- .select('id, title, status, created_by, created_at, completed_at')
41
- .eq('project_id', project_id)
42
- .order('created_at', { ascending: false })
43
- .limit(perTypeLimit);
44
- if (created_by)
45
- query = query.eq('created_by', created_by);
46
- if (since)
47
- query = query.gt('created_at', since);
48
- const { data: tasks } = await query;
49
- if (tasks) {
50
- for (const task of tasks) {
51
- activities.push({
52
- type: 'task',
53
- id: task.id,
54
- title: task.title,
55
- status: task.status,
56
- created_by: task.created_by,
57
- created_at: task.created_at,
58
- });
59
- }
60
- }
61
- }
62
- // Fetch progress logs if included
63
- if (includeTypes.includes('progress')) {
64
- let query = supabase
65
- .from('progress_logs')
66
- .select('id, summary, created_by, created_at')
67
- .eq('project_id', project_id)
68
- .order('created_at', { ascending: false })
69
- .limit(perTypeLimit);
70
- if (created_by)
71
- query = query.eq('created_by', created_by);
72
- if (since)
73
- query = query.gt('created_at', since);
74
- const { data: logs } = await query;
75
- if (logs) {
76
- for (const log of logs) {
77
- activities.push({
78
- type: 'progress',
79
- id: log.id,
80
- summary: log.summary,
81
- created_by: log.created_by,
82
- created_at: log.created_at,
83
- });
84
- }
85
- }
86
- }
87
- // Fetch blockers if included
88
- if (includeTypes.includes('blocker')) {
89
- let query = supabase
90
- .from('blockers')
91
- .select('id, description, status, created_by, created_at')
92
- .eq('project_id', project_id)
93
- .order('created_at', { ascending: false })
94
- .limit(perTypeLimit);
95
- if (created_by)
96
- query = query.eq('created_by', created_by);
97
- if (since)
98
- query = query.gt('created_at', since);
99
- const { data: blockers } = await query;
100
- if (blockers) {
101
- for (const blocker of blockers) {
102
- activities.push({
103
- type: 'blocker',
104
- id: blocker.id,
105
- description: blocker.description,
106
- status: blocker.status,
107
- created_by: blocker.created_by,
108
- created_at: blocker.created_at,
109
- });
110
- }
111
- }
112
- }
113
- // Fetch decisions if included
114
- if (includeTypes.includes('decision')) {
115
- let query = supabase
116
- .from('decisions')
117
- .select('id, title, created_by, created_at')
118
- .eq('project_id', project_id)
119
- .order('created_at', { ascending: false })
120
- .limit(perTypeLimit);
121
- if (created_by)
122
- query = query.eq('created_by', created_by);
123
- if (since)
124
- query = query.gt('created_at', since);
125
- const { data: decisions } = await query;
126
- if (decisions) {
127
- for (const decision of decisions) {
128
- activities.push({
129
- type: 'decision',
130
- id: decision.id,
131
- title: decision.title,
132
- created_by: decision.created_by,
133
- created_at: decision.created_at,
134
- });
135
- }
136
- }
36
+ const response = await apiClient.getActivityFeed(project_id, {
37
+ limit: effectiveLimit,
38
+ since
39
+ });
40
+ if (!response.ok) {
41
+ throw new Error(`Failed to fetch activity feed: ${response.error}`);
137
42
  }
138
- // Sort by created_at descending and limit
139
- activities.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
140
- const limitedActivities = activities.slice(0, effectiveLimit);
141
- return { result: { activities: limitedActivities } };
43
+ return { result: { activities: response.data?.activities || [] } };
142
44
  };
143
45
  /**
144
46
  * Progress handlers registry
@@ -9,262 +9,83 @@
9
9
  * - update_project_readme
10
10
  */
11
11
  import { validateRequired, validateUUID, validateProjectStatus } from '../validators.js';
12
- /**
13
- * Get user-created items since last sync
14
- */
15
- async function getUserUpdates(supabase, auth, projectId, currentSessionId) {
16
- let lastSyncedAt;
17
- if (currentSessionId) {
18
- const { data: session } = await supabase
19
- .from('agent_sessions')
20
- .select('last_synced_at')
21
- .eq('id', currentSessionId)
22
- .single();
23
- lastSyncedAt = session?.last_synced_at || new Date(0).toISOString();
24
- }
25
- else {
26
- const { data: session } = await supabase
27
- .from('agent_sessions')
28
- .select('last_synced_at')
29
- .eq('api_key_id', auth.apiKeyId)
30
- .eq('project_id', projectId)
31
- .single();
32
- lastSyncedAt = session?.last_synced_at || new Date(0).toISOString();
33
- }
34
- const [tasksResult, blockersResult, ideasResult] = await Promise.all([
35
- supabase
36
- .from('tasks')
37
- .select('id, title, created_at')
38
- .eq('project_id', projectId)
39
- .eq('created_by', 'user')
40
- .gt('created_at', lastSyncedAt)
41
- .order('created_at', { ascending: false })
42
- .limit(5),
43
- supabase
44
- .from('blockers')
45
- .select('id, description, created_at')
46
- .eq('project_id', projectId)
47
- .eq('created_by', 'user')
48
- .gt('created_at', lastSyncedAt)
49
- .order('created_at', { ascending: false })
50
- .limit(5),
51
- supabase
52
- .from('ideas')
53
- .select('id, title, created_at')
54
- .eq('project_id', projectId)
55
- .eq('created_by', 'user')
56
- .gt('created_at', lastSyncedAt)
57
- .order('created_at', { ascending: false })
58
- .limit(5),
59
- ]);
60
- const tasks = tasksResult.data || [];
61
- const blockers = blockersResult.data || [];
62
- const ideas = ideasResult.data || [];
63
- if (tasks.length === 0 && blockers.length === 0 && ideas.length === 0) {
64
- return undefined;
65
- }
66
- return { tasks, blockers, ideas };
67
- }
12
+ import { getApiClient } from '../api-client.js';
68
13
  export const getProjectContext = async (args, ctx) => {
69
14
  const { project_id, git_url } = args;
70
- const { supabase, auth, session } = ctx;
15
+ const apiClient = getApiClient();
71
16
  // If no project_id or git_url, list all projects
72
17
  if (!project_id && !git_url) {
73
- const { data: projects, error } = await supabase
74
- .from('projects')
75
- .select('id, name, description, status, git_url')
76
- .eq('user_id', auth.userId)
77
- .order('updated_at', { ascending: false });
78
- if (error)
79
- throw new Error(`Failed to fetch projects: ${error.message}`);
80
- return { result: { projects: projects || [] } };
18
+ const response = await apiClient.listProjects();
19
+ if (!response.ok) {
20
+ throw new Error(response.error || 'Failed to fetch projects');
21
+ }
22
+ return { result: { projects: response.data?.projects || [] } };
81
23
  }
82
24
  // Find project by ID or git_url
83
- let query = supabase
84
- .from('projects')
85
- .select('id, name, description, goal, status, git_url, agent_instructions, tech_stack')
86
- .eq('user_id', auth.userId);
87
- if (project_id) {
88
- query = query.eq('id', project_id);
89
- }
90
- else if (git_url) {
91
- query = query.eq('git_url', git_url);
25
+ const response = await apiClient.getProject(project_id || 'by-git-url', git_url);
26
+ if (!response.ok) {
27
+ throw new Error(response.error || 'Failed to fetch project');
92
28
  }
93
- const { data: project, error: projectError } = await query.single();
94
- if (projectError || !project) {
29
+ if (!response.data?.found) {
95
30
  return {
96
31
  result: {
97
32
  found: false,
98
- message: 'Project not found. Use create_project to create one.',
33
+ message: response.data?.message || 'Project not found. Use create_project to create one.',
99
34
  },
100
35
  };
101
36
  }
102
- // Fetch related data with minimal fields
103
- const [tasksResult, blockersResult, decisionsResult, progressResult] = await Promise.all([
104
- supabase
105
- .from('tasks')
106
- .select('id, title, description, priority, status, estimated_minutes')
107
- .eq('project_id', project.id)
108
- .in('status', ['pending', 'in_progress'])
109
- .order('priority', { ascending: true })
110
- .limit(10),
111
- supabase
112
- .from('blockers')
113
- .select('id, description')
114
- .eq('project_id', project.id)
115
- .eq('status', 'open')
116
- .limit(5),
117
- supabase
118
- .from('decisions')
119
- .select('title')
120
- .eq('project_id', project.id)
121
- .order('created_at', { ascending: false })
122
- .limit(5),
123
- supabase
124
- .from('progress_logs')
125
- .select('summary')
126
- .eq('project_id', project.id)
127
- .order('created_at', { ascending: false })
128
- .limit(5),
129
- ]);
130
- const userUpdates = await getUserUpdates(supabase, auth, project.id, session.currentSessionId);
131
- // Build compact response
132
- const result = {
133
- found: true,
134
- project,
135
- active_tasks: tasksResult.data || [],
136
- };
137
- const blockers = blockersResult.data || [];
138
- const decisions = decisionsResult.data || [];
139
- const progress = progressResult.data || [];
140
- if (blockers.length > 0)
141
- result.open_blockers = blockers;
142
- if (decisions.length > 0)
143
- result.recent_decisions = decisions;
144
- if (progress.length > 0)
145
- result.recent_progress = progress;
146
- return { result, user_updates: userUpdates };
37
+ return { result: response.data };
147
38
  };
148
39
  export const getGitWorkflow = async (args, ctx) => {
149
40
  const { project_id, task_id } = args;
150
41
  validateRequired(project_id, 'project_id');
151
42
  validateUUID(project_id, 'project_id');
152
- const { supabase } = ctx;
153
- const { data: project, error } = await supabase
154
- .from('projects')
155
- .select('git_workflow, git_main_branch, git_develop_branch, git_auto_branch, git_auto_tag, git_url')
156
- .eq('id', project_id)
157
- .single();
158
- if (error || !project)
159
- throw new Error(`Project not found: ${project_id}`);
160
- let task = null;
161
- if (task_id) {
162
- const { data: taskData } = await supabase
163
- .from('tasks')
164
- .select('id, title, git_branch')
165
- .eq('id', task_id)
166
- .single();
167
- task = taskData;
43
+ const apiClient = getApiClient();
44
+ const response = await apiClient.getGitWorkflow(project_id, task_id);
45
+ if (!response.ok) {
46
+ throw new Error(response.error || 'Failed to get git workflow');
168
47
  }
169
- // Generate workflow instructions
170
- const workflow = project.git_workflow || 'none';
171
- const mainBranch = project.git_main_branch || 'main';
172
- const developBranch = project.git_develop_branch || 'develop';
173
- const instructions = {
174
- 'none': [
175
- 'No git workflow configured for this project.',
176
- 'Commit changes directly without branching strategy.',
177
- ],
178
- 'trunk-based': [
179
- `Work directly on the ${mainBranch} branch.`,
180
- 'Make small, frequent commits.',
181
- 'Ensure all tests pass before committing.',
182
- `Tag deployments on ${mainBranch} (e.g., git tag v1.0.0).`,
183
- ],
184
- 'github-flow': [
185
- task ? `Create feature branch: git checkout -b feature/${task.id.slice(0, 8)}-${task.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').slice(0, 30)}` : 'Create a feature branch for each task.',
186
- 'Make commits with descriptive messages.',
187
- 'Push branch and create a pull request.',
188
- `Merge to ${mainBranch} after review/validation.`,
189
- project.git_auto_tag ? 'Deployments will be automatically tagged.' : `Tag deployments manually on ${mainBranch}.`,
190
- ],
191
- 'git-flow': [
192
- task ? `Create feature branch from ${developBranch}: git checkout -b feature/${task.id.slice(0, 8)}-${task.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').slice(0, 30)} ${developBranch}` : `Create feature branches from ${developBranch}.`,
193
- `Merge completed features back to ${developBranch}.`,
194
- `Create release branches from ${developBranch} when ready.`,
195
- `Merge releases to both ${mainBranch} and ${developBranch}.`,
196
- `Tag releases on ${mainBranch}.`,
197
- ],
198
- };
199
- const result = {
200
- workflow,
201
- main_branch: mainBranch,
202
- develop_branch: workflow === 'git-flow' ? developBranch : null,
203
- auto_branch: project.git_auto_branch,
204
- auto_tag: project.git_auto_tag,
205
- instructions: instructions[workflow] || instructions['none'],
206
- };
207
- if (task) {
208
- result.task = {
209
- id: task.id,
210
- title: task.title,
211
- current_branch: task.git_branch,
212
- suggested_branch: workflow !== 'none' && workflow !== 'trunk-based'
213
- ? `feature/${task.id.slice(0, 8)}-${task.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').slice(0, 30)}`
214
- : null,
215
- };
216
- }
217
- return { result };
48
+ return { result: response.data };
218
49
  };
219
50
  export const createProject = async (args, ctx) => {
220
51
  const { name, description, goal, git_url, tech_stack } = args;
221
- const { supabase, auth } = ctx;
222
- const { data, error } = await supabase
223
- .from('projects')
224
- .insert({
225
- user_id: auth.userId,
52
+ validateRequired(name, 'name');
53
+ const apiClient = getApiClient();
54
+ const response = await apiClient.createProject({
226
55
  name,
227
- description: description || null,
228
- goal: goal || null,
229
- git_url: git_url || null,
230
- tech_stack: tech_stack || null,
231
- })
232
- .select()
233
- .single();
234
- if (error)
235
- throw new Error(`Failed to create project: ${error.message}`);
236
- return { result: { success: true, project: data } };
56
+ description,
57
+ goal,
58
+ git_url,
59
+ tech_stack
60
+ });
61
+ if (!response.ok) {
62
+ throw new Error(response.error || 'Failed to create project');
63
+ }
64
+ return { result: response.data };
237
65
  };
238
66
  export const updateProject = async (args, ctx) => {
239
67
  const { project_id, ...updates } = args;
240
68
  validateRequired(project_id, 'project_id');
241
69
  validateUUID(project_id, 'project_id');
242
70
  validateProjectStatus(updates.status);
243
- const { supabase, auth } = ctx;
244
- const { error } = await supabase
245
- .from('projects')
246
- .update(updates)
247
- .eq('id', project_id)
248
- .eq('user_id', auth.userId);
249
- if (error)
250
- throw new Error(`Failed to update project: ${error.message}`);
251
- return { result: { success: true, project_id } };
71
+ const apiClient = getApiClient();
72
+ const response = await apiClient.updateProject(project_id, updates);
73
+ if (!response.ok) {
74
+ throw new Error(response.error || 'Failed to update project');
75
+ }
76
+ return { result: response.data };
252
77
  };
253
78
  export const updateProjectReadme = async (args, ctx) => {
254
79
  const { project_id, readme_content } = args;
255
80
  validateRequired(project_id, 'project_id');
256
81
  validateUUID(project_id, 'project_id');
257
82
  validateRequired(readme_content, 'readme_content');
258
- const { error } = await ctx.supabase
259
- .from('projects')
260
- .update({
261
- readme_content,
262
- readme_updated_at: new Date().toISOString(),
263
- })
264
- .eq('id', project_id);
265
- if (error)
266
- throw new Error(`Failed to update README: ${error.message}`);
267
- return { result: { success: true } };
83
+ const apiClient = getApiClient();
84
+ const response = await apiClient.updateProjectReadme(project_id, readme_content);
85
+ if (!response.ok) {
86
+ throw new Error(response.error || 'Failed to update README');
87
+ }
88
+ return { result: response.data };
268
89
  };
269
90
  /**
270
91
  * Project handlers registry
@@ -5,6 +5,8 @@
5
5
  * - get_pending_requests
6
6
  * - acknowledge_request
7
7
  * - answer_question
8
+ *
9
+ * MIGRATED: Uses Vibescope API client instead of direct Supabase
8
10
  */
9
11
  import type { Handler, HandlerRegistry } from './types.js';
10
12
  export declare const getPendingRequests: Handler;
@@ -5,68 +5,25 @@
5
5
  * - get_pending_requests
6
6
  * - acknowledge_request
7
7
  * - answer_question
8
+ *
9
+ * MIGRATED: Uses Vibescope API client instead of direct Supabase
8
10
  */
9
11
  import { validateRequired, validateUUID } from '../validators.js';
12
+ import { getApiClient } from '../api-client.js';
10
13
  export const getPendingRequests = async (args, ctx) => {
11
14
  const { project_id } = args;
12
15
  validateRequired(project_id, 'project_id');
13
16
  validateUUID(project_id, 'project_id');
14
- const { supabase, session } = ctx;
15
- const currentSessionId = session.currentSessionId;
16
- // Get active session IDs to identify orphaned questions
17
- const { data: activeSessions } = await supabase
18
- .from('agent_sessions')
19
- .select('id')
20
- .eq('project_id', project_id)
21
- .eq('status', 'active');
22
- const activeSessionIds = new Set((activeSessions || []).map((s) => s.id));
23
- // Get pending requests for this project:
24
- // - Unacknowledged requests OR unanswered questions (questions need answers, not just acknowledgment)
25
- const { data: requests, error } = await supabase
26
- .from('agent_requests')
27
- .select('*')
28
- .eq('project_id', project_id)
29
- .or('acknowledged_at.is.null,and(request_type.eq.question,answered_at.is.null)')
30
- .order('created_at', { ascending: false });
31
- if (error)
32
- throw error;
33
- // Filter to requests this agent can handle
34
- const filteredRequests = (requests || []).filter((r) => {
35
- // Broadcast requests (session_id is null) - anyone can handle
36
- if (!r.session_id)
37
- return true;
38
- // Targeted to this session
39
- if (r.session_id === currentSessionId)
40
- return true;
41
- // Orphaned questions (targeted session is disconnected) - any agent can answer
42
- if (r.request_type === 'question' && !activeSessionIds.has(r.session_id))
43
- return true;
44
- return false;
45
- });
46
- // Sort questions first (highest priority) and add wait times
47
- const now = new Date();
48
- const sortedRequests = filteredRequests
49
- .map((r) => ({
50
- ...r,
51
- wait_minutes: Math.floor((now.getTime() - new Date(r.created_at).getTime()) / 60000),
52
- }))
53
- .sort((a, b) => {
54
- // Questions first, then by created_at (oldest first for urgency)
55
- const aIsQuestion = a.request_type === 'question' && !a.answered_at;
56
- const bIsQuestion = b.request_type === 'question' && !b.answered_at;
57
- if (aIsQuestion && !bIsQuestion)
58
- return -1;
59
- if (!aIsQuestion && bIsQuestion)
60
- return 1;
61
- return new Date(a.created_at).getTime() - new Date(b.created_at).getTime();
62
- });
63
- // Count unanswered questions separately
64
- const questionsCount = sortedRequests.filter((r) => r.request_type === 'question' && !r.answered_at).length;
17
+ const { session } = ctx;
18
+ const apiClient = getApiClient();
19
+ const response = await apiClient.getPendingRequests(project_id, session.currentSessionId || undefined);
20
+ if (!response.ok) {
21
+ throw new Error(`Failed to get pending requests: ${response.error}`);
22
+ }
65
23
  return {
66
24
  result: {
67
- requests: sortedRequests,
68
- count: sortedRequests.length,
69
- questions_count: questionsCount,
25
+ requests: response.data?.requests || [],
26
+ count: response.data?.requests?.length || 0,
70
27
  },
71
28
  };
72
29
  };
@@ -74,22 +31,15 @@ export const acknowledgeRequest = async (args, ctx) => {
74
31
  const { request_id } = args;
75
32
  validateRequired(request_id, 'request_id');
76
33
  validateUUID(request_id, 'request_id');
77
- const { supabase, session } = ctx;
78
- const { data: request, error } = await supabase
79
- .from('agent_requests')
80
- .update({
81
- acknowledged_at: new Date().toISOString(),
82
- acknowledged_by_session_id: session.currentSessionId,
83
- })
84
- .eq('id', request_id)
85
- .select()
86
- .single();
87
- if (error)
88
- throw error;
34
+ const { session } = ctx;
35
+ const apiClient = getApiClient();
36
+ const response = await apiClient.acknowledgeRequest(request_id, session.currentSessionId || undefined);
37
+ if (!response.ok) {
38
+ throw new Error(`Failed to acknowledge request: ${response.error}`);
39
+ }
89
40
  return {
90
41
  result: {
91
42
  success: true,
92
- request,
93
43
  },
94
44
  };
95
45
  };
@@ -98,26 +48,16 @@ export const answerQuestion = async (args, ctx) => {
98
48
  validateRequired(request_id, 'request_id');
99
49
  validateRequired(answer, 'answer');
100
50
  validateUUID(request_id, 'request_id');
101
- const { supabase, session } = ctx;
102
- // Update the request with the answer
103
- const { data: request, error } = await supabase
104
- .from('agent_requests')
105
- .update({
106
- answer,
107
- answered_at: new Date().toISOString(),
108
- acknowledged_at: new Date().toISOString(),
109
- acknowledged_by_session_id: session.currentSessionId,
110
- })
111
- .eq('id', request_id)
112
- .select()
113
- .single();
114
- if (error)
115
- throw error;
51
+ const { session } = ctx;
52
+ const apiClient = getApiClient();
53
+ const response = await apiClient.answerQuestion(request_id, answer, session.currentSessionId || undefined);
54
+ if (!response.ok) {
55
+ throw new Error(`Failed to answer question: ${response.error}`);
56
+ }
116
57
  return {
117
58
  result: {
118
59
  success: true,
119
60
  message: 'Question answered successfully',
120
- request,
121
61
  },
122
62
  };
123
63
  };