@vibescope/mcp-server 0.1.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 (76) hide show
  1. package/README.md +1 -1
  2. package/dist/api-client.d.ts +120 -2
  3. package/dist/api-client.js +51 -5
  4. package/dist/handlers/bodies-of-work.js +84 -50
  5. package/dist/handlers/cost.js +62 -54
  6. package/dist/handlers/decisions.js +29 -16
  7. package/dist/handlers/deployment.js +114 -107
  8. package/dist/handlers/discovery.d.ts +3 -0
  9. package/dist/handlers/discovery.js +55 -657
  10. package/dist/handlers/fallback.js +42 -28
  11. package/dist/handlers/file-checkouts.d.ts +18 -0
  12. package/dist/handlers/file-checkouts.js +101 -0
  13. package/dist/handlers/findings.d.ts +14 -1
  14. package/dist/handlers/findings.js +104 -28
  15. package/dist/handlers/git-issues.js +36 -32
  16. package/dist/handlers/ideas.js +44 -26
  17. package/dist/handlers/index.d.ts +2 -0
  18. package/dist/handlers/index.js +6 -0
  19. package/dist/handlers/milestones.js +34 -27
  20. package/dist/handlers/organizations.js +86 -78
  21. package/dist/handlers/progress.js +22 -11
  22. package/dist/handlers/project.js +62 -22
  23. package/dist/handlers/requests.js +15 -11
  24. package/dist/handlers/roles.d.ts +18 -0
  25. package/dist/handlers/roles.js +130 -0
  26. package/dist/handlers/session.js +52 -15
  27. package/dist/handlers/sprints.js +78 -65
  28. package/dist/handlers/tasks.js +135 -74
  29. package/dist/handlers/tool-docs.d.ts +4 -3
  30. package/dist/handlers/tool-docs.js +252 -5
  31. package/dist/handlers/validation.js +30 -14
  32. package/dist/index.js +25 -7
  33. package/dist/tools.js +417 -4
  34. package/package.json +1 -1
  35. package/src/api-client.ts +161 -8
  36. package/src/handlers/__test-setup__.ts +12 -0
  37. package/src/handlers/bodies-of-work.ts +127 -111
  38. package/src/handlers/cost.test.ts +34 -44
  39. package/src/handlers/cost.ts +77 -92
  40. package/src/handlers/decisions.test.ts +3 -2
  41. package/src/handlers/decisions.ts +32 -27
  42. package/src/handlers/deployment.ts +144 -190
  43. package/src/handlers/discovery.test.ts +4 -5
  44. package/src/handlers/discovery.ts +60 -746
  45. package/src/handlers/fallback.test.ts +78 -0
  46. package/src/handlers/fallback.ts +51 -38
  47. package/src/handlers/file-checkouts.test.ts +477 -0
  48. package/src/handlers/file-checkouts.ts +127 -0
  49. package/src/handlers/findings.test.ts +274 -2
  50. package/src/handlers/findings.ts +123 -57
  51. package/src/handlers/git-issues.ts +40 -80
  52. package/src/handlers/ideas.ts +56 -54
  53. package/src/handlers/index.ts +6 -0
  54. package/src/handlers/milestones.test.ts +1 -1
  55. package/src/handlers/milestones.ts +47 -45
  56. package/src/handlers/organizations.ts +104 -129
  57. package/src/handlers/progress.ts +24 -22
  58. package/src/handlers/project.ts +89 -57
  59. package/src/handlers/requests.ts +18 -14
  60. package/src/handlers/roles.test.ts +303 -0
  61. package/src/handlers/roles.ts +208 -0
  62. package/src/handlers/session.test.ts +37 -2
  63. package/src/handlers/session.ts +64 -21
  64. package/src/handlers/sprints.ts +114 -134
  65. package/src/handlers/tasks.test.ts +61 -0
  66. package/src/handlers/tasks.ts +170 -139
  67. package/src/handlers/tool-docs.ts +1024 -0
  68. package/src/handlers/validation.test.ts +53 -1
  69. package/src/handlers/validation.ts +32 -21
  70. package/src/index.ts +25 -7
  71. package/src/tools.ts +417 -4
  72. package/dist/config/tool-categories.d.ts +0 -31
  73. package/dist/config/tool-categories.js +0 -253
  74. package/dist/knowledge.d.ts +0 -6
  75. package/dist/knowledge.js +0 -218
  76. package/src/knowledge.ts +0 -230
package/README.md CHANGED
@@ -13,7 +13,7 @@ Use directly in your MCP config without installing:
13
13
  "mcpServers": {
14
14
  "vibescope": {
15
15
  "command": "npx",
16
- "args": ["-y", "@vibescope/mcp-server"],
16
+ "args": ["-y", "-p", "@vibescope/mcp-server", "vibescope-mcp"],
17
17
  "env": {
18
18
  "VIBESCOPE_API_KEY": "your-api-key"
19
19
  }
@@ -81,7 +81,9 @@ export declare class VibescopeApiClient {
81
81
  };
82
82
  error?: string;
83
83
  }>>;
84
- heartbeat(sessionId: string): Promise<ApiResponse<{
84
+ heartbeat(sessionId: string, options?: {
85
+ current_worktree_path?: string | null;
86
+ }): Promise<ApiResponse<{
85
87
  success: boolean;
86
88
  session_id: string;
87
89
  timestamp: string;
@@ -207,6 +209,7 @@ export declare class VibescopeApiClient {
207
209
  estimated_minutes?: number;
208
210
  blocking?: boolean;
209
211
  session_id?: string;
212
+ task_type?: string;
210
213
  }): Promise<ApiResponse<{
211
214
  success: boolean;
212
215
  task_id: string;
@@ -276,6 +279,20 @@ export declare class VibescopeApiClient {
276
279
  }): Promise<ApiResponse<{
277
280
  success: boolean;
278
281
  task_id: string;
282
+ git_workflow?: {
283
+ workflow: string;
284
+ base_branch: string;
285
+ suggested_branch: string;
286
+ worktree_required: boolean;
287
+ };
288
+ worktree_setup?: {
289
+ message: string;
290
+ commands: string[];
291
+ worktree_path: string;
292
+ branch_name: string;
293
+ cleanup_command: string;
294
+ };
295
+ next_step?: string;
279
296
  }>>;
280
297
  completeTask(taskId: string, params: {
281
298
  summary?: string;
@@ -297,6 +314,7 @@ export declare class VibescopeApiClient {
297
314
  deployment?: string;
298
315
  };
299
316
  next_action: string;
317
+ warnings?: string[];
300
318
  }>>;
301
319
  deleteTask(taskId: string): Promise<ApiResponse<{
302
320
  success: boolean;
@@ -358,7 +376,11 @@ export declare class VibescopeApiClient {
358
376
  deleteBlocker(blockerId: string): Promise<ApiResponse<{
359
377
  success: boolean;
360
378
  }>>;
361
- getDecisions(projectId: string): Promise<ApiResponse<{
379
+ getDecisions(projectId: string, options?: {
380
+ limit?: number;
381
+ offset?: number;
382
+ search_query?: string;
383
+ }): Promise<ApiResponse<{
362
384
  decisions: Array<{
363
385
  id: string;
364
386
  title: string;
@@ -434,6 +456,9 @@ export declare class VibescopeApiClient {
434
456
  severity?: string;
435
457
  status?: string;
436
458
  limit?: number;
459
+ offset?: number;
460
+ search_query?: string;
461
+ summary_only?: boolean;
437
462
  }): Promise<ApiResponse<{
438
463
  findings: Array<{
439
464
  id: string;
@@ -447,6 +472,14 @@ export declare class VibescopeApiClient {
447
472
  resolution_note?: string;
448
473
  created_at: string;
449
474
  }>;
475
+ total_count?: number;
476
+ has_more?: boolean;
477
+ }>>;
478
+ getFindingsStats(projectId: string): Promise<ApiResponse<{
479
+ total: number;
480
+ by_status: Record<string, number>;
481
+ by_severity: Record<string, number>;
482
+ by_category: Record<string, number>;
450
483
  }>>;
451
484
  addFinding(projectId: string, params: {
452
485
  title: string;
@@ -550,16 +583,33 @@ export declare class VibescopeApiClient {
550
583
  validateTask(taskId: string, params: {
551
584
  approved: boolean;
552
585
  validation_notes?: string;
586
+ skip_pr_check?: boolean;
553
587
  }, sessionId?: string): Promise<ApiResponse<{
554
588
  success: boolean;
555
589
  approved: boolean;
556
590
  task_id: string;
557
591
  message?: string;
592
+ workflow?: string;
558
593
  }>>;
559
594
  startFallbackActivity(projectId: string, activity: string, sessionId?: string): Promise<ApiResponse<{
560
595
  success: boolean;
561
596
  activity: string;
562
597
  message: string;
598
+ git_workflow?: {
599
+ workflow: string;
600
+ base_branch: string;
601
+ worktree_recommended: boolean;
602
+ note: string;
603
+ };
604
+ worktree_setup?: {
605
+ message: string;
606
+ commands: string[];
607
+ worktree_path: string;
608
+ branch_name: string;
609
+ cleanup_command: string;
610
+ report_worktree: string;
611
+ };
612
+ next_step?: string;
563
613
  }>>;
564
614
  stopFallbackActivity(projectId: string, summary?: string, sessionId?: string): Promise<ApiResponse<{
565
615
  success: boolean;
@@ -617,6 +667,8 @@ export declare class VibescopeApiClient {
617
667
  getActivityFeed(projectId: string, params?: {
618
668
  limit?: number;
619
669
  since?: string;
670
+ types?: string[];
671
+ created_by?: string;
620
672
  }): Promise<ApiResponse<{
621
673
  activities: Array<{
622
674
  type: string;
@@ -685,6 +737,7 @@ export declare class VibescopeApiClient {
685
737
  success: boolean;
686
738
  }>>;
687
739
  queryKnowledgeBase(projectId: string, params?: {
740
+ scope?: 'summary' | 'detailed';
688
741
  categories?: string[];
689
742
  limit?: number;
690
743
  search_query?: string;
@@ -694,22 +747,35 @@ export declare class VibescopeApiClient {
694
747
  title: string;
695
748
  category: string;
696
749
  severity: string;
750
+ description?: string;
697
751
  }>;
698
752
  decisions?: Array<{
699
753
  id: string;
700
754
  title: string;
701
755
  description: string;
756
+ rationale?: string;
702
757
  }>;
703
758
  completed_tasks?: Array<{
704
759
  id: string;
705
760
  title: string;
706
761
  completed_at: string;
762
+ summary?: string;
707
763
  }>;
708
764
  resolved_blockers?: Array<{
709
765
  id: string;
710
766
  description: string;
711
767
  resolution_note?: string;
712
768
  }>;
769
+ progress?: Array<{
770
+ id: string;
771
+ summary: string;
772
+ details?: string;
773
+ }>;
774
+ qa?: Array<{
775
+ id: string;
776
+ question: string;
777
+ answer: string;
778
+ }>;
713
779
  }>>;
714
780
  syncSession(sessionId: string, params?: {
715
781
  total_tokens?: number;
@@ -1023,6 +1089,7 @@ export declare class VibescopeApiClient {
1023
1089
  file_path?: string;
1024
1090
  stage?: string;
1025
1091
  blocking?: boolean;
1092
+ recurring?: boolean;
1026
1093
  }): Promise<ApiResponse<{
1027
1094
  success: boolean;
1028
1095
  requirement_id: string;
@@ -1108,6 +1175,57 @@ export declare class VibescopeApiClient {
1108
1175
  success: boolean;
1109
1176
  project_id: string;
1110
1177
  }>>;
1178
+ getHelpTopic(slug: string): Promise<ApiResponse<{
1179
+ slug: string;
1180
+ title: string;
1181
+ content: string;
1182
+ } | null>>;
1183
+ getHelpTopics(): Promise<ApiResponse<Array<{
1184
+ slug: string;
1185
+ title: string;
1186
+ }>>>;
1187
+ checkoutFile(projectId: string, filePath: string, reason?: string, sessionId?: string): Promise<ApiResponse<{
1188
+ success: boolean;
1189
+ checkout_id: string;
1190
+ file_path: string;
1191
+ already_checked_out?: boolean;
1192
+ message: string;
1193
+ }>>;
1194
+ checkinFile(params: {
1195
+ checkout_id?: string;
1196
+ project_id?: string;
1197
+ file_path?: string;
1198
+ summary?: string;
1199
+ }, sessionId?: string): Promise<ApiResponse<{
1200
+ success: boolean;
1201
+ checkout_id: string;
1202
+ message: string;
1203
+ }>>;
1204
+ getFileCheckouts(projectId: string, options?: {
1205
+ status?: string;
1206
+ file_path?: string;
1207
+ limit?: number;
1208
+ }): Promise<ApiResponse<{
1209
+ checkouts: Array<{
1210
+ id: string;
1211
+ file_path: string;
1212
+ status: string;
1213
+ checked_out_at: string;
1214
+ checkout_reason?: string;
1215
+ checked_in_at?: string;
1216
+ checkin_summary?: string;
1217
+ checked_out_by?: string;
1218
+ }>;
1219
+ }>>;
1220
+ abandonCheckout(params: {
1221
+ checkout_id?: string;
1222
+ project_id?: string;
1223
+ file_path?: string;
1224
+ }): Promise<ApiResponse<{
1225
+ success: boolean;
1226
+ checkout_id: string;
1227
+ message: string;
1228
+ }>>;
1111
1229
  }
1112
1230
  export declare function getApiClient(): VibescopeApiClient;
1113
1231
  export declare function initApiClient(config: ApiClientConfig): VibescopeApiClient;
@@ -28,7 +28,8 @@ export class VibescopeApiClient {
28
28
  return {
29
29
  ok: false,
30
30
  status: response.status,
31
- error: data.error || `HTTP ${response.status}`
31
+ error: data.error || `HTTP ${response.status}`,
32
+ data // Include full response data for additional error context
32
33
  };
33
34
  }
34
35
  return {
@@ -55,9 +56,10 @@ export class VibescopeApiClient {
55
56
  async startSession(params) {
56
57
  return this.request('POST', '/api/mcp/sessions/start', params);
57
58
  }
58
- async heartbeat(sessionId) {
59
+ async heartbeat(sessionId, options) {
59
60
  return this.request('POST', '/api/mcp/sessions/heartbeat', {
60
- session_id: sessionId
61
+ session_id: sessionId,
62
+ ...options
61
63
  });
62
64
  }
63
65
  async endSession(sessionId) {
@@ -170,8 +172,8 @@ export class VibescopeApiClient {
170
172
  // ============================================================================
171
173
  // Decision endpoints
172
174
  // ============================================================================
173
- async getDecisions(projectId) {
174
- return this.proxy('get_decisions', { project_id: projectId });
175
+ async getDecisions(projectId, options) {
176
+ return this.proxy('get_decisions', { project_id: projectId, ...options });
175
177
  }
176
178
  async logDecision(projectId, params, sessionId) {
177
179
  return this.proxy('log_decision', {
@@ -221,6 +223,11 @@ export class VibescopeApiClient {
221
223
  ...params
222
224
  });
223
225
  }
226
+ async getFindingsStats(projectId) {
227
+ return this.proxy('get_findings_stats', {
228
+ project_id: projectId
229
+ });
230
+ }
224
231
  async addFinding(projectId, params, sessionId) {
225
232
  return this.proxy('add_finding', {
226
233
  project_id: projectId,
@@ -679,6 +686,45 @@ export class VibescopeApiClient {
679
686
  readme_content: readmeContent
680
687
  });
681
688
  }
689
+ // ============================================================================
690
+ // Help Topics (database-backed)
691
+ // ============================================================================
692
+ async getHelpTopic(slug) {
693
+ return this.proxy('get_help_topic', { slug });
694
+ }
695
+ async getHelpTopics() {
696
+ return this.proxy('get_help_topics', {});
697
+ }
698
+ // ============================================================================
699
+ // File Checkout endpoints (multi-agent coordination)
700
+ // ============================================================================
701
+ async checkoutFile(projectId, filePath, reason, sessionId) {
702
+ return this.proxy('checkout_file', {
703
+ project_id: projectId,
704
+ file_path: filePath,
705
+ reason
706
+ }, sessionId ? {
707
+ session_id: sessionId,
708
+ persona: null,
709
+ instance_id: ''
710
+ } : undefined);
711
+ }
712
+ async checkinFile(params, sessionId) {
713
+ return this.proxy('checkin_file', params, sessionId ? {
714
+ session_id: sessionId,
715
+ persona: null,
716
+ instance_id: ''
717
+ } : undefined);
718
+ }
719
+ async getFileCheckouts(projectId, options) {
720
+ return this.proxy('get_file_checkouts', {
721
+ project_id: projectId,
722
+ ...options
723
+ });
724
+ }
725
+ async abandonCheckout(params) {
726
+ return this.proxy('abandon_checkout', params);
727
+ }
682
728
  }
683
729
  // Singleton instance
684
730
  let apiClient = null;
@@ -17,13 +17,78 @@
17
17
  *
18
18
  * MIGRATED: Uses Vibescope API client instead of direct Supabase
19
19
  */
20
- import { validateRequired, validateUUID } from '../validators.js';
20
+ import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
21
21
  import { getApiClient } from '../api-client.js';
22
+ const BODY_OF_WORK_STATUSES = ['draft', 'active', 'completed', 'cancelled'];
23
+ const TASK_PHASES = ['pre', 'core', 'post'];
24
+ const DEPLOY_ENVIRONMENTS = ['development', 'staging', 'production'];
25
+ const VERSION_BUMPS = ['patch', 'minor', 'major'];
26
+ const DEPLOY_TRIGGERS = ['all_completed', 'all_completed_validated'];
27
+ // ============================================================================
28
+ // Argument Schemas
29
+ // ============================================================================
30
+ const createBodyOfWorkSchema = {
31
+ project_id: { type: 'string', required: true, validate: uuidValidator },
32
+ title: { type: 'string', required: true },
33
+ description: { type: 'string' },
34
+ auto_deploy_on_completion: { type: 'boolean' },
35
+ deploy_environment: { type: 'string', validate: createEnumValidator(DEPLOY_ENVIRONMENTS) },
36
+ deploy_version_bump: { type: 'string', validate: createEnumValidator(VERSION_BUMPS) },
37
+ deploy_trigger: { type: 'string', validate: createEnumValidator(DEPLOY_TRIGGERS) },
38
+ };
39
+ const updateBodyOfWorkSchema = {
40
+ body_of_work_id: { type: 'string', required: true, validate: uuidValidator },
41
+ title: { type: 'string' },
42
+ description: { type: 'string' },
43
+ auto_deploy_on_completion: { type: 'boolean' },
44
+ deploy_environment: { type: 'string', validate: createEnumValidator(DEPLOY_ENVIRONMENTS) },
45
+ deploy_version_bump: { type: 'string', validate: createEnumValidator(VERSION_BUMPS) },
46
+ deploy_trigger: { type: 'string', validate: createEnumValidator(DEPLOY_TRIGGERS) },
47
+ };
48
+ const getBodyOfWorkSchema = {
49
+ body_of_work_id: { type: 'string', required: true, validate: uuidValidator },
50
+ summary_only: { type: 'boolean', default: false },
51
+ };
52
+ const getBodiesOfWorkSchema = {
53
+ project_id: { type: 'string', required: true, validate: uuidValidator },
54
+ status: { type: 'string', validate: createEnumValidator(BODY_OF_WORK_STATUSES) },
55
+ limit: { type: 'number', default: 50 },
56
+ offset: { type: 'number', default: 0 },
57
+ search_query: { type: 'string' },
58
+ };
59
+ const deleteBodyOfWorkSchema = {
60
+ body_of_work_id: { type: 'string', required: true, validate: uuidValidator },
61
+ };
62
+ const addTaskToBodyOfWorkSchema = {
63
+ body_of_work_id: { type: 'string', required: true, validate: uuidValidator },
64
+ task_id: { type: 'string', required: true, validate: uuidValidator },
65
+ phase: { type: 'string', validate: createEnumValidator(TASK_PHASES) },
66
+ order_index: { type: 'number' },
67
+ };
68
+ const removeTaskFromBodyOfWorkSchema = {
69
+ task_id: { type: 'string', required: true, validate: uuidValidator },
70
+ };
71
+ const activateBodyOfWorkSchema = {
72
+ body_of_work_id: { type: 'string', required: true, validate: uuidValidator },
73
+ };
74
+ const addTaskDependencySchema = {
75
+ body_of_work_id: { type: 'string', required: true, validate: uuidValidator },
76
+ task_id: { type: 'string', required: true, validate: uuidValidator },
77
+ depends_on_task_id: { type: 'string', required: true, validate: uuidValidator },
78
+ };
79
+ const removeTaskDependencySchema = {
80
+ task_id: { type: 'string', required: true, validate: uuidValidator },
81
+ depends_on_task_id: { type: 'string', required: true, validate: uuidValidator },
82
+ };
83
+ const getTaskDependenciesSchema = {
84
+ body_of_work_id: { type: 'string', validate: uuidValidator },
85
+ task_id: { type: 'string', validate: uuidValidator },
86
+ };
87
+ const getNextBodyOfWorkTaskSchema = {
88
+ body_of_work_id: { type: 'string', required: true, validate: uuidValidator },
89
+ };
22
90
  export const createBodyOfWork = async (args, ctx) => {
23
- const { project_id, title, description, auto_deploy_on_completion, deploy_environment, deploy_version_bump, deploy_trigger, } = args;
24
- validateRequired(project_id, 'project_id');
25
- validateUUID(project_id, 'project_id');
26
- validateRequired(title, 'title');
91
+ const { project_id, title, description, auto_deploy_on_completion, deploy_environment, deploy_version_bump, deploy_trigger, } = parseArgs(args, createBodyOfWorkSchema);
27
92
  const { session } = ctx;
28
93
  const apiClient = getApiClient();
29
94
  const response = await apiClient.proxy('create_body_of_work', {
@@ -53,9 +118,7 @@ export const createBodyOfWork = async (args, ctx) => {
53
118
  };
54
119
  };
55
120
  export const updateBodyOfWork = async (args, ctx) => {
56
- const { body_of_work_id, title, description, auto_deploy_on_completion, deploy_environment, deploy_version_bump, deploy_trigger, } = args;
57
- validateRequired(body_of_work_id, 'body_of_work_id');
58
- validateUUID(body_of_work_id, 'body_of_work_id');
121
+ const { body_of_work_id, title, description, auto_deploy_on_completion, deploy_environment, deploy_version_bump, deploy_trigger, } = parseArgs(args, updateBodyOfWorkSchema);
59
122
  // Check if any updates provided
60
123
  if (title === undefined && description === undefined && auto_deploy_on_completion === undefined &&
61
124
  deploy_environment === undefined && deploy_version_bump === undefined && deploy_trigger === undefined) {
@@ -77,25 +140,22 @@ export const updateBodyOfWork = async (args, ctx) => {
77
140
  return { result: { success: true, body_of_work_id } };
78
141
  };
79
142
  export const getBodyOfWork = async (args, ctx) => {
80
- const { body_of_work_id } = args;
81
- validateRequired(body_of_work_id, 'body_of_work_id');
82
- validateUUID(body_of_work_id, 'body_of_work_id');
143
+ const { body_of_work_id, summary_only } = parseArgs(args, getBodyOfWorkSchema);
83
144
  const apiClient = getApiClient();
84
- const response = await apiClient.proxy('get_body_of_work', { body_of_work_id });
145
+ // Response type varies based on summary_only
146
+ const response = await apiClient.proxy('get_body_of_work', { body_of_work_id, summary_only });
85
147
  if (!response.ok) {
86
148
  throw new Error(`Failed to get body of work: ${response.error}`);
87
149
  }
88
150
  return { result: response.data };
89
151
  };
90
152
  export const getBodiesOfWork = async (args, ctx) => {
91
- const { project_id, status, limit = 50, offset = 0, search_query } = args;
92
- validateRequired(project_id, 'project_id');
93
- validateUUID(project_id, 'project_id');
153
+ const { project_id, status, limit, offset, search_query } = parseArgs(args, getBodiesOfWorkSchema);
94
154
  const apiClient = getApiClient();
95
155
  const response = await apiClient.proxy('get_bodies_of_work', {
96
156
  project_id,
97
157
  status,
98
- limit: Math.min(limit, 100),
158
+ limit: Math.min(limit ?? 50, 100),
99
159
  offset,
100
160
  search_query
101
161
  });
@@ -105,9 +165,7 @@ export const getBodiesOfWork = async (args, ctx) => {
105
165
  return { result: response.data };
106
166
  };
107
167
  export const deleteBodyOfWork = async (args, ctx) => {
108
- const { body_of_work_id } = args;
109
- validateRequired(body_of_work_id, 'body_of_work_id');
110
- validateUUID(body_of_work_id, 'body_of_work_id');
168
+ const { body_of_work_id } = parseArgs(args, deleteBodyOfWorkSchema);
111
169
  const apiClient = getApiClient();
112
170
  const response = await apiClient.proxy('delete_body_of_work', {
113
171
  body_of_work_id
@@ -118,11 +176,7 @@ export const deleteBodyOfWork = async (args, ctx) => {
118
176
  return { result: { success: true, message: 'Body of work deleted. Tasks are preserved.' } };
119
177
  };
120
178
  export const addTaskToBodyOfWork = async (args, ctx) => {
121
- const { body_of_work_id, task_id, phase, order_index } = args;
122
- validateRequired(body_of_work_id, 'body_of_work_id');
123
- validateUUID(body_of_work_id, 'body_of_work_id');
124
- validateRequired(task_id, 'task_id');
125
- validateUUID(task_id, 'task_id');
179
+ const { body_of_work_id, task_id, phase, order_index } = parseArgs(args, addTaskToBodyOfWorkSchema);
126
180
  const apiClient = getApiClient();
127
181
  const response = await apiClient.proxy('add_task_to_body_of_work', {
128
182
  body_of_work_id,
@@ -136,9 +190,7 @@ export const addTaskToBodyOfWork = async (args, ctx) => {
136
190
  return { result: response.data };
137
191
  };
138
192
  export const removeTaskFromBodyOfWork = async (args, ctx) => {
139
- const { task_id } = args;
140
- validateRequired(task_id, 'task_id');
141
- validateUUID(task_id, 'task_id');
193
+ const { task_id } = parseArgs(args, removeTaskFromBodyOfWorkSchema);
142
194
  const apiClient = getApiClient();
143
195
  const response = await apiClient.proxy('remove_task_from_body_of_work', { task_id });
144
196
  if (!response.ok) {
@@ -147,9 +199,7 @@ export const removeTaskFromBodyOfWork = async (args, ctx) => {
147
199
  return { result: response.data };
148
200
  };
149
201
  export const activateBodyOfWork = async (args, ctx) => {
150
- const { body_of_work_id } = args;
151
- validateRequired(body_of_work_id, 'body_of_work_id');
152
- validateUUID(body_of_work_id, 'body_of_work_id');
202
+ const { body_of_work_id } = parseArgs(args, activateBodyOfWorkSchema);
153
203
  const apiClient = getApiClient();
154
204
  const response = await apiClient.proxy('activate_body_of_work', { body_of_work_id });
155
205
  if (!response.ok) {
@@ -158,13 +208,7 @@ export const activateBodyOfWork = async (args, ctx) => {
158
208
  return { result: response.data };
159
209
  };
160
210
  export const addTaskDependency = async (args, ctx) => {
161
- const { body_of_work_id, task_id, depends_on_task_id } = args;
162
- validateRequired(body_of_work_id, 'body_of_work_id');
163
- validateUUID(body_of_work_id, 'body_of_work_id');
164
- validateRequired(task_id, 'task_id');
165
- validateUUID(task_id, 'task_id');
166
- validateRequired(depends_on_task_id, 'depends_on_task_id');
167
- validateUUID(depends_on_task_id, 'depends_on_task_id');
211
+ const { body_of_work_id, task_id, depends_on_task_id } = parseArgs(args, addTaskDependencySchema);
168
212
  if (task_id === depends_on_task_id) {
169
213
  throw new Error('A task cannot depend on itself');
170
214
  }
@@ -180,11 +224,7 @@ export const addTaskDependency = async (args, ctx) => {
180
224
  return { result: response.data };
181
225
  };
182
226
  export const removeTaskDependency = async (args, ctx) => {
183
- const { task_id, depends_on_task_id } = args;
184
- validateRequired(task_id, 'task_id');
185
- validateUUID(task_id, 'task_id');
186
- validateRequired(depends_on_task_id, 'depends_on_task_id');
187
- validateUUID(depends_on_task_id, 'depends_on_task_id');
227
+ const { task_id, depends_on_task_id } = parseArgs(args, removeTaskDependencySchema);
188
228
  const apiClient = getApiClient();
189
229
  const response = await apiClient.proxy('remove_task_dependency', {
190
230
  task_id,
@@ -196,14 +236,10 @@ export const removeTaskDependency = async (args, ctx) => {
196
236
  return { result: response.data };
197
237
  };
198
238
  export const getTaskDependencies = async (args, ctx) => {
199
- const { body_of_work_id, task_id } = args;
239
+ const { body_of_work_id, task_id } = parseArgs(args, getTaskDependenciesSchema);
200
240
  if (!body_of_work_id && !task_id) {
201
241
  throw new Error('Either body_of_work_id or task_id is required');
202
242
  }
203
- if (body_of_work_id)
204
- validateUUID(body_of_work_id, 'body_of_work_id');
205
- if (task_id)
206
- validateUUID(task_id, 'task_id');
207
243
  const apiClient = getApiClient();
208
244
  const response = await apiClient.proxy('get_task_dependencies', {
209
245
  body_of_work_id,
@@ -215,9 +251,7 @@ export const getTaskDependencies = async (args, ctx) => {
215
251
  return { result: response.data };
216
252
  };
217
253
  export const getNextBodyOfWorkTask = async (args, ctx) => {
218
- const { body_of_work_id } = args;
219
- validateRequired(body_of_work_id, 'body_of_work_id');
220
- validateUUID(body_of_work_id, 'body_of_work_id');
254
+ const { body_of_work_id } = parseArgs(args, getNextBodyOfWorkTaskSchema);
221
255
  const apiClient = getApiClient();
222
256
  const response = await apiClient.proxy('get_next_body_of_work_task', { body_of_work_id });
223
257
  if (!response.ok) {