Skip to main content
Glama

Task Trellis MCP

pruneClosed.ts4.1 kB
import { isClosed, isOpen } from "../../models"; import { Repository } from "../../repositories"; /** * Recursively checks if an object has any open descendants at any level. * * @param objectId - The ID of the object to check for open descendants * @param repository - The repository to query for children * @param visited - Set to track visited objects and prevent infinite loops * @returns Promise<boolean> - true if any descendant at any level is open */ async function hasOpenDescendants( objectId: string, repository: Repository, visited: Set<string> = new Set(), ): Promise<boolean> { // Prevent infinite loops by tracking visited objects if (visited.has(objectId)) { return false; } visited.add(objectId); try { // Get all children (including closed ones) const children = await repository.getChildrenOf(objectId, true); // Check each child for (const child of children) { // If any direct child is open, return true immediately if (isOpen(child)) { return true; } // Recursively check if this child has open descendants if (await hasOpenDescendants(child.id, repository, visited)) { return true; } } return false; } catch (error) { // If we can't check descendants, err on the side of caution console.error(`Error checking descendants for ${objectId}:`, error); return false; } } export async function pruneClosed( repository: Repository, age: number, scope?: string, ): Promise<{ content: Array<{ type: string; text: string }> }> { try { // Early return for disabled auto-prune if (age <= 0) { const message = scope ? `Auto-prune disabled (threshold: ${age} days) in scope ${scope}` : `Auto-prune disabled (threshold: ${age} days)`; return { content: [{ type: "text", text: message }], }; } // Get all objects including closed ones const objects = await repository.getObjects(true, scope); // Filter to only closed objects const closedObjects = objects.filter(isClosed); // Calculate cutoff time (age in days ago) const cutoffTime = new Date(Date.now() - age * 24 * 60 * 60 * 1000); // Filter to objects older than cutoff const objectsToDelete = closedObjects.filter((obj) => { const updatedTime = new Date(obj.updated); return updatedTime < cutoffTime; }); // Delete each object with hierarchical child validation const deletedIds: string[] = []; const skippedIds: string[] = []; for (const obj of objectsToDelete) { try { // Check if object has open descendants at any level const hasOpenDescendantsResult = await hasOpenDescendants( obj.id, repository, ); if (hasOpenDescendantsResult) { // Skip deletion if object has open descendants skippedIds.push(obj.id); continue; } // Proceed with deletion if no open children await repository.deleteObject(obj.id, true); deletedIds.push(obj.id); } catch (error) { // Continue with other objects even if one fails console.error(`Failed to delete object ${obj.id}:`, error); } } const message = scope ? `Pruned ${deletedIds.length} closed objects older than ${age} days in scope ${scope}` : `Pruned ${deletedIds.length} closed objects older than ${age} days`; const skippedMessage = skippedIds.length > 0 ? `\nSkipped ${skippedIds.length} objects with open children: ${skippedIds.join(", ")}` : ""; return { content: [ { type: "text", text: `${message}${ deletedIds.length > 0 ? `\nDeleted objects: ${deletedIds.join(", ")}` : "" }${skippedMessage}`, }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error pruning closed objects: ${error instanceof Error ? error.message : String(error)}`, }, ], }; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/langadventurellc/task-trellis-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server