@doist/todoist-ai 4.7.1 → 4.8.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.
@@ -40,7 +40,7 @@ You have access to comprehensive Todoist management tools for personal productiv
40
40
  - **update-tasks**: Modify existing tasks - get task IDs from search results first, only include fields that need changes
41
41
  - **complete-tasks**: Mark tasks as done using task IDs
42
42
  - **find-tasks**: Search by text, project/section/parent container, responsible user, or labels. Requires at least one search parameter
43
- - **find-tasks-by-date**: Get tasks by date range (startDate: YYYY-MM-DD, 'today', or 'overdue') or specific day counts
43
+ - **find-tasks-by-date**: Get tasks by date range (startDate: YYYY-MM-DD or 'today' which includes overdue tasks) or specific day counts
44
44
  - **find-completed-tasks**: View completed tasks by completion date or original due date
45
45
 
46
46
  **Project & Organization:**
@@ -67,7 +67,7 @@ You have access to comprehensive Todoist management tools for personal productiv
67
67
 
68
68
  4. **Bulk Operations**: When working with multiple items, prefer bulk tools (complete-tasks, manage-assignments) over individual operations for better performance.
69
69
 
70
- 5. **Date Handling**: All dates respect user timezone settings. Use 'today' and 'overdue' keywords for dynamic date filtering.
70
+ 5. **Date Handling**: All dates respect user timezone settings. Use 'today' keyword for dynamic date filtering (includes overdue tasks).
71
71
 
72
72
  6. **Labels**: Use label filtering with AND/OR operators for advanced task organization. Most search tools support labels parameter.
73
73
 
@@ -249,9 +249,7 @@ describe(`${ADD_TASKS} tool`, () => {
249
249
  // Verify structured content includes labels
250
250
  const structuredContent = extractStructuredContent(result);
251
251
  expect(structuredContent.tasks).toHaveLength(1);
252
- expect(structuredContent.tasks[0]).toEqual(expect.objectContaining({
253
- labels: ['urgent', 'work'],
254
- }));
252
+ expect(structuredContent.tasks).toEqual(expect.arrayContaining([expect.objectContaining({ labels: ['urgent', 'work'] })]));
255
253
  });
256
254
  it('should add task with empty labels array', async () => {
257
255
  const mockApiResponse = createMockTask({
@@ -139,7 +139,7 @@ describe(`${COMPLETE_TASKS} tool`, () => {
139
139
  const result = await completeTasks.execute({ ids: ['task-1', 'task-2'] }, mockTodoistApi);
140
140
  const textContent = extractTextContent(result);
141
141
  expect(textContent).toMatchSnapshot();
142
- expect(textContent).toContain("Use find-tasks-by-date('overdue')");
142
+ expect(textContent).toContain("Use find-tasks-by-date('today')");
143
143
  });
144
144
  it('should suggest reviewing failures when mixed results', async () => {
145
145
  mockTodoistApi.closeTask
@@ -36,55 +36,15 @@ describe(`${FIND_TASKS_BY_DATE} tool`, () => {
36
36
  afterEach(() => {
37
37
  jest.restoreAllMocks();
38
38
  });
39
- describe('listing overdue tasks', () => {
40
- it.each([
41
- { daysCount: 7, hasTasks: true, description: 'with tasks' },
42
- { daysCount: 5, hasTasks: false, description: 'ignoring daysCount' },
43
- ])('should handle overdue tasks $description', async ({ daysCount, hasTasks }) => {
44
- const mockTasks = hasTasks
45
- ? [
46
- createMappedTask({
47
- id: TEST_IDS.TASK_1,
48
- content: 'Overdue task',
49
- dueDate: '2025-08-10',
50
- priority: 2,
51
- labels: ['urgent'],
52
- }),
53
- ]
54
- : [];
55
- const mockResponse = { tasks: mockTasks, nextCursor: null };
56
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
57
- const result = await findTasksByDate.execute({ startDate: 'overdue', limit: 50, daysCount }, mockTodoistApi);
58
- expect(mockGetTasksByFilter).toHaveBeenCalledWith({
59
- client: mockTodoistApi,
60
- query: 'overdue',
61
- cursor: undefined,
62
- limit: 50,
63
- });
64
- // Verify result is a concise summary
65
- expect(extractTextContent(result)).toMatchSnapshot();
66
- // Verify structured content
67
- const structuredContent = extractStructuredContent(result);
68
- expect(structuredContent.tasks).toHaveLength(hasTasks ? 1 : 0);
69
- expect(structuredContent).toEqual(expect.objectContaining({
70
- totalCount: hasTasks ? 1 : 0,
71
- hasMore: false,
72
- nextCursor: null,
73
- appliedFilters: expect.objectContaining({
74
- startDate: 'overdue',
75
- }),
76
- }));
77
- });
78
- });
79
39
  describe('listing tasks by date range', () => {
80
- it('should get tasks for today when startDate is "today"', async () => {
40
+ it('should get tasks for today when startDate is "today" (includes overdue)', async () => {
81
41
  const mockTasks = [createMappedTask({ content: 'Today task', dueDate: '2025-08-15' })];
82
42
  const mockResponse = { tasks: mockTasks, nextCursor: null };
83
43
  mockGetTasksByFilter.mockResolvedValue(mockResponse);
84
44
  const result = await findTasksByDate.execute({ startDate: 'today', limit: 50, daysCount: 7 }, mockTodoistApi);
85
45
  expect(mockGetTasksByFilter).toHaveBeenCalledWith({
86
46
  client: mockTodoistApi,
87
- query: expect.stringContaining('due after:') && expect.stringContaining('due before:'),
47
+ query: 'today | overdue',
88
48
  cursor: undefined,
89
49
  limit: 50,
90
50
  });
@@ -217,29 +177,13 @@ describe(`${FIND_TASKS_BY_DATE} tool`, () => {
217
177
  expect(textContent).toMatchSnapshot();
218
178
  expect(textContent).toContain(`Use ${UPDATE_TASKS} to modify priorities or due dates`);
219
179
  });
220
- it('should suggest appropriate actions when startDate is overdue', async () => {
221
- const mockTasks = [
222
- createMappedTask({
223
- id: TEST_IDS.TASK_1,
224
- content: 'Overdue task',
225
- dueDate: '2025-08-10',
226
- }),
227
- ];
228
- const mockResponse = { tasks: mockTasks, nextCursor: null };
229
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
230
- const result = await findTasksByDate.execute({ startDate: 'overdue', limit: 10, daysCount: 1 }, mockTodoistApi);
231
- const textContent = extractTextContent(result);
232
- expect(textContent).toMatchSnapshot();
233
- expect(textContent).toContain(`Use ${UPDATE_TASKS} to modify priorities or due dates`);
234
- });
235
- it('should provide helpful suggestions for empty overdue results', async () => {
180
+ it('should provide helpful suggestions for empty today results', async () => {
236
181
  const mockResponse = { tasks: [], nextCursor: null };
237
182
  mockGetTasksByFilter.mockResolvedValue(mockResponse);
238
- const result = await findTasksByDate.execute({ startDate: 'overdue', limit: 10, daysCount: 1 }, mockTodoistApi);
183
+ const result = await findTasksByDate.execute({ startDate: 'today', limit: 10, daysCount: 1 }, mockTodoistApi);
239
184
  const textContent = extractTextContent(result);
240
185
  expect(textContent).toMatchSnapshot();
241
- expect(textContent).toContain('Great job! No overdue tasks');
242
- expect(textContent).toContain("Check today's tasks with startDate='today'");
186
+ expect(textContent).toContain('Great job! No tasks for today or overdue');
243
187
  });
244
188
  it('should provide helpful suggestions for empty date range results', async () => {
245
189
  const mockResponse = { tasks: [], nextCursor: null };
@@ -252,7 +196,7 @@ describe(`${FIND_TASKS_BY_DATE} tool`, () => {
252
196
  const textContent = extractTextContent(result);
253
197
  expect(textContent).toMatchSnapshot();
254
198
  expect(textContent).toContain("Expand date range with larger 'daysCount'");
255
- expect(textContent).toContain("Check 'overdue' for past-due items");
199
+ expect(textContent).toContain("Check today's tasks with startDate='today'");
256
200
  });
257
201
  });
258
202
  describe('label filtering', () => {
@@ -265,18 +209,18 @@ describe(`${FIND_TASKS_BY_DATE} tool`, () => {
265
209
  limit: 50,
266
210
  labels: ['work'],
267
211
  },
268
- expectedQueryPattern: '((@work))', // Will be combined with date query
212
+ expectedQueryPattern: 'today | overdue & ((@work))', // Will be combined with date query
269
213
  },
270
214
  {
271
215
  name: 'multiple labels with AND operator',
272
216
  params: {
273
- startDate: 'overdue',
217
+ startDate: 'today',
274
218
  daysCount: 1,
275
219
  limit: 50,
276
220
  labels: ['work', 'urgent'],
277
221
  labelsOperator: 'and',
278
222
  },
279
- expectedQueryPattern: 'overdue & ((@work & @urgent))',
223
+ expectedQueryPattern: 'today | overdue & ((@work & @urgent))',
280
224
  },
281
225
  {
282
226
  name: 'multiple labels with OR operator',
@@ -307,8 +251,8 @@ describe(`${FIND_TASKS_BY_DATE} tool`, () => {
307
251
  cursor: undefined,
308
252
  limit: 50,
309
253
  });
310
- // For overdue specifically, check the exact pattern
311
- if (params.startDate === 'overdue') {
254
+ // For today specifically, check the exact pattern
255
+ if (params.startDate === 'today') {
312
256
  expect(mockGetTasksByFilter).toHaveBeenCalledWith({
313
257
  client: mockTodoistApi,
314
258
  query: expectedQueryPattern,
@@ -374,7 +318,7 @@ describe(`${FIND_TASKS_BY_DATE} tool`, () => {
374
318
  },
375
319
  {
376
320
  error: TEST_ERRORS.API_RATE_LIMIT,
377
- params: { startDate: 'overdue', limit: 50, daysCount: 7 },
321
+ params: { startDate: 'today', limit: 50, daysCount: 7 },
378
322
  },
379
323
  {
380
324
  error: TEST_ERRORS.INVALID_CURSOR,
@@ -45,7 +45,7 @@ const completeTasks = {
45
45
  function generateNextSteps(completed, failures) {
46
46
  if (completed > 0) {
47
47
  const moveResult = failures === 0
48
- ? "Use find-tasks-by-date('overdue') to tackle remaining overdue items."
48
+ ? "Use find-tasks-by-date('today') to tackle remaining overdue items."
49
49
  : 'Review failed completions and retry if needed.';
50
50
  return [moveResult];
51
51
  }
@@ -1 +1 @@
1
- {"version":3,"file":"find-tasks-by-date.d.ts","sourceRoot":"","sources":["../../src/tools/find-tasks-by-date.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AA8CvB,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoDqB,CAAA;AAyE1C,OAAO,EAAE,eAAe,EAAE,CAAA"}
1
+ {"version":3,"file":"find-tasks-by-date.d.ts","sourceRoot":"","sources":["../../src/tools/find-tasks-by-date.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AA0CvB,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiDqB,CAAA;AAoE1C,OAAO,EAAE,eAAe,EAAE,CAAA"}
@@ -9,15 +9,15 @@ import { ToolNames } from '../utils/tool-names.js';
9
9
  const ArgsSchema = {
10
10
  startDate: z
11
11
  .string()
12
- .regex(/^(\d{4}-\d{2}-\d{2}|today|overdue)$/)
13
- .describe("The start date to get the tasks for. Format: YYYY-MM-DD, 'today', or 'overdue'."),
12
+ .regex(/^(\d{4}-\d{2}-\d{2}|today)$/)
13
+ .describe("The start date to get the tasks for. Format: YYYY-MM-DD or 'today'."),
14
14
  daysCount: z
15
15
  .number()
16
16
  .int()
17
17
  .min(1)
18
18
  .max(30)
19
19
  .default(1)
20
- .describe("The number of days to get the tasks for, starting from the start date. Ignored when startDate is 'overdue'."),
20
+ .describe('The number of days to get the tasks for, starting from the start date.'),
21
21
  limit: z
22
22
  .number()
23
23
  .int()
@@ -33,17 +33,15 @@ const ArgsSchema = {
33
33
  };
34
34
  const findTasksByDate = {
35
35
  name: ToolNames.FIND_TASKS_BY_DATE,
36
- description: "Get tasks by date range or overdue tasks. Use startDate 'overdue' for overdue tasks, or provide a date/date range.",
36
+ description: "Get tasks by date range. Use startDate 'today' to get today's tasks including overdue items, or provide a specific date/date range.",
37
37
  parameters: ArgsSchema,
38
38
  async execute(args, client) {
39
39
  let query = '';
40
- if (args.startDate === 'overdue') {
41
- query = 'overdue';
40
+ if (args.startDate === 'today') {
41
+ query = 'today | overdue';
42
42
  }
43
43
  else {
44
- const startDate = args.startDate === 'today'
45
- ? formatISO(new Date(), { representation: 'date' })
46
- : args.startDate;
44
+ const startDate = args.startDate;
47
45
  const endDate = addDays(startDate, args.daysCount + 1);
48
46
  const endDateStr = formatISO(endDate, { representation: 'date' });
49
47
  query = `(due after: ${startDate} | due: ${startDate}) & due before: ${endDateStr}`;
@@ -82,11 +80,8 @@ const findTasksByDate = {
82
80
  function generateTextContent({ tasks, args, nextCursor, }) {
83
81
  // Generate filter description
84
82
  const filterHints = [];
85
- if (args.startDate === 'overdue') {
86
- filterHints.push('overdue tasks only');
87
- }
88
- else if (args.startDate === 'today') {
89
- filterHints.push(`today${args.daysCount > 1 ? ` + ${args.daysCount - 1} more days` : ''}`);
83
+ if (args.startDate === 'today') {
84
+ filterHints.push(`today + overdue tasks${args.daysCount > 1 ? ` + ${args.daysCount - 1} more days` : ''}`);
90
85
  }
91
86
  else {
92
87
  filterHints.push(`${args.startDate}${args.daysCount > 1 ? ` to ${getDateString(addDays(args.startDate, args.daysCount))}` : ''}`);
@@ -99,21 +94,16 @@ function generateTextContent({ tasks, args, nextCursor, }) {
99
94
  filterHints.push(`labels: ${labelText}`);
100
95
  }
101
96
  // Generate subject description
102
- const subject = args.startDate === 'overdue'
103
- ? 'Overdue tasks'
104
- : args.startDate === 'today'
105
- ? `Today's tasks`
106
- : `Tasks for ${args.startDate}`;
97
+ const subject = args.startDate === 'today' ? `Today's tasks + overdue` : `Tasks for ${args.startDate}`;
107
98
  // Generate helpful suggestions for empty results
108
99
  const zeroReasonHints = [];
109
100
  if (tasks.length === 0) {
110
- if (args.startDate === 'overdue') {
111
- zeroReasonHints.push('Great job! No overdue tasks');
112
- zeroReasonHints.push("Check today's tasks with startDate='today'");
101
+ if (args.startDate === 'today') {
102
+ zeroReasonHints.push('Great job! No tasks for today or overdue');
113
103
  }
114
104
  else {
115
105
  zeroReasonHints.push("Expand date range with larger 'daysCount'");
116
- zeroReasonHints.push("Check 'overdue' for past-due items");
106
+ zeroReasonHints.push("Check today's tasks with startDate='today'");
117
107
  }
118
108
  }
119
109
  // Generate contextual next steps
@@ -121,7 +111,7 @@ function generateTextContent({ tasks, args, nextCursor, }) {
121
111
  const todayStr = getDateString(now);
122
112
  const nextSteps = generateTaskNextSteps('listed', tasks, {
123
113
  hasToday: args.startDate === 'today' || tasks.some((task) => task.dueDate === todayStr),
124
- hasOverdue: args.startDate === 'overdue' ||
114
+ hasOverdue: args.startDate === 'today' ||
125
115
  tasks.some((task) => task.dueDate && new Date(task.dueDate) < now),
126
116
  });
127
117
  return summarizeList({
@@ -148,7 +148,7 @@ export function generateTaskNextSteps(operation, tasks, context) {
148
148
  nextSteps.push(`Use ${FIND_TASKS_BY_DATE}('today') to review today's updated schedule`);
149
149
  }
150
150
  else if (context?.hasOverdue) {
151
- nextSteps.push(`Use ${FIND_TASKS_BY_DATE}('overdue') to prioritize past-due items`);
151
+ nextSteps.push(`Use ${FIND_TASKS_BY_DATE}('today') to prioritize past-due items`);
152
152
  }
153
153
  else if (context?.projectName) {
154
154
  nextSteps.push(`Use ${GET_OVERVIEW} with projectId to see ${context.projectName} structure`);
@@ -178,7 +178,7 @@ export function generateTaskNextSteps(operation, tasks, context) {
178
178
  nextSteps.push(`Use ${FIND_TASKS_BY_DATE}('tomorrow') to plan upcoming work`);
179
179
  }
180
180
  else if (context?.hasOverdue) {
181
- nextSteps.push(`Use ${FIND_TASKS_BY_DATE}('overdue') to tackle remaining past-due items`);
181
+ nextSteps.push(`Use ${FIND_TASKS_BY_DATE}('today') to tackle remaining past-due items`);
182
182
  }
183
183
  else {
184
184
  nextSteps.push(`Use ${FIND_TASKS_BY_DATE}('today') to see remaining work`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doist/todoist-ai",
3
- "version": "4.7.1",
3
+ "version": "4.8.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",