task-master-neo-sdlc
Version:
Enhanced task management system with Neo SDLC agents and MCP tools for comprehensive, AI-driven software development lifecycle management.
289 lines (248 loc) • 8.34 kB
JavaScript
import { log } from '../../utils/logging.js';
/**
* @typedef {Object} ChainConfig
* @property {string} id - Unique chain identifier
* @property {string} type - Chain type (e.g., 'sequential', 'parallel', 'conditional')
* @property {Object} options - Chain-specific options
* @property {Array<string>} steps - Step IDs in execution order
*/
/**
* @typedef {Object} StepResult
* @property {string} stepId - ID of the completed step
* @property {boolean} success - Whether the step completed successfully
* @property {Object} data - Output data from the step
* @property {Object} metrics - Performance metrics
*/
export class ChainSystem {
constructor(knowledgeGraph) {
this.knowledgeGraph = knowledgeGraph;
this.chains = new Map();
this.stepHandlers = new Map();
this.activeChains = new Map();
this.chainTypes = new Map();
}
async initialize() {
// Register chain system in knowledge graph
await this.knowledgeGraph.addNode({
id: 'chain-system',
type: 'system',
data: {
status: 'active',
registeredChains: 0,
activeChains: 0,
completedChains: 0
}
});
// Register built-in chain types
this._registerBuiltInChainTypes();
log.info('Chain system initialized');
}
_registerBuiltInChainTypes() {
// Sequential chain - executes steps in order
this.registerChainType('sequential', {
validate: (config) => {
if (!config.steps || !Array.isArray(config.steps) || config.steps.length === 0) {
return { valid: false, errors: ['Sequential chain requires non-empty steps array'] };
}
return { valid: true };
},
getNextStep: (chain, completedSteps) => {
const nextIndex = completedSteps.length;
return nextIndex < chain.steps.length ? chain.steps[nextIndex] : null;
}
});
// Parallel chain - executes steps concurrently
this.registerChainType('parallel', {
validate: (config) => {
if (!config.steps || !Array.isArray(config.steps) || config.steps.length === 0) {
return { valid: false, errors: ['Parallel chain requires non-empty steps array'] };
}
if (!config.options?.maxConcurrent) {
return { valid: false, errors: ['Parallel chain requires maxConcurrent option'] };
}
return { valid: true };
},
getNextStep: (chain, completedSteps) => {
const remainingSteps = chain.steps.filter(step => !completedSteps.includes(step));
const activeCount = chain.steps.length - completedSteps.length;
if (activeCount >= chain.options.maxConcurrent) return null;
return remainingSteps[0] || null;
}
});
// Conditional chain - executes steps based on conditions
this.registerChainType('conditional', {
validate: (config) => {
if (!config.options?.conditions || typeof config.options.conditions !== 'object') {
return { valid: false, errors: ['Conditional chain requires conditions map'] };
}
return { valid: true };
},
getNextStep: (chain, completedSteps, stepResults) => {
if (completedSteps.length === 0) return chain.steps[0];
const lastResult = stepResults[stepResults.length - 1];
const nextStep = chain.options.conditions[lastResult.data.condition];
return nextStep || null;
}
});
}
registerChainType(type, handlers) {
if (this.chainTypes.has(type)) {
throw new Error(`Chain type ${type} already registered`);
}
if (!handlers.validate || !handlers.getNextStep) {
throw new Error('Chain type handlers must include validate and getNextStep functions');
}
this.chainTypes.set(type, handlers);
log.info(`Registered chain type: ${type}`);
}
async registerChain(config) {
// Validate chain configuration
if (!config.id || !config.type) {
throw new Error('Chain requires id and type');
}
const chainType = this.chainTypes.get(config.type);
if (!chainType) {
throw new Error(`Unknown chain type: ${config.type}`);
}
const validation = chainType.validate(config);
if (!validation.valid) {
throw new Error(`Invalid chain configuration: ${validation.errors.join(', ')}`);
}
this.chains.set(config.id, {
...config,
status: 'registered',
completedSteps: [],
stepResults: []
});
// Update knowledge graph
await this.knowledgeGraph.addNode({
id: `chain:${config.id}`,
type: 'workflow-chain',
data: config
});
await this._updateSystemMetrics();
return config.id;
}
async executeChain(chainId) {
const chain = this.chains.get(chainId);
if (!chain) {
throw new Error(`Chain not found: ${chainId}`);
}
if (this.activeChains.has(chainId)) {
throw new Error(`Chain ${chainId} is already executing`);
}
chain.status = 'active';
this.activeChains.set(chainId, chain);
try {
await this._executeChainSteps(chain);
chain.status = 'completed';
log.info(`Chain ${chainId} completed successfully`);
} catch (error) {
chain.status = 'failed';
log.error(`Chain ${chainId} failed: ${error.message}`);
throw error;
} finally {
this.activeChains.delete(chainId);
await this._updateSystemMetrics();
}
return chain.stepResults;
}
async _executeChainSteps(chain) {
const chainType = this.chainTypes.get(chain.type);
while (true) {
const nextStep = chainType.getNextStep(chain, chain.completedSteps, chain.stepResults);
if (!nextStep) break;
try {
const result = await this.executeStep(nextStep, chain);
chain.completedSteps.push(nextStep);
chain.stepResults.push(result);
// Update knowledge graph
await this.knowledgeGraph.updateContext({
id: `chain:${chain.id}`,
type: 'workflow-chain',
data: {
completedSteps: chain.completedSteps,
lastStepResult: result
}
});
} catch (error) {
log.error(`Step ${nextStep} failed: ${error.message}`);
throw error;
}
}
}
async executeStep(stepId, chain) {
const handler = this.stepHandlers.get(stepId);
if (!handler) {
throw new Error(`No handler registered for step: ${stepId}`);
}
const startTime = Date.now();
try {
const data = await handler(chain);
const result = {
stepId,
success: true,
data,
metrics: {
executionTime: Date.now() - startTime,
timestamp: new Date().toISOString()
}
};
// Log step completion
log.info(`Step ${stepId} completed successfully`, {
chainId: chain.id,
executionTime: result.metrics.executionTime
});
return result;
} catch (error) {
const result = {
stepId,
success: false,
error: error.message,
metrics: {
executionTime: Date.now() - startTime,
timestamp: new Date().toISOString()
}
};
// Log step failure
log.error(`Step ${stepId} failed`, {
chainId: chain.id,
error: error.message
});
throw error;
}
}
registerStepHandler(stepId, handler) {
if (typeof handler !== 'function') {
throw new Error('Step handler must be a function');
}
this.stepHandlers.set(stepId, handler);
}
async getChainStatus(chainId) {
const chain = this.chains.get(chainId);
if (!chain) {
throw new Error(`Chain not found: ${chainId}`);
}
return {
id: chain.id,
type: chain.type,
status: chain.status,
completedSteps: chain.completedSteps,
progress: chain.steps.length > 0
? (chain.completedSteps.length / chain.steps.length) * 100
: 0
};
}
async _updateSystemMetrics() {
await this.knowledgeGraph.updateContext({
id: 'chain-system',
type: 'system',
data: {
registeredChains: this.chains.size,
activeChains: this.activeChains.size,
completedChains: Array.from(this.chains.values())
.filter(chain => chain.status === 'completed').length
}
});
}
}