From cc6d3665efbe71d73398755c7126ef1c7b6350d8 Mon Sep 17 00:00:00 2001 From: hatayama Date: Sun, 12 Jul 2026 09:18:07 +0900 Subject: [PATCH 1/3] Unify pause-point Tool Settings key across C# layer Use pause-point as the logical settings tool name so the UI toggle matches the pause point family it controls, and route await-pause-point through the same aggregated disable key. Co-authored-by: Cursor --- .../Tests/Editor/SkillInstallLayoutTests.cs | 2 +- .../Tests/Editor/ToolSettingsUseCaseTests.cs | 74 ++++++++++++++++--- .../UseCases/ToolSettingsUseCase.cs | 2 +- .../Domain/ToolSettingsToolLinkPolicy.cs | 3 +- .../ToolContracts/UnityCliLoopConstants.cs | 1 + 5 files changed, 69 insertions(+), 13 deletions(-) diff --git a/Assets/Tests/Editor/SkillInstallLayoutTests.cs b/Assets/Tests/Editor/SkillInstallLayoutTests.cs index 4c4c2d0ee..2ed19de65 100644 --- a/Assets/Tests/Editor/SkillInstallLayoutTests.cs +++ b/Assets/Tests/Editor/SkillInstallLayoutTests.cs @@ -164,7 +164,7 @@ public void GetToolDescriptionsByToolName_WhenSkillHasDescription_MapsDescriptio IReadOnlyDictionary descriptions = SkillInstallLayout.GetToolDescriptionsByToolName(_projectRoot); Assert.That(descriptions["compile"], Is.EqualTo("Compile the Unity project and report errors/warnings. Use after C# edits.")); - Assert.That(descriptions[UnityCliLoopConstants.COMMAND_NAME_AWAIT_PAUSE_POINT], Does.StartWith("Pauses Unity playback")); + Assert.That(descriptions[UnityCliLoopConstants.SETTINGS_TOOL_NAME_PAUSE_POINT], Does.StartWith("Pauses Unity playback")); } // Tests that duplicate skill names use the earlier source root across each precedence boundary. diff --git a/Assets/Tests/Editor/ToolSettingsUseCaseTests.cs b/Assets/Tests/Editor/ToolSettingsUseCaseTests.cs index 61faa9709..9257be45d 100644 --- a/Assets/Tests/Editor/ToolSettingsUseCaseTests.cs +++ b/Assets/Tests/Editor/ToolSettingsUseCaseTests.cs @@ -17,9 +17,9 @@ namespace io.github.hatayama.UnityCliLoop.Tests.Editor public class ToolSettingsUseCaseTests { [Test] - public void TryGetToolCatalog_WhenRegistryAvailable_ShowsOnlyWaitForPausePointCommand() + public void TryGetToolCatalog_WhenRegistryAvailable_ShowsOnlyPausePointSettingsTool() { - // Verifies Tool Settings exposes only the parent pause point command. + // Verifies Tool Settings exposes only the pause-point family toggle. IToolSettingsPort toolSettingsPort = new InMemoryToolSettingsPort(); UnityCliLoopToolRegistrarService toolRegistrarService = new( new EmptyInternalToolNameProvider(), @@ -36,16 +36,17 @@ public void TryGetToolCatalog_WhenRegistryAvailable_ShowsOnlyWaitForPausePointCo string[] toolNames = allTools.Select(tool => tool.Name).ToArray(); Assert.That(isAvailable, Is.True); - Assert.That(toolNames, Does.Contain(UnityCliLoopConstants.COMMAND_NAME_AWAIT_PAUSE_POINT)); + Assert.That(toolNames, Does.Contain(UnityCliLoopConstants.SETTINGS_TOOL_NAME_PAUSE_POINT)); + Assert.That(toolNames, Does.Not.Contain(UnityCliLoopConstants.COMMAND_NAME_AWAIT_PAUSE_POINT)); Assert.That(toolNames, Does.Not.Contain(UnityCliLoopConstants.TOOL_NAME_ENABLE_PAUSE_POINT)); Assert.That(toolNames, Does.Not.Contain(UnityCliLoopConstants.TOOL_NAME_CLEAR_PAUSE_POINT)); Assert.That(toolNames, Does.Not.Contain(UnityCliLoopConstants.COMMAND_NAME_PAUSE_POINT_STATUS)); } [Test] - public void IsToolEnabled_WhenWaitForPausePointDisabled_DisablesPausePointAuxiliaryTools() + public void IsToolEnabled_WhenPausePointDisabled_DisablesPausePointAuxiliaryTools() { - // Verifies pause point auxiliary tools follow the await-pause-point setting. + // Verifies pause point auxiliary tools follow the pause-point settings toggle. IToolSettingsPort toolSettingsPort = new InMemoryToolSettingsPort(); UnityCliLoopToolRegistrarService toolRegistrarService = new( new EmptyInternalToolNameProvider(), @@ -57,14 +58,50 @@ public void IsToolEnabled_WhenWaitForPausePointDisabled_DisablesPausePointAuxili toolRegistrarService, new StaticToolSkillDescriptionProvider(new Dictionary())); - useCase.SetToolEnabled(UnityCliLoopConstants.COMMAND_NAME_AWAIT_PAUSE_POINT, false); + useCase.SetToolEnabled(UnityCliLoopConstants.SETTINGS_TOOL_NAME_PAUSE_POINT, false); + Assert.That(useCase.IsToolEnabled(UnityCliLoopConstants.SETTINGS_TOOL_NAME_PAUSE_POINT), Is.False); Assert.That(useCase.IsToolEnabled(UnityCliLoopConstants.COMMAND_NAME_AWAIT_PAUSE_POINT), Is.False); Assert.That(useCase.IsToolEnabled(UnityCliLoopConstants.TOOL_NAME_ENABLE_PAUSE_POINT), Is.False); Assert.That(useCase.IsToolEnabled(UnityCliLoopConstants.TOOL_NAME_CLEAR_PAUSE_POINT), Is.False); Assert.That(useCase.IsToolEnabled(UnityCliLoopConstants.COMMAND_NAME_PAUSE_POINT_STATUS), Is.False); } + [Test] + public void IsToolEnabled_WhenAwaitPausePointDisabled_DisablesPausePointAuxiliaryTools() + { + // Verifies disabling via the await-pause-point command name still maps to the pause-point toggle. + IToolSettingsPort toolSettingsPort = new InMemoryToolSettingsPort(); + UnityCliLoopToolRegistrarService toolRegistrarService = new( + new EmptyInternalToolNameProvider(), + toolSettingsPort, + new UnityCliLoopToolExecutionService(new NoOpEditorRuntimeStatePort()), + UnityCliLoopToolDiscovery.DiscoverTools); + ToolSettingsUseCase useCase = new( + toolSettingsPort, + toolRegistrarService, + new StaticToolSkillDescriptionProvider(new Dictionary())); + + useCase.SetToolEnabled(UnityCliLoopConstants.COMMAND_NAME_AWAIT_PAUSE_POINT, false); + + Assert.That(useCase.IsToolEnabled(UnityCliLoopConstants.COMMAND_NAME_AWAIT_PAUSE_POINT), Is.False); + Assert.That(useCase.IsToolEnabled(UnityCliLoopConstants.COMMAND_NAME_PAUSE_POINT_STATUS), Is.False); + } + + [Test] + public void ToolSettingsToolLinkPolicy_WhenAwaitPausePointRequested_IsNotUserFacingAndMapsToPausePoint() + { + // Verifies await-pause-point is auxiliary and resolves to the pause-point settings key. + Assert.That( + ToolSettingsToolLinkPolicy.IsUserFacingToolSettingsTool( + UnityCliLoopConstants.COMMAND_NAME_AWAIT_PAUSE_POINT), + Is.False); + Assert.That( + ToolSettingsToolLinkPolicy.GetSettingsToolName( + UnityCliLoopConstants.COMMAND_NAME_AWAIT_PAUSE_POINT), + Is.EqualTo(UnityCliLoopConstants.SETTINGS_TOOL_NAME_PAUSE_POINT)); + } + [Test] public void TryGetToolCatalog_WhenSkillDescriptionExists_IncludesDescription() { @@ -77,7 +114,7 @@ public void TryGetToolCatalog_WhenSkillDescriptionExists_IncludesDescription() UnityCliLoopToolDiscovery.DiscoverTools); Dictionary descriptions = new() { - [UnityCliLoopConstants.COMMAND_NAME_AWAIT_PAUSE_POINT] = "Pause point description" + [UnityCliLoopConstants.SETTINGS_TOOL_NAME_PAUSE_POINT] = "Pause point description" }; ToolSettingsUseCase useCase = new( toolSettingsPort, @@ -86,11 +123,28 @@ public void TryGetToolCatalog_WhenSkillDescriptionExists_IncludesDescription() useCase.WarmupRegistry(); bool isAvailable = useCase.TryGetToolCatalog(out ToolSettingsUseCase.ToolCatalogItem[] allTools); - ToolSettingsUseCase.ToolCatalogItem waitTool = allTools - .Single(tool => tool.Name == UnityCliLoopConstants.COMMAND_NAME_AWAIT_PAUSE_POINT); + ToolSettingsUseCase.ToolCatalogItem pausePointTool = allTools + .Single(tool => tool.Name == UnityCliLoopConstants.SETTINGS_TOOL_NAME_PAUSE_POINT); Assert.That(isAvailable, Is.True); - Assert.That(waitTool.SkillDescription, Is.EqualTo("Pause point description")); + Assert.That(pausePointTool.SkillDescription, Is.EqualTo("Pause point description")); + } + + [Test] + public void IsSkillDisabledByToolSettings_WhenPausePointDisabled_ReturnsTrueForPausePointSkill() + { + // Verifies disabledTools pause-point key matches the pause-point skill tool name. + SkillInstallLayout.SkillSourceInfo skill = new( + "uloop-pause-point", + UnityCliLoopConstants.SETTINGS_TOOL_NAME_PAUSE_POINT, + new Dictionary()); + string[] disabledTools = { UnityCliLoopConstants.SETTINGS_TOOL_NAME_PAUSE_POINT }; + + bool isDisabled = SkillDisabledToolFilter.IsSkillDisabledByToolSettings( + skill, + disabledTools); + + Assert.That(isDisabled, Is.True); } private sealed class StaticToolSkillDescriptionProvider : IToolSkillDescriptionProvider diff --git a/Packages/src/Editor/Application/UseCases/ToolSettingsUseCase.cs b/Packages/src/Editor/Application/UseCases/ToolSettingsUseCase.cs index d315ea586..a9f82b284 100644 --- a/Packages/src/Editor/Application/UseCases/ToolSettingsUseCase.cs +++ b/Packages/src/Editor/Application/UseCases/ToolSettingsUseCase.cs @@ -17,7 +17,7 @@ internal sealed class ToolSettingsUseCase { private static readonly string[] NativeToolNames = { - UnityCliLoopConstants.COMMAND_NAME_AWAIT_PAUSE_POINT + UnityCliLoopConstants.SETTINGS_TOOL_NAME_PAUSE_POINT }; private readonly IToolSettingsPort _toolSettingsPort; diff --git a/Packages/src/Editor/Domain/ToolSettingsToolLinkPolicy.cs b/Packages/src/Editor/Domain/ToolSettingsToolLinkPolicy.cs index 4d42090e5..1ab865c93 100644 --- a/Packages/src/Editor/Domain/ToolSettingsToolLinkPolicy.cs +++ b/Packages/src/Editor/Domain/ToolSettingsToolLinkPolicy.cs @@ -12,6 +12,7 @@ internal static class ToolSettingsToolLinkPolicy { private static readonly HashSet PausePointAuxiliaryToolNames = new(System.StringComparer.Ordinal) { + UnityCliLoopConstants.COMMAND_NAME_AWAIT_PAUSE_POINT, UnityCliLoopConstants.TOOL_NAME_ENABLE_PAUSE_POINT, UnityCliLoopConstants.TOOL_NAME_CLEAR_PAUSE_POINT, UnityCliLoopConstants.COMMAND_NAME_PAUSE_POINT_STATUS @@ -38,7 +39,7 @@ internal static string GetSettingsToolName(string toolName) if (PausePointAuxiliaryToolNames.Contains(toolName)) { - return UnityCliLoopConstants.COMMAND_NAME_AWAIT_PAUSE_POINT; + return UnityCliLoopConstants.SETTINGS_TOOL_NAME_PAUSE_POINT; } return toolName; diff --git a/Packages/src/Editor/ToolContracts/UnityCliLoopConstants.cs b/Packages/src/Editor/ToolContracts/UnityCliLoopConstants.cs index ab6b6b499..0fb8fbe16 100644 --- a/Packages/src/Editor/ToolContracts/UnityCliLoopConstants.cs +++ b/Packages/src/Editor/ToolContracts/UnityCliLoopConstants.cs @@ -71,6 +71,7 @@ public static UnityEditor.PackageManager.PackageInfo PackageInfo public const string COMMAND_NAME_GET_VERSION = "get-version"; public const string COMMAND_NAME_GET_COMPILE_STATUS = "get-compile-status"; public const string COMMAND_NAME_AWAIT_PAUSE_POINT = "await-pause-point"; + public const string SETTINGS_TOOL_NAME_PAUSE_POINT = "pause-point"; public const string COMMAND_NAME_PAUSE_POINT_STATUS = "pause-point-status"; public const string COMMAND_NAME_GET_PAUSE_POINT_STATUS = "get-pause-point-status"; public const string COMMAND_NAME_CLEAR_PAUSE_POINT_STATUS = "clear-pause-point-status"; From 0639ea27c3f3323bea0f54acdeb905b3cdee6c8e Mon Sep 17 00:00:00 2001 From: hatayama Date: Sun, 12 Jul 2026 09:18:13 +0900 Subject: [PATCH 2/3] Map native pause-point commands to pause-point settings key Resolve await-pause-point and pause-point-status to the shared pause-point disabledTools entry so runner-side gating matches Unity, including regression coverage for pause-point-status rejection. Co-authored-by: Cursor --- .../projectrunner/native_tool_settings.go | 11 +++++--- .../projectrunner/pause_point_wait_test.go | 28 ++++++++++++++++++- .../internal/projectrunner/runner_commands.go | 4 +-- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/cli/project-runner/internal/projectrunner/native_tool_settings.go b/cli/project-runner/internal/projectrunner/native_tool_settings.go index 0b6917e99..4736bfa1d 100644 --- a/cli/project-runner/internal/projectrunner/native_tool_settings.go +++ b/cli/project-runner/internal/projectrunner/native_tool_settings.go @@ -1,16 +1,19 @@ package projectrunner import ( - "github.com/hatayama/unity-cli-loop/common/clicore" clierrors "github.com/hatayama/unity-cli-loop/common/errors" + + "github.com/hatayama/unity-cli-loop/common/clicore" ) -func isSettingsManagedNativeToolCommand(command string) bool { +const pausePointSettingsToolName = "pause-point" + +func settingsToolNameForNativeCommand(command string) (string, bool) { switch command { case clicore.PausePointAwaitCommandName, clicore.PausePointStatusUserCommandName: - return true + return pausePointSettingsToolName, true default: - return false + return "", false } } diff --git a/cli/project-runner/internal/projectrunner/pause_point_wait_test.go b/cli/project-runner/internal/projectrunner/pause_point_wait_test.go index d13da172f..030352174 100644 --- a/cli/project-runner/internal/projectrunner/pause_point_wait_test.go +++ b/cli/project-runner/internal/projectrunner/pause_point_wait_test.go @@ -829,7 +829,7 @@ func TestPausePointExpiredErrorReportsNoRemainingTime(t *testing.T) { // Verifies disabled native pause-point commands are rejected before Unity dispatch. func TestRunProjectLocalWaitForPausePointRespectsToolSettings(t *testing.T) { projectRoot := createLaunchTestProject(t) - writeToolSettings(t, projectRoot, `{"disabledTools":["await-pause-point"]}`) + writeToolSettings(t, projectRoot, `{"disabledTools":["pause-point"]}`) t.Chdir(filepath.Dir(projectRoot)) var stdout bytes.Buffer @@ -852,6 +852,32 @@ func TestRunProjectLocalWaitForPausePointRespectsToolSettings(t *testing.T) { } } +// Verifies disabled pause-point-status is rejected before Unity dispatch. +func TestRunProjectLocalPausePointStatusRespectsToolSettings(t *testing.T) { + projectRoot := createLaunchTestProject(t) + writeToolSettings(t, projectRoot, `{"disabledTools":["pause-point"]}`) + t.Chdir(filepath.Dir(projectRoot)) + + var stdout bytes.Buffer + var stderr bytes.Buffer + code := RunProjectLocal( + context.Background(), + []string{"--project-path", projectRoot, clicore.PausePointStatusUserCommandName, "--id", "jump"}, + &stdout, + &stderr) + + if code != 1 { + t.Fatalf("expected disabled command failure, got %d with stdout %s", code, stdout.String()) + } + envelope := parsePausePointErrorEnvelope(t, stderr.Bytes()) + if envelope.Error.ErrorCode != clierrors.ErrorCodeToolDisabled { + t.Fatalf("error code mismatch: %#v", envelope.Error) + } + if envelope.Error.Command != clicore.PausePointStatusUserCommandName { + t.Fatalf("command mismatch: %#v", envelope.Error) + } +} + // Verifies await-pause-point requires a marker id. func TestParseWaitForPausePointOptionsRequiresID(t *testing.T) { _, err := parseWaitForPausePointOptions([]string{"--timeout-seconds", "1"}) diff --git a/cli/project-runner/internal/projectrunner/runner_commands.go b/cli/project-runner/internal/projectrunner/runner_commands.go index 16369d741..733333e6f 100644 --- a/cli/project-runner/internal/projectrunner/runner_commands.go +++ b/cli/project-runner/internal/projectrunner/runner_commands.go @@ -20,8 +20,8 @@ func runResolvedProjectCommand( stdout io.Writer, stderr io.Writer, ) int { - if isSettingsManagedNativeToolCommand(command) && - clicore.IsToolDisabledByToolSettings(command, clicore.LoadDisabledTools(connection.ProjectRoot)) { + if settingsToolName, isSettingsManaged := settingsToolNameForNativeCommand(command); isSettingsManaged && + clicore.IsToolDisabledByToolSettings(settingsToolName, clicore.LoadDisabledTools(connection.ProjectRoot)) { clierrors.WriteErrorEnvelope(stderr, nativeToolDisabledError(connection.ProjectRoot, command)) return 1 } From 3d50484b996b59c4e51561b0328a14bd142cf40b Mon Sep 17 00:00:00 2001 From: hatayama Date: Sun, 12 Jul 2026 09:18:14 +0900 Subject: [PATCH 3/3] Derive pause-point skill toolName from uloop-pause-point name Remove the explicit await-pause-point toolName override so skill sync and Tool Settings descriptions align with the pause-point settings key. Co-authored-by: Cursor --- .agents/skills/uloop-pause-point/SKILL.md | 1 - .claude/skills/uloop-pause-point/SKILL.md | 1 - Packages/src/Editor/CliOnlyTools~/PausePoint/Skill/SKILL.md | 1 - 3 files changed, 3 deletions(-) diff --git a/.agents/skills/uloop-pause-point/SKILL.md b/.agents/skills/uloop-pause-point/SKILL.md index 906a32261..dba1bc4e9 100644 --- a/.agents/skills/uloop-pause-point/SKILL.md +++ b/.agents/skills/uloop-pause-point/SKILL.md @@ -1,6 +1,5 @@ --- name: uloop-pause-point -toolName: await-pause-point description: "Pauses Unity playback at any source file:line without editing code or recompiling, and returns a snapshot of the locals, parameters, and instance fields at that exact frame. Use for bug investigation, PlayMode/E2E verification, checking variable values at a specific frame, or confirming that a code path executed." --- diff --git a/.claude/skills/uloop-pause-point/SKILL.md b/.claude/skills/uloop-pause-point/SKILL.md index 906a32261..dba1bc4e9 100644 --- a/.claude/skills/uloop-pause-point/SKILL.md +++ b/.claude/skills/uloop-pause-point/SKILL.md @@ -1,6 +1,5 @@ --- name: uloop-pause-point -toolName: await-pause-point description: "Pauses Unity playback at any source file:line without editing code or recompiling, and returns a snapshot of the locals, parameters, and instance fields at that exact frame. Use for bug investigation, PlayMode/E2E verification, checking variable values at a specific frame, or confirming that a code path executed." --- diff --git a/Packages/src/Editor/CliOnlyTools~/PausePoint/Skill/SKILL.md b/Packages/src/Editor/CliOnlyTools~/PausePoint/Skill/SKILL.md index 906a32261..dba1bc4e9 100644 --- a/Packages/src/Editor/CliOnlyTools~/PausePoint/Skill/SKILL.md +++ b/Packages/src/Editor/CliOnlyTools~/PausePoint/Skill/SKILL.md @@ -1,6 +1,5 @@ --- name: uloop-pause-point -toolName: await-pause-point description: "Pauses Unity playback at any source file:line without editing code or recompiling, and returns a snapshot of the locals, parameters, and instance fields at that exact frame. Use for bug investigation, PlayMode/E2E verification, checking variable values at a specific frame, or confirming that a code path executed." ---