@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.
- package/dist/api-client.d.ts +64 -1
- package/dist/api-client.js +34 -3
- package/dist/handlers/bodies-of-work.js +82 -49
- package/dist/handlers/cost.js +62 -54
- package/dist/handlers/decisions.js +29 -16
- package/dist/handlers/deployment.js +112 -106
- package/dist/handlers/discovery.js +35 -5
- package/dist/handlers/fallback.js +24 -19
- package/dist/handlers/file-checkouts.d.ts +18 -0
- package/dist/handlers/file-checkouts.js +101 -0
- package/dist/handlers/findings.d.ts +6 -0
- package/dist/handlers/findings.js +85 -30
- package/dist/handlers/git-issues.js +36 -32
- package/dist/handlers/ideas.js +44 -26
- package/dist/handlers/index.d.ts +2 -0
- package/dist/handlers/index.js +6 -0
- package/dist/handlers/milestones.js +34 -27
- package/dist/handlers/organizations.js +86 -78
- package/dist/handlers/progress.js +22 -11
- package/dist/handlers/project.js +62 -22
- package/dist/handlers/requests.js +15 -11
- package/dist/handlers/roles.d.ts +18 -0
- package/dist/handlers/roles.js +130 -0
- package/dist/handlers/session.js +30 -8
- package/dist/handlers/sprints.js +76 -64
- package/dist/handlers/tasks.js +113 -73
- package/dist/handlers/validation.js +18 -14
- package/dist/tools.js +387 -0
- package/package.json +1 -1
- package/src/api-client.ts +89 -6
- package/src/handlers/__test-setup__.ts +7 -0
- package/src/handlers/bodies-of-work.ts +101 -101
- package/src/handlers/cost.test.ts +34 -44
- package/src/handlers/cost.ts +77 -92
- package/src/handlers/decisions.test.ts +3 -2
- package/src/handlers/decisions.ts +32 -27
- package/src/handlers/deployment.ts +142 -190
- package/src/handlers/discovery.test.ts +4 -5
- package/src/handlers/discovery.ts +37 -6
- package/src/handlers/fallback.ts +31 -29
- package/src/handlers/file-checkouts.test.ts +477 -0
- package/src/handlers/file-checkouts.ts +127 -0
- package/src/handlers/findings.test.ts +145 -0
- package/src/handlers/findings.ts +101 -64
- package/src/handlers/git-issues.ts +40 -80
- package/src/handlers/ideas.ts +56 -54
- package/src/handlers/index.ts +6 -0
- package/src/handlers/milestones.test.ts +1 -1
- package/src/handlers/milestones.ts +47 -45
- package/src/handlers/organizations.ts +104 -129
- package/src/handlers/progress.ts +24 -22
- package/src/handlers/project.ts +89 -57
- package/src/handlers/requests.ts +18 -14
- package/src/handlers/roles.test.ts +303 -0
- package/src/handlers/roles.ts +208 -0
- package/src/handlers/session.ts +39 -17
- package/src/handlers/sprints.ts +96 -129
- package/src/handlers/tasks.ts +144 -138
- package/src/handlers/validation.test.ts +1 -1
- package/src/handlers/validation.ts +20 -22
- package/src/tools.ts +387 -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
package/src/handlers/tasks.ts
CHANGED
|
@@ -18,16 +18,105 @@
|
|
|
18
18
|
|
|
19
19
|
import type { Handler, HandlerRegistry } from './types.js';
|
|
20
20
|
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
parseArgs,
|
|
22
|
+
uuidValidator,
|
|
23
|
+
taskStatusValidator,
|
|
24
|
+
priorityValidator,
|
|
25
|
+
progressValidator,
|
|
26
|
+
minutesValidator,
|
|
27
|
+
createEnumValidator,
|
|
27
28
|
ValidationError,
|
|
28
29
|
} from '../validators.js';
|
|
29
30
|
import { getApiClient } from '../api-client.js';
|
|
30
31
|
|
|
32
|
+
// Valid task types
|
|
33
|
+
const VALID_TASK_TYPES = [
|
|
34
|
+
'frontend', 'backend', 'database', 'feature', 'bugfix',
|
|
35
|
+
'design', 'mcp', 'testing', 'docs', 'infra', 'other'
|
|
36
|
+
] as const;
|
|
37
|
+
|
|
38
|
+
// ============================================================================
|
|
39
|
+
// Argument Schemas
|
|
40
|
+
// ============================================================================
|
|
41
|
+
|
|
42
|
+
const getTasksSchema = {
|
|
43
|
+
project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
|
|
44
|
+
status: { type: 'string' as const, validate: taskStatusValidator },
|
|
45
|
+
limit: { type: 'number' as const, default: 50 },
|
|
46
|
+
offset: { type: 'number' as const, default: 0 },
|
|
47
|
+
search_query: { type: 'string' as const },
|
|
48
|
+
include_subtasks: { type: 'boolean' as const, default: false },
|
|
49
|
+
include_metadata: { type: 'boolean' as const, default: false },
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const getNextTaskSchema = {
|
|
53
|
+
project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const addTaskSchema = {
|
|
57
|
+
project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
|
|
58
|
+
title: { type: 'string' as const, required: true as const },
|
|
59
|
+
description: { type: 'string' as const },
|
|
60
|
+
priority: { type: 'number' as const, default: 3, validate: priorityValidator },
|
|
61
|
+
estimated_minutes: { type: 'number' as const, validate: minutesValidator },
|
|
62
|
+
blocking: { type: 'boolean' as const, default: false },
|
|
63
|
+
task_type: { type: 'string' as const, validate: createEnumValidator(VALID_TASK_TYPES) },
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const updateTaskSchema = {
|
|
67
|
+
task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
|
|
68
|
+
title: { type: 'string' as const },
|
|
69
|
+
description: { type: 'string' as const },
|
|
70
|
+
priority: { type: 'number' as const, validate: priorityValidator },
|
|
71
|
+
status: { type: 'string' as const, validate: taskStatusValidator },
|
|
72
|
+
progress_percentage: { type: 'number' as const, validate: progressValidator },
|
|
73
|
+
progress_note: { type: 'string' as const },
|
|
74
|
+
estimated_minutes: { type: 'number' as const, validate: minutesValidator },
|
|
75
|
+
git_branch: { type: 'string' as const },
|
|
76
|
+
task_type: { type: 'string' as const, validate: createEnumValidator(VALID_TASK_TYPES) },
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const completeTaskSchema = {
|
|
80
|
+
task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
|
|
81
|
+
summary: { type: 'string' as const },
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const deleteTaskSchema = {
|
|
85
|
+
task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const addTaskReferenceSchema = {
|
|
89
|
+
task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
|
|
90
|
+
url: { type: 'string' as const, required: true as const },
|
|
91
|
+
label: { type: 'string' as const },
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const removeTaskReferenceSchema = {
|
|
95
|
+
task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
|
|
96
|
+
url: { type: 'string' as const, required: true as const },
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const batchUpdateTasksSchema = {
|
|
100
|
+
updates: { type: 'array' as const, required: true as const },
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const batchCompleteTasksSchema = {
|
|
104
|
+
completions: { type: 'array' as const, required: true as const },
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const addSubtaskSchema = {
|
|
108
|
+
parent_task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
|
|
109
|
+
title: { type: 'string' as const, required: true as const },
|
|
110
|
+
description: { type: 'string' as const },
|
|
111
|
+
priority: { type: 'number' as const, validate: priorityValidator },
|
|
112
|
+
estimated_minutes: { type: 'number' as const, validate: minutesValidator },
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const getSubtasksSchema = {
|
|
116
|
+
parent_task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
|
|
117
|
+
status: { type: 'string' as const, validate: taskStatusValidator },
|
|
118
|
+
};
|
|
119
|
+
|
|
31
120
|
// ============================================================================
|
|
32
121
|
// Git workflow helpers (used by complete_task response)
|
|
33
122
|
// ============================================================================
|
|
@@ -126,24 +215,12 @@ export function getValidationApprovedGitInstructions(
|
|
|
126
215
|
// ============================================================================
|
|
127
216
|
|
|
128
217
|
export const getTasks: Handler = async (args, ctx) => {
|
|
129
|
-
const { project_id, status, limit
|
|
130
|
-
project_id: string;
|
|
131
|
-
status?: string;
|
|
132
|
-
limit?: number;
|
|
133
|
-
offset?: number;
|
|
134
|
-
search_query?: string;
|
|
135
|
-
include_subtasks?: boolean;
|
|
136
|
-
include_metadata?: boolean; // When true, returns all task fields; when false (default), only id/title/priority/status
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
validateRequired(project_id, 'project_id');
|
|
140
|
-
validateUUID(project_id, 'project_id');
|
|
141
|
-
validateTaskStatus(status);
|
|
218
|
+
const { project_id, status, limit, offset, search_query, include_subtasks, include_metadata } = parseArgs(args, getTasksSchema);
|
|
142
219
|
|
|
143
220
|
const api = getApiClient();
|
|
144
221
|
const response = await api.getTasks(project_id, {
|
|
145
222
|
status,
|
|
146
|
-
limit: Math.min(limit, 200),
|
|
223
|
+
limit: Math.min(limit ?? 50, 200),
|
|
147
224
|
offset,
|
|
148
225
|
include_subtasks,
|
|
149
226
|
search_query,
|
|
@@ -164,10 +241,7 @@ export const getTasks: Handler = async (args, ctx) => {
|
|
|
164
241
|
};
|
|
165
242
|
|
|
166
243
|
export const getNextTask: Handler = async (args, ctx) => {
|
|
167
|
-
const { project_id } = args
|
|
168
|
-
|
|
169
|
-
validateRequired(project_id, 'project_id');
|
|
170
|
-
validateUUID(project_id, 'project_id');
|
|
244
|
+
const { project_id } = parseArgs(args, getNextTaskSchema);
|
|
171
245
|
|
|
172
246
|
const api = getApiClient();
|
|
173
247
|
const response = await api.getNextTask(project_id, ctx.session.currentSessionId || undefined);
|
|
@@ -211,21 +285,7 @@ export const getNextTask: Handler = async (args, ctx) => {
|
|
|
211
285
|
};
|
|
212
286
|
|
|
213
287
|
export const addTask: Handler = async (args, ctx) => {
|
|
214
|
-
const { project_id, title, description, priority
|
|
215
|
-
project_id: string;
|
|
216
|
-
title: string;
|
|
217
|
-
description?: string;
|
|
218
|
-
priority?: number;
|
|
219
|
-
estimated_minutes?: number;
|
|
220
|
-
blocking?: boolean;
|
|
221
|
-
task_type?: string;
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
validateRequired(project_id, 'project_id');
|
|
225
|
-
validateRequired(title, 'title');
|
|
226
|
-
validateUUID(project_id, 'project_id');
|
|
227
|
-
validatePriority(priority);
|
|
228
|
-
validateEstimatedMinutes(estimated_minutes);
|
|
288
|
+
const { project_id, title, description, priority, estimated_minutes, blocking, task_type } = parseArgs(args, addTaskSchema);
|
|
229
289
|
|
|
230
290
|
const api = getApiClient();
|
|
231
291
|
const response = await api.createTask(project_id, {
|
|
@@ -235,6 +295,7 @@ export const addTask: Handler = async (args, ctx) => {
|
|
|
235
295
|
estimated_minutes,
|
|
236
296
|
blocking,
|
|
237
297
|
session_id: ctx.session.currentSessionId || undefined,
|
|
298
|
+
task_type,
|
|
238
299
|
});
|
|
239
300
|
|
|
240
301
|
if (!response.ok) {
|
|
@@ -257,25 +318,8 @@ export const addTask: Handler = async (args, ctx) => {
|
|
|
257
318
|
};
|
|
258
319
|
|
|
259
320
|
export const updateTask: Handler = async (args, ctx) => {
|
|
260
|
-
const { task_id, progress_note,
|
|
261
|
-
|
|
262
|
-
title?: string;
|
|
263
|
-
description?: string;
|
|
264
|
-
priority?: number;
|
|
265
|
-
status?: string;
|
|
266
|
-
progress_percentage?: number;
|
|
267
|
-
progress_note?: string;
|
|
268
|
-
estimated_minutes?: number;
|
|
269
|
-
git_branch?: string;
|
|
270
|
-
task_type?: string;
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
validateRequired(task_id, 'task_id');
|
|
274
|
-
validateUUID(task_id, 'task_id');
|
|
275
|
-
validateTaskStatus(updates.status);
|
|
276
|
-
validatePriority(updates.priority);
|
|
277
|
-
validateProgressPercentage(updates.progress_percentage);
|
|
278
|
-
validateEstimatedMinutes(updates.estimated_minutes);
|
|
321
|
+
const { task_id, title, description, priority, status, progress_percentage, progress_note, estimated_minutes, git_branch, task_type } = parseArgs(args, updateTaskSchema);
|
|
322
|
+
const updates = { title, description, priority, status, progress_percentage, estimated_minutes, git_branch, task_type };
|
|
279
323
|
|
|
280
324
|
const api = getApiClient();
|
|
281
325
|
const response = await api.updateTask(task_id, {
|
|
@@ -310,6 +354,16 @@ export const updateTask: Handler = async (args, ctx) => {
|
|
|
310
354
|
},
|
|
311
355
|
};
|
|
312
356
|
}
|
|
357
|
+
if (response.error?.includes('branch_conflict')) {
|
|
358
|
+
return {
|
|
359
|
+
result: {
|
|
360
|
+
error: 'branch_conflict',
|
|
361
|
+
message: response.error,
|
|
362
|
+
conflicting_task_id: (response.data as { conflicting_task_id?: string })?.conflicting_task_id,
|
|
363
|
+
conflicting_task_title: (response.data as { conflicting_task_title?: string })?.conflicting_task_title,
|
|
364
|
+
},
|
|
365
|
+
};
|
|
366
|
+
}
|
|
313
367
|
throw new Error(`Failed to update task: ${response.error}`);
|
|
314
368
|
}
|
|
315
369
|
|
|
@@ -337,13 +391,7 @@ export const updateTask: Handler = async (args, ctx) => {
|
|
|
337
391
|
};
|
|
338
392
|
|
|
339
393
|
export const completeTask: Handler = async (args, ctx) => {
|
|
340
|
-
const { task_id, summary } = args
|
|
341
|
-
task_id: string;
|
|
342
|
-
summary?: string;
|
|
343
|
-
};
|
|
344
|
-
|
|
345
|
-
validateRequired(task_id, 'task_id');
|
|
346
|
-
validateUUID(task_id, 'task_id');
|
|
394
|
+
const { task_id, summary } = parseArgs(args, completeTaskSchema);
|
|
347
395
|
|
|
348
396
|
const api = getApiClient();
|
|
349
397
|
const response = await api.completeTask(task_id, {
|
|
@@ -386,10 +434,7 @@ export const completeTask: Handler = async (args, ctx) => {
|
|
|
386
434
|
};
|
|
387
435
|
|
|
388
436
|
export const deleteTask: Handler = async (args, ctx) => {
|
|
389
|
-
const { task_id } = args
|
|
390
|
-
|
|
391
|
-
validateRequired(task_id, 'task_id');
|
|
392
|
-
validateUUID(task_id, 'task_id');
|
|
437
|
+
const { task_id } = parseArgs(args, deleteTaskSchema);
|
|
393
438
|
|
|
394
439
|
const api = getApiClient();
|
|
395
440
|
const response = await api.deleteTask(task_id);
|
|
@@ -402,11 +447,7 @@ export const deleteTask: Handler = async (args, ctx) => {
|
|
|
402
447
|
};
|
|
403
448
|
|
|
404
449
|
export const addTaskReference: Handler = async (args, ctx) => {
|
|
405
|
-
const { task_id, url, label } = args
|
|
406
|
-
|
|
407
|
-
validateRequired(task_id, 'task_id');
|
|
408
|
-
validateUUID(task_id, 'task_id');
|
|
409
|
-
validateRequired(url, 'url');
|
|
450
|
+
const { task_id, url, label } = parseArgs(args, addTaskReferenceSchema);
|
|
410
451
|
|
|
411
452
|
const api = getApiClient();
|
|
412
453
|
const response = await api.addTaskReference(task_id, url, label);
|
|
@@ -427,11 +468,7 @@ export const addTaskReference: Handler = async (args, ctx) => {
|
|
|
427
468
|
};
|
|
428
469
|
|
|
429
470
|
export const removeTaskReference: Handler = async (args, ctx) => {
|
|
430
|
-
const { task_id, url } = args
|
|
431
|
-
|
|
432
|
-
validateRequired(task_id, 'task_id');
|
|
433
|
-
validateUUID(task_id, 'task_id');
|
|
434
|
-
validateRequired(url, 'url');
|
|
471
|
+
const { task_id, url } = parseArgs(args, removeTaskReferenceSchema);
|
|
435
472
|
|
|
436
473
|
const api = getApiClient();
|
|
437
474
|
const response = await api.removeTaskReference(task_id, url);
|
|
@@ -447,41 +484,33 @@ export const removeTaskReference: Handler = async (args, ctx) => {
|
|
|
447
484
|
};
|
|
448
485
|
|
|
449
486
|
export const batchUpdateTasks: Handler = async (args, ctx) => {
|
|
450
|
-
const { updates } = args
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
}
|
|
487
|
+
const { updates } = parseArgs(args, batchUpdateTasksSchema);
|
|
488
|
+
|
|
489
|
+
const typedUpdates = updates as Array<{
|
|
490
|
+
task_id: string;
|
|
491
|
+
status?: string;
|
|
492
|
+
progress_percentage?: number;
|
|
493
|
+
progress_note?: string;
|
|
494
|
+
priority?: number;
|
|
495
|
+
}>;
|
|
459
496
|
|
|
460
|
-
if (!
|
|
497
|
+
if (!Array.isArray(typedUpdates) || typedUpdates.length === 0) {
|
|
461
498
|
throw new ValidationError('updates must be a non-empty array', {
|
|
462
499
|
field: 'updates',
|
|
463
500
|
hint: 'Provide an array of task updates with at least one item',
|
|
464
501
|
});
|
|
465
502
|
}
|
|
466
503
|
|
|
467
|
-
if (
|
|
504
|
+
if (typedUpdates.length > 50) {
|
|
468
505
|
throw new ValidationError('Too many updates. Maximum is 50 per batch.', {
|
|
469
506
|
field: 'updates',
|
|
470
507
|
hint: 'Split your updates into smaller batches',
|
|
471
508
|
});
|
|
472
509
|
}
|
|
473
510
|
|
|
474
|
-
//
|
|
475
|
-
for (const update of updates) {
|
|
476
|
-
validateRequired(update.task_id, 'task_id');
|
|
477
|
-
validateUUID(update.task_id, 'task_id');
|
|
478
|
-
validateTaskStatus(update.status);
|
|
479
|
-
validatePriority(update.priority);
|
|
480
|
-
validateProgressPercentage(update.progress_percentage);
|
|
481
|
-
}
|
|
482
|
-
|
|
511
|
+
// Individual item validation happens at API level
|
|
483
512
|
const api = getApiClient();
|
|
484
|
-
const response = await api.batchUpdateTasks(
|
|
513
|
+
const response = await api.batchUpdateTasks(typedUpdates);
|
|
485
514
|
|
|
486
515
|
if (!response.ok) {
|
|
487
516
|
throw new Error(`Failed to batch update tasks: ${response.error}`);
|
|
@@ -490,42 +519,38 @@ export const batchUpdateTasks: Handler = async (args, ctx) => {
|
|
|
490
519
|
return {
|
|
491
520
|
result: {
|
|
492
521
|
success: response.data?.success || false,
|
|
493
|
-
total:
|
|
522
|
+
total: typedUpdates.length,
|
|
494
523
|
succeeded: response.data?.updated_count || 0,
|
|
495
524
|
},
|
|
496
525
|
};
|
|
497
526
|
};
|
|
498
527
|
|
|
499
528
|
export const batchCompleteTasks: Handler = async (args, ctx) => {
|
|
500
|
-
const { completions } = args
|
|
501
|
-
completions: Array<{
|
|
502
|
-
task_id: string;
|
|
503
|
-
summary?: string;
|
|
504
|
-
}>;
|
|
505
|
-
};
|
|
529
|
+
const { completions } = parseArgs(args, batchCompleteTasksSchema);
|
|
506
530
|
|
|
507
|
-
|
|
531
|
+
const typedCompletions = completions as Array<{
|
|
532
|
+
task_id: string;
|
|
533
|
+
summary?: string;
|
|
534
|
+
}>;
|
|
535
|
+
|
|
536
|
+
if (!Array.isArray(typedCompletions) || typedCompletions.length === 0) {
|
|
508
537
|
throw new ValidationError('completions must be a non-empty array', {
|
|
509
538
|
field: 'completions',
|
|
510
539
|
hint: 'Provide an array of task completions with at least one item',
|
|
511
540
|
});
|
|
512
541
|
}
|
|
513
542
|
|
|
514
|
-
if (
|
|
543
|
+
if (typedCompletions.length > 50) {
|
|
515
544
|
throw new ValidationError('Too many completions. Maximum is 50 per batch.', {
|
|
516
545
|
field: 'completions',
|
|
517
546
|
hint: 'Split your completions into smaller batches',
|
|
518
547
|
});
|
|
519
548
|
}
|
|
520
549
|
|
|
521
|
-
//
|
|
522
|
-
for (const completion of completions) {
|
|
523
|
-
validateRequired(completion.task_id, 'task_id');
|
|
524
|
-
validateUUID(completion.task_id, 'task_id');
|
|
525
|
-
}
|
|
550
|
+
// Individual item validation happens at API level
|
|
526
551
|
|
|
527
552
|
const api = getApiClient();
|
|
528
|
-
const response = await api.batchCompleteTasks(
|
|
553
|
+
const response = await api.batchCompleteTasks(typedCompletions);
|
|
529
554
|
|
|
530
555
|
if (!response.ok) {
|
|
531
556
|
throw new Error(`Failed to batch complete tasks: ${response.error}`);
|
|
@@ -535,9 +560,9 @@ export const batchCompleteTasks: Handler = async (args, ctx) => {
|
|
|
535
560
|
return {
|
|
536
561
|
result: {
|
|
537
562
|
success: data?.success || false,
|
|
538
|
-
total:
|
|
563
|
+
total: typedCompletions.length,
|
|
539
564
|
succeeded: data?.completed_count || 0,
|
|
540
|
-
failed:
|
|
565
|
+
failed: typedCompletions.length - (data?.completed_count || 0),
|
|
541
566
|
next_task: data?.next_task,
|
|
542
567
|
},
|
|
543
568
|
};
|
|
@@ -548,19 +573,7 @@ export const batchCompleteTasks: Handler = async (args, ctx) => {
|
|
|
548
573
|
// ============================================================================
|
|
549
574
|
|
|
550
575
|
export const addSubtask: Handler = async (args, ctx) => {
|
|
551
|
-
const { parent_task_id, title, description, priority, estimated_minutes } = args
|
|
552
|
-
parent_task_id: string;
|
|
553
|
-
title: string;
|
|
554
|
-
description?: string;
|
|
555
|
-
priority?: number;
|
|
556
|
-
estimated_minutes?: number;
|
|
557
|
-
};
|
|
558
|
-
|
|
559
|
-
validateRequired(parent_task_id, 'parent_task_id');
|
|
560
|
-
validateUUID(parent_task_id, 'parent_task_id');
|
|
561
|
-
validateRequired(title, 'title');
|
|
562
|
-
if (priority !== undefined) validatePriority(priority);
|
|
563
|
-
if (estimated_minutes !== undefined) validateEstimatedMinutes(estimated_minutes);
|
|
576
|
+
const { parent_task_id, title, description, priority, estimated_minutes } = parseArgs(args, addSubtaskSchema);
|
|
564
577
|
|
|
565
578
|
const api = getApiClient();
|
|
566
579
|
const response = await api.addSubtask(parent_task_id, {
|
|
@@ -593,14 +606,7 @@ export const addSubtask: Handler = async (args, ctx) => {
|
|
|
593
606
|
};
|
|
594
607
|
|
|
595
608
|
export const getSubtasks: Handler = async (args, ctx) => {
|
|
596
|
-
const { parent_task_id, status } = args
|
|
597
|
-
parent_task_id: string;
|
|
598
|
-
status?: string;
|
|
599
|
-
};
|
|
600
|
-
|
|
601
|
-
validateRequired(parent_task_id, 'parent_task_id');
|
|
602
|
-
validateUUID(parent_task_id, 'parent_task_id');
|
|
603
|
-
if (status) validateTaskStatus(status);
|
|
609
|
+
const { parent_task_id, status } = parseArgs(args, getSubtasksSchema);
|
|
604
610
|
|
|
605
611
|
const api = getApiClient();
|
|
606
612
|
const response = await api.getSubtasks(parent_task_id, status);
|
|
@@ -309,7 +309,7 @@ describe('validateTask', () => {
|
|
|
309
309
|
|
|
310
310
|
await expect(
|
|
311
311
|
validateTask({ task_id: VALID_UUID }, ctx)
|
|
312
|
-
).rejects.toThrow('
|
|
312
|
+
).rejects.toThrow('Missing required field: approved');
|
|
313
313
|
});
|
|
314
314
|
|
|
315
315
|
it('should throw error when task not found', async () => {
|
|
@@ -8,14 +8,27 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { Handler, HandlerRegistry } from './types.js';
|
|
11
|
-
import {
|
|
11
|
+
import { parseArgs, uuidValidator } from '../validators.js';
|
|
12
12
|
import { getApiClient } from '../api-client.js';
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
// Argument schemas for type-safe parsing
|
|
15
|
+
const getTasksAwaitingValidationSchema = {
|
|
16
|
+
project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const claimValidationSchema = {
|
|
20
|
+
task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
|
|
21
|
+
};
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
const validateTaskSchema = {
|
|
24
|
+
task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
|
|
25
|
+
approved: { type: 'boolean' as const, required: true as const },
|
|
26
|
+
validation_notes: { type: 'string' as const },
|
|
27
|
+
skip_pr_check: { type: 'boolean' as const },
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const getTasksAwaitingValidation: Handler = async (args, _ctx) => {
|
|
31
|
+
const { project_id } = parseArgs(args, getTasksAwaitingValidationSchema);
|
|
19
32
|
|
|
20
33
|
const apiClient = getApiClient();
|
|
21
34
|
const response = await apiClient.getTasksAwaitingValidation(project_id);
|
|
@@ -28,10 +41,7 @@ export const getTasksAwaitingValidation: Handler = async (args, ctx) => {
|
|
|
28
41
|
};
|
|
29
42
|
|
|
30
43
|
export const claimValidation: Handler = async (args, ctx) => {
|
|
31
|
-
const { task_id } = args
|
|
32
|
-
|
|
33
|
-
validateRequired(task_id, 'task_id');
|
|
34
|
-
validateUUID(task_id, 'task_id');
|
|
44
|
+
const { task_id } = parseArgs(args, claimValidationSchema);
|
|
35
45
|
|
|
36
46
|
const { session } = ctx;
|
|
37
47
|
const currentSessionId = session.currentSessionId;
|
|
@@ -47,19 +57,7 @@ export const claimValidation: Handler = async (args, ctx) => {
|
|
|
47
57
|
};
|
|
48
58
|
|
|
49
59
|
export const validateTask: Handler = async (args, ctx) => {
|
|
50
|
-
const { task_id,
|
|
51
|
-
task_id: string;
|
|
52
|
-
validation_notes?: string;
|
|
53
|
-
approved: boolean;
|
|
54
|
-
skip_pr_check?: boolean;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
validateRequired(task_id, 'task_id');
|
|
58
|
-
validateUUID(task_id, 'task_id');
|
|
59
|
-
|
|
60
|
-
if (approved === undefined) {
|
|
61
|
-
throw new Error('approved is required');
|
|
62
|
-
}
|
|
60
|
+
const { task_id, approved, validation_notes, skip_pr_check } = parseArgs(args, validateTaskSchema);
|
|
63
61
|
|
|
64
62
|
const { session } = ctx;
|
|
65
63
|
const currentSessionId = session.currentSessionId;
|