@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.
- package/README.md +1 -1
- package/dist/api-client.d.ts +120 -2
- package/dist/api-client.js +51 -5
- package/dist/handlers/bodies-of-work.js +84 -50
- package/dist/handlers/cost.js +62 -54
- package/dist/handlers/decisions.js +29 -16
- package/dist/handlers/deployment.js +114 -107
- package/dist/handlers/discovery.d.ts +3 -0
- package/dist/handlers/discovery.js +55 -657
- package/dist/handlers/fallback.js +42 -28
- package/dist/handlers/file-checkouts.d.ts +18 -0
- package/dist/handlers/file-checkouts.js +101 -0
- package/dist/handlers/findings.d.ts +14 -1
- package/dist/handlers/findings.js +104 -28
- 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 +52 -15
- package/dist/handlers/sprints.js +78 -65
- package/dist/handlers/tasks.js +135 -74
- package/dist/handlers/tool-docs.d.ts +4 -3
- package/dist/handlers/tool-docs.js +252 -5
- package/dist/handlers/validation.js +30 -14
- package/dist/index.js +25 -7
- package/dist/tools.js +417 -4
- package/package.json +1 -1
- package/src/api-client.ts +161 -8
- package/src/handlers/__test-setup__.ts +12 -0
- package/src/handlers/bodies-of-work.ts +127 -111
- 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 +144 -190
- package/src/handlers/discovery.test.ts +4 -5
- package/src/handlers/discovery.ts +60 -746
- package/src/handlers/fallback.test.ts +78 -0
- package/src/handlers/fallback.ts +51 -38
- package/src/handlers/file-checkouts.test.ts +477 -0
- package/src/handlers/file-checkouts.ts +127 -0
- package/src/handlers/findings.test.ts +274 -2
- package/src/handlers/findings.ts +123 -57
- 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.test.ts +37 -2
- package/src/handlers/session.ts +64 -21
- package/src/handlers/sprints.ts +114 -134
- package/src/handlers/tasks.test.ts +61 -0
- package/src/handlers/tasks.ts +170 -139
- package/src/handlers/tool-docs.ts +1024 -0
- package/src/handlers/validation.test.ts +53 -1
- package/src/handlers/validation.ts +32 -21
- package/src/index.ts +25 -7
- package/src/tools.ts +417 -4
- 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/knowledge.ts +0 -230
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,20 +354,44 @@ 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
|
|
|
316
|
-
|
|
370
|
+
// Build result - include git workflow info when transitioning to in_progress
|
|
371
|
+
const data = response.data;
|
|
372
|
+
const result: Record<string, unknown> = { success: true, task_id };
|
|
373
|
+
|
|
374
|
+
if (data?.git_workflow) {
|
|
375
|
+
result.git_workflow = data.git_workflow;
|
|
376
|
+
}
|
|
377
|
+
if (data?.worktree_setup) {
|
|
378
|
+
result.worktree_setup = data.worktree_setup;
|
|
379
|
+
}
|
|
380
|
+
if (data?.next_step) {
|
|
381
|
+
result.next_step = data.next_step;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Warn if transitioning to in_progress without git_branch
|
|
385
|
+
if (updates.status === 'in_progress' && !updates.git_branch) {
|
|
386
|
+
result.warning = 'git_branch not set. For multi-agent collaboration, set git_branch when marking in_progress to track your worktree.';
|
|
387
|
+
result.hint = 'Call update_task again with git_branch parameter after creating your worktree.';
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return { result };
|
|
317
391
|
};
|
|
318
392
|
|
|
319
393
|
export const completeTask: Handler = async (args, ctx) => {
|
|
320
|
-
const { task_id, summary } = args
|
|
321
|
-
task_id: string;
|
|
322
|
-
summary?: string;
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
validateRequired(task_id, 'task_id');
|
|
326
|
-
validateUUID(task_id, 'task_id');
|
|
394
|
+
const { task_id, summary } = parseArgs(args, completeTaskSchema);
|
|
327
395
|
|
|
328
396
|
const api = getApiClient();
|
|
329
397
|
const response = await api.completeTask(task_id, {
|
|
@@ -353,6 +421,11 @@ export const completeTask: Handler = async (args, ctx) => {
|
|
|
353
421
|
result.context = data.context;
|
|
354
422
|
}
|
|
355
423
|
|
|
424
|
+
// Pass through warnings (e.g., missing git_branch)
|
|
425
|
+
if (data.warnings) {
|
|
426
|
+
result.warnings = data.warnings;
|
|
427
|
+
}
|
|
428
|
+
|
|
356
429
|
// Git workflow instructions are already in API response but we need to fetch
|
|
357
430
|
// task details if we want to include them (API should return these)
|
|
358
431
|
result.next_action = data.next_action;
|
|
@@ -361,10 +434,7 @@ export const completeTask: Handler = async (args, ctx) => {
|
|
|
361
434
|
};
|
|
362
435
|
|
|
363
436
|
export const deleteTask: Handler = async (args, ctx) => {
|
|
364
|
-
const { task_id } = args
|
|
365
|
-
|
|
366
|
-
validateRequired(task_id, 'task_id');
|
|
367
|
-
validateUUID(task_id, 'task_id');
|
|
437
|
+
const { task_id } = parseArgs(args, deleteTaskSchema);
|
|
368
438
|
|
|
369
439
|
const api = getApiClient();
|
|
370
440
|
const response = await api.deleteTask(task_id);
|
|
@@ -377,11 +447,7 @@ export const deleteTask: Handler = async (args, ctx) => {
|
|
|
377
447
|
};
|
|
378
448
|
|
|
379
449
|
export const addTaskReference: Handler = async (args, ctx) => {
|
|
380
|
-
const { task_id, url, label } = args
|
|
381
|
-
|
|
382
|
-
validateRequired(task_id, 'task_id');
|
|
383
|
-
validateUUID(task_id, 'task_id');
|
|
384
|
-
validateRequired(url, 'url');
|
|
450
|
+
const { task_id, url, label } = parseArgs(args, addTaskReferenceSchema);
|
|
385
451
|
|
|
386
452
|
const api = getApiClient();
|
|
387
453
|
const response = await api.addTaskReference(task_id, url, label);
|
|
@@ -402,11 +468,7 @@ export const addTaskReference: Handler = async (args, ctx) => {
|
|
|
402
468
|
};
|
|
403
469
|
|
|
404
470
|
export const removeTaskReference: Handler = async (args, ctx) => {
|
|
405
|
-
const { task_id, url } = args
|
|
406
|
-
|
|
407
|
-
validateRequired(task_id, 'task_id');
|
|
408
|
-
validateUUID(task_id, 'task_id');
|
|
409
|
-
validateRequired(url, 'url');
|
|
471
|
+
const { task_id, url } = parseArgs(args, removeTaskReferenceSchema);
|
|
410
472
|
|
|
411
473
|
const api = getApiClient();
|
|
412
474
|
const response = await api.removeTaskReference(task_id, url);
|
|
@@ -422,41 +484,33 @@ export const removeTaskReference: Handler = async (args, ctx) => {
|
|
|
422
484
|
};
|
|
423
485
|
|
|
424
486
|
export const batchUpdateTasks: Handler = async (args, ctx) => {
|
|
425
|
-
const { updates } = args
|
|
426
|
-
updates: Array<{
|
|
427
|
-
task_id: string;
|
|
428
|
-
status?: string;
|
|
429
|
-
progress_percentage?: number;
|
|
430
|
-
progress_note?: string;
|
|
431
|
-
priority?: number;
|
|
432
|
-
}>;
|
|
433
|
-
};
|
|
487
|
+
const { updates } = parseArgs(args, batchUpdateTasksSchema);
|
|
434
488
|
|
|
435
|
-
|
|
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
|
+
}>;
|
|
496
|
+
|
|
497
|
+
if (!Array.isArray(typedUpdates) || typedUpdates.length === 0) {
|
|
436
498
|
throw new ValidationError('updates must be a non-empty array', {
|
|
437
499
|
field: 'updates',
|
|
438
500
|
hint: 'Provide an array of task updates with at least one item',
|
|
439
501
|
});
|
|
440
502
|
}
|
|
441
503
|
|
|
442
|
-
if (
|
|
504
|
+
if (typedUpdates.length > 50) {
|
|
443
505
|
throw new ValidationError('Too many updates. Maximum is 50 per batch.', {
|
|
444
506
|
field: 'updates',
|
|
445
507
|
hint: 'Split your updates into smaller batches',
|
|
446
508
|
});
|
|
447
509
|
}
|
|
448
510
|
|
|
449
|
-
//
|
|
450
|
-
for (const update of updates) {
|
|
451
|
-
validateRequired(update.task_id, 'task_id');
|
|
452
|
-
validateUUID(update.task_id, 'task_id');
|
|
453
|
-
validateTaskStatus(update.status);
|
|
454
|
-
validatePriority(update.priority);
|
|
455
|
-
validateProgressPercentage(update.progress_percentage);
|
|
456
|
-
}
|
|
457
|
-
|
|
511
|
+
// Individual item validation happens at API level
|
|
458
512
|
const api = getApiClient();
|
|
459
|
-
const response = await api.batchUpdateTasks(
|
|
513
|
+
const response = await api.batchUpdateTasks(typedUpdates);
|
|
460
514
|
|
|
461
515
|
if (!response.ok) {
|
|
462
516
|
throw new Error(`Failed to batch update tasks: ${response.error}`);
|
|
@@ -465,42 +519,38 @@ export const batchUpdateTasks: Handler = async (args, ctx) => {
|
|
|
465
519
|
return {
|
|
466
520
|
result: {
|
|
467
521
|
success: response.data?.success || false,
|
|
468
|
-
total:
|
|
522
|
+
total: typedUpdates.length,
|
|
469
523
|
succeeded: response.data?.updated_count || 0,
|
|
470
524
|
},
|
|
471
525
|
};
|
|
472
526
|
};
|
|
473
527
|
|
|
474
528
|
export const batchCompleteTasks: Handler = async (args, ctx) => {
|
|
475
|
-
const { completions } = args
|
|
476
|
-
completions: Array<{
|
|
477
|
-
task_id: string;
|
|
478
|
-
summary?: string;
|
|
479
|
-
}>;
|
|
480
|
-
};
|
|
529
|
+
const { completions } = parseArgs(args, batchCompleteTasksSchema);
|
|
481
530
|
|
|
482
|
-
|
|
531
|
+
const typedCompletions = completions as Array<{
|
|
532
|
+
task_id: string;
|
|
533
|
+
summary?: string;
|
|
534
|
+
}>;
|
|
535
|
+
|
|
536
|
+
if (!Array.isArray(typedCompletions) || typedCompletions.length === 0) {
|
|
483
537
|
throw new ValidationError('completions must be a non-empty array', {
|
|
484
538
|
field: 'completions',
|
|
485
539
|
hint: 'Provide an array of task completions with at least one item',
|
|
486
540
|
});
|
|
487
541
|
}
|
|
488
542
|
|
|
489
|
-
if (
|
|
543
|
+
if (typedCompletions.length > 50) {
|
|
490
544
|
throw new ValidationError('Too many completions. Maximum is 50 per batch.', {
|
|
491
545
|
field: 'completions',
|
|
492
546
|
hint: 'Split your completions into smaller batches',
|
|
493
547
|
});
|
|
494
548
|
}
|
|
495
549
|
|
|
496
|
-
//
|
|
497
|
-
for (const completion of completions) {
|
|
498
|
-
validateRequired(completion.task_id, 'task_id');
|
|
499
|
-
validateUUID(completion.task_id, 'task_id');
|
|
500
|
-
}
|
|
550
|
+
// Individual item validation happens at API level
|
|
501
551
|
|
|
502
552
|
const api = getApiClient();
|
|
503
|
-
const response = await api.batchCompleteTasks(
|
|
553
|
+
const response = await api.batchCompleteTasks(typedCompletions);
|
|
504
554
|
|
|
505
555
|
if (!response.ok) {
|
|
506
556
|
throw new Error(`Failed to batch complete tasks: ${response.error}`);
|
|
@@ -510,9 +560,9 @@ export const batchCompleteTasks: Handler = async (args, ctx) => {
|
|
|
510
560
|
return {
|
|
511
561
|
result: {
|
|
512
562
|
success: data?.success || false,
|
|
513
|
-
total:
|
|
563
|
+
total: typedCompletions.length,
|
|
514
564
|
succeeded: data?.completed_count || 0,
|
|
515
|
-
failed:
|
|
565
|
+
failed: typedCompletions.length - (data?.completed_count || 0),
|
|
516
566
|
next_task: data?.next_task,
|
|
517
567
|
},
|
|
518
568
|
};
|
|
@@ -523,19 +573,7 @@ export const batchCompleteTasks: Handler = async (args, ctx) => {
|
|
|
523
573
|
// ============================================================================
|
|
524
574
|
|
|
525
575
|
export const addSubtask: Handler = async (args, ctx) => {
|
|
526
|
-
const { parent_task_id, title, description, priority, estimated_minutes } = args
|
|
527
|
-
parent_task_id: string;
|
|
528
|
-
title: string;
|
|
529
|
-
description?: string;
|
|
530
|
-
priority?: number;
|
|
531
|
-
estimated_minutes?: number;
|
|
532
|
-
};
|
|
533
|
-
|
|
534
|
-
validateRequired(parent_task_id, 'parent_task_id');
|
|
535
|
-
validateUUID(parent_task_id, 'parent_task_id');
|
|
536
|
-
validateRequired(title, 'title');
|
|
537
|
-
if (priority !== undefined) validatePriority(priority);
|
|
538
|
-
if (estimated_minutes !== undefined) validateEstimatedMinutes(estimated_minutes);
|
|
576
|
+
const { parent_task_id, title, description, priority, estimated_minutes } = parseArgs(args, addSubtaskSchema);
|
|
539
577
|
|
|
540
578
|
const api = getApiClient();
|
|
541
579
|
const response = await api.addSubtask(parent_task_id, {
|
|
@@ -568,14 +606,7 @@ export const addSubtask: Handler = async (args, ctx) => {
|
|
|
568
606
|
};
|
|
569
607
|
|
|
570
608
|
export const getSubtasks: Handler = async (args, ctx) => {
|
|
571
|
-
const { parent_task_id, status } = args
|
|
572
|
-
parent_task_id: string;
|
|
573
|
-
status?: string;
|
|
574
|
-
};
|
|
575
|
-
|
|
576
|
-
validateRequired(parent_task_id, 'parent_task_id');
|
|
577
|
-
validateUUID(parent_task_id, 'parent_task_id');
|
|
578
|
-
if (status) validateTaskStatus(status);
|
|
609
|
+
const { parent_task_id, status } = parseArgs(args, getSubtasksSchema);
|
|
579
610
|
|
|
580
611
|
const api = getApiClient();
|
|
581
612
|
const response = await api.getSubtasks(parent_task_id, status);
|