From ebe1fe895458cd74f375ecfe8645e1ab436b26a5 Mon Sep 17 00:00:00 2001 From: yhc5091 Date: Tue, 18 Aug 2026 19:57:10 +0900 Subject: [PATCH 1/2] feat(bridge): add Grok Build as an AI-agent skill target Unity CLI Manager can now install the skill into .grok/skills for Grok Build, next to Claude Code and Codex. Target directory and whether to copy agents/ live in one layout so a new agent is one row instead of two switches. Also fixes leftover README text that still said the package pins Recorder, makes the overwrite dialog English, syncs user-facing execute/force/qa rules into the shipped SKILL.md, and adds SkillTemplateReferenceSyncTests so the two references/ trees cannot drift. --- CHANGELOG.md | 10 +++++ CLAUDE.md | 6 +-- README.md | 8 ++-- docs/architecture.md | 4 +- .../SkillTemplateReferenceSyncTests.cs | 32 +++++++++++++++ .../references/qa-testing.md | 16 ++++---- .../com.yhc509.unity-cli-bridge/CHANGELOG.md | 10 +++++ .../Editor/CliInstallerWindow.cs | 2 +- .../Editor/SkillInstaller.cs | 39 ++++++++++++------- .../com.yhc509.unity-cli-bridge/README.md | 6 +-- .../SkillTemplates~/SKILL.md | 23 +++++++++-- .../SkillTemplates~/references/qa-testing.md | 16 ++++---- .../Tests~/Editor/SkillInstallerTests.cs | 12 ++++++ 13 files changed, 139 insertions(+), 45 deletions(-) create mode 100644 tests/UnityCli.Cli.Tests/SkillTemplateReferenceSyncTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 17d2497..e336ed6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] +### Added +- **Window > Unity CLI Manager** can now install the AI Agent Skill for Grok Build, next to Claude Code and Codex. Project scope writes to `/.grok/skills/`; global scope writes to `~/.grok/skills/`. + +### Changed +- The installed AI-agent skill now carries the same execute timeout, force-gate, backup, dirty-scene, and `qa --button right` rules as the maintainer copy. Reinstall the skill from **Window > Unity CLI Manager** to pick them up. QA examples in the skill now use `--output compact` and the default JPEG screenshot instead of a full-resolution PNG. + +### Fixed +- The README still said this package pins Unity Recorder `5.1.6`. Recorder is optional and is not installed with the package — add it yourself if you record. On Unity `6000.4` and newer, use Recorder `5.1.6` or later. +- The **Overwrite Skill?** confirmation in **Window > Unity CLI Manager** is now in English, matching the rest of the window. + ## [0.5.3] - 2026-08-15 ### Added diff --git a/CLAUDE.md b/CLAUDE.md index 0ce83e5..4530ab6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -83,7 +83,7 @@ Tests live in `tests/UnityCli.Cli.Tests/` (xUnit, `.NET`-testable surface only). - **Headless editors are first-class:** the bridge starts in any main editor process (GUI or `-batchmode`); only secondary Unity processes (MPE / `-adb2` AssetImportWorker) are excluded. Recommended headless launch is `-batchmode` *without* `-nographics` so the GPU stays available. Commands whose catalog entry sets `requiresGraphics` fail with `HEADLESS_NO_GRAPHICS` when `SystemInfo.graphicsDeviceType == Null`. - **`editor launch` / `editor stop`:** `editor launch` is a local command — pre-flight (live registry match → idempotent reuse; stray editor process → `EDITOR_ALREADY_RUNNING_CONFLICT`) then spawn + registry-readiness polling (default 300 s). The spawned editor's stdio is detached (Unix: `sh -c 'exec …'` wrapper redirecting to the null device, so `Process.Id` stays the editor PID) — never let it inherit the CLI's streams, or `editor launch | grep …` pipelines hang forever after the CLI exits. `editor stop` is the `editor-quit` wire command (ForceRule OnDestructiveOp): the bridge replies first and schedules `EditorApplication.Exit` via an `EditorApplication.update` one-shot callback (+0.5 s grace), then the CLI waits for PID exit (default 30 s). The deferred quit must stay `update`-based, not `delayCall` — `delayCall` rides the inspector-update cycle and starves in an unfocused GUI editor, timing out every stop. A graceful quit removes the registry entry and token sidecar. The CLI is installed per version under `~/.unity-cli-bridge/versions//` (binary + `meta.json` = `{"cliVersion","protocolVersion"}`), and `~/.unity-cli-bridge/unity-cli/` stays the PATH target — a symlink to the newest version on macOS/Linux, a copy on Windows, plus a `meta.json` marker naming the version it resolves to. `CliInstallLayout` (`Runtime/Protocol/`) owns that layout for both sides. On a `PROTOCOL_MISMATCH` the CLI reads the bridge's protocol off `response.protocolVersion`, finds the newest installed version speaking it, and re-execs with the original argv (`execve` on macOS/Linux, child process on Windows) with `UNITY_CLI_DISPATCHED=1` set; if that guard is already set it reports the mismatch instead of dispatching again. This works because the bridge checks the protocol before auth and before dispatch and `return`s, so nothing ran and re-sending cannot double-execute — do not move that check. `LocalIpcClient.EnsureCompatibleResponse` must keep the *peer's* `protocolVersion` on the envelope it synthesizes; that field is what routing depends on. Local-only commands (`status`, `instances`, `doctor`) never dispatch. Happy path pays nothing: the decision short-circuits before touching disk. - **CLI install target = package version:** The Manager downloads the CLI release matching *its own package version*, never the newest release. Only the same-version CLI is guaranteed to speak the package's protocol, and the Manager writes that protocol into `meta.json` from `ProtocolConstants.ProtocolVersion`. Old versions are never garbage-collected; removal is a per-version button in the Manager. -- **AI Agent Skill install scope:** Default to project-scoped installs under `/.claude/skills/` or `/.codex/skills/`; global installs remain available, can shadow project copies, and can be updated or removed from the manager. +- **AI Agent Skill install scope:** Default to project-scoped installs under `/.claude/skills/`, `/.codex/skills/`, or `/.grok/skills/`; global installs remain available, can shadow project copies, and can be updated or removed from the manager. - **Scene paths:** Format `/Root[0]/Child[0]` with array notation for sibling indexing; `/` is the virtual scene root. - **Scene/prefab node flags:** Convenience commands that point at a hierarchy node use `--node`; JSON patch specs still use `target`/`parent`. - **Prefab editing:** Based on `SerializedProperty.propertyPath` (run `prefab inspect --with-values` to verify paths before patching). @@ -103,8 +103,8 @@ Tests live in `tests/UnityCli.Cli.Tests/` (xUnit, `.NET`-testable surface only). 1. `dotnet run --project cli/UnityCli.DocGen -- --write` — auto-updates `docs/cli-reference.md` 2. `README.md` — update examples for new/changed commands in both Scene and Prefab sections 3. `CLAUDE.md` — update Architecture tree if new files are added, update Key Conventions if behavior changes - 4. **The AI-agent skill ships in two copies — update both.** `tools/skills/unity-cli-operator/` is the maintainer copy; `unity-package/com.yhc509.unity-cli-bridge/SkillTemplates~/` is what `SkillInstaller` actually installs into a user's project, and nothing in CI compares them. - - `references/*.md` must stay **byte-identical** between the two (`diff -rq` on the two `references/` folders reports nothing). + 4. **The AI-agent skill ships in two copies — update both.** `tools/skills/unity-cli-operator/` is the maintainer copy; `unity-package/com.yhc509.unity-cli-bridge/SkillTemplates~/` is what `SkillInstaller` actually installs into a user's project. + - `references/*.md` must stay identical between the two. `SkillTemplateReferenceSyncTests` fails if they drift. - `SKILL.md` is a deliberate fork: the shipped copy drops maintainer-only content (`## 진입 규칙`). Sync it *where the content applies to end users* — do not make the two identical. 5. `dotnet run --project cli/UnityCli.DocGen -- --check` — verify cli-reference is up to date - **Release checklist:** Cutting a new version: diff --git a/README.md b/README.md index 656c300..7f4845f 100644 --- a/README.md +++ b/README.md @@ -99,11 +99,11 @@ Extract and add the binary to your PATH. A manually placed binary carries no ver ### 3. Install AI Agent Skill -In the Unity Editor, open `Window > Unity CLI Manager`. Select your AI tool (Claude Code or Codex), keep **Scope** set to **Project** unless you need a global install, and click **Install Skill**. +In the Unity Editor, open `Window > Unity CLI Manager`. Select your AI tool (Claude Code, Codex, or Grok Build), keep **Scope** set to **Project** unless you need a global install, and click **Install Skill**. Once installed, the same section offers **Remove Skill** for the selected tool and scope. -Project-scoped installs go under `/.claude/skills/` or `/.codex/skills/`. Commit that folder if you want the team to share the same skill version as the Unity package. +Project-scoped installs go under `/.claude/skills/`, `/.codex/skills/`, or `/.grok/skills/`. Commit that folder if you want the team to share the same skill version as the Unity package. The skill teaches AI agents how to pick the right commands, run them safely, and verify results with `read-console`. The installed `SKILL.md` records the package version it came from, so the CLI Manager can tell you when the skill needs an update. @@ -181,7 +181,7 @@ Assign a value to `__pucResult` when the caller needs a structured return value. `record start` captures the Play Mode Game View as mp4 via Unity Recorder. It returns `STARTED` plus a `recordingId` immediately; add `--duration N --wait` to auto-stop and poll until the mp4 is finalized. Without `--duration`, stop manually with `record stop`. Outputs default to `Library/com.yhc509.unity-cli-bridge/recordings/`; pass `--path` to move the finalized mp4. -Recording depends on Unity Recorder. The package requires Unity `2023.1` or newer and pins `com.unity.recorder` `5.1.6`, which includes the compatibility fixes older Recorder releases need for Unity `6000.4` and newer. +Recording depends on Unity Recorder, which this package does not install. Add `com.unity.recorder` if you want `record` or `qa run-sequence --record`. On Unity `6000.4` and newer, use Recorder `5.1.6` or later — older Recorder releases fail to compile there. ### Assets @@ -443,7 +443,7 @@ A disabled bridge constructs nothing and logs one line saying so. The `unity-cli-bridge` skill teaches AI agents how to use the CLI safely: pick the right command, verify with `read-console`, follow inspect-before-patch patterns. -Install from **Window > Unity CLI Manager** in the Unity Editor — select your AI tool and scope, then click **Install Skill**. Once installed, use **Remove Skill** from the same section to remove that scoped copy. Project scope is the default and writes to `/.claude/skills/` or `/.codex/skills/`; global scope writes to the user's agent skills directory. Supports Claude Code and Codex. +Install from **Window > Unity CLI Manager** in the Unity Editor — select your AI tool and scope, then click **Install Skill**. Once installed, use **Remove Skill** from the same section to remove that scoped copy. Project scope is the default and writes to `/.claude/skills/`, `/.codex/skills/`, or `/.grok/skills/`; global scope writes to the user's agent skills directory. Supports Claude Code, Codex, and Grok Build. ## Development diff --git a/docs/architecture.md b/docs/architecture.md index 92fa24e..e8321cd 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -6,7 +6,7 @@ - `cli/`: the .NET CLI that receives user commands and routes them to live IPC - `unity-package/com.yhc509.unity-cli-bridge/`: the bridge package that starts automatically inside the Unity Editor -- `tools/skills/unity-cli-operator/`: the Codex skill that keeps `unity-cli` usage consistent +- `tools/skills/unity-cli-operator/`: the AI-agent skill that keeps `unity-cli` usage consistent ## Runtime Flow @@ -39,7 +39,7 @@ - `cli/UnityCli.Cli`: user-facing CLI - `cli/UnityCli.Protocol`: protocol models shared by the CLI and Unity package - `unity-package/com.yhc509.unity-cli-bridge`: UPM package -- `tools/skills/unity-cli-operator`: Codex skill +- `tools/skills/unity-cli-operator`: AI-agent skill (maintainer copy) ## Editor Bridge Layout diff --git a/tests/UnityCli.Cli.Tests/SkillTemplateReferenceSyncTests.cs b/tests/UnityCli.Cli.Tests/SkillTemplateReferenceSyncTests.cs new file mode 100644 index 0000000..3b83b67 --- /dev/null +++ b/tests/UnityCli.Cli.Tests/SkillTemplateReferenceSyncTests.cs @@ -0,0 +1,32 @@ +using UnityCli.DocGen; + +namespace UnityCli.Cli.Tests; + +public sealed class SkillTemplateReferenceSyncTests +{ + [Fact] + public void MaintainerAndShippedSkillReferences_AreIdentical() + { + string repoRoot = RepositoryPaths.FindRepoRoot(AppContext.BaseDirectory); + string maintainerRoot = Path.Combine(repoRoot, "tools", "skills", "unity-cli-operator", "references"); + string shippedRoot = Path.Combine( + repoRoot, + "unity-package", + "com.yhc509.unity-cli-bridge", + "SkillTemplates~", + "references"); + + Assert.True(Directory.Exists(maintainerRoot), "Maintainer skill references are missing: " + maintainerRoot); + Assert.True(Directory.Exists(shippedRoot), "Shipped skill references are missing: " + shippedRoot); + Assert.Equal(ReadTree(maintainerRoot), ReadTree(shippedRoot)); + } + + private static Dictionary ReadTree(string root) + { + return Directory.GetFiles(root, "*", SearchOption.AllDirectories) + .ToDictionary( + path => Path.GetRelativePath(root, path).Replace('\\', '/'), + path => File.ReadAllText(path).Replace("\r\n", "\n"), + StringComparer.Ordinal); + } +} diff --git a/tools/skills/unity-cli-operator/references/qa-testing.md b/tools/skills/unity-cli-operator/references/qa-testing.md index ce3ad0d..e3bbf00 100644 --- a/tools/skills/unity-cli-operator/references/qa-testing.md +++ b/tools/skills/unity-cli-operator/references/qa-testing.md @@ -157,7 +157,7 @@ ucli screenshot --view game --format png --max-width 0 --path /tmp/qa-check.png ### qa ui-dump Workflow 1. Enter Play Mode and, if you already captured a reference image, keep its `width`/`height` available -2. Dump candidates: `qa ui-dump --limit 30 --interactable-only --omit-rect` -> inspect each element's `path`, `text`, `interactable`, `centerX`, `centerY` +2. Dump candidates: `qa ui-dump --limit 30 --interactable-only --omit-rect --output compact` -> inspect each element's `path`, `text`, `interactable`, `centerX`, `centerY` 3. Prefer path click: `qa click --target ` 4. If a coordinate tap is better, use `qa tap --x --y ` @@ -172,7 +172,7 @@ If the visible label is known, add `--text ` so filtering happens in UI elements come from `qa ui-dump`. For non-UI world objects (units on a battle grid, world-space interactables), the game opts them in: - Implement `UnityCliBridge.Bridge.IQaTappable` on your own component, or attach the `QaTappable` marker component (set `label`, optional `anchor`, optional `onQaTap`). -- `qa world-dump --limit 30` then lists them with `label`, `path`, image-space `centerX`/`centerY`, `onScreen`, `hasAction`; add `--text ` when the label is known. +- `qa world-dump --limit 30 --output compact` then lists them with `label`, `path`, image-space `centerX`/`centerY`, `onScreen`, `hasAction`; add `--text ` when the label is known. - Tap with `qa tap --target `. If the object's `TryQaTap()` handles it (e.g. `onQaTap` is wired), the bridge invokes it directly; otherwise the bridge simulates an Input System tap at the anchor, which reaches games that poll the Input System directly (where `qa tap --x --y`'s EventSystem path does not). - Off-screen objects are hidden unless you pass `--include-offscreen`. - Same-named sibling world objects share a path and resolve to the first match; give tappable objects unique names/labels to target them individually. @@ -335,18 +335,18 @@ The bridge converts automatically when screenshot dimensions are known. ```bash P="$(pwd -P)" -ucli play --project "$P" --json +ucli play --project "$P" --output compact sleep 1 -ucli qa click --target "/Canvas/Button" --project "$P" --json +ucli qa click --target "/Canvas/Button" --project "$P" --output compact # 로그 검증 -ucli read-console --type log --limit 5 --no-stacktrace --project "$P" --json +ucli read-console --type log --limit 5 --no-stacktrace --project "$P" --output compact -# 시각 검증 -ucli screenshot --view game --path /tmp/qa-click.png --project "$P" --json +# 시각 검증 — 기본값이 이미 jpg q75 + 1024px 축소 +ucli screenshot --view game --path /tmp/qa-click.jpg --project "$P" --output compact -ucli stop --project "$P" --json +ucli stop --project "$P" --output compact ``` ## 예시: 조건 대기 후 검증 diff --git a/unity-package/com.yhc509.unity-cli-bridge/CHANGELOG.md b/unity-package/com.yhc509.unity-cli-bridge/CHANGELOG.md index 17d2497..e336ed6 100644 --- a/unity-package/com.yhc509.unity-cli-bridge/CHANGELOG.md +++ b/unity-package/com.yhc509.unity-cli-bridge/CHANGELOG.md @@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] +### Added +- **Window > Unity CLI Manager** can now install the AI Agent Skill for Grok Build, next to Claude Code and Codex. Project scope writes to `/.grok/skills/`; global scope writes to `~/.grok/skills/`. + +### Changed +- The installed AI-agent skill now carries the same execute timeout, force-gate, backup, dirty-scene, and `qa --button right` rules as the maintainer copy. Reinstall the skill from **Window > Unity CLI Manager** to pick them up. QA examples in the skill now use `--output compact` and the default JPEG screenshot instead of a full-resolution PNG. + +### Fixed +- The README still said this package pins Unity Recorder `5.1.6`. Recorder is optional and is not installed with the package — add it yourself if you record. On Unity `6000.4` and newer, use Recorder `5.1.6` or later. +- The **Overwrite Skill?** confirmation in **Window > Unity CLI Manager** is now in English, matching the rest of the window. + ## [0.5.3] - 2026-08-15 ### Added diff --git a/unity-package/com.yhc509.unity-cli-bridge/Editor/CliInstallerWindow.cs b/unity-package/com.yhc509.unity-cli-bridge/Editor/CliInstallerWindow.cs index 1f67fec..a894ffd 100644 --- a/unity-package/com.yhc509.unity-cli-bridge/Editor/CliInstallerWindow.cs +++ b/unity-package/com.yhc509.unity-cli-bridge/Editor/CliInstallerWindow.cs @@ -450,7 +450,7 @@ private void InstallSkill() { bool shouldOverwrite = EditorUtility.DisplayDialog( "Overwrite Skill?", - "기존 스킬이 이미 설치되어 있습니다: " + _skillDestinationPath + "\n덮어쓰시겠습니까?", + "A skill is already installed at:\n" + _skillDestinationPath + "\nOverwrite it?", "Overwrite", "Cancel"); if (!shouldOverwrite) diff --git a/unity-package/com.yhc509.unity-cli-bridge/Editor/SkillInstaller.cs b/unity-package/com.yhc509.unity-cli-bridge/Editor/SkillInstaller.cs index a3d604f..f5b9fa6 100644 --- a/unity-package/com.yhc509.unity-cli-bridge/Editor/SkillInstaller.cs +++ b/unity-package/com.yhc509.unity-cli-bridge/Editor/SkillInstaller.cs @@ -14,6 +14,8 @@ internal enum SkillTarget ClaudeCode = 0, [InspectorName("Codex")] Codex = 1, + [InspectorName("Grok Build")] + GrokBuild = 2, } internal enum SkillScope @@ -36,8 +38,8 @@ internal static class SkillInstaller internal static void Install(SkillTarget target, SkillScope scope) { SkillTemplateInfo templateInfo = GetSkillTemplateInfo(); + SkillTargetLayout layout = GetLayout(target); string destination = GetDestination(target, scope); - bool includeAgents = target == SkillTarget.Codex; if (Directory.Exists(destination)) { @@ -45,7 +47,7 @@ internal static void Install(SkillTarget target, SkillScope scope) } Directory.CreateDirectory(destination); - CopyDirectory(templateInfo.TemplateRoot, destination, includeAgents, templateInfo.PackageVersion); + CopyDirectory(templateInfo.TemplateRoot, destination, layout.IncludeAgents, templateInfo.PackageVersion); Debug.Log($"[SkillInstaller] Installed {target} skill ({scope}) to: {destination}"); } @@ -146,15 +148,7 @@ private static string GetGlobalDestination(SkillTarget target) throw new InvalidOperationException("Failed to resolve user home directory."); } - switch (target) - { - case SkillTarget.ClaudeCode: - return Path.Combine(home, ".claude", "skills", SkillName); - case SkillTarget.Codex: - return Path.Combine(home, ".codex", "skills", SkillName); - default: - throw new ArgumentOutOfRangeException(nameof(target), target, "Unsupported skill target."); - } + return Path.Combine(home, GetLayout(target).ToolDirectoryName, "skills", SkillName); } private static string GetProjectDestination(SkillTarget target) @@ -165,12 +159,19 @@ private static string GetProjectDestination(SkillTarget target) throw new InvalidOperationException("Failed to resolve Unity project root from Application.dataPath: " + Application.dataPath); } + return Path.Combine(projectRoot.FullName, GetLayout(target).ToolDirectoryName, "skills", SkillName); + } + + private static SkillTargetLayout GetLayout(SkillTarget target) + { switch (target) { case SkillTarget.ClaudeCode: - return Path.Combine(projectRoot.FullName, ".claude", "skills", SkillName); + return new SkillTargetLayout(".claude", includeAgents: false); case SkillTarget.Codex: - return Path.Combine(projectRoot.FullName, ".codex", "skills", SkillName); + return new SkillTargetLayout(".codex", includeAgents: true); + case SkillTarget.GrokBuild: + return new SkillTargetLayout(".grok", includeAgents: false); default: throw new ArgumentOutOfRangeException(nameof(target), target, "Unsupported skill target."); } @@ -239,6 +240,18 @@ private static void CopyDirectory(string source, string destination, bool includ } } + private readonly struct SkillTargetLayout + { + internal SkillTargetLayout(string toolDirectoryName, bool includeAgents) + { + ToolDirectoryName = toolDirectoryName; + IncludeAgents = includeAgents; + } + + internal string ToolDirectoryName { get; } + internal bool IncludeAgents { get; } + } + private readonly struct SkillTemplateInfo { internal SkillTemplateInfo(string templateRoot, string packageVersion) diff --git a/unity-package/com.yhc509.unity-cli-bridge/README.md b/unity-package/com.yhc509.unity-cli-bridge/README.md index d230922..ded960b 100644 --- a/unity-package/com.yhc509.unity-cli-bridge/README.md +++ b/unity-package/com.yhc509.unity-cli-bridge/README.md @@ -44,14 +44,14 @@ For Git-based installation, use the package path inside the repository. If you are migrating from the old package, update the dependency key in `Packages/manifest.json` from `com.puc.bridge` to `com.yhc509.unity-cli-bridge`. If your Unity project references the bridge asmdefs directly, also rename `PUC.Editor` / `PUC.Runtime` references to `UnityCliBridge.Bridge.Editor` / `UnityCliBridge.Bridge.Runtime`. -The CLI executable is installed separately from **Window > Unity CLI Manager**. The package includes the AI Agent Skill template, and the same window can install or remove it for Claude Code or Codex. +The CLI executable is installed separately from **Window > Unity CLI Manager**. The package includes the AI Agent Skill template, and the same window can install or remove it for Claude Code, Codex, or Grok Build. -By default, the skill installs into the current Unity project under `/.claude/skills/` or `/.codex/skills/`. Commit that folder if you want teammates to use the same skill version as the package. Choose global scope only when you want one user-wide copy. +By default, the skill installs into the current Unity project under `/.claude/skills/`, `/.codex/skills/`, or `/.grok/skills/`. Commit that folder if you want teammates to use the same skill version as the package. Choose global scope only when you want one user-wide copy. ## Notes - This package includes `Newtonsoft.Json.dll` in `Editor/Plugins` for scene/prefab spec parsing. -- Play Mode recording depends on Unity Recorder. This package requires Unity `2023.1` or newer and pins `com.unity.recorder` `5.1.6`, which includes the compatibility fixes older Recorder releases need for Unity `6000.4` and newer. +- Play Mode recording depends on Unity Recorder, which this package does not install. Add `com.unity.recorder` if you want `record` or `qa run-sequence --record`. On Unity `6000.4` and newer, use Recorder `5.1.6` or later — older Recorder releases fail to compile there. - In Play Mode, `screenshot --view game` uses `ScreenCapture.CaptureScreenshotAsTexture()`. `--width` and `--height` can downscale the native Game View capture, but larger requests log a warning and save the native capture without upscaling. - `input-actions` assets are created as JSON files that Unity's Input System importer reads. - `scene inspect --with-values` is meant to be used as the source of truth when authoring `scene patch` specs. diff --git a/unity-package/com.yhc509.unity-cli-bridge/SkillTemplates~/SKILL.md b/unity-package/com.yhc509.unity-cli-bridge/SkillTemplates~/SKILL.md index 16bb10d..233e479 100644 --- a/unity-package/com.yhc509.unity-cli-bridge/SkillTemplates~/SKILL.md +++ b/unity-package/com.yhc509.unity-cli-bridge/SkillTemplates~/SKILL.md @@ -49,13 +49,29 @@ unity-cli editor stop --project # graceful 종료 (미저장 변 ## Operating Rules - 모든 asset 경로는 `Assets/...` 형식으로 다룬다. 조회 전용(`asset find`, `asset info`)은 `Packages/...`도 허용된다. -- 파괴 연산과 덮어쓰기는 `--force`가 있을 때만 허용된다고 가정한다. +- 파괴 연산과 덮어쓰기는 `--force`가 있을 때만 허용된다고 가정한다. 다음은 항상 force-gated: `asset delete`, `execute`, `package remove`, `scene remove-component`, `prefab remove-component`. 다음은 조건부 force-gated: 기존 경로를 덮어쓰는 `asset move`/`asset rename`/`asset create`/`prefab create`; `scene patch` 안의 `delete-gameobject`/`remove-component`; `prefab patch` 안의 `remove-node`/`remove-component`. +- scene/prefab patch와 asset overwrite는 `Library/com.yhc509.unity-cli-bridge/backups/` 디렉터리에 본체와 `.meta`를 백업한 뒤 실행된다. `BACKUP_RESTORE_FAILED`가 나오면 즉시 중단하고 응답의 백업 경로로 수동 복구를 안내한다. +- `scene patch`는 대상 scene이 이미 dirty이면 `--force`와 무관하게 거부된다. 먼저 저장하거나 변경을 폐기한 뒤 다시 실행한다. +- `execute`/`execute-code`는 임의 C# 실행이므로 항상 `--force`가 필요하다. - `execute --code 'Debug.Log(__pucArgsJson);' --args '{"k":"v"}' --force`로 넘긴 JSON은 사용자 코드에서 `__pucArgsJson` 문자열 변수로 읽는다. -- `execute --args` 사용자 코드에서는 wrapper 예약 prefix인 `__puc_internal_*` 변수를 선언하지 않는다. +- 값을 회수해야 하면 사용자 코드에서 `__pucResult = <값>;`에 담는다. 응답의 `hasResult`가 `true`가 되고 `result`에는 타입 보존 JSON이 들어온다(float는 G9, double은 G17 round-trip 포맷). +- Editor 어셈블리의 `[PucCommand]` custom 명령은 `ExecuteValueSerializer.Serialize(obj)`를 반환할 수 있지만, runtime 어셈블리 명령은 정밀 JSON을 직접 직렬화해야 한다. +- 오래 돌 수 있는 `execute`에는 `--timeout <초>`를 붙인다. 기본 30초, 상한 600초이며 협력적 cancel이라 사용자 코드가 `__pucToken`을 체크해야 멈춘다. +- 반복문/대기 코드 생성 시 다음 패턴을 우선 사용한다: + +```csharp +for (int i = 0; i < workItems.Count; i++) +{ + __pucToken.ThrowIfCancellationRequested(); + // work +} +``` + +- `execute --args` 사용자 코드에서는 wrapper 예약 prefix인 `__puc_internal_*` 변수와 `__pucToken`, `__pucResult` 변수를 선언하지 않는다. - `execute --args` 값에는 secret/credential을 넣지 않는다. CodeDOM 컴파일 중 OS temp에 `.cs` 파일이 잠시 생성될 수 있다. - **LLM이 소비하는 명령에는 `--output compact`를 기본으로 붙인다.** 이유와 예외는 [references/command-flows.md](references/command-flows.md)의 `상태 확인` 절 설명을 따른다. - `scene patch` 전에는 가능하면 `scene inspect --with-values`를 먼저 실행해서 GameObject path와 field 이름을 확인한다. -- `prefab patch` 전에는 가능하면 `prefab inspect --with-values`를 먼저 실행해서 path와 field 이름을 확인한다. +- `prefab patch` 전에는 가능하면 `prefab inspect --with-values`를 먼저 실행해서 path와 field 이름을 확인한다. `remove-node`나 `remove-component` 같은 destructive op가 있으면 `--force`를 붙인다. - inspect 응답이 클 때는 `--max-depth N`으로 깊이를 제한하고 `--omit-defaults`로 기본값을 생략한다. **`--with-values`에는 항상 `--omit-defaults`를 함께 붙여** 0/null/false/빈 값 기본값을 잘라낸다(컴포넌트당 30~55% 절약). - `material info`도 `--omit-defaults`를 지원한다. URP/Lit 기준 48개 속성 → 변경된 것만 반환하여 토큰을 71% 절약한다. - `--omit-defaults` 결과는 read-only이다. patch input으로 그대로 쓰면 생략된 필드가 복원되지 않는다. @@ -69,6 +85,7 @@ unity-cli editor stop --project # graceful 종료 (미저장 변 - **스크린샷은 옵션 없이 그대로 찍으면 된다.** 기본값이 이미 JPEG quality 75 + 1024px 가로 축소라 에이전트가 읽기 좋은 크기로 나온다(1080p PNG 대비 이미지 토큰 ~72% 절약). lossless 원본이 필요할 때만 `--format png --max-width 0`을 붙인다. `--path`가 `.png`로 끝나면 `--format` 없이도 PNG로 저장된다. - Play Mode 영상을 남겨야 하면 `record start --duration N --wait --path /tmp/out.mp4`를 쓴다. 수동 녹화는 `record start` 후 `record status`, `record stop` 순서로 종료한다. `record start`는 Play Mode 전용이고 `com.unity.recorder`가 설치된 프로젝트에서만 동작한다 — 미설치면 `RECORD_FAILED`와 함께 `unity-cli package add --name com.unity.recorder` 안내가 돌아오므로 그대로 설치한 뒤 재시도한다. - `qa tap --x --y`에는 `screenshot`에서 확인한 이미지 좌표를 그대로 넣는다. 응답의 `imageOrigin`은 `top-left`, `coordinateOrigin`은 `bottom-left`다. +- `qa click`, `qa tap`, `qa swipe`는 기본 좌클릭/좌드래그이며, 우클릭 입력 경로를 검증할 때는 `--button right`를 붙인다. - 별도 Y-flip이나 해상도 스케일 변환은 하지 않는다. Bridge가 마지막 `screenshot` 크기 또는 명시한 `--screenshot-width`/`--screenshot-height`를 기준으로 내부 처리한다. - 좌표를 추측하지 말고 탭 대상을 먼저 열거한다: uGUI 버튼은 `qa ui-dump --limit 30 --interactable-only --omit-rect --output compact`, 비-UI 월드 오브젝트(전투 그리드 유닛 등)는 `qa world-dump --limit 30 --output compact`. 찾을 텍스트/라벨을 알면 `--text `으로 서버사이드 필터링한다. 둘 다 `centerX`/`centerY` 이미지 좌표를 그대로 반환하며, 대형 화면 dump에서는 envelope 제거 효과가 특히 크다. - `qa world-dump`는 게임이 opt-in한 오브젝트만 본다: 게임 컴포넌트가 `UnityCliBridge.Bridge.IQaTappable`을 구현하거나 `QaTappable` 마커를 부착해야 한다. 화면 밖은 `--include-offscreen`을 줄 때만 포함된다. diff --git a/unity-package/com.yhc509.unity-cli-bridge/SkillTemplates~/references/qa-testing.md b/unity-package/com.yhc509.unity-cli-bridge/SkillTemplates~/references/qa-testing.md index ce3ad0d..e3bbf00 100644 --- a/unity-package/com.yhc509.unity-cli-bridge/SkillTemplates~/references/qa-testing.md +++ b/unity-package/com.yhc509.unity-cli-bridge/SkillTemplates~/references/qa-testing.md @@ -157,7 +157,7 @@ ucli screenshot --view game --format png --max-width 0 --path /tmp/qa-check.png ### qa ui-dump Workflow 1. Enter Play Mode and, if you already captured a reference image, keep its `width`/`height` available -2. Dump candidates: `qa ui-dump --limit 30 --interactable-only --omit-rect` -> inspect each element's `path`, `text`, `interactable`, `centerX`, `centerY` +2. Dump candidates: `qa ui-dump --limit 30 --interactable-only --omit-rect --output compact` -> inspect each element's `path`, `text`, `interactable`, `centerX`, `centerY` 3. Prefer path click: `qa click --target ` 4. If a coordinate tap is better, use `qa tap --x --y ` @@ -172,7 +172,7 @@ If the visible label is known, add `--text ` so filtering happens in UI elements come from `qa ui-dump`. For non-UI world objects (units on a battle grid, world-space interactables), the game opts them in: - Implement `UnityCliBridge.Bridge.IQaTappable` on your own component, or attach the `QaTappable` marker component (set `label`, optional `anchor`, optional `onQaTap`). -- `qa world-dump --limit 30` then lists them with `label`, `path`, image-space `centerX`/`centerY`, `onScreen`, `hasAction`; add `--text ` when the label is known. +- `qa world-dump --limit 30 --output compact` then lists them with `label`, `path`, image-space `centerX`/`centerY`, `onScreen`, `hasAction`; add `--text ` when the label is known. - Tap with `qa tap --target `. If the object's `TryQaTap()` handles it (e.g. `onQaTap` is wired), the bridge invokes it directly; otherwise the bridge simulates an Input System tap at the anchor, which reaches games that poll the Input System directly (where `qa tap --x --y`'s EventSystem path does not). - Off-screen objects are hidden unless you pass `--include-offscreen`. - Same-named sibling world objects share a path and resolve to the first match; give tappable objects unique names/labels to target them individually. @@ -335,18 +335,18 @@ The bridge converts automatically when screenshot dimensions are known. ```bash P="$(pwd -P)" -ucli play --project "$P" --json +ucli play --project "$P" --output compact sleep 1 -ucli qa click --target "/Canvas/Button" --project "$P" --json +ucli qa click --target "/Canvas/Button" --project "$P" --output compact # 로그 검증 -ucli read-console --type log --limit 5 --no-stacktrace --project "$P" --json +ucli read-console --type log --limit 5 --no-stacktrace --project "$P" --output compact -# 시각 검증 -ucli screenshot --view game --path /tmp/qa-click.png --project "$P" --json +# 시각 검증 — 기본값이 이미 jpg q75 + 1024px 축소 +ucli screenshot --view game --path /tmp/qa-click.jpg --project "$P" --output compact -ucli stop --project "$P" --json +ucli stop --project "$P" --output compact ``` ## 예시: 조건 대기 후 검증 diff --git a/unity-package/com.yhc509.unity-cli-bridge/Tests~/Editor/SkillInstallerTests.cs b/unity-package/com.yhc509.unity-cli-bridge/Tests~/Editor/SkillInstallerTests.cs index 5b295ef..44c612f 100644 --- a/unity-package/com.yhc509.unity-cli-bridge/Tests~/Editor/SkillInstallerTests.cs +++ b/unity-package/com.yhc509.unity-cli-bridge/Tests~/Editor/SkillInstallerTests.cs @@ -10,8 +10,10 @@ public sealed class SkillInstallerTests { [TestCase(SkillTarget.ClaudeCode, SkillScope.Project, ".claude")] [TestCase(SkillTarget.Codex, SkillScope.Project, ".codex")] + [TestCase(SkillTarget.GrokBuild, SkillScope.Project, ".grok")] [TestCase(SkillTarget.ClaudeCode, SkillScope.Global, ".claude")] [TestCase(SkillTarget.Codex, SkillScope.Global, ".codex")] + [TestCase(SkillTarget.GrokBuild, SkillScope.Global, ".grok")] public void GetDestination_UsesExpectedTargetAndScopePath(SkillTarget target, SkillScope scope, string toolDirectoryName) { string root = scope == SkillScope.Project @@ -24,6 +26,16 @@ public void GetDestination_UsesExpectedTargetAndScopePath(SkillTarget target, Sk Assert.AreEqual(expected, actual); } + [Test] + public void GetDestination_SupportsEverySkillTarget() + { + foreach (SkillTarget target in Enum.GetValues(typeof(SkillTarget))) + { + Assert.DoesNotThrow(() => SkillInstaller.GetDestination(target, SkillScope.Project)); + Assert.DoesNotThrow(() => SkillInstaller.GetDestination(target, SkillScope.Global)); + } + } + [Test] public void ParsePackageVersionMarker_ReturnsVersion() { From 9d5cf6ed6d78b3a8b1d95c6477b7ab6f5cccb582 Mon Sep 17 00:00:00 2001 From: yhc5091 Date: Tue, 18 Aug 2026 20:02:49 +0900 Subject: [PATCH 2/2] chore: release v0.5.4 Move Unreleased notes into 0.5.4, bump the package version, and point the README pin example at the new tag. --- CHANGELOG.md | 2 ++ README.md | 2 +- unity-package/com.yhc509.unity-cli-bridge/CHANGELOG.md | 2 ++ unity-package/com.yhc509.unity-cli-bridge/package.json | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e336ed6..c796b02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] +## [0.5.4] - 2026-08-18 + ### Added - **Window > Unity CLI Manager** can now install the AI Agent Skill for Grok Build, next to Claude Code and Codex. Project scope writes to `/.grok/skills/`; global scope writes to `~/.grok/skills/`. diff --git a/README.md b/README.md index 7f4845f..272be5c 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Add the following to your `Packages/manifest.json`: The bridge starts automatically when the Editor opens. No configuration needed. -`#main` always tracks the latest release. For a build you need to reproduce later, pin a release tag instead — `...#v0.5.3` — and commit `Packages/packages-lock.json`. +`#main` always tracks the latest release. For a build you need to reproduce later, pin a release tag instead — `...#v0.5.4` — and commit `Packages/packages-lock.json`. Unity Recorder is optional and is not installed for you. Add `com.unity.recorder` if you want the `record` commands or `qa run-sequence --record`; without it those commands fail with an install hint and everything else works normally. diff --git a/unity-package/com.yhc509.unity-cli-bridge/CHANGELOG.md b/unity-package/com.yhc509.unity-cli-bridge/CHANGELOG.md index e336ed6..c796b02 100644 --- a/unity-package/com.yhc509.unity-cli-bridge/CHANGELOG.md +++ b/unity-package/com.yhc509.unity-cli-bridge/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] +## [0.5.4] - 2026-08-18 + ### Added - **Window > Unity CLI Manager** can now install the AI Agent Skill for Grok Build, next to Claude Code and Codex. Project scope writes to `/.grok/skills/`; global scope writes to `~/.grok/skills/`. diff --git a/unity-package/com.yhc509.unity-cli-bridge/package.json b/unity-package/com.yhc509.unity-cli-bridge/package.json index a7d566a..3210752 100644 --- a/unity-package/com.yhc509.unity-cli-bridge/package.json +++ b/unity-package/com.yhc509.unity-cli-bridge/package.json @@ -1,7 +1,7 @@ { "name": "com.yhc509.unity-cli-bridge", "displayName": "Unity CLI Bridge", - "version": "0.5.3", + "version": "0.5.4", "unity": "2023.1", "description": "Project-aware Unity Editor bridge for CLI control without manual servers or per-project ports.", "author": {