@doist/todoist-ai 4.1.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 +405 -50
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -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 +80 -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-completed-tasks.test.js +136 -2
- 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 +122 -3
- package/dist/tools/__tests__/find-tasks.test.js +258 -11
- package/dist/tools/__tests__/get-overview.test.js +1 -1
- package/dist/tools/__tests__/update-sections.test.js +1 -0
- package/dist/tools/__tests__/update-tasks.test.js +6 -6
- package/dist/tools/__tests__/user-info.test.d.ts +2 -0
- package/dist/tools/__tests__/user-info.test.d.ts.map +1 -0
- package/dist/tools/__tests__/user-info.test.js +139 -0
- package/dist/tools/add-comments.d.ts +28 -5
- 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 +46 -2
- 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 +14 -2
- 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 +16 -10
- 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 +27 -4
- package/dist/tools/find-comments.d.ts.map +1 -1
- package/dist/tools/find-completed-tasks.d.ts +12 -4
- package/dist/tools/find-completed-tasks.d.ts.map +1 -1
- package/dist/tools/find-completed-tasks.js +20 -4
- 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 +8 -0
- package/dist/tools/find-tasks-by-date.d.ts.map +1 -1
- package/dist/tools/find-tasks-by-date.js +19 -2
- package/dist/tools/find-tasks.d.ts +13 -2
- package/dist/tools/find-tasks.d.ts.map +1 -1
- package/dist/tools/find-tasks.js +172 -23
- 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 +25 -2
- package/dist/tools/update-comments.d.ts.map +1 -1
- package/dist/tools/update-comments.js +1 -1
- package/dist/tools/update-projects.d.ts +46 -2
- package/dist/tools/update-projects.d.ts.map +1 -1
- package/dist/tools/update-sections.d.ts +14 -2
- 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 +16 -10
- package/dist/tools/update-tasks.d.ts.map +1 -1
- package/dist/tools/update-tasks.js +32 -9
- package/dist/tools/user-info.d.ts +44 -0
- package/dist/tools/user-info.d.ts.map +1 -0
- package/dist/tools/user-info.js +142 -0
- 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/labels.d.ts +10 -0
- package/dist/utils/labels.d.ts.map +1 -0
- package/dist/utils/labels.js +18 -0
- 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 +3 -0
- package/dist/utils/tool-names.d.ts +3 -0
- package/dist/utils/tool-names.d.ts.map +1 -1
- package/dist/utils/tool-names.js +4 -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 +7 -7
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { TodoistApi } from '@doist/todoist-api-typescript';
|
|
2
|
+
type UserPlan = 'Todoist Free' | 'Todoist Pro' | 'Todoist Business';
|
|
3
|
+
type UserInfoStructured = Record<string, unknown> & {
|
|
4
|
+
type: 'user_info';
|
|
5
|
+
userId: string;
|
|
6
|
+
fullName: string;
|
|
7
|
+
timezone: string;
|
|
8
|
+
currentLocalTime: string;
|
|
9
|
+
startDay: number;
|
|
10
|
+
startDayName: string;
|
|
11
|
+
weekStartDate: string;
|
|
12
|
+
weekEndDate: string;
|
|
13
|
+
currentWeekNumber: number;
|
|
14
|
+
completedToday: number;
|
|
15
|
+
dailyGoal: number;
|
|
16
|
+
weeklyGoal: number;
|
|
17
|
+
email: string;
|
|
18
|
+
plan: UserPlan;
|
|
19
|
+
};
|
|
20
|
+
declare const userInfo: {
|
|
21
|
+
name: "user-info";
|
|
22
|
+
description: string;
|
|
23
|
+
parameters: {};
|
|
24
|
+
execute(_args: {}, client: TodoistApi): Promise<{
|
|
25
|
+
content: {
|
|
26
|
+
type: "text";
|
|
27
|
+
text: string;
|
|
28
|
+
}[];
|
|
29
|
+
structuredContent: UserInfoStructured;
|
|
30
|
+
} | {
|
|
31
|
+
content: ({
|
|
32
|
+
type: "text";
|
|
33
|
+
text: string;
|
|
34
|
+
mimeType?: undefined;
|
|
35
|
+
} | {
|
|
36
|
+
type: "text";
|
|
37
|
+
mimeType: string;
|
|
38
|
+
text: string;
|
|
39
|
+
})[];
|
|
40
|
+
structuredContent?: undefined;
|
|
41
|
+
}>;
|
|
42
|
+
};
|
|
43
|
+
export { userInfo, type UserInfoStructured };
|
|
44
|
+
//# sourceMappingURL=user-info.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-info.d.ts","sourceRoot":"","sources":["../../src/tools/user-info.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAO/D,KAAK,QAAQ,GAAG,cAAc,GAAG,aAAa,GAAG,kBAAkB,CAAA;AAEnE,KAAK,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAChD,IAAI,EAAE,WAAW,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,gBAAgB,EAAE,MAAM,CAAA;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,iBAAiB,EAAE,MAAM,CAAA;IACzB,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,QAAQ,CAAA;CACjB,CAAA;AAoJD,QAAA,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;CAa4B,CAAA;AAE1C,OAAO,EAAE,QAAQ,EAAE,KAAK,kBAAkB,EAAE,CAAA"}
|
|
@@ -0,0 +1,142 @@
|
|
|
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 };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { TodoistApi } from '@doist/todoist-api-typescript';
|
|
2
|
+
import { type ResolvedUser } from './user-resolver.js';
|
|
3
|
+
export declare const AssignmentErrorType: {
|
|
4
|
+
readonly USER_NOT_FOUND: "USER_NOT_FOUND";
|
|
5
|
+
readonly USER_NOT_COLLABORATOR: "USER_NOT_COLLABORATOR";
|
|
6
|
+
readonly PROJECT_NOT_SHARED: "PROJECT_NOT_SHARED";
|
|
7
|
+
readonly TASK_NOT_ACCESSIBLE: "TASK_NOT_ACCESSIBLE";
|
|
8
|
+
readonly PERMISSION_DENIED: "PERMISSION_DENIED";
|
|
9
|
+
readonly PROJECT_NOT_FOUND: "PROJECT_NOT_FOUND";
|
|
10
|
+
readonly TASK_NOT_FOUND: "TASK_NOT_FOUND";
|
|
11
|
+
};
|
|
12
|
+
export type AssignmentErrorType = (typeof AssignmentErrorType)[keyof typeof AssignmentErrorType];
|
|
13
|
+
export type ValidationError = {
|
|
14
|
+
type: AssignmentErrorType;
|
|
15
|
+
message: string;
|
|
16
|
+
suggestions?: string[];
|
|
17
|
+
};
|
|
18
|
+
export type ValidationResult = {
|
|
19
|
+
isValid: boolean;
|
|
20
|
+
resolvedUser?: ResolvedUser;
|
|
21
|
+
error?: ValidationError;
|
|
22
|
+
taskId?: string;
|
|
23
|
+
projectId?: string;
|
|
24
|
+
};
|
|
25
|
+
export type Assignment = {
|
|
26
|
+
taskId?: string;
|
|
27
|
+
projectId: string;
|
|
28
|
+
responsibleUid: string;
|
|
29
|
+
};
|
|
30
|
+
export declare class AssignmentValidator {
|
|
31
|
+
/**
|
|
32
|
+
* Validate a single assignment operation
|
|
33
|
+
*/
|
|
34
|
+
validateAssignment(client: TodoistApi, assignment: Assignment): Promise<ValidationResult>;
|
|
35
|
+
/**
|
|
36
|
+
* Validate multiple assignment operations in bulk
|
|
37
|
+
*/
|
|
38
|
+
validateBulkAssignment(client: TodoistApi, assignments: Assignment[]): Promise<ValidationResult[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Validate assignment for task creation (no taskId required)
|
|
41
|
+
*/
|
|
42
|
+
validateTaskCreationAssignment(client: TodoistApi, projectId: string, responsibleUid: string): Promise<ValidationResult>;
|
|
43
|
+
/**
|
|
44
|
+
* Validate assignment for task update
|
|
45
|
+
*/
|
|
46
|
+
validateTaskUpdateAssignment(client: TodoistApi, taskId: string, responsibleUid: string | null): Promise<ValidationResult>;
|
|
47
|
+
/**
|
|
48
|
+
* Get detailed assignment eligibility information for troubleshooting
|
|
49
|
+
*/
|
|
50
|
+
getAssignmentEligibility(client: TodoistApi, projectId: string, responsibleUid: string, taskIds?: string[]): Promise<{
|
|
51
|
+
canAssign: boolean;
|
|
52
|
+
projectInfo: {
|
|
53
|
+
name: string;
|
|
54
|
+
isShared: boolean;
|
|
55
|
+
collaboratorCount: number;
|
|
56
|
+
};
|
|
57
|
+
userInfo?: {
|
|
58
|
+
resolvedName: string;
|
|
59
|
+
isCollaborator: boolean;
|
|
60
|
+
};
|
|
61
|
+
taskInfo?: {
|
|
62
|
+
accessibleTasks: number;
|
|
63
|
+
inaccessibleTasks: number;
|
|
64
|
+
};
|
|
65
|
+
recommendations: string[];
|
|
66
|
+
}>;
|
|
67
|
+
}
|
|
68
|
+
export declare const assignmentValidator: AssignmentValidator;
|
|
69
|
+
//# sourceMappingURL=assignment-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assignment-validator.d.ts","sourceRoot":"","sources":["../../src/utils/assignment-validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAE/D,OAAO,EAAE,KAAK,YAAY,EAAgB,MAAM,oBAAoB,CAAA;AAEpE,eAAO,MAAM,mBAAmB;;;;;;;;CAQtB,CAAA;AAEV,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,OAAO,mBAAmB,CAAC,CAAA;AAEhG,MAAM,MAAM,eAAe,GAAG;IAC1B,IAAI,EAAE,mBAAmB,CAAA;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC3B,OAAO,EAAE,OAAO,CAAA;IAChB,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B,KAAK,CAAC,EAAE,eAAe,CAAA;IACvB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACrB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,MAAM,CAAA;CACzB,CAAA;AAED,qBAAa,mBAAmB;IAC5B;;OAEG;IACG,kBAAkB,CACpB,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,UAAU,GACvB,OAAO,CAAC,gBAAgB,CAAC;IAqH5B;;OAEG;IACG,sBAAsB,CACxB,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,UAAU,EAAE,GAC1B,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAS9B;;OAEG;IACG,8BAA8B,CAChC,MAAM,EAAE,UAAU,EAClB,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,GACvB,OAAO,CAAC,gBAAgB,CAAC;IAO5B;;OAEG;IACG,4BAA4B,CAC9B,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,GAAG,IAAI,GAC9B,OAAO,CAAC,gBAAgB,CAAC;IAkC5B;;OAEG;IACG,wBAAwB,CAC1B,MAAM,EAAE,UAAU,EAClB,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE,MAAM,EAAE,GACnB,OAAO,CAAC;QACP,SAAS,EAAE,OAAO,CAAA;QAClB,WAAW,EAAE;YACT,IAAI,EAAE,MAAM,CAAA;YACZ,QAAQ,EAAE,OAAO,CAAA;YACjB,iBAAiB,EAAE,MAAM,CAAA;SAC5B,CAAA;QACD,QAAQ,CAAC,EAAE;YACP,YAAY,EAAE,MAAM,CAAA;YACpB,cAAc,EAAE,OAAO,CAAA;SAC1B,CAAA;QACD,QAAQ,CAAC,EAAE;YACP,eAAe,EAAE,MAAM,CAAA;YACvB,iBAAiB,EAAE,MAAM,CAAA;SAC5B,CAAA;QACD,eAAe,EAAE,MAAM,EAAE,CAAA;KAC5B,CAAC;CAqFL;AAGD,eAAO,MAAM,mBAAmB,qBAA4B,CAAA"}
|
|
@@ -0,0 +1,253 @@
|
|
|
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();
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
* - "1.5h" (decimal hours)
|
|
10
10
|
* - Supports optional spaces: "2h 30m"
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
type ParsedDuration = {
|
|
13
13
|
minutes: number;
|
|
14
|
-
}
|
|
14
|
+
};
|
|
15
15
|
export declare class DurationParseError extends Error {
|
|
16
16
|
constructor(input: string, reason: string);
|
|
17
17
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"duration-parser.d.ts","sourceRoot":"","sources":["../../src/utils/duration-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,
|
|
1
|
+
{"version":3,"file":"duration-parser.d.ts","sourceRoot":"","sources":["../../src/utils/duration-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,KAAK,cAAc,GAAG;IAClB,OAAO,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,qBAAa,kBAAmB,SAAQ,KAAK;gBAC7B,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAI5C;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,CAgEjE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAetD"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
declare const LABELS_OPERATORS: readonly ["and", "or"];
|
|
3
|
+
type LabelsOperator = (typeof LABELS_OPERATORS)[number];
|
|
4
|
+
export declare const LabelsSchema: {
|
|
5
|
+
labels: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
6
|
+
labelsOperator: z.ZodOptional<z.ZodEnum<["and", "or"]>>;
|
|
7
|
+
};
|
|
8
|
+
export declare function generateLabelsFilter(labels?: string[], labelsOperator?: LabelsOperator): string;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=labels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../../src/utils/labels.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,QAAA,MAAM,gBAAgB,wBAAyB,CAAA;AAC/C,KAAK,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEvD,eAAO,MAAM,YAAY;;;CAQxB,CAAA;AAED,wBAAgB,oBAAoB,CAAC,MAAM,GAAE,MAAM,EAAO,EAAE,cAAc,GAAE,cAAqB,UAOhG"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const LABELS_OPERATORS = ['and', 'or'];
|
|
3
|
+
export const LabelsSchema = {
|
|
4
|
+
labels: z.string().array().optional().describe('The labels to filter the tasks by'),
|
|
5
|
+
labelsOperator: z
|
|
6
|
+
.enum(LABELS_OPERATORS)
|
|
7
|
+
.optional()
|
|
8
|
+
.describe('The operator to use when filtering by labels. This will dictate whether a task has all labels, or some of them. Default is "or".'),
|
|
9
|
+
};
|
|
10
|
+
export function generateLabelsFilter(labels = [], labelsOperator = 'or') {
|
|
11
|
+
if (labels.length === 0)
|
|
12
|
+
return '';
|
|
13
|
+
const operator = labelsOperator === 'and' ? ' & ' : ' | ';
|
|
14
|
+
// Add @ prefix to labels for Todoist API query
|
|
15
|
+
const prefixedLabels = labels.map((label) => (label.startsWith('@') ? label : `@${label}`));
|
|
16
|
+
const labelStr = prefixedLabels.join(` ${operator} `);
|
|
17
|
+
return `(${labelStr})`;
|
|
18
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
declare const PRIORITY_VALUES: readonly ["p1", "p2", "p3", "p4"];
|
|
3
|
+
type Priority = (typeof PRIORITY_VALUES)[number];
|
|
4
|
+
export declare const PrioritySchema: z.ZodEnum<["p1", "p2", "p3", "p4"]>;
|
|
5
|
+
export declare function convertPriorityToNumber(priority: Priority): number;
|
|
6
|
+
export declare function convertNumberToPriority(priority: number): Priority | undefined;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=priorities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"priorities.d.ts","sourceRoot":"","sources":["../../src/utils/priorities.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,QAAA,MAAM,eAAe,mCAAoC,CAAA;AACzD,KAAK,QAAQ,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAEhD,eAAO,MAAM,cAAc,qCAEzB,CAAA;AAEF,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAIlE;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAI9E"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const PRIORITY_VALUES = ['p1', 'p2', 'p3', 'p4'];
|
|
3
|
+
export const PrioritySchema = z.enum(PRIORITY_VALUES, {
|
|
4
|
+
description: 'Task priority: p1 (highest), p2 (high), p3 (medium), p4 (lowest/default)',
|
|
5
|
+
});
|
|
6
|
+
export function convertPriorityToNumber(priority) {
|
|
7
|
+
// Todoist API uses inverse mapping: p1=4 (highest), p2=3, p3=2, p4=1 (lowest)
|
|
8
|
+
const priorityMap = { p1: 4, p2: 3, p3: 2, p4: 1 };
|
|
9
|
+
return priorityMap[priority];
|
|
10
|
+
}
|
|
11
|
+
export function convertNumberToPriority(priority) {
|
|
12
|
+
// Convert Todoist API numbers back to our enum
|
|
13
|
+
const numberMap = { 4: 'p1', 3: 'p2', 2: 'p3', 1: 'p4' };
|
|
14
|
+
return numberMap[priority];
|
|
15
|
+
}
|
|
@@ -53,7 +53,7 @@ export declare function formatProjectPreview(project: ProjectLike): string;
|
|
|
53
53
|
* Creates preview lines for task lists
|
|
54
54
|
*/
|
|
55
55
|
export declare function previewTasks(tasks: TaskLike[], limit?: number): string;
|
|
56
|
-
|
|
56
|
+
type SummarizeListParams = {
|
|
57
57
|
subject: string;
|
|
58
58
|
count: number;
|
|
59
59
|
limit?: number;
|
|
@@ -62,7 +62,7 @@ interface SummarizeListParams {
|
|
|
62
62
|
previewLines?: string;
|
|
63
63
|
zeroReasonHints?: string[];
|
|
64
64
|
nextSteps?: string[];
|
|
65
|
-
}
|
|
65
|
+
};
|
|
66
66
|
/**
|
|
67
67
|
* Creates list summaries with counts, filters, and guidance
|
|
68
68
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response-builders.d.ts","sourceRoot":"","sources":["../../src/utils/response-builders.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,GAAE,IAAiB,GAAG,MAAM,CAG7D;AAID,KAAK,QAAQ,GAAG;IACZ,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,KAAK,WAAW,GAAG;IACf,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,KAAK,oBAAoB,GAAG;IACxB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,OAAO,CAAA;CACxB,CAAA;AAED,KAAK,oBAAoB,GAAG;IACxB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,QAAQ,CAAC,EAAE,KAAK,CAAC;QACb,IAAI,EAAE,MAAM,CAAA;QACZ,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,CAAC,EAAE,MAAM,CAAA;KAChB,CAAC,CAAA;IACF,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;CACvB,CAAA;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAClC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,QAAQ,EAAE,EACjB,OAAO,GAAE,oBAAyB,GACnC,MAAM,CA0BR;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CA+BnE;AAcD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAQjE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,KAAK,SAAI,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"response-builders.d.ts","sourceRoot":"","sources":["../../src/utils/response-builders.ts"],"names":[],"mappings":"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"}
|
|
@@ -88,7 +88,14 @@ export function formatProjectPreview(project) {
|
|
|
88
88
|
* Creates preview lines for task lists
|
|
89
89
|
*/
|
|
90
90
|
export function previewTasks(tasks, limit = 5) {
|
|
91
|
-
|
|
91
|
+
const previewTasks = tasks.slice(0, limit);
|
|
92
|
+
const lines = previewTasks.map(formatTaskPreview).join('\n');
|
|
93
|
+
// If we're showing fewer tasks than the total, add an indicator
|
|
94
|
+
if (tasks.length > limit) {
|
|
95
|
+
const remaining = tasks.length - limit;
|
|
96
|
+
return `${lines}\n ... and ${remaining} more task${remaining === 1 ? '' : 's'}`;
|
|
97
|
+
}
|
|
98
|
+
return lines;
|
|
92
99
|
}
|
|
93
100
|
/**
|
|
94
101
|
* Creates list summaries with counts, filters, and guidance
|
|
@@ -16,6 +16,8 @@ export type MappedTask = {
|
|
|
16
16
|
parentId: string | null;
|
|
17
17
|
labels: string[];
|
|
18
18
|
duration: string | null;
|
|
19
|
+
responsibleUid: string | null;
|
|
20
|
+
assignedByUid: string | null;
|
|
19
21
|
};
|
|
20
22
|
/**
|
|
21
23
|
* Creates a mock Task with all required properties and sensible defaults.
|