@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.
- package/README.md +11 -3
- package/dist/index.d.ts +496 -255
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +41 -29
- package/dist/mcp-helpers.d.ts +25 -3
- package/dist/mcp-helpers.d.ts.map +1 -1
- package/dist/mcp-helpers.js +37 -19
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +32 -28
- package/dist/tools/__tests__/add-tasks.test.d.ts +2 -0
- package/dist/tools/__tests__/add-tasks.test.d.ts.map +1 -0
- package/dist/tools/__tests__/{tasks-add-multiple.test.js → add-tasks.test.js} +85 -81
- package/dist/tools/__tests__/complete-tasks.test.d.ts +2 -0
- package/dist/tools/__tests__/complete-tasks.test.d.ts.map +1 -0
- package/dist/tools/__tests__/complete-tasks.test.js +206 -0
- package/dist/tools/__tests__/delete-object.test.d.ts +2 -0
- package/dist/tools/__tests__/delete-object.test.d.ts.map +1 -0
- package/dist/tools/__tests__/{delete-one.test.js → delete-object.test.js} +42 -22
- package/dist/tools/__tests__/find-completed-tasks.test.d.ts +2 -0
- package/dist/tools/__tests__/find-completed-tasks.test.d.ts.map +1 -0
- package/dist/tools/__tests__/{tasks-list-completed.test.js → find-completed-tasks.test.js} +13 -36
- package/dist/tools/__tests__/find-projects.test.d.ts +2 -0
- package/dist/tools/__tests__/find-projects.test.d.ts.map +1 -0
- package/dist/tools/__tests__/{projects-list.test.js → find-projects.test.js} +55 -39
- package/dist/tools/__tests__/find-sections.test.d.ts +2 -0
- package/dist/tools/__tests__/find-sections.test.d.ts.map +1 -0
- package/dist/tools/__tests__/{sections-search.test.js → find-sections.test.js} +64 -50
- package/dist/tools/__tests__/find-tasks-by-date.test.d.ts +2 -0
- package/dist/tools/__tests__/find-tasks-by-date.test.d.ts.map +1 -0
- package/dist/tools/__tests__/{tasks-list-by-date.test.js → find-tasks-by-date.test.js} +96 -14
- package/dist/tools/__tests__/find-tasks.test.d.ts +2 -0
- package/dist/tools/__tests__/find-tasks.test.d.ts.map +1 -0
- package/dist/tools/__tests__/find-tasks.test.js +334 -0
- package/dist/tools/__tests__/get-overview.test.d.ts +2 -0
- package/dist/tools/__tests__/get-overview.test.d.ts.map +1 -0
- package/dist/tools/__tests__/{overview.test.js → get-overview.test.js} +77 -13
- package/dist/tools/__tests__/manage-projects.test.d.ts +2 -0
- package/dist/tools/__tests__/manage-projects.test.d.ts.map +1 -0
- package/dist/tools/__tests__/{projects-manage.test.js → manage-projects.test.js} +33 -30
- package/dist/tools/__tests__/manage-sections.test.d.ts +2 -0
- package/dist/tools/__tests__/manage-sections.test.d.ts.map +1 -0
- package/dist/tools/__tests__/manage-sections.test.js +162 -0
- package/dist/tools/__tests__/update-tasks.test.d.ts +2 -0
- package/dist/tools/__tests__/update-tasks.test.d.ts.map +1 -0
- package/dist/tools/__tests__/update-tasks.test.js +645 -0
- package/dist/tools/{tasks-add-multiple.d.ts → add-tasks.d.ts} +36 -16
- package/dist/tools/add-tasks.d.ts.map +1 -0
- package/dist/tools/{tasks-add-multiple.js → add-tasks.js} +39 -4
- package/dist/tools/complete-tasks.d.ts +40 -0
- package/dist/tools/complete-tasks.d.ts.map +1 -0
- package/dist/tools/complete-tasks.js +68 -0
- package/dist/tools/delete-object.d.ts +38 -0
- package/dist/tools/delete-object.d.ts.map +1 -0
- package/dist/tools/delete-object.js +69 -0
- package/dist/tools/find-completed-tasks.d.ts +74 -0
- package/dist/tools/find-completed-tasks.d.ts.map +1 -0
- package/dist/tools/find-completed-tasks.js +112 -0
- package/dist/tools/find-projects.d.ts +53 -0
- package/dist/tools/find-projects.d.ts.map +1 -0
- package/dist/tools/find-projects.js +101 -0
- package/dist/tools/find-sections.d.ts +42 -0
- package/dist/tools/find-sections.d.ts.map +1 -0
- package/dist/tools/find-sections.js +96 -0
- package/dist/tools/find-tasks-by-date.d.ts +59 -0
- package/dist/tools/find-tasks-by-date.d.ts.map +1 -0
- package/dist/tools/find-tasks-by-date.js +121 -0
- package/dist/tools/find-tasks.d.ts +65 -0
- package/dist/tools/find-tasks.d.ts.map +1 -0
- package/dist/tools/find-tasks.js +182 -0
- package/dist/tools/get-overview.d.ts +67 -0
- package/dist/tools/get-overview.d.ts.map +1 -0
- package/dist/tools/{overview.js → get-overview.js} +66 -19
- package/dist/tools/manage-projects.d.ts +35 -0
- package/dist/tools/manage-projects.d.ts.map +1 -0
- package/dist/tools/manage-projects.js +63 -0
- package/dist/tools/manage-sections.d.ts +38 -0
- package/dist/tools/manage-sections.d.ts.map +1 -0
- package/dist/tools/manage-sections.js +78 -0
- package/dist/tools/update-tasks.d.ts +94 -0
- package/dist/tools/update-tasks.d.ts.map +1 -0
- package/dist/tools/update-tasks.js +120 -0
- package/dist/utils/constants.d.ts +35 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +37 -0
- package/dist/utils/response-builders.d.ts +88 -0
- package/dist/utils/response-builders.d.ts.map +1 -0
- package/dist/utils/response-builders.js +202 -0
- package/dist/{tools → utils}/test-helpers.d.ts +16 -0
- package/dist/utils/test-helpers.d.ts.map +1 -0
- package/dist/{tools → utils}/test-helpers.js +51 -0
- package/dist/utils/tool-names.d.ts +23 -0
- package/dist/utils/tool-names.d.ts.map +1 -0
- package/dist/utils/tool-names.js +25 -0
- package/package.json +1 -1
- package/dist/tools/__tests__/delete-one.test.d.ts +0 -2
- package/dist/tools/__tests__/delete-one.test.d.ts.map +0 -1
- package/dist/tools/__tests__/overview.test.d.ts +0 -2
- package/dist/tools/__tests__/overview.test.d.ts.map +0 -1
- package/dist/tools/__tests__/projects-list.test.d.ts +0 -2
- package/dist/tools/__tests__/projects-list.test.d.ts.map +0 -1
- package/dist/tools/__tests__/projects-manage.test.d.ts +0 -2
- package/dist/tools/__tests__/projects-manage.test.d.ts.map +0 -1
- package/dist/tools/__tests__/sections-manage.test.d.ts +0 -2
- package/dist/tools/__tests__/sections-manage.test.d.ts.map +0 -1
- package/dist/tools/__tests__/sections-manage.test.js +0 -138
- package/dist/tools/__tests__/sections-search.test.d.ts +0 -2
- package/dist/tools/__tests__/sections-search.test.d.ts.map +0 -1
- package/dist/tools/__tests__/tasks-add-multiple.test.d.ts +0 -2
- package/dist/tools/__tests__/tasks-add-multiple.test.d.ts.map +0 -1
- package/dist/tools/__tests__/tasks-complete-multiple.test.d.ts +0 -2
- package/dist/tools/__tests__/tasks-complete-multiple.test.d.ts.map +0 -1
- package/dist/tools/__tests__/tasks-complete-multiple.test.js +0 -146
- package/dist/tools/__tests__/tasks-list-by-date.test.d.ts +0 -2
- package/dist/tools/__tests__/tasks-list-by-date.test.d.ts.map +0 -1
- package/dist/tools/__tests__/tasks-list-completed.test.d.ts +0 -2
- package/dist/tools/__tests__/tasks-list-completed.test.d.ts.map +0 -1
- package/dist/tools/__tests__/tasks-list-for-container.test.d.ts +0 -2
- package/dist/tools/__tests__/tasks-list-for-container.test.d.ts.map +0 -1
- package/dist/tools/__tests__/tasks-list-for-container.test.js +0 -232
- package/dist/tools/__tests__/tasks-organize-multiple.test.d.ts +0 -2
- package/dist/tools/__tests__/tasks-organize-multiple.test.d.ts.map +0 -1
- package/dist/tools/__tests__/tasks-organize-multiple.test.js +0 -245
- package/dist/tools/__tests__/tasks-search.test.d.ts +0 -2
- package/dist/tools/__tests__/tasks-search.test.d.ts.map +0 -1
- package/dist/tools/__tests__/tasks-search.test.js +0 -106
- package/dist/tools/__tests__/tasks-update-one.test.d.ts +0 -2
- package/dist/tools/__tests__/tasks-update-one.test.d.ts.map +0 -1
- package/dist/tools/__tests__/tasks-update-one.test.js +0 -251
- package/dist/tools/delete-one.d.ts +0 -17
- package/dist/tools/delete-one.d.ts.map +0 -1
- package/dist/tools/delete-one.js +0 -25
- package/dist/tools/overview.d.ts +0 -14
- package/dist/tools/overview.d.ts.map +0 -1
- package/dist/tools/projects-list.d.ts +0 -29
- package/dist/tools/projects-list.d.ts.map +0 -1
- package/dist/tools/projects-list.js +0 -39
- package/dist/tools/projects-manage.d.ts +0 -24
- package/dist/tools/projects-manage.d.ts.map +0 -1
- package/dist/tools/projects-manage.js +0 -26
- package/dist/tools/sections-manage.d.ts +0 -23
- package/dist/tools/sections-manage.d.ts.map +0 -1
- package/dist/tools/sections-manage.js +0 -37
- package/dist/tools/sections-search.d.ts +0 -18
- package/dist/tools/sections-search.d.ts.map +0 -1
- package/dist/tools/sections-search.js +0 -27
- package/dist/tools/tasks-add-multiple.d.ts.map +0 -1
- package/dist/tools/tasks-complete-multiple.d.ts +0 -16
- package/dist/tools/tasks-complete-multiple.d.ts.map +0 -1
- package/dist/tools/tasks-complete-multiple.js +0 -23
- package/dist/tools/tasks-list-by-date.d.ts +0 -34
- package/dist/tools/tasks-list-by-date.d.ts.map +0 -1
- package/dist/tools/tasks-list-by-date.js +0 -53
- package/dist/tools/tasks-list-completed.d.ts +0 -44
- package/dist/tools/tasks-list-completed.d.ts.map +0 -1
- package/dist/tools/tasks-list-completed.js +0 -49
- package/dist/tools/tasks-list-for-container.d.ts +0 -34
- package/dist/tools/tasks-list-for-container.d.ts.map +0 -1
- package/dist/tools/tasks-list-for-container.js +0 -48
- package/dist/tools/tasks-organize-multiple.d.ts +0 -37
- package/dist/tools/tasks-organize-multiple.d.ts.map +0 -1
- package/dist/tools/tasks-organize-multiple.js +0 -34
- package/dist/tools/tasks-search.d.ts +0 -32
- package/dist/tools/tasks-search.d.ts.map +0 -1
- package/dist/tools/tasks-search.js +0 -30
- package/dist/tools/tasks-update-one.d.ts +0 -29
- package/dist/tools/tasks-update-one.d.ts.map +0 -1
- package/dist/tools/tasks-update-one.js +0 -63
- 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 };
|