@doist/todoist-ai 4.10.0 → 4.13.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 (35) hide show
  1. package/dist/filter-helpers.d.ts +51 -0
  2. package/dist/filter-helpers.d.ts.map +1 -0
  3. package/dist/filter-helpers.js +79 -0
  4. package/dist/index.d.ts +47 -23
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/tool-helpers.d.ts +8 -20
  7. package/dist/tool-helpers.d.ts.map +1 -1
  8. package/dist/tool-helpers.js +6 -23
  9. package/dist/tools/__tests__/find-completed-tasks.test.js +71 -12
  10. package/dist/tools/__tests__/find-tasks-by-date.test.js +118 -20
  11. package/dist/tools/__tests__/find-tasks.test.js +2 -0
  12. package/dist/tools/add-projects.d.ts +3 -3
  13. package/dist/tools/add-tasks.d.ts +6 -3
  14. package/dist/tools/add-tasks.d.ts.map +1 -1
  15. package/dist/tools/delete-object.d.ts +1 -1
  16. package/dist/tools/find-completed-tasks.d.ts +8 -2
  17. package/dist/tools/find-completed-tasks.d.ts.map +1 -1
  18. package/dist/tools/find-completed-tasks.js +37 -4
  19. package/dist/tools/find-tasks-by-date.d.ts +9 -0
  20. package/dist/tools/find-tasks-by-date.d.ts.map +1 -1
  21. package/dist/tools/find-tasks-by-date.js +39 -15
  22. package/dist/tools/find-tasks.d.ts +5 -2
  23. package/dist/tools/find-tasks.d.ts.map +1 -1
  24. package/dist/tools/find-tasks.js +20 -36
  25. package/dist/tools/update-comments.d.ts +3 -3
  26. package/dist/tools/update-sections.d.ts +3 -3
  27. package/dist/tools/update-tasks.d.ts +9 -6
  28. package/dist/tools/update-tasks.d.ts.map +1 -1
  29. package/dist/utils/test-helpers.d.ts +3 -0
  30. package/dist/utils/test-helpers.d.ts.map +1 -1
  31. package/dist/utils/test-helpers.js +3 -0
  32. package/dist/utils/user-resolver.d.ts +2 -4
  33. package/dist/utils/user-resolver.d.ts.map +1 -1
  34. package/dist/utils/user-resolver.js +5 -5
  35. package/package.json +4 -4
@@ -1,7 +1,8 @@
1
1
  import { addDays, formatISO } from 'date-fns';
2
2
  import { z } from 'zod';
3
+ import { appendToQuery, buildResponsibleUserQueryFilter, RESPONSIBLE_USER_FILTERING, resolveResponsibleUser, } from '../filter-helpers.js';
3
4
  import { getToolOutput } from '../mcp-helpers.js';
4
- import { filterTasksByResponsibleUser, getTasksByFilter } from '../tool-helpers.js';
5
+ import { getTasksByFilter } from '../tool-helpers.js';
5
6
  import { ApiLimits } from '../utils/constants.js';
6
7
  import { generateLabelsFilter, LabelsSchema } from '../utils/labels.js';
7
8
  import { generateTaskNextSteps, getDateString, previewTasks, summarizeList, } from '../utils/response-builders.js';
@@ -34,6 +35,14 @@ const ArgsSchema = {
34
35
  .string()
35
36
  .optional()
36
37
  .describe('The cursor to get the next page of tasks (cursor is obtained from the previous call to this tool, with the same parameters).'),
38
+ responsibleUser: z
39
+ .string()
40
+ .optional()
41
+ .describe('Find tasks assigned to this user. Can be a user ID, name, or email address.'),
42
+ responsibleUserFiltering: z
43
+ .enum(RESPONSIBLE_USER_FILTERING)
44
+ .optional()
45
+ .describe('How to filter by responsible user when responsibleUser is not provided. "assigned" = only tasks assigned to others; "unassignedOrMe" = only unassigned tasks or tasks assigned to me; "all" = all tasks regardless of assignment. Default is "unassignedOrMe".'),
37
46
  ...LabelsSchema,
38
47
  };
39
48
  const findTasksByDate = {
@@ -44,14 +53,18 @@ const findTasksByDate = {
44
53
  if (!args.startDate && args.overdueOption !== 'overdue-only') {
45
54
  throw new Error('Either startDate must be provided or overdueOption must be set to overdue-only');
46
55
  }
56
+ // Resolve assignee name to user ID if provided
57
+ const resolved = await resolveResponsibleUser(client, args.responsibleUser);
58
+ const resolvedAssigneeId = resolved?.userId;
59
+ const assigneeEmail = resolved?.email;
47
60
  let query = '';
48
- const todoistUser = await client.getUser();
49
61
  if (args.overdueOption === 'overdue-only') {
50
62
  query = 'overdue';
51
63
  }
52
64
  else if (args.startDate === 'today') {
53
65
  // For 'today', include overdue unless explicitly excluded
54
- query = args.overdueOption === 'exclude-overdue' ? 'today' : 'today | overdue';
66
+ // Use parentheses to ensure correct operator precedence when combining with other filters
67
+ query = args.overdueOption === 'exclude-overdue' ? 'today' : '(today | overdue)';
55
68
  }
56
69
  else if (args.startDate) {
57
70
  // For specific dates, never include overdue tasks
@@ -60,30 +73,31 @@ const findTasksByDate = {
60
73
  const endDateStr = formatISO(endDate, { representation: 'date' });
61
74
  query = `(due after: ${startDate} | due: ${startDate}) & due before: ${endDateStr}`;
62
75
  }
76
+ // Add labels filter
63
77
  const labelsFilter = generateLabelsFilter(args.labels, args.labelsOperator);
64
78
  if (labelsFilter.length > 0) {
65
- // If there is already a query, we need to append the & operator first
66
- if (query.length > 0)
67
- query += ' & ';
68
- // Add the labels to the filter
69
- query += `(${labelsFilter})`;
79
+ query = appendToQuery(query, `(${labelsFilter})`);
70
80
  }
81
+ // Add responsible user filtering to the query (backend filtering)
82
+ const responsibleUserFilter = buildResponsibleUserQueryFilter({
83
+ resolvedAssigneeId,
84
+ assigneeEmail,
85
+ responsibleUserFiltering: args.responsibleUserFiltering,
86
+ });
87
+ query = appendToQuery(query, responsibleUserFilter);
71
88
  const result = await getTasksByFilter({
72
89
  client,
73
90
  query,
74
91
  cursor: args.cursor,
75
92
  limit: args.limit,
76
93
  });
77
- // Apply responsible user filtering - only show unassigned tasks or tasks assigned to current user
78
- const filteredTasks = filterTasksByResponsibleUser({
79
- tasks: result.tasks,
80
- resolvedAssigneeId: undefined,
81
- currentUserId: todoistUser.id,
82
- });
94
+ // No need for post-fetch filtering since it's handled in the query
95
+ const filteredTasks = result.tasks;
83
96
  const textContent = generateTextContent({
84
97
  tasks: filteredTasks,
85
98
  args,
86
99
  nextCursor: result.nextCursor,
100
+ assigneeEmail,
87
101
  });
88
102
  return getToolOutput({
89
103
  textContent,
@@ -97,7 +111,7 @@ const findTasksByDate = {
97
111
  });
98
112
  },
99
113
  };
100
- function generateTextContent({ tasks, args, nextCursor, }) {
114
+ function generateTextContent({ tasks, args, nextCursor, assigneeEmail, }) {
101
115
  // Generate filter description
102
116
  const filterHints = [];
103
117
  if (args.overdueOption === 'overdue-only') {
@@ -120,6 +134,11 @@ function generateTextContent({ tasks, args, nextCursor, }) {
120
134
  .join(args.labelsOperator === 'and' ? ' & ' : ' | ');
121
135
  filterHints.push(`labels: ${labelText}`);
122
136
  }
137
+ // Add responsible user filter information
138
+ if (args.responsibleUser) {
139
+ const email = assigneeEmail || args.responsibleUser;
140
+ filterHints.push(`assigned to: ${email}`);
141
+ }
123
142
  // Generate subject description
124
143
  let subject = '';
125
144
  if (args.overdueOption === 'overdue-only') {
@@ -135,6 +154,11 @@ function generateTextContent({ tasks, args, nextCursor, }) {
135
154
  else {
136
155
  subject = 'Tasks';
137
156
  }
157
+ // Append responsible user to subject if provided
158
+ if (args.responsibleUser) {
159
+ const email = assigneeEmail || args.responsibleUser;
160
+ subject += ` assigned to ${email}`;
161
+ }
138
162
  // Generate helpful suggestions for empty results
139
163
  const zeroReasonHints = [];
140
164
  if (tasks.length === 0) {
@@ -16,9 +16,9 @@ declare const findTasks: {
16
16
  };
17
17
  execute(args: {
18
18
  limit: number;
19
- projectId?: string | undefined;
20
19
  parentId?: string | undefined;
21
20
  responsibleUserFiltering?: "assigned" | "unassignedOrMe" | "all" | undefined;
21
+ projectId?: string | undefined;
22
22
  sectionId?: string | undefined;
23
23
  labels?: string[] | undefined;
24
24
  cursor?: string | undefined;
@@ -45,15 +45,18 @@ declare const findTasks: {
45
45
  duration: string | null;
46
46
  responsibleUid: string | null;
47
47
  assignedByUid: string | null;
48
+ checked: boolean;
49
+ completedAt: string | null;
50
+ updatedAt: string | null;
48
51
  }[];
49
52
  nextCursor: string | null;
50
53
  totalCount: number;
51
54
  hasMore: boolean;
52
55
  appliedFilters: {
53
56
  limit: number;
54
- projectId?: string | undefined;
55
57
  parentId?: string | undefined;
56
58
  responsibleUserFiltering?: "assigned" | "unassignedOrMe" | "all" | undefined;
59
+ projectId?: string | undefined;
57
60
  sectionId?: string | undefined;
58
61
  labels?: string[] | undefined;
59
62
  cursor?: string | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"find-tasks.d.ts","sourceRoot":"","sources":["../../src/tools/find-tasks.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAsDvB,QAAA,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqM2B,CAAA;AA2K1C,OAAO,EAAE,SAAS,EAAE,CAAA"}
1
+ {"version":3,"file":"find-tasks.d.ts","sourceRoot":"","sources":["../../src/tools/find-tasks.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAsDvB,QAAA,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqL2B,CAAA;AA2K1C,OAAO,EAAE,SAAS,EAAE,CAAA"}
@@ -1,11 +1,11 @@
1
1
  import { z } from 'zod';
2
+ import { appendToQuery, filterTasksByResponsibleUser, RESPONSIBLE_USER_FILTERING, resolveResponsibleUser, } from '../filter-helpers.js';
2
3
  import { getToolOutput } from '../mcp-helpers.js';
3
- import { filterTasksByResponsibleUser, getTasksByFilter, mapTask, RESPONSIBLE_USER_FILTERING, } from '../tool-helpers.js';
4
+ import { getTasksByFilter, mapTask } from '../tool-helpers.js';
4
5
  import { ApiLimits } from '../utils/constants.js';
5
6
  import { generateLabelsFilter, LabelsSchema } from '../utils/labels.js';
6
7
  import { generateTaskNextSteps, getDateString, previewTasks, summarizeList, } from '../utils/response-builders.js';
7
8
  import { ToolNames } from '../utils/tool-names.js';
8
- import { resolveUserNameToId } from '../utils/user-resolver.js';
9
9
  const { FIND_COMPLETED_TASKS, ADD_TASKS } = ToolNames;
10
10
  const ArgsSchema = {
11
11
  searchText: z.string().optional().describe('The text to search for in tasks.'),
@@ -51,18 +51,9 @@ const findTasks = {
51
51
  throw new Error('At least one filter must be provided: searchText, projectId, sectionId, parentId, responsibleUser, or labels');
52
52
  }
53
53
  // Resolve assignee name to user ID if provided
54
- let resolvedAssigneeId = responsibleUser;
55
- let assigneeDisplayName;
56
- if (responsibleUser) {
57
- const resolved = await resolveUserNameToId(client, responsibleUser);
58
- if (resolved) {
59
- resolvedAssigneeId = resolved.userId;
60
- assigneeDisplayName = resolved.displayName;
61
- }
62
- else {
63
- throw new Error(`Could not find user: "${responsibleUser}". Make sure the user is a collaborator on a shared project.`);
64
- }
65
- }
54
+ const resolved = await resolveResponsibleUser(client, responsibleUser);
55
+ const resolvedAssigneeId = resolved?.userId;
56
+ const assigneeEmail = resolved?.email;
66
57
  // If using container-based filtering, use direct API
67
58
  if (projectId || sectionId || parentId) {
68
59
  const taskParams = {
@@ -101,7 +92,7 @@ const findTasks = {
101
92
  args,
102
93
  nextCursor,
103
94
  isContainerSearch: true,
104
- assigneeDisplayName,
95
+ assigneeEmail,
105
96
  });
106
97
  return getToolOutput({
107
98
  textContent,
@@ -117,7 +108,7 @@ const findTasks = {
117
108
  // If only responsibleUid is provided (without containers), use assignee filter
118
109
  if (resolvedAssigneeId && !searchText && !hasLabels) {
119
110
  const tasks = await client.getTasksByFilter({
120
- query: `assigned to: ${assigneeDisplayName}`,
111
+ query: `assigned to: ${assigneeEmail}`,
121
112
  lang: 'en',
122
113
  limit,
123
114
  cursor: cursor ?? null,
@@ -128,7 +119,7 @@ const findTasks = {
128
119
  args,
129
120
  nextCursor: tasks.nextCursor,
130
121
  isContainerSearch: false,
131
- assigneeDisplayName,
122
+ assigneeEmail,
132
123
  });
133
124
  return getToolOutput({
134
125
  textContent,
@@ -149,14 +140,7 @@ const findTasks = {
149
140
  }
150
141
  // Add labels component
151
142
  const labelsFilter = generateLabelsFilter(labels, labelsOperator);
152
- if (labelsFilter.length > 0) {
153
- if (query.length > 0) {
154
- query += ` & ${labelsFilter}`;
155
- }
156
- else {
157
- query = labelsFilter;
158
- }
159
- }
143
+ query = appendToQuery(query, labelsFilter);
160
144
  // Execute filter query
161
145
  const result = await getTasksByFilter({
162
146
  client,
@@ -175,7 +159,7 @@ const findTasks = {
175
159
  args,
176
160
  nextCursor: result.nextCursor,
177
161
  isContainerSearch: false,
178
- assigneeDisplayName,
162
+ assigneeEmail,
179
163
  });
180
164
  return getToolOutput({
181
165
  textContent,
@@ -215,7 +199,7 @@ function getContainerZeroReasonHints(args) {
215
199
  }
216
200
  return [];
217
201
  }
218
- function generateTextContent({ tasks, args, nextCursor, isContainerSearch, assigneeDisplayName, }) {
202
+ function generateTextContent({ tasks, args, nextCursor, isContainerSearch, assigneeEmail, }) {
219
203
  // Generate subject and filter descriptions based on search type
220
204
  let subject = 'Tasks';
221
205
  const filterHints = [];
@@ -244,9 +228,9 @@ function generateTextContent({ tasks, args, nextCursor, isContainerSearch, assig
244
228
  }
245
229
  // Add responsibleUid filter if present
246
230
  if (args.responsibleUser) {
247
- const displayName = assigneeDisplayName || args.responsibleUser;
248
- subject += ` assigned to ${displayName}`;
249
- filterHints.push(`assigned to ${displayName}`);
231
+ const email = assigneeEmail || args.responsibleUser;
232
+ subject += ` assigned to ${email}`;
233
+ filterHints.push(`assigned to ${email}`);
250
234
  }
251
235
  // Add label filter information
252
236
  if (args.labels && args.labels.length > 0) {
@@ -262,14 +246,14 @@ function generateTextContent({ tasks, args, nextCursor, isContainerSearch, assig
262
246
  }
263
247
  else {
264
248
  // Text, responsibleUid, or labels search
265
- const displayName = assigneeDisplayName || args.responsibleUser;
249
+ const email = assigneeEmail || args.responsibleUser;
266
250
  // Build subject based on filters
267
251
  const subjectParts = [];
268
252
  if (args.searchText) {
269
253
  subjectParts.push(`"${args.searchText}"`);
270
254
  }
271
255
  if (args.responsibleUser) {
272
- subjectParts.push(`assigned to ${displayName}`);
256
+ subjectParts.push(`assigned to ${email}`);
273
257
  }
274
258
  if (args.labels && args.labels.length > 0) {
275
259
  const labelText = args.labels
@@ -282,7 +266,7 @@ function generateTextContent({ tasks, args, nextCursor, isContainerSearch, assig
282
266
  filterHints.push(`matching "${args.searchText}"`);
283
267
  }
284
268
  else if (args.responsibleUser && (!args.labels || args.labels.length === 0)) {
285
- subject = `Tasks assigned to ${displayName}`;
269
+ subject = `Tasks assigned to ${email}`;
286
270
  }
287
271
  else if (args.labels && args.labels.length > 0 && !args.responsibleUser) {
288
272
  const labelText = args.labels
@@ -295,7 +279,7 @@ function generateTextContent({ tasks, args, nextCursor, isContainerSearch, assig
295
279
  }
296
280
  // Add filter hints
297
281
  if (args.responsibleUser) {
298
- filterHints.push(`assigned to ${displayName}`);
282
+ filterHints.push(`assigned to ${email}`);
299
283
  }
300
284
  if (args.labels && args.labels.length > 0) {
301
285
  const labelText = args.labels
@@ -305,8 +289,8 @@ function generateTextContent({ tasks, args, nextCursor, isContainerSearch, assig
305
289
  }
306
290
  if (tasks.length === 0) {
307
291
  if (args.responsibleUser) {
308
- const displayName = assigneeDisplayName || args.responsibleUser;
309
- zeroReasonHints.push(`No tasks assigned to ${displayName}`);
292
+ const email = assigneeEmail || args.responsibleUser;
293
+ zeroReasonHints.push(`No tasks assigned to ${email}`);
310
294
  zeroReasonHints.push('Check if the user name is correct');
311
295
  zeroReasonHints.push(`Check completed tasks with ${FIND_COMPLETED_TASKS}`);
312
296
  }
@@ -7,17 +7,17 @@ declare const updateComments: {
7
7
  id: z.ZodString;
8
8
  content: z.ZodString;
9
9
  }, "strip", z.ZodTypeAny, {
10
- content: string;
11
10
  id: string;
12
- }, {
13
11
  content: string;
12
+ }, {
14
13
  id: string;
14
+ content: string;
15
15
  }>, "many">;
16
16
  };
17
17
  execute(args: {
18
18
  comments: {
19
- content: string;
20
19
  id: string;
20
+ content: string;
21
21
  }[];
22
22
  }, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
23
23
  content: {
@@ -7,17 +7,17 @@ declare const updateSections: {
7
7
  id: z.ZodString;
8
8
  name: z.ZodString;
9
9
  }, "strip", z.ZodTypeAny, {
10
- name: string;
11
10
  id: string;
12
- }, {
13
11
  name: string;
12
+ }, {
14
13
  id: string;
14
+ name: string;
15
15
  }>, "many">;
16
16
  };
17
17
  execute({ sections }: {
18
18
  sections: {
19
- name: string;
20
19
  id: string;
20
+ name: string;
21
21
  }[];
22
22
  }, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
23
23
  content: {
@@ -18,10 +18,10 @@ declare const updateTasks: {
18
18
  labels: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
19
19
  }, "strip", z.ZodTypeAny, {
20
20
  id: string;
21
- content?: string | undefined;
22
21
  description?: string | undefined;
23
- projectId?: string | undefined;
24
22
  parentId?: string | undefined;
23
+ content?: string | undefined;
24
+ projectId?: string | undefined;
25
25
  sectionId?: string | undefined;
26
26
  labels?: string[] | undefined;
27
27
  duration?: string | undefined;
@@ -31,10 +31,10 @@ declare const updateTasks: {
31
31
  order?: number | undefined;
32
32
  }, {
33
33
  id: string;
34
- content?: string | undefined;
35
34
  description?: string | undefined;
36
- projectId?: string | undefined;
37
35
  parentId?: string | undefined;
36
+ content?: string | undefined;
37
+ projectId?: string | undefined;
38
38
  sectionId?: string | undefined;
39
39
  labels?: string[] | undefined;
40
40
  duration?: string | undefined;
@@ -47,10 +47,10 @@ declare const updateTasks: {
47
47
  execute(args: {
48
48
  tasks: {
49
49
  id: string;
50
- content?: string | undefined;
51
50
  description?: string | undefined;
52
- projectId?: string | undefined;
53
51
  parentId?: string | undefined;
52
+ content?: string | undefined;
53
+ projectId?: string | undefined;
54
54
  sectionId?: string | undefined;
55
55
  labels?: string[] | undefined;
56
56
  duration?: string | undefined;
@@ -79,6 +79,9 @@ declare const updateTasks: {
79
79
  duration: string | null;
80
80
  responsibleUid: string | null;
81
81
  assignedByUid: string | null;
82
+ checked: boolean;
83
+ completedAt: string | null;
84
+ updatedAt: string | null;
82
85
  }[];
83
86
  totalCount: number;
84
87
  updatedTaskIds: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"update-tasks.d.ts","sourceRoot":"","sources":["../../src/tools/update-tasks.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AA8DvB,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkHyB,CAAA;AAqC1C,OAAO,EAAE,WAAW,EAAE,CAAA"}
1
+ {"version":3,"file":"update-tasks.d.ts","sourceRoot":"","sources":["../../src/tools/update-tasks.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AA8DvB,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkHyB,CAAA;AAqC1C,OAAO,EAAE,WAAW,EAAE,CAAA"}
@@ -18,6 +18,9 @@ export type MappedTask = {
18
18
  duration: string | null;
19
19
  responsibleUid: string | null;
20
20
  assignedByUid: string | null;
21
+ checked: boolean;
22
+ completedAt: string | null;
23
+ updatedAt: string | null;
21
24
  };
22
25
  /**
23
26
  * Creates a mock Task with all required properties and sensible defaults.
@@ -1 +1 @@
1
- {"version":3,"file":"test-helpers.d.ts","sourceRoot":"","sources":["../../src/utils/test-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,+BAA+B,CAAA;AAChG,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAE9C;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG;IACrB,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,SAAS,EAAE,MAAM,GAAG,OAAO,CAAA;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B,CAAA;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,GAAE,OAAO,CAAC,IAAI,CAAM,GAAG,IAAI,CA8BlE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,GAAE,OAAO,CAAC,OAAO,CAAM,GAAG,OAAO,CAgB3E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,GAAE,OAAO,CAAC,eAAe,CAAM,GAAG,eAAe,CAuB3F;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EACnC,OAAO,EAAE,CAAC,EAAE,EACZ,UAAU,GAAE,MAAM,GAAG,IAAW,GACjC;IACC,OAAO,EAAE,CAAC,EAAE,CAAA;IACZ,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B,CAKA;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,GAAE,OAAO,CAAC,UAAU,CAAM,GAAG,UAAU,CAiBhF;AAED;;GAEG;AACH,eAAO,MAAM,WAAW;;;;;CAKd,CAAA;AAEV;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,EAC1C,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;CAAE,CAAC;UAAjC,MAAM;WAAS,CAAC;eAAa,CAAC;IAGtD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,OAAO,GAAG,MAAM,CAqB9D;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACpC,MAAM,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,GACzC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAuBzB;AAED;;GAEG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;CAUX,CAAA;AAEV;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAG,YAAqB,CAAA;AAE1C;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CAmChF"}
1
+ {"version":3,"file":"test-helpers.d.ts","sourceRoot":"","sources":["../../src/utils/test-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,+BAA+B,CAAA;AAChG,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAE9C;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG;IACrB,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,SAAS,EAAE,MAAM,GAAG,OAAO,CAAA;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B,CAAA;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,GAAE,OAAO,CAAC,IAAI,CAAM,GAAG,IAAI,CA8BlE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,GAAE,OAAO,CAAC,OAAO,CAAM,GAAG,OAAO,CAgB3E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,GAAE,OAAO,CAAC,eAAe,CAAM,GAAG,eAAe,CAuB3F;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EACnC,OAAO,EAAE,CAAC,EAAE,EACZ,UAAU,GAAE,MAAM,GAAG,IAAW,GACjC;IACC,OAAO,EAAE,CAAC,EAAE,CAAA;IACZ,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B,CAKA;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,GAAE,OAAO,CAAC,UAAU,CAAM,GAAG,UAAU,CAoBhF;AAED;;GAEG;AACH,eAAO,MAAM,WAAW;;;;;CAKd,CAAA;AAEV;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,CAAC,GAAG,OAAO,EAC1C,KAAK,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;CAAE,CAAC;UAAjC,MAAM;WAAS,CAAC;eAAa,CAAC;IAGtD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,OAAO,GAAG,MAAM,CAqB9D;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACpC,MAAM,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,GACzC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAuBzB;AAED;;GAEG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;CAUX,CAAA;AAEV;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAG,YAAqB,CAAA;AAE1C;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CAmChF"}
@@ -109,6 +109,9 @@ export function createMappedTask(overrides = {}) {
109
109
  duration: null,
110
110
  responsibleUid: null,
111
111
  assignedByUid: null,
112
+ checked: false,
113
+ completedAt: null,
114
+ updatedAt: '2025-08-13T22:09:56.123456Z',
112
115
  ...overrides,
113
116
  };
114
117
  }
@@ -2,6 +2,7 @@ import type { TodoistApi } from '@doist/todoist-api-typescript';
2
2
  export type ResolvedUser = {
3
3
  userId: string;
4
4
  displayName: string;
5
+ email: string;
5
6
  };
6
7
  export type ProjectCollaborator = {
7
8
  id: string;
@@ -32,8 +33,5 @@ export declare class UserResolver {
32
33
  clearCache(): void;
33
34
  }
34
35
  export declare const userResolver: UserResolver;
35
- export declare function resolveUserNameToId(client: TodoistApi, nameOrId: string): Promise<{
36
- userId: string;
37
- displayName: string;
38
- } | null>;
36
+ export declare function resolveUserNameToId(client: TodoistApi, nameOrId: string): Promise<ResolvedUser | null>;
39
37
  //# sourceMappingURL=user-resolver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"user-resolver.d.ts","sourceRoot":"","sources":["../../src/utils/user-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAE/D,MAAM,MAAM,YAAY,GAAG;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CAChB,CAAA;AAsBD,qBAAa,YAAY;IACrB;;;OAGG;IACG,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAoFrF;;OAEG;IACG,2BAA2B,CAC7B,MAAM,EAAE,UAAU,EAClB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC;IASnB;;OAEG;IACG,uBAAuB,CACzB,MAAM,EAAE,UAAU,EAClB,SAAS,EAAE,MAAM,GAClB,OAAO,CAAC,mBAAmB,EAAE,CAAC;IA2BjC;;OAEG;YACW,mBAAmB;IAqDjC;;OAEG;IACH,UAAU,IAAI,IAAI;CAIrB;AAGD,eAAO,MAAM,YAAY,cAAqB,CAAA;AAG9C,wBAAsB,mBAAmB,CACrC,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,MAAM,GACjB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAEzD"}
1
+ {"version":3,"file":"user-resolver.d.ts","sourceRoot":"","sources":["../../src/utils/user-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAE/D,MAAM,MAAM,YAAY,GAAG;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CAChB,CAAA;AAsBD,qBAAa,YAAY;IACrB;;;OAGG;IACG,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAoFrF;;OAEG;IACG,2BAA2B,CAC7B,MAAM,EAAE,UAAU,EAClB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC;IASnB;;OAEG;IACG,uBAAuB,CACzB,MAAM,EAAE,UAAU,EAClB,SAAS,EAAE,MAAM,GAClB,OAAO,CAAC,mBAAmB,EAAE,CAAC;IA2BjC;;OAEG;YACW,mBAAmB;IAqDjC;;OAEG;IACH,UAAU,IAAI,IAAI;CAIrB;AAGD,eAAO,MAAM,YAAY,cAAqB,CAAA;AAG9C,wBAAsB,mBAAmB,CACrC,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAE9B"}
@@ -26,7 +26,7 @@ export class UserResolver {
26
26
  (/^[a-z0-9_]{6,}$/i.test(trimmedInput) &&
27
27
  !/^[a-z]+[\s-]/.test(trimmedInput) &&
28
28
  /[0-9_]/.test(trimmedInput))) {
29
- const result = { userId: trimmedInput, displayName: trimmedInput };
29
+ const result = { userId: trimmedInput, displayName: trimmedInput, email: trimmedInput };
30
30
  userResolutionCache.set(trimmedInput, { result, timestamp: Date.now() });
31
31
  return result;
32
32
  }
@@ -42,28 +42,28 @@ export class UserResolver {
42
42
  // Try exact name match first
43
43
  let match = allCollaborators.find((c) => c.name.toLowerCase() === searchTerm);
44
44
  if (match) {
45
- const result = { userId: match.id, displayName: match.name };
45
+ const result = { userId: match.id, displayName: match.name, email: match.email };
46
46
  userResolutionCache.set(trimmedInput, { result, timestamp: Date.now() });
47
47
  return result;
48
48
  }
49
49
  // Try exact email match
50
50
  match = allCollaborators.find((c) => c.email.toLowerCase() === searchTerm);
51
51
  if (match) {
52
- const result = { userId: match.id, displayName: match.name };
52
+ const result = { userId: match.id, displayName: match.name, email: match.email };
53
53
  userResolutionCache.set(trimmedInput, { result, timestamp: Date.now() });
54
54
  return result;
55
55
  }
56
56
  // Try partial name match (contains)
57
57
  match = allCollaborators.find((c) => c.name.toLowerCase().includes(searchTerm));
58
58
  if (match) {
59
- const result = { userId: match.id, displayName: match.name };
59
+ const result = { userId: match.id, displayName: match.name, email: match.email };
60
60
  userResolutionCache.set(trimmedInput, { result, timestamp: Date.now() });
61
61
  return result;
62
62
  }
63
63
  // Try partial email match
64
64
  match = allCollaborators.find((c) => c.email.toLowerCase().includes(searchTerm));
65
65
  if (match) {
66
- const result = { userId: match.id, displayName: match.name };
66
+ const result = { userId: match.id, displayName: match.name, email: match.email };
67
67
  userResolutionCache.set(trimmedInput, { result, timestamp: Date.now() });
68
68
  return result;
69
69
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doist/todoist-ai",
3
- "version": "4.10.0",
3
+ "version": "4.13.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -45,14 +45,14 @@
45
45
  "prepare": "husky"
46
46
  },
47
47
  "dependencies": {
48
- "@doist/todoist-api-typescript": "5.5.1",
48
+ "@doist/todoist-api-typescript": "5.6.4",
49
49
  "@modelcontextprotocol/sdk": "^1.11.1",
50
50
  "date-fns": "^4.1.0",
51
51
  "dotenv": "^16.5.0",
52
52
  "zod": "^3.25.7"
53
53
  },
54
54
  "devDependencies": {
55
- "@biomejs/biome": "2.2.5",
55
+ "@biomejs/biome": "2.2.6",
56
56
  "@types/express": "^5.0.2",
57
57
  "@types/jest": "30.0.0",
58
58
  "@types/morgan": "^1.9.9",
@@ -65,7 +65,7 @@
65
65
  "morgan": "^1.10.0",
66
66
  "nodemon": "^3.1.10",
67
67
  "rimraf": "^6.0.1",
68
- "ts-jest": "29.4.4",
68
+ "ts-jest": "29.4.5",
69
69
  "typescript": "^5.8.3"
70
70
  },
71
71
  "lint-staged": {