@doist/todoist-ai 2.2.2 → 4.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 (202) hide show
  1. package/README.md +6 -14
  2. package/dist/index.d.ts +619 -250
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +45 -29
  5. package/dist/main.js +2 -1
  6. package/dist/mcp-helpers.d.ts +25 -3
  7. package/dist/mcp-helpers.d.ts.map +1 -1
  8. package/dist/mcp-helpers.js +37 -19
  9. package/dist/mcp-server.d.ts.map +1 -1
  10. package/dist/mcp-server.js +44 -28
  11. package/dist/tools/__tests__/add-comments.test.d.ts +2 -0
  12. package/dist/tools/__tests__/add-comments.test.d.ts.map +1 -0
  13. package/dist/tools/__tests__/add-comments.test.js +241 -0
  14. package/dist/tools/__tests__/add-projects.test.d.ts +2 -0
  15. package/dist/tools/__tests__/add-projects.test.d.ts.map +1 -0
  16. package/dist/tools/__tests__/add-projects.test.js +152 -0
  17. package/dist/tools/__tests__/add-sections.test.d.ts +2 -0
  18. package/dist/tools/__tests__/add-sections.test.d.ts.map +1 -0
  19. package/dist/tools/__tests__/add-sections.test.js +181 -0
  20. package/dist/tools/__tests__/add-tasks.test.d.ts +2 -0
  21. package/dist/tools/__tests__/add-tasks.test.d.ts.map +1 -0
  22. package/dist/tools/__tests__/{tasks-add-multiple.test.js → add-tasks.test.js} +89 -79
  23. package/dist/tools/__tests__/complete-tasks.test.d.ts +2 -0
  24. package/dist/tools/__tests__/complete-tasks.test.d.ts.map +1 -0
  25. package/dist/tools/__tests__/complete-tasks.test.js +206 -0
  26. package/dist/tools/__tests__/delete-object.test.d.ts +2 -0
  27. package/dist/tools/__tests__/delete-object.test.d.ts.map +1 -0
  28. package/dist/tools/__tests__/{delete-one.test.js → delete-object.test.js} +42 -22
  29. package/dist/tools/__tests__/find-comments.test.d.ts +2 -0
  30. package/dist/tools/__tests__/find-comments.test.d.ts.map +1 -0
  31. package/dist/tools/__tests__/find-comments.test.js +242 -0
  32. package/dist/tools/__tests__/find-completed-tasks.test.d.ts +2 -0
  33. package/dist/tools/__tests__/find-completed-tasks.test.d.ts.map +1 -0
  34. package/dist/tools/__tests__/{tasks-list-completed.test.js → find-completed-tasks.test.js} +13 -36
  35. package/dist/tools/__tests__/find-projects.test.d.ts +2 -0
  36. package/dist/tools/__tests__/find-projects.test.d.ts.map +1 -0
  37. package/dist/tools/__tests__/{projects-list.test.js → find-projects.test.js} +55 -39
  38. package/dist/tools/__tests__/find-sections.test.d.ts +2 -0
  39. package/dist/tools/__tests__/find-sections.test.d.ts.map +1 -0
  40. package/dist/tools/__tests__/{sections-search.test.js → find-sections.test.js} +64 -50
  41. package/dist/tools/__tests__/find-tasks-by-date.test.d.ts +2 -0
  42. package/dist/tools/__tests__/find-tasks-by-date.test.d.ts.map +1 -0
  43. package/dist/tools/__tests__/{tasks-list-by-date.test.js → find-tasks-by-date.test.js} +96 -14
  44. package/dist/tools/__tests__/find-tasks.test.d.ts +2 -0
  45. package/dist/tools/__tests__/find-tasks.test.d.ts.map +1 -0
  46. package/dist/tools/__tests__/find-tasks.test.js +334 -0
  47. package/dist/tools/__tests__/get-overview.test.d.ts +2 -0
  48. package/dist/tools/__tests__/get-overview.test.d.ts.map +1 -0
  49. package/dist/tools/__tests__/{overview.test.js → get-overview.test.js} +77 -13
  50. package/dist/tools/__tests__/update-comments.test.d.ts +2 -0
  51. package/dist/tools/__tests__/update-comments.test.d.ts.map +1 -0
  52. package/dist/tools/__tests__/update-comments.test.js +296 -0
  53. package/dist/tools/__tests__/update-projects.test.d.ts +2 -0
  54. package/dist/tools/__tests__/update-projects.test.d.ts.map +1 -0
  55. package/dist/tools/__tests__/update-projects.test.js +205 -0
  56. package/dist/tools/__tests__/update-sections.test.d.ts +2 -0
  57. package/dist/tools/__tests__/update-sections.test.d.ts.map +1 -0
  58. package/dist/tools/__tests__/update-sections.test.js +156 -0
  59. package/dist/tools/__tests__/update-tasks.test.d.ts +2 -0
  60. package/dist/tools/__tests__/update-tasks.test.d.ts.map +1 -0
  61. package/dist/tools/__tests__/update-tasks.test.js +645 -0
  62. package/dist/tools/add-comments.d.ts +51 -0
  63. package/dist/tools/add-comments.d.ts.map +1 -0
  64. package/dist/tools/add-comments.js +79 -0
  65. package/dist/tools/add-projects.d.ts +50 -0
  66. package/dist/tools/add-projects.d.ts.map +1 -0
  67. package/dist/tools/add-projects.js +59 -0
  68. package/dist/tools/add-sections.d.ts +46 -0
  69. package/dist/tools/add-sections.d.ts.map +1 -0
  70. package/dist/tools/add-sections.js +61 -0
  71. package/dist/tools/add-tasks.d.ts +82 -0
  72. package/dist/tools/add-tasks.d.ts.map +1 -0
  73. package/dist/tools/add-tasks.js +96 -0
  74. package/dist/tools/complete-tasks.d.ts +40 -0
  75. package/dist/tools/complete-tasks.d.ts.map +1 -0
  76. package/dist/tools/complete-tasks.js +68 -0
  77. package/dist/tools/delete-object.d.ts +38 -0
  78. package/dist/tools/delete-object.d.ts.map +1 -0
  79. package/dist/tools/delete-object.js +79 -0
  80. package/dist/tools/find-comments.d.ts +46 -0
  81. package/dist/tools/find-comments.d.ts.map +1 -0
  82. package/dist/tools/find-comments.js +143 -0
  83. package/dist/tools/find-completed-tasks.d.ts +74 -0
  84. package/dist/tools/find-completed-tasks.d.ts.map +1 -0
  85. package/dist/tools/find-completed-tasks.js +112 -0
  86. package/dist/tools/find-projects.d.ts +53 -0
  87. package/dist/tools/find-projects.d.ts.map +1 -0
  88. package/dist/tools/find-projects.js +101 -0
  89. package/dist/tools/find-sections.d.ts +42 -0
  90. package/dist/tools/find-sections.d.ts.map +1 -0
  91. package/dist/tools/find-sections.js +96 -0
  92. package/dist/tools/find-tasks-by-date.d.ts +59 -0
  93. package/dist/tools/find-tasks-by-date.d.ts.map +1 -0
  94. package/dist/tools/find-tasks-by-date.js +121 -0
  95. package/dist/tools/find-tasks.d.ts +65 -0
  96. package/dist/tools/find-tasks.d.ts.map +1 -0
  97. package/dist/tools/find-tasks.js +182 -0
  98. package/dist/tools/get-overview.d.ts +67 -0
  99. package/dist/tools/get-overview.d.ts.map +1 -0
  100. package/dist/tools/{overview.js → get-overview.js} +66 -19
  101. package/dist/tools/update-comments.d.ts +50 -0
  102. package/dist/tools/update-comments.d.ts.map +1 -0
  103. package/dist/tools/update-comments.js +82 -0
  104. package/dist/tools/update-projects.d.ts +59 -0
  105. package/dist/tools/update-projects.d.ts.map +1 -0
  106. package/dist/tools/update-projects.js +84 -0
  107. package/dist/tools/update-sections.d.ts +47 -0
  108. package/dist/tools/update-sections.d.ts.map +1 -0
  109. package/dist/tools/update-sections.js +70 -0
  110. package/dist/tools/update-tasks.d.ts +94 -0
  111. package/dist/tools/update-tasks.d.ts.map +1 -0
  112. package/dist/tools/update-tasks.js +120 -0
  113. package/dist/utils/constants.d.ts +39 -0
  114. package/dist/utils/constants.d.ts.map +1 -0
  115. package/dist/utils/constants.js +41 -0
  116. package/dist/utils/response-builders.d.ts +88 -0
  117. package/dist/utils/response-builders.d.ts.map +1 -0
  118. package/dist/utils/response-builders.js +202 -0
  119. package/dist/{tools → utils}/test-helpers.d.ts +16 -0
  120. package/dist/utils/test-helpers.d.ts.map +1 -0
  121. package/dist/{tools → utils}/test-helpers.js +51 -0
  122. package/dist/utils/tool-names.d.ts +28 -0
  123. package/dist/utils/tool-names.d.ts.map +1 -0
  124. package/dist/utils/tool-names.js +31 -0
  125. package/package.json +1 -1
  126. package/dist/tools/__tests__/delete-one.test.d.ts +0 -2
  127. package/dist/tools/__tests__/delete-one.test.d.ts.map +0 -1
  128. package/dist/tools/__tests__/overview.test.d.ts +0 -2
  129. package/dist/tools/__tests__/overview.test.d.ts.map +0 -1
  130. package/dist/tools/__tests__/projects-list.test.d.ts +0 -2
  131. package/dist/tools/__tests__/projects-list.test.d.ts.map +0 -1
  132. package/dist/tools/__tests__/projects-manage.test.d.ts +0 -2
  133. package/dist/tools/__tests__/projects-manage.test.d.ts.map +0 -1
  134. package/dist/tools/__tests__/projects-manage.test.js +0 -106
  135. package/dist/tools/__tests__/sections-manage.test.d.ts +0 -2
  136. package/dist/tools/__tests__/sections-manage.test.d.ts.map +0 -1
  137. package/dist/tools/__tests__/sections-manage.test.js +0 -138
  138. package/dist/tools/__tests__/sections-search.test.d.ts +0 -2
  139. package/dist/tools/__tests__/sections-search.test.d.ts.map +0 -1
  140. package/dist/tools/__tests__/tasks-add-multiple.test.d.ts +0 -2
  141. package/dist/tools/__tests__/tasks-add-multiple.test.d.ts.map +0 -1
  142. package/dist/tools/__tests__/tasks-complete-multiple.test.d.ts +0 -2
  143. package/dist/tools/__tests__/tasks-complete-multiple.test.d.ts.map +0 -1
  144. package/dist/tools/__tests__/tasks-complete-multiple.test.js +0 -146
  145. package/dist/tools/__tests__/tasks-list-by-date.test.d.ts +0 -2
  146. package/dist/tools/__tests__/tasks-list-by-date.test.d.ts.map +0 -1
  147. package/dist/tools/__tests__/tasks-list-completed.test.d.ts +0 -2
  148. package/dist/tools/__tests__/tasks-list-completed.test.d.ts.map +0 -1
  149. package/dist/tools/__tests__/tasks-list-for-container.test.d.ts +0 -2
  150. package/dist/tools/__tests__/tasks-list-for-container.test.d.ts.map +0 -1
  151. package/dist/tools/__tests__/tasks-list-for-container.test.js +0 -232
  152. package/dist/tools/__tests__/tasks-organize-multiple.test.d.ts +0 -2
  153. package/dist/tools/__tests__/tasks-organize-multiple.test.d.ts.map +0 -1
  154. package/dist/tools/__tests__/tasks-organize-multiple.test.js +0 -245
  155. package/dist/tools/__tests__/tasks-search.test.d.ts +0 -2
  156. package/dist/tools/__tests__/tasks-search.test.d.ts.map +0 -1
  157. package/dist/tools/__tests__/tasks-search.test.js +0 -106
  158. package/dist/tools/__tests__/tasks-update-one.test.d.ts +0 -2
  159. package/dist/tools/__tests__/tasks-update-one.test.d.ts.map +0 -1
  160. package/dist/tools/__tests__/tasks-update-one.test.js +0 -251
  161. package/dist/tools/delete-one.d.ts +0 -17
  162. package/dist/tools/delete-one.d.ts.map +0 -1
  163. package/dist/tools/delete-one.js +0 -25
  164. package/dist/tools/overview.d.ts +0 -14
  165. package/dist/tools/overview.d.ts.map +0 -1
  166. package/dist/tools/projects-list.d.ts +0 -29
  167. package/dist/tools/projects-list.d.ts.map +0 -1
  168. package/dist/tools/projects-list.js +0 -39
  169. package/dist/tools/projects-manage.d.ts +0 -24
  170. package/dist/tools/projects-manage.d.ts.map +0 -1
  171. package/dist/tools/projects-manage.js +0 -26
  172. package/dist/tools/sections-manage.d.ts +0 -23
  173. package/dist/tools/sections-manage.d.ts.map +0 -1
  174. package/dist/tools/sections-manage.js +0 -37
  175. package/dist/tools/sections-search.d.ts +0 -18
  176. package/dist/tools/sections-search.d.ts.map +0 -1
  177. package/dist/tools/sections-search.js +0 -27
  178. package/dist/tools/tasks-add-multiple.d.ts +0 -55
  179. package/dist/tools/tasks-add-multiple.d.ts.map +0 -1
  180. package/dist/tools/tasks-add-multiple.js +0 -52
  181. package/dist/tools/tasks-complete-multiple.d.ts +0 -16
  182. package/dist/tools/tasks-complete-multiple.d.ts.map +0 -1
  183. package/dist/tools/tasks-complete-multiple.js +0 -23
  184. package/dist/tools/tasks-list-by-date.d.ts +0 -34
  185. package/dist/tools/tasks-list-by-date.d.ts.map +0 -1
  186. package/dist/tools/tasks-list-by-date.js +0 -53
  187. package/dist/tools/tasks-list-completed.d.ts +0 -44
  188. package/dist/tools/tasks-list-completed.d.ts.map +0 -1
  189. package/dist/tools/tasks-list-completed.js +0 -49
  190. package/dist/tools/tasks-list-for-container.d.ts +0 -34
  191. package/dist/tools/tasks-list-for-container.d.ts.map +0 -1
  192. package/dist/tools/tasks-list-for-container.js +0 -48
  193. package/dist/tools/tasks-organize-multiple.d.ts +0 -37
  194. package/dist/tools/tasks-organize-multiple.d.ts.map +0 -1
  195. package/dist/tools/tasks-organize-multiple.js +0 -34
  196. package/dist/tools/tasks-search.d.ts +0 -32
  197. package/dist/tools/tasks-search.d.ts.map +0 -1
  198. package/dist/tools/tasks-search.js +0 -30
  199. package/dist/tools/tasks-update-one.d.ts +0 -29
  200. package/dist/tools/tasks-update-one.d.ts.map +0 -1
  201. package/dist/tools/tasks-update-one.js +0 -63
  202. package/dist/tools/test-helpers.d.ts.map +0 -1
@@ -1,232 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { tasksListForContainer } from '../tasks-list-for-container.js';
3
- import { TEST_IDS, createMockApiResponse, createMockTask } from '../test-helpers.js';
4
- // Mock the Todoist API
5
- const mockTodoistApi = {
6
- getTasks: jest.fn(),
7
- };
8
- describe('tasks-list-for-container tool', () => {
9
- beforeEach(() => {
10
- jest.clearAllMocks();
11
- });
12
- describe('container types', () => {
13
- it.each([
14
- {
15
- name: 'project',
16
- type: 'project',
17
- id: TEST_IDS.PROJECT_TEST,
18
- expectedParam: { projectId: TEST_IDS.PROJECT_TEST },
19
- tasks: [createMockTask({ content: 'Project task' })],
20
- },
21
- {
22
- name: 'section',
23
- type: 'section',
24
- id: TEST_IDS.SECTION_1,
25
- expectedParam: { sectionId: TEST_IDS.SECTION_1 },
26
- tasks: [createMockTask({ content: 'Section task' })],
27
- },
28
- {
29
- name: 'parent task',
30
- type: 'parent',
31
- id: TEST_IDS.TASK_1,
32
- expectedParam: { parentId: TEST_IDS.TASK_1 },
33
- tasks: [createMockTask({ content: 'Subtask' })],
34
- },
35
- ])('should get tasks for $name', async ({ type, id, expectedParam, tasks }) => {
36
- mockTodoistApi.getTasks.mockResolvedValue(createMockApiResponse(tasks));
37
- const result = await tasksListForContainer.execute({ type, id, limit: 10 }, mockTodoistApi);
38
- expect(mockTodoistApi.getTasks).toHaveBeenCalledWith({
39
- limit: 10,
40
- cursor: null,
41
- ...expectedParam,
42
- });
43
- expect(result.tasks).toHaveLength(1);
44
- expect(result.nextCursor).toBeNull();
45
- });
46
- });
47
- describe('getting tasks for a section', () => {
48
- it('should get tasks for a specific section', async () => {
49
- const mockTasks = [
50
- createMockTask({
51
- id: '8485093751',
52
- content: 'Section task 1',
53
- description: 'Task in specific section',
54
- labels: ['urgent'],
55
- childOrder: 1,
56
- priority: 3,
57
- projectId: '6cfCcrrCFg2xP94Q',
58
- sectionId: 'section-123',
59
- url: 'https://todoist.com/showTask?id=8485093751',
60
- addedAt: '2025-08-13T22:09:59.123456Z',
61
- due: {
62
- date: '2025-08-16',
63
- isRecurring: true,
64
- lang: 'en',
65
- string: 'every week',
66
- timezone: null,
67
- },
68
- }),
69
- ];
70
- mockTodoistApi.getTasks.mockResolvedValue({ results: mockTasks, nextCursor: null });
71
- const result = await tasksListForContainer.execute({ type: 'section', id: 'section-123', limit: 15 }, mockTodoistApi);
72
- expect(mockTodoistApi.getTasks).toHaveBeenCalledWith({
73
- limit: 15,
74
- cursor: null,
75
- sectionId: 'section-123',
76
- });
77
- expect(result).toEqual({
78
- tasks: [
79
- expect.objectContaining({
80
- id: '8485093751',
81
- content: 'Section task 1',
82
- description: 'Task in specific section',
83
- dueDate: '2025-08-16',
84
- recurring: 'every week',
85
- priority: 3,
86
- sectionId: 'section-123',
87
- labels: ['urgent'],
88
- }),
89
- ],
90
- nextCursor: null,
91
- });
92
- });
93
- it('should handle empty section', async () => {
94
- mockTodoistApi.getTasks.mockResolvedValue({ results: [], nextCursor: null });
95
- const result = await tasksListForContainer.execute({ type: 'section', id: 'empty-section-id', limit: 10 }, mockTodoistApi);
96
- expect(mockTodoistApi.getTasks).toHaveBeenCalledWith({
97
- limit: 10,
98
- cursor: null,
99
- sectionId: 'empty-section-id',
100
- });
101
- expect(result).toEqual({ tasks: [], nextCursor: null });
102
- });
103
- });
104
- describe('getting subtasks for a parent task', () => {
105
- it('should get subtasks for a specific parent task', async () => {
106
- const mockSubtasks = [
107
- createMockTask({
108
- id: '8485093752',
109
- content: 'Subtask 1',
110
- description: 'First subtask',
111
- childOrder: 1,
112
- priority: 1,
113
- projectId: '6cfCcrrCFg2xP94Q',
114
- parentId: 'parent-task-123',
115
- url: 'https://todoist.com/showTask?id=8485093752',
116
- addedAt: '2025-08-13T22:10:00.123456Z',
117
- }),
118
- createMockTask({
119
- id: '8485093753',
120
- content: 'Subtask 2',
121
- description: 'Second subtask',
122
- labels: ['follow-up'],
123
- childOrder: 2,
124
- priority: 2,
125
- projectId: '6cfCcrrCFg2xP94Q',
126
- parentId: 'parent-task-123',
127
- url: 'https://todoist.com/showTask?id=8485093753',
128
- addedAt: '2025-08-13T22:10:01.123456Z',
129
- due: {
130
- date: '2025-08-18',
131
- isRecurring: false,
132
- lang: 'en',
133
- string: 'Aug 18',
134
- timezone: null,
135
- },
136
- }),
137
- ];
138
- mockTodoistApi.getTasks.mockResolvedValue({ results: mockSubtasks, nextCursor: null });
139
- const result = await tasksListForContainer.execute({ type: 'parent', id: 'parent-task-123', limit: 10 }, mockTodoistApi);
140
- expect(mockTodoistApi.getTasks).toHaveBeenCalledWith({
141
- parentId: 'parent-task-123',
142
- cursor: null,
143
- limit: 10,
144
- });
145
- expect(result).toEqual({
146
- tasks: [
147
- expect.objectContaining({
148
- id: '8485093752',
149
- content: 'Subtask 1',
150
- description: 'First subtask',
151
- parentId: 'parent-task-123',
152
- labels: [],
153
- }),
154
- expect.objectContaining({
155
- id: '8485093753',
156
- content: 'Subtask 2',
157
- description: 'Second subtask',
158
- dueDate: '2025-08-18',
159
- parentId: 'parent-task-123',
160
- priority: 2,
161
- labels: ['follow-up'],
162
- }),
163
- ],
164
- nextCursor: null,
165
- });
166
- });
167
- it('should handle parent task with no subtasks', async () => {
168
- mockTodoistApi.getTasks.mockResolvedValue({ results: [], nextCursor: null });
169
- const result = await tasksListForContainer.execute({ type: 'parent', id: 'parent-with-no-subtasks', limit: 10 }, mockTodoistApi);
170
- expect(mockTodoistApi.getTasks).toHaveBeenCalledWith({
171
- parentId: 'parent-with-no-subtasks',
172
- cursor: null,
173
- limit: 10,
174
- });
175
- expect(result).toEqual({ tasks: [], nextCursor: null });
176
- });
177
- });
178
- describe('pagination and limits', () => {
179
- it('should handle custom limit and cursor', async () => {
180
- mockTodoistApi.getTasks.mockResolvedValue({ results: [], nextCursor: 'next-cursor' });
181
- const result = await tasksListForContainer.execute({ type: 'section', id: 'test-section', limit: 25, cursor: 'current-cursor' }, mockTodoistApi);
182
- expect(mockTodoistApi.getTasks).toHaveBeenCalledWith({
183
- limit: 25,
184
- cursor: 'current-cursor',
185
- sectionId: 'test-section',
186
- });
187
- expect(result.nextCursor).toBe('next-cursor');
188
- });
189
- });
190
- describe('container type validation', () => {
191
- it('should handle all container types correctly', async () => {
192
- mockTodoistApi.getTasks.mockResolvedValue({ results: [], nextCursor: null });
193
- // Test project container
194
- await tasksListForContainer.execute({ type: 'project', id: 'proj-1', limit: 10 }, mockTodoistApi);
195
- expect(mockTodoistApi.getTasks).toHaveBeenLastCalledWith(expect.objectContaining({ projectId: 'proj-1' }));
196
- // Test section container
197
- await tasksListForContainer.execute({ type: 'section', id: 'sect-1', limit: 10 }, mockTodoistApi);
198
- expect(mockTodoistApi.getTasks).toHaveBeenLastCalledWith(expect.objectContaining({ sectionId: 'sect-1' }));
199
- // Test parent container
200
- await tasksListForContainer.execute({ type: 'parent', id: 'parent-1', limit: 10 }, mockTodoistApi);
201
- expect(mockTodoistApi.getTasks).toHaveBeenLastCalledWith(expect.objectContaining({ parentId: 'parent-1' }));
202
- expect(mockTodoistApi.getTasks).toHaveBeenCalledTimes(3);
203
- });
204
- });
205
- describe('error handling', () => {
206
- it('should propagate API errors for project queries', async () => {
207
- const apiError = new Error('API Error: Project not found');
208
- mockTodoistApi.getTasks.mockRejectedValue(apiError);
209
- await expect(tasksListForContainer.execute({ type: 'project', id: 'non-existent-project', limit: 10 }, mockTodoistApi)).rejects.toThrow('API Error: Project not found');
210
- });
211
- it('should propagate API errors for section queries', async () => {
212
- const apiError = new Error('API Error: Section not found');
213
- mockTodoistApi.getTasks.mockRejectedValue(apiError);
214
- await expect(tasksListForContainer.execute({ type: 'section', id: 'non-existent-section', limit: 10 }, mockTodoistApi)).rejects.toThrow('API Error: Section not found');
215
- });
216
- it('should propagate API errors for parent task queries', async () => {
217
- const apiError = new Error('API Error: Parent task not found');
218
- mockTodoistApi.getTasks.mockRejectedValue(apiError);
219
- await expect(tasksListForContainer.execute({ type: 'parent', id: 'non-existent-parent', limit: 10 }, mockTodoistApi)).rejects.toThrow('API Error: Parent task not found');
220
- });
221
- it('should handle permission errors', async () => {
222
- const permissionError = new Error('API Error: Insufficient permissions');
223
- mockTodoistApi.getTasks.mockRejectedValue(permissionError);
224
- await expect(tasksListForContainer.execute({ type: 'project', id: 'restricted-project', limit: 10 }, mockTodoistApi)).rejects.toThrow('API Error: Insufficient permissions');
225
- });
226
- it('should handle cursor validation errors', async () => {
227
- const cursorError = new Error('API Error: Invalid cursor');
228
- mockTodoistApi.getTasks.mockRejectedValue(cursorError);
229
- await expect(tasksListForContainer.execute({ type: 'project', id: 'test-project', cursor: 'invalid-cursor', limit: 10 }, mockTodoistApi)).rejects.toThrow('API Error: Invalid cursor');
230
- });
231
- });
232
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=tasks-organize-multiple.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tasks-organize-multiple.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/tasks-organize-multiple.test.ts"],"names":[],"mappings":""}
@@ -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":""}