@doist/todoist-ai 4.4.0 → 4.6.0
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/index.d.ts +168 -35
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -16
- package/dist/mcp-helpers.d.ts.map +1 -1
- package/dist/mcp-helpers.js +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +78 -17
- package/dist/tool-helpers.d.ts +4 -0
- package/dist/tool-helpers.d.ts.map +1 -1
- package/dist/tool-helpers.js +2 -0
- package/dist/tools/__tests__/add-projects.test.js +1 -1
- package/dist/tools/__tests__/add-sections.test.js +1 -1
- package/dist/tools/__tests__/add-tasks.test.js +182 -13
- package/dist/tools/__tests__/assignment-integration.test.d.ts +2 -0
- package/dist/tools/__tests__/assignment-integration.test.d.ts.map +1 -0
- package/dist/tools/__tests__/assignment-integration.test.js +415 -0
- package/dist/tools/__tests__/find-projects.test.js +1 -1
- package/dist/tools/__tests__/find-sections.test.js +1 -1
- package/dist/tools/__tests__/find-tasks-by-date.test.js +1 -1
- package/dist/tools/__tests__/find-tasks.test.js +3 -3
- package/dist/tools/__tests__/get-overview.test.js +1 -1
- package/dist/tools/__tests__/update-tasks.test.js +82 -6
- package/dist/tools/__tests__/user-info.test.js +1 -1
- package/dist/tools/add-comments.d.ts +3 -3
- package/dist/tools/add-comments.d.ts.map +1 -1
- package/dist/tools/add-comments.js +1 -1
- package/dist/tools/add-projects.d.ts.map +1 -1
- package/dist/tools/add-projects.js +1 -1
- package/dist/tools/add-sections.d.ts.map +1 -1
- package/dist/tools/add-sections.js +1 -1
- package/dist/tools/add-tasks.d.ts +17 -7
- package/dist/tools/add-tasks.d.ts.map +1 -1
- package/dist/tools/add-tasks.js +51 -4
- package/dist/tools/find-comments.d.ts +2 -2
- package/dist/tools/find-completed-tasks.d.ts +4 -2
- package/dist/tools/find-completed-tasks.d.ts.map +1 -1
- package/dist/tools/find-completed-tasks.js +2 -2
- package/dist/tools/find-project-collaborators.d.ts +64 -0
- package/dist/tools/find-project-collaborators.d.ts.map +1 -0
- package/dist/tools/find-project-collaborators.js +151 -0
- package/dist/tools/find-tasks-by-date.d.ts +2 -0
- package/dist/tools/find-tasks-by-date.d.ts.map +1 -1
- package/dist/tools/find-tasks-by-date.js +2 -2
- package/dist/tools/find-tasks.d.ts +7 -2
- package/dist/tools/find-tasks.d.ts.map +1 -1
- package/dist/tools/find-tasks.js +128 -33
- package/dist/tools/get-overview.d.ts +2 -2
- package/dist/tools/get-overview.d.ts.map +1 -1
- package/dist/tools/get-overview.js +1 -1
- package/dist/tools/manage-assignments.d.ts +52 -0
- package/dist/tools/manage-assignments.d.ts.map +1 -0
- package/dist/tools/manage-assignments.js +337 -0
- package/dist/tools/update-comments.d.ts.map +1 -1
- package/dist/tools/update-comments.js +1 -1
- package/dist/tools/update-sections.d.ts.map +1 -1
- package/dist/tools/update-sections.js +1 -1
- package/dist/tools/update-tasks.d.ts +17 -7
- package/dist/tools/update-tasks.d.ts.map +1 -1
- package/dist/tools/update-tasks.js +40 -10
- package/dist/utils/assignment-validator.d.ts +69 -0
- package/dist/utils/assignment-validator.d.ts.map +1 -0
- package/dist/utils/assignment-validator.js +253 -0
- package/dist/utils/duration-parser.d.ts +2 -2
- package/dist/utils/duration-parser.d.ts.map +1 -1
- package/dist/utils/priorities.d.ts +8 -0
- package/dist/utils/priorities.d.ts.map +1 -0
- package/dist/utils/priorities.js +15 -0
- package/dist/utils/response-builders.d.ts +2 -2
- package/dist/utils/response-builders.d.ts.map +1 -1
- package/dist/utils/response-builders.js +8 -1
- package/dist/utils/test-helpers.d.ts +2 -0
- package/dist/utils/test-helpers.d.ts.map +1 -1
- package/dist/utils/test-helpers.js +2 -0
- package/dist/utils/tool-names.d.ts +2 -0
- package/dist/utils/tool-names.d.ts.map +1 -1
- package/dist/utils/tool-names.js +3 -0
- package/dist/utils/user-resolver.d.ts +39 -0
- package/dist/utils/user-resolver.d.ts.map +1 -0
- package/dist/utils/user-resolver.js +179 -0
- package/package.json +6 -6
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jest } from '@jest/globals';
|
|
2
|
-
import {
|
|
2
|
+
import { createMockProject, extractStructuredContent, extractTextContent, TEST_IDS, } from '../../utils/test-helpers.js';
|
|
3
3
|
import { ToolNames } from '../../utils/tool-names.js';
|
|
4
4
|
import { addProjects } from '../add-projects.js';
|
|
5
5
|
// Mock the Todoist API
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jest } from '@jest/globals';
|
|
2
|
-
import {
|
|
2
|
+
import { createMockSection, extractStructuredContent, extractTextContent, TEST_IDS, } from '../../utils/test-helpers.js';
|
|
3
3
|
import { ToolNames } from '../../utils/tool-names.js';
|
|
4
4
|
import { addSections } from '../add-sections.js';
|
|
5
5
|
// Mock the Todoist API
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jest } from '@jest/globals';
|
|
2
|
-
import {
|
|
2
|
+
import { createMockTask, extractStructuredContent, extractTextContent, TODAY, } from '../../utils/test-helpers.js';
|
|
3
3
|
import { ToolNames } from '../../utils/tool-names.js';
|
|
4
4
|
import { addTasks } from '../add-tasks.js';
|
|
5
5
|
// Mock the Todoist API
|
|
@@ -42,11 +42,14 @@ describe(`${ADD_TASKS} tool`, () => {
|
|
|
42
42
|
.mockResolvedValueOnce(mockApiResponse2);
|
|
43
43
|
const result = await addTasks.execute({
|
|
44
44
|
tasks: [
|
|
45
|
-
{
|
|
45
|
+
{
|
|
46
|
+
content: 'First task content',
|
|
47
|
+
projectId: '6cfCcrrCFg2xP94Q',
|
|
48
|
+
},
|
|
46
49
|
{
|
|
47
50
|
content: 'Second task content',
|
|
48
51
|
description: 'Task description',
|
|
49
|
-
priority:
|
|
52
|
+
priority: 'p2',
|
|
50
53
|
dueString: 'Aug 15',
|
|
51
54
|
projectId: '6cfCcrrCFg2xP94Q',
|
|
52
55
|
},
|
|
@@ -63,7 +66,7 @@ describe(`${ADD_TASKS} tool`, () => {
|
|
|
63
66
|
expect(mockTodoistApi.addTask).toHaveBeenNthCalledWith(2, {
|
|
64
67
|
content: 'Second task content',
|
|
65
68
|
description: 'Task description',
|
|
66
|
-
priority:
|
|
69
|
+
priority: 3,
|
|
67
70
|
dueString: 'Aug 15',
|
|
68
71
|
projectId: '6cfCcrrCFg2xP94Q',
|
|
69
72
|
sectionId: undefined,
|
|
@@ -99,7 +102,7 @@ describe(`${ADD_TASKS} tool`, () => {
|
|
|
99
102
|
{
|
|
100
103
|
content: 'Subtask content',
|
|
101
104
|
description: 'Subtask description',
|
|
102
|
-
priority:
|
|
105
|
+
priority: 'p3',
|
|
103
106
|
projectId: '6cfCcrrCFg2xP94Q',
|
|
104
107
|
sectionId: 'section-123',
|
|
105
108
|
parentId: 'parent-task-456',
|
|
@@ -109,7 +112,7 @@ describe(`${ADD_TASKS} tool`, () => {
|
|
|
109
112
|
expect(mockTodoistApi.addTask).toHaveBeenCalledWith({
|
|
110
113
|
content: 'Subtask content',
|
|
111
114
|
description: 'Subtask description',
|
|
112
|
-
priority:
|
|
115
|
+
priority: 2,
|
|
113
116
|
projectId: '6cfCcrrCFg2xP94Q',
|
|
114
117
|
sectionId: 'section-123',
|
|
115
118
|
parentId: 'parent-task-456',
|
|
@@ -203,25 +206,179 @@ describe(`${ADD_TASKS} tool`, () => {
|
|
|
203
206
|
];
|
|
204
207
|
for (const testCase of testCases) {
|
|
205
208
|
mockTodoistApi.addTask.mockClear();
|
|
206
|
-
await addTasks.execute({
|
|
209
|
+
await addTasks.execute({
|
|
210
|
+
tasks: [
|
|
211
|
+
{
|
|
212
|
+
content: 'Test task',
|
|
213
|
+
duration: testCase.input,
|
|
214
|
+
projectId: '6cfCcrrCFg2xP94Q',
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
}, mockTodoistApi);
|
|
207
218
|
expect(mockTodoistApi.addTask).toHaveBeenCalledWith(expect.objectContaining({
|
|
208
219
|
duration: testCase.expectedMinutes,
|
|
209
220
|
durationUnit: 'minute',
|
|
210
221
|
}));
|
|
211
222
|
}
|
|
212
223
|
});
|
|
224
|
+
it('should add task with labels', async () => {
|
|
225
|
+
const mockApiResponse = createMockTask({
|
|
226
|
+
id: '8485093755',
|
|
227
|
+
content: 'Task with labels',
|
|
228
|
+
labels: ['urgent', 'work'],
|
|
229
|
+
url: 'https://todoist.com/showTask?id=8485093755',
|
|
230
|
+
addedAt: '2025-08-13T22:09:56.123456Z',
|
|
231
|
+
});
|
|
232
|
+
mockTodoistApi.addTask.mockResolvedValue(mockApiResponse);
|
|
233
|
+
const result = await addTasks.execute({
|
|
234
|
+
tasks: [
|
|
235
|
+
{
|
|
236
|
+
content: 'Task with labels',
|
|
237
|
+
labels: ['urgent', 'work'],
|
|
238
|
+
projectId: '6cfCcrrCFg2xP94Q',
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
}, mockTodoistApi);
|
|
242
|
+
expect(mockTodoistApi.addTask).toHaveBeenCalledWith({
|
|
243
|
+
content: 'Task with labels',
|
|
244
|
+
labels: ['urgent', 'work'],
|
|
245
|
+
projectId: '6cfCcrrCFg2xP94Q',
|
|
246
|
+
sectionId: undefined,
|
|
247
|
+
parentId: undefined,
|
|
248
|
+
});
|
|
249
|
+
// Verify structured content includes labels
|
|
250
|
+
const structuredContent = extractStructuredContent(result);
|
|
251
|
+
expect(structuredContent.tasks).toHaveLength(1);
|
|
252
|
+
expect(structuredContent.tasks[0]).toEqual(expect.objectContaining({
|
|
253
|
+
labels: ['urgent', 'work'],
|
|
254
|
+
}));
|
|
255
|
+
});
|
|
256
|
+
it('should add task with empty labels array', async () => {
|
|
257
|
+
const mockApiResponse = createMockTask({
|
|
258
|
+
id: '8485093756',
|
|
259
|
+
content: 'Task with empty labels',
|
|
260
|
+
labels: [],
|
|
261
|
+
url: 'https://todoist.com/showTask?id=8485093756',
|
|
262
|
+
addedAt: '2025-08-13T22:09:56.123456Z',
|
|
263
|
+
});
|
|
264
|
+
mockTodoistApi.addTask.mockResolvedValue(mockApiResponse);
|
|
265
|
+
await addTasks.execute({
|
|
266
|
+
tasks: [
|
|
267
|
+
{
|
|
268
|
+
content: 'Task with empty labels',
|
|
269
|
+
labels: [],
|
|
270
|
+
projectId: '6cfCcrrCFg2xP94Q',
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
}, mockTodoistApi);
|
|
274
|
+
expect(mockTodoistApi.addTask).toHaveBeenCalledWith({
|
|
275
|
+
content: 'Task with empty labels',
|
|
276
|
+
labels: [],
|
|
277
|
+
projectId: '6cfCcrrCFg2xP94Q',
|
|
278
|
+
sectionId: undefined,
|
|
279
|
+
parentId: undefined,
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
it('should add task without labels field', async () => {
|
|
283
|
+
const mockApiResponse = createMockTask({
|
|
284
|
+
id: '8485093757',
|
|
285
|
+
content: 'Task without labels',
|
|
286
|
+
url: 'https://todoist.com/showTask?id=8485093757',
|
|
287
|
+
addedAt: '2025-08-13T22:09:56.123456Z',
|
|
288
|
+
});
|
|
289
|
+
mockTodoistApi.addTask.mockResolvedValue(mockApiResponse);
|
|
290
|
+
await addTasks.execute({
|
|
291
|
+
tasks: [
|
|
292
|
+
{
|
|
293
|
+
content: 'Task without labels',
|
|
294
|
+
projectId: '6cfCcrrCFg2xP94Q',
|
|
295
|
+
},
|
|
296
|
+
],
|
|
297
|
+
}, mockTodoistApi);
|
|
298
|
+
expect(mockTodoistApi.addTask).toHaveBeenCalledWith({
|
|
299
|
+
content: 'Task without labels',
|
|
300
|
+
labels: undefined,
|
|
301
|
+
projectId: '6cfCcrrCFg2xP94Q',
|
|
302
|
+
sectionId: undefined,
|
|
303
|
+
parentId: undefined,
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
it('should add multiple tasks with different label configurations', async () => {
|
|
307
|
+
const mockApiResponse1 = createMockTask({
|
|
308
|
+
id: '8485093758',
|
|
309
|
+
content: 'Task with labels',
|
|
310
|
+
labels: ['personal'],
|
|
311
|
+
});
|
|
312
|
+
const mockApiResponse2 = createMockTask({
|
|
313
|
+
id: '8485093759',
|
|
314
|
+
content: 'Task without labels',
|
|
315
|
+
});
|
|
316
|
+
const mockApiResponse3 = createMockTask({
|
|
317
|
+
id: '8485093760',
|
|
318
|
+
content: 'Task with multiple labels',
|
|
319
|
+
labels: ['work', 'urgent', 'review'],
|
|
320
|
+
});
|
|
321
|
+
mockTodoistApi.addTask
|
|
322
|
+
.mockResolvedValueOnce(mockApiResponse1)
|
|
323
|
+
.mockResolvedValueOnce(mockApiResponse2)
|
|
324
|
+
.mockResolvedValueOnce(mockApiResponse3);
|
|
325
|
+
await addTasks.execute({
|
|
326
|
+
tasks: [
|
|
327
|
+
{
|
|
328
|
+
content: 'Task with labels',
|
|
329
|
+
labels: ['personal'],
|
|
330
|
+
projectId: '6cfCcrrCFg2xP94Q',
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
content: 'Task without labels',
|
|
334
|
+
projectId: '6cfCcrrCFg2xP94Q',
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
content: 'Task with multiple labels',
|
|
338
|
+
labels: ['work', 'urgent', 'review'],
|
|
339
|
+
projectId: '6cfCcrrCFg2xP94Q',
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
}, mockTodoistApi);
|
|
343
|
+
expect(mockTodoistApi.addTask).toHaveBeenCalledTimes(3);
|
|
344
|
+
expect(mockTodoistApi.addTask).toHaveBeenNthCalledWith(1, expect.objectContaining({
|
|
345
|
+
labels: ['personal'],
|
|
346
|
+
}));
|
|
347
|
+
expect(mockTodoistApi.addTask).toHaveBeenNthCalledWith(2, expect.objectContaining({
|
|
348
|
+
labels: undefined,
|
|
349
|
+
}));
|
|
350
|
+
expect(mockTodoistApi.addTask).toHaveBeenNthCalledWith(3, expect.objectContaining({
|
|
351
|
+
labels: ['work', 'urgent', 'review'],
|
|
352
|
+
}));
|
|
353
|
+
});
|
|
213
354
|
});
|
|
214
355
|
describe('error handling', () => {
|
|
215
356
|
it('should throw error for invalid duration format', async () => {
|
|
216
|
-
await expect(addTasks.execute({
|
|
357
|
+
await expect(addTasks.execute({
|
|
358
|
+
tasks: [
|
|
359
|
+
{
|
|
360
|
+
content: 'Task with invalid duration',
|
|
361
|
+
duration: 'invalid',
|
|
362
|
+
projectId: '6cfCcrrCFg2xP94Q',
|
|
363
|
+
},
|
|
364
|
+
],
|
|
365
|
+
}, mockTodoistApi)).rejects.toThrow('Task "Task with invalid duration": Invalid duration format "invalid"');
|
|
217
366
|
});
|
|
218
367
|
it('should throw error for duration exceeding 24 hours', async () => {
|
|
219
|
-
await expect(addTasks.execute({
|
|
368
|
+
await expect(addTasks.execute({
|
|
369
|
+
tasks: [
|
|
370
|
+
{
|
|
371
|
+
content: 'Task with too long duration',
|
|
372
|
+
duration: '25h',
|
|
373
|
+
projectId: '6cfCcrrCFg2xP94Q',
|
|
374
|
+
},
|
|
375
|
+
],
|
|
376
|
+
}, mockTodoistApi)).rejects.toThrow('Task "Task with too long duration": Invalid duration format "25h": Duration cannot exceed 24 hours (1440 minutes)');
|
|
220
377
|
});
|
|
221
378
|
it('should propagate API errors', async () => {
|
|
222
379
|
const apiError = new Error('API Error: Task content is required');
|
|
223
380
|
mockTodoistApi.addTask.mockRejectedValue(apiError);
|
|
224
|
-
await expect(addTasks.execute({ tasks: [{ content: '' }] }, mockTodoistApi)).rejects.toThrow(apiError.message);
|
|
381
|
+
await expect(addTasks.execute({ tasks: [{ content: '', projectId: '6cfCcrrCFg2xP94Q' }] }, mockTodoistApi)).rejects.toThrow(apiError.message);
|
|
225
382
|
});
|
|
226
383
|
it('should handle partial failures when adding multiple tasks', async () => {
|
|
227
384
|
const mockApiResponse = createMockTask({
|
|
@@ -236,8 +393,8 @@ describe(`${ADD_TASKS} tool`, () => {
|
|
|
236
393
|
.mockRejectedValueOnce(apiError);
|
|
237
394
|
await expect(addTasks.execute({
|
|
238
395
|
tasks: [
|
|
239
|
-
{ content: 'First task content' },
|
|
240
|
-
{ content: 'Second task content' },
|
|
396
|
+
{ content: 'First task content', projectId: '6cfCcrrCFg2xP94Q' },
|
|
397
|
+
{ content: 'Second task content', projectId: '6cfCcrrCFg2xP94Q' },
|
|
241
398
|
],
|
|
242
399
|
}, mockTodoistApi)).rejects.toThrow('API Error: Second task failed');
|
|
243
400
|
// Verify first task was attempted
|
|
@@ -246,6 +403,8 @@ describe(`${ADD_TASKS} tool`, () => {
|
|
|
246
403
|
});
|
|
247
404
|
describe('next steps logic', () => {
|
|
248
405
|
it('should suggest find-tasks-by-date for today when hasToday is true', async () => {
|
|
406
|
+
// Clear any leftover mocks from previous tests
|
|
407
|
+
mockTodoistApi.addTask.mockClear();
|
|
249
408
|
const mockApiResponse = createMockTask({
|
|
250
409
|
id: '8485093755',
|
|
251
410
|
content: 'Task due today',
|
|
@@ -260,12 +419,22 @@ describe(`${ADD_TASKS} tool`, () => {
|
|
|
260
419
|
},
|
|
261
420
|
});
|
|
262
421
|
mockTodoistApi.addTask.mockResolvedValue(mockApiResponse);
|
|
263
|
-
const result = await addTasks.execute({
|
|
422
|
+
const result = await addTasks.execute({
|
|
423
|
+
tasks: [
|
|
424
|
+
{
|
|
425
|
+
content: 'Task due today',
|
|
426
|
+
dueString: 'today',
|
|
427
|
+
projectId: '6cfCcrrCFg2xP94Q',
|
|
428
|
+
},
|
|
429
|
+
],
|
|
430
|
+
}, mockTodoistApi);
|
|
264
431
|
const textContent = extractTextContent(result);
|
|
265
432
|
expect(textContent).toMatchSnapshot();
|
|
266
433
|
expect(textContent).toContain(`Use ${GET_OVERVIEW} to see your updated project organization`);
|
|
267
434
|
});
|
|
268
435
|
it('should suggest overview tool when no hasToday context', async () => {
|
|
436
|
+
// Clear any leftover mocks from previous tests
|
|
437
|
+
mockTodoistApi.addTask.mockClear();
|
|
269
438
|
const mockApiResponse = createMockTask({
|
|
270
439
|
id: '8485093756',
|
|
271
440
|
content: 'Regular task',
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assignment-integration.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/assignment-integration.test.ts"],"names":[],"mappings":""}
|