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
JavaScript
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;
}
}