@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.
- package/README.md +60 -7
- package/dist/api-client.d.ts +251 -1
- package/dist/api-client.js +82 -3
- package/dist/handlers/blockers.js +9 -8
- package/dist/handlers/bodies-of-work.js +96 -63
- package/dist/handlers/connectors.d.ts +45 -0
- package/dist/handlers/connectors.js +183 -0
- package/dist/handlers/cost.d.ts +10 -0
- package/dist/handlers/cost.js +112 -50
- package/dist/handlers/decisions.js +32 -19
- package/dist/handlers/deployment.js +144 -122
- package/dist/handlers/discovery.d.ts +7 -0
- package/dist/handlers/discovery.js +96 -7
- package/dist/handlers/fallback.js +29 -23
- package/dist/handlers/file-checkouts.d.ts +20 -0
- package/dist/handlers/file-checkouts.js +133 -0
- package/dist/handlers/findings.d.ts +6 -0
- package/dist/handlers/findings.js +96 -40
- package/dist/handlers/git-issues.js +40 -36
- package/dist/handlers/ideas.js +49 -31
- package/dist/handlers/index.d.ts +3 -0
- package/dist/handlers/index.js +9 -0
- package/dist/handlers/milestones.js +39 -32
- package/dist/handlers/organizations.js +99 -91
- package/dist/handlers/progress.js +24 -13
- package/dist/handlers/project.js +68 -28
- package/dist/handlers/requests.js +18 -14
- package/dist/handlers/roles.d.ts +18 -0
- package/dist/handlers/roles.js +130 -0
- package/dist/handlers/session.js +58 -17
- package/dist/handlers/sprints.js +93 -81
- package/dist/handlers/tasks.d.ts +2 -0
- package/dist/handlers/tasks.js +189 -91
- package/dist/handlers/types.d.ts +64 -2
- package/dist/handlers/types.js +48 -1
- package/dist/handlers/validation.js +21 -17
- package/dist/index.js +7 -2716
- package/dist/token-tracking.d.ts +74 -0
- package/dist/token-tracking.js +122 -0
- package/dist/tools.js +685 -9
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +17 -0
- package/docs/TOOLS.md +2053 -0
- package/package.json +4 -1
- package/scripts/generate-docs.ts +212 -0
- package/src/api-client.test.ts +718 -0
- package/src/api-client.ts +320 -6
- package/src/handlers/__test-setup__.ts +16 -0
- package/src/handlers/blockers.test.ts +31 -19
- package/src/handlers/blockers.ts +9 -8
- package/src/handlers/bodies-of-work.test.ts +55 -32
- package/src/handlers/bodies-of-work.ts +115 -115
- package/src/handlers/connectors.test.ts +834 -0
- package/src/handlers/connectors.ts +229 -0
- package/src/handlers/cost.test.ts +34 -44
- package/src/handlers/cost.ts +136 -85
- package/src/handlers/decisions.test.ts +37 -27
- package/src/handlers/decisions.ts +35 -30
- package/src/handlers/deployment.ts +180 -208
- package/src/handlers/discovery.test.ts +4 -5
- package/src/handlers/discovery.ts +98 -8
- package/src/handlers/fallback.test.ts +26 -22
- package/src/handlers/fallback.ts +36 -33
- package/src/handlers/file-checkouts.test.ts +670 -0
- package/src/handlers/file-checkouts.ts +165 -0
- package/src/handlers/findings.test.ts +178 -19
- package/src/handlers/findings.ts +112 -74
- package/src/handlers/git-issues.test.ts +51 -43
- package/src/handlers/git-issues.ts +44 -84
- package/src/handlers/ideas.test.ts +28 -23
- package/src/handlers/ideas.ts +61 -59
- package/src/handlers/index.ts +9 -0
- package/src/handlers/milestones.test.ts +33 -28
- package/src/handlers/milestones.ts +52 -50
- package/src/handlers/organizations.test.ts +104 -83
- package/src/handlers/organizations.ts +117 -142
- package/src/handlers/progress.test.ts +20 -14
- package/src/handlers/progress.ts +26 -24
- package/src/handlers/project.test.ts +34 -27
- package/src/handlers/project.ts +95 -63
- package/src/handlers/requests.test.ts +27 -18
- package/src/handlers/requests.ts +21 -17
- package/src/handlers/roles.test.ts +303 -0
- package/src/handlers/roles.ts +208 -0
- package/src/handlers/session.test.ts +47 -0
- package/src/handlers/session.ts +71 -26
- package/src/handlers/sprints.test.ts +71 -50
- package/src/handlers/sprints.ts +113 -146
- package/src/handlers/tasks.test.ts +77 -15
- package/src/handlers/tasks.ts +231 -156
- package/src/handlers/tool-categories.test.ts +66 -0
- package/src/handlers/types.ts +81 -2
- package/src/handlers/validation.test.ts +78 -45
- package/src/handlers/validation.ts +23 -25
- package/src/index.ts +12 -2732
- package/src/token-tracking.test.ts +453 -0
- package/src/token-tracking.ts +164 -0
- package/src/tools.ts +685 -9
- package/src/utils.test.ts +2 -2
- package/src/utils.ts +17 -0
- package/dist/config/tool-categories.d.ts +0 -31
- package/dist/config/tool-categories.js +0 -253
- package/dist/knowledge.d.ts +0 -6
- package/dist/knowledge.js +0 -218
|
@@ -109,16 +109,19 @@ describe('createBodyOfWork', () => {
|
|
|
109
109
|
);
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
-
it('should
|
|
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
|
|
120
|
-
|
|
121
|
-
).
|
|
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
|
|
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
|
|
212
|
-
|
|
213
|
-
).
|
|
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
|
|
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
|
|
342
|
-
|
|
343
|
-
).
|
|
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
|
|
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
|
|
446
|
-
|
|
447
|
-
).
|
|
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
|
|
499
|
+
it('should return error when task depends on itself', async () => {
|
|
488
500
|
const ctx = createMockContext();
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
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
|
|
591
|
+
it('should return error when neither body_of_work_id nor task_id provided', async () => {
|
|
576
592
|
const ctx = createMockContext();
|
|
577
|
-
|
|
578
|
-
|
|
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
|
|
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
|
|
637
|
-
|
|
638
|
-
).
|
|
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 {
|
|
22
|
+
import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
|
|
23
23
|
import { getApiClient } from '../api-client.js';
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 };
|