@doist/todoist-ai 4.15.1 → 4.16.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.
Files changed (167) hide show
  1. package/dist/filter-helpers.d.ts +1 -1
  2. package/dist/index.d.ts +175 -175
  3. package/dist/index.js +61 -81
  4. package/dist/main.js +15 -23
  5. package/dist/mcp-helpers.d.ts +4 -4
  6. package/dist/mcp-server-6tm7Rhyz.js +2840 -0
  7. package/dist/todoist-tool.d.ts +2 -2
  8. package/dist/tool-helpers.d.ts +1 -1
  9. package/dist/tools/add-comments.d.ts +1 -1
  10. package/dist/tools/add-comments.d.ts.map +1 -1
  11. package/dist/tools/add-projects.d.ts +4 -4
  12. package/dist/tools/add-projects.d.ts.map +1 -1
  13. package/dist/tools/add-sections.d.ts +1 -1
  14. package/dist/tools/add-sections.d.ts.map +1 -1
  15. package/dist/tools/add-tasks.d.ts +4 -4
  16. package/dist/tools/add-tasks.d.ts.map +1 -1
  17. package/dist/tools/complete-tasks.d.ts +1 -1
  18. package/dist/tools/complete-tasks.d.ts.map +1 -1
  19. package/dist/tools/delete-object.d.ts +3 -3
  20. package/dist/tools/delete-object.d.ts.map +1 -1
  21. package/dist/tools/fetch.d.ts +1 -1
  22. package/dist/tools/find-activity.d.ts +5 -5
  23. package/dist/tools/find-activity.d.ts.map +1 -1
  24. package/dist/tools/find-comments.d.ts +2 -2
  25. package/dist/tools/find-comments.d.ts.map +1 -1
  26. package/dist/tools/find-completed-tasks.d.ts +3 -3
  27. package/dist/tools/find-completed-tasks.d.ts.map +1 -1
  28. package/dist/tools/find-project-collaborators.d.ts +2 -2
  29. package/dist/tools/find-projects.d.ts +1 -1
  30. package/dist/tools/find-projects.d.ts.map +1 -1
  31. package/dist/tools/find-sections.d.ts +1 -1
  32. package/dist/tools/find-sections.d.ts.map +1 -1
  33. package/dist/tools/find-tasks-by-date.d.ts +1 -1
  34. package/dist/tools/find-tasks-by-date.d.ts.map +1 -1
  35. package/dist/tools/find-tasks.d.ts +3 -3
  36. package/dist/tools/find-tasks.d.ts.map +1 -1
  37. package/dist/tools/get-overview.d.ts +1 -1
  38. package/dist/tools/manage-assignments.d.ts +1 -1
  39. package/dist/tools/search.d.ts +1 -1
  40. package/dist/tools/update-comments.d.ts +4 -4
  41. package/dist/tools/update-comments.d.ts.map +1 -1
  42. package/dist/tools/update-projects.d.ts +1 -1
  43. package/dist/tools/update-projects.d.ts.map +1 -1
  44. package/dist/tools/update-sections.d.ts +4 -4
  45. package/dist/tools/update-sections.d.ts.map +1 -1
  46. package/dist/tools/update-tasks.d.ts +7 -7
  47. package/dist/tools/update-tasks.d.ts.map +1 -1
  48. package/dist/tools/user-info.d.ts +1 -1
  49. package/dist/utils/assignment-validator.d.ts +2 -2
  50. package/dist/utils/response-builders.d.ts +1 -3
  51. package/dist/utils/response-builders.d.ts.map +1 -1
  52. package/dist/utils/test-helpers.d.ts +1 -1
  53. package/dist/utils/user-resolver.d.ts +1 -1
  54. package/package.json +11 -9
  55. package/dist/filter-helpers.js +0 -79
  56. package/dist/mcp-helpers.js +0 -71
  57. package/dist/mcp-server.js +0 -142
  58. package/dist/todoist-tool.js +0 -1
  59. package/dist/tool-helpers.js +0 -125
  60. package/dist/tool-helpers.test.d.ts +0 -2
  61. package/dist/tool-helpers.test.d.ts.map +0 -1
  62. package/dist/tool-helpers.test.js +0 -223
  63. package/dist/tools/__tests__/add-comments.test.d.ts +0 -2
  64. package/dist/tools/__tests__/add-comments.test.d.ts.map +0 -1
  65. package/dist/tools/__tests__/add-comments.test.js +0 -241
  66. package/dist/tools/__tests__/add-projects.test.d.ts +0 -2
  67. package/dist/tools/__tests__/add-projects.test.d.ts.map +0 -1
  68. package/dist/tools/__tests__/add-projects.test.js +0 -174
  69. package/dist/tools/__tests__/add-sections.test.d.ts +0 -2
  70. package/dist/tools/__tests__/add-sections.test.d.ts.map +0 -1
  71. package/dist/tools/__tests__/add-sections.test.js +0 -185
  72. package/dist/tools/__tests__/add-tasks.test.d.ts +0 -2
  73. package/dist/tools/__tests__/add-tasks.test.d.ts.map +0 -1
  74. package/dist/tools/__tests__/add-tasks.test.js +0 -533
  75. package/dist/tools/__tests__/assignment-integration.test.d.ts +0 -2
  76. package/dist/tools/__tests__/assignment-integration.test.d.ts.map +0 -1
  77. package/dist/tools/__tests__/assignment-integration.test.js +0 -428
  78. package/dist/tools/__tests__/complete-tasks.test.d.ts +0 -2
  79. package/dist/tools/__tests__/complete-tasks.test.d.ts.map +0 -1
  80. package/dist/tools/__tests__/complete-tasks.test.js +0 -206
  81. package/dist/tools/__tests__/delete-object.test.d.ts +0 -2
  82. package/dist/tools/__tests__/delete-object.test.d.ts.map +0 -1
  83. package/dist/tools/__tests__/delete-object.test.js +0 -110
  84. package/dist/tools/__tests__/fetch.test.d.ts +0 -2
  85. package/dist/tools/__tests__/fetch.test.d.ts.map +0 -1
  86. package/dist/tools/__tests__/fetch.test.js +0 -279
  87. package/dist/tools/__tests__/find-activity.test.d.ts +0 -2
  88. package/dist/tools/__tests__/find-activity.test.d.ts.map +0 -1
  89. package/dist/tools/__tests__/find-activity.test.js +0 -229
  90. package/dist/tools/__tests__/find-comments.test.d.ts +0 -2
  91. package/dist/tools/__tests__/find-comments.test.d.ts.map +0 -1
  92. package/dist/tools/__tests__/find-comments.test.js +0 -236
  93. package/dist/tools/__tests__/find-completed-tasks.test.d.ts +0 -2
  94. package/dist/tools/__tests__/find-completed-tasks.test.d.ts.map +0 -1
  95. package/dist/tools/__tests__/find-completed-tasks.test.js +0 -324
  96. package/dist/tools/__tests__/find-projects.test.d.ts +0 -2
  97. package/dist/tools/__tests__/find-projects.test.d.ts.map +0 -1
  98. package/dist/tools/__tests__/find-projects.test.js +0 -154
  99. package/dist/tools/__tests__/find-sections.test.d.ts +0 -2
  100. package/dist/tools/__tests__/find-sections.test.d.ts.map +0 -1
  101. package/dist/tools/__tests__/find-sections.test.js +0 -245
  102. package/dist/tools/__tests__/find-tasks-by-date.test.d.ts +0 -2
  103. package/dist/tools/__tests__/find-tasks-by-date.test.d.ts.map +0 -1
  104. package/dist/tools/__tests__/find-tasks-by-date.test.js +0 -528
  105. package/dist/tools/__tests__/find-tasks.test.d.ts +0 -2
  106. package/dist/tools/__tests__/find-tasks.test.d.ts.map +0 -1
  107. package/dist/tools/__tests__/find-tasks.test.js +0 -771
  108. package/dist/tools/__tests__/get-overview.test.d.ts +0 -2
  109. package/dist/tools/__tests__/get-overview.test.d.ts.map +0 -1
  110. package/dist/tools/__tests__/get-overview.test.js +0 -225
  111. package/dist/tools/__tests__/search.test.d.ts +0 -2
  112. package/dist/tools/__tests__/search.test.d.ts.map +0 -1
  113. package/dist/tools/__tests__/search.test.js +0 -206
  114. package/dist/tools/__tests__/update-comments.test.d.ts +0 -2
  115. package/dist/tools/__tests__/update-comments.test.d.ts.map +0 -1
  116. package/dist/tools/__tests__/update-comments.test.js +0 -294
  117. package/dist/tools/__tests__/update-projects.test.d.ts +0 -2
  118. package/dist/tools/__tests__/update-projects.test.d.ts.map +0 -1
  119. package/dist/tools/__tests__/update-projects.test.js +0 -217
  120. package/dist/tools/__tests__/update-sections.test.d.ts +0 -2
  121. package/dist/tools/__tests__/update-sections.test.d.ts.map +0 -1
  122. package/dist/tools/__tests__/update-sections.test.js +0 -169
  123. package/dist/tools/__tests__/update-tasks.test.d.ts +0 -2
  124. package/dist/tools/__tests__/update-tasks.test.d.ts.map +0 -1
  125. package/dist/tools/__tests__/update-tasks.test.js +0 -788
  126. package/dist/tools/__tests__/user-info.test.d.ts +0 -2
  127. package/dist/tools/__tests__/user-info.test.d.ts.map +0 -1
  128. package/dist/tools/__tests__/user-info.test.js +0 -139
  129. package/dist/tools/add-comments.js +0 -79
  130. package/dist/tools/add-projects.js +0 -63
  131. package/dist/tools/add-sections.js +0 -61
  132. package/dist/tools/add-tasks.js +0 -160
  133. package/dist/tools/complete-tasks.js +0 -68
  134. package/dist/tools/delete-object.js +0 -79
  135. package/dist/tools/fetch.js +0 -102
  136. package/dist/tools/find-activity.js +0 -221
  137. package/dist/tools/find-comments.js +0 -143
  138. package/dist/tools/find-completed-tasks.js +0 -161
  139. package/dist/tools/find-project-collaborators.js +0 -151
  140. package/dist/tools/find-projects.js +0 -101
  141. package/dist/tools/find-sections.js +0 -96
  142. package/dist/tools/find-tasks-by-date.js +0 -198
  143. package/dist/tools/find-tasks.js +0 -329
  144. package/dist/tools/get-overview.js +0 -249
  145. package/dist/tools/manage-assignments.js +0 -337
  146. package/dist/tools/search.js +0 -65
  147. package/dist/tools/update-comments.js +0 -82
  148. package/dist/tools/update-projects.js +0 -84
  149. package/dist/tools/update-sections.js +0 -70
  150. package/dist/tools/update-tasks.js +0 -170
  151. package/dist/tools/user-info.js +0 -142
  152. package/dist/utils/assignment-validator.js +0 -253
  153. package/dist/utils/constants.js +0 -45
  154. package/dist/utils/duration-parser.js +0 -96
  155. package/dist/utils/duration-parser.test.d.ts +0 -2
  156. package/dist/utils/duration-parser.test.d.ts.map +0 -1
  157. package/dist/utils/duration-parser.test.js +0 -147
  158. package/dist/utils/labels.js +0 -18
  159. package/dist/utils/priorities.js +0 -20
  160. package/dist/utils/response-builders.js +0 -210
  161. package/dist/utils/sanitize-data.js +0 -37
  162. package/dist/utils/sanitize-data.test.d.ts +0 -2
  163. package/dist/utils/sanitize-data.test.d.ts.map +0 -1
  164. package/dist/utils/sanitize-data.test.js +0 -93
  165. package/dist/utils/test-helpers.js +0 -237
  166. package/dist/utils/tool-names.js +0 -40
  167. package/dist/utils/user-resolver.js +0 -179
@@ -1,170 +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.string().optional().describe('The new project ID for the task.'),
21
- sectionId: z.string().optional().describe('The new section ID for the task.'),
22
- parentId: z.string().optional().describe('The new parent task ID (for subtasks).'),
23
- order: z.number().optional().describe('The new order of the task within its parent/section.'),
24
- priority: PrioritySchema.optional().describe('The new priority of the task: p1 (highest), p2 (high), p3 (medium), p4 (lowest/default).'),
25
- dueString: z
26
- .string()
27
- .optional()
28
- .describe("The new due date for the task, in natural language (e.g., 'tomorrow at 5pm')."),
29
- deadlineDate: z
30
- .string()
31
- .optional()
32
- .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.'),
33
- duration: z
34
- .string()
35
- .optional()
36
- .describe('The duration of the task. Use format: "2h" (hours), "90m" (minutes), "2h30m" (combined), or "1.5h" (decimal hours). Max 24h.'),
37
- responsibleUser: z
38
- .string()
39
- .optional()
40
- .describe('Change task assignment. Use "unassign" to remove assignment. Can be user ID, name, or email. User must be a project collaborator.'),
41
- labels: z
42
- .array(z.string())
43
- .optional()
44
- .describe('The new labels for the task. Replaces all existing labels.'),
45
- });
46
- const ArgsSchema = {
47
- tasks: z.array(TasksUpdateSchema).min(1).describe('The tasks to update.'),
48
- };
49
- const updateTasks = {
50
- name: ToolNames.UPDATE_TASKS,
51
- description: 'Update existing tasks including content, dates, priorities, and assignments.',
52
- parameters: ArgsSchema,
53
- async execute(args, client) {
54
- const { tasks } = args;
55
- const updateTasksPromises = tasks.map(async (task) => {
56
- if (!hasUpdatesToMake(task)) {
57
- return undefined;
58
- }
59
- const { id, projectId, sectionId, parentId, duration: durationStr, responsibleUser, priority, labels, deadlineDate, ...otherUpdateArgs } = task;
60
- let updateArgs = {
61
- ...otherUpdateArgs,
62
- ...(labels !== undefined && { labels }),
63
- };
64
- // Handle priority conversion if provided
65
- if (priority) {
66
- updateArgs.priority = convertPriorityToNumber(priority);
67
- }
68
- // Handle deadline changes if provided
69
- if (deadlineDate !== undefined) {
70
- if (deadlineDate === null || deadlineDate === 'remove') {
71
- // Remove deadline - support both legacy null and new "remove" string
72
- updateArgs = { ...updateArgs, deadlineDate: null };
73
- }
74
- else {
75
- // Set new deadline
76
- updateArgs = { ...updateArgs, deadlineDate };
77
- }
78
- }
79
- // Parse duration if provided
80
- if (durationStr) {
81
- try {
82
- const { minutes } = parseDuration(durationStr);
83
- updateArgs = {
84
- ...updateArgs,
85
- duration: minutes,
86
- durationUnit: 'minute',
87
- };
88
- }
89
- catch (error) {
90
- if (error instanceof DurationParseError) {
91
- throw new Error(`Task ${id}: ${error.message}`);
92
- }
93
- throw error;
94
- }
95
- }
96
- // Handle assignment changes if provided
97
- if (responsibleUser !== undefined) {
98
- if (responsibleUser === null || responsibleUser === 'unassign') {
99
- // Unassign task - no validation needed
100
- updateArgs = { ...updateArgs, assigneeId: null };
101
- }
102
- else {
103
- // Validate assignment using comprehensive validator
104
- const validation = await assignmentValidator.validateTaskUpdateAssignment(client, id, responsibleUser);
105
- if (!validation.isValid) {
106
- const errorMsg = validation.error?.message || 'Assignment validation failed';
107
- const suggestions = validation.error?.suggestions?.join('. ') || '';
108
- throw new Error(`Task ${id}: ${errorMsg}${suggestions ? `. ${suggestions}` : ''}`);
109
- }
110
- // Use the validated assignee ID
111
- updateArgs = { ...updateArgs, assigneeId: validation.resolvedUser?.userId };
112
- }
113
- }
114
- // If no move parameters are provided, use updateTask without moveTask
115
- if (!projectId && !sectionId && !parentId) {
116
- return await client.updateTask(id, updateArgs);
117
- }
118
- const moveArgs = createMoveTaskArgs(id, projectId, sectionId, parentId);
119
- const movedTask = await client.moveTask(id, moveArgs);
120
- if (Object.keys(updateArgs).length > 0) {
121
- return await client.updateTask(id, updateArgs);
122
- }
123
- return movedTask;
124
- });
125
- const updatedTasks = (await Promise.all(updateTasksPromises)).filter((task) => task !== undefined);
126
- const mappedTasks = updatedTasks.map(mapTask);
127
- const textContent = generateTextContent({
128
- tasks: mappedTasks,
129
- args,
130
- });
131
- return getToolOutput({
132
- textContent,
133
- structuredContent: {
134
- tasks: mappedTasks,
135
- totalCount: mappedTasks.length,
136
- updatedTaskIds: updatedTasks.map((task) => task.id),
137
- appliedOperations: {
138
- updateCount: mappedTasks.length,
139
- skippedCount: tasks.length - mappedTasks.length,
140
- },
141
- },
142
- });
143
- },
144
- };
145
- function generateTextContent({ tasks, args, }) {
146
- const totalRequested = args.tasks.length;
147
- const actuallyUpdated = tasks.length;
148
- const skipped = totalRequested - actuallyUpdated;
149
- let context = '';
150
- if (skipped > 0) {
151
- context = ` (${skipped} skipped - no changes)`;
152
- }
153
- const nextSteps = [];
154
- if (tasks.length > 0) {
155
- nextSteps.push(`Use ${FIND_TASKS_BY_DATE} to see your updated schedule`);
156
- nextSteps.push(`Use ${GET_OVERVIEW} to see updated project organization`);
157
- }
158
- else {
159
- nextSteps.push(`Use ${FIND_TASKS_BY_DATE} to see current tasks`);
160
- }
161
- return summarizeTaskOperation('Updated', tasks, {
162
- context,
163
- nextSteps,
164
- showDetails: tasks.length <= 5,
165
- });
166
- }
167
- function hasUpdatesToMake({ id, ...otherUpdateArgs }) {
168
- return Object.keys(otherUpdateArgs).length > 0;
169
- }
170
- export { updateTasks };
@@ -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 };
@@ -1,253 +0,0 @@
1
- import { userResolver } from './user-resolver.js';
2
- export const AssignmentErrorType = {
3
- USER_NOT_FOUND: 'USER_NOT_FOUND',
4
- USER_NOT_COLLABORATOR: 'USER_NOT_COLLABORATOR',
5
- PROJECT_NOT_SHARED: 'PROJECT_NOT_SHARED',
6
- TASK_NOT_ACCESSIBLE: 'TASK_NOT_ACCESSIBLE',
7
- PERMISSION_DENIED: 'PERMISSION_DENIED',
8
- PROJECT_NOT_FOUND: 'PROJECT_NOT_FOUND',
9
- TASK_NOT_FOUND: 'TASK_NOT_FOUND',
10
- };
11
- export class AssignmentValidator {
12
- /**
13
- * Validate a single assignment operation
14
- */
15
- async validateAssignment(client, assignment) {
16
- const { taskId, projectId, responsibleUid } = assignment;
17
- // First, resolve the user
18
- const resolvedUser = await userResolver.resolveUser(client, responsibleUid);
19
- if (!resolvedUser) {
20
- return {
21
- isValid: false,
22
- taskId,
23
- projectId,
24
- error: {
25
- type: AssignmentErrorType.USER_NOT_FOUND,
26
- message: `User "${responsibleUid}" not found`,
27
- suggestions: [
28
- 'Check the spelling of the user name or email',
29
- 'Ensure the user is a collaborator on at least one shared project',
30
- "Try using the user's full email address",
31
- ],
32
- },
33
- };
34
- }
35
- // Validate the project exists and is accessible
36
- try {
37
- const targetProject = await client.getProject(projectId);
38
- // Check if project is shared (required for assignments)
39
- if (!targetProject.isShared) {
40
- return {
41
- isValid: false,
42
- taskId,
43
- projectId,
44
- resolvedUser,
45
- error: {
46
- type: AssignmentErrorType.PROJECT_NOT_SHARED,
47
- message: `Project "${targetProject.name}" is not shared`,
48
- suggestions: [
49
- 'Share the project with collaborators before assigning tasks',
50
- 'Only shared projects support task assignments',
51
- ],
52
- },
53
- };
54
- }
55
- // Validate the user is a collaborator on this specific project
56
- const isCollaborator = await userResolver.validateProjectCollaborator(client, projectId, resolvedUser.userId);
57
- if (!isCollaborator) {
58
- return {
59
- isValid: false,
60
- taskId,
61
- projectId,
62
- resolvedUser,
63
- error: {
64
- type: AssignmentErrorType.USER_NOT_COLLABORATOR,
65
- message: `User "${resolvedUser.displayName}" is not a collaborator on project "${targetProject.name}"`,
66
- suggestions: [
67
- 'Invite the user to collaborate on this project first',
68
- 'Check if the user has been removed from the project',
69
- 'Verify you have permission to see project collaborators',
70
- ],
71
- },
72
- };
73
- }
74
- // If taskId is provided, validate the task exists and is accessible
75
- if (taskId) {
76
- try {
77
- await client.getTask(taskId);
78
- }
79
- catch (_error) {
80
- return {
81
- isValid: false,
82
- taskId,
83
- projectId,
84
- resolvedUser,
85
- error: {
86
- type: AssignmentErrorType.TASK_NOT_FOUND,
87
- message: `Task "${taskId}" not found or not accessible`,
88
- suggestions: [
89
- 'Verify the task ID is correct',
90
- 'Check if the task has been deleted',
91
- 'Ensure you have access to the task',
92
- ],
93
- },
94
- };
95
- }
96
- }
97
- return {
98
- isValid: true,
99
- taskId,
100
- projectId,
101
- resolvedUser,
102
- };
103
- }
104
- catch (_error) {
105
- return {
106
- isValid: false,
107
- taskId,
108
- projectId,
109
- resolvedUser,
110
- error: {
111
- type: AssignmentErrorType.PERMISSION_DENIED,
112
- message: 'Permission denied or API error occurred',
113
- suggestions: [
114
- 'Check your API permissions',
115
- 'Verify you have access to the project',
116
- 'Try again later if this is a temporary API issue',
117
- ],
118
- },
119
- };
120
- }
121
- }
122
- /**
123
- * Validate multiple assignment operations in bulk
124
- */
125
- async validateBulkAssignment(client, assignments) {
126
- // Process assignments in parallel for better performance
127
- const validationPromises = assignments.map((assignment) => this.validateAssignment(client, assignment));
128
- return Promise.all(validationPromises);
129
- }
130
- /**
131
- * Validate assignment for task creation (no taskId required)
132
- */
133
- async validateTaskCreationAssignment(client, projectId, responsibleUid) {
134
- return this.validateAssignment(client, {
135
- projectId,
136
- responsibleUid,
137
- });
138
- }
139
- /**
140
- * Validate assignment for task update
141
- */
142
- async validateTaskUpdateAssignment(client, taskId, responsibleUid) {
143
- // If responsibleUid is null, it's an unassignment - always valid
144
- if (responsibleUid === null) {
145
- return {
146
- isValid: true,
147
- taskId,
148
- };
149
- }
150
- // Get the task to find its project
151
- try {
152
- const task = await client.getTask(taskId);
153
- return this.validateAssignment(client, {
154
- taskId,
155
- projectId: task.projectId,
156
- responsibleUid,
157
- });
158
- }
159
- catch (_error) {
160
- return {
161
- isValid: false,
162
- taskId,
163
- error: {
164
- type: AssignmentErrorType.TASK_NOT_FOUND,
165
- message: `Task "${taskId}" not found or not accessible`,
166
- suggestions: [
167
- 'Verify the task ID is correct',
168
- 'Check if the task has been deleted',
169
- 'Ensure you have access to the task',
170
- ],
171
- },
172
- };
173
- }
174
- }
175
- /**
176
- * Get detailed assignment eligibility information for troubleshooting
177
- */
178
- async getAssignmentEligibility(client, projectId, responsibleUid, taskIds) {
179
- const recommendations = [];
180
- let canAssign = false;
181
- // Get project information
182
- let project;
183
- try {
184
- project = await client.getProject(projectId);
185
- }
186
- catch {
187
- return {
188
- canAssign: false,
189
- projectInfo: {
190
- name: 'Unknown',
191
- isShared: false,
192
- collaboratorCount: 0,
193
- },
194
- recommendations: ['Project not found or not accessible'],
195
- };
196
- }
197
- const collaborators = await userResolver.getProjectCollaborators(client, projectId);
198
- const projectInfo = {
199
- name: project.name,
200
- isShared: project.isShared,
201
- collaboratorCount: collaborators.length,
202
- };
203
- if (!project.isShared) {
204
- recommendations.push('Share this project to enable task assignments');
205
- return { canAssign: false, projectInfo, recommendations };
206
- }
207
- // Resolve user
208
- const resolvedUser = await userResolver.resolveUser(client, responsibleUid);
209
- if (!resolvedUser) {
210
- recommendations.push('User not found - check spelling or invite to a shared project');
211
- return { canAssign: false, projectInfo, recommendations };
212
- }
213
- const isCollaborator = collaborators.some((c) => c.id === resolvedUser.userId);
214
- const userInfo = {
215
- resolvedName: resolvedUser.displayName,
216
- isCollaborator,
217
- };
218
- if (!isCollaborator) {
219
- recommendations.push(`Invite ${resolvedUser.displayName} to collaborate on project "${project.name}"`);
220
- return { canAssign: false, projectInfo, userInfo, recommendations };
221
- }
222
- // Check task accessibility if provided
223
- let taskInfo;
224
- if (taskIds && taskIds.length > 0) {
225
- let accessible = 0;
226
- let inaccessible = 0;
227
- for (const taskId of taskIds) {
228
- try {
229
- await client.getTask(taskId);
230
- accessible++;
231
- }
232
- catch {
233
- inaccessible++;
234
- }
235
- }
236
- taskInfo = { accessibleTasks: accessible, inaccessibleTasks: inaccessible };
237
- if (inaccessible > 0) {
238
- recommendations.push(`${inaccessible} task(s) are not accessible`);
239
- }
240
- }
241
- canAssign = true;
242
- recommendations.push(`Ready to assign tasks to ${resolvedUser.displayName}`);
243
- return {
244
- canAssign,
245
- projectInfo,
246
- userInfo,
247
- taskInfo,
248
- recommendations,
249
- };
250
- }
251
- }
252
- // Export singleton instance
253
- export const assignmentValidator = new AssignmentValidator();
@@ -1,45 +0,0 @@
1
- /**
2
- * Application-wide constants
3
- *
4
- * This module centralizes magic numbers and configuration values
5
- * to improve maintainability and provide a single source of truth.
6
- */
7
- // API Pagination Limits
8
- export const ApiLimits = {
9
- /** Default limit for task listings */
10
- TASKS_DEFAULT: 10,
11
- /** Maximum limit for task search and list operations */
12
- TASKS_MAX: 100,
13
- /** Default limit for completed tasks */
14
- COMPLETED_TASKS_DEFAULT: 50,
15
- /** Maximum limit for completed tasks */
16
- COMPLETED_TASKS_MAX: 200,
17
- /** Default limit for project listings */
18
- PROJECTS_DEFAULT: 50,
19
- /** Maximum limit for project listings */
20
- PROJECTS_MAX: 100,
21
- /** Batch size for fetching all tasks in a project */
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,
27
- /** Default limit for activity log listings */
28
- ACTIVITY_DEFAULT: 20,
29
- /** Maximum limit for activity log search and list operations */
30
- ACTIVITY_MAX: 100,
31
- };
32
- // UI Display Limits
33
- export const DisplayLimits = {
34
- /** Maximum number of failures to show in detailed error messages */
35
- MAX_FAILURES_SHOWN: 3,
36
- /** Threshold for suggesting batch operations */
37
- BATCH_OPERATION_THRESHOLD: 10,
38
- };
39
- // Response Builder Configuration
40
- export const ResponseConfig = {
41
- /** Maximum characters per line in text responses */
42
- MAX_LINE_LENGTH: 100,
43
- /** Indentation for nested items */
44
- INDENT_SIZE: 2,
45
- };