@vibescope/mcp-server 0.0.1

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 +98 -0
  2. package/dist/cli.d.ts +34 -0
  3. package/dist/cli.js +356 -0
  4. package/dist/cli.test.d.ts +1 -0
  5. package/dist/cli.test.js +367 -0
  6. package/dist/handlers/__test-utils__.d.ts +72 -0
  7. package/dist/handlers/__test-utils__.js +176 -0
  8. package/dist/handlers/blockers.d.ts +18 -0
  9. package/dist/handlers/blockers.js +81 -0
  10. package/dist/handlers/bodies-of-work.d.ts +34 -0
  11. package/dist/handlers/bodies-of-work.js +614 -0
  12. package/dist/handlers/checkouts.d.ts +37 -0
  13. package/dist/handlers/checkouts.js +377 -0
  14. package/dist/handlers/cost.d.ts +39 -0
  15. package/dist/handlers/cost.js +247 -0
  16. package/dist/handlers/decisions.d.ts +16 -0
  17. package/dist/handlers/decisions.js +64 -0
  18. package/dist/handlers/deployment.d.ts +36 -0
  19. package/dist/handlers/deployment.js +1062 -0
  20. package/dist/handlers/discovery.d.ts +14 -0
  21. package/dist/handlers/discovery.js +870 -0
  22. package/dist/handlers/fallback.d.ts +18 -0
  23. package/dist/handlers/fallback.js +216 -0
  24. package/dist/handlers/findings.d.ts +18 -0
  25. package/dist/handlers/findings.js +110 -0
  26. package/dist/handlers/git-issues.d.ts +22 -0
  27. package/dist/handlers/git-issues.js +247 -0
  28. package/dist/handlers/ideas.d.ts +19 -0
  29. package/dist/handlers/ideas.js +188 -0
  30. package/dist/handlers/index.d.ts +29 -0
  31. package/dist/handlers/index.js +65 -0
  32. package/dist/handlers/knowledge-query.d.ts +22 -0
  33. package/dist/handlers/knowledge-query.js +253 -0
  34. package/dist/handlers/knowledge.d.ts +12 -0
  35. package/dist/handlers/knowledge.js +108 -0
  36. package/dist/handlers/milestones.d.ts +20 -0
  37. package/dist/handlers/milestones.js +179 -0
  38. package/dist/handlers/organizations.d.ts +36 -0
  39. package/dist/handlers/organizations.js +428 -0
  40. package/dist/handlers/progress.d.ts +14 -0
  41. package/dist/handlers/progress.js +149 -0
  42. package/dist/handlers/project.d.ts +20 -0
  43. package/dist/handlers/project.js +278 -0
  44. package/dist/handlers/requests.d.ts +16 -0
  45. package/dist/handlers/requests.js +131 -0
  46. package/dist/handlers/roles.d.ts +30 -0
  47. package/dist/handlers/roles.js +281 -0
  48. package/dist/handlers/session.d.ts +20 -0
  49. package/dist/handlers/session.js +791 -0
  50. package/dist/handlers/tasks.d.ts +52 -0
  51. package/dist/handlers/tasks.js +1111 -0
  52. package/dist/handlers/tasks.test.d.ts +1 -0
  53. package/dist/handlers/tasks.test.js +431 -0
  54. package/dist/handlers/types.d.ts +94 -0
  55. package/dist/handlers/types.js +1 -0
  56. package/dist/handlers/validation.d.ts +16 -0
  57. package/dist/handlers/validation.js +188 -0
  58. package/dist/index.d.ts +2 -0
  59. package/dist/index.js +2707 -0
  60. package/dist/knowledge.d.ts +6 -0
  61. package/dist/knowledge.js +121 -0
  62. package/dist/tools.d.ts +2 -0
  63. package/dist/tools.js +2498 -0
  64. package/dist/utils.d.ts +149 -0
  65. package/dist/utils.js +317 -0
  66. package/dist/utils.test.d.ts +1 -0
  67. package/dist/utils.test.js +532 -0
  68. package/dist/validators.d.ts +35 -0
  69. package/dist/validators.js +111 -0
  70. package/dist/validators.test.d.ts +1 -0
  71. package/dist/validators.test.js +176 -0
  72. package/package.json +44 -0
  73. package/src/cli.test.ts +442 -0
  74. package/src/cli.ts +439 -0
  75. package/src/handlers/__test-utils__.ts +217 -0
  76. package/src/handlers/blockers.test.ts +390 -0
  77. package/src/handlers/blockers.ts +110 -0
  78. package/src/handlers/bodies-of-work.test.ts +1276 -0
  79. package/src/handlers/bodies-of-work.ts +783 -0
  80. package/src/handlers/cost.test.ts +436 -0
  81. package/src/handlers/cost.ts +322 -0
  82. package/src/handlers/decisions.test.ts +401 -0
  83. package/src/handlers/decisions.ts +86 -0
  84. package/src/handlers/deployment.test.ts +516 -0
  85. package/src/handlers/deployment.ts +1289 -0
  86. package/src/handlers/discovery.test.ts +254 -0
  87. package/src/handlers/discovery.ts +969 -0
  88. package/src/handlers/fallback.test.ts +687 -0
  89. package/src/handlers/fallback.ts +260 -0
  90. package/src/handlers/findings.test.ts +565 -0
  91. package/src/handlers/findings.ts +153 -0
  92. package/src/handlers/ideas.test.ts +753 -0
  93. package/src/handlers/ideas.ts +247 -0
  94. package/src/handlers/index.ts +69 -0
  95. package/src/handlers/milestones.test.ts +584 -0
  96. package/src/handlers/milestones.ts +217 -0
  97. package/src/handlers/organizations.test.ts +997 -0
  98. package/src/handlers/organizations.ts +550 -0
  99. package/src/handlers/progress.test.ts +369 -0
  100. package/src/handlers/progress.ts +188 -0
  101. package/src/handlers/project.test.ts +562 -0
  102. package/src/handlers/project.ts +352 -0
  103. package/src/handlers/requests.test.ts +531 -0
  104. package/src/handlers/requests.ts +150 -0
  105. package/src/handlers/session.test.ts +459 -0
  106. package/src/handlers/session.ts +912 -0
  107. package/src/handlers/tasks.test.ts +602 -0
  108. package/src/handlers/tasks.ts +1393 -0
  109. package/src/handlers/types.ts +88 -0
  110. package/src/handlers/validation.test.ts +880 -0
  111. package/src/handlers/validation.ts +223 -0
  112. package/src/index.ts +3205 -0
  113. package/src/knowledge.ts +132 -0
  114. package/src/tmpclaude-0078-cwd +1 -0
  115. package/src/tmpclaude-0ee1-cwd +1 -0
  116. package/src/tmpclaude-2dd5-cwd +1 -0
  117. package/src/tmpclaude-344c-cwd +1 -0
  118. package/src/tmpclaude-3860-cwd +1 -0
  119. package/src/tmpclaude-4b63-cwd +1 -0
  120. package/src/tmpclaude-5c73-cwd +1 -0
  121. package/src/tmpclaude-5ee3-cwd +1 -0
  122. package/src/tmpclaude-6795-cwd +1 -0
  123. package/src/tmpclaude-709e-cwd +1 -0
  124. package/src/tmpclaude-9839-cwd +1 -0
  125. package/src/tmpclaude-d829-cwd +1 -0
  126. package/src/tmpclaude-e072-cwd +1 -0
  127. package/src/tmpclaude-f6ee-cwd +1 -0
  128. package/src/utils.test.ts +681 -0
  129. package/src/utils.ts +375 -0
  130. package/src/validators.test.ts +223 -0
  131. package/src/validators.ts +122 -0
  132. package/tmpclaude-0439-cwd +1 -0
  133. package/tmpclaude-132f-cwd +1 -0
  134. package/tmpclaude-15bb-cwd +1 -0
  135. package/tmpclaude-165a-cwd +1 -0
  136. package/tmpclaude-1ba9-cwd +1 -0
  137. package/tmpclaude-21a3-cwd +1 -0
  138. package/tmpclaude-2a38-cwd +1 -0
  139. package/tmpclaude-2adf-cwd +1 -0
  140. package/tmpclaude-2f56-cwd +1 -0
  141. package/tmpclaude-3626-cwd +1 -0
  142. package/tmpclaude-3727-cwd +1 -0
  143. package/tmpclaude-40bc-cwd +1 -0
  144. package/tmpclaude-436f-cwd +1 -0
  145. package/tmpclaude-4783-cwd +1 -0
  146. package/tmpclaude-4b6d-cwd +1 -0
  147. package/tmpclaude-4ba4-cwd +1 -0
  148. package/tmpclaude-51e6-cwd +1 -0
  149. package/tmpclaude-5ecf-cwd +1 -0
  150. package/tmpclaude-6f97-cwd +1 -0
  151. package/tmpclaude-7fb2-cwd +1 -0
  152. package/tmpclaude-825c-cwd +1 -0
  153. package/tmpclaude-8baf-cwd +1 -0
  154. package/tmpclaude-8d9f-cwd +1 -0
  155. package/tmpclaude-975c-cwd +1 -0
  156. package/tmpclaude-9983-cwd +1 -0
  157. package/tmpclaude-a045-cwd +1 -0
  158. package/tmpclaude-ac4a-cwd +1 -0
  159. package/tmpclaude-b593-cwd +1 -0
  160. package/tmpclaude-b891-cwd +1 -0
  161. package/tmpclaude-c032-cwd +1 -0
  162. package/tmpclaude-cf43-cwd +1 -0
  163. package/tmpclaude-d040-cwd +1 -0
  164. package/tmpclaude-dcdd-cwd +1 -0
  165. package/tmpclaude-dcee-cwd +1 -0
  166. package/tmpclaude-e16b-cwd +1 -0
  167. package/tmpclaude-ecd2-cwd +1 -0
  168. package/tmpclaude-f48d-cwd +1 -0
  169. package/tsconfig.json +16 -0
  170. package/vitest.config.ts +13 -0
@@ -0,0 +1,352 @@
1
+ /**
2
+ * Project Handlers
3
+ *
4
+ * Handles project CRUD and configuration:
5
+ * - get_project_context
6
+ * - get_git_workflow
7
+ * - create_project
8
+ * - update_project
9
+ * - update_project_readme
10
+ */
11
+
12
+ import type { Handler, HandlerRegistry, UserUpdates, AuthContext } from './types.js';
13
+ import type { SupabaseClient } from '@supabase/supabase-js';
14
+ import { validateRequired, validateUUID, validateProjectStatus } from '../validators.js';
15
+
16
+ /**
17
+ * Get user-created items since last sync
18
+ */
19
+ async function getUserUpdates(
20
+ supabase: SupabaseClient,
21
+ auth: AuthContext,
22
+ projectId: string,
23
+ currentSessionId: string | null
24
+ ): Promise<UserUpdates | undefined> {
25
+ let lastSyncedAt: string;
26
+
27
+ if (currentSessionId) {
28
+ const { data: session } = await supabase
29
+ .from('agent_sessions')
30
+ .select('last_synced_at')
31
+ .eq('id', currentSessionId)
32
+ .single();
33
+ lastSyncedAt = session?.last_synced_at || new Date(0).toISOString();
34
+ } else {
35
+ const { data: session } = await supabase
36
+ .from('agent_sessions')
37
+ .select('last_synced_at')
38
+ .eq('api_key_id', auth.apiKeyId)
39
+ .eq('project_id', projectId)
40
+ .single();
41
+ lastSyncedAt = session?.last_synced_at || new Date(0).toISOString();
42
+ }
43
+
44
+ const [tasksResult, blockersResult, ideasResult] = await Promise.all([
45
+ supabase
46
+ .from('tasks')
47
+ .select('id, title, created_at')
48
+ .eq('project_id', projectId)
49
+ .eq('created_by', 'user')
50
+ .gt('created_at', lastSyncedAt)
51
+ .order('created_at', { ascending: false })
52
+ .limit(5),
53
+ supabase
54
+ .from('blockers')
55
+ .select('id, description, created_at')
56
+ .eq('project_id', projectId)
57
+ .eq('created_by', 'user')
58
+ .gt('created_at', lastSyncedAt)
59
+ .order('created_at', { ascending: false })
60
+ .limit(5),
61
+ supabase
62
+ .from('ideas')
63
+ .select('id, title, created_at')
64
+ .eq('project_id', projectId)
65
+ .eq('created_by', 'user')
66
+ .gt('created_at', lastSyncedAt)
67
+ .order('created_at', { ascending: false })
68
+ .limit(5),
69
+ ]);
70
+
71
+ const tasks = tasksResult.data || [];
72
+ const blockers = blockersResult.data || [];
73
+ const ideas = ideasResult.data || [];
74
+
75
+ if (tasks.length === 0 && blockers.length === 0 && ideas.length === 0) {
76
+ return undefined;
77
+ }
78
+
79
+ return { tasks, blockers, ideas };
80
+ }
81
+
82
+ export const getProjectContext: Handler = async (args, ctx) => {
83
+ const { project_id, git_url } = args as {
84
+ project_id?: string;
85
+ git_url?: string;
86
+ };
87
+
88
+ const { supabase, auth, session } = ctx;
89
+
90
+ // If no project_id or git_url, list all projects
91
+ if (!project_id && !git_url) {
92
+ const { data: projects, error } = await supabase
93
+ .from('projects')
94
+ .select('id, name, description, status, git_url')
95
+ .eq('user_id', auth.userId)
96
+ .order('updated_at', { ascending: false });
97
+
98
+ if (error) throw new Error(`Failed to fetch projects: ${error.message}`);
99
+ return { result: { projects: projects || [] } };
100
+ }
101
+
102
+ // Find project by ID or git_url
103
+ let query = supabase
104
+ .from('projects')
105
+ .select('id, name, description, goal, status, git_url, agent_instructions, tech_stack')
106
+ .eq('user_id', auth.userId);
107
+
108
+ if (project_id) {
109
+ query = query.eq('id', project_id);
110
+ } else if (git_url) {
111
+ query = query.eq('git_url', git_url);
112
+ }
113
+
114
+ const { data: project, error: projectError } = await query.single();
115
+
116
+ if (projectError || !project) {
117
+ return {
118
+ result: {
119
+ found: false,
120
+ message: 'Project not found. Use create_project to create one.',
121
+ },
122
+ };
123
+ }
124
+
125
+ // Fetch related data with minimal fields
126
+ const [tasksResult, blockersResult, decisionsResult, progressResult] = await Promise.all([
127
+ supabase
128
+ .from('tasks')
129
+ .select('id, title, description, priority, status, estimated_minutes')
130
+ .eq('project_id', project.id)
131
+ .in('status', ['pending', 'in_progress'])
132
+ .order('priority', { ascending: true })
133
+ .limit(10),
134
+ supabase
135
+ .from('blockers')
136
+ .select('id, description')
137
+ .eq('project_id', project.id)
138
+ .eq('status', 'open')
139
+ .limit(5),
140
+ supabase
141
+ .from('decisions')
142
+ .select('title')
143
+ .eq('project_id', project.id)
144
+ .order('created_at', { ascending: false })
145
+ .limit(5),
146
+ supabase
147
+ .from('progress_logs')
148
+ .select('summary')
149
+ .eq('project_id', project.id)
150
+ .order('created_at', { ascending: false })
151
+ .limit(5),
152
+ ]);
153
+
154
+ const userUpdates = await getUserUpdates(supabase, auth, project.id, session.currentSessionId);
155
+
156
+ // Build compact response
157
+ const result: Record<string, unknown> = {
158
+ found: true,
159
+ project,
160
+ active_tasks: tasksResult.data || [],
161
+ };
162
+
163
+ const blockers = blockersResult.data || [];
164
+ const decisions = decisionsResult.data || [];
165
+ const progress = progressResult.data || [];
166
+
167
+ if (blockers.length > 0) result.open_blockers = blockers;
168
+ if (decisions.length > 0) result.recent_decisions = decisions;
169
+ if (progress.length > 0) result.recent_progress = progress;
170
+
171
+ return { result, user_updates: userUpdates } as { result: Record<string, unknown>; user_updates?: UserUpdates };
172
+ };
173
+
174
+ export const getGitWorkflow: Handler = async (args, ctx) => {
175
+ const { project_id, task_id } = args as {
176
+ project_id: string;
177
+ task_id?: string;
178
+ };
179
+
180
+ validateRequired(project_id, 'project_id');
181
+ validateUUID(project_id, 'project_id');
182
+
183
+ const { supabase } = ctx;
184
+
185
+ const { data: project, error } = await supabase
186
+ .from('projects')
187
+ .select('git_workflow, git_main_branch, git_develop_branch, git_auto_branch, git_auto_tag, git_url')
188
+ .eq('id', project_id)
189
+ .single();
190
+
191
+ if (error || !project) throw new Error(`Project not found: ${project_id}`);
192
+
193
+ let task = null;
194
+ if (task_id) {
195
+ const { data: taskData } = await supabase
196
+ .from('tasks')
197
+ .select('id, title, git_branch')
198
+ .eq('id', task_id)
199
+ .single();
200
+ task = taskData;
201
+ }
202
+
203
+ // Generate workflow instructions
204
+ const workflow = project.git_workflow || 'none';
205
+ const mainBranch = project.git_main_branch || 'main';
206
+ const developBranch = project.git_develop_branch || 'develop';
207
+
208
+ const instructions: Record<string, string[]> = {
209
+ 'none': [
210
+ 'No git workflow configured for this project.',
211
+ 'Commit changes directly without branching strategy.',
212
+ ],
213
+ 'trunk-based': [
214
+ `Work directly on the ${mainBranch} branch.`,
215
+ 'Make small, frequent commits.',
216
+ 'Ensure all tests pass before committing.',
217
+ `Tag deployments on ${mainBranch} (e.g., git tag v1.0.0).`,
218
+ ],
219
+ 'github-flow': [
220
+ 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.',
221
+ 'Make commits with descriptive messages.',
222
+ 'Push branch and create a pull request.',
223
+ `Merge to ${mainBranch} after review/validation.`,
224
+ project.git_auto_tag ? 'Deployments will be automatically tagged.' : `Tag deployments manually on ${mainBranch}.`,
225
+ ],
226
+ 'git-flow': [
227
+ 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}.`,
228
+ `Merge completed features back to ${developBranch}.`,
229
+ `Create release branches from ${developBranch} when ready.`,
230
+ `Merge releases to both ${mainBranch} and ${developBranch}.`,
231
+ `Tag releases on ${mainBranch}.`,
232
+ ],
233
+ };
234
+
235
+ const result: Record<string, unknown> = {
236
+ workflow,
237
+ main_branch: mainBranch,
238
+ develop_branch: workflow === 'git-flow' ? developBranch : null,
239
+ auto_branch: project.git_auto_branch,
240
+ auto_tag: project.git_auto_tag,
241
+ instructions: instructions[workflow] || instructions['none'],
242
+ };
243
+
244
+ if (task) {
245
+ result.task = {
246
+ id: task.id,
247
+ title: task.title,
248
+ current_branch: task.git_branch,
249
+ suggested_branch: workflow !== 'none' && workflow !== 'trunk-based'
250
+ ? `feature/${task.id.slice(0, 8)}-${task.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').slice(0, 30)}`
251
+ : null,
252
+ };
253
+ }
254
+
255
+ return { result };
256
+ };
257
+
258
+ export const createProject: Handler = async (args, ctx) => {
259
+ const { name, description, goal, git_url, tech_stack } = args as {
260
+ name: string;
261
+ description?: string;
262
+ goal?: string;
263
+ git_url?: string;
264
+ tech_stack?: string[];
265
+ };
266
+
267
+ const { supabase, auth } = ctx;
268
+
269
+ const { data, error } = await supabase
270
+ .from('projects')
271
+ .insert({
272
+ user_id: auth.userId,
273
+ name,
274
+ description: description || null,
275
+ goal: goal || null,
276
+ git_url: git_url || null,
277
+ tech_stack: tech_stack || null,
278
+ })
279
+ .select()
280
+ .single();
281
+
282
+ if (error) throw new Error(`Failed to create project: ${error.message}`);
283
+ return { result: { success: true, project: data } };
284
+ };
285
+
286
+ export const updateProject: Handler = async (args, ctx) => {
287
+ const { project_id, ...updates } = args as {
288
+ project_id: string;
289
+ name?: string;
290
+ description?: string;
291
+ goal?: string;
292
+ git_url?: string;
293
+ tech_stack?: string[];
294
+ status?: string;
295
+ git_workflow?: 'none' | 'trunk-based' | 'github-flow' | 'git-flow';
296
+ git_main_branch?: string;
297
+ git_develop_branch?: string;
298
+ git_auto_branch?: boolean;
299
+ git_auto_tag?: boolean;
300
+ deployment_instructions?: string;
301
+ };
302
+
303
+ validateRequired(project_id, 'project_id');
304
+ validateUUID(project_id, 'project_id');
305
+ validateProjectStatus(updates.status);
306
+
307
+ const { supabase, auth } = ctx;
308
+
309
+ const { error } = await supabase
310
+ .from('projects')
311
+ .update(updates)
312
+ .eq('id', project_id)
313
+ .eq('user_id', auth.userId);
314
+
315
+ if (error) throw new Error(`Failed to update project: ${error.message}`);
316
+
317
+ return { result: { success: true, project_id } };
318
+ };
319
+
320
+ export const updateProjectReadme: Handler = async (args, ctx) => {
321
+ const { project_id, readme_content } = args as {
322
+ project_id: string;
323
+ readme_content: string;
324
+ };
325
+
326
+ validateRequired(project_id, 'project_id');
327
+ validateUUID(project_id, 'project_id');
328
+ validateRequired(readme_content, 'readme_content');
329
+
330
+ const { error } = await ctx.supabase
331
+ .from('projects')
332
+ .update({
333
+ readme_content,
334
+ readme_updated_at: new Date().toISOString(),
335
+ })
336
+ .eq('id', project_id);
337
+
338
+ if (error) throw new Error(`Failed to update README: ${error.message}`);
339
+
340
+ return { result: { success: true } };
341
+ };
342
+
343
+ /**
344
+ * Project handlers registry
345
+ */
346
+ export const projectHandlers: HandlerRegistry = {
347
+ get_project_context: getProjectContext,
348
+ get_git_workflow: getGitWorkflow,
349
+ create_project: createProject,
350
+ update_project: updateProject,
351
+ update_project_readme: updateProjectReadme,
352
+ };