@vibescope/mcp-server 0.2.0 → 0.2.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 (65) hide show
  1. package/dist/api-client.d.ts +64 -1
  2. package/dist/api-client.js +34 -3
  3. package/dist/handlers/bodies-of-work.js +82 -49
  4. package/dist/handlers/cost.js +62 -54
  5. package/dist/handlers/decisions.js +29 -16
  6. package/dist/handlers/deployment.js +112 -106
  7. package/dist/handlers/discovery.js +35 -5
  8. package/dist/handlers/fallback.js +24 -19
  9. package/dist/handlers/file-checkouts.d.ts +18 -0
  10. package/dist/handlers/file-checkouts.js +101 -0
  11. package/dist/handlers/findings.d.ts +6 -0
  12. package/dist/handlers/findings.js +85 -30
  13. package/dist/handlers/git-issues.js +36 -32
  14. package/dist/handlers/ideas.js +44 -26
  15. package/dist/handlers/index.d.ts +2 -0
  16. package/dist/handlers/index.js +6 -0
  17. package/dist/handlers/milestones.js +34 -27
  18. package/dist/handlers/organizations.js +86 -78
  19. package/dist/handlers/progress.js +22 -11
  20. package/dist/handlers/project.js +62 -22
  21. package/dist/handlers/requests.js +15 -11
  22. package/dist/handlers/roles.d.ts +18 -0
  23. package/dist/handlers/roles.js +130 -0
  24. package/dist/handlers/session.js +30 -8
  25. package/dist/handlers/sprints.js +76 -64
  26. package/dist/handlers/tasks.js +113 -73
  27. package/dist/handlers/validation.js +18 -14
  28. package/dist/tools.js +387 -0
  29. package/package.json +1 -1
  30. package/src/api-client.ts +89 -6
  31. package/src/handlers/__test-setup__.ts +7 -0
  32. package/src/handlers/bodies-of-work.ts +101 -101
  33. package/src/handlers/cost.test.ts +34 -44
  34. package/src/handlers/cost.ts +77 -92
  35. package/src/handlers/decisions.test.ts +3 -2
  36. package/src/handlers/decisions.ts +32 -27
  37. package/src/handlers/deployment.ts +142 -190
  38. package/src/handlers/discovery.test.ts +4 -5
  39. package/src/handlers/discovery.ts +37 -6
  40. package/src/handlers/fallback.ts +31 -29
  41. package/src/handlers/file-checkouts.test.ts +477 -0
  42. package/src/handlers/file-checkouts.ts +127 -0
  43. package/src/handlers/findings.test.ts +145 -0
  44. package/src/handlers/findings.ts +101 -64
  45. package/src/handlers/git-issues.ts +40 -80
  46. package/src/handlers/ideas.ts +56 -54
  47. package/src/handlers/index.ts +6 -0
  48. package/src/handlers/milestones.test.ts +1 -1
  49. package/src/handlers/milestones.ts +47 -45
  50. package/src/handlers/organizations.ts +104 -129
  51. package/src/handlers/progress.ts +24 -22
  52. package/src/handlers/project.ts +89 -57
  53. package/src/handlers/requests.ts +18 -14
  54. package/src/handlers/roles.test.ts +303 -0
  55. package/src/handlers/roles.ts +208 -0
  56. package/src/handlers/session.ts +39 -17
  57. package/src/handlers/sprints.ts +96 -129
  58. package/src/handlers/tasks.ts +144 -138
  59. package/src/handlers/validation.test.ts +1 -1
  60. package/src/handlers/validation.ts +20 -22
  61. package/src/tools.ts +387 -0
  62. package/dist/config/tool-categories.d.ts +0 -31
  63. package/dist/config/tool-categories.js +0 -253
  64. package/dist/knowledge.d.ts +0 -6
  65. package/dist/knowledge.js +0 -218
@@ -18,22 +18,91 @@
18
18
  */
19
19
 
20
20
  import type { Handler, HandlerRegistry } from './types.js';
21
- import { validateRequired, validateUUID } from '../validators.js';
21
+ import { parseArgs, uuidValidator, createEnumValidator, ValidationError } from '../validators.js';
22
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;
26
+ const ASSIGNABLE_ROLES = ['viewer', 'member', 'admin'] as const;
26
27
  type Role = (typeof ROLE_ORDER)[number];
28
+ type AssignableRole = (typeof ASSIGNABLE_ROLES)[number];
27
29
 
28
30
  // Valid share permissions
29
31
  const PERMISSION_ORDER = ['read', 'write', 'admin'] as const;
30
32
  type Permission = (typeof PERMISSION_ORDER)[number];
31
33
 
34
+ // ============================================================================
35
+ // Argument Schemas
36
+ // ============================================================================
37
+
38
+ const createOrganizationSchema = {
39
+ name: { type: 'string' as const, required: true as const },
40
+ description: { type: 'string' as const },
41
+ slug: { type: 'string' as const },
42
+ };
43
+
44
+ const updateOrganizationSchema = {
45
+ organization_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
46
+ name: { type: 'string' as const },
47
+ description: { type: 'string' as const },
48
+ logo_url: { type: 'string' as const },
49
+ };
50
+
51
+ const deleteOrganizationSchema = {
52
+ organization_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
53
+ };
54
+
55
+ const listOrgMembersSchema = {
56
+ organization_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
57
+ };
58
+
59
+ const inviteMemberSchema = {
60
+ organization_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
61
+ email: { type: 'string' as const, required: true as const },
62
+ role: { type: 'string' as const, default: 'member', validate: createEnumValidator(ASSIGNABLE_ROLES) },
63
+ };
64
+
65
+ const updateMemberRoleSchema = {
66
+ organization_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
67
+ user_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
68
+ role: { type: 'string' as const, required: true as const, validate: createEnumValidator(ROLE_ORDER) },
69
+ };
70
+
71
+ const removeMemberSchema = {
72
+ organization_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
73
+ user_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
74
+ };
75
+
76
+ const leaveOrganizationSchema = {
77
+ organization_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
78
+ };
79
+
80
+ const shareProjectWithOrgSchema = {
81
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
82
+ organization_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
83
+ permission: { type: 'string' as const, default: 'read', validate: createEnumValidator(PERMISSION_ORDER) },
84
+ };
85
+
86
+ const updateProjectShareSchema = {
87
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
88
+ organization_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
89
+ permission: { type: 'string' as const, required: true as const, validate: createEnumValidator(PERMISSION_ORDER) },
90
+ };
91
+
92
+ const unshareProjectSchema = {
93
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
94
+ organization_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
95
+ };
96
+
97
+ const listProjectSharesSchema = {
98
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
99
+ };
100
+
32
101
  // ============================================================================
33
102
  // Organization Management
34
103
  // ============================================================================
35
104
 
36
- export const listOrganizations: Handler = async (_args, ctx) => {
105
+ export const listOrganizations: Handler = async (_args, _ctx) => {
37
106
  const apiClient = getApiClient();
38
107
  const response = await apiClient.listOrganizations();
39
108
 
@@ -44,14 +113,8 @@ export const listOrganizations: Handler = async (_args, ctx) => {
44
113
  return { result: response.data };
45
114
  };
46
115
 
47
- export const createOrganization: Handler = async (args, ctx) => {
48
- const { name, description, slug } = args as {
49
- name: string;
50
- description?: string;
51
- slug?: string;
52
- };
53
-
54
- validateRequired(name, 'name');
116
+ export const createOrganization: Handler = async (args, _ctx) => {
117
+ const { name, description, slug } = parseArgs(args, createOrganizationSchema);
55
118
 
56
119
  const apiClient = getApiClient();
57
120
  const response = await apiClient.createOrganization({
@@ -67,26 +130,19 @@ export const createOrganization: Handler = async (args, ctx) => {
67
130
  return { result: response.data };
68
131
  };
69
132
 
70
- export const updateOrganization: Handler = async (args, ctx) => {
71
- const { organization_id, name, description, logo_url } = args as {
72
- organization_id: string;
73
- name?: string;
74
- description?: string;
75
- logo_url?: string;
76
- };
133
+ export const updateOrganization: Handler = async (args, _ctx) => {
134
+ const { organization_id, name, description, logo_url } = parseArgs(args, updateOrganizationSchema);
77
135
 
78
- validateRequired(organization_id, 'organization_id');
79
- validateUUID(organization_id, 'organization_id');
136
+ // Check that at least one update is provided
137
+ if (name === undefined && description === undefined && logo_url === undefined) {
138
+ throw new ValidationError('No updates provided');
139
+ }
80
140
 
81
141
  const updates: Record<string, unknown> = {};
82
142
  if (name !== undefined) updates.name = name;
83
143
  if (description !== undefined) updates.description = description;
84
144
  if (logo_url !== undefined) updates.logo_url = logo_url;
85
145
 
86
- if (Object.keys(updates).length === 0) {
87
- throw new Error('No updates provided');
88
- }
89
-
90
146
  const apiClient = getApiClient();
91
147
  const response = await apiClient.updateOrganization(organization_id, updates);
92
148
 
@@ -97,11 +153,8 @@ export const updateOrganization: Handler = async (args, ctx) => {
97
153
  return { result: response.data };
98
154
  };
99
155
 
100
- export const deleteOrganization: Handler = async (args, ctx) => {
101
- const { organization_id } = args as { organization_id: string };
102
-
103
- validateRequired(organization_id, 'organization_id');
104
- validateUUID(organization_id, 'organization_id');
156
+ export const deleteOrganization: Handler = async (args, _ctx) => {
157
+ const { organization_id } = parseArgs(args, deleteOrganizationSchema);
105
158
 
106
159
  const apiClient = getApiClient();
107
160
  const response = await apiClient.deleteOrganization(organization_id);
@@ -117,11 +170,8 @@ export const deleteOrganization: Handler = async (args, ctx) => {
117
170
  // Member Management
118
171
  // ============================================================================
119
172
 
120
- export const listOrgMembers: Handler = async (args, ctx) => {
121
- const { organization_id } = args as { organization_id: string };
122
-
123
- validateRequired(organization_id, 'organization_id');
124
- validateUUID(organization_id, 'organization_id');
173
+ export const listOrgMembers: Handler = async (args, _ctx) => {
174
+ const { organization_id } = parseArgs(args, listOrgMembersSchema);
125
175
 
126
176
  const apiClient = getApiClient();
127
177
  const response = await apiClient.listOrgMembers(organization_id);
@@ -133,23 +183,11 @@ export const listOrgMembers: Handler = async (args, ctx) => {
133
183
  return { result: response.data };
134
184
  };
135
185
 
136
- export const inviteMember: Handler = async (args, ctx) => {
137
- const { organization_id, email, role = 'member' } = args as {
138
- organization_id: string;
139
- email: string;
140
- role?: 'admin' | 'member' | 'viewer';
141
- };
142
-
143
- validateRequired(organization_id, 'organization_id');
144
- validateRequired(email, 'email');
145
- validateUUID(organization_id, 'organization_id');
146
-
147
- if (!['admin', 'member', 'viewer'].includes(role)) {
148
- throw new Error('Invalid role. Must be admin, member, or viewer.');
149
- }
186
+ export const inviteMember: Handler = async (args, _ctx) => {
187
+ const { organization_id, email, role } = parseArgs(args, inviteMemberSchema);
150
188
 
151
189
  const apiClient = getApiClient();
152
- const response = await apiClient.inviteMember(organization_id, email, role);
190
+ const response = await apiClient.inviteMember(organization_id, email, role as AssignableRole);
153
191
 
154
192
  if (!response.ok) {
155
193
  throw new Error(response.error || 'Failed to create invite');
@@ -158,29 +196,15 @@ export const inviteMember: Handler = async (args, ctx) => {
158
196
  return { result: response.data };
159
197
  };
160
198
 
161
- export const updateMemberRole: Handler = async (args, ctx) => {
162
- const { organization_id, user_id, role } = args as {
163
- organization_id: string;
164
- user_id: string;
165
- role: Role;
166
- };
167
-
168
- validateRequired(organization_id, 'organization_id');
169
- validateRequired(user_id, 'user_id');
170
- validateRequired(role, 'role');
171
- validateUUID(organization_id, 'organization_id');
172
- validateUUID(user_id, 'user_id');
173
-
174
- if (!ROLE_ORDER.includes(role)) {
175
- throw new Error(`Invalid role. Must be one of: ${ROLE_ORDER.join(', ')}`);
176
- }
199
+ export const updateMemberRole: Handler = async (args, _ctx) => {
200
+ const { organization_id, user_id, role } = parseArgs(args, updateMemberRoleSchema);
177
201
 
178
202
  if (role === 'owner') {
179
- throw new Error('Cannot assign owner role. Use transfer ownership instead.');
203
+ throw new ValidationError('Cannot assign owner role. Use transfer ownership instead.');
180
204
  }
181
205
 
182
206
  const apiClient = getApiClient();
183
- const response = await apiClient.updateMemberRole(organization_id, user_id, role);
207
+ const response = await apiClient.updateMemberRole(organization_id, user_id, role as Role);
184
208
 
185
209
  if (!response.ok) {
186
210
  throw new Error(response.error || 'Failed to update member role');
@@ -189,16 +213,8 @@ export const updateMemberRole: Handler = async (args, ctx) => {
189
213
  return { result: response.data };
190
214
  };
191
215
 
192
- export const removeMember: Handler = async (args, ctx) => {
193
- const { organization_id, user_id } = args as {
194
- organization_id: string;
195
- user_id: string;
196
- };
197
-
198
- validateRequired(organization_id, 'organization_id');
199
- validateRequired(user_id, 'user_id');
200
- validateUUID(organization_id, 'organization_id');
201
- validateUUID(user_id, 'user_id');
216
+ export const removeMember: Handler = async (args, _ctx) => {
217
+ const { organization_id, user_id } = parseArgs(args, removeMemberSchema);
202
218
 
203
219
  const apiClient = getApiClient();
204
220
  const response = await apiClient.removeMember(organization_id, user_id);
@@ -210,11 +226,8 @@ export const removeMember: Handler = async (args, ctx) => {
210
226
  return { result: response.data };
211
227
  };
212
228
 
213
- export const leaveOrganization: Handler = async (args, ctx) => {
214
- const { organization_id } = args as { organization_id: string };
215
-
216
- validateRequired(organization_id, 'organization_id');
217
- validateUUID(organization_id, 'organization_id');
229
+ export const leaveOrganization: Handler = async (args, _ctx) => {
230
+ const { organization_id } = parseArgs(args, leaveOrganizationSchema);
218
231
 
219
232
  const apiClient = getApiClient();
220
233
  const response = await apiClient.leaveOrganization(organization_id);
@@ -230,24 +243,11 @@ export const leaveOrganization: Handler = async (args, ctx) => {
230
243
  // Project Sharing
231
244
  // ============================================================================
232
245
 
233
- export const shareProjectWithOrg: Handler = async (args, ctx) => {
234
- const { project_id, organization_id, permission = 'read' } = args as {
235
- project_id: string;
236
- organization_id: string;
237
- permission?: Permission;
238
- };
239
-
240
- validateRequired(project_id, 'project_id');
241
- validateRequired(organization_id, 'organization_id');
242
- validateUUID(project_id, 'project_id');
243
- validateUUID(organization_id, 'organization_id');
244
-
245
- if (!PERMISSION_ORDER.includes(permission)) {
246
- throw new Error(`Invalid permission. Must be one of: ${PERMISSION_ORDER.join(', ')}`);
247
- }
246
+ export const shareProjectWithOrg: Handler = async (args, _ctx) => {
247
+ const { project_id, organization_id, permission } = parseArgs(args, shareProjectWithOrgSchema);
248
248
 
249
249
  const apiClient = getApiClient();
250
- const response = await apiClient.shareProjectWithOrg(project_id, organization_id, permission);
250
+ const response = await apiClient.shareProjectWithOrg(project_id, organization_id, permission as Permission);
251
251
 
252
252
  if (!response.ok) {
253
253
  throw new Error(response.error || 'Failed to share project');
@@ -256,25 +256,11 @@ export const shareProjectWithOrg: Handler = async (args, ctx) => {
256
256
  return { result: response.data };
257
257
  };
258
258
 
259
- export const updateProjectShare: Handler = async (args, ctx) => {
260
- const { project_id, organization_id, permission } = args as {
261
- project_id: string;
262
- organization_id: string;
263
- permission: Permission;
264
- };
265
-
266
- validateRequired(project_id, 'project_id');
267
- validateRequired(organization_id, 'organization_id');
268
- validateRequired(permission, 'permission');
269
- validateUUID(project_id, 'project_id');
270
- validateUUID(organization_id, 'organization_id');
271
-
272
- if (!PERMISSION_ORDER.includes(permission)) {
273
- throw new Error(`Invalid permission. Must be one of: ${PERMISSION_ORDER.join(', ')}`);
274
- }
259
+ export const updateProjectShare: Handler = async (args, _ctx) => {
260
+ const { project_id, organization_id, permission } = parseArgs(args, updateProjectShareSchema);
275
261
 
276
262
  const apiClient = getApiClient();
277
- const response = await apiClient.updateProjectShare(project_id, organization_id, permission);
263
+ const response = await apiClient.updateProjectShare(project_id, organization_id, permission as Permission);
278
264
 
279
265
  if (!response.ok) {
280
266
  throw new Error(response.error || 'Failed to update share');
@@ -283,16 +269,8 @@ export const updateProjectShare: Handler = async (args, ctx) => {
283
269
  return { result: response.data };
284
270
  };
285
271
 
286
- export const unshareProject: Handler = async (args, ctx) => {
287
- const { project_id, organization_id } = args as {
288
- project_id: string;
289
- organization_id: string;
290
- };
291
-
292
- validateRequired(project_id, 'project_id');
293
- validateRequired(organization_id, 'organization_id');
294
- validateUUID(project_id, 'project_id');
295
- validateUUID(organization_id, 'organization_id');
272
+ export const unshareProject: Handler = async (args, _ctx) => {
273
+ const { project_id, organization_id } = parseArgs(args, unshareProjectSchema);
296
274
 
297
275
  const apiClient = getApiClient();
298
276
  const response = await apiClient.unshareProject(project_id, organization_id);
@@ -304,11 +282,8 @@ export const unshareProject: Handler = async (args, ctx) => {
304
282
  return { result: response.data };
305
283
  };
306
284
 
307
- export const listProjectShares: Handler = async (args, ctx) => {
308
- const { project_id } = args as { project_id: string };
309
-
310
- validateRequired(project_id, 'project_id');
311
- validateUUID(project_id, 'project_id');
285
+ export const listProjectShares: Handler = async (args, _ctx) => {
286
+ const { project_id } = parseArgs(args, listProjectSharesSchema);
312
287
 
313
288
  const apiClient = getApiClient();
314
289
  const response = await apiClient.listProjectShares(project_id);
@@ -9,20 +9,27 @@
9
9
  */
10
10
 
11
11
  import type { Handler, HandlerRegistry } from './types.js';
12
- import { validateRequired, validateUUID } from '../validators.js';
12
+ import { parseArgs, uuidValidator } from '../validators.js';
13
13
  import { getApiClient } from '../api-client.js';
14
14
 
15
- export const logProgress: Handler = async (args, ctx) => {
16
- const { project_id, task_id, summary, details } = args as {
17
- project_id: string;
18
- task_id?: string;
19
- summary: string;
20
- details?: string;
21
- };
15
+ // Argument schemas for type-safe parsing
16
+ const logProgressSchema = {
17
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
18
+ task_id: { type: 'string' as const, validate: uuidValidator },
19
+ summary: { type: 'string' as const, required: true as const },
20
+ details: { type: 'string' as const },
21
+ };
22
+
23
+ const getActivityFeedSchema = {
24
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
25
+ limit: { type: 'number' as const, default: 50 },
26
+ since: { type: 'string' as const },
27
+ types: { type: 'array' as const },
28
+ created_by: { type: 'string' as const },
29
+ };
22
30
 
23
- validateRequired(project_id, 'project_id');
24
- validateUUID(project_id, 'project_id');
25
- validateRequired(summary, 'summary');
31
+ export const logProgress: Handler = async (args, ctx) => {
32
+ const { project_id, task_id, summary, details } = parseArgs(args, logProgressSchema);
26
33
 
27
34
  const { session } = ctx;
28
35
  const apiClient = getApiClient();
@@ -41,22 +48,17 @@ export const logProgress: Handler = async (args, ctx) => {
41
48
  return { result: { success: true, progress_id: response.data?.progress_id } };
42
49
  };
43
50
 
44
- export const getActivityFeed: Handler = async (args, ctx) => {
45
- const { project_id, limit = 50, since } = args as {
46
- project_id: string;
47
- limit?: number;
48
- since?: string;
49
- };
50
-
51
- validateRequired(project_id, 'project_id');
52
- validateUUID(project_id, 'project_id');
51
+ export const getActivityFeed: Handler = async (args, _ctx) => {
52
+ const { project_id, limit, since, types, created_by } = parseArgs(args, getActivityFeedSchema);
53
53
 
54
54
  const apiClient = getApiClient();
55
- const effectiveLimit = Math.min(limit, 200);
55
+ const effectiveLimit = Math.min(limit ?? 50, 200);
56
56
 
57
57
  const response = await apiClient.getActivityFeed(project_id, {
58
58
  limit: effectiveLimit,
59
- since
59
+ since,
60
+ types: types as string[] | undefined,
61
+ created_by
60
62
  });
61
63
 
62
64
  if (!response.ok) {
@@ -10,14 +10,60 @@
10
10
  */
11
11
 
12
12
  import type { Handler, HandlerRegistry } from './types.js';
13
- import { validateRequired, validateUUID, validateProjectStatus } from '../validators.js';
13
+ import {
14
+ parseArgs,
15
+ uuidValidator,
16
+ projectStatusValidator,
17
+ createEnumValidator,
18
+ VALID_PROJECT_STATUSES,
19
+ } from '../validators.js';
14
20
  import { getApiClient } from '../api-client.js';
15
21
 
16
- export const getProjectContext: Handler = async (args, ctx) => {
17
- const { project_id, git_url } = args as {
18
- project_id?: string;
19
- git_url?: string;
20
- };
22
+ const VALID_GIT_WORKFLOWS = ['none', 'trunk-based', 'github-flow', 'git-flow'] as const;
23
+ type GitWorkflow = typeof VALID_GIT_WORKFLOWS[number];
24
+
25
+ // Argument schemas for type-safe parsing
26
+ const getProjectContextSchema = {
27
+ project_id: { type: 'string' as const, validate: uuidValidator },
28
+ git_url: { type: 'string' as const },
29
+ };
30
+
31
+ const getGitWorkflowSchema = {
32
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
33
+ task_id: { type: 'string' as const, validate: uuidValidator },
34
+ };
35
+
36
+ const createProjectSchema = {
37
+ name: { type: 'string' as const, required: true as const },
38
+ description: { type: 'string' as const },
39
+ goal: { type: 'string' as const },
40
+ git_url: { type: 'string' as const },
41
+ tech_stack: { type: 'array' as const },
42
+ };
43
+
44
+ const updateProjectSchema = {
45
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
46
+ name: { type: 'string' as const },
47
+ description: { type: 'string' as const },
48
+ goal: { type: 'string' as const },
49
+ git_url: { type: 'string' as const },
50
+ tech_stack: { type: 'array' as const },
51
+ status: { type: 'string' as const, validate: projectStatusValidator },
52
+ git_workflow: { type: 'string' as const, validate: createEnumValidator(VALID_GIT_WORKFLOWS) },
53
+ git_main_branch: { type: 'string' as const },
54
+ git_develop_branch: { type: 'string' as const },
55
+ git_auto_branch: { type: 'boolean' as const },
56
+ git_auto_tag: { type: 'boolean' as const },
57
+ deployment_instructions: { type: 'string' as const },
58
+ };
59
+
60
+ const updateProjectReadmeSchema = {
61
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
62
+ readme_content: { type: 'string' as const, required: true as const },
63
+ };
64
+
65
+ export const getProjectContext: Handler = async (args, _ctx) => {
66
+ const { project_id, git_url } = parseArgs(args, getProjectContextSchema);
21
67
 
22
68
  const apiClient = getApiClient();
23
69
 
@@ -51,14 +97,8 @@ export const getProjectContext: Handler = async (args, ctx) => {
51
97
  return { result: response.data };
52
98
  };
53
99
 
54
- export const getGitWorkflow: Handler = async (args, ctx) => {
55
- const { project_id, task_id } = args as {
56
- project_id: string;
57
- task_id?: string;
58
- };
59
-
60
- validateRequired(project_id, 'project_id');
61
- validateUUID(project_id, 'project_id');
100
+ export const getGitWorkflow: Handler = async (args, _ctx) => {
101
+ const { project_id, task_id } = parseArgs(args, getGitWorkflowSchema);
62
102
 
63
103
  const apiClient = getApiClient();
64
104
  const response = await apiClient.getGitWorkflow(project_id, task_id);
@@ -70,16 +110,8 @@ export const getGitWorkflow: Handler = async (args, ctx) => {
70
110
  return { result: response.data };
71
111
  };
72
112
 
73
- export const createProject: Handler = async (args, ctx) => {
74
- const { name, description, goal, git_url, tech_stack } = args as {
75
- name: string;
76
- description?: string;
77
- goal?: string;
78
- git_url?: string;
79
- tech_stack?: string[];
80
- };
81
-
82
- validateRequired(name, 'name');
113
+ export const createProject: Handler = async (args, _ctx) => {
114
+ const { name, description, goal, git_url, tech_stack } = parseArgs(args, createProjectSchema);
83
115
 
84
116
  const apiClient = getApiClient();
85
117
  const response = await apiClient.createProject({
@@ -87,7 +119,7 @@ export const createProject: Handler = async (args, ctx) => {
87
119
  description,
88
120
  goal,
89
121
  git_url,
90
- tech_stack
122
+ tech_stack: tech_stack as string[] | undefined
91
123
  });
92
124
 
93
125
  if (!response.ok) {
@@ -97,31 +129,38 @@ export const createProject: Handler = async (args, ctx) => {
97
129
  return { result: response.data };
98
130
  };
99
131
 
100
- export const updateProject: Handler = async (args, ctx) => {
101
- const { project_id, ...updates } = args as {
102
- project_id: string;
103
- name?: string;
104
- description?: string;
105
- goal?: string;
106
- git_url?: string;
107
- tech_stack?: string[];
108
- status?: string;
109
- git_workflow?: 'none' | 'trunk-based' | 'github-flow' | 'git-flow';
110
- git_main_branch?: string;
111
- git_develop_branch?: string;
112
- git_auto_branch?: boolean;
113
- git_auto_tag?: boolean;
114
- git_auto_commit_on_complete?: boolean;
115
- git_auto_pr_on_complete?: boolean;
116
- deployment_instructions?: string;
117
- };
118
-
119
- validateRequired(project_id, 'project_id');
120
- validateUUID(project_id, 'project_id');
121
- validateProjectStatus(updates.status);
132
+ export const updateProject: Handler = async (args, _ctx) => {
133
+ const {
134
+ project_id,
135
+ name,
136
+ description,
137
+ goal,
138
+ git_url,
139
+ tech_stack,
140
+ status,
141
+ git_workflow,
142
+ git_main_branch,
143
+ git_develop_branch,
144
+ git_auto_branch,
145
+ git_auto_tag,
146
+ deployment_instructions
147
+ } = parseArgs(args, updateProjectSchema);
122
148
 
123
149
  const apiClient = getApiClient();
124
- const response = await apiClient.updateProject(project_id, updates);
150
+ const response = await apiClient.updateProject(project_id, {
151
+ name,
152
+ description,
153
+ goal,
154
+ git_url,
155
+ tech_stack: tech_stack as string[] | undefined,
156
+ status: status as typeof VALID_PROJECT_STATUSES[number] | undefined,
157
+ git_workflow: git_workflow as GitWorkflow | undefined,
158
+ git_main_branch,
159
+ git_develop_branch,
160
+ git_auto_branch,
161
+ git_auto_tag,
162
+ deployment_instructions
163
+ });
125
164
 
126
165
  if (!response.ok) {
127
166
  throw new Error(response.error || 'Failed to update project');
@@ -130,15 +169,8 @@ export const updateProject: Handler = async (args, ctx) => {
130
169
  return { result: response.data };
131
170
  };
132
171
 
133
- export const updateProjectReadme: Handler = async (args, ctx) => {
134
- const { project_id, readme_content } = args as {
135
- project_id: string;
136
- readme_content: string;
137
- };
138
-
139
- validateRequired(project_id, 'project_id');
140
- validateUUID(project_id, 'project_id');
141
- validateRequired(readme_content, 'readme_content');
172
+ export const updateProjectReadme: Handler = async (args, _ctx) => {
173
+ const { project_id, readme_content } = parseArgs(args, updateProjectReadmeSchema);
142
174
 
143
175
  const apiClient = getApiClient();
144
176
  const response = await apiClient.updateProjectReadme(project_id, readme_content);
@@ -10,14 +10,25 @@
10
10
  */
11
11
 
12
12
  import type { Handler, HandlerRegistry } from './types.js';
13
- import { validateRequired, validateUUID } from '../validators.js';
13
+ import { parseArgs, uuidValidator } from '../validators.js';
14
14
  import { getApiClient } from '../api-client.js';
15
15
 
16
- export const getPendingRequests: Handler = async (args, ctx) => {
17
- const { project_id } = args as { project_id: string };
16
+ // Argument schemas for type-safe parsing
17
+ const getPendingRequestsSchema = {
18
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
19
+ };
18
20
 
19
- validateRequired(project_id, 'project_id');
20
- validateUUID(project_id, 'project_id');
21
+ const acknowledgeRequestSchema = {
22
+ request_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
23
+ };
24
+
25
+ const answerQuestionSchema = {
26
+ request_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
27
+ answer: { type: 'string' as const, required: true as const },
28
+ };
29
+
30
+ export const getPendingRequests: Handler = async (args, ctx) => {
31
+ const { project_id } = parseArgs(args, getPendingRequestsSchema);
21
32
 
22
33
  const { session } = ctx;
23
34
  const apiClient = getApiClient();
@@ -37,10 +48,7 @@ export const getPendingRequests: Handler = async (args, ctx) => {
37
48
  };
38
49
 
39
50
  export const acknowledgeRequest: Handler = async (args, ctx) => {
40
- const { request_id } = args as { request_id: string };
41
-
42
- validateRequired(request_id, 'request_id');
43
- validateUUID(request_id, 'request_id');
51
+ const { request_id } = parseArgs(args, acknowledgeRequestSchema);
44
52
 
45
53
  const { session } = ctx;
46
54
  const apiClient = getApiClient();
@@ -59,11 +67,7 @@ export const acknowledgeRequest: Handler = async (args, ctx) => {
59
67
  };
60
68
 
61
69
  export const answerQuestion: Handler = async (args, ctx) => {
62
- const { request_id, answer } = args as { request_id: string; answer: string };
63
-
64
- validateRequired(request_id, 'request_id');
65
- validateRequired(answer, 'answer');
66
- validateUUID(request_id, 'request_id');
70
+ const { request_id, answer } = parseArgs(args, answerQuestionSchema);
67
71
 
68
72
  const { session } = ctx;
69
73
  const apiClient = getApiClient();