@doist/todoist-ai 4.4.0 → 4.5.1
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/index.d.ts +160 -35
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -16
- package/dist/mcp-helpers.d.ts.map +1 -1
- package/dist/mcp-helpers.js +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +78 -17
- package/dist/tool-helpers.d.ts +4 -0
- package/dist/tool-helpers.d.ts.map +1 -1
- package/dist/tool-helpers.js +2 -0
- package/dist/tools/__tests__/add-projects.test.js +1 -1
- package/dist/tools/__tests__/add-sections.test.js +1 -1
- package/dist/tools/__tests__/add-tasks.test.js +52 -13
- package/dist/tools/__tests__/assignment-integration.test.d.ts +2 -0
- package/dist/tools/__tests__/assignment-integration.test.d.ts.map +1 -0
- package/dist/tools/__tests__/assignment-integration.test.js +415 -0
- package/dist/tools/__tests__/find-projects.test.js +1 -1
- package/dist/tools/__tests__/find-sections.test.js +1 -1
- package/dist/tools/__tests__/find-tasks-by-date.test.js +1 -1
- package/dist/tools/__tests__/find-tasks.test.js +3 -3
- package/dist/tools/__tests__/get-overview.test.js +1 -1
- package/dist/tools/__tests__/update-tasks.test.js +6 -6
- package/dist/tools/__tests__/user-info.test.js +1 -1
- package/dist/tools/add-comments.d.ts +3 -3
- package/dist/tools/add-comments.d.ts.map +1 -1
- package/dist/tools/add-comments.js +1 -1
- package/dist/tools/add-projects.d.ts.map +1 -1
- package/dist/tools/add-projects.js +1 -1
- package/dist/tools/add-sections.d.ts.map +1 -1
- package/dist/tools/add-sections.js +1 -1
- package/dist/tools/add-tasks.d.ts +13 -7
- package/dist/tools/add-tasks.d.ts.map +1 -1
- package/dist/tools/add-tasks.js +49 -3
- package/dist/tools/find-comments.d.ts +2 -2
- package/dist/tools/find-completed-tasks.d.ts +4 -2
- package/dist/tools/find-completed-tasks.d.ts.map +1 -1
- package/dist/tools/find-completed-tasks.js +2 -2
- package/dist/tools/find-project-collaborators.d.ts +64 -0
- package/dist/tools/find-project-collaborators.d.ts.map +1 -0
- package/dist/tools/find-project-collaborators.js +151 -0
- package/dist/tools/find-tasks-by-date.d.ts +2 -0
- package/dist/tools/find-tasks-by-date.d.ts.map +1 -1
- package/dist/tools/find-tasks-by-date.js +2 -2
- package/dist/tools/find-tasks.d.ts +7 -2
- package/dist/tools/find-tasks.d.ts.map +1 -1
- package/dist/tools/find-tasks.js +128 -33
- package/dist/tools/get-overview.d.ts +2 -2
- package/dist/tools/get-overview.d.ts.map +1 -1
- package/dist/tools/get-overview.js +1 -1
- package/dist/tools/manage-assignments.d.ts +52 -0
- package/dist/tools/manage-assignments.d.ts.map +1 -0
- package/dist/tools/manage-assignments.js +337 -0
- package/dist/tools/update-comments.d.ts.map +1 -1
- package/dist/tools/update-comments.js +1 -1
- package/dist/tools/update-sections.d.ts.map +1 -1
- package/dist/tools/update-sections.js +1 -1
- package/dist/tools/update-tasks.d.ts +13 -7
- package/dist/tools/update-tasks.d.ts.map +1 -1
- package/dist/tools/update-tasks.js +32 -9
- package/dist/utils/assignment-validator.d.ts +69 -0
- package/dist/utils/assignment-validator.d.ts.map +1 -0
- package/dist/utils/assignment-validator.js +253 -0
- package/dist/utils/duration-parser.d.ts +2 -2
- package/dist/utils/duration-parser.d.ts.map +1 -1
- package/dist/utils/priorities.d.ts +8 -0
- package/dist/utils/priorities.d.ts.map +1 -0
- package/dist/utils/priorities.js +15 -0
- package/dist/utils/response-builders.d.ts +2 -2
- package/dist/utils/response-builders.d.ts.map +1 -1
- package/dist/utils/response-builders.js +8 -1
- package/dist/utils/test-helpers.d.ts +2 -0
- package/dist/utils/test-helpers.d.ts.map +1 -1
- package/dist/utils/test-helpers.js +2 -0
- package/dist/utils/tool-names.d.ts +2 -0
- package/dist/utils/tool-names.d.ts.map +1 -1
- package/dist/utils/tool-names.js +3 -0
- package/dist/utils/user-resolver.d.ts +39 -0
- package/dist/utils/user-resolver.d.ts.map +1 -0
- package/dist/utils/user-resolver.js +179 -0
- package/package.json +5 -5
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jest } from '@jest/globals';
|
|
2
|
-
import {
|
|
2
|
+
import { createMockTask, extractStructuredContent, extractTextContent, TEST_IDS, } from '../../utils/test-helpers.js';
|
|
3
3
|
import { ToolNames } from '../../utils/tool-names.js';
|
|
4
4
|
import { updateTasks } from '../update-tasks.js';
|
|
5
5
|
// Mock the Todoist API
|
|
@@ -109,13 +109,13 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
109
109
|
tasks: [
|
|
110
110
|
{
|
|
111
111
|
id: '8485093749',
|
|
112
|
-
priority:
|
|
112
|
+
priority: 'p3',
|
|
113
113
|
dueString: 'Aug 20',
|
|
114
114
|
},
|
|
115
115
|
],
|
|
116
116
|
}, mockTodoistApi);
|
|
117
117
|
expect(mockTodoistApi.updateTask).toHaveBeenCalledWith('8485093749', {
|
|
118
|
-
priority:
|
|
118
|
+
priority: 2,
|
|
119
119
|
dueString: 'Aug 20',
|
|
120
120
|
});
|
|
121
121
|
// Verify result structure
|
|
@@ -208,7 +208,7 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
208
208
|
id: '8485093752',
|
|
209
209
|
content: 'Completely updated task',
|
|
210
210
|
description: 'New description with details',
|
|
211
|
-
priority:
|
|
211
|
+
priority: 'p4',
|
|
212
212
|
dueString: 'every Friday',
|
|
213
213
|
projectId: 'different-project-id',
|
|
214
214
|
},
|
|
@@ -222,7 +222,7 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
222
222
|
expect(mockTodoistApi.updateTask).toHaveBeenCalledWith('8485093752', {
|
|
223
223
|
content: 'Completely updated task',
|
|
224
224
|
description: 'New description with details',
|
|
225
|
-
priority:
|
|
225
|
+
priority: 1,
|
|
226
226
|
dueString: 'every Friday',
|
|
227
227
|
});
|
|
228
228
|
// Verify result structure
|
|
@@ -391,7 +391,7 @@ describe(`${UPDATE_TASKS} tool`, () => {
|
|
|
391
391
|
},
|
|
392
392
|
{
|
|
393
393
|
error: 'API Error: Invalid priority value',
|
|
394
|
-
params: { id: '8485093748',
|
|
394
|
+
params: { id: '8485093748', content: 'Test task' },
|
|
395
395
|
},
|
|
396
396
|
])('should propagate $error', async ({ error, params }) => {
|
|
397
397
|
mockTodoistApi.updateTask.mockRejectedValue(new Error(error));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jest } from '@jest/globals';
|
|
2
|
-
import {
|
|
2
|
+
import { extractStructuredContent, extractTextContent, TEST_ERRORS, } from '../../utils/test-helpers.js';
|
|
3
3
|
import { ToolNames } from '../../utils/tool-names.js';
|
|
4
4
|
import { userInfo } from '../user-info.js';
|
|
5
5
|
// Mock the Todoist API
|
|
@@ -9,19 +9,19 @@ declare const addComments: {
|
|
|
9
9
|
content: z.ZodString;
|
|
10
10
|
}, "strip", z.ZodTypeAny, {
|
|
11
11
|
content: string;
|
|
12
|
-
projectId?: string | undefined;
|
|
13
12
|
taskId?: string | undefined;
|
|
13
|
+
projectId?: string | undefined;
|
|
14
14
|
}, {
|
|
15
15
|
content: string;
|
|
16
|
-
projectId?: string | undefined;
|
|
17
16
|
taskId?: string | undefined;
|
|
17
|
+
projectId?: string | undefined;
|
|
18
18
|
}>, "many">;
|
|
19
19
|
};
|
|
20
20
|
execute(args: {
|
|
21
21
|
comments: {
|
|
22
22
|
content: string;
|
|
23
|
-
projectId?: string | undefined;
|
|
24
23
|
taskId?: string | undefined;
|
|
24
|
+
projectId?: string | undefined;
|
|
25
25
|
}[];
|
|
26
26
|
}, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
|
|
27
27
|
content: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add-comments.d.ts","sourceRoot":"","sources":["../../src/tools/add-comments.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAkBvB,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"add-comments.d.ts","sourceRoot":"","sources":["../../src/tools/add-comments.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAkBvB,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAoFygV,CAAC;4BAA6C,CAAC;4BAA6C,CAAC;2BAA4C,CAAC;gCAAiD,CAAC;+BAAgD,CAAC;yBAA2D,CAAC;8BAA+C,CAAC;+BAAgD,CAAC;uBAAwC,CAAC;yBAA0C,CAAC;;;;;;;;;;;;;;;;;;;;;;;CA1C98V,CAAA;AAyC1C,OAAO,EAAE,WAAW,EAAE,CAAA"}
|
|
@@ -42,7 +42,7 @@ const addComments = {
|
|
|
42
42
|
});
|
|
43
43
|
},
|
|
44
44
|
};
|
|
45
|
-
function generateTextContent({ comments
|
|
45
|
+
function generateTextContent({ comments }) {
|
|
46
46
|
// Group comments by entity type and count
|
|
47
47
|
const taskComments = comments.filter((c) => c.taskId).length;
|
|
48
48
|
const projectComments = comments.filter((c) => c.projectId).length;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add-projects.d.ts","sourceRoot":"","sources":["../../src/tools/add-projects.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAwBvB,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgByB,CAAA;
|
|
1
|
+
{"version":3,"file":"add-projects.d.ts","sourceRoot":"","sources":["../../src/tools/add-projects.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAwBvB,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgByB,CAAA;AA+B1C,OAAO,EAAE,WAAW,EAAE,CAAA"}
|
|
@@ -33,7 +33,7 @@ const addProjects = {
|
|
|
33
33
|
});
|
|
34
34
|
},
|
|
35
35
|
};
|
|
36
|
-
function generateTextContent({ projects
|
|
36
|
+
function generateTextContent({ projects }) {
|
|
37
37
|
const count = projects.length;
|
|
38
38
|
const projectList = projects.map((project) => `• ${project.name} (id=${project.id})`).join('\n');
|
|
39
39
|
const summary = `Added ${count} project${count === 1 ? '' : 's'}:\n${projectList}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add-sections.d.ts","sourceRoot":"","sources":["../../src/tools/add-sections.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAiBvB,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgByB,CAAA;
|
|
1
|
+
{"version":3,"file":"add-sections.d.ts","sourceRoot":"","sources":["../../src/tools/add-sections.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAiBvB,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgByB,CAAA;AA6C1C,OAAO,EAAE,WAAW,EAAE,CAAA"}
|
|
@@ -26,7 +26,7 @@ const addSections = {
|
|
|
26
26
|
});
|
|
27
27
|
},
|
|
28
28
|
};
|
|
29
|
-
function generateTextContent({ sections
|
|
29
|
+
function generateTextContent({ sections }) {
|
|
30
30
|
const count = sections.length;
|
|
31
31
|
const sectionList = sections
|
|
32
32
|
.map((section) => `• ${section.name} (id=${section.id}, projectId=${section.projectId})`)
|
|
@@ -7,42 +7,46 @@ declare const addTasks: {
|
|
|
7
7
|
tasks: z.ZodArray<z.ZodObject<{
|
|
8
8
|
content: z.ZodString;
|
|
9
9
|
description: z.ZodOptional<z.ZodString>;
|
|
10
|
-
priority: z.ZodOptional<z.
|
|
10
|
+
priority: z.ZodOptional<z.ZodEnum<["p1", "p2", "p3", "p4"]>>;
|
|
11
11
|
dueString: z.ZodOptional<z.ZodString>;
|
|
12
12
|
duration: z.ZodOptional<z.ZodString>;
|
|
13
13
|
projectId: z.ZodOptional<z.ZodString>;
|
|
14
14
|
sectionId: z.ZodOptional<z.ZodString>;
|
|
15
15
|
parentId: z.ZodOptional<z.ZodString>;
|
|
16
|
+
responsibleUser: z.ZodOptional<z.ZodString>;
|
|
16
17
|
}, "strip", z.ZodTypeAny, {
|
|
17
18
|
content: string;
|
|
18
19
|
description?: string | undefined;
|
|
19
|
-
parentId?: string | undefined;
|
|
20
20
|
projectId?: string | undefined;
|
|
21
|
+
parentId?: string | undefined;
|
|
21
22
|
sectionId?: string | undefined;
|
|
22
23
|
duration?: string | undefined;
|
|
23
|
-
priority?:
|
|
24
|
+
priority?: "p1" | "p2" | "p3" | "p4" | undefined;
|
|
24
25
|
dueString?: string | undefined;
|
|
26
|
+
responsibleUser?: string | undefined;
|
|
25
27
|
}, {
|
|
26
28
|
content: string;
|
|
27
29
|
description?: string | undefined;
|
|
28
|
-
parentId?: string | undefined;
|
|
29
30
|
projectId?: string | undefined;
|
|
31
|
+
parentId?: string | undefined;
|
|
30
32
|
sectionId?: string | undefined;
|
|
31
33
|
duration?: string | undefined;
|
|
32
|
-
priority?:
|
|
34
|
+
priority?: "p1" | "p2" | "p3" | "p4" | undefined;
|
|
33
35
|
dueString?: string | undefined;
|
|
36
|
+
responsibleUser?: string | undefined;
|
|
34
37
|
}>, "many">;
|
|
35
38
|
};
|
|
36
39
|
execute({ tasks }: {
|
|
37
40
|
tasks: {
|
|
38
41
|
content: string;
|
|
39
42
|
description?: string | undefined;
|
|
40
|
-
parentId?: string | undefined;
|
|
41
43
|
projectId?: string | undefined;
|
|
44
|
+
parentId?: string | undefined;
|
|
42
45
|
sectionId?: string | undefined;
|
|
43
46
|
duration?: string | undefined;
|
|
44
|
-
priority?:
|
|
47
|
+
priority?: "p1" | "p2" | "p3" | "p4" | undefined;
|
|
45
48
|
dueString?: string | undefined;
|
|
49
|
+
responsibleUser?: string | undefined;
|
|
46
50
|
}[];
|
|
47
51
|
}, client: TodoistApi): Promise<{
|
|
48
52
|
content: {
|
|
@@ -62,6 +66,8 @@ declare const addTasks: {
|
|
|
62
66
|
parentId: string | null;
|
|
63
67
|
labels: string[];
|
|
64
68
|
duration: string | null;
|
|
69
|
+
responsibleUid: string | null;
|
|
70
|
+
assignedByUid: string | null;
|
|
65
71
|
}[];
|
|
66
72
|
totalCount: number;
|
|
67
73
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add-tasks.d.ts","sourceRoot":"","sources":["../../src/tools/add-tasks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqB,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAClF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"add-tasks.d.ts","sourceRoot":"","sources":["../../src/tools/add-tasks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqB,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAClF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AA0CvB,QAAA,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuB4B,CAAA;AA+H1C,OAAO,EAAE,QAAQ,EAAE,CAAA"}
|
package/dist/tools/add-tasks.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { getToolOutput } from '../mcp-helpers.js';
|
|
3
3
|
import { mapTask } from '../tool-helpers.js';
|
|
4
|
+
import { assignmentValidator } from '../utils/assignment-validator.js';
|
|
4
5
|
import { DurationParseError, parseDuration } from '../utils/duration-parser.js';
|
|
6
|
+
import { convertPriorityToNumber, PrioritySchema } from '../utils/priorities.js';
|
|
5
7
|
import { generateTaskNextSteps, getDateString, summarizeTaskOperation, } from '../utils/response-builders.js';
|
|
6
8
|
import { ToolNames } from '../utils/tool-names.js';
|
|
7
9
|
const TaskSchema = z.object({
|
|
8
10
|
content: z.string().min(1).describe('The content of the task to create.'),
|
|
9
11
|
description: z.string().optional().describe('The description of the task.'),
|
|
10
|
-
priority:
|
|
12
|
+
priority: PrioritySchema.optional().describe('The priority of the task: p1 (highest), p2 (high), p3 (medium), p4 (lowest/default).'),
|
|
11
13
|
dueString: z.string().optional().describe('The due date for the task, in natural language.'),
|
|
12
14
|
duration: z
|
|
13
15
|
.string()
|
|
@@ -16,13 +18,17 @@ const TaskSchema = z.object({
|
|
|
16
18
|
projectId: z.string().optional().describe('The project ID to add this task to.'),
|
|
17
19
|
sectionId: z.string().optional().describe('The section ID to add this task to.'),
|
|
18
20
|
parentId: z.string().optional().describe('The parent task ID (for subtasks).'),
|
|
21
|
+
responsibleUser: z
|
|
22
|
+
.string()
|
|
23
|
+
.optional()
|
|
24
|
+
.describe('Assign task to this user. Can be a user ID, name, or email address. User must be a collaborator on the target project.'),
|
|
19
25
|
});
|
|
20
26
|
const ArgsSchema = {
|
|
21
27
|
tasks: z.array(TaskSchema).min(1).describe('The array of tasks to add.'),
|
|
22
28
|
};
|
|
23
29
|
const addTasks = {
|
|
24
30
|
name: ToolNames.ADD_TASKS,
|
|
25
|
-
description: 'Add one or more tasks to a project, section, or parent.',
|
|
31
|
+
description: 'Add one or more tasks to a project, section, or parent. Supports assignment to project collaborators.',
|
|
26
32
|
parameters: ArgsSchema,
|
|
27
33
|
async execute({ tasks }, client) {
|
|
28
34
|
const addTaskPromises = tasks.map((task) => processTask(task, client));
|
|
@@ -42,8 +48,16 @@ const addTasks = {
|
|
|
42
48
|
},
|
|
43
49
|
};
|
|
44
50
|
async function processTask(task, client) {
|
|
45
|
-
const { duration: durationStr, projectId, sectionId, parentId, ...otherTaskArgs } = task;
|
|
51
|
+
const { duration: durationStr, projectId, sectionId, parentId, responsibleUser, priority, ...otherTaskArgs } = task;
|
|
46
52
|
let taskArgs = { ...otherTaskArgs, projectId, sectionId, parentId };
|
|
53
|
+
// Handle priority conversion if provided
|
|
54
|
+
if (priority) {
|
|
55
|
+
taskArgs.priority = convertPriorityToNumber(priority);
|
|
56
|
+
}
|
|
57
|
+
// Prevent assignment to tasks without sufficient project context
|
|
58
|
+
if (!projectId && !sectionId && !parentId) {
|
|
59
|
+
throw new Error(`Task "${task.content}": Cannot assign tasks without specifying project context. Please specify a projectId, sectionId, or parentId.`);
|
|
60
|
+
}
|
|
47
61
|
// Parse duration if provided
|
|
48
62
|
if (durationStr) {
|
|
49
63
|
try {
|
|
@@ -61,6 +75,38 @@ async function processTask(task, client) {
|
|
|
61
75
|
throw error;
|
|
62
76
|
}
|
|
63
77
|
}
|
|
78
|
+
// Handle assignment if provided
|
|
79
|
+
if (responsibleUser) {
|
|
80
|
+
// Resolve target project for validation
|
|
81
|
+
let targetProjectId = projectId;
|
|
82
|
+
if (!targetProjectId && parentId) {
|
|
83
|
+
// For subtasks, get project from parent task
|
|
84
|
+
try {
|
|
85
|
+
const parentTask = await client.getTask(parentId);
|
|
86
|
+
targetProjectId = parentTask.projectId;
|
|
87
|
+
}
|
|
88
|
+
catch (_error) {
|
|
89
|
+
throw new Error(`Task "${task.content}": Parent task "${parentId}" not found`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else if (!targetProjectId && sectionId) {
|
|
93
|
+
// For section tasks, we need to find the project - this is a limitation
|
|
94
|
+
// For now, we'll require explicit projectId when using assignments with sections
|
|
95
|
+
throw new Error(`Task "${task.content}": When assigning tasks to sections, please also specify projectId`);
|
|
96
|
+
}
|
|
97
|
+
if (!targetProjectId) {
|
|
98
|
+
throw new Error(`Task "${task.content}": Cannot determine target project for assignment validation`);
|
|
99
|
+
}
|
|
100
|
+
// Validate assignment using comprehensive validator
|
|
101
|
+
const validation = await assignmentValidator.validateTaskCreationAssignment(client, targetProjectId, responsibleUser);
|
|
102
|
+
if (!validation.isValid) {
|
|
103
|
+
const errorMsg = validation.error?.message || 'Assignment validation failed';
|
|
104
|
+
const suggestions = validation.error?.suggestions?.join('. ') || '';
|
|
105
|
+
throw new Error(`Task "${task.content}": ${errorMsg}${suggestions ? `. ${suggestions}` : ''}`);
|
|
106
|
+
}
|
|
107
|
+
// Use the validated assignee ID
|
|
108
|
+
taskArgs.assigneeId = validation.resolvedUser?.userId;
|
|
109
|
+
}
|
|
64
110
|
return await client.addTask(taskArgs);
|
|
65
111
|
}
|
|
66
112
|
function generateTextContent({ tasks, args, }) {
|
|
@@ -10,10 +10,10 @@ declare const findComments: {
|
|
|
10
10
|
limit: z.ZodOptional<z.ZodNumber>;
|
|
11
11
|
};
|
|
12
12
|
execute(args: {
|
|
13
|
-
projectId?: string | undefined;
|
|
14
13
|
limit?: number | undefined;
|
|
15
|
-
cursor?: string | undefined;
|
|
16
14
|
taskId?: string | undefined;
|
|
15
|
+
projectId?: string | undefined;
|
|
16
|
+
cursor?: string | undefined;
|
|
17
17
|
commentId?: string | undefined;
|
|
18
18
|
}, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
|
|
19
19
|
content: {
|
|
@@ -20,9 +20,9 @@ declare const findCompletedTasks: {
|
|
|
20
20
|
getBy: "due" | "completion";
|
|
21
21
|
since: string;
|
|
22
22
|
until: string;
|
|
23
|
+
projectId?: string | undefined;
|
|
23
24
|
parentId?: string | undefined;
|
|
24
25
|
workspaceId?: string | undefined;
|
|
25
|
-
projectId?: string | undefined;
|
|
26
26
|
sectionId?: string | undefined;
|
|
27
27
|
labels?: string[] | undefined;
|
|
28
28
|
cursor?: string | undefined;
|
|
@@ -45,6 +45,8 @@ declare const findCompletedTasks: {
|
|
|
45
45
|
parentId: string | null;
|
|
46
46
|
labels: string[];
|
|
47
47
|
duration: string | null;
|
|
48
|
+
responsibleUid: string | null;
|
|
49
|
+
assignedByUid: string | null;
|
|
48
50
|
}[];
|
|
49
51
|
nextCursor: string | null;
|
|
50
52
|
totalCount: number;
|
|
@@ -54,9 +56,9 @@ declare const findCompletedTasks: {
|
|
|
54
56
|
getBy: "due" | "completion";
|
|
55
57
|
since: string;
|
|
56
58
|
until: string;
|
|
59
|
+
projectId?: string | undefined;
|
|
57
60
|
parentId?: string | undefined;
|
|
58
61
|
workspaceId?: string | undefined;
|
|
59
|
-
projectId?: string | undefined;
|
|
60
62
|
sectionId?: string | undefined;
|
|
61
63
|
labels?: string[] | undefined;
|
|
62
64
|
cursor?: string | undefined;
|
|
@@ -1 +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;AAiDvB,QAAA,MAAM,kBAAkB
|
|
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;AAiDvB,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCkB,CAAA;AAmE1C,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
|
|
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { getToolOutput } from '../mcp-helpers.js';
|
|
3
3
|
import { mapTask } from '../tool-helpers.js';
|
|
4
4
|
import { ApiLimits } from '../utils/constants.js';
|
|
5
|
-
import {
|
|
5
|
+
import { generateLabelsFilter, LabelsSchema } from '../utils/labels.js';
|
|
6
6
|
import { previewTasks, summarizeList } from '../utils/response-builders.js';
|
|
7
7
|
import { ToolNames } from '../utils/tool-names.js';
|
|
8
8
|
const { FIND_TASKS_BY_DATE, GET_OVERVIEW } = ToolNames;
|
|
@@ -120,7 +120,7 @@ function generateTextContent({ tasks, args, nextCursor, }) {
|
|
|
120
120
|
limit: args.limit,
|
|
121
121
|
nextCursor: nextCursor ?? undefined,
|
|
122
122
|
filterHints,
|
|
123
|
-
previewLines: previewTasks(tasks),
|
|
123
|
+
previewLines: previewTasks(tasks, Math.min(tasks.length, args.limit)),
|
|
124
124
|
zeroReasonHints,
|
|
125
125
|
nextSteps,
|
|
126
126
|
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type ProjectCollaborator } from '../utils/user-resolver.js';
|
|
3
|
+
declare const findProjectCollaborators: {
|
|
4
|
+
name: "find-project-collaborators";
|
|
5
|
+
description: string;
|
|
6
|
+
parameters: {
|
|
7
|
+
projectId: z.ZodString;
|
|
8
|
+
searchTerm: z.ZodOptional<z.ZodString>;
|
|
9
|
+
};
|
|
10
|
+
execute(args: {
|
|
11
|
+
projectId: string;
|
|
12
|
+
searchTerm?: string | undefined;
|
|
13
|
+
}, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
|
|
14
|
+
content: {
|
|
15
|
+
type: "text";
|
|
16
|
+
text: string;
|
|
17
|
+
}[];
|
|
18
|
+
structuredContent: {
|
|
19
|
+
collaborators: never[];
|
|
20
|
+
projectInfo: {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
isShared: boolean;
|
|
24
|
+
};
|
|
25
|
+
totalCount: number;
|
|
26
|
+
appliedFilters: {
|
|
27
|
+
projectId: string;
|
|
28
|
+
searchTerm?: string | undefined;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
} | {
|
|
32
|
+
content: ({
|
|
33
|
+
type: "text";
|
|
34
|
+
text: string;
|
|
35
|
+
mimeType?: undefined;
|
|
36
|
+
} | {
|
|
37
|
+
type: "text";
|
|
38
|
+
mimeType: string;
|
|
39
|
+
text: string;
|
|
40
|
+
})[];
|
|
41
|
+
structuredContent?: undefined;
|
|
42
|
+
} | {
|
|
43
|
+
content: {
|
|
44
|
+
type: "text";
|
|
45
|
+
text: string;
|
|
46
|
+
}[];
|
|
47
|
+
structuredContent: {
|
|
48
|
+
collaborators: ProjectCollaborator[];
|
|
49
|
+
projectInfo: {
|
|
50
|
+
id: string;
|
|
51
|
+
name: string;
|
|
52
|
+
isShared: boolean;
|
|
53
|
+
};
|
|
54
|
+
totalCount: number;
|
|
55
|
+
totalAvailable: number;
|
|
56
|
+
appliedFilters: {
|
|
57
|
+
projectId: string;
|
|
58
|
+
searchTerm?: string | undefined;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
}>;
|
|
62
|
+
};
|
|
63
|
+
export { findProjectCollaborators };
|
|
64
|
+
//# sourceMappingURL=find-project-collaborators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-project-collaborators.d.ts","sourceRoot":"","sources":["../../src/tools/find-project-collaborators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAMvB,OAAO,EAAE,KAAK,mBAAmB,EAAgB,MAAM,2BAA2B,CAAA;AAclF,QAAA,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8FY,CAAA;AAwE1C,OAAO,EAAE,wBAAwB,EAAE,CAAA"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getToolOutput } from '../mcp-helpers.js';
|
|
3
|
+
import { summarizeList } from '../utils/response-builders.js';
|
|
4
|
+
import { ToolNames } from '../utils/tool-names.js';
|
|
5
|
+
import { userResolver } from '../utils/user-resolver.js';
|
|
6
|
+
const { FIND_PROJECTS, ADD_TASKS, UPDATE_TASKS } = ToolNames;
|
|
7
|
+
const ArgsSchema = {
|
|
8
|
+
projectId: z.string().min(1).describe('The ID of the project to search for collaborators in.'),
|
|
9
|
+
searchTerm: z
|
|
10
|
+
.string()
|
|
11
|
+
.optional()
|
|
12
|
+
.describe('Search for a collaborator by name or email (partial and case insensitive match). If omitted, all collaborators in the project are returned.'),
|
|
13
|
+
};
|
|
14
|
+
const findProjectCollaborators = {
|
|
15
|
+
name: ToolNames.FIND_PROJECT_COLLABORATORS,
|
|
16
|
+
description: 'Search for collaborators by name or other criteria in a project.',
|
|
17
|
+
parameters: ArgsSchema,
|
|
18
|
+
async execute(args, client) {
|
|
19
|
+
const { projectId, searchTerm } = args;
|
|
20
|
+
// First, validate that the project exists and get basic info
|
|
21
|
+
let projectName = projectId;
|
|
22
|
+
let project;
|
|
23
|
+
try {
|
|
24
|
+
project = await client.getProject(projectId);
|
|
25
|
+
if (!project) {
|
|
26
|
+
throw new Error(`Project with ID "${projectId}" not found or not accessible`);
|
|
27
|
+
}
|
|
28
|
+
projectName = project.name;
|
|
29
|
+
if (!project.isShared) {
|
|
30
|
+
const textContent = `Project "${projectName}" is not shared and has no collaborators.\n\n**Next steps:**\n• Share the project to enable collaboration\n• Use ${ADD_TASKS} and ${UPDATE_TASKS} for assignment features once shared`;
|
|
31
|
+
return getToolOutput({
|
|
32
|
+
textContent,
|
|
33
|
+
structuredContent: {
|
|
34
|
+
collaborators: [],
|
|
35
|
+
projectInfo: {
|
|
36
|
+
id: projectId,
|
|
37
|
+
name: projectName,
|
|
38
|
+
isShared: false,
|
|
39
|
+
},
|
|
40
|
+
totalCount: 0,
|
|
41
|
+
appliedFilters: args,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
throw new Error(`Failed to access project "${projectId}": ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
48
|
+
}
|
|
49
|
+
// Get collaborators for the project
|
|
50
|
+
const allCollaborators = await userResolver.getProjectCollaborators(client, projectId);
|
|
51
|
+
if (allCollaborators.length === 0) {
|
|
52
|
+
const textContent = `Project "${projectName}" has no collaborators or collaborator data is not accessible.\n\n**Next steps:**\n• Check project sharing settings\n• Ensure you have permission to view collaborators\n• Try refreshing or re-sharing the project`;
|
|
53
|
+
return getToolOutput({
|
|
54
|
+
textContent,
|
|
55
|
+
structuredContent: {
|
|
56
|
+
collaborators: [],
|
|
57
|
+
projectInfo: {
|
|
58
|
+
id: projectId,
|
|
59
|
+
name: projectName,
|
|
60
|
+
isShared: true,
|
|
61
|
+
},
|
|
62
|
+
totalCount: 0,
|
|
63
|
+
appliedFilters: args,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
// Filter collaborators if search term provided
|
|
68
|
+
let filteredCollaborators = allCollaborators;
|
|
69
|
+
if (searchTerm) {
|
|
70
|
+
const searchLower = searchTerm.toLowerCase().trim();
|
|
71
|
+
filteredCollaborators = allCollaborators.filter((collaborator) => collaborator.name.toLowerCase().includes(searchLower) ||
|
|
72
|
+
collaborator.email.toLowerCase().includes(searchLower));
|
|
73
|
+
}
|
|
74
|
+
const textContent = generateTextContent({
|
|
75
|
+
collaborators: filteredCollaborators,
|
|
76
|
+
projectName,
|
|
77
|
+
searchTerm,
|
|
78
|
+
totalAvailable: allCollaborators.length,
|
|
79
|
+
});
|
|
80
|
+
return getToolOutput({
|
|
81
|
+
textContent,
|
|
82
|
+
structuredContent: {
|
|
83
|
+
collaborators: filteredCollaborators,
|
|
84
|
+
projectInfo: {
|
|
85
|
+
id: projectId,
|
|
86
|
+
name: projectName,
|
|
87
|
+
isShared: true,
|
|
88
|
+
},
|
|
89
|
+
totalCount: filteredCollaborators.length,
|
|
90
|
+
totalAvailable: allCollaborators.length,
|
|
91
|
+
appliedFilters: args,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
function generateTextContent({ collaborators, projectName, searchTerm, totalAvailable, }) {
|
|
97
|
+
const subject = searchTerm
|
|
98
|
+
? `Project collaborators matching "${searchTerm}"`
|
|
99
|
+
: 'Project collaborators';
|
|
100
|
+
const filterHints = [];
|
|
101
|
+
if (searchTerm) {
|
|
102
|
+
filterHints.push(`matching "${searchTerm}"`);
|
|
103
|
+
}
|
|
104
|
+
filterHints.push(`in project "${projectName}"`);
|
|
105
|
+
let previewLines = [];
|
|
106
|
+
if (collaborators.length > 0) {
|
|
107
|
+
previewLines = collaborators.slice(0, 10).map((collaborator) => {
|
|
108
|
+
const displayName = collaborator.name || 'Unknown Name';
|
|
109
|
+
const email = collaborator.email || 'No email';
|
|
110
|
+
return `• ${displayName} (${email}) - ID: ${collaborator.id}`;
|
|
111
|
+
});
|
|
112
|
+
if (collaborators.length > 10) {
|
|
113
|
+
previewLines.push(`... and ${collaborators.length - 10} more`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const zeroReasonHints = [];
|
|
117
|
+
if (collaborators.length === 0) {
|
|
118
|
+
if (searchTerm) {
|
|
119
|
+
zeroReasonHints.push(`No collaborators match "${searchTerm}"`);
|
|
120
|
+
zeroReasonHints.push('Try a broader search term or check spelling');
|
|
121
|
+
if (totalAvailable > 0) {
|
|
122
|
+
zeroReasonHints.push(`${totalAvailable} collaborators available without filter`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
zeroReasonHints.push('Project has no collaborators');
|
|
127
|
+
zeroReasonHints.push('Share the project to add collaborators');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const nextSteps = [];
|
|
131
|
+
if (collaborators.length > 0) {
|
|
132
|
+
nextSteps.push(`Use ${ADD_TASKS} with responsibleUser to assign new tasks`);
|
|
133
|
+
nextSteps.push(`Use ${UPDATE_TASKS} with responsibleUser to reassign existing tasks`);
|
|
134
|
+
nextSteps.push('Use collaborator names, emails, or IDs for assignments');
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
nextSteps.push(`Use ${FIND_PROJECTS} to find other projects`);
|
|
138
|
+
if (searchTerm && totalAvailable > 0) {
|
|
139
|
+
nextSteps.push('Try searching without filters to see all collaborators');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return summarizeList({
|
|
143
|
+
subject,
|
|
144
|
+
count: collaborators.length,
|
|
145
|
+
filterHints,
|
|
146
|
+
previewLines: previewLines.join('\n'),
|
|
147
|
+
zeroReasonHints,
|
|
148
|
+
nextSteps,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
export { findProjectCollaborators };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"find-tasks-by-date.d.ts","sourceRoot":"","sources":["../../src/tools/find-tasks-by-date.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AA8CvB,QAAA,MAAM,eAAe
|
|
1
|
+
{"version":3,"file":"find-tasks-by-date.d.ts","sourceRoot":"","sources":["../../src/tools/find-tasks-by-date.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AA8CvB,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoDqB,CAAA;AAyE1C,OAAO,EAAE,eAAe,EAAE,CAAA"}
|
|
@@ -3,7 +3,7 @@ import { z } from 'zod';
|
|
|
3
3
|
import { getToolOutput } from '../mcp-helpers.js';
|
|
4
4
|
import { getTasksByFilter } from '../tool-helpers.js';
|
|
5
5
|
import { ApiLimits } from '../utils/constants.js';
|
|
6
|
-
import {
|
|
6
|
+
import { generateLabelsFilter, LabelsSchema } from '../utils/labels.js';
|
|
7
7
|
import { generateTaskNextSteps, getDateString, previewTasks, summarizeList, } from '../utils/response-builders.js';
|
|
8
8
|
import { ToolNames } from '../utils/tool-names.js';
|
|
9
9
|
const ArgsSchema = {
|
|
@@ -130,7 +130,7 @@ function generateTextContent({ tasks, args, nextCursor, }) {
|
|
|
130
130
|
limit: args.limit,
|
|
131
131
|
nextCursor: nextCursor ?? undefined,
|
|
132
132
|
filterHints,
|
|
133
|
-
previewLines: previewTasks(tasks),
|
|
133
|
+
previewLines: previewTasks(tasks, Math.min(tasks.length, args.limit)),
|
|
134
134
|
zeroReasonHints,
|
|
135
135
|
nextSteps,
|
|
136
136
|
});
|
|
@@ -9,16 +9,18 @@ declare const findTasks: {
|
|
|
9
9
|
projectId: z.ZodOptional<z.ZodString>;
|
|
10
10
|
sectionId: z.ZodOptional<z.ZodString>;
|
|
11
11
|
parentId: z.ZodOptional<z.ZodString>;
|
|
12
|
+
responsibleUser: z.ZodOptional<z.ZodString>;
|
|
12
13
|
limit: z.ZodDefault<z.ZodNumber>;
|
|
13
14
|
cursor: z.ZodOptional<z.ZodString>;
|
|
14
15
|
};
|
|
15
16
|
execute(args: {
|
|
16
17
|
limit: number;
|
|
17
|
-
parentId?: string | undefined;
|
|
18
18
|
projectId?: string | undefined;
|
|
19
|
+
parentId?: string | undefined;
|
|
19
20
|
sectionId?: string | undefined;
|
|
20
21
|
labels?: string[] | undefined;
|
|
21
22
|
cursor?: string | undefined;
|
|
23
|
+
responsibleUser?: string | undefined;
|
|
22
24
|
labelsOperator?: "and" | "or" | undefined;
|
|
23
25
|
searchText?: string | undefined;
|
|
24
26
|
}, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
|
|
@@ -39,17 +41,20 @@ declare const findTasks: {
|
|
|
39
41
|
parentId: string | null;
|
|
40
42
|
labels: string[];
|
|
41
43
|
duration: string | null;
|
|
44
|
+
responsibleUid: string | null;
|
|
45
|
+
assignedByUid: string | null;
|
|
42
46
|
}[];
|
|
43
47
|
nextCursor: string | null;
|
|
44
48
|
totalCount: number;
|
|
45
49
|
hasMore: boolean;
|
|
46
50
|
appliedFilters: {
|
|
47
51
|
limit: number;
|
|
48
|
-
parentId?: string | undefined;
|
|
49
52
|
projectId?: string | undefined;
|
|
53
|
+
parentId?: string | undefined;
|
|
50
54
|
sectionId?: string | undefined;
|
|
51
55
|
labels?: string[] | undefined;
|
|
52
56
|
cursor?: string | undefined;
|
|
57
|
+
responsibleUser?: string | undefined;
|
|
53
58
|
labelsOperator?: "and" | "or" | undefined;
|
|
54
59
|
searchText?: string | undefined;
|
|
55
60
|
};
|