diff --git a/.agents/skills/uloop-compile/SKILL.md b/.agents/skills/uloop-compile/SKILL.md index 9aaba36ce4..108abc5a84 100644 --- a/.agents/skills/uloop-compile/SKILL.md +++ b/.agents/skills/uloop-compile/SKILL.md @@ -18,10 +18,27 @@ uloop compile [--force-recompile] [--no-wait-for-domain-reload] [--stop-on-exter | Parameter | Type | Default | Description | |-----------|------|---------|-------------| -| `--force-recompile` | flag | - | Use for broader validation, including warnings hidden by other asmdefs; much slower than normal compile | +| `--force-recompile` | flag | - | Full recompile plus domain reload. Rarely needed — see "When to use --force-recompile" below | | `--no-wait-for-domain-reload` | flag | - | Return before Domain Reload completion | | `--stop-on-external-scene-changes` | flag | - | Stop before compilation if open Scene files changed externally instead of auto-reloading them | +## When to use --force-recompile + +`--force-recompile` is almost never needed. Detecting changed files is Unity's job: even when +files were edited outside the Editor, a plain `uloop compile` refreshes assets and runs every +recompilation the changes require. "The files were changed externally, so recompile everything +just in case" is not a valid reason. + +Why to avoid it: + +- On large projects a full recompile plus domain reload can freeze Unity for a long time. +- The result crosses a domain reload, so it often comes back as `COMPILE_RESULT_UNKNOWN` and + does not work as a verification step. +- It puts the Editor into the unstable just-after-reload state for no benefit. + +The one legitimate use case: you need warnings hidden by other asmdefs surfaced by a full +build. Otherwise always run plain `uloop compile`. + ## Output Returns JSON: diff --git a/.agents/skills/uloop-execute-dynamic-code/SKILL.md b/.agents/skills/uloop-execute-dynamic-code/SKILL.md index 42208832b9..9831d85fa2 100644 --- a/.agents/skills/uloop-execute-dynamic-code/SKILL.md +++ b/.agents/skills/uloop-execute-dynamic-code/SKILL.md @@ -13,6 +13,8 @@ For basic selected GameObject discovery or property inspection, use `find-game-o 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, enable a source pause point on that line instead (`uloop enable-pause-point --file --line `, see the `uloop-pause-point` skill): the hit response's `CapturedVariables` already contains the locals, parameters, and instance fields at that line, with no code edit or recompile. `CapturedVariables` is a pre-line snapshot; this tool during the pause sees the interrupted method's post-interrupt state instead. While Unity stays paused on the hit, use `UloopPausePoint.TryGetCapturedValue(name)` (plus `GetCapturedNames()` / `GetCapturedPausePointId()`) for live captured references such as collections. Do not try to reconstruct pre-line locals with execute-dynamic-code alone. +The reverse combination also works: to freeze Unity on the first frame where a runtime condition holds (an animation peak, HP reaching zero, an enemy spawning), enable an id-only pause point, then use this tool to register an `EditorApplication.update` watcher that calls `UloopPausePoint.Pause(id)` when the condition is met, and return immediately. Wait with `uloop await-pause-point` on the CLI side — never poll or sleep inside the snippet itself, because the body runs synchronously on the main thread and frames stop advancing. See "Catching a Runtime Condition with a Dynamic-Code Trigger" in the `uloop-pause-point` skill. + ## 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. diff --git a/.agents/skills/uloop-pause-point/SKILL.md b/.agents/skills/uloop-pause-point/SKILL.md index 433843c0b1..be0e3f4c4d 100644 --- a/.agents/skills/uloop-pause-point/SKILL.md +++ b/.agents/skills/uloop-pause-point/SKILL.md @@ -94,9 +94,58 @@ Watch expressions are in-memory Editor state. A domain reload clears them, so re ## Marker Types - `uloop enable-pause-point --file --line` patches the already-compiled method at a source line. No code edit or recompile is required. -- `UloopPausePoint.Pause(id)` is a hand-written marker call for code paths that file:line patching cannot reach. Pair it with `uloop enable-pause-point --id ` (no `--file`/`--line`). +- `UloopPausePoint.Pause(id)` is a hand-written marker call for code paths that file:line patching cannot reach. Pair it with `uloop enable-pause-point --id ` (no `--file`/`--line`). The call does not need to live in committed source — a dynamic-code watcher can fire it (see the next section). - For ordinary file:line debugging you do not need `UloopPausePoint.Pause` in source. Prefer CLI enable when the target line can be patched. +## Catching a Runtime Condition with a Dynamic-Code Trigger + +A file:line pause point freezes a specific source line. When the moment you need is defined by a runtime condition instead — an animation passing a normalized time, HP reaching zero, an enemy spawning — combine an id-only marker with `execute-dynamic-code`. Timing-sensitive verification such as short motions or one-frame effects cannot be captured by sleeping and then taking a screenshot; this pattern freezes the first frame where the condition holds, without writing any .cs file. + +1. Enable an id-only marker: `uloop enable-pause-point --id hit-peak --timeout-seconds 120` (single-shot by default). +2. Run `uloop execute-dynamic-code` to trigger the action and register a watcher on `EditorApplication.update`, then return immediately. The watcher evaluates the condition every frame; on the first frame it holds, it removes itself and calls `UloopPausePoint.Pause("hit-peak")`. +3. Wait on the CLI side: `uloop await-pause-point --id hit-peak --timeout-seconds 120`. +4. While Unity is paused, collect evidence: `uloop screenshot`, state reads with `execute-dynamic-code`, or `control-play-mode --action Step` frame stepping. +5. Resume with `uloop control-play-mode --action Play`. + +Example watcher (freeze when the Hit animation passes 30% of the motion): + +```csharp +using UnityEngine; +using UnityEditor; +using io.github.hatayama.UnityCliLoop.Runtime; +Animator animator = GameObject.Find("Zombie").GetComponent(); +// Match the marker's --timeout-seconds so an unmet condition cannot leak the delegate +double deadline = EditorApplication.timeSinceStartup + 120d; +EditorApplication.CallbackFunction watcher = null; +watcher = () => +{ + if (EditorApplication.timeSinceStartup > deadline) + { + EditorApplication.update -= watcher; + return; + } + AnimatorStateInfo state = animator.GetCurrentAnimatorStateInfo(0); + if (!state.IsName("Hit") || state.normalizedTime < 0.3f) return; + EditorApplication.update -= watcher; + UloopPausePoint.Pause("hit-peak"); +}; +EditorApplication.update += watcher; +return "watcher registered"; +``` + +Rules for this pattern: + +- The dynamic-code body runs synchronously on the main thread. Never poll or sleep inside the snippet — frames stop advancing and the animation freezes with them. Register the watcher and return; the waiting belongs to `await-pause-point`. +- The watcher must unsubscribe itself from `EditorApplication.update` when it fires, and also on a deadline in case the condition never holds — a leaked delegate keeps running until the next domain reload. Match the deadline to the marker's `--timeout-seconds`. +- `UloopPausePoint.Pause(id)` is a public static Runtime API, and dynamic code compiles against the project's assemblies, so the watcher can call it exactly like game code. It fires only while the same id is enabled; otherwise it is a no-op, so a stray watcher cannot pause Unity unexpectedly. +- A single-shot marker disarms after the first hit. To catch repeated occurrences, enable with `--mode continuous` and run `await-pause-point` again after each resume. + +## Pausing Right After Simulated Input, Plus N Frames + +To freeze the frame where a `simulate-mouse-ui` click or a `simulate-keyboard` key press lands, you do not need a watcher: enable a file:line pause point on the input-consuming line before sending the input. When the pause lands mid-command, the `simulate-*` command returns promptly with `InterruptedByPausePoint=true` (see Line Placement). + +For "N frames after the input" (for example, three frames after a key press), advance from that hit with `control-play-mode --action Step` exactly N times — `Step` works right after a hit. Do not compute frame offsets in a dynamic-code watcher (recording `Time.frameCount` and pausing at `recorded + N`): frames keep advancing between CLI commands, so the recorded baseline is race-prone and the pause lands on an unpredictable frame. Reserve the watcher pattern for condition-defined moments; use hit-then-Step for frame-offset positioning. + ## Hit Preconditions A pause point hits only when control flow reaches the patched line (or the `Pause(id)` call). `simulate-keyboard` returning `PressEdgeObserved=true` means the input edge was observed, not that your target game logic has reached the pause line yet. diff --git a/.claude/skills/uloop-compile/SKILL.md b/.claude/skills/uloop-compile/SKILL.md index 9aaba36ce4..108abc5a84 100644 --- a/.claude/skills/uloop-compile/SKILL.md +++ b/.claude/skills/uloop-compile/SKILL.md @@ -18,10 +18,27 @@ uloop compile [--force-recompile] [--no-wait-for-domain-reload] [--stop-on-exter | Parameter | Type | Default | Description | |-----------|------|---------|-------------| -| `--force-recompile` | flag | - | Use for broader validation, including warnings hidden by other asmdefs; much slower than normal compile | +| `--force-recompile` | flag | - | Full recompile plus domain reload. Rarely needed — see "When to use --force-recompile" below | | `--no-wait-for-domain-reload` | flag | - | Return before Domain Reload completion | | `--stop-on-external-scene-changes` | flag | - | Stop before compilation if open Scene files changed externally instead of auto-reloading them | +## When to use --force-recompile + +`--force-recompile` is almost never needed. Detecting changed files is Unity's job: even when +files were edited outside the Editor, a plain `uloop compile` refreshes assets and runs every +recompilation the changes require. "The files were changed externally, so recompile everything +just in case" is not a valid reason. + +Why to avoid it: + +- On large projects a full recompile plus domain reload can freeze Unity for a long time. +- The result crosses a domain reload, so it often comes back as `COMPILE_RESULT_UNKNOWN` and + does not work as a verification step. +- It puts the Editor into the unstable just-after-reload state for no benefit. + +The one legitimate use case: you need warnings hidden by other asmdefs surfaced by a full +build. Otherwise always run plain `uloop compile`. + ## Output Returns JSON: diff --git a/.claude/skills/uloop-execute-dynamic-code/SKILL.md b/.claude/skills/uloop-execute-dynamic-code/SKILL.md index 42208832b9..9831d85fa2 100644 --- a/.claude/skills/uloop-execute-dynamic-code/SKILL.md +++ b/.claude/skills/uloop-execute-dynamic-code/SKILL.md @@ -13,6 +13,8 @@ For basic selected GameObject discovery or property inspection, use `find-game-o 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, enable a source pause point on that line instead (`uloop enable-pause-point --file --line `, see the `uloop-pause-point` skill): the hit response's `CapturedVariables` already contains the locals, parameters, and instance fields at that line, with no code edit or recompile. `CapturedVariables` is a pre-line snapshot; this tool during the pause sees the interrupted method's post-interrupt state instead. While Unity stays paused on the hit, use `UloopPausePoint.TryGetCapturedValue(name)` (plus `GetCapturedNames()` / `GetCapturedPausePointId()`) for live captured references such as collections. Do not try to reconstruct pre-line locals with execute-dynamic-code alone. +The reverse combination also works: to freeze Unity on the first frame where a runtime condition holds (an animation peak, HP reaching zero, an enemy spawning), enable an id-only pause point, then use this tool to register an `EditorApplication.update` watcher that calls `UloopPausePoint.Pause(id)` when the condition is met, and return immediately. Wait with `uloop await-pause-point` on the CLI side — never poll or sleep inside the snippet itself, because the body runs synchronously on the main thread and frames stop advancing. See "Catching a Runtime Condition with a Dynamic-Code Trigger" in the `uloop-pause-point` skill. + ## 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. diff --git a/.claude/skills/uloop-pause-point/SKILL.md b/.claude/skills/uloop-pause-point/SKILL.md index 433843c0b1..be0e3f4c4d 100644 --- a/.claude/skills/uloop-pause-point/SKILL.md +++ b/.claude/skills/uloop-pause-point/SKILL.md @@ -94,9 +94,58 @@ Watch expressions are in-memory Editor state. A domain reload clears them, so re ## Marker Types - `uloop enable-pause-point --file --line` patches the already-compiled method at a source line. No code edit or recompile is required. -- `UloopPausePoint.Pause(id)` is a hand-written marker call for code paths that file:line patching cannot reach. Pair it with `uloop enable-pause-point --id ` (no `--file`/`--line`). +- `UloopPausePoint.Pause(id)` is a hand-written marker call for code paths that file:line patching cannot reach. Pair it with `uloop enable-pause-point --id ` (no `--file`/`--line`). The call does not need to live in committed source — a dynamic-code watcher can fire it (see the next section). - For ordinary file:line debugging you do not need `UloopPausePoint.Pause` in source. Prefer CLI enable when the target line can be patched. +## Catching a Runtime Condition with a Dynamic-Code Trigger + +A file:line pause point freezes a specific source line. When the moment you need is defined by a runtime condition instead — an animation passing a normalized time, HP reaching zero, an enemy spawning — combine an id-only marker with `execute-dynamic-code`. Timing-sensitive verification such as short motions or one-frame effects cannot be captured by sleeping and then taking a screenshot; this pattern freezes the first frame where the condition holds, without writing any .cs file. + +1. Enable an id-only marker: `uloop enable-pause-point --id hit-peak --timeout-seconds 120` (single-shot by default). +2. Run `uloop execute-dynamic-code` to trigger the action and register a watcher on `EditorApplication.update`, then return immediately. The watcher evaluates the condition every frame; on the first frame it holds, it removes itself and calls `UloopPausePoint.Pause("hit-peak")`. +3. Wait on the CLI side: `uloop await-pause-point --id hit-peak --timeout-seconds 120`. +4. While Unity is paused, collect evidence: `uloop screenshot`, state reads with `execute-dynamic-code`, or `control-play-mode --action Step` frame stepping. +5. Resume with `uloop control-play-mode --action Play`. + +Example watcher (freeze when the Hit animation passes 30% of the motion): + +```csharp +using UnityEngine; +using UnityEditor; +using io.github.hatayama.UnityCliLoop.Runtime; +Animator animator = GameObject.Find("Zombie").GetComponent(); +// Match the marker's --timeout-seconds so an unmet condition cannot leak the delegate +double deadline = EditorApplication.timeSinceStartup + 120d; +EditorApplication.CallbackFunction watcher = null; +watcher = () => +{ + if (EditorApplication.timeSinceStartup > deadline) + { + EditorApplication.update -= watcher; + return; + } + AnimatorStateInfo state = animator.GetCurrentAnimatorStateInfo(0); + if (!state.IsName("Hit") || state.normalizedTime < 0.3f) return; + EditorApplication.update -= watcher; + UloopPausePoint.Pause("hit-peak"); +}; +EditorApplication.update += watcher; +return "watcher registered"; +``` + +Rules for this pattern: + +- The dynamic-code body runs synchronously on the main thread. Never poll or sleep inside the snippet — frames stop advancing and the animation freezes with them. Register the watcher and return; the waiting belongs to `await-pause-point`. +- The watcher must unsubscribe itself from `EditorApplication.update` when it fires, and also on a deadline in case the condition never holds — a leaked delegate keeps running until the next domain reload. Match the deadline to the marker's `--timeout-seconds`. +- `UloopPausePoint.Pause(id)` is a public static Runtime API, and dynamic code compiles against the project's assemblies, so the watcher can call it exactly like game code. It fires only while the same id is enabled; otherwise it is a no-op, so a stray watcher cannot pause Unity unexpectedly. +- A single-shot marker disarms after the first hit. To catch repeated occurrences, enable with `--mode continuous` and run `await-pause-point` again after each resume. + +## Pausing Right After Simulated Input, Plus N Frames + +To freeze the frame where a `simulate-mouse-ui` click or a `simulate-keyboard` key press lands, you do not need a watcher: enable a file:line pause point on the input-consuming line before sending the input. When the pause lands mid-command, the `simulate-*` command returns promptly with `InterruptedByPausePoint=true` (see Line Placement). + +For "N frames after the input" (for example, three frames after a key press), advance from that hit with `control-play-mode --action Step` exactly N times — `Step` works right after a hit. Do not compute frame offsets in a dynamic-code watcher (recording `Time.frameCount` and pausing at `recorded + N`): frames keep advancing between CLI commands, so the recorded baseline is race-prone and the pause lands on an unpredictable frame. Reserve the watcher pattern for condition-defined moments; use hit-then-Step for frame-offset positioning. + ## Hit Preconditions A pause point hits only when control flow reaches the patched line (or the `Pause(id)` call). `simulate-keyboard` returning `PressEdgeObserved=true` means the input edge was observed, not that your target game logic has reached the pause line yet. diff --git a/Packages/src/Editor/CliOnlyTools~/PausePoint/Skill/SKILL.md b/Packages/src/Editor/CliOnlyTools~/PausePoint/Skill/SKILL.md index 433843c0b1..be0e3f4c4d 100644 --- a/Packages/src/Editor/CliOnlyTools~/PausePoint/Skill/SKILL.md +++ b/Packages/src/Editor/CliOnlyTools~/PausePoint/Skill/SKILL.md @@ -94,9 +94,58 @@ Watch expressions are in-memory Editor state. A domain reload clears them, so re ## Marker Types - `uloop enable-pause-point --file --line` patches the already-compiled method at a source line. No code edit or recompile is required. -- `UloopPausePoint.Pause(id)` is a hand-written marker call for code paths that file:line patching cannot reach. Pair it with `uloop enable-pause-point --id ` (no `--file`/`--line`). +- `UloopPausePoint.Pause(id)` is a hand-written marker call for code paths that file:line patching cannot reach. Pair it with `uloop enable-pause-point --id ` (no `--file`/`--line`). The call does not need to live in committed source — a dynamic-code watcher can fire it (see the next section). - For ordinary file:line debugging you do not need `UloopPausePoint.Pause` in source. Prefer CLI enable when the target line can be patched. +## Catching a Runtime Condition with a Dynamic-Code Trigger + +A file:line pause point freezes a specific source line. When the moment you need is defined by a runtime condition instead — an animation passing a normalized time, HP reaching zero, an enemy spawning — combine an id-only marker with `execute-dynamic-code`. Timing-sensitive verification such as short motions or one-frame effects cannot be captured by sleeping and then taking a screenshot; this pattern freezes the first frame where the condition holds, without writing any .cs file. + +1. Enable an id-only marker: `uloop enable-pause-point --id hit-peak --timeout-seconds 120` (single-shot by default). +2. Run `uloop execute-dynamic-code` to trigger the action and register a watcher on `EditorApplication.update`, then return immediately. The watcher evaluates the condition every frame; on the first frame it holds, it removes itself and calls `UloopPausePoint.Pause("hit-peak")`. +3. Wait on the CLI side: `uloop await-pause-point --id hit-peak --timeout-seconds 120`. +4. While Unity is paused, collect evidence: `uloop screenshot`, state reads with `execute-dynamic-code`, or `control-play-mode --action Step` frame stepping. +5. Resume with `uloop control-play-mode --action Play`. + +Example watcher (freeze when the Hit animation passes 30% of the motion): + +```csharp +using UnityEngine; +using UnityEditor; +using io.github.hatayama.UnityCliLoop.Runtime; +Animator animator = GameObject.Find("Zombie").GetComponent(); +// Match the marker's --timeout-seconds so an unmet condition cannot leak the delegate +double deadline = EditorApplication.timeSinceStartup + 120d; +EditorApplication.CallbackFunction watcher = null; +watcher = () => +{ + if (EditorApplication.timeSinceStartup > deadline) + { + EditorApplication.update -= watcher; + return; + } + AnimatorStateInfo state = animator.GetCurrentAnimatorStateInfo(0); + if (!state.IsName("Hit") || state.normalizedTime < 0.3f) return; + EditorApplication.update -= watcher; + UloopPausePoint.Pause("hit-peak"); +}; +EditorApplication.update += watcher; +return "watcher registered"; +``` + +Rules for this pattern: + +- The dynamic-code body runs synchronously on the main thread. Never poll or sleep inside the snippet — frames stop advancing and the animation freezes with them. Register the watcher and return; the waiting belongs to `await-pause-point`. +- The watcher must unsubscribe itself from `EditorApplication.update` when it fires, and also on a deadline in case the condition never holds — a leaked delegate keeps running until the next domain reload. Match the deadline to the marker's `--timeout-seconds`. +- `UloopPausePoint.Pause(id)` is a public static Runtime API, and dynamic code compiles against the project's assemblies, so the watcher can call it exactly like game code. It fires only while the same id is enabled; otherwise it is a no-op, so a stray watcher cannot pause Unity unexpectedly. +- A single-shot marker disarms after the first hit. To catch repeated occurrences, enable with `--mode continuous` and run `await-pause-point` again after each resume. + +## Pausing Right After Simulated Input, Plus N Frames + +To freeze the frame where a `simulate-mouse-ui` click or a `simulate-keyboard` key press lands, you do not need a watcher: enable a file:line pause point on the input-consuming line before sending the input. When the pause lands mid-command, the `simulate-*` command returns promptly with `InterruptedByPausePoint=true` (see Line Placement). + +For "N frames after the input" (for example, three frames after a key press), advance from that hit with `control-play-mode --action Step` exactly N times — `Step` works right after a hit. Do not compute frame offsets in a dynamic-code watcher (recording `Time.frameCount` and pausing at `recorded + N`): frames keep advancing between CLI commands, so the recorded baseline is race-prone and the pause lands on an unpredictable frame. Reserve the watcher pattern for condition-defined moments; use hit-then-Step for frame-offset positioning. + ## Hit Preconditions A pause point hits only when control flow reaches the patched line (or the `Pause(id)` call). `simulate-keyboard` returning `PressEdgeObserved=true` means the input edge was observed, not that your target game logic has reached the pause line yet. diff --git a/Packages/src/Editor/FirstPartyTools/Compile/Skill/SKILL.md b/Packages/src/Editor/FirstPartyTools/Compile/Skill/SKILL.md index 9aaba36ce4..108abc5a84 100644 --- a/Packages/src/Editor/FirstPartyTools/Compile/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/Compile/Skill/SKILL.md @@ -18,10 +18,27 @@ uloop compile [--force-recompile] [--no-wait-for-domain-reload] [--stop-on-exter | Parameter | Type | Default | Description | |-----------|------|---------|-------------| -| `--force-recompile` | flag | - | Use for broader validation, including warnings hidden by other asmdefs; much slower than normal compile | +| `--force-recompile` | flag | - | Full recompile plus domain reload. Rarely needed — see "When to use --force-recompile" below | | `--no-wait-for-domain-reload` | flag | - | Return before Domain Reload completion | | `--stop-on-external-scene-changes` | flag | - | Stop before compilation if open Scene files changed externally instead of auto-reloading them | +## When to use --force-recompile + +`--force-recompile` is almost never needed. Detecting changed files is Unity's job: even when +files were edited outside the Editor, a plain `uloop compile` refreshes assets and runs every +recompilation the changes require. "The files were changed externally, so recompile everything +just in case" is not a valid reason. + +Why to avoid it: + +- On large projects a full recompile plus domain reload can freeze Unity for a long time. +- The result crosses a domain reload, so it often comes back as `COMPILE_RESULT_UNKNOWN` and + does not work as a verification step. +- It puts the Editor into the unstable just-after-reload state for no benefit. + +The one legitimate use case: you need warnings hidden by other asmdefs surfaced by a full +build. Otherwise always run plain `uloop compile`. + ## Output Returns JSON: diff --git a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/SKILL.md b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/SKILL.md index 42208832b9..9831d85fa2 100644 --- a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/SKILL.md +++ b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/SKILL.md @@ -13,6 +13,8 @@ For basic selected GameObject discovery or property inspection, use `find-game-o 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, enable a source pause point on that line instead (`uloop enable-pause-point --file --line `, see the `uloop-pause-point` skill): the hit response's `CapturedVariables` already contains the locals, parameters, and instance fields at that line, with no code edit or recompile. `CapturedVariables` is a pre-line snapshot; this tool during the pause sees the interrupted method's post-interrupt state instead. While Unity stays paused on the hit, use `UloopPausePoint.TryGetCapturedValue(name)` (plus `GetCapturedNames()` / `GetCapturedPausePointId()`) for live captured references such as collections. Do not try to reconstruct pre-line locals with execute-dynamic-code alone. +The reverse combination also works: to freeze Unity on the first frame where a runtime condition holds (an animation peak, HP reaching zero, an enemy spawning), enable an id-only pause point, then use this tool to register an `EditorApplication.update` watcher that calls `UloopPausePoint.Pause(id)` when the condition is met, and return immediately. Wait with `uloop await-pause-point` on the CLI side — never poll or sleep inside the snippet itself, because the body runs synchronously on the main thread and frames stop advancing. See "Catching a Runtime Condition with a Dynamic-Code Trigger" in the `uloop-pause-point` skill. + ## 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.