Skip to main content
Glama
backlog-todo-shared.ts3.93 kB
import { mkdirSync, readFileSync, writeFileSync } from 'fs'; import { generateBacklogFilename } from './backlog-shared.js'; import { getBacklogDir } from './path-resolver.js'; export interface Todo { id: string; content: string; status: "pending" | "in_progress" | "completed" | "cancelled"; batch: string | null; dependencies: string[]; created: string; agent: string; session: string; } export interface TodoData { backlogTopic: string; todos: Todo[]; } export interface Context { agent: string; sessionID: string; } export function getBacklogItemPath(topic: string): string { const normalized = generateBacklogFilename(topic); const backlogDir = getBacklogDir(); return `${backlogDir}/${normalized}/item.md`; } export function getTodosFilePath(topic: string): string { const normalized = generateBacklogFilename(topic); const backlogDir = getBacklogDir(); return `${backlogDir}/${normalized}/todos.json`; } export function ensureTodosDirectory(topic: string): void { const normalized = generateBacklogFilename(topic); const backlogDir = getBacklogDir(); const dirPath = `${backlogDir}/${normalized}`; mkdirSync(dirPath, { recursive: true }); } export function readTodos(topic: string): TodoData { const filePath = getTodosFilePath(topic); try { const content = readFileSync(filePath, 'utf8'); return JSON.parse(content); } catch (error) { // File doesn't exist or is invalid, return empty structure return { backlogTopic: topic, todos: [] }; } } export function writeTodos(topic: string, data: TodoData): void { ensureTodosDirectory(topic); const filePath = getTodosFilePath(topic); const json = JSON.stringify(data, null, 2); writeFileSync(filePath, json); } export function createTodo(topic: string, content: string, context: Context): Todo { const data = readTodos(topic); const existingIds = data.todos.map(t => parseInt(t.id)).filter(id => !isNaN(id)); const nextId = existingIds.length > 0 ? Math.max(...existingIds) + 1 : 1; const id = nextId.toString(); const todo: Todo = { id, content, status: "pending", batch: null, dependencies: [], created: new Date().toISOString(), agent: context.agent, session: context.sessionID }; data.todos.push(todo); writeTodos(topic, data); return todo; } export function updateTodoStatus(topic: string, todoId: string, newStatus: string): Todo { const data = readTodos(topic); const todo = data.todos.find(t => t.id === todoId); if (!todo) { throw new Error(`Todo with ID ${todoId} not found in topic ${topic}`); } const validStatuses = ["pending", "in_progress", "completed", "cancelled"]; if (!validStatuses.includes(newStatus)) { throw new Error(`Invalid status: ${newStatus}. Must be one of: ${validStatuses.join(', ')}`); } todo.status = newStatus as Todo['status']; writeTodos(topic, data); return todo; } export function listTodos(topic: string, filters?: { status?: string, batch?: string }): Todo[] { const data = readTodos(topic); let todos = data.todos; if (filters?.status) { todos = todos.filter(t => t.status === filters.status); } if (filters?.batch) { todos = todos.filter(t => t.batch === filters.batch); } return todos; } export function validateDependencies(todos: Todo[], todoId: string): { valid: boolean, missing: string[], incomplete: string[] } { const todo = todos.find(t => t.id === todoId); if (!todo) { throw new Error(`Todo with ID ${todoId} not found`); } const missing: string[] = []; const incomplete: string[] = []; for (const depId of todo.dependencies) { const dep = todos.find(t => t.id === depId); if (!dep) { missing.push(depId); } else if (dep.status !== "completed") { incomplete.push(depId); } } return { valid: missing.length === 0 && incomplete.length === 0, missing, incomplete }; }

Latest Blog Posts

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/rwese/mcp-backlog'

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