@doist/todoist-ai 4.15.1 → 4.16.1

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 (167) hide show
  1. package/dist/filter-helpers.d.ts +1 -1
  2. package/dist/index.d.ts +175 -175
  3. package/dist/index.js +61 -81
  4. package/dist/main.js +15 -23
  5. package/dist/mcp-helpers.d.ts +4 -4
  6. package/dist/mcp-server-6tm7Rhyz.js +2840 -0
  7. package/dist/todoist-tool.d.ts +2 -2
  8. package/dist/tool-helpers.d.ts +1 -1
  9. package/dist/tools/add-comments.d.ts +1 -1
  10. package/dist/tools/add-comments.d.ts.map +1 -1
  11. package/dist/tools/add-projects.d.ts +4 -4
  12. package/dist/tools/add-projects.d.ts.map +1 -1
  13. package/dist/tools/add-sections.d.ts +1 -1
  14. package/dist/tools/add-sections.d.ts.map +1 -1
  15. package/dist/tools/add-tasks.d.ts +4 -4
  16. package/dist/tools/add-tasks.d.ts.map +1 -1
  17. package/dist/tools/complete-tasks.d.ts +1 -1
  18. package/dist/tools/complete-tasks.d.ts.map +1 -1
  19. package/dist/tools/delete-object.d.ts +3 -3
  20. package/dist/tools/delete-object.d.ts.map +1 -1
  21. package/dist/tools/fetch.d.ts +1 -1
  22. package/dist/tools/find-activity.d.ts +5 -5
  23. package/dist/tools/find-activity.d.ts.map +1 -1
  24. package/dist/tools/find-comments.d.ts +2 -2
  25. package/dist/tools/find-comments.d.ts.map +1 -1
  26. package/dist/tools/find-completed-tasks.d.ts +3 -3
  27. package/dist/tools/find-completed-tasks.d.ts.map +1 -1
  28. package/dist/tools/find-project-collaborators.d.ts +2 -2
  29. package/dist/tools/find-projects.d.ts +1 -1
  30. package/dist/tools/find-projects.d.ts.map +1 -1
  31. package/dist/tools/find-sections.d.ts +1 -1
  32. package/dist/tools/find-sections.d.ts.map +1 -1
  33. package/dist/tools/find-tasks-by-date.d.ts +1 -1
  34. package/dist/tools/find-tasks-by-date.d.ts.map +1 -1
  35. package/dist/tools/find-tasks.d.ts +3 -3
  36. package/dist/tools/find-tasks.d.ts.map +1 -1
  37. package/dist/tools/get-overview.d.ts +1 -1
  38. package/dist/tools/manage-assignments.d.ts +1 -1
  39. package/dist/tools/search.d.ts +1 -1
  40. package/dist/tools/update-comments.d.ts +4 -4
  41. package/dist/tools/update-comments.d.ts.map +1 -1
  42. package/dist/tools/update-projects.d.ts +1 -1
  43. package/dist/tools/update-projects.d.ts.map +1 -1
  44. package/dist/tools/update-sections.d.ts +4 -4
  45. package/dist/tools/update-sections.d.ts.map +1 -1
  46. package/dist/tools/update-tasks.d.ts +7 -7
  47. package/dist/tools/update-tasks.d.ts.map +1 -1
  48. package/dist/tools/user-info.d.ts +1 -1
  49. package/dist/utils/assignment-validator.d.ts +2 -2
  50. package/dist/utils/response-builders.d.ts +1 -3
  51. package/dist/utils/response-builders.d.ts.map +1 -1
  52. package/dist/utils/test-helpers.d.ts +1 -1
  53. package/dist/utils/user-resolver.d.ts +1 -1
  54. package/package.json +11 -9
  55. package/dist/filter-helpers.js +0 -79
  56. package/dist/mcp-helpers.js +0 -71
  57. package/dist/mcp-server.js +0 -142
  58. package/dist/todoist-tool.js +0 -1
  59. package/dist/tool-helpers.js +0 -125
  60. package/dist/tool-helpers.test.d.ts +0 -2
  61. package/dist/tool-helpers.test.d.ts.map +0 -1
  62. package/dist/tool-helpers.test.js +0 -223
  63. package/dist/tools/__tests__/add-comments.test.d.ts +0 -2
  64. package/dist/tools/__tests__/add-comments.test.d.ts.map +0 -1
  65. package/dist/tools/__tests__/add-comments.test.js +0 -241
  66. package/dist/tools/__tests__/add-projects.test.d.ts +0 -2
  67. package/dist/tools/__tests__/add-projects.test.d.ts.map +0 -1
  68. package/dist/tools/__tests__/add-projects.test.js +0 -174
  69. package/dist/tools/__tests__/add-sections.test.d.ts +0 -2
  70. package/dist/tools/__tests__/add-sections.test.d.ts.map +0 -1
  71. package/dist/tools/__tests__/add-sections.test.js +0 -185
  72. package/dist/tools/__tests__/add-tasks.test.d.ts +0 -2
  73. package/dist/tools/__tests__/add-tasks.test.d.ts.map +0 -1
  74. package/dist/tools/__tests__/add-tasks.test.js +0 -533
  75. package/dist/tools/__tests__/assignment-integration.test.d.ts +0 -2
  76. package/dist/tools/__tests__/assignment-integration.test.d.ts.map +0 -1
  77. package/dist/tools/__tests__/assignment-integration.test.js +0 -428
  78. package/dist/tools/__tests__/complete-tasks.test.d.ts +0 -2
  79. package/dist/tools/__tests__/complete-tasks.test.d.ts.map +0 -1
  80. package/dist/tools/__tests__/complete-tasks.test.js +0 -206
  81. package/dist/tools/__tests__/delete-object.test.d.ts +0 -2
  82. package/dist/tools/__tests__/delete-object.test.d.ts.map +0 -1
  83. package/dist/tools/__tests__/delete-object.test.js +0 -110
  84. package/dist/tools/__tests__/fetch.test.d.ts +0 -2
  85. package/dist/tools/__tests__/fetch.test.d.ts.map +0 -1
  86. package/dist/tools/__tests__/fetch.test.js +0 -279
  87. package/dist/tools/__tests__/find-activity.test.d.ts +0 -2
  88. package/dist/tools/__tests__/find-activity.test.d.ts.map +0 -1
  89. package/dist/tools/__tests__/find-activity.test.js +0 -229
  90. package/dist/tools/__tests__/find-comments.test.d.ts +0 -2
  91. package/dist/tools/__tests__/find-comments.test.d.ts.map +0 -1
  92. package/dist/tools/__tests__/find-comments.test.js +0 -236
  93. package/dist/tools/__tests__/find-completed-tasks.test.d.ts +0 -2
  94. package/dist/tools/__tests__/find-completed-tasks.test.d.ts.map +0 -1
  95. package/dist/tools/__tests__/find-completed-tasks.test.js +0 -324
  96. package/dist/tools/__tests__/find-projects.test.d.ts +0 -2
  97. package/dist/tools/__tests__/find-projects.test.d.ts.map +0 -1
  98. package/dist/tools/__tests__/find-projects.test.js +0 -154
  99. package/dist/tools/__tests__/find-sections.test.d.ts +0 -2
  100. package/dist/tools/__tests__/find-sections.test.d.ts.map +0 -1
  101. package/dist/tools/__tests__/find-sections.test.js +0 -245
  102. package/dist/tools/__tests__/find-tasks-by-date.test.d.ts +0 -2
  103. package/dist/tools/__tests__/find-tasks-by-date.test.d.ts.map +0 -1
  104. package/dist/tools/__tests__/find-tasks-by-date.test.js +0 -528
  105. package/dist/tools/__tests__/find-tasks.test.d.ts +0 -2
  106. package/dist/tools/__tests__/find-tasks.test.d.ts.map +0 -1
  107. package/dist/tools/__tests__/find-tasks.test.js +0 -771
  108. package/dist/tools/__tests__/get-overview.test.d.ts +0 -2
  109. package/dist/tools/__tests__/get-overview.test.d.ts.map +0 -1
  110. package/dist/tools/__tests__/get-overview.test.js +0 -225
  111. package/dist/tools/__tests__/search.test.d.ts +0 -2
  112. package/dist/tools/__tests__/search.test.d.ts.map +0 -1
  113. package/dist/tools/__tests__/search.test.js +0 -206
  114. package/dist/tools/__tests__/update-comments.test.d.ts +0 -2
  115. package/dist/tools/__tests__/update-comments.test.d.ts.map +0 -1
  116. package/dist/tools/__tests__/update-comments.test.js +0 -294
  117. package/dist/tools/__tests__/update-projects.test.d.ts +0 -2
  118. package/dist/tools/__tests__/update-projects.test.d.ts.map +0 -1
  119. package/dist/tools/__tests__/update-projects.test.js +0 -217
  120. package/dist/tools/__tests__/update-sections.test.d.ts +0 -2
  121. package/dist/tools/__tests__/update-sections.test.d.ts.map +0 -1
  122. package/dist/tools/__tests__/update-sections.test.js +0 -169
  123. package/dist/tools/__tests__/update-tasks.test.d.ts +0 -2
  124. package/dist/tools/__tests__/update-tasks.test.d.ts.map +0 -1
  125. package/dist/tools/__tests__/update-tasks.test.js +0 -788
  126. package/dist/tools/__tests__/user-info.test.d.ts +0 -2
  127. package/dist/tools/__tests__/user-info.test.d.ts.map +0 -1
  128. package/dist/tools/__tests__/user-info.test.js +0 -139
  129. package/dist/tools/add-comments.js +0 -79
  130. package/dist/tools/add-projects.js +0 -63
  131. package/dist/tools/add-sections.js +0 -61
  132. package/dist/tools/add-tasks.js +0 -160
  133. package/dist/tools/complete-tasks.js +0 -68
  134. package/dist/tools/delete-object.js +0 -79
  135. package/dist/tools/fetch.js +0 -102
  136. package/dist/tools/find-activity.js +0 -221
  137. package/dist/tools/find-comments.js +0 -143
  138. package/dist/tools/find-completed-tasks.js +0 -161
  139. package/dist/tools/find-project-collaborators.js +0 -151
  140. package/dist/tools/find-projects.js +0 -101
  141. package/dist/tools/find-sections.js +0 -96
  142. package/dist/tools/find-tasks-by-date.js +0 -198
  143. package/dist/tools/find-tasks.js +0 -329
  144. package/dist/tools/get-overview.js +0 -249
  145. package/dist/tools/manage-assignments.js +0 -337
  146. package/dist/tools/search.js +0 -65
  147. package/dist/tools/update-comments.js +0 -82
  148. package/dist/tools/update-projects.js +0 -84
  149. package/dist/tools/update-sections.js +0 -70
  150. package/dist/tools/update-tasks.js +0 -170
  151. package/dist/tools/user-info.js +0 -142
  152. package/dist/utils/assignment-validator.js +0 -253
  153. package/dist/utils/constants.js +0 -45
  154. package/dist/utils/duration-parser.js +0 -96
  155. package/dist/utils/duration-parser.test.d.ts +0 -2
  156. package/dist/utils/duration-parser.test.d.ts.map +0 -1
  157. package/dist/utils/duration-parser.test.js +0 -147
  158. package/dist/utils/labels.js +0 -18
  159. package/dist/utils/priorities.js +0 -20
  160. package/dist/utils/response-builders.js +0 -210
  161. package/dist/utils/sanitize-data.js +0 -37
  162. package/dist/utils/sanitize-data.test.d.ts +0 -2
  163. package/dist/utils/sanitize-data.test.d.ts.map +0 -1
  164. package/dist/utils/sanitize-data.test.js +0 -93
  165. package/dist/utils/test-helpers.js +0 -237
  166. package/dist/utils/tool-names.js +0 -40
  167. 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":""}