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,335 @@
/**
* Plugin Service - Manages frontend plugins for DSS Admin UI
*
* Supports loading, enabling/disabling, and configuring plugins.
* Plugins can extend UI, add commands, and integrate with services.
*/
import logger from '../core/logger.js';
class PluginService {
constructor() {
this.plugins = new Map();
this.hooks = new Map();
this.loadedPlugins = [];
this.storageKey = 'dss_plugins_config';
// Load saved plugin states
this.config = this._loadConfig();
}
/**
* Load plugin configuration from localStorage
*/
_loadConfig() {
try {
const saved = localStorage.getItem(this.storageKey);
return saved ? JSON.parse(saved) : { enabled: {}, settings: {} };
} catch (e) {
logger.warn('PluginService', 'Failed to load plugin config', e);
return { enabled: {}, settings: {} };
}
}
/**
* Save plugin configuration to localStorage
*/
_saveConfig() {
try {
localStorage.setItem(this.storageKey, JSON.stringify(this.config));
} catch (e) {
logger.warn('PluginService', 'Failed to save plugin config', e);
}
}
/**
* Register a plugin
* @param {Object} plugin - Plugin definition
*/
register(plugin) {
if (!plugin.id || !plugin.name) {
throw new Error('Plugin must have id and name');
}
if (this.plugins.has(plugin.id)) {
logger.warn('PluginService', `Plugin ${plugin.id} already registered`);
return;
}
const pluginDef = {
id: plugin.id,
name: plugin.name,
version: plugin.version || '1.0.0',
description: plugin.description || '',
author: plugin.author || 'Unknown',
icon: plugin.icon || '🔌',
category: plugin.category || 'general',
// Lifecycle hooks
onInit: plugin.onInit || (() => {}),
onEnable: plugin.onEnable || (() => {}),
onDisable: plugin.onDisable || (() => {}),
onDestroy: plugin.onDestroy || (() => {}),
// Extension points
commands: plugin.commands || [],
panels: plugin.panels || [],
settings: plugin.settings || [],
// State
enabled: this.config.enabled[plugin.id] ?? plugin.enabledByDefault ?? false,
initialized: false
};
this.plugins.set(plugin.id, pluginDef);
logger.info('PluginService', `Registered plugin: ${plugin.name} v${pluginDef.version}`);
// Auto-init if enabled
if (pluginDef.enabled) {
this._initPlugin(pluginDef);
}
return pluginDef;
}
/**
* Initialize a plugin
*/
async _initPlugin(plugin) {
if (plugin.initialized) return;
try {
await plugin.onInit(this._createPluginContext(plugin));
plugin.initialized = true;
this.loadedPlugins.push(plugin.id);
logger.info('PluginService', `Initialized plugin: ${plugin.name}`);
} catch (e) {
logger.error('PluginService', `Failed to init plugin ${plugin.name}`, e);
}
}
/**
* Create context object passed to plugin hooks
*/
_createPluginContext(plugin) {
return {
pluginId: plugin.id,
settings: this.config.settings[plugin.id] || {},
// API for plugins
registerHook: (hookName, callback) => this.registerHook(plugin.id, hookName, callback),
unregisterHook: (hookName) => this.unregisterHook(plugin.id, hookName),
getSettings: () => this.config.settings[plugin.id] || {},
setSetting: (key, value) => this.setPluginSetting(plugin.id, key, value),
// Access to app APIs
emit: (event, data) => this._emitEvent(plugin.id, event, data),
log: (msg, data) => logger.info(`Plugin:${plugin.name}`, msg, data)
};
}
/**
* Enable a plugin
*/
async enable(pluginId) {
const plugin = this.plugins.get(pluginId);
if (!plugin) {
throw new Error(`Plugin ${pluginId} not found`);
}
if (plugin.enabled) return;
plugin.enabled = true;
this.config.enabled[pluginId] = true;
this._saveConfig();
if (!plugin.initialized) {
await this._initPlugin(plugin);
}
try {
await plugin.onEnable(this._createPluginContext(plugin));
logger.info('PluginService', `Enabled plugin: ${plugin.name}`);
this._emitEvent('system', 'plugin:enabled', { pluginId });
} catch (e) {
logger.error('PluginService', `Failed to enable plugin ${plugin.name}`, e);
plugin.enabled = false;
this.config.enabled[pluginId] = false;
this._saveConfig();
throw e;
}
}
/**
* Disable a plugin
*/
async disable(pluginId) {
const plugin = this.plugins.get(pluginId);
if (!plugin) {
throw new Error(`Plugin ${pluginId} not found`);
}
if (!plugin.enabled) return;
try {
await plugin.onDisable(this._createPluginContext(plugin));
plugin.enabled = false;
this.config.enabled[pluginId] = false;
this._saveConfig();
logger.info('PluginService', `Disabled plugin: ${plugin.name}`);
this._emitEvent('system', 'plugin:disabled', { pluginId });
} catch (e) {
logger.error('PluginService', `Failed to disable plugin ${plugin.name}`, e);
throw e;
}
}
/**
* Toggle plugin enabled state
*/
async toggle(pluginId) {
const plugin = this.plugins.get(pluginId);
if (!plugin) {
throw new Error(`Plugin ${pluginId} not found`);
}
if (plugin.enabled) {
await this.disable(pluginId);
} else {
await this.enable(pluginId);
}
}
/**
* Register a hook callback
*/
registerHook(pluginId, hookName, callback) {
const key = `${pluginId}:${hookName}`;
if (!this.hooks.has(hookName)) {
this.hooks.set(hookName, new Map());
}
this.hooks.get(hookName).set(pluginId, callback);
logger.debug('PluginService', `Registered hook ${hookName} for ${pluginId}`);
}
/**
* Unregister a hook callback
*/
unregisterHook(pluginId, hookName) {
const hookMap = this.hooks.get(hookName);
if (hookMap) {
hookMap.delete(pluginId);
}
}
/**
* Execute all callbacks for a hook
*/
async executeHook(hookName, data = {}) {
const hookMap = this.hooks.get(hookName);
if (!hookMap) return [];
const results = [];
for (const [pluginId, callback] of hookMap) {
const plugin = this.plugins.get(pluginId);
if (plugin?.enabled) {
try {
const result = await callback(data);
results.push({ pluginId, result });
} catch (e) {
logger.error('PluginService', `Hook ${hookName} failed for ${pluginId}`, e);
}
}
}
return results;
}
/**
* Set a plugin setting
*/
setPluginSetting(pluginId, key, value) {
if (!this.config.settings[pluginId]) {
this.config.settings[pluginId] = {};
}
this.config.settings[pluginId][key] = value;
this._saveConfig();
this._emitEvent(pluginId, 'settings:changed', { key, value });
}
/**
* Get plugin settings
*/
getPluginSettings(pluginId) {
return this.config.settings[pluginId] || {};
}
/**
* Emit event to listeners
*/
_emitEvent(source, event, data) {
window.dispatchEvent(new CustomEvent('dss:plugin:event', {
detail: { source, event, data }
}));
}
/**
* Get all registered plugins
*/
getAll() {
return Array.from(this.plugins.values());
}
/**
* Get enabled plugins
*/
getEnabled() {
return this.getAll().filter(p => p.enabled);
}
/**
* Get plugin by ID
*/
get(pluginId) {
return this.plugins.get(pluginId);
}
/**
* Get all commands from enabled plugins
*/
getAllCommands() {
const commands = [];
for (const plugin of this.getEnabled()) {
for (const cmd of plugin.commands) {
commands.push({
...cmd,
pluginId: plugin.id,
pluginName: plugin.name
});
}
}
return commands;
}
/**
* Get all panels from enabled plugins
*/
getAllPanels() {
const panels = [];
for (const plugin of this.getEnabled()) {
for (const panel of plugin.panels) {
panels.push({
...panel,
pluginId: plugin.id,
pluginName: plugin.name
});
}
}
return panels;
}
}
// Singleton instance
const pluginService = new PluginService();
export default pluginService;
export { PluginService };