UNPKG

@boundless-oss/atlas

Version:

Atlas - MCP Server for comprehensive startup project management

142 lines (119 loc) 4.08 kB
import { KanbanStore, KanbanBoard, Task } from './types.js'; import { ConfigManager } from '../../config/config-manager.js'; export class KanbanDataStore { private store: KanbanStore = { boards: {} }; private configManager: ConfigManager; private readonly MODULE_NAME = 'kanban'; private readonly DATA_FILE = 'kanban.json'; constructor(configManager?: ConfigManager) { this.configManager = configManager || new ConfigManager(); } async init(): Promise<void> { const storageManager = this.configManager.getStorageManager(); await storageManager.ensureStorageDirectories(); const data = await storageManager.loadData(this.MODULE_NAME, this.DATA_FILE); if (data) { this.store = data; } else { // If file doesn't exist, create it with empty store await this.save(); } } async save(): Promise<void> { const storageManager = this.configManager.getStorageManager(); await storageManager.saveData(this.MODULE_NAME, this.DATA_FILE, this.store); } createBoard(name: string, columns: string[] = ['Backlog', 'To Do', 'In Progress', 'Review', 'Done']): KanbanBoard { const board: KanbanBoard = { id: this.generateId(), name, columns, tasks: [], createdAt: new Date(), updatedAt: new Date(), }; this.store.boards[board.id] = board; return board; } getBoard(nameOrId: string): KanbanBoard | undefined { // Try to find by ID first if (this.store.boards[nameOrId]) { return this.store.boards[nameOrId]; } // Try to find by name return Object.values(this.store.boards).find(board => board.name === nameOrId); } getAllBoards(): KanbanBoard[] { return Object.values(this.store.boards); } addTask(boardNameOrId: string, task: Omit<Task, 'id' | 'createdAt' | 'updatedAt'>): Task | null { const board = this.getBoard(boardNameOrId); if (!board) { return null; } const newTask: Task = { ...task, id: this.generateId(), createdAt: new Date(), updatedAt: new Date(), // Initialize new enhanced properties with defaults if not provided acceptanceCriteria: task.acceptanceCriteria || [], dependencies: task.dependencies || [], timeTracking: task.timeTracking || undefined, attachments: task.attachments || [], comments: task.comments || [], customFields: task.customFields || [], subtasks: task.subtasks || [], watchers: task.watchers || [], // Ensure tags is used instead of labels for consistency tags: task.tags || (task as any).labels || [], }; board.tasks.push(newTask); board.updatedAt = new Date(); return newTask; } moveTask(boardNameOrId: string, taskId: string, newColumn: string): boolean { const board = this.getBoard(boardNameOrId); if (!board) { return false; } const task = board.tasks.find(t => t.id === taskId); if (!task || !board.columns.includes(newColumn)) { return false; } task.column = newColumn; task.updatedAt = new Date(); board.updatedAt = new Date(); return true; } updateTask(boardNameOrId: string, taskId: string, updates: Partial<Omit<Task, 'id' | 'createdAt' | 'updatedAt'>>): boolean { const board = this.getBoard(boardNameOrId); if (!board) { return false; } const task = board.tasks.find(t => t.id === taskId); if (!task) { return false; } Object.assign(task, updates); task.updatedAt = new Date(); board.updatedAt = new Date(); return true; } deleteTask(boardNameOrId: string, taskId: string): boolean { const board = this.getBoard(boardNameOrId); if (!board) { return false; } const taskIndex = board.tasks.findIndex(t => t.id === taskId); if (taskIndex === -1) { return false; } board.tasks.splice(taskIndex, 1); board.updatedAt = new Date(); return true; } private generateId(): string { return Date.now().toString(36) + Math.random().toString(36).substr(2); } }