@doist/todoist-ai 2.2.2 → 3.0.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 (168) hide show
  1. package/README.md +11 -3
  2. package/dist/index.d.ts +496 -255
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +41 -29
  5. package/dist/mcp-helpers.d.ts +25 -3
  6. package/dist/mcp-helpers.d.ts.map +1 -1
  7. package/dist/mcp-helpers.js +37 -19
  8. package/dist/mcp-server.d.ts.map +1 -1
  9. package/dist/mcp-server.js +32 -28
  10. package/dist/tools/__tests__/add-tasks.test.d.ts +2 -0
  11. package/dist/tools/__tests__/add-tasks.test.d.ts.map +1 -0
  12. package/dist/tools/__tests__/{tasks-add-multiple.test.js → add-tasks.test.js} +85 -81
  13. package/dist/tools/__tests__/complete-tasks.test.d.ts +2 -0
  14. package/dist/tools/__tests__/complete-tasks.test.d.ts.map +1 -0
  15. package/dist/tools/__tests__/complete-tasks.test.js +206 -0
  16. package/dist/tools/__tests__/delete-object.test.d.ts +2 -0
  17. package/dist/tools/__tests__/delete-object.test.d.ts.map +1 -0
  18. package/dist/tools/__tests__/{delete-one.test.js → delete-object.test.js} +42 -22
  19. package/dist/tools/__tests__/find-completed-tasks.test.d.ts +2 -0
  20. package/dist/tools/__tests__/find-completed-tasks.test.d.ts.map +1 -0
  21. package/dist/tools/__tests__/{tasks-list-completed.test.js → find-completed-tasks.test.js} +13 -36
  22. package/dist/tools/__tests__/find-projects.test.d.ts +2 -0
  23. package/dist/tools/__tests__/find-projects.test.d.ts.map +1 -0
  24. package/dist/tools/__tests__/{projects-list.test.js → find-projects.test.js} +55 -39
  25. package/dist/tools/__tests__/find-sections.test.d.ts +2 -0
  26. package/dist/tools/__tests__/find-sections.test.d.ts.map +1 -0
  27. package/dist/tools/__tests__/{sections-search.test.js → find-sections.test.js} +64 -50
  28. package/dist/tools/__tests__/find-tasks-by-date.test.d.ts +2 -0
  29. package/dist/tools/__tests__/find-tasks-by-date.test.d.ts.map +1 -0
  30. package/dist/tools/__tests__/{tasks-list-by-date.test.js → find-tasks-by-date.test.js} +96 -14
  31. package/dist/tools/__tests__/find-tasks.test.d.ts +2 -0
  32. package/dist/tools/__tests__/find-tasks.test.d.ts.map +1 -0
  33. package/dist/tools/__tests__/find-tasks.test.js +334 -0
  34. package/dist/tools/__tests__/get-overview.test.d.ts +2 -0
  35. package/dist/tools/__tests__/get-overview.test.d.ts.map +1 -0
  36. package/dist/tools/__tests__/{overview.test.js → get-overview.test.js} +77 -13
  37. package/dist/tools/__tests__/manage-projects.test.d.ts +2 -0
  38. package/dist/tools/__tests__/manage-projects.test.d.ts.map +1 -0
  39. package/dist/tools/__tests__/{projects-manage.test.js → manage-projects.test.js} +33 -30
  40. package/dist/tools/__tests__/manage-sections.test.d.ts +2 -0
  41. package/dist/tools/__tests__/manage-sections.test.d.ts.map +1 -0
  42. package/dist/tools/__tests__/manage-sections.test.js +162 -0
  43. package/dist/tools/__tests__/update-tasks.test.d.ts +2 -0
  44. package/dist/tools/__tests__/update-tasks.test.d.ts.map +1 -0
  45. package/dist/tools/__tests__/update-tasks.test.js +645 -0
  46. package/dist/tools/{tasks-add-multiple.d.ts → add-tasks.d.ts} +36 -16
  47. package/dist/tools/add-tasks.d.ts.map +1 -0
  48. package/dist/tools/{tasks-add-multiple.js → add-tasks.js} +39 -4
  49. package/dist/tools/complete-tasks.d.ts +40 -0
  50. package/dist/tools/complete-tasks.d.ts.map +1 -0
  51. package/dist/tools/complete-tasks.js +68 -0
  52. package/dist/tools/delete-object.d.ts +38 -0
  53. package/dist/tools/delete-object.d.ts.map +1 -0
  54. package/dist/tools/delete-object.js +69 -0
  55. package/dist/tools/find-completed-tasks.d.ts +74 -0
  56. package/dist/tools/find-completed-tasks.d.ts.map +1 -0
  57. package/dist/tools/find-completed-tasks.js +112 -0
  58. package/dist/tools/find-projects.d.ts +53 -0
  59. package/dist/tools/find-projects.d.ts.map +1 -0
  60. package/dist/tools/find-projects.js +101 -0
  61. package/dist/tools/find-sections.d.ts +42 -0
  62. package/dist/tools/find-sections.d.ts.map +1 -0
  63. package/dist/tools/find-sections.js +96 -0
  64. package/dist/tools/find-tasks-by-date.d.ts +59 -0
  65. package/dist/tools/find-tasks-by-date.d.ts.map +1 -0
  66. package/dist/tools/find-tasks-by-date.js +121 -0
  67. package/dist/tools/find-tasks.d.ts +65 -0
  68. package/dist/tools/find-tasks.d.ts.map +1 -0
  69. package/dist/tools/find-tasks.js +182 -0
  70. package/dist/tools/get-overview.d.ts +67 -0
  71. package/dist/tools/get-overview.d.ts.map +1 -0
  72. package/dist/tools/{overview.js → get-overview.js} +66 -19
  73. package/dist/tools/manage-projects.d.ts +35 -0
  74. package/dist/tools/manage-projects.d.ts.map +1 -0
  75. package/dist/tools/manage-projects.js +63 -0
  76. package/dist/tools/manage-sections.d.ts +38 -0
  77. package/dist/tools/manage-sections.d.ts.map +1 -0
  78. package/dist/tools/manage-sections.js +78 -0
  79. package/dist/tools/update-tasks.d.ts +94 -0
  80. package/dist/tools/update-tasks.d.ts.map +1 -0
  81. package/dist/tools/update-tasks.js +120 -0
  82. package/dist/utils/constants.d.ts +35 -0
  83. package/dist/utils/constants.d.ts.map +1 -0
  84. package/dist/utils/constants.js +37 -0
  85. package/dist/utils/response-builders.d.ts +88 -0
  86. package/dist/utils/response-builders.d.ts.map +1 -0
  87. package/dist/utils/response-builders.js +202 -0
  88. package/dist/{tools → utils}/test-helpers.d.ts +16 -0
  89. package/dist/utils/test-helpers.d.ts.map +1 -0
  90. package/dist/{tools → utils}/test-helpers.js +51 -0
  91. package/dist/utils/tool-names.d.ts +23 -0
  92. package/dist/utils/tool-names.d.ts.map +1 -0
  93. package/dist/utils/tool-names.js +25 -0
  94. package/package.json +1 -1
  95. package/dist/tools/__tests__/delete-one.test.d.ts +0 -2
  96. package/dist/tools/__tests__/delete-one.test.d.ts.map +0 -1
  97. package/dist/tools/__tests__/overview.test.d.ts +0 -2
  98. package/dist/tools/__tests__/overview.test.d.ts.map +0 -1
  99. package/dist/tools/__tests__/projects-list.test.d.ts +0 -2
  100. package/dist/tools/__tests__/projects-list.test.d.ts.map +0 -1
  101. package/dist/tools/__tests__/projects-manage.test.d.ts +0 -2
  102. package/dist/tools/__tests__/projects-manage.test.d.ts.map +0 -1
  103. package/dist/tools/__tests__/sections-manage.test.d.ts +0 -2
  104. package/dist/tools/__tests__/sections-manage.test.d.ts.map +0 -1
  105. package/dist/tools/__tests__/sections-manage.test.js +0 -138
  106. package/dist/tools/__tests__/sections-search.test.d.ts +0 -2
  107. package/dist/tools/__tests__/sections-search.test.d.ts.map +0 -1
  108. package/dist/tools/__tests__/tasks-add-multiple.test.d.ts +0 -2
  109. package/dist/tools/__tests__/tasks-add-multiple.test.d.ts.map +0 -1
  110. package/dist/tools/__tests__/tasks-complete-multiple.test.d.ts +0 -2
  111. package/dist/tools/__tests__/tasks-complete-multiple.test.d.ts.map +0 -1
  112. package/dist/tools/__tests__/tasks-complete-multiple.test.js +0 -146
  113. package/dist/tools/__tests__/tasks-list-by-date.test.d.ts +0 -2
  114. package/dist/tools/__tests__/tasks-list-by-date.test.d.ts.map +0 -1
  115. package/dist/tools/__tests__/tasks-list-completed.test.d.ts +0 -2
  116. package/dist/tools/__tests__/tasks-list-completed.test.d.ts.map +0 -1
  117. package/dist/tools/__tests__/tasks-list-for-container.test.d.ts +0 -2
  118. package/dist/tools/__tests__/tasks-list-for-container.test.d.ts.map +0 -1
  119. package/dist/tools/__tests__/tasks-list-for-container.test.js +0 -232
  120. package/dist/tools/__tests__/tasks-organize-multiple.test.d.ts +0 -2
  121. package/dist/tools/__tests__/tasks-organize-multiple.test.d.ts.map +0 -1
  122. package/dist/tools/__tests__/tasks-organize-multiple.test.js +0 -245
  123. package/dist/tools/__tests__/tasks-search.test.d.ts +0 -2
  124. package/dist/tools/__tests__/tasks-search.test.d.ts.map +0 -1
  125. package/dist/tools/__tests__/tasks-search.test.js +0 -106
  126. package/dist/tools/__tests__/tasks-update-one.test.d.ts +0 -2
  127. package/dist/tools/__tests__/tasks-update-one.test.d.ts.map +0 -1
  128. package/dist/tools/__tests__/tasks-update-one.test.js +0 -251
  129. package/dist/tools/delete-one.d.ts +0 -17
  130. package/dist/tools/delete-one.d.ts.map +0 -1
  131. package/dist/tools/delete-one.js +0 -25
  132. package/dist/tools/overview.d.ts +0 -14
  133. package/dist/tools/overview.d.ts.map +0 -1
  134. package/dist/tools/projects-list.d.ts +0 -29
  135. package/dist/tools/projects-list.d.ts.map +0 -1
  136. package/dist/tools/projects-list.js +0 -39
  137. package/dist/tools/projects-manage.d.ts +0 -24
  138. package/dist/tools/projects-manage.d.ts.map +0 -1
  139. package/dist/tools/projects-manage.js +0 -26
  140. package/dist/tools/sections-manage.d.ts +0 -23
  141. package/dist/tools/sections-manage.d.ts.map +0 -1
  142. package/dist/tools/sections-manage.js +0 -37
  143. package/dist/tools/sections-search.d.ts +0 -18
  144. package/dist/tools/sections-search.d.ts.map +0 -1
  145. package/dist/tools/sections-search.js +0 -27
  146. package/dist/tools/tasks-add-multiple.d.ts.map +0 -1
  147. package/dist/tools/tasks-complete-multiple.d.ts +0 -16
  148. package/dist/tools/tasks-complete-multiple.d.ts.map +0 -1
  149. package/dist/tools/tasks-complete-multiple.js +0 -23
  150. package/dist/tools/tasks-list-by-date.d.ts +0 -34
  151. package/dist/tools/tasks-list-by-date.d.ts.map +0 -1
  152. package/dist/tools/tasks-list-by-date.js +0 -53
  153. package/dist/tools/tasks-list-completed.d.ts +0 -44
  154. package/dist/tools/tasks-list-completed.d.ts.map +0 -1
  155. package/dist/tools/tasks-list-completed.js +0 -49
  156. package/dist/tools/tasks-list-for-container.d.ts +0 -34
  157. package/dist/tools/tasks-list-for-container.d.ts.map +0 -1
  158. package/dist/tools/tasks-list-for-container.js +0 -48
  159. package/dist/tools/tasks-organize-multiple.d.ts +0 -37
  160. package/dist/tools/tasks-organize-multiple.d.ts.map +0 -1
  161. package/dist/tools/tasks-organize-multiple.js +0 -34
  162. package/dist/tools/tasks-search.d.ts +0 -32
  163. package/dist/tools/tasks-search.d.ts.map +0 -1
  164. package/dist/tools/tasks-search.js +0 -30
  165. package/dist/tools/tasks-update-one.d.ts +0 -29
  166. package/dist/tools/tasks-update-one.d.ts.map +0 -1
  167. package/dist/tools/tasks-update-one.js +0 -63
  168. package/dist/tools/test-helpers.d.ts.map +0 -1
@@ -0,0 +1,101 @@
1
+ import { z } from 'zod';
2
+ import { getToolOutput } from '../mcp-helpers.js';
3
+ import { mapProject } from '../tool-helpers.js';
4
+ import { ApiLimits } from '../utils/constants.js';
5
+ import { formatProjectPreview, summarizeList } from '../utils/response-builders.js';
6
+ import { ToolNames } from '../utils/tool-names.js';
7
+ const { MANAGE_PROJECTS, FIND_TASKS } = ToolNames;
8
+ const ArgsSchema = {
9
+ search: z
10
+ .string()
11
+ .optional()
12
+ .describe('Search for a project by name (partial and case insensitive match). If omitted, all projects are returned.'),
13
+ limit: z
14
+ .number()
15
+ .int()
16
+ .min(1)
17
+ .max(ApiLimits.PROJECTS_MAX)
18
+ .default(ApiLimits.PROJECTS_DEFAULT)
19
+ .describe('The maximum number of projects to return.'),
20
+ cursor: z
21
+ .string()
22
+ .optional()
23
+ .describe('The cursor to get the next page of projects (cursor is obtained from the previous call to this tool, with the same parameters).'),
24
+ };
25
+ const findProjects = {
26
+ name: ToolNames.FIND_PROJECTS,
27
+ description: 'List all projects or search for projects by name. If search parameter is omitted, all projects are returned.',
28
+ parameters: ArgsSchema,
29
+ async execute(args, client) {
30
+ const { results, nextCursor } = await client.getProjects({
31
+ limit: args.limit,
32
+ cursor: args.cursor ?? null,
33
+ });
34
+ const searchLower = args.search ? args.search.toLowerCase() : undefined;
35
+ const filtered = searchLower
36
+ ? results.filter((project) => project.name.toLowerCase().includes(searchLower))
37
+ : results;
38
+ const projects = filtered.map(mapProject);
39
+ return getToolOutput({
40
+ textContent: generateTextContent({
41
+ projects,
42
+ args,
43
+ nextCursor,
44
+ }),
45
+ structuredContent: {
46
+ projects,
47
+ nextCursor,
48
+ totalCount: projects.length,
49
+ hasMore: Boolean(nextCursor),
50
+ appliedFilters: args,
51
+ },
52
+ });
53
+ },
54
+ };
55
+ function generateTextContent({ projects, args, nextCursor, }) {
56
+ // Generate subject description
57
+ const subject = args.search ? `Projects matching "${args.search}"` : 'Projects';
58
+ // Generate filter hints
59
+ const filterHints = [];
60
+ if (args.search) {
61
+ filterHints.push(`search: "${args.search}"`);
62
+ }
63
+ // Generate project preview lines
64
+ const previewLimit = 10;
65
+ const previewProjects = projects.slice(0, previewLimit);
66
+ const previewLines = previewProjects.map(formatProjectPreview).join('\n');
67
+ const remainingCount = projects.length - previewLimit;
68
+ const previewWithMore = remainingCount > 0 ? `${previewLines}\n …and ${remainingCount} more` : previewLines;
69
+ // Generate helpful suggestions for empty results
70
+ const zeroReasonHints = [];
71
+ if (projects.length === 0) {
72
+ if (args.search) {
73
+ zeroReasonHints.push('Try broader search terms');
74
+ zeroReasonHints.push('Check spelling');
75
+ zeroReasonHints.push('Remove search to see all projects');
76
+ }
77
+ else {
78
+ zeroReasonHints.push('No projects created yet');
79
+ zeroReasonHints.push(`Use ${MANAGE_PROJECTS} to create a project`);
80
+ }
81
+ }
82
+ // Generate contextual next steps
83
+ const nextSteps = [];
84
+ if (projects.length > 0) {
85
+ nextSteps.push(`Use ${FIND_TASKS} with projectId to see tasks in specific projects.`);
86
+ if (projects.some((p) => p.isFavorite)) {
87
+ nextSteps.push('Favorite projects appear first in most Todoist views.');
88
+ }
89
+ }
90
+ return summarizeList({
91
+ subject,
92
+ count: projects.length,
93
+ limit: args.limit,
94
+ nextCursor: nextCursor ?? undefined,
95
+ filterHints,
96
+ previewLines: previewWithMore,
97
+ zeroReasonHints,
98
+ nextSteps,
99
+ });
100
+ }
101
+ export { findProjects };
@@ -0,0 +1,42 @@
1
+ import { z } from 'zod';
2
+ declare const findSections: {
3
+ name: "find-sections";
4
+ description: string;
5
+ parameters: {
6
+ projectId: z.ZodString;
7
+ search: z.ZodOptional<z.ZodString>;
8
+ };
9
+ execute(args: {
10
+ projectId: string;
11
+ search?: string | undefined;
12
+ }, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
13
+ content: {
14
+ type: "text";
15
+ text: string;
16
+ }[];
17
+ structuredContent: {
18
+ sections: {
19
+ id: string;
20
+ name: string;
21
+ }[];
22
+ totalCount: number;
23
+ appliedFilters: {
24
+ projectId: string;
25
+ search?: string | undefined;
26
+ };
27
+ };
28
+ } | {
29
+ content: ({
30
+ type: "text";
31
+ text: string;
32
+ mimeType?: undefined;
33
+ } | {
34
+ type: "text";
35
+ mimeType: string;
36
+ text: string;
37
+ })[];
38
+ structuredContent?: undefined;
39
+ }>;
40
+ };
41
+ export { findSections };
42
+ //# sourceMappingURL=find-sections.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-sections.d.ts","sourceRoot":"","sources":["../../src/tools/find-sections.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAuBvB,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCwB,CAAA;AAqE1C,OAAO,EAAE,YAAY,EAAE,CAAA"}
@@ -0,0 +1,96 @@
1
+ import { z } from 'zod';
2
+ import { getToolOutput } from '../mcp-helpers.js';
3
+ import { summarizeList } from '../utils/response-builders.js';
4
+ import { ToolNames } from '../utils/tool-names.js';
5
+ const { MANAGE_SECTIONS, FIND_TASKS, UPDATE_TASKS, DELETE_OBJECT } = ToolNames;
6
+ const ArgsSchema = {
7
+ projectId: z.string().min(1).describe('The ID of the project to search sections in.'),
8
+ search: z
9
+ .string()
10
+ .optional()
11
+ .describe('Search for a section by name (partial and case insensitive match). If omitted, all sections in the project are returned.'),
12
+ };
13
+ const findSections = {
14
+ name: ToolNames.FIND_SECTIONS,
15
+ description: 'Search for sections by name or other criteria in a project.',
16
+ parameters: ArgsSchema,
17
+ async execute(args, client) {
18
+ const { results } = await client.getSections({
19
+ projectId: args.projectId,
20
+ });
21
+ const searchLower = args.search ? args.search.toLowerCase() : undefined;
22
+ const filtered = searchLower
23
+ ? results.filter((section) => section.name.toLowerCase().includes(searchLower))
24
+ : results;
25
+ const sections = filtered.map((section) => ({
26
+ id: section.id,
27
+ name: section.name,
28
+ }));
29
+ const textContent = generateTextContent({
30
+ sections,
31
+ projectId: args.projectId,
32
+ search: args.search,
33
+ });
34
+ return getToolOutput({
35
+ textContent,
36
+ structuredContent: {
37
+ sections,
38
+ totalCount: sections.length,
39
+ appliedFilters: args,
40
+ },
41
+ });
42
+ },
43
+ };
44
+ function generateTextContent({ sections, projectId, search, }) {
45
+ const zeroReasonHints = [];
46
+ if (search) {
47
+ zeroReasonHints.push('Try broader search terms');
48
+ zeroReasonHints.push('Check spelling');
49
+ zeroReasonHints.push('Remove search to see all sections');
50
+ }
51
+ else {
52
+ zeroReasonHints.push('Project has no sections yet');
53
+ zeroReasonHints.push(`Use ${MANAGE_SECTIONS} to create sections`);
54
+ }
55
+ // Data-driven next steps based on results
56
+ const nextSteps = [];
57
+ if (sections.length > 0) {
58
+ // Suggestions based on number of sections found
59
+ if (sections.length === 1) {
60
+ const sectionId = sections[0]?.id;
61
+ nextSteps.push(`Use ${FIND_TASKS} with sectionId=${sectionId} to see tasks`);
62
+ nextSteps.push(`Use ${MANAGE_SECTIONS} to create additional sections for organization`);
63
+ }
64
+ else if (sections.length > 8) {
65
+ nextSteps.push('Consider consolidating sections - many small sections can reduce productivity');
66
+ nextSteps.push(`Use ${UPDATE_TASKS} to move tasks between sections`);
67
+ nextSteps.push(`Use ${DELETE_OBJECT} with type=section to delete empty sections`);
68
+ }
69
+ else {
70
+ nextSteps.push(`Use ${FIND_TASKS} with sectionId to see tasks in specific sections`);
71
+ nextSteps.push(`Use ${MANAGE_SECTIONS} to modify section names or order`);
72
+ }
73
+ // Search-specific suggestions
74
+ if (search) {
75
+ nextSteps.push('Remove search parameter to see all sections in this project');
76
+ }
77
+ }
78
+ else {
79
+ // Empty result suggestions are already handled in zeroReasonHints
80
+ // No additional nextSteps needed for empty results
81
+ }
82
+ const subject = search
83
+ ? `Sections in project ${projectId} matching "${search}"`
84
+ : `Sections in project ${projectId}`;
85
+ const previewLines = sections.length > 0
86
+ ? sections.map((section) => ` ${section.name} • id=${section.id}`).join('\n')
87
+ : undefined;
88
+ return summarizeList({
89
+ subject,
90
+ count: sections.length,
91
+ previewLines,
92
+ zeroReasonHints,
93
+ nextSteps,
94
+ });
95
+ }
96
+ export { findSections };
@@ -0,0 +1,59 @@
1
+ import { z } from 'zod';
2
+ declare const findTasksByDate: {
3
+ name: "find-tasks-by-date";
4
+ description: string;
5
+ parameters: {
6
+ startDate: z.ZodString;
7
+ daysCount: z.ZodDefault<z.ZodNumber>;
8
+ limit: z.ZodDefault<z.ZodNumber>;
9
+ cursor: z.ZodOptional<z.ZodString>;
10
+ };
11
+ execute(args: {
12
+ limit: number;
13
+ startDate: string;
14
+ daysCount: number;
15
+ cursor?: string | undefined;
16
+ }, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
17
+ content: {
18
+ type: "text";
19
+ text: string;
20
+ }[];
21
+ structuredContent: {
22
+ tasks: {
23
+ id: string;
24
+ content: string;
25
+ description: string;
26
+ dueDate: string | undefined;
27
+ recurring: string | boolean;
28
+ priority: number;
29
+ projectId: string;
30
+ sectionId: string | null;
31
+ parentId: string | null;
32
+ labels: string[];
33
+ duration: string | null;
34
+ }[];
35
+ nextCursor: string | null;
36
+ totalCount: number;
37
+ hasMore: boolean;
38
+ appliedFilters: {
39
+ limit: number;
40
+ startDate: string;
41
+ daysCount: number;
42
+ cursor?: string | undefined;
43
+ };
44
+ };
45
+ } | {
46
+ content: ({
47
+ type: "text";
48
+ text: string;
49
+ mimeType?: undefined;
50
+ } | {
51
+ type: "text";
52
+ mimeType: string;
53
+ text: string;
54
+ })[];
55
+ structuredContent?: undefined;
56
+ }>;
57
+ };
58
+ export { findTasksByDate };
59
+ //# sourceMappingURL=find-tasks-by-date.d.ts.map
@@ -0,0 +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;AA4CvB,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4CqB,CAAA;AAiE1C,OAAO,EAAE,eAAe,EAAE,CAAA"}
@@ -0,0 +1,121 @@
1
+ import { addDays, formatISO } from 'date-fns';
2
+ import { z } from 'zod';
3
+ import { getToolOutput } from '../mcp-helpers.js';
4
+ import { getTasksByFilter } from '../tool-helpers.js';
5
+ import { ApiLimits } from '../utils/constants.js';
6
+ import { generateTaskNextSteps, getDateString, previewTasks, summarizeList, } from '../utils/response-builders.js';
7
+ import { ToolNames } from '../utils/tool-names.js';
8
+ const ArgsSchema = {
9
+ startDate: z
10
+ .string()
11
+ .regex(/^(\d{4}-\d{2}-\d{2}|today|overdue)$/)
12
+ .describe("The start date to get the tasks for. Format: YYYY-MM-DD, 'today', or 'overdue'."),
13
+ daysCount: z
14
+ .number()
15
+ .int()
16
+ .min(1)
17
+ .max(30)
18
+ .default(1)
19
+ .describe("The number of days to get the tasks for, starting from the start date. Ignored when startDate is 'overdue'."),
20
+ limit: z
21
+ .number()
22
+ .int()
23
+ .min(1)
24
+ .max(ApiLimits.TASKS_MAX)
25
+ .default(ApiLimits.TASKS_DEFAULT)
26
+ .describe('The maximum number of tasks to return.'),
27
+ cursor: z
28
+ .string()
29
+ .optional()
30
+ .describe('The cursor to get the next page of tasks (cursor is obtained from the previous call to this tool, with the same parameters).'),
31
+ };
32
+ const findTasksByDate = {
33
+ name: ToolNames.FIND_TASKS_BY_DATE,
34
+ description: "Get tasks by date range or overdue tasks. Use startDate 'overdue' for overdue tasks, or provide a date/date range.",
35
+ parameters: ArgsSchema,
36
+ async execute(args, client) {
37
+ let query;
38
+ if (args.startDate === 'overdue') {
39
+ query = 'overdue';
40
+ }
41
+ else {
42
+ const startDate = args.startDate === 'today'
43
+ ? formatISO(new Date(), { representation: 'date' })
44
+ : args.startDate;
45
+ const endDate = addDays(startDate, args.daysCount + 1);
46
+ const endDateStr = formatISO(endDate, { representation: 'date' });
47
+ query = `(due after: ${startDate} | due: ${startDate}) & due before: ${endDateStr}`;
48
+ }
49
+ const result = await getTasksByFilter({
50
+ client,
51
+ query,
52
+ cursor: args.cursor,
53
+ limit: args.limit,
54
+ });
55
+ const textContent = generateTextContent({
56
+ tasks: result.tasks,
57
+ args,
58
+ nextCursor: result.nextCursor,
59
+ });
60
+ return getToolOutput({
61
+ textContent,
62
+ structuredContent: {
63
+ tasks: result.tasks,
64
+ nextCursor: result.nextCursor,
65
+ totalCount: result.tasks.length,
66
+ hasMore: Boolean(result.nextCursor),
67
+ appliedFilters: args,
68
+ },
69
+ });
70
+ },
71
+ };
72
+ function generateTextContent({ tasks, args, nextCursor, }) {
73
+ // Generate filter description
74
+ const filterHints = [];
75
+ if (args.startDate === 'overdue') {
76
+ filterHints.push('overdue tasks only');
77
+ }
78
+ else if (args.startDate === 'today') {
79
+ filterHints.push(`today${args.daysCount > 1 ? ` + ${args.daysCount - 1} more days` : ''}`);
80
+ }
81
+ else {
82
+ filterHints.push(`${args.startDate}${args.daysCount > 1 ? ` to ${getDateString(addDays(args.startDate, args.daysCount))}` : ''}`);
83
+ }
84
+ // Generate subject description
85
+ const subject = args.startDate === 'overdue'
86
+ ? 'Overdue tasks'
87
+ : args.startDate === 'today'
88
+ ? `Today's tasks`
89
+ : `Tasks for ${args.startDate}`;
90
+ // Generate helpful suggestions for empty results
91
+ const zeroReasonHints = [];
92
+ if (tasks.length === 0) {
93
+ if (args.startDate === 'overdue') {
94
+ zeroReasonHints.push('Great job! No overdue tasks');
95
+ zeroReasonHints.push("Check today's tasks with startDate='today'");
96
+ }
97
+ else {
98
+ zeroReasonHints.push("Expand date range with larger 'daysCount'");
99
+ zeroReasonHints.push("Check 'overdue' for past-due items");
100
+ }
101
+ }
102
+ // Generate contextual next steps
103
+ const now = new Date();
104
+ const todayStr = getDateString(now);
105
+ const nextSteps = generateTaskNextSteps('listed', tasks, {
106
+ hasToday: args.startDate === 'today' || tasks.some((task) => task.dueDate === todayStr),
107
+ hasOverdue: args.startDate === 'overdue' ||
108
+ tasks.some((task) => task.dueDate && new Date(task.dueDate) < now),
109
+ });
110
+ return summarizeList({
111
+ subject,
112
+ count: tasks.length,
113
+ limit: args.limit,
114
+ nextCursor: nextCursor ?? undefined,
115
+ filterHints,
116
+ previewLines: previewTasks(tasks),
117
+ zeroReasonHints,
118
+ nextSteps,
119
+ });
120
+ }
121
+ export { findTasksByDate };
@@ -0,0 +1,65 @@
1
+ import { z } from 'zod';
2
+ declare const findTasks: {
3
+ name: "find-tasks";
4
+ description: string;
5
+ parameters: {
6
+ searchText: z.ZodOptional<z.ZodString>;
7
+ projectId: z.ZodOptional<z.ZodString>;
8
+ sectionId: z.ZodOptional<z.ZodString>;
9
+ parentId: z.ZodOptional<z.ZodString>;
10
+ limit: z.ZodDefault<z.ZodNumber>;
11
+ cursor: z.ZodOptional<z.ZodString>;
12
+ };
13
+ execute(args: {
14
+ limit: number;
15
+ parentId?: string | undefined;
16
+ projectId?: string | undefined;
17
+ sectionId?: string | undefined;
18
+ cursor?: string | undefined;
19
+ searchText?: string | undefined;
20
+ }, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
21
+ content: {
22
+ type: "text";
23
+ text: string;
24
+ }[];
25
+ structuredContent: {
26
+ tasks: {
27
+ id: string;
28
+ content: string;
29
+ description: string;
30
+ dueDate: string | undefined;
31
+ recurring: string | boolean;
32
+ priority: number;
33
+ projectId: string;
34
+ sectionId: string | null;
35
+ parentId: string | null;
36
+ labels: string[];
37
+ duration: string | null;
38
+ }[];
39
+ nextCursor: string | null;
40
+ totalCount: number;
41
+ hasMore: boolean;
42
+ appliedFilters: {
43
+ limit: number;
44
+ parentId?: string | undefined;
45
+ projectId?: string | undefined;
46
+ sectionId?: string | undefined;
47
+ cursor?: string | undefined;
48
+ searchText?: string | undefined;
49
+ };
50
+ };
51
+ } | {
52
+ content: ({
53
+ type: "text";
54
+ text: string;
55
+ mimeType?: undefined;
56
+ } | {
57
+ type: "text";
58
+ mimeType: string;
59
+ text: string;
60
+ })[];
61
+ structuredContent?: undefined;
62
+ }>;
63
+ };
64
+ export { findTasks };
65
+ //# sourceMappingURL=find-tasks.d.ts.map
@@ -0,0 +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;AAoCvB,QAAA,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmF2B,CAAA;AAyG1C,OAAO,EAAE,SAAS,EAAE,CAAA"}
@@ -0,0 +1,182 @@
1
+ import { z } from 'zod';
2
+ import { getToolOutput } from '../mcp-helpers.js';
3
+ import { getTasksByFilter, mapTask } from '../tool-helpers.js';
4
+ import { ApiLimits } from '../utils/constants.js';
5
+ import { generateTaskNextSteps, getDateString, previewTasks, summarizeList, } from '../utils/response-builders.js';
6
+ import { ToolNames } from '../utils/tool-names.js';
7
+ const { FIND_COMPLETED_TASKS, ADD_TASKS } = ToolNames;
8
+ const ArgsSchema = {
9
+ searchText: z.string().optional().describe('The text to search for in tasks.'),
10
+ projectId: z.string().optional().describe('Find tasks in this project.'),
11
+ sectionId: z.string().optional().describe('Find tasks in this section.'),
12
+ parentId: z.string().optional().describe('Find subtasks of this parent task.'),
13
+ limit: z
14
+ .number()
15
+ .int()
16
+ .min(1)
17
+ .max(ApiLimits.TASKS_MAX)
18
+ .default(ApiLimits.TASKS_DEFAULT)
19
+ .describe('The maximum number of tasks to return.'),
20
+ cursor: z
21
+ .string()
22
+ .optional()
23
+ .describe('The cursor to get the next page of tasks (cursor is obtained from the previous call to this tool, with the same parameters).'),
24
+ };
25
+ const findTasks = {
26
+ name: ToolNames.FIND_TASKS,
27
+ description: 'Find tasks by text search, or by project/section/parent container. At least one filter must be provided.',
28
+ parameters: ArgsSchema,
29
+ async execute(args, client) {
30
+ const { searchText, projectId, sectionId, parentId, limit, cursor } = args;
31
+ // Validate at least one filter is provided
32
+ if (!searchText && !projectId && !sectionId && !parentId) {
33
+ throw new Error('At least one filter must be provided: searchText, projectId, sectionId, or parentId');
34
+ }
35
+ // If using container-based filtering, use direct API
36
+ if (projectId || sectionId || parentId) {
37
+ const taskParams = {
38
+ limit,
39
+ cursor: cursor ?? null,
40
+ };
41
+ if (projectId)
42
+ taskParams.projectId = projectId;
43
+ if (sectionId)
44
+ taskParams.sectionId = sectionId;
45
+ if (parentId)
46
+ taskParams.parentId = parentId;
47
+ const { results, nextCursor } = await client.getTasks(taskParams);
48
+ const mappedTasks = results.map(mapTask);
49
+ // If also has searchText, filter the results
50
+ const finalTasks = searchText
51
+ ? mappedTasks.filter((task) => task.content.toLowerCase().includes(searchText.toLowerCase()) ||
52
+ task.description?.toLowerCase().includes(searchText.toLowerCase()))
53
+ : mappedTasks;
54
+ const textContent = generateTextContent({
55
+ tasks: finalTasks,
56
+ args,
57
+ nextCursor,
58
+ isContainerSearch: true,
59
+ });
60
+ return getToolOutput({
61
+ textContent,
62
+ structuredContent: {
63
+ tasks: finalTasks,
64
+ nextCursor,
65
+ totalCount: finalTasks.length,
66
+ hasMore: Boolean(nextCursor),
67
+ appliedFilters: args,
68
+ },
69
+ });
70
+ }
71
+ // Text-only search using filter query
72
+ const result = await getTasksByFilter({
73
+ client,
74
+ query: `search: ${searchText}`,
75
+ cursor: args.cursor,
76
+ limit: args.limit,
77
+ });
78
+ const textContent = generateTextContent({
79
+ tasks: result.tasks,
80
+ args,
81
+ nextCursor: result.nextCursor,
82
+ isContainerSearch: false,
83
+ });
84
+ return getToolOutput({
85
+ textContent,
86
+ structuredContent: {
87
+ tasks: result.tasks,
88
+ nextCursor: result.nextCursor,
89
+ totalCount: result.tasks.length,
90
+ hasMore: Boolean(result.nextCursor),
91
+ appliedFilters: args,
92
+ },
93
+ });
94
+ },
95
+ };
96
+ function getContainerZeroReasonHints(args) {
97
+ if (args.projectId) {
98
+ const hints = [
99
+ args.searchText ? 'No tasks in project match search' : 'Project has no tasks yet',
100
+ ];
101
+ if (!args.searchText) {
102
+ hints.push(`Use ${ADD_TASKS} to create tasks`);
103
+ }
104
+ return hints;
105
+ }
106
+ if (args.sectionId) {
107
+ const hints = [args.searchText ? 'No tasks in section match search' : 'Section is empty'];
108
+ if (!args.searchText) {
109
+ hints.push('Tasks may be in other sections of the project');
110
+ }
111
+ return hints;
112
+ }
113
+ if (args.parentId) {
114
+ const hints = [args.searchText ? 'No subtasks match search' : 'No subtasks created yet'];
115
+ if (!args.searchText) {
116
+ hints.push(`Use ${ADD_TASKS} with parentId to add subtasks`);
117
+ }
118
+ return hints;
119
+ }
120
+ return [];
121
+ }
122
+ function generateTextContent({ tasks, args, nextCursor, isContainerSearch, }) {
123
+ // Generate subject and filter descriptions based on search type
124
+ let subject;
125
+ const filterHints = [];
126
+ const zeroReasonHints = [];
127
+ if (isContainerSearch) {
128
+ // Container-based search
129
+ if (args.projectId) {
130
+ subject = 'Tasks in project';
131
+ filterHints.push(`in project ${args.projectId}`);
132
+ }
133
+ else if (args.sectionId) {
134
+ subject = 'Tasks in section';
135
+ filterHints.push(`in section ${args.sectionId}`);
136
+ }
137
+ else if (args.parentId) {
138
+ subject = 'Subtasks';
139
+ filterHints.push(`subtasks of ${args.parentId}`);
140
+ }
141
+ else {
142
+ subject = 'Tasks'; // fallback, though this shouldn't happen
143
+ }
144
+ // Add search text filter if present
145
+ if (args.searchText) {
146
+ subject += ` matching "${args.searchText}"`;
147
+ filterHints.push(`containing "${args.searchText}"`);
148
+ }
149
+ // Container-specific zero result hints
150
+ if (tasks.length === 0) {
151
+ zeroReasonHints.push(...getContainerZeroReasonHints(args));
152
+ }
153
+ }
154
+ else {
155
+ // Text-only search
156
+ subject = `Search results for "${args.searchText}"`;
157
+ filterHints.push(`matching "${args.searchText}"`);
158
+ if (tasks.length === 0) {
159
+ zeroReasonHints.push('Try broader search terms');
160
+ zeroReasonHints.push(`Check completed tasks with ${FIND_COMPLETED_TASKS}`);
161
+ zeroReasonHints.push('Verify spelling and try partial words');
162
+ }
163
+ }
164
+ // Generate contextual next steps
165
+ const now = new Date();
166
+ const todayDateString = getDateString(now);
167
+ const nextSteps = generateTaskNextSteps('listed', tasks, {
168
+ hasToday: tasks.some((task) => task.dueDate === todayDateString),
169
+ hasOverdue: tasks.some((task) => task.dueDate && new Date(task.dueDate) < now),
170
+ });
171
+ return summarizeList({
172
+ subject,
173
+ count: tasks.length,
174
+ limit: args.limit,
175
+ nextCursor: nextCursor ?? undefined,
176
+ filterHints,
177
+ previewLines: previewTasks(tasks),
178
+ zeroReasonHints,
179
+ nextSteps,
180
+ });
181
+ }
182
+ export { findTasks };