@taazkareem/clickup-mcp-server 0.6.5 → 0.6.7
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 +4 -4
- package/build/server.js +3 -6
- package/build/services/clickup/base.js +156 -45
- package/build/services/clickup/bulk.js +10 -22
- package/build/services/clickup/tag.js +51 -2
- package/build/services/clickup/task/task-core.js +76 -11
- package/build/services/clickup/task/task-search.js +378 -25
- package/build/services/clickup/workspace.js +14 -12
- package/build/tools/folder.js +5 -53
- package/build/tools/list.js +5 -66
- package/build/tools/tag.js +92 -76
- package/build/tools/task/attachments.js +16 -29
- package/build/tools/task/bulk-operations.js +7 -52
- package/build/tools/task/handlers.js +253 -136
- package/build/tools/task/main.js +9 -33
- package/build/tools/task/single-operations.js +15 -106
- package/build/tools/task/utilities.js +59 -12
- package/build/tools/task/workspace-operations.js +15 -8
- package/build/tools/workspace.js +1 -12
- package/build/utils/date-utils.js +7 -4
- package/build/utils/resolver-utils.js +102 -29
- package/package.json +1 -1
|
@@ -12,10 +12,40 @@ import { clickUpServices } from '../../services/shared.js';
|
|
|
12
12
|
import { BulkService } from '../../services/clickup/bulk.js';
|
|
13
13
|
import { parseDueDate } from '../utils.js';
|
|
14
14
|
import { validateTaskIdentification, validateListIdentification, validateTaskUpdateData, validateBulkTasks, parseBulkOptions, resolveListIdWithValidation } from './utilities.js';
|
|
15
|
+
import { workspaceService } from '../../services/shared.js';
|
|
16
|
+
import { isNameMatch } from '../../utils/resolver-utils.js';
|
|
17
|
+
import { Logger } from '../../logger.js';
|
|
15
18
|
// Use shared services instance
|
|
16
19
|
const { task: taskService, list: listService } = clickUpServices;
|
|
17
20
|
// Create a bulk service instance that uses the task service
|
|
18
21
|
const bulkService = new BulkService(taskService);
|
|
22
|
+
// Create a logger instance for task handlers
|
|
23
|
+
const logger = new Logger('TaskHandlers');
|
|
24
|
+
// Cache for task context between sequential operations
|
|
25
|
+
const taskContextCache = new Map();
|
|
26
|
+
const TASK_CONTEXT_TTL = 5 * 60 * 1000; // 5 minutes
|
|
27
|
+
/**
|
|
28
|
+
* Store task context for sequential operations
|
|
29
|
+
*/
|
|
30
|
+
function storeTaskContext(taskName, taskId) {
|
|
31
|
+
taskContextCache.set(taskName, {
|
|
32
|
+
id: taskId,
|
|
33
|
+
timestamp: Date.now()
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Get cached task context if valid
|
|
38
|
+
*/
|
|
39
|
+
function getCachedTaskContext(taskName) {
|
|
40
|
+
const context = taskContextCache.get(taskName);
|
|
41
|
+
if (!context)
|
|
42
|
+
return null;
|
|
43
|
+
if (Date.now() - context.timestamp > TASK_CONTEXT_TTL) {
|
|
44
|
+
taskContextCache.delete(taskName);
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
return context.id;
|
|
48
|
+
}
|
|
19
49
|
//=============================================================================
|
|
20
50
|
// SHARED UTILITY FUNCTIONS
|
|
21
51
|
//=============================================================================
|
|
@@ -50,31 +80,212 @@ function buildUpdateData(params) {
|
|
|
50
80
|
return updateData;
|
|
51
81
|
}
|
|
52
82
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
83
|
+
* Core function to find a task by ID or name
|
|
84
|
+
* This consolidates all task lookup logic in one place for consistency
|
|
85
|
+
*/
|
|
86
|
+
async function findTask(params) {
|
|
87
|
+
const { taskId, taskName, listName, customTaskId, requireId = false, includeSubtasks = false } = params;
|
|
88
|
+
// Validate that we have enough information to identify a task
|
|
89
|
+
const validationResult = validateTaskIdentification({ taskId, taskName, listName, customTaskId }, { requireTaskId: requireId, useGlobalLookup: true });
|
|
90
|
+
if (!validationResult.isValid) {
|
|
91
|
+
throw new Error(validationResult.errorMessage);
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
// Direct path for taskId - most efficient
|
|
95
|
+
if (taskId) {
|
|
96
|
+
const task = await taskService.getTask(taskId);
|
|
97
|
+
// Add subtasks if requested
|
|
98
|
+
if (includeSubtasks) {
|
|
99
|
+
const subtasks = await taskService.getSubtasks(task.id);
|
|
100
|
+
return { task, subtasks };
|
|
101
|
+
}
|
|
102
|
+
return { task };
|
|
103
|
+
}
|
|
104
|
+
// Direct path for customTaskId - also efficient
|
|
105
|
+
if (customTaskId) {
|
|
106
|
+
const task = await taskService.getTaskByCustomId(customTaskId);
|
|
107
|
+
// Add subtasks if requested
|
|
108
|
+
if (includeSubtasks) {
|
|
109
|
+
const subtasks = await taskService.getSubtasks(task.id);
|
|
110
|
+
return { task, subtasks };
|
|
111
|
+
}
|
|
112
|
+
return { task };
|
|
113
|
+
}
|
|
114
|
+
// Special optimized path for taskName + listName combination
|
|
115
|
+
if (taskName && listName) {
|
|
116
|
+
const listId = await resolveListIdWithValidation(null, listName);
|
|
117
|
+
// Get all tasks in the list
|
|
118
|
+
const allTasks = await taskService.getTasks(listId);
|
|
119
|
+
// Find the task that matches the name
|
|
120
|
+
const matchingTask = findTaskByName(allTasks, taskName);
|
|
121
|
+
if (!matchingTask) {
|
|
122
|
+
throw new Error(`Task "${taskName}" not found in list "${listName}"`);
|
|
123
|
+
}
|
|
124
|
+
// Add subtasks if requested
|
|
125
|
+
if (includeSubtasks) {
|
|
126
|
+
const subtasks = await taskService.getSubtasks(matchingTask.id);
|
|
127
|
+
return { task: matchingTask, subtasks };
|
|
128
|
+
}
|
|
129
|
+
return { task: matchingTask };
|
|
130
|
+
}
|
|
131
|
+
// Fallback to searching all lists for taskName-only case
|
|
132
|
+
if (taskName) {
|
|
133
|
+
logger.debug(`Searching all lists for task: "${taskName}"`);
|
|
134
|
+
// Get workspace hierarchy which contains all lists
|
|
135
|
+
const hierarchy = await workspaceService.getWorkspaceHierarchy();
|
|
136
|
+
// Extract all list IDs from the hierarchy
|
|
137
|
+
const listIds = [];
|
|
138
|
+
const extractListIds = (node) => {
|
|
139
|
+
if (node.type === 'list') {
|
|
140
|
+
listIds.push(node.id);
|
|
141
|
+
}
|
|
142
|
+
if (node.children) {
|
|
143
|
+
node.children.forEach(extractListIds);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
// Start from the root's children
|
|
147
|
+
hierarchy.root.children.forEach(extractListIds);
|
|
148
|
+
// Search through each list
|
|
149
|
+
const searchPromises = listIds.map(async (listId) => {
|
|
150
|
+
try {
|
|
151
|
+
const tasks = await taskService.getTasks(listId);
|
|
152
|
+
const matchingTask = findTaskByName(tasks, taskName);
|
|
153
|
+
if (matchingTask) {
|
|
154
|
+
logger.debug(`Found task "${matchingTask.name}" (ID: ${matchingTask.id}) in list with ID "${listId}"`);
|
|
155
|
+
return matchingTask;
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
logger.warn(`Error searching list ${listId}: ${error.message}`);
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
// Wait for all searches to complete
|
|
165
|
+
const results = await Promise.all(searchPromises);
|
|
166
|
+
// Filter out null results and sort by match quality and recency
|
|
167
|
+
const matchingTasks = results
|
|
168
|
+
.filter(task => task !== null)
|
|
169
|
+
.sort((a, b) => {
|
|
170
|
+
const aMatch = isNameMatch(a.name, taskName);
|
|
171
|
+
const bMatch = isNameMatch(b.name, taskName);
|
|
172
|
+
// First sort by match quality
|
|
173
|
+
if (bMatch.score !== aMatch.score) {
|
|
174
|
+
return bMatch.score - aMatch.score;
|
|
175
|
+
}
|
|
176
|
+
// Then sort by recency
|
|
177
|
+
return parseInt(b.date_updated) - parseInt(a.date_updated);
|
|
178
|
+
});
|
|
179
|
+
if (matchingTasks.length === 0) {
|
|
180
|
+
throw new Error(`Task "${taskName}" not found in any list across your workspace. Please check the task name and try again.`);
|
|
181
|
+
}
|
|
182
|
+
const bestMatch = matchingTasks[0];
|
|
183
|
+
// Add subtasks if requested
|
|
184
|
+
if (includeSubtasks) {
|
|
185
|
+
const subtasks = await taskService.getSubtasks(bestMatch.id);
|
|
186
|
+
return { task: bestMatch, subtasks };
|
|
187
|
+
}
|
|
188
|
+
return { task: bestMatch };
|
|
189
|
+
}
|
|
190
|
+
// We shouldn't reach here if validation is working correctly
|
|
191
|
+
throw new Error("No valid task identification provided");
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
// Enhance error message for non-existent tasks
|
|
195
|
+
if (taskName && error.message.includes('not found')) {
|
|
196
|
+
throw new Error(`Task "${taskName}" not found. Please check the task name and try again.`);
|
|
197
|
+
}
|
|
198
|
+
// Pass along other formatted errors
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Helper function to find a task by name in an array of tasks
|
|
204
|
+
*/
|
|
205
|
+
function findTaskByName(tasks, name) {
|
|
206
|
+
if (!tasks || !Array.isArray(tasks) || !name)
|
|
207
|
+
return null;
|
|
208
|
+
const normalizedSearchName = name.toLowerCase().trim();
|
|
209
|
+
// Get match scores for all tasks
|
|
210
|
+
const taskMatchScores = tasks.map(task => {
|
|
211
|
+
const matchResult = isNameMatch(task.name, name);
|
|
212
|
+
return {
|
|
213
|
+
task,
|
|
214
|
+
matchResult,
|
|
215
|
+
// Parse the date_updated field as a number for sorting
|
|
216
|
+
updatedAt: task.date_updated ? parseInt(task.date_updated, 10) : 0
|
|
217
|
+
};
|
|
218
|
+
}).filter(result => result.matchResult.isMatch);
|
|
219
|
+
if (taskMatchScores.length === 0) {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
// First, try to find exact matches
|
|
223
|
+
const exactMatches = taskMatchScores
|
|
224
|
+
.filter(result => result.matchResult.exactMatch)
|
|
225
|
+
.sort((a, b) => {
|
|
226
|
+
// For exact matches with the same score, sort by most recently updated
|
|
227
|
+
if (b.matchResult.score === a.matchResult.score) {
|
|
228
|
+
return b.updatedAt - a.updatedAt;
|
|
229
|
+
}
|
|
230
|
+
return b.matchResult.score - a.matchResult.score;
|
|
231
|
+
});
|
|
232
|
+
// Get the best matches based on whether we have exact matches or need to fall back to fuzzy matches
|
|
233
|
+
const bestMatches = exactMatches.length > 0 ? exactMatches : taskMatchScores.sort((a, b) => {
|
|
234
|
+
// First sort by match score (highest first)
|
|
235
|
+
if (b.matchResult.score !== a.matchResult.score) {
|
|
236
|
+
return b.matchResult.score - a.matchResult.score;
|
|
237
|
+
}
|
|
238
|
+
// Then sort by most recently updated
|
|
239
|
+
return b.updatedAt - a.updatedAt;
|
|
240
|
+
});
|
|
241
|
+
// Get the best match
|
|
242
|
+
return bestMatches[0].task;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Handler for getting a task - uses the consolidated findTask function
|
|
246
|
+
*/
|
|
247
|
+
export async function getTaskHandler(params) {
|
|
248
|
+
try {
|
|
249
|
+
const result = await findTask({
|
|
250
|
+
taskId: params.taskId,
|
|
251
|
+
taskName: params.taskName,
|
|
252
|
+
listName: params.listName,
|
|
253
|
+
customTaskId: params.customTaskId,
|
|
254
|
+
includeSubtasks: params.subtasks
|
|
255
|
+
});
|
|
256
|
+
if (result.subtasks) {
|
|
257
|
+
return { ...result.task, subtasks: result.subtasks };
|
|
258
|
+
}
|
|
259
|
+
return result.task;
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
throw error;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Get task ID from various identifiers - uses the consolidated findTask function
|
|
61
267
|
*/
|
|
62
|
-
export async function getTaskId(taskId, taskName, listName, customTaskId) {
|
|
63
|
-
|
|
64
|
-
|
|
268
|
+
export async function getTaskId(taskId, taskName, listName, customTaskId, requireId, includeSubtasks) {
|
|
269
|
+
// Check task context cache first if we have a task name
|
|
270
|
+
if (taskName && !taskId && !customTaskId) {
|
|
271
|
+
const cachedId = getCachedTaskContext(taskName);
|
|
272
|
+
if (cachedId) {
|
|
273
|
+
return cachedId;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
const result = await findTask({
|
|
65
277
|
taskId,
|
|
66
|
-
customTaskId,
|
|
67
278
|
taskName,
|
|
68
279
|
listName,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
includeListContext: false
|
|
280
|
+
customTaskId,
|
|
281
|
+
requireId,
|
|
282
|
+
includeSubtasks
|
|
73
283
|
});
|
|
74
|
-
|
|
75
|
-
|
|
284
|
+
// Store task context for future operations
|
|
285
|
+
if (taskName && result.task.id) {
|
|
286
|
+
storeTaskContext(taskName, result.task.id);
|
|
76
287
|
}
|
|
77
|
-
|
|
288
|
+
return result.task.id;
|
|
78
289
|
}
|
|
79
290
|
/**
|
|
80
291
|
* Process a list identification validation, returning the list ID
|
|
@@ -107,7 +318,10 @@ function buildTaskFilters(params) {
|
|
|
107
318
|
*/
|
|
108
319
|
async function mapTaskIds(tasks) {
|
|
109
320
|
return Promise.all(tasks.map(async (task) => {
|
|
110
|
-
validateTaskIdentification(task.taskId, task.taskName, task.listName, task.customTaskId);
|
|
321
|
+
const validationResult = validateTaskIdentification({ taskId: task.taskId, taskName: task.taskName, listName: task.listName, customTaskId: task.customTaskId }, { useGlobalLookup: true });
|
|
322
|
+
if (!validationResult.isValid) {
|
|
323
|
+
throw new Error(validationResult.errorMessage);
|
|
324
|
+
}
|
|
111
325
|
return await getTaskId(task.taskId, task.taskName, task.listName, task.customTaskId);
|
|
112
326
|
}));
|
|
113
327
|
}
|
|
@@ -150,20 +364,29 @@ export async function createTaskHandler(params) {
|
|
|
150
364
|
/**
|
|
151
365
|
* Handler for updating a task
|
|
152
366
|
*/
|
|
153
|
-
export async function updateTaskHandler(params) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
367
|
+
export async function updateTaskHandler(taskService, params) {
|
|
368
|
+
const { taskId, taskName, listName, customTaskId, ...updateData } = params;
|
|
369
|
+
// Validate task identification with global lookup enabled
|
|
370
|
+
const validationResult = validateTaskIdentification(params, { useGlobalLookup: true });
|
|
371
|
+
if (!validationResult.isValid) {
|
|
372
|
+
throw new Error(validationResult.errorMessage);
|
|
373
|
+
}
|
|
374
|
+
// Validate update data
|
|
375
|
+
validateTaskUpdateData(updateData);
|
|
376
|
+
try {
|
|
377
|
+
// Get the task ID using global lookup
|
|
378
|
+
const id = await getTaskId(taskId, taskName, listName, customTaskId);
|
|
379
|
+
return await taskService.updateTask(id, updateData);
|
|
380
|
+
}
|
|
381
|
+
catch (error) {
|
|
382
|
+
throw new Error(`Failed to update task: ${error instanceof Error ? error.message : String(error)}`);
|
|
383
|
+
}
|
|
161
384
|
}
|
|
162
385
|
/**
|
|
163
386
|
* Handler for moving a task
|
|
164
387
|
*/
|
|
165
388
|
export async function moveTaskHandler(params) {
|
|
166
|
-
const taskId = await getTaskId(params.taskId, params.taskName, params.
|
|
389
|
+
const taskId = await getTaskId(params.taskId, params.taskName, undefined, params.customTaskId, false);
|
|
167
390
|
const listId = await getListId(params.listId, params.listName);
|
|
168
391
|
return await taskService.moveTask(taskId, listId);
|
|
169
392
|
}
|
|
@@ -171,119 +394,13 @@ export async function moveTaskHandler(params) {
|
|
|
171
394
|
* Handler for duplicating a task
|
|
172
395
|
*/
|
|
173
396
|
export async function duplicateTaskHandler(params) {
|
|
174
|
-
const taskId = await getTaskId(params.taskId, params.taskName, params.
|
|
397
|
+
const taskId = await getTaskId(params.taskId, params.taskName, undefined, params.customTaskId, false);
|
|
175
398
|
let listId;
|
|
176
399
|
if (params.listId || params.listName) {
|
|
177
400
|
listId = await getListId(params.listId, params.listName);
|
|
178
401
|
}
|
|
179
402
|
return await taskService.duplicateTask(taskId, listId);
|
|
180
403
|
}
|
|
181
|
-
/**
|
|
182
|
-
* Handler for getting a task
|
|
183
|
-
*/
|
|
184
|
-
export async function getTaskHandler(params) {
|
|
185
|
-
try {
|
|
186
|
-
// Direct path for taskId - most efficient
|
|
187
|
-
if (params.taskId) {
|
|
188
|
-
const task = await taskService.getTask(params.taskId);
|
|
189
|
-
// Add subtasks if requested
|
|
190
|
-
if (params.subtasks) {
|
|
191
|
-
const subtasks = await taskService.getSubtasks(task.id);
|
|
192
|
-
return { ...task, subtasks };
|
|
193
|
-
}
|
|
194
|
-
return task;
|
|
195
|
-
}
|
|
196
|
-
// Direct path for customTaskId - also efficient
|
|
197
|
-
if (params.customTaskId) {
|
|
198
|
-
const task = await taskService.getTaskByCustomId(params.customTaskId);
|
|
199
|
-
// Add subtasks if requested
|
|
200
|
-
if (params.subtasks) {
|
|
201
|
-
const subtasks = await taskService.getSubtasks(task.id);
|
|
202
|
-
return { ...task, subtasks };
|
|
203
|
-
}
|
|
204
|
-
return task;
|
|
205
|
-
}
|
|
206
|
-
// Special optimized path for taskName + listName combination
|
|
207
|
-
if (params.taskName && params.listName) {
|
|
208
|
-
// First, get the list ID
|
|
209
|
-
const listId = await getListId(null, params.listName);
|
|
210
|
-
if (!listId) {
|
|
211
|
-
throw new Error(`List "${params.listName}" not found`);
|
|
212
|
-
}
|
|
213
|
-
// Use the ClickUp API to get filtered tasks
|
|
214
|
-
// Need to get all tasks and filter on client side
|
|
215
|
-
// This is more efficient than the original approach because it's a dedicated path
|
|
216
|
-
// that skips the global lookup framework entirely
|
|
217
|
-
const allTasks = await taskService.getTasks(listId);
|
|
218
|
-
// Find the matching task
|
|
219
|
-
// Extract this to avoid dependency on internal isNameMatch implementation
|
|
220
|
-
const matchingTask = findTaskByName(allTasks, params.taskName);
|
|
221
|
-
if (!matchingTask) {
|
|
222
|
-
throw new Error(`Task "${params.taskName}" not found in list "${params.listName}"`);
|
|
223
|
-
}
|
|
224
|
-
// Add subtasks if requested
|
|
225
|
-
if (params.subtasks) {
|
|
226
|
-
const subtasks = await taskService.getSubtasks(matchingTask.id);
|
|
227
|
-
return { ...matchingTask, subtasks };
|
|
228
|
-
}
|
|
229
|
-
return matchingTask;
|
|
230
|
-
}
|
|
231
|
-
// Fallback to the original global lookup for all other cases
|
|
232
|
-
const result = await taskService.findTasks({
|
|
233
|
-
taskName: params.taskName,
|
|
234
|
-
allowMultipleMatches: true,
|
|
235
|
-
useSmartDisambiguation: false,
|
|
236
|
-
includeFullDetails: true,
|
|
237
|
-
includeListContext: true
|
|
238
|
-
});
|
|
239
|
-
// Handle the response based on the result type
|
|
240
|
-
if (Array.isArray(result)) {
|
|
241
|
-
// If multiple tasks matched, format them with task count
|
|
242
|
-
return {
|
|
243
|
-
matches: result,
|
|
244
|
-
count: result.length
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
else if (result) {
|
|
248
|
-
// Single task found, check if we need to include subtasks
|
|
249
|
-
if (params.subtasks) {
|
|
250
|
-
const subtasks = await taskService.getSubtasks(result.id);
|
|
251
|
-
return { ...result, subtasks };
|
|
252
|
-
}
|
|
253
|
-
// Return the single task
|
|
254
|
-
return result;
|
|
255
|
-
}
|
|
256
|
-
else {
|
|
257
|
-
throw new Error("Task not found");
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
catch (error) {
|
|
261
|
-
// Enhance error message for non-existent tasks
|
|
262
|
-
if (params.taskName && error.message.includes('not found')) {
|
|
263
|
-
throw new Error(`Task "${params.taskName}" not found. Please check the task name and try again.`);
|
|
264
|
-
}
|
|
265
|
-
// Pass along other formatted errors
|
|
266
|
-
throw error;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
/**
|
|
270
|
-
* Helper function to find a task by name in an array of tasks
|
|
271
|
-
*/
|
|
272
|
-
function findTaskByName(tasks, name) {
|
|
273
|
-
if (!tasks || !Array.isArray(tasks) || !name)
|
|
274
|
-
return null;
|
|
275
|
-
// Try exact match first
|
|
276
|
-
let match = tasks.find(task => task.name === name);
|
|
277
|
-
if (match)
|
|
278
|
-
return match;
|
|
279
|
-
// Try case-insensitive match
|
|
280
|
-
match = tasks.find(task => task.name.toLowerCase() === name.toLowerCase());
|
|
281
|
-
if (match)
|
|
282
|
-
return match;
|
|
283
|
-
// Try fuzzy match - looking for name as substring
|
|
284
|
-
match = tasks.find(task => task.name.toLowerCase().includes(name.toLowerCase()));
|
|
285
|
-
return match || null;
|
|
286
|
-
}
|
|
287
404
|
/**
|
|
288
405
|
* Handler for getting tasks
|
|
289
406
|
*/
|
package/build/tools/task/main.js
CHANGED
|
@@ -13,7 +13,7 @@ import { createTaskTool, getTaskTool, getTasksTool, updateTaskTool, moveTaskTool
|
|
|
13
13
|
import { createBulkTasksTool, updateBulkTasksTool, moveBulkTasksTool, deleteBulkTasksTool } from './bulk-operations.js';
|
|
14
14
|
import { getWorkspaceTasksTool } from './workspace-operations.js';
|
|
15
15
|
// Import handlers
|
|
16
|
-
import { createTaskHandler, getTaskHandler, getTasksHandler, updateTaskHandler, moveTaskHandler, duplicateTaskHandler, deleteTaskHandler, getTaskCommentsHandler, createTaskCommentHandler, createBulkTasksHandler, updateBulkTasksHandler, moveBulkTasksHandler, deleteBulkTasksHandler, getWorkspaceTasksHandler } from './
|
|
16
|
+
import { createTaskHandler, getTaskHandler, getTasksHandler, updateTaskHandler, moveTaskHandler, duplicateTaskHandler, deleteTaskHandler, getTaskCommentsHandler, createTaskCommentHandler, createBulkTasksHandler, updateBulkTasksHandler, moveBulkTasksHandler, deleteBulkTasksHandler, getWorkspaceTasksHandler, formatTaskData } from './index.js';
|
|
17
17
|
// Import shared services
|
|
18
18
|
import { clickUpServices } from '../../services/shared.js';
|
|
19
19
|
const { task: taskService } = clickUpServices;
|
|
@@ -43,42 +43,18 @@ export const handleGetTasks = createHandlerWrapper(getTasksHandler, (tasks) => (
|
|
|
43
43
|
tasks,
|
|
44
44
|
count: tasks.length
|
|
45
45
|
}));
|
|
46
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Handle task update operation
|
|
48
|
+
*/
|
|
49
|
+
export async function handleUpdateTask(parameters) {
|
|
47
50
|
try {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
// Ensure priority is converted to a number if it's a valid value
|
|
51
|
-
if (parameters.priority === null) {
|
|
52
|
-
// null is valid for clearing priority
|
|
53
|
-
}
|
|
54
|
-
else if (typeof parameters.priority === 'number' && [1, 2, 3, 4].includes(parameters.priority)) {
|
|
55
|
-
// Valid priority number, keep as is
|
|
56
|
-
}
|
|
57
|
-
else if (typeof parameters.priority === 'string') {
|
|
58
|
-
// Try to convert string to number
|
|
59
|
-
const numPriority = parseInt(parameters.priority, 10);
|
|
60
|
-
if (!isNaN(numPriority) && [1, 2, 3, 4].includes(numPriority)) {
|
|
61
|
-
parameters.priority = numPriority;
|
|
62
|
-
}
|
|
63
|
-
else if (parameters.priority === 'null') {
|
|
64
|
-
parameters.priority = null;
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
throw new Error(`Invalid priority value: ${parameters.priority}. Must be 1, 2, 3, 4, or null.`);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
throw new Error(`Invalid priority value: ${parameters.priority}. Must be 1, 2, 3, 4, or null.`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
// Proceed with normal handling
|
|
75
|
-
const result = await updateTaskHandler(parameters);
|
|
76
|
-
return sponsorService.createResponse(result, true);
|
|
51
|
+
const result = await updateTaskHandler(taskService, parameters);
|
|
52
|
+
return sponsorService.createResponse(formatTaskData(result), true);
|
|
77
53
|
}
|
|
78
54
|
catch (error) {
|
|
79
|
-
return sponsorService.createErrorResponse(error
|
|
55
|
+
return sponsorService.createErrorResponse(error instanceof Error ? error.message : String(error));
|
|
80
56
|
}
|
|
81
|
-
}
|
|
57
|
+
}
|
|
82
58
|
export const handleMoveTask = createHandlerWrapper(moveTaskHandler);
|
|
83
59
|
export const handleDuplicateTask = createHandlerWrapper(duplicateTaskHandler);
|
|
84
60
|
export const handleDeleteTask = createHandlerWrapper(deleteTaskHandler, () => ({
|