@doist/todoist-ai 2.2.2 → 3.0.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.
Files changed (168) hide show
  1. package/README.md +11 -3
  2. package/dist/index.d.ts +496 -255
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +41 -29
  5. package/dist/mcp-helpers.d.ts +25 -3
  6. package/dist/mcp-helpers.d.ts.map +1 -1
  7. package/dist/mcp-helpers.js +37 -19
  8. package/dist/mcp-server.d.ts.map +1 -1
  9. package/dist/mcp-server.js +32 -28
  10. package/dist/tools/__tests__/add-tasks.test.d.ts +2 -0
  11. package/dist/tools/__tests__/add-tasks.test.d.ts.map +1 -0
  12. package/dist/tools/__tests__/{tasks-add-multiple.test.js → add-tasks.test.js} +85 -81
  13. package/dist/tools/__tests__/complete-tasks.test.d.ts +2 -0
  14. package/dist/tools/__tests__/complete-tasks.test.d.ts.map +1 -0
  15. package/dist/tools/__tests__/complete-tasks.test.js +206 -0
  16. package/dist/tools/__tests__/delete-object.test.d.ts +2 -0
  17. package/dist/tools/__tests__/delete-object.test.d.ts.map +1 -0
  18. package/dist/tools/__tests__/{delete-one.test.js → delete-object.test.js} +42 -22
  19. package/dist/tools/__tests__/find-completed-tasks.test.d.ts +2 -0
  20. package/dist/tools/__tests__/find-completed-tasks.test.d.ts.map +1 -0
  21. package/dist/tools/__tests__/{tasks-list-completed.test.js → find-completed-tasks.test.js} +13 -36
  22. package/dist/tools/__tests__/find-projects.test.d.ts +2 -0
  23. package/dist/tools/__tests__/find-projects.test.d.ts.map +1 -0
  24. package/dist/tools/__tests__/{projects-list.test.js → find-projects.test.js} +55 -39
  25. package/dist/tools/__tests__/find-sections.test.d.ts +2 -0
  26. package/dist/tools/__tests__/find-sections.test.d.ts.map +1 -0
  27. package/dist/tools/__tests__/{sections-search.test.js → find-sections.test.js} +64 -50
  28. package/dist/tools/__tests__/find-tasks-by-date.test.d.ts +2 -0
  29. package/dist/tools/__tests__/find-tasks-by-date.test.d.ts.map +1 -0
  30. package/dist/tools/__tests__/{tasks-list-by-date.test.js → find-tasks-by-date.test.js} +96 -14
  31. package/dist/tools/__tests__/find-tasks.test.d.ts +2 -0
  32. package/dist/tools/__tests__/find-tasks.test.d.ts.map +1 -0
  33. package/dist/tools/__tests__/find-tasks.test.js +334 -0
  34. package/dist/tools/__tests__/get-overview.test.d.ts +2 -0
  35. package/dist/tools/__tests__/get-overview.test.d.ts.map +1 -0
  36. package/dist/tools/__tests__/{overview.test.js → get-overview.test.js} +77 -13
  37. package/dist/tools/__tests__/manage-projects.test.d.ts +2 -0
  38. package/dist/tools/__tests__/manage-projects.test.d.ts.map +1 -0
  39. package/dist/tools/__tests__/{projects-manage.test.js → manage-projects.test.js} +33 -30
  40. package/dist/tools/__tests__/manage-sections.test.d.ts +2 -0
  41. package/dist/tools/__tests__/manage-sections.test.d.ts.map +1 -0
  42. package/dist/tools/__tests__/manage-sections.test.js +162 -0
  43. package/dist/tools/__tests__/update-tasks.test.d.ts +2 -0
  44. package/dist/tools/__tests__/update-tasks.test.d.ts.map +1 -0
  45. package/dist/tools/__tests__/update-tasks.test.js +645 -0
  46. package/dist/tools/{tasks-add-multiple.d.ts → add-tasks.d.ts} +36 -16
  47. package/dist/tools/add-tasks.d.ts.map +1 -0
  48. package/dist/tools/{tasks-add-multiple.js → add-tasks.js} +39 -4
  49. package/dist/tools/complete-tasks.d.ts +40 -0
  50. package/dist/tools/complete-tasks.d.ts.map +1 -0
  51. package/dist/tools/complete-tasks.js +68 -0
  52. package/dist/tools/delete-object.d.ts +38 -0
  53. package/dist/tools/delete-object.d.ts.map +1 -0
  54. package/dist/tools/delete-object.js +69 -0
  55. package/dist/tools/find-completed-tasks.d.ts +74 -0
  56. package/dist/tools/find-completed-tasks.d.ts.map +1 -0
  57. package/dist/tools/find-completed-tasks.js +112 -0
  58. package/dist/tools/find-projects.d.ts +53 -0
  59. package/dist/tools/find-projects.d.ts.map +1 -0
  60. package/dist/tools/find-projects.js +101 -0
  61. package/dist/tools/find-sections.d.ts +42 -0
  62. package/dist/tools/find-sections.d.ts.map +1 -0
  63. package/dist/tools/find-sections.js +96 -0
  64. package/dist/tools/find-tasks-by-date.d.ts +59 -0
  65. package/dist/tools/find-tasks-by-date.d.ts.map +1 -0
  66. package/dist/tools/find-tasks-by-date.js +121 -0
  67. package/dist/tools/find-tasks.d.ts +65 -0
  68. package/dist/tools/find-tasks.d.ts.map +1 -0
  69. package/dist/tools/find-tasks.js +182 -0
  70. package/dist/tools/get-overview.d.ts +67 -0
  71. package/dist/tools/get-overview.d.ts.map +1 -0
  72. package/dist/tools/{overview.js → get-overview.js} +66 -19
  73. package/dist/tools/manage-projects.d.ts +35 -0
  74. package/dist/tools/manage-projects.d.ts.map +1 -0
  75. package/dist/tools/manage-projects.js +63 -0
  76. package/dist/tools/manage-sections.d.ts +38 -0
  77. package/dist/tools/manage-sections.d.ts.map +1 -0
  78. package/dist/tools/manage-sections.js +78 -0
  79. package/dist/tools/update-tasks.d.ts +94 -0
  80. package/dist/tools/update-tasks.d.ts.map +1 -0
  81. package/dist/tools/update-tasks.js +120 -0
  82. package/dist/utils/constants.d.ts +35 -0
  83. package/dist/utils/constants.d.ts.map +1 -0
  84. package/dist/utils/constants.js +37 -0
  85. package/dist/utils/response-builders.d.ts +88 -0
  86. package/dist/utils/response-builders.d.ts.map +1 -0
  87. package/dist/utils/response-builders.js +202 -0
  88. package/dist/{tools → utils}/test-helpers.d.ts +16 -0
  89. package/dist/utils/test-helpers.d.ts.map +1 -0
  90. package/dist/{tools → utils}/test-helpers.js +51 -0
  91. package/dist/utils/tool-names.d.ts +23 -0
  92. package/dist/utils/tool-names.d.ts.map +1 -0
  93. package/dist/utils/tool-names.js +25 -0
  94. package/package.json +1 -1
  95. package/dist/tools/__tests__/delete-one.test.d.ts +0 -2
  96. package/dist/tools/__tests__/delete-one.test.d.ts.map +0 -1
  97. package/dist/tools/__tests__/overview.test.d.ts +0 -2
  98. package/dist/tools/__tests__/overview.test.d.ts.map +0 -1
  99. package/dist/tools/__tests__/projects-list.test.d.ts +0 -2
  100. package/dist/tools/__tests__/projects-list.test.d.ts.map +0 -1
  101. package/dist/tools/__tests__/projects-manage.test.d.ts +0 -2
  102. package/dist/tools/__tests__/projects-manage.test.d.ts.map +0 -1
  103. package/dist/tools/__tests__/sections-manage.test.d.ts +0 -2
  104. package/dist/tools/__tests__/sections-manage.test.d.ts.map +0 -1
  105. package/dist/tools/__tests__/sections-manage.test.js +0 -138
  106. package/dist/tools/__tests__/sections-search.test.d.ts +0 -2
  107. package/dist/tools/__tests__/sections-search.test.d.ts.map +0 -1
  108. package/dist/tools/__tests__/tasks-add-multiple.test.d.ts +0 -2
  109. package/dist/tools/__tests__/tasks-add-multiple.test.d.ts.map +0 -1
  110. package/dist/tools/__tests__/tasks-complete-multiple.test.d.ts +0 -2
  111. package/dist/tools/__tests__/tasks-complete-multiple.test.d.ts.map +0 -1
  112. package/dist/tools/__tests__/tasks-complete-multiple.test.js +0 -146
  113. package/dist/tools/__tests__/tasks-list-by-date.test.d.ts +0 -2
  114. package/dist/tools/__tests__/tasks-list-by-date.test.d.ts.map +0 -1
  115. package/dist/tools/__tests__/tasks-list-completed.test.d.ts +0 -2
  116. package/dist/tools/__tests__/tasks-list-completed.test.d.ts.map +0 -1
  117. package/dist/tools/__tests__/tasks-list-for-container.test.d.ts +0 -2
  118. package/dist/tools/__tests__/tasks-list-for-container.test.d.ts.map +0 -1
  119. package/dist/tools/__tests__/tasks-list-for-container.test.js +0 -232
  120. package/dist/tools/__tests__/tasks-organize-multiple.test.d.ts +0 -2
  121. package/dist/tools/__tests__/tasks-organize-multiple.test.d.ts.map +0 -1
  122. package/dist/tools/__tests__/tasks-organize-multiple.test.js +0 -245
  123. package/dist/tools/__tests__/tasks-search.test.d.ts +0 -2
  124. package/dist/tools/__tests__/tasks-search.test.d.ts.map +0 -1
  125. package/dist/tools/__tests__/tasks-search.test.js +0 -106
  126. package/dist/tools/__tests__/tasks-update-one.test.d.ts +0 -2
  127. package/dist/tools/__tests__/tasks-update-one.test.d.ts.map +0 -1
  128. package/dist/tools/__tests__/tasks-update-one.test.js +0 -251
  129. package/dist/tools/delete-one.d.ts +0 -17
  130. package/dist/tools/delete-one.d.ts.map +0 -1
  131. package/dist/tools/delete-one.js +0 -25
  132. package/dist/tools/overview.d.ts +0 -14
  133. package/dist/tools/overview.d.ts.map +0 -1
  134. package/dist/tools/projects-list.d.ts +0 -29
  135. package/dist/tools/projects-list.d.ts.map +0 -1
  136. package/dist/tools/projects-list.js +0 -39
  137. package/dist/tools/projects-manage.d.ts +0 -24
  138. package/dist/tools/projects-manage.d.ts.map +0 -1
  139. package/dist/tools/projects-manage.js +0 -26
  140. package/dist/tools/sections-manage.d.ts +0 -23
  141. package/dist/tools/sections-manage.d.ts.map +0 -1
  142. package/dist/tools/sections-manage.js +0 -37
  143. package/dist/tools/sections-search.d.ts +0 -18
  144. package/dist/tools/sections-search.d.ts.map +0 -1
  145. package/dist/tools/sections-search.js +0 -27
  146. package/dist/tools/tasks-add-multiple.d.ts.map +0 -1
  147. package/dist/tools/tasks-complete-multiple.d.ts +0 -16
  148. package/dist/tools/tasks-complete-multiple.d.ts.map +0 -1
  149. package/dist/tools/tasks-complete-multiple.js +0 -23
  150. package/dist/tools/tasks-list-by-date.d.ts +0 -34
  151. package/dist/tools/tasks-list-by-date.d.ts.map +0 -1
  152. package/dist/tools/tasks-list-by-date.js +0 -53
  153. package/dist/tools/tasks-list-completed.d.ts +0 -44
  154. package/dist/tools/tasks-list-completed.d.ts.map +0 -1
  155. package/dist/tools/tasks-list-completed.js +0 -49
  156. package/dist/tools/tasks-list-for-container.d.ts +0 -34
  157. package/dist/tools/tasks-list-for-container.d.ts.map +0 -1
  158. package/dist/tools/tasks-list-for-container.js +0 -48
  159. package/dist/tools/tasks-organize-multiple.d.ts +0 -37
  160. package/dist/tools/tasks-organize-multiple.d.ts.map +0 -1
  161. package/dist/tools/tasks-organize-multiple.js +0 -34
  162. package/dist/tools/tasks-search.d.ts +0 -32
  163. package/dist/tools/tasks-search.d.ts.map +0 -1
  164. package/dist/tools/tasks-search.js +0 -30
  165. package/dist/tools/tasks-update-one.d.ts +0 -29
  166. package/dist/tools/tasks-update-one.d.ts.map +0 -1
  167. package/dist/tools/tasks-update-one.js +0 -63
  168. package/dist/tools/test-helpers.d.ts.map +0 -1
@@ -1,245 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { tasksOrganizeMultiple } from '../tasks-organize-multiple.js';
3
- import { TEST_IDS, createMockTask } from '../test-helpers.js';
4
- // Mock the Todoist API
5
- const mockTodoistApi = {
6
- updateTask: jest.fn(),
7
- moveTasks: jest.fn(),
8
- };
9
- describe('tasks-organize-multiple tool', () => {
10
- beforeEach(() => {
11
- jest.clearAllMocks();
12
- });
13
- describe('organizing multiple tasks', () => {
14
- it('should move multiple tasks to the same destination', async () => {
15
- const sectionId = '6cfPqr9xgvmgW6J0';
16
- const mockResponses = [
17
- createMockTask({ id: '6cPuJm79x4QhMwR4', content: 'First task', sectionId }),
18
- createMockTask({ id: '6cPHJj2MV4HMj92W', content: 'Second task', sectionId }),
19
- ];
20
- // Each task should be moved individually to avoid bulk operation issues
21
- mockTodoistApi.moveTasks
22
- .mockResolvedValueOnce([mockResponses[0]])
23
- .mockResolvedValueOnce([mockResponses[1]]);
24
- const result = await tasksOrganizeMultiple.execute({
25
- tasks: [
26
- { id: '6cPHJm59x4WhMwR4', sectionId },
27
- { id: '6cPHJj2MV4HMj92W', sectionId },
28
- ],
29
- }, mockTodoistApi);
30
- // Should call moveTasks twice, once for each task individually
31
- expect(mockTodoistApi.moveTasks).toHaveBeenCalledTimes(2);
32
- expect(mockTodoistApi.moveTasks).toHaveBeenNthCalledWith(1, ['6cPHJm59x4WhMwR4'], {
33
- sectionId,
34
- });
35
- expect(mockTodoistApi.moveTasks).toHaveBeenNthCalledWith(2, ['6cPHJj2MV4HMj92W'], {
36
- sectionId,
37
- });
38
- expect(mockTodoistApi.updateTask).not.toHaveBeenCalled();
39
- expect(result).toEqual(mockResponses);
40
- });
41
- it('should move multiple tasks with different destinations', async () => {
42
- const { TASK_1, TASK_2, TASK_3 } = TEST_IDS;
43
- const mockResponses = [
44
- createMockTask({ id: TASK_1, content: 'Task 1', projectId: 'new-project-id' }),
45
- createMockTask({ id: TASK_2, content: 'Task 2', sectionId: 'new-section-id' }),
46
- createMockTask({ id: TASK_3, content: 'Task 3', parentId: 'parent-task-123' }),
47
- ];
48
- // Each task should be moved individually
49
- mockTodoistApi.moveTasks
50
- .mockResolvedValueOnce([mockResponses[0]])
51
- .mockResolvedValueOnce([mockResponses[1]])
52
- .mockResolvedValueOnce([mockResponses[2]]);
53
- const result = await tasksOrganizeMultiple.execute({
54
- tasks: [
55
- { id: '8485093748', projectId: 'new-project-id' },
56
- { id: '8485093749', sectionId: 'new-section-id' },
57
- { id: '8485093750', parentId: 'parent-task-123' },
58
- ],
59
- }, mockTodoistApi);
60
- // Verify API was called correctly - 3 individual move calls
61
- expect(mockTodoistApi.moveTasks).toHaveBeenCalledTimes(3);
62
- expect(mockTodoistApi.moveTasks).toHaveBeenNthCalledWith(1, ['8485093748'], {
63
- projectId: 'new-project-id',
64
- });
65
- expect(mockTodoistApi.moveTasks).toHaveBeenNthCalledWith(2, ['8485093749'], {
66
- sectionId: 'new-section-id',
67
- });
68
- expect(mockTodoistApi.moveTasks).toHaveBeenNthCalledWith(3, ['8485093750'], {
69
- parentId: 'parent-task-123',
70
- });
71
- expect(mockTodoistApi.updateTask).not.toHaveBeenCalled();
72
- // Verify results are returned in the correct order
73
- expect(result).toEqual(mockResponses);
74
- });
75
- it('should handle single task organization', async () => {
76
- const mockTaskResponse = createMockTask({
77
- id: '8485093751',
78
- content: 'Single task update',
79
- sectionId: 'target-section',
80
- url: 'https://todoist.com/showTask?id=8485093751',
81
- addedAt: '2025-08-13T22:09:59.123456Z',
82
- });
83
- mockTodoistApi.moveTasks.mockResolvedValue([mockTaskResponse]);
84
- const result = await tasksOrganizeMultiple.execute({ tasks: [{ id: '8485093751', sectionId: 'target-section' }] }, mockTodoistApi);
85
- expect(mockTodoistApi.moveTasks).toHaveBeenCalledTimes(1);
86
- expect(mockTodoistApi.moveTasks).toHaveBeenCalledWith(['8485093751'], {
87
- sectionId: 'target-section',
88
- });
89
- expect(mockTodoistApi.updateTask).not.toHaveBeenCalled();
90
- expect(result).toEqual([mockTaskResponse]);
91
- });
92
- it('should handle complex reorganization scenario', async () => {
93
- // Simulate moving tasks to different destinations (one move param per task)
94
- const mockResponses = [
95
- createMockTask({
96
- id: 'task-1',
97
- content: 'Task moved to new project',
98
- projectId: 'project-new',
99
- url: 'https://todoist.com/showTask?id=task-1',
100
- addedAt: '2025-08-13T22:10:00.123456Z',
101
- }),
102
- createMockTask({
103
- id: 'task-2',
104
- content: 'Task made into subtask',
105
- parentId: 'task-1',
106
- url: 'https://todoist.com/showTask?id=task-2',
107
- addedAt: '2025-08-13T22:10:01.123456Z',
108
- }),
109
- createMockTask({
110
- id: 'task-3',
111
- content: 'Task moved to section',
112
- sectionId: 'section-new',
113
- url: 'https://todoist.com/showTask?id=task-3',
114
- addedAt: '2025-08-13T22:10:02.123456Z',
115
- }),
116
- ];
117
- // Each task should be moved individually
118
- mockTodoistApi.moveTasks
119
- .mockResolvedValueOnce([mockResponses[0]])
120
- .mockResolvedValueOnce([mockResponses[1]])
121
- .mockResolvedValueOnce([mockResponses[2]]);
122
- const result = await tasksOrganizeMultiple.execute({
123
- tasks: [
124
- { id: 'task-1', projectId: 'project-new' },
125
- { id: 'task-2', parentId: 'task-1' },
126
- { id: 'task-3', sectionId: 'section-new' },
127
- ],
128
- }, mockTodoistApi);
129
- // Verify API was called correctly - 3 individual move calls
130
- expect(mockTodoistApi.moveTasks).toHaveBeenCalledTimes(3);
131
- expect(mockTodoistApi.moveTasks).toHaveBeenNthCalledWith(1, ['task-1'], {
132
- projectId: 'project-new',
133
- });
134
- expect(mockTodoistApi.moveTasks).toHaveBeenNthCalledWith(2, ['task-2'], {
135
- parentId: 'task-1',
136
- });
137
- expect(mockTodoistApi.moveTasks).toHaveBeenNthCalledWith(3, ['task-3'], {
138
- sectionId: 'section-new',
139
- });
140
- expect(mockTodoistApi.updateTask).not.toHaveBeenCalled();
141
- expect(result).toEqual(mockResponses);
142
- });
143
- });
144
- describe('order management', () => {
145
- it('should skip tasks with only order changes (no move operations)', async () => {
146
- const result = await tasksOrganizeMultiple.execute({
147
- tasks: [
148
- { id: 'task-a', order: 3 }, // Only order, no move params
149
- { id: 'task-b', order: 1 }, // Only order, no move params
150
- { id: 'task-c', order: 2 }, // Only order, no move params
151
- ],
152
- }, mockTodoistApi);
153
- // No API calls should be made since only order is specified (no projectId/sectionId/parentId)
154
- expect(mockTodoistApi.moveTasks).not.toHaveBeenCalled();
155
- expect(mockTodoistApi.updateTask).not.toHaveBeenCalled();
156
- // Returns empty array since no moves were processed
157
- expect(result).toEqual([]);
158
- });
159
- });
160
- describe('partial updates', () => {
161
- it('should handle move operations with single parameters', async () => {
162
- const mockResponse = createMockTask({
163
- id: '8485093752',
164
- content: 'Minimal update task',
165
- projectId: 'new-project-only',
166
- url: 'https://todoist.com/showTask?id=8485093752',
167
- addedAt: '2025-08-13T22:10:07.123456Z',
168
- });
169
- mockTodoistApi.moveTasks.mockResolvedValue([mockResponse]);
170
- const result = await tasksOrganizeMultiple.execute({
171
- tasks: [
172
- {
173
- id: '8485093752',
174
- projectId: 'new-project-only',
175
- // Only updating projectId (move operation)
176
- },
177
- ],
178
- }, mockTodoistApi);
179
- expect(mockTodoistApi.moveTasks).toHaveBeenCalledWith(['8485093752'], {
180
- projectId: 'new-project-only',
181
- });
182
- expect(mockTodoistApi.updateTask).not.toHaveBeenCalled();
183
- expect(result).toEqual([mockResponse]);
184
- });
185
- it('should handle empty updates (only id provided)', async () => {
186
- const result = await tasksOrganizeMultiple.execute({ tasks: [{ id: '8485093753' }] }, mockTodoistApi);
187
- // No API calls should be made since no move parameters are provided
188
- expect(mockTodoistApi.moveTasks).not.toHaveBeenCalled();
189
- expect(mockTodoistApi.updateTask).not.toHaveBeenCalled();
190
- // Returns empty array since no moves were processed
191
- expect(result).toEqual([]);
192
- });
193
- });
194
- describe('error handling', () => {
195
- it('should throw error when task has multiple move parameters', async () => {
196
- await expect(tasksOrganizeMultiple.execute({
197
- tasks: [
198
- { id: 'task-1', projectId: 'new-project', sectionId: 'new-section' },
199
- ],
200
- }, mockTodoistApi)).rejects.toThrow('Task task-1: Only one of projectId, sectionId, or parentId can be specified at a time');
201
- });
202
- it('should propagate API errors for individual task moves', async () => {
203
- const apiError = new Error('API Error: Task not found');
204
- mockTodoistApi.moveTasks.mockRejectedValue(apiError);
205
- await expect(tasksOrganizeMultiple.execute({ tasks: [{ id: 'non-existent-task', projectId: 'some-project' }] }, mockTodoistApi)).rejects.toThrow('API Error: Task not found');
206
- });
207
- it('should fail fast on first error (not continue with remaining tasks)', async () => {
208
- const apiError = new Error('API Error: Invalid project ID');
209
- mockTodoistApi.moveTasks.mockRejectedValue(apiError);
210
- await expect(tasksOrganizeMultiple.execute({
211
- tasks: [
212
- { id: 'task-1', projectId: 'invalid-project' },
213
- { id: 'task-2', projectId: 'valid-project' },
214
- ],
215
- }, mockTodoistApi)).rejects.toThrow('API Error: Invalid project ID');
216
- // Should only attempt the first move
217
- expect(mockTodoistApi.moveTasks).toHaveBeenCalledTimes(1);
218
- expect(mockTodoistApi.moveTasks).toHaveBeenCalledWith(['task-1'], {
219
- projectId: 'invalid-project',
220
- });
221
- });
222
- it('should handle validation errors', async () => {
223
- const validationError = new Error('API Error: Invalid section ID');
224
- mockTodoistApi.moveTasks.mockRejectedValue(validationError);
225
- await expect(tasksOrganizeMultiple.execute({ tasks: [{ id: 'task-1', sectionId: 'invalid-section-format' }] }, mockTodoistApi)).rejects.toThrow('API Error: Invalid section ID');
226
- });
227
- it('should handle permission errors', async () => {
228
- const permissionError = new Error('API Error: Insufficient permissions to move task');
229
- mockTodoistApi.moveTasks.mockRejectedValue(permissionError);
230
- await expect(tasksOrganizeMultiple.execute({ tasks: [{ id: 'restricted-task', projectId: 'restricted-project' }] }, mockTodoistApi)).rejects.toThrow('API Error: Insufficient permissions to move task');
231
- });
232
- it('should handle circular parent dependency errors', async () => {
233
- const circularError = new Error('API Error: Circular dependency detected');
234
- mockTodoistApi.moveTasks.mockRejectedValue(circularError);
235
- await expect(tasksOrganizeMultiple.execute({
236
- tasks: [
237
- {
238
- id: 'task-parent',
239
- parentId: 'task-child', // This would create a circular dependency
240
- },
241
- ],
242
- }, mockTodoistApi)).rejects.toThrow('API Error: Circular dependency detected');
243
- });
244
- });
245
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=tasks-search.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tasks-search.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/tasks-search.test.ts"],"names":[],"mappings":""}
@@ -1,106 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { getTasksByFilter } from '../../tool-helpers.js';
3
- import { tasksSearch } from '../tasks-search.js';
4
- import { TEST_ERRORS, TEST_IDS, createMappedTask } from '../test-helpers.js';
5
- // Mock the tool helpers
6
- jest.mock('../../tool-helpers', () => ({
7
- getTasksByFilter: jest.fn(),
8
- }));
9
- const mockGetTasksByFilter = getTasksByFilter;
10
- // Mock the Todoist API (not directly used by tasks-search, but needed for type)
11
- const mockTodoistApi = {};
12
- describe('tasks-search tool', () => {
13
- beforeEach(() => {
14
- jest.clearAllMocks();
15
- });
16
- describe('searching tasks', () => {
17
- it('should search tasks and return results', async () => {
18
- const mockTasks = [
19
- createMappedTask({
20
- id: TEST_IDS.TASK_1,
21
- content: 'Task containing search term',
22
- description: 'Description with more details',
23
- labels: ['work'],
24
- }),
25
- createMappedTask({
26
- id: TEST_IDS.TASK_2,
27
- content: 'Another matching task',
28
- priority: 2,
29
- sectionId: TEST_IDS.SECTION_1,
30
- }),
31
- ];
32
- const mockResponse = { tasks: mockTasks, nextCursor: 'cursor-for-next-page' };
33
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
34
- const result = await tasksSearch.execute({
35
- searchText: 'important meeting',
36
- limit: 10,
37
- }, mockTodoistApi);
38
- expect(mockGetTasksByFilter).toHaveBeenCalledWith({
39
- client: mockTodoistApi,
40
- query: 'search: important meeting',
41
- cursor: undefined,
42
- limit: 10,
43
- });
44
- expect(result).toEqual(mockResponse);
45
- });
46
- it.each([
47
- {
48
- name: 'custom limit',
49
- params: { searchText: 'project update', limit: 5 },
50
- expectedQuery: 'search: project update',
51
- expectedLimit: 5,
52
- expectedCursor: undefined,
53
- },
54
- {
55
- name: 'pagination cursor',
56
- params: { searchText: 'follow up', limit: 20, cursor: 'cursor-from-first-page' },
57
- expectedQuery: 'search: follow up',
58
- expectedLimit: 20,
59
- expectedCursor: 'cursor-from-first-page',
60
- },
61
- ])('should handle $name', async ({ params, expectedQuery, expectedLimit, expectedCursor }) => {
62
- const mockTask = createMappedTask({ content: 'Test result' });
63
- const mockResponse = { tasks: [mockTask], nextCursor: null };
64
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
65
- const result = await tasksSearch.execute(params, mockTodoistApi);
66
- expect(mockGetTasksByFilter).toHaveBeenCalledWith({
67
- client: mockTodoistApi,
68
- query: expectedQuery,
69
- cursor: expectedCursor,
70
- limit: expectedLimit,
71
- });
72
- expect(result).toEqual(mockResponse);
73
- });
74
- it.each([
75
- { searchText: '@work #urgent "exact phrase"', description: 'special characters' },
76
- { searchText: 'nonexistent keyword', description: 'empty results' },
77
- ])('should handle search with $description', async ({ searchText }) => {
78
- const mockResponse = { tasks: [], nextCursor: null };
79
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
80
- const result = await tasksSearch.execute({ searchText, limit: 10 }, mockTodoistApi);
81
- expect(mockGetTasksByFilter).toHaveBeenCalledWith({
82
- client: mockTodoistApi,
83
- query: `search: ${searchText}`,
84
- cursor: undefined,
85
- limit: 10,
86
- });
87
- expect(result).toEqual(mockResponse);
88
- });
89
- });
90
- describe('error handling', () => {
91
- it.each([
92
- { error: 'Invalid filter query: search: ', params: { searchText: '', limit: 10 } },
93
- {
94
- error: TEST_ERRORS.API_RATE_LIMIT,
95
- params: { searchText: 'any search term', limit: 10 },
96
- },
97
- {
98
- error: TEST_ERRORS.INVALID_CURSOR,
99
- params: { searchText: 'test', cursor: 'invalid-cursor-format', limit: 10 },
100
- },
101
- ])('should propagate $error', async ({ error, params }) => {
102
- mockGetTasksByFilter.mockRejectedValue(new Error(error));
103
- await expect(tasksSearch.execute(params, mockTodoistApi)).rejects.toThrow(error);
104
- });
105
- });
106
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=tasks-update-one.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tasks-update-one.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/tasks-update-one.test.ts"],"names":[],"mappings":""}
@@ -1,251 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { tasksUpdateOne } from '../tasks-update-one.js';
3
- import { createMockTask } from '../test-helpers.js';
4
- // Mock the Todoist API
5
- const mockTodoistApi = {
6
- updateTask: jest.fn(),
7
- moveTasks: jest.fn(),
8
- };
9
- describe('tasks-update-one tool', () => {
10
- beforeEach(() => {
11
- jest.clearAllMocks();
12
- });
13
- describe('updating task properties', () => {
14
- it('should update task content and description', async () => {
15
- // Mock API response extracted from recordings (Task type)
16
- const mockApiResponse = createMockTask({
17
- id: '8485093748',
18
- content: 'Updated task content',
19
- description: 'Updated task description',
20
- url: 'https://todoist.com/showTask?id=8485093748',
21
- addedAt: '2025-08-13T22:09:56.123456Z',
22
- });
23
- mockTodoistApi.updateTask.mockResolvedValue(mockApiResponse);
24
- const result = await tasksUpdateOne.execute({
25
- id: '8485093748',
26
- content: 'Updated task content',
27
- description: 'Updated task description',
28
- }, mockTodoistApi);
29
- // Verify API was called correctly
30
- expect(mockTodoistApi.updateTask).toHaveBeenCalledWith('8485093748', {
31
- content: 'Updated task content',
32
- description: 'Updated task description',
33
- });
34
- // Verify result matches API response
35
- expect(result).toEqual(mockApiResponse);
36
- });
37
- it('should update task priority and due date', async () => {
38
- const mockApiResponse = createMockTask({
39
- id: '8485093749',
40
- content: 'Original task content',
41
- labels: ['urgent'],
42
- priority: 3,
43
- url: 'https://todoist.com/showTask?id=8485093749',
44
- addedAt: '2025-08-13T22:09:56.123456Z',
45
- due: {
46
- date: '2025-08-20',
47
- isRecurring: false,
48
- lang: 'en',
49
- string: 'Aug 20',
50
- timezone: null,
51
- },
52
- });
53
- mockTodoistApi.updateTask.mockResolvedValue(mockApiResponse);
54
- const result = await tasksUpdateOne.execute({ id: '8485093749', priority: 3, dueString: 'Aug 20' }, mockTodoistApi);
55
- expect(mockTodoistApi.updateTask).toHaveBeenCalledWith('8485093749', {
56
- priority: 3,
57
- dueString: 'Aug 20',
58
- });
59
- expect(result).toEqual(mockApiResponse);
60
- });
61
- it('should move task to different project', async () => {
62
- const mockApiResponse = createMockTask({
63
- id: '8485093750',
64
- content: 'Task to move',
65
- projectId: 'new-project-id',
66
- url: 'https://todoist.com/showTask?id=8485093750',
67
- addedAt: '2025-08-13T22:09:56.123456Z',
68
- });
69
- mockTodoistApi.moveTasks.mockResolvedValue([mockApiResponse]);
70
- const result = await tasksUpdateOne.execute({ id: '8485093750', projectId: 'new-project-id' }, mockTodoistApi);
71
- expect(mockTodoistApi.moveTasks).toHaveBeenCalledWith(['8485093750'], {
72
- projectId: 'new-project-id',
73
- });
74
- expect(mockTodoistApi.updateTask).not.toHaveBeenCalled();
75
- expect(result).toEqual(mockApiResponse);
76
- });
77
- it('should update task parent (create subtask relationship)', async () => {
78
- const mockApiResponse = createMockTask({
79
- id: '8485093751',
80
- content: 'Subtask content',
81
- parentId: 'parent-task-123',
82
- url: 'https://todoist.com/showTask?id=8485093751',
83
- addedAt: '2025-08-13T22:09:56.123456Z',
84
- });
85
- mockTodoistApi.moveTasks.mockResolvedValue([mockApiResponse]);
86
- const result = await tasksUpdateOne.execute({ id: '8485093751', parentId: 'parent-task-123' }, mockTodoistApi);
87
- expect(mockTodoistApi.moveTasks).toHaveBeenCalledWith(['8485093751'], {
88
- parentId: 'parent-task-123',
89
- });
90
- expect(mockTodoistApi.updateTask).not.toHaveBeenCalled();
91
- expect(result).toEqual(mockApiResponse);
92
- });
93
- it('should move task and update properties at once', async () => {
94
- const movedTask = createMockTask({
95
- id: '8485093752',
96
- content: 'Task to move',
97
- projectId: 'different-project-id',
98
- });
99
- const updatedTask = createMockTask({
100
- id: '8485093752',
101
- content: 'Completely updated task',
102
- description: 'New description with details',
103
- priority: 4,
104
- projectId: 'different-project-id',
105
- url: 'https://todoist.com/showTask?id=8485093752',
106
- addedAt: '2025-08-13T22:09:56.123456Z',
107
- due: {
108
- date: '2025-08-25',
109
- isRecurring: true,
110
- lang: 'en',
111
- string: 'every Friday',
112
- timezone: null,
113
- },
114
- });
115
- mockTodoistApi.moveTasks.mockResolvedValue([movedTask]);
116
- mockTodoistApi.updateTask.mockResolvedValue(updatedTask);
117
- const result = await tasksUpdateOne.execute({
118
- id: '8485093752',
119
- content: 'Completely updated task',
120
- description: 'New description with details',
121
- priority: 4,
122
- dueString: 'every Friday',
123
- projectId: 'different-project-id',
124
- }, mockTodoistApi);
125
- // Should call moveTasks first for the projectId
126
- expect(mockTodoistApi.moveTasks).toHaveBeenCalledWith(['8485093752'], {
127
- projectId: 'different-project-id',
128
- });
129
- // Then call updateTask for the other properties
130
- expect(mockTodoistApi.updateTask).toHaveBeenCalledWith('8485093752', {
131
- content: 'Completely updated task',
132
- description: 'New description with details',
133
- priority: 4,
134
- dueString: 'every Friday',
135
- });
136
- expect(result).toEqual(updatedTask);
137
- });
138
- it('should update task duration', async () => {
139
- const mockApiResponse = createMockTask({
140
- id: '8485093753',
141
- content: 'Task with updated duration',
142
- duration: { amount: 150, unit: 'minute' },
143
- url: 'https://todoist.com/showTask?id=8485093753',
144
- addedAt: '2025-08-13T22:09:56.123456Z',
145
- });
146
- mockTodoistApi.updateTask.mockResolvedValue(mockApiResponse);
147
- const result = await tasksUpdateOne.execute({
148
- id: '8485093753',
149
- duration: '2h30m',
150
- }, mockTodoistApi);
151
- expect(mockTodoistApi.updateTask).toHaveBeenCalledWith('8485093753', {
152
- duration: 150,
153
- durationUnit: 'minute',
154
- });
155
- expect(result).toEqual(mockApiResponse);
156
- });
157
- it('should handle various duration formats', async () => {
158
- const mockApiResponse = createMockTask({
159
- id: '8485093754',
160
- content: 'Test task',
161
- duration: { amount: 120, unit: 'minute' },
162
- });
163
- mockTodoistApi.updateTask.mockResolvedValue(mockApiResponse);
164
- // Test different duration formats
165
- const testCases = [
166
- { input: '2h', expectedMinutes: 120 },
167
- { input: '90m', expectedMinutes: 90 },
168
- { input: '1.5h', expectedMinutes: 90 },
169
- { input: ' 2h 30m ', expectedMinutes: 150 },
170
- { input: '2H30M', expectedMinutes: 150 },
171
- ];
172
- for (const testCase of testCases) {
173
- mockTodoistApi.updateTask.mockClear();
174
- await tasksUpdateOne.execute({
175
- id: '8485093754',
176
- duration: testCase.input,
177
- }, mockTodoistApi);
178
- expect(mockTodoistApi.updateTask).toHaveBeenCalledWith('8485093754', expect.objectContaining({
179
- duration: testCase.expectedMinutes,
180
- durationUnit: 'minute',
181
- }));
182
- }
183
- });
184
- it('should update task with duration and move at once', async () => {
185
- const movedTask = createMockTask({
186
- id: '8485093755',
187
- content: 'Task to move and update',
188
- projectId: 'new-project-id',
189
- });
190
- const updatedTask = createMockTask({
191
- id: '8485093755',
192
- content: 'Updated task with duration',
193
- duration: { amount: 120, unit: 'minute' },
194
- projectId: 'new-project-id',
195
- });
196
- mockTodoistApi.moveTasks.mockResolvedValue([movedTask]);
197
- mockTodoistApi.updateTask.mockResolvedValue(updatedTask);
198
- const result = await tasksUpdateOne.execute({
199
- id: '8485093755',
200
- content: 'Updated task with duration',
201
- duration: '2h',
202
- projectId: 'new-project-id',
203
- }, mockTodoistApi);
204
- // Should call moveTasks first
205
- expect(mockTodoistApi.moveTasks).toHaveBeenCalledWith(['8485093755'], {
206
- projectId: 'new-project-id',
207
- });
208
- // Then call updateTask with duration
209
- expect(mockTodoistApi.updateTask).toHaveBeenCalledWith('8485093755', {
210
- content: 'Updated task with duration',
211
- duration: 120,
212
- durationUnit: 'minute',
213
- });
214
- expect(result).toEqual(updatedTask);
215
- });
216
- });
217
- describe('error handling', () => {
218
- it('should throw error for invalid duration format', async () => {
219
- await expect(tasksUpdateOne.execute({
220
- id: '8485093756',
221
- duration: 'invalid',
222
- }, mockTodoistApi)).rejects.toThrow('Task 8485093756: Invalid duration format "invalid"');
223
- });
224
- it('should throw error for duration exceeding 24 hours', async () => {
225
- await expect(tasksUpdateOne.execute({
226
- id: '8485093757',
227
- duration: '25h',
228
- }, mockTodoistApi)).rejects.toThrow('Task 8485093757: Invalid duration format "25h": Duration cannot exceed 24 hours (1440 minutes)');
229
- });
230
- it('should throw error when multiple move parameters are provided', async () => {
231
- await expect(tasksUpdateOne.execute({ id: '8485093748', projectId: 'new-project', sectionId: 'new-section' }, mockTodoistApi)).rejects.toThrow('Only one of projectId, sectionId, or parentId can be specified at a time. ' +
232
- 'The Todoist API requires exactly one destination for move operations.');
233
- });
234
- it('should throw error when all three move parameters are provided', async () => {
235
- await expect(tasksUpdateOne.execute({ id: '8485093748', projectId: 'p1', sectionId: 's1', parentId: 't1' }, mockTodoistApi)).rejects.toThrow('Only one of projectId, sectionId, or parentId can be specified at a time');
236
- });
237
- it.each([
238
- {
239
- error: 'API Error: Task not found',
240
- params: { id: 'non-existent-task', content: 'Updated content' },
241
- },
242
- {
243
- error: 'API Error: Invalid priority value',
244
- params: { id: '8485093748', priority: 5 },
245
- },
246
- ])('should propagate $error', async ({ error, params }) => {
247
- mockTodoistApi.updateTask.mockRejectedValue(new Error(error));
248
- await expect(tasksUpdateOne.execute(params, mockTodoistApi)).rejects.toThrow(error);
249
- });
250
- });
251
- });
@@ -1,17 +0,0 @@
1
- import { z } from 'zod';
2
- declare const deleteOne: {
3
- name: string;
4
- description: string;
5
- parameters: {
6
- type: z.ZodEnum<["project", "section", "task"]>;
7
- id: z.ZodString;
8
- };
9
- execute(args: {
10
- type: "project" | "section" | "task";
11
- id: string;
12
- }, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
13
- success: boolean;
14
- }>;
15
- };
16
- export { deleteOne };
17
- //# sourceMappingURL=delete-one.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"delete-one.d.ts","sourceRoot":"","sources":["../../src/tools/delete-one.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAQvB,QAAA,MAAM,SAAS;;;;;;;;;;;;;CAkB2B,CAAA;AAE1C,OAAO,EAAE,SAAS,EAAE,CAAA"}
@@ -1,25 +0,0 @@
1
- import { z } from 'zod';
2
- const ArgsSchema = {
3
- type: z.enum(['project', 'section', 'task']).describe('The type of entity to delete.'),
4
- id: z.string().min(1).describe('The ID of the entity to delete.'),
5
- };
6
- const deleteOne = {
7
- name: 'delete-one',
8
- description: 'Delete a project, section, or task by its ID.',
9
- parameters: ArgsSchema,
10
- async execute(args, client) {
11
- switch (args.type) {
12
- case 'project':
13
- await client.deleteProject(args.id);
14
- break;
15
- case 'section':
16
- await client.deleteSection(args.id);
17
- break;
18
- case 'task':
19
- await client.deleteTask(args.id);
20
- break;
21
- }
22
- return { success: true };
23
- },
24
- };
25
- export { deleteOne };