@doist/todoist-ai 4.16.0 → 4.17.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 (178) hide show
  1. package/dist/filter-helpers.d.ts +1 -1
  2. package/dist/index.d.ts +1044 -196
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +61 -81
  5. package/dist/main.js +15 -23
  6. package/dist/mcp-helpers.d.ts +5 -5
  7. package/dist/mcp-helpers.d.ts.map +1 -1
  8. package/dist/mcp-server-BADReNAy.js +3092 -0
  9. package/dist/todoist-tool.d.ts +9 -3
  10. package/dist/todoist-tool.d.ts.map +1 -1
  11. package/dist/tool-helpers.d.ts +1 -1
  12. package/dist/tools/add-comments.d.ts +69 -3
  13. package/dist/tools/add-comments.d.ts.map +1 -1
  14. package/dist/tools/add-projects.d.ts +34 -3
  15. package/dist/tools/add-projects.d.ts.map +1 -1
  16. package/dist/tools/add-sections.d.ts +14 -1
  17. package/dist/tools/add-sections.d.ts.map +1 -1
  18. package/dist/tools/add-tasks.d.ts +65 -10
  19. package/dist/tools/add-tasks.d.ts.map +1 -1
  20. package/dist/tools/complete-tasks.d.ts +20 -1
  21. package/dist/tools/complete-tasks.d.ts.map +1 -1
  22. package/dist/tools/delete-object.d.ts +16 -3
  23. package/dist/tools/delete-object.d.ts.map +1 -1
  24. package/dist/tools/fetch.d.ts +8 -1
  25. package/dist/tools/fetch.d.ts.map +1 -1
  26. package/dist/tools/find-activity.d.ts +44 -7
  27. package/dist/tools/find-activity.d.ts.map +1 -1
  28. package/dist/tools/find-comments.d.ts +69 -3
  29. package/dist/tools/find-comments.d.ts.map +1 -1
  30. package/dist/tools/find-completed-tasks.d.ts +63 -5
  31. package/dist/tools/find-completed-tasks.d.ts.map +1 -1
  32. package/dist/tools/find-project-collaborators.d.ts +33 -2
  33. package/dist/tools/find-project-collaborators.d.ts.map +1 -1
  34. package/dist/tools/find-projects.d.ts +35 -1
  35. package/dist/tools/find-projects.d.ts.map +1 -1
  36. package/dist/tools/find-sections.d.ts +15 -1
  37. package/dist/tools/find-sections.d.ts.map +1 -1
  38. package/dist/tools/find-tasks-by-date.d.ts +61 -3
  39. package/dist/tools/find-tasks-by-date.d.ts.map +1 -1
  40. package/dist/tools/find-tasks.d.ts +63 -5
  41. package/dist/tools/find-tasks.d.ts.map +1 -1
  42. package/dist/tools/get-overview.d.ts +24 -1
  43. package/dist/tools/get-overview.d.ts.map +1 -1
  44. package/dist/tools/manage-assignments.d.ts +39 -2
  45. package/dist/tools/manage-assignments.d.ts.map +1 -1
  46. package/dist/tools/search.d.ts +17 -1
  47. package/dist/tools/search.d.ts.map +1 -1
  48. package/dist/tools/update-comments.d.ts +76 -3
  49. package/dist/tools/update-comments.d.ts.map +1 -1
  50. package/dist/tools/update-projects.d.ts +43 -1
  51. package/dist/tools/update-projects.d.ts.map +1 -1
  52. package/dist/tools/update-sections.d.ts +17 -3
  53. package/dist/tools/update-sections.d.ts.map +1 -1
  54. package/dist/tools/update-tasks.d.ts +79 -13
  55. package/dist/tools/update-tasks.d.ts.map +1 -1
  56. package/dist/tools/user-info.d.ts +19 -1
  57. package/dist/tools/user-info.d.ts.map +1 -1
  58. package/dist/utils/assignment-validator.d.ts +2 -2
  59. package/dist/utils/output-schemas.d.ts +233 -0
  60. package/dist/utils/output-schemas.d.ts.map +1 -0
  61. package/dist/utils/response-builders.d.ts +1 -3
  62. package/dist/utils/response-builders.d.ts.map +1 -1
  63. package/dist/utils/test-helpers.d.ts +1 -1
  64. package/dist/utils/user-resolver.d.ts +1 -1
  65. package/package.json +10 -8
  66. package/dist/filter-helpers.js +0 -79
  67. package/dist/mcp-helpers.js +0 -71
  68. package/dist/mcp-server.js +0 -142
  69. package/dist/todoist-tool.js +0 -1
  70. package/dist/tool-helpers.js +0 -125
  71. package/dist/tool-helpers.test.d.ts +0 -2
  72. package/dist/tool-helpers.test.d.ts.map +0 -1
  73. package/dist/tool-helpers.test.js +0 -223
  74. package/dist/tools/__tests__/add-comments.test.d.ts +0 -2
  75. package/dist/tools/__tests__/add-comments.test.d.ts.map +0 -1
  76. package/dist/tools/__tests__/add-comments.test.js +0 -241
  77. package/dist/tools/__tests__/add-projects.test.d.ts +0 -2
  78. package/dist/tools/__tests__/add-projects.test.d.ts.map +0 -1
  79. package/dist/tools/__tests__/add-projects.test.js +0 -174
  80. package/dist/tools/__tests__/add-sections.test.d.ts +0 -2
  81. package/dist/tools/__tests__/add-sections.test.d.ts.map +0 -1
  82. package/dist/tools/__tests__/add-sections.test.js +0 -185
  83. package/dist/tools/__tests__/add-tasks.test.d.ts +0 -2
  84. package/dist/tools/__tests__/add-tasks.test.d.ts.map +0 -1
  85. package/dist/tools/__tests__/add-tasks.test.js +0 -606
  86. package/dist/tools/__tests__/assignment-integration.test.d.ts +0 -2
  87. package/dist/tools/__tests__/assignment-integration.test.d.ts.map +0 -1
  88. package/dist/tools/__tests__/assignment-integration.test.js +0 -428
  89. package/dist/tools/__tests__/complete-tasks.test.d.ts +0 -2
  90. package/dist/tools/__tests__/complete-tasks.test.d.ts.map +0 -1
  91. package/dist/tools/__tests__/complete-tasks.test.js +0 -206
  92. package/dist/tools/__tests__/delete-object.test.d.ts +0 -2
  93. package/dist/tools/__tests__/delete-object.test.d.ts.map +0 -1
  94. package/dist/tools/__tests__/delete-object.test.js +0 -110
  95. package/dist/tools/__tests__/fetch.test.d.ts +0 -2
  96. package/dist/tools/__tests__/fetch.test.d.ts.map +0 -1
  97. package/dist/tools/__tests__/fetch.test.js +0 -279
  98. package/dist/tools/__tests__/find-activity.test.d.ts +0 -2
  99. package/dist/tools/__tests__/find-activity.test.d.ts.map +0 -1
  100. package/dist/tools/__tests__/find-activity.test.js +0 -229
  101. package/dist/tools/__tests__/find-comments.test.d.ts +0 -2
  102. package/dist/tools/__tests__/find-comments.test.d.ts.map +0 -1
  103. package/dist/tools/__tests__/find-comments.test.js +0 -236
  104. package/dist/tools/__tests__/find-completed-tasks.test.d.ts +0 -2
  105. package/dist/tools/__tests__/find-completed-tasks.test.d.ts.map +0 -1
  106. package/dist/tools/__tests__/find-completed-tasks.test.js +0 -423
  107. package/dist/tools/__tests__/find-projects.test.d.ts +0 -2
  108. package/dist/tools/__tests__/find-projects.test.d.ts.map +0 -1
  109. package/dist/tools/__tests__/find-projects.test.js +0 -154
  110. package/dist/tools/__tests__/find-sections.test.d.ts +0 -2
  111. package/dist/tools/__tests__/find-sections.test.d.ts.map +0 -1
  112. package/dist/tools/__tests__/find-sections.test.js +0 -313
  113. package/dist/tools/__tests__/find-tasks-by-date.test.d.ts +0 -2
  114. package/dist/tools/__tests__/find-tasks-by-date.test.d.ts.map +0 -1
  115. package/dist/tools/__tests__/find-tasks-by-date.test.js +0 -528
  116. package/dist/tools/__tests__/find-tasks.test.d.ts +0 -2
  117. package/dist/tools/__tests__/find-tasks.test.d.ts.map +0 -1
  118. package/dist/tools/__tests__/find-tasks.test.js +0 -771
  119. package/dist/tools/__tests__/get-overview.test.d.ts +0 -2
  120. package/dist/tools/__tests__/get-overview.test.d.ts.map +0 -1
  121. package/dist/tools/__tests__/get-overview.test.js +0 -225
  122. package/dist/tools/__tests__/search.test.d.ts +0 -2
  123. package/dist/tools/__tests__/search.test.d.ts.map +0 -1
  124. package/dist/tools/__tests__/search.test.js +0 -206
  125. package/dist/tools/__tests__/update-comments.test.d.ts +0 -2
  126. package/dist/tools/__tests__/update-comments.test.d.ts.map +0 -1
  127. package/dist/tools/__tests__/update-comments.test.js +0 -294
  128. package/dist/tools/__tests__/update-projects.test.d.ts +0 -2
  129. package/dist/tools/__tests__/update-projects.test.d.ts.map +0 -1
  130. package/dist/tools/__tests__/update-projects.test.js +0 -217
  131. package/dist/tools/__tests__/update-sections.test.d.ts +0 -2
  132. package/dist/tools/__tests__/update-sections.test.d.ts.map +0 -1
  133. package/dist/tools/__tests__/update-sections.test.js +0 -169
  134. package/dist/tools/__tests__/update-tasks.test.d.ts +0 -2
  135. package/dist/tools/__tests__/update-tasks.test.d.ts.map +0 -1
  136. package/dist/tools/__tests__/update-tasks.test.js +0 -788
  137. package/dist/tools/__tests__/user-info.test.d.ts +0 -2
  138. package/dist/tools/__tests__/user-info.test.d.ts.map +0 -1
  139. package/dist/tools/__tests__/user-info.test.js +0 -139
  140. package/dist/tools/add-comments.js +0 -89
  141. package/dist/tools/add-projects.js +0 -63
  142. package/dist/tools/add-sections.js +0 -74
  143. package/dist/tools/add-tasks.js +0 -169
  144. package/dist/tools/complete-tasks.js +0 -68
  145. package/dist/tools/delete-object.js +0 -79
  146. package/dist/tools/fetch.js +0 -102
  147. package/dist/tools/find-activity.js +0 -221
  148. package/dist/tools/find-comments.js +0 -148
  149. package/dist/tools/find-completed-tasks.js +0 -168
  150. package/dist/tools/find-project-collaborators.js +0 -151
  151. package/dist/tools/find-projects.js +0 -101
  152. package/dist/tools/find-sections.js +0 -101
  153. package/dist/tools/find-tasks-by-date.js +0 -198
  154. package/dist/tools/find-tasks.js +0 -329
  155. package/dist/tools/get-overview.js +0 -249
  156. package/dist/tools/manage-assignments.js +0 -337
  157. package/dist/tools/search.js +0 -65
  158. package/dist/tools/update-comments.js +0 -82
  159. package/dist/tools/update-projects.js +0 -84
  160. package/dist/tools/update-sections.js +0 -70
  161. package/dist/tools/update-tasks.js +0 -179
  162. package/dist/tools/user-info.js +0 -142
  163. package/dist/utils/assignment-validator.js +0 -253
  164. package/dist/utils/constants.js +0 -45
  165. package/dist/utils/duration-parser.js +0 -96
  166. package/dist/utils/duration-parser.test.d.ts +0 -2
  167. package/dist/utils/duration-parser.test.d.ts.map +0 -1
  168. package/dist/utils/duration-parser.test.js +0 -147
  169. package/dist/utils/labels.js +0 -18
  170. package/dist/utils/priorities.js +0 -20
  171. package/dist/utils/response-builders.js +0 -210
  172. package/dist/utils/sanitize-data.js +0 -37
  173. package/dist/utils/sanitize-data.test.d.ts +0 -2
  174. package/dist/utils/sanitize-data.test.d.ts.map +0 -1
  175. package/dist/utils/sanitize-data.test.js +0 -93
  176. package/dist/utils/test-helpers.js +0 -237
  177. package/dist/utils/tool-names.js +0 -40
  178. package/dist/utils/user-resolver.js +0 -179
@@ -1,606 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { createMockTask, createMockUser, extractStructuredContent, extractTextContent, TEST_IDS, TODAY, } from '../../utils/test-helpers.js';
3
- import { ToolNames } from '../../utils/tool-names.js';
4
- import { addTasks } from '../add-tasks.js';
5
- // Mock the Todoist API
6
- const mockTodoistApi = {
7
- addTask: jest.fn(),
8
- getUser: jest.fn(),
9
- };
10
- const { ADD_TASKS, GET_OVERVIEW } = ToolNames;
11
- describe(`${ADD_TASKS} tool`, () => {
12
- beforeEach(() => {
13
- jest.clearAllMocks();
14
- });
15
- describe('adding multiple tasks', () => {
16
- it('should add multiple tasks and return mapped results', async () => {
17
- // Mock API responses extracted from recordings (Task type)
18
- const mockApiResponse1 = createMockTask({
19
- id: '8485093748',
20
- content: 'First task content',
21
- url: 'https://todoist.com/showTask?id=8485093748',
22
- addedAt: '2025-08-13T22:09:56.123456Z',
23
- });
24
- const mockApiResponse2 = createMockTask({
25
- id: '8485093749',
26
- content: 'Second task content',
27
- description: 'Task description',
28
- labels: ['work', 'urgent'],
29
- childOrder: 2,
30
- priority: 2,
31
- url: 'https://todoist.com/showTask?id=8485093749',
32
- addedAt: '2025-08-13T22:09:57.123456Z',
33
- due: {
34
- date: '2025-08-15',
35
- isRecurring: false,
36
- lang: 'en',
37
- string: 'Aug 15',
38
- timezone: null,
39
- },
40
- });
41
- mockTodoistApi.addTask
42
- .mockResolvedValueOnce(mockApiResponse1)
43
- .mockResolvedValueOnce(mockApiResponse2);
44
- const result = await addTasks.execute({
45
- tasks: [
46
- {
47
- content: 'First task content',
48
- projectId: '6cfCcrrCFg2xP94Q',
49
- },
50
- {
51
- content: 'Second task content',
52
- description: 'Task description',
53
- priority: 'p2',
54
- dueString: 'Aug 15',
55
- projectId: '6cfCcrrCFg2xP94Q',
56
- },
57
- ],
58
- }, mockTodoistApi);
59
- // Verify API was called correctly for each task
60
- expect(mockTodoistApi.addTask).toHaveBeenCalledTimes(2);
61
- expect(mockTodoistApi.addTask).toHaveBeenNthCalledWith(1, {
62
- content: 'First task content',
63
- projectId: '6cfCcrrCFg2xP94Q',
64
- sectionId: undefined,
65
- parentId: undefined,
66
- });
67
- expect(mockTodoistApi.addTask).toHaveBeenNthCalledWith(2, {
68
- content: 'Second task content',
69
- description: 'Task description',
70
- priority: 3,
71
- dueString: 'Aug 15',
72
- projectId: '6cfCcrrCFg2xP94Q',
73
- sectionId: undefined,
74
- parentId: undefined,
75
- });
76
- // Verify result is a concise summary
77
- expect(extractTextContent(result)).toMatchSnapshot();
78
- // Verify structured content
79
- const structuredContent = extractStructuredContent(result);
80
- expect(structuredContent.tasks).toHaveLength(2);
81
- expect(structuredContent).toEqual(expect.objectContaining({
82
- totalCount: 2,
83
- tasks: expect.arrayContaining([
84
- expect.objectContaining({ id: '8485093748' }),
85
- expect.objectContaining({ id: '8485093749' }),
86
- ]),
87
- }));
88
- });
89
- it('should handle tasks with section and parent IDs', async () => {
90
- const mockApiResponse = createMockTask({
91
- id: '8485093750',
92
- content: 'Subtask content',
93
- description: 'Subtask description',
94
- priority: 3,
95
- sectionId: 'section-123',
96
- parentId: 'parent-task-456',
97
- url: 'https://todoist.com/showTask?id=8485093750',
98
- addedAt: '2025-08-13T22:09:58.123456Z',
99
- });
100
- mockTodoistApi.addTask.mockResolvedValue(mockApiResponse);
101
- const result = await addTasks.execute({
102
- tasks: [
103
- {
104
- content: 'Subtask content',
105
- description: 'Subtask description',
106
- priority: 'p3',
107
- projectId: '6cfCcrrCFg2xP94Q',
108
- sectionId: 'section-123',
109
- parentId: 'parent-task-456',
110
- },
111
- ],
112
- }, mockTodoistApi);
113
- expect(mockTodoistApi.addTask).toHaveBeenCalledWith({
114
- content: 'Subtask content',
115
- description: 'Subtask description',
116
- priority: 2,
117
- projectId: '6cfCcrrCFg2xP94Q',
118
- sectionId: 'section-123',
119
- parentId: 'parent-task-456',
120
- });
121
- // Verify result is a concise summary
122
- expect(extractTextContent(result)).toMatchSnapshot();
123
- // Verify structured content
124
- const structuredContent = extractStructuredContent(result);
125
- expect(structuredContent).toEqual(expect.objectContaining({
126
- totalCount: 1,
127
- tasks: expect.arrayContaining([expect.objectContaining({ id: '8485093750' })]),
128
- }));
129
- });
130
- it('should add tasks with duration', async () => {
131
- const mockApiResponse1 = createMockTask({
132
- id: '8485093752',
133
- content: 'Task with 2 hour duration',
134
- duration: { amount: 120, unit: 'minute' },
135
- url: 'https://todoist.com/showTask?id=8485093752',
136
- addedAt: '2025-08-13T22:09:56.123456Z',
137
- });
138
- const mockApiResponse2 = createMockTask({
139
- id: '8485093753',
140
- content: 'Task with 45 minute duration',
141
- duration: { amount: 45, unit: 'minute' },
142
- url: 'https://todoist.com/showTask?id=8485093753',
143
- addedAt: '2025-08-13T22:09:57.123456Z',
144
- });
145
- mockTodoistApi.addTask
146
- .mockResolvedValueOnce(mockApiResponse1)
147
- .mockResolvedValueOnce(mockApiResponse2);
148
- const result = await addTasks.execute({
149
- tasks: [
150
- {
151
- content: 'Task with 2 hour duration',
152
- duration: '2h',
153
- projectId: '6cfCcrrCFg2xP94Q',
154
- },
155
- {
156
- content: 'Task with 45 minute duration',
157
- duration: '45m',
158
- projectId: '6cfCcrrCFg2xP94Q',
159
- },
160
- ],
161
- }, mockTodoistApi);
162
- // Verify API was called with parsed duration
163
- expect(mockTodoistApi.addTask).toHaveBeenNthCalledWith(1, {
164
- content: 'Task with 2 hour duration',
165
- projectId: '6cfCcrrCFg2xP94Q',
166
- sectionId: undefined,
167
- parentId: undefined,
168
- duration: 120,
169
- durationUnit: 'minute',
170
- });
171
- expect(mockTodoistApi.addTask).toHaveBeenNthCalledWith(2, {
172
- content: 'Task with 45 minute duration',
173
- projectId: '6cfCcrrCFg2xP94Q',
174
- sectionId: undefined,
175
- parentId: undefined,
176
- duration: 45,
177
- durationUnit: 'minute',
178
- });
179
- // Verify result is a concise summary
180
- expect(extractTextContent(result)).toMatchSnapshot();
181
- // Verify structured content
182
- const structuredContent = extractStructuredContent(result);
183
- expect(structuredContent.tasks).toHaveLength(2);
184
- expect(structuredContent).toEqual(expect.objectContaining({
185
- totalCount: 2,
186
- tasks: expect.arrayContaining([
187
- expect.objectContaining({ id: '8485093752' }),
188
- expect.objectContaining({ id: '8485093753' }),
189
- ]),
190
- }));
191
- });
192
- it('should handle various duration formats', async () => {
193
- const mockApiResponse = createMockTask({
194
- id: '8485093754',
195
- content: 'Task with combined duration',
196
- duration: { amount: 150, unit: 'minute' },
197
- url: 'https://todoist.com/showTask?id=8485093754',
198
- addedAt: '2025-08-13T22:09:56.123456Z',
199
- });
200
- mockTodoistApi.addTask.mockResolvedValue(mockApiResponse);
201
- // Test different duration formats
202
- const testCases = [
203
- { input: '2h30m', expectedMinutes: 150 },
204
- { input: '1.5h', expectedMinutes: 90 },
205
- { input: ' 90m ', expectedMinutes: 90 },
206
- { input: '2H30M', expectedMinutes: 150 },
207
- ];
208
- for (const testCase of testCases) {
209
- mockTodoistApi.addTask.mockClear();
210
- await addTasks.execute({
211
- tasks: [
212
- {
213
- content: 'Test task',
214
- duration: testCase.input,
215
- projectId: '6cfCcrrCFg2xP94Q',
216
- },
217
- ],
218
- }, mockTodoistApi);
219
- expect(mockTodoistApi.addTask).toHaveBeenCalledWith(expect.objectContaining({
220
- duration: testCase.expectedMinutes,
221
- durationUnit: 'minute',
222
- }));
223
- }
224
- });
225
- it('should add task with deadline', async () => {
226
- const mockApiResponse = createMockTask({
227
- id: '8485093756',
228
- content: 'Task with deadline',
229
- deadline: {
230
- date: '2025-12-31',
231
- lang: 'en',
232
- },
233
- url: 'https://todoist.com/showTask?id=8485093756',
234
- addedAt: '2025-08-13T22:09:56.123456Z',
235
- });
236
- mockTodoistApi.addTask.mockResolvedValue(mockApiResponse);
237
- const result = await addTasks.execute({
238
- tasks: [
239
- {
240
- content: 'Task with deadline',
241
- projectId: '6cfCcrrCFg2xP94Q',
242
- deadlineDate: '2025-12-31',
243
- },
244
- ],
245
- }, mockTodoistApi);
246
- // Verify API was called with deadline
247
- expect(mockTodoistApi.addTask).toHaveBeenCalledWith({
248
- content: 'Task with deadline',
249
- projectId: '6cfCcrrCFg2xP94Q',
250
- sectionId: undefined,
251
- parentId: undefined,
252
- deadlineDate: '2025-12-31',
253
- });
254
- // Verify result is a concise summary
255
- expect(extractTextContent(result)).toMatchSnapshot();
256
- // Verify structured content includes deadline
257
- const structuredContent = extractStructuredContent(result);
258
- expect(structuredContent.tasks).toHaveLength(1);
259
- expect(structuredContent).toEqual(expect.objectContaining({
260
- totalCount: 1,
261
- tasks: expect.arrayContaining([
262
- expect.objectContaining({
263
- id: '8485093756',
264
- deadlineDate: '2025-12-31',
265
- }),
266
- ]),
267
- }));
268
- });
269
- it('should add task with labels', async () => {
270
- const mockApiResponse = createMockTask({
271
- id: '8485093755',
272
- content: 'Task with labels',
273
- labels: ['urgent', 'work'],
274
- url: 'https://todoist.com/showTask?id=8485093755',
275
- addedAt: '2025-08-13T22:09:56.123456Z',
276
- });
277
- mockTodoistApi.addTask.mockResolvedValue(mockApiResponse);
278
- const result = await addTasks.execute({
279
- tasks: [
280
- {
281
- content: 'Task with labels',
282
- labels: ['urgent', 'work'],
283
- projectId: '6cfCcrrCFg2xP94Q',
284
- },
285
- ],
286
- }, mockTodoistApi);
287
- expect(mockTodoistApi.addTask).toHaveBeenCalledWith({
288
- content: 'Task with labels',
289
- labels: ['urgent', 'work'],
290
- projectId: '6cfCcrrCFg2xP94Q',
291
- sectionId: undefined,
292
- parentId: undefined,
293
- });
294
- // Verify structured content includes labels
295
- const structuredContent = extractStructuredContent(result);
296
- expect(structuredContent.tasks).toHaveLength(1);
297
- expect(structuredContent.tasks).toEqual(expect.arrayContaining([expect.objectContaining({ labels: ['urgent', 'work'] })]));
298
- });
299
- it('should add task with empty labels array', async () => {
300
- const mockApiResponse = createMockTask({
301
- id: '8485093756',
302
- content: 'Task with empty labels',
303
- labels: [],
304
- url: 'https://todoist.com/showTask?id=8485093756',
305
- addedAt: '2025-08-13T22:09:56.123456Z',
306
- });
307
- mockTodoistApi.addTask.mockResolvedValue(mockApiResponse);
308
- await addTasks.execute({
309
- tasks: [
310
- {
311
- content: 'Task with empty labels',
312
- labels: [],
313
- projectId: '6cfCcrrCFg2xP94Q',
314
- },
315
- ],
316
- }, mockTodoistApi);
317
- expect(mockTodoistApi.addTask).toHaveBeenCalledWith({
318
- content: 'Task with empty labels',
319
- labels: [],
320
- projectId: '6cfCcrrCFg2xP94Q',
321
- sectionId: undefined,
322
- parentId: undefined,
323
- });
324
- });
325
- it('should add task without labels field', async () => {
326
- const mockApiResponse = createMockTask({
327
- id: '8485093757',
328
- content: 'Task without labels',
329
- url: 'https://todoist.com/showTask?id=8485093757',
330
- addedAt: '2025-08-13T22:09:56.123456Z',
331
- });
332
- mockTodoistApi.addTask.mockResolvedValue(mockApiResponse);
333
- await addTasks.execute({
334
- tasks: [
335
- {
336
- content: 'Task without labels',
337
- projectId: '6cfCcrrCFg2xP94Q',
338
- },
339
- ],
340
- }, mockTodoistApi);
341
- expect(mockTodoistApi.addTask).toHaveBeenCalledWith({
342
- content: 'Task without labels',
343
- labels: undefined,
344
- projectId: '6cfCcrrCFg2xP94Q',
345
- sectionId: undefined,
346
- parentId: undefined,
347
- });
348
- });
349
- it('should add multiple tasks with different label configurations', async () => {
350
- const mockApiResponse1 = createMockTask({
351
- id: '8485093758',
352
- content: 'Task with labels',
353
- labels: ['personal'],
354
- });
355
- const mockApiResponse2 = createMockTask({
356
- id: '8485093759',
357
- content: 'Task without labels',
358
- });
359
- const mockApiResponse3 = createMockTask({
360
- id: '8485093760',
361
- content: 'Task with multiple labels',
362
- labels: ['work', 'urgent', 'review'],
363
- });
364
- mockTodoistApi.addTask
365
- .mockResolvedValueOnce(mockApiResponse1)
366
- .mockResolvedValueOnce(mockApiResponse2)
367
- .mockResolvedValueOnce(mockApiResponse3);
368
- await addTasks.execute({
369
- tasks: [
370
- {
371
- content: 'Task with labels',
372
- labels: ['personal'],
373
- projectId: '6cfCcrrCFg2xP94Q',
374
- },
375
- {
376
- content: 'Task without labels',
377
- projectId: '6cfCcrrCFg2xP94Q',
378
- },
379
- {
380
- content: 'Task with multiple labels',
381
- labels: ['work', 'urgent', 'review'],
382
- projectId: '6cfCcrrCFg2xP94Q',
383
- },
384
- ],
385
- }, mockTodoistApi);
386
- expect(mockTodoistApi.addTask).toHaveBeenCalledTimes(3);
387
- expect(mockTodoistApi.addTask).toHaveBeenNthCalledWith(1, expect.objectContaining({
388
- labels: ['personal'],
389
- }));
390
- expect(mockTodoistApi.addTask).toHaveBeenNthCalledWith(2, expect.objectContaining({
391
- labels: undefined,
392
- }));
393
- expect(mockTodoistApi.addTask).toHaveBeenNthCalledWith(3, expect.objectContaining({
394
- labels: ['work', 'urgent', 'review'],
395
- }));
396
- });
397
- });
398
- describe('error handling', () => {
399
- it('should throw error for invalid duration format', async () => {
400
- await expect(addTasks.execute({
401
- tasks: [
402
- {
403
- content: 'Task with invalid duration',
404
- duration: 'invalid',
405
- projectId: '6cfCcrrCFg2xP94Q',
406
- },
407
- ],
408
- }, mockTodoistApi)).rejects.toThrow('Task "Task with invalid duration": Invalid duration format "invalid"');
409
- });
410
- it('should throw error for duration exceeding 24 hours', async () => {
411
- await expect(addTasks.execute({
412
- tasks: [
413
- {
414
- content: 'Task with too long duration',
415
- duration: '25h',
416
- projectId: '6cfCcrrCFg2xP94Q',
417
- },
418
- ],
419
- }, mockTodoistApi)).rejects.toThrow('Task "Task with too long duration": Invalid duration format "25h": Duration cannot exceed 24 hours (1440 minutes)');
420
- });
421
- it('should propagate API errors', async () => {
422
- const apiError = new Error('API Error: Task content is required');
423
- mockTodoistApi.addTask.mockRejectedValue(apiError);
424
- await expect(addTasks.execute({ tasks: [{ content: '', projectId: '6cfCcrrCFg2xP94Q' }] }, mockTodoistApi)).rejects.toThrow(apiError.message);
425
- });
426
- it('should handle partial failures when adding multiple tasks', async () => {
427
- const mockApiResponse = createMockTask({
428
- id: '8485093751',
429
- content: 'First task content',
430
- url: 'https://todoist.com/showTask?id=8485093751',
431
- addedAt: '2025-08-13T22:09:59.123456Z',
432
- });
433
- const apiError = new Error('API Error: Second task failed');
434
- mockTodoistApi.addTask
435
- .mockResolvedValueOnce(mockApiResponse)
436
- .mockRejectedValueOnce(apiError);
437
- await expect(addTasks.execute({
438
- tasks: [
439
- { content: 'First task content', projectId: '6cfCcrrCFg2xP94Q' },
440
- { content: 'Second task content', projectId: '6cfCcrrCFg2xP94Q' },
441
- ],
442
- }, mockTodoistApi)).rejects.toThrow('API Error: Second task failed');
443
- // Verify first task was attempted
444
- expect(mockTodoistApi.addTask).toHaveBeenCalledTimes(2);
445
- });
446
- });
447
- describe('next steps logic', () => {
448
- it('should suggest find-tasks-by-date for today when hasToday is true', async () => {
449
- // Clear any leftover mocks from previous tests
450
- mockTodoistApi.addTask.mockClear();
451
- const mockApiResponse = createMockTask({
452
- id: '8485093755',
453
- content: 'Task due today',
454
- url: 'https://todoist.com/showTask?id=8485093755',
455
- addedAt: '2025-08-13T22:09:56.123456Z',
456
- due: {
457
- date: TODAY,
458
- isRecurring: false,
459
- lang: 'en',
460
- string: 'today',
461
- timezone: null,
462
- },
463
- });
464
- mockTodoistApi.addTask.mockResolvedValue(mockApiResponse);
465
- const result = await addTasks.execute({
466
- tasks: [
467
- {
468
- content: 'Task due today',
469
- dueString: 'today',
470
- projectId: '6cfCcrrCFg2xP94Q',
471
- },
472
- ],
473
- }, mockTodoistApi);
474
- const textContent = extractTextContent(result);
475
- expect(textContent).toMatchSnapshot();
476
- expect(textContent).toContain(`Use ${GET_OVERVIEW} to see your updated project organization`);
477
- });
478
- it('should suggest overview tool when no hasToday context', async () => {
479
- // Clear any leftover mocks from previous tests
480
- mockTodoistApi.addTask.mockClear();
481
- const mockApiResponse = createMockTask({
482
- id: '8485093756',
483
- content: 'Regular task',
484
- url: 'https://todoist.com/showTask?id=8485093756',
485
- addedAt: '2025-08-13T22:09:56.123456Z',
486
- });
487
- mockTodoistApi.addTask.mockResolvedValue(mockApiResponse);
488
- const result = await addTasks.execute({
489
- tasks: [{ content: 'Regular task', projectId: '6cfCcrrCFg2xP94Q' }],
490
- }, mockTodoistApi);
491
- const textContent = extractTextContent(result);
492
- expect(textContent).toMatchSnapshot();
493
- expect(textContent).toContain(`Use ${GET_OVERVIEW} to see your updated project organization`);
494
- });
495
- });
496
- describe('tasks without project context', () => {
497
- it('should allow creating tasks with only content (goes to Inbox)', async () => {
498
- const mockApiResponse = createMockTask({
499
- id: '8485093758',
500
- content: 'Simple inbox task',
501
- url: 'https://todoist.com/showTask?id=8485093758',
502
- addedAt: '2025-08-13T22:09:56.123456Z',
503
- });
504
- mockTodoistApi.addTask.mockResolvedValue(mockApiResponse);
505
- const result = await addTasks.execute({
506
- tasks: [
507
- {
508
- content: 'Simple inbox task',
509
- },
510
- ],
511
- }, mockTodoistApi);
512
- expect(mockTodoistApi.addTask).toHaveBeenCalledWith({
513
- content: 'Simple inbox task',
514
- labels: undefined,
515
- projectId: undefined,
516
- sectionId: undefined,
517
- parentId: undefined,
518
- });
519
- const textContent = extractTextContent(result);
520
- expect(textContent).toContain('Added 1 task');
521
- expect(textContent).toContain('Simple inbox task');
522
- });
523
- it('should prevent assignment without project context', async () => {
524
- await expect(addTasks.execute({
525
- tasks: [
526
- {
527
- content: 'Task with assignment but no project',
528
- responsibleUser: 'user@example.com',
529
- },
530
- ],
531
- }, mockTodoistApi)).rejects.toThrow('Task "Task with assignment but no project": Cannot assign tasks without specifying project context. Please specify a projectId, sectionId, or parentId.');
532
- });
533
- });
534
- describe('inbox project ID resolution', () => {
535
- it('should resolve "inbox" to actual inbox project ID', async () => {
536
- const mockUser = createMockUser({
537
- inboxProjectId: TEST_IDS.PROJECT_INBOX,
538
- });
539
- const mockApiResponse = createMockTask({
540
- id: '8485093760',
541
- content: 'Task for inbox',
542
- projectId: TEST_IDS.PROJECT_INBOX,
543
- url: 'https://todoist.com/showTask?id=8485093760',
544
- addedAt: '2025-08-13T22:09:56.123456Z',
545
- });
546
- // Mock the API calls
547
- mockTodoistApi.getUser.mockResolvedValue(mockUser);
548
- mockTodoistApi.addTask.mockResolvedValue(mockApiResponse);
549
- const result = await addTasks.execute({
550
- tasks: [
551
- {
552
- content: 'Task for inbox',
553
- projectId: 'inbox',
554
- },
555
- ],
556
- }, mockTodoistApi);
557
- // Verify getUser was called to resolve inbox
558
- expect(mockTodoistApi.getUser).toHaveBeenCalledTimes(1);
559
- // Verify addTask was called with resolved inbox project ID
560
- expect(mockTodoistApi.addTask).toHaveBeenCalledWith({
561
- content: 'Task for inbox',
562
- projectId: TEST_IDS.PROJECT_INBOX,
563
- sectionId: undefined,
564
- parentId: undefined,
565
- labels: undefined,
566
- });
567
- // Verify result contains the task
568
- const structuredContent = extractStructuredContent(result);
569
- expect(structuredContent.totalCount).toBe(1);
570
- expect(structuredContent.tasks).toEqual(expect.arrayContaining([
571
- expect.objectContaining({
572
- id: '8485093760',
573
- projectId: TEST_IDS.PROJECT_INBOX,
574
- }),
575
- ]));
576
- });
577
- it('should not call getUser when projectId is not "inbox"', async () => {
578
- const mockApiResponse = createMockTask({
579
- id: '8485093761',
580
- content: 'Regular task',
581
- projectId: '6cfCcrrCFg2xP94Q',
582
- url: 'https://todoist.com/showTask?id=8485093761',
583
- addedAt: '2025-08-13T22:09:56.123456Z',
584
- });
585
- mockTodoistApi.addTask.mockResolvedValue(mockApiResponse);
586
- await addTasks.execute({
587
- tasks: [
588
- {
589
- content: 'Regular task',
590
- projectId: '6cfCcrrCFg2xP94Q',
591
- },
592
- ],
593
- }, mockTodoistApi);
594
- // Verify getUser was NOT called for regular project ID
595
- expect(mockTodoistApi.getUser).not.toHaveBeenCalled();
596
- // Verify addTask was called with original project ID
597
- expect(mockTodoistApi.addTask).toHaveBeenCalledWith({
598
- content: 'Regular task',
599
- projectId: '6cfCcrrCFg2xP94Q',
600
- sectionId: undefined,
601
- parentId: undefined,
602
- labels: undefined,
603
- });
604
- });
605
- });
606
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=assignment-integration.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"assignment-integration.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/assignment-integration.test.ts"],"names":[],"mappings":""}