UNPKG

task-master-neo-sdlc

Version:

Enhanced task management system with Neo SDLC agents and MCP tools for comprehensive, AI-driven software development lifecycle management.

530 lines (440 loc) 14.6 kB
import { log } from '../../utils/logging.js'; /** * @typedef {Object} DesignToken * @property {string} id - Unique identifier * @property {'color'|'spacing'|'typography'|'shadow'|'breakpoint'} type - Token type * @property {any} value - Token value * @property {Object} metadata - Additional token information * @property {string} [metadata.description] - Token description * @property {string} [metadata.usage] - Usage guidelines * @property {string} [metadata.category] - Token category */ /** * @typedef {Object} Component * @property {string} id - Unique identifier * @property {'atom'|'molecule'|'organism'} type - Component type * @property {string} name - Component name * @property {string} description - Component description * @property {Array<Object>} props - Component properties * @property {Array<string>} dependencies - Other component IDs * @property {Array<string>} tokens - Design token IDs used * @property {string} template - Component code * @property {string} documentation - Component documentation * @property {Array<string>} tests - Component tests */ /** * @typedef {Object} GridConfig * @property {number} columns - Number of grid columns * @property {string} gutter - Gutter size * @property {Object} breakpoints - Responsive breakpoints * @property {string} containerWidth - Max container width */ /** * @typedef {Object} TemplateConfig * @property {string} name - Template name * @property {string} description - Template description * @property {Object} layout - Layout configuration * @property {Array<string>} components - Component IDs used */ /** * @typedef {Object} ValidationResult * @property {boolean} valid - Whether validation passed * @property {Array<string>} errors - List of validation errors * @property {Array<string>} warnings - List of validation warnings */ export class DesignSystemManager { constructor(knowledgeGraph) { this.tokens = new Map(); this.components = new Map(); this.knowledgeGraph = knowledgeGraph; this.gridConfig = null; this.templates = new Map(); } async initialize() { // Register design system node in knowledge graph await this.knowledgeGraph.addNode({ id: 'design-system', type: 'system', data: { status: 'active', tokenCount: 0, componentCount: 0, templateCount: 0 } }); log.info('Design system initialized'); } // Token Management async addToken(token) { if (this.tokens.has(token.id)) { throw new Error(`Token ${token.id} already exists`); } // Validate token this._validateToken(token); this.tokens.set(token.id, token); // Update knowledge graph await this.knowledgeGraph.addNode({ id: `token:${token.id}`, type: 'design-token', data: token }); await this._updateSystemMetrics(); return token; } async updateToken(id, updates) { const token = this.tokens.get(id); if (!token) { throw new Error(`Token ${id} not found`); } const updatedToken = { ...token, ...updates }; this._validateToken(updatedToken); this.tokens.set(id, updatedToken); // Update knowledge graph await this.knowledgeGraph.updateContext({ id: `token:${id}`, type: 'design-token', data: updatedToken }); // Update components using this token for (const [componentId, component] of this.components.entries()) { if (component.tokens.includes(id)) { await this.validateComponent(componentId); } } return updatedToken; } async getToken(id) { const token = this.tokens.get(id); if (!token) { throw new Error(`Token ${id} not found`); } return token; } async getAllTokens(type) { const tokens = Array.from(this.tokens.values()); return type ? tokens.filter(token => token.type === type) : tokens; } // Component Management async addComponent(component) { if (this.components.has(component.id)) { throw new Error(`Component ${component.id} already exists`); } // Validate component const validation = await this.validateComponent(component); if (!validation.valid) { throw new Error(`Invalid component: ${validation.errors.join(', ')}`); } this.components.set(component.id, component); // Update knowledge graph await this.knowledgeGraph.addNode({ id: `component:${component.id}`, type: 'design-component', data: component }); await this._updateSystemMetrics(); return component; } async updateComponent(id, updates) { const component = this.components.get(id); if (!component) { throw new Error(`Component ${id} not found`); } const updatedComponent = { ...component, ...updates }; const validation = await this.validateComponent(updatedComponent); if (!validation.valid) { throw new Error(`Invalid component: ${validation.errors.join(', ')}`); } this.components.set(id, updatedComponent); // Update knowledge graph await this.knowledgeGraph.updateContext({ id: `component:${id}`, type: 'design-component', data: updatedComponent }); // Update dependent components for (const [componentId, comp] of this.components.entries()) { if (comp.dependencies.includes(id)) { await this.validateComponent(componentId); } } return updatedComponent; } async getComponent(id) { const component = this.components.get(id); if (!component) { throw new Error(`Component ${id} not found`); } return component; } async getComponents(type) { const components = Array.from(this.components.values()); return type ? components.filter(component => component.type === type) : components; } // Documentation async generateDocumentation() { let documentation = '# Design System Documentation\n\n'; // Add tokens documentation documentation += '## Design Tokens\n\n'; const tokensByType = new Map(); for (const token of this.tokens.values()) { if (!tokensByType.has(token.type)) { tokensByType.set(token.type, []); } tokensByType.get(token.type).push(token); } for (const [type, tokens] of tokensByType.entries()) { documentation += `### ${type.charAt(0).toUpperCase() + type.slice(1)}\n\n`; for (const token of tokens) { documentation += `- **${token.id}**: ${token.metadata.description || 'No description'}\n`; documentation += ` - Value: \`${JSON.stringify(token.value)}\`\n`; if (token.metadata.usage) { documentation += ` - Usage: ${token.metadata.usage}\n`; } documentation += '\n'; } } // Add components documentation documentation += '## Components\n\n'; const componentsByType = new Map(); for (const component of this.components.values()) { if (!componentsByType.has(component.type)) { componentsByType.set(component.type, []); } componentsByType.get(component.type).push(component); } for (const [type, components] of componentsByType.entries()) { documentation += `### ${type.charAt(0).toUpperCase() + type.slice(1)}s\n\n`; for (const component of components) { documentation += `#### ${component.name}\n\n`; documentation += `${component.description}\n\n`; if (component.props.length > 0) { documentation += '**Props:**\n\n'; for (const prop of component.props) { documentation += `- \`${prop.name}\` (${prop.type})${prop.required ? ' *required*' : ''}: ${prop.description}\n`; } documentation += '\n'; } if (component.dependencies.length > 0) { documentation += '**Dependencies:**\n\n'; for (const depId of component.dependencies) { const dep = this.components.get(depId); if (dep) { documentation += `- ${dep.name}\n`; } } documentation += '\n'; } if (component.tokens.length > 0) { documentation += '**Design Tokens:**\n\n'; for (const tokenId of component.tokens) { const token = this.tokens.get(tokenId); if (token) { documentation += `- ${token.id}\n`; } } documentation += '\n'; } } } return documentation; } async validateComponent(component) { const errors = []; const warnings = []; // Check required fields if (!component.id || !component.type || !component.name) { errors.push('Missing required fields (id, type, name)'); } // Validate component type if (!['atom', 'molecule', 'organism'].includes(component.type)) { errors.push(`Invalid component type: ${component.type}`); } // Check dependencies for (const depId of component.dependencies) { if (!this.components.has(depId)) { errors.push(`Dependency not found: ${depId}`); } } // Check tokens for (const tokenId of component.tokens) { if (!this.tokens.has(tokenId)) { errors.push(`Token not found: ${tokenId}`); } } // Check for circular dependencies if (this._hasCircularDependency(component.id, new Set())) { errors.push('Circular dependency detected'); } // Validate props if (component.props) { for (const prop of component.props) { if (!prop.name || !prop.type) { errors.push(`Invalid prop definition: ${JSON.stringify(prop)}`); } } } // Check documentation if (!component.documentation) { warnings.push('Missing documentation'); } // Check tests if (!component.tests || component.tests.length === 0) { warnings.push('No tests defined'); } return { valid: errors.length === 0, errors, warnings }; } // Grid System async defineGridSystem(config) { // Validate grid configuration if (!config.columns || !config.gutter || !config.breakpoints) { throw new Error('Invalid grid configuration'); } this.gridConfig = config; // Update knowledge graph await this.knowledgeGraph.updateContext({ id: 'design-system', type: 'system', data: { gridConfig: config } }); return config; } async generateGridStyles() { if (!this.gridConfig) { throw new Error('Grid system not configured'); } const { columns, gutter, breakpoints, containerWidth } = this.gridConfig; let styles = ` .container { width: 100%; max-width: ${containerWidth}; margin: 0 auto; padding: 0 ${gutter}; } .row { display: flex; flex-wrap: wrap; margin: 0 -${gutter}; } .col { padding: 0 ${gutter}; flex-basis: 0; flex-grow: 1; max-width: 100%; } `; // Generate column classes for each breakpoint for (const [breakpoint, width] of Object.entries(breakpoints)) { const prefix = breakpoint === 'xs' ? '' : `${breakpoint}:`; styles += ` @media (min-width: ${width}) { ${Array.from({ length: columns }, (_, i) => { const col = i + 1; return ` .${prefix}col-${col} { flex: 0 0 ${(col / columns) * 100}%; max-width: ${(col / columns) * 100}%; }`; }).join('')} }`; } return styles; } // Templates async createTemplate(config) { if (!config.name || !config.layout || !config.components) { throw new Error('Invalid template configuration'); } // Validate components for (const componentId of config.components) { if (!this.components.has(componentId)) { throw new Error(`Component not found: ${componentId}`); } } const templateId = `template:${config.name.toLowerCase().replace(/\s+/g, '-')}`; this.templates.set(templateId, config); // Update knowledge graph await this.knowledgeGraph.addNode({ id: templateId, type: 'design-template', data: config }); await this._updateSystemMetrics(); return templateId; } async applyTemplate(templateId, data) { const template = this.templates.get(templateId); if (!template) { throw new Error(`Template not found: ${templateId}`); } // Generate template code based on layout and components let code = ''; // Add container and grid system code += '<div class="container">\n'; // Apply layout structure for (const section of template.layout.sections) { code += ` <div class="row ${section.class || ''}">\n`; for (const component of section.components) { const componentData = this.components.get(component.id); if (!componentData) { throw new Error(`Component not found: ${component.id}`); } code += ` <div class="col ${component.class || ''}">\n`; code += ` ${this._renderComponent(componentData, data[component.id] || {})}\n`; code += ' </div>\n'; } code += ' </div>\n'; } code += '</div>'; return code; } // Private methods async _updateSystemMetrics() { await this.knowledgeGraph.updateContext({ id: 'design-system', type: 'system', data: { tokenCount: this.tokens.size, componentCount: this.components.size, templateCount: this.templates.size } }); } _validateToken(token) { if (!token.id || !token.type || token.value === undefined) { throw new Error('Invalid token: missing required fields'); } if (!['color', 'spacing', 'typography', 'shadow', 'breakpoint'].includes(token.type)) { throw new Error(`Invalid token type: ${token.type}`); } } _hasCircularDependency(componentId, visited) { const component = this.components.get(componentId); if (!component) return false; if (visited.has(componentId)) { return true; } visited.add(componentId); for (const depId of component.dependencies) { if (this._hasCircularDependency(depId, new Set(visited))) { return true; } } return false; } _renderComponent(component, data) { // Basic component rendering - in a real implementation, // this would use a proper templating engine let rendered = component.template; // Replace prop placeholders with actual data for (const [key, value] of Object.entries(data)) { rendered = rendered.replace(new RegExp(`{{${key}}}`, 'g'), value); } return rendered; } }