"""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}")