@doist/todoist-ai 3.0.0 → 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 +2 -18
- package/dist/index.d.ts +175 -47
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -6
- package/dist/main.js +2 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +16 -4
- 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.js +16 -10
- 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-sections.test.js +2 -2
- 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/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/{manage-sections.d.ts → add-sections.d.ts} +21 -13
- 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 +15 -8
- package/dist/tools/add-tasks.d.ts.map +1 -1
- package/dist/tools/add-tasks.js +46 -37
- package/dist/tools/delete-object.d.ts +3 -3
- package/dist/tools/delete-object.d.ts.map +1 -1
- package/dist/tools/delete-object.js +13 -3
- 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-projects.js +2 -2
- package/dist/tools/find-sections.js +4 -4
- 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/utils/constants.d.ts +4 -0
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +4 -0
- package/dist/utils/tool-names.d.ts +7 -2
- package/dist/utils/tool-names.d.ts.map +1 -1
- package/dist/utils/tool-names.js +8 -2
- package/package.json +1 -1
- package/dist/tools/__tests__/manage-projects.test.d.ts +0 -2
- package/dist/tools/__tests__/manage-projects.test.d.ts.map +0 -1
- package/dist/tools/__tests__/manage-projects.test.js +0 -109
- package/dist/tools/__tests__/manage-sections.test.d.ts +0 -2
- package/dist/tools/__tests__/manage-sections.test.d.ts.map +0 -1
- package/dist/tools/__tests__/manage-sections.test.js +0 -162
- package/dist/tools/manage-projects.d.ts +0 -35
- package/dist/tools/manage-projects.d.ts.map +0 -1
- package/dist/tools/manage-projects.js +0 -63
- package/dist/tools/manage-sections.d.ts.map +0 -1
- package/dist/tools/manage-sections.js +0 -78
|
@@ -0,0 +1,82 @@
|
|
|
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 };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { PersonalProject, WorkspaceProject } from '@doist/todoist-api-typescript';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
declare const updateProjects: {
|
|
4
|
+
name: "update-projects";
|
|
5
|
+
description: string;
|
|
6
|
+
parameters: {
|
|
7
|
+
projects: z.ZodArray<z.ZodObject<{
|
|
8
|
+
id: z.ZodString;
|
|
9
|
+
name: z.ZodOptional<z.ZodString>;
|
|
10
|
+
isFavorite: z.ZodOptional<z.ZodBoolean>;
|
|
11
|
+
viewStyle: z.ZodOptional<z.ZodEnum<["list", "board", "calendar"]>>;
|
|
12
|
+
}, "strip", z.ZodTypeAny, {
|
|
13
|
+
id: string;
|
|
14
|
+
name?: string | undefined;
|
|
15
|
+
isFavorite?: boolean | undefined;
|
|
16
|
+
viewStyle?: "list" | "board" | "calendar" | undefined;
|
|
17
|
+
}, {
|
|
18
|
+
id: string;
|
|
19
|
+
name?: string | undefined;
|
|
20
|
+
isFavorite?: boolean | undefined;
|
|
21
|
+
viewStyle?: "list" | "board" | "calendar" | undefined;
|
|
22
|
+
}>, "many">;
|
|
23
|
+
};
|
|
24
|
+
execute(args: {
|
|
25
|
+
projects: {
|
|
26
|
+
id: string;
|
|
27
|
+
name?: string | undefined;
|
|
28
|
+
isFavorite?: boolean | undefined;
|
|
29
|
+
viewStyle?: "list" | "board" | "calendar" | undefined;
|
|
30
|
+
}[];
|
|
31
|
+
}, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
|
|
32
|
+
content: {
|
|
33
|
+
type: "text";
|
|
34
|
+
text: string;
|
|
35
|
+
}[];
|
|
36
|
+
structuredContent: {
|
|
37
|
+
projects: (PersonalProject | WorkspaceProject)[];
|
|
38
|
+
totalCount: number;
|
|
39
|
+
updatedProjectIds: string[];
|
|
40
|
+
appliedOperations: {
|
|
41
|
+
updateCount: number;
|
|
42
|
+
skippedCount: number;
|
|
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 { updateProjects };
|
|
59
|
+
//# sourceMappingURL=update-projects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-projects.d.ts","sourceRoot":"","sources":["../../src/tools/update-projects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AACtF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAqBvB,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCsB,CAAA;AAuD1C,OAAO,EAAE,cAAc,EAAE,CAAA"}
|
|
@@ -0,0 +1,84 @@
|
|
|
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 };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Section } from '@doist/todoist-api-typescript';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
declare const updateSections: {
|
|
4
|
+
name: "update-sections";
|
|
5
|
+
description: string;
|
|
6
|
+
parameters: {
|
|
7
|
+
sections: z.ZodArray<z.ZodObject<{
|
|
8
|
+
id: z.ZodString;
|
|
9
|
+
name: z.ZodString;
|
|
10
|
+
}, "strip", z.ZodTypeAny, {
|
|
11
|
+
name: string;
|
|
12
|
+
id: string;
|
|
13
|
+
}, {
|
|
14
|
+
name: string;
|
|
15
|
+
id: string;
|
|
16
|
+
}>, "many">;
|
|
17
|
+
};
|
|
18
|
+
execute({ sections }: {
|
|
19
|
+
sections: {
|
|
20
|
+
name: string;
|
|
21
|
+
id: string;
|
|
22
|
+
}[];
|
|
23
|
+
}, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
|
|
24
|
+
content: {
|
|
25
|
+
type: "text";
|
|
26
|
+
text: string;
|
|
27
|
+
}[];
|
|
28
|
+
structuredContent: {
|
|
29
|
+
sections: Section[];
|
|
30
|
+
totalCount: number;
|
|
31
|
+
updatedSectionIds: string[];
|
|
32
|
+
};
|
|
33
|
+
} | {
|
|
34
|
+
content: ({
|
|
35
|
+
type: "text";
|
|
36
|
+
text: string;
|
|
37
|
+
mimeType?: undefined;
|
|
38
|
+
} | {
|
|
39
|
+
type: "text";
|
|
40
|
+
mimeType: string;
|
|
41
|
+
text: string;
|
|
42
|
+
})[];
|
|
43
|
+
structuredContent?: undefined;
|
|
44
|
+
}>;
|
|
45
|
+
};
|
|
46
|
+
export { updateSections };
|
|
47
|
+
//# sourceMappingURL=update-sections.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-sections.d.ts","sourceRoot":"","sources":["../../src/tools/update-sections.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAA;AAC5D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAiBvB,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsBsB,CAAA;AAsD1C,OAAO,EAAE,cAAc,EAAE,CAAA"}
|
|
@@ -0,0 +1,70 @@
|
|
|
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 };
|
|
@@ -19,6 +19,10 @@ export declare const ApiLimits: {
|
|
|
19
19
|
readonly PROJECTS_MAX: 100;
|
|
20
20
|
/** Batch size for fetching all tasks in a project */
|
|
21
21
|
readonly TASKS_BATCH_SIZE: 50;
|
|
22
|
+
/** Default limit for comment listings */
|
|
23
|
+
readonly COMMENTS_DEFAULT: 10;
|
|
24
|
+
/** Maximum limit for comment search and list operations */
|
|
25
|
+
readonly COMMENTS_MAX: 10;
|
|
22
26
|
};
|
|
23
27
|
export declare const DisplayLimits: {
|
|
24
28
|
/** Maximum number of failures to show in detailed error messages */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/utils/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,eAAO,MAAM,SAAS;IAClB,sCAAsC;;IAEtC,wDAAwD;;IAExD,wCAAwC;;IAExC,wCAAwC;;IAExC,yCAAyC;;IAEzC,yCAAyC;;IAEzC,qDAAqD;;
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/utils/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,eAAO,MAAM,SAAS;IAClB,sCAAsC;;IAEtC,wDAAwD;;IAExD,wCAAwC;;IAExC,wCAAwC;;IAExC,yCAAyC;;IAEzC,yCAAyC;;IAEzC,qDAAqD;;IAErD,yCAAyC;;IAEzC,2DAA2D;;CAErD,CAAA;AAGV,eAAO,MAAM,aAAa;IACtB,oEAAoE;;IAEpE,gDAAgD;;CAE1C,CAAA;AAGV,eAAO,MAAM,cAAc;IACvB,oDAAoD;;IAEpD,mCAAmC;;CAE7B,CAAA"}
|
package/dist/utils/constants.js
CHANGED
|
@@ -20,6 +20,10 @@ export const ApiLimits = {
|
|
|
20
20
|
PROJECTS_MAX: 100,
|
|
21
21
|
/** Batch size for fetching all tasks in a project */
|
|
22
22
|
TASKS_BATCH_SIZE: 50,
|
|
23
|
+
/** Default limit for comment listings */
|
|
24
|
+
COMMENTS_DEFAULT: 10,
|
|
25
|
+
/** Maximum limit for comment search and list operations */
|
|
26
|
+
COMMENTS_MAX: 10,
|
|
23
27
|
};
|
|
24
28
|
// UI Display Limits
|
|
25
29
|
export const DisplayLimits = {
|
|
@@ -12,10 +12,15 @@ export declare const ToolNames: {
|
|
|
12
12
|
readonly FIND_TASKS: "find-tasks";
|
|
13
13
|
readonly FIND_TASKS_BY_DATE: "find-tasks-by-date";
|
|
14
14
|
readonly FIND_COMPLETED_TASKS: "find-completed-tasks";
|
|
15
|
+
readonly ADD_PROJECTS: "add-projects";
|
|
16
|
+
readonly UPDATE_PROJECTS: "update-projects";
|
|
15
17
|
readonly FIND_PROJECTS: "find-projects";
|
|
16
|
-
readonly
|
|
18
|
+
readonly ADD_SECTIONS: "add-sections";
|
|
19
|
+
readonly UPDATE_SECTIONS: "update-sections";
|
|
17
20
|
readonly FIND_SECTIONS: "find-sections";
|
|
18
|
-
readonly
|
|
21
|
+
readonly ADD_COMMENTS: "add-comments";
|
|
22
|
+
readonly UPDATE_COMMENTS: "update-comments";
|
|
23
|
+
readonly FIND_COMMENTS: "find-comments";
|
|
19
24
|
readonly GET_OVERVIEW: "get-overview";
|
|
20
25
|
readonly DELETE_OBJECT: "delete-object";
|
|
21
26
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-names.d.ts","sourceRoot":"","sources":["../../src/utils/tool-names.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,SAAS
|
|
1
|
+
{"version":3,"file":"tool-names.d.ts","sourceRoot":"","sources":["../../src/utils/tool-names.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;CA2BZ,CAAA;AAGV,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAA"}
|
package/dist/utils/tool-names.js
CHANGED
|
@@ -14,11 +14,17 @@ export const ToolNames = {
|
|
|
14
14
|
FIND_TASKS_BY_DATE: 'find-tasks-by-date',
|
|
15
15
|
FIND_COMPLETED_TASKS: 'find-completed-tasks',
|
|
16
16
|
// Project management tools
|
|
17
|
+
ADD_PROJECTS: 'add-projects',
|
|
18
|
+
UPDATE_PROJECTS: 'update-projects',
|
|
17
19
|
FIND_PROJECTS: 'find-projects',
|
|
18
|
-
MANAGE_PROJECTS: 'manage-projects',
|
|
19
20
|
// Section management tools
|
|
21
|
+
ADD_SECTIONS: 'add-sections',
|
|
22
|
+
UPDATE_SECTIONS: 'update-sections',
|
|
20
23
|
FIND_SECTIONS: 'find-sections',
|
|
21
|
-
|
|
24
|
+
// Comment management tools
|
|
25
|
+
ADD_COMMENTS: 'add-comments',
|
|
26
|
+
UPDATE_COMMENTS: 'update-comments',
|
|
27
|
+
FIND_COMMENTS: 'find-comments',
|
|
22
28
|
// General tools
|
|
23
29
|
GET_OVERVIEW: 'get-overview',
|
|
24
30
|
DELETE_OBJECT: 'delete-object',
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"manage-projects.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/manage-projects.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { jest } from '@jest/globals';
|
|
2
|
-
import { TEST_IDS, createMockProject, extractStructuredContent, extractTextContent, } from '../../utils/test-helpers.js';
|
|
3
|
-
import { ToolNames } from '../../utils/tool-names.js';
|
|
4
|
-
import { manageProjects } from '../manage-projects.js';
|
|
5
|
-
// Mock the Todoist API
|
|
6
|
-
const mockTodoistApi = {
|
|
7
|
-
addProject: jest.fn(),
|
|
8
|
-
updateProject: jest.fn(),
|
|
9
|
-
};
|
|
10
|
-
const { ADD_TASKS, FIND_PROJECTS, MANAGE_PROJECTS, MANAGE_SECTIONS } = ToolNames;
|
|
11
|
-
describe(`${MANAGE_PROJECTS} tool`, () => {
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
jest.clearAllMocks();
|
|
14
|
-
});
|
|
15
|
-
describe('creating a new project', () => {
|
|
16
|
-
it('should create a project and return mapped result', async () => {
|
|
17
|
-
const mockApiResponse = createMockProject({
|
|
18
|
-
id: TEST_IDS.PROJECT_TEST,
|
|
19
|
-
name: 'test-abc123def456-project',
|
|
20
|
-
childOrder: 1,
|
|
21
|
-
createdAt: '2024-01-01T00:00:00Z',
|
|
22
|
-
});
|
|
23
|
-
mockTodoistApi.addProject.mockResolvedValue(mockApiResponse);
|
|
24
|
-
const result = await manageProjects.execute({ name: 'test-abc123def456-project' }, mockTodoistApi);
|
|
25
|
-
// Verify API was called correctly
|
|
26
|
-
expect(mockTodoistApi.addProject).toHaveBeenCalledWith({
|
|
27
|
-
name: 'test-abc123def456-project',
|
|
28
|
-
});
|
|
29
|
-
const textContent = extractTextContent(result);
|
|
30
|
-
expect(textContent).toMatchSnapshot();
|
|
31
|
-
expect(textContent).toContain('Created project: test-abc123def456-project');
|
|
32
|
-
expect(textContent).toContain(`id=${TEST_IDS.PROJECT_TEST}`);
|
|
33
|
-
expect(textContent).toContain(`Use ${ADD_TASKS} to add your first tasks`);
|
|
34
|
-
// Verify structured content
|
|
35
|
-
const structuredContent = extractStructuredContent(result);
|
|
36
|
-
expect(structuredContent).toEqual(expect.objectContaining({
|
|
37
|
-
project: expect.objectContaining({
|
|
38
|
-
id: TEST_IDS.PROJECT_TEST,
|
|
39
|
-
name: 'test-abc123def456-project',
|
|
40
|
-
}),
|
|
41
|
-
operation: 'created',
|
|
42
|
-
}));
|
|
43
|
-
});
|
|
44
|
-
it('should handle different project properties from API', async () => {
|
|
45
|
-
const mockApiResponse = createMockProject({
|
|
46
|
-
id: 'project-456',
|
|
47
|
-
name: 'My Blue Project',
|
|
48
|
-
color: 'blue',
|
|
49
|
-
isFavorite: true,
|
|
50
|
-
isShared: true,
|
|
51
|
-
parentId: 'parent-123',
|
|
52
|
-
viewStyle: 'board',
|
|
53
|
-
childOrder: 2,
|
|
54
|
-
description: 'A test project',
|
|
55
|
-
createdAt: '2024-01-01T00:00:00Z',
|
|
56
|
-
});
|
|
57
|
-
mockTodoistApi.addProject.mockResolvedValue(mockApiResponse);
|
|
58
|
-
const result = await manageProjects.execute({ name: 'My Blue Project' }, mockTodoistApi);
|
|
59
|
-
expect(mockTodoistApi.addProject).toHaveBeenCalledWith({ name: 'My Blue Project' });
|
|
60
|
-
const textContent = extractTextContent(result);
|
|
61
|
-
expect(textContent).toMatchSnapshot();
|
|
62
|
-
expect(textContent).toContain('Created project: My Blue Project');
|
|
63
|
-
expect(textContent).toContain('id=project-456');
|
|
64
|
-
expect(textContent).toContain(`Use ${MANAGE_SECTIONS} to organize this project`);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
describe('updating an existing project', () => {
|
|
68
|
-
it('should update a project when id is provided', async () => {
|
|
69
|
-
const mockApiResponse = {
|
|
70
|
-
url: 'https://todoist.com/projects/existing-project-123',
|
|
71
|
-
id: 'existing-project-123',
|
|
72
|
-
parentId: null,
|
|
73
|
-
isDeleted: false,
|
|
74
|
-
updatedAt: '2025-08-13T22:10:30.000000Z',
|
|
75
|
-
childOrder: 1,
|
|
76
|
-
description: '',
|
|
77
|
-
isCollapsed: false,
|
|
78
|
-
canAssignTasks: false,
|
|
79
|
-
color: 'red',
|
|
80
|
-
isFavorite: false,
|
|
81
|
-
isFrozen: false,
|
|
82
|
-
name: 'Updated Project Name',
|
|
83
|
-
viewStyle: 'list',
|
|
84
|
-
isArchived: false,
|
|
85
|
-
inboxProject: false,
|
|
86
|
-
isShared: false,
|
|
87
|
-
createdAt: '2024-01-01T00:00:00Z',
|
|
88
|
-
defaultOrder: 0,
|
|
89
|
-
};
|
|
90
|
-
mockTodoistApi.updateProject.mockResolvedValue(mockApiResponse);
|
|
91
|
-
const result = await manageProjects.execute({ id: 'existing-project-123', name: 'Updated Project Name' }, mockTodoistApi);
|
|
92
|
-
expect(mockTodoistApi.updateProject).toHaveBeenCalledWith('existing-project-123', {
|
|
93
|
-
name: 'Updated Project Name',
|
|
94
|
-
});
|
|
95
|
-
const textContent = extractTextContent(result);
|
|
96
|
-
expect(textContent).toMatchSnapshot();
|
|
97
|
-
expect(textContent).toContain('Updated project: Updated Project Name');
|
|
98
|
-
expect(textContent).toContain('id=existing-project-123');
|
|
99
|
-
expect(textContent).toContain(`Use ${FIND_PROJECTS} to see all projects`);
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
describe('error handling', () => {
|
|
103
|
-
it('should propagate API errors', async () => {
|
|
104
|
-
const apiError = new Error('API Error: Project name is required');
|
|
105
|
-
mockTodoistApi.addProject.mockRejectedValue(apiError);
|
|
106
|
-
await expect(manageProjects.execute({ name: '' }, mockTodoistApi)).rejects.toThrow('API Error: Project name is required');
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"manage-sections.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/manage-sections.test.ts"],"names":[],"mappings":""}
|