@doist/todoist-ai 2.2.2 → 4.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 +6 -14
- package/dist/index.d.ts +619 -250
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +45 -29
- package/dist/main.js +2 -1
- 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 +44 -28
- package/dist/tools/__tests__/add-comments.test.d.ts +2 -0
- package/dist/tools/__tests__/add-comments.test.d.ts.map +1 -0
- package/dist/tools/__tests__/add-comments.test.js +241 -0
- package/dist/tools/__tests__/add-projects.test.d.ts +2 -0
- package/dist/tools/__tests__/add-projects.test.d.ts.map +1 -0
- package/dist/tools/__tests__/add-projects.test.js +152 -0
- package/dist/tools/__tests__/add-sections.test.d.ts +2 -0
- package/dist/tools/__tests__/add-sections.test.d.ts.map +1 -0
- package/dist/tools/__tests__/add-sections.test.js +181 -0
- 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} +89 -79
- 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-comments.test.d.ts +2 -0
- package/dist/tools/__tests__/find-comments.test.d.ts.map +1 -0
- package/dist/tools/__tests__/find-comments.test.js +242 -0
- 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__/update-comments.test.d.ts +2 -0
- package/dist/tools/__tests__/update-comments.test.d.ts.map +1 -0
- package/dist/tools/__tests__/update-comments.test.js +296 -0
- package/dist/tools/__tests__/update-projects.test.d.ts +2 -0
- package/dist/tools/__tests__/update-projects.test.d.ts.map +1 -0
- package/dist/tools/__tests__/update-projects.test.js +205 -0
- package/dist/tools/__tests__/update-sections.test.d.ts +2 -0
- package/dist/tools/__tests__/update-sections.test.d.ts.map +1 -0
- package/dist/tools/__tests__/update-sections.test.js +156 -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/add-comments.d.ts +51 -0
- package/dist/tools/add-comments.d.ts.map +1 -0
- package/dist/tools/add-comments.js +79 -0
- package/dist/tools/add-projects.d.ts +50 -0
- package/dist/tools/add-projects.d.ts.map +1 -0
- package/dist/tools/add-projects.js +59 -0
- package/dist/tools/add-sections.d.ts +46 -0
- package/dist/tools/add-sections.d.ts.map +1 -0
- package/dist/tools/add-sections.js +61 -0
- package/dist/tools/add-tasks.d.ts +82 -0
- package/dist/tools/add-tasks.d.ts.map +1 -0
- package/dist/tools/add-tasks.js +96 -0
- 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 +79 -0
- package/dist/tools/find-comments.d.ts +46 -0
- package/dist/tools/find-comments.d.ts.map +1 -0
- package/dist/tools/find-comments.js +143 -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/update-comments.d.ts +50 -0
- package/dist/tools/update-comments.d.ts.map +1 -0
- package/dist/tools/update-comments.js +82 -0
- package/dist/tools/update-projects.d.ts +59 -0
- package/dist/tools/update-projects.d.ts.map +1 -0
- package/dist/tools/update-projects.js +84 -0
- package/dist/tools/update-sections.d.ts +47 -0
- package/dist/tools/update-sections.d.ts.map +1 -0
- package/dist/tools/update-sections.js +70 -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 +39 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +41 -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 +28 -0
- package/dist/utils/tool-names.d.ts.map +1 -0
- package/dist/utils/tool-names.js +31 -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__/projects-manage.test.js +0 -106
- 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 +0 -55
- package/dist/tools/tasks-add-multiple.d.ts.map +0 -1
- package/dist/tools/tasks-add-multiple.js +0 -52
- 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,38 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
declare const deleteObject: {
|
|
3
|
+
name: "delete-object";
|
|
4
|
+
description: string;
|
|
5
|
+
parameters: {
|
|
6
|
+
type: z.ZodEnum<["project", "section", "task", "comment"]>;
|
|
7
|
+
id: z.ZodString;
|
|
8
|
+
};
|
|
9
|
+
execute(args: {
|
|
10
|
+
type: "task" | "comment" | "project" | "section";
|
|
11
|
+
id: string;
|
|
12
|
+
}, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
|
|
13
|
+
content: {
|
|
14
|
+
type: "text";
|
|
15
|
+
text: string;
|
|
16
|
+
}[];
|
|
17
|
+
structuredContent: {
|
|
18
|
+
deletedEntity: {
|
|
19
|
+
type: "task" | "comment" | "project" | "section";
|
|
20
|
+
id: string;
|
|
21
|
+
};
|
|
22
|
+
success: boolean;
|
|
23
|
+
};
|
|
24
|
+
} | {
|
|
25
|
+
content: ({
|
|
26
|
+
type: "text";
|
|
27
|
+
text: string;
|
|
28
|
+
mimeType?: undefined;
|
|
29
|
+
} | {
|
|
30
|
+
type: "text";
|
|
31
|
+
mimeType: string;
|
|
32
|
+
text: string;
|
|
33
|
+
})[];
|
|
34
|
+
structuredContent?: undefined;
|
|
35
|
+
}>;
|
|
36
|
+
};
|
|
37
|
+
export { deleteObject };
|
|
38
|
+
//# sourceMappingURL=delete-object.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete-object.d.ts","sourceRoot":"","sources":["../../src/tools/delete-object.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAsBvB,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCwB,CAAA;AA+C1C,OAAO,EAAE,YAAY,EAAE,CAAA"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getToolOutput } from '../mcp-helpers.js';
|
|
3
|
+
import { formatNextSteps } from '../utils/response-builders.js';
|
|
4
|
+
import { ToolNames } from '../utils/tool-names.js';
|
|
5
|
+
const { FIND_PROJECTS, GET_OVERVIEW, FIND_SECTIONS, FIND_TASKS, FIND_TASKS_BY_DATE, FIND_COMMENTS, } = ToolNames;
|
|
6
|
+
const ArgsSchema = {
|
|
7
|
+
type: z
|
|
8
|
+
.enum(['project', 'section', 'task', 'comment'])
|
|
9
|
+
.describe('The type of entity to delete.'),
|
|
10
|
+
id: z.string().min(1).describe('The ID of the entity to delete.'),
|
|
11
|
+
};
|
|
12
|
+
const deleteObject = {
|
|
13
|
+
name: ToolNames.DELETE_OBJECT,
|
|
14
|
+
description: 'Delete a project, section, task, or comment by its ID.',
|
|
15
|
+
parameters: ArgsSchema,
|
|
16
|
+
async execute(args, client) {
|
|
17
|
+
switch (args.type) {
|
|
18
|
+
case 'project':
|
|
19
|
+
await client.deleteProject(args.id);
|
|
20
|
+
break;
|
|
21
|
+
case 'section':
|
|
22
|
+
await client.deleteSection(args.id);
|
|
23
|
+
break;
|
|
24
|
+
case 'task':
|
|
25
|
+
await client.deleteTask(args.id);
|
|
26
|
+
break;
|
|
27
|
+
case 'comment':
|
|
28
|
+
await client.deleteComment(args.id);
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
const textContent = generateTextContent({
|
|
32
|
+
type: args.type,
|
|
33
|
+
id: args.id,
|
|
34
|
+
});
|
|
35
|
+
return getToolOutput({
|
|
36
|
+
textContent,
|
|
37
|
+
structuredContent: {
|
|
38
|
+
deletedEntity: {
|
|
39
|
+
type: args.type,
|
|
40
|
+
id: args.id,
|
|
41
|
+
},
|
|
42
|
+
success: true,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
function generateTextContent({ type, id, }) {
|
|
48
|
+
const summary = `Deleted ${type}: id=${id}`;
|
|
49
|
+
// Recovery-focused next steps based on what was deleted
|
|
50
|
+
const nextSteps = [];
|
|
51
|
+
switch (type) {
|
|
52
|
+
case 'project':
|
|
53
|
+
// Help user understand impact and navigate remaining work
|
|
54
|
+
nextSteps.push(`Use ${FIND_PROJECTS} to see remaining projects`);
|
|
55
|
+
nextSteps.push('Note: All tasks and sections in this project were also deleted');
|
|
56
|
+
nextSteps.push(`Use ${GET_OVERVIEW} to review your updated project structure`);
|
|
57
|
+
break;
|
|
58
|
+
case 'section':
|
|
59
|
+
// Guide user to reorganize remaining sections and tasks
|
|
60
|
+
nextSteps.push(`Use ${FIND_SECTIONS} to see remaining sections in the project`);
|
|
61
|
+
nextSteps.push('Note: Tasks in this section were also deleted');
|
|
62
|
+
nextSteps.push(`Use ${FIND_TASKS} with projectId to see unorganized tasks`);
|
|
63
|
+
break;
|
|
64
|
+
case 'task':
|
|
65
|
+
// Help user stay focused on remaining work
|
|
66
|
+
nextSteps.push(`Use ${FIND_TASKS_BY_DATE} to see remaining tasks for today`);
|
|
67
|
+
nextSteps.push(`Use ${GET_OVERVIEW} to check if this affects any dependent tasks`);
|
|
68
|
+
nextSteps.push('Note: Any subtasks of this task were also deleted');
|
|
69
|
+
break;
|
|
70
|
+
case 'comment':
|
|
71
|
+
// Help user understand comment deletion impact
|
|
72
|
+
nextSteps.push(`Use ${FIND_COMMENTS} to see remaining comments on the task/project`);
|
|
73
|
+
nextSteps.push('Note: Comment attachments were also deleted');
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
const next = formatNextSteps(nextSteps);
|
|
77
|
+
return `${summary}\n${next}`;
|
|
78
|
+
}
|
|
79
|
+
export { deleteObject };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Comment } from '@doist/todoist-api-typescript';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
declare const findComments: {
|
|
4
|
+
name: "find-comments";
|
|
5
|
+
description: string;
|
|
6
|
+
parameters: {
|
|
7
|
+
taskId: z.ZodOptional<z.ZodString>;
|
|
8
|
+
projectId: z.ZodOptional<z.ZodString>;
|
|
9
|
+
commentId: z.ZodOptional<z.ZodString>;
|
|
10
|
+
cursor: z.ZodOptional<z.ZodString>;
|
|
11
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
12
|
+
};
|
|
13
|
+
execute(args: {
|
|
14
|
+
projectId?: string | undefined;
|
|
15
|
+
limit?: number | undefined;
|
|
16
|
+
cursor?: string | undefined;
|
|
17
|
+
taskId?: string | undefined;
|
|
18
|
+
commentId?: string | undefined;
|
|
19
|
+
}, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
|
|
20
|
+
content: {
|
|
21
|
+
type: "text";
|
|
22
|
+
text: string;
|
|
23
|
+
}[];
|
|
24
|
+
structuredContent: {
|
|
25
|
+
comments: Comment[];
|
|
26
|
+
searchType: string;
|
|
27
|
+
searchId: string;
|
|
28
|
+
hasMore: boolean;
|
|
29
|
+
nextCursor: string | null;
|
|
30
|
+
totalCount: number;
|
|
31
|
+
};
|
|
32
|
+
} | {
|
|
33
|
+
content: ({
|
|
34
|
+
type: "text";
|
|
35
|
+
text: string;
|
|
36
|
+
mimeType?: undefined;
|
|
37
|
+
} | {
|
|
38
|
+
type: "text";
|
|
39
|
+
mimeType: string;
|
|
40
|
+
text: string;
|
|
41
|
+
})[];
|
|
42
|
+
structuredContent?: undefined;
|
|
43
|
+
}>;
|
|
44
|
+
};
|
|
45
|
+
export { findComments };
|
|
46
|
+
//# sourceMappingURL=find-comments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-comments.d.ts","sourceRoot":"","sources":["../../src/tools/find-comments.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAA;AAC5D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAuBvB,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsEwB,CAAA;AAoF1C,OAAO,EAAE,YAAY,EAAE,CAAA"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getToolOutput } from '../mcp-helpers.js';
|
|
3
|
+
import { ApiLimits } from '../utils/constants.js';
|
|
4
|
+
import { formatNextSteps } from '../utils/response-builders.js';
|
|
5
|
+
import { ToolNames } from '../utils/tool-names.js';
|
|
6
|
+
const { ADD_COMMENTS, UPDATE_COMMENTS, DELETE_OBJECT } = ToolNames;
|
|
7
|
+
const ArgsSchema = {
|
|
8
|
+
taskId: z.string().optional().describe('Find comments for a specific task.'),
|
|
9
|
+
projectId: z.string().optional().describe('Find comments for a specific project.'),
|
|
10
|
+
commentId: z.string().optional().describe('Get a specific comment by ID.'),
|
|
11
|
+
cursor: z.string().optional().describe('Pagination cursor for retrieving more results.'),
|
|
12
|
+
limit: z
|
|
13
|
+
.number()
|
|
14
|
+
.int()
|
|
15
|
+
.min(1)
|
|
16
|
+
.max(ApiLimits.COMMENTS_MAX)
|
|
17
|
+
.optional()
|
|
18
|
+
.describe('Maximum number of comments to return'),
|
|
19
|
+
};
|
|
20
|
+
const findComments = {
|
|
21
|
+
name: ToolNames.FIND_COMMENTS,
|
|
22
|
+
description: 'Find comments by task, project, or get a specific comment by ID. Exactly one of taskId, projectId, or commentId must be provided.',
|
|
23
|
+
parameters: ArgsSchema,
|
|
24
|
+
async execute(args, client) {
|
|
25
|
+
// Validate that exactly one search parameter is provided
|
|
26
|
+
const searchParams = [args.taskId, args.projectId, args.commentId].filter(Boolean);
|
|
27
|
+
if (searchParams.length === 0) {
|
|
28
|
+
throw new Error('Must provide exactly one of: taskId, projectId, or commentId.');
|
|
29
|
+
}
|
|
30
|
+
if (searchParams.length > 1) {
|
|
31
|
+
throw new Error('Cannot provide multiple search parameters. Choose one of: taskId, projectId, or commentId.');
|
|
32
|
+
}
|
|
33
|
+
let comments;
|
|
34
|
+
let hasMore = false;
|
|
35
|
+
let nextCursor = null;
|
|
36
|
+
if (args.commentId) {
|
|
37
|
+
// Get single comment
|
|
38
|
+
const comment = await client.getComment(args.commentId);
|
|
39
|
+
comments = [comment];
|
|
40
|
+
}
|
|
41
|
+
else if (args.taskId) {
|
|
42
|
+
// Get comments by task
|
|
43
|
+
const response = await client.getComments({
|
|
44
|
+
taskId: args.taskId,
|
|
45
|
+
cursor: args.cursor || null,
|
|
46
|
+
limit: args.limit || ApiLimits.COMMENTS_DEFAULT,
|
|
47
|
+
});
|
|
48
|
+
comments = response.results;
|
|
49
|
+
hasMore = response.nextCursor !== null;
|
|
50
|
+
nextCursor = response.nextCursor;
|
|
51
|
+
}
|
|
52
|
+
else if (args.projectId) {
|
|
53
|
+
// Get comments by project
|
|
54
|
+
const response = await client.getComments({
|
|
55
|
+
projectId: args.projectId,
|
|
56
|
+
cursor: args.cursor || null,
|
|
57
|
+
limit: args.limit || ApiLimits.COMMENTS_DEFAULT,
|
|
58
|
+
});
|
|
59
|
+
comments = response.results;
|
|
60
|
+
hasMore = response.nextCursor !== null;
|
|
61
|
+
nextCursor = response.nextCursor;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// This should never happen due to validation, but TypeScript needs it
|
|
65
|
+
throw new Error('Invalid state: no search parameter provided');
|
|
66
|
+
}
|
|
67
|
+
const textContent = generateTextContent({
|
|
68
|
+
comments,
|
|
69
|
+
searchType: args.commentId ? 'single' : args.taskId ? 'task' : 'project',
|
|
70
|
+
searchId: args.commentId || args.taskId || args.projectId || '',
|
|
71
|
+
hasMore,
|
|
72
|
+
nextCursor,
|
|
73
|
+
});
|
|
74
|
+
return getToolOutput({
|
|
75
|
+
textContent,
|
|
76
|
+
structuredContent: {
|
|
77
|
+
comments,
|
|
78
|
+
searchType: args.commentId ? 'single' : args.taskId ? 'task' : 'project',
|
|
79
|
+
searchId: args.commentId || args.taskId || args.projectId || '',
|
|
80
|
+
hasMore,
|
|
81
|
+
nextCursor,
|
|
82
|
+
totalCount: comments.length,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
function generateTextContent({ comments, searchType, searchId, hasMore, nextCursor, }) {
|
|
88
|
+
if (comments.length === 0) {
|
|
89
|
+
return `No comments found for ${searchType}${searchType !== 'single' ? ` ${searchId}` : ''}`;
|
|
90
|
+
}
|
|
91
|
+
// Build summary
|
|
92
|
+
let summary;
|
|
93
|
+
if (searchType === 'single') {
|
|
94
|
+
const comment = comments[0];
|
|
95
|
+
if (!comment) {
|
|
96
|
+
return 'Comment not found';
|
|
97
|
+
}
|
|
98
|
+
const hasAttachment = comment.fileAttachment !== null;
|
|
99
|
+
const attachmentInfo = hasAttachment
|
|
100
|
+
? ` • Has attachment: ${comment.fileAttachment?.fileName || 'file'}`
|
|
101
|
+
: '';
|
|
102
|
+
summary = `Found comment${attachmentInfo} • id=${comment.id}`;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
const attachmentCount = comments.filter((c) => c.fileAttachment !== null).length;
|
|
106
|
+
const attachmentInfo = attachmentCount > 0 ? ` (${attachmentCount} with attachments)` : '';
|
|
107
|
+
const commentsLabel = comments.length === 1 ? 'comment' : 'comments';
|
|
108
|
+
summary = `Found ${comments.length} ${commentsLabel} for ${searchType} ${searchId}${attachmentInfo}`;
|
|
109
|
+
if (hasMore) {
|
|
110
|
+
summary += ' • More available';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Context-aware next steps
|
|
114
|
+
const nextSteps = [];
|
|
115
|
+
if (searchType === 'single') {
|
|
116
|
+
const comment = comments[0];
|
|
117
|
+
if (comment) {
|
|
118
|
+
nextSteps.push(`Use ${UPDATE_COMMENTS} with id=${comment.id} to edit content`);
|
|
119
|
+
nextSteps.push(`Use ${DELETE_OBJECT} with type=comment id=${comment.id} to remove`);
|
|
120
|
+
// Suggest viewing related comments
|
|
121
|
+
if (comment.taskId) {
|
|
122
|
+
nextSteps.push(`Use ${ToolNames.FIND_COMMENTS} with taskId=${comment.taskId} to see all task comments`);
|
|
123
|
+
}
|
|
124
|
+
else if (comment.projectId) {
|
|
125
|
+
nextSteps.push(`Use ${ToolNames.FIND_COMMENTS} with projectId=${comment.projectId} to see all project comments`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// Multiple comments
|
|
131
|
+
nextSteps.push(`Use ${ADD_COMMENTS} with ${searchType}Id=${searchId} to add new comment`);
|
|
132
|
+
if (comments.length > 0) {
|
|
133
|
+
nextSteps.push(`Use ${ToolNames.FIND_COMMENTS} with commentId to view specific comment details`);
|
|
134
|
+
}
|
|
135
|
+
// Pagination
|
|
136
|
+
if (hasMore && nextCursor) {
|
|
137
|
+
nextSteps.push(`Use ${ToolNames.FIND_COMMENTS} with cursor="${nextCursor}" to get more results`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const next = formatNextSteps(nextSteps);
|
|
141
|
+
return `${summary}\n${next}`;
|
|
142
|
+
}
|
|
143
|
+
export { findComments };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
declare const findCompletedTasks: {
|
|
3
|
+
name: "find-completed-tasks";
|
|
4
|
+
description: string;
|
|
5
|
+
parameters: {
|
|
6
|
+
getBy: z.ZodDefault<z.ZodEnum<["completion", "due"]>>;
|
|
7
|
+
since: z.ZodString;
|
|
8
|
+
until: z.ZodString;
|
|
9
|
+
workspaceId: z.ZodOptional<z.ZodString>;
|
|
10
|
+
projectId: z.ZodOptional<z.ZodString>;
|
|
11
|
+
sectionId: z.ZodOptional<z.ZodString>;
|
|
12
|
+
parentId: z.ZodOptional<z.ZodString>;
|
|
13
|
+
limit: z.ZodDefault<z.ZodNumber>;
|
|
14
|
+
cursor: z.ZodOptional<z.ZodString>;
|
|
15
|
+
};
|
|
16
|
+
execute(args: {
|
|
17
|
+
limit: number;
|
|
18
|
+
getBy: "completion" | "due";
|
|
19
|
+
since: string;
|
|
20
|
+
until: string;
|
|
21
|
+
parentId?: string | undefined;
|
|
22
|
+
workspaceId?: string | undefined;
|
|
23
|
+
projectId?: string | undefined;
|
|
24
|
+
sectionId?: string | undefined;
|
|
25
|
+
cursor?: string | undefined;
|
|
26
|
+
}, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
|
|
27
|
+
content: {
|
|
28
|
+
type: "text";
|
|
29
|
+
text: string;
|
|
30
|
+
}[];
|
|
31
|
+
structuredContent: {
|
|
32
|
+
tasks: {
|
|
33
|
+
id: string;
|
|
34
|
+
content: string;
|
|
35
|
+
description: string;
|
|
36
|
+
dueDate: string | undefined;
|
|
37
|
+
recurring: string | boolean;
|
|
38
|
+
priority: number;
|
|
39
|
+
projectId: string;
|
|
40
|
+
sectionId: string | null;
|
|
41
|
+
parentId: string | null;
|
|
42
|
+
labels: string[];
|
|
43
|
+
duration: string | null;
|
|
44
|
+
}[];
|
|
45
|
+
nextCursor: string | null;
|
|
46
|
+
totalCount: number;
|
|
47
|
+
hasMore: boolean;
|
|
48
|
+
appliedFilters: {
|
|
49
|
+
limit: number;
|
|
50
|
+
getBy: "completion" | "due";
|
|
51
|
+
since: string;
|
|
52
|
+
until: string;
|
|
53
|
+
parentId?: string | undefined;
|
|
54
|
+
workspaceId?: string | undefined;
|
|
55
|
+
projectId?: string | undefined;
|
|
56
|
+
sectionId?: string | undefined;
|
|
57
|
+
cursor?: string | undefined;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
} | {
|
|
61
|
+
content: ({
|
|
62
|
+
type: "text";
|
|
63
|
+
text: string;
|
|
64
|
+
mimeType?: undefined;
|
|
65
|
+
} | {
|
|
66
|
+
type: "text";
|
|
67
|
+
mimeType: string;
|
|
68
|
+
text: string;
|
|
69
|
+
})[];
|
|
70
|
+
structuredContent?: undefined;
|
|
71
|
+
}>;
|
|
72
|
+
};
|
|
73
|
+
export { findCompletedTasks };
|
|
74
|
+
//# sourceMappingURL=find-completed-tasks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-completed-tasks.d.ts","sourceRoot":"","sources":["../../src/tools/find-completed-tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AA8CvB,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BkB,CAAA;AA2D1C,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getToolOutput } from '../mcp-helpers.js';
|
|
3
|
+
import { mapTask } from '../tool-helpers.js';
|
|
4
|
+
import { ApiLimits } from '../utils/constants.js';
|
|
5
|
+
import { previewTasks, summarizeList } from '../utils/response-builders.js';
|
|
6
|
+
import { ToolNames } from '../utils/tool-names.js';
|
|
7
|
+
const { FIND_TASKS_BY_DATE, GET_OVERVIEW } = ToolNames;
|
|
8
|
+
const ArgsSchema = {
|
|
9
|
+
getBy: z
|
|
10
|
+
.enum(['completion', 'due'])
|
|
11
|
+
.default('completion')
|
|
12
|
+
.describe('The method to use to get the tasks: "completion" to get tasks by completion date (ie, when the task was actually completed), "due" to get tasks by due date (ie, when the task was due to be completed by).'),
|
|
13
|
+
since: z
|
|
14
|
+
.string()
|
|
15
|
+
.date()
|
|
16
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
17
|
+
.describe('The start date to get the tasks for. Format: YYYY-MM-DD.'),
|
|
18
|
+
until: z
|
|
19
|
+
.string()
|
|
20
|
+
.date()
|
|
21
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
22
|
+
.describe('The start date to get the tasks for. Format: YYYY-MM-DD.'),
|
|
23
|
+
workspaceId: z.string().optional().describe('The ID of the workspace to get the tasks for.'),
|
|
24
|
+
projectId: z.string().optional().describe('The ID of the project to get the tasks for.'),
|
|
25
|
+
sectionId: z.string().optional().describe('The ID of the section to get the tasks for.'),
|
|
26
|
+
parentId: z.string().optional().describe('The ID of the parent task to get the tasks for.'),
|
|
27
|
+
limit: z
|
|
28
|
+
.number()
|
|
29
|
+
.int()
|
|
30
|
+
.min(1)
|
|
31
|
+
.max(ApiLimits.COMPLETED_TASKS_MAX)
|
|
32
|
+
.default(ApiLimits.COMPLETED_TASKS_DEFAULT)
|
|
33
|
+
.describe('The maximum number of tasks to return.'),
|
|
34
|
+
cursor: z
|
|
35
|
+
.string()
|
|
36
|
+
.optional()
|
|
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
|
+
};
|
|
39
|
+
const findCompletedTasks = {
|
|
40
|
+
name: ToolNames.FIND_COMPLETED_TASKS,
|
|
41
|
+
description: 'Get completed tasks.',
|
|
42
|
+
parameters: ArgsSchema,
|
|
43
|
+
async execute(args, client) {
|
|
44
|
+
const { getBy, ...rest } = args;
|
|
45
|
+
const { items, nextCursor } = getBy === 'completion'
|
|
46
|
+
? await client.getCompletedTasksByCompletionDate(rest)
|
|
47
|
+
: await client.getCompletedTasksByDueDate(rest);
|
|
48
|
+
const mappedTasks = items.map(mapTask);
|
|
49
|
+
const textContent = generateTextContent({
|
|
50
|
+
tasks: mappedTasks,
|
|
51
|
+
args,
|
|
52
|
+
nextCursor,
|
|
53
|
+
});
|
|
54
|
+
return getToolOutput({
|
|
55
|
+
textContent,
|
|
56
|
+
structuredContent: {
|
|
57
|
+
tasks: mappedTasks,
|
|
58
|
+
nextCursor,
|
|
59
|
+
totalCount: mappedTasks.length,
|
|
60
|
+
hasMore: Boolean(nextCursor),
|
|
61
|
+
appliedFilters: args,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
function generateTextContent({ tasks, args, nextCursor, }) {
|
|
67
|
+
// Generate subject description
|
|
68
|
+
const getByText = args.getBy === 'completion' ? 'completed' : 'due';
|
|
69
|
+
const subject = `Completed tasks (by ${getByText} date)`;
|
|
70
|
+
// Generate filter hints
|
|
71
|
+
const filterHints = [];
|
|
72
|
+
filterHints.push(`${getByText} date: ${args.since} to ${args.until}`);
|
|
73
|
+
if (args.projectId)
|
|
74
|
+
filterHints.push(`project: ${args.projectId}`);
|
|
75
|
+
if (args.sectionId)
|
|
76
|
+
filterHints.push(`section: ${args.sectionId}`);
|
|
77
|
+
if (args.parentId)
|
|
78
|
+
filterHints.push(`parent: ${args.parentId}`);
|
|
79
|
+
if (args.workspaceId)
|
|
80
|
+
filterHints.push(`workspace: ${args.workspaceId}`);
|
|
81
|
+
// Generate helpful suggestions for empty results
|
|
82
|
+
const zeroReasonHints = [];
|
|
83
|
+
if (tasks.length === 0) {
|
|
84
|
+
zeroReasonHints.push('No tasks completed in this date range');
|
|
85
|
+
zeroReasonHints.push('Try expanding the date range');
|
|
86
|
+
if (args.projectId || args.sectionId || args.parentId) {
|
|
87
|
+
zeroReasonHints.push('Try removing project/section/parent filters');
|
|
88
|
+
}
|
|
89
|
+
if (args.getBy === 'due') {
|
|
90
|
+
zeroReasonHints.push('Try switching to "completion" date instead');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Generate contextual next steps
|
|
94
|
+
const nextSteps = [];
|
|
95
|
+
if (tasks.length > 0) {
|
|
96
|
+
nextSteps.push(`Use ${FIND_TASKS_BY_DATE} for active tasks or ${GET_OVERVIEW} for current productivity.`);
|
|
97
|
+
if (tasks.some((task) => task.recurring)) {
|
|
98
|
+
nextSteps.push('Recurring tasks will automatically create new instances.');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return summarizeList({
|
|
102
|
+
subject,
|
|
103
|
+
count: tasks.length,
|
|
104
|
+
limit: args.limit,
|
|
105
|
+
nextCursor: nextCursor ?? undefined,
|
|
106
|
+
filterHints,
|
|
107
|
+
previewLines: previewTasks(tasks),
|
|
108
|
+
zeroReasonHints,
|
|
109
|
+
nextSteps,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
export { findCompletedTasks };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
declare const findProjects: {
|
|
3
|
+
name: "find-projects";
|
|
4
|
+
description: string;
|
|
5
|
+
parameters: {
|
|
6
|
+
search: z.ZodOptional<z.ZodString>;
|
|
7
|
+
limit: z.ZodDefault<z.ZodNumber>;
|
|
8
|
+
cursor: z.ZodOptional<z.ZodString>;
|
|
9
|
+
};
|
|
10
|
+
execute(args: {
|
|
11
|
+
limit: number;
|
|
12
|
+
cursor?: string | undefined;
|
|
13
|
+
search?: string | undefined;
|
|
14
|
+
}, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
|
|
15
|
+
content: {
|
|
16
|
+
type: "text";
|
|
17
|
+
text: string;
|
|
18
|
+
}[];
|
|
19
|
+
structuredContent: {
|
|
20
|
+
projects: {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
color: string;
|
|
24
|
+
isFavorite: boolean;
|
|
25
|
+
isShared: boolean;
|
|
26
|
+
parentId: string | null;
|
|
27
|
+
inboxProject: boolean;
|
|
28
|
+
viewStyle: string;
|
|
29
|
+
}[];
|
|
30
|
+
nextCursor: string | null;
|
|
31
|
+
totalCount: number;
|
|
32
|
+
hasMore: boolean;
|
|
33
|
+
appliedFilters: {
|
|
34
|
+
limit: number;
|
|
35
|
+
cursor?: string | undefined;
|
|
36
|
+
search?: string | undefined;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
} | {
|
|
40
|
+
content: ({
|
|
41
|
+
type: "text";
|
|
42
|
+
text: string;
|
|
43
|
+
mimeType?: undefined;
|
|
44
|
+
} | {
|
|
45
|
+
type: "text";
|
|
46
|
+
mimeType: string;
|
|
47
|
+
text: string;
|
|
48
|
+
})[];
|
|
49
|
+
structuredContent?: undefined;
|
|
50
|
+
}>;
|
|
51
|
+
};
|
|
52
|
+
export { findProjects };
|
|
53
|
+
//# sourceMappingURL=find-projects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-projects.d.ts","sourceRoot":"","sources":["../../src/tools/find-projects.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAgCvB,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BwB,CAAA;AA8D1C,OAAO,EAAE,YAAY,EAAE,CAAA"}
|
|
@@ -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 { ADD_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 ${ADD_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 };
|