@hauptsache.net/clickup-mcp 1.1.1 → 1.3.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/LICENSE +21 -0
- package/README.md +20 -21
- package/dist/cli.js +8 -0
- package/dist/clickup-text.d.ts +5 -2
- package/dist/clickup-text.d.ts.map +1 -1
- package/dist/clickup-text.js +133 -61
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +37 -13
- package/dist/shared/config.d.ts +1 -0
- package/dist/shared/config.d.ts.map +1 -1
- package/dist/shared/config.js +1 -0
- package/dist/shared/image-processing.d.ts +13 -0
- package/dist/shared/image-processing.d.ts.map +1 -0
- package/dist/shared/image-processing.js +126 -0
- package/dist/shared/types.d.ts +7 -1
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/utils.d.ts +24 -26
- package/dist/shared/utils.d.ts.map +1 -1
- package/dist/shared/utils.js +424 -101
- package/dist/tools/doc-tools.d.ts +4 -0
- package/dist/tools/doc-tools.d.ts.map +1 -0
- package/dist/tools/doc-tools.js +485 -0
- package/dist/tools/list-tools.d.ts.map +1 -1
- package/dist/tools/list-tools.js +26 -149
- package/dist/tools/search-tools.d.ts +1 -1
- package/dist/tools/search-tools.d.ts.map +1 -1
- package/dist/tools/search-tools.js +85 -62
- package/dist/tools/space-tools.d.ts.map +1 -1
- package/dist/tools/space-tools.js +153 -27
- package/dist/tools/task-tools.d.ts +1 -2
- package/dist/tools/task-tools.d.ts.map +1 -1
- package/dist/tools/task-tools.js +86 -405
- package/dist/tools/task-write-tools.d.ts +3 -0
- package/dist/tools/task-write-tools.d.ts.map +1 -0
- package/dist/tools/task-write-tools.js +375 -0
- package/dist/tools/time-tools.d.ts.map +1 -1
- package/dist/tools/time-tools.js +2 -30
- package/package.json +15 -5
package/dist/shared/utils.js
CHANGED
|
@@ -3,21 +3,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.downloadImages = void 0;
|
|
6
7
|
exports.isTaskId = isTaskId;
|
|
7
8
|
exports.getCurrentUser = getCurrentUser;
|
|
8
|
-
exports.limitImages = limitImages;
|
|
9
9
|
exports.getSpaceDetails = getSpaceDetails;
|
|
10
10
|
exports.getTaskSearchIndex = getTaskSearchIndex;
|
|
11
11
|
exports.generateTaskUrl = generateTaskUrl;
|
|
12
12
|
exports.generateListUrl = generateListUrl;
|
|
13
13
|
exports.generateSpaceUrl = generateSpaceUrl;
|
|
14
14
|
exports.generateFolderUrl = generateFolderUrl;
|
|
15
|
-
exports.
|
|
16
|
-
exports.
|
|
17
|
-
exports.
|
|
18
|
-
exports.
|
|
19
|
-
exports.
|
|
20
|
-
exports.
|
|
15
|
+
exports.generateDocumentUrl = generateDocumentUrl;
|
|
16
|
+
exports.getSpaceSearchIndex = getSpaceSearchIndex;
|
|
17
|
+
exports.getSpaceContent = getSpaceContent;
|
|
18
|
+
exports.getAllTeamMembers = getAllTeamMembers;
|
|
19
|
+
exports.getDocumentSearchIndex = getDocumentSearchIndex;
|
|
20
|
+
exports.performMultiTermSearch = performMultiTermSearch;
|
|
21
21
|
const config_1 = require("./config");
|
|
22
22
|
const fuse_js_1 = __importDefault(require("fuse.js"));
|
|
23
23
|
const GLOBAL_REFRESH_INTERVAL = 60000; // 60 seconds - that is the rate limit time frame
|
|
@@ -29,62 +29,39 @@ function isTaskId(str) {
|
|
|
29
29
|
// Task IDs are 6-9 characters long and contain only alphanumeric characters
|
|
30
30
|
return /^[a-z0-9]{6,9}$/i.test(str);
|
|
31
31
|
}
|
|
32
|
-
// Cache for current user info to avoid repeated API calls
|
|
33
|
-
let
|
|
32
|
+
// Cache for current user info to avoid repeated API calls and race conditions
|
|
33
|
+
let cachedUserPromise = null;
|
|
34
34
|
/**
|
|
35
35
|
* Get current authenticated user information from ClickUp API
|
|
36
|
-
* Caches the
|
|
36
|
+
* Caches the promise to prevent race conditions on concurrent calls
|
|
37
37
|
*/
|
|
38
38
|
async function getCurrentUser() {
|
|
39
|
-
// Return cached
|
|
40
|
-
if (
|
|
41
|
-
return
|
|
39
|
+
// Return cached promise if available
|
|
40
|
+
if (cachedUserPromise) {
|
|
41
|
+
return cachedUserPromise;
|
|
42
42
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
cachedUserData = await userResponse.json();
|
|
51
|
-
return cachedUserData;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Limits the number of images in the content array, replacing excess images with text placeholders
|
|
55
|
-
* Prioritizes keeping the most recent images (assumes content is ordered with newest items last)
|
|
56
|
-
*
|
|
57
|
-
* @param content Array of content blocks that may contain images
|
|
58
|
-
* @param maxImages Maximum number of images to keep
|
|
59
|
-
* @returns Modified content array with limited images
|
|
60
|
-
*/
|
|
61
|
-
function limitImages(content, maxImages) {
|
|
62
|
-
// Count how many images we have
|
|
63
|
-
const imageIndices = [];
|
|
64
|
-
// Find all image blocks
|
|
65
|
-
content.forEach((block, index) => {
|
|
66
|
-
if (block.type === "image") {
|
|
67
|
-
imageIndices.push(index);
|
|
43
|
+
// Create the fetch promise
|
|
44
|
+
const fetchPromise = (async () => {
|
|
45
|
+
const userResponse = await fetch("https://api.clickup.com/api/v2/user", {
|
|
46
|
+
headers: { Authorization: config_1.CONFIG.apiKey },
|
|
47
|
+
});
|
|
48
|
+
if (!userResponse.ok) {
|
|
49
|
+
throw new Error(`Error fetching user info: ${userResponse.status} ${userResponse.statusText}`);
|
|
68
50
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return
|
|
79
|
-
if (block.type === "image" && imagesToRemove.includes(index)) {
|
|
80
|
-
return {
|
|
81
|
-
type: "text",
|
|
82
|
-
text: "[Image removed due to size limitations. Only the most recent images are shown.]",
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
return block;
|
|
86
|
-
});
|
|
51
|
+
return await userResponse.json();
|
|
52
|
+
})();
|
|
53
|
+
// Cache the promise
|
|
54
|
+
cachedUserPromise = fetchPromise;
|
|
55
|
+
// Auto-cleanup after 60 seconds
|
|
56
|
+
setTimeout(() => {
|
|
57
|
+
cachedUserPromise = null;
|
|
58
|
+
console.error(`Auto-cleaned user data cache`);
|
|
59
|
+
}, GLOBAL_REFRESH_INTERVAL);
|
|
60
|
+
return fetchPromise;
|
|
87
61
|
}
|
|
62
|
+
// Re-export image processing functions for backward compatibility
|
|
63
|
+
var image_processing_1 = require("./image-processing");
|
|
64
|
+
Object.defineProperty(exports, "downloadImages", { enumerable: true, get: function () { return image_processing_1.downloadImages; } });
|
|
88
65
|
const spaceCache = new Map(); // Global cache for space details promises
|
|
89
66
|
/**
|
|
90
67
|
* Function to get space details, using a cache to avoid redundant fetches
|
|
@@ -111,10 +88,11 @@ function getSpaceDetails(spaceId) {
|
|
|
111
88
|
spaceCache.set(spaceId, fetchPromise);
|
|
112
89
|
return fetchPromise;
|
|
113
90
|
}
|
|
114
|
-
// Task search index management
|
|
91
|
+
// Task search index management - cache promises to prevent race conditions
|
|
115
92
|
const taskIndices = new Map();
|
|
116
93
|
/**
|
|
117
94
|
* Get or create a task search index with specified filters
|
|
95
|
+
* Caches promises to prevent race conditions on concurrent calls
|
|
118
96
|
*/
|
|
119
97
|
async function getTaskSearchIndex(space_ids, list_ids, assignees) {
|
|
120
98
|
// Create cache key from sorted filter arrays
|
|
@@ -123,23 +101,26 @@ async function getTaskSearchIndex(space_ids, list_ids, assignees) {
|
|
|
123
101
|
list_ids: list_ids?.sort(),
|
|
124
102
|
assignees: assignees?.sort()
|
|
125
103
|
});
|
|
126
|
-
// Check for existing valid index
|
|
127
|
-
const
|
|
128
|
-
if (
|
|
129
|
-
return
|
|
104
|
+
// Check for existing valid index promise
|
|
105
|
+
const cachedPromise = taskIndices.get(key);
|
|
106
|
+
if (cachedPromise) {
|
|
107
|
+
return cachedPromise;
|
|
130
108
|
}
|
|
131
|
-
//
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
109
|
+
// Create the fetch promise
|
|
110
|
+
const fetchPromise = (async () => {
|
|
111
|
+
console.error(`Refreshing task index for filters: ${key}`);
|
|
112
|
+
const tasks = await fetchTasks(space_ids, list_ids, assignees);
|
|
113
|
+
const index = createFuseIndex(tasks);
|
|
114
|
+
console.error(`Task index created with ${tasks.length} tasks`);
|
|
115
|
+
return index;
|
|
116
|
+
})();
|
|
117
|
+
// Store promise with auto-cleanup
|
|
118
|
+
taskIndices.set(key, fetchPromise);
|
|
137
119
|
setTimeout(() => {
|
|
138
120
|
taskIndices.delete(key);
|
|
139
121
|
console.error(`Auto-cleaned index for filters: ${key}`);
|
|
140
122
|
}, GLOBAL_REFRESH_INTERVAL);
|
|
141
|
-
|
|
142
|
-
return index;
|
|
123
|
+
return fetchPromise;
|
|
143
124
|
}
|
|
144
125
|
/**
|
|
145
126
|
* Fetch tasks using team endpoint with dynamic filters
|
|
@@ -188,9 +169,10 @@ function createFuseIndex(tasks) {
|
|
|
188
169
|
{ name: 'folder.name', weight: 0.2 },
|
|
189
170
|
{ name: 'space.name', weight: 0.1 }
|
|
190
171
|
],
|
|
172
|
+
findAllMatches: true,
|
|
191
173
|
includeScore: true,
|
|
192
|
-
threshold: 0.4,
|
|
193
174
|
minMatchCharLength: 2,
|
|
175
|
+
threshold: 0.4,
|
|
194
176
|
});
|
|
195
177
|
}
|
|
196
178
|
// ===== LINK UTILITIES =====
|
|
@@ -204,63 +186,404 @@ function generateTaskUrl(taskId) {
|
|
|
204
186
|
* Generate a ClickUp list URL from a list ID
|
|
205
187
|
*/
|
|
206
188
|
function generateListUrl(listId) {
|
|
207
|
-
return `https://app.clickup.com/v/
|
|
189
|
+
return `https://app.clickup.com/${config_1.CONFIG.teamId}/v/li/${listId}`;
|
|
208
190
|
}
|
|
209
191
|
/**
|
|
210
192
|
* Generate a ClickUp space URL from a space ID
|
|
211
193
|
*/
|
|
212
194
|
function generateSpaceUrl(spaceId) {
|
|
213
|
-
return `https://app.clickup.com/v/s/${spaceId}`;
|
|
195
|
+
return `https://app.clickup.com/${config_1.CONFIG.teamId}/v/s/${spaceId}`;
|
|
214
196
|
}
|
|
215
197
|
/**
|
|
216
198
|
* Generate a ClickUp folder URL from a folder ID
|
|
217
199
|
*/
|
|
218
200
|
function generateFolderUrl(folderId) {
|
|
219
|
-
return `https://app.clickup.com/v/f/${folderId}`;
|
|
201
|
+
return `https://app.clickup.com/${config_1.CONFIG.teamId}/v/f/${folderId}`;
|
|
220
202
|
}
|
|
221
203
|
/**
|
|
222
|
-
*
|
|
204
|
+
* Generate a ClickUp document URL from a document ID and optional page ID
|
|
223
205
|
*/
|
|
224
|
-
function
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
206
|
+
function generateDocumentUrl(docId, pageId) {
|
|
207
|
+
if (pageId) {
|
|
208
|
+
return `https://app.clickup.com/${config_1.CONFIG.teamId}/v/dc/${docId}/${pageId}`;
|
|
209
|
+
}
|
|
210
|
+
return `https://app.clickup.com/${config_1.CONFIG.teamId}/v/dc/${docId}`;
|
|
228
211
|
}
|
|
212
|
+
// Space search index cache - cache promise to prevent race conditions
|
|
213
|
+
let spaceSearchIndexPromise = null;
|
|
229
214
|
/**
|
|
230
|
-
*
|
|
215
|
+
* Get or refresh the space search index
|
|
216
|
+
* Caches promise to prevent race conditions on concurrent calls
|
|
231
217
|
*/
|
|
232
|
-
function
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
218
|
+
async function getSpaceSearchIndex() {
|
|
219
|
+
// Return cached promise if available
|
|
220
|
+
if (spaceSearchIndexPromise) {
|
|
221
|
+
return spaceSearchIndexPromise;
|
|
222
|
+
}
|
|
223
|
+
// Create the fetch promise
|
|
224
|
+
const fetchPromise = (async () => {
|
|
225
|
+
try {
|
|
226
|
+
const url = `https://api.clickup.com/api/v2/team/${config_1.CONFIG.teamId}/space`;
|
|
227
|
+
const response = await fetch(url, {
|
|
228
|
+
headers: { Authorization: config_1.CONFIG.apiKey },
|
|
229
|
+
});
|
|
230
|
+
if (!response.ok) {
|
|
231
|
+
throw new Error(`Error fetching spaces: ${response.status} ${response.statusText}`);
|
|
232
|
+
}
|
|
233
|
+
const data = await response.json();
|
|
234
|
+
const spacesData = data.spaces || [];
|
|
235
|
+
// Create Fuse search index
|
|
236
|
+
return new fuse_js_1.default(spacesData, {
|
|
237
|
+
keys: [
|
|
238
|
+
{ name: 'name', weight: 0.7 },
|
|
239
|
+
{ name: 'id', weight: 0.6 }
|
|
240
|
+
],
|
|
241
|
+
includeScore: true,
|
|
242
|
+
threshold: 0.4,
|
|
243
|
+
minMatchCharLength: 1,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
console.error('Error creating space search index:', error);
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
})();
|
|
251
|
+
// Cache the promise
|
|
252
|
+
spaceSearchIndexPromise = fetchPromise;
|
|
253
|
+
// Auto-cleanup after 60 seconds
|
|
254
|
+
setTimeout(() => {
|
|
255
|
+
spaceSearchIndexPromise = null;
|
|
256
|
+
console.error('Auto-cleaned space search index');
|
|
257
|
+
}, GLOBAL_REFRESH_INTERVAL);
|
|
258
|
+
return fetchPromise;
|
|
236
259
|
}
|
|
260
|
+
const listCache = new Map(); // Cache for space lists/folders
|
|
237
261
|
/**
|
|
238
|
-
*
|
|
262
|
+
* Get lists, folders, and documents for a specific space with caching
|
|
239
263
|
*/
|
|
240
|
-
function
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
264
|
+
async function getSpaceContent(spaceId) {
|
|
265
|
+
const cacheKey = `space-content-${spaceId}`;
|
|
266
|
+
// Check cache first
|
|
267
|
+
const cachedContent = listCache.get(cacheKey);
|
|
268
|
+
if (cachedContent) {
|
|
269
|
+
return cachedContent;
|
|
270
|
+
}
|
|
271
|
+
// Fetch content with parallel requests
|
|
272
|
+
const fetchPromise = (async () => {
|
|
273
|
+
try {
|
|
274
|
+
const [folders, lists, documents] = await Promise.all([
|
|
275
|
+
fetch(`https://api.clickup.com/api/v2/space/${spaceId}/folder`, {
|
|
276
|
+
headers: { Authorization: config_1.CONFIG.apiKey },
|
|
277
|
+
})
|
|
278
|
+
.then(response => response.json())
|
|
279
|
+
.then(json => json.folders || [])
|
|
280
|
+
.catch(e => {
|
|
281
|
+
console.error(e);
|
|
282
|
+
return [];
|
|
283
|
+
}),
|
|
284
|
+
fetch(`https://api.clickup.com/api/v2/space/${spaceId}/list`, {
|
|
285
|
+
headers: { Authorization: config_1.CONFIG.apiKey },
|
|
286
|
+
})
|
|
287
|
+
.then(response => response.json())
|
|
288
|
+
.then(json => json.lists || [])
|
|
289
|
+
.catch(e => {
|
|
290
|
+
console.error(e);
|
|
291
|
+
return [];
|
|
292
|
+
}),
|
|
293
|
+
fetch(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs?parent_id=${spaceId}`, {
|
|
294
|
+
headers: { Authorization: config_1.CONFIG.apiKey },
|
|
295
|
+
})
|
|
296
|
+
.then(response => response.json())
|
|
297
|
+
.then(json => json.docs || [])
|
|
298
|
+
.catch(e => {
|
|
299
|
+
console.error(e);
|
|
300
|
+
return [];
|
|
301
|
+
})
|
|
302
|
+
]);
|
|
303
|
+
// For each folder, also fetch its lists
|
|
304
|
+
const folderListPromises = folders.map(async (folder) => {
|
|
305
|
+
try {
|
|
306
|
+
const folderListResponse = await fetch(`https://api.clickup.com/api/v2/folder/${folder.id}/list`, { headers: { Authorization: config_1.CONFIG.apiKey } });
|
|
307
|
+
if (folderListResponse.ok) {
|
|
308
|
+
const folderListData = await folderListResponse.json();
|
|
309
|
+
folder.lists = folderListData.lists || [];
|
|
310
|
+
}
|
|
311
|
+
return folder;
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
console.error(`Error fetching lists for folder ${folder.id}:`, error);
|
|
315
|
+
folder.lists = [];
|
|
316
|
+
return folder;
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
const foldersWithLists = await Promise.all(folderListPromises);
|
|
320
|
+
return { lists, folders: foldersWithLists, documents };
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
console.error(`Error fetching space content for ${spaceId}:`, error);
|
|
324
|
+
return { lists: [], folders: [], documents: [] };
|
|
325
|
+
}
|
|
326
|
+
})();
|
|
327
|
+
// Cache the promise
|
|
328
|
+
listCache.set(cacheKey, fetchPromise);
|
|
329
|
+
// Auto-cleanup after 60 seconds
|
|
330
|
+
setTimeout(() => {
|
|
331
|
+
listCache.delete(cacheKey);
|
|
332
|
+
console.error(`Auto-cleaned space content cache for ${spaceId}`);
|
|
333
|
+
}, GLOBAL_REFRESH_INTERVAL);
|
|
334
|
+
return fetchPromise;
|
|
244
335
|
}
|
|
336
|
+
// Cache for team members to avoid repeated API calls and race conditions
|
|
337
|
+
let cachedTeamMembersPromise = null;
|
|
245
338
|
/**
|
|
246
|
-
*
|
|
339
|
+
* Gets all team members from ClickUp API with caching
|
|
247
340
|
*/
|
|
248
|
-
function
|
|
249
|
-
|
|
250
|
-
|
|
341
|
+
async function getAllTeamMembers() {
|
|
342
|
+
// Return cached promise if available
|
|
343
|
+
if (cachedTeamMembersPromise) {
|
|
344
|
+
return cachedTeamMembersPromise;
|
|
345
|
+
}
|
|
346
|
+
// Create the fetch promise
|
|
347
|
+
const fetchPromise = (async () => {
|
|
348
|
+
try {
|
|
349
|
+
const response = await fetch(`https://api.clickup.com/api/v2/team`, {
|
|
350
|
+
headers: { Authorization: config_1.CONFIG.apiKey },
|
|
351
|
+
});
|
|
352
|
+
if (!response.ok) {
|
|
353
|
+
console.error(`Error fetching teams: ${response.status} ${response.statusText}`);
|
|
354
|
+
return [];
|
|
355
|
+
}
|
|
356
|
+
const data = await response.json();
|
|
357
|
+
if (!data.teams || !Array.isArray(data.teams)) {
|
|
358
|
+
return [];
|
|
359
|
+
}
|
|
360
|
+
// Find the team that matches our configured team ID and extract all user IDs
|
|
361
|
+
const currentTeam = data.teams.find((team) => team.id === config_1.CONFIG.teamId);
|
|
362
|
+
if (!currentTeam || !currentTeam.members || !Array.isArray(currentTeam.members)) {
|
|
363
|
+
console.error(`Team ${config_1.CONFIG.teamId} not found or has no members`);
|
|
364
|
+
return [];
|
|
365
|
+
}
|
|
366
|
+
return currentTeam.members.map((member) => member.user?.id).filter(Boolean);
|
|
367
|
+
}
|
|
368
|
+
catch (error) {
|
|
369
|
+
console.error('Error fetching team members:', error);
|
|
370
|
+
return [];
|
|
371
|
+
}
|
|
372
|
+
})();
|
|
373
|
+
// Cache the promise
|
|
374
|
+
cachedTeamMembersPromise = fetchPromise;
|
|
375
|
+
// Auto-cleanup after 60 seconds
|
|
376
|
+
setTimeout(() => {
|
|
377
|
+
cachedTeamMembersPromise = null;
|
|
378
|
+
console.error(`Auto-cleaned team members cache`);
|
|
379
|
+
}, GLOBAL_REFRESH_INTERVAL);
|
|
380
|
+
return fetchPromise;
|
|
251
381
|
}
|
|
382
|
+
// Document search index management - cache promises to prevent race conditions
|
|
383
|
+
const documentIndices = new Map();
|
|
252
384
|
/**
|
|
253
|
-
*
|
|
385
|
+
* Get or create a document search index with space name resolution
|
|
386
|
+
* Caches promises to prevent race conditions on concurrent calls
|
|
254
387
|
*/
|
|
255
|
-
function
|
|
256
|
-
|
|
388
|
+
async function getDocumentSearchIndex(space_ids) {
|
|
389
|
+
// Create cache key from sorted filter arrays
|
|
390
|
+
const key = JSON.stringify({
|
|
391
|
+
space_ids: space_ids?.sort()
|
|
392
|
+
});
|
|
393
|
+
// Check for existing valid index promise
|
|
394
|
+
const cachedPromise = documentIndices.get(key);
|
|
395
|
+
if (cachedPromise) {
|
|
396
|
+
return cachedPromise;
|
|
397
|
+
}
|
|
398
|
+
// Create the fetch promise
|
|
399
|
+
const fetchPromise = (async () => {
|
|
400
|
+
console.error(`Refreshing document index for filters: ${key}`);
|
|
401
|
+
const documents = await fetchDocuments(space_ids);
|
|
402
|
+
const index = createDocumentFuseIndex(documents);
|
|
403
|
+
console.error(`Document index created with ${documents.length} documents`);
|
|
404
|
+
return index;
|
|
405
|
+
})();
|
|
406
|
+
// Store promise with auto-cleanup
|
|
407
|
+
documentIndices.set(key, fetchPromise);
|
|
408
|
+
setTimeout(() => {
|
|
409
|
+
documentIndices.delete(key);
|
|
410
|
+
console.error(`Auto-cleaned document index for filters: ${key}`);
|
|
411
|
+
}, GLOBAL_REFRESH_INTERVAL);
|
|
412
|
+
return fetchPromise;
|
|
257
413
|
}
|
|
258
414
|
/**
|
|
259
|
-
*
|
|
415
|
+
* Fetch documents and resolve space names
|
|
260
416
|
*/
|
|
261
|
-
function
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
417
|
+
async function fetchDocuments(space_ids) {
|
|
418
|
+
try {
|
|
419
|
+
// Fetch spaces first
|
|
420
|
+
const spacesResponse = await fetch(`https://api.clickup.com/api/v2/team/${config_1.CONFIG.teamId}/space`, {
|
|
421
|
+
headers: { Authorization: config_1.CONFIG.apiKey }
|
|
422
|
+
});
|
|
423
|
+
if (!spacesResponse.ok) {
|
|
424
|
+
console.error('Error fetching spaces:', spacesResponse.status);
|
|
425
|
+
return [];
|
|
426
|
+
}
|
|
427
|
+
const spacesData = await spacesResponse.json();
|
|
428
|
+
// Fetch documents with pagination
|
|
429
|
+
const allDocuments = [];
|
|
430
|
+
let nextCursor = null;
|
|
431
|
+
let pageCount = 0;
|
|
432
|
+
const maxPages = 10; // Limit to 10 pages to avoid excessive API calls
|
|
433
|
+
do {
|
|
434
|
+
const url = new URL(`https://api.clickup.com/api/v3/workspaces/${config_1.CONFIG.teamId}/docs`);
|
|
435
|
+
url.searchParams.set('limit', '100');
|
|
436
|
+
if (nextCursor) {
|
|
437
|
+
url.searchParams.set('next_cursor', nextCursor);
|
|
438
|
+
}
|
|
439
|
+
const documentsResponse = await fetch(url.toString(), {
|
|
440
|
+
headers: { Authorization: config_1.CONFIG.apiKey }
|
|
441
|
+
});
|
|
442
|
+
if (!documentsResponse.ok) {
|
|
443
|
+
console.error('Error fetching documents:', documentsResponse.status);
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
const documentsData = await documentsResponse.json();
|
|
447
|
+
const pageDocs = documentsData.docs || [];
|
|
448
|
+
allDocuments.push(...pageDocs);
|
|
449
|
+
nextCursor = documentsData.next_cursor || null;
|
|
450
|
+
pageCount++;
|
|
451
|
+
console.error(`Fetched page ${pageCount} with ${pageDocs.length} documents (total: ${allDocuments.length})`);
|
|
452
|
+
} while (nextCursor && pageCount < maxPages);
|
|
453
|
+
const documents = allDocuments;
|
|
454
|
+
const spaces = spacesData.spaces || [];
|
|
455
|
+
// Create space lookup map
|
|
456
|
+
const spaceMap = new Map(spaces.map((space) => [space.id, space]));
|
|
457
|
+
// Enhance documents with parent information
|
|
458
|
+
const enhancedDocuments = documents.map((doc) => {
|
|
459
|
+
// Parent types: 4=Space, 5=Folder, 6=List, 7=Workspace
|
|
460
|
+
if (doc.parent?.type === 4) {
|
|
461
|
+
// Space parent - we can resolve the name
|
|
462
|
+
const space = spaceMap.get(doc.parent.id);
|
|
463
|
+
return {
|
|
464
|
+
...doc,
|
|
465
|
+
space_name: space?.name || 'Unknown',
|
|
466
|
+
space_id: doc.parent.id,
|
|
467
|
+
parent_info: space?.name ? `Space: ${space.name} (${doc.parent.id})` : `Space: ${doc.parent.id}`
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
else if (doc.parent?.type === 6) {
|
|
471
|
+
// List parent - just show ID
|
|
472
|
+
return {
|
|
473
|
+
...doc,
|
|
474
|
+
space_name: 'N/A',
|
|
475
|
+
space_id: null,
|
|
476
|
+
parent_info: `List: ${doc.parent.id}`
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
else if (doc.parent?.type === 5) {
|
|
480
|
+
// Folder parent - just show ID
|
|
481
|
+
return {
|
|
482
|
+
...doc,
|
|
483
|
+
space_name: 'N/A',
|
|
484
|
+
space_id: null,
|
|
485
|
+
parent_info: `Folder: ${doc.parent.id}`
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
else if (doc.parent?.type === 7) {
|
|
489
|
+
// Workspace parent
|
|
490
|
+
return {
|
|
491
|
+
...doc,
|
|
492
|
+
space_name: 'N/A',
|
|
493
|
+
space_id: null,
|
|
494
|
+
parent_info: `Workspace`
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
// Unknown parent type
|
|
498
|
+
return {
|
|
499
|
+
...doc,
|
|
500
|
+
space_name: 'Unknown',
|
|
501
|
+
space_id: doc.parent?.id,
|
|
502
|
+
parent_info: doc.parent ? `Unknown (type ${doc.parent.type})` : 'Unknown'
|
|
503
|
+
};
|
|
504
|
+
});
|
|
505
|
+
// Filter by space_ids if provided
|
|
506
|
+
if (space_ids?.length) {
|
|
507
|
+
return enhancedDocuments.filter((doc) => space_ids.includes(doc.space_id));
|
|
508
|
+
}
|
|
509
|
+
return enhancedDocuments;
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
console.error('Error fetching documents:', error);
|
|
513
|
+
return [];
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Create a Fuse index from documents array
|
|
518
|
+
*/
|
|
519
|
+
function createDocumentFuseIndex(documents) {
|
|
520
|
+
return new fuse_js_1.default(documents, {
|
|
521
|
+
keys: [
|
|
522
|
+
{ name: 'name', weight: 0.8 },
|
|
523
|
+
{ name: 'space_name', weight: 0.6 },
|
|
524
|
+
{ name: 'id', weight: 0.4 }
|
|
525
|
+
],
|
|
526
|
+
findAllMatches: true,
|
|
527
|
+
includeScore: true,
|
|
528
|
+
minMatchCharLength: 2,
|
|
529
|
+
threshold: 0.4,
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Performs multi-term search with aggressive boosting for items matching multiple terms
|
|
534
|
+
* @param searchIndex Fuse search index to search within
|
|
535
|
+
* @param terms Array of search terms
|
|
536
|
+
* @returns Array of items sorted by relevance (multi-term matches ranked higher)
|
|
537
|
+
*/
|
|
538
|
+
async function performMultiTermSearch(searchIndex, terms) {
|
|
539
|
+
// Filter valid search terms
|
|
540
|
+
const validTerms = terms.filter(term => term && term.trim().length > 0);
|
|
541
|
+
if (validTerms.length === 0) {
|
|
542
|
+
return [];
|
|
543
|
+
}
|
|
544
|
+
// Track multiple matches per item for aggressive boosting
|
|
545
|
+
const itemMatches = new Map();
|
|
546
|
+
// Collect all matches for each term
|
|
547
|
+
validTerms.forEach(term => {
|
|
548
|
+
const results = searchIndex.search(term);
|
|
549
|
+
results.forEach(result => {
|
|
550
|
+
if (result.item && typeof result.item.id === 'string') {
|
|
551
|
+
const itemId = result.item.id;
|
|
552
|
+
const currentScore = result.score ?? 1;
|
|
553
|
+
const existing = itemMatches.get(itemId);
|
|
554
|
+
if (!existing) {
|
|
555
|
+
itemMatches.set(itemId, {
|
|
556
|
+
item: result.item,
|
|
557
|
+
scores: [currentScore],
|
|
558
|
+
matchedTerms: [term]
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
existing.scores.push(currentScore);
|
|
563
|
+
existing.matchedTerms.push(term);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
// Calculate aggressively boosted scores for multi-term matches
|
|
569
|
+
const uniqueResults = new Map();
|
|
570
|
+
itemMatches.forEach((match, itemId) => {
|
|
571
|
+
const bestScore = Math.min(...match.scores);
|
|
572
|
+
const matchCount = match.scores.length;
|
|
573
|
+
const totalTerms = validTerms.length;
|
|
574
|
+
// Aggressive multi-term boost: exponential improvement for multiple matches
|
|
575
|
+
// 1 match: base score
|
|
576
|
+
// 2+ matches: exponentially better score based on match ratio
|
|
577
|
+
const matchRatio = matchCount / totalTerms;
|
|
578
|
+
const boostFactor = Math.pow(0.1, matchRatio * 4); // Very aggressive boost
|
|
579
|
+
const finalScore = bestScore * boostFactor;
|
|
580
|
+
uniqueResults.set(itemId, {
|
|
581
|
+
item: match.item,
|
|
582
|
+
score: finalScore
|
|
583
|
+
});
|
|
584
|
+
});
|
|
585
|
+
// Return sorted results (best scores first)
|
|
586
|
+
return Array.from(uniqueResults.values())
|
|
587
|
+
.sort((a, b) => a.score - b.score)
|
|
588
|
+
.map(entry => entry.item);
|
|
266
589
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doc-tools.d.ts","sourceRoot":"","sources":["../../src/tools/doc-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAoDpE,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,QAiR1D;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,QA8O3D"}
|