@boundless-oss/atlas
Version:
Atlas - MCP Server for comprehensive startup project management
142 lines • 6.13 kB
JavaScript
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