Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/skills/uloop-execute-dynamic-code/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Run focused C# snippets in the active Unity Editor with `uloop execute-dynamic-c

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, enable a source pause point on that line instead (`uloop enable-pause-point --file <file> --line <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. Do not try to reconstruct those values with execute-dynamic-code.
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 <file> --line <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.

## Parameters

Expand Down
23 changes: 23 additions & 0 deletions .agents/skills/uloop-pause-point/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Use this small loop for one representative frame you care about. No source edit
uloop enable-pause-point --file Assets/Scripts/Enemy.cs --line 42 --timeout-seconds 30
```

`--timeout-seconds` on enable starts the marker lifetime clock at enable time, not when you later run `await-pause-point`.

The response returns the derived marker `Id` (`Assets/Scripts/Enemy.cs:42`), the `ResolvedLine` that was actually patched, and the `ResolvedMethod`. When the requested line has no executable statement, the pause point rounds forward to the next executable line — check `ResolvedLine` when precision matters. Use the returned `Id` for every follow-up command.

2. Trigger the action with a `simulate-*` command.
Expand All @@ -33,6 +35,7 @@ uloop await-pause-point --id "Assets/Scripts/Enemy.cs:42" --timeout-seconds 30
Every hit response embeds `CapturedVariables`: the method's in-scope locals, its parameters, and the `this` instance fields, captured at the exact moment execution reached the patched line. Values are point-in-time strings, not live references, so they stay valid as evidence even after Unity resumes.

- The snapshot is taken **before** the resolved line executes, exactly like an IDE breakpoint on that line. To inspect a value after an assignment, place the pause point on the following line.
- `execute-dynamic-code` during the pause sees the interrupted method's **post-interrupt** state, not this pre-line snapshot. Use `CapturedVariables` for pre-line evidence; use the raw capture API below when you need live references while paused.
- `Scope` is `Local`, `Parameter`, or `InstanceField`.
- `UnityEngine.Object` values additionally carry `UnityObjectKind` (`SceneObject`, `PrefabAsset`, `Asset`, `RuntimeInstance`, or `Destroyed`), `UnityObjectPath`, and `UnityObjectInstanceId`. Use these as handles for the next dig: a `SceneObject` path feeds `get-hierarchy`/`find-game-objects`, an asset path locates the asset, and the InstanceID works with `execute-dynamic-code`.
- `CapturedVariablesTruncated=true` means at least one value was clipped to the length cap or the variable-count cap stopped enumeration; clipped values are still present up to the cap.
Expand All @@ -43,6 +46,26 @@ Read `EvidenceSummary` first when it is present. It groups `EditorState`, pause

Use `Generation`, `EnabledAtUtc`, and the hit sequence fields from the hit or status response to tell a fresh marker from stale evidence with the same id. `RemainingMilliseconds` and `Expired` are returned directly so you do not need to infer marker lifetime from elapsed time.

## Raw Capture While Paused

While Unity is paused on a hit, `execute-dynamic-code` can read live captured references through `UloopPausePoint`:

- `TryGetCapturedValue(string name)` returns `(bool Found, object Value)` for the latest hit only. When multiple captured variables share the same name, the last one wins.
- `GetCapturedNames()` lists captured variable names from that snapshot.
- `GetCapturedPausePointId()` returns the pause-point id for the held snapshot.

The holder clears when Unity resumes (not when you `Step` while still paused), when the matching pause point is cleared, when a new hit replaces the snapshot, or when PlayMode exits. After resume, `TryGetCapturedValue` returns `Found=false`.

## 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 <id>` (no `--file`/`--line`).
- For ordinary file:line debugging you do not need `UloopPausePoint.Pause` in source. Prefer CLI enable when the target line can be patched.

## 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.

## When To Use

- Use this as the standard frame proof for state-changing PlayMode/E2E simulated input, physics, or UI transitions.
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/uloop-execute-dynamic-code/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Run focused C# snippets in the active Unity Editor with `uloop execute-dynamic-c

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, enable a source pause point on that line instead (`uloop enable-pause-point --file <file> --line <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. Do not try to reconstruct those values with execute-dynamic-code.
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 <file> --line <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.

## Parameters

Expand Down
23 changes: 23 additions & 0 deletions .claude/skills/uloop-pause-point/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Use this small loop for one representative frame you care about. No source edit
uloop enable-pause-point --file Assets/Scripts/Enemy.cs --line 42 --timeout-seconds 30
```

`--timeout-seconds` on enable starts the marker lifetime clock at enable time, not when you later run `await-pause-point`.

The response returns the derived marker `Id` (`Assets/Scripts/Enemy.cs:42`), the `ResolvedLine` that was actually patched, and the `ResolvedMethod`. When the requested line has no executable statement, the pause point rounds forward to the next executable line — check `ResolvedLine` when precision matters. Use the returned `Id` for every follow-up command.

2. Trigger the action with a `simulate-*` command.
Expand All @@ -33,6 +35,7 @@ uloop await-pause-point --id "Assets/Scripts/Enemy.cs:42" --timeout-seconds 30
Every hit response embeds `CapturedVariables`: the method's in-scope locals, its parameters, and the `this` instance fields, captured at the exact moment execution reached the patched line. Values are point-in-time strings, not live references, so they stay valid as evidence even after Unity resumes.

- The snapshot is taken **before** the resolved line executes, exactly like an IDE breakpoint on that line. To inspect a value after an assignment, place the pause point on the following line.
- `execute-dynamic-code` during the pause sees the interrupted method's **post-interrupt** state, not this pre-line snapshot. Use `CapturedVariables` for pre-line evidence; use the raw capture API below when you need live references while paused.
- `Scope` is `Local`, `Parameter`, or `InstanceField`.
- `UnityEngine.Object` values additionally carry `UnityObjectKind` (`SceneObject`, `PrefabAsset`, `Asset`, `RuntimeInstance`, or `Destroyed`), `UnityObjectPath`, and `UnityObjectInstanceId`. Use these as handles for the next dig: a `SceneObject` path feeds `get-hierarchy`/`find-game-objects`, an asset path locates the asset, and the InstanceID works with `execute-dynamic-code`.
- `CapturedVariablesTruncated=true` means at least one value was clipped to the length cap or the variable-count cap stopped enumeration; clipped values are still present up to the cap.
Expand All @@ -43,6 +46,26 @@ Read `EvidenceSummary` first when it is present. It groups `EditorState`, pause

Use `Generation`, `EnabledAtUtc`, and the hit sequence fields from the hit or status response to tell a fresh marker from stale evidence with the same id. `RemainingMilliseconds` and `Expired` are returned directly so you do not need to infer marker lifetime from elapsed time.

## Raw Capture While Paused

While Unity is paused on a hit, `execute-dynamic-code` can read live captured references through `UloopPausePoint`:

- `TryGetCapturedValue(string name)` returns `(bool Found, object Value)` for the latest hit only. When multiple captured variables share the same name, the last one wins.
- `GetCapturedNames()` lists captured variable names from that snapshot.
- `GetCapturedPausePointId()` returns the pause-point id for the held snapshot.

The holder clears when Unity resumes (not when you `Step` while still paused), when the matching pause point is cleared, when a new hit replaces the snapshot, or when PlayMode exits. After resume, `TryGetCapturedValue` returns `Found=false`.

## 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 <id>` (no `--file`/`--line`).
- For ordinary file:line debugging you do not need `UloopPausePoint.Pause` in source. Prefer CLI enable when the target line can be patched.

## 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.

## When To Use

- Use this as the standard frame proof for state-changing PlayMode/E2E simulated input, physics, or UI transitions.
Expand Down
14 changes: 14 additions & 0 deletions Assets/Tests/Editor/CollectionPreviewPausePointFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Fixture for collection JSON preview smoke verification. Line numbers are referenced by manual E2E checks.
namespace io.github.hatayama.UnityCliLoop.Tests.PausePointToolsFixtures
{
using System.Collections.Generic;

public sealed class CollectionPreviewPausePointFixture
{
public List<int> BuildScores()
{
List<int> scores = new() { 10, 20, 30 };
return scores;
}
}
}
11 changes: 11 additions & 0 deletions Assets/Tests/Editor/CollectionPreviewPausePointFixture.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Assets/Tests/Editor/PausePointStatusResponseContractTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ public void PausePointStatusResponse_WhenSerialized_MatchesSharedContractFieldSh
UnityObjectInstanceId = -1234
}
},
CapturedVariablesTruncated = true
CapturedVariablesTruncated = true,
ClearedReason = "",
StatusBeforeClear = "",
LateHitDiscardedAfterClear = false
};
string json = JsonConvert.SerializeObject(
response,
Expand Down
Loading
Loading