@doist/todoist-ai 4.13.2 → 4.13.3
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.
|
@@ -5,7 +5,7 @@ import { updateTasks } from '../update-tasks.js';
|
|
|
5
5
|
// Mock the Todoist API
|
|
6
6
|
const mockTodoistApi = {
|
|
7
7
|
updateTask: jest.fn(),
|
|
8
|
-
|
|
8
|
+
moveTask: jest.fn(),
|
|
9
9
|
};
|
|
10
10
|
const { UPDATE_TASKS } = ToolNames;
|
|
11
11
|
describe(`${UPDATE_TASKS} tool`, () => {
|
|
@@ -132,7 +132,7 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
132
132
|
url: 'https://todoist.com/showTask?id=8485093750',
|
|
133
133
|
addedAt: '2025-08-13T22:09:56.123456Z',
|
|
134
134
|
});
|
|
135
|
-
mockTodoistApi.
|
|
135
|
+
mockTodoistApi.moveTask.mockResolvedValue(mockApiResponse);
|
|
136
136
|
const result = await updateTasks.execute({
|
|
137
137
|
tasks: [
|
|
138
138
|
{
|
|
@@ -141,7 +141,7 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
141
141
|
},
|
|
142
142
|
],
|
|
143
143
|
}, mockTodoistApi);
|
|
144
|
-
expect(mockTodoistApi.
|
|
144
|
+
expect(mockTodoistApi.moveTask).toHaveBeenCalledWith('8485093750', {
|
|
145
145
|
projectId: 'new-project-id',
|
|
146
146
|
});
|
|
147
147
|
expect(mockTodoistApi.updateTask).not.toHaveBeenCalled();
|
|
@@ -159,7 +159,7 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
159
159
|
url: 'https://todoist.com/showTask?id=8485093751',
|
|
160
160
|
addedAt: '2025-08-13T22:09:56.123456Z',
|
|
161
161
|
});
|
|
162
|
-
mockTodoistApi.
|
|
162
|
+
mockTodoistApi.moveTask.mockResolvedValue(mockApiResponse);
|
|
163
163
|
const result = await updateTasks.execute({
|
|
164
164
|
tasks: [
|
|
165
165
|
{
|
|
@@ -168,7 +168,7 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
168
168
|
},
|
|
169
169
|
],
|
|
170
170
|
}, mockTodoistApi);
|
|
171
|
-
expect(mockTodoistApi.
|
|
171
|
+
expect(mockTodoistApi.moveTask).toHaveBeenCalledWith('8485093751', {
|
|
172
172
|
parentId: 'parent-task-123',
|
|
173
173
|
});
|
|
174
174
|
expect(mockTodoistApi.updateTask).not.toHaveBeenCalled();
|
|
@@ -200,7 +200,7 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
200
200
|
timezone: null,
|
|
201
201
|
},
|
|
202
202
|
});
|
|
203
|
-
mockTodoistApi.
|
|
203
|
+
mockTodoistApi.moveTask.mockResolvedValue(movedTask);
|
|
204
204
|
mockTodoistApi.updateTask.mockResolvedValue(updatedTask);
|
|
205
205
|
const result = await updateTasks.execute({
|
|
206
206
|
tasks: [
|
|
@@ -214,8 +214,8 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
214
214
|
},
|
|
215
215
|
],
|
|
216
216
|
}, mockTodoistApi);
|
|
217
|
-
// Should call
|
|
218
|
-
expect(mockTodoistApi.
|
|
217
|
+
// Should call moveTask first for the projectId
|
|
218
|
+
expect(mockTodoistApi.moveTask).toHaveBeenCalledWith('8485093752', {
|
|
219
219
|
projectId: 'different-project-id',
|
|
220
220
|
});
|
|
221
221
|
// Then call updateTask for the other properties
|
|
@@ -307,7 +307,7 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
307
307
|
duration: { amount: 120, unit: 'minute' },
|
|
308
308
|
projectId: 'new-project-id',
|
|
309
309
|
});
|
|
310
|
-
mockTodoistApi.
|
|
310
|
+
mockTodoistApi.moveTask.mockResolvedValue(movedTask);
|
|
311
311
|
mockTodoistApi.updateTask.mockResolvedValue(updatedTask);
|
|
312
312
|
const result = await updateTasks.execute({
|
|
313
313
|
tasks: [
|
|
@@ -319,8 +319,8 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
319
319
|
},
|
|
320
320
|
],
|
|
321
321
|
}, mockTodoistApi);
|
|
322
|
-
// Should call
|
|
323
|
-
expect(mockTodoistApi.
|
|
322
|
+
// Should call moveTask first
|
|
323
|
+
expect(mockTodoistApi.moveTask).toHaveBeenCalledWith('8485093755', {
|
|
324
324
|
projectId: 'new-project-id',
|
|
325
325
|
});
|
|
326
326
|
// Then call updateTask with duration
|
|
@@ -485,21 +485,21 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
485
485
|
createMockTask({ id: '6cPHJj2MV4HMj92W', content: 'Second task', sectionId }),
|
|
486
486
|
];
|
|
487
487
|
// Each task should be moved individually to avoid bulk operation issues
|
|
488
|
-
mockTodoistApi.
|
|
489
|
-
.mockResolvedValueOnce(
|
|
490
|
-
.mockResolvedValueOnce(
|
|
488
|
+
mockTodoistApi.moveTask
|
|
489
|
+
.mockResolvedValueOnce(mockResponses[0])
|
|
490
|
+
.mockResolvedValueOnce(mockResponses[1]);
|
|
491
491
|
const result = await updateTasks.execute({
|
|
492
492
|
tasks: [
|
|
493
493
|
{ id: '6cPHJm59x4WhMwR4', sectionId },
|
|
494
494
|
{ id: '6cPHJj2MV4HMj92W', sectionId },
|
|
495
495
|
],
|
|
496
496
|
}, mockTodoistApi);
|
|
497
|
-
// Should call
|
|
498
|
-
expect(mockTodoistApi.
|
|
499
|
-
expect(mockTodoistApi.
|
|
497
|
+
// Should call moveTask twice, once for each task individually
|
|
498
|
+
expect(mockTodoistApi.moveTask).toHaveBeenCalledTimes(2);
|
|
499
|
+
expect(mockTodoistApi.moveTask).toHaveBeenNthCalledWith(1, '6cPHJm59x4WhMwR4', {
|
|
500
500
|
sectionId,
|
|
501
501
|
});
|
|
502
|
-
expect(mockTodoistApi.
|
|
502
|
+
expect(mockTodoistApi.moveTask).toHaveBeenNthCalledWith(2, '6cPHJj2MV4HMj92W', {
|
|
503
503
|
sectionId,
|
|
504
504
|
});
|
|
505
505
|
expect(mockTodoistApi.updateTask).not.toHaveBeenCalled();
|
|
@@ -518,10 +518,10 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
518
518
|
createMockTask({ id: TASK_3, content: 'Task 3', parentId: 'parent-task-123' }),
|
|
519
519
|
];
|
|
520
520
|
// Each task should be moved individually
|
|
521
|
-
mockTodoistApi.
|
|
522
|
-
.mockResolvedValueOnce(
|
|
523
|
-
.mockResolvedValueOnce(
|
|
524
|
-
.mockResolvedValueOnce(
|
|
521
|
+
mockTodoistApi.moveTask
|
|
522
|
+
.mockResolvedValueOnce(mockResponses[0])
|
|
523
|
+
.mockResolvedValueOnce(mockResponses[1])
|
|
524
|
+
.mockResolvedValueOnce(mockResponses[2]);
|
|
525
525
|
const result = await updateTasks.execute({
|
|
526
526
|
tasks: [
|
|
527
527
|
{ id: '8485093748', projectId: 'new-project-id' },
|
|
@@ -530,14 +530,14 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
530
530
|
],
|
|
531
531
|
}, mockTodoistApi);
|
|
532
532
|
// Verify API was called correctly - 3 individual move calls
|
|
533
|
-
expect(mockTodoistApi.
|
|
534
|
-
expect(mockTodoistApi.
|
|
533
|
+
expect(mockTodoistApi.moveTask).toHaveBeenCalledTimes(3);
|
|
534
|
+
expect(mockTodoistApi.moveTask).toHaveBeenNthCalledWith(1, '8485093748', {
|
|
535
535
|
projectId: 'new-project-id',
|
|
536
536
|
});
|
|
537
|
-
expect(mockTodoistApi.
|
|
537
|
+
expect(mockTodoistApi.moveTask).toHaveBeenNthCalledWith(2, '8485093749', {
|
|
538
538
|
sectionId: 'new-section-id',
|
|
539
539
|
});
|
|
540
|
-
expect(mockTodoistApi.
|
|
540
|
+
expect(mockTodoistApi.moveTask).toHaveBeenNthCalledWith(3, '8485093750', {
|
|
541
541
|
parentId: 'parent-task-123',
|
|
542
542
|
});
|
|
543
543
|
expect(mockTodoistApi.updateTask).not.toHaveBeenCalled();
|
|
@@ -556,10 +556,10 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
556
556
|
url: 'https://todoist.com/showTask?id=8485093751',
|
|
557
557
|
addedAt: '2025-08-13T22:09:59.123456Z',
|
|
558
558
|
});
|
|
559
|
-
mockTodoistApi.
|
|
559
|
+
mockTodoistApi.moveTask.mockResolvedValue(mockTaskResponse);
|
|
560
560
|
const result = await updateTasks.execute({ tasks: [{ id: '8485093751', sectionId: 'target-section' }] }, mockTodoistApi);
|
|
561
|
-
expect(mockTodoistApi.
|
|
562
|
-
expect(mockTodoistApi.
|
|
561
|
+
expect(mockTodoistApi.moveTask).toHaveBeenCalledTimes(1);
|
|
562
|
+
expect(mockTodoistApi.moveTask).toHaveBeenCalledWith('8485093751', {
|
|
563
563
|
sectionId: 'target-section',
|
|
564
564
|
});
|
|
565
565
|
expect(mockTodoistApi.updateTask).not.toHaveBeenCalled();
|
|
@@ -600,10 +600,10 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
600
600
|
}),
|
|
601
601
|
];
|
|
602
602
|
// Each task should be moved individually
|
|
603
|
-
mockTodoistApi.
|
|
604
|
-
.mockResolvedValueOnce(
|
|
605
|
-
.mockResolvedValueOnce(
|
|
606
|
-
.mockResolvedValueOnce(
|
|
603
|
+
mockTodoistApi.moveTask
|
|
604
|
+
.mockResolvedValueOnce(mockResponses[0])
|
|
605
|
+
.mockResolvedValueOnce(mockResponses[1])
|
|
606
|
+
.mockResolvedValueOnce(mockResponses[2]);
|
|
607
607
|
const result = await updateTasks.execute({
|
|
608
608
|
tasks: [
|
|
609
609
|
{ id: 'task-1', projectId: 'project-new' },
|
|
@@ -612,14 +612,14 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
612
612
|
],
|
|
613
613
|
}, mockTodoistApi);
|
|
614
614
|
// Verify API was called correctly - 3 individual move calls
|
|
615
|
-
expect(mockTodoistApi.
|
|
616
|
-
expect(mockTodoistApi.
|
|
615
|
+
expect(mockTodoistApi.moveTask).toHaveBeenCalledTimes(3);
|
|
616
|
+
expect(mockTodoistApi.moveTask).toHaveBeenNthCalledWith(1, 'task-1', {
|
|
617
617
|
projectId: 'project-new',
|
|
618
618
|
});
|
|
619
|
-
expect(mockTodoistApi.
|
|
619
|
+
expect(mockTodoistApi.moveTask).toHaveBeenNthCalledWith(2, 'task-2', {
|
|
620
620
|
parentId: 'task-1',
|
|
621
621
|
});
|
|
622
|
-
expect(mockTodoistApi.
|
|
622
|
+
expect(mockTodoistApi.moveTask).toHaveBeenNthCalledWith(3, 'task-3', {
|
|
623
623
|
sectionId: 'section-new',
|
|
624
624
|
});
|
|
625
625
|
expect(mockTodoistApi.updateTask).not.toHaveBeenCalled();
|
|
@@ -640,7 +640,7 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
640
640
|
url: 'https://todoist.com/showTask?id=8485093752',
|
|
641
641
|
addedAt: '2025-08-13T22:10:07.123456Z',
|
|
642
642
|
});
|
|
643
|
-
mockTodoistApi.
|
|
643
|
+
mockTodoistApi.moveTask.mockResolvedValue(mockResponse);
|
|
644
644
|
const result = await updateTasks.execute({
|
|
645
645
|
tasks: [
|
|
646
646
|
{
|
|
@@ -650,7 +650,7 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
650
650
|
},
|
|
651
651
|
],
|
|
652
652
|
}, mockTodoistApi);
|
|
653
|
-
expect(mockTodoistApi.
|
|
653
|
+
expect(mockTodoistApi.moveTask).toHaveBeenCalledWith('8485093752', {
|
|
654
654
|
projectId: 'new-project-only',
|
|
655
655
|
});
|
|
656
656
|
expect(mockTodoistApi.updateTask).not.toHaveBeenCalled();
|
|
@@ -667,7 +667,7 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
667
667
|
it('should handle empty updates (only id provided)', async () => {
|
|
668
668
|
const result = await updateTasks.execute({ tasks: [{ id: '8485093753' }] }, mockTodoistApi);
|
|
669
669
|
// No API calls should be made since no move parameters are provided
|
|
670
|
-
expect(mockTodoistApi.
|
|
670
|
+
expect(mockTodoistApi.moveTask).not.toHaveBeenCalled();
|
|
671
671
|
expect(mockTodoistApi.updateTask).not.toHaveBeenCalled();
|
|
672
672
|
// Returns empty results since no moves were processed
|
|
673
673
|
const textContent = extractTextContent(result);
|
|
@@ -691,22 +691,22 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
691
691
|
});
|
|
692
692
|
it('should propagate API errors for individual task moves', async () => {
|
|
693
693
|
const apiError = new Error('API Error: Task not found');
|
|
694
|
-
mockTodoistApi.
|
|
694
|
+
mockTodoistApi.moveTask.mockRejectedValue(apiError);
|
|
695
695
|
await expect(updateTasks.execute({ tasks: [{ id: 'non-existent-task', projectId: 'some-project' }] }, mockTodoistApi)).rejects.toThrow('API Error: Task not found');
|
|
696
696
|
});
|
|
697
697
|
it('should handle validation errors', async () => {
|
|
698
698
|
const validationError = new Error('API Error: Invalid section ID');
|
|
699
|
-
mockTodoistApi.
|
|
699
|
+
mockTodoistApi.moveTask.mockRejectedValue(validationError);
|
|
700
700
|
await expect(updateTasks.execute({ tasks: [{ id: 'task-1', sectionId: 'invalid-section-format' }] }, mockTodoistApi)).rejects.toThrow('API Error: Invalid section ID');
|
|
701
701
|
});
|
|
702
702
|
it('should handle permission errors', async () => {
|
|
703
703
|
const permissionError = new Error('API Error: Insufficient permissions to move task');
|
|
704
|
-
mockTodoistApi.
|
|
704
|
+
mockTodoistApi.moveTask.mockRejectedValue(permissionError);
|
|
705
705
|
await expect(updateTasks.execute({ tasks: [{ id: 'restricted-task', projectId: 'restricted-project' }] }, mockTodoistApi)).rejects.toThrow('API Error: Insufficient permissions to move task');
|
|
706
706
|
});
|
|
707
707
|
it('should handle circular parent dependency errors', async () => {
|
|
708
708
|
const circularError = new Error('API Error: Circular dependency detected');
|
|
709
|
-
mockTodoistApi.
|
|
709
|
+
mockTodoistApi.moveTask.mockRejectedValue(circularError);
|
|
710
710
|
await expect(updateTasks.execute({
|
|
711
711
|
tasks: [
|
|
712
712
|
{
|
|
@@ -97,16 +97,16 @@ const updateTasks = {
|
|
|
97
97
|
updateArgs = { ...updateArgs, assigneeId: validation.resolvedUser?.userId };
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
|
-
// If no move parameters are provided, use updateTask without
|
|
100
|
+
// If no move parameters are provided, use updateTask without moveTask
|
|
101
101
|
if (!projectId && !sectionId && !parentId) {
|
|
102
102
|
return await client.updateTask(id, updateArgs);
|
|
103
103
|
}
|
|
104
104
|
const moveArgs = createMoveTaskArgs(id, projectId, sectionId, parentId);
|
|
105
|
-
const
|
|
105
|
+
const movedTask = await client.moveTask(id, moveArgs);
|
|
106
106
|
if (Object.keys(updateArgs).length > 0) {
|
|
107
107
|
return await client.updateTask(id, updateArgs);
|
|
108
108
|
}
|
|
109
|
-
return
|
|
109
|
+
return movedTask;
|
|
110
110
|
});
|
|
111
111
|
const updatedTasks = (await Promise.all(updateTasksPromises)).filter((task) => task !== undefined);
|
|
112
112
|
const mappedTasks = updatedTasks.map(mapTask);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doist/todoist-ai",
|
|
3
|
-
"version": "4.13.
|
|
3
|
+
"version": "4.13.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"prepare": "husky"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@doist/todoist-api-typescript": "5.
|
|
48
|
+
"@doist/todoist-api-typescript": "5.7.0",
|
|
49
49
|
"@modelcontextprotocol/sdk": "^1.11.1",
|
|
50
50
|
"date-fns": "^4.1.0",
|
|
51
51
|
"dotenv": "^16.5.0",
|