@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,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=get-overview.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"get-overview.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/get-overview.test.ts"],"names":[],"mappings":""}
@@ -1,225 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { createMockProject, createMockSection, createMockTask, extractStructuredContent, extractTextContent, TEST_ERRORS, TEST_IDS, } from '../../utils/test-helpers.js';
3
- import { ToolNames } from '../../utils/tool-names.js';
4
- import { getOverview } from '../get-overview.js';
5
- // Mock the Todoist API
6
- const mockTodoistApi = {
7
- getProjects: jest.fn(),
8
- getProject: jest.fn(),
9
- getSections: jest.fn(),
10
- getTasks: jest.fn(),
11
- };
12
- const { GET_OVERVIEW } = ToolNames;
13
- describe(`${GET_OVERVIEW} tool`, () => {
14
- beforeEach(() => {
15
- jest.clearAllMocks();
16
- });
17
- describe('account overview (no projectId)', () => {
18
- it('should generate account overview with projects and sections', async () => {
19
- const mockProjects = [
20
- createMockProject({
21
- id: TEST_IDS.PROJECT_INBOX,
22
- name: 'Inbox',
23
- color: 'grey',
24
- inboxProject: true,
25
- childOrder: 0,
26
- }),
27
- createMockProject({
28
- id: TEST_IDS.PROJECT_TEST,
29
- name: 'test-abc123def456-project',
30
- childOrder: 1,
31
- }),
32
- ];
33
- const mockSections = [
34
- createMockSection({
35
- id: TEST_IDS.SECTION_1,
36
- projectId: TEST_IDS.PROJECT_TEST,
37
- name: 'test-section',
38
- }),
39
- ];
40
- mockTodoistApi.getProjects.mockResolvedValue({
41
- results: mockProjects,
42
- nextCursor: null,
43
- });
44
- mockTodoistApi.getSections.mockImplementation(({ projectId }) => {
45
- if (projectId === TEST_IDS.PROJECT_TEST) {
46
- return Promise.resolve({ results: mockSections, nextCursor: null });
47
- }
48
- return Promise.resolve({ results: [], nextCursor: null });
49
- });
50
- const result = await getOverview.execute({}, mockTodoistApi);
51
- expect(mockTodoistApi.getProjects).toHaveBeenCalledWith({});
52
- expect(mockTodoistApi.getSections).toHaveBeenCalledTimes(2); // Once for each project
53
- // Test text content with snapshot
54
- expect(extractTextContent(result)).toMatchSnapshot();
55
- // Test structured content sanity checks
56
- const structuredContent = extractStructuredContent(result);
57
- expect(structuredContent).toEqual(expect.objectContaining({
58
- type: 'account_overview',
59
- inbox: expect.objectContaining({
60
- id: TEST_IDS.PROJECT_INBOX,
61
- name: 'Inbox',
62
- // sections array removed if empty
63
- }),
64
- projects: expect.any(Array),
65
- totalProjects: 2,
66
- totalSections: 1,
67
- hasNestedProjects: false,
68
- }));
69
- expect(structuredContent.projects).toHaveLength(1); // Only non-inbox projects
70
- });
71
- it('should handle empty projects list', async () => {
72
- mockTodoistApi.getProjects.mockResolvedValue({ results: [], nextCursor: null });
73
- const result = await getOverview.execute({}, mockTodoistApi);
74
- // Test text content with snapshot
75
- expect(extractTextContent(result)).toMatchSnapshot();
76
- // Test structured content sanity checks
77
- const structuredContent = extractStructuredContent(result);
78
- expect(structuredContent).toEqual({
79
- type: 'account_overview',
80
- // projects array is removed when empty
81
- totalProjects: 0,
82
- totalSections: 0,
83
- hasNestedProjects: false,
84
- });
85
- });
86
- });
87
- describe('project overview (with projectId)', () => {
88
- it('should generate detailed project overview with tasks', async () => {
89
- const mockProject = createMockProject({
90
- id: TEST_IDS.PROJECT_TEST,
91
- name: 'test-abc123def456-project',
92
- });
93
- const mockSections = [
94
- createMockSection({
95
- id: TEST_IDS.SECTION_1,
96
- projectId: TEST_IDS.PROJECT_TEST,
97
- name: 'To Do',
98
- }),
99
- createMockSection({
100
- id: TEST_IDS.SECTION_2,
101
- projectId: TEST_IDS.PROJECT_TEST,
102
- sectionOrder: 2,
103
- name: 'In Progress',
104
- }),
105
- ];
106
- const mockTasks = [
107
- createMockTask({
108
- id: TEST_IDS.TASK_1,
109
- content: 'Task without section',
110
- projectId: TEST_IDS.PROJECT_TEST,
111
- deadline: {
112
- date: '2025-08-15',
113
- lang: 'en',
114
- },
115
- responsibleUid: TEST_IDS.USER_ID,
116
- assignedByUid: TEST_IDS.USER_ID,
117
- }),
118
- createMockTask({
119
- id: TEST_IDS.TASK_2,
120
- content: 'Task in To Do section',
121
- description: 'Important task',
122
- labels: ['work'],
123
- priority: 2,
124
- projectId: TEST_IDS.PROJECT_TEST,
125
- sectionId: TEST_IDS.SECTION_1,
126
- }),
127
- createMockTask({
128
- id: TEST_IDS.TASK_3,
129
- content: 'Subtask of important task',
130
- childOrder: 2,
131
- projectId: TEST_IDS.PROJECT_TEST,
132
- sectionId: TEST_IDS.SECTION_1,
133
- parentId: TEST_IDS.TASK_2,
134
- }),
135
- ];
136
- mockTodoistApi.getProject.mockResolvedValue(mockProject);
137
- mockTodoistApi.getSections.mockResolvedValue({
138
- results: mockSections,
139
- nextCursor: null,
140
- });
141
- mockTodoistApi.getTasks.mockResolvedValue({
142
- results: mockTasks,
143
- nextCursor: null,
144
- });
145
- const result = await getOverview.execute({ projectId: TEST_IDS.PROJECT_TEST }, mockTodoistApi);
146
- expect(mockTodoistApi.getProject).toHaveBeenCalledWith(TEST_IDS.PROJECT_TEST);
147
- expect(mockTodoistApi.getSections).toHaveBeenCalledWith({
148
- projectId: TEST_IDS.PROJECT_TEST,
149
- });
150
- expect(mockTodoistApi.getTasks).toHaveBeenCalledWith({
151
- projectId: TEST_IDS.PROJECT_TEST,
152
- limit: 50,
153
- cursor: undefined,
154
- });
155
- // Test text content with snapshot
156
- expect(extractTextContent(result)).toMatchSnapshot();
157
- // Test structured content sanity checks
158
- const structuredContent = extractStructuredContent(result);
159
- expect(structuredContent).toEqual(expect.objectContaining({
160
- type: 'project_overview',
161
- project: expect.objectContaining({
162
- id: TEST_IDS.PROJECT_TEST,
163
- name: 'test-abc123def456-project',
164
- }),
165
- sections: expect.any(Array),
166
- tasks: expect.any(Array),
167
- stats: expect.objectContaining({
168
- totalTasks: 3,
169
- totalSections: 2,
170
- tasksWithoutSection: 1,
171
- }),
172
- }));
173
- expect(structuredContent.sections).toHaveLength(2);
174
- expect(structuredContent.tasks).toHaveLength(3);
175
- });
176
- it('should handle project with no tasks', async () => {
177
- const mockProject = createMockProject({
178
- id: 'empty-project-id',
179
- name: 'Empty Project',
180
- color: 'blue',
181
- });
182
- mockTodoistApi.getProject.mockResolvedValue(mockProject);
183
- mockTodoistApi.getSections.mockResolvedValue({ results: [], nextCursor: null });
184
- mockTodoistApi.getTasks.mockResolvedValue({ results: [], nextCursor: null });
185
- const result = await getOverview.execute({ projectId: 'empty-project-id' }, mockTodoistApi);
186
- // Test text content with snapshot
187
- expect(extractTextContent(result)).toMatchSnapshot();
188
- // Test structured content sanity checks
189
- const structuredContent = extractStructuredContent(result);
190
- expect(structuredContent).toEqual({
191
- type: 'project_overview',
192
- project: expect.objectContaining({
193
- id: 'empty-project-id',
194
- name: 'Empty Project',
195
- }),
196
- // sections and tasks arrays are removed when empty
197
- stats: expect.objectContaining({
198
- totalTasks: 0,
199
- totalSections: 0,
200
- tasksWithoutSection: 0,
201
- }),
202
- });
203
- });
204
- });
205
- describe('error handling', () => {
206
- it.each([
207
- {
208
- scenario: 'project retrieval',
209
- error: 'API Error: Project not found',
210
- params: { projectId: 'non-existent-project' },
211
- mockMethod: 'getProject',
212
- },
213
- {
214
- scenario: 'projects list',
215
- error: TEST_ERRORS.API_UNAUTHORIZED,
216
- params: {},
217
- mockMethod: 'getProjects',
218
- },
219
- ])('should propagate API errors for $scenario', async ({ error, params, mockMethod }) => {
220
- const apiError = new Error(error);
221
- mockTodoistApi[mockMethod].mockRejectedValue(apiError);
222
- await expect(getOverview.execute(params, mockTodoistApi)).rejects.toThrow(error);
223
- });
224
- });
225
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=search.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"search.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/search.test.ts"],"names":[],"mappings":""}
@@ -1,206 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { getTasksByFilter } from '../../tool-helpers.js';
3
- import { createMappedTask, createMockApiResponse, createMockProject, TEST_IDS, } from '../../utils/test-helpers.js';
4
- import { ToolNames } from '../../utils/tool-names.js';
5
- import { search } from '../search.js';
6
- jest.mock('../../tool-helpers', () => {
7
- return {
8
- getTasksByFilter: jest.fn(),
9
- };
10
- });
11
- const { SEARCH } = ToolNames;
12
- const mockGetTasksByFilter = getTasksByFilter;
13
- // Mock the Todoist API
14
- const mockTodoistApi = {
15
- getProjects: jest.fn(),
16
- };
17
- describe(`${SEARCH} tool`, () => {
18
- beforeEach(() => {
19
- jest.clearAllMocks();
20
- });
21
- describe('searching tasks and projects', () => {
22
- it('should search both tasks and projects and return combined results', async () => {
23
- const mockTasks = [
24
- createMappedTask({
25
- id: TEST_IDS.TASK_1,
26
- content: 'Important meeting task',
27
- }),
28
- createMappedTask({
29
- id: TEST_IDS.TASK_2,
30
- content: 'Another important item',
31
- }),
32
- ];
33
- const mockProjects = [
34
- createMockProject({
35
- id: TEST_IDS.PROJECT_WORK,
36
- name: 'Important Work Project',
37
- }),
38
- createMockProject({
39
- id: TEST_IDS.PROJECT_TEST,
40
- name: 'Test Project',
41
- }),
42
- ];
43
- mockGetTasksByFilter.mockResolvedValue({ tasks: mockTasks, nextCursor: null });
44
- mockTodoistApi.getProjects.mockResolvedValue(createMockApiResponse(mockProjects));
45
- const result = await search.execute({ query: 'important' }, mockTodoistApi);
46
- // Verify both API calls were made
47
- expect(mockGetTasksByFilter).toHaveBeenCalledWith({
48
- client: mockTodoistApi,
49
- query: 'search: important',
50
- limit: 100, // TASKS_MAX
51
- cursor: undefined,
52
- });
53
- expect(mockTodoistApi.getProjects).toHaveBeenCalledWith({
54
- limit: 100, // PROJECTS_MAX
55
- });
56
- // Verify result structure
57
- expect(result.content).toHaveLength(1);
58
- expect(result.content[0]?.type).toBe('text');
59
- // Parse the JSON response
60
- const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
61
- expect(jsonResponse).toHaveProperty('results');
62
- expect(jsonResponse.results).toHaveLength(3); // 2 tasks + 1 project matching "important"
63
- // Verify task results
64
- expect(jsonResponse.results[0]).toEqual({
65
- id: `task:${TEST_IDS.TASK_1}`,
66
- title: 'Important meeting task',
67
- url: `https://app.todoist.com/app/task/${TEST_IDS.TASK_1}`,
68
- });
69
- expect(jsonResponse.results[1]).toEqual({
70
- id: `task:${TEST_IDS.TASK_2}`,
71
- title: 'Another important item',
72
- url: `https://app.todoist.com/app/task/${TEST_IDS.TASK_2}`,
73
- });
74
- // Verify project result (only "Important Work Project" matches)
75
- expect(jsonResponse.results[2]).toEqual({
76
- id: `project:${TEST_IDS.PROJECT_WORK}`,
77
- title: 'Important Work Project',
78
- url: `https://app.todoist.com/app/project/${TEST_IDS.PROJECT_WORK}`,
79
- });
80
- });
81
- it('should return only matching tasks when no projects match', async () => {
82
- const mockTasks = [
83
- createMappedTask({
84
- id: TEST_IDS.TASK_1,
85
- content: 'Unique task content',
86
- }),
87
- ];
88
- const mockProjects = [
89
- createMockProject({
90
- id: TEST_IDS.PROJECT_WORK,
91
- name: 'Work Project',
92
- }),
93
- ];
94
- mockGetTasksByFilter.mockResolvedValue({ tasks: mockTasks, nextCursor: null });
95
- mockTodoistApi.getProjects.mockResolvedValue(createMockApiResponse(mockProjects));
96
- const result = await search.execute({ query: 'unique' }, mockTodoistApi);
97
- const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
98
- expect(jsonResponse.results).toHaveLength(1);
99
- expect(jsonResponse.results[0].id).toBe(`task:${TEST_IDS.TASK_1}`);
100
- });
101
- it('should return only matching projects when no tasks match', async () => {
102
- const mockProjects = [
103
- createMockProject({
104
- id: TEST_IDS.PROJECT_WORK,
105
- name: 'Special Project Name',
106
- }),
107
- createMockProject({
108
- id: TEST_IDS.PROJECT_TEST,
109
- name: 'Another Project',
110
- }),
111
- ];
112
- mockGetTasksByFilter.mockResolvedValue({ tasks: [], nextCursor: null });
113
- mockTodoistApi.getProjects.mockResolvedValue(createMockApiResponse(mockProjects));
114
- const result = await search.execute({ query: 'special' }, mockTodoistApi);
115
- const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
116
- expect(jsonResponse.results).toHaveLength(1);
117
- expect(jsonResponse.results[0]).toEqual({
118
- id: `project:${TEST_IDS.PROJECT_WORK}`,
119
- title: 'Special Project Name',
120
- url: `https://app.todoist.com/app/project/${TEST_IDS.PROJECT_WORK}`,
121
- });
122
- });
123
- it('should return empty results when nothing matches', async () => {
124
- mockGetTasksByFilter.mockResolvedValue({ tasks: [], nextCursor: null });
125
- mockTodoistApi.getProjects.mockResolvedValue(createMockApiResponse([]));
126
- const result = await search.execute({ query: 'nonexistent' }, mockTodoistApi);
127
- const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
128
- expect(jsonResponse.results).toHaveLength(0);
129
- });
130
- it('should perform case-insensitive project filtering', async () => {
131
- const mockProjects = [
132
- createMockProject({
133
- id: TEST_IDS.PROJECT_WORK,
134
- name: 'Important Work',
135
- }),
136
- ];
137
- mockGetTasksByFilter.mockResolvedValue({ tasks: [], nextCursor: null });
138
- mockTodoistApi.getProjects.mockResolvedValue(createMockApiResponse(mockProjects));
139
- const result = await search.execute({ query: 'IMPORTANT' }, mockTodoistApi);
140
- const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
141
- expect(jsonResponse.results).toHaveLength(1);
142
- expect(jsonResponse.results[0].title).toBe('Important Work');
143
- });
144
- it('should handle partial matches in project names', async () => {
145
- const mockProjects = [
146
- createMockProject({ id: 'project-1', name: 'Development Tasks' }),
147
- createMockProject({ id: 'project-2', name: 'Developer Resources' }),
148
- createMockProject({ id: 'project-3', name: 'Marketing' }),
149
- ];
150
- mockGetTasksByFilter.mockResolvedValue({ tasks: [], nextCursor: null });
151
- mockTodoistApi.getProjects.mockResolvedValue(createMockApiResponse(mockProjects));
152
- const result = await search.execute({ query: 'develop' }, mockTodoistApi);
153
- const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
154
- expect(jsonResponse.results).toHaveLength(2);
155
- expect(jsonResponse.results[0].title).toBe('Development Tasks');
156
- expect(jsonResponse.results[1].title).toBe('Developer Resources');
157
- });
158
- });
159
- describe('error handling', () => {
160
- it('should return error response for task search failure', async () => {
161
- mockGetTasksByFilter.mockRejectedValue(new Error('Task search failed'));
162
- mockTodoistApi.getProjects.mockResolvedValue(createMockApiResponse([]));
163
- const result = await search.execute({ query: 'test' }, mockTodoistApi);
164
- expect(result.isError).toBe(true);
165
- expect(result.content[0]?.text).toBe('Task search failed');
166
- });
167
- it('should return error response for project search failure', async () => {
168
- mockGetTasksByFilter.mockResolvedValue({ tasks: [], nextCursor: null });
169
- mockTodoistApi.getProjects.mockRejectedValue(new Error('Project search failed'));
170
- const result = await search.execute({ query: 'test' }, mockTodoistApi);
171
- expect(result.isError).toBe(true);
172
- expect(result.content[0]?.text).toBe('Project search failed');
173
- });
174
- });
175
- describe('OpenAI MCP spec compliance', () => {
176
- it('should return exactly one content item with type "text"', async () => {
177
- mockGetTasksByFilter.mockResolvedValue({ tasks: [], nextCursor: null });
178
- mockTodoistApi.getProjects.mockResolvedValue(createMockApiResponse([]));
179
- const result = await search.execute({ query: 'test' }, mockTodoistApi);
180
- expect(result.content).toHaveLength(1);
181
- expect(result.content[0]?.type).toBe('text');
182
- });
183
- it('should return valid JSON string in text field', async () => {
184
- mockGetTasksByFilter.mockResolvedValue({ tasks: [], nextCursor: null });
185
- mockTodoistApi.getProjects.mockResolvedValue(createMockApiResponse([]));
186
- const result = await search.execute({ query: 'test' }, mockTodoistApi);
187
- expect(() => JSON.parse(result.content[0]?.text ?? '{}')).not.toThrow();
188
- });
189
- it('should include required fields (id, title, url) in each result', async () => {
190
- const mockTasks = [createMappedTask({ id: TEST_IDS.TASK_1, content: 'Test' })];
191
- const mockProjects = [createMockProject({ id: TEST_IDS.PROJECT_WORK, name: 'Test' })];
192
- mockGetTasksByFilter.mockResolvedValue({ tasks: mockTasks, nextCursor: null });
193
- mockTodoistApi.getProjects.mockResolvedValue(createMockApiResponse(mockProjects));
194
- const result = await search.execute({ query: 'test' }, mockTodoistApi);
195
- const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
196
- for (const item of jsonResponse.results) {
197
- expect(item).toHaveProperty('id');
198
- expect(item).toHaveProperty('title');
199
- expect(item).toHaveProperty('url');
200
- expect(typeof item.id).toBe('string');
201
- expect(typeof item.title).toBe('string');
202
- expect(typeof item.url).toBe('string');
203
- }
204
- });
205
- });
206
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=update-comments.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"update-comments.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/update-comments.test.ts"],"names":[],"mappings":""}