@doist/todoist-ai 4.16.0 → 4.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/filter-helpers.d.ts +1 -1
- package/dist/index.d.ts +1044 -196
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +61 -81
- package/dist/main.js +15 -23
- package/dist/mcp-helpers.d.ts +5 -5
- package/dist/mcp-helpers.d.ts.map +1 -1
- package/dist/mcp-server-BADReNAy.js +3092 -0
- package/dist/todoist-tool.d.ts +9 -3
- package/dist/todoist-tool.d.ts.map +1 -1
- package/dist/tool-helpers.d.ts +1 -1
- package/dist/tools/add-comments.d.ts +69 -3
- package/dist/tools/add-comments.d.ts.map +1 -1
- package/dist/tools/add-projects.d.ts +34 -3
- package/dist/tools/add-projects.d.ts.map +1 -1
- package/dist/tools/add-sections.d.ts +14 -1
- package/dist/tools/add-sections.d.ts.map +1 -1
- package/dist/tools/add-tasks.d.ts +65 -10
- package/dist/tools/add-tasks.d.ts.map +1 -1
- package/dist/tools/complete-tasks.d.ts +20 -1
- package/dist/tools/complete-tasks.d.ts.map +1 -1
- package/dist/tools/delete-object.d.ts +16 -3
- package/dist/tools/delete-object.d.ts.map +1 -1
- package/dist/tools/fetch.d.ts +8 -1
- package/dist/tools/fetch.d.ts.map +1 -1
- package/dist/tools/find-activity.d.ts +44 -7
- package/dist/tools/find-activity.d.ts.map +1 -1
- package/dist/tools/find-comments.d.ts +69 -3
- package/dist/tools/find-comments.d.ts.map +1 -1
- package/dist/tools/find-completed-tasks.d.ts +63 -5
- package/dist/tools/find-completed-tasks.d.ts.map +1 -1
- package/dist/tools/find-project-collaborators.d.ts +33 -2
- package/dist/tools/find-project-collaborators.d.ts.map +1 -1
- package/dist/tools/find-projects.d.ts +35 -1
- package/dist/tools/find-projects.d.ts.map +1 -1
- package/dist/tools/find-sections.d.ts +15 -1
- package/dist/tools/find-sections.d.ts.map +1 -1
- package/dist/tools/find-tasks-by-date.d.ts +61 -3
- package/dist/tools/find-tasks-by-date.d.ts.map +1 -1
- package/dist/tools/find-tasks.d.ts +63 -5
- package/dist/tools/find-tasks.d.ts.map +1 -1
- package/dist/tools/get-overview.d.ts +24 -1
- package/dist/tools/get-overview.d.ts.map +1 -1
- package/dist/tools/manage-assignments.d.ts +39 -2
- package/dist/tools/manage-assignments.d.ts.map +1 -1
- package/dist/tools/search.d.ts +17 -1
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/update-comments.d.ts +76 -3
- package/dist/tools/update-comments.d.ts.map +1 -1
- package/dist/tools/update-projects.d.ts +43 -1
- package/dist/tools/update-projects.d.ts.map +1 -1
- package/dist/tools/update-sections.d.ts +17 -3
- package/dist/tools/update-sections.d.ts.map +1 -1
- package/dist/tools/update-tasks.d.ts +79 -13
- package/dist/tools/update-tasks.d.ts.map +1 -1
- package/dist/tools/user-info.d.ts +19 -1
- package/dist/tools/user-info.d.ts.map +1 -1
- package/dist/utils/assignment-validator.d.ts +2 -2
- package/dist/utils/output-schemas.d.ts +233 -0
- package/dist/utils/output-schemas.d.ts.map +1 -0
- package/dist/utils/response-builders.d.ts +1 -3
- package/dist/utils/response-builders.d.ts.map +1 -1
- package/dist/utils/test-helpers.d.ts +1 -1
- package/dist/utils/user-resolver.d.ts +1 -1
- package/package.json +10 -8
- package/dist/filter-helpers.js +0 -79
- package/dist/mcp-helpers.js +0 -71
- package/dist/mcp-server.js +0 -142
- package/dist/todoist-tool.js +0 -1
- package/dist/tool-helpers.js +0 -125
- package/dist/tool-helpers.test.d.ts +0 -2
- package/dist/tool-helpers.test.d.ts.map +0 -1
- package/dist/tool-helpers.test.js +0 -223
- package/dist/tools/__tests__/add-comments.test.d.ts +0 -2
- package/dist/tools/__tests__/add-comments.test.d.ts.map +0 -1
- package/dist/tools/__tests__/add-comments.test.js +0 -241
- package/dist/tools/__tests__/add-projects.test.d.ts +0 -2
- package/dist/tools/__tests__/add-projects.test.d.ts.map +0 -1
- package/dist/tools/__tests__/add-projects.test.js +0 -174
- package/dist/tools/__tests__/add-sections.test.d.ts +0 -2
- package/dist/tools/__tests__/add-sections.test.d.ts.map +0 -1
- package/dist/tools/__tests__/add-sections.test.js +0 -185
- package/dist/tools/__tests__/add-tasks.test.d.ts +0 -2
- package/dist/tools/__tests__/add-tasks.test.d.ts.map +0 -1
- package/dist/tools/__tests__/add-tasks.test.js +0 -606
- package/dist/tools/__tests__/assignment-integration.test.d.ts +0 -2
- package/dist/tools/__tests__/assignment-integration.test.d.ts.map +0 -1
- package/dist/tools/__tests__/assignment-integration.test.js +0 -428
- package/dist/tools/__tests__/complete-tasks.test.d.ts +0 -2
- package/dist/tools/__tests__/complete-tasks.test.d.ts.map +0 -1
- package/dist/tools/__tests__/complete-tasks.test.js +0 -206
- package/dist/tools/__tests__/delete-object.test.d.ts +0 -2
- package/dist/tools/__tests__/delete-object.test.d.ts.map +0 -1
- package/dist/tools/__tests__/delete-object.test.js +0 -110
- package/dist/tools/__tests__/fetch.test.d.ts +0 -2
- package/dist/tools/__tests__/fetch.test.d.ts.map +0 -1
- package/dist/tools/__tests__/fetch.test.js +0 -279
- package/dist/tools/__tests__/find-activity.test.d.ts +0 -2
- package/dist/tools/__tests__/find-activity.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-activity.test.js +0 -229
- package/dist/tools/__tests__/find-comments.test.d.ts +0 -2
- package/dist/tools/__tests__/find-comments.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-comments.test.js +0 -236
- package/dist/tools/__tests__/find-completed-tasks.test.d.ts +0 -2
- package/dist/tools/__tests__/find-completed-tasks.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-completed-tasks.test.js +0 -423
- package/dist/tools/__tests__/find-projects.test.d.ts +0 -2
- package/dist/tools/__tests__/find-projects.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-projects.test.js +0 -154
- package/dist/tools/__tests__/find-sections.test.d.ts +0 -2
- package/dist/tools/__tests__/find-sections.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-sections.test.js +0 -313
- package/dist/tools/__tests__/find-tasks-by-date.test.d.ts +0 -2
- package/dist/tools/__tests__/find-tasks-by-date.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-tasks-by-date.test.js +0 -528
- package/dist/tools/__tests__/find-tasks.test.d.ts +0 -2
- package/dist/tools/__tests__/find-tasks.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-tasks.test.js +0 -771
- package/dist/tools/__tests__/get-overview.test.d.ts +0 -2
- package/dist/tools/__tests__/get-overview.test.d.ts.map +0 -1
- package/dist/tools/__tests__/get-overview.test.js +0 -225
- package/dist/tools/__tests__/search.test.d.ts +0 -2
- package/dist/tools/__tests__/search.test.d.ts.map +0 -1
- package/dist/tools/__tests__/search.test.js +0 -206
- package/dist/tools/__tests__/update-comments.test.d.ts +0 -2
- package/dist/tools/__tests__/update-comments.test.d.ts.map +0 -1
- package/dist/tools/__tests__/update-comments.test.js +0 -294
- package/dist/tools/__tests__/update-projects.test.d.ts +0 -2
- package/dist/tools/__tests__/update-projects.test.d.ts.map +0 -1
- package/dist/tools/__tests__/update-projects.test.js +0 -217
- package/dist/tools/__tests__/update-sections.test.d.ts +0 -2
- package/dist/tools/__tests__/update-sections.test.d.ts.map +0 -1
- package/dist/tools/__tests__/update-sections.test.js +0 -169
- package/dist/tools/__tests__/update-tasks.test.d.ts +0 -2
- package/dist/tools/__tests__/update-tasks.test.d.ts.map +0 -1
- package/dist/tools/__tests__/update-tasks.test.js +0 -788
- package/dist/tools/__tests__/user-info.test.d.ts +0 -2
- package/dist/tools/__tests__/user-info.test.d.ts.map +0 -1
- package/dist/tools/__tests__/user-info.test.js +0 -139
- package/dist/tools/add-comments.js +0 -89
- package/dist/tools/add-projects.js +0 -63
- package/dist/tools/add-sections.js +0 -74
- package/dist/tools/add-tasks.js +0 -169
- package/dist/tools/complete-tasks.js +0 -68
- package/dist/tools/delete-object.js +0 -79
- package/dist/tools/fetch.js +0 -102
- package/dist/tools/find-activity.js +0 -221
- package/dist/tools/find-comments.js +0 -148
- package/dist/tools/find-completed-tasks.js +0 -168
- package/dist/tools/find-project-collaborators.js +0 -151
- package/dist/tools/find-projects.js +0 -101
- package/dist/tools/find-sections.js +0 -101
- package/dist/tools/find-tasks-by-date.js +0 -198
- package/dist/tools/find-tasks.js +0 -329
- package/dist/tools/get-overview.js +0 -249
- package/dist/tools/manage-assignments.js +0 -337
- package/dist/tools/search.js +0 -65
- package/dist/tools/update-comments.js +0 -82
- package/dist/tools/update-projects.js +0 -84
- package/dist/tools/update-sections.js +0 -70
- package/dist/tools/update-tasks.js +0 -179
- package/dist/tools/user-info.js +0 -142
- package/dist/utils/assignment-validator.js +0 -253
- package/dist/utils/constants.js +0 -45
- package/dist/utils/duration-parser.js +0 -96
- package/dist/utils/duration-parser.test.d.ts +0 -2
- package/dist/utils/duration-parser.test.d.ts.map +0 -1
- package/dist/utils/duration-parser.test.js +0 -147
- package/dist/utils/labels.js +0 -18
- package/dist/utils/priorities.js +0 -20
- package/dist/utils/response-builders.js +0 -210
- package/dist/utils/sanitize-data.js +0 -37
- package/dist/utils/sanitize-data.test.d.ts +0 -2
- package/dist/utils/sanitize-data.test.d.ts.map +0 -1
- package/dist/utils/sanitize-data.test.js +0 -93
- package/dist/utils/test-helpers.js +0 -237
- package/dist/utils/tool-names.js +0 -40
- package/dist/utils/user-resolver.js +0 -179
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import { DisplayLimits } from './constants.js';
|
|
2
|
-
import { formatPriorityForDisplay } from './priorities.js';
|
|
3
|
-
import { ToolNames } from './tool-names.js';
|
|
4
|
-
/**
|
|
5
|
-
* Helper function to get date string in YYYY-MM-DD format
|
|
6
|
-
*/
|
|
7
|
-
export function getDateString(date = new Date()) {
|
|
8
|
-
const parts = date.toISOString().split('T');
|
|
9
|
-
return parts[0] ?? '';
|
|
10
|
-
}
|
|
11
|
-
const { FIND_TASKS_BY_DATE, ADD_TASKS, UPDATE_TASKS, COMPLETE_TASKS, GET_OVERVIEW } = ToolNames;
|
|
12
|
-
/**
|
|
13
|
-
* Creates concise, actionable summaries for task operations instead of raw JSON
|
|
14
|
-
*/
|
|
15
|
-
export function summarizeTaskOperation(action, tasks, options = {}) {
|
|
16
|
-
const { nextSteps, context, showDetails = false } = options;
|
|
17
|
-
const count = tasks.length;
|
|
18
|
-
const bits = [];
|
|
19
|
-
// Main action summary
|
|
20
|
-
const taskOrTasks = count === 1 ? 'task' : 'tasks';
|
|
21
|
-
const actionSummary = `${action} ${count} ${taskOrTasks}${context ? ` ${context}` : ''}.`;
|
|
22
|
-
bits.push(actionSummary);
|
|
23
|
-
// Task details preview (if requested or small batch)
|
|
24
|
-
const smallBatchLimit = 5;
|
|
25
|
-
if (showDetails || count <= smallBatchLimit) {
|
|
26
|
-
const previews = previewTasks(tasks, smallBatchLimit);
|
|
27
|
-
if (previews.length > 0) {
|
|
28
|
-
const moreInfo = count > smallBatchLimit ? `, +${count - smallBatchLimit} more` : '';
|
|
29
|
-
bits.push(`Tasks:\n${previews}${moreInfo}.`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
// Next steps guidance
|
|
33
|
-
if (nextSteps?.length) {
|
|
34
|
-
bits.push(formatNextSteps(nextSteps));
|
|
35
|
-
}
|
|
36
|
-
return bits.join('\n');
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Creates batch operation summaries with success/failure breakdown
|
|
40
|
-
*/
|
|
41
|
-
export function summarizeBatch(params) {
|
|
42
|
-
const { action, success, total, successItems, failures, nextSteps } = params;
|
|
43
|
-
const bits = [];
|
|
44
|
-
// Main result summary
|
|
45
|
-
const successBit = `${action}: ${success}/${total} successful.`;
|
|
46
|
-
bits.push(successBit);
|
|
47
|
-
// Success items (if provided and reasonable count)
|
|
48
|
-
if (successItems?.length && successItems.length <= 5) {
|
|
49
|
-
bits.push(`Completed:\n${successItems.map((item) => ` ${item}`).join('\n')}.`);
|
|
50
|
-
}
|
|
51
|
-
// Failure details (if any)
|
|
52
|
-
if (failures?.length) {
|
|
53
|
-
const failureCount = failures.length;
|
|
54
|
-
const failureBit = `Failed (${failureCount}):\n${failures
|
|
55
|
-
.slice(0, DisplayLimits.MAX_FAILURES_SHOWN)
|
|
56
|
-
.map((f) => ` ${f.item} (Error: ${f.error}${f.code ? ` [${f.code}]` : ''})`)
|
|
57
|
-
.join('\n')}${failureCount > DisplayLimits.MAX_FAILURES_SHOWN ? `, +${failureCount - DisplayLimits.MAX_FAILURES_SHOWN} more` : ''}.`;
|
|
58
|
-
bits.push(failureBit);
|
|
59
|
-
}
|
|
60
|
-
// Next steps
|
|
61
|
-
if (nextSteps?.length) {
|
|
62
|
-
bits.push(formatNextSteps(nextSteps));
|
|
63
|
-
}
|
|
64
|
-
return bits.join('\n');
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Formats a single task-like object into a readable preview line
|
|
68
|
-
*/
|
|
69
|
-
function formatTaskPreview(task) {
|
|
70
|
-
const content = task.content || task.title || 'Untitled';
|
|
71
|
-
const due = task.dueDate ? ` • due ${task.dueDate}` : '';
|
|
72
|
-
const priority = task.priority ? ` • ${formatPriorityForDisplay(task.priority)}` : '';
|
|
73
|
-
const project = task.projectName ? ` • ${task.projectName}` : '';
|
|
74
|
-
const id = task.id ? ` • id=${task.id}` : '';
|
|
75
|
-
return ` ${content}${due}${priority}${project}${id}`;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Formats a single project-like object into a readable preview line
|
|
79
|
-
*/
|
|
80
|
-
export function formatProjectPreview(project) {
|
|
81
|
-
const isInbox = project.inboxProject ? ' • Inbox' : '';
|
|
82
|
-
const isFavorite = project.isFavorite ? ' • ⭐' : '';
|
|
83
|
-
const isShared = project.isShared ? ' • Shared' : '';
|
|
84
|
-
const viewStyle = project.viewStyle && project.viewStyle !== 'list' ? ` • ${project.viewStyle}` : '';
|
|
85
|
-
const id = ` • id=${project.id}`;
|
|
86
|
-
return ` ${project.name}${isInbox}${isFavorite}${isShared}${viewStyle}${id}`;
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Creates preview lines for task lists
|
|
90
|
-
*/
|
|
91
|
-
export function previewTasks(tasks, limit = 5) {
|
|
92
|
-
const previewTasks = tasks.slice(0, limit);
|
|
93
|
-
const lines = previewTasks.map(formatTaskPreview).join('\n');
|
|
94
|
-
// If we're showing fewer tasks than the total, add an indicator
|
|
95
|
-
if (tasks.length > limit) {
|
|
96
|
-
const remaining = tasks.length - limit;
|
|
97
|
-
return `${lines}\n ... and ${remaining} more task${remaining === 1 ? '' : 's'}`;
|
|
98
|
-
}
|
|
99
|
-
return lines;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Creates list summaries with counts, filters, and guidance
|
|
103
|
-
*/
|
|
104
|
-
export function summarizeList({ subject, count, limit, nextCursor, filterHints, previewLines, zeroReasonHints, nextSteps, }) {
|
|
105
|
-
const bits = [];
|
|
106
|
-
// Header with count and pagination info
|
|
107
|
-
const header = `${subject}: ${count}${typeof limit === 'number' ? ` (limit ${limit})` : ''}${nextCursor ? ', more available' : ''}.`;
|
|
108
|
-
bits.push(header);
|
|
109
|
-
// Filter information
|
|
110
|
-
if (filterHints?.length) {
|
|
111
|
-
bits.push(`Filter: ${filterHints.join('; ')}.`);
|
|
112
|
-
}
|
|
113
|
-
// Preview of items
|
|
114
|
-
if (previewLines?.length) {
|
|
115
|
-
bits.push(`Preview:\n${previewLines}`);
|
|
116
|
-
}
|
|
117
|
-
// Help for empty results
|
|
118
|
-
if (!count && zeroReasonHints?.length) {
|
|
119
|
-
bits.push(`No results. ${zeroReasonHints.join('; ')}.`);
|
|
120
|
-
}
|
|
121
|
-
// Next steps guidance
|
|
122
|
-
if (nextSteps?.length || nextCursor) {
|
|
123
|
-
bits.push(formatNextSteps(nextSteps || [], nextCursor));
|
|
124
|
-
}
|
|
125
|
-
return bits.join('\n');
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Formats next steps array into a consistent "Next:" section
|
|
129
|
-
* If nextCursor is provided, adds cursor instruction to the steps
|
|
130
|
-
*/
|
|
131
|
-
export function formatNextSteps(nextSteps, nextCursor) {
|
|
132
|
-
const allSteps = [...nextSteps];
|
|
133
|
-
if (nextCursor) {
|
|
134
|
-
allSteps.push(`Pass cursor '${nextCursor}' to fetch more results.`);
|
|
135
|
-
}
|
|
136
|
-
return `Next:\n${allSteps.map((step) => `- ${step}`).join('\n')}`;
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Helper to generate contextual next steps based on task operations
|
|
140
|
-
*/
|
|
141
|
-
export function generateTaskNextSteps(operation, tasks, context) {
|
|
142
|
-
const nextSteps = [];
|
|
143
|
-
const count = context?.count ?? tasks.length;
|
|
144
|
-
switch (operation.toLowerCase()) {
|
|
145
|
-
case 'added':
|
|
146
|
-
// Context-aware suggestions for newly added tasks
|
|
147
|
-
if (context?.hasToday) {
|
|
148
|
-
nextSteps.push(`Use ${FIND_TASKS_BY_DATE}('today') to review today's updated schedule`);
|
|
149
|
-
}
|
|
150
|
-
else if (context?.hasOverdue) {
|
|
151
|
-
nextSteps.push(`Use ${FIND_TASKS_BY_DATE}('today') to prioritize past-due items`);
|
|
152
|
-
}
|
|
153
|
-
else if (context?.projectName) {
|
|
154
|
-
nextSteps.push(`Use ${GET_OVERVIEW} with projectId to see ${context.projectName} structure`);
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
nextSteps.push(`Use ${GET_OVERVIEW} to see your updated project organization`);
|
|
158
|
-
}
|
|
159
|
-
// Time-based suggestions
|
|
160
|
-
if (context?.timeOfDay === 'morning') {
|
|
161
|
-
nextSteps.push(`Use ${FIND_TASKS_BY_DATE}('today') to plan your day`);
|
|
162
|
-
}
|
|
163
|
-
break;
|
|
164
|
-
case 'updated':
|
|
165
|
-
case 'organized':
|
|
166
|
-
if (context?.hasToday) {
|
|
167
|
-
nextSteps.push(`Use ${FIND_TASKS_BY_DATE}('today') to see your prioritized schedule`);
|
|
168
|
-
}
|
|
169
|
-
else if (context?.hasHighPriority) {
|
|
170
|
-
nextSteps.push(`Use ${FIND_TASKS_BY_DATE} with filter to focus on high-priority items`);
|
|
171
|
-
}
|
|
172
|
-
else {
|
|
173
|
-
nextSteps.push(`Use ${GET_OVERVIEW} to see your updated project structure`);
|
|
174
|
-
}
|
|
175
|
-
break;
|
|
176
|
-
case 'completed':
|
|
177
|
-
if (context?.timeOfDay === 'evening') {
|
|
178
|
-
nextSteps.push(`Use ${FIND_TASKS_BY_DATE}('tomorrow') to plan upcoming work`);
|
|
179
|
-
}
|
|
180
|
-
else if (context?.hasOverdue) {
|
|
181
|
-
nextSteps.push(`Use ${FIND_TASKS_BY_DATE}('today') to tackle remaining past-due items`);
|
|
182
|
-
}
|
|
183
|
-
else {
|
|
184
|
-
nextSteps.push(`Use ${FIND_TASKS_BY_DATE}('today') to see remaining work`);
|
|
185
|
-
}
|
|
186
|
-
break;
|
|
187
|
-
case 'listed':
|
|
188
|
-
if (context?.isEmptyResult) {
|
|
189
|
-
nextSteps.push(`Use ${ADD_TASKS} to add tasks for this timeframe`);
|
|
190
|
-
nextSteps.push(`Use ${GET_OVERVIEW} with projectId to see tasks in other projects`);
|
|
191
|
-
}
|
|
192
|
-
else if (count > 0) {
|
|
193
|
-
// Tailor suggestions based on result size
|
|
194
|
-
if (count > DisplayLimits.BATCH_OPERATION_THRESHOLD) {
|
|
195
|
-
nextSteps.push(`Use ${UPDATE_TASKS} to batch-update priorities or dates`);
|
|
196
|
-
nextSteps.push('Consider breaking large tasks into subtasks');
|
|
197
|
-
}
|
|
198
|
-
else {
|
|
199
|
-
nextSteps.push(`Use ${UPDATE_TASKS} to modify priorities or due dates`);
|
|
200
|
-
}
|
|
201
|
-
nextSteps.push(`Use ${COMPLETE_TASKS} to mark finished tasks`);
|
|
202
|
-
// Time-sensitive suggestions
|
|
203
|
-
if (context?.hasOverdue) {
|
|
204
|
-
nextSteps.push('Focus on overdue items first to get back on track');
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
break;
|
|
208
|
-
}
|
|
209
|
-
return nextSteps;
|
|
210
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Removes all null fields, empty objects, and empty arrays from an object recursively.
|
|
3
|
-
* This ensures that data sent to agents doesn't include unnecessary empty values.
|
|
4
|
-
*
|
|
5
|
-
* @param obj - The object to sanitize
|
|
6
|
-
* @returns A new object with all null fields, empty objects, and empty arrays removed
|
|
7
|
-
*/
|
|
8
|
-
export function removeNullFields(obj) {
|
|
9
|
-
if (obj === null || obj === undefined) {
|
|
10
|
-
return obj;
|
|
11
|
-
}
|
|
12
|
-
if (Array.isArray(obj)) {
|
|
13
|
-
return obj.map((item) => removeNullFields(item));
|
|
14
|
-
}
|
|
15
|
-
if (typeof obj === 'object') {
|
|
16
|
-
const sanitized = {};
|
|
17
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
18
|
-
if (value !== null) {
|
|
19
|
-
const cleanedValue = removeNullFields(value);
|
|
20
|
-
// Skip empty arrays
|
|
21
|
-
if (Array.isArray(cleanedValue) && cleanedValue.length === 0) {
|
|
22
|
-
continue;
|
|
23
|
-
}
|
|
24
|
-
// Skip empty objects
|
|
25
|
-
if (cleanedValue !== null &&
|
|
26
|
-
typeof cleanedValue === 'object' &&
|
|
27
|
-
!Array.isArray(cleanedValue) &&
|
|
28
|
-
Object.keys(cleanedValue).length === 0) {
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
sanitized[key] = cleanedValue;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return sanitized;
|
|
35
|
-
}
|
|
36
|
-
return obj;
|
|
37
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sanitize-data.test.d.ts","sourceRoot":"","sources":["../../src/utils/sanitize-data.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { removeNullFields } from './sanitize-data.js';
|
|
2
|
-
describe('removeNullFields', () => {
|
|
3
|
-
it('should remove null fields from objects including nested objects', () => {
|
|
4
|
-
const input = {
|
|
5
|
-
name: 'John',
|
|
6
|
-
age: null,
|
|
7
|
-
email: 'john@example.com',
|
|
8
|
-
phone: null,
|
|
9
|
-
address: {
|
|
10
|
-
street: '123 Main St',
|
|
11
|
-
city: null,
|
|
12
|
-
country: 'USA',
|
|
13
|
-
},
|
|
14
|
-
};
|
|
15
|
-
const result = removeNullFields(input);
|
|
16
|
-
expect(result).toEqual({
|
|
17
|
-
name: 'John',
|
|
18
|
-
email: 'john@example.com',
|
|
19
|
-
address: {
|
|
20
|
-
street: '123 Main St',
|
|
21
|
-
country: 'USA',
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
it('should handle arrays with null values', () => {
|
|
26
|
-
const input = {
|
|
27
|
-
items: [
|
|
28
|
-
{ id: 1, value: 'test' },
|
|
29
|
-
{ id: 2, value: null },
|
|
30
|
-
{ id: 3, value: 'another' },
|
|
31
|
-
],
|
|
32
|
-
};
|
|
33
|
-
const result = removeNullFields(input);
|
|
34
|
-
expect(result).toEqual({
|
|
35
|
-
items: [{ id: 1, value: 'test' }, { id: 2 }, { id: 3, value: 'another' }],
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
it('should handle null objects', () => {
|
|
39
|
-
const result = removeNullFields(null);
|
|
40
|
-
expect(result).toBeNull();
|
|
41
|
-
});
|
|
42
|
-
it('should remove empty objects and empty arrays', () => {
|
|
43
|
-
const input = {
|
|
44
|
-
something: 'hello',
|
|
45
|
-
another: {},
|
|
46
|
-
yetAnother: [],
|
|
47
|
-
};
|
|
48
|
-
const result = removeNullFields(input);
|
|
49
|
-
expect(result).toEqual({
|
|
50
|
-
something: 'hello',
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
it('should remove empty objects and empty arrays in nested structures', () => {
|
|
54
|
-
const input = {
|
|
55
|
-
name: 'Test',
|
|
56
|
-
metadata: {},
|
|
57
|
-
tags: [],
|
|
58
|
-
nested: {
|
|
59
|
-
data: 'value',
|
|
60
|
-
emptyObj: {},
|
|
61
|
-
emptyArr: [],
|
|
62
|
-
deepNested: {
|
|
63
|
-
anotherEmpty: {},
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
items: [
|
|
67
|
-
{ id: 1, data: 'test', empty: {} },
|
|
68
|
-
{ id: 2, list: [] },
|
|
69
|
-
],
|
|
70
|
-
};
|
|
71
|
-
const result = removeNullFields(input);
|
|
72
|
-
expect(result).toEqual({
|
|
73
|
-
name: 'Test',
|
|
74
|
-
nested: {
|
|
75
|
-
data: 'value',
|
|
76
|
-
},
|
|
77
|
-
items: [{ id: 1, data: 'test' }, { id: 2 }],
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
it('should keep arrays with values and objects with properties', () => {
|
|
81
|
-
const input = {
|
|
82
|
-
emptyArray: [],
|
|
83
|
-
arrayWithValues: [1, 2, 3],
|
|
84
|
-
emptyObject: {},
|
|
85
|
-
objectWithProps: { key: 'value' },
|
|
86
|
-
};
|
|
87
|
-
const result = removeNullFields(input);
|
|
88
|
-
expect(result).toEqual({
|
|
89
|
-
arrayWithValues: [1, 2, 3],
|
|
90
|
-
objectWithProps: { key: 'value' },
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
});
|
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Creates a mock Task with all required properties and sensible defaults.
|
|
3
|
-
* Pass only the properties you want to override for your specific test.
|
|
4
|
-
*/
|
|
5
|
-
export function createMockTask(overrides = {}) {
|
|
6
|
-
return {
|
|
7
|
-
id: '8485093748',
|
|
8
|
-
content: 'Test task content',
|
|
9
|
-
description: '',
|
|
10
|
-
completedAt: null,
|
|
11
|
-
labels: [],
|
|
12
|
-
childOrder: 1,
|
|
13
|
-
priority: 1,
|
|
14
|
-
projectId: '6cfCcrrCFg2xP94Q',
|
|
15
|
-
sectionId: null,
|
|
16
|
-
parentId: null,
|
|
17
|
-
url: 'https://todoist.com/showTask?id=8485093748',
|
|
18
|
-
// Use correct property names from Task schema
|
|
19
|
-
noteCount: 0,
|
|
20
|
-
addedByUid: '713437',
|
|
21
|
-
addedAt: '2025-08-13T22:09:56.123456Z',
|
|
22
|
-
deadline: null,
|
|
23
|
-
responsibleUid: null,
|
|
24
|
-
assignedByUid: null,
|
|
25
|
-
isCollapsed: false,
|
|
26
|
-
isDeleted: false,
|
|
27
|
-
duration: null,
|
|
28
|
-
checked: false,
|
|
29
|
-
updatedAt: '2025-08-13T22:09:56.123456Z',
|
|
30
|
-
due: null,
|
|
31
|
-
dayOrder: 0,
|
|
32
|
-
userId: '713437',
|
|
33
|
-
...overrides,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Creates a mock Section with all required properties and sensible defaults.
|
|
38
|
-
* Pass only the properties you want to override for your specific test.
|
|
39
|
-
*/
|
|
40
|
-
export function createMockSection(overrides = {}) {
|
|
41
|
-
return {
|
|
42
|
-
id: 'section-123',
|
|
43
|
-
projectId: '6cfCcrrCFg2xP94Q',
|
|
44
|
-
sectionOrder: 1,
|
|
45
|
-
userId: 'test-user',
|
|
46
|
-
addedAt: '2024-01-01T00:00:00Z',
|
|
47
|
-
updatedAt: '2024-01-01T00:00:00Z',
|
|
48
|
-
archivedAt: null,
|
|
49
|
-
isArchived: false,
|
|
50
|
-
isDeleted: false,
|
|
51
|
-
isCollapsed: false,
|
|
52
|
-
name: 'Test Section',
|
|
53
|
-
url: 'https://todoist.com/sections/section-123',
|
|
54
|
-
...overrides,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Creates a mock PersonalProject with all required properties and sensible defaults.
|
|
59
|
-
* Pass only the properties you want to override for your specific test.
|
|
60
|
-
*/
|
|
61
|
-
export function createMockProject(overrides = {}) {
|
|
62
|
-
return {
|
|
63
|
-
id: '6cfCcrrCFg2xP94Q',
|
|
64
|
-
name: 'Test Project',
|
|
65
|
-
color: 'charcoal',
|
|
66
|
-
isFavorite: false,
|
|
67
|
-
isShared: false,
|
|
68
|
-
parentId: null,
|
|
69
|
-
inboxProject: false,
|
|
70
|
-
viewStyle: 'list',
|
|
71
|
-
url: 'https://todoist.com/projects/6cfCcrrCFg2xP94Q',
|
|
72
|
-
isDeleted: false,
|
|
73
|
-
updatedAt: '2025-08-13T22:09:55.841800Z',
|
|
74
|
-
createdAt: '2025-08-13T22:09:55.841785Z',
|
|
75
|
-
childOrder: 1,
|
|
76
|
-
defaultOrder: 0,
|
|
77
|
-
description: '',
|
|
78
|
-
isCollapsed: false,
|
|
79
|
-
canAssignTasks: false,
|
|
80
|
-
isFrozen: false,
|
|
81
|
-
isArchived: false,
|
|
82
|
-
...overrides,
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Creates a mock API response object with results and nextCursor.
|
|
87
|
-
*/
|
|
88
|
-
export function createMockApiResponse(results, nextCursor = null) {
|
|
89
|
-
return {
|
|
90
|
-
results,
|
|
91
|
-
nextCursor,
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Creates a simplified mapped task (matches mapTask output) for filter-based query tests.
|
|
96
|
-
*/
|
|
97
|
-
export function createMappedTask(overrides = {}) {
|
|
98
|
-
return {
|
|
99
|
-
id: TEST_IDS.TASK_1,
|
|
100
|
-
content: 'Test task content',
|
|
101
|
-
description: '',
|
|
102
|
-
dueDate: undefined,
|
|
103
|
-
recurring: false,
|
|
104
|
-
deadlineDate: undefined,
|
|
105
|
-
priority: 1,
|
|
106
|
-
projectId: TEST_IDS.PROJECT_TEST,
|
|
107
|
-
sectionId: null,
|
|
108
|
-
parentId: null,
|
|
109
|
-
labels: [],
|
|
110
|
-
duration: null,
|
|
111
|
-
responsibleUid: null,
|
|
112
|
-
assignedByUid: null,
|
|
113
|
-
checked: false,
|
|
114
|
-
completedAt: null,
|
|
115
|
-
...overrides,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Common error messages used across tests.
|
|
120
|
-
*/
|
|
121
|
-
export const TEST_ERRORS = {
|
|
122
|
-
API_RATE_LIMIT: 'API Error: Rate limit exceeded',
|
|
123
|
-
API_UNAUTHORIZED: 'API Error: Unauthorized',
|
|
124
|
-
INVALID_CURSOR: 'Invalid cursor format',
|
|
125
|
-
INVALID_FILTER: 'Invalid filter query',
|
|
126
|
-
};
|
|
127
|
-
/**
|
|
128
|
-
* Creates multiple test cases for parameterized testing.
|
|
129
|
-
*/
|
|
130
|
-
export function createTestCases(cases) {
|
|
131
|
-
return cases;
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Extracts the text content from a tool output for snapshot testing.
|
|
135
|
-
* This allows tests to match against just the text content while tools return structured output.
|
|
136
|
-
*/
|
|
137
|
-
export function extractTextContent(toolOutput) {
|
|
138
|
-
if (typeof toolOutput === 'string') {
|
|
139
|
-
return toolOutput;
|
|
140
|
-
}
|
|
141
|
-
if (typeof toolOutput === 'object' && toolOutput !== null && 'content' in toolOutput) {
|
|
142
|
-
const output = toolOutput;
|
|
143
|
-
if (Array.isArray(output.content) &&
|
|
144
|
-
output.content[0] &&
|
|
145
|
-
typeof output.content[0] === 'object' &&
|
|
146
|
-
output.content[0] !== null &&
|
|
147
|
-
'type' in output.content[0] &&
|
|
148
|
-
'text' in output.content[0] &&
|
|
149
|
-
output.content[0].type === 'text') {
|
|
150
|
-
return output.content[0].text;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
throw new Error('Expected tool output to have text content');
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Extracts the structured content from a tool output for testing.
|
|
157
|
-
* This handles both the new `structuredContent` field and legacy JSON-encoded content.
|
|
158
|
-
*/
|
|
159
|
-
export function extractStructuredContent(output) {
|
|
160
|
-
// Check for new structuredContent field first
|
|
161
|
-
if ('structuredContent' in output && typeof output.structuredContent === 'object') {
|
|
162
|
-
return output.structuredContent;
|
|
163
|
-
}
|
|
164
|
-
// Fall back to checking for JSON content in the content array
|
|
165
|
-
if ('content' in output && Array.isArray(output.content)) {
|
|
166
|
-
for (const item of output.content) {
|
|
167
|
-
if (typeof item === 'object' &&
|
|
168
|
-
item !== null &&
|
|
169
|
-
'type' in item &&
|
|
170
|
-
'text' in item &&
|
|
171
|
-
item.type === 'text' &&
|
|
172
|
-
item.mimeType === 'application/json') {
|
|
173
|
-
return JSON.parse(item.text);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
throw new Error('Expected tool output to have structured content');
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Common mock IDs used across tests for consistency.
|
|
181
|
-
*/
|
|
182
|
-
export const TEST_IDS = {
|
|
183
|
-
TASK_1: '8485093748',
|
|
184
|
-
TASK_2: '8485093749',
|
|
185
|
-
TASK_3: '8485093750',
|
|
186
|
-
PROJECT_INBOX: 'inbox-project-id',
|
|
187
|
-
PROJECT_WORK: 'work-project-id',
|
|
188
|
-
PROJECT_TEST: '6cfCcrrCFg2xP94Q',
|
|
189
|
-
SECTION_1: 'section-123',
|
|
190
|
-
SECTION_2: 'section-456',
|
|
191
|
-
USER_ID: '713437',
|
|
192
|
-
};
|
|
193
|
-
/**
|
|
194
|
-
* Fixed date for consistent test snapshots.
|
|
195
|
-
* Use this instead of new Date() in tests to avoid snapshot drift.
|
|
196
|
-
*/
|
|
197
|
-
export const TODAY = '2025-08-17';
|
|
198
|
-
/**
|
|
199
|
-
* Creates a mock CurrentUser with all required properties and sensible defaults.
|
|
200
|
-
* Pass only the properties you want to override for your specific test.
|
|
201
|
-
*/
|
|
202
|
-
export function createMockUser(overrides = {}) {
|
|
203
|
-
return {
|
|
204
|
-
id: TEST_IDS.USER_ID,
|
|
205
|
-
email: 'test@example.com',
|
|
206
|
-
fullName: 'Test User',
|
|
207
|
-
businessAccountId: null,
|
|
208
|
-
isPremium: false,
|
|
209
|
-
dateFormat: 0,
|
|
210
|
-
timeFormat: 0,
|
|
211
|
-
weeklyGoal: 5,
|
|
212
|
-
dailyGoal: 5,
|
|
213
|
-
completedCount: 0,
|
|
214
|
-
completedToday: 0,
|
|
215
|
-
daysOff: [],
|
|
216
|
-
inboxProjectId: TEST_IDS.PROJECT_INBOX,
|
|
217
|
-
karma: 0,
|
|
218
|
-
karmaTrend: 'up',
|
|
219
|
-
lang: 'en',
|
|
220
|
-
nextWeek: 1,
|
|
221
|
-
startDay: 1,
|
|
222
|
-
startPage: 'today',
|
|
223
|
-
weekendStartDay: 6,
|
|
224
|
-
tzInfo: {
|
|
225
|
-
timezone: 'UTC',
|
|
226
|
-
gmtString: '+00:00',
|
|
227
|
-
hours: 0,
|
|
228
|
-
minutes: 0,
|
|
229
|
-
isDst: 0,
|
|
230
|
-
},
|
|
231
|
-
avatarBig: null,
|
|
232
|
-
avatarMedium: null,
|
|
233
|
-
avatarS640: null,
|
|
234
|
-
avatarSmall: null,
|
|
235
|
-
...overrides,
|
|
236
|
-
};
|
|
237
|
-
}
|
package/dist/utils/tool-names.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Centralized tool names module
|
|
3
|
-
*
|
|
4
|
-
* This module provides a single source of truth for all tool names used throughout the codebase.
|
|
5
|
-
* Each tool should import its own name from this module to avoid hardcoded strings.
|
|
6
|
-
* This prevents outdated references when tool names change.
|
|
7
|
-
*/
|
|
8
|
-
export const ToolNames = {
|
|
9
|
-
// Task management tools
|
|
10
|
-
ADD_TASKS: 'add-tasks',
|
|
11
|
-
COMPLETE_TASKS: 'complete-tasks',
|
|
12
|
-
UPDATE_TASKS: 'update-tasks',
|
|
13
|
-
FIND_TASKS: 'find-tasks',
|
|
14
|
-
FIND_TASKS_BY_DATE: 'find-tasks-by-date',
|
|
15
|
-
FIND_COMPLETED_TASKS: 'find-completed-tasks',
|
|
16
|
-
// Project management tools
|
|
17
|
-
ADD_PROJECTS: 'add-projects',
|
|
18
|
-
UPDATE_PROJECTS: 'update-projects',
|
|
19
|
-
FIND_PROJECTS: 'find-projects',
|
|
20
|
-
// Section management tools
|
|
21
|
-
ADD_SECTIONS: 'add-sections',
|
|
22
|
-
UPDATE_SECTIONS: 'update-sections',
|
|
23
|
-
FIND_SECTIONS: 'find-sections',
|
|
24
|
-
// Comment management tools
|
|
25
|
-
ADD_COMMENTS: 'add-comments',
|
|
26
|
-
UPDATE_COMMENTS: 'update-comments',
|
|
27
|
-
FIND_COMMENTS: 'find-comments',
|
|
28
|
-
// Assignment and collaboration tools
|
|
29
|
-
FIND_PROJECT_COLLABORATORS: 'find-project-collaborators',
|
|
30
|
-
MANAGE_ASSIGNMENTS: 'manage-assignments',
|
|
31
|
-
// Activity and audit tools
|
|
32
|
-
FIND_ACTIVITY: 'find-activity',
|
|
33
|
-
// General tools
|
|
34
|
-
GET_OVERVIEW: 'get-overview',
|
|
35
|
-
DELETE_OBJECT: 'delete-object',
|
|
36
|
-
USER_INFO: 'user-info',
|
|
37
|
-
// OpenAI MCP tools
|
|
38
|
-
SEARCH: 'search',
|
|
39
|
-
FETCH: 'fetch',
|
|
40
|
-
};
|