Files
dss/admin-ui/js/services/tools-service.js
Digital Production Factory 276ed71f31 Initial commit: Clean DSS implementation
Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm

Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)

Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability

Migration completed: $(date)
🤖 Clean migration with full functionality preserved
2025-12-09 18:45:48 -03:00

450 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Tools Service - Manages MCP tools catalog and execution
*/
import api from '../core/api.js';
import logger from '../core/logger.js';
class ToolsService {
constructor() {
this.tools = [];
this.toolsMetadata = this._getToolsMetadata();
this.executionHistory = [];
this.toolStatus = {};
this.teamToolMappings = this._getTeamToolMappings();
}
/**
* Define which tools are relevant for each team
*/
_getTeamToolMappings() {
return {
'ui': [
'extract_tokens', 'extract_components', 'generate_component_code',
'sync_tokens_to_file', 'ingest_css_tokens', 'ingest_scss_tokens',
'ingest_tailwind_tokens', 'merge_tokens', 'export_tokens',
'scan_storybook', 'generate_story', 'generate_stories_batch',
'generate_storybook_theme'
],
'ux': [
'analyze_style_values', 'check_naming_consistency', 'find_style_patterns',
'validate_tokens', 'extract_tokens', 'export_tokens',
'discover_project', 'get_sync_history'
],
'qa': [
'get_quick_wins', 'get_quick_wins_report', 'validate_tokens',
'find_unused_styles', 'find_inline_styles', 'analyze_react_components',
'build_source_graph', 'get_story_coverage'
],
'all': [] // Empty means all tools
};
}
/**
* Get metadata for all 32 MCP tools
*/
_getToolsMetadata() {
return {
// Projects
list_projects: {
name: 'list_projects',
category: 'Projects',
description: 'List all design system projects',
parameters: [],
icon: '📁'
},
create_project: {
name: 'create_project',
category: 'Projects',
description: 'Create a new design system project',
parameters: ['name', 'description', 'figma_file_key'],
icon: ''
},
get_project: {
name: 'get_project',
category: 'Projects',
description: 'Get details of a specific project',
parameters: ['project_id'],
icon: '🔍'
},
// Figma
extract_tokens: {
name: 'extract_tokens',
category: 'Figma',
description: 'Extract design tokens from Figma file',
parameters: ['file_key', 'format'],
icon: '🎨',
requiresConfig: ['figma_token']
},
extract_components: {
name: 'extract_components',
category: 'Figma',
description: 'Extract components from Figma file',
parameters: ['file_key'],
icon: '🧩',
requiresConfig: ['figma_token']
},
generate_component_code: {
name: 'generate_component_code',
category: 'Figma',
description: 'Generate React code from Figma component',
parameters: ['file_key', 'node_id', 'framework'],
icon: '⚛️',
requiresConfig: ['figma_token']
},
sync_tokens_to_file: {
name: 'sync_tokens_to_file',
category: 'Figma',
description: 'Sync tokens to output file',
parameters: ['file_key', 'output_path', 'format'],
icon: '🔄',
requiresConfig: ['figma_token']
},
// Ingestion
ingest_css_tokens: {
name: 'ingest_css_tokens',
category: 'Ingestion',
description: 'Ingest tokens from CSS variables',
parameters: ['source'],
icon: '📥'
},
ingest_scss_tokens: {
name: 'ingest_scss_tokens',
category: 'Ingestion',
description: 'Ingest tokens from SCSS variables',
parameters: ['source'],
icon: '📥'
},
ingest_tailwind_tokens: {
name: 'ingest_tailwind_tokens',
category: 'Ingestion',
description: 'Ingest tokens from Tailwind config',
parameters: ['source'],
icon: '📥'
},
ingest_json_tokens: {
name: 'ingest_json_tokens',
category: 'Ingestion',
description: 'Ingest tokens from JSON format',
parameters: ['source'],
icon: '📥'
},
merge_tokens: {
name: 'merge_tokens',
category: 'Ingestion',
description: 'Merge tokens from multiple sources',
parameters: ['sources', 'strategy'],
icon: '🔗'
},
export_tokens: {
name: 'export_tokens',
category: 'Ingestion',
description: 'Export tokens to specified format',
parameters: ['format', 'output_path'],
icon: '📤'
},
validate_tokens: {
name: 'validate_tokens',
category: 'Ingestion',
description: 'Validate token structure and values',
parameters: ['source'],
icon: '✓'
},
// Analysis
discover_project: {
name: 'discover_project',
category: 'Analysis',
description: 'Discover project structure and frameworks',
parameters: ['path'],
icon: '🔎',
quickWin: true
},
analyze_react_components: {
name: 'analyze_react_components',
category: 'Analysis',
description: 'Analyze React components in project',
parameters: ['path'],
icon: '⚛️',
quickWin: true
},
find_inline_styles: {
name: 'find_inline_styles',
category: 'Analysis',
description: 'Find components with inline styles',
parameters: ['path'],
icon: '🎯',
quickWin: true
},
find_style_patterns: {
name: 'find_style_patterns',
category: 'Analysis',
description: 'Identify common style patterns',
parameters: ['path'],
icon: '📊',
quickWin: true
},
analyze_style_values: {
name: 'analyze_style_values',
category: 'Analysis',
description: 'Analyze style property values',
parameters: ['path', 'property'],
icon: '📈',
quickWin: true
},
find_unused_styles: {
name: 'find_unused_styles',
category: 'Analysis',
description: 'Find unused style definitions',
parameters: ['path'],
icon: '🗑️',
quickWin: true
},
build_source_graph: {
name: 'build_source_graph',
category: 'Analysis',
description: 'Build component dependency graph',
parameters: ['path', 'depth'],
icon: '🕸️'
},
get_quick_wins: {
name: 'get_quick_wins',
category: 'Analysis',
description: 'Get actionable improvement opportunities',
parameters: ['path'],
icon: '⚡',
quickWin: true,
featured: true
},
get_quick_wins_report: {
name: 'get_quick_wins_report',
category: 'Analysis',
description: 'Generate detailed quick wins report',
parameters: ['path'],
icon: '📋',
quickWin: true
},
check_naming_consistency: {
name: 'check_naming_consistency',
category: 'Analysis',
description: 'Check component naming consistency',
parameters: ['path'],
icon: '🏷️',
quickWin: true
},
// Storybook
scan_storybook: {
name: 'scan_storybook',
category: 'Storybook',
description: 'Scan existing Storybook configuration',
parameters: ['path'],
icon: '📚'
},
generate_story: {
name: 'generate_story',
category: 'Storybook',
description: 'Generate Storybook story for component',
parameters: ['component_path', 'template'],
icon: '📖'
},
generate_stories_batch: {
name: 'generate_stories_batch',
category: 'Storybook',
description: 'Generate stories for multiple components',
parameters: ['components', 'template'],
icon: '📚'
},
generate_storybook_theme: {
name: 'generate_storybook_theme',
category: 'Storybook',
description: 'Generate Storybook theme from tokens',
parameters: ['tokens', 'output_path'],
icon: '🎨'
},
get_story_coverage: {
name: 'get_story_coverage',
category: 'Storybook',
description: 'Check which components have stories',
parameters: ['path'],
icon: '📊'
},
// Activity
get_status: {
name: 'get_status',
category: 'Activity',
description: 'Get server status and statistics',
parameters: [],
icon: '💚'
},
get_sync_history: {
name: 'get_sync_history',
category: 'Activity',
description: 'Get token sync history',
parameters: ['limit'],
icon: '📜'
},
get_activity: {
name: 'get_activity',
category: 'Activity',
description: 'Get recent activity log',
parameters: ['limit'],
icon: '📝'
}
};
}
/**
* Get all tools with metadata
*/
getAllTools() {
return Object.values(this.toolsMetadata).map(tool => ({
...tool,
status: this.toolStatus[tool.name] || 'available',
lastUsed: this._getLastUsed(tool.name)
}));
}
/**
* Get tools by category
*/
getToolsByCategory(category) {
return this.getAllTools().filter(tool => tool.category === category);
}
/**
* Get quick win tools
*/
getQuickWinTools() {
return this.getAllTools().filter(tool => tool.quickWin);
}
/**
* Get featured tools
*/
getFeaturedTools() {
return this.getAllTools().filter(tool => tool.featured);
}
/**
* Get tools filtered by team context
*/
getToolsByTeam(teamContext = 'all') {
if (teamContext === 'all' || !this.teamToolMappings[teamContext]) {
return this.getAllTools();
}
const teamTools = this.teamToolMappings[teamContext];
return this.getAllTools().filter(tool => teamTools.includes(tool.name));
}
/**
* Execute a tool
*/
async executeTool(toolName, params = {}) {
logger.info('Tools', `Executing tool: ${toolName}`, params);
this.toolStatus[toolName] = 'executing';
const startTime = Date.now();
try {
// Map tool name to API endpoint
const result = await api.executeMCPTool(toolName, params);
const duration = Date.now() - startTime;
this.toolStatus[toolName] = 'success';
// Log execution
this._logExecution(toolName, params, result, duration, 'success');
logger.info('Tools', `Tool ${toolName} succeeded in ${duration}ms`);
return result;
} catch (error) {
const duration = Date.now() - startTime;
this.toolStatus[toolName] = 'error';
// Log error
this._logExecution(toolName, params, null, duration, 'error', error.message);
logger.error('Tools', `Tool ${toolName} failed: ${error.message}`, error);
throw error;
}
}
/**
* Log tool execution
*/
_logExecution(toolName, params, result, duration, status, error = null) {
const execution = {
toolName,
params,
result,
duration,
status,
error,
timestamp: new Date().toISOString()
};
this.executionHistory.push(execution);
// Keep only last 100 executions
if (this.executionHistory.length > 100) {
this.executionHistory.shift();
}
}
/**
* Get execution history for a tool
*/
getToolHistory(toolName) {
return this.executionHistory
.filter(exec => exec.toolName === toolName)
.slice(-10);
}
/**
* Get last used timestamp
*/
_getLastUsed(toolName) {
const executions = this.executionHistory.filter(exec => exec.toolName === toolName);
if (executions.length === 0) return null;
return executions[executions.length - 1].timestamp;
}
/**
* Get tool success rate
*/
getSuccessRate(toolName) {
const executions = this.executionHistory.filter(exec => exec.toolName === toolName);
if (executions.length === 0) return null;
const successful = executions.filter(exec => exec.status === 'success').length;
return successful / executions.length;
}
/**
* Get all categories
*/
getCategories() {
const categories = new Set();
Object.values(this.toolsMetadata).forEach(tool => {
categories.add(tool.category);
});
return Array.from(categories);
}
}
// Singleton instance
const toolsService = new ToolsService();
export default toolsService;
export { ToolsService };