diff --git a/Packages/src/Editor/CliOnlyTools~/PausePoint/Skill/SKILL.md b/Packages/src/Editor/CliOnlyTools~/PausePoint/Skill/SKILL.md index 1af3de1e2..e4106e44f 100644 --- a/Packages/src/Editor/CliOnlyTools~/PausePoint/Skill/SKILL.md +++ b/Packages/src/Editor/CliOnlyTools~/PausePoint/Skill/SKILL.md @@ -1,54 +1,63 @@ --- name: uloop-wait-for-debug-break -description: "Use this for Unity PlayMode/E2E checks when simulated input or gameplay events cause a state transition that a screenshot, durable state, or specific value log cannot prove. Pause at the natural transition point after the input/event is consumed and inspect the paused frame. Do not use simulate-* Success=true, generic action logs, sleeps/retries, testing-only counters, or Time.timeScale changes as substitutes for debug-break evidence of transient transitions." +description: "Use this like an IDE breakpoint for Unity PlayMode/E2E when you need to pause a specific frame and inspect variables, GameObjects, or runtime state." --- -## Workflow +## Quick Check Template -1. Add a marker at the state you want to inspect: +Use this small loop for one representative frame you care about: + +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 +using UnityEngine; using io.github.hatayama.UnityCliLoop.Runtime; -UnityCliLoopDebug.Break("player-jumped"); +Debug.Log($"state-transition-applied localValue={localValue} reason={reason}"); +UnityCliLoopDebug.Break("state-transition-applied"); ``` -2. Compile the project. -3. Enable the marker before triggering the target code path: +2. Compile, enter PlayMode, then enable the marker: ```bash -uloop enable-debug-break --id player-jumped --timeout-seconds 30 +uloop enable-debug-break --id state-transition-applied --timeout-seconds 30 ``` -4. Check marker state if needed: +3. Trigger the action with a `simulate-*` command. +4. Run `uloop wait-for-debug-break --id state-transition-applied --timeout-seconds 30`, even if the trigger command already returned `InterruptedByDebugBreak=true`. +5. Before resuming, read the focused log for the same marker id: ```bash -uloop debug-break-status --id player-jumped +uloop get-logs --search-text state-transition-applied --max-count 20 ``` -5. Trigger the behavior with `simulate-keyboard`, `simulate-mouse-input`, UI interaction, dynamic code, or similar commands. -6. Wait for the marker: +6. While Unity is still paused, capture any additional evidence with `uloop execute-dynamic-code`, `uloop get-hierarchy`, `uloop find-game-objects`, and one screenshot. +7. Clear the marker with `uloop clear-debug-break --id state-transition-applied` or stop PlayMode before moving on. -```bash -uloop wait-for-debug-break --id player-jumped --timeout-seconds 30 -``` +## When To Use -If this command times out, the marker line was not reached while the command waited. Inspect `error.details.status`, `hitCount`, `isPlaying`, `isPaused`, `elapsedSinceEnabledMilliseconds`, and `remainingMilliseconds` to distinguish input not being consumed, gameplay conditions not being met, an id mismatch, or Unity already being paused. `elapsedSinceEnabledMilliseconds` is measured from `enable-debug-break`, not from `wait-for-debug-break`. +- Use this as the standard frame proof for state-changing PlayMode/E2E simulated input, physics, or UI transitions. +- Pause at least one representative transition per E2E pass, 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. -7. While Unity is paused, inspect state with `uloop get-logs`, `uloop get-hierarchy`, `uloop find-game-objects`, screenshots, or `uloop execute-dynamic-code`. -8. Clear the marker if you stop waiting: +## Timeout Checks -```bash -uloop clear-debug-break --id player-jumped -``` +If this command times out, the marker line was not reached while the command waited. Inspect `error.details.status`, `hitCount`, `isPlaying`, `isPaused`, `elapsedSinceEnabledMilliseconds`, and `remainingMilliseconds` to distinguish input not being consumed, runtime conditions not being met, an id mismatch, or Unity already being paused. `elapsedSinceEnabledMilliseconds` is measured from `enable-debug-break`, not from `wait-for-debug-break`. + +Use `uloop debug-break-status --id state-transition-applied` only when you need to confirm the marker is armed or inspect the current hit state. Add focused debug logs before the marker when local variables must be captured. ## Marker Placement -- Prefer natural gameplay points or state-transition points after input has been consumed, such as after jump velocity or state changes, physics contact, or damage application. +- Prefer natural runtime points after input has been consumed, such as after a command is accepted, a state value changes, an evaluation step resolves, or a dependent component is updated. - For frame-specific bugs, place the marker on the suspicious state branch or immediately after the state mutation you need to freeze. - To avoid Domain Reload loss or tool Busy states, enable markers after Play Mode is running, and prefer checkpoints reached after the triggering input command can return. -- Avoid placing the marker immediately after issuing simulated input unless that exact input handling line is the state you need to inspect. Immediate markers can interrupt the input command before the resulting gameplay state settles. -- Use separate ids for strict phases, for example `jump-input-read`, `jump-velocity-applied`, and `jump-landed`, instead of reusing one broad marker. +- Avoid placing the marker immediately after issuing simulated input unless that exact input handling line is the state you need to inspect. Immediate markers can interrupt the input command before the resulting runtime state settles. +- Use separate ids for strict phases, for example `input-read`, `state-updated`, and `result-committed`, instead of reusing one broad marker. ## Safety diff --git a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/SKILL.md b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/SKILL.md index d29fcdc9c..bfd3f4e0a 100644 --- a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/SKILL.md @@ -1,7 +1,7 @@ --- name: uloop-execute-dynamic-code toolName: execute-dynamic-code -description: "Execute C# with Unity APIs when existing uloop tools cannot inspect or edit enough. Use for scene, prefab, SerializedObject, AssetDatabase refresh/.meta generation, menu, or PlayMode automation." +description: "Execute C# with Unity APIs when existing uloop tools cannot inspect or edit enough. Use for reachable scene/component state, scene/prefab/menu automation, and PlayMode checks" context: fork --- @@ -11,6 +11,8 @@ 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. +This tool can inspect reachable Unity state, such as GameObjects, components, public properties, static values, and method results. It cannot directly read local variables or intermediate calculations inside an already-running method. When those values matter, add a focused `Debug.Log` immediately before `UnityCliLoopDebug.Break("")`, then run `get-logs --search-text ` while Unity is paused. Do not replace that log read with execute-dynamic-code. + ## Workflow 1. Read the relevant reference file(s) from the Code Examples section below diff --git a/Packages/src/Editor/FirstPartyTools/GetLogs/Skill/SKILL.md b/Packages/src/Editor/FirstPartyTools/GetLogs/Skill/SKILL.md index a74e55532..2ad7bd92a 100644 --- a/Packages/src/Editor/FirstPartyTools/GetLogs/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/GetLogs/Skill/SKILL.md @@ -1,13 +1,15 @@ --- name: uloop-get-logs toolName: get-logs -description: "Read current Unity Console entries from a running Editor. Use during bug investigation after compile, tests, PlayMode, or dynamic code to inspect logs, warnings, errors, and stack traces." +description: "Read current Unity Console entries from a running Editor. Use during bug investigation after compile, tests, PlayMode, dynamic code, or immediately after `uloop-wait-for-debug-break`." --- # uloop get-logs Retrieve logs from Unity Console. +When a debug-break 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 diff --git a/Packages/src/Editor/FirstPartyTools/SimulateKeyboard/Skill/SKILL.md b/Packages/src/Editor/FirstPartyTools/SimulateKeyboard/Skill/SKILL.md index fc6596887..210b8cc58 100644 --- a/Packages/src/Editor/FirstPartyTools/SimulateKeyboard/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/SimulateKeyboard/Skill/SKILL.md @@ -12,10 +12,10 @@ Simulate keyboard input on Unity PlayMode: $ARGUMENTS ## Workflow 1. Ensure Unity is in PlayMode (use `uloop control-play-mode --action Play` if not) -2. Execute the appropriate `uloop simulate-keyboard` command -3. Take a screenshot to verify the result: `uloop screenshot --capture-mode rendering` -4. If the screenshot cannot prove the gameplay state, place and enable a `UnityCliLoopDebug.Break("")` marker with `uloop enable-debug-break`, run the input again, then inspect while Unity is paused -5. Report what happened +2. Execute the needed `uloop simulate-keyboard` commands +3. Inspect the result with the lightest useful evidence: runtime state, logs, or a screenshot +4. If exact-frame proof would reduce uncertainty, treat Debug Break inspection as an optional follow-up using the section below +5. Report what happened and which evidence was used ## Tool Reference @@ -39,17 +39,19 @@ uloop simulate-keyboard --action --key [options] | `KeyDown` | KeyDown only (held until KeyUp) | Start continuous movement, hold sprint | | `KeyUp` | KeyUp only (release held key) | Stop movement, release sprint | -Use `Press` for edge-triggered gameplay code such as `Keyboard.current.spaceKey.wasPressedThisFrame`. +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 the game state does not change, do not immediately rewrite the user's gameplay code to `isPressed`. First verify that the gameplay 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. +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. -### Debug Break verification +### Optional Debug Break Inspection -- Use `UnityCliLoopDebug.Break("")` when a screenshot cannot prove that the keyboard input changed gameplay state, such as jump, sprint, or interaction. -- Put the marker at a natural state transition after the game consumed the key, such as after jump velocity is applied, not immediately after sending `simulate-keyboard`. +- Use `UnityCliLoopDebug.Break("")` with `uloop-wait-for-debug-break` only when exact-frame evidence would reduce uncertainty. Final logs, screenshots, or durable state may be enough for simpler checks. +- 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 `UnityCliLoopDebug.Break("")` and read them with `uloop-get-logs` while Unity is paused. A break 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 `InterruptedByDebugBreak: true`, Unity is paused for inspection and the tool released its held input bookkeeping. If a `UnityCliLoopDebug.Break` marker caused the pause, `DebugBreakId` and `DebugBreakHitCount` identify it. Use `get-logs`, `get-hierarchy`, `find-game-objects`, or `execute-dynamic-code` before resuming. -- Use distinct marker ids for strict phases, for example `jump-key-read` and `jump-velocity-applied`. +- Use distinct marker ids for strict phases, for example `input-read`, `state-updated`, and `result-committed`. ### KeyDown/KeyUp Rules @@ -68,16 +70,16 @@ Use `KeyDown` / `KeyUp` when the scenario intentionally needs a held key. ## Examples ```bash -# One-shot key press (tap W once) +# One-shot key press uloop simulate-keyboard --action Press --key W -# Jump (tap Space) +# One-shot action key uloop simulate-keyboard --action Press --key Space -# Hold W for 2 seconds (move forward) +# Hold a key for 2 seconds uloop simulate-keyboard --action Press --key W --duration 2.0 -# Sprint forward (hold Shift + W, then release) +# Hold two keys, then release them uloop simulate-keyboard --action KeyDown --key LeftShift uloop simulate-keyboard --action KeyDown --key W uloop screenshot --capture-mode rendering diff --git a/Packages/src/Editor/FirstPartyTools/SimulateMouseInput/Skill/SKILL.md b/Packages/src/Editor/FirstPartyTools/SimulateMouseInput/Skill/SKILL.md index 78b3f0fa8..8bb19ad24 100644 --- a/Packages/src/Editor/FirstPartyTools/SimulateMouseInput/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/SimulateMouseInput/Skill/SKILL.md @@ -1,22 +1,22 @@ --- name: uloop-simulate-mouse-input toolName: simulate-mouse-input -description: "Simulate Mouse.current input in PlayMode through Unity Input System. Use for gameplay clicks, mouse delta, or scroll; use simulate-mouse-ui for EventSystem UI elements." +description: "Simulate Mouse.current input in PlayMode through Unity Input System. Use for gameplay mouse clicks, held button input, movement delta, or scroll. Use simulate-mouse-ui for UI." context: fork --- # Task -Simulate mouse input via Input System in Unity PlayMode: $ARGUMENTS +Simulate mouse input via Input System in Unity PlayMode. ## Workflow 1. Ensure Unity is in PlayMode (use `uloop control-play-mode --action Play` if not) 2. For Click/LongPress: determine the target screen position (use `uloop screenshot` to find coordinates) -3. Execute the appropriate `uloop simulate-mouse-input` command -4. Take a screenshot to verify the result: `uloop screenshot --capture-mode rendering` -5. If the screenshot cannot prove the gameplay state, place and enable a `UnityCliLoopDebug.Break("")` marker with `uloop enable-debug-break`, run the input again, then inspect while Unity is paused -6. Report what happened +3. Execute the needed `uloop simulate-mouse-input` commands +4. Inspect the result with the lightest useful evidence: runtime state, logs, or a screenshot +5. If exact-frame proof would reduce uncertainty, treat Debug Break inspection as an optional follow-up using the section below +6. Report what happened and which evidence was used ## Tool Reference @@ -42,18 +42,20 @@ uloop simulate-mouse-input --action [options] | Action | What it injects | Description | |--------|----------------|-------------| -| `Click` | Mouse.current button press → release | Inject a button click so game logic detects `wasPressedThisFrame` | +| `Click` | Mouse.current button press → release | Inject a button click so runtime logic detects `wasPressedThisFrame` | | `LongPress` | Mouse.current button press → hold → release | Hold a button for `--duration` seconds | -| `MoveDelta` | Mouse.current.delta | Inject mouse movement delta one-shot (e.g. for FPS camera look) | +| `MoveDelta` | Mouse.current.delta | Inject mouse movement delta one-shot | | `SmoothDelta` | Mouse.current.delta (per-frame) | Inject mouse delta smoothly over `--duration` seconds (human-like camera pan) | -| `Scroll` | Mouse.current.scroll | Inject scroll wheel input (e.g. for hotbar or zoom) | +| `Scroll` | Mouse.current.scroll | Inject scroll wheel input | -### Debug Break verification +### Optional Debug Break Inspection -- Use `UnityCliLoopDebug.Break("")` when a screenshot cannot prove that mouse input changed gameplay state, such as block hit, camera turn, or item placement. -- Put the marker at a natural state transition after the game consumed the mouse input, such as after a raycast hit, damage application, placement, or camera rotation update, not immediately after sending `simulate-mouse-input`. -- If the response has `InterruptedByDebugBreak: true`, Unity is paused for inspection and the tool released its held input bookkeeping. If a `UnityCliLoopDebug.Break` marker caused the pause, `DebugBreakId` and `DebugBreakHitCount` identify it. Use `get-logs`, `get-hierarchy`, `find-game-objects`, or `execute-dynamic-code` before resuming. -- Use distinct marker ids for strict phases, for example `block-raycast-hit` and `block-damage-applied`. +- Use `UnityCliLoopDebug.Break("")` with `uloop-wait-for-debug-break` only when exact-frame evidence would reduce uncertainty. Final logs, screenshots, or durable state may be enough for simpler checks. +- Place the break 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 `UnityCliLoopDebug.Break("")` and read them with `uloop-get-logs` while Unity is paused. A break hit proves the line was reached, not the frame-local values. +- If the response has `InterruptedByDebugBreak: true`, Unity is paused for inspection and the tool released its held input bookkeeping. `DebugBreakId` and `DebugBreakHitCount` identify the break that paused Unity. Use `get-logs`, `get-hierarchy`, `find-game-objects`, or `execute-dynamic-code` before resuming. +- Use distinct ids for strict phases, for example `input-read`, `state-updated`, and `result-committed`. +- Remove temporary break/log instrumentation before final validation when it was added only for inspection. ### Global Options (optional) @@ -67,31 +69,31 @@ uloop simulate-mouse-input --action [options] | Scenario | Tool | |----------|------| | Click a Unity UI Button (IPointerClickHandler) | `simulate-mouse-ui` | -| Destroy a block in Minecraft (reads `Mouse.current.leftButton`) | `simulate-mouse-input` when the project uses the New Input System | -| Place a block with right-click | `simulate-mouse-input --button Right` when the project uses the New Input System | +| 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` | -| Look around with mouse (FPS camera) | `simulate-mouse-input --action MoveDelta` when the project uses the New Input System | -| Scroll hotbar slots | `simulate-mouse-input --action Scroll` when the project uses the New Input System | +| 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 ```bash -# Left-click at screen center (for game logic) +# Left-click at screen center for runtime input uloop simulate-mouse-input --action Click --x 400 --y 300 -# Right-click at screen center (e.g. place block) +# Right-click at screen center uloop simulate-mouse-input --action Click --x 400 --y 300 --button Right -# Hold left-click for 2 seconds (e.g. mine block) +# Hold left-click for 2 seconds uloop simulate-mouse-input --action LongPress --x 400 --y 300 --duration 2.0 -# Look right (FPS camera) +# Send a one-shot mouse delta uloop simulate-mouse-input --action MoveDelta --delta-x 100 --delta-y 0 -# Scroll up (e.g. previous hotbar slot) +# Scroll up uloop simulate-mouse-input --action Scroll --scroll-y 120 -# Scroll down (e.g. next hotbar slot) +# Scroll down uloop simulate-mouse-input --action Scroll --scroll-y -120 # Smooth camera pan right over 0.5 seconds @@ -115,7 +117,7 @@ Returns JSON with: - `PositionX`: Target X coordinate (nullable float; populated for `Click` / `LongPress`) - `PositionY`: Target Y coordinate (nullable float; populated for `Click` / `LongPress`) - `InterruptedByDebugBreak`: True when Unity paused during Debug Break inspection and the input bookkeeping was safely released -- `DebugBreakId`: The marker id when a `UnityCliLoopDebug.Break` marker caused the interruption -- `DebugBreakHitCount`: The marker hit count when a `UnityCliLoopDebug.Break` marker caused the interruption +- `DebugBreakId`: The id from `UnityCliLoopDebug.Break("")` when it caused the interruption +- `DebugBreakHitCount`: The hit count for that `UnityCliLoopDebug.Break("")` There is no `DeltaX`, `DeltaY`, `ScrollX`, `ScrollY`, `Duration`, or hit-element field in the response — only the issued action, button, target position, and Debug Break interruption state are echoed back. 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 54e34d693..3629eeff8 100644 --- a/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/SimulateMouseUi/Skill/SKILL.md @@ -7,16 +7,17 @@ context: fork # Task -Simulate mouse interaction on Unity PlayMode UI: $ARGUMENTS +Simulate mouse interaction on Unity PlayMode UI. ## Workflow 1. Ensure Unity is in PlayMode (use `uloop control-play-mode --action Play` if not) 2. Get UI element info: `uloop screenshot --capture-mode rendering --annotate-elements --elements-only` 3. Use the `AnnotatedElements` array to find the target element by `Label`, `Name`, or `Path` (A=frontmost, B=next, ...). Use `Interaction` to distinguish click targets from drag/drop/text targets, then use `SimX`/`SimY` directly as `--x`/`--y` coordinates. -4. Execute the appropriate `uloop simulate-mouse-ui` command -5. Take a screenshot to verify the result: `uloop screenshot --capture-mode rendering --annotate-elements` -6. Report what happened +4. Execute the needed `uloop simulate-mouse-ui` commands +5. Inspect the result with the lightest useful evidence: runtime state, logs, or a screenshot +6. If exact-frame proof would reduce uncertainty, treat Debug Break inspection as an optional follow-up using the section below +7. Report what happened and which evidence was used ## Tool Reference @@ -75,6 +76,15 @@ uloop simulate-mouse-ui --action --x --y [options] - `--bypass-raycast` still uses coordinates for pointer event positions, but chooses the clicked, long-pressed, or dragged GameObject by `--target-path` - If `--target-path` or `--drop-target-path` matches multiple active GameObjects, the command fails instead of choosing an arbitrary duplicate +## Optional Debug Break Inspection + +- Use `UnityCliLoopDebug.Break("")` with `uloop-wait-for-debug-break` only when exact-frame evidence would reduce uncertainty. Final logs, screenshots, or durable state may be enough for simpler checks. +- Place the break 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 `execute-dynamic-code` cannot inspect after the fact, log just those values near `UnityCliLoopDebug.Break("")` and read them with `uloop-get-logs` while Unity is paused. A break hit proves the line was reached, not the frame-local values. +- Treat `simulate-mouse-ui Success=true`, generic action logs, and final durable counters as useful evidence, not paused-frame proof. +- If a `UnityCliLoopDebug.Break` pauses Unity, inspect with `get-logs`, `get-hierarchy`, `find-game-objects`, or `execute-dynamic-code` before resuming. +- Remove temporary break/log instrumentation before final validation when it was added only for inspection. + ## Examples ```bash @@ -114,7 +124,7 @@ uloop simulate-mouse-ui --action DragEnd --x 600 --y 300 - Unity must be in **PlayMode** - Target scene must have an **EventSystem** GameObject - UI elements must have a **GraphicRaycaster** on their Canvas -- If you need gameplay mouse input rather than UI pointer events, `simulate-mouse-input` assumes the project uses the New Input System; otherwise prefer `execute-dynamic-code` +- If you need runtime mouse input rather than UI pointer events, `simulate-mouse-input` assumes the project uses the New Input System; otherwise prefer `execute-dynamic-code` ## Output diff --git a/scripts/refresh-neighbor-game-skills.sh b/scripts/refresh-neighbor-game-skills.sh index 87a3c1092..e84b8e0f5 100755 --- a/scripts/refresh-neighbor-game-skills.sh +++ b/scripts/refresh-neighbor-game-skills.sh @@ -1,13 +1,15 @@ #!/bin/sh # Development helper for refreshing generated uloop skill files in sibling Unity projects. # This is not an installed agent skill or a runtime command. It exists to support local -# uloop development by quitting each target Unity Editor, regenerating Claude/Agents -# skill copies, committing those generated files locally, removing Library after Unity -# has stopped, and relaunching each project. +# uloop development by resetting each target Git repository, quitting each target +# Unity Editor, regenerating Claude/Agents skill copies, committing those generated +# files locally, removing Library after Unity has stopped, relaunching each project, +# and opening the sample scene. set -eu skill_name="refresh-neighbor-game-skills" expected_project_count=3 +sample_scene_path="Assets/Scenes/SampleScene.unity" dry_run=0 uloop_root="${ULOOP_ROOT:-}" project_file="$(mktemp "${TMPDIR:-/tmp}/${skill_name}.projects.XXXXXX")" @@ -31,11 +33,13 @@ Usage: refresh-neighbor-game-skills.sh [--dry-run] [--uloop-root PATH] [--project PATH ...] Workflow for each target Unity project: - 0. Quit Unity if running - 1. Install uloop skills for Claude and Agents - 2. Commit generated skill changes without pushing - 3. Remove Library - 4. Launch Unity with launch-unity + 0. Reset Git state to HEAD and remove untracked files + 1. Quit Unity if running + 2. Install uloop skills for Claude and Agents + 3. Commit generated skill changes without pushing + 4. Remove Library + 5. Launch Unity with launch-unity + 6. Open Assets/Scenes/SampleScene.unity USAGE } @@ -238,6 +242,24 @@ assert_project_count() { [ "$count" -eq "$expected_project_count" ] || fail "expected $expected_project_count sibling Unity projects, found $count" } +reset_git_state() { + project=$1 + log "Resetting Git state: $project" + run git -C "$project" reset --hard HEAD + run git -C "$project" clean -fd + + if [ "$dry_run" -eq 1 ]; then + log "[dry-run] assert Git state is clean for $project" + return 0 + fi + + dirty=$(git -C "$project" status --porcelain) + if [ -n "$dirty" ]; then + printf '%s\n' "$dirty" >&2 + fail "git state is not clean after reset: $project" + fi +} + assert_clean_skill_dirs() { project=$1 existing=$(git -C "$project" status --porcelain -- .claude/skills .agents/skills) @@ -320,6 +342,63 @@ launch_project() { run launch-unity "$project" } +wait_for_unity_ready() { + project=$1 + timeout_seconds=$2 + elapsed_seconds=0 + + if [ "$dry_run" -eq 1 ]; then + log "[dry-run] wait until Unity responds to uloop for $project" + return 0 + fi + + while [ "$elapsed_seconds" -lt "$timeout_seconds" ]; do + if "$uloop_bin" --project-path "$project" get-logs --max-count 1 >/dev/null 2>&1; then + return 0 + fi + + sleep 2 + elapsed_seconds=$((elapsed_seconds + 2)) + done + + fail "Unity did not become ready after launch: $project" +} + +open_sample_scene() { + project=$1 + [ -f "$project/$sample_scene_path" ] || fail "sample scene not found: $project/$sample_scene_path" + + wait_for_unity_ready "$project" 300 + code=" +using UnityEditor.SceneManagement; +using UnityEngine.SceneManagement; + +string scenePath = \"$sample_scene_path\"; +if (SceneManager.GetActiveScene().path != scenePath) +{ + EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single); +} + +return SceneManager.GetActiveScene().path; +" + if [ "$dry_run" -eq 1 ]; then + run "$uloop_bin" --project-path "$project" execute-dynamic-code --code "$code" + return 0 + fi + + command -v jq >/dev/null 2>&1 || fail "jq is not available on PATH" + response='' + if ! response=$("$uloop_bin" --project-path "$project" execute-dynamic-code --code "$code"); then + [ -z "$response" ] || printf '%s\n' "$response" >&2 + fail "failed to execute sample scene open command: $project" + fi + + if ! printf '%s\n' "$response" | jq -e --arg scene_path "$sample_scene_path" '.Success == true and .Result == $scene_path' >/dev/null; then + printf '%s\n' "$response" >&2 + fail "failed to open sample scene: $project" + fi +} + parse_args "$@" resolve_uloop_root discover_projects @@ -334,11 +413,16 @@ while IFS= read -r project; do log " $project" done <"$project_file" +log "Phase 0/6: reset Git state" +while IFS= read -r project; do + reset_git_state "$project" +done <"$project_file" + while IFS= read -r project; do assert_clean_skill_dirs "$project" done <"$project_file" -log "Phase 0/4: quit Unity" +log "Phase 1/6: quit Unity" while IFS= read -r project; do quit_unity "$project" done <"$project_file" @@ -347,24 +431,29 @@ while IFS= read -r project; do assert_unity_stopped "$project" done <"$project_file" -log "Phase 1/4: install skills" +log "Phase 2/6: install skills" while IFS= read -r project; do install_skills "$project" done <"$project_file" -log "Phase 2/4: commit generated skills" +log "Phase 3/6: commit generated skills" while IFS= read -r project; do commit_generated_skills "$project" done <"$project_file" -log "Phase 3/4: remove Library" +log "Phase 4/6: remove Library" while IFS= read -r project; do remove_library "$project" done <"$project_file" -log "Phase 4/4: launch Unity" +log "Phase 5/6: launch Unity" while IFS= read -r project; do launch_project "$project" done <"$project_file" +log "Phase 6/6: open sample scene" +while IFS= read -r project; do + open_sample_scene "$project" +done <"$project_file" + log "Done."