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
This commit is contained in:
Digital Production Factory
2025-12-09 18:45:48 -03:00
commit 276ed71f31
884 changed files with 373737 additions and 0 deletions

View File

@@ -0,0 +1,378 @@
/**
* Claude Service - Chat interface with Claude AI and MCP Integration
*
* Provides:
* - Direct Claude chat
* - MCP tool execution
* - Project context integration
* - Integration management (Figma, Jira, Confluence)
*/
import api from '../core/api.js';
import logger from '../core/logger.js';
class ClaudeService {
constructor() {
this.conversationHistory = [];
this.maxHistory = 50;
this.currentProjectId = null;
this.userId = 1; // Default user ID (TODO: implement proper auth)
this.mcpTools = null;
this.mcpStatus = null;
// Load persisted chat history
this.loadHistory();
}
/**
* Get storage key for current user
* @private
*/
_getStorageKey() {
return `ds-chat-history-user-${this.userId}`;
}
/**
* Load chat history from localStorage
* @private
*/
loadHistory() {
const key = this._getStorageKey();
try {
const stored = localStorage.getItem(key);
if (stored) {
this.conversationHistory = JSON.parse(stored);
logger.info('Claude', `Loaded ${this.conversationHistory.length} messages from history`);
}
} catch (error) {
logger.warn('Claude', 'Failed to load chat history, resetting', error);
localStorage.removeItem(key);
this.conversationHistory = [];
}
}
/**
* Save chat history to localStorage with quota management
* @private
*/
saveHistory() {
const key = this._getStorageKey();
try {
// Keep only last 50 messages to respect localStorage limits
const historyToSave = this.conversationHistory.slice(-50);
localStorage.setItem(key, JSON.stringify(historyToSave));
} catch (error) {
logger.warn('Claude', 'Failed to save chat history', error);
if (error.name === 'QuotaExceededError') {
// Emergency trim: cut in half and retry
this.conversationHistory = this.conversationHistory.slice(-25);
try {
localStorage.setItem(key, JSON.stringify(this.conversationHistory));
logger.info('Claude', 'History trimmed to 25 messages due to quota');
} catch (retryError) {
logger.error('Claude', 'Failed to save history even after trim', retryError);
}
}
}
}
/**
* Set current project context
*/
setProject(projectId) {
this.currentProjectId = projectId;
logger.info('Claude', 'Project context set', { projectId });
}
/**
* Send message to Claude with MCP context
*/
async chat(message, context = {}) {
logger.info('Claude', 'Sending message', { message: message.substring(0, 50) + '...' });
// Add project context if available
if (this.currentProjectId && !context.projectId) {
context.projectId = this.currentProjectId;
}
// Add to history
this.conversationHistory.push({
role: 'user',
content: message,
timestamp: new Date().toISOString()
});
try {
// Call backend Claude endpoint with MCP context
const response = await api.post('/claude/chat', {
message,
context: {
...context,
mcp_enabled: true,
available_tools: this.mcpTools ? Object.keys(this.mcpTools).flatMap(k => this.mcpTools[k].map(t => t.name)) : []
},
history: this.conversationHistory.slice(-10) // Send last 10 messages
});
// Add response to history
this.conversationHistory.push({
role: 'assistant',
content: response.response,
timestamp: new Date().toISOString(),
tools_used: response.tools_used || []
});
// Keep history manageable
if (this.conversationHistory.length > this.maxHistory) {
this.conversationHistory = this.conversationHistory.slice(-this.maxHistory);
}
// Persist updated history
this.saveHistory();
logger.info('Claude', 'Received response', { length: response.response.length });
return response.response;
} catch (error) {
logger.error('Claude', 'Chat failed', error);
throw error;
}
}
/**
* Get conversation history
*/
getHistory() {
return this.conversationHistory;
}
/**
* Clear history
*/
clearHistory() {
this.conversationHistory = [];
this.saveHistory(); // Persist empty state
logger.info('Claude', 'Conversation history cleared');
}
/**
* Export conversation
*/
exportConversation() {
const data = JSON.stringify(this.conversationHistory, null, 2);
const dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(data);
const exportFileDefaultName = `claude-conversation-${Date.now()}.json`;
const linkElement = document.createElement('a');
linkElement.setAttribute('href', dataUri);
linkElement.setAttribute('download', exportFileDefaultName);
linkElement.click();
}
// === MCP Integration Methods ===
/**
* Get MCP server status
*/
async getMcpStatus() {
try {
const status = await api.get('/mcp/status');
this.mcpStatus = status;
logger.info('Claude', 'MCP status fetched', status);
return status;
} catch (error) {
logger.error('Claude', 'Failed to fetch MCP status', error);
throw error;
}
}
/**
* Get all available MCP tools
*/
async getMcpTools() {
try {
const tools = await api.get('/mcp/tools');
this.mcpTools = tools.tools;
logger.info('Claude', 'MCP tools fetched', { count: tools.total_count });
return tools;
} catch (error) {
logger.error('Claude', 'Failed to fetch MCP tools', error);
throw error;
}
}
/**
* Execute an MCP tool directly
*/
async executeMcpTool(toolName, args = {}) {
if (this.currentProjectId && !args.project_id) {
args.project_id = this.currentProjectId;
}
try {
logger.info('Claude', 'Executing MCP tool', { toolName, args });
const result = await api.post(`/mcp/tools/${toolName}/execute?user_id=${this.userId}`, args);
logger.info('Claude', 'MCP tool executed', { toolName, success: !result.error });
return result;
} catch (error) {
logger.error('Claude', 'MCP tool execution failed', error);
throw error;
}
}
// === Integration Management ===
/**
* Get all integration types and their health status
*/
async getIntegrations() {
try {
const integrations = await api.get('/mcp/integrations');
logger.info('Claude', 'Integrations fetched', { count: integrations.integrations.length });
return integrations.integrations;
} catch (error) {
logger.error('Claude', 'Failed to fetch integrations', error);
throw error;
}
}
/**
* Get project-specific integrations
*/
async getProjectIntegrations(projectId = null) {
const pid = projectId || this.currentProjectId;
if (!pid) {
throw new Error('No project ID specified');
}
try {
const integrations = await api.get(`/projects/${pid}/integrations?user_id=${this.userId}`);
return integrations.integrations;
} catch (error) {
logger.error('Claude', 'Failed to fetch project integrations', error);
throw error;
}
}
/**
* Configure an integration for a project
*/
async configureIntegration(integrationType, config, projectId = null) {
const pid = projectId || this.currentProjectId;
if (!pid) {
throw new Error('No project ID specified');
}
try {
const result = await api.post(`/projects/${pid}/integrations?user_id=${this.userId}`, {
integration_type: integrationType,
config,
enabled: true
});
logger.info('Claude', 'Integration configured', { integrationType, projectId: pid });
return result;
} catch (error) {
logger.error('Claude', 'Failed to configure integration', error);
throw error;
}
}
/**
* Update an integration
*/
async updateIntegration(integrationType, updates, projectId = null) {
const pid = projectId || this.currentProjectId;
if (!pid) {
throw new Error('No project ID specified');
}
try {
const result = await api.put(
`/projects/${pid}/integrations/${integrationType}?user_id=${this.userId}`,
updates
);
logger.info('Claude', 'Integration updated', { integrationType, projectId: pid });
return result;
} catch (error) {
logger.error('Claude', 'Failed to update integration', error);
throw error;
}
}
/**
* Delete an integration
*/
async deleteIntegration(integrationType, projectId = null) {
const pid = projectId || this.currentProjectId;
if (!pid) {
throw new Error('No project ID specified');
}
try {
const result = await api.delete(
`/projects/${pid}/integrations/${integrationType}?user_id=${this.userId}`
);
logger.info('Claude', 'Integration deleted', { integrationType, projectId: pid });
return result;
} catch (error) {
logger.error('Claude', 'Failed to delete integration', error);
throw error;
}
}
/**
* Toggle integration enabled/disabled
*/
async toggleIntegration(integrationType, enabled, projectId = null) {
return this.updateIntegration(integrationType, { enabled }, projectId);
}
// === Project Context Tools ===
/**
* Get project summary via MCP
*/
async getProjectSummary(projectId = null) {
return this.executeMcpTool('dss_get_project_summary', {
project_id: projectId || this.currentProjectId,
include_components: false
});
}
/**
* Get project health via MCP
*/
async getProjectHealth(projectId = null) {
return this.executeMcpTool('dss_get_project_health', {
project_id: projectId || this.currentProjectId
});
}
/**
* List project components via MCP
*/
async listComponents(projectId = null, filter = null) {
const args = { project_id: projectId || this.currentProjectId };
if (filter) {
args.filter_name = filter;
}
return this.executeMcpTool('dss_list_components', args);
}
/**
* Get design tokens via MCP
*/
async getDesignTokens(projectId = null, category = null) {
const args = { project_id: projectId || this.currentProjectId };
if (category) {
args.token_category = category;
}
return this.executeMcpTool('dss_get_design_tokens', args);
}
}
// Singleton instance
const claudeService = new ClaudeService();
export default claudeService;
export { ClaudeService };