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,295 @@
"""
ProjectManager - Project Registry Service
Manages the server-side registry of projects, including:
- Project registration with path validation
- Root path storage and retrieval
- Project lifecycle management
"""
import os
from pathlib import Path
from typing import Optional, List, Dict, Any
import logging
logger = logging.getLogger(__name__)
class ProjectManager:
"""
Manages project registry with root path validation.
Works with the existing Projects database class to add root_path support.
Validates paths exist and are accessible before registration.
"""
def __init__(self, projects_db, config_service=None):
"""
Initialize project manager.
Args:
projects_db: Projects database class (from storage.database)
config_service: Optional ConfigService for config initialization
"""
self.db = projects_db
self.config_service = config_service
logger.info("ProjectManager initialized")
def register_project(
self,
name: str,
root_path: str,
description: str = "",
figma_file_key: str = ""
) -> Dict[str, Any]:
"""
Register a new project with validated root path.
Args:
name: Human-readable project name
root_path: Absolute path to project directory
description: Optional project description
figma_file_key: Optional Figma file key
Returns:
Created project dict
Raises:
ValueError: If path doesn't exist or isn't a directory
PermissionError: If no write access to path
"""
# Resolve and validate path
root_path = os.path.abspath(root_path)
if not os.path.isdir(root_path):
raise ValueError(f"Path does not exist or is not a directory: {root_path}")
if not os.access(root_path, os.W_OK):
raise PermissionError(f"No write access to path: {root_path}")
# Check if path already registered
existing = self.get_by_path(root_path)
if existing:
raise ValueError(f"Path already registered as project: {existing['name']}")
# Generate project ID
import uuid
project_id = str(uuid.uuid4())[:8]
# Create project in database
project = self.db.create(
id=project_id,
name=name,
description=description,
figma_file_key=figma_file_key
)
# Update with root_path (need to add this column)
self._update_root_path(project_id, root_path)
project['root_path'] = root_path
# Initialize .dss folder and config if config_service available
if self.config_service:
try:
self.config_service.init_config(root_path)
logger.info(f"Initialized .dss config for project {name}")
except Exception as e:
logger.warning(f"Failed to init config for {name}: {e}")
logger.info(f"Registered project: {name} at {root_path}")
return project
def get_project(self, project_id: str) -> Optional[Dict[str, Any]]:
"""
Get project by ID with path validation.
Args:
project_id: Project UUID
Returns:
Project dict or None if not found
Raises:
ValueError: If project path no longer exists
"""
project = self.db.get(project_id)
if not project:
return None
root_path = project.get('root_path')
if root_path and not os.path.isdir(root_path):
logger.warning(f"Project path no longer exists: {root_path}")
# Don't raise, just mark it
project['path_valid'] = False
else:
project['path_valid'] = True
return project
def list_projects(self, status: str = None, valid_only: bool = False) -> List[Dict[str, Any]]:
"""
List all projects with optional filtering.
Args:
status: Filter by status (active, archived, etc.)
valid_only: Only return projects with valid paths
Returns:
List of project dicts
"""
projects = self.db.list(status=status)
# Add path validation status
for project in projects:
root_path = project.get('root_path')
project['path_valid'] = bool(root_path and os.path.isdir(root_path))
if valid_only:
projects = [p for p in projects if p.get('path_valid', False)]
return projects
def get_by_path(self, root_path: str) -> Optional[Dict[str, Any]]:
"""
Find project by root path.
Args:
root_path: Absolute path to search for
Returns:
Project dict or None if not found
"""
root_path = os.path.abspath(root_path)
projects = self.list_projects()
for project in projects:
if project.get('root_path') == root_path:
return project
return None
def update_project(
self,
project_id: str,
name: str = None,
description: str = None,
root_path: str = None,
figma_file_key: str = None,
status: str = None
) -> Optional[Dict[str, Any]]:
"""
Update project fields.
Args:
project_id: Project UUID
name: Optional new name
description: Optional new description
root_path: Optional new root path (validated)
figma_file_key: Optional new Figma key
status: Optional new status
Returns:
Updated project dict or None if not found
"""
project = self.db.get(project_id)
if not project:
return None
# Validate new root_path if provided
if root_path:
root_path = os.path.abspath(root_path)
if not os.path.isdir(root_path):
raise ValueError(f"Path does not exist: {root_path}")
if not os.access(root_path, os.W_OK):
raise PermissionError(f"No write access: {root_path}")
self._update_root_path(project_id, root_path)
# Update other fields via existing update method
updates = {}
if name is not None:
updates['name'] = name
if description is not None:
updates['description'] = description
if figma_file_key is not None:
updates['figma_file_key'] = figma_file_key
if status is not None:
updates['status'] = status
if updates:
self.db.update(project_id, **updates)
return self.get_project(project_id)
def delete_project(self, project_id: str, delete_config: bool = False) -> bool:
"""
Delete a project from registry.
Args:
project_id: Project UUID
delete_config: If True, also delete .dss folder
Returns:
True if deleted, False if not found
"""
project = self.db.get(project_id)
if not project:
return False
if delete_config and project.get('root_path'):
import shutil
dss_path = Path(project['root_path']) / '.dss'
if dss_path.exists():
shutil.rmtree(dss_path)
logger.info(f"Deleted .dss folder at {dss_path}")
self.db.delete(project_id)
logger.info(f"Deleted project: {project_id}")
return True
def _update_root_path(self, project_id: str, root_path: str) -> None:
"""
Update root_path in database.
Uses raw SQL since the column may not be in the existing model.
"""
from storage.database import get_connection
with get_connection() as conn:
# Ensure column exists
try:
conn.execute("""
ALTER TABLE projects ADD COLUMN root_path TEXT DEFAULT ''
""")
logger.info("Added root_path column to projects table")
except Exception:
# Column already exists
pass
# Update the value
conn.execute(
"UPDATE projects SET root_path = ? WHERE id = ?",
(root_path, project_id)
)
@staticmethod
def ensure_schema():
"""
Ensure database schema has root_path column.
Call this on startup to migrate existing databases.
"""
from storage.database import get_connection
with get_connection() as conn:
cursor = conn.cursor()
# Check if column exists
cursor.execute("PRAGMA table_info(projects)")
columns = [col[1] for col in cursor.fetchall()]
if 'root_path' not in columns:
cursor.execute("""
ALTER TABLE projects ADD COLUMN root_path TEXT DEFAULT ''
""")
logger.info("Migration: Added root_path column to projects table")
else:
logger.debug("Schema check: root_path column exists")