@vibescope/mcp-server 0.2.1 → 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 +187 -0
- package/dist/api-client.js +48 -0
- package/dist/handlers/blockers.js +9 -8
- package/dist/handlers/bodies-of-work.js +14 -14
- 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 +54 -0
- package/dist/handlers/decisions.js +3 -3
- package/dist/handlers/deployment.js +35 -19
- package/dist/handlers/discovery.d.ts +7 -0
- package/dist/handlers/discovery.js +61 -2
- package/dist/handlers/fallback.js +5 -4
- package/dist/handlers/file-checkouts.d.ts +2 -0
- package/dist/handlers/file-checkouts.js +38 -6
- package/dist/handlers/findings.js +13 -12
- package/dist/handlers/git-issues.js +4 -4
- package/dist/handlers/ideas.js +5 -5
- package/dist/handlers/index.d.ts +1 -0
- package/dist/handlers/index.js +3 -0
- package/dist/handlers/milestones.js +5 -5
- package/dist/handlers/organizations.js +13 -13
- package/dist/handlers/progress.js +2 -2
- package/dist/handlers/project.js +6 -6
- package/dist/handlers/requests.js +3 -3
- package/dist/handlers/session.js +28 -9
- package/dist/handlers/sprints.js +17 -17
- package/dist/handlers/tasks.d.ts +2 -0
- package/dist/handlers/tasks.js +78 -20
- package/dist/handlers/types.d.ts +64 -2
- package/dist/handlers/types.js +48 -1
- package/dist/handlers/validation.js +3 -3
- 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 +298 -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 +231 -0
- package/src/handlers/__test-setup__.ts +9 -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 +14 -14
- package/src/handlers/connectors.test.ts +834 -0
- package/src/handlers/connectors.ts +229 -0
- package/src/handlers/cost.ts +66 -0
- package/src/handlers/decisions.test.ts +34 -25
- package/src/handlers/decisions.ts +3 -3
- package/src/handlers/deployment.ts +39 -19
- package/src/handlers/discovery.ts +61 -2
- package/src/handlers/fallback.test.ts +26 -22
- package/src/handlers/fallback.ts +5 -4
- package/src/handlers/file-checkouts.test.ts +242 -49
- package/src/handlers/file-checkouts.ts +44 -6
- package/src/handlers/findings.test.ts +38 -24
- package/src/handlers/findings.ts +13 -12
- package/src/handlers/git-issues.test.ts +51 -43
- package/src/handlers/git-issues.ts +4 -4
- package/src/handlers/ideas.test.ts +28 -23
- package/src/handlers/ideas.ts +5 -5
- package/src/handlers/index.ts +3 -0
- package/src/handlers/milestones.test.ts +33 -28
- package/src/handlers/milestones.ts +5 -5
- package/src/handlers/organizations.test.ts +104 -83
- package/src/handlers/organizations.ts +13 -13
- package/src/handlers/progress.test.ts +20 -14
- package/src/handlers/progress.ts +2 -2
- package/src/handlers/project.test.ts +34 -27
- package/src/handlers/project.ts +6 -6
- package/src/handlers/requests.test.ts +27 -18
- package/src/handlers/requests.ts +3 -3
- package/src/handlers/session.test.ts +47 -0
- package/src/handlers/session.ts +32 -9
- package/src/handlers/sprints.test.ts +71 -50
- package/src/handlers/sprints.ts +17 -17
- package/src/handlers/tasks.test.ts +77 -15
- package/src/handlers/tasks.ts +90 -21
- 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 +3 -3
- 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 +298 -9
- package/src/utils.test.ts +2 -2
- package/src/utils.ts +17 -0
package/dist/handlers/sprints.js
CHANGED
|
@@ -88,13 +88,13 @@ export const createSprint = async (args, ctx) => {
|
|
|
88
88
|
const startDateObj = new Date(start_date);
|
|
89
89
|
const endDateObj = new Date(end_date);
|
|
90
90
|
if (isNaN(startDateObj.getTime())) {
|
|
91
|
-
|
|
91
|
+
return { result: { error: 'Invalid start_date format. Use YYYY-MM-DD' }, isError: true };
|
|
92
92
|
}
|
|
93
93
|
if (isNaN(endDateObj.getTime())) {
|
|
94
|
-
|
|
94
|
+
return { result: { error: 'Invalid end_date format. Use YYYY-MM-DD' }, isError: true };
|
|
95
95
|
}
|
|
96
96
|
if (endDateObj < startDateObj) {
|
|
97
|
-
|
|
97
|
+
return { result: { error: 'end_date must be on or after start_date' }, isError: true };
|
|
98
98
|
}
|
|
99
99
|
const { session } = ctx;
|
|
100
100
|
const apiClient = getApiClient();
|
|
@@ -113,7 +113,7 @@ export const createSprint = async (args, ctx) => {
|
|
|
113
113
|
instance_id: session.instanceId
|
|
114
114
|
});
|
|
115
115
|
if (!response.ok) {
|
|
116
|
-
|
|
116
|
+
return { result: { error: response.error || 'Failed to create sprint' }, isError: true };
|
|
117
117
|
}
|
|
118
118
|
return {
|
|
119
119
|
result: {
|
|
@@ -134,13 +134,13 @@ export const updateSprint = async (args, ctx) => {
|
|
|
134
134
|
if (start_date) {
|
|
135
135
|
const startDateObj = new Date(start_date);
|
|
136
136
|
if (isNaN(startDateObj.getTime())) {
|
|
137
|
-
|
|
137
|
+
return { result: { error: 'Invalid start_date format. Use YYYY-MM-DD' }, isError: true };
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
if (end_date) {
|
|
141
141
|
const endDateObj = new Date(end_date);
|
|
142
142
|
if (isNaN(endDateObj.getTime())) {
|
|
143
|
-
|
|
143
|
+
return { result: { error: 'Invalid end_date format. Use YYYY-MM-DD' }, isError: true };
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
const apiClient = getApiClient();
|
|
@@ -155,7 +155,7 @@ export const updateSprint = async (args, ctx) => {
|
|
|
155
155
|
deploy_version_bump,
|
|
156
156
|
});
|
|
157
157
|
if (!response.ok) {
|
|
158
|
-
|
|
158
|
+
return { result: { error: response.error || 'Failed to update sprint' }, isError: true };
|
|
159
159
|
}
|
|
160
160
|
return { result: { success: true, sprint_id } };
|
|
161
161
|
};
|
|
@@ -165,7 +165,7 @@ export const getSprint = async (args, ctx) => {
|
|
|
165
165
|
// Response type varies based on summary_only
|
|
166
166
|
const response = await apiClient.proxy('get_sprint', { sprint_id, summary_only });
|
|
167
167
|
if (!response.ok) {
|
|
168
|
-
|
|
168
|
+
return { result: { error: response.error || 'Failed to get sprint' }, isError: true };
|
|
169
169
|
}
|
|
170
170
|
return { result: response.data };
|
|
171
171
|
};
|
|
@@ -179,7 +179,7 @@ export const getSprints = async (args, ctx) => {
|
|
|
179
179
|
offset,
|
|
180
180
|
});
|
|
181
181
|
if (!response.ok) {
|
|
182
|
-
|
|
182
|
+
return { result: { error: response.error || 'Failed to fetch sprints' }, isError: true };
|
|
183
183
|
}
|
|
184
184
|
return { result: response.data };
|
|
185
185
|
};
|
|
@@ -190,7 +190,7 @@ export const deleteSprint = async (args, ctx) => {
|
|
|
190
190
|
sprint_id
|
|
191
191
|
});
|
|
192
192
|
if (!response.ok) {
|
|
193
|
-
|
|
193
|
+
return { result: { error: response.error || 'Failed to delete sprint' }, isError: true };
|
|
194
194
|
}
|
|
195
195
|
return { result: { success: true, message: 'Sprint deleted. Tasks are preserved.' } };
|
|
196
196
|
};
|
|
@@ -199,7 +199,7 @@ export const startSprint = async (args, ctx) => {
|
|
|
199
199
|
const apiClient = getApiClient();
|
|
200
200
|
const response = await apiClient.proxy('start_sprint', { sprint_id });
|
|
201
201
|
if (!response.ok) {
|
|
202
|
-
|
|
202
|
+
return { result: { error: response.error || 'Failed to start sprint' }, isError: true };
|
|
203
203
|
}
|
|
204
204
|
return { result: response.data };
|
|
205
205
|
};
|
|
@@ -212,14 +212,14 @@ export const completeSprint = async (args, ctx) => {
|
|
|
212
212
|
skip_retrospective,
|
|
213
213
|
});
|
|
214
214
|
if (!response.ok) {
|
|
215
|
-
|
|
215
|
+
return { result: { error: response.error || 'Failed to complete sprint' }, isError: true };
|
|
216
216
|
}
|
|
217
217
|
return { result: response.data };
|
|
218
218
|
};
|
|
219
219
|
export const addTaskToSprint = async (args, ctx) => {
|
|
220
220
|
const { sprint_id, task_id, story_points, phase } = parseArgs(args, addTaskToSprintSchema);
|
|
221
221
|
if (story_points !== undefined && (story_points < 0 || !Number.isInteger(story_points))) {
|
|
222
|
-
|
|
222
|
+
return { result: { error: 'story_points must be a non-negative integer' }, isError: true };
|
|
223
223
|
}
|
|
224
224
|
const apiClient = getApiClient();
|
|
225
225
|
const response = await apiClient.proxy('add_task_to_sprint', {
|
|
@@ -229,7 +229,7 @@ export const addTaskToSprint = async (args, ctx) => {
|
|
|
229
229
|
phase: phase || 'core',
|
|
230
230
|
});
|
|
231
231
|
if (!response.ok) {
|
|
232
|
-
|
|
232
|
+
return { result: { error: response.error || 'Failed to add task to sprint' }, isError: true };
|
|
233
233
|
}
|
|
234
234
|
return { result: response.data };
|
|
235
235
|
};
|
|
@@ -241,7 +241,7 @@ export const removeTaskFromSprint = async (args, ctx) => {
|
|
|
241
241
|
task_id,
|
|
242
242
|
});
|
|
243
243
|
if (!response.ok) {
|
|
244
|
-
|
|
244
|
+
return { result: { error: response.error || 'Failed to remove task from sprint' }, isError: true };
|
|
245
245
|
}
|
|
246
246
|
return { result: response.data };
|
|
247
247
|
};
|
|
@@ -253,7 +253,7 @@ export const getSprintBacklog = async (args, ctx) => {
|
|
|
253
253
|
sprint_id,
|
|
254
254
|
});
|
|
255
255
|
if (!response.ok) {
|
|
256
|
-
|
|
256
|
+
return { result: { error: response.error || 'Failed to get sprint backlog' }, isError: true };
|
|
257
257
|
}
|
|
258
258
|
return { result: response.data };
|
|
259
259
|
};
|
|
@@ -265,7 +265,7 @@ export const getSprintVelocity = async (args, ctx) => {
|
|
|
265
265
|
limit: Math.min(limit ?? 10, 50),
|
|
266
266
|
});
|
|
267
267
|
if (!response.ok) {
|
|
268
|
-
|
|
268
|
+
return { result: { error: response.error || 'Failed to get sprint velocity' }, isError: true };
|
|
269
269
|
}
|
|
270
270
|
return { result: response.data };
|
|
271
271
|
};
|
package/dist/handlers/tasks.d.ts
CHANGED
|
@@ -42,6 +42,8 @@ export declare const batchUpdateTasks: Handler;
|
|
|
42
42
|
export declare const batchCompleteTasks: Handler;
|
|
43
43
|
export declare const addSubtask: Handler;
|
|
44
44
|
export declare const getSubtasks: Handler;
|
|
45
|
+
export declare const getStaleWorktrees: Handler;
|
|
46
|
+
export declare const clearWorktreePath: Handler;
|
|
45
47
|
/**
|
|
46
48
|
* Task handlers registry
|
|
47
49
|
*/
|
package/dist/handlers/tasks.js
CHANGED
|
@@ -56,7 +56,9 @@ const updateTaskSchema = {
|
|
|
56
56
|
progress_note: { type: 'string' },
|
|
57
57
|
estimated_minutes: { type: 'number', validate: minutesValidator },
|
|
58
58
|
git_branch: { type: 'string' },
|
|
59
|
+
worktree_path: { type: 'string' },
|
|
59
60
|
task_type: { type: 'string', validate: createEnumValidator(VALID_TASK_TYPES) },
|
|
61
|
+
skip_worktree_requirement: { type: 'boolean', default: false },
|
|
60
62
|
};
|
|
61
63
|
const completeTaskSchema = {
|
|
62
64
|
task_id: { type: 'string', required: true, validate: uuidValidator },
|
|
@@ -153,7 +155,7 @@ export const getTasks = async (args, ctx) => {
|
|
|
153
155
|
include_metadata,
|
|
154
156
|
});
|
|
155
157
|
if (!response.ok) {
|
|
156
|
-
|
|
158
|
+
return { result: { error: response.error || 'Failed to fetch tasks' }, isError: true };
|
|
157
159
|
}
|
|
158
160
|
return {
|
|
159
161
|
result: {
|
|
@@ -168,7 +170,7 @@ export const getNextTask = async (args, ctx) => {
|
|
|
168
170
|
const api = getApiClient();
|
|
169
171
|
const response = await api.getNextTask(project_id, ctx.session.currentSessionId || undefined);
|
|
170
172
|
if (!response.ok) {
|
|
171
|
-
|
|
173
|
+
return { result: { error: response.error || 'Failed to get next task' }, isError: true };
|
|
172
174
|
}
|
|
173
175
|
const data = response.data;
|
|
174
176
|
if (!data) {
|
|
@@ -219,7 +221,7 @@ export const addTask = async (args, ctx) => {
|
|
|
219
221
|
task_type,
|
|
220
222
|
});
|
|
221
223
|
if (!response.ok) {
|
|
222
|
-
|
|
224
|
+
return { result: { error: response.error || 'Failed to add task' }, isError: true };
|
|
223
225
|
}
|
|
224
226
|
const data = response.data;
|
|
225
227
|
const result = {
|
|
@@ -234,8 +236,24 @@ export const addTask = async (args, ctx) => {
|
|
|
234
236
|
return { result };
|
|
235
237
|
};
|
|
236
238
|
export const updateTask = async (args, ctx) => {
|
|
237
|
-
const { task_id, title, description, priority, status, progress_percentage, progress_note, estimated_minutes, git_branch, task_type } = parseArgs(args, updateTaskSchema);
|
|
238
|
-
const updates = { title, description, priority, status, progress_percentage, estimated_minutes, git_branch, task_type };
|
|
239
|
+
const { task_id, title, description, priority, status, progress_percentage, progress_note, estimated_minutes, git_branch, worktree_path, task_type, skip_worktree_requirement } = parseArgs(args, updateTaskSchema);
|
|
240
|
+
const updates = { title, description, priority, status, progress_percentage, estimated_minutes, git_branch, worktree_path, task_type };
|
|
241
|
+
// Enforce worktree creation: require git_branch when marking task as in_progress
|
|
242
|
+
// This ensures multi-agent collaboration works properly with isolated worktrees
|
|
243
|
+
if (status === 'in_progress' && !git_branch && !skip_worktree_requirement) {
|
|
244
|
+
return {
|
|
245
|
+
result: {
|
|
246
|
+
error: 'worktree_required',
|
|
247
|
+
message: 'git_branch is required when marking a task as in_progress. Create a worktree first and provide the branch name.',
|
|
248
|
+
hint: 'Create a worktree with: git worktree add ../PROJECT-task-TASKID -b feature/TASKID-description BASE_BRANCH, then call update_task with both status and git_branch parameters.',
|
|
249
|
+
worktree_example: {
|
|
250
|
+
command: `git worktree add ../worktree-${task_id.substring(0, 8)} -b feature/${task_id.substring(0, 8)}-task develop`,
|
|
251
|
+
then: `update_task(task_id: "${task_id}", status: "in_progress", git_branch: "feature/${task_id.substring(0, 8)}-task")`,
|
|
252
|
+
},
|
|
253
|
+
skip_option: 'If this project does not use git branching (trunk-based or no git workflow), pass skip_worktree_requirement: true',
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
}
|
|
239
257
|
const api = getApiClient();
|
|
240
258
|
const response = await api.updateTask(task_id, {
|
|
241
259
|
...updates,
|
|
@@ -278,7 +296,7 @@ export const updateTask = async (args, ctx) => {
|
|
|
278
296
|
},
|
|
279
297
|
};
|
|
280
298
|
}
|
|
281
|
-
|
|
299
|
+
return { result: { error: response.error || 'Failed to update task' }, isError: true };
|
|
282
300
|
}
|
|
283
301
|
// Build result - include git workflow info when transitioning to in_progress
|
|
284
302
|
const data = response.data;
|
|
@@ -292,11 +310,6 @@ export const updateTask = async (args, ctx) => {
|
|
|
292
310
|
if (data?.next_step) {
|
|
293
311
|
result.next_step = data.next_step;
|
|
294
312
|
}
|
|
295
|
-
// Warn if transitioning to in_progress without git_branch
|
|
296
|
-
if (updates.status === 'in_progress' && !updates.git_branch) {
|
|
297
|
-
result.warning = 'git_branch not set. For multi-agent collaboration, set git_branch when marking in_progress to track your worktree.';
|
|
298
|
-
result.hint = 'Call update_task again with git_branch parameter after creating your worktree.';
|
|
299
|
-
}
|
|
300
313
|
return { result };
|
|
301
314
|
};
|
|
302
315
|
export const completeTask = async (args, ctx) => {
|
|
@@ -307,11 +320,11 @@ export const completeTask = async (args, ctx) => {
|
|
|
307
320
|
session_id: ctx.session.currentSessionId || undefined,
|
|
308
321
|
});
|
|
309
322
|
if (!response.ok) {
|
|
310
|
-
|
|
323
|
+
return { result: { error: response.error || 'Failed to complete task' }, isError: true };
|
|
311
324
|
}
|
|
312
325
|
const data = response.data;
|
|
313
326
|
if (!data) {
|
|
314
|
-
|
|
327
|
+
return { result: { error: 'No response data from complete task' }, isError: true };
|
|
315
328
|
}
|
|
316
329
|
// Build result matching expected format
|
|
317
330
|
const result = {
|
|
@@ -338,7 +351,7 @@ export const deleteTask = async (args, ctx) => {
|
|
|
338
351
|
const api = getApiClient();
|
|
339
352
|
const response = await api.deleteTask(task_id);
|
|
340
353
|
if (!response.ok) {
|
|
341
|
-
|
|
354
|
+
return { result: { error: response.error || 'Failed to delete task' }, isError: true };
|
|
342
355
|
}
|
|
343
356
|
return { result: { success: true, deleted_id: task_id } };
|
|
344
357
|
};
|
|
@@ -350,7 +363,7 @@ export const addTaskReference = async (args, ctx) => {
|
|
|
350
363
|
if (response.error?.includes('already exists')) {
|
|
351
364
|
return { result: { success: false, error: 'Reference with this URL already exists' } };
|
|
352
365
|
}
|
|
353
|
-
|
|
366
|
+
return { result: { error: response.error || 'Failed to add reference' }, isError: true };
|
|
354
367
|
}
|
|
355
368
|
return {
|
|
356
369
|
result: {
|
|
@@ -367,7 +380,7 @@ export const removeTaskReference = async (args, ctx) => {
|
|
|
367
380
|
if (response.error?.includes('not found')) {
|
|
368
381
|
return { result: { success: false, error: 'Reference with this URL not found' } };
|
|
369
382
|
}
|
|
370
|
-
|
|
383
|
+
return { result: { error: response.error || 'Failed to remove reference' }, isError: true };
|
|
371
384
|
}
|
|
372
385
|
return { result: { success: true } };
|
|
373
386
|
};
|
|
@@ -390,7 +403,7 @@ export const batchUpdateTasks = async (args, ctx) => {
|
|
|
390
403
|
const api = getApiClient();
|
|
391
404
|
const response = await api.batchUpdateTasks(typedUpdates);
|
|
392
405
|
if (!response.ok) {
|
|
393
|
-
|
|
406
|
+
return { result: { error: response.error || 'Failed to batch update tasks' }, isError: true };
|
|
394
407
|
}
|
|
395
408
|
return {
|
|
396
409
|
result: {
|
|
@@ -419,7 +432,7 @@ export const batchCompleteTasks = async (args, ctx) => {
|
|
|
419
432
|
const api = getApiClient();
|
|
420
433
|
const response = await api.batchCompleteTasks(typedCompletions);
|
|
421
434
|
if (!response.ok) {
|
|
422
|
-
|
|
435
|
+
return { result: { error: response.error || 'Failed to batch complete tasks' }, isError: true };
|
|
423
436
|
}
|
|
424
437
|
const data = response.data;
|
|
425
438
|
return {
|
|
@@ -454,7 +467,7 @@ export const addSubtask = async (args, ctx) => {
|
|
|
454
467
|
},
|
|
455
468
|
};
|
|
456
469
|
}
|
|
457
|
-
|
|
470
|
+
return { result: { error: response.error || 'Failed to add subtask' }, isError: true };
|
|
458
471
|
}
|
|
459
472
|
return {
|
|
460
473
|
result: {
|
|
@@ -469,7 +482,7 @@ export const getSubtasks = async (args, ctx) => {
|
|
|
469
482
|
const api = getApiClient();
|
|
470
483
|
const response = await api.getSubtasks(parent_task_id, status);
|
|
471
484
|
if (!response.ok) {
|
|
472
|
-
|
|
485
|
+
return { result: { error: response.error || 'Failed to fetch subtasks' }, isError: true };
|
|
473
486
|
}
|
|
474
487
|
return {
|
|
475
488
|
result: {
|
|
@@ -482,6 +495,48 @@ export const getSubtasks = async (args, ctx) => {
|
|
|
482
495
|
},
|
|
483
496
|
};
|
|
484
497
|
};
|
|
498
|
+
// ============================================================================
|
|
499
|
+
// Worktree Cleanup Handlers
|
|
500
|
+
// ============================================================================
|
|
501
|
+
const getStaleWorktreesSchema = {
|
|
502
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
503
|
+
};
|
|
504
|
+
const clearWorktreePathSchema = {
|
|
505
|
+
task_id: { type: 'string', required: true, validate: uuidValidator },
|
|
506
|
+
};
|
|
507
|
+
export const getStaleWorktrees = async (args, ctx) => {
|
|
508
|
+
const { project_id } = parseArgs(args, getStaleWorktreesSchema);
|
|
509
|
+
const api = getApiClient();
|
|
510
|
+
const response = await api.getStaleWorktrees(project_id);
|
|
511
|
+
if (!response.ok) {
|
|
512
|
+
return { result: { error: response.error || 'Failed to get stale worktrees' }, isError: true };
|
|
513
|
+
}
|
|
514
|
+
const data = response.data;
|
|
515
|
+
return {
|
|
516
|
+
result: {
|
|
517
|
+
project_id: data?.project_id,
|
|
518
|
+
project_name: data?.project_name,
|
|
519
|
+
stale_worktrees: data?.stale_worktrees || [],
|
|
520
|
+
count: data?.count || 0,
|
|
521
|
+
cleanup_instructions: data?.cleanup_instructions,
|
|
522
|
+
},
|
|
523
|
+
};
|
|
524
|
+
};
|
|
525
|
+
export const clearWorktreePath = async (args, ctx) => {
|
|
526
|
+
const { task_id } = parseArgs(args, clearWorktreePathSchema);
|
|
527
|
+
const api = getApiClient();
|
|
528
|
+
const response = await api.clearWorktreePath(task_id);
|
|
529
|
+
if (!response.ok) {
|
|
530
|
+
return { result: { error: response.error || 'Failed to clear worktree path' }, isError: true };
|
|
531
|
+
}
|
|
532
|
+
return {
|
|
533
|
+
result: {
|
|
534
|
+
success: true,
|
|
535
|
+
task_id,
|
|
536
|
+
message: 'Worktree path cleared. The worktree can now be safely removed if not already done.',
|
|
537
|
+
},
|
|
538
|
+
};
|
|
539
|
+
};
|
|
485
540
|
/**
|
|
486
541
|
* Task handlers registry
|
|
487
542
|
*/
|
|
@@ -499,4 +554,7 @@ export const taskHandlers = {
|
|
|
499
554
|
// Subtask handlers
|
|
500
555
|
add_subtask: addSubtask,
|
|
501
556
|
get_subtasks: getSubtasks,
|
|
557
|
+
// Worktree cleanup handlers
|
|
558
|
+
get_stale_worktrees: getStaleWorktrees,
|
|
559
|
+
clear_worktree_path: clearWorktreePath,
|
|
502
560
|
};
|
package/dist/handlers/types.d.ts
CHANGED
|
@@ -82,9 +82,40 @@ export interface HandlerContext {
|
|
|
82
82
|
extractProjectNameFromGitUrl?: (gitUrl: string) => string;
|
|
83
83
|
}
|
|
84
84
|
/**
|
|
85
|
-
*
|
|
85
|
+
* Success result with typed data
|
|
86
86
|
*/
|
|
87
|
-
export interface
|
|
87
|
+
export interface SuccessResult<T = unknown> {
|
|
88
|
+
result: T;
|
|
89
|
+
content?: Array<{
|
|
90
|
+
type: string;
|
|
91
|
+
text: string;
|
|
92
|
+
}>;
|
|
93
|
+
isError?: false;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Error result with error information
|
|
97
|
+
*/
|
|
98
|
+
export interface ErrorResult {
|
|
99
|
+
result: {
|
|
100
|
+
error: string;
|
|
101
|
+
[key: string]: unknown;
|
|
102
|
+
};
|
|
103
|
+
content?: Array<{
|
|
104
|
+
type: string;
|
|
105
|
+
text: string;
|
|
106
|
+
}>;
|
|
107
|
+
isError: true;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Result returned by handlers - discriminated union for type safety
|
|
111
|
+
* Use the helper functions success() and error() to create properly typed results.
|
|
112
|
+
*/
|
|
113
|
+
export type HandlerResult<T = unknown> = SuccessResult<T> | ErrorResult;
|
|
114
|
+
/**
|
|
115
|
+
* Legacy HandlerResult interface for backward compatibility
|
|
116
|
+
* @deprecated Use HandlerResult<T> discriminated union instead
|
|
117
|
+
*/
|
|
118
|
+
export interface LegacyHandlerResult {
|
|
88
119
|
result?: unknown;
|
|
89
120
|
content?: Array<{
|
|
90
121
|
type: string;
|
|
@@ -92,6 +123,37 @@ export interface HandlerResult {
|
|
|
92
123
|
}>;
|
|
93
124
|
isError?: boolean;
|
|
94
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* Create a success result with typed data
|
|
128
|
+
* @example
|
|
129
|
+
* return success({ tasks: data, total_count: count });
|
|
130
|
+
*/
|
|
131
|
+
export declare function success<T>(data: T): SuccessResult<T>;
|
|
132
|
+
/**
|
|
133
|
+
* Create an error result
|
|
134
|
+
* @example
|
|
135
|
+
* return error('Task not found');
|
|
136
|
+
* return error('Validation failed', { field: 'title', reason: 'too long' });
|
|
137
|
+
*/
|
|
138
|
+
export declare function error(message: string, details?: Record<string, unknown>): ErrorResult;
|
|
139
|
+
/**
|
|
140
|
+
* Check if a handler result is a success (not an error)
|
|
141
|
+
* @example
|
|
142
|
+
* const result = await handler(args, ctx);
|
|
143
|
+
* if (isSuccess(result)) {
|
|
144
|
+
* console.log(result.result); // typed as T
|
|
145
|
+
* }
|
|
146
|
+
*/
|
|
147
|
+
export declare function isSuccess<T>(result: HandlerResult<T>): result is SuccessResult<T>;
|
|
148
|
+
/**
|
|
149
|
+
* Check if a handler result is an error
|
|
150
|
+
* @example
|
|
151
|
+
* const result = await handler(args, ctx);
|
|
152
|
+
* if (isError(result)) {
|
|
153
|
+
* console.log(result.result.error); // string
|
|
154
|
+
* }
|
|
155
|
+
*/
|
|
156
|
+
export declare function isError(result: HandlerResult<unknown>): result is ErrorResult;
|
|
95
157
|
/**
|
|
96
158
|
* Handler function type
|
|
97
159
|
*/
|
package/dist/handlers/types.js
CHANGED
|
@@ -1 +1,48 @@
|
|
|
1
|
-
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Result Factory Functions - use these for type-safe handler results
|
|
3
|
+
// ============================================================================
|
|
4
|
+
/**
|
|
5
|
+
* Create a success result with typed data
|
|
6
|
+
* @example
|
|
7
|
+
* return success({ tasks: data, total_count: count });
|
|
8
|
+
*/
|
|
9
|
+
export function success(data) {
|
|
10
|
+
return { result: data };
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Create an error result
|
|
14
|
+
* @example
|
|
15
|
+
* return error('Task not found');
|
|
16
|
+
* return error('Validation failed', { field: 'title', reason: 'too long' });
|
|
17
|
+
*/
|
|
18
|
+
export function error(message, details) {
|
|
19
|
+
return {
|
|
20
|
+
result: { error: message, ...details },
|
|
21
|
+
isError: true
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Type Predicates - use for runtime type narrowing
|
|
26
|
+
// ============================================================================
|
|
27
|
+
/**
|
|
28
|
+
* Check if a handler result is a success (not an error)
|
|
29
|
+
* @example
|
|
30
|
+
* const result = await handler(args, ctx);
|
|
31
|
+
* if (isSuccess(result)) {
|
|
32
|
+
* console.log(result.result); // typed as T
|
|
33
|
+
* }
|
|
34
|
+
*/
|
|
35
|
+
export function isSuccess(result) {
|
|
36
|
+
return !result.isError;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Check if a handler result is an error
|
|
40
|
+
* @example
|
|
41
|
+
* const result = await handler(args, ctx);
|
|
42
|
+
* if (isError(result)) {
|
|
43
|
+
* console.log(result.result.error); // string
|
|
44
|
+
* }
|
|
45
|
+
*/
|
|
46
|
+
export function isError(result) {
|
|
47
|
+
return result.isError === true;
|
|
48
|
+
}
|
|
@@ -26,7 +26,7 @@ export const getTasksAwaitingValidation = async (args, _ctx) => {
|
|
|
26
26
|
const apiClient = getApiClient();
|
|
27
27
|
const response = await apiClient.getTasksAwaitingValidation(project_id);
|
|
28
28
|
if (!response.ok) {
|
|
29
|
-
|
|
29
|
+
return { result: { error: response.error || 'Failed to fetch tasks awaiting validation' }, isError: true };
|
|
30
30
|
}
|
|
31
31
|
return { result: response.data };
|
|
32
32
|
};
|
|
@@ -37,7 +37,7 @@ export const claimValidation = async (args, ctx) => {
|
|
|
37
37
|
const apiClient = getApiClient();
|
|
38
38
|
const response = await apiClient.claimValidation(task_id, currentSessionId || undefined);
|
|
39
39
|
if (!response.ok) {
|
|
40
|
-
|
|
40
|
+
return { result: { error: response.error || 'Failed to claim task for validation' }, isError: true };
|
|
41
41
|
}
|
|
42
42
|
return { result: response.data };
|
|
43
43
|
};
|
|
@@ -63,7 +63,7 @@ export const validateTask = async (args, ctx) => {
|
|
|
63
63
|
},
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
|
-
|
|
66
|
+
return { result: { error: response.error || 'Failed to validate task' }, isError: true };
|
|
67
67
|
}
|
|
68
68
|
return { result: response.data };
|
|
69
69
|
};
|