@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,110 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { extractTextContent } from '../../utils/test-helpers.js';
3
- import { ToolNames } from '../../utils/tool-names.js';
4
- import { deleteObject } from '../delete-object.js';
5
- // Mock the Todoist API
6
- const mockTodoistApi = {
7
- deleteProject: jest.fn(),
8
- deleteSection: jest.fn(),
9
- deleteTask: jest.fn(),
10
- };
11
- const { FIND_PROJECTS, FIND_TASKS_BY_DATE, DELETE_OBJECT } = ToolNames;
12
- describe(`${DELETE_OBJECT} tool`, () => {
13
- beforeEach(() => {
14
- jest.clearAllMocks();
15
- });
16
- describe('deleting projects', () => {
17
- it('should delete a project by ID', async () => {
18
- mockTodoistApi.deleteProject.mockResolvedValue(true);
19
- const result = await deleteObject.execute({ type: 'project', id: '6cfCcrrCFg2xP94Q' }, mockTodoistApi);
20
- expect(mockTodoistApi.deleteProject).toHaveBeenCalledWith('6cfCcrrCFg2xP94Q');
21
- expect(mockTodoistApi.deleteSection).not.toHaveBeenCalled();
22
- expect(mockTodoistApi.deleteTask).not.toHaveBeenCalled();
23
- const textContent = extractTextContent(result);
24
- expect(textContent).toMatchSnapshot();
25
- expect(textContent).toContain('Deleted project: id=6cfCcrrCFg2xP94Q');
26
- expect(textContent).toContain(`Use ${FIND_PROJECTS} to see remaining projects`);
27
- expect(result.structuredContent).toEqual({
28
- deletedEntity: {
29
- type: 'project',
30
- id: '6cfCcrrCFg2xP94Q',
31
- },
32
- success: true,
33
- });
34
- });
35
- it('should propagate project deletion errors', async () => {
36
- const apiError = new Error('API Error: Cannot delete project with tasks');
37
- mockTodoistApi.deleteProject.mockRejectedValue(apiError);
38
- await expect(deleteObject.execute({ type: 'project', id: 'project-with-tasks' }, mockTodoistApi)).rejects.toThrow('API Error: Cannot delete project with tasks');
39
- });
40
- });
41
- describe('deleting sections', () => {
42
- it('should delete a section by ID', async () => {
43
- mockTodoistApi.deleteSection.mockResolvedValue(true);
44
- const result = await deleteObject.execute({ type: 'section', id: 'section-123' }, mockTodoistApi);
45
- expect(mockTodoistApi.deleteSection).toHaveBeenCalledWith('section-123');
46
- expect(mockTodoistApi.deleteProject).not.toHaveBeenCalled();
47
- expect(mockTodoistApi.deleteTask).not.toHaveBeenCalled();
48
- const textContent = extractTextContent(result);
49
- expect(textContent).toMatchSnapshot();
50
- expect(textContent).toContain('Deleted section: id=section-123');
51
- expect(textContent).toContain(`Use ${ToolNames.FIND_SECTIONS} to see remaining sections`);
52
- expect(result.structuredContent).toEqual({
53
- deletedEntity: { type: 'section', id: 'section-123' },
54
- success: true,
55
- });
56
- });
57
- it('should propagate section deletion errors', async () => {
58
- const apiError = new Error('API Error: Section not found');
59
- mockTodoistApi.deleteSection.mockRejectedValue(apiError);
60
- await expect(deleteObject.execute({ type: 'section', id: 'non-existent-section' }, mockTodoistApi)).rejects.toThrow('API Error: Section not found');
61
- });
62
- });
63
- describe('deleting tasks', () => {
64
- it('should delete a task by ID', async () => {
65
- mockTodoistApi.deleteTask.mockResolvedValue(true);
66
- const result = await deleteObject.execute({ type: 'task', id: '8485093748' }, mockTodoistApi);
67
- expect(mockTodoistApi.deleteTask).toHaveBeenCalledWith('8485093748');
68
- expect(mockTodoistApi.deleteProject).not.toHaveBeenCalled();
69
- expect(mockTodoistApi.deleteSection).not.toHaveBeenCalled();
70
- const textContent = extractTextContent(result);
71
- expect(textContent).toMatchSnapshot();
72
- expect(textContent).toContain('Deleted task: id=8485093748');
73
- expect(textContent).toContain(`Use ${FIND_TASKS_BY_DATE} to see remaining tasks`);
74
- expect(result.structuredContent).toEqual({
75
- deletedEntity: { type: 'task', id: '8485093748' },
76
- success: true,
77
- });
78
- });
79
- it('should propagate task deletion errors', async () => {
80
- const apiError = new Error('API Error: Task not found');
81
- mockTodoistApi.deleteTask.mockRejectedValue(apiError);
82
- await expect(deleteObject.execute({ type: 'task', id: 'non-existent-task' }, mockTodoistApi)).rejects.toThrow('API Error: Task not found');
83
- });
84
- it('should handle permission errors', async () => {
85
- const apiError = new Error('API Error: Insufficient permissions to delete task');
86
- mockTodoistApi.deleteTask.mockRejectedValue(apiError);
87
- await expect(deleteObject.execute({ type: 'task', id: 'restricted-task' }, mockTodoistApi)).rejects.toThrow('API Error: Insufficient permissions to delete task');
88
- });
89
- });
90
- describe('type validation', () => {
91
- it('should handle all supported entity types', async () => {
92
- mockTodoistApi.deleteProject.mockResolvedValue(true);
93
- mockTodoistApi.deleteSection.mockResolvedValue(true);
94
- mockTodoistApi.deleteTask.mockResolvedValue(true);
95
- // Delete project
96
- await deleteObject.execute({ type: 'project', id: 'proj-1' }, mockTodoistApi);
97
- expect(mockTodoistApi.deleteProject).toHaveBeenCalledWith('proj-1');
98
- // Delete section
99
- await deleteObject.execute({ type: 'section', id: 'sect-1' }, mockTodoistApi);
100
- expect(mockTodoistApi.deleteSection).toHaveBeenCalledWith('sect-1');
101
- // Delete task
102
- await deleteObject.execute({ type: 'task', id: 'task-1' }, mockTodoistApi);
103
- expect(mockTodoistApi.deleteTask).toHaveBeenCalledWith('task-1');
104
- // Verify each API method was called exactly once
105
- expect(mockTodoistApi.deleteProject).toHaveBeenCalledTimes(1);
106
- expect(mockTodoistApi.deleteSection).toHaveBeenCalledTimes(1);
107
- expect(mockTodoistApi.deleteTask).toHaveBeenCalledTimes(1);
108
- });
109
- });
110
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=fetch.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"fetch.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/fetch.test.ts"],"names":[],"mappings":""}
@@ -1,279 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { createMockProject, createMockTask, TEST_IDS } from '../../utils/test-helpers.js';
3
- import { ToolNames } from '../../utils/tool-names.js';
4
- import { fetch } from '../fetch.js';
5
- // Mock the Todoist API
6
- const mockTodoistApi = {
7
- getTask: jest.fn(),
8
- getProject: jest.fn(),
9
- };
10
- const { FETCH } = ToolNames;
11
- describe(`${FETCH} tool`, () => {
12
- beforeEach(() => {
13
- jest.clearAllMocks();
14
- });
15
- describe('fetching tasks', () => {
16
- it('should fetch a task by composite ID and return full content', async () => {
17
- const mockTask = createMockTask({
18
- id: TEST_IDS.TASK_1,
19
- content: 'Important meeting with team',
20
- description: 'Discuss project roadmap and timeline',
21
- labels: ['work', 'urgent'],
22
- priority: 2,
23
- projectId: TEST_IDS.PROJECT_WORK,
24
- sectionId: TEST_IDS.SECTION_1,
25
- due: {
26
- date: '2025-10-15',
27
- isRecurring: false,
28
- datetime: null,
29
- string: '2025-10-15',
30
- timezone: null,
31
- lang: 'en',
32
- },
33
- });
34
- mockTodoistApi.getTask.mockResolvedValue(mockTask);
35
- const result = await fetch.execute({ id: `task:${TEST_IDS.TASK_1}` }, mockTodoistApi);
36
- // Verify API was called correctly
37
- expect(mockTodoistApi.getTask).toHaveBeenCalledWith(TEST_IDS.TASK_1);
38
- // Verify result structure
39
- expect(result.content).toHaveLength(1);
40
- expect(result.content[0]?.type).toBe('text');
41
- // Parse the JSON response
42
- const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
43
- expect(jsonResponse).toEqual({
44
- id: `task:${TEST_IDS.TASK_1}`,
45
- title: 'Important meeting with team',
46
- text: 'Important meeting with team\n\nDescription: Discuss project roadmap and timeline\nDue: 2025-10-15\nLabels: work, urgent',
47
- url: `https://app.todoist.com/app/task/${TEST_IDS.TASK_1}`,
48
- metadata: {
49
- priority: 2,
50
- projectId: TEST_IDS.PROJECT_WORK,
51
- sectionId: TEST_IDS.SECTION_1,
52
- parentId: null,
53
- recurring: false,
54
- duration: null,
55
- responsibleUid: null,
56
- assignedByUid: null,
57
- checked: false,
58
- completedAt: null,
59
- },
60
- });
61
- });
62
- it('should fetch a task without optional fields', async () => {
63
- const mockTask = createMockTask({
64
- id: TEST_IDS.TASK_2,
65
- content: 'Simple task',
66
- description: '',
67
- labels: [],
68
- due: null,
69
- });
70
- mockTodoistApi.getTask.mockResolvedValue(mockTask);
71
- const result = await fetch.execute({ id: `task:${TEST_IDS.TASK_2}` }, mockTodoistApi);
72
- const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
73
- expect(jsonResponse.title).toBe('Simple task');
74
- expect(jsonResponse.text).toBe('Simple task');
75
- expect(jsonResponse.metadata).toEqual({
76
- priority: 1,
77
- projectId: TEST_IDS.PROJECT_TEST,
78
- sectionId: null,
79
- parentId: null,
80
- recurring: false,
81
- duration: null,
82
- responsibleUid: null,
83
- assignedByUid: null,
84
- checked: false,
85
- completedAt: null,
86
- });
87
- });
88
- it('should handle tasks with recurring due dates', async () => {
89
- const mockTask = createMockTask({
90
- id: TEST_IDS.TASK_3,
91
- content: 'Weekly meeting',
92
- due: {
93
- date: '2025-10-15',
94
- isRecurring: true,
95
- datetime: null,
96
- string: 'every monday',
97
- timezone: null,
98
- lang: 'en',
99
- },
100
- });
101
- mockTodoistApi.getTask.mockResolvedValue(mockTask);
102
- const result = await fetch.execute({ id: `task:${TEST_IDS.TASK_3}` }, mockTodoistApi);
103
- const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
104
- expect(jsonResponse.metadata.recurring).toBe('every monday');
105
- });
106
- it('should handle tasks with duration', async () => {
107
- const mockTask = createMockTask({
108
- id: TEST_IDS.TASK_1,
109
- content: 'Task with duration',
110
- duration: {
111
- amount: 90,
112
- unit: 'minute',
113
- },
114
- });
115
- mockTodoistApi.getTask.mockResolvedValue(mockTask);
116
- const result = await fetch.execute({ id: `task:${TEST_IDS.TASK_1}` }, mockTodoistApi);
117
- const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
118
- expect(jsonResponse.metadata.duration).toBe('1h30m');
119
- });
120
- it('should handle tasks with assignments', async () => {
121
- const mockTask = createMockTask({
122
- id: TEST_IDS.TASK_1,
123
- content: 'Assigned task',
124
- responsibleUid: 'user-123',
125
- assignedByUid: 'user-456',
126
- });
127
- mockTodoistApi.getTask.mockResolvedValue(mockTask);
128
- const result = await fetch.execute({ id: `task:${TEST_IDS.TASK_1}` }, mockTodoistApi);
129
- const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
130
- expect(jsonResponse.metadata.responsibleUid).toBe('user-123');
131
- expect(jsonResponse.metadata.assignedByUid).toBe('user-456');
132
- });
133
- });
134
- describe('fetching projects', () => {
135
- it('should fetch a project by composite ID and return full content', async () => {
136
- const mockProject = createMockProject({
137
- id: TEST_IDS.PROJECT_WORK,
138
- name: 'Work Project',
139
- color: 'blue',
140
- isFavorite: true,
141
- isShared: true,
142
- viewStyle: 'board',
143
- parentId: null,
144
- inboxProject: false,
145
- });
146
- mockTodoistApi.getProject.mockResolvedValue(mockProject);
147
- const result = await fetch.execute({ id: `project:${TEST_IDS.PROJECT_WORK}` }, mockTodoistApi);
148
- // Verify API was called correctly
149
- expect(mockTodoistApi.getProject).toHaveBeenCalledWith(TEST_IDS.PROJECT_WORK);
150
- // Verify result structure
151
- expect(result.content).toHaveLength(1);
152
- expect(result.content[0]?.type).toBe('text');
153
- // Parse the JSON response
154
- const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
155
- expect(jsonResponse).toEqual({
156
- id: `project:${TEST_IDS.PROJECT_WORK}`,
157
- title: 'Work Project',
158
- text: 'Work Project\n\nShared project\nFavorite: Yes',
159
- url: `https://app.todoist.com/app/project/${TEST_IDS.PROJECT_WORK}`,
160
- metadata: {
161
- color: 'blue',
162
- isFavorite: true,
163
- isShared: true,
164
- parentId: null,
165
- inboxProject: false,
166
- viewStyle: 'board',
167
- },
168
- });
169
- });
170
- it('should fetch a project without optional flags', async () => {
171
- const mockProject = createMockProject({
172
- id: TEST_IDS.PROJECT_TEST,
173
- name: 'Simple Project',
174
- isFavorite: false,
175
- isShared: false,
176
- });
177
- mockTodoistApi.getProject.mockResolvedValue(mockProject);
178
- const result = await fetch.execute({ id: `project:${TEST_IDS.PROJECT_TEST}` }, mockTodoistApi);
179
- const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
180
- expect(jsonResponse.title).toBe('Simple Project');
181
- expect(jsonResponse.text).toBe('Simple Project');
182
- expect(jsonResponse.metadata.isFavorite).toBe(false);
183
- expect(jsonResponse.metadata.isShared).toBe(false);
184
- });
185
- it('should fetch inbox project', async () => {
186
- const mockProject = createMockProject({
187
- id: TEST_IDS.PROJECT_INBOX,
188
- name: 'Inbox',
189
- inboxProject: true,
190
- });
191
- mockTodoistApi.getProject.mockResolvedValue(mockProject);
192
- const result = await fetch.execute({ id: `project:${TEST_IDS.PROJECT_INBOX}` }, mockTodoistApi);
193
- const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
194
- expect(jsonResponse.metadata.inboxProject).toBe(true);
195
- });
196
- it('should fetch project with parent ID', async () => {
197
- const mockProject = createMockProject({
198
- id: 'sub-project-id',
199
- name: 'Sub Project',
200
- parentId: TEST_IDS.PROJECT_WORK,
201
- });
202
- mockTodoistApi.getProject.mockResolvedValue(mockProject);
203
- const result = await fetch.execute({ id: 'project:sub-project-id' }, mockTodoistApi);
204
- const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
205
- expect(jsonResponse.metadata.parentId).toBe(TEST_IDS.PROJECT_WORK);
206
- });
207
- });
208
- describe('error handling', () => {
209
- it('should return error response for invalid ID format (missing colon)', async () => {
210
- const result = await fetch.execute({ id: 'invalid-id' }, mockTodoistApi);
211
- expect(result.isError).toBe(true);
212
- expect(result.content[0]?.text).toContain('Invalid ID format');
213
- });
214
- it('should return error response for invalid ID format (missing type)', async () => {
215
- const result = await fetch.execute({ id: ':8485093748' }, mockTodoistApi);
216
- expect(result.isError).toBe(true);
217
- expect(result.content[0]?.text).toContain('Invalid ID format');
218
- });
219
- it('should return error response for invalid ID format (missing object ID)', async () => {
220
- const result = await fetch.execute({ id: 'task:' }, mockTodoistApi);
221
- expect(result.isError).toBe(true);
222
- expect(result.content[0]?.text).toContain('Invalid ID format');
223
- });
224
- it('should return error response for invalid type', async () => {
225
- const result = await fetch.execute({ id: 'section:123' }, mockTodoistApi);
226
- expect(result.isError).toBe(true);
227
- expect(result.content[0]?.text).toContain('Invalid ID format');
228
- });
229
- it('should return error response for task fetch failure', async () => {
230
- mockTodoistApi.getTask.mockRejectedValue(new Error('Task not found'));
231
- const result = await fetch.execute({ id: `task:${TEST_IDS.TASK_1}` }, mockTodoistApi);
232
- expect(result.isError).toBe(true);
233
- expect(result.content[0]?.text).toBe('Task not found');
234
- });
235
- it('should return error response for project fetch failure', async () => {
236
- mockTodoistApi.getProject.mockRejectedValue(new Error('Project not found'));
237
- const result = await fetch.execute({ id: `project:${TEST_IDS.PROJECT_WORK}` }, mockTodoistApi);
238
- expect(result.isError).toBe(true);
239
- expect(result.content[0]?.text).toBe('Project not found');
240
- });
241
- });
242
- describe('OpenAI MCP spec compliance', () => {
243
- it('should return exactly one content item with type "text"', async () => {
244
- const mockTask = createMockTask({ id: TEST_IDS.TASK_1, content: 'Test' });
245
- mockTodoistApi.getTask.mockResolvedValue(mockTask);
246
- const result = await fetch.execute({ id: `task:${TEST_IDS.TASK_1}` }, mockTodoistApi);
247
- expect(result.content).toHaveLength(1);
248
- expect(result.content[0]?.type).toBe('text');
249
- });
250
- it('should return valid JSON string in text field', async () => {
251
- const mockTask = createMockTask({ id: TEST_IDS.TASK_1, content: 'Test' });
252
- mockTodoistApi.getTask.mockResolvedValue(mockTask);
253
- const result = await fetch.execute({ id: `task:${TEST_IDS.TASK_1}` }, mockTodoistApi);
254
- expect(() => JSON.parse(result.content[0]?.text ?? '{}')).not.toThrow();
255
- });
256
- it('should include all required fields (id, title, text, url)', async () => {
257
- const mockTask = createMockTask({ id: TEST_IDS.TASK_1, content: 'Test' });
258
- mockTodoistApi.getTask.mockResolvedValue(mockTask);
259
- const result = await fetch.execute({ id: `task:${TEST_IDS.TASK_1}` }, mockTodoistApi);
260
- const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
261
- expect(jsonResponse).toHaveProperty('id');
262
- expect(jsonResponse).toHaveProperty('title');
263
- expect(jsonResponse).toHaveProperty('text');
264
- expect(jsonResponse).toHaveProperty('url');
265
- expect(typeof jsonResponse.id).toBe('string');
266
- expect(typeof jsonResponse.title).toBe('string');
267
- expect(typeof jsonResponse.text).toBe('string');
268
- expect(typeof jsonResponse.url).toBe('string');
269
- });
270
- it('should include optional metadata field', async () => {
271
- const mockTask = createMockTask({ id: TEST_IDS.TASK_1, content: 'Test' });
272
- mockTodoistApi.getTask.mockResolvedValue(mockTask);
273
- const result = await fetch.execute({ id: `task:${TEST_IDS.TASK_1}` }, mockTodoistApi);
274
- const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
275
- expect(jsonResponse).toHaveProperty('metadata');
276
- expect(typeof jsonResponse.metadata).toBe('object');
277
- });
278
- });
279
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=find-activity.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"find-activity.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/find-activity.test.ts"],"names":[],"mappings":""}
@@ -1,229 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { extractTextContent } from '../../utils/test-helpers.js';
3
- import { ToolNames } from '../../utils/tool-names.js';
4
- import { findActivity } from '../find-activity.js';
5
- // Mock the Todoist API
6
- const mockTodoistApi = {
7
- getActivityLogs: jest.fn(),
8
- };
9
- const { FIND_ACTIVITY } = ToolNames;
10
- /**
11
- * Helper to create a mock activity event
12
- */
13
- function createMockActivityEvent(overrides = {}) {
14
- return {
15
- id: 'event-123',
16
- objectType: 'task',
17
- objectId: 'task-456',
18
- eventType: 'added',
19
- eventDate: '2024-10-23T10:30:00Z',
20
- parentProjectId: 'project-789',
21
- parentItemId: null,
22
- initiatorId: 'user-001',
23
- extraData: null,
24
- ...overrides,
25
- };
26
- }
27
- describe(`${FIND_ACTIVITY} tool`, () => {
28
- beforeEach(() => {
29
- jest.clearAllMocks();
30
- });
31
- describe('basic functionality', () => {
32
- it('should retrieve activity events with default parameters', async () => {
33
- const mockEvents = [
34
- createMockActivityEvent({
35
- id: 'event-1',
36
- eventType: 'added',
37
- eventDate: '2024-10-23T10:00:00Z',
38
- }),
39
- createMockActivityEvent({
40
- id: 'event-2',
41
- eventType: 'completed',
42
- eventDate: '2024-10-23T11:00:00Z',
43
- }),
44
- ];
45
- mockTodoistApi.getActivityLogs.mockResolvedValue({
46
- results: mockEvents,
47
- nextCursor: null,
48
- });
49
- const result = await findActivity.execute({ limit: 20 }, mockTodoistApi);
50
- expect(mockTodoistApi.getActivityLogs).toHaveBeenCalledWith({
51
- limit: 20,
52
- cursor: null,
53
- });
54
- expect(extractTextContent(result)).toMatchSnapshot();
55
- });
56
- it('should handle empty results', async () => {
57
- mockTodoistApi.getActivityLogs.mockResolvedValue({
58
- results: [],
59
- nextCursor: null,
60
- });
61
- const result = await findActivity.execute({ limit: 20 }, mockTodoistApi);
62
- expect(extractTextContent(result)).toMatchSnapshot();
63
- });
64
- it('should handle pagination with cursor', async () => {
65
- const mockEvents = Array.from({ length: 20 }, (_, i) => createMockActivityEvent({
66
- id: `event-${i}`,
67
- objectId: `task-${i}`,
68
- }));
69
- mockTodoistApi.getActivityLogs.mockResolvedValue({
70
- results: mockEvents,
71
- nextCursor: 'next-page-cursor',
72
- });
73
- const result = await findActivity.execute({ limit: 20, cursor: 'current-cursor' }, mockTodoistApi);
74
- expect(mockTodoistApi.getActivityLogs).toHaveBeenCalledWith({
75
- limit: 20,
76
- cursor: 'current-cursor',
77
- });
78
- expect(extractTextContent(result)).toContain('Pass cursor');
79
- expect(extractTextContent(result)).toContain('next-page-cursor');
80
- });
81
- });
82
- describe('filtering', () => {
83
- it.each([
84
- ['task', 'added'],
85
- ['project', 'updated'],
86
- ['comment', 'deleted'],
87
- ])('should filter by object type: %s', async (objectType, eventType) => {
88
- const mockEvents = [
89
- createMockActivityEvent({
90
- objectType: objectType,
91
- eventType: eventType,
92
- }),
93
- ];
94
- mockTodoistApi.getActivityLogs.mockResolvedValue({
95
- results: mockEvents,
96
- nextCursor: null,
97
- });
98
- const result = await findActivity.execute({ objectType: objectType, limit: 20 }, mockTodoistApi);
99
- expect(mockTodoistApi.getActivityLogs).toHaveBeenCalledWith({
100
- objectType,
101
- limit: 20,
102
- cursor: null,
103
- });
104
- expect(extractTextContent(result)).toContain(objectType);
105
- });
106
- it.each([
107
- ['added', 'task-1'],
108
- ['completed', 'task-2'],
109
- ['updated', 'task-3'],
110
- ['deleted', 'task-4'],
111
- ])('should filter by event type: %s', async (eventType, objectId) => {
112
- const mockEvents = [
113
- createMockActivityEvent({
114
- eventType: eventType,
115
- objectId,
116
- }),
117
- ];
118
- mockTodoistApi.getActivityLogs.mockResolvedValue({
119
- results: mockEvents,
120
- nextCursor: null,
121
- });
122
- const result = await findActivity.execute({
123
- eventType: eventType,
124
- limit: 20,
125
- }, mockTodoistApi);
126
- expect(mockTodoistApi.getActivityLogs).toHaveBeenCalledWith({
127
- eventType,
128
- limit: 20,
129
- cursor: null,
130
- });
131
- expect(extractTextContent(result)).toContain(eventType);
132
- });
133
- it.each([
134
- ['objectId', 'task-123', { objectId: 'task-123' }],
135
- ['projectId', 'project-abc', { parentProjectId: 'project-abc' }],
136
- ['taskId', 'parent-task-789', { parentItemId: 'parent-task-789' }],
137
- ['initiatorId', 'user-alice', { initiatorId: 'user-alice' }],
138
- ])('should filter by %s', async (filterName, filterId, expectedApiCall) => {
139
- const mockEvents = [createMockActivityEvent()];
140
- mockTodoistApi.getActivityLogs.mockResolvedValue({
141
- results: mockEvents,
142
- nextCursor: null,
143
- });
144
- const args = { limit: 20 };
145
- args[filterName] = filterId;
146
- await findActivity.execute(args, mockTodoistApi);
147
- expect(mockTodoistApi.getActivityLogs).toHaveBeenCalledWith({
148
- ...expectedApiCall,
149
- limit: 20,
150
- cursor: null,
151
- });
152
- });
153
- it('should support multiple filters simultaneously', async () => {
154
- const mockEvents = [
155
- createMockActivityEvent({
156
- objectType: 'task',
157
- eventType: 'completed',
158
- parentProjectId: 'project-work',
159
- initiatorId: 'user-bob',
160
- }),
161
- ];
162
- mockTodoistApi.getActivityLogs.mockResolvedValue({
163
- results: mockEvents,
164
- nextCursor: null,
165
- });
166
- const result = await findActivity.execute({
167
- objectType: 'task',
168
- eventType: 'completed',
169
- projectId: 'project-work',
170
- initiatorId: 'user-bob',
171
- limit: 50,
172
- }, mockTodoistApi);
173
- expect(mockTodoistApi.getActivityLogs).toHaveBeenCalledWith({
174
- objectType: 'task',
175
- eventType: 'completed',
176
- parentProjectId: 'project-work',
177
- initiatorId: 'user-bob',
178
- limit: 50,
179
- cursor: null,
180
- });
181
- expect(extractTextContent(result)).toMatchSnapshot();
182
- });
183
- });
184
- describe('content extraction', () => {
185
- it('should extract task content from extraData', async () => {
186
- const mockEvents = [
187
- createMockActivityEvent({
188
- eventType: 'added',
189
- extraData: { content: 'Buy groceries' },
190
- }),
191
- ];
192
- mockTodoistApi.getActivityLogs.mockResolvedValue({
193
- results: mockEvents,
194
- nextCursor: null,
195
- });
196
- const result = await findActivity.execute({ limit: 20 }, mockTodoistApi);
197
- expect(extractTextContent(result)).toContain('Buy groceries');
198
- });
199
- it('should handle system-generated events with no initiator', async () => {
200
- const mockEvents = [
201
- createMockActivityEvent({
202
- initiatorId: null,
203
- eventType: 'completed',
204
- }),
205
- ];
206
- mockTodoistApi.getActivityLogs.mockResolvedValue({
207
- results: mockEvents,
208
- nextCursor: null,
209
- });
210
- const result = await findActivity.execute({ limit: 20 }, mockTodoistApi);
211
- expect(extractTextContent(result)).toContain('system');
212
- });
213
- it('should truncate long content', async () => {
214
- const longContent = 'A'.repeat(100);
215
- const mockEvents = [
216
- createMockActivityEvent({
217
- extraData: { content: longContent },
218
- }),
219
- ];
220
- mockTodoistApi.getActivityLogs.mockResolvedValue({
221
- results: mockEvents,
222
- nextCursor: null,
223
- });
224
- const result = await findActivity.execute({ limit: 20 }, mockTodoistApi);
225
- expect(extractTextContent(result)).toContain('...');
226
- expect(extractTextContent(result)).not.toContain(longContent);
227
- });
228
- });
229
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=find-comments.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"find-comments.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/find-comments.test.ts"],"names":[],"mappings":""}