@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
@@ -8,21 +8,62 @@
8
8
  * - update_finding
9
9
  * - delete_finding
10
10
  */
11
- import { validateRequired, validateUUID } from '../validators.js';
11
+ import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
12
12
  import { getApiClient } from '../api-client.js';
13
+ const VALID_FINDING_CATEGORIES = ['performance', 'security', 'code_quality', 'accessibility', 'documentation', 'architecture', 'testing', 'other'];
14
+ const VALID_FINDING_SEVERITIES = ['info', 'low', 'medium', 'high', 'critical'];
15
+ const VALID_FINDING_STATUSES = ['open', 'addressed', 'dismissed', 'wontfix'];
16
+ // Argument schemas for type-safe parsing
17
+ const addFindingSchema = {
18
+ project_id: { type: 'string', required: true, validate: uuidValidator },
19
+ title: { type: 'string', required: true },
20
+ description: { type: 'string' },
21
+ category: { type: 'string', validate: createEnumValidator(VALID_FINDING_CATEGORIES) },
22
+ severity: { type: 'string', validate: createEnumValidator(VALID_FINDING_SEVERITIES) },
23
+ file_path: { type: 'string' },
24
+ line_number: { type: 'number' },
25
+ related_task_id: { type: 'string', validate: uuidValidator },
26
+ };
27
+ const getFindingsSchema = {
28
+ project_id: { type: 'string', required: true, validate: uuidValidator },
29
+ category: { type: 'string', validate: createEnumValidator(VALID_FINDING_CATEGORIES) },
30
+ severity: { type: 'string', validate: createEnumValidator(VALID_FINDING_SEVERITIES) },
31
+ status: { type: 'string', validate: createEnumValidator(VALID_FINDING_STATUSES) },
32
+ limit: { type: 'number', default: 50 },
33
+ offset: { type: 'number', default: 0 },
34
+ search_query: { type: 'string' },
35
+ summary_only: { type: 'boolean', default: false },
36
+ };
37
+ const getFindingsStatsSchema = {
38
+ project_id: { type: 'string', required: true, validate: uuidValidator },
39
+ };
40
+ const updateFindingSchema = {
41
+ finding_id: { type: 'string', required: true, validate: uuidValidator },
42
+ title: { type: 'string' },
43
+ description: { type: 'string' },
44
+ severity: { type: 'string', validate: createEnumValidator(VALID_FINDING_SEVERITIES) },
45
+ status: { type: 'string', validate: createEnumValidator(VALID_FINDING_STATUSES) },
46
+ resolution_note: { type: 'string' },
47
+ };
48
+ const deleteFindingSchema = {
49
+ finding_id: { type: 'string', required: true, validate: uuidValidator },
50
+ };
51
+ const VALID_SCOPES = ['summary', 'detailed'];
52
+ const queryKnowledgeBaseSchema = {
53
+ project_id: { type: 'string', required: true, validate: uuidValidator },
54
+ scope: { type: 'string', default: 'summary', validate: createEnumValidator(VALID_SCOPES) },
55
+ categories: { type: 'array' },
56
+ limit: { type: 'number', default: 5 },
57
+ search_query: { type: 'string' },
58
+ };
13
59
  export const addFinding = async (args, ctx) => {
14
- const { project_id, category, title, description, severity, file_path, line_number, related_task_id } = args;
15
- validateRequired(project_id, 'project_id');
16
- validateUUID(project_id, 'project_id');
17
- validateRequired(title, 'title');
18
- if (related_task_id)
19
- validateUUID(related_task_id, 'related_task_id');
60
+ const { project_id, title, description, category, severity, file_path, line_number, related_task_id } = parseArgs(args, addFindingSchema);
20
61
  const apiClient = getApiClient();
21
62
  const response = await apiClient.addFinding(project_id, {
22
63
  title,
23
64
  description,
24
- category,
25
- severity,
65
+ category: category,
66
+ severity: severity,
26
67
  file_path,
27
68
  line_number,
28
69
  related_task_id
@@ -32,15 +73,13 @@ export const addFinding = async (args, ctx) => {
32
73
  }
33
74
  return { result: response.data };
34
75
  };
35
- export const getFindings = async (args, ctx) => {
36
- const { project_id, category, severity, status, limit = 50, offset = 0, search_query, summary_only = false } = args;
37
- validateRequired(project_id, 'project_id');
38
- validateUUID(project_id, 'project_id');
76
+ export const getFindings = async (args, _ctx) => {
77
+ const { project_id, category, severity, status, limit, offset, search_query, summary_only } = parseArgs(args, getFindingsSchema);
39
78
  const apiClient = getApiClient();
40
79
  const response = await apiClient.getFindings(project_id, {
41
- category,
42
- severity,
43
- status,
80
+ category: category,
81
+ severity: severity,
82
+ status: status,
44
83
  limit,
45
84
  offset,
46
85
  search_query,
@@ -56,10 +95,8 @@ export const getFindings = async (args, ctx) => {
56
95
  * Returns counts by category, severity, and status without the actual finding data.
57
96
  * This is much more token-efficient than get_findings for understanding the overall state.
58
97
  */
59
- export const getFindingsStats = async (args, ctx) => {
60
- const { project_id } = args;
61
- validateRequired(project_id, 'project_id');
62
- validateUUID(project_id, 'project_id');
98
+ export const getFindingsStats = async (args, _ctx) => {
99
+ const { project_id } = parseArgs(args, getFindingsStatsSchema);
63
100
  const apiClient = getApiClient();
64
101
  const response = await apiClient.getFindingsStats(project_id);
65
102
  if (!response.ok) {
@@ -67,16 +104,14 @@ export const getFindingsStats = async (args, ctx) => {
67
104
  }
68
105
  return { result: response.data };
69
106
  };
70
- export const updateFinding = async (args, ctx) => {
71
- const { finding_id, status, resolution_note, title, description, severity } = args;
72
- validateRequired(finding_id, 'finding_id');
73
- validateUUID(finding_id, 'finding_id');
107
+ export const updateFinding = async (args, _ctx) => {
108
+ const { finding_id, title, description, severity, status, resolution_note } = parseArgs(args, updateFindingSchema);
74
109
  const apiClient = getApiClient();
75
110
  const response = await apiClient.updateFinding(finding_id, {
76
111
  title,
77
112
  description,
78
- severity,
79
- status,
113
+ severity: severity,
114
+ status: status,
80
115
  resolution_note
81
116
  });
82
117
  if (!response.ok) {
@@ -84,10 +119,8 @@ export const updateFinding = async (args, ctx) => {
84
119
  }
85
120
  return { result: response.data };
86
121
  };
87
- export const deleteFinding = async (args, ctx) => {
88
- const { finding_id } = args;
89
- validateRequired(finding_id, 'finding_id');
90
- validateUUID(finding_id, 'finding_id');
122
+ export const deleteFinding = async (args, _ctx) => {
123
+ const { finding_id } = parseArgs(args, deleteFindingSchema);
91
124
  const apiClient = getApiClient();
92
125
  const response = await apiClient.deleteFinding(finding_id);
93
126
  if (!response.ok) {
@@ -95,6 +128,27 @@ export const deleteFinding = async (args, ctx) => {
95
128
  }
96
129
  return { result: response.data };
97
130
  };
131
+ /**
132
+ * Query aggregated project knowledge in a single call.
133
+ * Returns findings, Q&A, decisions, completed tasks, and resolved blockers.
134
+ * Use this instead of multiple separate tool calls to reduce token usage.
135
+ */
136
+ export const queryKnowledgeBase = async (args, _ctx) => {
137
+ const { project_id, scope, categories, limit, search_query } = parseArgs(args, queryKnowledgeBaseSchema);
138
+ // Validate limit range
139
+ const effectiveLimit = Math.min(Math.max(1, limit ?? 5), 20);
140
+ const apiClient = getApiClient();
141
+ const response = await apiClient.queryKnowledgeBase(project_id, {
142
+ scope: scope,
143
+ categories: categories,
144
+ limit: effectiveLimit,
145
+ search_query
146
+ });
147
+ if (!response.ok) {
148
+ throw new Error(response.error || 'Failed to query knowledge base');
149
+ }
150
+ return { result: response.data };
151
+ };
98
152
  /**
99
153
  * Findings handlers registry
100
154
  */
@@ -104,4 +158,5 @@ export const findingHandlers = {
104
158
  get_findings_stats: getFindingsStats,
105
159
  update_finding: updateFinding,
106
160
  delete_finding: deleteFinding,
161
+ query_knowledge_base: queryKnowledgeBase,
107
162
  };
@@ -7,7 +7,7 @@
7
7
  * - get_git_issues: List git issues for a project
8
8
  * - delete_git_issue: Remove a git issue
9
9
  */
10
- import { validateRequired, validateUUID } from '../validators.js';
10
+ import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
11
11
  import { getApiClient } from '../api-client.js';
12
12
  const VALID_GIT_ISSUE_TYPES = [
13
13
  'merge_conflict',
@@ -17,25 +17,41 @@ const VALID_GIT_ISSUE_TYPES = [
17
17
  'pr_not_mergeable',
18
18
  ];
19
19
  const VALID_GIT_ISSUE_STATUSES = ['open', 'resolved'];
20
+ // Argument schemas for type-safe parsing
21
+ const addGitIssueSchema = {
22
+ project_id: { type: 'string', required: true, validate: uuidValidator },
23
+ issue_type: { type: 'string', required: true, validate: createEnumValidator(VALID_GIT_ISSUE_TYPES) },
24
+ branch: { type: 'string', required: true },
25
+ target_branch: { type: 'string' },
26
+ pr_url: { type: 'string' },
27
+ conflicting_files: { type: 'array' },
28
+ error_message: { type: 'string' },
29
+ task_id: { type: 'string', validate: uuidValidator },
30
+ };
31
+ const resolveGitIssueSchema = {
32
+ git_issue_id: { type: 'string', required: true, validate: uuidValidator },
33
+ resolution_note: { type: 'string' },
34
+ auto_resolved: { type: 'boolean' },
35
+ };
36
+ const getGitIssuesSchema = {
37
+ project_id: { type: 'string', required: true, validate: uuidValidator },
38
+ status: { type: 'string', default: 'open', validate: createEnumValidator(VALID_GIT_ISSUE_STATUSES) },
39
+ issue_type: { type: 'string', validate: createEnumValidator(VALID_GIT_ISSUE_TYPES) },
40
+ branch: { type: 'string' },
41
+ limit: { type: 'number', default: 50 },
42
+ };
43
+ const deleteGitIssueSchema = {
44
+ git_issue_id: { type: 'string', required: true, validate: uuidValidator },
45
+ };
20
46
  export const addGitIssue = async (args, ctx) => {
21
- const { project_id, issue_type, branch, target_branch, pr_url, conflicting_files, error_message, task_id, } = args;
22
- validateRequired(project_id, 'project_id');
23
- validateUUID(project_id, 'project_id');
24
- validateRequired(issue_type, 'issue_type');
25
- validateRequired(branch, 'branch');
26
- if (!VALID_GIT_ISSUE_TYPES.includes(issue_type)) {
27
- throw new Error(`Invalid issue_type. Valid types: ${VALID_GIT_ISSUE_TYPES.join(', ')}`);
28
- }
29
- if (task_id) {
30
- validateUUID(task_id, 'task_id');
31
- }
47
+ const { project_id, issue_type, branch, target_branch, pr_url, conflicting_files, error_message, task_id } = parseArgs(args, addGitIssueSchema);
32
48
  const apiClient = getApiClient();
33
49
  const response = await apiClient.addGitIssue(project_id, {
34
50
  issue_type: issue_type,
35
51
  branch,
36
52
  target_branch,
37
53
  pr_url,
38
- conflicting_files,
54
+ conflicting_files: conflicting_files,
39
55
  error_message,
40
56
  task_id
41
57
  }, ctx.session.currentSessionId || undefined);
@@ -45,9 +61,7 @@ export const addGitIssue = async (args, ctx) => {
45
61
  return { result: response.data };
46
62
  };
47
63
  export const resolveGitIssue = async (args, ctx) => {
48
- const { git_issue_id, resolution_note, auto_resolved } = args;
49
- validateRequired(git_issue_id, 'git_issue_id');
50
- validateUUID(git_issue_id, 'git_issue_id');
64
+ const { git_issue_id, resolution_note, auto_resolved } = parseArgs(args, resolveGitIssueSchema);
51
65
  const apiClient = getApiClient();
52
66
  const response = await apiClient.resolveGitIssue(git_issue_id, {
53
67
  resolution_note,
@@ -58,20 +72,12 @@ export const resolveGitIssue = async (args, ctx) => {
58
72
  }
59
73
  return { result: response.data };
60
74
  };
61
- export const getGitIssues = async (args, ctx) => {
62
- const { project_id, status = 'open', issue_type, branch, limit = 50, } = args;
63
- validateRequired(project_id, 'project_id');
64
- validateUUID(project_id, 'project_id');
65
- if (status && !VALID_GIT_ISSUE_STATUSES.includes(status)) {
66
- throw new Error(`Invalid status. Valid statuses: ${VALID_GIT_ISSUE_STATUSES.join(', ')}`);
67
- }
68
- if (issue_type && !VALID_GIT_ISSUE_TYPES.includes(issue_type)) {
69
- throw new Error(`Invalid issue_type. Valid types: ${VALID_GIT_ISSUE_TYPES.join(', ')}`);
70
- }
75
+ export const getGitIssues = async (args, _ctx) => {
76
+ const { project_id, status, issue_type, branch, limit } = parseArgs(args, getGitIssuesSchema);
71
77
  const apiClient = getApiClient();
72
78
  const response = await apiClient.getGitIssues(project_id, {
73
- status,
74
- issue_type,
79
+ status: status,
80
+ issue_type: issue_type,
75
81
  branch,
76
82
  limit
77
83
  });
@@ -80,10 +86,8 @@ export const getGitIssues = async (args, ctx) => {
80
86
  }
81
87
  return { result: response.data };
82
88
  };
83
- export const deleteGitIssue = async (args, ctx) => {
84
- const { git_issue_id } = args;
85
- validateRequired(git_issue_id, 'git_issue_id');
86
- validateUUID(git_issue_id, 'git_issue_id');
89
+ export const deleteGitIssue = async (args, _ctx) => {
90
+ const { git_issue_id } = parseArgs(args, deleteGitIssueSchema);
87
91
  const apiClient = getApiClient();
88
92
  const response = await apiClient.deleteGitIssue(git_issue_id);
89
93
  if (!response.ok) {
@@ -10,34 +10,60 @@
10
10
  *
11
11
  * MIGRATED: Uses Vibescope API client instead of direct Supabase
12
12
  */
13
- import { validateRequired, validateUUID, validatePriority, validateEstimatedMinutes } from '../validators.js';
13
+ import { parseArgs, uuidValidator, priorityValidator, minutesValidator, createEnumValidator, } from '../validators.js';
14
14
  import { getApiClient } from '../api-client.js';
15
+ const VALID_IDEA_STATUSES = ['raw', 'exploring', 'planned', 'in_development', 'shipped'];
16
+ // Argument schemas for type-safe parsing
17
+ const addIdeaSchema = {
18
+ project_id: { type: 'string', required: true, validate: uuidValidator },
19
+ title: { type: 'string', required: true },
20
+ description: { type: 'string' },
21
+ status: { type: 'string', validate: createEnumValidator(VALID_IDEA_STATUSES) },
22
+ };
23
+ const updateIdeaSchema = {
24
+ idea_id: { type: 'string', required: true, validate: uuidValidator },
25
+ title: { type: 'string' },
26
+ description: { type: 'string' },
27
+ status: { type: 'string', validate: createEnumValidator(VALID_IDEA_STATUSES) },
28
+ doc_url: { type: 'string' },
29
+ };
30
+ const getIdeasSchema = {
31
+ project_id: { type: 'string', required: true, validate: uuidValidator },
32
+ status: { type: 'string', validate: createEnumValidator(VALID_IDEA_STATUSES) },
33
+ limit: { type: 'number', default: 50 },
34
+ offset: { type: 'number', default: 0 },
35
+ search_query: { type: 'string' },
36
+ };
37
+ const deleteIdeaSchema = {
38
+ idea_id: { type: 'string', required: true, validate: uuidValidator },
39
+ };
40
+ const convertIdeaToTaskSchema = {
41
+ idea_id: { type: 'string', required: true, validate: uuidValidator },
42
+ priority: { type: 'number', default: 3, validate: priorityValidator },
43
+ estimated_minutes: { type: 'number', validate: minutesValidator },
44
+ update_status: { type: 'boolean', default: true },
45
+ };
15
46
  export const addIdea = async (args, ctx) => {
16
- const { project_id, title, description, status } = args;
17
- validateRequired(project_id, 'project_id');
18
- validateUUID(project_id, 'project_id');
19
- validateRequired(title, 'title');
47
+ const { project_id, title, description, status } = parseArgs(args, addIdeaSchema);
20
48
  const { session } = ctx;
21
49
  const apiClient = getApiClient();
22
50
  const response = await apiClient.addIdea(project_id, {
23
51
  title,
24
52
  description,
25
- status
53
+ status: status
26
54
  }, session.currentSessionId || undefined);
27
55
  if (!response.ok) {
28
56
  throw new Error(`Failed to add idea: ${response.error}`);
29
57
  }
30
58
  return { result: { success: true, idea_id: response.data?.idea_id, title } };
31
59
  };
32
- export const updateIdea = async (args, ctx) => {
33
- const { idea_id, title, description, status, doc_url } = args;
34
- validateRequired(idea_id, 'idea_id');
35
- validateUUID(idea_id, 'idea_id');
60
+ export const updateIdea = async (args, _ctx) => {
61
+ const { idea_id, title, description, status, doc_url } = parseArgs(args, updateIdeaSchema);
36
62
  const apiClient = getApiClient();
37
63
  const response = await apiClient.updateIdea(idea_id, {
38
64
  title,
39
65
  description,
40
- status,
66
+ status: status,
41
67
  doc_url
42
68
  });
43
69
  if (!response.ok) {
@@ -45,13 +71,11 @@ export const updateIdea = async (args, ctx) => {
45
71
  }
46
72
  return { result: { success: true, idea_id } };
47
73
  };
48
- export const getIdeas = async (args, ctx) => {
49
- const { project_id, status, limit = 50, offset = 0, search_query } = args;
50
- validateRequired(project_id, 'project_id');
51
- validateUUID(project_id, 'project_id');
74
+ export const getIdeas = async (args, _ctx) => {
75
+ const { project_id, status, limit, offset, search_query } = parseArgs(args, getIdeasSchema);
52
76
  const apiClient = getApiClient();
53
77
  const response = await apiClient.getIdeas(project_id, {
54
- status,
78
+ status: status,
55
79
  limit,
56
80
  offset,
57
81
  search_query
@@ -65,10 +89,8 @@ export const getIdeas = async (args, ctx) => {
65
89
  },
66
90
  };
67
91
  };
68
- export const deleteIdea = async (args, ctx) => {
69
- const { idea_id } = args;
70
- validateRequired(idea_id, 'idea_id');
71
- validateUUID(idea_id, 'idea_id');
92
+ export const deleteIdea = async (args, _ctx) => {
93
+ const { idea_id } = parseArgs(args, deleteIdeaSchema);
72
94
  const apiClient = getApiClient();
73
95
  const response = await apiClient.deleteIdea(idea_id);
74
96
  if (!response.ok) {
@@ -76,12 +98,8 @@ export const deleteIdea = async (args, ctx) => {
76
98
  }
77
99
  return { result: { success: true } };
78
100
  };
79
- export const convertIdeaToTask = async (args, ctx) => {
80
- const { idea_id, priority = 3, estimated_minutes, update_status = true } = args;
81
- validateRequired(idea_id, 'idea_id');
82
- validateUUID(idea_id, 'idea_id');
83
- validatePriority(priority);
84
- validateEstimatedMinutes(estimated_minutes);
101
+ export const convertIdeaToTask = async (args, _ctx) => {
102
+ const { idea_id, priority, estimated_minutes, update_status } = parseArgs(args, convertIdeaToTaskSchema);
85
103
  const apiClient = getApiClient();
86
104
  // Use proxy for convert_idea_to_task operation
87
105
  const response = await apiClient.proxy('convert_idea_to_task', {
@@ -24,6 +24,8 @@ export * from './organizations.js';
24
24
  export * from './cost.js';
25
25
  export * from './git-issues.js';
26
26
  export * from './sprints.js';
27
+ export * from './file-checkouts.js';
28
+ export * from './roles.js';
27
29
  import type { HandlerRegistry } from './types.js';
28
30
  /**
29
31
  * Build the complete handler registry from all modules
@@ -24,6 +24,8 @@ export * from './organizations.js';
24
24
  export * from './cost.js';
25
25
  export * from './git-issues.js';
26
26
  export * from './sprints.js';
27
+ export * from './file-checkouts.js';
28
+ export * from './roles.js';
27
29
  import { milestoneHandlers } from './milestones.js';
28
30
  import { sessionHandlers } from './session.js';
29
31
  import { ideaHandlers } from './ideas.js';
@@ -43,6 +45,8 @@ import { organizationHandlers } from './organizations.js';
43
45
  import { costHandlers } from './cost.js';
44
46
  import { gitIssueHandlers } from './git-issues.js';
45
47
  import { sprintHandlers } from './sprints.js';
48
+ import { fileCheckoutHandlers } from './file-checkouts.js';
49
+ import { roleHandlers } from './roles.js';
46
50
  /**
47
51
  * Build the complete handler registry from all modules
48
52
  */
@@ -67,5 +71,7 @@ export function buildHandlerRegistry() {
67
71
  ...costHandlers,
68
72
  ...gitIssueHandlers,
69
73
  ...sprintHandlers,
74
+ ...fileCheckoutHandlers,
75
+ ...roleHandlers,
70
76
  };
71
77
  }
@@ -10,13 +10,34 @@
10
10
  *
11
11
  * MIGRATED: Uses Vibescope API client instead of direct Supabase
12
12
  */
13
- import { ValidationError, validateRequired, validateUUID } from '../validators.js';
13
+ import { parseArgs, uuidValidator, createEnumValidator, ValidationError, } from '../validators.js';
14
14
  import { getApiClient } from '../api-client.js';
15
+ const VALID_MILESTONE_STATUSES = ['pending', 'in_progress', 'completed'];
16
+ // Argument schemas for type-safe parsing
17
+ const addMilestoneSchema = {
18
+ task_id: { type: 'string', required: true, validate: uuidValidator },
19
+ title: { type: 'string', required: true },
20
+ description: { type: 'string' },
21
+ order_index: { type: 'number' },
22
+ };
23
+ const updateMilestoneSchema = {
24
+ milestone_id: { type: 'string', required: true, validate: uuidValidator },
25
+ title: { type: 'string' },
26
+ description: { type: 'string' },
27
+ status: { type: 'string', validate: createEnumValidator(VALID_MILESTONE_STATUSES) },
28
+ order_index: { type: 'number' },
29
+ };
30
+ const completeMilestoneSchema = {
31
+ milestone_id: { type: 'string', required: true, validate: uuidValidator },
32
+ };
33
+ const deleteMilestoneSchema = {
34
+ milestone_id: { type: 'string', required: true, validate: uuidValidator },
35
+ };
36
+ const getMilestonesSchema = {
37
+ task_id: { type: 'string', required: true, validate: uuidValidator },
38
+ };
15
39
  export const addMilestone = async (args, ctx) => {
16
- const { task_id, title, description, order_index } = args;
17
- validateRequired(task_id, 'task_id');
18
- validateUUID(task_id, 'task_id');
19
- validateRequired(title, 'title');
40
+ const { task_id, title, description, order_index } = parseArgs(args, addMilestoneSchema);
20
41
  const { session } = ctx;
21
42
  const apiClient = getApiClient();
22
43
  const response = await apiClient.addMilestone(task_id, {
@@ -34,16 +55,8 @@ export const addMilestone = async (args, ctx) => {
34
55
  },
35
56
  };
36
57
  };
37
- export const updateMilestone = async (args, ctx) => {
38
- const { milestone_id, title, description, status, order_index } = args;
39
- validateRequired(milestone_id, 'milestone_id');
40
- validateUUID(milestone_id, 'milestone_id');
41
- // Validate status if provided
42
- if (status !== undefined) {
43
- if (!['pending', 'in_progress', 'completed'].includes(status)) {
44
- throw new ValidationError('status must be pending, in_progress, or completed');
45
- }
46
- }
58
+ export const updateMilestone = async (args, _ctx) => {
59
+ const { milestone_id, title, description, status, order_index } = parseArgs(args, updateMilestoneSchema);
47
60
  // Check that at least one field is provided
48
61
  if (title === undefined && description === undefined && status === undefined && order_index === undefined) {
49
62
  throw new ValidationError('At least one field to update is required');
@@ -65,10 +78,8 @@ export const updateMilestone = async (args, ctx) => {
65
78
  },
66
79
  };
67
80
  };
68
- export const completeMilestone = async (args, ctx) => {
69
- const { milestone_id } = args;
70
- validateRequired(milestone_id, 'milestone_id');
71
- validateUUID(milestone_id, 'milestone_id');
81
+ export const completeMilestone = async (args, _ctx) => {
82
+ const { milestone_id } = parseArgs(args, completeMilestoneSchema);
72
83
  const apiClient = getApiClient();
73
84
  const response = await apiClient.completeMilestone(milestone_id);
74
85
  if (!response.ok) {
@@ -81,10 +92,8 @@ export const completeMilestone = async (args, ctx) => {
81
92
  },
82
93
  };
83
94
  };
84
- export const deleteMilestone = async (args, ctx) => {
85
- const { milestone_id } = args;
86
- validateRequired(milestone_id, 'milestone_id');
87
- validateUUID(milestone_id, 'milestone_id');
95
+ export const deleteMilestone = async (args, _ctx) => {
96
+ const { milestone_id } = parseArgs(args, deleteMilestoneSchema);
88
97
  const apiClient = getApiClient();
89
98
  const response = await apiClient.deleteMilestone(milestone_id);
90
99
  if (!response.ok) {
@@ -97,10 +106,8 @@ export const deleteMilestone = async (args, ctx) => {
97
106
  },
98
107
  };
99
108
  };
100
- export const getMilestones = async (args, ctx) => {
101
- const { task_id } = args;
102
- validateRequired(task_id, 'task_id');
103
- validateUUID(task_id, 'task_id');
109
+ export const getMilestones = async (args, _ctx) => {
110
+ const { task_id } = parseArgs(args, getMilestonesSchema);
104
111
  const apiClient = getApiClient();
105
112
  const response = await apiClient.getMilestones(task_id);
106
113
  if (!response.ok) {