"""Shader CLI commands for managing Unity shaders.""" import sys import click from typing import Optional from cli.utils.config import get_config from cli.utils.output import format_output, print_error, print_success from cli.utils.connection import run_command, handle_unity_errors from cli.utils.confirmation import confirm_destructive_action @click.group() def shader(): """Shader operations - create, read, update, delete shaders.""" pass @shader.command("read") @click.argument("path") @handle_unity_errors def read_shader(path: str): """Read a shader file. \\b Examples: unity-mcp shader read "Assets/Shaders/MyShader.shader" """ config = get_config() # Extract name from path import os name = os.path.splitext(os.path.basename(path))[0] directory = os.path.dirname(path) result = run_command("manage_shader", { "action": "read", "name": name, "path": directory or "Assets/", }, config) # If successful, display the contents nicely if result.get("success") and result.get("data", {}).get("contents"): click.echo(result["data"]["contents"]) else: click.echo(format_output(result, config.format)) @shader.command("create") @click.argument("name") @click.option( "--path", "-p", default="Assets/Shaders", help="Directory to create shader in." ) @click.option( "--contents", "-c", default=None, help="Shader code (reads from stdin if not provided)." ) @click.option( "--file", "-f", "file_path", default=None, type=click.Path(exists=True), help="Read shader code from file." ) @handle_unity_errors def create_shader(name: str, path: str, contents: Optional[str], file_path: Optional[str]): """Create a new shader. \\b Examples: unity-mcp shader create "MyShader" --path "Assets/Shaders" unity-mcp shader create "MyShader" --file local_shader.shader echo "Shader code..." | unity-mcp shader create "MyShader" """ config = get_config() # Get contents from file, option, or stdin if file_path: with open(file_path, 'r') as f: shader_contents = f.read() elif contents: shader_contents = contents else: # Read from stdin if available import sys if not sys.stdin.isatty(): shader_contents = sys.stdin.read() else: # Provide default shader template shader_contents = f'''Shader "Custom/{name}" {{ Properties {{ _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {{}} }} SubShader {{ Tags {{ "RenderType"="Opaque" }} LOD 200 CGPROGRAM #pragma surface surf Standard fullforwardshadows #pragma target 3.0 sampler2D _MainTex; fixed4 _Color; struct Input {{ float2 uv_MainTex; }}; void surf (Input IN, inout SurfaceOutputStandard o) {{ fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Alpha = c.a; }} ENDCG }} FallBack "Diffuse" }} ''' result = run_command("manage_shader", { "action": "create", "name": name, "path": path, "contents": shader_contents, }, config) click.echo(format_output(result, config.format)) if result.get("success"): print_success(f"Created shader: {path}/{name}.shader") @shader.command("update") @click.argument("path") @click.option( "--contents", "-c", default=None, help="New shader code." ) @click.option( "--file", "-f", "file_path", default=None, type=click.Path(exists=True), help="Read shader code from file." ) @handle_unity_errors def update_shader(path: str, contents: Optional[str], file_path: Optional[str]): """Update an existing shader. \\b Examples: unity-mcp shader update "Assets/Shaders/MyShader.shader" --file updated.shader echo "New shader code" | unity-mcp shader update "Assets/Shaders/MyShader.shader" """ config = get_config() import os name = os.path.splitext(os.path.basename(path))[0] directory = os.path.dirname(path) # Get contents from file, option, or stdin if file_path: with open(file_path, 'r') as f: shader_contents = f.read() elif contents: shader_contents = contents else: import sys if not sys.stdin.isatty(): shader_contents = sys.stdin.read() else: print_error( "No shader contents provided. Use --contents, --file, or pipe via stdin.") sys.exit(1) result = run_command("manage_shader", { "action": "update", "name": name, "path": directory or "Assets/", "contents": shader_contents, }, config) click.echo(format_output(result, config.format)) if result.get("success"): print_success(f"Updated shader: {path}") @shader.command("delete") @click.argument("path") @click.option( "--force", "-f", is_flag=True, help="Skip confirmation prompt." ) @handle_unity_errors def delete_shader(path: str, force: bool): """Delete a shader. \\b Examples: unity-mcp shader delete "Assets/Shaders/OldShader.shader" unity-mcp shader delete "Assets/Shaders/OldShader.shader" --force """ config = get_config() confirm_destructive_action("Delete", "shader", path, force) import os name = os.path.splitext(os.path.basename(path))[0] directory = os.path.dirname(path) result = run_command("manage_shader", { "action": "delete", "name": name, "path": directory or "Assets/", }, config) click.echo(format_output(result, config.format)) if result.get("success"): print_success(f"Deleted shader: {path}")