From d3a6b4456247f24772d4f2d38167810d403b118e Mon Sep 17 00:00:00 2001 From: hatayama Date: Thu, 18 Jun 2026 15:36:28 +0900 Subject: [PATCH 1/9] Simplify uloop skill files and remove cross-file duplication - Consolidate Pause Point Inspection sections in simulate-keyboard, simulate-mouse-input, and simulate-mouse-ui into references to uloop-wait-for-pause-point instead of repeating the full guidance - Merge duplicate Input System prerequisite lines across 4 files - Remove defensive "non-existent field enumeration" patterns from record-input, replay-input, simulate-mouse-input, simulate-mouse-ui - Shorten verbose explanations: run-tests NoTestsFound, get-logs pause-point intro, control-play-mode Notes, focus-window Notes - Remove implementation-detail Notes from hello-world - Deduplicate When To Use / Marker Placement in wait-for-pause-point - Clean up launch Output section (separate JSON fields from prose) - Remove redundant compare-table repetition in simulate-mouse-input --- .claude/skills/uloop-clear-console/SKILL.md | 3 +- .../skills/uloop-control-play-mode/SKILL.md | 7 ++-- .../uloop-execute-dynamic-code/SKILL.md | 6 ++-- .../skills/uloop-find-game-objects/SKILL.md | 2 +- .claude/skills/uloop-focus-window/SKILL.md | 8 ----- .claude/skills/uloop-get-logs/SKILL.md | 7 ++-- .claude/skills/uloop-hello-world/SKILL.md | 7 ---- .claude/skills/uloop-launch/SKILL.md | 36 +++++++++---------- .claude/skills/uloop-record-input/SKILL.md | 5 ++- .claude/skills/uloop-replay-input/SKILL.md | 4 +-- .claude/skills/uloop-run-tests/SKILL.md | 4 +-- .claude/skills/uloop-screenshot/SKILL.md | 3 -- .../skills/uloop-simulate-keyboard/SKILL.md | 14 +++----- .../uloop-simulate-mouse-input/SKILL.md | 23 ++++++------ .../skills/uloop-simulate-mouse-ui/SKILL.md | 10 ++---- .../uloop-wait-for-pause-point/SKILL.md | 1 - 16 files changed, 49 insertions(+), 91 deletions(-) diff --git a/.claude/skills/uloop-clear-console/SKILL.md b/.claude/skills/uloop-clear-console/SKILL.md index 5c633ca30b..3cb9e2be3f 100644 --- a/.claude/skills/uloop-clear-console/SKILL.md +++ b/.claude/skills/uloop-clear-console/SKILL.md @@ -45,5 +45,4 @@ Returns JSON with: - `errorCount` (number): Errors cleared - `warningCount` (number): Warnings cleared - `logCount` (number): Info logs cleared -- `message` (string): Description of the result; carries the failure summary when the operation fails (e.g. `"Failed to clear console: ..."`) -- `errorMessage` (string): Currently always empty for this tool — read `message` for failure details +- `message` (string): Description of the result. On failure, this field carries the error summary. diff --git a/.claude/skills/uloop-control-play-mode/SKILL.md b/.claude/skills/uloop-control-play-mode/SKILL.md index 662c027629..bfdb9e9bc9 100644 --- a/.claude/skills/uloop-control-play-mode/SKILL.md +++ b/.claude/skills/uloop-control-play-mode/SKILL.md @@ -57,9 +57,6 @@ Returns JSON with the current play mode state: ## Notes -- Play action starts the game in the Unity Editor (also resumes from pause) -- Stop action exits play mode and returns to edit mode. If Play Mode was already stopped, `changed` is `false`, `wasAlreadyStopped` is `true`, and `message` is `Play mode was already stopped`. -- Pause action pauses the game while remaining in play mode -- Step action advances exactly one frame and leaves play mode paused (the Editor's Next Frame button; independent of Time.timeScale). Requires PlayMode to be running; repeat to walk transitions frame by frame -- Useful for automated testing workflows +- Stop on an already-stopped Editor sets `changed: false`, `wasAlreadyStopped: true` +- Step is independent of `Time.timeScale`; requires PlayMode to be running - The command waits for the requested state before returning. Increase `--timeout-seconds` for projects with slow PlayMode entry. diff --git a/.claude/skills/uloop-execute-dynamic-code/SKILL.md b/.claude/skills/uloop-execute-dynamic-code/SKILL.md index 45ce0d1acc..1b2c08ebf9 100644 --- a/.claude/skills/uloop-execute-dynamic-code/SKILL.md +++ b/.claude/skills/uloop-execute-dynamic-code/SKILL.md @@ -18,8 +18,7 @@ This tool can inspect reachable Unity state, such as GameObjects, components, pu 1. Read the relevant reference file(s) from the Code Examples section below 2. Construct C# code based on the reference examples 3. Execute with `--code` or `--code-file` using the active shell's quoting guidance -4. If execution fails, adjust code and retry -5. Report the execution result +4. Report the execution result ## Parameters @@ -48,8 +47,7 @@ Returns JSON: - `result`: string — value of the snippet's `return` statement (empty when omitted) - `logs`: string[] — execution messages from the dynamic-code tool; read Unity Console `Debug.Log` output with `get-logs` - `compilationErrors`: object[] — Roslyn diagnostics with `message`, `line`, `column`, `errorCode`, optional `hint` and `suggestions` -- `errorMessage`: string — top-level failure summary (empty on success) -- `error`: string — alias of `errorMessage` +- `error` / `errorMessage`: string — top-level failure summary (empty on success) - `updatedCode`: string|null — the wrapped form actually compiled (handy when debugging using-statement reordering) - `diagnosticsSummary`: string|null — compact summary when diagnostics are available - `diagnostics`: object[] — structured diagnostics; same shape as `compilationErrors`, usually populated together with it diff --git a/.claude/skills/uloop-find-game-objects/SKILL.md b/.claude/skills/uloop-find-game-objects/SKILL.md index 620c44e32c..24edd909a3 100644 --- a/.claude/skills/uloop-find-game-objects/SKILL.md +++ b/.claude/skills/uloop-find-game-objects/SKILL.md @@ -77,7 +77,7 @@ Returns JSON with: - `tag` (string): GameObject tag - `layer` (number): Layer index - `components` (array): Each entry has `type` (short name, e.g., `Rigidbody`), `fullTypeName` (e.g., `UnityEngine.Rigidbody`), and `properties` (array of Inspector-visible `{name, type, value}` pairs) -- `totalFound` (number): Number of results returned inline, or number exported for multi-selection file output. For search modes, this is after `--max-results` clipping and serialization. +- `totalFound` (number): Results returned (after `--max-results` clipping). For multi-selection file export, this is the number exported. - `errorMessage` (string): Top-level failure summary (empty on success) - `processingErrors` (array): Selected-mode per-GameObject serialization failures, each `{gameObjectName, gameObjectPath, error}`. Omitted/null or empty on clean runs. diff --git a/.claude/skills/uloop-focus-window/SKILL.md b/.claude/skills/uloop-focus-window/SKILL.md index 515bfa29c3..e2ca7a23bb 100644 --- a/.claude/skills/uloop-focus-window/SKILL.md +++ b/.claude/skills/uloop-focus-window/SKILL.md @@ -13,10 +13,6 @@ Bring Unity Editor window to front using OS-level commands. uloop focus-window ``` -## Parameters - -None. - ## Global Options | Option | Description | @@ -36,11 +32,7 @@ Returns JSON with: - `success`: Whether the focus operation succeeded - `message`: Status message (e.g. `Unity Editor window focused (PID: 12345)`, or the failure reason such as `Unity project not found` / `No running Unity process found for this project` / `Failed to focus Unity window: `) -These are the only two fields. There is no PID, window-handle, or platform field returned to the caller. - ## Notes - **Works even when Unity is busy** (compiling, domain reload, etc.) -- Uses OS-level commands (osascript on macOS, PowerShell on Windows) - Useful before `uloop capture-unity-window` to ensure the target window is visible -- Brings the main Unity Editor window to the foreground diff --git a/.claude/skills/uloop-get-logs/SKILL.md b/.claude/skills/uloop-get-logs/SKILL.md index 2ecb25aff7..f59bec58e1 100644 --- a/.claude/skills/uloop-get-logs/SKILL.md +++ b/.claude/skills/uloop-get-logs/SKILL.md @@ -8,7 +8,7 @@ description: "Read current Unity Console entries from a running Editor. Use duri Retrieve logs from Unity Console. -When a pause-point marker pauses Unity and the value you need is a method local, intermediate calculation, or branch reason, read the focused `Debug.Log` entry added immediately before the marker before resuming PlayMode. Use `--search-text ` so the marker and its log are checked as one breakpoint-style proof. +After a pause-point hit, use `--search-text ` to read the focused log alongside the marker. ## Usage @@ -54,10 +54,7 @@ uloop get-logs --search-text "Missing.*Component" --use-regex Returns JSON with: - `totalCount` (number): Total logs available before max-count clipping - `displayedCount` (number): Logs returned in this response (≤ `--max-count`) -- `logType` (string): The `--log-type` filter that was applied -- `maxCount` (number): The `--max-count` cap that was applied -- `searchText` (string): The `--search-text` filter that was applied (empty when omitted) -- `includeStackTrace` (boolean): Whether stack traces are included in `logs[]` +- Input filters (`logType`, `maxCount`, `searchText`, `includeStackTrace`) are echoed back in the response - `logs` (array): Each entry has: - `type` (string): `"Error"`, `"Warning"`, or `"Log"` - `message` (string): Log message body diff --git a/.claude/skills/uloop-hello-world/SKILL.md b/.claude/skills/uloop-hello-world/SKILL.md index 2724067ddd..1f78d5a0ee 100644 --- a/.claude/skills/uloop-hello-world/SKILL.md +++ b/.claude/skills/uloop-hello-world/SKILL.md @@ -44,10 +44,3 @@ Returns JSON with: - `language`: Language used for greeting - `timestamp`: Current timestamp (if enabled) -## Notes - -This is a sample custom tool demonstrating: -- Type-safe parameter handling with Schema -- Enum parameters for language selection -- Value-less flag parameters -- Multi-language support diff --git a/.claude/skills/uloop-launch/SKILL.md b/.claude/skills/uloop-launch/SKILL.md index 2bf0e0929b..525fe110b8 100644 --- a/.claude/skills/uloop-launch/SKILL.md +++ b/.claude/skills/uloop-launch/SKILL.md @@ -47,22 +47,22 @@ uloop launch --quit ## Output -- Prints detected Unity version -- Prints project path +Returns JSON with: +- `success`: whether the command completed +- `ready`: whether Unity CLI Loop is ready for commands +- `serverReady`: whether the Unity CLI Loop server accepted requests +- `projectIpcReady`: whether the project IPC path accepted tool requests +- `alreadyRunning`: whether an existing Unity process was reused +- `launched`: whether this command launched a Unity process +- `restarted`: whether this command stopped an existing process and launched a new one +- `quit`: whether this command stopped Unity without launching a new process +- `previousProcessId`: process ID stopped by restart or quit, when available +- `currentProcessId`: current Unity process ID, when available +- `projectRoot`: resolved project root +- `message`: readiness summary + +## Notes + - If Unity is already running, focuses the existing window and verifies tool readiness -- If the process scan is blocked by the environment (e.g. sandboxed `ps`), plain launch falls back to probing the project IPC; when Unity responds it reports `alreadyRunning: true` without focusing the window instead of failing. `--restart` and `--quit` still fail because they need the process id -- If launching or restarting, prints when it is waiting for Unity CLI Loop server readiness -- If launching or restarting, waits until Unity finishes startup and the CLI can connect to the project -- Successful launch, restart, existing-process, and quit paths return JSON with: - - `success`: whether the command completed - - `ready`: whether Unity CLI Loop is ready for commands - - `serverReady`: whether the Unity CLI Loop server accepted requests - - `projectIpcReady`: whether the project IPC path accepted tool requests - - `alreadyRunning`: whether an existing Unity process was reused - - `launched`: whether this command launched a Unity process - - `restarted`: whether this command stopped an existing process and launched a new one - - `quit`: whether this command stopped Unity without launching a new process - - `previousProcessId`: process ID stopped by restart or quit, when available - - `currentProcessId`: current Unity process ID, when available - - `projectRoot`: resolved project root - - `message`: readiness summary +- If process scan is blocked (e.g. sandboxed `ps`), plain launch falls back to IPC probing; `--restart` and `--quit` still fail because they need the process id +- The command waits until Unity finishes startup and the CLI can connect before returning diff --git a/.claude/skills/uloop-record-input/SKILL.md b/.claude/skills/uloop-record-input/SKILL.md index 6b1c530f38..a76d38cb33 100644 --- a/.claude/skills/uloop-record-input/SKILL.md +++ b/.claude/skills/uloop-record-input/SKILL.md @@ -39,8 +39,7 @@ Replay injects input frame-by-frame, so the game must also be deterministic to p ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** must be installed (`com.unity.inputsystem`) -- Use this only when the project already uses the New Input System. +- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. ## Output @@ -52,4 +51,4 @@ The CLI prints JSON with: - `totalFrames`: Number of frames recorded (nullable int; populated on `Stop` only) - `durationSeconds`: Recording duration in seconds (nullable float; populated on `Stop` only) -The CLI output contains only these six payload fields. Internal metadata such as `ver` is removed before printing. There is no `recordingId`, `startTimestamp`, `keysCaptured`, or per-frame data in the response — frame data lives only in the JSON file at `outputPath`. +Frame data lives only in the JSON file at `outputPath`; internal metadata fields are not included in the response. diff --git a/.claude/skills/uloop-replay-input/SKILL.md b/.claude/skills/uloop-replay-input/SKILL.md index d69e1ad07e..9a23fabd47 100644 --- a/.claude/skills/uloop-replay-input/SKILL.md +++ b/.claude/skills/uloop-replay-input/SKILL.md @@ -43,8 +43,7 @@ Replay injects the exact same input frame-by-frame, but the game must also be de ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** must be installed (`com.unity.inputsystem`) -- Use this only when the project already uses the New Input System. +- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. ## Output @@ -58,4 +57,3 @@ Returns JSON with: - `progress`: Replay progress (nullable float in 0.0 – 1.0) - `isReplaying`: Whether replay is currently active (nullable bool) -These are the only eight fields. There is no `loopCount`, `elapsedSeconds`, `overlayVisible`, or per-frame inspection data in the response. diff --git a/.claude/skills/uloop-run-tests/SKILL.md b/.claude/skills/uloop-run-tests/SKILL.md index ba6909c219..421e47b21d 100644 --- a/.claude/skills/uloop-run-tests/SKILL.md +++ b/.claude/skills/uloop-run-tests/SKILL.md @@ -8,11 +8,11 @@ description: "Run Unity Test Runner and report detailed results. Use for EditMod Execute Unity Test Runner. When tests fail, NUnit XML results with error messages and stack traces are automatically saved. Read the XML file at `xmlPath` for detailed failure diagnosis. -Before running `uloop run-tests`, run `uloop compile` for the same Unity project when the current task created, deleted, renamed, moved, or edited C# source files, test files, `.asmdef`, `.asmref`, package manifest files, or scripting define settings. This refreshes the AssetDatabase, lets Unity discover new tests, and surfaces compile errors before test execution. You may skip this compile step when rerunning tests without code or assembly-definition changes since the last successful compile. +Before running tests, run `uloop compile` first if you edited C# files, test files, `.asmdef`/`.asmref`, or package manifests since the last successful compile. This refreshes the AssetDatabase and surfaces compile errors before test execution. Before executing tests, `uloop run-tests` saves unsaved loaded Scene changes and unsaved current Prefab Stage changes by default. If saving fails, it returns `success: false`, keeps `testCount` at `0`, lists the unsaved items in `message`, and does not start the Unity Test Runner. -When a run returns `status: "NoTestsFound"` or `noTestsFound: true`, no matching tests were discovered. This is not the same as a failed test case: `hasFailures` remains `false`, `failedCount` remains `0`, and `noTestsFoundExplanation` explains the zero-discovery state for agents. When an unfiltered `--filter-type all` run also has `message` set to the no-tests message, read the `message` for asmdef hints. `run-tests` only appends those hints when it detects likely test assembly configuration issues; exact, regex, and assembly filter misses keep the plain no-tests message. +`NoTestsFound` means zero tests matched — not a test failure. Check `noTestsFoundExplanation` and `message` for asmdef hints. ## Usage diff --git a/.claude/skills/uloop-screenshot/SKILL.md b/.claude/skills/uloop-screenshot/SKILL.md index d10d199ea2..bc721a5ee7 100644 --- a/.claude/skills/uloop-screenshot/SKILL.md +++ b/.claude/skills/uloop-screenshot/SKILL.md @@ -67,9 +67,6 @@ uloop screenshot --window-name Project --match-mode prefix # Save screenshot to a specific directory uloop screenshot --output-directory /tmp/screenshots - -# Combine options -uloop screenshot --window-name Scene --resolution-scale 0.5 --output-directory /tmp/screenshots ``` ## Output diff --git a/.claude/skills/uloop-simulate-keyboard/SKILL.md b/.claude/skills/uloop-simulate-keyboard/SKILL.md index 6cb374cc93..7b65d6b135 100644 --- a/.claude/skills/uloop-simulate-keyboard/SKILL.md +++ b/.claude/skills/uloop-simulate-keyboard/SKILL.md @@ -42,16 +42,11 @@ uloop simulate-keyboard --action --key [options] Use `Press` for edge-triggered keyboard code such as `Keyboard.current.spaceKey.wasPressedThisFrame`. `KeyDown` emits one initial press edge, then only keeps the key held. It does not keep `wasPressedThisFrame` true while the key remains held. If a successful `Press` or `KeyDown` leaves `Keyboard.current..isPressed` true but runtime state does not change, do not immediately rewrite the user's runtime code to `isPressed`. First verify that the target component is active during the command, that it polls input in the configured Input System update phase, and that a missed `KeyDown` edge is followed by `KeyUp` before retrying. -Use `KeyDown` / `KeyUp` when the scenario intentionally needs a held key. - ### Pause Point Inspection (Standard for E2E) -- Use `UloopPausePoint.Pause("")` with `uloop-wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. -- Put the marker at a natural state transition after the app consumed the key, such as after a command is accepted, a state mutation is committed, an evaluation step resolves, or a dependent component is updated. Do not place it immediately after sending `simulate-keyboard`. -- If the key handler has local variables, intermediate calculations, or branch reasons that `execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop-get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. -- Treat `simulate-keyboard success=true`, generic action logs, and final durable counters as useful evidence, but not as paused-frame proof. -- If the response has `interruptedByPausePoint: true`, Unity is paused for inspection and the tool released its held input bookkeeping. If a `UloopPausePoint.Pause` marker caused the pause, `pausePointId` and `pausePointHitCount` identify it. Use `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. -- Use distinct marker ids for strict phases, for example `input-read`, `state-updated`, and `result-committed`. +For standard frame proof when this input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the key, not immediately after `simulate-keyboard`. + +- If `interruptedByPausePoint: true`, Unity is paused and input bookkeeping was released. `pausePointId` and `pausePointHitCount` identify the marker. `pressEdgeObserved` is still reported on pause-point interruptions. ### KeyDown/KeyUp Rules @@ -103,6 +98,5 @@ Returns JSON with: ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** must be installed (`com.unity.inputsystem`) -- Use this only when the project already uses the New Input System. +- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. - Game code must read input via Input System API (e.g. `Keyboard.current[Key.W].isPressed`), not legacy `Input.GetKey()` diff --git a/.claude/skills/uloop-simulate-mouse-input/SKILL.md b/.claude/skills/uloop-simulate-mouse-input/SKILL.md index 12b73569e4..8bc5b09e69 100644 --- a/.claude/skills/uloop-simulate-mouse-input/SKILL.md +++ b/.claude/skills/uloop-simulate-mouse-input/SKILL.md @@ -50,11 +50,9 @@ uloop simulate-mouse-input --action [options] ### Pause Point Inspection (Standard for E2E) -- Use `UloopPausePoint.Pause("")` with `uloop-wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. -- Place the pause point at a natural state transition after the app consumed the mouse input, such as after a command is accepted, a state mutation is committed, an evaluation step resolves, a tracked value changes, or a dependent component is updated. Do not place it immediately after sending `simulate-mouse-input`. -- If the mouse handler has local variables, intermediate calculations, or branch reasons that `execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop-get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. -- If the response has `interruptedByPausePoint: true`, Unity is paused for inspection and the tool released its held input bookkeeping. `pausePointId` and `pausePointHitCount` identify the pause point that paused Unity. Use `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. -- Use distinct ids for strict phases, for example `input-read`, `state-updated`, and `result-committed`. +For standard frame proof when this input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the mouse input, not immediately after `simulate-mouse-input`. + +- If `interruptedByPausePoint: true`, Unity is paused and input bookkeeping was released. `pausePointId` and `pausePointHitCount` identify the marker. - Remove temporary pause-point/log instrumentation before final validation when it was added only for inspection. ### Global Options (optional) @@ -66,14 +64,16 @@ uloop simulate-mouse-input --action [options] ## When to use this vs simulate-mouse-ui +All rows below assume the New Input System is installed. + | Scenario | Tool | |----------|------| | Click a Unity UI Button (IPointerClickHandler) | `simulate-mouse-ui` | -| Runtime logic reads `Mouse.current.leftButton` | `simulate-mouse-input` when the project uses the New Input System | -| Runtime logic reads right-click | `simulate-mouse-input --button Right` when the project uses the New Input System | +| Runtime logic reads `Mouse.current.leftButton` | `simulate-mouse-input` | +| Runtime logic reads right-click | `simulate-mouse-input --button Right` | | Drag a UI slider | `simulate-mouse-ui --action Drag` | -| Runtime logic reads `Mouse.current.delta` | `simulate-mouse-input --action MoveDelta` when the project uses the New Input System | -| Runtime logic reads `Mouse.current.scroll` | `simulate-mouse-input --action Scroll` when the project uses the New Input System | +| Runtime logic reads `Mouse.current.delta` | `simulate-mouse-input --action MoveDelta` | +| Runtime logic reads `Mouse.current.scroll` | `simulate-mouse-input --action Scroll` | ## Examples @@ -103,9 +103,8 @@ uloop simulate-mouse-input --action SmoothDelta --delta-x 300 --delta-y 0 --dura ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** must be installed (`com.unity.inputsystem`) +- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. - Game code must read input via Input System API (e.g. `Mouse.current.leftButton.wasPressedThisFrame`) -- Use this only when the project already uses the New Input System. ## Output @@ -121,4 +120,4 @@ Returns JSON with: - `pausePointHitCount`: The hit count for that `UloopPausePoint.Pause("")` - `pausePointHits` (array, nullable): Every marker hit during this input as `{id, hitCount}` entries, in hit order. Read this when one input may trigger several markers; `pausePointId` only names the latest one -There is no `deltaX`, `deltaY`, `scrollX`, `scrollY`, `duration`, or hit-element field in the response — only the issued action, button, target position, and Pause Point interruption state are echoed back. Verify visual outcome with a follow-up screenshot. +Verify visual outcome with a follow-up screenshot. diff --git a/.claude/skills/uloop-simulate-mouse-ui/SKILL.md b/.claude/skills/uloop-simulate-mouse-ui/SKILL.md index f5e2799aec..8a9a797b6b 100644 --- a/.claude/skills/uloop-simulate-mouse-ui/SKILL.md +++ b/.claude/skills/uloop-simulate-mouse-ui/SKILL.md @@ -57,7 +57,6 @@ uloop simulate-mouse-ui --action --x --y [options] - `DragStart` must be called before `DragMove` or `DragEnd` - `DragEnd` must be called to release an active drag — failing to call it leaves drag state stuck - Calling `DragMove` or `DragEnd` without an active drag returns an error -- `Drag`, `DragStart`, `DragMove`, and `DragEnd` only support `--button Left` ### Global Options (all optional, mutually exclusive) @@ -78,11 +77,8 @@ uloop simulate-mouse-ui --action --x --y [options] ## Pause Point Inspection (Standard for E2E) -- Use `UloopPausePoint.Pause("")` with `uloop wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. -- Place the pause point at a natural transition after the app consumed the UI event, such as after a command is accepted, a state mutation is committed, a tracked value changes, a UI/domain state syncs, or a success/failure/end condition is entered. -- If the UI handler has local variables, intermediate calculations, or branch reasons that `uloop execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. -- Treat `uloop simulate-mouse-ui` `success=true`, generic action logs, and final durable counters as useful evidence, not paused-frame proof. -- If a `UloopPausePoint.Pause` pauses Unity, inspect with `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. +For standard frame proof when this UI input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the UI event, not immediately after `simulate-mouse-ui`. + - Remove temporary pause-point/log instrumentation before final validation when it was added only for inspection. ## Examples @@ -138,6 +134,6 @@ Returns JSON with: - `endPositionX`: Drag end X coordinate (nullable float; populated for drag actions only) - `endPositionY`: Drag end Y coordinate (nullable float; populated for drag actions only) -These are the only eight fields. There is no `button`, `duration`, `dragSpeed`, raycast list, or pointer-event log in the response — verify the visual outcome with a follow-up `uloop screenshot --capture-mode rendering --annotate-elements`. +Verify the visual outcome with a follow-up `uloop screenshot --capture-mode rendering --annotate-elements`. Note: Click and LongPress on empty space (no UI element) still return `success = true` with `hitGameObjectName = null`. Drag actions on empty space return `success = false`. diff --git a/.claude/skills/uloop-wait-for-pause-point/SKILL.md b/.claude/skills/uloop-wait-for-pause-point/SKILL.md index 2ac9b2c58b..14699e0deb 100644 --- a/.claude/skills/uloop-wait-for-pause-point/SKILL.md +++ b/.claude/skills/uloop-wait-for-pause-point/SKILL.md @@ -47,7 +47,6 @@ Use `generation`, `enabledAtUtc`, and the hit sequence fields from the hit or st - Consider a pause point during E2E passes when transition-frame evidence would add confidence, even if durable state, logs, or screenshots can later confirm the final result. - Use this before reaching for `Time.timeScale`, sleeps, repeated polling, or after-the-fact `execute-dynamic-code`; those checks can supplement the paused-frame proof, but they are not substitutes. - If the value you need is a method local, an intermediate calculation, or a branch reason that `execute-dynamic-code` cannot reach, add a focused `Debug.Log` immediately before the marker and read it with `get-logs` while paused. Do not count the breakpoint check complete until the matching log has been read. -- Good pause points include after input is consumed, a command is accepted, a state mutation is committed, an evaluation step resolves, a tracked value changes, a UI/domain state syncs, or a success/failure/end condition is entered. - Treat the pause like a lightweight breakpoint for one important transition: combine nearby debug logs with paused-frame inspection to confirm the variables and component state at that point. - Do not treat `simulate-* success=true`, generic action logs, sleeps/retries, testing-only counters, or `Time.timeScale` changes as paused-frame proof. - Skip this only for ordinary persistent-state checks when you are not validating simulated input delivery, event ordering, or transition-frame fidelity. From 67e84b85f3ab94172c0e59d4cc9a804a33cd9121 Mon Sep 17 00:00:00 2001 From: hatayama Date: Thu, 18 Jun 2026 16:11:28 +0900 Subject: [PATCH 2/9] Remove pause-point guidance from get-logs skill The pause-point + get-logs workflow is already documented in uloop-wait-for-pause-point, including the --search-text fallback. --- .claude/skills/uloop-get-logs/SKILL.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/.claude/skills/uloop-get-logs/SKILL.md b/.claude/skills/uloop-get-logs/SKILL.md index f59bec58e1..57d1d97f62 100644 --- a/.claude/skills/uloop-get-logs/SKILL.md +++ b/.claude/skills/uloop-get-logs/SKILL.md @@ -8,8 +8,6 @@ description: "Read current Unity Console entries from a running Editor. Use duri Retrieve logs from Unity Console. -After a pause-point hit, use `--search-text ` to read the focused log alongside the marker. - ## Usage ```bash From f19940481669a9fd72f442d8569ebd52d941b424 Mon Sep 17 00:00:00 2001 From: hatayama Date: Thu, 18 Jun 2026 16:32:00 +0900 Subject: [PATCH 3/9] Sync skill simplifications to package sources and generated copies Propagate the .claude/skills changes to the source definitions in Packages/src/Editor and to the .agents and .codex generated copies. --- .agents/skills/uloop-clear-console/SKILL.md | 3 +- .../skills/uloop-control-play-mode/SKILL.md | 7 ++-- .../uloop-execute-dynamic-code/SKILL.md | 6 ++-- .../skills/uloop-find-game-objects/SKILL.md | 2 +- .agents/skills/uloop-focus-window/SKILL.md | 8 ----- .agents/skills/uloop-get-logs/SKILL.md | 7 +--- .agents/skills/uloop-launch/SKILL.md | 36 +++++++++---------- .agents/skills/uloop-record-input/SKILL.md | 5 ++- .agents/skills/uloop-replay-input/SKILL.md | 4 +-- .agents/skills/uloop-run-tests/SKILL.md | 4 +-- .agents/skills/uloop-screenshot/SKILL.md | 3 -- .../skills/uloop-simulate-keyboard/SKILL.md | 14 +++----- .../uloop-simulate-mouse-input/SKILL.md | 23 ++++++------ .../skills/uloop-simulate-mouse-ui/SKILL.md | 10 ++---- .../uloop-wait-for-pause-point/SKILL.md | 1 - .codex/skills/uloop-clear-console/SKILL.md | 3 +- .../skills/uloop-control-play-mode/SKILL.md | 7 ++-- .../uloop-execute-dynamic-code/SKILL.md | 6 ++-- .../skills/uloop-find-game-objects/SKILL.md | 2 +- .codex/skills/uloop-focus-window/SKILL.md | 8 ----- .codex/skills/uloop-get-logs/SKILL.md | 7 +--- .codex/skills/uloop-launch/SKILL.md | 36 +++++++++---------- .codex/skills/uloop-record-input/SKILL.md | 5 ++- .codex/skills/uloop-replay-input/SKILL.md | 4 +-- .codex/skills/uloop-run-tests/SKILL.md | 4 +-- .codex/skills/uloop-screenshot/SKILL.md | 3 -- .../skills/uloop-simulate-keyboard/SKILL.md | 14 +++----- .../uloop-simulate-mouse-input/SKILL.md | 23 ++++++------ .../skills/uloop-simulate-mouse-ui/SKILL.md | 10 ++---- .../uloop-wait-for-pause-point/SKILL.md | 1 - .../CliOnlyTools~/FocusWindow/Skill/SKILL.md | 8 ----- .../CliOnlyTools~/Launch/Skill/SKILL.md | 36 +++++++++---------- .../CliOnlyTools~/PausePoint/Skill/SKILL.md | 1 - .../ClearConsole/Skill/SKILL.md | 3 +- .../ControlPlayMode/Skill/SKILL.md | 7 ++-- .../ExecuteDynamicCode/Skill/SKILL.md | 6 ++-- .../FindGameObjects/Skill/SKILL.md | 2 +- .../FirstPartyTools/GetLogs/Skill/SKILL.md | 7 +--- .../RecordInput/Skill/SKILL.md | 5 ++- .../ReplayInput/Skill/SKILL.md | 4 +-- .../FirstPartyTools/RunTests/Skill/SKILL.md | 4 +-- .../FirstPartyTools/Screenshot/Skill/SKILL.md | 3 -- .../SimulateKeyboard/Skill/SKILL.md | 14 +++----- .../SimulateMouseInput/Skill/SKILL.md | 23 ++++++------ .../SimulateMouseUi/Skill/SKILL.md | 10 ++---- 45 files changed, 144 insertions(+), 255 deletions(-) diff --git a/.agents/skills/uloop-clear-console/SKILL.md b/.agents/skills/uloop-clear-console/SKILL.md index 5c633ca30b..3cb9e2be3f 100644 --- a/.agents/skills/uloop-clear-console/SKILL.md +++ b/.agents/skills/uloop-clear-console/SKILL.md @@ -45,5 +45,4 @@ Returns JSON with: - `errorCount` (number): Errors cleared - `warningCount` (number): Warnings cleared - `logCount` (number): Info logs cleared -- `message` (string): Description of the result; carries the failure summary when the operation fails (e.g. `"Failed to clear console: ..."`) -- `errorMessage` (string): Currently always empty for this tool — read `message` for failure details +- `message` (string): Description of the result. On failure, this field carries the error summary. diff --git a/.agents/skills/uloop-control-play-mode/SKILL.md b/.agents/skills/uloop-control-play-mode/SKILL.md index 662c027629..bfdb9e9bc9 100644 --- a/.agents/skills/uloop-control-play-mode/SKILL.md +++ b/.agents/skills/uloop-control-play-mode/SKILL.md @@ -57,9 +57,6 @@ Returns JSON with the current play mode state: ## Notes -- Play action starts the game in the Unity Editor (also resumes from pause) -- Stop action exits play mode and returns to edit mode. If Play Mode was already stopped, `changed` is `false`, `wasAlreadyStopped` is `true`, and `message` is `Play mode was already stopped`. -- Pause action pauses the game while remaining in play mode -- Step action advances exactly one frame and leaves play mode paused (the Editor's Next Frame button; independent of Time.timeScale). Requires PlayMode to be running; repeat to walk transitions frame by frame -- Useful for automated testing workflows +- Stop on an already-stopped Editor sets `changed: false`, `wasAlreadyStopped: true` +- Step is independent of `Time.timeScale`; requires PlayMode to be running - The command waits for the requested state before returning. Increase `--timeout-seconds` for projects with slow PlayMode entry. diff --git a/.agents/skills/uloop-execute-dynamic-code/SKILL.md b/.agents/skills/uloop-execute-dynamic-code/SKILL.md index 45ce0d1acc..1b2c08ebf9 100644 --- a/.agents/skills/uloop-execute-dynamic-code/SKILL.md +++ b/.agents/skills/uloop-execute-dynamic-code/SKILL.md @@ -18,8 +18,7 @@ This tool can inspect reachable Unity state, such as GameObjects, components, pu 1. Read the relevant reference file(s) from the Code Examples section below 2. Construct C# code based on the reference examples 3. Execute with `--code` or `--code-file` using the active shell's quoting guidance -4. If execution fails, adjust code and retry -5. Report the execution result +4. Report the execution result ## Parameters @@ -48,8 +47,7 @@ Returns JSON: - `result`: string — value of the snippet's `return` statement (empty when omitted) - `logs`: string[] — execution messages from the dynamic-code tool; read Unity Console `Debug.Log` output with `get-logs` - `compilationErrors`: object[] — Roslyn diagnostics with `message`, `line`, `column`, `errorCode`, optional `hint` and `suggestions` -- `errorMessage`: string — top-level failure summary (empty on success) -- `error`: string — alias of `errorMessage` +- `error` / `errorMessage`: string — top-level failure summary (empty on success) - `updatedCode`: string|null — the wrapped form actually compiled (handy when debugging using-statement reordering) - `diagnosticsSummary`: string|null — compact summary when diagnostics are available - `diagnostics`: object[] — structured diagnostics; same shape as `compilationErrors`, usually populated together with it diff --git a/.agents/skills/uloop-find-game-objects/SKILL.md b/.agents/skills/uloop-find-game-objects/SKILL.md index 620c44e32c..24edd909a3 100644 --- a/.agents/skills/uloop-find-game-objects/SKILL.md +++ b/.agents/skills/uloop-find-game-objects/SKILL.md @@ -77,7 +77,7 @@ Returns JSON with: - `tag` (string): GameObject tag - `layer` (number): Layer index - `components` (array): Each entry has `type` (short name, e.g., `Rigidbody`), `fullTypeName` (e.g., `UnityEngine.Rigidbody`), and `properties` (array of Inspector-visible `{name, type, value}` pairs) -- `totalFound` (number): Number of results returned inline, or number exported for multi-selection file output. For search modes, this is after `--max-results` clipping and serialization. +- `totalFound` (number): Results returned (after `--max-results` clipping). For multi-selection file export, this is the number exported. - `errorMessage` (string): Top-level failure summary (empty on success) - `processingErrors` (array): Selected-mode per-GameObject serialization failures, each `{gameObjectName, gameObjectPath, error}`. Omitted/null or empty on clean runs. diff --git a/.agents/skills/uloop-focus-window/SKILL.md b/.agents/skills/uloop-focus-window/SKILL.md index 515bfa29c3..e2ca7a23bb 100644 --- a/.agents/skills/uloop-focus-window/SKILL.md +++ b/.agents/skills/uloop-focus-window/SKILL.md @@ -13,10 +13,6 @@ Bring Unity Editor window to front using OS-level commands. uloop focus-window ``` -## Parameters - -None. - ## Global Options | Option | Description | @@ -36,11 +32,7 @@ Returns JSON with: - `success`: Whether the focus operation succeeded - `message`: Status message (e.g. `Unity Editor window focused (PID: 12345)`, or the failure reason such as `Unity project not found` / `No running Unity process found for this project` / `Failed to focus Unity window: `) -These are the only two fields. There is no PID, window-handle, or platform field returned to the caller. - ## Notes - **Works even when Unity is busy** (compiling, domain reload, etc.) -- Uses OS-level commands (osascript on macOS, PowerShell on Windows) - Useful before `uloop capture-unity-window` to ensure the target window is visible -- Brings the main Unity Editor window to the foreground diff --git a/.agents/skills/uloop-get-logs/SKILL.md b/.agents/skills/uloop-get-logs/SKILL.md index 2ecb25aff7..57d1d97f62 100644 --- a/.agents/skills/uloop-get-logs/SKILL.md +++ b/.agents/skills/uloop-get-logs/SKILL.md @@ -8,8 +8,6 @@ description: "Read current Unity Console entries from a running Editor. Use duri Retrieve logs from Unity Console. -When a pause-point marker pauses Unity and the value you need is a method local, intermediate calculation, or branch reason, read the focused `Debug.Log` entry added immediately before the marker before resuming PlayMode. Use `--search-text ` so the marker and its log are checked as one breakpoint-style proof. - ## Usage ```bash @@ -54,10 +52,7 @@ uloop get-logs --search-text "Missing.*Component" --use-regex Returns JSON with: - `totalCount` (number): Total logs available before max-count clipping - `displayedCount` (number): Logs returned in this response (≤ `--max-count`) -- `logType` (string): The `--log-type` filter that was applied -- `maxCount` (number): The `--max-count` cap that was applied -- `searchText` (string): The `--search-text` filter that was applied (empty when omitted) -- `includeStackTrace` (boolean): Whether stack traces are included in `logs[]` +- Input filters (`logType`, `maxCount`, `searchText`, `includeStackTrace`) are echoed back in the response - `logs` (array): Each entry has: - `type` (string): `"Error"`, `"Warning"`, or `"Log"` - `message` (string): Log message body diff --git a/.agents/skills/uloop-launch/SKILL.md b/.agents/skills/uloop-launch/SKILL.md index 2bf0e0929b..525fe110b8 100644 --- a/.agents/skills/uloop-launch/SKILL.md +++ b/.agents/skills/uloop-launch/SKILL.md @@ -47,22 +47,22 @@ uloop launch --quit ## Output -- Prints detected Unity version -- Prints project path +Returns JSON with: +- `success`: whether the command completed +- `ready`: whether Unity CLI Loop is ready for commands +- `serverReady`: whether the Unity CLI Loop server accepted requests +- `projectIpcReady`: whether the project IPC path accepted tool requests +- `alreadyRunning`: whether an existing Unity process was reused +- `launched`: whether this command launched a Unity process +- `restarted`: whether this command stopped an existing process and launched a new one +- `quit`: whether this command stopped Unity without launching a new process +- `previousProcessId`: process ID stopped by restart or quit, when available +- `currentProcessId`: current Unity process ID, when available +- `projectRoot`: resolved project root +- `message`: readiness summary + +## Notes + - If Unity is already running, focuses the existing window and verifies tool readiness -- If the process scan is blocked by the environment (e.g. sandboxed `ps`), plain launch falls back to probing the project IPC; when Unity responds it reports `alreadyRunning: true` without focusing the window instead of failing. `--restart` and `--quit` still fail because they need the process id -- If launching or restarting, prints when it is waiting for Unity CLI Loop server readiness -- If launching or restarting, waits until Unity finishes startup and the CLI can connect to the project -- Successful launch, restart, existing-process, and quit paths return JSON with: - - `success`: whether the command completed - - `ready`: whether Unity CLI Loop is ready for commands - - `serverReady`: whether the Unity CLI Loop server accepted requests - - `projectIpcReady`: whether the project IPC path accepted tool requests - - `alreadyRunning`: whether an existing Unity process was reused - - `launched`: whether this command launched a Unity process - - `restarted`: whether this command stopped an existing process and launched a new one - - `quit`: whether this command stopped Unity without launching a new process - - `previousProcessId`: process ID stopped by restart or quit, when available - - `currentProcessId`: current Unity process ID, when available - - `projectRoot`: resolved project root - - `message`: readiness summary +- If process scan is blocked (e.g. sandboxed `ps`), plain launch falls back to IPC probing; `--restart` and `--quit` still fail because they need the process id +- The command waits until Unity finishes startup and the CLI can connect before returning diff --git a/.agents/skills/uloop-record-input/SKILL.md b/.agents/skills/uloop-record-input/SKILL.md index 6b1c530f38..a76d38cb33 100644 --- a/.agents/skills/uloop-record-input/SKILL.md +++ b/.agents/skills/uloop-record-input/SKILL.md @@ -39,8 +39,7 @@ Replay injects input frame-by-frame, so the game must also be deterministic to p ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** must be installed (`com.unity.inputsystem`) -- Use this only when the project already uses the New Input System. +- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. ## Output @@ -52,4 +51,4 @@ The CLI prints JSON with: - `totalFrames`: Number of frames recorded (nullable int; populated on `Stop` only) - `durationSeconds`: Recording duration in seconds (nullable float; populated on `Stop` only) -The CLI output contains only these six payload fields. Internal metadata such as `ver` is removed before printing. There is no `recordingId`, `startTimestamp`, `keysCaptured`, or per-frame data in the response — frame data lives only in the JSON file at `outputPath`. +Frame data lives only in the JSON file at `outputPath`; internal metadata fields are not included in the response. diff --git a/.agents/skills/uloop-replay-input/SKILL.md b/.agents/skills/uloop-replay-input/SKILL.md index d69e1ad07e..9a23fabd47 100644 --- a/.agents/skills/uloop-replay-input/SKILL.md +++ b/.agents/skills/uloop-replay-input/SKILL.md @@ -43,8 +43,7 @@ Replay injects the exact same input frame-by-frame, but the game must also be de ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** must be installed (`com.unity.inputsystem`) -- Use this only when the project already uses the New Input System. +- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. ## Output @@ -58,4 +57,3 @@ Returns JSON with: - `progress`: Replay progress (nullable float in 0.0 – 1.0) - `isReplaying`: Whether replay is currently active (nullable bool) -These are the only eight fields. There is no `loopCount`, `elapsedSeconds`, `overlayVisible`, or per-frame inspection data in the response. diff --git a/.agents/skills/uloop-run-tests/SKILL.md b/.agents/skills/uloop-run-tests/SKILL.md index ba6909c219..421e47b21d 100644 --- a/.agents/skills/uloop-run-tests/SKILL.md +++ b/.agents/skills/uloop-run-tests/SKILL.md @@ -8,11 +8,11 @@ description: "Run Unity Test Runner and report detailed results. Use for EditMod Execute Unity Test Runner. When tests fail, NUnit XML results with error messages and stack traces are automatically saved. Read the XML file at `xmlPath` for detailed failure diagnosis. -Before running `uloop run-tests`, run `uloop compile` for the same Unity project when the current task created, deleted, renamed, moved, or edited C# source files, test files, `.asmdef`, `.asmref`, package manifest files, or scripting define settings. This refreshes the AssetDatabase, lets Unity discover new tests, and surfaces compile errors before test execution. You may skip this compile step when rerunning tests without code or assembly-definition changes since the last successful compile. +Before running tests, run `uloop compile` first if you edited C# files, test files, `.asmdef`/`.asmref`, or package manifests since the last successful compile. This refreshes the AssetDatabase and surfaces compile errors before test execution. Before executing tests, `uloop run-tests` saves unsaved loaded Scene changes and unsaved current Prefab Stage changes by default. If saving fails, it returns `success: false`, keeps `testCount` at `0`, lists the unsaved items in `message`, and does not start the Unity Test Runner. -When a run returns `status: "NoTestsFound"` or `noTestsFound: true`, no matching tests were discovered. This is not the same as a failed test case: `hasFailures` remains `false`, `failedCount` remains `0`, and `noTestsFoundExplanation` explains the zero-discovery state for agents. When an unfiltered `--filter-type all` run also has `message` set to the no-tests message, read the `message` for asmdef hints. `run-tests` only appends those hints when it detects likely test assembly configuration issues; exact, regex, and assembly filter misses keep the plain no-tests message. +`NoTestsFound` means zero tests matched — not a test failure. Check `noTestsFoundExplanation` and `message` for asmdef hints. ## Usage diff --git a/.agents/skills/uloop-screenshot/SKILL.md b/.agents/skills/uloop-screenshot/SKILL.md index d10d199ea2..bc721a5ee7 100644 --- a/.agents/skills/uloop-screenshot/SKILL.md +++ b/.agents/skills/uloop-screenshot/SKILL.md @@ -67,9 +67,6 @@ uloop screenshot --window-name Project --match-mode prefix # Save screenshot to a specific directory uloop screenshot --output-directory /tmp/screenshots - -# Combine options -uloop screenshot --window-name Scene --resolution-scale 0.5 --output-directory /tmp/screenshots ``` ## Output diff --git a/.agents/skills/uloop-simulate-keyboard/SKILL.md b/.agents/skills/uloop-simulate-keyboard/SKILL.md index 6cb374cc93..7b65d6b135 100644 --- a/.agents/skills/uloop-simulate-keyboard/SKILL.md +++ b/.agents/skills/uloop-simulate-keyboard/SKILL.md @@ -42,16 +42,11 @@ uloop simulate-keyboard --action --key [options] Use `Press` for edge-triggered keyboard code such as `Keyboard.current.spaceKey.wasPressedThisFrame`. `KeyDown` emits one initial press edge, then only keeps the key held. It does not keep `wasPressedThisFrame` true while the key remains held. If a successful `Press` or `KeyDown` leaves `Keyboard.current..isPressed` true but runtime state does not change, do not immediately rewrite the user's runtime code to `isPressed`. First verify that the target component is active during the command, that it polls input in the configured Input System update phase, and that a missed `KeyDown` edge is followed by `KeyUp` before retrying. -Use `KeyDown` / `KeyUp` when the scenario intentionally needs a held key. - ### Pause Point Inspection (Standard for E2E) -- Use `UloopPausePoint.Pause("")` with `uloop-wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. -- Put the marker at a natural state transition after the app consumed the key, such as after a command is accepted, a state mutation is committed, an evaluation step resolves, or a dependent component is updated. Do not place it immediately after sending `simulate-keyboard`. -- If the key handler has local variables, intermediate calculations, or branch reasons that `execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop-get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. -- Treat `simulate-keyboard success=true`, generic action logs, and final durable counters as useful evidence, but not as paused-frame proof. -- If the response has `interruptedByPausePoint: true`, Unity is paused for inspection and the tool released its held input bookkeeping. If a `UloopPausePoint.Pause` marker caused the pause, `pausePointId` and `pausePointHitCount` identify it. Use `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. -- Use distinct marker ids for strict phases, for example `input-read`, `state-updated`, and `result-committed`. +For standard frame proof when this input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the key, not immediately after `simulate-keyboard`. + +- If `interruptedByPausePoint: true`, Unity is paused and input bookkeeping was released. `pausePointId` and `pausePointHitCount` identify the marker. `pressEdgeObserved` is still reported on pause-point interruptions. ### KeyDown/KeyUp Rules @@ -103,6 +98,5 @@ Returns JSON with: ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** must be installed (`com.unity.inputsystem`) -- Use this only when the project already uses the New Input System. +- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. - Game code must read input via Input System API (e.g. `Keyboard.current[Key.W].isPressed`), not legacy `Input.GetKey()` diff --git a/.agents/skills/uloop-simulate-mouse-input/SKILL.md b/.agents/skills/uloop-simulate-mouse-input/SKILL.md index 12b73569e4..8bc5b09e69 100644 --- a/.agents/skills/uloop-simulate-mouse-input/SKILL.md +++ b/.agents/skills/uloop-simulate-mouse-input/SKILL.md @@ -50,11 +50,9 @@ uloop simulate-mouse-input --action [options] ### Pause Point Inspection (Standard for E2E) -- Use `UloopPausePoint.Pause("")` with `uloop-wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. -- Place the pause point at a natural state transition after the app consumed the mouse input, such as after a command is accepted, a state mutation is committed, an evaluation step resolves, a tracked value changes, or a dependent component is updated. Do not place it immediately after sending `simulate-mouse-input`. -- If the mouse handler has local variables, intermediate calculations, or branch reasons that `execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop-get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. -- If the response has `interruptedByPausePoint: true`, Unity is paused for inspection and the tool released its held input bookkeeping. `pausePointId` and `pausePointHitCount` identify the pause point that paused Unity. Use `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. -- Use distinct ids for strict phases, for example `input-read`, `state-updated`, and `result-committed`. +For standard frame proof when this input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the mouse input, not immediately after `simulate-mouse-input`. + +- If `interruptedByPausePoint: true`, Unity is paused and input bookkeeping was released. `pausePointId` and `pausePointHitCount` identify the marker. - Remove temporary pause-point/log instrumentation before final validation when it was added only for inspection. ### Global Options (optional) @@ -66,14 +64,16 @@ uloop simulate-mouse-input --action [options] ## When to use this vs simulate-mouse-ui +All rows below assume the New Input System is installed. + | Scenario | Tool | |----------|------| | Click a Unity UI Button (IPointerClickHandler) | `simulate-mouse-ui` | -| Runtime logic reads `Mouse.current.leftButton` | `simulate-mouse-input` when the project uses the New Input System | -| Runtime logic reads right-click | `simulate-mouse-input --button Right` when the project uses the New Input System | +| Runtime logic reads `Mouse.current.leftButton` | `simulate-mouse-input` | +| Runtime logic reads right-click | `simulate-mouse-input --button Right` | | Drag a UI slider | `simulate-mouse-ui --action Drag` | -| Runtime logic reads `Mouse.current.delta` | `simulate-mouse-input --action MoveDelta` when the project uses the New Input System | -| Runtime logic reads `Mouse.current.scroll` | `simulate-mouse-input --action Scroll` when the project uses the New Input System | +| Runtime logic reads `Mouse.current.delta` | `simulate-mouse-input --action MoveDelta` | +| Runtime logic reads `Mouse.current.scroll` | `simulate-mouse-input --action Scroll` | ## Examples @@ -103,9 +103,8 @@ uloop simulate-mouse-input --action SmoothDelta --delta-x 300 --delta-y 0 --dura ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** must be installed (`com.unity.inputsystem`) +- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. - Game code must read input via Input System API (e.g. `Mouse.current.leftButton.wasPressedThisFrame`) -- Use this only when the project already uses the New Input System. ## Output @@ -121,4 +120,4 @@ Returns JSON with: - `pausePointHitCount`: The hit count for that `UloopPausePoint.Pause("")` - `pausePointHits` (array, nullable): Every marker hit during this input as `{id, hitCount}` entries, in hit order. Read this when one input may trigger several markers; `pausePointId` only names the latest one -There is no `deltaX`, `deltaY`, `scrollX`, `scrollY`, `duration`, or hit-element field in the response — only the issued action, button, target position, and Pause Point interruption state are echoed back. Verify visual outcome with a follow-up screenshot. +Verify visual outcome with a follow-up screenshot. diff --git a/.agents/skills/uloop-simulate-mouse-ui/SKILL.md b/.agents/skills/uloop-simulate-mouse-ui/SKILL.md index f5e2799aec..8a9a797b6b 100644 --- a/.agents/skills/uloop-simulate-mouse-ui/SKILL.md +++ b/.agents/skills/uloop-simulate-mouse-ui/SKILL.md @@ -57,7 +57,6 @@ uloop simulate-mouse-ui --action --x --y [options] - `DragStart` must be called before `DragMove` or `DragEnd` - `DragEnd` must be called to release an active drag — failing to call it leaves drag state stuck - Calling `DragMove` or `DragEnd` without an active drag returns an error -- `Drag`, `DragStart`, `DragMove`, and `DragEnd` only support `--button Left` ### Global Options (all optional, mutually exclusive) @@ -78,11 +77,8 @@ uloop simulate-mouse-ui --action --x --y [options] ## Pause Point Inspection (Standard for E2E) -- Use `UloopPausePoint.Pause("")` with `uloop wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. -- Place the pause point at a natural transition after the app consumed the UI event, such as after a command is accepted, a state mutation is committed, a tracked value changes, a UI/domain state syncs, or a success/failure/end condition is entered. -- If the UI handler has local variables, intermediate calculations, or branch reasons that `uloop execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. -- Treat `uloop simulate-mouse-ui` `success=true`, generic action logs, and final durable counters as useful evidence, not paused-frame proof. -- If a `UloopPausePoint.Pause` pauses Unity, inspect with `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. +For standard frame proof when this UI input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the UI event, not immediately after `simulate-mouse-ui`. + - Remove temporary pause-point/log instrumentation before final validation when it was added only for inspection. ## Examples @@ -138,6 +134,6 @@ Returns JSON with: - `endPositionX`: Drag end X coordinate (nullable float; populated for drag actions only) - `endPositionY`: Drag end Y coordinate (nullable float; populated for drag actions only) -These are the only eight fields. There is no `button`, `duration`, `dragSpeed`, raycast list, or pointer-event log in the response — verify the visual outcome with a follow-up `uloop screenshot --capture-mode rendering --annotate-elements`. +Verify the visual outcome with a follow-up `uloop screenshot --capture-mode rendering --annotate-elements`. Note: Click and LongPress on empty space (no UI element) still return `success = true` with `hitGameObjectName = null`. Drag actions on empty space return `success = false`. diff --git a/.agents/skills/uloop-wait-for-pause-point/SKILL.md b/.agents/skills/uloop-wait-for-pause-point/SKILL.md index 2ac9b2c58b..14699e0deb 100644 --- a/.agents/skills/uloop-wait-for-pause-point/SKILL.md +++ b/.agents/skills/uloop-wait-for-pause-point/SKILL.md @@ -47,7 +47,6 @@ Use `generation`, `enabledAtUtc`, and the hit sequence fields from the hit or st - Consider a pause point during E2E passes when transition-frame evidence would add confidence, even if durable state, logs, or screenshots can later confirm the final result. - Use this before reaching for `Time.timeScale`, sleeps, repeated polling, or after-the-fact `execute-dynamic-code`; those checks can supplement the paused-frame proof, but they are not substitutes. - If the value you need is a method local, an intermediate calculation, or a branch reason that `execute-dynamic-code` cannot reach, add a focused `Debug.Log` immediately before the marker and read it with `get-logs` while paused. Do not count the breakpoint check complete until the matching log has been read. -- Good pause points include after input is consumed, a command is accepted, a state mutation is committed, an evaluation step resolves, a tracked value changes, a UI/domain state syncs, or a success/failure/end condition is entered. - Treat the pause like a lightweight breakpoint for one important transition: combine nearby debug logs with paused-frame inspection to confirm the variables and component state at that point. - Do not treat `simulate-* success=true`, generic action logs, sleeps/retries, testing-only counters, or `Time.timeScale` changes as paused-frame proof. - Skip this only for ordinary persistent-state checks when you are not validating simulated input delivery, event ordering, or transition-frame fidelity. diff --git a/.codex/skills/uloop-clear-console/SKILL.md b/.codex/skills/uloop-clear-console/SKILL.md index 5c633ca30b..3cb9e2be3f 100644 --- a/.codex/skills/uloop-clear-console/SKILL.md +++ b/.codex/skills/uloop-clear-console/SKILL.md @@ -45,5 +45,4 @@ Returns JSON with: - `errorCount` (number): Errors cleared - `warningCount` (number): Warnings cleared - `logCount` (number): Info logs cleared -- `message` (string): Description of the result; carries the failure summary when the operation fails (e.g. `"Failed to clear console: ..."`) -- `errorMessage` (string): Currently always empty for this tool — read `message` for failure details +- `message` (string): Description of the result. On failure, this field carries the error summary. diff --git a/.codex/skills/uloop-control-play-mode/SKILL.md b/.codex/skills/uloop-control-play-mode/SKILL.md index 662c027629..bfdb9e9bc9 100644 --- a/.codex/skills/uloop-control-play-mode/SKILL.md +++ b/.codex/skills/uloop-control-play-mode/SKILL.md @@ -57,9 +57,6 @@ Returns JSON with the current play mode state: ## Notes -- Play action starts the game in the Unity Editor (also resumes from pause) -- Stop action exits play mode and returns to edit mode. If Play Mode was already stopped, `changed` is `false`, `wasAlreadyStopped` is `true`, and `message` is `Play mode was already stopped`. -- Pause action pauses the game while remaining in play mode -- Step action advances exactly one frame and leaves play mode paused (the Editor's Next Frame button; independent of Time.timeScale). Requires PlayMode to be running; repeat to walk transitions frame by frame -- Useful for automated testing workflows +- Stop on an already-stopped Editor sets `changed: false`, `wasAlreadyStopped: true` +- Step is independent of `Time.timeScale`; requires PlayMode to be running - The command waits for the requested state before returning. Increase `--timeout-seconds` for projects with slow PlayMode entry. diff --git a/.codex/skills/uloop-execute-dynamic-code/SKILL.md b/.codex/skills/uloop-execute-dynamic-code/SKILL.md index 45ce0d1acc..1b2c08ebf9 100644 --- a/.codex/skills/uloop-execute-dynamic-code/SKILL.md +++ b/.codex/skills/uloop-execute-dynamic-code/SKILL.md @@ -18,8 +18,7 @@ This tool can inspect reachable Unity state, such as GameObjects, components, pu 1. Read the relevant reference file(s) from the Code Examples section below 2. Construct C# code based on the reference examples 3. Execute with `--code` or `--code-file` using the active shell's quoting guidance -4. If execution fails, adjust code and retry -5. Report the execution result +4. Report the execution result ## Parameters @@ -48,8 +47,7 @@ Returns JSON: - `result`: string — value of the snippet's `return` statement (empty when omitted) - `logs`: string[] — execution messages from the dynamic-code tool; read Unity Console `Debug.Log` output with `get-logs` - `compilationErrors`: object[] — Roslyn diagnostics with `message`, `line`, `column`, `errorCode`, optional `hint` and `suggestions` -- `errorMessage`: string — top-level failure summary (empty on success) -- `error`: string — alias of `errorMessage` +- `error` / `errorMessage`: string — top-level failure summary (empty on success) - `updatedCode`: string|null — the wrapped form actually compiled (handy when debugging using-statement reordering) - `diagnosticsSummary`: string|null — compact summary when diagnostics are available - `diagnostics`: object[] — structured diagnostics; same shape as `compilationErrors`, usually populated together with it diff --git a/.codex/skills/uloop-find-game-objects/SKILL.md b/.codex/skills/uloop-find-game-objects/SKILL.md index 620c44e32c..24edd909a3 100644 --- a/.codex/skills/uloop-find-game-objects/SKILL.md +++ b/.codex/skills/uloop-find-game-objects/SKILL.md @@ -77,7 +77,7 @@ Returns JSON with: - `tag` (string): GameObject tag - `layer` (number): Layer index - `components` (array): Each entry has `type` (short name, e.g., `Rigidbody`), `fullTypeName` (e.g., `UnityEngine.Rigidbody`), and `properties` (array of Inspector-visible `{name, type, value}` pairs) -- `totalFound` (number): Number of results returned inline, or number exported for multi-selection file output. For search modes, this is after `--max-results` clipping and serialization. +- `totalFound` (number): Results returned (after `--max-results` clipping). For multi-selection file export, this is the number exported. - `errorMessage` (string): Top-level failure summary (empty on success) - `processingErrors` (array): Selected-mode per-GameObject serialization failures, each `{gameObjectName, gameObjectPath, error}`. Omitted/null or empty on clean runs. diff --git a/.codex/skills/uloop-focus-window/SKILL.md b/.codex/skills/uloop-focus-window/SKILL.md index 515bfa29c3..e2ca7a23bb 100644 --- a/.codex/skills/uloop-focus-window/SKILL.md +++ b/.codex/skills/uloop-focus-window/SKILL.md @@ -13,10 +13,6 @@ Bring Unity Editor window to front using OS-level commands. uloop focus-window ``` -## Parameters - -None. - ## Global Options | Option | Description | @@ -36,11 +32,7 @@ Returns JSON with: - `success`: Whether the focus operation succeeded - `message`: Status message (e.g. `Unity Editor window focused (PID: 12345)`, or the failure reason such as `Unity project not found` / `No running Unity process found for this project` / `Failed to focus Unity window: `) -These are the only two fields. There is no PID, window-handle, or platform field returned to the caller. - ## Notes - **Works even when Unity is busy** (compiling, domain reload, etc.) -- Uses OS-level commands (osascript on macOS, PowerShell on Windows) - Useful before `uloop capture-unity-window` to ensure the target window is visible -- Brings the main Unity Editor window to the foreground diff --git a/.codex/skills/uloop-get-logs/SKILL.md b/.codex/skills/uloop-get-logs/SKILL.md index 2ecb25aff7..57d1d97f62 100644 --- a/.codex/skills/uloop-get-logs/SKILL.md +++ b/.codex/skills/uloop-get-logs/SKILL.md @@ -8,8 +8,6 @@ description: "Read current Unity Console entries from a running Editor. Use duri Retrieve logs from Unity Console. -When a pause-point marker pauses Unity and the value you need is a method local, intermediate calculation, or branch reason, read the focused `Debug.Log` entry added immediately before the marker before resuming PlayMode. Use `--search-text ` so the marker and its log are checked as one breakpoint-style proof. - ## Usage ```bash @@ -54,10 +52,7 @@ uloop get-logs --search-text "Missing.*Component" --use-regex Returns JSON with: - `totalCount` (number): Total logs available before max-count clipping - `displayedCount` (number): Logs returned in this response (≤ `--max-count`) -- `logType` (string): The `--log-type` filter that was applied -- `maxCount` (number): The `--max-count` cap that was applied -- `searchText` (string): The `--search-text` filter that was applied (empty when omitted) -- `includeStackTrace` (boolean): Whether stack traces are included in `logs[]` +- Input filters (`logType`, `maxCount`, `searchText`, `includeStackTrace`) are echoed back in the response - `logs` (array): Each entry has: - `type` (string): `"Error"`, `"Warning"`, or `"Log"` - `message` (string): Log message body diff --git a/.codex/skills/uloop-launch/SKILL.md b/.codex/skills/uloop-launch/SKILL.md index 2bf0e0929b..525fe110b8 100644 --- a/.codex/skills/uloop-launch/SKILL.md +++ b/.codex/skills/uloop-launch/SKILL.md @@ -47,22 +47,22 @@ uloop launch --quit ## Output -- Prints detected Unity version -- Prints project path +Returns JSON with: +- `success`: whether the command completed +- `ready`: whether Unity CLI Loop is ready for commands +- `serverReady`: whether the Unity CLI Loop server accepted requests +- `projectIpcReady`: whether the project IPC path accepted tool requests +- `alreadyRunning`: whether an existing Unity process was reused +- `launched`: whether this command launched a Unity process +- `restarted`: whether this command stopped an existing process and launched a new one +- `quit`: whether this command stopped Unity without launching a new process +- `previousProcessId`: process ID stopped by restart or quit, when available +- `currentProcessId`: current Unity process ID, when available +- `projectRoot`: resolved project root +- `message`: readiness summary + +## Notes + - If Unity is already running, focuses the existing window and verifies tool readiness -- If the process scan is blocked by the environment (e.g. sandboxed `ps`), plain launch falls back to probing the project IPC; when Unity responds it reports `alreadyRunning: true` without focusing the window instead of failing. `--restart` and `--quit` still fail because they need the process id -- If launching or restarting, prints when it is waiting for Unity CLI Loop server readiness -- If launching or restarting, waits until Unity finishes startup and the CLI can connect to the project -- Successful launch, restart, existing-process, and quit paths return JSON with: - - `success`: whether the command completed - - `ready`: whether Unity CLI Loop is ready for commands - - `serverReady`: whether the Unity CLI Loop server accepted requests - - `projectIpcReady`: whether the project IPC path accepted tool requests - - `alreadyRunning`: whether an existing Unity process was reused - - `launched`: whether this command launched a Unity process - - `restarted`: whether this command stopped an existing process and launched a new one - - `quit`: whether this command stopped Unity without launching a new process - - `previousProcessId`: process ID stopped by restart or quit, when available - - `currentProcessId`: current Unity process ID, when available - - `projectRoot`: resolved project root - - `message`: readiness summary +- If process scan is blocked (e.g. sandboxed `ps`), plain launch falls back to IPC probing; `--restart` and `--quit` still fail because they need the process id +- The command waits until Unity finishes startup and the CLI can connect before returning diff --git a/.codex/skills/uloop-record-input/SKILL.md b/.codex/skills/uloop-record-input/SKILL.md index 6b1c530f38..a76d38cb33 100644 --- a/.codex/skills/uloop-record-input/SKILL.md +++ b/.codex/skills/uloop-record-input/SKILL.md @@ -39,8 +39,7 @@ Replay injects input frame-by-frame, so the game must also be deterministic to p ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** must be installed (`com.unity.inputsystem`) -- Use this only when the project already uses the New Input System. +- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. ## Output @@ -52,4 +51,4 @@ The CLI prints JSON with: - `totalFrames`: Number of frames recorded (nullable int; populated on `Stop` only) - `durationSeconds`: Recording duration in seconds (nullable float; populated on `Stop` only) -The CLI output contains only these six payload fields. Internal metadata such as `ver` is removed before printing. There is no `recordingId`, `startTimestamp`, `keysCaptured`, or per-frame data in the response — frame data lives only in the JSON file at `outputPath`. +Frame data lives only in the JSON file at `outputPath`; internal metadata fields are not included in the response. diff --git a/.codex/skills/uloop-replay-input/SKILL.md b/.codex/skills/uloop-replay-input/SKILL.md index d69e1ad07e..9a23fabd47 100644 --- a/.codex/skills/uloop-replay-input/SKILL.md +++ b/.codex/skills/uloop-replay-input/SKILL.md @@ -43,8 +43,7 @@ Replay injects the exact same input frame-by-frame, but the game must also be de ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** must be installed (`com.unity.inputsystem`) -- Use this only when the project already uses the New Input System. +- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. ## Output @@ -58,4 +57,3 @@ Returns JSON with: - `progress`: Replay progress (nullable float in 0.0 – 1.0) - `isReplaying`: Whether replay is currently active (nullable bool) -These are the only eight fields. There is no `loopCount`, `elapsedSeconds`, `overlayVisible`, or per-frame inspection data in the response. diff --git a/.codex/skills/uloop-run-tests/SKILL.md b/.codex/skills/uloop-run-tests/SKILL.md index ba6909c219..421e47b21d 100644 --- a/.codex/skills/uloop-run-tests/SKILL.md +++ b/.codex/skills/uloop-run-tests/SKILL.md @@ -8,11 +8,11 @@ description: "Run Unity Test Runner and report detailed results. Use for EditMod Execute Unity Test Runner. When tests fail, NUnit XML results with error messages and stack traces are automatically saved. Read the XML file at `xmlPath` for detailed failure diagnosis. -Before running `uloop run-tests`, run `uloop compile` for the same Unity project when the current task created, deleted, renamed, moved, or edited C# source files, test files, `.asmdef`, `.asmref`, package manifest files, or scripting define settings. This refreshes the AssetDatabase, lets Unity discover new tests, and surfaces compile errors before test execution. You may skip this compile step when rerunning tests without code or assembly-definition changes since the last successful compile. +Before running tests, run `uloop compile` first if you edited C# files, test files, `.asmdef`/`.asmref`, or package manifests since the last successful compile. This refreshes the AssetDatabase and surfaces compile errors before test execution. Before executing tests, `uloop run-tests` saves unsaved loaded Scene changes and unsaved current Prefab Stage changes by default. If saving fails, it returns `success: false`, keeps `testCount` at `0`, lists the unsaved items in `message`, and does not start the Unity Test Runner. -When a run returns `status: "NoTestsFound"` or `noTestsFound: true`, no matching tests were discovered. This is not the same as a failed test case: `hasFailures` remains `false`, `failedCount` remains `0`, and `noTestsFoundExplanation` explains the zero-discovery state for agents. When an unfiltered `--filter-type all` run also has `message` set to the no-tests message, read the `message` for asmdef hints. `run-tests` only appends those hints when it detects likely test assembly configuration issues; exact, regex, and assembly filter misses keep the plain no-tests message. +`NoTestsFound` means zero tests matched — not a test failure. Check `noTestsFoundExplanation` and `message` for asmdef hints. ## Usage diff --git a/.codex/skills/uloop-screenshot/SKILL.md b/.codex/skills/uloop-screenshot/SKILL.md index d10d199ea2..bc721a5ee7 100644 --- a/.codex/skills/uloop-screenshot/SKILL.md +++ b/.codex/skills/uloop-screenshot/SKILL.md @@ -67,9 +67,6 @@ uloop screenshot --window-name Project --match-mode prefix # Save screenshot to a specific directory uloop screenshot --output-directory /tmp/screenshots - -# Combine options -uloop screenshot --window-name Scene --resolution-scale 0.5 --output-directory /tmp/screenshots ``` ## Output diff --git a/.codex/skills/uloop-simulate-keyboard/SKILL.md b/.codex/skills/uloop-simulate-keyboard/SKILL.md index 6cb374cc93..7b65d6b135 100644 --- a/.codex/skills/uloop-simulate-keyboard/SKILL.md +++ b/.codex/skills/uloop-simulate-keyboard/SKILL.md @@ -42,16 +42,11 @@ uloop simulate-keyboard --action --key [options] Use `Press` for edge-triggered keyboard code such as `Keyboard.current.spaceKey.wasPressedThisFrame`. `KeyDown` emits one initial press edge, then only keeps the key held. It does not keep `wasPressedThisFrame` true while the key remains held. If a successful `Press` or `KeyDown` leaves `Keyboard.current..isPressed` true but runtime state does not change, do not immediately rewrite the user's runtime code to `isPressed`. First verify that the target component is active during the command, that it polls input in the configured Input System update phase, and that a missed `KeyDown` edge is followed by `KeyUp` before retrying. -Use `KeyDown` / `KeyUp` when the scenario intentionally needs a held key. - ### Pause Point Inspection (Standard for E2E) -- Use `UloopPausePoint.Pause("")` with `uloop-wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. -- Put the marker at a natural state transition after the app consumed the key, such as after a command is accepted, a state mutation is committed, an evaluation step resolves, or a dependent component is updated. Do not place it immediately after sending `simulate-keyboard`. -- If the key handler has local variables, intermediate calculations, or branch reasons that `execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop-get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. -- Treat `simulate-keyboard success=true`, generic action logs, and final durable counters as useful evidence, but not as paused-frame proof. -- If the response has `interruptedByPausePoint: true`, Unity is paused for inspection and the tool released its held input bookkeeping. If a `UloopPausePoint.Pause` marker caused the pause, `pausePointId` and `pausePointHitCount` identify it. Use `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. -- Use distinct marker ids for strict phases, for example `input-read`, `state-updated`, and `result-committed`. +For standard frame proof when this input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the key, not immediately after `simulate-keyboard`. + +- If `interruptedByPausePoint: true`, Unity is paused and input bookkeeping was released. `pausePointId` and `pausePointHitCount` identify the marker. `pressEdgeObserved` is still reported on pause-point interruptions. ### KeyDown/KeyUp Rules @@ -103,6 +98,5 @@ Returns JSON with: ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** must be installed (`com.unity.inputsystem`) -- Use this only when the project already uses the New Input System. +- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. - Game code must read input via Input System API (e.g. `Keyboard.current[Key.W].isPressed`), not legacy `Input.GetKey()` diff --git a/.codex/skills/uloop-simulate-mouse-input/SKILL.md b/.codex/skills/uloop-simulate-mouse-input/SKILL.md index 12b73569e4..8bc5b09e69 100644 --- a/.codex/skills/uloop-simulate-mouse-input/SKILL.md +++ b/.codex/skills/uloop-simulate-mouse-input/SKILL.md @@ -50,11 +50,9 @@ uloop simulate-mouse-input --action [options] ### Pause Point Inspection (Standard for E2E) -- Use `UloopPausePoint.Pause("")` with `uloop-wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. -- Place the pause point at a natural state transition after the app consumed the mouse input, such as after a command is accepted, a state mutation is committed, an evaluation step resolves, a tracked value changes, or a dependent component is updated. Do not place it immediately after sending `simulate-mouse-input`. -- If the mouse handler has local variables, intermediate calculations, or branch reasons that `execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop-get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. -- If the response has `interruptedByPausePoint: true`, Unity is paused for inspection and the tool released its held input bookkeeping. `pausePointId` and `pausePointHitCount` identify the pause point that paused Unity. Use `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. -- Use distinct ids for strict phases, for example `input-read`, `state-updated`, and `result-committed`. +For standard frame proof when this input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the mouse input, not immediately after `simulate-mouse-input`. + +- If `interruptedByPausePoint: true`, Unity is paused and input bookkeeping was released. `pausePointId` and `pausePointHitCount` identify the marker. - Remove temporary pause-point/log instrumentation before final validation when it was added only for inspection. ### Global Options (optional) @@ -66,14 +64,16 @@ uloop simulate-mouse-input --action [options] ## When to use this vs simulate-mouse-ui +All rows below assume the New Input System is installed. + | Scenario | Tool | |----------|------| | Click a Unity UI Button (IPointerClickHandler) | `simulate-mouse-ui` | -| Runtime logic reads `Mouse.current.leftButton` | `simulate-mouse-input` when the project uses the New Input System | -| Runtime logic reads right-click | `simulate-mouse-input --button Right` when the project uses the New Input System | +| Runtime logic reads `Mouse.current.leftButton` | `simulate-mouse-input` | +| Runtime logic reads right-click | `simulate-mouse-input --button Right` | | Drag a UI slider | `simulate-mouse-ui --action Drag` | -| Runtime logic reads `Mouse.current.delta` | `simulate-mouse-input --action MoveDelta` when the project uses the New Input System | -| Runtime logic reads `Mouse.current.scroll` | `simulate-mouse-input --action Scroll` when the project uses the New Input System | +| Runtime logic reads `Mouse.current.delta` | `simulate-mouse-input --action MoveDelta` | +| Runtime logic reads `Mouse.current.scroll` | `simulate-mouse-input --action Scroll` | ## Examples @@ -103,9 +103,8 @@ uloop simulate-mouse-input --action SmoothDelta --delta-x 300 --delta-y 0 --dura ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** must be installed (`com.unity.inputsystem`) +- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. - Game code must read input via Input System API (e.g. `Mouse.current.leftButton.wasPressedThisFrame`) -- Use this only when the project already uses the New Input System. ## Output @@ -121,4 +120,4 @@ Returns JSON with: - `pausePointHitCount`: The hit count for that `UloopPausePoint.Pause("")` - `pausePointHits` (array, nullable): Every marker hit during this input as `{id, hitCount}` entries, in hit order. Read this when one input may trigger several markers; `pausePointId` only names the latest one -There is no `deltaX`, `deltaY`, `scrollX`, `scrollY`, `duration`, or hit-element field in the response — only the issued action, button, target position, and Pause Point interruption state are echoed back. Verify visual outcome with a follow-up screenshot. +Verify visual outcome with a follow-up screenshot. diff --git a/.codex/skills/uloop-simulate-mouse-ui/SKILL.md b/.codex/skills/uloop-simulate-mouse-ui/SKILL.md index f5e2799aec..8a9a797b6b 100644 --- a/.codex/skills/uloop-simulate-mouse-ui/SKILL.md +++ b/.codex/skills/uloop-simulate-mouse-ui/SKILL.md @@ -57,7 +57,6 @@ uloop simulate-mouse-ui --action --x --y [options] - `DragStart` must be called before `DragMove` or `DragEnd` - `DragEnd` must be called to release an active drag — failing to call it leaves drag state stuck - Calling `DragMove` or `DragEnd` without an active drag returns an error -- `Drag`, `DragStart`, `DragMove`, and `DragEnd` only support `--button Left` ### Global Options (all optional, mutually exclusive) @@ -78,11 +77,8 @@ uloop simulate-mouse-ui --action --x --y [options] ## Pause Point Inspection (Standard for E2E) -- Use `UloopPausePoint.Pause("")` with `uloop wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. -- Place the pause point at a natural transition after the app consumed the UI event, such as after a command is accepted, a state mutation is committed, a tracked value changes, a UI/domain state syncs, or a success/failure/end condition is entered. -- If the UI handler has local variables, intermediate calculations, or branch reasons that `uloop execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. -- Treat `uloop simulate-mouse-ui` `success=true`, generic action logs, and final durable counters as useful evidence, not paused-frame proof. -- If a `UloopPausePoint.Pause` pauses Unity, inspect with `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. +For standard frame proof when this UI input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the UI event, not immediately after `simulate-mouse-ui`. + - Remove temporary pause-point/log instrumentation before final validation when it was added only for inspection. ## Examples @@ -138,6 +134,6 @@ Returns JSON with: - `endPositionX`: Drag end X coordinate (nullable float; populated for drag actions only) - `endPositionY`: Drag end Y coordinate (nullable float; populated for drag actions only) -These are the only eight fields. There is no `button`, `duration`, `dragSpeed`, raycast list, or pointer-event log in the response — verify the visual outcome with a follow-up `uloop screenshot --capture-mode rendering --annotate-elements`. +Verify the visual outcome with a follow-up `uloop screenshot --capture-mode rendering --annotate-elements`. Note: Click and LongPress on empty space (no UI element) still return `success = true` with `hitGameObjectName = null`. Drag actions on empty space return `success = false`. diff --git a/.codex/skills/uloop-wait-for-pause-point/SKILL.md b/.codex/skills/uloop-wait-for-pause-point/SKILL.md index 2ac9b2c58b..14699e0deb 100644 --- a/.codex/skills/uloop-wait-for-pause-point/SKILL.md +++ b/.codex/skills/uloop-wait-for-pause-point/SKILL.md @@ -47,7 +47,6 @@ Use `generation`, `enabledAtUtc`, and the hit sequence fields from the hit or st - Consider a pause point during E2E passes when transition-frame evidence would add confidence, even if durable state, logs, or screenshots can later confirm the final result. - Use this before reaching for `Time.timeScale`, sleeps, repeated polling, or after-the-fact `execute-dynamic-code`; those checks can supplement the paused-frame proof, but they are not substitutes. - If the value you need is a method local, an intermediate calculation, or a branch reason that `execute-dynamic-code` cannot reach, add a focused `Debug.Log` immediately before the marker and read it with `get-logs` while paused. Do not count the breakpoint check complete until the matching log has been read. -- Good pause points include after input is consumed, a command is accepted, a state mutation is committed, an evaluation step resolves, a tracked value changes, a UI/domain state syncs, or a success/failure/end condition is entered. - Treat the pause like a lightweight breakpoint for one important transition: combine nearby debug logs with paused-frame inspection to confirm the variables and component state at that point. - Do not treat `simulate-* success=true`, generic action logs, sleeps/retries, testing-only counters, or `Time.timeScale` changes as paused-frame proof. - Skip this only for ordinary persistent-state checks when you are not validating simulated input delivery, event ordering, or transition-frame fidelity. diff --git a/Packages/src/Editor/CliOnlyTools~/FocusWindow/Skill/SKILL.md b/Packages/src/Editor/CliOnlyTools~/FocusWindow/Skill/SKILL.md index 515bfa29c3..e2ca7a23bb 100644 --- a/Packages/src/Editor/CliOnlyTools~/FocusWindow/Skill/SKILL.md +++ b/Packages/src/Editor/CliOnlyTools~/FocusWindow/Skill/SKILL.md @@ -13,10 +13,6 @@ Bring Unity Editor window to front using OS-level commands. uloop focus-window ``` -## Parameters - -None. - ## Global Options | Option | Description | @@ -36,11 +32,7 @@ Returns JSON with: - `success`: Whether the focus operation succeeded - `message`: Status message (e.g. `Unity Editor window focused (PID: 12345)`, or the failure reason such as `Unity project not found` / `No running Unity process found for this project` / `Failed to focus Unity window: `) -These are the only two fields. There is no PID, window-handle, or platform field returned to the caller. - ## Notes - **Works even when Unity is busy** (compiling, domain reload, etc.) -- Uses OS-level commands (osascript on macOS, PowerShell on Windows) - Useful before `uloop capture-unity-window` to ensure the target window is visible -- Brings the main Unity Editor window to the foreground diff --git a/Packages/src/Editor/CliOnlyTools~/Launch/Skill/SKILL.md b/Packages/src/Editor/CliOnlyTools~/Launch/Skill/SKILL.md index 2bf0e0929b..525fe110b8 100644 --- a/Packages/src/Editor/CliOnlyTools~/Launch/Skill/SKILL.md +++ b/Packages/src/Editor/CliOnlyTools~/Launch/Skill/SKILL.md @@ -47,22 +47,22 @@ uloop launch --quit ## Output -- Prints detected Unity version -- Prints project path +Returns JSON with: +- `success`: whether the command completed +- `ready`: whether Unity CLI Loop is ready for commands +- `serverReady`: whether the Unity CLI Loop server accepted requests +- `projectIpcReady`: whether the project IPC path accepted tool requests +- `alreadyRunning`: whether an existing Unity process was reused +- `launched`: whether this command launched a Unity process +- `restarted`: whether this command stopped an existing process and launched a new one +- `quit`: whether this command stopped Unity without launching a new process +- `previousProcessId`: process ID stopped by restart or quit, when available +- `currentProcessId`: current Unity process ID, when available +- `projectRoot`: resolved project root +- `message`: readiness summary + +## Notes + - If Unity is already running, focuses the existing window and verifies tool readiness -- If the process scan is blocked by the environment (e.g. sandboxed `ps`), plain launch falls back to probing the project IPC; when Unity responds it reports `alreadyRunning: true` without focusing the window instead of failing. `--restart` and `--quit` still fail because they need the process id -- If launching or restarting, prints when it is waiting for Unity CLI Loop server readiness -- If launching or restarting, waits until Unity finishes startup and the CLI can connect to the project -- Successful launch, restart, existing-process, and quit paths return JSON with: - - `success`: whether the command completed - - `ready`: whether Unity CLI Loop is ready for commands - - `serverReady`: whether the Unity CLI Loop server accepted requests - - `projectIpcReady`: whether the project IPC path accepted tool requests - - `alreadyRunning`: whether an existing Unity process was reused - - `launched`: whether this command launched a Unity process - - `restarted`: whether this command stopped an existing process and launched a new one - - `quit`: whether this command stopped Unity without launching a new process - - `previousProcessId`: process ID stopped by restart or quit, when available - - `currentProcessId`: current Unity process ID, when available - - `projectRoot`: resolved project root - - `message`: readiness summary +- If process scan is blocked (e.g. sandboxed `ps`), plain launch falls back to IPC probing; `--restart` and `--quit` still fail because they need the process id +- The command waits until Unity finishes startup and the CLI can connect before returning diff --git a/Packages/src/Editor/CliOnlyTools~/PausePoint/Skill/SKILL.md b/Packages/src/Editor/CliOnlyTools~/PausePoint/Skill/SKILL.md index 2ac9b2c58b..14699e0deb 100644 --- a/Packages/src/Editor/CliOnlyTools~/PausePoint/Skill/SKILL.md +++ b/Packages/src/Editor/CliOnlyTools~/PausePoint/Skill/SKILL.md @@ -47,7 +47,6 @@ Use `generation`, `enabledAtUtc`, and the hit sequence fields from the hit or st - Consider a pause point during E2E passes when transition-frame evidence would add confidence, even if durable state, logs, or screenshots can later confirm the final result. - Use this before reaching for `Time.timeScale`, sleeps, repeated polling, or after-the-fact `execute-dynamic-code`; those checks can supplement the paused-frame proof, but they are not substitutes. - If the value you need is a method local, an intermediate calculation, or a branch reason that `execute-dynamic-code` cannot reach, add a focused `Debug.Log` immediately before the marker and read it with `get-logs` while paused. Do not count the breakpoint check complete until the matching log has been read. -- Good pause points include after input is consumed, a command is accepted, a state mutation is committed, an evaluation step resolves, a tracked value changes, a UI/domain state syncs, or a success/failure/end condition is entered. - Treat the pause like a lightweight breakpoint for one important transition: combine nearby debug logs with paused-frame inspection to confirm the variables and component state at that point. - Do not treat `simulate-* success=true`, generic action logs, sleeps/retries, testing-only counters, or `Time.timeScale` changes as paused-frame proof. - Skip this only for ordinary persistent-state checks when you are not validating simulated input delivery, event ordering, or transition-frame fidelity. diff --git a/Packages/src/Editor/FirstPartyTools/ClearConsole/Skill/SKILL.md b/Packages/src/Editor/FirstPartyTools/ClearConsole/Skill/SKILL.md index 5c633ca30b..3cb9e2be3f 100644 --- a/Packages/src/Editor/FirstPartyTools/ClearConsole/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/ClearConsole/Skill/SKILL.md @@ -45,5 +45,4 @@ Returns JSON with: - `errorCount` (number): Errors cleared - `warningCount` (number): Warnings cleared - `logCount` (number): Info logs cleared -- `message` (string): Description of the result; carries the failure summary when the operation fails (e.g. `"Failed to clear console: ..."`) -- `errorMessage` (string): Currently always empty for this tool — read `message` for failure details +- `message` (string): Description of the result. On failure, this field carries the error summary. diff --git a/Packages/src/Editor/FirstPartyTools/ControlPlayMode/Skill/SKILL.md b/Packages/src/Editor/FirstPartyTools/ControlPlayMode/Skill/SKILL.md index 662c027629..bfdb9e9bc9 100644 --- a/Packages/src/Editor/FirstPartyTools/ControlPlayMode/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/ControlPlayMode/Skill/SKILL.md @@ -57,9 +57,6 @@ Returns JSON with the current play mode state: ## Notes -- Play action starts the game in the Unity Editor (also resumes from pause) -- Stop action exits play mode and returns to edit mode. If Play Mode was already stopped, `changed` is `false`, `wasAlreadyStopped` is `true`, and `message` is `Play mode was already stopped`. -- Pause action pauses the game while remaining in play mode -- Step action advances exactly one frame and leaves play mode paused (the Editor's Next Frame button; independent of Time.timeScale). Requires PlayMode to be running; repeat to walk transitions frame by frame -- Useful for automated testing workflows +- Stop on an already-stopped Editor sets `changed: false`, `wasAlreadyStopped: true` +- Step is independent of `Time.timeScale`; requires PlayMode to be running - The command waits for the requested state before returning. Increase `--timeout-seconds` for projects with slow PlayMode entry. diff --git a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/SKILL.md b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/SKILL.md index 45ce0d1acc..1b2c08ebf9 100644 --- a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/SKILL.md @@ -18,8 +18,7 @@ This tool can inspect reachable Unity state, such as GameObjects, components, pu 1. Read the relevant reference file(s) from the Code Examples section below 2. Construct C# code based on the reference examples 3. Execute with `--code` or `--code-file` using the active shell's quoting guidance -4. If execution fails, adjust code and retry -5. Report the execution result +4. Report the execution result ## Parameters @@ -48,8 +47,7 @@ Returns JSON: - `result`: string — value of the snippet's `return` statement (empty when omitted) - `logs`: string[] — execution messages from the dynamic-code tool; read Unity Console `Debug.Log` output with `get-logs` - `compilationErrors`: object[] — Roslyn diagnostics with `message`, `line`, `column`, `errorCode`, optional `hint` and `suggestions` -- `errorMessage`: string — top-level failure summary (empty on success) -- `error`: string — alias of `errorMessage` +- `error` / `errorMessage`: string — top-level failure summary (empty on success) - `updatedCode`: string|null — the wrapped form actually compiled (handy when debugging using-statement reordering) - `diagnosticsSummary`: string|null — compact summary when diagnostics are available - `diagnostics`: object[] — structured diagnostics; same shape as `compilationErrors`, usually populated together with it diff --git a/Packages/src/Editor/FirstPartyTools/FindGameObjects/Skill/SKILL.md b/Packages/src/Editor/FirstPartyTools/FindGameObjects/Skill/SKILL.md index 620c44e32c..24edd909a3 100644 --- a/Packages/src/Editor/FirstPartyTools/FindGameObjects/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/FindGameObjects/Skill/SKILL.md @@ -77,7 +77,7 @@ Returns JSON with: - `tag` (string): GameObject tag - `layer` (number): Layer index - `components` (array): Each entry has `type` (short name, e.g., `Rigidbody`), `fullTypeName` (e.g., `UnityEngine.Rigidbody`), and `properties` (array of Inspector-visible `{name, type, value}` pairs) -- `totalFound` (number): Number of results returned inline, or number exported for multi-selection file output. For search modes, this is after `--max-results` clipping and serialization. +- `totalFound` (number): Results returned (after `--max-results` clipping). For multi-selection file export, this is the number exported. - `errorMessage` (string): Top-level failure summary (empty on success) - `processingErrors` (array): Selected-mode per-GameObject serialization failures, each `{gameObjectName, gameObjectPath, error}`. Omitted/null or empty on clean runs. diff --git a/Packages/src/Editor/FirstPartyTools/GetLogs/Skill/SKILL.md b/Packages/src/Editor/FirstPartyTools/GetLogs/Skill/SKILL.md index 2ecb25aff7..57d1d97f62 100644 --- a/Packages/src/Editor/FirstPartyTools/GetLogs/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/GetLogs/Skill/SKILL.md @@ -8,8 +8,6 @@ description: "Read current Unity Console entries from a running Editor. Use duri Retrieve logs from Unity Console. -When a pause-point marker pauses Unity and the value you need is a method local, intermediate calculation, or branch reason, read the focused `Debug.Log` entry added immediately before the marker before resuming PlayMode. Use `--search-text ` so the marker and its log are checked as one breakpoint-style proof. - ## Usage ```bash @@ -54,10 +52,7 @@ uloop get-logs --search-text "Missing.*Component" --use-regex Returns JSON with: - `totalCount` (number): Total logs available before max-count clipping - `displayedCount` (number): Logs returned in this response (≤ `--max-count`) -- `logType` (string): The `--log-type` filter that was applied -- `maxCount` (number): The `--max-count` cap that was applied -- `searchText` (string): The `--search-text` filter that was applied (empty when omitted) -- `includeStackTrace` (boolean): Whether stack traces are included in `logs[]` +- Input filters (`logType`, `maxCount`, `searchText`, `includeStackTrace`) are echoed back in the response - `logs` (array): Each entry has: - `type` (string): `"Error"`, `"Warning"`, or `"Log"` - `message` (string): Log message body diff --git a/Packages/src/Editor/FirstPartyTools/RecordInput/Skill/SKILL.md b/Packages/src/Editor/FirstPartyTools/RecordInput/Skill/SKILL.md index 6b1c530f38..a76d38cb33 100644 --- a/Packages/src/Editor/FirstPartyTools/RecordInput/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/RecordInput/Skill/SKILL.md @@ -39,8 +39,7 @@ Replay injects input frame-by-frame, so the game must also be deterministic to p ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** must be installed (`com.unity.inputsystem`) -- Use this only when the project already uses the New Input System. +- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. ## Output @@ -52,4 +51,4 @@ The CLI prints JSON with: - `totalFrames`: Number of frames recorded (nullable int; populated on `Stop` only) - `durationSeconds`: Recording duration in seconds (nullable float; populated on `Stop` only) -The CLI output contains only these six payload fields. Internal metadata such as `ver` is removed before printing. There is no `recordingId`, `startTimestamp`, `keysCaptured`, or per-frame data in the response — frame data lives only in the JSON file at `outputPath`. +Frame data lives only in the JSON file at `outputPath`; internal metadata fields are not included in the response. diff --git a/Packages/src/Editor/FirstPartyTools/ReplayInput/Skill/SKILL.md b/Packages/src/Editor/FirstPartyTools/ReplayInput/Skill/SKILL.md index d69e1ad07e..9a23fabd47 100644 --- a/Packages/src/Editor/FirstPartyTools/ReplayInput/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/ReplayInput/Skill/SKILL.md @@ -43,8 +43,7 @@ Replay injects the exact same input frame-by-frame, but the game must also be de ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** must be installed (`com.unity.inputsystem`) -- Use this only when the project already uses the New Input System. +- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. ## Output @@ -58,4 +57,3 @@ Returns JSON with: - `progress`: Replay progress (nullable float in 0.0 – 1.0) - `isReplaying`: Whether replay is currently active (nullable bool) -These are the only eight fields. There is no `loopCount`, `elapsedSeconds`, `overlayVisible`, or per-frame inspection data in the response. diff --git a/Packages/src/Editor/FirstPartyTools/RunTests/Skill/SKILL.md b/Packages/src/Editor/FirstPartyTools/RunTests/Skill/SKILL.md index ba6909c219..421e47b21d 100644 --- a/Packages/src/Editor/FirstPartyTools/RunTests/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/RunTests/Skill/SKILL.md @@ -8,11 +8,11 @@ description: "Run Unity Test Runner and report detailed results. Use for EditMod Execute Unity Test Runner. When tests fail, NUnit XML results with error messages and stack traces are automatically saved. Read the XML file at `xmlPath` for detailed failure diagnosis. -Before running `uloop run-tests`, run `uloop compile` for the same Unity project when the current task created, deleted, renamed, moved, or edited C# source files, test files, `.asmdef`, `.asmref`, package manifest files, or scripting define settings. This refreshes the AssetDatabase, lets Unity discover new tests, and surfaces compile errors before test execution. You may skip this compile step when rerunning tests without code or assembly-definition changes since the last successful compile. +Before running tests, run `uloop compile` first if you edited C# files, test files, `.asmdef`/`.asmref`, or package manifests since the last successful compile. This refreshes the AssetDatabase and surfaces compile errors before test execution. Before executing tests, `uloop run-tests` saves unsaved loaded Scene changes and unsaved current Prefab Stage changes by default. If saving fails, it returns `success: false`, keeps `testCount` at `0`, lists the unsaved items in `message`, and does not start the Unity Test Runner. -When a run returns `status: "NoTestsFound"` or `noTestsFound: true`, no matching tests were discovered. This is not the same as a failed test case: `hasFailures` remains `false`, `failedCount` remains `0`, and `noTestsFoundExplanation` explains the zero-discovery state for agents. When an unfiltered `--filter-type all` run also has `message` set to the no-tests message, read the `message` for asmdef hints. `run-tests` only appends those hints when it detects likely test assembly configuration issues; exact, regex, and assembly filter misses keep the plain no-tests message. +`NoTestsFound` means zero tests matched — not a test failure. Check `noTestsFoundExplanation` and `message` for asmdef hints. ## Usage diff --git a/Packages/src/Editor/FirstPartyTools/Screenshot/Skill/SKILL.md b/Packages/src/Editor/FirstPartyTools/Screenshot/Skill/SKILL.md index d10d199ea2..bc721a5ee7 100644 --- a/Packages/src/Editor/FirstPartyTools/Screenshot/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/Screenshot/Skill/SKILL.md @@ -67,9 +67,6 @@ uloop screenshot --window-name Project --match-mode prefix # Save screenshot to a specific directory uloop screenshot --output-directory /tmp/screenshots - -# Combine options -uloop screenshot --window-name Scene --resolution-scale 0.5 --output-directory /tmp/screenshots ``` ## Output diff --git a/Packages/src/Editor/FirstPartyTools/SimulateKeyboard/Skill/SKILL.md b/Packages/src/Editor/FirstPartyTools/SimulateKeyboard/Skill/SKILL.md index 6cb374cc93..7b65d6b135 100644 --- a/Packages/src/Editor/FirstPartyTools/SimulateKeyboard/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/SimulateKeyboard/Skill/SKILL.md @@ -42,16 +42,11 @@ uloop simulate-keyboard --action --key [options] Use `Press` for edge-triggered keyboard code such as `Keyboard.current.spaceKey.wasPressedThisFrame`. `KeyDown` emits one initial press edge, then only keeps the key held. It does not keep `wasPressedThisFrame` true while the key remains held. If a successful `Press` or `KeyDown` leaves `Keyboard.current..isPressed` true but runtime state does not change, do not immediately rewrite the user's runtime code to `isPressed`. First verify that the target component is active during the command, that it polls input in the configured Input System update phase, and that a missed `KeyDown` edge is followed by `KeyUp` before retrying. -Use `KeyDown` / `KeyUp` when the scenario intentionally needs a held key. - ### Pause Point Inspection (Standard for E2E) -- Use `UloopPausePoint.Pause("")` with `uloop-wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. -- Put the marker at a natural state transition after the app consumed the key, such as after a command is accepted, a state mutation is committed, an evaluation step resolves, or a dependent component is updated. Do not place it immediately after sending `simulate-keyboard`. -- If the key handler has local variables, intermediate calculations, or branch reasons that `execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop-get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. -- Treat `simulate-keyboard success=true`, generic action logs, and final durable counters as useful evidence, but not as paused-frame proof. -- If the response has `interruptedByPausePoint: true`, Unity is paused for inspection and the tool released its held input bookkeeping. If a `UloopPausePoint.Pause` marker caused the pause, `pausePointId` and `pausePointHitCount` identify it. Use `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. -- Use distinct marker ids for strict phases, for example `input-read`, `state-updated`, and `result-committed`. +For standard frame proof when this input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the key, not immediately after `simulate-keyboard`. + +- If `interruptedByPausePoint: true`, Unity is paused and input bookkeeping was released. `pausePointId` and `pausePointHitCount` identify the marker. `pressEdgeObserved` is still reported on pause-point interruptions. ### KeyDown/KeyUp Rules @@ -103,6 +98,5 @@ Returns JSON with: ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** must be installed (`com.unity.inputsystem`) -- Use this only when the project already uses the New Input System. +- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. - Game code must read input via Input System API (e.g. `Keyboard.current[Key.W].isPressed`), not legacy `Input.GetKey()` diff --git a/Packages/src/Editor/FirstPartyTools/SimulateMouseInput/Skill/SKILL.md b/Packages/src/Editor/FirstPartyTools/SimulateMouseInput/Skill/SKILL.md index 12b73569e4..8bc5b09e69 100644 --- a/Packages/src/Editor/FirstPartyTools/SimulateMouseInput/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/SimulateMouseInput/Skill/SKILL.md @@ -50,11 +50,9 @@ uloop simulate-mouse-input --action [options] ### Pause Point Inspection (Standard for E2E) -- Use `UloopPausePoint.Pause("")` with `uloop-wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. -- Place the pause point at a natural state transition after the app consumed the mouse input, such as after a command is accepted, a state mutation is committed, an evaluation step resolves, a tracked value changes, or a dependent component is updated. Do not place it immediately after sending `simulate-mouse-input`. -- If the mouse handler has local variables, intermediate calculations, or branch reasons that `execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop-get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. -- If the response has `interruptedByPausePoint: true`, Unity is paused for inspection and the tool released its held input bookkeeping. `pausePointId` and `pausePointHitCount` identify the pause point that paused Unity. Use `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. -- Use distinct ids for strict phases, for example `input-read`, `state-updated`, and `result-committed`. +For standard frame proof when this input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the mouse input, not immediately after `simulate-mouse-input`. + +- If `interruptedByPausePoint: true`, Unity is paused and input bookkeeping was released. `pausePointId` and `pausePointHitCount` identify the marker. - Remove temporary pause-point/log instrumentation before final validation when it was added only for inspection. ### Global Options (optional) @@ -66,14 +64,16 @@ uloop simulate-mouse-input --action [options] ## When to use this vs simulate-mouse-ui +All rows below assume the New Input System is installed. + | Scenario | Tool | |----------|------| | Click a Unity UI Button (IPointerClickHandler) | `simulate-mouse-ui` | -| Runtime logic reads `Mouse.current.leftButton` | `simulate-mouse-input` when the project uses the New Input System | -| Runtime logic reads right-click | `simulate-mouse-input --button Right` when the project uses the New Input System | +| Runtime logic reads `Mouse.current.leftButton` | `simulate-mouse-input` | +| Runtime logic reads right-click | `simulate-mouse-input --button Right` | | Drag a UI slider | `simulate-mouse-ui --action Drag` | -| Runtime logic reads `Mouse.current.delta` | `simulate-mouse-input --action MoveDelta` when the project uses the New Input System | -| Runtime logic reads `Mouse.current.scroll` | `simulate-mouse-input --action Scroll` when the project uses the New Input System | +| Runtime logic reads `Mouse.current.delta` | `simulate-mouse-input --action MoveDelta` | +| Runtime logic reads `Mouse.current.scroll` | `simulate-mouse-input --action Scroll` | ## Examples @@ -103,9 +103,8 @@ uloop simulate-mouse-input --action SmoothDelta --delta-x 300 --delta-y 0 --dura ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** must be installed (`com.unity.inputsystem`) +- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. - Game code must read input via Input System API (e.g. `Mouse.current.leftButton.wasPressedThisFrame`) -- Use this only when the project already uses the New Input System. ## Output @@ -121,4 +120,4 @@ Returns JSON with: - `pausePointHitCount`: The hit count for that `UloopPausePoint.Pause("")` - `pausePointHits` (array, nullable): Every marker hit during this input as `{id, hitCount}` entries, in hit order. Read this when one input may trigger several markers; `pausePointId` only names the latest one -There is no `deltaX`, `deltaY`, `scrollX`, `scrollY`, `duration`, or hit-element field in the response — only the issued action, button, target position, and Pause Point interruption state are echoed back. Verify visual outcome with a follow-up screenshot. +Verify visual outcome with a follow-up screenshot. diff --git a/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/Skill/SKILL.md b/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/Skill/SKILL.md index f5e2799aec..8a9a797b6b 100644 --- a/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/Skill/SKILL.md @@ -57,7 +57,6 @@ uloop simulate-mouse-ui --action --x --y [options] - `DragStart` must be called before `DragMove` or `DragEnd` - `DragEnd` must be called to release an active drag — failing to call it leaves drag state stuck - Calling `DragMove` or `DragEnd` without an active drag returns an error -- `Drag`, `DragStart`, `DragMove`, and `DragEnd` only support `--button Left` ### Global Options (all optional, mutually exclusive) @@ -78,11 +77,8 @@ uloop simulate-mouse-ui --action --x --y [options] ## Pause Point Inspection (Standard for E2E) -- Use `UloopPausePoint.Pause("")` with `uloop wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. -- Place the pause point at a natural transition after the app consumed the UI event, such as after a command is accepted, a state mutation is committed, a tracked value changes, a UI/domain state syncs, or a success/failure/end condition is entered. -- If the UI handler has local variables, intermediate calculations, or branch reasons that `uloop execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. -- Treat `uloop simulate-mouse-ui` `success=true`, generic action logs, and final durable counters as useful evidence, not paused-frame proof. -- If a `UloopPausePoint.Pause` pauses Unity, inspect with `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. +For standard frame proof when this UI input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the UI event, not immediately after `simulate-mouse-ui`. + - Remove temporary pause-point/log instrumentation before final validation when it was added only for inspection. ## Examples @@ -138,6 +134,6 @@ Returns JSON with: - `endPositionX`: Drag end X coordinate (nullable float; populated for drag actions only) - `endPositionY`: Drag end Y coordinate (nullable float; populated for drag actions only) -These are the only eight fields. There is no `button`, `duration`, `dragSpeed`, raycast list, or pointer-event log in the response — verify the visual outcome with a follow-up `uloop screenshot --capture-mode rendering --annotate-elements`. +Verify the visual outcome with a follow-up `uloop screenshot --capture-mode rendering --annotate-elements`. Note: Click and LongPress on empty space (no UI element) still return `success = true` with `hitGameObjectName = null`. Drag actions on empty space return `success = false`. From 652e816d764bcc27f817a9f78f2880c7c5637fe0 Mon Sep 17 00:00:00 2001 From: hatayama Date: Thu, 18 Jun 2026 16:42:38 +0900 Subject: [PATCH 4/9] Revert generated skill copies in .agents and .codex These are generated copies; revert to let the normal regeneration workflow handle the sync. --- .agents/skills/uloop-clear-console/SKILL.md | 3 +- .../skills/uloop-control-play-mode/SKILL.md | 7 ++-- .../uloop-execute-dynamic-code/SKILL.md | 6 ++-- .../skills/uloop-find-game-objects/SKILL.md | 2 +- .agents/skills/uloop-focus-window/SKILL.md | 8 +++++ .agents/skills/uloop-get-logs/SKILL.md | 7 +++- .agents/skills/uloop-launch/SKILL.md | 36 +++++++++---------- .agents/skills/uloop-record-input/SKILL.md | 5 +-- .agents/skills/uloop-replay-input/SKILL.md | 4 ++- .agents/skills/uloop-run-tests/SKILL.md | 4 +-- .agents/skills/uloop-screenshot/SKILL.md | 3 ++ .../skills/uloop-simulate-keyboard/SKILL.md | 14 +++++--- .../uloop-simulate-mouse-input/SKILL.md | 23 ++++++------ .../skills/uloop-simulate-mouse-ui/SKILL.md | 10 ++++-- .../uloop-wait-for-pause-point/SKILL.md | 1 + .codex/skills/uloop-clear-console/SKILL.md | 3 +- .../skills/uloop-control-play-mode/SKILL.md | 7 ++-- .../uloop-execute-dynamic-code/SKILL.md | 6 ++-- .../skills/uloop-find-game-objects/SKILL.md | 2 +- .codex/skills/uloop-focus-window/SKILL.md | 8 +++++ .codex/skills/uloop-get-logs/SKILL.md | 7 +++- .codex/skills/uloop-launch/SKILL.md | 36 +++++++++---------- .codex/skills/uloop-record-input/SKILL.md | 5 +-- .codex/skills/uloop-replay-input/SKILL.md | 4 ++- .codex/skills/uloop-run-tests/SKILL.md | 4 +-- .codex/skills/uloop-screenshot/SKILL.md | 3 ++ .../skills/uloop-simulate-keyboard/SKILL.md | 14 +++++--- .../uloop-simulate-mouse-input/SKILL.md | 23 ++++++------ .../skills/uloop-simulate-mouse-ui/SKILL.md | 10 ++++-- .../uloop-wait-for-pause-point/SKILL.md | 1 + 30 files changed, 170 insertions(+), 96 deletions(-) diff --git a/.agents/skills/uloop-clear-console/SKILL.md b/.agents/skills/uloop-clear-console/SKILL.md index 3cb9e2be3f..5c633ca30b 100644 --- a/.agents/skills/uloop-clear-console/SKILL.md +++ b/.agents/skills/uloop-clear-console/SKILL.md @@ -45,4 +45,5 @@ Returns JSON with: - `errorCount` (number): Errors cleared - `warningCount` (number): Warnings cleared - `logCount` (number): Info logs cleared -- `message` (string): Description of the result. On failure, this field carries the error summary. +- `message` (string): Description of the result; carries the failure summary when the operation fails (e.g. `"Failed to clear console: ..."`) +- `errorMessage` (string): Currently always empty for this tool — read `message` for failure details diff --git a/.agents/skills/uloop-control-play-mode/SKILL.md b/.agents/skills/uloop-control-play-mode/SKILL.md index bfdb9e9bc9..662c027629 100644 --- a/.agents/skills/uloop-control-play-mode/SKILL.md +++ b/.agents/skills/uloop-control-play-mode/SKILL.md @@ -57,6 +57,9 @@ Returns JSON with the current play mode state: ## Notes -- Stop on an already-stopped Editor sets `changed: false`, `wasAlreadyStopped: true` -- Step is independent of `Time.timeScale`; requires PlayMode to be running +- Play action starts the game in the Unity Editor (also resumes from pause) +- Stop action exits play mode and returns to edit mode. If Play Mode was already stopped, `changed` is `false`, `wasAlreadyStopped` is `true`, and `message` is `Play mode was already stopped`. +- Pause action pauses the game while remaining in play mode +- Step action advances exactly one frame and leaves play mode paused (the Editor's Next Frame button; independent of Time.timeScale). Requires PlayMode to be running; repeat to walk transitions frame by frame +- Useful for automated testing workflows - The command waits for the requested state before returning. Increase `--timeout-seconds` for projects with slow PlayMode entry. diff --git a/.agents/skills/uloop-execute-dynamic-code/SKILL.md b/.agents/skills/uloop-execute-dynamic-code/SKILL.md index 1b2c08ebf9..45ce0d1acc 100644 --- a/.agents/skills/uloop-execute-dynamic-code/SKILL.md +++ b/.agents/skills/uloop-execute-dynamic-code/SKILL.md @@ -18,7 +18,8 @@ This tool can inspect reachable Unity state, such as GameObjects, components, pu 1. Read the relevant reference file(s) from the Code Examples section below 2. Construct C# code based on the reference examples 3. Execute with `--code` or `--code-file` using the active shell's quoting guidance -4. Report the execution result +4. If execution fails, adjust code and retry +5. Report the execution result ## Parameters @@ -47,7 +48,8 @@ Returns JSON: - `result`: string — value of the snippet's `return` statement (empty when omitted) - `logs`: string[] — execution messages from the dynamic-code tool; read Unity Console `Debug.Log` output with `get-logs` - `compilationErrors`: object[] — Roslyn diagnostics with `message`, `line`, `column`, `errorCode`, optional `hint` and `suggestions` -- `error` / `errorMessage`: string — top-level failure summary (empty on success) +- `errorMessage`: string — top-level failure summary (empty on success) +- `error`: string — alias of `errorMessage` - `updatedCode`: string|null — the wrapped form actually compiled (handy when debugging using-statement reordering) - `diagnosticsSummary`: string|null — compact summary when diagnostics are available - `diagnostics`: object[] — structured diagnostics; same shape as `compilationErrors`, usually populated together with it diff --git a/.agents/skills/uloop-find-game-objects/SKILL.md b/.agents/skills/uloop-find-game-objects/SKILL.md index 24edd909a3..620c44e32c 100644 --- a/.agents/skills/uloop-find-game-objects/SKILL.md +++ b/.agents/skills/uloop-find-game-objects/SKILL.md @@ -77,7 +77,7 @@ Returns JSON with: - `tag` (string): GameObject tag - `layer` (number): Layer index - `components` (array): Each entry has `type` (short name, e.g., `Rigidbody`), `fullTypeName` (e.g., `UnityEngine.Rigidbody`), and `properties` (array of Inspector-visible `{name, type, value}` pairs) -- `totalFound` (number): Results returned (after `--max-results` clipping). For multi-selection file export, this is the number exported. +- `totalFound` (number): Number of results returned inline, or number exported for multi-selection file output. For search modes, this is after `--max-results` clipping and serialization. - `errorMessage` (string): Top-level failure summary (empty on success) - `processingErrors` (array): Selected-mode per-GameObject serialization failures, each `{gameObjectName, gameObjectPath, error}`. Omitted/null or empty on clean runs. diff --git a/.agents/skills/uloop-focus-window/SKILL.md b/.agents/skills/uloop-focus-window/SKILL.md index e2ca7a23bb..515bfa29c3 100644 --- a/.agents/skills/uloop-focus-window/SKILL.md +++ b/.agents/skills/uloop-focus-window/SKILL.md @@ -13,6 +13,10 @@ Bring Unity Editor window to front using OS-level commands. uloop focus-window ``` +## Parameters + +None. + ## Global Options | Option | Description | @@ -32,7 +36,11 @@ Returns JSON with: - `success`: Whether the focus operation succeeded - `message`: Status message (e.g. `Unity Editor window focused (PID: 12345)`, or the failure reason such as `Unity project not found` / `No running Unity process found for this project` / `Failed to focus Unity window: `) +These are the only two fields. There is no PID, window-handle, or platform field returned to the caller. + ## Notes - **Works even when Unity is busy** (compiling, domain reload, etc.) +- Uses OS-level commands (osascript on macOS, PowerShell on Windows) - Useful before `uloop capture-unity-window` to ensure the target window is visible +- Brings the main Unity Editor window to the foreground diff --git a/.agents/skills/uloop-get-logs/SKILL.md b/.agents/skills/uloop-get-logs/SKILL.md index 57d1d97f62..2ecb25aff7 100644 --- a/.agents/skills/uloop-get-logs/SKILL.md +++ b/.agents/skills/uloop-get-logs/SKILL.md @@ -8,6 +8,8 @@ description: "Read current Unity Console entries from a running Editor. Use duri Retrieve logs from Unity Console. +When a pause-point marker pauses Unity and the value you need is a method local, intermediate calculation, or branch reason, read the focused `Debug.Log` entry added immediately before the marker before resuming PlayMode. Use `--search-text ` so the marker and its log are checked as one breakpoint-style proof. + ## Usage ```bash @@ -52,7 +54,10 @@ uloop get-logs --search-text "Missing.*Component" --use-regex Returns JSON with: - `totalCount` (number): Total logs available before max-count clipping - `displayedCount` (number): Logs returned in this response (≤ `--max-count`) -- Input filters (`logType`, `maxCount`, `searchText`, `includeStackTrace`) are echoed back in the response +- `logType` (string): The `--log-type` filter that was applied +- `maxCount` (number): The `--max-count` cap that was applied +- `searchText` (string): The `--search-text` filter that was applied (empty when omitted) +- `includeStackTrace` (boolean): Whether stack traces are included in `logs[]` - `logs` (array): Each entry has: - `type` (string): `"Error"`, `"Warning"`, or `"Log"` - `message` (string): Log message body diff --git a/.agents/skills/uloop-launch/SKILL.md b/.agents/skills/uloop-launch/SKILL.md index 525fe110b8..2bf0e0929b 100644 --- a/.agents/skills/uloop-launch/SKILL.md +++ b/.agents/skills/uloop-launch/SKILL.md @@ -47,22 +47,22 @@ uloop launch --quit ## Output -Returns JSON with: -- `success`: whether the command completed -- `ready`: whether Unity CLI Loop is ready for commands -- `serverReady`: whether the Unity CLI Loop server accepted requests -- `projectIpcReady`: whether the project IPC path accepted tool requests -- `alreadyRunning`: whether an existing Unity process was reused -- `launched`: whether this command launched a Unity process -- `restarted`: whether this command stopped an existing process and launched a new one -- `quit`: whether this command stopped Unity without launching a new process -- `previousProcessId`: process ID stopped by restart or quit, when available -- `currentProcessId`: current Unity process ID, when available -- `projectRoot`: resolved project root -- `message`: readiness summary - -## Notes - +- Prints detected Unity version +- Prints project path - If Unity is already running, focuses the existing window and verifies tool readiness -- If process scan is blocked (e.g. sandboxed `ps`), plain launch falls back to IPC probing; `--restart` and `--quit` still fail because they need the process id -- The command waits until Unity finishes startup and the CLI can connect before returning +- If the process scan is blocked by the environment (e.g. sandboxed `ps`), plain launch falls back to probing the project IPC; when Unity responds it reports `alreadyRunning: true` without focusing the window instead of failing. `--restart` and `--quit` still fail because they need the process id +- If launching or restarting, prints when it is waiting for Unity CLI Loop server readiness +- If launching or restarting, waits until Unity finishes startup and the CLI can connect to the project +- Successful launch, restart, existing-process, and quit paths return JSON with: + - `success`: whether the command completed + - `ready`: whether Unity CLI Loop is ready for commands + - `serverReady`: whether the Unity CLI Loop server accepted requests + - `projectIpcReady`: whether the project IPC path accepted tool requests + - `alreadyRunning`: whether an existing Unity process was reused + - `launched`: whether this command launched a Unity process + - `restarted`: whether this command stopped an existing process and launched a new one + - `quit`: whether this command stopped Unity without launching a new process + - `previousProcessId`: process ID stopped by restart or quit, when available + - `currentProcessId`: current Unity process ID, when available + - `projectRoot`: resolved project root + - `message`: readiness summary diff --git a/.agents/skills/uloop-record-input/SKILL.md b/.agents/skills/uloop-record-input/SKILL.md index a76d38cb33..6b1c530f38 100644 --- a/.agents/skills/uloop-record-input/SKILL.md +++ b/.agents/skills/uloop-record-input/SKILL.md @@ -39,7 +39,8 @@ Replay injects input frame-by-frame, so the game must also be deterministic to p ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. +- **Input System package** must be installed (`com.unity.inputsystem`) +- Use this only when the project already uses the New Input System. ## Output @@ -51,4 +52,4 @@ The CLI prints JSON with: - `totalFrames`: Number of frames recorded (nullable int; populated on `Stop` only) - `durationSeconds`: Recording duration in seconds (nullable float; populated on `Stop` only) -Frame data lives only in the JSON file at `outputPath`; internal metadata fields are not included in the response. +The CLI output contains only these six payload fields. Internal metadata such as `ver` is removed before printing. There is no `recordingId`, `startTimestamp`, `keysCaptured`, or per-frame data in the response — frame data lives only in the JSON file at `outputPath`. diff --git a/.agents/skills/uloop-replay-input/SKILL.md b/.agents/skills/uloop-replay-input/SKILL.md index 9a23fabd47..d69e1ad07e 100644 --- a/.agents/skills/uloop-replay-input/SKILL.md +++ b/.agents/skills/uloop-replay-input/SKILL.md @@ -43,7 +43,8 @@ Replay injects the exact same input frame-by-frame, but the game must also be de ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. +- **Input System package** must be installed (`com.unity.inputsystem`) +- Use this only when the project already uses the New Input System. ## Output @@ -57,3 +58,4 @@ Returns JSON with: - `progress`: Replay progress (nullable float in 0.0 – 1.0) - `isReplaying`: Whether replay is currently active (nullable bool) +These are the only eight fields. There is no `loopCount`, `elapsedSeconds`, `overlayVisible`, or per-frame inspection data in the response. diff --git a/.agents/skills/uloop-run-tests/SKILL.md b/.agents/skills/uloop-run-tests/SKILL.md index 421e47b21d..ba6909c219 100644 --- a/.agents/skills/uloop-run-tests/SKILL.md +++ b/.agents/skills/uloop-run-tests/SKILL.md @@ -8,11 +8,11 @@ description: "Run Unity Test Runner and report detailed results. Use for EditMod Execute Unity Test Runner. When tests fail, NUnit XML results with error messages and stack traces are automatically saved. Read the XML file at `xmlPath` for detailed failure diagnosis. -Before running tests, run `uloop compile` first if you edited C# files, test files, `.asmdef`/`.asmref`, or package manifests since the last successful compile. This refreshes the AssetDatabase and surfaces compile errors before test execution. +Before running `uloop run-tests`, run `uloop compile` for the same Unity project when the current task created, deleted, renamed, moved, or edited C# source files, test files, `.asmdef`, `.asmref`, package manifest files, or scripting define settings. This refreshes the AssetDatabase, lets Unity discover new tests, and surfaces compile errors before test execution. You may skip this compile step when rerunning tests without code or assembly-definition changes since the last successful compile. Before executing tests, `uloop run-tests` saves unsaved loaded Scene changes and unsaved current Prefab Stage changes by default. If saving fails, it returns `success: false`, keeps `testCount` at `0`, lists the unsaved items in `message`, and does not start the Unity Test Runner. -`NoTestsFound` means zero tests matched — not a test failure. Check `noTestsFoundExplanation` and `message` for asmdef hints. +When a run returns `status: "NoTestsFound"` or `noTestsFound: true`, no matching tests were discovered. This is not the same as a failed test case: `hasFailures` remains `false`, `failedCount` remains `0`, and `noTestsFoundExplanation` explains the zero-discovery state for agents. When an unfiltered `--filter-type all` run also has `message` set to the no-tests message, read the `message` for asmdef hints. `run-tests` only appends those hints when it detects likely test assembly configuration issues; exact, regex, and assembly filter misses keep the plain no-tests message. ## Usage diff --git a/.agents/skills/uloop-screenshot/SKILL.md b/.agents/skills/uloop-screenshot/SKILL.md index bc721a5ee7..d10d199ea2 100644 --- a/.agents/skills/uloop-screenshot/SKILL.md +++ b/.agents/skills/uloop-screenshot/SKILL.md @@ -67,6 +67,9 @@ uloop screenshot --window-name Project --match-mode prefix # Save screenshot to a specific directory uloop screenshot --output-directory /tmp/screenshots + +# Combine options +uloop screenshot --window-name Scene --resolution-scale 0.5 --output-directory /tmp/screenshots ``` ## Output diff --git a/.agents/skills/uloop-simulate-keyboard/SKILL.md b/.agents/skills/uloop-simulate-keyboard/SKILL.md index 7b65d6b135..6cb374cc93 100644 --- a/.agents/skills/uloop-simulate-keyboard/SKILL.md +++ b/.agents/skills/uloop-simulate-keyboard/SKILL.md @@ -42,11 +42,16 @@ uloop simulate-keyboard --action --key [options] Use `Press` for edge-triggered keyboard code such as `Keyboard.current.spaceKey.wasPressedThisFrame`. `KeyDown` emits one initial press edge, then only keeps the key held. It does not keep `wasPressedThisFrame` true while the key remains held. If a successful `Press` or `KeyDown` leaves `Keyboard.current..isPressed` true but runtime state does not change, do not immediately rewrite the user's runtime code to `isPressed`. First verify that the target component is active during the command, that it polls input in the configured Input System update phase, and that a missed `KeyDown` edge is followed by `KeyUp` before retrying. -### Pause Point Inspection (Standard for E2E) +Use `KeyDown` / `KeyUp` when the scenario intentionally needs a held key. -For standard frame proof when this input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the key, not immediately after `simulate-keyboard`. +### Pause Point Inspection (Standard for E2E) -- If `interruptedByPausePoint: true`, Unity is paused and input bookkeeping was released. `pausePointId` and `pausePointHitCount` identify the marker. `pressEdgeObserved` is still reported on pause-point interruptions. +- Use `UloopPausePoint.Pause("")` with `uloop-wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. +- Put the marker at a natural state transition after the app consumed the key, such as after a command is accepted, a state mutation is committed, an evaluation step resolves, or a dependent component is updated. Do not place it immediately after sending `simulate-keyboard`. +- If the key handler has local variables, intermediate calculations, or branch reasons that `execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop-get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. +- Treat `simulate-keyboard success=true`, generic action logs, and final durable counters as useful evidence, but not as paused-frame proof. +- If the response has `interruptedByPausePoint: true`, Unity is paused for inspection and the tool released its held input bookkeeping. If a `UloopPausePoint.Pause` marker caused the pause, `pausePointId` and `pausePointHitCount` identify it. Use `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. +- Use distinct marker ids for strict phases, for example `input-read`, `state-updated`, and `result-committed`. ### KeyDown/KeyUp Rules @@ -98,5 +103,6 @@ Returns JSON with: ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. +- **Input System package** must be installed (`com.unity.inputsystem`) +- Use this only when the project already uses the New Input System. - Game code must read input via Input System API (e.g. `Keyboard.current[Key.W].isPressed`), not legacy `Input.GetKey()` diff --git a/.agents/skills/uloop-simulate-mouse-input/SKILL.md b/.agents/skills/uloop-simulate-mouse-input/SKILL.md index 8bc5b09e69..12b73569e4 100644 --- a/.agents/skills/uloop-simulate-mouse-input/SKILL.md +++ b/.agents/skills/uloop-simulate-mouse-input/SKILL.md @@ -50,9 +50,11 @@ uloop simulate-mouse-input --action [options] ### Pause Point Inspection (Standard for E2E) -For standard frame proof when this input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the mouse input, not immediately after `simulate-mouse-input`. - -- If `interruptedByPausePoint: true`, Unity is paused and input bookkeeping was released. `pausePointId` and `pausePointHitCount` identify the marker. +- Use `UloopPausePoint.Pause("")` with `uloop-wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. +- Place the pause point at a natural state transition after the app consumed the mouse input, such as after a command is accepted, a state mutation is committed, an evaluation step resolves, a tracked value changes, or a dependent component is updated. Do not place it immediately after sending `simulate-mouse-input`. +- If the mouse handler has local variables, intermediate calculations, or branch reasons that `execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop-get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. +- If the response has `interruptedByPausePoint: true`, Unity is paused for inspection and the tool released its held input bookkeeping. `pausePointId` and `pausePointHitCount` identify the pause point that paused Unity. Use `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. +- Use distinct ids for strict phases, for example `input-read`, `state-updated`, and `result-committed`. - Remove temporary pause-point/log instrumentation before final validation when it was added only for inspection. ### Global Options (optional) @@ -64,16 +66,14 @@ For standard frame proof when this input drives a state transition, follow the ` ## When to use this vs simulate-mouse-ui -All rows below assume the New Input System is installed. - | Scenario | Tool | |----------|------| | Click a Unity UI Button (IPointerClickHandler) | `simulate-mouse-ui` | -| Runtime logic reads `Mouse.current.leftButton` | `simulate-mouse-input` | -| Runtime logic reads right-click | `simulate-mouse-input --button Right` | +| Runtime logic reads `Mouse.current.leftButton` | `simulate-mouse-input` when the project uses the New Input System | +| Runtime logic reads right-click | `simulate-mouse-input --button Right` when the project uses the New Input System | | Drag a UI slider | `simulate-mouse-ui --action Drag` | -| Runtime logic reads `Mouse.current.delta` | `simulate-mouse-input --action MoveDelta` | -| Runtime logic reads `Mouse.current.scroll` | `simulate-mouse-input --action Scroll` | +| Runtime logic reads `Mouse.current.delta` | `simulate-mouse-input --action MoveDelta` when the project uses the New Input System | +| Runtime logic reads `Mouse.current.scroll` | `simulate-mouse-input --action Scroll` when the project uses the New Input System | ## Examples @@ -103,8 +103,9 @@ uloop simulate-mouse-input --action SmoothDelta --delta-x 300 --delta-y 0 --dura ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. +- **Input System package** must be installed (`com.unity.inputsystem`) - Game code must read input via Input System API (e.g. `Mouse.current.leftButton.wasPressedThisFrame`) +- Use this only when the project already uses the New Input System. ## Output @@ -120,4 +121,4 @@ Returns JSON with: - `pausePointHitCount`: The hit count for that `UloopPausePoint.Pause("")` - `pausePointHits` (array, nullable): Every marker hit during this input as `{id, hitCount}` entries, in hit order. Read this when one input may trigger several markers; `pausePointId` only names the latest one -Verify visual outcome with a follow-up screenshot. +There is no `deltaX`, `deltaY`, `scrollX`, `scrollY`, `duration`, or hit-element field in the response — only the issued action, button, target position, and Pause Point interruption state are echoed back. Verify visual outcome with a follow-up screenshot. diff --git a/.agents/skills/uloop-simulate-mouse-ui/SKILL.md b/.agents/skills/uloop-simulate-mouse-ui/SKILL.md index 8a9a797b6b..f5e2799aec 100644 --- a/.agents/skills/uloop-simulate-mouse-ui/SKILL.md +++ b/.agents/skills/uloop-simulate-mouse-ui/SKILL.md @@ -57,6 +57,7 @@ uloop simulate-mouse-ui --action --x --y [options] - `DragStart` must be called before `DragMove` or `DragEnd` - `DragEnd` must be called to release an active drag — failing to call it leaves drag state stuck - Calling `DragMove` or `DragEnd` without an active drag returns an error +- `Drag`, `DragStart`, `DragMove`, and `DragEnd` only support `--button Left` ### Global Options (all optional, mutually exclusive) @@ -77,8 +78,11 @@ uloop simulate-mouse-ui --action --x --y [options] ## Pause Point Inspection (Standard for E2E) -For standard frame proof when this UI input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the UI event, not immediately after `simulate-mouse-ui`. - +- Use `UloopPausePoint.Pause("")` with `uloop wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. +- Place the pause point at a natural transition after the app consumed the UI event, such as after a command is accepted, a state mutation is committed, a tracked value changes, a UI/domain state syncs, or a success/failure/end condition is entered. +- If the UI handler has local variables, intermediate calculations, or branch reasons that `uloop execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. +- Treat `uloop simulate-mouse-ui` `success=true`, generic action logs, and final durable counters as useful evidence, not paused-frame proof. +- If a `UloopPausePoint.Pause` pauses Unity, inspect with `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. - Remove temporary pause-point/log instrumentation before final validation when it was added only for inspection. ## Examples @@ -134,6 +138,6 @@ Returns JSON with: - `endPositionX`: Drag end X coordinate (nullable float; populated for drag actions only) - `endPositionY`: Drag end Y coordinate (nullable float; populated for drag actions only) -Verify the visual outcome with a follow-up `uloop screenshot --capture-mode rendering --annotate-elements`. +These are the only eight fields. There is no `button`, `duration`, `dragSpeed`, raycast list, or pointer-event log in the response — verify the visual outcome with a follow-up `uloop screenshot --capture-mode rendering --annotate-elements`. Note: Click and LongPress on empty space (no UI element) still return `success = true` with `hitGameObjectName = null`. Drag actions on empty space return `success = false`. diff --git a/.agents/skills/uloop-wait-for-pause-point/SKILL.md b/.agents/skills/uloop-wait-for-pause-point/SKILL.md index 14699e0deb..2ac9b2c58b 100644 --- a/.agents/skills/uloop-wait-for-pause-point/SKILL.md +++ b/.agents/skills/uloop-wait-for-pause-point/SKILL.md @@ -47,6 +47,7 @@ Use `generation`, `enabledAtUtc`, and the hit sequence fields from the hit or st - Consider a pause point during E2E passes when transition-frame evidence would add confidence, even if durable state, logs, or screenshots can later confirm the final result. - Use this before reaching for `Time.timeScale`, sleeps, repeated polling, or after-the-fact `execute-dynamic-code`; those checks can supplement the paused-frame proof, but they are not substitutes. - If the value you need is a method local, an intermediate calculation, or a branch reason that `execute-dynamic-code` cannot reach, add a focused `Debug.Log` immediately before the marker and read it with `get-logs` while paused. Do not count the breakpoint check complete until the matching log has been read. +- Good pause points include after input is consumed, a command is accepted, a state mutation is committed, an evaluation step resolves, a tracked value changes, a UI/domain state syncs, or a success/failure/end condition is entered. - Treat the pause like a lightweight breakpoint for one important transition: combine nearby debug logs with paused-frame inspection to confirm the variables and component state at that point. - Do not treat `simulate-* success=true`, generic action logs, sleeps/retries, testing-only counters, or `Time.timeScale` changes as paused-frame proof. - Skip this only for ordinary persistent-state checks when you are not validating simulated input delivery, event ordering, or transition-frame fidelity. diff --git a/.codex/skills/uloop-clear-console/SKILL.md b/.codex/skills/uloop-clear-console/SKILL.md index 3cb9e2be3f..5c633ca30b 100644 --- a/.codex/skills/uloop-clear-console/SKILL.md +++ b/.codex/skills/uloop-clear-console/SKILL.md @@ -45,4 +45,5 @@ Returns JSON with: - `errorCount` (number): Errors cleared - `warningCount` (number): Warnings cleared - `logCount` (number): Info logs cleared -- `message` (string): Description of the result. On failure, this field carries the error summary. +- `message` (string): Description of the result; carries the failure summary when the operation fails (e.g. `"Failed to clear console: ..."`) +- `errorMessage` (string): Currently always empty for this tool — read `message` for failure details diff --git a/.codex/skills/uloop-control-play-mode/SKILL.md b/.codex/skills/uloop-control-play-mode/SKILL.md index bfdb9e9bc9..662c027629 100644 --- a/.codex/skills/uloop-control-play-mode/SKILL.md +++ b/.codex/skills/uloop-control-play-mode/SKILL.md @@ -57,6 +57,9 @@ Returns JSON with the current play mode state: ## Notes -- Stop on an already-stopped Editor sets `changed: false`, `wasAlreadyStopped: true` -- Step is independent of `Time.timeScale`; requires PlayMode to be running +- Play action starts the game in the Unity Editor (also resumes from pause) +- Stop action exits play mode and returns to edit mode. If Play Mode was already stopped, `changed` is `false`, `wasAlreadyStopped` is `true`, and `message` is `Play mode was already stopped`. +- Pause action pauses the game while remaining in play mode +- Step action advances exactly one frame and leaves play mode paused (the Editor's Next Frame button; independent of Time.timeScale). Requires PlayMode to be running; repeat to walk transitions frame by frame +- Useful for automated testing workflows - The command waits for the requested state before returning. Increase `--timeout-seconds` for projects with slow PlayMode entry. diff --git a/.codex/skills/uloop-execute-dynamic-code/SKILL.md b/.codex/skills/uloop-execute-dynamic-code/SKILL.md index 1b2c08ebf9..45ce0d1acc 100644 --- a/.codex/skills/uloop-execute-dynamic-code/SKILL.md +++ b/.codex/skills/uloop-execute-dynamic-code/SKILL.md @@ -18,7 +18,8 @@ This tool can inspect reachable Unity state, such as GameObjects, components, pu 1. Read the relevant reference file(s) from the Code Examples section below 2. Construct C# code based on the reference examples 3. Execute with `--code` or `--code-file` using the active shell's quoting guidance -4. Report the execution result +4. If execution fails, adjust code and retry +5. Report the execution result ## Parameters @@ -47,7 +48,8 @@ Returns JSON: - `result`: string — value of the snippet's `return` statement (empty when omitted) - `logs`: string[] — execution messages from the dynamic-code tool; read Unity Console `Debug.Log` output with `get-logs` - `compilationErrors`: object[] — Roslyn diagnostics with `message`, `line`, `column`, `errorCode`, optional `hint` and `suggestions` -- `error` / `errorMessage`: string — top-level failure summary (empty on success) +- `errorMessage`: string — top-level failure summary (empty on success) +- `error`: string — alias of `errorMessage` - `updatedCode`: string|null — the wrapped form actually compiled (handy when debugging using-statement reordering) - `diagnosticsSummary`: string|null — compact summary when diagnostics are available - `diagnostics`: object[] — structured diagnostics; same shape as `compilationErrors`, usually populated together with it diff --git a/.codex/skills/uloop-find-game-objects/SKILL.md b/.codex/skills/uloop-find-game-objects/SKILL.md index 24edd909a3..620c44e32c 100644 --- a/.codex/skills/uloop-find-game-objects/SKILL.md +++ b/.codex/skills/uloop-find-game-objects/SKILL.md @@ -77,7 +77,7 @@ Returns JSON with: - `tag` (string): GameObject tag - `layer` (number): Layer index - `components` (array): Each entry has `type` (short name, e.g., `Rigidbody`), `fullTypeName` (e.g., `UnityEngine.Rigidbody`), and `properties` (array of Inspector-visible `{name, type, value}` pairs) -- `totalFound` (number): Results returned (after `--max-results` clipping). For multi-selection file export, this is the number exported. +- `totalFound` (number): Number of results returned inline, or number exported for multi-selection file output. For search modes, this is after `--max-results` clipping and serialization. - `errorMessage` (string): Top-level failure summary (empty on success) - `processingErrors` (array): Selected-mode per-GameObject serialization failures, each `{gameObjectName, gameObjectPath, error}`. Omitted/null or empty on clean runs. diff --git a/.codex/skills/uloop-focus-window/SKILL.md b/.codex/skills/uloop-focus-window/SKILL.md index e2ca7a23bb..515bfa29c3 100644 --- a/.codex/skills/uloop-focus-window/SKILL.md +++ b/.codex/skills/uloop-focus-window/SKILL.md @@ -13,6 +13,10 @@ Bring Unity Editor window to front using OS-level commands. uloop focus-window ``` +## Parameters + +None. + ## Global Options | Option | Description | @@ -32,7 +36,11 @@ Returns JSON with: - `success`: Whether the focus operation succeeded - `message`: Status message (e.g. `Unity Editor window focused (PID: 12345)`, or the failure reason such as `Unity project not found` / `No running Unity process found for this project` / `Failed to focus Unity window: `) +These are the only two fields. There is no PID, window-handle, or platform field returned to the caller. + ## Notes - **Works even when Unity is busy** (compiling, domain reload, etc.) +- Uses OS-level commands (osascript on macOS, PowerShell on Windows) - Useful before `uloop capture-unity-window` to ensure the target window is visible +- Brings the main Unity Editor window to the foreground diff --git a/.codex/skills/uloop-get-logs/SKILL.md b/.codex/skills/uloop-get-logs/SKILL.md index 57d1d97f62..2ecb25aff7 100644 --- a/.codex/skills/uloop-get-logs/SKILL.md +++ b/.codex/skills/uloop-get-logs/SKILL.md @@ -8,6 +8,8 @@ description: "Read current Unity Console entries from a running Editor. Use duri Retrieve logs from Unity Console. +When a pause-point marker pauses Unity and the value you need is a method local, intermediate calculation, or branch reason, read the focused `Debug.Log` entry added immediately before the marker before resuming PlayMode. Use `--search-text ` so the marker and its log are checked as one breakpoint-style proof. + ## Usage ```bash @@ -52,7 +54,10 @@ uloop get-logs --search-text "Missing.*Component" --use-regex Returns JSON with: - `totalCount` (number): Total logs available before max-count clipping - `displayedCount` (number): Logs returned in this response (≤ `--max-count`) -- Input filters (`logType`, `maxCount`, `searchText`, `includeStackTrace`) are echoed back in the response +- `logType` (string): The `--log-type` filter that was applied +- `maxCount` (number): The `--max-count` cap that was applied +- `searchText` (string): The `--search-text` filter that was applied (empty when omitted) +- `includeStackTrace` (boolean): Whether stack traces are included in `logs[]` - `logs` (array): Each entry has: - `type` (string): `"Error"`, `"Warning"`, or `"Log"` - `message` (string): Log message body diff --git a/.codex/skills/uloop-launch/SKILL.md b/.codex/skills/uloop-launch/SKILL.md index 525fe110b8..2bf0e0929b 100644 --- a/.codex/skills/uloop-launch/SKILL.md +++ b/.codex/skills/uloop-launch/SKILL.md @@ -47,22 +47,22 @@ uloop launch --quit ## Output -Returns JSON with: -- `success`: whether the command completed -- `ready`: whether Unity CLI Loop is ready for commands -- `serverReady`: whether the Unity CLI Loop server accepted requests -- `projectIpcReady`: whether the project IPC path accepted tool requests -- `alreadyRunning`: whether an existing Unity process was reused -- `launched`: whether this command launched a Unity process -- `restarted`: whether this command stopped an existing process and launched a new one -- `quit`: whether this command stopped Unity without launching a new process -- `previousProcessId`: process ID stopped by restart or quit, when available -- `currentProcessId`: current Unity process ID, when available -- `projectRoot`: resolved project root -- `message`: readiness summary - -## Notes - +- Prints detected Unity version +- Prints project path - If Unity is already running, focuses the existing window and verifies tool readiness -- If process scan is blocked (e.g. sandboxed `ps`), plain launch falls back to IPC probing; `--restart` and `--quit` still fail because they need the process id -- The command waits until Unity finishes startup and the CLI can connect before returning +- If the process scan is blocked by the environment (e.g. sandboxed `ps`), plain launch falls back to probing the project IPC; when Unity responds it reports `alreadyRunning: true` without focusing the window instead of failing. `--restart` and `--quit` still fail because they need the process id +- If launching or restarting, prints when it is waiting for Unity CLI Loop server readiness +- If launching or restarting, waits until Unity finishes startup and the CLI can connect to the project +- Successful launch, restart, existing-process, and quit paths return JSON with: + - `success`: whether the command completed + - `ready`: whether Unity CLI Loop is ready for commands + - `serverReady`: whether the Unity CLI Loop server accepted requests + - `projectIpcReady`: whether the project IPC path accepted tool requests + - `alreadyRunning`: whether an existing Unity process was reused + - `launched`: whether this command launched a Unity process + - `restarted`: whether this command stopped an existing process and launched a new one + - `quit`: whether this command stopped Unity without launching a new process + - `previousProcessId`: process ID stopped by restart or quit, when available + - `currentProcessId`: current Unity process ID, when available + - `projectRoot`: resolved project root + - `message`: readiness summary diff --git a/.codex/skills/uloop-record-input/SKILL.md b/.codex/skills/uloop-record-input/SKILL.md index a76d38cb33..6b1c530f38 100644 --- a/.codex/skills/uloop-record-input/SKILL.md +++ b/.codex/skills/uloop-record-input/SKILL.md @@ -39,7 +39,8 @@ Replay injects input frame-by-frame, so the game must also be deterministic to p ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. +- **Input System package** must be installed (`com.unity.inputsystem`) +- Use this only when the project already uses the New Input System. ## Output @@ -51,4 +52,4 @@ The CLI prints JSON with: - `totalFrames`: Number of frames recorded (nullable int; populated on `Stop` only) - `durationSeconds`: Recording duration in seconds (nullable float; populated on `Stop` only) -Frame data lives only in the JSON file at `outputPath`; internal metadata fields are not included in the response. +The CLI output contains only these six payload fields. Internal metadata such as `ver` is removed before printing. There is no `recordingId`, `startTimestamp`, `keysCaptured`, or per-frame data in the response — frame data lives only in the JSON file at `outputPath`. diff --git a/.codex/skills/uloop-replay-input/SKILL.md b/.codex/skills/uloop-replay-input/SKILL.md index 9a23fabd47..d69e1ad07e 100644 --- a/.codex/skills/uloop-replay-input/SKILL.md +++ b/.codex/skills/uloop-replay-input/SKILL.md @@ -43,7 +43,8 @@ Replay injects the exact same input frame-by-frame, but the game must also be de ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. +- **Input System package** must be installed (`com.unity.inputsystem`) +- Use this only when the project already uses the New Input System. ## Output @@ -57,3 +58,4 @@ Returns JSON with: - `progress`: Replay progress (nullable float in 0.0 – 1.0) - `isReplaying`: Whether replay is currently active (nullable bool) +These are the only eight fields. There is no `loopCount`, `elapsedSeconds`, `overlayVisible`, or per-frame inspection data in the response. diff --git a/.codex/skills/uloop-run-tests/SKILL.md b/.codex/skills/uloop-run-tests/SKILL.md index 421e47b21d..ba6909c219 100644 --- a/.codex/skills/uloop-run-tests/SKILL.md +++ b/.codex/skills/uloop-run-tests/SKILL.md @@ -8,11 +8,11 @@ description: "Run Unity Test Runner and report detailed results. Use for EditMod Execute Unity Test Runner. When tests fail, NUnit XML results with error messages and stack traces are automatically saved. Read the XML file at `xmlPath` for detailed failure diagnosis. -Before running tests, run `uloop compile` first if you edited C# files, test files, `.asmdef`/`.asmref`, or package manifests since the last successful compile. This refreshes the AssetDatabase and surfaces compile errors before test execution. +Before running `uloop run-tests`, run `uloop compile` for the same Unity project when the current task created, deleted, renamed, moved, or edited C# source files, test files, `.asmdef`, `.asmref`, package manifest files, or scripting define settings. This refreshes the AssetDatabase, lets Unity discover new tests, and surfaces compile errors before test execution. You may skip this compile step when rerunning tests without code or assembly-definition changes since the last successful compile. Before executing tests, `uloop run-tests` saves unsaved loaded Scene changes and unsaved current Prefab Stage changes by default. If saving fails, it returns `success: false`, keeps `testCount` at `0`, lists the unsaved items in `message`, and does not start the Unity Test Runner. -`NoTestsFound` means zero tests matched — not a test failure. Check `noTestsFoundExplanation` and `message` for asmdef hints. +When a run returns `status: "NoTestsFound"` or `noTestsFound: true`, no matching tests were discovered. This is not the same as a failed test case: `hasFailures` remains `false`, `failedCount` remains `0`, and `noTestsFoundExplanation` explains the zero-discovery state for agents. When an unfiltered `--filter-type all` run also has `message` set to the no-tests message, read the `message` for asmdef hints. `run-tests` only appends those hints when it detects likely test assembly configuration issues; exact, regex, and assembly filter misses keep the plain no-tests message. ## Usage diff --git a/.codex/skills/uloop-screenshot/SKILL.md b/.codex/skills/uloop-screenshot/SKILL.md index bc721a5ee7..d10d199ea2 100644 --- a/.codex/skills/uloop-screenshot/SKILL.md +++ b/.codex/skills/uloop-screenshot/SKILL.md @@ -67,6 +67,9 @@ uloop screenshot --window-name Project --match-mode prefix # Save screenshot to a specific directory uloop screenshot --output-directory /tmp/screenshots + +# Combine options +uloop screenshot --window-name Scene --resolution-scale 0.5 --output-directory /tmp/screenshots ``` ## Output diff --git a/.codex/skills/uloop-simulate-keyboard/SKILL.md b/.codex/skills/uloop-simulate-keyboard/SKILL.md index 7b65d6b135..6cb374cc93 100644 --- a/.codex/skills/uloop-simulate-keyboard/SKILL.md +++ b/.codex/skills/uloop-simulate-keyboard/SKILL.md @@ -42,11 +42,16 @@ uloop simulate-keyboard --action --key [options] Use `Press` for edge-triggered keyboard code such as `Keyboard.current.spaceKey.wasPressedThisFrame`. `KeyDown` emits one initial press edge, then only keeps the key held. It does not keep `wasPressedThisFrame` true while the key remains held. If a successful `Press` or `KeyDown` leaves `Keyboard.current..isPressed` true but runtime state does not change, do not immediately rewrite the user's runtime code to `isPressed`. First verify that the target component is active during the command, that it polls input in the configured Input System update phase, and that a missed `KeyDown` edge is followed by `KeyUp` before retrying. -### Pause Point Inspection (Standard for E2E) +Use `KeyDown` / `KeyUp` when the scenario intentionally needs a held key. -For standard frame proof when this input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the key, not immediately after `simulate-keyboard`. +### Pause Point Inspection (Standard for E2E) -- If `interruptedByPausePoint: true`, Unity is paused and input bookkeeping was released. `pausePointId` and `pausePointHitCount` identify the marker. `pressEdgeObserved` is still reported on pause-point interruptions. +- Use `UloopPausePoint.Pause("")` with `uloop-wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. +- Put the marker at a natural state transition after the app consumed the key, such as after a command is accepted, a state mutation is committed, an evaluation step resolves, or a dependent component is updated. Do not place it immediately after sending `simulate-keyboard`. +- If the key handler has local variables, intermediate calculations, or branch reasons that `execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop-get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. +- Treat `simulate-keyboard success=true`, generic action logs, and final durable counters as useful evidence, but not as paused-frame proof. +- If the response has `interruptedByPausePoint: true`, Unity is paused for inspection and the tool released its held input bookkeeping. If a `UloopPausePoint.Pause` marker caused the pause, `pausePointId` and `pausePointHitCount` identify it. Use `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. +- Use distinct marker ids for strict phases, for example `input-read`, `state-updated`, and `result-committed`. ### KeyDown/KeyUp Rules @@ -98,5 +103,6 @@ Returns JSON with: ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. +- **Input System package** must be installed (`com.unity.inputsystem`) +- Use this only when the project already uses the New Input System. - Game code must read input via Input System API (e.g. `Keyboard.current[Key.W].isPressed`), not legacy `Input.GetKey()` diff --git a/.codex/skills/uloop-simulate-mouse-input/SKILL.md b/.codex/skills/uloop-simulate-mouse-input/SKILL.md index 8bc5b09e69..12b73569e4 100644 --- a/.codex/skills/uloop-simulate-mouse-input/SKILL.md +++ b/.codex/skills/uloop-simulate-mouse-input/SKILL.md @@ -50,9 +50,11 @@ uloop simulate-mouse-input --action [options] ### Pause Point Inspection (Standard for E2E) -For standard frame proof when this input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the mouse input, not immediately after `simulate-mouse-input`. - -- If `interruptedByPausePoint: true`, Unity is paused and input bookkeeping was released. `pausePointId` and `pausePointHitCount` identify the marker. +- Use `UloopPausePoint.Pause("")` with `uloop-wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. +- Place the pause point at a natural state transition after the app consumed the mouse input, such as after a command is accepted, a state mutation is committed, an evaluation step resolves, a tracked value changes, or a dependent component is updated. Do not place it immediately after sending `simulate-mouse-input`. +- If the mouse handler has local variables, intermediate calculations, or branch reasons that `execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop-get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. +- If the response has `interruptedByPausePoint: true`, Unity is paused for inspection and the tool released its held input bookkeeping. `pausePointId` and `pausePointHitCount` identify the pause point that paused Unity. Use `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. +- Use distinct ids for strict phases, for example `input-read`, `state-updated`, and `result-committed`. - Remove temporary pause-point/log instrumentation before final validation when it was added only for inspection. ### Global Options (optional) @@ -64,16 +66,14 @@ For standard frame proof when this input drives a state transition, follow the ` ## When to use this vs simulate-mouse-ui -All rows below assume the New Input System is installed. - | Scenario | Tool | |----------|------| | Click a Unity UI Button (IPointerClickHandler) | `simulate-mouse-ui` | -| Runtime logic reads `Mouse.current.leftButton` | `simulate-mouse-input` | -| Runtime logic reads right-click | `simulate-mouse-input --button Right` | +| Runtime logic reads `Mouse.current.leftButton` | `simulate-mouse-input` when the project uses the New Input System | +| Runtime logic reads right-click | `simulate-mouse-input --button Right` when the project uses the New Input System | | Drag a UI slider | `simulate-mouse-ui --action Drag` | -| Runtime logic reads `Mouse.current.delta` | `simulate-mouse-input --action MoveDelta` | -| Runtime logic reads `Mouse.current.scroll` | `simulate-mouse-input --action Scroll` | +| Runtime logic reads `Mouse.current.delta` | `simulate-mouse-input --action MoveDelta` when the project uses the New Input System | +| Runtime logic reads `Mouse.current.scroll` | `simulate-mouse-input --action Scroll` when the project uses the New Input System | ## Examples @@ -103,8 +103,9 @@ uloop simulate-mouse-input --action SmoothDelta --delta-x 300 --delta-y 0 --dura ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. +- **Input System package** must be installed (`com.unity.inputsystem`) - Game code must read input via Input System API (e.g. `Mouse.current.leftButton.wasPressedThisFrame`) +- Use this only when the project already uses the New Input System. ## Output @@ -120,4 +121,4 @@ Returns JSON with: - `pausePointHitCount`: The hit count for that `UloopPausePoint.Pause("")` - `pausePointHits` (array, nullable): Every marker hit during this input as `{id, hitCount}` entries, in hit order. Read this when one input may trigger several markers; `pausePointId` only names the latest one -Verify visual outcome with a follow-up screenshot. +There is no `deltaX`, `deltaY`, `scrollX`, `scrollY`, `duration`, or hit-element field in the response — only the issued action, button, target position, and Pause Point interruption state are echoed back. Verify visual outcome with a follow-up screenshot. diff --git a/.codex/skills/uloop-simulate-mouse-ui/SKILL.md b/.codex/skills/uloop-simulate-mouse-ui/SKILL.md index 8a9a797b6b..f5e2799aec 100644 --- a/.codex/skills/uloop-simulate-mouse-ui/SKILL.md +++ b/.codex/skills/uloop-simulate-mouse-ui/SKILL.md @@ -57,6 +57,7 @@ uloop simulate-mouse-ui --action --x --y [options] - `DragStart` must be called before `DragMove` or `DragEnd` - `DragEnd` must be called to release an active drag — failing to call it leaves drag state stuck - Calling `DragMove` or `DragEnd` without an active drag returns an error +- `Drag`, `DragStart`, `DragMove`, and `DragEnd` only support `--button Left` ### Global Options (all optional, mutually exclusive) @@ -77,8 +78,11 @@ uloop simulate-mouse-ui --action --x --y [options] ## Pause Point Inspection (Standard for E2E) -For standard frame proof when this UI input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the UI event, not immediately after `simulate-mouse-ui`. - +- Use `UloopPausePoint.Pause("")` with `uloop wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. +- Place the pause point at a natural transition after the app consumed the UI event, such as after a command is accepted, a state mutation is committed, a tracked value changes, a UI/domain state syncs, or a success/failure/end condition is entered. +- If the UI handler has local variables, intermediate calculations, or branch reasons that `uloop execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. +- Treat `uloop simulate-mouse-ui` `success=true`, generic action logs, and final durable counters as useful evidence, not paused-frame proof. +- If a `UloopPausePoint.Pause` pauses Unity, inspect with `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. - Remove temporary pause-point/log instrumentation before final validation when it was added only for inspection. ## Examples @@ -134,6 +138,6 @@ Returns JSON with: - `endPositionX`: Drag end X coordinate (nullable float; populated for drag actions only) - `endPositionY`: Drag end Y coordinate (nullable float; populated for drag actions only) -Verify the visual outcome with a follow-up `uloop screenshot --capture-mode rendering --annotate-elements`. +These are the only eight fields. There is no `button`, `duration`, `dragSpeed`, raycast list, or pointer-event log in the response — verify the visual outcome with a follow-up `uloop screenshot --capture-mode rendering --annotate-elements`. Note: Click and LongPress on empty space (no UI element) still return `success = true` with `hitGameObjectName = null`. Drag actions on empty space return `success = false`. diff --git a/.codex/skills/uloop-wait-for-pause-point/SKILL.md b/.codex/skills/uloop-wait-for-pause-point/SKILL.md index 14699e0deb..2ac9b2c58b 100644 --- a/.codex/skills/uloop-wait-for-pause-point/SKILL.md +++ b/.codex/skills/uloop-wait-for-pause-point/SKILL.md @@ -47,6 +47,7 @@ Use `generation`, `enabledAtUtc`, and the hit sequence fields from the hit or st - Consider a pause point during E2E passes when transition-frame evidence would add confidence, even if durable state, logs, or screenshots can later confirm the final result. - Use this before reaching for `Time.timeScale`, sleeps, repeated polling, or after-the-fact `execute-dynamic-code`; those checks can supplement the paused-frame proof, but they are not substitutes. - If the value you need is a method local, an intermediate calculation, or a branch reason that `execute-dynamic-code` cannot reach, add a focused `Debug.Log` immediately before the marker and read it with `get-logs` while paused. Do not count the breakpoint check complete until the matching log has been read. +- Good pause points include after input is consumed, a command is accepted, a state mutation is committed, an evaluation step resolves, a tracked value changes, a UI/domain state syncs, or a success/failure/end condition is entered. - Treat the pause like a lightweight breakpoint for one important transition: combine nearby debug logs with paused-frame inspection to confirm the variables and component state at that point. - Do not treat `simulate-* success=true`, generic action logs, sleeps/retries, testing-only counters, or `Time.timeScale` changes as paused-frame proof. - Skip this only for ordinary persistent-state checks when you are not validating simulated input delivery, event ordering, or transition-frame fidelity. From ab9c22eb6b6b28e64fb006793821f7b00e78dbcf Mon Sep 17 00:00:00 2001 From: hatayama Date: Thu, 18 Jun 2026 16:43:42 +0900 Subject: [PATCH 5/9] Revert generated skill copies in .claude/skills These are generated copies; only the package source definitions should carry the changes. --- .claude/skills/uloop-clear-console/SKILL.md | 3 +- .../skills/uloop-control-play-mode/SKILL.md | 7 +- .../uloop-execute-dynamic-code/SKILL.md | 23 ++--- .../playmode-automation-powershell.md | 84 +++++++++++++------ .../references/undo-operations.md | 1 + .../skills/uloop-find-game-objects/SKILL.md | 2 +- .claude/skills/uloop-focus-window/SKILL.md | 8 ++ .claude/skills/uloop-get-logs/SKILL.md | 7 +- .claude/skills/uloop-hello-world/SKILL.md | 7 ++ .claude/skills/uloop-launch/SKILL.md | 36 ++++---- .claude/skills/uloop-record-input/SKILL.md | 5 +- .claude/skills/uloop-replay-input/SKILL.md | 4 +- .claude/skills/uloop-run-tests/SKILL.md | 4 +- .claude/skills/uloop-screenshot/SKILL.md | 3 + .../skills/uloop-simulate-keyboard/SKILL.md | 14 +++- .../uloop-simulate-mouse-input/SKILL.md | 23 ++--- .../skills/uloop-simulate-mouse-ui/SKILL.md | 10 ++- .../uloop-wait-for-pause-point/SKILL.md | 3 +- 18 files changed, 161 insertions(+), 83 deletions(-) diff --git a/.claude/skills/uloop-clear-console/SKILL.md b/.claude/skills/uloop-clear-console/SKILL.md index 3cb9e2be3f..5c633ca30b 100644 --- a/.claude/skills/uloop-clear-console/SKILL.md +++ b/.claude/skills/uloop-clear-console/SKILL.md @@ -45,4 +45,5 @@ Returns JSON with: - `errorCount` (number): Errors cleared - `warningCount` (number): Warnings cleared - `logCount` (number): Info logs cleared -- `message` (string): Description of the result. On failure, this field carries the error summary. +- `message` (string): Description of the result; carries the failure summary when the operation fails (e.g. `"Failed to clear console: ..."`) +- `errorMessage` (string): Currently always empty for this tool — read `message` for failure details diff --git a/.claude/skills/uloop-control-play-mode/SKILL.md b/.claude/skills/uloop-control-play-mode/SKILL.md index bfdb9e9bc9..662c027629 100644 --- a/.claude/skills/uloop-control-play-mode/SKILL.md +++ b/.claude/skills/uloop-control-play-mode/SKILL.md @@ -57,6 +57,9 @@ Returns JSON with the current play mode state: ## Notes -- Stop on an already-stopped Editor sets `changed: false`, `wasAlreadyStopped: true` -- Step is independent of `Time.timeScale`; requires PlayMode to be running +- Play action starts the game in the Unity Editor (also resumes from pause) +- Stop action exits play mode and returns to edit mode. If Play Mode was already stopped, `changed` is `false`, `wasAlreadyStopped` is `true`, and `message` is `Play mode was already stopped`. +- Pause action pauses the game while remaining in play mode +- Step action advances exactly one frame and leaves play mode paused (the Editor's Next Frame button; independent of Time.timeScale). Requires PlayMode to be running; repeat to walk transitions frame by frame +- Useful for automated testing workflows - The command waits for the requested state before returning. Increase `--timeout-seconds` for projects with slow PlayMode entry. diff --git a/.claude/skills/uloop-execute-dynamic-code/SKILL.md b/.claude/skills/uloop-execute-dynamic-code/SKILL.md index 1b2c08ebf9..77060d33fb 100644 --- a/.claude/skills/uloop-execute-dynamic-code/SKILL.md +++ b/.claude/skills/uloop-execute-dynamic-code/SKILL.md @@ -7,7 +7,7 @@ context: fork # Task -Run focused C# snippets in the active Unity Editor with `uloop execute-dynamic-code`. +Execute the following request using `uloop execute-dynamic-code`: $ARGUMENTS For basic selected GameObject discovery or property inspection, use `find-game-objects --search-mode Selected` before this tool. Use this tool after the built-in inspection tools are not enough or when you need to modify Unity state. @@ -17,14 +17,15 @@ This tool can inspect reachable Unity state, such as GameObjects, components, pu 1. Read the relevant reference file(s) from the Code Examples section below 2. Construct C# code based on the reference examples -3. Execute with `--code` or `--code-file` using the active shell's quoting guidance -4. Report the execution result +3. Execute inline code: `uloop execute-dynamic-code --code ''` +4. If execution fails, adjust code and retry +5. Report the execution result ## Parameters - `--code ''`: Inline C# statements to execute. Use direct statements only; `return` is optional, and `using` directives may appear at the top of the snippet. - `--code-file `: Read the C# statements from a file instead of `--code`. Use this when the active shell or launcher cannot preserve inline code exactly. Exactly one of `--code` or `--code-file` is required; combining them is an error. -- **Shell-specific quoting**: Read [references/playmode-automation-powershell.md](references/playmode-automation-powershell.md) for Windows/PowerShell multiline commands and [references/playmode-automation-zsh.md](references/playmode-automation-zsh.md) for zsh/macOS examples. +- **Shell quoting**: bash/zsh uses single quotes, for example `uloop execute-dynamic-code --code 'using UnityEngine; return Mathf.PI;'`. PowerShell 7 (`pwsh`) can pass multiline snippets without `--code-file` by using a single-quoted here-string, for example `$code = @' ... '@; uloop execute-dynamic-code --code $code`. Windows PowerShell 5.1 removes unescaped double quotes when invoking native commands; escape C# double quotes as `\"` for short inline snippets, or use `--code-file` when the snippet has many strings. - `--parameters {}` (advanced, optional): Pass a shell-quoted JSON object literal when reusing a snippet with varying data or when keeping values outside the code. Values are exposed as `parameters["param0"]`, `parameters["param1"]`, and so on. Omit this flag for most snippets. Do not pass a JSON string value such as `"{\"param0\":\"value\"}"`. - `--wait-for-domain-reload` (optional): Wait for Domain Reload recovery after snippets that intentionally trigger Unity script reload or import work. Omit this for normal inspection and editor-state workflows. @@ -38,7 +39,7 @@ float x = Mathf.PI; return x; ``` -Prefer terminal commands for file operations and keep snippets focused on Unity Editor state that existing uloop tools cannot inspect or change. +**Forbidden** — these will be rejected at compile time: `System.IO.*`, `AssetDatabase.CreateFolder`, creating/editing `.cs`/`.asmdef` files. Use terminal commands for file operations instead. ## Output @@ -47,12 +48,14 @@ Returns JSON: - `result`: string — value of the snippet's `return` statement (empty when omitted) - `logs`: string[] — execution messages from the dynamic-code tool; read Unity Console `Debug.Log` output with `get-logs` - `compilationErrors`: object[] — Roslyn diagnostics with `message`, `line`, `column`, `errorCode`, optional `hint` and `suggestions` -- `error` / `errorMessage`: string — top-level failure summary (empty on success) +- `errorMessage`: string — top-level failure summary (empty on success) +- `error`: string — alias of `errorMessage` +- `securityLevel`: string — dynamic-code security level active for the request - `updatedCode`: string|null — the wrapped form actually compiled (handy when debugging using-statement reordering) - `diagnosticsSummary`: string|null — compact summary when diagnostics are available - `diagnostics`: object[] — structured diagnostics; same shape as `compilationErrors`, usually populated together with it -On `success: false`, inspect `compilationErrors` first. If empty, read `errorMessage` (and `logs` for extra context) — the failure may be a runtime exception, cancellation, or an "execution in progress" rejection, all of which return empty `compilationErrors`. Both EditMode and PlayMode are supported targets — the snippet runs in whichever mode the Editor is currently in. +On `success: false`, inspect `compilationErrors` first. If empty, read `errorMessage` (and `logs` for extra context) — the failure may be a runtime exception, security violation, cancellation, or an "execution in progress" rejection, all of which return empty `compilationErrors`. Both EditMode and PlayMode are supported targets — the snippet runs in whichever mode the Editor is currently in. ## Code Examples by Category @@ -76,10 +79,10 @@ For detailed code examples, refer to these files: - Undo-aware operations: RecordObject, AddComponent, SetParent, grouping - **Selection operations**: See [references/selection-operations.md](references/selection-operations.md) - Get/set selection, multi-select, filter by type/editability -- **PlayMode automation (PowerShell/Windows)**: See [references/playmode-automation-powershell.md](references/playmode-automation-powershell.md) - - Click UI buttons, invoke methods, set fields, tool combination workflows for PowerShell users -- **PlayMode automation (zsh/macOS)**: See [references/playmode-automation-zsh.md](references/playmode-automation-zsh.md) +- **PlayMode automation (zsh)**: See [references/playmode-automation-zsh.md](references/playmode-automation-zsh.md) - Click UI buttons, invoke methods, set fields, tool combination workflows for zsh users +- **PlayMode automation (PowerShell)**: See [references/playmode-automation-powershell.md](references/playmode-automation-powershell.md) + - Click UI buttons, invoke methods, set fields, tool combination workflows for PowerShell users - **PlayMode UI controls**: See [references/playmode-ui-controls.md](references/playmode-ui-controls.md) - InputField, Slider, Toggle, Dropdown, drag & drop simulation, list all UI controls - **PlayMode inspection**: See [references/playmode-inspection.md](references/playmode-inspection.md) diff --git a/.claude/skills/uloop-execute-dynamic-code/references/playmode-automation-powershell.md b/.claude/skills/uloop-execute-dynamic-code/references/playmode-automation-powershell.md index aeab072a4b..fe39803099 100644 --- a/.claude/skills/uloop-execute-dynamic-code/references/playmode-automation-powershell.md +++ b/.claude/skills/uloop-execute-dynamic-code/references/playmode-automation-powershell.md @@ -22,12 +22,14 @@ Choose the tool based on what you are trying to validate: In short: do not default everything to mouse simulation. Use `execute-dynamic-code` for direct automation and diagnostics, and switch to `simulate-mouse-ui` or `simulate-mouse-input` when reproducing the real input path is the thing you need to test. -## PowerShell 7 Multiline Snippets +## PowerShell Quoting Notes -Default to a single-quoted here-string variable in PowerShell 7 (`pwsh`), then pass it to `--code`. +Use these patterns when you need shell-safe inline code. +PowerShell's equivalent to a bash heredoc is a single-quoted here-string. +In PowerShell 7 (`pwsh`), it can be passed to `--code` directly without creating a temporary file: ```powershell -$code = @' +uloop execute-dynamic-code --code @' using UnityEngine; using UnityEngine.SceneManagement; @@ -35,27 +37,20 @@ Scene scene = SceneManager.GetActiveScene(); GameObject[] objects = UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); return $"{scene.name}: {objects.Length} objects"; '@ - -uloop execute-dynamic-code --code $code ``` -### With parameters +Use `--code-file` when the active shell or launcher cannot preserve inline code exactly, especially for Windows PowerShell 5.1 snippets with many C# string literals. -Pass a JSON object literal, not a JSON string value. Use a second here-string when the parameters are easier to read on separate lines. - -```powershell -$code = @' -return parameters["param0"]; -'@ +### Native launcher check -$parameters = @' -{"param0":"Hello from PowerShell"} -'@ +Multi-line `--code` expects PowerShell to call the native Go `uloop.exe` launcher directly. +If `Get-Command uloop` resolves to an old `.cmd` shim from a previous installation, run `uloop install` and open a new terminal before relying on multi-line inline snippets. -uloop execute-dynamic-code --code $code --parameters $parameters +```powershell +(Get-Command uloop).Source ``` -## Short Inline Snippets +### Double quotes inside C# strings PowerShell 7 (`pwsh`) preserves C# double quotes inside single-quoted native command arguments. @@ -63,34 +58,73 @@ PowerShell 7 (`pwsh`) preserves C# double quotes inside single-quoted native com uloop execute-dynamic-code --code 'return "Hello from PowerShell";' ``` +Windows PowerShell 5.1 removes unescaped double quotes when invoking native commands. +Escape C# double quotes as `\"` when using inline `--code` from Windows PowerShell 5.1. + +```powershell +uloop execute-dynamic-code --code 'return \"Hello from Windows PowerShell\";' +``` + +### Single quotes inside inline C# code + If the C# snippet itself contains a single quote, double it inside the PowerShell single-quoted string. ```powershell uloop execute-dynamic-code --code 'char initial = ''A''; return initial.ToString();' ``` -Wrap JSON object literals for `--parameters` in single quotes so PowerShell passes inner double quotes unchanged. +### JSON object values passed via `--parameters` + +Pass a JSON object literal, not a JSON string value. Wrap the object literal in single quotes so PowerShell passes the inner double quotes through unchanged. ```powershell uloop execute-dynamic-code --code 'return parameters["param0"];' --parameters '{"param0":"Hello from PowerShell"}' ``` -## Windows PowerShell 5.1 +### Multi-line C# snippets -Windows PowerShell 5.1 removes unescaped double quotes when invoking native commands. Escape C# double quotes as `\"` in short inline snippets. +Use a here-string variable when you want to prepare or reuse a snippet before executing it in PowerShell 7 (`pwsh`). ```powershell -uloop execute-dynamic-code --code 'return \"Hello from Windows PowerShell\";' +$code = @' +using UnityEngine; + +GameObject obj = GameObject.Find("Player"); +if (obj == null) return "Player not found"; + +return obj.name; +'@ + +uloop execute-dynamic-code --code $code ``` -For multiline or string-heavy snippets, prefer `--code-file`. +When using Windows PowerShell 5.1, escape C# double quotes inside the here-string before passing it to native `uloop.exe`. + +```powershell +$code = @' +using UnityEngine; + +GameObject obj = GameObject.Find(\"Player\"); +if (obj == null) return \"Player not found\"; + +return obj.name; +'@ -## Native Launcher Check +uloop execute-dynamic-code --code $code +``` -Multiline `--code` expects the native Go `uloop.exe`. If `Get-Command uloop` resolves to an old `.cmd` shim, run `uloop install` and open a new terminal. +You can combine multi-line code with a multi-line JSON object literal the same way. ```powershell -(Get-Command uloop).Source +$code = @' +return parameters["param0"]; +'@ + +$parameters = @' +{"param0":"Hello from PowerShell"} +'@ + +uloop execute-dynamic-code --code $code --parameters $parameters ``` ## Click UI Button by Path diff --git a/.claude/skills/uloop-execute-dynamic-code/references/undo-operations.md b/.claude/skills/uloop-execute-dynamic-code/references/undo-operations.md index 95a95b85a2..34e31895d4 100644 --- a/.claude/skills/uloop-execute-dynamic-code/references/undo-operations.md +++ b/.claude/skills/uloop-execute-dynamic-code/references/undo-operations.md @@ -208,3 +208,4 @@ Undo.RecordObject(mat, "Change Material Color"); mat.color = Color.red; return "Changed material color (Undo available)"; ``` + diff --git a/.claude/skills/uloop-find-game-objects/SKILL.md b/.claude/skills/uloop-find-game-objects/SKILL.md index 24edd909a3..620c44e32c 100644 --- a/.claude/skills/uloop-find-game-objects/SKILL.md +++ b/.claude/skills/uloop-find-game-objects/SKILL.md @@ -77,7 +77,7 @@ Returns JSON with: - `tag` (string): GameObject tag - `layer` (number): Layer index - `components` (array): Each entry has `type` (short name, e.g., `Rigidbody`), `fullTypeName` (e.g., `UnityEngine.Rigidbody`), and `properties` (array of Inspector-visible `{name, type, value}` pairs) -- `totalFound` (number): Results returned (after `--max-results` clipping). For multi-selection file export, this is the number exported. +- `totalFound` (number): Number of results returned inline, or number exported for multi-selection file output. For search modes, this is after `--max-results` clipping and serialization. - `errorMessage` (string): Top-level failure summary (empty on success) - `processingErrors` (array): Selected-mode per-GameObject serialization failures, each `{gameObjectName, gameObjectPath, error}`. Omitted/null or empty on clean runs. diff --git a/.claude/skills/uloop-focus-window/SKILL.md b/.claude/skills/uloop-focus-window/SKILL.md index e2ca7a23bb..515bfa29c3 100644 --- a/.claude/skills/uloop-focus-window/SKILL.md +++ b/.claude/skills/uloop-focus-window/SKILL.md @@ -13,6 +13,10 @@ Bring Unity Editor window to front using OS-level commands. uloop focus-window ``` +## Parameters + +None. + ## Global Options | Option | Description | @@ -32,7 +36,11 @@ Returns JSON with: - `success`: Whether the focus operation succeeded - `message`: Status message (e.g. `Unity Editor window focused (PID: 12345)`, or the failure reason such as `Unity project not found` / `No running Unity process found for this project` / `Failed to focus Unity window: `) +These are the only two fields. There is no PID, window-handle, or platform field returned to the caller. + ## Notes - **Works even when Unity is busy** (compiling, domain reload, etc.) +- Uses OS-level commands (osascript on macOS, PowerShell on Windows) - Useful before `uloop capture-unity-window` to ensure the target window is visible +- Brings the main Unity Editor window to the foreground diff --git a/.claude/skills/uloop-get-logs/SKILL.md b/.claude/skills/uloop-get-logs/SKILL.md index 57d1d97f62..2ecb25aff7 100644 --- a/.claude/skills/uloop-get-logs/SKILL.md +++ b/.claude/skills/uloop-get-logs/SKILL.md @@ -8,6 +8,8 @@ description: "Read current Unity Console entries from a running Editor. Use duri Retrieve logs from Unity Console. +When a pause-point marker pauses Unity and the value you need is a method local, intermediate calculation, or branch reason, read the focused `Debug.Log` entry added immediately before the marker before resuming PlayMode. Use `--search-text ` so the marker and its log are checked as one breakpoint-style proof. + ## Usage ```bash @@ -52,7 +54,10 @@ uloop get-logs --search-text "Missing.*Component" --use-regex Returns JSON with: - `totalCount` (number): Total logs available before max-count clipping - `displayedCount` (number): Logs returned in this response (≤ `--max-count`) -- Input filters (`logType`, `maxCount`, `searchText`, `includeStackTrace`) are echoed back in the response +- `logType` (string): The `--log-type` filter that was applied +- `maxCount` (number): The `--max-count` cap that was applied +- `searchText` (string): The `--search-text` filter that was applied (empty when omitted) +- `includeStackTrace` (boolean): Whether stack traces are included in `logs[]` - `logs` (array): Each entry has: - `type` (string): `"Error"`, `"Warning"`, or `"Log"` - `message` (string): Log message body diff --git a/.claude/skills/uloop-hello-world/SKILL.md b/.claude/skills/uloop-hello-world/SKILL.md index 1f78d5a0ee..2724067ddd 100644 --- a/.claude/skills/uloop-hello-world/SKILL.md +++ b/.claude/skills/uloop-hello-world/SKILL.md @@ -44,3 +44,10 @@ Returns JSON with: - `language`: Language used for greeting - `timestamp`: Current timestamp (if enabled) +## Notes + +This is a sample custom tool demonstrating: +- Type-safe parameter handling with Schema +- Enum parameters for language selection +- Value-less flag parameters +- Multi-language support diff --git a/.claude/skills/uloop-launch/SKILL.md b/.claude/skills/uloop-launch/SKILL.md index 525fe110b8..2bf0e0929b 100644 --- a/.claude/skills/uloop-launch/SKILL.md +++ b/.claude/skills/uloop-launch/SKILL.md @@ -47,22 +47,22 @@ uloop launch --quit ## Output -Returns JSON with: -- `success`: whether the command completed -- `ready`: whether Unity CLI Loop is ready for commands -- `serverReady`: whether the Unity CLI Loop server accepted requests -- `projectIpcReady`: whether the project IPC path accepted tool requests -- `alreadyRunning`: whether an existing Unity process was reused -- `launched`: whether this command launched a Unity process -- `restarted`: whether this command stopped an existing process and launched a new one -- `quit`: whether this command stopped Unity without launching a new process -- `previousProcessId`: process ID stopped by restart or quit, when available -- `currentProcessId`: current Unity process ID, when available -- `projectRoot`: resolved project root -- `message`: readiness summary - -## Notes - +- Prints detected Unity version +- Prints project path - If Unity is already running, focuses the existing window and verifies tool readiness -- If process scan is blocked (e.g. sandboxed `ps`), plain launch falls back to IPC probing; `--restart` and `--quit` still fail because they need the process id -- The command waits until Unity finishes startup and the CLI can connect before returning +- If the process scan is blocked by the environment (e.g. sandboxed `ps`), plain launch falls back to probing the project IPC; when Unity responds it reports `alreadyRunning: true` without focusing the window instead of failing. `--restart` and `--quit` still fail because they need the process id +- If launching or restarting, prints when it is waiting for Unity CLI Loop server readiness +- If launching or restarting, waits until Unity finishes startup and the CLI can connect to the project +- Successful launch, restart, existing-process, and quit paths return JSON with: + - `success`: whether the command completed + - `ready`: whether Unity CLI Loop is ready for commands + - `serverReady`: whether the Unity CLI Loop server accepted requests + - `projectIpcReady`: whether the project IPC path accepted tool requests + - `alreadyRunning`: whether an existing Unity process was reused + - `launched`: whether this command launched a Unity process + - `restarted`: whether this command stopped an existing process and launched a new one + - `quit`: whether this command stopped Unity without launching a new process + - `previousProcessId`: process ID stopped by restart or quit, when available + - `currentProcessId`: current Unity process ID, when available + - `projectRoot`: resolved project root + - `message`: readiness summary diff --git a/.claude/skills/uloop-record-input/SKILL.md b/.claude/skills/uloop-record-input/SKILL.md index a76d38cb33..6b1c530f38 100644 --- a/.claude/skills/uloop-record-input/SKILL.md +++ b/.claude/skills/uloop-record-input/SKILL.md @@ -39,7 +39,8 @@ Replay injects input frame-by-frame, so the game must also be deterministic to p ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. +- **Input System package** must be installed (`com.unity.inputsystem`) +- Use this only when the project already uses the New Input System. ## Output @@ -51,4 +52,4 @@ The CLI prints JSON with: - `totalFrames`: Number of frames recorded (nullable int; populated on `Stop` only) - `durationSeconds`: Recording duration in seconds (nullable float; populated on `Stop` only) -Frame data lives only in the JSON file at `outputPath`; internal metadata fields are not included in the response. +The CLI output contains only these six payload fields. Internal metadata such as `ver` is removed before printing. There is no `recordingId`, `startTimestamp`, `keysCaptured`, or per-frame data in the response — frame data lives only in the JSON file at `outputPath`. diff --git a/.claude/skills/uloop-replay-input/SKILL.md b/.claude/skills/uloop-replay-input/SKILL.md index 9a23fabd47..d69e1ad07e 100644 --- a/.claude/skills/uloop-replay-input/SKILL.md +++ b/.claude/skills/uloop-replay-input/SKILL.md @@ -43,7 +43,8 @@ Replay injects the exact same input frame-by-frame, but the game must also be de ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. +- **Input System package** must be installed (`com.unity.inputsystem`) +- Use this only when the project already uses the New Input System. ## Output @@ -57,3 +58,4 @@ Returns JSON with: - `progress`: Replay progress (nullable float in 0.0 – 1.0) - `isReplaying`: Whether replay is currently active (nullable bool) +These are the only eight fields. There is no `loopCount`, `elapsedSeconds`, `overlayVisible`, or per-frame inspection data in the response. diff --git a/.claude/skills/uloop-run-tests/SKILL.md b/.claude/skills/uloop-run-tests/SKILL.md index 421e47b21d..ba6909c219 100644 --- a/.claude/skills/uloop-run-tests/SKILL.md +++ b/.claude/skills/uloop-run-tests/SKILL.md @@ -8,11 +8,11 @@ description: "Run Unity Test Runner and report detailed results. Use for EditMod Execute Unity Test Runner. When tests fail, NUnit XML results with error messages and stack traces are automatically saved. Read the XML file at `xmlPath` for detailed failure diagnosis. -Before running tests, run `uloop compile` first if you edited C# files, test files, `.asmdef`/`.asmref`, or package manifests since the last successful compile. This refreshes the AssetDatabase and surfaces compile errors before test execution. +Before running `uloop run-tests`, run `uloop compile` for the same Unity project when the current task created, deleted, renamed, moved, or edited C# source files, test files, `.asmdef`, `.asmref`, package manifest files, or scripting define settings. This refreshes the AssetDatabase, lets Unity discover new tests, and surfaces compile errors before test execution. You may skip this compile step when rerunning tests without code or assembly-definition changes since the last successful compile. Before executing tests, `uloop run-tests` saves unsaved loaded Scene changes and unsaved current Prefab Stage changes by default. If saving fails, it returns `success: false`, keeps `testCount` at `0`, lists the unsaved items in `message`, and does not start the Unity Test Runner. -`NoTestsFound` means zero tests matched — not a test failure. Check `noTestsFoundExplanation` and `message` for asmdef hints. +When a run returns `status: "NoTestsFound"` or `noTestsFound: true`, no matching tests were discovered. This is not the same as a failed test case: `hasFailures` remains `false`, `failedCount` remains `0`, and `noTestsFoundExplanation` explains the zero-discovery state for agents. When an unfiltered `--filter-type all` run also has `message` set to the no-tests message, read the `message` for asmdef hints. `run-tests` only appends those hints when it detects likely test assembly configuration issues; exact, regex, and assembly filter misses keep the plain no-tests message. ## Usage diff --git a/.claude/skills/uloop-screenshot/SKILL.md b/.claude/skills/uloop-screenshot/SKILL.md index bc721a5ee7..d10d199ea2 100644 --- a/.claude/skills/uloop-screenshot/SKILL.md +++ b/.claude/skills/uloop-screenshot/SKILL.md @@ -67,6 +67,9 @@ uloop screenshot --window-name Project --match-mode prefix # Save screenshot to a specific directory uloop screenshot --output-directory /tmp/screenshots + +# Combine options +uloop screenshot --window-name Scene --resolution-scale 0.5 --output-directory /tmp/screenshots ``` ## Output diff --git a/.claude/skills/uloop-simulate-keyboard/SKILL.md b/.claude/skills/uloop-simulate-keyboard/SKILL.md index 7b65d6b135..6cb374cc93 100644 --- a/.claude/skills/uloop-simulate-keyboard/SKILL.md +++ b/.claude/skills/uloop-simulate-keyboard/SKILL.md @@ -42,11 +42,16 @@ uloop simulate-keyboard --action --key [options] Use `Press` for edge-triggered keyboard code such as `Keyboard.current.spaceKey.wasPressedThisFrame`. `KeyDown` emits one initial press edge, then only keeps the key held. It does not keep `wasPressedThisFrame` true while the key remains held. If a successful `Press` or `KeyDown` leaves `Keyboard.current..isPressed` true but runtime state does not change, do not immediately rewrite the user's runtime code to `isPressed`. First verify that the target component is active during the command, that it polls input in the configured Input System update phase, and that a missed `KeyDown` edge is followed by `KeyUp` before retrying. -### Pause Point Inspection (Standard for E2E) +Use `KeyDown` / `KeyUp` when the scenario intentionally needs a held key. -For standard frame proof when this input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the key, not immediately after `simulate-keyboard`. +### Pause Point Inspection (Standard for E2E) -- If `interruptedByPausePoint: true`, Unity is paused and input bookkeeping was released. `pausePointId` and `pausePointHitCount` identify the marker. `pressEdgeObserved` is still reported on pause-point interruptions. +- Use `UloopPausePoint.Pause("")` with `uloop-wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. +- Put the marker at a natural state transition after the app consumed the key, such as after a command is accepted, a state mutation is committed, an evaluation step resolves, or a dependent component is updated. Do not place it immediately after sending `simulate-keyboard`. +- If the key handler has local variables, intermediate calculations, or branch reasons that `execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop-get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. +- Treat `simulate-keyboard success=true`, generic action logs, and final durable counters as useful evidence, but not as paused-frame proof. +- If the response has `interruptedByPausePoint: true`, Unity is paused for inspection and the tool released its held input bookkeeping. If a `UloopPausePoint.Pause` marker caused the pause, `pausePointId` and `pausePointHitCount` identify it. Use `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. +- Use distinct marker ids for strict phases, for example `input-read`, `state-updated`, and `result-committed`. ### KeyDown/KeyUp Rules @@ -98,5 +103,6 @@ Returns JSON with: ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. +- **Input System package** must be installed (`com.unity.inputsystem`) +- Use this only when the project already uses the New Input System. - Game code must read input via Input System API (e.g. `Keyboard.current[Key.W].isPressed`), not legacy `Input.GetKey()` diff --git a/.claude/skills/uloop-simulate-mouse-input/SKILL.md b/.claude/skills/uloop-simulate-mouse-input/SKILL.md index 8bc5b09e69..12b73569e4 100644 --- a/.claude/skills/uloop-simulate-mouse-input/SKILL.md +++ b/.claude/skills/uloop-simulate-mouse-input/SKILL.md @@ -50,9 +50,11 @@ uloop simulate-mouse-input --action [options] ### Pause Point Inspection (Standard for E2E) -For standard frame proof when this input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the mouse input, not immediately after `simulate-mouse-input`. - -- If `interruptedByPausePoint: true`, Unity is paused and input bookkeeping was released. `pausePointId` and `pausePointHitCount` identify the marker. +- Use `UloopPausePoint.Pause("")` with `uloop-wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. +- Place the pause point at a natural state transition after the app consumed the mouse input, such as after a command is accepted, a state mutation is committed, an evaluation step resolves, a tracked value changes, or a dependent component is updated. Do not place it immediately after sending `simulate-mouse-input`. +- If the mouse handler has local variables, intermediate calculations, or branch reasons that `execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop-get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. +- If the response has `interruptedByPausePoint: true`, Unity is paused for inspection and the tool released its held input bookkeeping. `pausePointId` and `pausePointHitCount` identify the pause point that paused Unity. Use `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. +- Use distinct ids for strict phases, for example `input-read`, `state-updated`, and `result-committed`. - Remove temporary pause-point/log instrumentation before final validation when it was added only for inspection. ### Global Options (optional) @@ -64,16 +66,14 @@ For standard frame proof when this input drives a state transition, follow the ` ## When to use this vs simulate-mouse-ui -All rows below assume the New Input System is installed. - | Scenario | Tool | |----------|------| | Click a Unity UI Button (IPointerClickHandler) | `simulate-mouse-ui` | -| Runtime logic reads `Mouse.current.leftButton` | `simulate-mouse-input` | -| Runtime logic reads right-click | `simulate-mouse-input --button Right` | +| Runtime logic reads `Mouse.current.leftButton` | `simulate-mouse-input` when the project uses the New Input System | +| Runtime logic reads right-click | `simulate-mouse-input --button Right` when the project uses the New Input System | | Drag a UI slider | `simulate-mouse-ui --action Drag` | -| Runtime logic reads `Mouse.current.delta` | `simulate-mouse-input --action MoveDelta` | -| Runtime logic reads `Mouse.current.scroll` | `simulate-mouse-input --action Scroll` | +| Runtime logic reads `Mouse.current.delta` | `simulate-mouse-input --action MoveDelta` when the project uses the New Input System | +| Runtime logic reads `Mouse.current.scroll` | `simulate-mouse-input --action Scroll` when the project uses the New Input System | ## Examples @@ -103,8 +103,9 @@ uloop simulate-mouse-input --action SmoothDelta --delta-x 300 --delta-y 0 --dura ## Prerequisites - Unity must be in **PlayMode** -- **Input System package** (`com.unity.inputsystem`) must be installed; this tool only works with the New Input System. +- **Input System package** must be installed (`com.unity.inputsystem`) - Game code must read input via Input System API (e.g. `Mouse.current.leftButton.wasPressedThisFrame`) +- Use this only when the project already uses the New Input System. ## Output @@ -120,4 +121,4 @@ Returns JSON with: - `pausePointHitCount`: The hit count for that `UloopPausePoint.Pause("")` - `pausePointHits` (array, nullable): Every marker hit during this input as `{id, hitCount}` entries, in hit order. Read this when one input may trigger several markers; `pausePointId` only names the latest one -Verify visual outcome with a follow-up screenshot. +There is no `deltaX`, `deltaY`, `scrollX`, `scrollY`, `duration`, or hit-element field in the response — only the issued action, button, target position, and Pause Point interruption state are echoed back. Verify visual outcome with a follow-up screenshot. diff --git a/.claude/skills/uloop-simulate-mouse-ui/SKILL.md b/.claude/skills/uloop-simulate-mouse-ui/SKILL.md index 8a9a797b6b..f5e2799aec 100644 --- a/.claude/skills/uloop-simulate-mouse-ui/SKILL.md +++ b/.claude/skills/uloop-simulate-mouse-ui/SKILL.md @@ -57,6 +57,7 @@ uloop simulate-mouse-ui --action --x --y [options] - `DragStart` must be called before `DragMove` or `DragEnd` - `DragEnd` must be called to release an active drag — failing to call it leaves drag state stuck - Calling `DragMove` or `DragEnd` without an active drag returns an error +- `Drag`, `DragStart`, `DragMove`, and `DragEnd` only support `--button Left` ### Global Options (all optional, mutually exclusive) @@ -77,8 +78,11 @@ uloop simulate-mouse-ui --action --x --y [options] ## Pause Point Inspection (Standard for E2E) -For standard frame proof when this UI input drives a state transition, follow the `uloop-wait-for-pause-point` skill. Place markers after the app consumed the UI event, not immediately after `simulate-mouse-ui`. - +- Use `UloopPausePoint.Pause("")` with `uloop wait-for-pause-point` as the standard frame proof when this input drives a state transition you are verifying. Final logs, screenshots, and durable state supplement the paused-frame check but do not replace it. +- Place the pause point at a natural transition after the app consumed the UI event, such as after a command is accepted, a state mutation is committed, a tracked value changes, a UI/domain state syncs, or a success/failure/end condition is entered. +- If the UI handler has local variables, intermediate calculations, or branch reasons that `uloop execute-dynamic-code` cannot inspect after the fact, log just those values near `UloopPausePoint.Pause("")` and read them with `uloop get-logs` while Unity is paused. A pause point hit proves the line was reached, not the frame-local values. +- Treat `uloop simulate-mouse-ui` `success=true`, generic action logs, and final durable counters as useful evidence, not paused-frame proof. +- If a `UloopPausePoint.Pause` pauses Unity, inspect with `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, or `uloop execute-dynamic-code` before resuming. - Remove temporary pause-point/log instrumentation before final validation when it was added only for inspection. ## Examples @@ -134,6 +138,6 @@ Returns JSON with: - `endPositionX`: Drag end X coordinate (nullable float; populated for drag actions only) - `endPositionY`: Drag end Y coordinate (nullable float; populated for drag actions only) -Verify the visual outcome with a follow-up `uloop screenshot --capture-mode rendering --annotate-elements`. +These are the only eight fields. There is no `button`, `duration`, `dragSpeed`, raycast list, or pointer-event log in the response — verify the visual outcome with a follow-up `uloop screenshot --capture-mode rendering --annotate-elements`. Note: Click and LongPress on empty space (no UI element) still return `success = true` with `hitGameObjectName = null`. Drag actions on empty space return `success = false`. diff --git a/.claude/skills/uloop-wait-for-pause-point/SKILL.md b/.claude/skills/uloop-wait-for-pause-point/SKILL.md index 14699e0deb..430cc002c8 100644 --- a/.claude/skills/uloop-wait-for-pause-point/SKILL.md +++ b/.claude/skills/uloop-wait-for-pause-point/SKILL.md @@ -7,8 +7,6 @@ description: "Pauses Unity's playback and allows you to inspect specific frames. Use this small loop for one representative frame you care about: -Custom asmdef scripts must reference `UnityCLILoop.PausePoints.Runtime` before calling `UloopPausePoint.Pause`. `Assembly-CSharp` scripts can usually use it without a manual reference. - 1. Put a focused log and marker at the natural transition point. Log only local/intermediate values that will be hard to inspect later: ```csharp @@ -47,6 +45,7 @@ Use `generation`, `enabledAtUtc`, and the hit sequence fields from the hit or st - Consider a pause point during E2E passes when transition-frame evidence would add confidence, even if durable state, logs, or screenshots can later confirm the final result. - Use this before reaching for `Time.timeScale`, sleeps, repeated polling, or after-the-fact `execute-dynamic-code`; those checks can supplement the paused-frame proof, but they are not substitutes. - If the value you need is a method local, an intermediate calculation, or a branch reason that `execute-dynamic-code` cannot reach, add a focused `Debug.Log` immediately before the marker and read it with `get-logs` while paused. Do not count the breakpoint check complete until the matching log has been read. +- Good pause points include after input is consumed, a command is accepted, a state mutation is committed, an evaluation step resolves, a tracked value changes, a UI/domain state syncs, or a success/failure/end condition is entered. - Treat the pause like a lightweight breakpoint for one important transition: combine nearby debug logs with paused-frame inspection to confirm the variables and component state at that point. - Do not treat `simulate-* success=true`, generic action logs, sleeps/retries, testing-only counters, or `Time.timeScale` changes as paused-frame proof. - Skip this only for ordinary persistent-state checks when you are not validating simulated input delivery, event ordering, or transition-frame fidelity. From 6157a9c18cccf9f0e1f0ac41c6a59d83cd1d80bd Mon Sep 17 00:00:00 2001 From: hatayama Date: Thu, 18 Jun 2026 16:46:13 +0900 Subject: [PATCH 6/9] Revert remaining .claude/skills generated copies to origin/v3-beta Four files still diverged because local v3-beta tag was stale. --- .../uloop-execute-dynamic-code/SKILL.md | 17 ++-- .../playmode-automation-powershell.md | 84 ++++++------------- .../references/undo-operations.md | 1 - .../uloop-wait-for-pause-point/SKILL.md | 2 + 4 files changed, 35 insertions(+), 69 deletions(-) diff --git a/.claude/skills/uloop-execute-dynamic-code/SKILL.md b/.claude/skills/uloop-execute-dynamic-code/SKILL.md index 77060d33fb..45ce0d1acc 100644 --- a/.claude/skills/uloop-execute-dynamic-code/SKILL.md +++ b/.claude/skills/uloop-execute-dynamic-code/SKILL.md @@ -7,7 +7,7 @@ context: fork # Task -Execute the following request using `uloop execute-dynamic-code`: $ARGUMENTS +Run focused C# snippets in the active Unity Editor with `uloop execute-dynamic-code`. For basic selected GameObject discovery or property inspection, use `find-game-objects --search-mode Selected` before this tool. Use this tool after the built-in inspection tools are not enough or when you need to modify Unity state. @@ -17,7 +17,7 @@ This tool can inspect reachable Unity state, such as GameObjects, components, pu 1. Read the relevant reference file(s) from the Code Examples section below 2. Construct C# code based on the reference examples -3. Execute inline code: `uloop execute-dynamic-code --code ''` +3. Execute with `--code` or `--code-file` using the active shell's quoting guidance 4. If execution fails, adjust code and retry 5. Report the execution result @@ -25,7 +25,7 @@ This tool can inspect reachable Unity state, such as GameObjects, components, pu - `--code ''`: Inline C# statements to execute. Use direct statements only; `return` is optional, and `using` directives may appear at the top of the snippet. - `--code-file `: Read the C# statements from a file instead of `--code`. Use this when the active shell or launcher cannot preserve inline code exactly. Exactly one of `--code` or `--code-file` is required; combining them is an error. -- **Shell quoting**: bash/zsh uses single quotes, for example `uloop execute-dynamic-code --code 'using UnityEngine; return Mathf.PI;'`. PowerShell 7 (`pwsh`) can pass multiline snippets without `--code-file` by using a single-quoted here-string, for example `$code = @' ... '@; uloop execute-dynamic-code --code $code`. Windows PowerShell 5.1 removes unescaped double quotes when invoking native commands; escape C# double quotes as `\"` for short inline snippets, or use `--code-file` when the snippet has many strings. +- **Shell-specific quoting**: Read [references/playmode-automation-powershell.md](references/playmode-automation-powershell.md) for Windows/PowerShell multiline commands and [references/playmode-automation-zsh.md](references/playmode-automation-zsh.md) for zsh/macOS examples. - `--parameters {}` (advanced, optional): Pass a shell-quoted JSON object literal when reusing a snippet with varying data or when keeping values outside the code. Values are exposed as `parameters["param0"]`, `parameters["param1"]`, and so on. Omit this flag for most snippets. Do not pass a JSON string value such as `"{\"param0\":\"value\"}"`. - `--wait-for-domain-reload` (optional): Wait for Domain Reload recovery after snippets that intentionally trigger Unity script reload or import work. Omit this for normal inspection and editor-state workflows. @@ -39,7 +39,7 @@ float x = Mathf.PI; return x; ``` -**Forbidden** — these will be rejected at compile time: `System.IO.*`, `AssetDatabase.CreateFolder`, creating/editing `.cs`/`.asmdef` files. Use terminal commands for file operations instead. +Prefer terminal commands for file operations and keep snippets focused on Unity Editor state that existing uloop tools cannot inspect or change. ## Output @@ -50,12 +50,11 @@ Returns JSON: - `compilationErrors`: object[] — Roslyn diagnostics with `message`, `line`, `column`, `errorCode`, optional `hint` and `suggestions` - `errorMessage`: string — top-level failure summary (empty on success) - `error`: string — alias of `errorMessage` -- `securityLevel`: string — dynamic-code security level active for the request - `updatedCode`: string|null — the wrapped form actually compiled (handy when debugging using-statement reordering) - `diagnosticsSummary`: string|null — compact summary when diagnostics are available - `diagnostics`: object[] — structured diagnostics; same shape as `compilationErrors`, usually populated together with it -On `success: false`, inspect `compilationErrors` first. If empty, read `errorMessage` (and `logs` for extra context) — the failure may be a runtime exception, security violation, cancellation, or an "execution in progress" rejection, all of which return empty `compilationErrors`. Both EditMode and PlayMode are supported targets — the snippet runs in whichever mode the Editor is currently in. +On `success: false`, inspect `compilationErrors` first. If empty, read `errorMessage` (and `logs` for extra context) — the failure may be a runtime exception, cancellation, or an "execution in progress" rejection, all of which return empty `compilationErrors`. Both EditMode and PlayMode are supported targets — the snippet runs in whichever mode the Editor is currently in. ## Code Examples by Category @@ -79,10 +78,10 @@ For detailed code examples, refer to these files: - Undo-aware operations: RecordObject, AddComponent, SetParent, grouping - **Selection operations**: See [references/selection-operations.md](references/selection-operations.md) - Get/set selection, multi-select, filter by type/editability -- **PlayMode automation (zsh)**: See [references/playmode-automation-zsh.md](references/playmode-automation-zsh.md) - - Click UI buttons, invoke methods, set fields, tool combination workflows for zsh users -- **PlayMode automation (PowerShell)**: See [references/playmode-automation-powershell.md](references/playmode-automation-powershell.md) +- **PlayMode automation (PowerShell/Windows)**: See [references/playmode-automation-powershell.md](references/playmode-automation-powershell.md) - Click UI buttons, invoke methods, set fields, tool combination workflows for PowerShell users +- **PlayMode automation (zsh/macOS)**: See [references/playmode-automation-zsh.md](references/playmode-automation-zsh.md) + - Click UI buttons, invoke methods, set fields, tool combination workflows for zsh users - **PlayMode UI controls**: See [references/playmode-ui-controls.md](references/playmode-ui-controls.md) - InputField, Slider, Toggle, Dropdown, drag & drop simulation, list all UI controls - **PlayMode inspection**: See [references/playmode-inspection.md](references/playmode-inspection.md) diff --git a/.claude/skills/uloop-execute-dynamic-code/references/playmode-automation-powershell.md b/.claude/skills/uloop-execute-dynamic-code/references/playmode-automation-powershell.md index fe39803099..aeab072a4b 100644 --- a/.claude/skills/uloop-execute-dynamic-code/references/playmode-automation-powershell.md +++ b/.claude/skills/uloop-execute-dynamic-code/references/playmode-automation-powershell.md @@ -22,14 +22,12 @@ Choose the tool based on what you are trying to validate: In short: do not default everything to mouse simulation. Use `execute-dynamic-code` for direct automation and diagnostics, and switch to `simulate-mouse-ui` or `simulate-mouse-input` when reproducing the real input path is the thing you need to test. -## PowerShell Quoting Notes +## PowerShell 7 Multiline Snippets -Use these patterns when you need shell-safe inline code. -PowerShell's equivalent to a bash heredoc is a single-quoted here-string. -In PowerShell 7 (`pwsh`), it can be passed to `--code` directly without creating a temporary file: +Default to a single-quoted here-string variable in PowerShell 7 (`pwsh`), then pass it to `--code`. ```powershell -uloop execute-dynamic-code --code @' +$code = @' using UnityEngine; using UnityEngine.SceneManagement; @@ -37,20 +35,27 @@ Scene scene = SceneManager.GetActiveScene(); GameObject[] objects = UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); return $"{scene.name}: {objects.Length} objects"; '@ -``` -Use `--code-file` when the active shell or launcher cannot preserve inline code exactly, especially for Windows PowerShell 5.1 snippets with many C# string literals. +uloop execute-dynamic-code --code $code +``` -### Native launcher check +### With parameters -Multi-line `--code` expects PowerShell to call the native Go `uloop.exe` launcher directly. -If `Get-Command uloop` resolves to an old `.cmd` shim from a previous installation, run `uloop install` and open a new terminal before relying on multi-line inline snippets. +Pass a JSON object literal, not a JSON string value. Use a second here-string when the parameters are easier to read on separate lines. ```powershell -(Get-Command uloop).Source +$code = @' +return parameters["param0"]; +'@ + +$parameters = @' +{"param0":"Hello from PowerShell"} +'@ + +uloop execute-dynamic-code --code $code --parameters $parameters ``` -### Double quotes inside C# strings +## Short Inline Snippets PowerShell 7 (`pwsh`) preserves C# double quotes inside single-quoted native command arguments. @@ -58,73 +63,34 @@ PowerShell 7 (`pwsh`) preserves C# double quotes inside single-quoted native com uloop execute-dynamic-code --code 'return "Hello from PowerShell";' ``` -Windows PowerShell 5.1 removes unescaped double quotes when invoking native commands. -Escape C# double quotes as `\"` when using inline `--code` from Windows PowerShell 5.1. - -```powershell -uloop execute-dynamic-code --code 'return \"Hello from Windows PowerShell\";' -``` - -### Single quotes inside inline C# code - If the C# snippet itself contains a single quote, double it inside the PowerShell single-quoted string. ```powershell uloop execute-dynamic-code --code 'char initial = ''A''; return initial.ToString();' ``` -### JSON object values passed via `--parameters` - -Pass a JSON object literal, not a JSON string value. Wrap the object literal in single quotes so PowerShell passes the inner double quotes through unchanged. +Wrap JSON object literals for `--parameters` in single quotes so PowerShell passes inner double quotes unchanged. ```powershell uloop execute-dynamic-code --code 'return parameters["param0"];' --parameters '{"param0":"Hello from PowerShell"}' ``` -### Multi-line C# snippets +## Windows PowerShell 5.1 -Use a here-string variable when you want to prepare or reuse a snippet before executing it in PowerShell 7 (`pwsh`). +Windows PowerShell 5.1 removes unescaped double quotes when invoking native commands. Escape C# double quotes as `\"` in short inline snippets. ```powershell -$code = @' -using UnityEngine; - -GameObject obj = GameObject.Find("Player"); -if (obj == null) return "Player not found"; - -return obj.name; -'@ - -uloop execute-dynamic-code --code $code +uloop execute-dynamic-code --code 'return \"Hello from Windows PowerShell\";' ``` -When using Windows PowerShell 5.1, escape C# double quotes inside the here-string before passing it to native `uloop.exe`. - -```powershell -$code = @' -using UnityEngine; - -GameObject obj = GameObject.Find(\"Player\"); -if (obj == null) return \"Player not found\"; - -return obj.name; -'@ +For multiline or string-heavy snippets, prefer `--code-file`. -uloop execute-dynamic-code --code $code -``` +## Native Launcher Check -You can combine multi-line code with a multi-line JSON object literal the same way. +Multiline `--code` expects the native Go `uloop.exe`. If `Get-Command uloop` resolves to an old `.cmd` shim, run `uloop install` and open a new terminal. ```powershell -$code = @' -return parameters["param0"]; -'@ - -$parameters = @' -{"param0":"Hello from PowerShell"} -'@ - -uloop execute-dynamic-code --code $code --parameters $parameters +(Get-Command uloop).Source ``` ## Click UI Button by Path diff --git a/.claude/skills/uloop-execute-dynamic-code/references/undo-operations.md b/.claude/skills/uloop-execute-dynamic-code/references/undo-operations.md index 34e31895d4..95a95b85a2 100644 --- a/.claude/skills/uloop-execute-dynamic-code/references/undo-operations.md +++ b/.claude/skills/uloop-execute-dynamic-code/references/undo-operations.md @@ -208,4 +208,3 @@ Undo.RecordObject(mat, "Change Material Color"); mat.color = Color.red; return "Changed material color (Undo available)"; ``` - diff --git a/.claude/skills/uloop-wait-for-pause-point/SKILL.md b/.claude/skills/uloop-wait-for-pause-point/SKILL.md index 430cc002c8..2ac9b2c58b 100644 --- a/.claude/skills/uloop-wait-for-pause-point/SKILL.md +++ b/.claude/skills/uloop-wait-for-pause-point/SKILL.md @@ -7,6 +7,8 @@ description: "Pauses Unity's playback and allows you to inspect specific frames. Use this small loop for one representative frame you care about: +Custom asmdef scripts must reference `UnityCLILoop.PausePoints.Runtime` before calling `UloopPausePoint.Pause`. `Assembly-CSharp` scripts can usually use it without a manual reference. + 1. Put a focused log and marker at the natural transition point. Log only local/intermediate values that will be hard to inspect later: ```csharp From cb060b6b80298bfc2d774198528c3350cf7ff02f Mon Sep 17 00:00:00 2001 From: hatayama Date: Thu, 18 Jun 2026 17:00:57 +0900 Subject: [PATCH 7/9] Deduplicate and simplify execute-dynamic-code reference examples - asset-operations: merge 3 FindAssets variants into one with commented filter alternatives; clarify Rename (no Undo) heading - cleanup-operations: merge single-object and scene-wide missing reference scans into one; remove Find Empty GameObjects (covered by Remove Empty Leaf with a comment for list-only usage) - selection-operations: merge 3 SelectionMode variants into one; merge asset selection variants with usage note - playmode-ui-controls: merge 2 slider sections (bounds check subsumes plain set); merge 2 toggle sections with flip/set note - scriptableobject: merge single-property and multi-property SerializedObject examples into one with type setter guidance - scene-operations: remove Find GameObjects by Tag (playmode- inspection has a more detailed version) --- .../Skill/references/asset-operations.md | 47 ++-------- .../Skill/references/cleanup-operations.md | 85 +++---------------- .../Skill/references/playmode-ui-controls.md | 40 ++------- .../Skill/references/scene-operations.md | 7 -- .../Skill/references/scriptableobject.md | 27 +----- .../Skill/references/selection-operations.md | 63 ++------------ 6 files changed, 39 insertions(+), 230 deletions(-) diff --git a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/asset-operations.md b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/asset-operations.md index 31ddf7adc1..0dfc3876b9 100644 --- a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/asset-operations.md +++ b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/asset-operations.md @@ -2,54 +2,25 @@ Code examples for AssetDatabase operations using `execute-dynamic-code`. -## Find Assets by Type +## Find Assets ```csharp using UnityEditor; using System.Collections.Generic; -string[] prefabGuids = AssetDatabase.FindAssets("t:Prefab"); -List paths = new List(); - -foreach (string guid in prefabGuids) -{ - paths.Add(AssetDatabase.GUIDToAssetPath(guid)); -} -return $"Found {paths.Count} prefabs"; -``` - -## Find Assets by Name +// By type +string[] guids = AssetDatabase.FindAssets("t:Prefab"); +// By name +// string[] guids = AssetDatabase.FindAssets("Player"); +// By type in folder +// string[] guids = AssetDatabase.FindAssets("t:Prefab", new[] { "Assets/Prefabs" }); -```csharp -using UnityEditor; -using System.Collections.Generic; - -string searchName = "Player"; -string[] guids = AssetDatabase.FindAssets(searchName); List paths = new List(); - -foreach (string guid in guids) -{ - paths.Add(AssetDatabase.GUIDToAssetPath(guid)); -} -return $"Found {paths.Count} assets matching '{searchName}'"; -``` - -## Find Assets in Folder - -```csharp -using UnityEditor; -using System.Collections.Generic; - -string folder = "Assets/Prefabs"; -string[] guids = AssetDatabase.FindAssets("t:Prefab", new[] { folder }); -List paths = new List(); - foreach (string guid in guids) { paths.Add(AssetDatabase.GUIDToAssetPath(guid)); } -return $"Found {paths.Count} prefabs in {folder}"; +return $"Found {paths.Count} assets"; ``` ## Duplicate Asset @@ -76,7 +47,7 @@ string error = AssetDatabase.MoveAsset(sourcePath, destPath); return string.IsNullOrEmpty(error) ? $"Moved to {destPath}" : $"Error: {error}"; ``` -## Rename Asset +## Rename Asset (no Undo) ```csharp using UnityEditor; diff --git a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/cleanup-operations.md b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/cleanup-operations.md index 40eec9a52f..9d253d49a5 100644 --- a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/cleanup-operations.md +++ b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/cleanup-operations.md @@ -78,56 +78,22 @@ Undo.CollapseUndoOperations(undoGroup); return $"Removed {totalRemoved} missing scripts from scene"; ``` -## Detect Missing References in Component +## Scan for Missing References -```csharp -using UnityEditor; - -GameObject selected = Selection.activeGameObject; -if (selected == null) -{ - return "No GameObject selected"; -} - -List missingRefs = new List(); - -Component[] components = selected.GetComponents(); -foreach (Component comp in components) -{ - if (comp == null) continue; - - SerializedObject so = new SerializedObject(comp); - SerializedProperty prop = so.GetIterator(); - - while (prop.NextVisible(true)) - { - if (prop.propertyType == SerializedPropertyType.ObjectReference) - { - if (prop.objectReferenceValue == null && prop.objectReferenceInstanceIDValue != 0) - { - missingRefs.Add($"{comp.GetType().Name}.{prop.name}"); - } - } - } -} - -if (missingRefs.Count == 0) -{ - return "No missing references found"; -} - -return $"Missing references: {string.Join(", ", missingRefs)}"; -``` - -## Scan Scene for Missing References +Scans the selected GameObject or the entire scene for null ObjectReference fields. ```csharp using UnityEditor; -GameObject[] allObjects = Object.FindObjectsByType(FindObjectsSortMode.None); +// Single object: use Selection.activeGameObject +// Whole scene: use Object.FindObjectsByType(FindObjectsSortMode.None) +GameObject[] targets = Selection.activeGameObject != null + ? new[] { Selection.activeGameObject } + : Object.FindObjectsByType(FindObjectsSortMode.None); + List results = new List(); -foreach (GameObject obj in allObjects) +foreach (GameObject obj in targets) { Component[] components = obj.GetComponents(); foreach (Component comp in components) @@ -152,10 +118,10 @@ foreach (GameObject obj in allObjects) if (results.Count == 0) { - return "No missing references found in scene"; + return "No missing references found"; } -return $"Missing references ({results.Count}): {string.Join(", ", results.Take(10))}..."; +return $"Missing references ({results.Count}): {string.Join(", ", results.Take(10))}"; ``` ## Find Unused Materials in Project @@ -196,31 +162,6 @@ foreach (string guid in materialGuids) return $"Found {unusedMaterials.Count} materials not referenced by prefabs. Verify scene and other asset references manually before deleting."; ``` -## Find Empty GameObjects - -```csharp -using UnityEditor; - -GameObject[] allObjects = Object.FindObjectsByType(FindObjectsSortMode.None); -List emptyObjects = new List(); - -foreach (GameObject obj in allObjects) -{ - Component[] components = obj.GetComponents(); - if (components.Length == 1 && obj.transform.childCount == 0) - { - emptyObjects.Add(obj.name); - } -} - -if (emptyObjects.Count == 0) -{ - return "No empty GameObjects found"; -} - -return $"Empty objects ({emptyObjects.Count}): {string.Join(", ", emptyObjects.Take(20))}"; -``` - ## Find Duplicate Names in Hierarchy ```csharp @@ -311,7 +252,9 @@ if (negativeScale.Count == 0) return $"Negative scale objects: {string.Join(", ", negativeScale.Take(10))}"; ``` -## Remove Empty Leaf GameObjects +## Find or Remove Empty Leaf GameObjects + +To list only, replace `Undo.DestroyObjectImmediate(obj)` with collecting names into a list. ```csharp using UnityEditor; diff --git a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/playmode-ui-controls.md b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/playmode-ui-controls.md index f7bda52218..7b532285e1 100644 --- a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/playmode-ui-controls.md +++ b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/playmode-ui-controls.md @@ -18,57 +18,33 @@ return $"Set InputField text to: {input.text}"; ## Set Slider Value -```csharp -using UnityEngine.UI; - -Slider slider = GameObject.Find("Canvas/VolumeSlider")?.GetComponent(); -if (slider == null) return "Slider not found"; - -float oldValue = slider.value; -slider.value = 0.75f; -return $"Slider changed from {oldValue} to {slider.value}"; -``` - -## Set Slider Value with Bounds Check - ```csharp using UnityEngine.UI; using System.Linq; Slider[] sliders = Object.FindObjectsByType(FindObjectsSortMode.None); -Slider target = sliders.FirstOrDefault(s => s.gameObject.name == "HealthBar"); -if (target == null) return $"HealthBar not found. Available: {string.Join(", ", sliders.Select(s => s.gameObject.name))}"; +Slider target = sliders.FirstOrDefault(s => s.gameObject.name == "VolumeSlider"); +if (target == null) return $"VolumeSlider not found. Available: {string.Join(", ", sliders.Select(s => s.gameObject.name))}"; -float targetValue = 0.5f; +float targetValue = 0.75f; float clamped = Mathf.Clamp(targetValue, target.minValue, target.maxValue); target.value = clamped; return $"Set {target.gameObject.name} to {clamped} (range: {target.minValue}-{target.maxValue})"; ``` -## Toggle On/Off - -```csharp -using UnityEngine.UI; - -Toggle toggle = GameObject.Find("Canvas/MuteToggle")?.GetComponent(); -if (toggle == null) return "Toggle not found"; - -bool oldValue = toggle.isOn; -toggle.isOn = !toggle.isOn; -return $"Toggle changed from {oldValue} to {toggle.isOn}"; -``` +## Set Toggle Value -## Set Toggle to Specific Value +Use `!toggle.isOn` to flip, or assign a specific value directly. ```csharp using UnityEngine.UI; using System.Linq; Toggle[] toggles = Object.FindObjectsByType(FindObjectsSortMode.None); -Toggle target = toggles.FirstOrDefault(t => t.gameObject.name == "SoundToggle"); -if (target == null) return $"SoundToggle not found. Available: {string.Join(", ", toggles.Select(t => t.gameObject.name))}"; +Toggle target = toggles.FirstOrDefault(t => t.gameObject.name == "MuteToggle"); +if (target == null) return $"MuteToggle not found. Available: {string.Join(", ", toggles.Select(t => t.gameObject.name))}"; -target.isOn = true; +target.isOn = !target.isOn; return $"Set {target.gameObject.name} to {target.isOn}"; ``` diff --git a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/scene-operations.md b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/scene-operations.md index d34db4c49f..d5e17890d4 100644 --- a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/scene-operations.md +++ b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/scene-operations.md @@ -64,13 +64,6 @@ if (obj == null) return $"Found: {obj.name} at {obj.transform.position}"; ``` -## Find GameObjects by Tag - -```csharp -GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy"); -return $"Found {enemies.Length} GameObjects with tag 'Enemy'"; -``` - ## Set Parent ```csharp diff --git a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/scriptableobject.md b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/scriptableobject.md index debc01b9f8..1827a4fb5a 100644 --- a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/scriptableobject.md +++ b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/scriptableobject.md @@ -33,32 +33,7 @@ return $"Created {so.GetType().Name} at {path}"; ## Modify ScriptableObject with SerializedObject -```csharp -using UnityEditor; - -string path = "Assets/Data/MyData.asset"; -ScriptableObject so = AssetDatabase.LoadAssetAtPath(path); - -if (so == null) -{ - return $"Asset not found at {path}"; -} - -SerializedObject serializedObj = new SerializedObject(so); -SerializedProperty prop = serializedObj.FindProperty("myField"); - -if (prop != null) -{ - prop.stringValue = "New Value"; - serializedObj.ApplyModifiedProperties(); - EditorUtility.SetDirty(so); - AssetDatabase.SaveAssets(); - return "Property updated"; -} -return "Property 'myField' not found"; -``` - -## Set Int/Float/Bool Properties +Use `FindProperty` with the matching typed setter (`stringValue`, `intValue`, `floatValue`, `boolValue`, etc.). ```csharp using UnityEditor; diff --git a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/selection-operations.md b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/selection-operations.md index 629c57d8b2..c23f5587f3 100644 --- a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/selection-operations.md +++ b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/selection-operations.md @@ -65,12 +65,15 @@ Selection.objects = enemies; return $"Selected {enemies.Length} enemies"; ``` -## Get Top-Level Transforms Only +## Get Transforms by Selection Mode + +`SelectionMode.TopLevel` excludes children, `Deep` includes children, `Editable` excludes locked/immutable objects. ```csharp using UnityEditor; using System.Collections.Generic; +// TopLevel | Deep | Editable Transform[] transforms = Selection.GetTransforms(SelectionMode.TopLevel); if (transforms.Length == 0) { @@ -82,45 +85,13 @@ foreach (Transform t in transforms) { names.Add(t.name); } -return $"Top-level: {string.Join(", ", names)}"; -``` - -## Get Deep Selection (Including Children) - -```csharp -using UnityEditor; - -Transform[] transforms = Selection.GetTransforms(SelectionMode.Deep); -if (transforms.Length == 0) -{ - return "No transforms selected"; -} - -return $"Deep selection count: {transforms.Length}"; -``` - -## Get Editable Objects Only - -```csharp -using UnityEditor; -using System.Collections.Generic; - -Transform[] transforms = Selection.GetTransforms(SelectionMode.Editable); -if (transforms.Length == 0) -{ - return "No editable transforms selected"; -} - -List names = new List(); -foreach (Transform t in transforms) -{ - names.Add(t.name); -} -return $"Editable: {string.Join(", ", names)}"; +return $"Selected ({transforms.Length}): {string.Join(", ", names)}"; ``` ## Get Selected Assets +Use `Selection.GetFiltered` when you need the object reference, or `Selection.assetGUIDs` when you need GUIDs. + ```csharp using UnityEditor; using System.Collections.Generic; @@ -139,26 +110,6 @@ foreach (Object asset in selectedAssets) return $"Assets: {string.Join(", ", paths)}"; ``` -## Get Selected Asset GUIDs - -```csharp -using UnityEditor; -using System.Collections.Generic; - -string[] guids = Selection.assetGUIDs; -if (guids.Length == 0) -{ - return "No assets selected"; -} - -List paths = new List(); -foreach (string guid in guids) -{ - paths.Add(AssetDatabase.GUIDToAssetPath(guid)); -} -return $"Selected assets: {string.Join(", ", paths)}"; -``` - ## Select All Children of Selected Object ```csharp From 212aba4ccaf53cf138c0bfbe927d718d677f14ce Mon Sep 17 00:00:00 2001 From: hatayama Date: Thu, 18 Jun 2026 17:19:36 +0900 Subject: [PATCH 8/9] Restore full verb list in run-tests compile prerequisite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "edited" alone was too narrow — creating, deleting, renaming, or moving C#/test/asmdef files also requires recompilation. --- Packages/src/Editor/FirstPartyTools/RunTests/Skill/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Packages/src/Editor/FirstPartyTools/RunTests/Skill/SKILL.md b/Packages/src/Editor/FirstPartyTools/RunTests/Skill/SKILL.md index 421e47b21d..7660586e11 100644 --- a/Packages/src/Editor/FirstPartyTools/RunTests/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/RunTests/Skill/SKILL.md @@ -8,7 +8,7 @@ description: "Run Unity Test Runner and report detailed results. Use for EditMod Execute Unity Test Runner. When tests fail, NUnit XML results with error messages and stack traces are automatically saved. Read the XML file at `xmlPath` for detailed failure diagnosis. -Before running tests, run `uloop compile` first if you edited C# files, test files, `.asmdef`/`.asmref`, or package manifests since the last successful compile. This refreshes the AssetDatabase and surfaces compile errors before test execution. +Before running tests, run `uloop compile` first if you created, deleted, renamed, moved, or edited C# files, test files, `.asmdef`/`.asmref`, or package manifests since the last successful compile. This refreshes the AssetDatabase and surfaces compile errors before test execution. Before executing tests, `uloop run-tests` saves unsaved loaded Scene changes and unsaved current Prefab Stage changes by default. If saving fails, it returns `success: false`, keeps `testCount` at `0`, lists the unsaved items in `message`, and does not start the Unity Test Runner. From 2d21489c3f0af16d73e4f59ab61575f22224dc66 Mon Sep 17 00:00:00 2001 From: hatayama Date: Thu, 18 Jun 2026 18:31:00 +0900 Subject: [PATCH 9/9] Fix reviewed skill documentation examples Address codex-review findings by making dynamic-code examples deterministic, non-destructive, and aligned with actual launch output. --- .../CliOnlyTools~/Launch/Skill/SKILL.md | 4 ++- .../Skill/references/cleanup-operations.md | 36 ++++++++++--------- .../Skill/references/playmode-ui-controls.md | 5 +-- .../ReplayInput/Skill/SKILL.md | 1 - 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/Packages/src/Editor/CliOnlyTools~/Launch/Skill/SKILL.md b/Packages/src/Editor/CliOnlyTools~/Launch/Skill/SKILL.md index 525fe110b8..7bbee3d3e5 100644 --- a/Packages/src/Editor/CliOnlyTools~/Launch/Skill/SKILL.md +++ b/Packages/src/Editor/CliOnlyTools~/Launch/Skill/SKILL.md @@ -47,7 +47,9 @@ uloop launch --quit ## Output -Returns JSON with: +May print status/progress lines before the final JSON payload, such as project path, detected Unity version, or readiness wait messages. + +The final JSON payload includes: - `success`: whether the command completed - `ready`: whether Unity CLI Loop is ready for commands - `serverReady`: whether the Unity CLI Loop server accepted requests diff --git a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/cleanup-operations.md b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/cleanup-operations.md index 9d253d49a5..1c2e023f0a 100644 --- a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/cleanup-operations.md +++ b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/cleanup-operations.md @@ -80,15 +80,20 @@ return $"Removed {totalRemoved} missing scripts from scene"; ## Scan for Missing References -Scans the selected GameObject or the entire scene for null ObjectReference fields. +Scans the entire scene by default. Set `scanSelectionOnly` to `true` only when the task asks for the selected GameObject. ```csharp using UnityEditor; -// Single object: use Selection.activeGameObject -// Whole scene: use Object.FindObjectsByType(FindObjectsSortMode.None) -GameObject[] targets = Selection.activeGameObject != null - ? new[] { Selection.activeGameObject } +bool scanSelectionOnly = false; +GameObject selected = Selection.activeGameObject; +if (scanSelectionOnly && selected == null) +{ + return "No GameObject selected"; +} + +GameObject[] targets = scanSelectionOnly + ? new[] { selected } : Object.FindObjectsByType(FindObjectsSortMode.None); List results = new List(); @@ -252,19 +257,13 @@ if (negativeScale.Count == 0) return $"Negative scale objects: {string.Join(", ", negativeScale.Take(10))}"; ``` -## Find or Remove Empty Leaf GameObjects - -To list only, replace `Undo.DestroyObjectImmediate(obj)` with collecting names into a list. +## Find Empty Leaf GameObjects ```csharp using UnityEditor; GameObject[] allObjects = Object.FindObjectsByType(FindObjectsSortMode.None); - -int undoGroup = Undo.GetCurrentGroup(); -Undo.SetCurrentGroupName("Remove Empty Leaves"); - -int removedCount = 0; +List emptyObjects = new List(); foreach (GameObject obj in allObjects) { if (obj == null) continue; @@ -272,13 +271,16 @@ foreach (GameObject obj in allObjects) Component[] components = obj.GetComponents(); if (components.Length == 1 && obj.transform.childCount == 0) { - Undo.DestroyObjectImmediate(obj); - removedCount++; + emptyObjects.Add(obj.name); } } -Undo.CollapseUndoOperations(undoGroup); -return $"Removed {removedCount} empty leaf GameObjects"; +if (emptyObjects.Count == 0) +{ + return "No empty leaf GameObjects found"; +} + +return $"Empty leaf GameObjects ({emptyObjects.Count}): {string.Join(", ", emptyObjects.Take(20))}"; ``` ## Find Large Meshes diff --git a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/playmode-ui-controls.md b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/playmode-ui-controls.md index 7b532285e1..f33e3e68c9 100644 --- a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/playmode-ui-controls.md +++ b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/playmode-ui-controls.md @@ -34,7 +34,7 @@ return $"Set {target.gameObject.name} to {clamped} (range: {target.minValue}-{ta ## Set Toggle Value -Use `!toggle.isOn` to flip, or assign a specific value directly. +Assign a specific value for deterministic setup. Use `!toggle.isOn` only when the task explicitly asks to flip the current value. ```csharp using UnityEngine.UI; @@ -44,7 +44,8 @@ Toggle[] toggles = Object.FindObjectsByType(FindObjectsSortMode.None); Toggle target = toggles.FirstOrDefault(t => t.gameObject.name == "MuteToggle"); if (target == null) return $"MuteToggle not found. Available: {string.Join(", ", toggles.Select(t => t.gameObject.name))}"; -target.isOn = !target.isOn; +bool targetValue = true; +target.isOn = targetValue; return $"Set {target.gameObject.name} to {target.isOn}"; ``` diff --git a/Packages/src/Editor/FirstPartyTools/ReplayInput/Skill/SKILL.md b/Packages/src/Editor/FirstPartyTools/ReplayInput/Skill/SKILL.md index 9a23fabd47..81793dbc8e 100644 --- a/Packages/src/Editor/FirstPartyTools/ReplayInput/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/ReplayInput/Skill/SKILL.md @@ -56,4 +56,3 @@ Returns JSON with: - `totalFrames`: Total frames in the recording (nullable int) - `progress`: Replay progress (nullable float in 0.0 – 1.0) - `isReplaying`: Whether replay is currently active (nullable bool) -