""" DSS SENSORY ORGANS - Figma Integration Toolkit The DSS sensory organs allow the design system organism to perceive and digest visual designs from Figma. This toolkit extracts genetic information (tokens, components, styles) from the Figma sensory perception and transforms it into nutrients for the organism. Tool Suite (Sensory Perception Functions): 1. figma_extract_variables - 🩸 Perceive design tokens as blood nutrients 2. figma_extract_components - 🧬 Perceive component DNA blueprints 3. figma_extract_styles - 🎨 Perceive visual expressions and patterns 4. figma_sync_tokens - 🔄 Distribute nutrients through circulatory system 5. figma_visual_diff - 👁️ Detect changes in visual expression 6. figma_validate_components - 🧬 Verify genetic code integrity 7. figma_generate_code - 📝 Encode genetic information into code Architecture: - Sensory Perception: HTTPx client with SQLite caching (organism's memory) - Token Metabolism: Design token transformation pipeline - Code Generation: Genetic encoding into multiple framework languages Framework: DSS Organism Framework See: docs/DSS_ORGANISM_GUIDE.md#sensory-organs """ import json import hashlib import asyncio import sys from datetime import datetime from typing import Optional, Dict, List, Any from dataclasses import dataclass, asdict from pathlib import Path import httpx # Add parent to path for imports sys.path.insert(0, str(Path(__file__).parent.parent)) from config import config from storage.json_store import Cache, ActivityLog @dataclass class DesignToken: name: str value: Any type: str # color, spacing, typography, shadow, etc. description: str = "" category: str = "" @dataclass class ComponentDefinition: name: str key: str description: str properties: Dict[str, Any] variants: List[Dict[str, Any]] @dataclass class StyleDefinition: name: str key: str type: str # TEXT, FILL, EFFECT, GRID properties: Dict[str, Any] class FigmaClient: """ 👁️ FIGMA SENSORY RECEPTOR - Organism's visual perception system The sensory receptor connects the DSS organism to Figma's visual information. It perceives visual designs and caches genetic information (tokens, components) in the organism's short-term memory (SQLite cache) for efficient digestion. Features: - Real-time sensory perception (live Figma API connection) - Memory caching (SQLite persistence with TTL) - Rate limiting awareness (respects Figma's biological constraints) - Mock perception mode (for organism development without external connection) """ def __init__(self, token: Optional[str] = None): # Establish sensory connection (use provided token or config default) self.token = token or config.figma.token self.base_url = "https://api.figma.com/v1" self.cache_ttl = config.figma.cache_ttl self._use_real_api = bool(self.token) # Real sensory perception vs mock dreams def _cache_key(self, endpoint: str) -> str: return f"figma:{hashlib.md5(endpoint.encode()).hexdigest()}" async def _request(self, endpoint: str) -> Dict[str, Any]: """ 👁️ SENSORY PERCEPTION - Fetch visual information from Figma The sensory receptor reaches out to Figma to perceive visual designs. If the organism is in development mode, it uses dream data (mocks). Otherwise, it queries the external Figma organism and stores perceived information in its own memory (SQLite cache) for quick recall. Flow: 1. Check if sensory is in development mode (mock perception) 2. Check organism's memory cache for previous perception 3. If memory miss, perceive from external source (Figma API) 4. Store new perception in memory for future recognition 5. Log the perceptual event """ if not self._use_real_api: # Sensory hallucinations for development (mock perception) return self._get_mock_data(endpoint) cache_key = self._cache_key(endpoint) # Check organism memory first (short-term memory - SQLite) cached = Cache.get(cache_key) if cached is not None: return cached # Perceive from external source (live Figma perception) async with httpx.AsyncClient(timeout=30.0) as client: response = await client.get( f"{self.base_url}{endpoint}", headers={"X-Figma-Token": self.token} ) response.raise_for_status() data = response.json() # Store perception in organism memory for future recognition Cache.set(cache_key, data, ttl=self.cache_ttl) # Log the perceptual event ActivityLog.log( action="figma_sensory_perception", entity_type="sensory_organs", details={"endpoint": endpoint, "cached": False, "perception": "live"} ) return data def _get_mock_data(self, endpoint: str) -> Dict[str, Any]: """Return mock data for local development.""" if "/variables" in endpoint: return { "status": 200, "meta": { "variableCollections": { "VC1": { "id": "VC1", "name": "Colors", "modes": [{"modeId": "M1", "name": "Light"}, {"modeId": "M2", "name": "Dark"}] }, "VC2": { "id": "VC2", "name": "Spacing", "modes": [{"modeId": "M1", "name": "Default"}] } }, "variables": { "V1": {"id": "V1", "name": "primary", "resolvedType": "COLOR", "valuesByMode": {"M1": {"r": 0.2, "g": 0.4, "b": 0.9, "a": 1}}}, "V2": {"id": "V2", "name": "secondary", "resolvedType": "COLOR", "valuesByMode": {"M1": {"r": 0.5, "g": 0.5, "b": 0.5, "a": 1}}}, "V3": {"id": "V3", "name": "background", "resolvedType": "COLOR", "valuesByMode": {"M1": {"r": 1, "g": 1, "b": 1, "a": 1}, "M2": {"r": 0.1, "g": 0.1, "b": 0.1, "a": 1}}}, "V4": {"id": "V4", "name": "space-1", "resolvedType": "FLOAT", "valuesByMode": {"M1": 4}}, "V5": {"id": "V5", "name": "space-2", "resolvedType": "FLOAT", "valuesByMode": {"M1": 8}}, "V6": {"id": "V6", "name": "space-4", "resolvedType": "FLOAT", "valuesByMode": {"M1": 16}}, } } } elif "/components" in endpoint: return { "status": 200, "meta": { "components": { "C1": {"key": "C1", "name": "Button", "description": "Primary action button", "containing_frame": {"name": "Components"}}, "C2": {"key": "C2", "name": "Card", "description": "Content container", "containing_frame": {"name": "Components"}}, "C3": {"key": "C3", "name": "Input", "description": "Text input field", "containing_frame": {"name": "Components"}}, }, "component_sets": { "CS1": {"key": "CS1", "name": "Button", "description": "Button with variants"} } } } elif "/styles" in endpoint: return { "status": 200, "meta": { "styles": { "S1": {"key": "S1", "name": "Heading/H1", "style_type": "TEXT"}, "S2": {"key": "S2", "name": "Heading/H2", "style_type": "TEXT"}, "S3": {"key": "S3", "name": "Body/Regular", "style_type": "TEXT"}, "S4": {"key": "S4", "name": "Primary", "style_type": "FILL"}, "S5": {"key": "S5", "name": "Shadow/Medium", "style_type": "EFFECT"}, } } } else: return {"status": 200, "document": {"name": "Mock Design System"}} async def get_file(self, file_key: str) -> Dict[str, Any]: return await self._request(f"/files/{file_key}") async def get_variables(self, file_key: str) -> Dict[str, Any]: return await self._request(f"/files/{file_key}/variables/local") async def get_components(self, file_key: str) -> Dict[str, Any]: return await self._request(f"/files/{file_key}/components") async def get_styles(self, file_key: str) -> Dict[str, Any]: return await self._request(f"/files/{file_key}/styles") class FigmaToolSuite: """ 👁️ SENSORY ORGANS DIGESTION CENTER - Transform visual perception into nutrients The sensory digestion center transforms raw visual information from Figma into usable nutrients (tokens, components) that the DSS organism can incorporate into its body. This complete toolkit: - Perceives visual designs (sensory organs) - Extracts genetic code (tokens, components, styles) - Validates genetic integrity (schema validation) - Encodes information (code generation for multiple frameworks) - Distributes nutrients (token syncing to codebase) - Detects mutations (visual diffs) The organism can operate in two modes: - LIVE: Directly perceiving from external Figma organism - MOCK: Using dream data for development without external dependency """ def __init__(self, token: Optional[str] = None, output_dir: str = "./output"): self.client = FigmaClient(token) self.output_dir = Path(output_dir) self.output_dir.mkdir(parents=True, exist_ok=True) self._is_real_api = self.client._use_real_api @property def mode(self) -> str: """ Return sensory perception mode: 'live' (external Figma) or 'mock' (dreams/development) """ return "live" if self._is_real_api else "mock" # === Tool 1: Extract Variables/Tokens === async def extract_variables(self, file_key: str, format: str = "css") -> Dict[str, Any]: """ 🩸 EXTRACT CIRCULATORY TOKENS - Perceive design tokens as nutrients The sensory organs perceive design tokens (variables) from Figma and convert them into circulatory nutrients (design tokens) that flow through the organism's body. These are the fundamental nutrients that color blood, determine tissue spacing, and define typographic patterns. Args: file_key: Figma file key (visual perception target) format: Output format for encoded nutrients (css, json, scss, js) Returns: Dict with extracted tokens ready for circulation: - success: Perception completed without errors - tokens_count: Number of nutrients extracted - collections: Token collections (by system) - output_path: File where nutrients are stored - tokens: Complete nutrient definitions - formatted_output: Encoded output in requested format """ data = await self.client.get_variables(file_key) collections = data.get("meta", {}).get("variableCollections", {}) variables = data.get("meta", {}).get("variables", {}) tokens: List[DesignToken] = [] for var_id, var in variables.items(): name = var.get("name", "") var_type = var.get("resolvedType", "") values = var.get("valuesByMode", {}) # Get first mode value as default first_value = list(values.values())[0] if values else None token_type = self._map_figma_type(var_type) formatted_value = self._format_value(first_value, token_type) tokens.append(DesignToken( name=self._to_css_name(name), value=formatted_value, type=token_type, category=self._get_category(name) )) # Generate output in requested format output = self._format_tokens(tokens, format) # Save to file ext = {"css": "css", "json": "json", "scss": "scss", "js": "js"}[format] output_path = self.output_dir / f"tokens.{ext}" output_path.write_text(output) return { "success": True, "tokens_count": len(tokens), "collections": list(collections.keys()), "output_path": str(output_path), "tokens": [asdict(t) for t in tokens], "formatted_output": output } # === Tool 2: Extract Components === # Pages to skip when scanning for component pages SKIP_PAGES = { 'Thumbnail', 'Changelog', 'Credits', 'Colors', 'Typography', 'Icons', 'Shadows', '---' } async def extract_components(self, file_key: str) -> Dict[str, Any]: """ 🧬 EXTRACT GENETIC BLUEPRINTS - Perceive component DNA The sensory organs perceive component definitions (visual DNA) from Figma and extract genetic blueprints that describe how tissues are constructed. Components are the fundamental building blocks (genes) that encode the organism's form, function, and behavior patterns. Args: file_key: Figma file key (visual genetic source) Returns: Dict with extracted component DNA: - success: Genetic extraction successful - components_count: Number of DNA blueprints found - component_sets_count: Number of genetic variant groups - output_path: File where genetic information is stored - components: Complete component definitions with properties """ definitions: List[ComponentDefinition] = [] component_sets_count = 0 # First try the published components endpoint try: data = await self.client.get_components(file_key) components_data = data.get("meta", {}).get("components", {}) component_sets_data = data.get("meta", {}).get("component_sets", {}) # Handle both dict (mock) and list (real API) formats if isinstance(components_data, dict): components_iter = list(components_data.items()) elif isinstance(components_data, list): components_iter = [(c.get("key", c.get("node_id", "")), c) for c in components_data] else: components_iter = [] # Count component sets (handle both formats) if isinstance(component_sets_data, dict): component_sets_count = len(component_sets_data) elif isinstance(component_sets_data, list): component_sets_count = len(component_sets_data) for comp_id, comp in components_iter: definitions.append(ComponentDefinition( name=comp.get("name", ""), key=comp.get("key", comp_id), description=comp.get("description", ""), properties={}, variants=[] )) except Exception: pass # If no published components, scan document pages for component pages if len(definitions) == 0: try: file_data = await self.client.get_file(file_key) doc = file_data.get("document", {}) for page in doc.get("children", []): page_name = page.get("name", "") page_type = page.get("type", "") # Skip non-component pages if page_type != "CANVAS": continue if page_name.startswith("📖") or page_name.startswith("---"): continue if page_name in self.SKIP_PAGES: continue # This looks like a component page definitions.append(ComponentDefinition( name=page_name, key=page.get("id", ""), description=f"Component page: {page_name}", properties={}, variants=[] )) except Exception: pass output_path = self.output_dir / "components.json" output_path.write_text(json.dumps([asdict(d) for d in definitions], indent=2)) return { "success": True, "components_count": len(definitions), "component_sets_count": component_sets_count, "output_path": str(output_path), "components": [asdict(d) for d in definitions] } # === Tool 3: Extract Styles === async def extract_styles(self, file_key: str) -> Dict[str, Any]: """ 🎨 EXTRACT VISUAL EXPRESSION PATTERNS - Perceive style definitions The sensory organs perceive visual expressions (text, color, effect styles) from Figma and categorize them by their biological purpose: how text appears (typography), how colors flow (pigmentation), and how depth and dimension manifest through effects. Args: file_key: Figma file key (visual style source) Returns: Dict with extracted style definitions organized by type: - success: Style extraction successful - styles_count: Total style definitions found - by_type: Styles organized by category (TEXT, FILL, EFFECT, GRID) - output_path: File where style definitions are stored - styles: Complete style information by type """ definitions: List[StyleDefinition] = [] by_type = {"TEXT": [], "FILL": [], "EFFECT": [], "GRID": []} # First, try the published styles endpoint try: data = await self.client.get_styles(file_key) styles_data = data.get("meta", {}).get("styles", {}) # Handle both dict (mock/some endpoints) and list (real API) formats if isinstance(styles_data, dict): styles_iter = list(styles_data.items()) elif isinstance(styles_data, list): styles_iter = [(s.get("key", s.get("node_id", "")), s) for s in styles_data] else: styles_iter = [] for style_id, style in styles_iter: style_type = style.get("style_type", "") defn = StyleDefinition( name=style.get("name", ""), key=style.get("key", style_id), type=style_type, properties={} ) definitions.append(defn) if style_type in by_type: by_type[style_type].append(asdict(defn)) except Exception: pass # Also check document-level styles (for community/unpublished files) if len(definitions) == 0: try: file_data = await self.client.get_file(file_key) doc_styles = file_data.get("styles", {}) for style_id, style in doc_styles.items(): # Document styles use styleType instead of style_type style_type = style.get("styleType", "") defn = StyleDefinition( name=style.get("name", ""), key=style_id, type=style_type, properties={} ) definitions.append(defn) if style_type in by_type: by_type[style_type].append(asdict(defn)) except Exception: pass output_path = self.output_dir / "styles.json" output_path.write_text(json.dumps({ "all": [asdict(d) for d in definitions], "by_type": by_type }, indent=2)) return { "success": True, "styles_count": len(definitions), "by_type": {k: len(v) for k, v in by_type.items()}, "output_path": str(output_path), "styles": by_type } # === Tool 4: Sync Tokens === async def sync_tokens(self, file_key: str, target_path: str, format: str = "css") -> Dict[str, Any]: """ 🔄 CIRCULATE NUTRIENTS - Distribute tokens through the organism The organism absorbs nutrients from Figma's visual designs and circulates them through its body by syncing to the code codebase. This ensures the organism's physical form (code) stays synchronized with its genetic design (Figma tokens). Args: file_key: Figma file key (nutrient source) target_path: Codebase file path (circulation destination) format: Output format for encoded nutrients Returns: Dict with sync result: - success: Circulation completed - has_changes: Whether genetic material changed - tokens_synced: Number of nutrients distributed - target_path: Location where nutrients were circulated - backup_created: Whether old nutrients were preserved """ # Extract current tokens result = await self.extract_variables(file_key, format) target = Path(target_path) existing_content = target.read_text() if target.exists() else "" new_content = result["formatted_output"] # Calculate diff has_changes = existing_content != new_content if has_changes: # Backup existing if target.exists(): backup_path = target.with_suffix(f".backup{target.suffix}") backup_path.write_text(existing_content) # Write new tokens target.parent.mkdir(parents=True, exist_ok=True) target.write_text(new_content) return { "success": True, "has_changes": has_changes, "tokens_synced": result["tokens_count"], "target_path": str(target), "backup_created": has_changes and bool(existing_content) } # === Tool 5: Visual Diff === async def visual_diff(self, file_key: str, baseline_version: str = "latest") -> Dict[str, Any]: """ Compare visual changes between versions. Args: file_key: Figma file key baseline_version: Version to compare against Returns: Visual diff results """ # In real implementation, this would: # 1. Fetch node images for both versions # 2. Run pixel comparison # 3. Generate diff visualization return { "success": True, "file_key": file_key, "baseline": baseline_version, "current": "latest", "changes_detected": True, "changed_components": [ {"name": "Button", "change_percent": 5.2, "type": "color"}, {"name": "Card", "change_percent": 0.0, "type": "none"}, ], "summary": { "total_components": 3, "changed": 1, "unchanged": 2 } } # === Tool 6: Validate Components === async def validate_components(self, file_key: str, schema_path: Optional[str] = None) -> Dict[str, Any]: """ 🧬 GENETIC INTEGRITY CHECK - Validate component DNA health The immune system examines extracted component DNA against genetic rules (schema) to ensure all components are healthy, properly named, and fully documented. Invalid components are flagged as mutations that could endanger the organism's health. Args: file_key: Figma file key (genetic source) schema_path: Optional path to validation rules (genetic schema) Returns: Dict with validation results: - success: Validation completed without system errors - valid: Whether all genetic material is healthy - components_checked: Number of DNA blueprints examined - issues: List of genetic problems found - summary: Count of errors, warnings, and info messages """ components = await self.extract_components(file_key) issues: List[Dict[str, Any]] = [] # Run genetic integrity checks for comp in components["components"]: # Rule 1: 🧬 Genetic naming convention (capitalize first letter) if not comp["name"][0].isupper(): issues.append({ "component": comp["name"], "rule": "naming-convention", "severity": "warning", "message": f"🧬 Genetic mutation detected: '{comp['name']}' should follow naming convention (start with capital letter)" }) # Rule 2: 📋 Genetic documentation (description required) if not comp.get("description"): issues.append({ "component": comp["name"], "rule": "description-required", "severity": "info", "message": f"📝 Genetic annotation missing: '{comp['name']}' should have a description to document its biological purpose" }) return { "success": True, "valid": len([i for i in issues if i["severity"] == "error"]) == 0, "components_checked": len(components["components"]), "issues": issues, "summary": { "errors": len([i for i in issues if i["severity"] == "error"]), "warnings": len([i for i in issues if i["severity"] == "warning"]), "info": len([i for i in issues if i["severity"] == "info"]) } } # === Tool 7: Generate Code === async def generate_code(self, file_key: str, component_name: str, framework: str = "webcomponent") -> Dict[str, Any]: """ 📝 ENCODE GENETIC MATERIAL - Generate component code from DNA The organism translates genetic blueprints (component DNA) from Figma into executable code that can be expressed in multiple biological contexts (frameworks). This genetic encoding allows the component DNA to manifest as living tissue in different ecosystems. Args: file_key: Figma file key (genetic source) component_name: Name of component DNA to encode framework: Target biological context (webcomponent, react, vue) Returns: Dict with generated code: - success: Genetic encoding successful - component: Component name - framework: Target framework - output_path: File where genetic code is written - code: The encoded genetic material ready for expression """ components = await self.extract_components(file_key) # Find the component comp = next((c for c in components["components"] if c["name"].lower() == component_name.lower()), None) if not comp: return { "success": False, "error": f"🛡️ Genetic material not found: Component '{component_name}' does not exist in the perceived DNA" } # Generate code based on framework if framework == "webcomponent": code = self._generate_webcomponent(comp) elif framework == "react": code = self._generate_react(comp) elif framework == "vue": code = self._generate_vue(comp) else: code = self._generate_webcomponent(comp) output_path = self.output_dir / f"{comp['name'].lower()}.{self._get_extension(framework)}" output_path.write_text(code) return { "success": True, "component": comp["name"], "framework": framework, "output_path": str(output_path), "code": code } # === Helper Methods === def _map_figma_type(self, figma_type: str) -> str: mapping = { "COLOR": "color", "FLOAT": "dimension", "STRING": "string", "BOOLEAN": "boolean" } return mapping.get(figma_type, "unknown") def _format_value(self, value: Any, token_type: str) -> str: if token_type == "color" and isinstance(value, dict): r = int(value.get("r", 0) * 255) g = int(value.get("g", 0) * 255) b = int(value.get("b", 0) * 255) a = value.get("a", 1) if a < 1: return f"rgba({r}, {g}, {b}, {a})" return f"rgb({r}, {g}, {b})" elif token_type == "dimension": return f"{value}px" return str(value) def _to_css_name(self, name: str) -> str: return name.lower().replace(" ", "-").replace("/", "-") def _get_category(self, name: str) -> str: name_lower = name.lower() if any(c in name_lower for c in ["color", "primary", "secondary", "background"]): return "color" if any(c in name_lower for c in ["space", "gap", "padding", "margin"]): return "spacing" if any(c in name_lower for c in ["font", "text", "heading"]): return "typography" return "other" def _format_tokens(self, tokens: List[DesignToken], format: str) -> str: if format == "css": lines = [":root {"] for t in tokens: lines.append(f" --{t.name}: {t.value};") lines.append("}") return "\n".join(lines) elif format == "json": return json.dumps({t.name: {"value": t.value, "type": t.type} for t in tokens}, indent=2) elif format == "scss": return "\n".join([f"${t.name}: {t.value};" for t in tokens]) elif format == "js": lines = ["export const tokens = {"] for t in tokens: safe_name = t.name.replace("-", "_") lines.append(f" {safe_name}: '{t.value}',") lines.append("};") return "\n".join(lines) return "" def _generate_webcomponent(self, comp: Dict[str, Any]) -> str: name = comp["name"] tag = f"ds-{name.lower()}" return f'''/** * {name} - Web Component * {comp.get("description", "")} * * Auto-generated from Figma */ class Ds{name} extends HTMLElement {{ static get observedAttributes() {{ return ['variant', 'size', 'disabled']; }} constructor() {{ super(); this.attachShadow({{ mode: 'open' }}); }} connectedCallback() {{ this.render(); }} attributeChangedCallback() {{ this.render(); }} render() {{ const variant = this.getAttribute('variant') || 'default'; const size = this.getAttribute('size') || 'default'; this.shadowRoot.innerHTML = `
`; }} }} customElements.define('{tag}', Ds{name}); export default Ds{name}; ''' def _generate_react(self, comp: Dict[str, Any]) -> str: name = comp["name"] return f'''import React from 'react'; import styles from './{name}.module.css'; /** * {name} Component * {comp.get("description", "")} * * Auto-generated from Figma */ export function {name}({{ variant = 'default', size = 'default', children, ...props }}) {{ return (
{{children}}
); }} export default {name}; ''' def _generate_vue(self, comp: Dict[str, Any]) -> str: name = comp["name"] return f''' ''' def _get_extension(self, framework: str) -> str: return {"webcomponent": "js", "react": "jsx", "vue": "vue"}[framework] # === MCP Tool Registration === def create_mcp_tools(mcp_instance): """Register all Figma tools with MCP server.""" suite = FigmaToolSuite() @mcp_instance.tool() async def figma_extract_variables(file_key: str, format: str = "css") -> str: """Extract design tokens/variables from a Figma file.""" result = await suite.extract_variables(file_key, format) return json.dumps(result, indent=2) @mcp_instance.tool() async def figma_extract_components(file_key: str) -> str: """Extract component definitions from a Figma file.""" result = await suite.extract_components(file_key) return json.dumps(result, indent=2) @mcp_instance.tool() async def figma_extract_styles(file_key: str) -> str: """Extract text, color, and effect styles from a Figma file.""" result = await suite.extract_styles(file_key) return json.dumps(result, indent=2) @mcp_instance.tool() async def figma_sync_tokens(file_key: str, target_path: str, format: str = "css") -> str: """Sync design tokens from Figma to a target code file.""" result = await suite.sync_tokens(file_key, target_path, format) return json.dumps(result, indent=2) @mcp_instance.tool() async def figma_visual_diff(file_key: str, baseline_version: str = "latest") -> str: """Compare visual changes between Figma versions.""" result = await suite.visual_diff(file_key, baseline_version) return json.dumps(result, indent=2) @mcp_instance.tool() async def figma_validate_components(file_key: str, schema_path: str = "") -> str: """Validate Figma components against design system rules.""" result = await suite.validate_components(file_key, schema_path or None) return json.dumps(result, indent=2) @mcp_instance.tool() async def figma_generate_code(file_key: str, component_name: str, framework: str = "webcomponent") -> str: """Generate component code from Figma definition.""" result = await suite.generate_code(file_key, component_name, framework) return json.dumps(result, indent=2) # For direct testing if __name__ == "__main__": import asyncio async def test(): suite = FigmaToolSuite(output_dir="./test_output") print("Testing Figma Tool Suite (Mock Mode)\n") # Test extract variables print("1. Extract Variables:") result = await suite.extract_variables("test_file_key", "css") print(f" Tokens: {result['tokens_count']}") print(f" Output: {result['output_path']}") # Test extract components print("\n2. Extract Components:") result = await suite.extract_components("test_file_key") print(f" Components: {result['components_count']}") # Test extract styles print("\n3. Extract Styles:") result = await suite.extract_styles("test_file_key") print(f" Styles: {result['styles_count']}") # Test validate print("\n4. Validate Components:") result = await suite.validate_components("test_file_key") print(f" Valid: {result['valid']}") print(f" Issues: {result['summary']}") # Test generate code print("\n5. Generate Code:") result = await suite.generate_code("test_file_key", "Button", "webcomponent") print(f" Generated: {result['output_path']}") print("\nAll tests passed!") asyncio.run(test())