UNPKG

orchestry-mcp

Version:

Orchestry MCP Server for multi-session task management

232 lines (205 loc) 6.75 kB
import { create } from 'zustand'; import { io, Socket } from 'socket.io-client'; import type { BMADProject, Business, Mission, Approach, Deployment, TaskStatus, KanbanColumn, } from '../../../shared/types.js'; import { bmadAPI } from '../lib/api'; import toast from 'react-hot-toast'; interface BMADStore { // State socket: Socket | null; currentProject: BMADProject | null; kanbanColumns: Record<TaskStatus, Deployment[]>; selectedDeployment: Deployment | null; isLoading: boolean; // Actions initSocket: () => void; loadProject: (projectId: string) => Promise<void>; createProject: (name: string, description: string) => Promise<BMADProject>; createBusiness: (projectId: string, business: Partial<Business>) => Promise<Business>; createMission: (businessId: string, mission: Partial<Mission>) => Promise<Mission>; createApproach: (missionId: string, approach: Partial<Approach>) => Promise<Approach>; createDeployment: (approachId: string, deployment: Partial<Deployment>) => Promise<Deployment>; updateDeploymentStatus: (id: string, status: TaskStatus) => Promise<void>; loadKanbanBoard: (projectId: string) => Promise<void>; selectDeployment: (deployment: Deployment | null) => void; } export const useBMADStore = create<BMADStore>((set, get) => ({ // Initial state socket: null, currentProject: null, kanbanColumns: { backlog: [], todo: [], in_progress: [], review: [], done: [], blocked: [], }, selectedDeployment: null, isLoading: false, // Initialize WebSocket connection initSocket: () => { const wsUrl = import.meta.env.VITE_WS_URL || 'ws://localhost:7531'; const socket = io(wsUrl.replace('ws://', 'http://'), { transports: ['websocket'], }); socket.on('connect', () => { console.log('Connected to server'); toast.success('Connected to server'); }); socket.on('disconnect', () => { console.log('Disconnected from server'); toast.error('Disconnected from server'); }); // Real-time updates socket.on('deployment_created', (deployment: Deployment) => { const { kanbanColumns } = get(); set({ kanbanColumns: { ...kanbanColumns, [deployment.status]: [...kanbanColumns[deployment.status], deployment], }, }); toast.success(`New task created: ${deployment.title}`); }); socket.on('deployment_updated', (deployment: Deployment) => { const { kanbanColumns } = get(); const newColumns = { ...kanbanColumns }; // Remove from all columns Object.keys(newColumns).forEach((status) => { newColumns[status as TaskStatus] = newColumns[status as TaskStatus].filter( (d) => d.id !== deployment.id ); }); // Add to new column newColumns[deployment.status] = [...newColumns[deployment.status], deployment]; set({ kanbanColumns: newColumns }); toast.success(`Task updated: ${deployment.title}`); }); set({ socket }); }, // Load project loadProject: async (projectId: string) => { set({ isLoading: true }); try { const project = await bmadAPI.getProject(projectId); set({ currentProject: project }); // Subscribe to project updates const { socket } = get(); if (socket) { socket.emit('subscribe', projectId); } // Load kanban board await get().loadKanbanBoard(projectId); } catch (error) { toast.error('Failed to load project'); console.error(error); } finally { set({ isLoading: false }); } }, // Create project createProject: async (name: string, description: string) => { try { const project = await bmadAPI.createProject(name, description); set({ currentProject: project }); toast.success(`Project created: ${name}`); return project; } catch (error) { toast.error('Failed to create project'); throw error; } }, // Create business createBusiness: async (projectId: string, business: Partial<Business>) => { try { const newBusiness = await bmadAPI.createBusiness(projectId, business); // Update current project const { currentProject } = get(); if (currentProject) { set({ currentProject: { ...currentProject, business: [...currentProject.business, newBusiness], }, }); } toast.success(`Business created: ${newBusiness.title}`); return newBusiness; } catch (error) { toast.error('Failed to create business'); throw error; } }, // Create mission createMission: async (businessId: string, mission: Partial<Mission>) => { try { const newMission = await bmadAPI.createMission(businessId, mission); toast.success(`Mission created: ${newMission.title}`); return newMission; } catch (error) { toast.error('Failed to create mission'); throw error; } }, // Create approach createApproach: async (missionId: string, approach: Partial<Approach>) => { try { const newApproach = await bmadAPI.createApproach(missionId, approach); toast.success(`Approach created: ${newApproach.title}`); return newApproach; } catch (error) { toast.error('Failed to create approach'); throw error; } }, // Create deployment createDeployment: async (approachId: string, deployment: Partial<Deployment>) => { try { const newDeployment = await bmadAPI.createDeployment(approachId, deployment); // Add to kanban board const { kanbanColumns } = get(); set({ kanbanColumns: { ...kanbanColumns, [newDeployment.status]: [...kanbanColumns[newDeployment.status], newDeployment], }, }); toast.success(`Task created: ${newDeployment.title}`); return newDeployment; } catch (error) { toast.error('Failed to create task'); throw error; } }, // Update deployment status updateDeploymentStatus: async (id: string, status: TaskStatus) => { try { await bmadAPI.updateDeploymentStatus(id, status); } catch (error) { toast.error('Failed to update task status'); throw error; } }, // Load kanban board loadKanbanBoard: async (projectId: string) => { try { const board = await bmadAPI.getBoard(projectId); set({ kanbanColumns: board }); } catch (error) { toast.error('Failed to load board'); console.error(error); } }, // Select deployment selectDeployment: (deployment: Deployment | null) => { set({ selectedDeployment: deployment }); }, }));