UNPKG

mcp-product-manager

Version:

MCP Orchestrator for task and project management with web interface

210 lines 8.95 kB
// orchestratorPurpose.js - Middleware for purpose-driven orchestration import { query, run } from '../utils/database.js'; // Purpose-driven middleware for orchestrator agents export const orchestratorPurposeMiddleware = async (req, res, next) => { // Only apply to orchestrator-specific endpoints or when agent type is orchestrator const isOrchestratorEndpoint = req.path.includes('/orchestrate') || req.path.includes('/review') || req.path.includes('/approve'); const agentType = req.headers['x-agent-type'] || req.body.agent_type; const isOrchestratorAgent = agentType === 'orchestrator'; if (!isOrchestratorEndpoint && !isOrchestratorAgent) { return next(); } const project = req.params.project || req.body.project || req.query.project; if (!project) { return next(); } try { // Get project purpose const projectData = await query('SELECT purpose, purpose_updated_at, purpose_context FROM projects WHERE name = ?', [project]); if (!projectData || projectData.length === 0) { // Create project entry if it doesn't exist await run('INSERT INTO projects (name, created_at) VALUES (?, datetime("now"))', [project]); req.projectPurpose = null; } else { req.projectPurpose = { purpose: projectData[0].purpose, context: projectData[0].purpose_context, updated_at: projectData[0].purpose_updated_at }; } // Add purpose filter to response const originalJson = res.json; res.json = function (data) { if (req.projectPurpose && req.projectPurpose.purpose && data.body) { // Add purpose context to responses data.body.purpose_context = { current_purpose: req.projectPurpose.purpose, purpose_filter: `All decisions should align with: ${req.projectPurpose.purpose}`, evaluation_criteria: [ 'Does this task/decision advance the stated purpose?', 'Is this the most efficient path toward the purpose?', 'Are there higher-priority items that better serve the purpose?' ] }; // If returning tasks, add purpose scoring if (data.body.tasks && Array.isArray(data.body.tasks)) { data.body.tasks = data.body.tasks.map(task => ({ ...task, purpose_alignment: calculatePurposeAlignment(task, req.projectPurpose.purpose) })); } } return originalJson.call(this, data); }; next(); } catch (err) { console.error('Purpose middleware error:', err); next(); // Continue even if purpose lookup fails } }; // Calculate how well a task aligns with the stated purpose const calculatePurposeAlignment = (task, purpose) => { if (!purpose) return 0.5; // Neutral if no purpose defined // Simple keyword matching for now - can be enhanced with better NLP const purposeKeywords = purpose.toLowerCase().split(/\s+/); const taskKeywords = (task.description + ' ' + (task.technical_context || '')).toLowerCase().split(/\s+/); let matches = 0; for (const keyword of purposeKeywords) { if (keyword.length > 3 && taskKeywords.includes(keyword)) { matches++; } } const score = Math.min(matches / purposeKeywords.length, 1); // Boost score for critical/high priority tasks if (task.priority === 'critical') return Math.min(score + 0.3, 1); if (task.priority === 'high') return Math.min(score + 0.1, 1); return score; }; // Endpoint to update project purpose export const updateProjectPurpose = async (req, res) => { const { project, purpose, context, updated_by } = req.body; if (!project || !purpose) { return res.status(400).json({ error: 'Project and purpose are required' }); } try { const result = await run(`UPDATE projects SET purpose = ?, purpose_context = ?, purpose_updated_at = datetime('now'), purpose_updated_by = ?, updated_at = datetime('now') WHERE name = ?`, [purpose, context || null, updated_by || 'orchestrator', project]); if (result.changes === 0) { // Project doesn't exist, create it await run(`INSERT INTO projects (name, purpose, purpose_context, purpose_updated_at, purpose_updated_by, created_at) VALUES (?, ?, ?, datetime('now'), ?, datetime('now'))`, [project, purpose, context || null, updated_by || 'orchestrator']); } // Log purpose change as orchestrator decision await run(`INSERT INTO orchestrator_decisions ( id, project, orchestrator_agent, decision_type, rationale, purpose_context, created_at ) VALUES (?, ?, ?, ?, ?, ?, datetime('now'))`, [ `PURPOSE-${Date.now()}`, project, updated_by || 'orchestrator', 'purpose_updated', `Updated project purpose to: ${purpose}`, purpose ]); res.json({ success: true, message: 'Project purpose updated', purpose: { project, purpose, context, updated_by: updated_by || 'orchestrator' } }); } catch (err) { console.error('Failed to update purpose:', err); res.status(500).json({ error: 'Failed to update project purpose', details: err.message }); } }; // Analyze project alignment with purpose export const analyzeProjectAlignment = async (req, res) => { const { project } = req.params; try { // Get project purpose const projectData = await query('SELECT purpose FROM projects WHERE name = ?', [project]); if (!projectData || !projectData[0]?.purpose) { return res.status(400).json({ error: 'Project purpose not defined' }); } const purpose = projectData[0].purpose; // Get all active tasks const tasks = await query(`SELECT id, description, priority, status, category, technical_context FROM tasks WHERE project = ? AND status NOT IN ('completed', 'archived')`, [project]); // Calculate alignment scores const alignmentAnalysis = tasks.map(task => ({ task_id: task.id, description: task.description, status: task.status, priority: task.priority, alignment_score: calculatePurposeAlignment(task, purpose), recommendation: getAlignmentRecommendation(task, calculatePurposeAlignment(task, purpose)) })); // Sort by alignment score alignmentAnalysis.sort((a, b) => b.alignment_score - a.alignment_score); // Calculate overall metrics const avgAlignment = alignmentAnalysis.reduce((sum, t) => sum + t.alignment_score, 0) / alignmentAnalysis.length; const highAlignmentTasks = alignmentAnalysis.filter(t => t.alignment_score >= 0.7).length; const lowAlignmentTasks = alignmentAnalysis.filter(t => t.alignment_score < 0.3).length; res.json({ success: true, project, purpose, metrics: { total_tasks: alignmentAnalysis.length, average_alignment: avgAlignment.toFixed(2), high_alignment_tasks: highAlignmentTasks, low_alignment_tasks: lowAlignmentTasks }, recommendations: { prioritize: alignmentAnalysis.filter(t => t.recommendation === 'prioritize').slice(0, 5), deprioritize: alignmentAnalysis.filter(t => t.recommendation === 'deprioritize').slice(0, 5), clarify: alignmentAnalysis.filter(t => t.recommendation === 'clarify').slice(0, 5) }, full_analysis: alignmentAnalysis }); } catch (err) { console.error('Failed to analyze alignment:', err); res.status(500).json({ error: 'Failed to analyze project alignment', details: err.message }); } }; const getAlignmentRecommendation = (task, score) => { if (score >= 0.7) { return task.priority === 'critical' || task.priority === 'high' ? 'maintain' : 'prioritize'; } else if (score < 0.3) { return task.status === 'in_progress' ? 'complete_and_archive' : 'deprioritize'; } else { return 'clarify'; } }; export default orchestratorPurposeMiddleware; //# sourceMappingURL=orchestratorPurpose.js.map