UNPKG

@boundless-oss/atlas

Version:

Atlas - MCP Server for comprehensive startup project management

142 lines 6.13 kB
import { EventEmitter } from 'events'; import { AGILE_TOOLS, KANBAN_TOOLS, COMMON_TOOLS } from './types.js'; import { ConfigManager } from '../../config/config-manager.js'; import { randomUUID } from 'crypto'; export class MethodologyManager extends EventEmitter { store; configManager; constructor(configManager) { super(); this.configManager = configManager || new ConfigManager(); this.store = {}; } async init() { await this.loadStore(); } async setMethodology(type, userId) { if (this.store.currentMethodology?.lockedAt) { throw new Error(`Methodology is locked since ${this.store.currentMethodology.lockedAt}. Cannot change.`); } const methodology = { type, allowedTools: [...COMMON_TOOLS, ...(type === 'agile' ? AGILE_TOOLS : KANBAN_TOOLS)], disallowedTools: type === 'agile' ? KANBAN_TOOLS : AGILE_TOOLS, configuration: this.getDefaultConfig(type), migrationHistory: [] }; if (this.store.currentMethodology && this.store.currentMethodology.type !== type) { // Record migration const migration = { id: randomUUID(), fromType: this.store.currentMethodology.type, toType: type, migratedAt: new Date(), migratedBy: userId, itemsMigrated: 0, dataLost: this.identifyDataLoss(this.store.currentMethodology.type, type) }; methodology.migrationHistory.push(migration); } this.store.currentMethodology = methodology; await this.saveStore(); this.emit('methodology-changed', methodology); return methodology; } async lockMethodology(userId) { if (!this.store.currentMethodology) { throw new Error('No methodology set to lock'); } if (this.store.currentMethodology.lockedAt) { throw new Error('Methodology is already locked'); } this.store.currentMethodology.lockedAt = new Date(); this.store.currentMethodology.lockedBy = userId; await this.saveStore(); this.emit('methodology-locked', this.store.currentMethodology); } isToolAllowed(toolName) { if (!this.store.currentMethodology) { return true; // No methodology set, allow all } return this.store.currentMethodology.allowedTools.includes(toolName); } getCurrentMethodology() { return this.store.currentMethodology; } async warnAboutConflict(toolName) { const now = new Date(); const lastWarning = this.store.lastWarningShown; // Only show warning once per hour if (lastWarning && (now.getTime() - lastWarning.getTime()) < 3600000) { return false; } this.store.lastWarningShown = now; await this.saveStore(); this.emit('methodology-conflict-warning', { toolName, currentMethodology: this.store.currentMethodology?.type, message: `Tool '${toolName}' belongs to a different methodology. Consider switching methodologies or using the appropriate tools.` }); return true; } getDefaultConfig(type) { if (type === 'agile') { return { sprintDuration: 14, velocityTracking: true, storyPointScale: [1, 2, 3, 5, 8, 13, 21], retrospectiveFrequency: 'end_of_sprint', customColumns: ['To Do', 'In Progress', 'Review', 'Testing', 'Done'], defaultPriorities: ['low', 'medium', 'high', 'critical'], requiredFields: ['title', 'description', 'storyPoints', 'acceptanceCriteria'] }; } else { return { wip_limits: { 'To Do': 0, 'In Progress': 3, 'Review': 2, 'Done': 0 }, cycleTimeTracking: true, leadTimeTracking: true, customColumns: ['Backlog', 'To Do', 'In Progress', 'Review', 'Done'], defaultPriorities: ['low', 'medium', 'high', 'urgent'], requiredFields: ['title', 'description'] }; } } identifyDataLoss(from, to) { const dataLoss = []; if (from === 'agile' && to === 'kanban') { dataLoss.push('Sprint information will be lost', 'Story points will not be used in Kanban', 'Velocity tracking will be disabled', 'Epic relationships may need adjustment'); } else if (from === 'kanban' && to === 'agile') { dataLoss.push('WIP limits will be removed', 'Cycle time tracking will be converted to velocity', 'Tasks will need story point estimation', 'Tasks will need to be assigned to sprints'); } return dataLoss; } async suggestMigrationPath(from, to) { const suggestions = []; if (from === 'agile' && to === 'kanban') { suggestions.push('1. Complete or cancel active sprints', '2. Convert stories to tasks', '3. Set up WIP limits for columns', '4. Archive velocity reports', '5. Train team on Kanban metrics'); } else if (from === 'kanban' && to === 'agile') { suggestions.push('1. Group related tasks into stories', '2. Estimate story points for all items', '3. Create initial sprint backlog', '4. Define sprint cadence', '5. Set up velocity tracking'); } return suggestions; } async loadStore() { const storageManager = this.configManager.getStorageManager(); const data = await storageManager.loadData('methodology-config', 'methodology.json'); if (data) { this.store = data; } } async saveStore() { const storageManager = this.configManager.getStorageManager(); await storageManager.saveData('methodology-config', 'methodology.json', this.store); } } //# sourceMappingURL=methodology-manager.js.map