diff --git a/README.ko.md b/README.ko.md index 104234a..5637d40 100644 --- a/README.ko.md +++ b/README.ko.md @@ -203,12 +203,13 @@ unity-cli console --clear 가장 강력한 명령어입니다. Unity Editor 런타임에서 임의의 C# 코드를 실행합니다. UnityEngine, UnityEditor, ECS 및 로드된 모든 어셈블리에 접근 가능합니다. 일회성 조회나 수정을 위해 커스텀 도구를 만들 필요가 없습니다. -`return`으로 결과를 받습니다. 주요 namespace는 기본 포함. 프로젝트 전용 타입(예: `Unity.Entities`)만 `--usings`로 추가합니다. +`return`으로 결과를 받습니다. 주요 namespace는 기본 포함. 프로젝트 전용 타입(예: `Unity.Entities`)만 `--usings`로 추가합니다. `--usings`는 콤마로 여러 namespace를 받을 수 있고 반복 지정도 가능합니다. ```bash unity-cli exec "return Application.dataPath;" unity-cli exec "return EditorSceneManager.GetActiveScene().name;" unity-cli exec "return World.All.Count;" --usings Unity.Entities +unity-cli exec "return World.All.Count;" --usings Unity.Entities --usings Unity.Mathematics # stdin으로 파이프하면 shell escaping 문제 없음 echo 'Debug.Log("hello"); return null;' | unity-cli exec diff --git a/README.md b/README.md index 8a3b920..17533af 100644 --- a/README.md +++ b/README.md @@ -203,12 +203,13 @@ unity-cli console --clear Run arbitrary C# code inside the Unity Editor at runtime. This is the most powerful command — it gives you full access to UnityEngine, UnityEditor, ECS, and every loaded assembly. No need to write a custom tool for one-off queries or mutations. -Use `return` to get output. Common namespaces are included by default. Add `--usings` only for project-specific types (e.g. `Unity.Entities`). The csc compiler and dotnet runtime are auto-detected; if detection fails, specify manually with `--csc ` or `--dotnet `. +Use `return` to get output. Common namespaces are included by default. Add `--usings` only for project-specific types (e.g. `Unity.Entities`). `--usings` accepts comma-separated namespaces and can be repeated. The csc compiler and dotnet runtime are auto-detected; if detection fails, specify manually with `--csc ` or `--dotnet `. ```bash unity-cli exec "return Application.dataPath;" unity-cli exec "return EditorSceneManager.GetActiveScene().name;" unity-cli exec "return World.All.Count;" --usings Unity.Entities +unity-cli exec "return World.All.Count;" --usings Unity.Entities --usings Unity.Mathematics # Pipe via stdin to avoid shell escaping issues echo 'Debug.Log("hello"); return null;' | unity-cli exec diff --git a/cmd/root.go b/cmd/root.go index f825c10..1e83b68 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -333,34 +333,41 @@ func buildParams(args []string, base map[string]interface{}) (map[string]interfa } var positional []string - flags := map[string]string{} + flags := map[string][]string{} for i := 0; i < len(args); i++ { a := args[i] if strings.HasPrefix(a, "--") { key := a[2:] if i+1 < len(args) && !strings.HasPrefix(args[i+1], "--") { - flags[key] = args[i+1] + flags[key] = append(flags[key], args[i+1]) i++ } else { - flags[key] = "true" + flags[key] = append(flags[key], "true") } } else { positional = append(positional, a) } } - if raw, ok := flags["params"]; ok { + if values, ok := flags["params"]; ok && len(values) > 0 { + raw := values[len(values)-1] if jsonErr := json.Unmarshal([]byte(raw), ¶ms); jsonErr != nil { return nil, fmt.Errorf("invalid JSON in --params: %w", jsonErr) } } - for k, v := range flags { + for k, values := range flags { if k == "params" { continue } if _, exists := params[k]; exists { continue } + if k == "usings" { + params[k] = normalizeUsings(values) + continue + } + + v := values[len(values)-1] if n, err := strconv.Atoi(v); err == nil { params[k] = n } else if v == "true" { @@ -379,6 +386,22 @@ func buildParams(args []string, base map[string]interface{}) (map[string]interfa return params, nil } +func normalizeUsings(values []string) []string { + var result []string + seen := map[string]bool{} + for _, value := range values { + for _, item := range strings.Split(value, ",") { + using := strings.TrimSpace(item) + if using == "" || seen[using] { + continue + } + seen[using] = true + result = append(result, using) + } + } + return result +} + func rejectRemovedFlags(args []string) error { for _, arg := range args { if arg == "--port" || strings.HasPrefix(arg, "--port=") { @@ -450,7 +473,7 @@ Console: Execute C#: exec "" Run C# code in Unity (return required for output) echo '' | exec Pipe code via stdin (avoids shell escaping) - exec "" --usings x,y Add extra using directives + exec "" --usings x,y Add extra using directives (repeatable) Examples: exec "Time.time" @@ -573,7 +596,7 @@ UnityEditor, and all loaded assemblies. Use 'return' to get output. Add --usings for types outside default namespaces. Options: - --usings Add extra using directives + --usings Add extra using directives (comma-separated or repeatable) --csc Path to csc compiler (csc.dll or csc.exe). Auto-detected if omitted. --dotnet Path to dotnet runtime. Auto-detected if omitted. @@ -588,10 +611,11 @@ Examples: echo 'return EditorSceneManager.GetActiveScene().name;' | unity-cli exec echo 'Debug.Log("hello"); return null;' | unity-cli exec unity-cli exec "return World.All.Count;" --usings Unity.Entities + unity-cli exec "return World.All.Count;" --usings Unity.Entities --usings Unity.Mathematics Stdin: Pipe code via stdin to avoid shell escaping issues. - echo '' | unity-cli exec [--usings ns1,ns2] + echo '' | unity-cli exec [--usings ns1,ns2] [--usings ns3] Notes: - Use 'return' for output, 'return null;' for void operations diff --git a/cmd/root_test.go b/cmd/root_test.go index 94822a7..5670474 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -605,6 +605,31 @@ func TestBuildParams_StringParsing(t *testing.T) { } } +func TestBuildParams_UsingsParsing(t *testing.T) { + p, err := buildParams([]string{ + "--usings", "UnityEditor.Build.Profile, Accelix.Editor.Tools.BuildManager", + "--usings", "Unity.Entities", + "--usings", "Unity.Entities", + }, nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + got, ok := p["usings"].([]string) + if !ok { + t.Fatalf("expected usings []string, got %T: %v", p["usings"], p["usings"]) + } + + want := []string{ + "UnityEditor.Build.Profile", + "Accelix.Editor.Tools.BuildManager", + "Unity.Entities", + } + if !sliceEqual(got, want) { + t.Errorf("expected usings=%v, got %v", want, got) + } +} + func TestBuildParams_BaseParams(t *testing.T) { p, err := buildParams([]string{"--depth", "5"}, map[string]interface{}{"action": "hierarchy"}) if err != nil { diff --git a/internal/client/client.go b/internal/client/client.go index d279763..ee5280d 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -178,7 +178,13 @@ func DiscoverInstance(project string) (*Instance, error) { } func normalizeProjectPath(path string) string { - normalized := strings.TrimRight(filepath.ToSlash(path), "/") + normalized := filepath.Clean(path) + if filepath.IsAbs(normalized) { + if resolved, err := filepath.EvalSymlinks(normalized); err == nil { + normalized = resolved + } + } + normalized = strings.TrimRight(filepath.ToSlash(normalized), "/") if runtime.GOOS == "windows" { normalized = strings.ToLower(normalized) } diff --git a/unity-connector/Editor/Tools/ExecuteCsharp.cs b/unity-connector/Editor/Tools/ExecuteCsharp.cs index 513736d..665f891 100644 --- a/unity-connector/Editor/Tools/ExecuteCsharp.cs +++ b/unity-connector/Editor/Tools/ExecuteCsharp.cs @@ -35,7 +35,7 @@ public class Parameters [ToolParameter("C# code to execute. Use 'return' for output.", Required = true)] public string Code { get; set; } - [ToolParameter("Additional using directives (comma-separated, e.g. Unity.Entities,Unity.Mathematics)")] + [ToolParameter("Additional using directives (comma-separated or repeated CLI --usings, e.g. Unity.Entities,Unity.Mathematics)")] public string[] Usings { get; set; } [ToolParameter("Path to csc compiler (csc.dll or csc.exe). Auto-detected if omitted.")]