@doist/todoist-ai 4.16.0 → 4.17.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/dist/filter-helpers.d.ts +1 -1
- package/dist/index.d.ts +1044 -196
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +61 -81
- package/dist/main.js +15 -23
- package/dist/mcp-helpers.d.ts +5 -5
- package/dist/mcp-helpers.d.ts.map +1 -1
- package/dist/mcp-server-BADReNAy.js +3092 -0
- package/dist/todoist-tool.d.ts +9 -3
- package/dist/todoist-tool.d.ts.map +1 -1
- package/dist/tool-helpers.d.ts +1 -1
- package/dist/tools/add-comments.d.ts +69 -3
- package/dist/tools/add-comments.d.ts.map +1 -1
- package/dist/tools/add-projects.d.ts +34 -3
- package/dist/tools/add-projects.d.ts.map +1 -1
- package/dist/tools/add-sections.d.ts +14 -1
- package/dist/tools/add-sections.d.ts.map +1 -1
- package/dist/tools/add-tasks.d.ts +65 -10
- package/dist/tools/add-tasks.d.ts.map +1 -1
- package/dist/tools/complete-tasks.d.ts +20 -1
- package/dist/tools/complete-tasks.d.ts.map +1 -1
- package/dist/tools/delete-object.d.ts +16 -3
- package/dist/tools/delete-object.d.ts.map +1 -1
- package/dist/tools/fetch.d.ts +8 -1
- package/dist/tools/fetch.d.ts.map +1 -1
- package/dist/tools/find-activity.d.ts +44 -7
- package/dist/tools/find-activity.d.ts.map +1 -1
- package/dist/tools/find-comments.d.ts +69 -3
- package/dist/tools/find-comments.d.ts.map +1 -1
- package/dist/tools/find-completed-tasks.d.ts +63 -5
- package/dist/tools/find-completed-tasks.d.ts.map +1 -1
- package/dist/tools/find-project-collaborators.d.ts +33 -2
- package/dist/tools/find-project-collaborators.d.ts.map +1 -1
- package/dist/tools/find-projects.d.ts +35 -1
- package/dist/tools/find-projects.d.ts.map +1 -1
- package/dist/tools/find-sections.d.ts +15 -1
- package/dist/tools/find-sections.d.ts.map +1 -1
- package/dist/tools/find-tasks-by-date.d.ts +61 -3
- package/dist/tools/find-tasks-by-date.d.ts.map +1 -1
- package/dist/tools/find-tasks.d.ts +63 -5
- package/dist/tools/find-tasks.d.ts.map +1 -1
- package/dist/tools/get-overview.d.ts +24 -1
- package/dist/tools/get-overview.d.ts.map +1 -1
- package/dist/tools/manage-assignments.d.ts +39 -2
- package/dist/tools/manage-assignments.d.ts.map +1 -1
- package/dist/tools/search.d.ts +17 -1
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/update-comments.d.ts +76 -3
- package/dist/tools/update-comments.d.ts.map +1 -1
- package/dist/tools/update-projects.d.ts +43 -1
- package/dist/tools/update-projects.d.ts.map +1 -1
- package/dist/tools/update-sections.d.ts +17 -3
- package/dist/tools/update-sections.d.ts.map +1 -1
- package/dist/tools/update-tasks.d.ts +79 -13
- package/dist/tools/update-tasks.d.ts.map +1 -1
- package/dist/tools/user-info.d.ts +19 -1
- package/dist/tools/user-info.d.ts.map +1 -1
- package/dist/utils/assignment-validator.d.ts +2 -2
- package/dist/utils/output-schemas.d.ts +233 -0
- package/dist/utils/output-schemas.d.ts.map +1 -0
- package/dist/utils/response-builders.d.ts +1 -3
- package/dist/utils/response-builders.d.ts.map +1 -1
- package/dist/utils/test-helpers.d.ts +1 -1
- package/dist/utils/user-resolver.d.ts +1 -1
- package/package.json +10 -8
- package/dist/filter-helpers.js +0 -79
- package/dist/mcp-helpers.js +0 -71
- package/dist/mcp-server.js +0 -142
- package/dist/todoist-tool.js +0 -1
- package/dist/tool-helpers.js +0 -125
- package/dist/tool-helpers.test.d.ts +0 -2
- package/dist/tool-helpers.test.d.ts.map +0 -1
- package/dist/tool-helpers.test.js +0 -223
- package/dist/tools/__tests__/add-comments.test.d.ts +0 -2
- package/dist/tools/__tests__/add-comments.test.d.ts.map +0 -1
- package/dist/tools/__tests__/add-comments.test.js +0 -241
- package/dist/tools/__tests__/add-projects.test.d.ts +0 -2
- package/dist/tools/__tests__/add-projects.test.d.ts.map +0 -1
- package/dist/tools/__tests__/add-projects.test.js +0 -174
- package/dist/tools/__tests__/add-sections.test.d.ts +0 -2
- package/dist/tools/__tests__/add-sections.test.d.ts.map +0 -1
- package/dist/tools/__tests__/add-sections.test.js +0 -185
- package/dist/tools/__tests__/add-tasks.test.d.ts +0 -2
- package/dist/tools/__tests__/add-tasks.test.d.ts.map +0 -1
- package/dist/tools/__tests__/add-tasks.test.js +0 -606
- package/dist/tools/__tests__/assignment-integration.test.d.ts +0 -2
- package/dist/tools/__tests__/assignment-integration.test.d.ts.map +0 -1
- package/dist/tools/__tests__/assignment-integration.test.js +0 -428
- package/dist/tools/__tests__/complete-tasks.test.d.ts +0 -2
- package/dist/tools/__tests__/complete-tasks.test.d.ts.map +0 -1
- package/dist/tools/__tests__/complete-tasks.test.js +0 -206
- package/dist/tools/__tests__/delete-object.test.d.ts +0 -2
- package/dist/tools/__tests__/delete-object.test.d.ts.map +0 -1
- package/dist/tools/__tests__/delete-object.test.js +0 -110
- package/dist/tools/__tests__/fetch.test.d.ts +0 -2
- package/dist/tools/__tests__/fetch.test.d.ts.map +0 -1
- package/dist/tools/__tests__/fetch.test.js +0 -279
- package/dist/tools/__tests__/find-activity.test.d.ts +0 -2
- package/dist/tools/__tests__/find-activity.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-activity.test.js +0 -229
- package/dist/tools/__tests__/find-comments.test.d.ts +0 -2
- package/dist/tools/__tests__/find-comments.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-comments.test.js +0 -236
- package/dist/tools/__tests__/find-completed-tasks.test.d.ts +0 -2
- package/dist/tools/__tests__/find-completed-tasks.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-completed-tasks.test.js +0 -423
- package/dist/tools/__tests__/find-projects.test.d.ts +0 -2
- package/dist/tools/__tests__/find-projects.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-projects.test.js +0 -154
- package/dist/tools/__tests__/find-sections.test.d.ts +0 -2
- package/dist/tools/__tests__/find-sections.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-sections.test.js +0 -313
- package/dist/tools/__tests__/find-tasks-by-date.test.d.ts +0 -2
- package/dist/tools/__tests__/find-tasks-by-date.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-tasks-by-date.test.js +0 -528
- package/dist/tools/__tests__/find-tasks.test.d.ts +0 -2
- package/dist/tools/__tests__/find-tasks.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-tasks.test.js +0 -771
- package/dist/tools/__tests__/get-overview.test.d.ts +0 -2
- package/dist/tools/__tests__/get-overview.test.d.ts.map +0 -1
- package/dist/tools/__tests__/get-overview.test.js +0 -225
- package/dist/tools/__tests__/search.test.d.ts +0 -2
- package/dist/tools/__tests__/search.test.d.ts.map +0 -1
- package/dist/tools/__tests__/search.test.js +0 -206
- package/dist/tools/__tests__/update-comments.test.d.ts +0 -2
- package/dist/tools/__tests__/update-comments.test.d.ts.map +0 -1
- package/dist/tools/__tests__/update-comments.test.js +0 -294
- package/dist/tools/__tests__/update-projects.test.d.ts +0 -2
- package/dist/tools/__tests__/update-projects.test.d.ts.map +0 -1
- package/dist/tools/__tests__/update-projects.test.js +0 -217
- package/dist/tools/__tests__/update-sections.test.d.ts +0 -2
- package/dist/tools/__tests__/update-sections.test.d.ts.map +0 -1
- package/dist/tools/__tests__/update-sections.test.js +0 -169
- package/dist/tools/__tests__/update-tasks.test.d.ts +0 -2
- package/dist/tools/__tests__/update-tasks.test.d.ts.map +0 -1
- package/dist/tools/__tests__/update-tasks.test.js +0 -788
- package/dist/tools/__tests__/user-info.test.d.ts +0 -2
- package/dist/tools/__tests__/user-info.test.d.ts.map +0 -1
- package/dist/tools/__tests__/user-info.test.js +0 -139
- package/dist/tools/add-comments.js +0 -89
- package/dist/tools/add-projects.js +0 -63
- package/dist/tools/add-sections.js +0 -74
- package/dist/tools/add-tasks.js +0 -169
- package/dist/tools/complete-tasks.js +0 -68
- package/dist/tools/delete-object.js +0 -79
- package/dist/tools/fetch.js +0 -102
- package/dist/tools/find-activity.js +0 -221
- package/dist/tools/find-comments.js +0 -148
- package/dist/tools/find-completed-tasks.js +0 -168
- package/dist/tools/find-project-collaborators.js +0 -151
- package/dist/tools/find-projects.js +0 -101
- package/dist/tools/find-sections.js +0 -101
- package/dist/tools/find-tasks-by-date.js +0 -198
- package/dist/tools/find-tasks.js +0 -329
- package/dist/tools/get-overview.js +0 -249
- package/dist/tools/manage-assignments.js +0 -337
- package/dist/tools/search.js +0 -65
- package/dist/tools/update-comments.js +0 -82
- package/dist/tools/update-projects.js +0 -84
- package/dist/tools/update-sections.js +0 -70
- package/dist/tools/update-tasks.js +0 -179
- package/dist/tools/user-info.js +0 -142
- package/dist/utils/assignment-validator.js +0 -253
- package/dist/utils/constants.js +0 -45
- package/dist/utils/duration-parser.js +0 -96
- package/dist/utils/duration-parser.test.d.ts +0 -2
- package/dist/utils/duration-parser.test.d.ts.map +0 -1
- package/dist/utils/duration-parser.test.js +0 -147
- package/dist/utils/labels.js +0 -18
- package/dist/utils/priorities.js +0 -20
- package/dist/utils/response-builders.js +0 -210
- package/dist/utils/sanitize-data.js +0 -37
- package/dist/utils/sanitize-data.test.d.ts +0 -2
- package/dist/utils/sanitize-data.test.d.ts.map +0 -1
- package/dist/utils/sanitize-data.test.js +0 -93
- package/dist/utils/test-helpers.js +0 -237
- package/dist/utils/tool-names.js +0 -40
- package/dist/utils/user-resolver.js +0 -179
|
@@ -1,82 +0,0 @@
|
|
|
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_COMMENTS, DELETE_OBJECT } = ToolNames;
|
|
6
|
-
const CommentUpdateSchema = z.object({
|
|
7
|
-
id: z.string().min(1).describe('The ID of the comment to update.'),
|
|
8
|
-
content: z.string().min(1).describe('The new content for the comment.'),
|
|
9
|
-
});
|
|
10
|
-
const ArgsSchema = {
|
|
11
|
-
comments: z.array(CommentUpdateSchema).min(1).describe('The comments to update.'),
|
|
12
|
-
};
|
|
13
|
-
const updateComments = {
|
|
14
|
-
name: ToolNames.UPDATE_COMMENTS,
|
|
15
|
-
description: 'Update multiple existing comments with new content.',
|
|
16
|
-
parameters: ArgsSchema,
|
|
17
|
-
async execute(args, client) {
|
|
18
|
-
const { comments } = args;
|
|
19
|
-
const updateCommentPromises = comments.map(async (comment) => {
|
|
20
|
-
return await client.updateComment(comment.id, { content: comment.content });
|
|
21
|
-
});
|
|
22
|
-
const updatedComments = await Promise.all(updateCommentPromises);
|
|
23
|
-
const textContent = generateTextContent({
|
|
24
|
-
comments: updatedComments,
|
|
25
|
-
});
|
|
26
|
-
return getToolOutput({
|
|
27
|
-
textContent,
|
|
28
|
-
structuredContent: {
|
|
29
|
-
comments: updatedComments,
|
|
30
|
-
totalCount: updatedComments.length,
|
|
31
|
-
updatedCommentIds: updatedComments.map((comment) => comment.id),
|
|
32
|
-
appliedOperations: {
|
|
33
|
-
updateCount: updatedComments.length,
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
});
|
|
37
|
-
},
|
|
38
|
-
};
|
|
39
|
-
function generateNextSteps(comments) {
|
|
40
|
-
const nextSteps = [];
|
|
41
|
-
// Early return for empty comments
|
|
42
|
-
if (comments.length === 0) {
|
|
43
|
-
return nextSteps;
|
|
44
|
-
}
|
|
45
|
-
// Multiple comments case
|
|
46
|
-
if (comments.length > 1) {
|
|
47
|
-
nextSteps.push(`Use ${FIND_COMMENTS} to view comments by task or project`);
|
|
48
|
-
nextSteps.push(`Use ${DELETE_OBJECT} with type=comment to remove comments`);
|
|
49
|
-
return nextSteps;
|
|
50
|
-
}
|
|
51
|
-
// Single comment case
|
|
52
|
-
const comment = comments[0];
|
|
53
|
-
if (!comment)
|
|
54
|
-
return nextSteps;
|
|
55
|
-
if (comment.taskId) {
|
|
56
|
-
nextSteps.push(`Use ${FIND_COMMENTS} with taskId=${comment.taskId} to see all task comments`);
|
|
57
|
-
}
|
|
58
|
-
else if (comment.projectId) {
|
|
59
|
-
nextSteps.push(`Use ${FIND_COMMENTS} with projectId=${comment.projectId} to see all project comments`);
|
|
60
|
-
}
|
|
61
|
-
nextSteps.push(`Use ${DELETE_OBJECT} with type=comment id=${comment.id} to remove comment`);
|
|
62
|
-
return nextSteps;
|
|
63
|
-
}
|
|
64
|
-
function generateTextContent({ comments }) {
|
|
65
|
-
// Group comments by entity type and count
|
|
66
|
-
const taskComments = comments.filter((c) => c.taskId).length;
|
|
67
|
-
const projectComments = comments.filter((c) => c.projectId).length;
|
|
68
|
-
const parts = [];
|
|
69
|
-
if (taskComments > 0) {
|
|
70
|
-
const commentsLabel = taskComments > 1 ? 'comments' : 'comment';
|
|
71
|
-
parts.push(`${taskComments} task ${commentsLabel}`);
|
|
72
|
-
}
|
|
73
|
-
if (projectComments > 0) {
|
|
74
|
-
const commentsLabel = projectComments > 1 ? 'comments' : 'comment';
|
|
75
|
-
parts.push(`${projectComments} project ${commentsLabel}`);
|
|
76
|
-
}
|
|
77
|
-
const summary = parts.length > 0 ? `Updated ${parts.join(' and ')}` : 'No comments updated';
|
|
78
|
-
const nextSteps = generateNextSteps(comments);
|
|
79
|
-
const next = formatNextSteps(nextSteps);
|
|
80
|
-
return `${summary}\n${next}`;
|
|
81
|
-
}
|
|
82
|
-
export { updateComments };
|
|
@@ -1,84 +0,0 @@
|
|
|
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, FIND_TASKS, GET_OVERVIEW } = ToolNames;
|
|
6
|
-
const ProjectUpdateSchema = z.object({
|
|
7
|
-
id: z.string().min(1).describe('The ID of the project to update.'),
|
|
8
|
-
name: z.string().min(1).optional().describe('The new name of the project.'),
|
|
9
|
-
isFavorite: z.boolean().optional().describe('Whether the project is a favorite.'),
|
|
10
|
-
viewStyle: z.enum(['list', 'board', 'calendar']).optional().describe('The project view style.'),
|
|
11
|
-
});
|
|
12
|
-
const ArgsSchema = {
|
|
13
|
-
projects: z.array(ProjectUpdateSchema).min(1).describe('The projects to update.'),
|
|
14
|
-
};
|
|
15
|
-
const updateProjects = {
|
|
16
|
-
name: ToolNames.UPDATE_PROJECTS,
|
|
17
|
-
description: 'Update multiple existing projects with new values.',
|
|
18
|
-
parameters: ArgsSchema,
|
|
19
|
-
async execute(args, client) {
|
|
20
|
-
const { projects } = args;
|
|
21
|
-
const updateProjectsPromises = projects.map(async (project) => {
|
|
22
|
-
if (!hasUpdatesToMake(project)) {
|
|
23
|
-
return undefined;
|
|
24
|
-
}
|
|
25
|
-
const { id, ...updateArgs } = project;
|
|
26
|
-
return await client.updateProject(id, updateArgs);
|
|
27
|
-
});
|
|
28
|
-
const updatedProjects = (await Promise.all(updateProjectsPromises)).filter((project) => project !== undefined);
|
|
29
|
-
const textContent = generateTextContent({
|
|
30
|
-
projects: updatedProjects,
|
|
31
|
-
args,
|
|
32
|
-
});
|
|
33
|
-
return getToolOutput({
|
|
34
|
-
textContent,
|
|
35
|
-
structuredContent: {
|
|
36
|
-
projects: updatedProjects,
|
|
37
|
-
totalCount: updatedProjects.length,
|
|
38
|
-
updatedProjectIds: updatedProjects.map((project) => project.id),
|
|
39
|
-
appliedOperations: {
|
|
40
|
-
updateCount: updatedProjects.length,
|
|
41
|
-
skippedCount: projects.length - updatedProjects.length,
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
function generateTextContent({ projects, args, }) {
|
|
48
|
-
const totalRequested = args.projects.length;
|
|
49
|
-
const actuallyUpdated = projects.length;
|
|
50
|
-
const skipped = totalRequested - actuallyUpdated;
|
|
51
|
-
const count = projects.length;
|
|
52
|
-
const projectList = projects.map((project) => `• ${project.name} (id=${project.id})`).join('\n');
|
|
53
|
-
let summary = `Updated ${count} project${count === 1 ? '' : 's'}`;
|
|
54
|
-
if (skipped > 0) {
|
|
55
|
-
summary += ` (${skipped} skipped - no changes)`;
|
|
56
|
-
}
|
|
57
|
-
if (count > 0) {
|
|
58
|
-
summary += `:\n${projectList}`;
|
|
59
|
-
}
|
|
60
|
-
// Context-aware next steps for updated projects
|
|
61
|
-
const nextSteps = [];
|
|
62
|
-
if (projects.length > 0) {
|
|
63
|
-
if (count === 1) {
|
|
64
|
-
const project = projects[0];
|
|
65
|
-
if (project) {
|
|
66
|
-
nextSteps.push(`Use ${GET_OVERVIEW} with projectId=${project.id} to see project structure`);
|
|
67
|
-
nextSteps.push(`Use ${FIND_TASKS} with projectId=${project.id} to review existing tasks`);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
nextSteps.push(`Use ${FIND_PROJECTS} to see all projects with updated names`);
|
|
72
|
-
nextSteps.push(`Use ${GET_OVERVIEW} to see updated project hierarchy`);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
nextSteps.push(`Use ${FIND_PROJECTS} to see current projects`);
|
|
77
|
-
}
|
|
78
|
-
const next = formatNextSteps(nextSteps);
|
|
79
|
-
return `${summary}\n${next}`;
|
|
80
|
-
}
|
|
81
|
-
function hasUpdatesToMake({ id, ...otherUpdateArgs }) {
|
|
82
|
-
return Object.keys(otherUpdateArgs).length > 0;
|
|
83
|
-
}
|
|
84
|
-
export { updateProjects };
|
|
@@ -1,70 +0,0 @@
|
|
|
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_TASKS, GET_OVERVIEW, FIND_SECTIONS } = ToolNames;
|
|
6
|
-
const SectionUpdateSchema = z.object({
|
|
7
|
-
id: z.string().min(1).describe('The ID of the section to update.'),
|
|
8
|
-
name: z.string().min(1).describe('The new name of the section.'),
|
|
9
|
-
});
|
|
10
|
-
const ArgsSchema = {
|
|
11
|
-
sections: z.array(SectionUpdateSchema).min(1).describe('The sections to update.'),
|
|
12
|
-
};
|
|
13
|
-
const updateSections = {
|
|
14
|
-
name: ToolNames.UPDATE_SECTIONS,
|
|
15
|
-
description: 'Update multiple existing sections with new values.',
|
|
16
|
-
parameters: ArgsSchema,
|
|
17
|
-
async execute({ sections }, client) {
|
|
18
|
-
const updatedSections = await Promise.all(sections.map((section) => client.updateSection(section.id, { name: section.name })));
|
|
19
|
-
const textContent = generateTextContent({
|
|
20
|
-
sections: updatedSections,
|
|
21
|
-
});
|
|
22
|
-
return getToolOutput({
|
|
23
|
-
textContent,
|
|
24
|
-
structuredContent: {
|
|
25
|
-
sections: updatedSections,
|
|
26
|
-
totalCount: updatedSections.length,
|
|
27
|
-
updatedSectionIds: updatedSections.map((section) => section.id),
|
|
28
|
-
},
|
|
29
|
-
});
|
|
30
|
-
},
|
|
31
|
-
};
|
|
32
|
-
function generateNextSteps(sections) {
|
|
33
|
-
// Handle empty sections first (early return)
|
|
34
|
-
if (sections.length === 0) {
|
|
35
|
-
return [`Use ${FIND_SECTIONS} to see current sections`];
|
|
36
|
-
}
|
|
37
|
-
// Handle single section case
|
|
38
|
-
if (sections.length === 1) {
|
|
39
|
-
const section = sections[0];
|
|
40
|
-
if (!section)
|
|
41
|
-
return [];
|
|
42
|
-
return [
|
|
43
|
-
`Use ${FIND_TASKS} with sectionId=${section.id} to see existing tasks`,
|
|
44
|
-
`Use ${GET_OVERVIEW} with projectId=${section.projectId} to see project structure`,
|
|
45
|
-
'Consider updating task descriptions if section purpose changed',
|
|
46
|
-
];
|
|
47
|
-
}
|
|
48
|
-
// Handle multiple sections case
|
|
49
|
-
const projectIds = [...new Set(sections.map((s) => s.projectId))];
|
|
50
|
-
const steps = [`Use ${FIND_SECTIONS} to see all sections with updated names`];
|
|
51
|
-
if (projectIds.length === 1) {
|
|
52
|
-
steps.push(`Use ${GET_OVERVIEW} with projectId=${projectIds[0]} to see updated project structure`);
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
steps.push(`Use ${GET_OVERVIEW} to see updated project structures`);
|
|
56
|
-
}
|
|
57
|
-
steps.push('Consider updating task descriptions if section purposes changed');
|
|
58
|
-
return steps;
|
|
59
|
-
}
|
|
60
|
-
function generateTextContent({ sections }) {
|
|
61
|
-
const count = sections.length;
|
|
62
|
-
const sectionList = sections
|
|
63
|
-
.map((section) => `• ${section.name} (id=${section.id}, projectId=${section.projectId})`)
|
|
64
|
-
.join('\n');
|
|
65
|
-
const summary = `Updated ${count} section${count === 1 ? '' : 's'}:\n${sectionList}`;
|
|
66
|
-
const nextSteps = generateNextSteps(sections);
|
|
67
|
-
const next = formatNextSteps(nextSteps);
|
|
68
|
-
return `${summary}\n${next}`;
|
|
69
|
-
}
|
|
70
|
-
export { updateSections };
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import { getToolOutput } from '../mcp-helpers.js';
|
|
3
|
-
import { createMoveTaskArgs, mapTask } from '../tool-helpers.js';
|
|
4
|
-
import { assignmentValidator } from '../utils/assignment-validator.js';
|
|
5
|
-
import { DurationParseError, parseDuration } from '../utils/duration-parser.js';
|
|
6
|
-
import { convertPriorityToNumber, PrioritySchema } from '../utils/priorities.js';
|
|
7
|
-
import { summarizeTaskOperation } from '../utils/response-builders.js';
|
|
8
|
-
import { ToolNames } from '../utils/tool-names.js';
|
|
9
|
-
const { FIND_TASKS_BY_DATE, GET_OVERVIEW } = ToolNames;
|
|
10
|
-
const TasksUpdateSchema = z.object({
|
|
11
|
-
id: z.string().min(1).describe('The ID of the task to update.'),
|
|
12
|
-
content: z
|
|
13
|
-
.string()
|
|
14
|
-
.optional()
|
|
15
|
-
.describe('The new task name/title. Should be concise and actionable (e.g., "Review PR #123", "Call dentist"). For longer content, use the description field instead. Supports Markdown.'),
|
|
16
|
-
description: z
|
|
17
|
-
.string()
|
|
18
|
-
.optional()
|
|
19
|
-
.describe('New additional details, notes, or context for the task. Use this for longer content rather than putting it in the task name. Supports Markdown.'),
|
|
20
|
-
projectId: z
|
|
21
|
-
.string()
|
|
22
|
-
.optional()
|
|
23
|
-
.describe('The new project ID for the task. Project ID should be an ID string, or the text "inbox", for inbox tasks.'),
|
|
24
|
-
sectionId: z.string().optional().describe('The new section ID for the task.'),
|
|
25
|
-
parentId: z.string().optional().describe('The new parent task ID (for subtasks).'),
|
|
26
|
-
order: z.number().optional().describe('The new order of the task within its parent/section.'),
|
|
27
|
-
priority: PrioritySchema.optional().describe('The new priority of the task: p1 (highest), p2 (high), p3 (medium), p4 (lowest/default).'),
|
|
28
|
-
dueString: z
|
|
29
|
-
.string()
|
|
30
|
-
.optional()
|
|
31
|
-
.describe("The new due date for the task, in natural language (e.g., 'tomorrow at 5pm')."),
|
|
32
|
-
deadlineDate: z
|
|
33
|
-
.string()
|
|
34
|
-
.optional()
|
|
35
|
-
.describe('The new deadline date for the task in ISO 8601 format (YYYY-MM-DD, e.g., "2025-12-31"). Deadlines are immovable constraints shown with a different indicator than due dates. Use "remove" to clear the deadline.'),
|
|
36
|
-
duration: z
|
|
37
|
-
.string()
|
|
38
|
-
.optional()
|
|
39
|
-
.describe('The duration of the task. Use format: "2h" (hours), "90m" (minutes), "2h30m" (combined), or "1.5h" (decimal hours). Max 24h.'),
|
|
40
|
-
responsibleUser: z
|
|
41
|
-
.string()
|
|
42
|
-
.optional()
|
|
43
|
-
.describe('Change task assignment. Use "unassign" to remove assignment. Can be user ID, name, or email. User must be a project collaborator.'),
|
|
44
|
-
labels: z
|
|
45
|
-
.array(z.string())
|
|
46
|
-
.optional()
|
|
47
|
-
.describe('The new labels for the task. Replaces all existing labels.'),
|
|
48
|
-
});
|
|
49
|
-
const ArgsSchema = {
|
|
50
|
-
tasks: z.array(TasksUpdateSchema).min(1).describe('The tasks to update.'),
|
|
51
|
-
};
|
|
52
|
-
const updateTasks = {
|
|
53
|
-
name: ToolNames.UPDATE_TASKS,
|
|
54
|
-
description: 'Update existing tasks including content, dates, priorities, and assignments.',
|
|
55
|
-
parameters: ArgsSchema,
|
|
56
|
-
async execute(args, client) {
|
|
57
|
-
const { tasks } = args;
|
|
58
|
-
const updateTasksPromises = tasks.map(async (task) => {
|
|
59
|
-
if (!hasUpdatesToMake(task)) {
|
|
60
|
-
return undefined;
|
|
61
|
-
}
|
|
62
|
-
const { id, projectId, sectionId, parentId, duration: durationStr, responsibleUser, priority, labels, deadlineDate, ...otherUpdateArgs } = task;
|
|
63
|
-
// Resolve "inbox" to actual inbox project ID if needed
|
|
64
|
-
let resolvedProjectId = projectId;
|
|
65
|
-
if (projectId === 'inbox') {
|
|
66
|
-
const todoistUser = await client.getUser();
|
|
67
|
-
resolvedProjectId = todoistUser.inboxProjectId;
|
|
68
|
-
}
|
|
69
|
-
let updateArgs = {
|
|
70
|
-
...otherUpdateArgs,
|
|
71
|
-
...(labels !== undefined && { labels }),
|
|
72
|
-
};
|
|
73
|
-
// Handle priority conversion if provided
|
|
74
|
-
if (priority) {
|
|
75
|
-
updateArgs.priority = convertPriorityToNumber(priority);
|
|
76
|
-
}
|
|
77
|
-
// Handle deadline changes if provided
|
|
78
|
-
if (deadlineDate !== undefined) {
|
|
79
|
-
if (deadlineDate === null || deadlineDate === 'remove') {
|
|
80
|
-
// Remove deadline - support both legacy null and new "remove" string
|
|
81
|
-
updateArgs = { ...updateArgs, deadlineDate: null };
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
// Set new deadline
|
|
85
|
-
updateArgs = { ...updateArgs, deadlineDate };
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
// Parse duration if provided
|
|
89
|
-
if (durationStr) {
|
|
90
|
-
try {
|
|
91
|
-
const { minutes } = parseDuration(durationStr);
|
|
92
|
-
updateArgs = {
|
|
93
|
-
...updateArgs,
|
|
94
|
-
duration: minutes,
|
|
95
|
-
durationUnit: 'minute',
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
catch (error) {
|
|
99
|
-
if (error instanceof DurationParseError) {
|
|
100
|
-
throw new Error(`Task ${id}: ${error.message}`);
|
|
101
|
-
}
|
|
102
|
-
throw error;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
// Handle assignment changes if provided
|
|
106
|
-
if (responsibleUser !== undefined) {
|
|
107
|
-
if (responsibleUser === null || responsibleUser === 'unassign') {
|
|
108
|
-
// Unassign task - no validation needed
|
|
109
|
-
updateArgs = { ...updateArgs, assigneeId: null };
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
// Validate assignment using comprehensive validator
|
|
113
|
-
const validation = await assignmentValidator.validateTaskUpdateAssignment(client, id, responsibleUser);
|
|
114
|
-
if (!validation.isValid) {
|
|
115
|
-
const errorMsg = validation.error?.message || 'Assignment validation failed';
|
|
116
|
-
const suggestions = validation.error?.suggestions?.join('. ') || '';
|
|
117
|
-
throw new Error(`Task ${id}: ${errorMsg}${suggestions ? `. ${suggestions}` : ''}`);
|
|
118
|
-
}
|
|
119
|
-
// Use the validated assignee ID
|
|
120
|
-
updateArgs = { ...updateArgs, assigneeId: validation.resolvedUser?.userId };
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
// If no move parameters are provided, use updateTask without moveTask
|
|
124
|
-
if (!resolvedProjectId && !sectionId && !parentId) {
|
|
125
|
-
return await client.updateTask(id, updateArgs);
|
|
126
|
-
}
|
|
127
|
-
const moveArgs = createMoveTaskArgs(id, resolvedProjectId, sectionId, parentId);
|
|
128
|
-
const movedTask = await client.moveTask(id, moveArgs);
|
|
129
|
-
if (Object.keys(updateArgs).length > 0) {
|
|
130
|
-
return await client.updateTask(id, updateArgs);
|
|
131
|
-
}
|
|
132
|
-
return movedTask;
|
|
133
|
-
});
|
|
134
|
-
const updatedTasks = (await Promise.all(updateTasksPromises)).filter((task) => task !== undefined);
|
|
135
|
-
const mappedTasks = updatedTasks.map(mapTask);
|
|
136
|
-
const textContent = generateTextContent({
|
|
137
|
-
tasks: mappedTasks,
|
|
138
|
-
args,
|
|
139
|
-
});
|
|
140
|
-
return getToolOutput({
|
|
141
|
-
textContent,
|
|
142
|
-
structuredContent: {
|
|
143
|
-
tasks: mappedTasks,
|
|
144
|
-
totalCount: mappedTasks.length,
|
|
145
|
-
updatedTaskIds: updatedTasks.map((task) => task.id),
|
|
146
|
-
appliedOperations: {
|
|
147
|
-
updateCount: mappedTasks.length,
|
|
148
|
-
skippedCount: tasks.length - mappedTasks.length,
|
|
149
|
-
},
|
|
150
|
-
},
|
|
151
|
-
});
|
|
152
|
-
},
|
|
153
|
-
};
|
|
154
|
-
function generateTextContent({ tasks, args, }) {
|
|
155
|
-
const totalRequested = args.tasks.length;
|
|
156
|
-
const actuallyUpdated = tasks.length;
|
|
157
|
-
const skipped = totalRequested - actuallyUpdated;
|
|
158
|
-
let context = '';
|
|
159
|
-
if (skipped > 0) {
|
|
160
|
-
context = ` (${skipped} skipped - no changes)`;
|
|
161
|
-
}
|
|
162
|
-
const nextSteps = [];
|
|
163
|
-
if (tasks.length > 0) {
|
|
164
|
-
nextSteps.push(`Use ${FIND_TASKS_BY_DATE} to see your updated schedule`);
|
|
165
|
-
nextSteps.push(`Use ${GET_OVERVIEW} to see updated project organization`);
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
nextSteps.push(`Use ${FIND_TASKS_BY_DATE} to see current tasks`);
|
|
169
|
-
}
|
|
170
|
-
return summarizeTaskOperation('Updated', tasks, {
|
|
171
|
-
context,
|
|
172
|
-
nextSteps,
|
|
173
|
-
showDetails: tasks.length <= 5,
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
function hasUpdatesToMake({ id, ...otherUpdateArgs }) {
|
|
177
|
-
return Object.keys(otherUpdateArgs).length > 0;
|
|
178
|
-
}
|
|
179
|
-
export { updateTasks };
|
package/dist/tools/user-info.js
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { getToolOutput } from '../mcp-helpers.js';
|
|
2
|
-
import { ToolNames } from '../utils/tool-names.js';
|
|
3
|
-
const ArgsSchema = {};
|
|
4
|
-
function getUserPlan(user) {
|
|
5
|
-
if (user.businessAccountId) {
|
|
6
|
-
return 'Todoist Business';
|
|
7
|
-
}
|
|
8
|
-
if (user.isPremium) {
|
|
9
|
-
return 'Todoist Pro';
|
|
10
|
-
}
|
|
11
|
-
return 'Todoist Free';
|
|
12
|
-
}
|
|
13
|
-
// Helper functions for date and time calculations
|
|
14
|
-
function getWeekStartDate(date, startDay) {
|
|
15
|
-
const currentDay = date.getDay() || 7; // Convert Sunday (0) to 7 for ISO format
|
|
16
|
-
const daysFromStart = (currentDay - startDay + 7) % 7;
|
|
17
|
-
const weekStart = new Date(date);
|
|
18
|
-
weekStart.setDate(date.getDate() - daysFromStart);
|
|
19
|
-
return weekStart;
|
|
20
|
-
}
|
|
21
|
-
function getWeekEndDate(weekStart) {
|
|
22
|
-
const weekEnd = new Date(weekStart);
|
|
23
|
-
weekEnd.setDate(weekStart.getDate() + 6);
|
|
24
|
-
return weekEnd;
|
|
25
|
-
}
|
|
26
|
-
function getWeekNumber(date) {
|
|
27
|
-
const firstDayOfYear = new Date(date.getFullYear(), 0, 1);
|
|
28
|
-
const pastDaysOfYear = (date.getTime() - firstDayOfYear.getTime()) / 86400000;
|
|
29
|
-
return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7);
|
|
30
|
-
}
|
|
31
|
-
function getDayName(dayNumber) {
|
|
32
|
-
const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
|
33
|
-
// Convert ISO day number (1=Monday, 7=Sunday) to array index (0=Sunday, 6=Saturday)
|
|
34
|
-
const index = dayNumber === 7 ? 0 : dayNumber;
|
|
35
|
-
return days[index] ?? 'Unknown';
|
|
36
|
-
}
|
|
37
|
-
function formatDate(date) {
|
|
38
|
-
return date.toISOString().split('T')[0] ?? '';
|
|
39
|
-
}
|
|
40
|
-
function isValidTimezone(timezone) {
|
|
41
|
-
try {
|
|
42
|
-
// Test if the timezone is valid by attempting to format a date with it
|
|
43
|
-
new Intl.DateTimeFormat('en-US', { timeZone: timezone });
|
|
44
|
-
return true;
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
function getSafeTimezone(timezone) {
|
|
51
|
-
return isValidTimezone(timezone) ? timezone : 'UTC';
|
|
52
|
-
}
|
|
53
|
-
function formatLocalTime(date, timezone) {
|
|
54
|
-
const safeTimezone = getSafeTimezone(timezone);
|
|
55
|
-
return date.toLocaleString('en-US', {
|
|
56
|
-
timeZone: safeTimezone,
|
|
57
|
-
year: 'numeric',
|
|
58
|
-
month: '2-digit',
|
|
59
|
-
day: '2-digit',
|
|
60
|
-
hour: '2-digit',
|
|
61
|
-
minute: '2-digit',
|
|
62
|
-
second: '2-digit',
|
|
63
|
-
hour12: false,
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
async function generateUserInfo(client) {
|
|
67
|
-
// Get user information from Todoist API
|
|
68
|
-
const user = await client.getUser();
|
|
69
|
-
// Parse timezone from user data and ensure it's valid
|
|
70
|
-
const rawTimezone = user.tzInfo?.timezone ?? 'UTC';
|
|
71
|
-
const timezone = getSafeTimezone(rawTimezone);
|
|
72
|
-
// Get current time in user's timezone
|
|
73
|
-
const now = new Date();
|
|
74
|
-
const localTime = formatLocalTime(now, timezone);
|
|
75
|
-
// Calculate week information based on user's start day
|
|
76
|
-
const startDay = user.startDay ?? 1; // Default to Monday if not set
|
|
77
|
-
const startDayName = getDayName(startDay);
|
|
78
|
-
// Determine user's plan
|
|
79
|
-
const plan = getUserPlan(user);
|
|
80
|
-
// Create a date object in user's timezone for accurate week calculations
|
|
81
|
-
const userDate = new Date(now.toLocaleString('en-US', { timeZone: timezone }));
|
|
82
|
-
const weekStart = getWeekStartDate(userDate, startDay);
|
|
83
|
-
const weekEnd = getWeekEndDate(weekStart);
|
|
84
|
-
const weekNumber = getWeekNumber(userDate);
|
|
85
|
-
// Generate markdown text content
|
|
86
|
-
const lines = [
|
|
87
|
-
'# User Information',
|
|
88
|
-
'',
|
|
89
|
-
`**User ID:** ${user.id}`,
|
|
90
|
-
`**Full Name:** ${user.fullName}`,
|
|
91
|
-
`**Email:** ${user.email}`,
|
|
92
|
-
`**Timezone:** ${timezone}`,
|
|
93
|
-
`**Current Local Time:** ${localTime}`,
|
|
94
|
-
'',
|
|
95
|
-
'## Week Settings',
|
|
96
|
-
`**Week Start Day:** ${startDayName} (${startDay})`,
|
|
97
|
-
`**Current Week:** Week ${weekNumber}`,
|
|
98
|
-
`**Week Start Date:** ${formatDate(weekStart)}`,
|
|
99
|
-
`**Week End Date:** ${formatDate(weekEnd)}`,
|
|
100
|
-
'',
|
|
101
|
-
'## Daily Progress',
|
|
102
|
-
`**Completed Today:** ${user.completedToday}`,
|
|
103
|
-
`**Daily Goal:** ${user.dailyGoal}`,
|
|
104
|
-
`**Weekly Goal:** ${user.weeklyGoal}`,
|
|
105
|
-
'',
|
|
106
|
-
'## Account Info',
|
|
107
|
-
`**Plan:** ${plan}`,
|
|
108
|
-
];
|
|
109
|
-
const textContent = lines.join('\n');
|
|
110
|
-
// Generate structured content
|
|
111
|
-
const structuredContent = {
|
|
112
|
-
type: 'user_info',
|
|
113
|
-
userId: user.id,
|
|
114
|
-
fullName: user.fullName,
|
|
115
|
-
timezone: timezone,
|
|
116
|
-
currentLocalTime: localTime,
|
|
117
|
-
startDay: startDay,
|
|
118
|
-
startDayName: startDayName,
|
|
119
|
-
weekStartDate: formatDate(weekStart),
|
|
120
|
-
weekEndDate: formatDate(weekEnd),
|
|
121
|
-
currentWeekNumber: weekNumber,
|
|
122
|
-
completedToday: user.completedToday,
|
|
123
|
-
dailyGoal: user.dailyGoal,
|
|
124
|
-
weeklyGoal: user.weeklyGoal,
|
|
125
|
-
email: user.email,
|
|
126
|
-
plan: plan,
|
|
127
|
-
};
|
|
128
|
-
return { textContent, structuredContent };
|
|
129
|
-
}
|
|
130
|
-
const userInfo = {
|
|
131
|
-
name: ToolNames.USER_INFO,
|
|
132
|
-
description: 'Get comprehensive user information including user ID, full name, email, timezone with current local time, week start day preferences, current week dates, daily/weekly goal progress, and user plan (Free/Pro/Business).',
|
|
133
|
-
parameters: ArgsSchema,
|
|
134
|
-
async execute(_args, client) {
|
|
135
|
-
const result = await generateUserInfo(client);
|
|
136
|
-
return getToolOutput({
|
|
137
|
-
textContent: result.textContent,
|
|
138
|
-
structuredContent: result.structuredContent,
|
|
139
|
-
});
|
|
140
|
-
},
|
|
141
|
-
};
|
|
142
|
-
export { userInfo };
|