@doist/todoist-ai 2.0.0 → 2.1.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.
- package/README.md +7 -0
- package/dist/index.d.ts +29 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -48
- package/dist/main.js +6 -11
- package/dist/mcp-helpers.d.ts +2 -2
- package/dist/mcp-helpers.d.ts.map +1 -1
- package/dist/mcp-helpers.js +1 -4
- package/dist/mcp-server.d.ts +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +34 -36
- package/dist/todoist-tool.js +1 -2
- package/dist/tool-helpers.d.ts +13 -1
- package/dist/tool-helpers.d.ts.map +1 -1
- package/dist/tool-helpers.js +43 -22
- package/dist/tool-helpers.test.js +55 -14
- package/dist/tools/__tests__/delete-one.test.d.ts +2 -0
- package/dist/tools/__tests__/delete-one.test.d.ts.map +1 -0
- package/dist/tools/__tests__/delete-one.test.js +90 -0
- package/dist/tools/__tests__/overview.test.d.ts +2 -0
- package/dist/tools/__tests__/overview.test.d.ts.map +1 -0
- package/dist/tools/__tests__/overview.test.js +163 -0
- package/dist/tools/__tests__/projects-list.test.d.ts +2 -0
- package/dist/tools/__tests__/projects-list.test.d.ts.map +1 -0
- package/dist/tools/__tests__/projects-list.test.js +140 -0
- package/dist/tools/__tests__/projects-manage.test.d.ts +2 -0
- package/dist/tools/__tests__/projects-manage.test.d.ts.map +1 -0
- package/dist/tools/__tests__/projects-manage.test.js +106 -0
- package/dist/tools/__tests__/sections-manage.test.d.ts +2 -0
- package/dist/tools/__tests__/sections-manage.test.d.ts.map +1 -0
- package/dist/tools/__tests__/sections-manage.test.js +138 -0
- package/dist/tools/__tests__/sections-search.test.d.ts +2 -0
- package/dist/tools/__tests__/sections-search.test.d.ts.map +1 -0
- package/dist/tools/__tests__/sections-search.test.js +235 -0
- package/dist/tools/__tests__/tasks-add-multiple.test.d.ts +2 -0
- package/dist/tools/__tests__/tasks-add-multiple.test.d.ts.map +1 -0
- package/dist/tools/__tests__/tasks-add-multiple.test.js +274 -0
- package/dist/tools/__tests__/tasks-complete-multiple.test.d.ts +2 -0
- package/dist/tools/__tests__/tasks-complete-multiple.test.d.ts.map +1 -0
- package/dist/tools/__tests__/tasks-complete-multiple.test.js +146 -0
- package/dist/tools/__tests__/tasks-list-by-date.test.d.ts +2 -0
- package/dist/tools/__tests__/tasks-list-by-date.test.d.ts.map +1 -0
- package/dist/tools/__tests__/tasks-list-by-date.test.js +192 -0
- package/dist/tools/__tests__/tasks-list-completed.test.d.ts +2 -0
- package/dist/tools/__tests__/tasks-list-completed.test.d.ts.map +1 -0
- package/dist/tools/__tests__/tasks-list-completed.test.js +154 -0
- package/dist/tools/__tests__/tasks-list-for-container.test.d.ts +2 -0
- package/dist/tools/__tests__/tasks-list-for-container.test.d.ts.map +1 -0
- package/dist/tools/__tests__/tasks-list-for-container.test.js +232 -0
- package/dist/tools/__tests__/tasks-organize-multiple.test.d.ts +2 -0
- package/dist/tools/__tests__/tasks-organize-multiple.test.d.ts.map +1 -0
- package/dist/tools/__tests__/tasks-organize-multiple.test.js +245 -0
- package/dist/tools/__tests__/tasks-search.test.d.ts +2 -0
- package/dist/tools/__tests__/tasks-search.test.d.ts.map +1 -0
- package/dist/tools/__tests__/tasks-search.test.js +106 -0
- package/dist/tools/__tests__/tasks-update-one.test.d.ts +2 -0
- package/dist/tools/__tests__/tasks-update-one.test.d.ts.map +1 -0
- package/dist/tools/__tests__/tasks-update-one.test.js +251 -0
- package/dist/tools/delete-one.js +4 -7
- package/dist/tools/overview.js +8 -11
- package/dist/tools/projects-list.js +7 -10
- package/dist/tools/projects-manage.js +6 -9
- package/dist/tools/sections-manage.js +7 -10
- package/dist/tools/sections-search.js +4 -7
- package/dist/tools/tasks-add-multiple.d.ts +5 -0
- package/dist/tools/tasks-add-multiple.d.ts.map +1 -1
- package/dist/tools/tasks-add-multiple.js +37 -17
- package/dist/tools/tasks-complete-multiple.js +3 -6
- package/dist/tools/tasks-list-by-date.d.ts +1 -0
- package/dist/tools/tasks-list-by-date.d.ts.map +1 -1
- package/dist/tools/tasks-list-by-date.js +12 -15
- package/dist/tools/tasks-list-completed.d.ts +2 -1
- package/dist/tools/tasks-list-completed.d.ts.map +1 -1
- package/dist/tools/tasks-list-completed.js +13 -16
- package/dist/tools/tasks-list-for-container.d.ts +1 -0
- package/dist/tools/tasks-list-for-container.d.ts.map +1 -1
- package/dist/tools/tasks-list-for-container.js +8 -11
- package/dist/tools/tasks-organize-multiple.d.ts.map +1 -1
- package/dist/tools/tasks-organize-multiple.js +20 -14
- package/dist/tools/tasks-search.d.ts +1 -0
- package/dist/tools/tasks-search.d.ts.map +1 -1
- package/dist/tools/tasks-search.js +7 -10
- package/dist/tools/tasks-update-one.d.ts +4 -2
- package/dist/tools/tasks-update-one.d.ts.map +1 -1
- package/dist/tools/tasks-update-one.js +45 -15
- package/dist/tools/test-helpers.d.ts +80 -0
- package/dist/tools/test-helpers.d.ts.map +1 -0
- package/dist/tools/test-helpers.js +140 -0
- package/dist/utils/duration-parser.d.ts +36 -0
- package/dist/utils/duration-parser.d.ts.map +1 -0
- package/dist/utils/duration-parser.js +96 -0
- package/dist/utils/duration-parser.test.d.ts +2 -0
- package/dist/utils/duration-parser.test.d.ts.map +1 -0
- package/dist/utils/duration-parser.test.js +147 -0
- package/package.json +6 -2
- package/scripts/test-executable.cjs +69 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
import { tasksUpdateOne } from '../tasks-update-one.js';
|
|
3
|
+
import { createMockTask } from '../test-helpers.js';
|
|
4
|
+
// Mock the Todoist API
|
|
5
|
+
const mockTodoistApi = {
|
|
6
|
+
updateTask: jest.fn(),
|
|
7
|
+
moveTasks: jest.fn(),
|
|
8
|
+
};
|
|
9
|
+
describe('tasks-update-one tool', () => {
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
jest.clearAllMocks();
|
|
12
|
+
});
|
|
13
|
+
describe('updating task properties', () => {
|
|
14
|
+
it('should update task content and description', async () => {
|
|
15
|
+
// Mock API response extracted from recordings (Task type)
|
|
16
|
+
const mockApiResponse = createMockTask({
|
|
17
|
+
id: '8485093748',
|
|
18
|
+
content: 'Updated task content',
|
|
19
|
+
description: 'Updated task description',
|
|
20
|
+
url: 'https://todoist.com/showTask?id=8485093748',
|
|
21
|
+
addedAt: '2025-08-13T22:09:56.123456Z',
|
|
22
|
+
});
|
|
23
|
+
mockTodoistApi.updateTask.mockResolvedValue(mockApiResponse);
|
|
24
|
+
const result = await tasksUpdateOne.execute({
|
|
25
|
+
id: '8485093748',
|
|
26
|
+
content: 'Updated task content',
|
|
27
|
+
description: 'Updated task description',
|
|
28
|
+
}, mockTodoistApi);
|
|
29
|
+
// Verify API was called correctly
|
|
30
|
+
expect(mockTodoistApi.updateTask).toHaveBeenCalledWith('8485093748', {
|
|
31
|
+
content: 'Updated task content',
|
|
32
|
+
description: 'Updated task description',
|
|
33
|
+
});
|
|
34
|
+
// Verify result matches API response
|
|
35
|
+
expect(result).toEqual(mockApiResponse);
|
|
36
|
+
});
|
|
37
|
+
it('should update task priority and due date', async () => {
|
|
38
|
+
const mockApiResponse = createMockTask({
|
|
39
|
+
id: '8485093749',
|
|
40
|
+
content: 'Original task content',
|
|
41
|
+
labels: ['urgent'],
|
|
42
|
+
priority: 3,
|
|
43
|
+
url: 'https://todoist.com/showTask?id=8485093749',
|
|
44
|
+
addedAt: '2025-08-13T22:09:56.123456Z',
|
|
45
|
+
due: {
|
|
46
|
+
date: '2025-08-20',
|
|
47
|
+
isRecurring: false,
|
|
48
|
+
lang: 'en',
|
|
49
|
+
string: 'Aug 20',
|
|
50
|
+
timezone: null,
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
mockTodoistApi.updateTask.mockResolvedValue(mockApiResponse);
|
|
54
|
+
const result = await tasksUpdateOne.execute({ id: '8485093749', priority: 3, dueString: 'Aug 20' }, mockTodoistApi);
|
|
55
|
+
expect(mockTodoistApi.updateTask).toHaveBeenCalledWith('8485093749', {
|
|
56
|
+
priority: 3,
|
|
57
|
+
dueString: 'Aug 20',
|
|
58
|
+
});
|
|
59
|
+
expect(result).toEqual(mockApiResponse);
|
|
60
|
+
});
|
|
61
|
+
it('should move task to different project', async () => {
|
|
62
|
+
const mockApiResponse = createMockTask({
|
|
63
|
+
id: '8485093750',
|
|
64
|
+
content: 'Task to move',
|
|
65
|
+
projectId: 'new-project-id',
|
|
66
|
+
url: 'https://todoist.com/showTask?id=8485093750',
|
|
67
|
+
addedAt: '2025-08-13T22:09:56.123456Z',
|
|
68
|
+
});
|
|
69
|
+
mockTodoistApi.moveTasks.mockResolvedValue([mockApiResponse]);
|
|
70
|
+
const result = await tasksUpdateOne.execute({ id: '8485093750', projectId: 'new-project-id' }, mockTodoistApi);
|
|
71
|
+
expect(mockTodoistApi.moveTasks).toHaveBeenCalledWith(['8485093750'], {
|
|
72
|
+
projectId: 'new-project-id',
|
|
73
|
+
});
|
|
74
|
+
expect(mockTodoistApi.updateTask).not.toHaveBeenCalled();
|
|
75
|
+
expect(result).toEqual(mockApiResponse);
|
|
76
|
+
});
|
|
77
|
+
it('should update task parent (create subtask relationship)', async () => {
|
|
78
|
+
const mockApiResponse = createMockTask({
|
|
79
|
+
id: '8485093751',
|
|
80
|
+
content: 'Subtask content',
|
|
81
|
+
parentId: 'parent-task-123',
|
|
82
|
+
url: 'https://todoist.com/showTask?id=8485093751',
|
|
83
|
+
addedAt: '2025-08-13T22:09:56.123456Z',
|
|
84
|
+
});
|
|
85
|
+
mockTodoistApi.moveTasks.mockResolvedValue([mockApiResponse]);
|
|
86
|
+
const result = await tasksUpdateOne.execute({ id: '8485093751', parentId: 'parent-task-123' }, mockTodoistApi);
|
|
87
|
+
expect(mockTodoistApi.moveTasks).toHaveBeenCalledWith(['8485093751'], {
|
|
88
|
+
parentId: 'parent-task-123',
|
|
89
|
+
});
|
|
90
|
+
expect(mockTodoistApi.updateTask).not.toHaveBeenCalled();
|
|
91
|
+
expect(result).toEqual(mockApiResponse);
|
|
92
|
+
});
|
|
93
|
+
it('should move task and update properties at once', async () => {
|
|
94
|
+
const movedTask = createMockTask({
|
|
95
|
+
id: '8485093752',
|
|
96
|
+
content: 'Task to move',
|
|
97
|
+
projectId: 'different-project-id',
|
|
98
|
+
});
|
|
99
|
+
const updatedTask = createMockTask({
|
|
100
|
+
id: '8485093752',
|
|
101
|
+
content: 'Completely updated task',
|
|
102
|
+
description: 'New description with details',
|
|
103
|
+
priority: 4,
|
|
104
|
+
projectId: 'different-project-id',
|
|
105
|
+
url: 'https://todoist.com/showTask?id=8485093752',
|
|
106
|
+
addedAt: '2025-08-13T22:09:56.123456Z',
|
|
107
|
+
due: {
|
|
108
|
+
date: '2025-08-25',
|
|
109
|
+
isRecurring: true,
|
|
110
|
+
lang: 'en',
|
|
111
|
+
string: 'every Friday',
|
|
112
|
+
timezone: null,
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
mockTodoistApi.moveTasks.mockResolvedValue([movedTask]);
|
|
116
|
+
mockTodoistApi.updateTask.mockResolvedValue(updatedTask);
|
|
117
|
+
const result = await tasksUpdateOne.execute({
|
|
118
|
+
id: '8485093752',
|
|
119
|
+
content: 'Completely updated task',
|
|
120
|
+
description: 'New description with details',
|
|
121
|
+
priority: 4,
|
|
122
|
+
dueString: 'every Friday',
|
|
123
|
+
projectId: 'different-project-id',
|
|
124
|
+
}, mockTodoistApi);
|
|
125
|
+
// Should call moveTasks first for the projectId
|
|
126
|
+
expect(mockTodoistApi.moveTasks).toHaveBeenCalledWith(['8485093752'], {
|
|
127
|
+
projectId: 'different-project-id',
|
|
128
|
+
});
|
|
129
|
+
// Then call updateTask for the other properties
|
|
130
|
+
expect(mockTodoistApi.updateTask).toHaveBeenCalledWith('8485093752', {
|
|
131
|
+
content: 'Completely updated task',
|
|
132
|
+
description: 'New description with details',
|
|
133
|
+
priority: 4,
|
|
134
|
+
dueString: 'every Friday',
|
|
135
|
+
});
|
|
136
|
+
expect(result).toEqual(updatedTask);
|
|
137
|
+
});
|
|
138
|
+
it('should update task duration', async () => {
|
|
139
|
+
const mockApiResponse = createMockTask({
|
|
140
|
+
id: '8485093753',
|
|
141
|
+
content: 'Task with updated duration',
|
|
142
|
+
duration: { amount: 150, unit: 'minute' },
|
|
143
|
+
url: 'https://todoist.com/showTask?id=8485093753',
|
|
144
|
+
addedAt: '2025-08-13T22:09:56.123456Z',
|
|
145
|
+
});
|
|
146
|
+
mockTodoistApi.updateTask.mockResolvedValue(mockApiResponse);
|
|
147
|
+
const result = await tasksUpdateOne.execute({
|
|
148
|
+
id: '8485093753',
|
|
149
|
+
duration: '2h30m',
|
|
150
|
+
}, mockTodoistApi);
|
|
151
|
+
expect(mockTodoistApi.updateTask).toHaveBeenCalledWith('8485093753', {
|
|
152
|
+
duration: 150,
|
|
153
|
+
durationUnit: 'minute',
|
|
154
|
+
});
|
|
155
|
+
expect(result).toEqual(mockApiResponse);
|
|
156
|
+
});
|
|
157
|
+
it('should handle various duration formats', async () => {
|
|
158
|
+
const mockApiResponse = createMockTask({
|
|
159
|
+
id: '8485093754',
|
|
160
|
+
content: 'Test task',
|
|
161
|
+
duration: { amount: 120, unit: 'minute' },
|
|
162
|
+
});
|
|
163
|
+
mockTodoistApi.updateTask.mockResolvedValue(mockApiResponse);
|
|
164
|
+
// Test different duration formats
|
|
165
|
+
const testCases = [
|
|
166
|
+
{ input: '2h', expectedMinutes: 120 },
|
|
167
|
+
{ input: '90m', expectedMinutes: 90 },
|
|
168
|
+
{ input: '1.5h', expectedMinutes: 90 },
|
|
169
|
+
{ input: ' 2h 30m ', expectedMinutes: 150 },
|
|
170
|
+
{ input: '2H30M', expectedMinutes: 150 },
|
|
171
|
+
];
|
|
172
|
+
for (const testCase of testCases) {
|
|
173
|
+
mockTodoistApi.updateTask.mockClear();
|
|
174
|
+
await tasksUpdateOne.execute({
|
|
175
|
+
id: '8485093754',
|
|
176
|
+
duration: testCase.input,
|
|
177
|
+
}, mockTodoistApi);
|
|
178
|
+
expect(mockTodoistApi.updateTask).toHaveBeenCalledWith('8485093754', expect.objectContaining({
|
|
179
|
+
duration: testCase.expectedMinutes,
|
|
180
|
+
durationUnit: 'minute',
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
it('should update task with duration and move at once', async () => {
|
|
185
|
+
const movedTask = createMockTask({
|
|
186
|
+
id: '8485093755',
|
|
187
|
+
content: 'Task to move and update',
|
|
188
|
+
projectId: 'new-project-id',
|
|
189
|
+
});
|
|
190
|
+
const updatedTask = createMockTask({
|
|
191
|
+
id: '8485093755',
|
|
192
|
+
content: 'Updated task with duration',
|
|
193
|
+
duration: { amount: 120, unit: 'minute' },
|
|
194
|
+
projectId: 'new-project-id',
|
|
195
|
+
});
|
|
196
|
+
mockTodoistApi.moveTasks.mockResolvedValue([movedTask]);
|
|
197
|
+
mockTodoistApi.updateTask.mockResolvedValue(updatedTask);
|
|
198
|
+
const result = await tasksUpdateOne.execute({
|
|
199
|
+
id: '8485093755',
|
|
200
|
+
content: 'Updated task with duration',
|
|
201
|
+
duration: '2h',
|
|
202
|
+
projectId: 'new-project-id',
|
|
203
|
+
}, mockTodoistApi);
|
|
204
|
+
// Should call moveTasks first
|
|
205
|
+
expect(mockTodoistApi.moveTasks).toHaveBeenCalledWith(['8485093755'], {
|
|
206
|
+
projectId: 'new-project-id',
|
|
207
|
+
});
|
|
208
|
+
// Then call updateTask with duration
|
|
209
|
+
expect(mockTodoistApi.updateTask).toHaveBeenCalledWith('8485093755', {
|
|
210
|
+
content: 'Updated task with duration',
|
|
211
|
+
duration: 120,
|
|
212
|
+
durationUnit: 'minute',
|
|
213
|
+
});
|
|
214
|
+
expect(result).toEqual(updatedTask);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
describe('error handling', () => {
|
|
218
|
+
it('should throw error for invalid duration format', async () => {
|
|
219
|
+
await expect(tasksUpdateOne.execute({
|
|
220
|
+
id: '8485093756',
|
|
221
|
+
duration: 'invalid',
|
|
222
|
+
}, mockTodoistApi)).rejects.toThrow('Task 8485093756: Invalid duration format "invalid"');
|
|
223
|
+
});
|
|
224
|
+
it('should throw error for duration exceeding 24 hours', async () => {
|
|
225
|
+
await expect(tasksUpdateOne.execute({
|
|
226
|
+
id: '8485093757',
|
|
227
|
+
duration: '25h',
|
|
228
|
+
}, mockTodoistApi)).rejects.toThrow('Task 8485093757: Invalid duration format "25h": Duration cannot exceed 24 hours (1440 minutes)');
|
|
229
|
+
});
|
|
230
|
+
it('should throw error when multiple move parameters are provided', async () => {
|
|
231
|
+
await expect(tasksUpdateOne.execute({ id: '8485093748', projectId: 'new-project', sectionId: 'new-section' }, mockTodoistApi)).rejects.toThrow('Only one of projectId, sectionId, or parentId can be specified at a time. ' +
|
|
232
|
+
'The Todoist API requires exactly one destination for move operations.');
|
|
233
|
+
});
|
|
234
|
+
it('should throw error when all three move parameters are provided', async () => {
|
|
235
|
+
await expect(tasksUpdateOne.execute({ id: '8485093748', projectId: 'p1', sectionId: 's1', parentId: 't1' }, mockTodoistApi)).rejects.toThrow('Only one of projectId, sectionId, or parentId can be specified at a time');
|
|
236
|
+
});
|
|
237
|
+
it.each([
|
|
238
|
+
{
|
|
239
|
+
error: 'API Error: Task not found',
|
|
240
|
+
params: { id: 'non-existent-task', content: 'Updated content' },
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
error: 'API Error: Invalid priority value',
|
|
244
|
+
params: { id: '8485093748', priority: 5 },
|
|
245
|
+
},
|
|
246
|
+
])('should propagate $error', async ({ error, params }) => {
|
|
247
|
+
mockTodoistApi.updateTask.mockRejectedValue(new Error(error));
|
|
248
|
+
await expect(tasksUpdateOne.execute(params, mockTodoistApi)).rejects.toThrow(error);
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
});
|
package/dist/tools/delete-one.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.deleteOne = void 0;
|
|
4
|
-
const zod_1 = require("zod");
|
|
1
|
+
import { z } from 'zod';
|
|
5
2
|
const ArgsSchema = {
|
|
6
|
-
type:
|
|
7
|
-
id:
|
|
3
|
+
type: z.enum(['project', 'section', 'task']).describe('The type of entity to delete.'),
|
|
4
|
+
id: z.string().min(1).describe('The ID of the entity to delete.'),
|
|
8
5
|
};
|
|
9
6
|
const deleteOne = {
|
|
10
7
|
name: 'delete-one',
|
|
@@ -25,4 +22,4 @@ const deleteOne = {
|
|
|
25
22
|
return { success: true };
|
|
26
23
|
},
|
|
27
24
|
};
|
|
28
|
-
|
|
25
|
+
export { deleteOne };
|
package/dist/tools/overview.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.overview = void 0;
|
|
4
|
-
const zod_1 = require("zod");
|
|
5
|
-
const tool_helpers_1 = require("../tool-helpers");
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { isPersonalProject, mapTask } from '../tool-helpers.js';
|
|
6
3
|
const ArgsSchema = {
|
|
7
|
-
projectId:
|
|
4
|
+
projectId: z
|
|
8
5
|
.string()
|
|
9
6
|
.min(1)
|
|
10
7
|
.optional()
|
|
@@ -25,7 +22,7 @@ function buildProjectTree(projects) {
|
|
|
25
22
|
const current = byId[p.id];
|
|
26
23
|
if (!current)
|
|
27
24
|
continue;
|
|
28
|
-
if (
|
|
25
|
+
if (isPersonalProject(p) && p.parentId) {
|
|
29
26
|
const parent = byId[p.parentId];
|
|
30
27
|
if (parent) {
|
|
31
28
|
parent.children.push(current);
|
|
@@ -113,7 +110,7 @@ async function getAllTasksForProject(client, projectId) {
|
|
|
113
110
|
limit: 50,
|
|
114
111
|
cursor: cursor ?? undefined,
|
|
115
112
|
});
|
|
116
|
-
allTasks = allTasks.concat(results.map(
|
|
113
|
+
allTasks = allTasks.concat(results.map(mapTask));
|
|
117
114
|
cursor = nextCursor ?? undefined;
|
|
118
115
|
} while (cursor);
|
|
119
116
|
return allTasks;
|
|
@@ -125,8 +122,8 @@ async function getProjectSections(client, projectId) {
|
|
|
125
122
|
// Account overview implementation
|
|
126
123
|
async function generateAccountOverview(client) {
|
|
127
124
|
const { results: projects } = await client.getProjects({});
|
|
128
|
-
const inbox = projects.find((p) =>
|
|
129
|
-
const nonInbox = projects.filter((p) => !
|
|
125
|
+
const inbox = projects.find((p) => isPersonalProject(p) && p.inboxProject === true);
|
|
126
|
+
const nonInbox = projects.filter((p) => !isPersonalProject(p) || p.inboxProject !== true);
|
|
130
127
|
const tree = buildProjectTree(nonInbox);
|
|
131
128
|
const allProjectIds = projects.map((p) => p.id);
|
|
132
129
|
const sectionsByProject = await getSectionsByProject(client, allProjectIds);
|
|
@@ -202,4 +199,4 @@ const overview = {
|
|
|
202
199
|
return await generateAccountOverview(client);
|
|
203
200
|
},
|
|
204
201
|
};
|
|
205
|
-
|
|
202
|
+
export { overview };
|
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.projectsList = void 0;
|
|
4
|
-
const zod_1 = require("zod");
|
|
5
|
-
const tool_helpers_1 = require("../tool-helpers");
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { mapProject } from '../tool-helpers.js';
|
|
6
3
|
const ArgsSchema = {
|
|
7
|
-
search:
|
|
4
|
+
search: z
|
|
8
5
|
.string()
|
|
9
6
|
.optional()
|
|
10
7
|
.describe('Search for a project by name (partial and case insensitive match). If omitted, all projects are returned.'),
|
|
11
|
-
limit:
|
|
8
|
+
limit: z
|
|
12
9
|
.number()
|
|
13
10
|
.int()
|
|
14
11
|
.min(1)
|
|
15
12
|
.max(100)
|
|
16
13
|
.default(50)
|
|
17
14
|
.describe('The maximum number of projects to return.'),
|
|
18
|
-
cursor:
|
|
15
|
+
cursor: z
|
|
19
16
|
.string()
|
|
20
17
|
.optional()
|
|
21
18
|
.describe('The cursor to get the next page of projects (cursor is obtained from the previous call to this tool, with the same parameters).'),
|
|
@@ -34,9 +31,9 @@ const projectsList = {
|
|
|
34
31
|
? results.filter((project) => project.name.toLowerCase().includes(searchLower))
|
|
35
32
|
: results;
|
|
36
33
|
return {
|
|
37
|
-
projects: filtered.map(
|
|
34
|
+
projects: filtered.map(mapProject),
|
|
38
35
|
nextCursor,
|
|
39
36
|
};
|
|
40
37
|
},
|
|
41
38
|
};
|
|
42
|
-
|
|
39
|
+
export { projectsList };
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.projectsManage = void 0;
|
|
4
|
-
const zod_1 = require("zod");
|
|
5
|
-
const tool_helpers_1 = require("../tool-helpers");
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { mapProject } from '../tool-helpers.js';
|
|
6
3
|
const ArgsSchema = {
|
|
7
|
-
id:
|
|
4
|
+
id: z
|
|
8
5
|
.string()
|
|
9
6
|
.min(1)
|
|
10
7
|
.optional()
|
|
11
8
|
.describe('The ID of the project to update. If provided, updates the project. If omitted, creates a new project.'),
|
|
12
|
-
name:
|
|
9
|
+
name: z.string().min(1).describe('The name of the project.'),
|
|
13
10
|
};
|
|
14
11
|
const projectsManage = {
|
|
15
12
|
name: 'projects-manage',
|
|
@@ -23,7 +20,7 @@ const projectsManage = {
|
|
|
23
20
|
}
|
|
24
21
|
// Create new project
|
|
25
22
|
const project = await client.addProject({ name: args.name });
|
|
26
|
-
return
|
|
23
|
+
return mapProject(project);
|
|
27
24
|
},
|
|
28
25
|
};
|
|
29
|
-
|
|
26
|
+
export { projectsManage };
|
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.sectionsManage = void 0;
|
|
4
|
-
const zod_1 = require("zod");
|
|
5
|
-
const mcp_helpers_1 = require("../mcp-helpers");
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { errorContent } from '../mcp-helpers.js';
|
|
6
3
|
const ArgsSchema = {
|
|
7
|
-
id:
|
|
4
|
+
id: z
|
|
8
5
|
.string()
|
|
9
6
|
.min(1)
|
|
10
7
|
.optional()
|
|
11
8
|
.describe('The ID of the section to update. If provided, updates the section. If omitted, creates a new section.'),
|
|
12
|
-
name:
|
|
13
|
-
projectId:
|
|
9
|
+
name: z.string().min(1).describe('The name of the section.'),
|
|
10
|
+
projectId: z
|
|
14
11
|
.string()
|
|
15
12
|
.min(1)
|
|
16
13
|
.optional()
|
|
@@ -28,7 +25,7 @@ const sectionsManage = {
|
|
|
28
25
|
}
|
|
29
26
|
// Create new section - projectId is required
|
|
30
27
|
if (!args.projectId) {
|
|
31
|
-
return
|
|
28
|
+
return errorContent('Error: projectId is required when creating a new section (when id is not provided).');
|
|
32
29
|
}
|
|
33
30
|
const section = await client.addSection({
|
|
34
31
|
name: args.name,
|
|
@@ -37,4 +34,4 @@ const sectionsManage = {
|
|
|
37
34
|
return section;
|
|
38
35
|
},
|
|
39
36
|
};
|
|
40
|
-
|
|
37
|
+
export { sectionsManage };
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sectionsSearch = void 0;
|
|
4
|
-
const zod_1 = require("zod");
|
|
1
|
+
import { z } from 'zod';
|
|
5
2
|
const ArgsSchema = {
|
|
6
|
-
projectId:
|
|
7
|
-
search:
|
|
3
|
+
projectId: z.string().min(1).describe('The ID of the project to search sections in.'),
|
|
4
|
+
search: z
|
|
8
5
|
.string()
|
|
9
6
|
.optional()
|
|
10
7
|
.describe('Search for a section by name (partial and case insensitive match). If omitted, all sections in the project are returned.'),
|
|
@@ -27,4 +24,4 @@ const sectionsSearch = {
|
|
|
27
24
|
}));
|
|
28
25
|
},
|
|
29
26
|
};
|
|
30
|
-
|
|
27
|
+
export { sectionsSearch };
|
|
@@ -11,16 +11,19 @@ declare const tasksAddMultiple: {
|
|
|
11
11
|
description: z.ZodOptional<z.ZodString>;
|
|
12
12
|
priority: z.ZodOptional<z.ZodNumber>;
|
|
13
13
|
dueString: z.ZodOptional<z.ZodString>;
|
|
14
|
+
duration: z.ZodOptional<z.ZodString>;
|
|
14
15
|
}, "strip", z.ZodTypeAny, {
|
|
15
16
|
content: string;
|
|
16
17
|
description?: string | undefined;
|
|
17
18
|
priority?: number | undefined;
|
|
18
19
|
dueString?: string | undefined;
|
|
20
|
+
duration?: string | undefined;
|
|
19
21
|
}, {
|
|
20
22
|
content: string;
|
|
21
23
|
description?: string | undefined;
|
|
22
24
|
priority?: number | undefined;
|
|
23
25
|
dueString?: string | undefined;
|
|
26
|
+
duration?: string | undefined;
|
|
24
27
|
}>, "many">;
|
|
25
28
|
};
|
|
26
29
|
execute(args: {
|
|
@@ -29,6 +32,7 @@ declare const tasksAddMultiple: {
|
|
|
29
32
|
description?: string | undefined;
|
|
30
33
|
priority?: number | undefined;
|
|
31
34
|
dueString?: string | undefined;
|
|
35
|
+
duration?: string | undefined;
|
|
32
36
|
}[];
|
|
33
37
|
parentId?: string | undefined;
|
|
34
38
|
projectId?: string | undefined;
|
|
@@ -44,6 +48,7 @@ declare const tasksAddMultiple: {
|
|
|
44
48
|
sectionId: string | null;
|
|
45
49
|
parentId: string | null;
|
|
46
50
|
labels: string[];
|
|
51
|
+
duration: string | null;
|
|
47
52
|
}[]>;
|
|
48
53
|
};
|
|
49
54
|
export { tasksAddMultiple };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tasks-add-multiple.d.ts","sourceRoot":"","sources":["../../src/tools/tasks-add-multiple.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"tasks-add-multiple.d.ts","sourceRoot":"","sources":["../../src/tools/tasks-add-multiple.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAyBvB,QAAA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCoB,CAAA;AAE1C,OAAO,EAAE,gBAAgB,EAAE,CAAA"}
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { mapTask } from '../tool-helpers.js';
|
|
3
|
+
import { DurationParseError, parseDuration } from '../utils/duration-parser.js';
|
|
4
|
+
const TaskSchema = z.object({
|
|
5
|
+
content: z.string().min(1).describe('The content of the task to create.'),
|
|
6
|
+
description: z.string().optional().describe('The description of the task.'),
|
|
7
|
+
priority: z.number().int().min(1).max(4).optional().describe('The priority of the task (1-4).'),
|
|
8
|
+
dueString: z.string().optional().describe('The due date for the task, in natural language.'),
|
|
9
|
+
duration: z
|
|
10
|
+
.string()
|
|
11
|
+
.optional()
|
|
12
|
+
.describe('The duration of the task. Use format: "2h" (hours), "90m" (minutes), "2h30m" (combined), or "1.5h" (decimal hours). Max 24h.'),
|
|
11
13
|
});
|
|
12
14
|
const ArgsSchema = {
|
|
13
|
-
projectId:
|
|
14
|
-
sectionId:
|
|
15
|
-
parentId:
|
|
16
|
-
tasks:
|
|
15
|
+
projectId: z.string().optional().describe('The project ID to add the tasks to.'),
|
|
16
|
+
sectionId: z.string().optional().describe('The section ID to add the tasks to.'),
|
|
17
|
+
parentId: z.string().optional().describe('The parent task ID (for subtasks).'),
|
|
18
|
+
tasks: z.array(TaskSchema).min(1).describe('The array of tasks to add.'),
|
|
17
19
|
};
|
|
18
20
|
const tasksAddMultiple = {
|
|
19
21
|
name: 'tasks-add-multiple',
|
|
@@ -23,10 +25,28 @@ const tasksAddMultiple = {
|
|
|
23
25
|
const { projectId, sectionId, parentId, tasks } = args;
|
|
24
26
|
const newTasks = [];
|
|
25
27
|
for (const task of tasks) {
|
|
26
|
-
const
|
|
28
|
+
const { duration: durationStr, ...otherTaskArgs } = task;
|
|
29
|
+
let taskArgs = { ...otherTaskArgs, projectId, sectionId, parentId };
|
|
30
|
+
// Parse duration if provided
|
|
31
|
+
if (durationStr) {
|
|
32
|
+
try {
|
|
33
|
+
const { minutes } = parseDuration(durationStr);
|
|
34
|
+
taskArgs = {
|
|
35
|
+
...taskArgs,
|
|
36
|
+
duration: minutes,
|
|
37
|
+
durationUnit: 'minute',
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
if (error instanceof DurationParseError) {
|
|
42
|
+
throw new Error(`Task "${task.content}": ${error.message}`);
|
|
43
|
+
}
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
27
47
|
newTasks.push(await client.addTask(taskArgs));
|
|
28
48
|
}
|
|
29
|
-
return newTasks.map(
|
|
49
|
+
return newTasks.map(mapTask);
|
|
30
50
|
},
|
|
31
51
|
};
|
|
32
|
-
|
|
52
|
+
export { tasksAddMultiple };
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.tasksCompleteMultiple = void 0;
|
|
4
|
-
const zod_1 = require("zod");
|
|
1
|
+
import { z } from 'zod';
|
|
5
2
|
const ArgsSchema = {
|
|
6
|
-
ids:
|
|
3
|
+
ids: z.array(z.string().min(1)).min(1).describe('The IDs of the tasks to complete.'),
|
|
7
4
|
};
|
|
8
5
|
const tasksCompleteMultiple = {
|
|
9
6
|
name: 'tasks-complete-multiple',
|
|
@@ -23,4 +20,4 @@ const tasksCompleteMultiple = {
|
|
|
23
20
|
return { success: true, completed };
|
|
24
21
|
},
|
|
25
22
|
};
|
|
26
|
-
|
|
23
|
+
export { tasksCompleteMultiple };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tasks-list-by-date.d.ts","sourceRoot":"","sources":["../../src/tools/tasks-list-by-date.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAmCvB,QAAA,MAAM,eAAe
|
|
1
|
+
{"version":3,"file":"tasks-list-by-date.d.ts","sourceRoot":"","sources":["../../src/tools/tasks-list-by-date.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAmCvB,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BqB,CAAA;AAE1C,OAAO,EAAE,eAAe,EAAE,CAAA"}
|
|
@@ -1,29 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const date_fns_1 = require("date-fns");
|
|
5
|
-
const zod_1 = require("zod");
|
|
6
|
-
const tool_helpers_1 = require("../tool-helpers");
|
|
1
|
+
import { addDays, formatISO } from 'date-fns';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { getTasksByFilter } from '../tool-helpers.js';
|
|
7
4
|
const ArgsSchema = {
|
|
8
|
-
startDate:
|
|
5
|
+
startDate: z
|
|
9
6
|
.string()
|
|
10
7
|
.regex(/^(\d{4}-\d{2}-\d{2}|today|overdue)$/)
|
|
11
8
|
.describe("The start date to get the tasks for. Format: YYYY-MM-DD, 'today', or 'overdue'."),
|
|
12
|
-
daysCount:
|
|
9
|
+
daysCount: z
|
|
13
10
|
.number()
|
|
14
11
|
.int()
|
|
15
12
|
.min(1)
|
|
16
13
|
.max(30)
|
|
17
14
|
.default(1)
|
|
18
15
|
.describe("The number of days to get the tasks for, starting from the start date. Ignored when startDate is 'overdue'."),
|
|
19
|
-
limit:
|
|
16
|
+
limit: z
|
|
20
17
|
.number()
|
|
21
18
|
.int()
|
|
22
19
|
.min(1)
|
|
23
20
|
.max(50)
|
|
24
21
|
.default(10)
|
|
25
22
|
.describe('The maximum number of tasks to return.'),
|
|
26
|
-
cursor:
|
|
23
|
+
cursor: z
|
|
27
24
|
.string()
|
|
28
25
|
.optional()
|
|
29
26
|
.describe('The cursor to get the next page of tasks (cursor is obtained from the previous call to this tool, with the same parameters).'),
|
|
@@ -39,13 +36,13 @@ const tasksListByDate = {
|
|
|
39
36
|
}
|
|
40
37
|
else {
|
|
41
38
|
const startDate = args.startDate === 'today'
|
|
42
|
-
?
|
|
39
|
+
? formatISO(new Date(), { representation: 'date' })
|
|
43
40
|
: args.startDate;
|
|
44
|
-
const endDate =
|
|
45
|
-
const endDateStr =
|
|
41
|
+
const endDate = addDays(startDate, args.daysCount + 1);
|
|
42
|
+
const endDateStr = formatISO(endDate, { representation: 'date' });
|
|
46
43
|
query = `(due after: ${startDate} | due: ${startDate}) & due before: ${endDateStr}`;
|
|
47
44
|
}
|
|
48
|
-
return await
|
|
45
|
+
return await getTasksByFilter({
|
|
49
46
|
client,
|
|
50
47
|
query,
|
|
51
48
|
cursor: args.cursor,
|
|
@@ -53,4 +50,4 @@ const tasksListByDate = {
|
|
|
53
50
|
});
|
|
54
51
|
},
|
|
55
52
|
};
|
|
56
|
-
|
|
53
|
+
export { tasksListByDate };
|