@vibescope/mcp-server 0.2.0 → 0.2.2

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 (104) hide show
  1. package/README.md +60 -7
  2. package/dist/api-client.d.ts +251 -1
  3. package/dist/api-client.js +82 -3
  4. package/dist/handlers/blockers.js +9 -8
  5. package/dist/handlers/bodies-of-work.js +96 -63
  6. package/dist/handlers/connectors.d.ts +45 -0
  7. package/dist/handlers/connectors.js +183 -0
  8. package/dist/handlers/cost.d.ts +10 -0
  9. package/dist/handlers/cost.js +112 -50
  10. package/dist/handlers/decisions.js +32 -19
  11. package/dist/handlers/deployment.js +144 -122
  12. package/dist/handlers/discovery.d.ts +7 -0
  13. package/dist/handlers/discovery.js +96 -7
  14. package/dist/handlers/fallback.js +29 -23
  15. package/dist/handlers/file-checkouts.d.ts +20 -0
  16. package/dist/handlers/file-checkouts.js +133 -0
  17. package/dist/handlers/findings.d.ts +6 -0
  18. package/dist/handlers/findings.js +96 -40
  19. package/dist/handlers/git-issues.js +40 -36
  20. package/dist/handlers/ideas.js +49 -31
  21. package/dist/handlers/index.d.ts +3 -0
  22. package/dist/handlers/index.js +9 -0
  23. package/dist/handlers/milestones.js +39 -32
  24. package/dist/handlers/organizations.js +99 -91
  25. package/dist/handlers/progress.js +24 -13
  26. package/dist/handlers/project.js +68 -28
  27. package/dist/handlers/requests.js +18 -14
  28. package/dist/handlers/roles.d.ts +18 -0
  29. package/dist/handlers/roles.js +130 -0
  30. package/dist/handlers/session.js +58 -17
  31. package/dist/handlers/sprints.js +93 -81
  32. package/dist/handlers/tasks.d.ts +2 -0
  33. package/dist/handlers/tasks.js +189 -91
  34. package/dist/handlers/types.d.ts +64 -2
  35. package/dist/handlers/types.js +48 -1
  36. package/dist/handlers/validation.js +21 -17
  37. package/dist/index.js +7 -2716
  38. package/dist/token-tracking.d.ts +74 -0
  39. package/dist/token-tracking.js +122 -0
  40. package/dist/tools.js +685 -9
  41. package/dist/utils.d.ts +5 -0
  42. package/dist/utils.js +17 -0
  43. package/docs/TOOLS.md +2053 -0
  44. package/package.json +4 -1
  45. package/scripts/generate-docs.ts +212 -0
  46. package/src/api-client.test.ts +718 -0
  47. package/src/api-client.ts +320 -6
  48. package/src/handlers/__test-setup__.ts +16 -0
  49. package/src/handlers/blockers.test.ts +31 -19
  50. package/src/handlers/blockers.ts +9 -8
  51. package/src/handlers/bodies-of-work.test.ts +55 -32
  52. package/src/handlers/bodies-of-work.ts +115 -115
  53. package/src/handlers/connectors.test.ts +834 -0
  54. package/src/handlers/connectors.ts +229 -0
  55. package/src/handlers/cost.test.ts +34 -44
  56. package/src/handlers/cost.ts +136 -85
  57. package/src/handlers/decisions.test.ts +37 -27
  58. package/src/handlers/decisions.ts +35 -30
  59. package/src/handlers/deployment.ts +180 -208
  60. package/src/handlers/discovery.test.ts +4 -5
  61. package/src/handlers/discovery.ts +98 -8
  62. package/src/handlers/fallback.test.ts +26 -22
  63. package/src/handlers/fallback.ts +36 -33
  64. package/src/handlers/file-checkouts.test.ts +670 -0
  65. package/src/handlers/file-checkouts.ts +165 -0
  66. package/src/handlers/findings.test.ts +178 -19
  67. package/src/handlers/findings.ts +112 -74
  68. package/src/handlers/git-issues.test.ts +51 -43
  69. package/src/handlers/git-issues.ts +44 -84
  70. package/src/handlers/ideas.test.ts +28 -23
  71. package/src/handlers/ideas.ts +61 -59
  72. package/src/handlers/index.ts +9 -0
  73. package/src/handlers/milestones.test.ts +33 -28
  74. package/src/handlers/milestones.ts +52 -50
  75. package/src/handlers/organizations.test.ts +104 -83
  76. package/src/handlers/organizations.ts +117 -142
  77. package/src/handlers/progress.test.ts +20 -14
  78. package/src/handlers/progress.ts +26 -24
  79. package/src/handlers/project.test.ts +34 -27
  80. package/src/handlers/project.ts +95 -63
  81. package/src/handlers/requests.test.ts +27 -18
  82. package/src/handlers/requests.ts +21 -17
  83. package/src/handlers/roles.test.ts +303 -0
  84. package/src/handlers/roles.ts +208 -0
  85. package/src/handlers/session.test.ts +47 -0
  86. package/src/handlers/session.ts +71 -26
  87. package/src/handlers/sprints.test.ts +71 -50
  88. package/src/handlers/sprints.ts +113 -146
  89. package/src/handlers/tasks.test.ts +77 -15
  90. package/src/handlers/tasks.ts +231 -156
  91. package/src/handlers/tool-categories.test.ts +66 -0
  92. package/src/handlers/types.ts +81 -2
  93. package/src/handlers/validation.test.ts +78 -45
  94. package/src/handlers/validation.ts +23 -25
  95. package/src/index.ts +12 -2732
  96. package/src/token-tracking.test.ts +453 -0
  97. package/src/token-tracking.ts +164 -0
  98. package/src/tools.ts +685 -9
  99. package/src/utils.test.ts +2 -2
  100. package/src/utils.ts +17 -0
  101. package/dist/config/tool-categories.d.ts +0 -31
  102. package/dist/config/tool-categories.js +0 -253
  103. package/dist/knowledge.d.ts +0 -6
  104. package/dist/knowledge.js +0 -218
@@ -109,16 +109,19 @@ describe('createBodyOfWork', () => {
109
109
  );
110
110
  });
111
111
 
112
- it('should throw error when API call fails', async () => {
112
+ it('should return error when API call fails', async () => {
113
113
  const ctx = createMockContext();
114
114
  mockApiClient.proxy.mockResolvedValue({
115
115
  ok: false,
116
116
  error: 'Insert failed',
117
117
  });
118
118
 
119
- await expect(
120
- createBodyOfWork({ project_id: VALID_UUID, title: 'Test' }, ctx)
121
- ).rejects.toThrow('Failed to create body of work');
119
+ const result = await createBodyOfWork({ project_id: VALID_UUID, title: 'Test' }, ctx);
120
+
121
+ expect(result.isError).toBe(true);
122
+ expect(result.result).toMatchObject({
123
+ error: 'Insert failed',
124
+ });
122
125
  });
123
126
  });
124
127
 
@@ -201,16 +204,19 @@ describe('getBodyOfWork', () => {
201
204
  await expect(getBodyOfWork({}, ctx)).rejects.toThrow(ValidationError);
202
205
  });
203
206
 
204
- it('should throw error when body of work not found', async () => {
207
+ it('should return error when body of work not found', async () => {
205
208
  const ctx = createMockContext();
206
209
  mockApiClient.proxy.mockResolvedValue({
207
210
  ok: false,
208
211
  error: 'Not found',
209
212
  });
210
213
 
211
- await expect(
212
- getBodyOfWork({ body_of_work_id: VALID_UUID }, ctx)
213
- ).rejects.toThrow('Failed to get body of work');
214
+ const result = await getBodyOfWork({ body_of_work_id: VALID_UUID }, ctx);
215
+
216
+ expect(result.isError).toBe(true);
217
+ expect(result.result).toMatchObject({
218
+ error: 'Not found',
219
+ });
214
220
  });
215
221
 
216
222
  it('should return body of work with tasks organized by phase', async () => {
@@ -331,16 +337,19 @@ describe('addTaskToBodyOfWork', () => {
331
337
  ).rejects.toThrow(ValidationError);
332
338
  });
333
339
 
334
- it('should throw error when API returns error', async () => {
340
+ it('should return error when API returns error', async () => {
335
341
  const ctx = createMockContext();
336
342
  mockApiClient.proxy.mockResolvedValue({
337
343
  ok: false,
338
344
  error: 'Body of work not found',
339
345
  });
340
346
 
341
- await expect(
342
- addTaskToBodyOfWork({ body_of_work_id: VALID_UUID, task_id: VALID_UUID_2 }, ctx)
343
- ).rejects.toThrow('Failed to add task to body of work');
347
+ const result = await addTaskToBodyOfWork({ body_of_work_id: VALID_UUID, task_id: VALID_UUID_2 }, ctx);
348
+
349
+ expect(result.isError).toBe(true);
350
+ expect(result.result).toMatchObject({
351
+ error: 'Body of work not found',
352
+ });
344
353
  });
345
354
 
346
355
  it('should add task with default phase "core"', async () => {
@@ -435,16 +444,19 @@ describe('activateBodyOfWork', () => {
435
444
  await expect(activateBodyOfWork({}, ctx)).rejects.toThrow(ValidationError);
436
445
  });
437
446
 
438
- it('should throw error when API returns error', async () => {
447
+ it('should return error when API returns error', async () => {
439
448
  const ctx = createMockContext();
440
449
  mockApiClient.proxy.mockResolvedValue({
441
450
  ok: false,
442
451
  error: 'Body of work not found',
443
452
  });
444
453
 
445
- await expect(
446
- activateBodyOfWork({ body_of_work_id: VALID_UUID }, ctx)
447
- ).rejects.toThrow('Failed to activate body of work');
454
+ const result = await activateBodyOfWork({ body_of_work_id: VALID_UUID }, ctx);
455
+
456
+ expect(result.isError).toBe(true);
457
+ expect(result.result).toMatchObject({
458
+ error: 'Body of work not found',
459
+ });
448
460
  });
449
461
 
450
462
  it('should activate body of work successfully', async () => {
@@ -484,15 +496,19 @@ describe('addTaskDependency', () => {
484
496
  ).rejects.toThrow(ValidationError);
485
497
  });
486
498
 
487
- it('should throw error when task depends on itself', async () => {
499
+ it('should return error when task depends on itself', async () => {
488
500
  const ctx = createMockContext();
489
- await expect(
490
- addTaskDependency({
491
- body_of_work_id: VALID_UUID,
492
- task_id: VALID_UUID_2,
493
- depends_on_task_id: VALID_UUID_2,
494
- }, ctx)
495
- ).rejects.toThrow('A task cannot depend on itself');
501
+
502
+ const result = await addTaskDependency({
503
+ body_of_work_id: VALID_UUID,
504
+ task_id: VALID_UUID_2,
505
+ depends_on_task_id: VALID_UUID_2,
506
+ }, ctx);
507
+
508
+ expect(result.isError).toBe(true);
509
+ expect(result.result).toMatchObject({
510
+ error: 'A task cannot depend on itself',
511
+ });
496
512
  });
497
513
 
498
514
  it('should add dependency successfully', async () => {
@@ -572,11 +588,15 @@ describe('removeTaskDependency', () => {
572
588
  describe('getTaskDependencies', () => {
573
589
  beforeEach(() => vi.clearAllMocks());
574
590
 
575
- it('should throw error when neither body_of_work_id nor task_id provided', async () => {
591
+ it('should return error when neither body_of_work_id nor task_id provided', async () => {
576
592
  const ctx = createMockContext();
577
- await expect(getTaskDependencies({}, ctx)).rejects.toThrow(
578
- 'Either body_of_work_id or task_id is required'
579
- );
593
+
594
+ const result = await getTaskDependencies({}, ctx);
595
+
596
+ expect(result.isError).toBe(true);
597
+ expect(result.result).toMatchObject({
598
+ error: 'Either body_of_work_id or task_id is required',
599
+ });
580
600
  });
581
601
 
582
602
  it('should return dependencies filtered by body_of_work_id', async () => {
@@ -626,16 +646,19 @@ describe('getNextBodyOfWorkTask', () => {
626
646
  await expect(getNextBodyOfWorkTask({}, ctx)).rejects.toThrow(ValidationError);
627
647
  });
628
648
 
629
- it('should throw error when API returns error', async () => {
649
+ it('should return error when API returns error', async () => {
630
650
  const ctx = createMockContext();
631
651
  mockApiClient.proxy.mockResolvedValue({
632
652
  ok: false,
633
653
  error: 'Body of work not found',
634
654
  });
635
655
 
636
- await expect(
637
- getNextBodyOfWorkTask({ body_of_work_id: VALID_UUID }, ctx)
638
- ).rejects.toThrow('Failed to get next body of work task');
656
+ const result = await getNextBodyOfWorkTask({ body_of_work_id: VALID_UUID }, ctx);
657
+
658
+ expect(result.isError).toBe(true);
659
+ expect(result.result).toMatchObject({
660
+ error: 'Body of work not found',
661
+ });
639
662
  });
640
663
 
641
664
  it('should return null when no tasks available', async () => {
@@ -19,14 +19,96 @@
19
19
  */
20
20
 
21
21
  import type { Handler, HandlerRegistry } from './types.js';
22
- import { validateRequired, validateUUID } from '../validators.js';
22
+ import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
23
23
  import { getApiClient } from '../api-client.js';
24
24
 
25
- type BodyOfWorkStatus = 'draft' | 'active' | 'completed' | 'cancelled';
26
- type TaskPhase = 'pre' | 'core' | 'post';
27
- type DeployEnvironment = 'development' | 'staging' | 'production';
28
- type VersionBump = 'patch' | 'minor' | 'major';
29
- type DeployTrigger = 'all_completed' | 'all_completed_validated';
25
+ const BODY_OF_WORK_STATUSES = ['draft', 'active', 'completed', 'cancelled'] as const;
26
+ const TASK_PHASES = ['pre', 'core', 'post'] as const;
27
+ const DEPLOY_ENVIRONMENTS = ['development', 'staging', 'production'] as const;
28
+ const VERSION_BUMPS = ['patch', 'minor', 'major'] as const;
29
+ const DEPLOY_TRIGGERS = ['all_completed', 'all_completed_validated'] as const;
30
+
31
+ type BodyOfWorkStatus = typeof BODY_OF_WORK_STATUSES[number];
32
+ type TaskPhase = typeof TASK_PHASES[number];
33
+ type DeployEnvironment = typeof DEPLOY_ENVIRONMENTS[number];
34
+ type VersionBump = typeof VERSION_BUMPS[number];
35
+ type DeployTrigger = typeof DEPLOY_TRIGGERS[number];
36
+
37
+ // ============================================================================
38
+ // Argument Schemas
39
+ // ============================================================================
40
+
41
+ const createBodyOfWorkSchema = {
42
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
43
+ title: { type: 'string' as const, required: true as const },
44
+ description: { type: 'string' as const },
45
+ auto_deploy_on_completion: { type: 'boolean' as const },
46
+ deploy_environment: { type: 'string' as const, validate: createEnumValidator(DEPLOY_ENVIRONMENTS) },
47
+ deploy_version_bump: { type: 'string' as const, validate: createEnumValidator(VERSION_BUMPS) },
48
+ deploy_trigger: { type: 'string' as const, validate: createEnumValidator(DEPLOY_TRIGGERS) },
49
+ };
50
+
51
+ const updateBodyOfWorkSchema = {
52
+ body_of_work_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
53
+ title: { type: 'string' as const },
54
+ description: { type: 'string' as const },
55
+ auto_deploy_on_completion: { type: 'boolean' as const },
56
+ deploy_environment: { type: 'string' as const, validate: createEnumValidator(DEPLOY_ENVIRONMENTS) },
57
+ deploy_version_bump: { type: 'string' as const, validate: createEnumValidator(VERSION_BUMPS) },
58
+ deploy_trigger: { type: 'string' as const, validate: createEnumValidator(DEPLOY_TRIGGERS) },
59
+ };
60
+
61
+ const getBodyOfWorkSchema = {
62
+ body_of_work_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
63
+ summary_only: { type: 'boolean' as const, default: false },
64
+ };
65
+
66
+ const getBodiesOfWorkSchema = {
67
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
68
+ status: { type: 'string' as const, validate: createEnumValidator(BODY_OF_WORK_STATUSES) },
69
+ limit: { type: 'number' as const, default: 50 },
70
+ offset: { type: 'number' as const, default: 0 },
71
+ search_query: { type: 'string' as const },
72
+ };
73
+
74
+ const deleteBodyOfWorkSchema = {
75
+ body_of_work_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
76
+ };
77
+
78
+ const addTaskToBodyOfWorkSchema = {
79
+ body_of_work_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
80
+ task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
81
+ phase: { type: 'string' as const, validate: createEnumValidator(TASK_PHASES) },
82
+ order_index: { type: 'number' as const },
83
+ };
84
+
85
+ const removeTaskFromBodyOfWorkSchema = {
86
+ task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
87
+ };
88
+
89
+ const activateBodyOfWorkSchema = {
90
+ body_of_work_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
91
+ };
92
+
93
+ const addTaskDependencySchema = {
94
+ body_of_work_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
95
+ task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
96
+ depends_on_task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
97
+ };
98
+
99
+ const removeTaskDependencySchema = {
100
+ task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
101
+ depends_on_task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
102
+ };
103
+
104
+ const getTaskDependenciesSchema = {
105
+ body_of_work_id: { type: 'string' as const, validate: uuidValidator },
106
+ task_id: { type: 'string' as const, validate: uuidValidator },
107
+ };
108
+
109
+ const getNextBodyOfWorkTaskSchema = {
110
+ body_of_work_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
111
+ };
30
112
 
31
113
  export const createBodyOfWork: Handler = async (args, ctx) => {
32
114
  const {
@@ -37,19 +119,7 @@ export const createBodyOfWork: Handler = async (args, ctx) => {
37
119
  deploy_environment,
38
120
  deploy_version_bump,
39
121
  deploy_trigger,
40
- } = args as {
41
- project_id: string;
42
- title: string;
43
- description?: string;
44
- auto_deploy_on_completion?: boolean;
45
- deploy_environment?: DeployEnvironment;
46
- deploy_version_bump?: VersionBump;
47
- deploy_trigger?: DeployTrigger;
48
- };
49
-
50
- validateRequired(project_id, 'project_id');
51
- validateUUID(project_id, 'project_id');
52
- validateRequired(title, 'title');
122
+ } = parseArgs(args, createBodyOfWorkSchema);
53
123
 
54
124
  const { session } = ctx;
55
125
  const apiClient = getApiClient();
@@ -72,7 +142,7 @@ export const createBodyOfWork: Handler = async (args, ctx) => {
72
142
  });
73
143
 
74
144
  if (!response.ok) {
75
- throw new Error(`Failed to create body of work: ${response.error}`);
145
+ return { result: { error: response.error || 'Failed to create body of work' }, isError: true };
76
146
  }
77
147
 
78
148
  return {
@@ -95,18 +165,7 @@ export const updateBodyOfWork: Handler = async (args, ctx) => {
95
165
  deploy_environment,
96
166
  deploy_version_bump,
97
167
  deploy_trigger,
98
- } = args as {
99
- body_of_work_id: string;
100
- title?: string;
101
- description?: string;
102
- auto_deploy_on_completion?: boolean;
103
- deploy_environment?: DeployEnvironment;
104
- deploy_version_bump?: VersionBump;
105
- deploy_trigger?: DeployTrigger;
106
- };
107
-
108
- validateRequired(body_of_work_id, 'body_of_work_id');
109
- validateUUID(body_of_work_id, 'body_of_work_id');
168
+ } = parseArgs(args, updateBodyOfWorkSchema);
110
169
 
111
170
  // Check if any updates provided
112
171
  if (title === undefined && description === undefined && auto_deploy_on_completion === undefined &&
@@ -127,17 +186,14 @@ export const updateBodyOfWork: Handler = async (args, ctx) => {
127
186
  });
128
187
 
129
188
  if (!response.ok) {
130
- throw new Error(`Failed to update body of work: ${response.error}`);
189
+ return { result: { error: response.error || 'Failed to update body of work' }, isError: true };
131
190
  }
132
191
 
133
192
  return { result: { success: true, body_of_work_id } };
134
193
  };
135
194
 
136
195
  export const getBodyOfWork: Handler = async (args, ctx) => {
137
- const { body_of_work_id, summary_only = false } = args as { body_of_work_id: string; summary_only?: boolean };
138
-
139
- validateRequired(body_of_work_id, 'body_of_work_id');
140
- validateUUID(body_of_work_id, 'body_of_work_id');
196
+ const { body_of_work_id, summary_only } = parseArgs(args, getBodyOfWorkSchema);
141
197
 
142
198
  const apiClient = getApiClient();
143
199
 
@@ -170,23 +226,14 @@ export const getBodyOfWork: Handler = async (args, ctx) => {
170
226
  }>('get_body_of_work', { body_of_work_id, summary_only });
171
227
 
172
228
  if (!response.ok) {
173
- throw new Error(`Failed to get body of work: ${response.error}`);
229
+ return { result: { error: response.error || 'Failed to get body of work' }, isError: true };
174
230
  }
175
231
 
176
232
  return { result: response.data };
177
233
  };
178
234
 
179
235
  export const getBodiesOfWork: Handler = async (args, ctx) => {
180
- const { project_id, status, limit = 50, offset = 0, search_query } = args as {
181
- project_id: string;
182
- status?: BodyOfWorkStatus;
183
- limit?: number;
184
- offset?: number;
185
- search_query?: string;
186
- };
187
-
188
- validateRequired(project_id, 'project_id');
189
- validateUUID(project_id, 'project_id');
236
+ const { project_id, status, limit, offset, search_query } = parseArgs(args, getBodiesOfWorkSchema);
190
237
 
191
238
  const apiClient = getApiClient();
192
239
 
@@ -208,23 +255,20 @@ export const getBodiesOfWork: Handler = async (args, ctx) => {
208
255
  }>('get_bodies_of_work', {
209
256
  project_id,
210
257
  status,
211
- limit: Math.min(limit, 100),
258
+ limit: Math.min(limit ?? 50, 100),
212
259
  offset,
213
260
  search_query
214
261
  });
215
262
 
216
263
  if (!response.ok) {
217
- throw new Error(`Failed to fetch bodies of work: ${response.error}`);
264
+ return { result: { error: response.error || 'Failed to fetch bodies of work' }, isError: true };
218
265
  }
219
266
 
220
267
  return { result: response.data };
221
268
  };
222
269
 
223
270
  export const deleteBodyOfWork: Handler = async (args, ctx) => {
224
- const { body_of_work_id } = args as { body_of_work_id: string };
225
-
226
- validateRequired(body_of_work_id, 'body_of_work_id');
227
- validateUUID(body_of_work_id, 'body_of_work_id');
271
+ const { body_of_work_id } = parseArgs(args, deleteBodyOfWorkSchema);
228
272
 
229
273
  const apiClient = getApiClient();
230
274
 
@@ -233,24 +277,14 @@ export const deleteBodyOfWork: Handler = async (args, ctx) => {
233
277
  });
234
278
 
235
279
  if (!response.ok) {
236
- throw new Error(`Failed to delete body of work: ${response.error}`);
280
+ return { result: { error: response.error || 'Failed to delete body of work' }, isError: true };
237
281
  }
238
282
 
239
283
  return { result: { success: true, message: 'Body of work deleted. Tasks are preserved.' } };
240
284
  };
241
285
 
242
286
  export const addTaskToBodyOfWork: Handler = async (args, ctx) => {
243
- const { body_of_work_id, task_id, phase, order_index } = args as {
244
- body_of_work_id: string;
245
- task_id: string;
246
- phase?: TaskPhase;
247
- order_index?: number;
248
- };
249
-
250
- validateRequired(body_of_work_id, 'body_of_work_id');
251
- validateUUID(body_of_work_id, 'body_of_work_id');
252
- validateRequired(task_id, 'task_id');
253
- validateUUID(task_id, 'task_id');
287
+ const { body_of_work_id, task_id, phase, order_index } = parseArgs(args, addTaskToBodyOfWorkSchema);
254
288
 
255
289
  const apiClient = getApiClient();
256
290
 
@@ -268,17 +302,14 @@ export const addTaskToBodyOfWork: Handler = async (args, ctx) => {
268
302
  });
269
303
 
270
304
  if (!response.ok) {
271
- throw new Error(`Failed to add task to body of work: ${response.error}`);
305
+ return { result: { error: response.error || 'Failed to add task to body of work' }, isError: true };
272
306
  }
273
307
 
274
308
  return { result: response.data };
275
309
  };
276
310
 
277
311
  export const removeTaskFromBodyOfWork: Handler = async (args, ctx) => {
278
- const { task_id } = args as { task_id: string };
279
-
280
- validateRequired(task_id, 'task_id');
281
- validateUUID(task_id, 'task_id');
312
+ const { task_id } = parseArgs(args, removeTaskFromBodyOfWorkSchema);
282
313
 
283
314
  const apiClient = getApiClient();
284
315
 
@@ -289,17 +320,14 @@ export const removeTaskFromBodyOfWork: Handler = async (args, ctx) => {
289
320
  }>('remove_task_from_body_of_work', { task_id });
290
321
 
291
322
  if (!response.ok) {
292
- throw new Error(`Failed to remove task from body of work: ${response.error}`);
323
+ return { result: { error: response.error || 'Failed to remove task from body of work' }, isError: true };
293
324
  }
294
325
 
295
326
  return { result: response.data };
296
327
  };
297
328
 
298
329
  export const activateBodyOfWork: Handler = async (args, ctx) => {
299
- const { body_of_work_id } = args as { body_of_work_id: string };
300
-
301
- validateRequired(body_of_work_id, 'body_of_work_id');
302
- validateUUID(body_of_work_id, 'body_of_work_id');
330
+ const { body_of_work_id } = parseArgs(args, activateBodyOfWorkSchema);
303
331
 
304
332
  const apiClient = getApiClient();
305
333
 
@@ -312,28 +340,17 @@ export const activateBodyOfWork: Handler = async (args, ctx) => {
312
340
  }>('activate_body_of_work', { body_of_work_id });
313
341
 
314
342
  if (!response.ok) {
315
- throw new Error(`Failed to activate body of work: ${response.error}`);
343
+ return { result: { error: response.error || 'Failed to activate body of work' }, isError: true };
316
344
  }
317
345
 
318
346
  return { result: response.data };
319
347
  };
320
348
 
321
349
  export const addTaskDependency: Handler = async (args, ctx) => {
322
- const { body_of_work_id, task_id, depends_on_task_id } = args as {
323
- body_of_work_id: string;
324
- task_id: string;
325
- depends_on_task_id: string;
326
- };
327
-
328
- validateRequired(body_of_work_id, 'body_of_work_id');
329
- validateUUID(body_of_work_id, 'body_of_work_id');
330
- validateRequired(task_id, 'task_id');
331
- validateUUID(task_id, 'task_id');
332
- validateRequired(depends_on_task_id, 'depends_on_task_id');
333
- validateUUID(depends_on_task_id, 'depends_on_task_id');
350
+ const { body_of_work_id, task_id, depends_on_task_id } = parseArgs(args, addTaskDependencySchema);
334
351
 
335
352
  if (task_id === depends_on_task_id) {
336
- throw new Error('A task cannot depend on itself');
353
+ return { result: { error: 'A task cannot depend on itself' }, isError: true };
337
354
  }
338
355
 
339
356
  const apiClient = getApiClient();
@@ -351,22 +368,14 @@ export const addTaskDependency: Handler = async (args, ctx) => {
351
368
  });
352
369
 
353
370
  if (!response.ok) {
354
- throw new Error(`Failed to add task dependency: ${response.error}`);
371
+ return { result: { error: response.error || 'Failed to add task dependency' }, isError: true };
355
372
  }
356
373
 
357
374
  return { result: response.data };
358
375
  };
359
376
 
360
377
  export const removeTaskDependency: Handler = async (args, ctx) => {
361
- const { task_id, depends_on_task_id } = args as {
362
- task_id: string;
363
- depends_on_task_id: string;
364
- };
365
-
366
- validateRequired(task_id, 'task_id');
367
- validateUUID(task_id, 'task_id');
368
- validateRequired(depends_on_task_id, 'depends_on_task_id');
369
- validateUUID(depends_on_task_id, 'depends_on_task_id');
378
+ const { task_id, depends_on_task_id } = parseArgs(args, removeTaskDependencySchema);
370
379
 
371
380
  const apiClient = getApiClient();
372
381
 
@@ -380,25 +389,19 @@ export const removeTaskDependency: Handler = async (args, ctx) => {
380
389
  });
381
390
 
382
391
  if (!response.ok) {
383
- throw new Error(`Failed to remove task dependency: ${response.error}`);
392
+ return { result: { error: response.error || 'Failed to remove task dependency' }, isError: true };
384
393
  }
385
394
 
386
395
  return { result: response.data };
387
396
  };
388
397
 
389
398
  export const getTaskDependencies: Handler = async (args, ctx) => {
390
- const { body_of_work_id, task_id } = args as {
391
- body_of_work_id?: string;
392
- task_id?: string;
393
- };
399
+ const { body_of_work_id, task_id } = parseArgs(args, getTaskDependenciesSchema);
394
400
 
395
401
  if (!body_of_work_id && !task_id) {
396
- throw new Error('Either body_of_work_id or task_id is required');
402
+ return { result: { error: 'Either body_of_work_id or task_id is required' }, isError: true };
397
403
  }
398
404
 
399
- if (body_of_work_id) validateUUID(body_of_work_id, 'body_of_work_id');
400
- if (task_id) validateUUID(task_id, 'task_id');
401
-
402
405
  const apiClient = getApiClient();
403
406
 
404
407
  const response = await apiClient.proxy<{
@@ -414,17 +417,14 @@ export const getTaskDependencies: Handler = async (args, ctx) => {
414
417
  });
415
418
 
416
419
  if (!response.ok) {
417
- throw new Error(`Failed to fetch task dependencies: ${response.error}`);
420
+ return { result: { error: response.error || 'Failed to fetch task dependencies' }, isError: true };
418
421
  }
419
422
 
420
423
  return { result: response.data };
421
424
  };
422
425
 
423
426
  export const getNextBodyOfWorkTask: Handler = async (args, ctx) => {
424
- const { body_of_work_id } = args as { body_of_work_id: string };
425
-
426
- validateRequired(body_of_work_id, 'body_of_work_id');
427
- validateUUID(body_of_work_id, 'body_of_work_id');
427
+ const { body_of_work_id } = parseArgs(args, getNextBodyOfWorkTaskSchema);
428
428
 
429
429
  const apiClient = getApiClient();
430
430
 
@@ -443,7 +443,7 @@ export const getNextBodyOfWorkTask: Handler = async (args, ctx) => {
443
443
  }>('get_next_body_of_work_task', { body_of_work_id });
444
444
 
445
445
  if (!response.ok) {
446
- throw new Error(`Failed to get next body of work task: ${response.error}`);
446
+ return { result: { error: response.error || 'Failed to get next body of work task' }, isError: true };
447
447
  }
448
448
 
449
449
  return { result: response.data };