@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
@@ -16,16 +16,71 @@
16
16
  * - unshare_project
17
17
  * - list_project_shares
18
18
  */
19
- import { validateRequired, validateUUID } from '../validators.js';
19
+ import { parseArgs, uuidValidator, createEnumValidator, ValidationError } from '../validators.js';
20
20
  import { getApiClient } from '../api-client.js';
21
21
  // Valid roles in order of permission level
22
22
  const ROLE_ORDER = ['viewer', 'member', 'admin', 'owner'];
23
+ const ASSIGNABLE_ROLES = ['viewer', 'member', 'admin'];
23
24
  // Valid share permissions
24
25
  const PERMISSION_ORDER = ['read', 'write', 'admin'];
25
26
  // ============================================================================
27
+ // Argument Schemas
28
+ // ============================================================================
29
+ const createOrganizationSchema = {
30
+ name: { type: 'string', required: true },
31
+ description: { type: 'string' },
32
+ slug: { type: 'string' },
33
+ };
34
+ const updateOrganizationSchema = {
35
+ organization_id: { type: 'string', required: true, validate: uuidValidator },
36
+ name: { type: 'string' },
37
+ description: { type: 'string' },
38
+ logo_url: { type: 'string' },
39
+ };
40
+ const deleteOrganizationSchema = {
41
+ organization_id: { type: 'string', required: true, validate: uuidValidator },
42
+ };
43
+ const listOrgMembersSchema = {
44
+ organization_id: { type: 'string', required: true, validate: uuidValidator },
45
+ };
46
+ const inviteMemberSchema = {
47
+ organization_id: { type: 'string', required: true, validate: uuidValidator },
48
+ email: { type: 'string', required: true },
49
+ role: { type: 'string', default: 'member', validate: createEnumValidator(ASSIGNABLE_ROLES) },
50
+ };
51
+ const updateMemberRoleSchema = {
52
+ organization_id: { type: 'string', required: true, validate: uuidValidator },
53
+ user_id: { type: 'string', required: true, validate: uuidValidator },
54
+ role: { type: 'string', required: true, validate: createEnumValidator(ROLE_ORDER) },
55
+ };
56
+ const removeMemberSchema = {
57
+ organization_id: { type: 'string', required: true, validate: uuidValidator },
58
+ user_id: { type: 'string', required: true, validate: uuidValidator },
59
+ };
60
+ const leaveOrganizationSchema = {
61
+ organization_id: { type: 'string', required: true, validate: uuidValidator },
62
+ };
63
+ const shareProjectWithOrgSchema = {
64
+ project_id: { type: 'string', required: true, validate: uuidValidator },
65
+ organization_id: { type: 'string', required: true, validate: uuidValidator },
66
+ permission: { type: 'string', default: 'read', validate: createEnumValidator(PERMISSION_ORDER) },
67
+ };
68
+ const updateProjectShareSchema = {
69
+ project_id: { type: 'string', required: true, validate: uuidValidator },
70
+ organization_id: { type: 'string', required: true, validate: uuidValidator },
71
+ permission: { type: 'string', required: true, validate: createEnumValidator(PERMISSION_ORDER) },
72
+ };
73
+ const unshareProjectSchema = {
74
+ project_id: { type: 'string', required: true, validate: uuidValidator },
75
+ organization_id: { type: 'string', required: true, validate: uuidValidator },
76
+ };
77
+ const listProjectSharesSchema = {
78
+ project_id: { type: 'string', required: true, validate: uuidValidator },
79
+ };
80
+ // ============================================================================
26
81
  // Organization Management
27
82
  // ============================================================================
28
- export const listOrganizations = async (_args, ctx) => {
83
+ export const listOrganizations = async (_args, _ctx) => {
29
84
  const apiClient = getApiClient();
30
85
  const response = await apiClient.listOrganizations();
31
86
  if (!response.ok) {
@@ -33,9 +88,8 @@ export const listOrganizations = async (_args, ctx) => {
33
88
  }
34
89
  return { result: response.data };
35
90
  };
36
- export const createOrganization = async (args, ctx) => {
37
- const { name, description, slug } = args;
38
- validateRequired(name, 'name');
91
+ export const createOrganization = async (args, _ctx) => {
92
+ const { name, description, slug } = parseArgs(args, createOrganizationSchema);
39
93
  const apiClient = getApiClient();
40
94
  const response = await apiClient.createOrganization({
41
95
  name,
@@ -47,10 +101,12 @@ export const createOrganization = async (args, ctx) => {
47
101
  }
48
102
  return { result: response.data };
49
103
  };
50
- export const updateOrganization = async (args, ctx) => {
51
- const { organization_id, name, description, logo_url } = args;
52
- validateRequired(organization_id, 'organization_id');
53
- validateUUID(organization_id, 'organization_id');
104
+ export const updateOrganization = async (args, _ctx) => {
105
+ const { organization_id, name, description, logo_url } = parseArgs(args, updateOrganizationSchema);
106
+ // Check that at least one update is provided
107
+ if (name === undefined && description === undefined && logo_url === undefined) {
108
+ throw new ValidationError('No updates provided');
109
+ }
54
110
  const updates = {};
55
111
  if (name !== undefined)
56
112
  updates.name = name;
@@ -58,9 +114,6 @@ export const updateOrganization = async (args, ctx) => {
58
114
  updates.description = description;
59
115
  if (logo_url !== undefined)
60
116
  updates.logo_url = logo_url;
61
- if (Object.keys(updates).length === 0) {
62
- throw new Error('No updates provided');
63
- }
64
117
  const apiClient = getApiClient();
65
118
  const response = await apiClient.updateOrganization(organization_id, updates);
66
119
  if (!response.ok) {
@@ -68,10 +121,8 @@ export const updateOrganization = async (args, ctx) => {
68
121
  }
69
122
  return { result: response.data };
70
123
  };
71
- export const deleteOrganization = async (args, ctx) => {
72
- const { organization_id } = args;
73
- validateRequired(organization_id, 'organization_id');
74
- validateUUID(organization_id, 'organization_id');
124
+ export const deleteOrganization = async (args, _ctx) => {
125
+ const { organization_id } = parseArgs(args, deleteOrganizationSchema);
75
126
  const apiClient = getApiClient();
76
127
  const response = await apiClient.deleteOrganization(organization_id);
77
128
  if (!response.ok) {
@@ -82,10 +133,8 @@ export const deleteOrganization = async (args, ctx) => {
82
133
  // ============================================================================
83
134
  // Member Management
84
135
  // ============================================================================
85
- export const listOrgMembers = async (args, ctx) => {
86
- const { organization_id } = args;
87
- validateRequired(organization_id, 'organization_id');
88
- validateUUID(organization_id, 'organization_id');
136
+ export const listOrgMembers = async (args, _ctx) => {
137
+ const { organization_id } = parseArgs(args, listOrgMembersSchema);
89
138
  const apiClient = getApiClient();
90
139
  const response = await apiClient.listOrgMembers(organization_id);
91
140
  if (!response.ok) {
@@ -93,14 +142,8 @@ export const listOrgMembers = async (args, ctx) => {
93
142
  }
94
143
  return { result: response.data };
95
144
  };
96
- export const inviteMember = async (args, ctx) => {
97
- const { organization_id, email, role = 'member' } = args;
98
- validateRequired(organization_id, 'organization_id');
99
- validateRequired(email, 'email');
100
- validateUUID(organization_id, 'organization_id');
101
- if (!['admin', 'member', 'viewer'].includes(role)) {
102
- throw new Error('Invalid role. Must be admin, member, or viewer.');
103
- }
145
+ export const inviteMember = async (args, _ctx) => {
146
+ const { organization_id, email, role } = parseArgs(args, inviteMemberSchema);
104
147
  const apiClient = getApiClient();
105
148
  const response = await apiClient.inviteMember(organization_id, email, role);
106
149
  if (!response.ok) {
@@ -108,18 +151,10 @@ export const inviteMember = async (args, ctx) => {
108
151
  }
109
152
  return { result: response.data };
110
153
  };
111
- export const updateMemberRole = async (args, ctx) => {
112
- const { organization_id, user_id, role } = args;
113
- validateRequired(organization_id, 'organization_id');
114
- validateRequired(user_id, 'user_id');
115
- validateRequired(role, 'role');
116
- validateUUID(organization_id, 'organization_id');
117
- validateUUID(user_id, 'user_id');
118
- if (!ROLE_ORDER.includes(role)) {
119
- throw new Error(`Invalid role. Must be one of: ${ROLE_ORDER.join(', ')}`);
120
- }
154
+ export const updateMemberRole = async (args, _ctx) => {
155
+ const { organization_id, user_id, role } = parseArgs(args, updateMemberRoleSchema);
121
156
  if (role === 'owner') {
122
- throw new Error('Cannot assign owner role. Use transfer ownership instead.');
157
+ throw new ValidationError('Cannot assign owner role. Use transfer ownership instead.');
123
158
  }
124
159
  const apiClient = getApiClient();
125
160
  const response = await apiClient.updateMemberRole(organization_id, user_id, role);
@@ -128,12 +163,8 @@ export const updateMemberRole = async (args, ctx) => {
128
163
  }
129
164
  return { result: response.data };
130
165
  };
131
- export const removeMember = async (args, ctx) => {
132
- const { organization_id, user_id } = args;
133
- validateRequired(organization_id, 'organization_id');
134
- validateRequired(user_id, 'user_id');
135
- validateUUID(organization_id, 'organization_id');
136
- validateUUID(user_id, 'user_id');
166
+ export const removeMember = async (args, _ctx) => {
167
+ const { organization_id, user_id } = parseArgs(args, removeMemberSchema);
137
168
  const apiClient = getApiClient();
138
169
  const response = await apiClient.removeMember(organization_id, user_id);
139
170
  if (!response.ok) {
@@ -141,10 +172,8 @@ export const removeMember = async (args, ctx) => {
141
172
  }
142
173
  return { result: response.data };
143
174
  };
144
- export const leaveOrganization = async (args, ctx) => {
145
- const { organization_id } = args;
146
- validateRequired(organization_id, 'organization_id');
147
- validateUUID(organization_id, 'organization_id');
175
+ export const leaveOrganization = async (args, _ctx) => {
176
+ const { organization_id } = parseArgs(args, leaveOrganizationSchema);
148
177
  const apiClient = getApiClient();
149
178
  const response = await apiClient.leaveOrganization(organization_id);
150
179
  if (!response.ok) {
@@ -155,15 +184,8 @@ export const leaveOrganization = async (args, ctx) => {
155
184
  // ============================================================================
156
185
  // Project Sharing
157
186
  // ============================================================================
158
- export const shareProjectWithOrg = async (args, ctx) => {
159
- const { project_id, organization_id, permission = 'read' } = args;
160
- validateRequired(project_id, 'project_id');
161
- validateRequired(organization_id, 'organization_id');
162
- validateUUID(project_id, 'project_id');
163
- validateUUID(organization_id, 'organization_id');
164
- if (!PERMISSION_ORDER.includes(permission)) {
165
- throw new Error(`Invalid permission. Must be one of: ${PERMISSION_ORDER.join(', ')}`);
166
- }
187
+ export const shareProjectWithOrg = async (args, _ctx) => {
188
+ const { project_id, organization_id, permission } = parseArgs(args, shareProjectWithOrgSchema);
167
189
  const apiClient = getApiClient();
168
190
  const response = await apiClient.shareProjectWithOrg(project_id, organization_id, permission);
169
191
  if (!response.ok) {
@@ -171,16 +193,8 @@ export const shareProjectWithOrg = async (args, ctx) => {
171
193
  }
172
194
  return { result: response.data };
173
195
  };
174
- export const updateProjectShare = async (args, ctx) => {
175
- const { project_id, organization_id, permission } = args;
176
- validateRequired(project_id, 'project_id');
177
- validateRequired(organization_id, 'organization_id');
178
- validateRequired(permission, 'permission');
179
- validateUUID(project_id, 'project_id');
180
- validateUUID(organization_id, 'organization_id');
181
- if (!PERMISSION_ORDER.includes(permission)) {
182
- throw new Error(`Invalid permission. Must be one of: ${PERMISSION_ORDER.join(', ')}`);
183
- }
196
+ export const updateProjectShare = async (args, _ctx) => {
197
+ const { project_id, organization_id, permission } = parseArgs(args, updateProjectShareSchema);
184
198
  const apiClient = getApiClient();
185
199
  const response = await apiClient.updateProjectShare(project_id, organization_id, permission);
186
200
  if (!response.ok) {
@@ -188,12 +202,8 @@ export const updateProjectShare = async (args, ctx) => {
188
202
  }
189
203
  return { result: response.data };
190
204
  };
191
- export const unshareProject = async (args, ctx) => {
192
- const { project_id, organization_id } = args;
193
- validateRequired(project_id, 'project_id');
194
- validateRequired(organization_id, 'organization_id');
195
- validateUUID(project_id, 'project_id');
196
- validateUUID(organization_id, 'organization_id');
205
+ export const unshareProject = async (args, _ctx) => {
206
+ const { project_id, organization_id } = parseArgs(args, unshareProjectSchema);
197
207
  const apiClient = getApiClient();
198
208
  const response = await apiClient.unshareProject(project_id, organization_id);
199
209
  if (!response.ok) {
@@ -201,10 +211,8 @@ export const unshareProject = async (args, ctx) => {
201
211
  }
202
212
  return { result: response.data };
203
213
  };
204
- export const listProjectShares = async (args, ctx) => {
205
- const { project_id } = args;
206
- validateRequired(project_id, 'project_id');
207
- validateUUID(project_id, 'project_id');
214
+ export const listProjectShares = async (args, _ctx) => {
215
+ const { project_id } = parseArgs(args, listProjectSharesSchema);
208
216
  const apiClient = getApiClient();
209
217
  const response = await apiClient.listProjectShares(project_id);
210
218
  if (!response.ok) {
@@ -7,13 +7,24 @@
7
7
  *
8
8
  * MIGRATED: Uses Vibescope API client instead of direct Supabase
9
9
  */
10
- import { validateRequired, validateUUID } from '../validators.js';
10
+ import { parseArgs, uuidValidator } from '../validators.js';
11
11
  import { getApiClient } from '../api-client.js';
12
+ // Argument schemas for type-safe parsing
13
+ const logProgressSchema = {
14
+ project_id: { type: 'string', required: true, validate: uuidValidator },
15
+ task_id: { type: 'string', validate: uuidValidator },
16
+ summary: { type: 'string', required: true },
17
+ details: { type: 'string' },
18
+ };
19
+ const getActivityFeedSchema = {
20
+ project_id: { type: 'string', required: true, validate: uuidValidator },
21
+ limit: { type: 'number', default: 50 },
22
+ since: { type: 'string' },
23
+ types: { type: 'array' },
24
+ created_by: { type: 'string' },
25
+ };
12
26
  export const logProgress = async (args, ctx) => {
13
- const { project_id, task_id, summary, details } = args;
14
- validateRequired(project_id, 'project_id');
15
- validateUUID(project_id, 'project_id');
16
- validateRequired(summary, 'summary');
27
+ const { project_id, task_id, summary, details } = parseArgs(args, logProgressSchema);
17
28
  const { session } = ctx;
18
29
  const apiClient = getApiClient();
19
30
  const response = await apiClient.logProgress(project_id, {
@@ -27,15 +38,15 @@ export const logProgress = async (args, ctx) => {
27
38
  }
28
39
  return { result: { success: true, progress_id: response.data?.progress_id } };
29
40
  };
30
- export const getActivityFeed = async (args, ctx) => {
31
- const { project_id, limit = 50, since } = args;
32
- validateRequired(project_id, 'project_id');
33
- validateUUID(project_id, 'project_id');
41
+ export const getActivityFeed = async (args, _ctx) => {
42
+ const { project_id, limit, since, types, created_by } = parseArgs(args, getActivityFeedSchema);
34
43
  const apiClient = getApiClient();
35
- const effectiveLimit = Math.min(limit, 200);
44
+ const effectiveLimit = Math.min(limit ?? 50, 200);
36
45
  const response = await apiClient.getActivityFeed(project_id, {
37
46
  limit: effectiveLimit,
38
- since
47
+ since,
48
+ types: types,
49
+ created_by
39
50
  });
40
51
  if (!response.ok) {
41
52
  throw new Error(`Failed to fetch activity feed: ${response.error}`);
@@ -8,10 +8,46 @@
8
8
  * - update_project
9
9
  * - update_project_readme
10
10
  */
11
- import { validateRequired, validateUUID, validateProjectStatus } from '../validators.js';
11
+ import { parseArgs, uuidValidator, projectStatusValidator, createEnumValidator, } from '../validators.js';
12
12
  import { getApiClient } from '../api-client.js';
13
- export const getProjectContext = async (args, ctx) => {
14
- const { project_id, git_url } = args;
13
+ const VALID_GIT_WORKFLOWS = ['none', 'trunk-based', 'github-flow', 'git-flow'];
14
+ // Argument schemas for type-safe parsing
15
+ const getProjectContextSchema = {
16
+ project_id: { type: 'string', validate: uuidValidator },
17
+ git_url: { type: 'string' },
18
+ };
19
+ const getGitWorkflowSchema = {
20
+ project_id: { type: 'string', required: true, validate: uuidValidator },
21
+ task_id: { type: 'string', validate: uuidValidator },
22
+ };
23
+ const createProjectSchema = {
24
+ name: { type: 'string', required: true },
25
+ description: { type: 'string' },
26
+ goal: { type: 'string' },
27
+ git_url: { type: 'string' },
28
+ tech_stack: { type: 'array' },
29
+ };
30
+ const updateProjectSchema = {
31
+ project_id: { type: 'string', required: true, validate: uuidValidator },
32
+ name: { type: 'string' },
33
+ description: { type: 'string' },
34
+ goal: { type: 'string' },
35
+ git_url: { type: 'string' },
36
+ tech_stack: { type: 'array' },
37
+ status: { type: 'string', validate: projectStatusValidator },
38
+ git_workflow: { type: 'string', validate: createEnumValidator(VALID_GIT_WORKFLOWS) },
39
+ git_main_branch: { type: 'string' },
40
+ git_develop_branch: { type: 'string' },
41
+ git_auto_branch: { type: 'boolean' },
42
+ git_auto_tag: { type: 'boolean' },
43
+ deployment_instructions: { type: 'string' },
44
+ };
45
+ const updateProjectReadmeSchema = {
46
+ project_id: { type: 'string', required: true, validate: uuidValidator },
47
+ readme_content: { type: 'string', required: true },
48
+ };
49
+ export const getProjectContext = async (args, _ctx) => {
50
+ const { project_id, git_url } = parseArgs(args, getProjectContextSchema);
15
51
  const apiClient = getApiClient();
16
52
  // If no project_id or git_url, list all projects
17
53
  if (!project_id && !git_url) {
@@ -36,10 +72,8 @@ export const getProjectContext = async (args, ctx) => {
36
72
  }
37
73
  return { result: response.data };
38
74
  };
39
- export const getGitWorkflow = async (args, ctx) => {
40
- const { project_id, task_id } = args;
41
- validateRequired(project_id, 'project_id');
42
- validateUUID(project_id, 'project_id');
75
+ export const getGitWorkflow = async (args, _ctx) => {
76
+ const { project_id, task_id } = parseArgs(args, getGitWorkflowSchema);
43
77
  const apiClient = getApiClient();
44
78
  const response = await apiClient.getGitWorkflow(project_id, task_id);
45
79
  if (!response.ok) {
@@ -47,39 +81,45 @@ export const getGitWorkflow = async (args, ctx) => {
47
81
  }
48
82
  return { result: response.data };
49
83
  };
50
- export const createProject = async (args, ctx) => {
51
- const { name, description, goal, git_url, tech_stack } = args;
52
- validateRequired(name, 'name');
84
+ export const createProject = async (args, _ctx) => {
85
+ const { name, description, goal, git_url, tech_stack } = parseArgs(args, createProjectSchema);
53
86
  const apiClient = getApiClient();
54
87
  const response = await apiClient.createProject({
55
88
  name,
56
89
  description,
57
90
  goal,
58
91
  git_url,
59
- tech_stack
92
+ tech_stack: tech_stack
60
93
  });
61
94
  if (!response.ok) {
62
95
  throw new Error(response.error || 'Failed to create project');
63
96
  }
64
97
  return { result: response.data };
65
98
  };
66
- export const updateProject = async (args, ctx) => {
67
- const { project_id, ...updates } = args;
68
- validateRequired(project_id, 'project_id');
69
- validateUUID(project_id, 'project_id');
70
- validateProjectStatus(updates.status);
99
+ export const updateProject = async (args, _ctx) => {
100
+ const { project_id, name, description, goal, git_url, tech_stack, status, git_workflow, git_main_branch, git_develop_branch, git_auto_branch, git_auto_tag, deployment_instructions } = parseArgs(args, updateProjectSchema);
71
101
  const apiClient = getApiClient();
72
- const response = await apiClient.updateProject(project_id, updates);
102
+ const response = await apiClient.updateProject(project_id, {
103
+ name,
104
+ description,
105
+ goal,
106
+ git_url,
107
+ tech_stack: tech_stack,
108
+ status: status,
109
+ git_workflow: git_workflow,
110
+ git_main_branch,
111
+ git_develop_branch,
112
+ git_auto_branch,
113
+ git_auto_tag,
114
+ deployment_instructions
115
+ });
73
116
  if (!response.ok) {
74
117
  throw new Error(response.error || 'Failed to update project');
75
118
  }
76
119
  return { result: response.data };
77
120
  };
78
- export const updateProjectReadme = async (args, ctx) => {
79
- const { project_id, readme_content } = args;
80
- validateRequired(project_id, 'project_id');
81
- validateUUID(project_id, 'project_id');
82
- validateRequired(readme_content, 'readme_content');
121
+ export const updateProjectReadme = async (args, _ctx) => {
122
+ const { project_id, readme_content } = parseArgs(args, updateProjectReadmeSchema);
83
123
  const apiClient = getApiClient();
84
124
  const response = await apiClient.updateProjectReadme(project_id, readme_content);
85
125
  if (!response.ok) {
@@ -8,12 +8,21 @@
8
8
  *
9
9
  * MIGRATED: Uses Vibescope API client instead of direct Supabase
10
10
  */
11
- import { validateRequired, validateUUID } from '../validators.js';
11
+ import { parseArgs, uuidValidator } from '../validators.js';
12
12
  import { getApiClient } from '../api-client.js';
13
+ // Argument schemas for type-safe parsing
14
+ const getPendingRequestsSchema = {
15
+ project_id: { type: 'string', required: true, validate: uuidValidator },
16
+ };
17
+ const acknowledgeRequestSchema = {
18
+ request_id: { type: 'string', required: true, validate: uuidValidator },
19
+ };
20
+ const answerQuestionSchema = {
21
+ request_id: { type: 'string', required: true, validate: uuidValidator },
22
+ answer: { type: 'string', required: true },
23
+ };
13
24
  export const getPendingRequests = async (args, ctx) => {
14
- const { project_id } = args;
15
- validateRequired(project_id, 'project_id');
16
- validateUUID(project_id, 'project_id');
25
+ const { project_id } = parseArgs(args, getPendingRequestsSchema);
17
26
  const { session } = ctx;
18
27
  const apiClient = getApiClient();
19
28
  const response = await apiClient.getPendingRequests(project_id, session.currentSessionId || undefined);
@@ -28,9 +37,7 @@ export const getPendingRequests = async (args, ctx) => {
28
37
  };
29
38
  };
30
39
  export const acknowledgeRequest = async (args, ctx) => {
31
- const { request_id } = args;
32
- validateRequired(request_id, 'request_id');
33
- validateUUID(request_id, 'request_id');
40
+ const { request_id } = parseArgs(args, acknowledgeRequestSchema);
34
41
  const { session } = ctx;
35
42
  const apiClient = getApiClient();
36
43
  const response = await apiClient.acknowledgeRequest(request_id, session.currentSessionId || undefined);
@@ -44,10 +51,7 @@ export const acknowledgeRequest = async (args, ctx) => {
44
51
  };
45
52
  };
46
53
  export const answerQuestion = async (args, ctx) => {
47
- const { request_id, answer } = args;
48
- validateRequired(request_id, 'request_id');
49
- validateRequired(answer, 'answer');
50
- validateUUID(request_id, 'request_id');
54
+ const { request_id, answer } = parseArgs(args, answerQuestionSchema);
51
55
  const { session } = ctx;
52
56
  const apiClient = getApiClient();
53
57
  const response = await apiClient.answerQuestion(request_id, answer, session.currentSessionId || undefined);
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Role Management Handlers
3
+ *
4
+ * Handles agent role configuration and assignment:
5
+ * - get_role_settings: Get role configuration for a project
6
+ * - update_role_settings: Update role settings for a project
7
+ * - set_session_role: Set the role for the current session
8
+ * - get_agents_by_role: Get active agents grouped by their assigned roles
9
+ */
10
+ import type { Handler, HandlerRegistry } from './types.js';
11
+ export declare const getRoleSettings: Handler;
12
+ export declare const updateRoleSettings: Handler;
13
+ export declare const setSessionRole: Handler;
14
+ export declare const getAgentsByRole: Handler;
15
+ /**
16
+ * Role handlers registry
17
+ */
18
+ export declare const roleHandlers: HandlerRegistry;
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Role Management Handlers
3
+ *
4
+ * Handles agent role configuration and assignment:
5
+ * - get_role_settings: Get role configuration for a project
6
+ * - update_role_settings: Update role settings for a project
7
+ * - set_session_role: Set the role for the current session
8
+ * - get_agents_by_role: Get active agents grouped by their assigned roles
9
+ */
10
+ import { getApiClient } from '../api-client.js';
11
+ export const getRoleSettings = async (args, _ctx) => {
12
+ const { project_id } = args;
13
+ if (!project_id) {
14
+ return {
15
+ result: { error: 'project_id is required' },
16
+ };
17
+ }
18
+ const apiClient = getApiClient();
19
+ const response = await apiClient.proxy('get_role_settings', { project_id });
20
+ if (!response.ok) {
21
+ return {
22
+ result: { error: response.error || 'Failed to get role settings' },
23
+ };
24
+ }
25
+ return { result: response.data };
26
+ };
27
+ export const updateRoleSettings = async (args, _ctx) => {
28
+ const { project_id, role, enabled, display_name, description, priority_filter, fallback_activities, auto_assign_validation, auto_assign_deployment, } = args;
29
+ if (!project_id) {
30
+ return {
31
+ result: { error: 'project_id is required' },
32
+ };
33
+ }
34
+ if (!role) {
35
+ return {
36
+ result: { error: 'role is required' },
37
+ };
38
+ }
39
+ const apiClient = getApiClient();
40
+ const response = await apiClient.proxy('update_role_settings', {
41
+ project_id,
42
+ role,
43
+ enabled,
44
+ display_name,
45
+ description,
46
+ priority_filter,
47
+ fallback_activities,
48
+ auto_assign_validation,
49
+ auto_assign_deployment,
50
+ });
51
+ if (!response.ok) {
52
+ return {
53
+ result: { error: response.error || 'Failed to update role settings' },
54
+ };
55
+ }
56
+ return { result: response.data };
57
+ };
58
+ export const setSessionRole = async (args, ctx) => {
59
+ const { role, role_config } = args;
60
+ const { session, updateSession } = ctx;
61
+ if (!role) {
62
+ return {
63
+ result: { error: 'role is required' },
64
+ };
65
+ }
66
+ const validRoles = ['developer', 'validator', 'deployer', 'reviewer', 'maintainer'];
67
+ if (!validRoles.includes(role)) {
68
+ return {
69
+ result: {
70
+ error: `Invalid role: ${role}. Must be one of: ${validRoles.join(', ')}`,
71
+ },
72
+ };
73
+ }
74
+ // Update local session state
75
+ updateSession({ currentRole: role });
76
+ // If there's an active session, update it on the server too
77
+ if (session.currentSessionId) {
78
+ const apiClient = getApiClient();
79
+ const response = await apiClient.proxy('set_session_role', {
80
+ session_id: session.currentSessionId,
81
+ role,
82
+ role_config,
83
+ });
84
+ if (!response.ok) {
85
+ return {
86
+ result: { error: response.error || 'Failed to update session role' },
87
+ };
88
+ }
89
+ return {
90
+ result: {
91
+ success: true,
92
+ session_id: session.currentSessionId,
93
+ role,
94
+ message: `Session role updated to ${role}. Task filtering and fallback suggestions will now be optimized for this role.`,
95
+ },
96
+ };
97
+ }
98
+ return {
99
+ result: {
100
+ success: true,
101
+ role,
102
+ message: `Local role set to ${role}. Start a session to persist this role.`,
103
+ },
104
+ };
105
+ };
106
+ export const getAgentsByRole = async (args, _ctx) => {
107
+ const { project_id } = args;
108
+ if (!project_id) {
109
+ return {
110
+ result: { error: 'project_id is required' },
111
+ };
112
+ }
113
+ const apiClient = getApiClient();
114
+ const response = await apiClient.proxy('get_agents_by_role', { project_id });
115
+ if (!response.ok) {
116
+ return {
117
+ result: { error: response.error || 'Failed to get agents by role' },
118
+ };
119
+ }
120
+ return { result: response.data };
121
+ };
122
+ /**
123
+ * Role handlers registry
124
+ */
125
+ export const roleHandlers = {
126
+ get_role_settings: getRoleSettings,
127
+ update_role_settings: updateRoleSettings,
128
+ set_session_role: setSessionRole,
129
+ get_agents_by_role: getAgentsByRole,
130
+ };