From 40ab4d67eb6a85c82535528ab303714f63a0984e Mon Sep 17 00:00:00 2001 From: hatayama Date: Tue, 27 Oct 2026 17:47:20 +0900 Subject: [PATCH 1/5] Fold the uloop-cancel skill into the uloop-status skill uloop cancel is a rare recovery step out of a Busy that status reports, so a skill of its own spent a description slot in every agent session for little gain. Agents already reach it from the BUSY error hint, the status skill, and execute-dynamic-code's cancellation reference. - Move the cancel contract (outcomes, what cannot be cancelled, what to do after) to the status skill's references/cancel.md, and mention cancelling in the status skill description so a skill search for it still lands there - Move the one execute-dynamic-code-only fact (a blocked main thread frees the slot about 5 seconds later) into that skill's cancellation reference - Point cancel --help at uloop-status, and add uloop-cancel to the deprecated skill names in the dispatcher and the Unity-side installer so existing installs drop the stale copy --- .../references/cancellation.md | 4 +- .agents/skills/uloop-status/SKILL.md | 4 +- .../references/cancel.md} | 16 ++----- .../references/cancellation.md | 4 +- .claude/skills/uloop-status/SKILL.md | 4 +- .../references/cancel.md} | 16 ++----- .../Editor/ToolSkillSynchronizerTests.cs | 14 ++++++ .../CliOnlyTools~/Status/Skill/SKILL.md | 4 +- .../Skill/references/cancel.md} | 16 ++----- .../Skill/references/cancellation.md | 4 +- .../SkillSetup/SkillTargetInstaller.cs | 3 +- README.md | 3 +- README_ja.md | 3 +- cli/common/tooldocs/skill_guidance.go | 4 +- cli/dispatcher/internal/dispatcher/skills.go | 1 + .../internal/dispatcher/skills_test.go | 47 ++++++++++--------- cli/dispatcher/shared-inputs-stamp.json | 2 +- .../projectrunner/native_command_help_test.go | 5 +- cli/project-runner/shared-inputs-stamp.json | 2 +- 19 files changed, 74 insertions(+), 82 deletions(-) rename .agents/skills/{uloop-cancel/SKILL.md => uloop-status/references/cancel.md} (68%) rename .claude/skills/{uloop-cancel/SKILL.md => uloop-status/references/cancel.md} (68%) rename Packages/src/Editor/CliOnlyTools~/{Cancel/Skill/SKILL.md => Status/Skill/references/cancel.md} (68%) diff --git a/.agents/skills/uloop-execute-dynamic-code/references/cancellation.md b/.agents/skills/uloop-execute-dynamic-code/references/cancellation.md index fb9dbbea2..5d00b49de 100644 --- a/.agents/skills/uloop-execute-dynamic-code/references/cancellation.md +++ b/.agents/skills/uloop-execute-dynamic-code/references/cancellation.md @@ -21,7 +21,7 @@ So a `CancellationToken` named `ct` is always in scope; use it without declaring ## Long awaits block other tool commands -uloop runs one tool command at a time per Editor. While a snippet is awaiting, other tool commands against that Editor (such as `compile`, `get-logs`, or `run-tests`) are rejected as BUSY. An await that may never finish — waiting for an animation or a scripted sequence to complete, or for an external event — keeps the Editor BUSY for as long as it waits. `uloop status` still answers while a tool runs, so use it to check what is running. To stop a running snippet from another terminal, run `uloop cancel`. +uloop runs one tool command at a time per Editor. While a snippet is awaiting, other tool commands against that Editor (such as `compile`, `get-logs`, or `run-tests`) are rejected as BUSY. An await that may never finish — waiting for an animation or a scripted sequence to complete, or for an external event — keeps the Editor BUSY for as long as it waits. `uloop status` still answers while a tool runs, so use it to check what is running. To stop a running snippet from another terminal, run `uloop cancel`; the `uloop-status` skill's `references/cancel.md` lists its outcomes. Another `execute-dynamic-code` is not rejected as BUSY: it returns `Success: false` with `ErrorMessage` "Another execution is already in progress". Since the next command an agent sends is often another snippet, treat that message as the sign that an earlier snippet is still awaiting. @@ -59,6 +59,6 @@ uloop stops waiting for the snippet and frees the Editor for the next command. A What the CLI that sent the snippet sees depends on why `ct` fired: -- Yielded to a foreground request, or stopped by `uloop cancel`: the connection is still open, so the CLI receives `Success: false` with `ErrorMessage` "Execution was cancelled or timed out". +- Yielded to a foreground request, or stopped by `uloop cancel`: the connection is still open, so the CLI receives `Success: false` with `ErrorMessage` "Execution was cancelled or timed out". While a command blocks Unity's main thread, the snippet cannot return; its slot is freed about 5 seconds later, when another command arrives. - The CLI disconnected: no response is sent; the CLI that was waiting is already gone. - Domain reload, Editor quit, or server stop/restart: no response arrives. The CLI treats it as a lost connection: after a domain reload it prints "Connection lost during execute-dynamic-code", waits for the Editor to come back, and then reports the disconnect as an error. Check the Editor with `uloop status` before sending the next command. diff --git a/.agents/skills/uloop-status/SKILL.md b/.agents/skills/uloop-status/SKILL.md index 7ce35564f..360a337bc 100644 --- a/.agents/skills/uloop-status/SKILL.md +++ b/.agents/skills/uloop-status/SKILL.md @@ -1,6 +1,6 @@ --- name: uloop-status -description: "Check whether the Unity Editor for this project can take a uloop command now, without side effects. Use before sending commands when Unity may be starting, compiling, busy, or closed." +description: "Check whether the Unity Editor for this project can take a uloop command now, without side effects; also covers `uloop cancel` for stopping the command it is running. Use before sending commands when Unity may be starting, compiling, busy, or closed, and when BUSY does not clear or a command does not finish." --- # uloop status @@ -52,7 +52,7 @@ Play Mode does not change the state: a paused Play Mode session is `Ready`, with ## Notes - Exits 0 only for Ready. -- When `Busy` does not clear and the running command shows no sign of finishing, stop it with `uloop cancel`. +- When `Busy` does not clear and the running command shows no sign of finishing, stop it with `uloop cancel`; its outcomes, what it cannot cancel, and what to do after it are in [references/cancel.md](references/cancel.md). - It never focuses the window, launches Unity, or takes the command slot, so it is safe to poll. - A sandboxed shell that cannot open the Unix socket gets an error, not NotRunning; run it outside the sandbox. - uloop cannot detect Safe Mode directly; ServerUnavailable that does not clear is the signal. diff --git a/.agents/skills/uloop-cancel/SKILL.md b/.agents/skills/uloop-status/references/cancel.md similarity index 68% rename from .agents/skills/uloop-cancel/SKILL.md rename to .agents/skills/uloop-status/references/cancel.md index df4da5e47..b94513a8a 100644 --- a/.agents/skills/uloop-cancel/SKILL.md +++ b/.agents/skills/uloop-status/references/cancel.md @@ -1,15 +1,10 @@ ---- -name: uloop-cancel -description: "Cancel the uloop command the Unity Editor for this project is running now. Use when BUSY keeps rejecting commands or your own command does not finish." ---- - # uloop cancel Cancel the uloop command that holds the Editor's command slot, from another terminal or after the waiting command was given up on. ## When to Use -- Every command is rejected with `UNITY_SERVER_BUSY` and the running command shows no sign of finishing. +- Every command is rejected with `UNITY_SERVER_BUSY`, `uloop status` keeps reporting `Busy`, and the running command shows no sign of finishing. - A command you started does not return, for example an `execute-dynamic-code` snippet that awaits something that never completes. ## Usage @@ -49,15 +44,10 @@ A default EditMode `run-tests` and `compile --no-wait-for-domain-reload` can be ## What the Waiting Command Gets -- Most commands fail with `ErrorCode` `TOOL_CANCELLED` (not retryable). -- `execute-dynamic-code` returns its usual cancelled result: `Success: false` with "Execution was cancelled or timed out". +Most commands fail with `ErrorCode` `TOOL_CANCELLED` (not retryable). `execute-dynamic-code` instead returns its usual cancelled result; the `uloop-execute-dynamic-code` skill's `references/cancellation.md` describes it and what happens to a snippet that keeps running. `Cancelled` also resumes Play Mode when the Editor is paused, whether a pause point or a manual pause holds it — the same as when the waiting CLI is killed. ## After Cancelling -Run `uloop status` and wait for `Ready` before the next command. `Cancelled` means the command was asked to stop, not that it already stopped: - -- A tool that ignores its cancellation token keeps the slot until it returns on its own. -- `execute-dynamic-code` returns at once and frees the slot, but a snippet that ignores `ct` keeps running in the background. -- While a command blocks Unity's main thread, `execute-dynamic-code` cannot return; its slot is freed about 5 seconds later, when another command arrives. +Run `uloop status` and wait for `Ready` before the next command. `Cancelled` means the command was asked to stop, not that it already stopped: a tool that ignores its cancellation token keeps the slot until it returns on its own. diff --git a/.claude/skills/uloop-execute-dynamic-code/references/cancellation.md b/.claude/skills/uloop-execute-dynamic-code/references/cancellation.md index fb9dbbea2..5d00b49de 100644 --- a/.claude/skills/uloop-execute-dynamic-code/references/cancellation.md +++ b/.claude/skills/uloop-execute-dynamic-code/references/cancellation.md @@ -21,7 +21,7 @@ So a `CancellationToken` named `ct` is always in scope; use it without declaring ## Long awaits block other tool commands -uloop runs one tool command at a time per Editor. While a snippet is awaiting, other tool commands against that Editor (such as `compile`, `get-logs`, or `run-tests`) are rejected as BUSY. An await that may never finish — waiting for an animation or a scripted sequence to complete, or for an external event — keeps the Editor BUSY for as long as it waits. `uloop status` still answers while a tool runs, so use it to check what is running. To stop a running snippet from another terminal, run `uloop cancel`. +uloop runs one tool command at a time per Editor. While a snippet is awaiting, other tool commands against that Editor (such as `compile`, `get-logs`, or `run-tests`) are rejected as BUSY. An await that may never finish — waiting for an animation or a scripted sequence to complete, or for an external event — keeps the Editor BUSY for as long as it waits. `uloop status` still answers while a tool runs, so use it to check what is running. To stop a running snippet from another terminal, run `uloop cancel`; the `uloop-status` skill's `references/cancel.md` lists its outcomes. Another `execute-dynamic-code` is not rejected as BUSY: it returns `Success: false` with `ErrorMessage` "Another execution is already in progress". Since the next command an agent sends is often another snippet, treat that message as the sign that an earlier snippet is still awaiting. @@ -59,6 +59,6 @@ uloop stops waiting for the snippet and frees the Editor for the next command. A What the CLI that sent the snippet sees depends on why `ct` fired: -- Yielded to a foreground request, or stopped by `uloop cancel`: the connection is still open, so the CLI receives `Success: false` with `ErrorMessage` "Execution was cancelled or timed out". +- Yielded to a foreground request, or stopped by `uloop cancel`: the connection is still open, so the CLI receives `Success: false` with `ErrorMessage` "Execution was cancelled or timed out". While a command blocks Unity's main thread, the snippet cannot return; its slot is freed about 5 seconds later, when another command arrives. - The CLI disconnected: no response is sent; the CLI that was waiting is already gone. - Domain reload, Editor quit, or server stop/restart: no response arrives. The CLI treats it as a lost connection: after a domain reload it prints "Connection lost during execute-dynamic-code", waits for the Editor to come back, and then reports the disconnect as an error. Check the Editor with `uloop status` before sending the next command. diff --git a/.claude/skills/uloop-status/SKILL.md b/.claude/skills/uloop-status/SKILL.md index 7ce35564f..360a337bc 100644 --- a/.claude/skills/uloop-status/SKILL.md +++ b/.claude/skills/uloop-status/SKILL.md @@ -1,6 +1,6 @@ --- name: uloop-status -description: "Check whether the Unity Editor for this project can take a uloop command now, without side effects. Use before sending commands when Unity may be starting, compiling, busy, or closed." +description: "Check whether the Unity Editor for this project can take a uloop command now, without side effects; also covers `uloop cancel` for stopping the command it is running. Use before sending commands when Unity may be starting, compiling, busy, or closed, and when BUSY does not clear or a command does not finish." --- # uloop status @@ -52,7 +52,7 @@ Play Mode does not change the state: a paused Play Mode session is `Ready`, with ## Notes - Exits 0 only for Ready. -- When `Busy` does not clear and the running command shows no sign of finishing, stop it with `uloop cancel`. +- When `Busy` does not clear and the running command shows no sign of finishing, stop it with `uloop cancel`; its outcomes, what it cannot cancel, and what to do after it are in [references/cancel.md](references/cancel.md). - It never focuses the window, launches Unity, or takes the command slot, so it is safe to poll. - A sandboxed shell that cannot open the Unix socket gets an error, not NotRunning; run it outside the sandbox. - uloop cannot detect Safe Mode directly; ServerUnavailable that does not clear is the signal. diff --git a/.claude/skills/uloop-cancel/SKILL.md b/.claude/skills/uloop-status/references/cancel.md similarity index 68% rename from .claude/skills/uloop-cancel/SKILL.md rename to .claude/skills/uloop-status/references/cancel.md index df4da5e47..b94513a8a 100644 --- a/.claude/skills/uloop-cancel/SKILL.md +++ b/.claude/skills/uloop-status/references/cancel.md @@ -1,15 +1,10 @@ ---- -name: uloop-cancel -description: "Cancel the uloop command the Unity Editor for this project is running now. Use when BUSY keeps rejecting commands or your own command does not finish." ---- - # uloop cancel Cancel the uloop command that holds the Editor's command slot, from another terminal or after the waiting command was given up on. ## When to Use -- Every command is rejected with `UNITY_SERVER_BUSY` and the running command shows no sign of finishing. +- Every command is rejected with `UNITY_SERVER_BUSY`, `uloop status` keeps reporting `Busy`, and the running command shows no sign of finishing. - A command you started does not return, for example an `execute-dynamic-code` snippet that awaits something that never completes. ## Usage @@ -49,15 +44,10 @@ A default EditMode `run-tests` and `compile --no-wait-for-domain-reload` can be ## What the Waiting Command Gets -- Most commands fail with `ErrorCode` `TOOL_CANCELLED` (not retryable). -- `execute-dynamic-code` returns its usual cancelled result: `Success: false` with "Execution was cancelled or timed out". +Most commands fail with `ErrorCode` `TOOL_CANCELLED` (not retryable). `execute-dynamic-code` instead returns its usual cancelled result; the `uloop-execute-dynamic-code` skill's `references/cancellation.md` describes it and what happens to a snippet that keeps running. `Cancelled` also resumes Play Mode when the Editor is paused, whether a pause point or a manual pause holds it — the same as when the waiting CLI is killed. ## After Cancelling -Run `uloop status` and wait for `Ready` before the next command. `Cancelled` means the command was asked to stop, not that it already stopped: - -- A tool that ignores its cancellation token keeps the slot until it returns on its own. -- `execute-dynamic-code` returns at once and frees the slot, but a snippet that ignores `ct` keeps running in the background. -- While a command blocks Unity's main thread, `execute-dynamic-code` cannot return; its slot is freed about 5 seconds later, when another command arrives. +Run `uloop status` and wait for `Ready` before the next command. `Cancelled` means the command was asked to stop, not that it already stopped: a tool that ignores its cancellation token keeps the slot until it returns on its own. diff --git a/Assets/Tests/Editor/ToolSkillSynchronizerTests.cs b/Assets/Tests/Editor/ToolSkillSynchronizerTests.cs index e2c798d56..f27f1fa59 100644 --- a/Assets/Tests/Editor/ToolSkillSynchronizerTests.cs +++ b/Assets/Tests/Editor/ToolSkillSynchronizerTests.cs @@ -1491,6 +1491,7 @@ public void DetectTargets_WhenDeprecatedManagedSkillDirectoryExists_DoesNotRepor Assert.That(detectedTargets[0].InstallState, Is.EqualTo(SkillInstallState.Installed)); } + // Tests that installing skills removes deprecated and retired skill directories from both layouts. [Test] public async Task InstallSkillFilesAtProjectRoot_WhenDeprecatedManagedSkillDirectoryExists_RemovesIt() { @@ -1528,6 +1529,17 @@ public async Task InstallSkillFilesAtProjectRoot_WhenDeprecatedManagedSkillDirec "uloop-record-input"); WriteSkillFile(flatRetiredRecordInputSkillDir, "---\nname: uloop-record-input\n---\n"); WriteSkillFile(groupedRetiredRecordInputSkillDir, "---\nname: uloop-record-input\n---\n"); + string flatRetiredCancelSkillDir = Path.Combine( + targetRoot, + SkillInstallLayout.SkillsDirName, + "uloop-cancel"); + string groupedRetiredCancelSkillDir = Path.Combine( + targetRoot, + SkillInstallLayout.SkillsDirName, + SkillInstallLayout.ManagedSkillsDirName, + "uloop-cancel"); + WriteSkillFile(flatRetiredCancelSkillDir, "---\nname: uloop-cancel\n---\n"); + WriteSkillFile(groupedRetiredCancelSkillDir, "---\nname: uloop-cancel\n---\n"); ToolSkillSynchronizer.SkillTargetInfo target = new( "Claude Code", @@ -1557,6 +1569,8 @@ await ToolSkillSynchronizer.InstallSkillFilesAtProjectRoot( Assert.That(Directory.Exists(executeMenuItemSkillDir), Is.False); Assert.That(Directory.Exists(flatRetiredRecordInputSkillDir), Is.False); Assert.That(Directory.Exists(groupedRetiredRecordInputSkillDir), Is.False); + Assert.That(Directory.Exists(flatRetiredCancelSkillDir), Is.False); + Assert.That(Directory.Exists(groupedRetiredCancelSkillDir), Is.False); Assert.That(File.Exists(Path.Combine(installedSkillDir, SkillInstallLayout.SkillFileName)), Is.True); Assert.That(File.ReadAllText(Path.Combine(installedSkillDir, "reference.md")), Is.EqualTo("reference")); } diff --git a/Packages/src/Editor/CliOnlyTools~/Status/Skill/SKILL.md b/Packages/src/Editor/CliOnlyTools~/Status/Skill/SKILL.md index 7ce35564f..360a337bc 100644 --- a/Packages/src/Editor/CliOnlyTools~/Status/Skill/SKILL.md +++ b/Packages/src/Editor/CliOnlyTools~/Status/Skill/SKILL.md @@ -1,6 +1,6 @@ --- name: uloop-status -description: "Check whether the Unity Editor for this project can take a uloop command now, without side effects. Use before sending commands when Unity may be starting, compiling, busy, or closed." +description: "Check whether the Unity Editor for this project can take a uloop command now, without side effects; also covers `uloop cancel` for stopping the command it is running. Use before sending commands when Unity may be starting, compiling, busy, or closed, and when BUSY does not clear or a command does not finish." --- # uloop status @@ -52,7 +52,7 @@ Play Mode does not change the state: a paused Play Mode session is `Ready`, with ## Notes - Exits 0 only for Ready. -- When `Busy` does not clear and the running command shows no sign of finishing, stop it with `uloop cancel`. +- When `Busy` does not clear and the running command shows no sign of finishing, stop it with `uloop cancel`; its outcomes, what it cannot cancel, and what to do after it are in [references/cancel.md](references/cancel.md). - It never focuses the window, launches Unity, or takes the command slot, so it is safe to poll. - A sandboxed shell that cannot open the Unix socket gets an error, not NotRunning; run it outside the sandbox. - uloop cannot detect Safe Mode directly; ServerUnavailable that does not clear is the signal. diff --git a/Packages/src/Editor/CliOnlyTools~/Cancel/Skill/SKILL.md b/Packages/src/Editor/CliOnlyTools~/Status/Skill/references/cancel.md similarity index 68% rename from Packages/src/Editor/CliOnlyTools~/Cancel/Skill/SKILL.md rename to Packages/src/Editor/CliOnlyTools~/Status/Skill/references/cancel.md index df4da5e47..b94513a8a 100644 --- a/Packages/src/Editor/CliOnlyTools~/Cancel/Skill/SKILL.md +++ b/Packages/src/Editor/CliOnlyTools~/Status/Skill/references/cancel.md @@ -1,15 +1,10 @@ ---- -name: uloop-cancel -description: "Cancel the uloop command the Unity Editor for this project is running now. Use when BUSY keeps rejecting commands or your own command does not finish." ---- - # uloop cancel Cancel the uloop command that holds the Editor's command slot, from another terminal or after the waiting command was given up on. ## When to Use -- Every command is rejected with `UNITY_SERVER_BUSY` and the running command shows no sign of finishing. +- Every command is rejected with `UNITY_SERVER_BUSY`, `uloop status` keeps reporting `Busy`, and the running command shows no sign of finishing. - A command you started does not return, for example an `execute-dynamic-code` snippet that awaits something that never completes. ## Usage @@ -49,15 +44,10 @@ A default EditMode `run-tests` and `compile --no-wait-for-domain-reload` can be ## What the Waiting Command Gets -- Most commands fail with `ErrorCode` `TOOL_CANCELLED` (not retryable). -- `execute-dynamic-code` returns its usual cancelled result: `Success: false` with "Execution was cancelled or timed out". +Most commands fail with `ErrorCode` `TOOL_CANCELLED` (not retryable). `execute-dynamic-code` instead returns its usual cancelled result; the `uloop-execute-dynamic-code` skill's `references/cancellation.md` describes it and what happens to a snippet that keeps running. `Cancelled` also resumes Play Mode when the Editor is paused, whether a pause point or a manual pause holds it — the same as when the waiting CLI is killed. ## After Cancelling -Run `uloop status` and wait for `Ready` before the next command. `Cancelled` means the command was asked to stop, not that it already stopped: - -- A tool that ignores its cancellation token keeps the slot until it returns on its own. -- `execute-dynamic-code` returns at once and frees the slot, but a snippet that ignores `ct` keeps running in the background. -- While a command blocks Unity's main thread, `execute-dynamic-code` cannot return; its slot is freed about 5 seconds later, when another command arrives. +Run `uloop status` and wait for `Ready` before the next command. `Cancelled` means the command was asked to stop, not that it already stopped: a tool that ignores its cancellation token keeps the slot until it returns on its own. diff --git a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/cancellation.md b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/cancellation.md index fb9dbbea2..5d00b49de 100644 --- a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/cancellation.md +++ b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/cancellation.md @@ -21,7 +21,7 @@ So a `CancellationToken` named `ct` is always in scope; use it without declaring ## Long awaits block other tool commands -uloop runs one tool command at a time per Editor. While a snippet is awaiting, other tool commands against that Editor (such as `compile`, `get-logs`, or `run-tests`) are rejected as BUSY. An await that may never finish — waiting for an animation or a scripted sequence to complete, or for an external event — keeps the Editor BUSY for as long as it waits. `uloop status` still answers while a tool runs, so use it to check what is running. To stop a running snippet from another terminal, run `uloop cancel`. +uloop runs one tool command at a time per Editor. While a snippet is awaiting, other tool commands against that Editor (such as `compile`, `get-logs`, or `run-tests`) are rejected as BUSY. An await that may never finish — waiting for an animation or a scripted sequence to complete, or for an external event — keeps the Editor BUSY for as long as it waits. `uloop status` still answers while a tool runs, so use it to check what is running. To stop a running snippet from another terminal, run `uloop cancel`; the `uloop-status` skill's `references/cancel.md` lists its outcomes. Another `execute-dynamic-code` is not rejected as BUSY: it returns `Success: false` with `ErrorMessage` "Another execution is already in progress". Since the next command an agent sends is often another snippet, treat that message as the sign that an earlier snippet is still awaiting. @@ -59,6 +59,6 @@ uloop stops waiting for the snippet and frees the Editor for the next command. A What the CLI that sent the snippet sees depends on why `ct` fired: -- Yielded to a foreground request, or stopped by `uloop cancel`: the connection is still open, so the CLI receives `Success: false` with `ErrorMessage` "Execution was cancelled or timed out". +- Yielded to a foreground request, or stopped by `uloop cancel`: the connection is still open, so the CLI receives `Success: false` with `ErrorMessage` "Execution was cancelled or timed out". While a command blocks Unity's main thread, the snippet cannot return; its slot is freed about 5 seconds later, when another command arrives. - The CLI disconnected: no response is sent; the CLI that was waiting is already gone. - Domain reload, Editor quit, or server stop/restart: no response arrives. The CLI treats it as a lost connection: after a domain reload it prints "Connection lost during execute-dynamic-code", waits for the Editor to come back, and then reports the disconnect as an error. Check the Editor with `uloop status` before sending the next command. diff --git a/Packages/src/Editor/Infrastructure/SkillSetup/SkillTargetInstaller.cs b/Packages/src/Editor/Infrastructure/SkillSetup/SkillTargetInstaller.cs index 95e22527f..a73d68d85 100644 --- a/Packages/src/Editor/Infrastructure/SkillSetup/SkillTargetInstaller.cs +++ b/Packages/src/Editor/Infrastructure/SkillSetup/SkillTargetInstaller.cs @@ -24,7 +24,8 @@ internal static class SkillTargetInstaller "uloop-get-unity-search-providers", "uloop-execute-menu-item", "uloop-raycast", - "uloop-record-input" + "uloop-record-input", + "uloop-cancel" }; private enum SkillSyncScope diff --git a/README.md b/README.md index 9060e89c2..67f2b7f02 100644 --- a/README.md +++ b/README.md @@ -246,8 +246,7 @@ That's it! After installing Skills, LLM tools can automatically handle instructi All 23 Bundled Skills - `/uloop-launch` - Launch Unity with correct version -- `/uloop-status` - Check whether Unity can take a command now -- `/uloop-cancel` - Cancel the command Unity is running now, when BUSY keeps rejecting commands +- `/uloop-status` - Check whether Unity can take a command now, and cancel the command Unity is running when BUSY keeps rejecting commands - `/uloop-compile` - Execute compilation - `/uloop-verify-project` - Check .meta files, GUIDs, conflict markers, and the package manifest without Unity - `/uloop-get-logs` - Get console logs diff --git a/README_ja.md b/README_ja.md index dff2af52e..2339db238 100644 --- a/README_ja.md +++ b/README_ja.md @@ -246,8 +246,7 @@ V2 のプロジェクトの Settings ウィンドウで **Update CLI** や **Dow バンドルされている全23個のSkills一覧 - `/uloop-launch` - 正しいバージョンでUnityを起動 -- `/uloop-status` - Unityが今コマンドを受け付けられるかを確認 -- `/uloop-cancel` - BUSYでコマンドが拒否され続けるときに、Unityが実行中のコマンドをキャンセル +- `/uloop-status` - Unityが今コマンドを受け付けられるかを確認し、BUSYでコマンドが拒否され続けるときは実行中のコマンドをキャンセル - `/uloop-compile` - コンパイルの実行 - `/uloop-verify-project` - Unityを起動せずに.metaファイル・GUID・コンフリクトマーカー・パッケージmanifestを検査 - `/uloop-get-logs` - Consoleログの取得 diff --git a/cli/common/tooldocs/skill_guidance.go b/cli/common/tooldocs/skill_guidance.go index 996f03eae..ac11631bf 100644 --- a/cli/common/tooldocs/skill_guidance.go +++ b/cli/common/tooldocs/skill_guidance.go @@ -27,7 +27,9 @@ var commandSkillNames = map[string]string{ "launch": "uloop-launch", "status": "uloop-status", - "cancel": "uloop-cancel", + // The status skill documents cancel in references/cancel.md: cancel is the way out of a Busy + // that status reports, and a skill of its own would sit in every session for a rare recovery. + "cancel": "uloop-status", // One skill covers the four pause-point commands and the three watch commands: watch // expressions are documented by the pause-point skill's references/watch-expressions.md. diff --git a/cli/dispatcher/internal/dispatcher/skills.go b/cli/dispatcher/internal/dispatcher/skills.go index b45bdff53..6102b5461 100644 --- a/cli/dispatcher/internal/dispatcher/skills.go +++ b/cli/dispatcher/internal/dispatcher/skills.go @@ -41,6 +41,7 @@ var deprecatedSkillNames = []string{ "uloop-execute-menu-item", "uloop-raycast", "uloop-record-input", + "uloop-cancel", } type skillTarget struct { diff --git a/cli/dispatcher/internal/dispatcher/skills_test.go b/cli/dispatcher/internal/dispatcher/skills_test.go index 5d07d547f..7cc367ef4 100644 --- a/cli/dispatcher/internal/dispatcher/skills_test.go +++ b/cli/dispatcher/internal/dispatcher/skills_test.go @@ -592,27 +592,32 @@ name: uloop-disabled-skill } } -// Verifies that installing skills removes the retired Record Input skill from both layouts. -func TestInstallSkillsForTargetRemovesRetiredRecordInputSkill(t *testing.T) { - projectRoot := t.TempDir() - target := targetConfigs["claude"] - skillsRoot := filepath.Join(projectRoot, target.projectDir, "skills") - flatDir := filepath.Join(skillsRoot, "uloop-record-input") - groupedDir := filepath.Join(skillsRoot, managedSkillsDir, "uloop-record-input") - writeSkillFile(t, flatDir, "---\nname: uloop-record-input\n---\n") - writeSkillFile(t, groupedDir, "---\nname: uloop-record-input\n---\n") - - result, err := installSkillsForTarget(projectRoot, projectRoot, target, []skillDefinition{}, false, true) - if err != nil { - t.Fatalf("installSkillsForTarget failed: %v", err) - } - if result.deprecatedRemoved != 2 { - t.Fatalf("deprecated removal count mismatch: %#v", result) - } - for _, missingDir := range []string{flatDir, groupedDir} { - if _, err := os.Stat(missingDir); err == nil { - t.Fatalf("retired skill should be removed: %s", missingDir) - } +// Verifies that installing skills removes each retired skill from both layouts: the Record Input +// skill, whose tool was retired, and the Cancel skill, whose command the uloop-status skill now documents. +func TestInstallSkillsForTargetRemovesRetiredSkill(t *testing.T) { + for _, skillName := range []string{"uloop-record-input", "uloop-cancel"} { + t.Run(skillName, func(t *testing.T) { + projectRoot := t.TempDir() + target := targetConfigs["claude"] + skillsRoot := filepath.Join(projectRoot, target.projectDir, "skills") + flatDir := filepath.Join(skillsRoot, skillName) + groupedDir := filepath.Join(skillsRoot, managedSkillsDir, skillName) + writeSkillFile(t, flatDir, "---\nname: "+skillName+"\n---\n") + writeSkillFile(t, groupedDir, "---\nname: "+skillName+"\n---\n") + + result, err := installSkillsForTarget(projectRoot, projectRoot, target, []skillDefinition{}, false, true) + if err != nil { + t.Fatalf("installSkillsForTarget failed: %v", err) + } + if result.deprecatedRemoved != 2 { + t.Fatalf("deprecated removal count mismatch: %#v", result) + } + for _, missingDir := range []string{flatDir, groupedDir} { + if _, err := os.Stat(missingDir); err == nil { + t.Fatalf("retired skill should be removed: %s", missingDir) + } + } + }) } } diff --git a/cli/dispatcher/shared-inputs-stamp.json b/cli/dispatcher/shared-inputs-stamp.json index 3ae049d31..a412513d0 100644 --- a/cli/dispatcher/shared-inputs-stamp.json +++ b/cli/dispatcher/shared-inputs-stamp.json @@ -1,4 +1,4 @@ { "schemaVersion": 1, - "sharedInputsHash": "201f7fc23d2f35969582d5f290edaf13a1556929" + "sharedInputsHash": "8a09619015502cefdf71db0e9a33254cd922371d" } diff --git a/cli/project-runner/internal/projectrunner/native_command_help_test.go b/cli/project-runner/internal/projectrunner/native_command_help_test.go index 2ad8f016e..faa4c02a4 100644 --- a/cli/project-runner/internal/projectrunner/native_command_help_test.go +++ b/cli/project-runner/internal/projectrunner/native_command_help_test.go @@ -141,7 +141,8 @@ func TestStatusHelpPrintsUsage(t *testing.T) { } // Verifies cancel --help prints its usage line and registry description with only the global -// option, since cancel takes no options of its own, and ends by pointing at the cancel skill. +// option, since cancel takes no options of its own, and ends by pointing at the status skill +// that documents it. func TestCancelHelpPrintsUsage(t *testing.T) { t.Chdir(t.TempDir()) var stdout bytes.Buffer @@ -160,7 +161,7 @@ func TestCancelHelpPrintsUsage(t *testing.T) { "Global options:\n" + " --project-path Run against a Unity project outside the current directory\n" + "\n" + - "Load the uloop-cancel skill for workflow rules and response fields that --help does not cover.\n" + "Load the uloop-status skill for workflow rules and response fields that --help does not cover.\n" if stdout.String() != expected { t.Fatalf("cancel --help output mismatch:\n got:\n%s\nwant:\n%s", stdout.String(), expected) } diff --git a/cli/project-runner/shared-inputs-stamp.json b/cli/project-runner/shared-inputs-stamp.json index 466b358c1..f73a155a7 100644 --- a/cli/project-runner/shared-inputs-stamp.json +++ b/cli/project-runner/shared-inputs-stamp.json @@ -1,4 +1,4 @@ { "schemaVersion": 1, - "sharedInputsHash": "30c21c3eae97d957604131bdd7f60b201fd1b64a" + "sharedInputsHash": "8fdaeecf094cce9bd32fee36305f2b508017e29e" } From 1094a2cf60b8e6732df2d95fbb3bc17bea7a10ea Mon Sep 17 00:00:00 2001 From: hatayama Date: Tue, 27 Oct 2026 17:57:58 +0900 Subject: [PATCH 2/5] Update the bundled skill count in the READMEs to 22 The skill list lost uloop-cancel when it was folded into uloop-status, so the heading still claimed 23 skills. --- README.md | 2 +- README_ja.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 67f2b7f02..60fbfff35 100644 --- a/README.md +++ b/README.md @@ -243,7 +243,7 @@ That's it! After installing Skills, LLM tools can automatically handle instructi
-All 23 Bundled Skills +All 22 Bundled Skills - `/uloop-launch` - Launch Unity with correct version - `/uloop-status` - Check whether Unity can take a command now, and cancel the command Unity is running when BUSY keeps rejecting commands diff --git a/README_ja.md b/README_ja.md index 2339db238..6a509ec0e 100644 --- a/README_ja.md +++ b/README_ja.md @@ -243,7 +243,7 @@ V2 のプロジェクトの Settings ウィンドウで **Update CLI** や **Dow
-バンドルされている全23個のSkills一覧 +バンドルされている全22個のSkills一覧 - `/uloop-launch` - 正しいバージョンでUnityを起動 - `/uloop-status` - Unityが今コマンドを受け付けられるかを確認し、BUSYでコマンドが拒否され続けるときは実行中のコマンドをキャンセル From a849730f5e80f2619c45431060e6a2765e9e0f7b Mon Sep 17 00:00:00 2001 From: hatayama Date: Tue, 27 Oct 2026 20:35:27 +0900 Subject: [PATCH 3/5] Tell agents how to free a slot held by a cancelled snippet that cannot return uloop status only reads the slot, so polling it never takes back the lease of a cancelled execute-dynamic-code snippet that is stuck. Only a request from another tool does: the first one starts a 5-second grace period and one sent after it revokes the lease. Folding the cancel skill had left the status reference saying to wait for Ready, with the grace detail only in the execute-dynamic-code reference. --- .../uloop-execute-dynamic-code/references/cancellation.md | 2 +- .agents/skills/uloop-status/references/cancel.md | 2 ++ .../uloop-execute-dynamic-code/references/cancellation.md | 2 +- .claude/skills/uloop-status/references/cancel.md | 2 ++ .../src/Editor/CliOnlyTools~/Status/Skill/references/cancel.md | 2 ++ .../ExecuteDynamicCode/Skill/references/cancellation.md | 2 +- 6 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.agents/skills/uloop-execute-dynamic-code/references/cancellation.md b/.agents/skills/uloop-execute-dynamic-code/references/cancellation.md index 5d00b49de..24e17b278 100644 --- a/.agents/skills/uloop-execute-dynamic-code/references/cancellation.md +++ b/.agents/skills/uloop-execute-dynamic-code/references/cancellation.md @@ -59,6 +59,6 @@ uloop stops waiting for the snippet and frees the Editor for the next command. A What the CLI that sent the snippet sees depends on why `ct` fired: -- Yielded to a foreground request, or stopped by `uloop cancel`: the connection is still open, so the CLI receives `Success: false` with `ErrorMessage` "Execution was cancelled or timed out". While a command blocks Unity's main thread, the snippet cannot return; its slot is freed about 5 seconds later, when another command arrives. +- Yielded to a foreground request, or stopped by `uloop cancel`: the connection is still open, so the CLI receives `Success: false` with `ErrorMessage` "Execution was cancelled or timed out". While a command blocks Unity's main thread, the snippet cannot return, and `uloop status` alone never frees its slot. The first tool command other than `execute-dynamic-code` that arrives after the cancel is rejected as BUSY and starts a 5-second grace period; one sent after that period takes the slot back. - The CLI disconnected: no response is sent; the CLI that was waiting is already gone. - Domain reload, Editor quit, or server stop/restart: no response arrives. The CLI treats it as a lost connection: after a domain reload it prints "Connection lost during execute-dynamic-code", waits for the Editor to come back, and then reports the disconnect as an error. Check the Editor with `uloop status` before sending the next command. diff --git a/.agents/skills/uloop-status/references/cancel.md b/.agents/skills/uloop-status/references/cancel.md index b94513a8a..9232fcd92 100644 --- a/.agents/skills/uloop-status/references/cancel.md +++ b/.agents/skills/uloop-status/references/cancel.md @@ -51,3 +51,5 @@ Most commands fail with `ErrorCode` `TOOL_CANCELLED` (not retryable). `execute-d ## After Cancelling Run `uloop status` and wait for `Ready` before the next command. `Cancelled` means the command was asked to stop, not that it already stopped: a tool that ignores its cancellation token keeps the slot until it returns on its own. + +The exception is an `execute-dynamic-code` snippet that still cannot return, for example because a command blocks Unity's main thread. Polling `uloop status` never frees its slot, because status only reads the slot. Send a tool command other than `execute-dynamic-code` (such as `uloop get-logs`): it is rejected as BUSY and starts a 5-second grace period. Send it again after those 5 seconds, and that request takes the slot back. diff --git a/.claude/skills/uloop-execute-dynamic-code/references/cancellation.md b/.claude/skills/uloop-execute-dynamic-code/references/cancellation.md index 5d00b49de..24e17b278 100644 --- a/.claude/skills/uloop-execute-dynamic-code/references/cancellation.md +++ b/.claude/skills/uloop-execute-dynamic-code/references/cancellation.md @@ -59,6 +59,6 @@ uloop stops waiting for the snippet and frees the Editor for the next command. A What the CLI that sent the snippet sees depends on why `ct` fired: -- Yielded to a foreground request, or stopped by `uloop cancel`: the connection is still open, so the CLI receives `Success: false` with `ErrorMessage` "Execution was cancelled or timed out". While a command blocks Unity's main thread, the snippet cannot return; its slot is freed about 5 seconds later, when another command arrives. +- Yielded to a foreground request, or stopped by `uloop cancel`: the connection is still open, so the CLI receives `Success: false` with `ErrorMessage` "Execution was cancelled or timed out". While a command blocks Unity's main thread, the snippet cannot return, and `uloop status` alone never frees its slot. The first tool command other than `execute-dynamic-code` that arrives after the cancel is rejected as BUSY and starts a 5-second grace period; one sent after that period takes the slot back. - The CLI disconnected: no response is sent; the CLI that was waiting is already gone. - Domain reload, Editor quit, or server stop/restart: no response arrives. The CLI treats it as a lost connection: after a domain reload it prints "Connection lost during execute-dynamic-code", waits for the Editor to come back, and then reports the disconnect as an error. Check the Editor with `uloop status` before sending the next command. diff --git a/.claude/skills/uloop-status/references/cancel.md b/.claude/skills/uloop-status/references/cancel.md index b94513a8a..9232fcd92 100644 --- a/.claude/skills/uloop-status/references/cancel.md +++ b/.claude/skills/uloop-status/references/cancel.md @@ -51,3 +51,5 @@ Most commands fail with `ErrorCode` `TOOL_CANCELLED` (not retryable). `execute-d ## After Cancelling Run `uloop status` and wait for `Ready` before the next command. `Cancelled` means the command was asked to stop, not that it already stopped: a tool that ignores its cancellation token keeps the slot until it returns on its own. + +The exception is an `execute-dynamic-code` snippet that still cannot return, for example because a command blocks Unity's main thread. Polling `uloop status` never frees its slot, because status only reads the slot. Send a tool command other than `execute-dynamic-code` (such as `uloop get-logs`): it is rejected as BUSY and starts a 5-second grace period. Send it again after those 5 seconds, and that request takes the slot back. diff --git a/Packages/src/Editor/CliOnlyTools~/Status/Skill/references/cancel.md b/Packages/src/Editor/CliOnlyTools~/Status/Skill/references/cancel.md index b94513a8a..9232fcd92 100644 --- a/Packages/src/Editor/CliOnlyTools~/Status/Skill/references/cancel.md +++ b/Packages/src/Editor/CliOnlyTools~/Status/Skill/references/cancel.md @@ -51,3 +51,5 @@ Most commands fail with `ErrorCode` `TOOL_CANCELLED` (not retryable). `execute-d ## After Cancelling Run `uloop status` and wait for `Ready` before the next command. `Cancelled` means the command was asked to stop, not that it already stopped: a tool that ignores its cancellation token keeps the slot until it returns on its own. + +The exception is an `execute-dynamic-code` snippet that still cannot return, for example because a command blocks Unity's main thread. Polling `uloop status` never frees its slot, because status only reads the slot. Send a tool command other than `execute-dynamic-code` (such as `uloop get-logs`): it is rejected as BUSY and starts a 5-second grace period. Send it again after those 5 seconds, and that request takes the slot back. diff --git a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/cancellation.md b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/cancellation.md index 5d00b49de..24e17b278 100644 --- a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/cancellation.md +++ b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/cancellation.md @@ -59,6 +59,6 @@ uloop stops waiting for the snippet and frees the Editor for the next command. A What the CLI that sent the snippet sees depends on why `ct` fired: -- Yielded to a foreground request, or stopped by `uloop cancel`: the connection is still open, so the CLI receives `Success: false` with `ErrorMessage` "Execution was cancelled or timed out". While a command blocks Unity's main thread, the snippet cannot return; its slot is freed about 5 seconds later, when another command arrives. +- Yielded to a foreground request, or stopped by `uloop cancel`: the connection is still open, so the CLI receives `Success: false` with `ErrorMessage` "Execution was cancelled or timed out". While a command blocks Unity's main thread, the snippet cannot return, and `uloop status` alone never frees its slot. The first tool command other than `execute-dynamic-code` that arrives after the cancel is rejected as BUSY and starts a 5-second grace period; one sent after that period takes the slot back. - The CLI disconnected: no response is sent; the CLI that was waiting is already gone. - Domain reload, Editor quit, or server stop/restart: no response arrives. The CLI treats it as a lost connection: after a domain reload it prints "Connection lost during execute-dynamic-code", waits for the Editor to come back, and then reports the disconnect as an error. Check the Editor with `uloop status` before sending the next command. From f105d46f34627a40fd4af284cbdbb306e3dd6163 Mon Sep 17 00:00:00 2001 From: hatayama Date: Tue, 27 Oct 2026 20:36:32 +0900 Subject: [PATCH 4/5] Update the bundled skill count in the READMEs to 21 main hid verify-project from installed skills while this branch folded uloop-cancel into uloop-status. Both sides rewrote the heading from 23 to 22, so the merge kept 22 although the list now has 21 entries. --- README.md | 2 +- README_ja.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dc82ce221..6f5f493c0 100644 --- a/README.md +++ b/README.md @@ -243,7 +243,7 @@ That's it! After installing Skills, LLM tools can automatically handle instructi
-All 22 Bundled Skills +All 21 Bundled Skills - `/uloop-launch` - Launch Unity with correct version - `/uloop-status` - Check whether Unity can take a command now, and cancel the command Unity is running when BUSY keeps rejecting commands diff --git a/README_ja.md b/README_ja.md index 0276d42d0..1f3f65488 100644 --- a/README_ja.md +++ b/README_ja.md @@ -243,7 +243,7 @@ V2 のプロジェクトの Settings ウィンドウで **Update CLI** や **Dow
-バンドルされている全22個のSkills一覧 +バンドルされている全21個のSkills一覧 - `/uloop-launch` - 正しいバージョンでUnityを起動 - `/uloop-status` - Unityが今コマンドを受け付けられるかを確認し、BUSYでコマンドが拒否され続けるときは実行中のコマンドをキャンセル From d04c6c49e51b6d560f25ab262596cab064328175 Mon Sep 17 00:00:00 2001 From: hatayama Date: Tue, 27 Oct 2026 20:46:15 +0900 Subject: [PATCH 5/5] Describe the stuck-snippet slot recovery as one tool command, not a resend The CLI retries a busy tool command every second for up to 10 seconds, and the 5-second grace period is sized to fit inside that window. One command such as uloop get-logs therefore waits through the grace period and runs; it does not come back BUSY for the agent to send again, as the previous wording said. --- .../uloop-execute-dynamic-code/references/cancellation.md | 2 +- .agents/skills/uloop-status/references/cancel.md | 2 +- .../uloop-execute-dynamic-code/references/cancellation.md | 2 +- .claude/skills/uloop-status/references/cancel.md | 2 +- .../src/Editor/CliOnlyTools~/Status/Skill/references/cancel.md | 2 +- .../ExecuteDynamicCode/Skill/references/cancellation.md | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.agents/skills/uloop-execute-dynamic-code/references/cancellation.md b/.agents/skills/uloop-execute-dynamic-code/references/cancellation.md index 24e17b278..3c67ecaed 100644 --- a/.agents/skills/uloop-execute-dynamic-code/references/cancellation.md +++ b/.agents/skills/uloop-execute-dynamic-code/references/cancellation.md @@ -59,6 +59,6 @@ uloop stops waiting for the snippet and frees the Editor for the next command. A What the CLI that sent the snippet sees depends on why `ct` fired: -- Yielded to a foreground request, or stopped by `uloop cancel`: the connection is still open, so the CLI receives `Success: false` with `ErrorMessage` "Execution was cancelled or timed out". While a command blocks Unity's main thread, the snippet cannot return, and `uloop status` alone never frees its slot. The first tool command other than `execute-dynamic-code` that arrives after the cancel is rejected as BUSY and starts a 5-second grace period; one sent after that period takes the slot back. +- Yielded to a foreground request, or stopped by `uloop cancel`: the connection is still open, so the CLI receives `Success: false` with `ErrorMessage` "Execution was cancelled or timed out". While a command blocks Unity's main thread, the snippet cannot return, and `uloop status` alone never frees its slot. A tool command other than `execute-dynamic-code` frees it: the slot is taken back 5 seconds after that command first finds it held, and the CLI keeps retrying the busy command through those 5 seconds, so the command then runs. - The CLI disconnected: no response is sent; the CLI that was waiting is already gone. - Domain reload, Editor quit, or server stop/restart: no response arrives. The CLI treats it as a lost connection: after a domain reload it prints "Connection lost during execute-dynamic-code", waits for the Editor to come back, and then reports the disconnect as an error. Check the Editor with `uloop status` before sending the next command. diff --git a/.agents/skills/uloop-status/references/cancel.md b/.agents/skills/uloop-status/references/cancel.md index 9232fcd92..e21d0d071 100644 --- a/.agents/skills/uloop-status/references/cancel.md +++ b/.agents/skills/uloop-status/references/cancel.md @@ -52,4 +52,4 @@ Most commands fail with `ErrorCode` `TOOL_CANCELLED` (not retryable). `execute-d Run `uloop status` and wait for `Ready` before the next command. `Cancelled` means the command was asked to stop, not that it already stopped: a tool that ignores its cancellation token keeps the slot until it returns on its own. -The exception is an `execute-dynamic-code` snippet that still cannot return, for example because a command blocks Unity's main thread. Polling `uloop status` never frees its slot, because status only reads the slot. Send a tool command other than `execute-dynamic-code` (such as `uloop get-logs`): it is rejected as BUSY and starts a 5-second grace period. Send it again after those 5 seconds, and that request takes the slot back. +The exception is an `execute-dynamic-code` snippet that still cannot return, for example because a command blocks Unity's main thread. Polling `uloop status` never frees its slot, because status only reads the slot. Send a tool command other than `execute-dynamic-code` (such as `uloop get-logs`) instead: the slot is taken back 5 seconds after that command first finds it held, and the CLI keeps retrying the busy command through those 5 seconds, so the command then runs. diff --git a/.claude/skills/uloop-execute-dynamic-code/references/cancellation.md b/.claude/skills/uloop-execute-dynamic-code/references/cancellation.md index 24e17b278..3c67ecaed 100644 --- a/.claude/skills/uloop-execute-dynamic-code/references/cancellation.md +++ b/.claude/skills/uloop-execute-dynamic-code/references/cancellation.md @@ -59,6 +59,6 @@ uloop stops waiting for the snippet and frees the Editor for the next command. A What the CLI that sent the snippet sees depends on why `ct` fired: -- Yielded to a foreground request, or stopped by `uloop cancel`: the connection is still open, so the CLI receives `Success: false` with `ErrorMessage` "Execution was cancelled or timed out". While a command blocks Unity's main thread, the snippet cannot return, and `uloop status` alone never frees its slot. The first tool command other than `execute-dynamic-code` that arrives after the cancel is rejected as BUSY and starts a 5-second grace period; one sent after that period takes the slot back. +- Yielded to a foreground request, or stopped by `uloop cancel`: the connection is still open, so the CLI receives `Success: false` with `ErrorMessage` "Execution was cancelled or timed out". While a command blocks Unity's main thread, the snippet cannot return, and `uloop status` alone never frees its slot. A tool command other than `execute-dynamic-code` frees it: the slot is taken back 5 seconds after that command first finds it held, and the CLI keeps retrying the busy command through those 5 seconds, so the command then runs. - The CLI disconnected: no response is sent; the CLI that was waiting is already gone. - Domain reload, Editor quit, or server stop/restart: no response arrives. The CLI treats it as a lost connection: after a domain reload it prints "Connection lost during execute-dynamic-code", waits for the Editor to come back, and then reports the disconnect as an error. Check the Editor with `uloop status` before sending the next command. diff --git a/.claude/skills/uloop-status/references/cancel.md b/.claude/skills/uloop-status/references/cancel.md index 9232fcd92..e21d0d071 100644 --- a/.claude/skills/uloop-status/references/cancel.md +++ b/.claude/skills/uloop-status/references/cancel.md @@ -52,4 +52,4 @@ Most commands fail with `ErrorCode` `TOOL_CANCELLED` (not retryable). `execute-d Run `uloop status` and wait for `Ready` before the next command. `Cancelled` means the command was asked to stop, not that it already stopped: a tool that ignores its cancellation token keeps the slot until it returns on its own. -The exception is an `execute-dynamic-code` snippet that still cannot return, for example because a command blocks Unity's main thread. Polling `uloop status` never frees its slot, because status only reads the slot. Send a tool command other than `execute-dynamic-code` (such as `uloop get-logs`): it is rejected as BUSY and starts a 5-second grace period. Send it again after those 5 seconds, and that request takes the slot back. +The exception is an `execute-dynamic-code` snippet that still cannot return, for example because a command blocks Unity's main thread. Polling `uloop status` never frees its slot, because status only reads the slot. Send a tool command other than `execute-dynamic-code` (such as `uloop get-logs`) instead: the slot is taken back 5 seconds after that command first finds it held, and the CLI keeps retrying the busy command through those 5 seconds, so the command then runs. diff --git a/Packages/src/Editor/CliOnlyTools~/Status/Skill/references/cancel.md b/Packages/src/Editor/CliOnlyTools~/Status/Skill/references/cancel.md index 9232fcd92..e21d0d071 100644 --- a/Packages/src/Editor/CliOnlyTools~/Status/Skill/references/cancel.md +++ b/Packages/src/Editor/CliOnlyTools~/Status/Skill/references/cancel.md @@ -52,4 +52,4 @@ Most commands fail with `ErrorCode` `TOOL_CANCELLED` (not retryable). `execute-d Run `uloop status` and wait for `Ready` before the next command. `Cancelled` means the command was asked to stop, not that it already stopped: a tool that ignores its cancellation token keeps the slot until it returns on its own. -The exception is an `execute-dynamic-code` snippet that still cannot return, for example because a command blocks Unity's main thread. Polling `uloop status` never frees its slot, because status only reads the slot. Send a tool command other than `execute-dynamic-code` (such as `uloop get-logs`): it is rejected as BUSY and starts a 5-second grace period. Send it again after those 5 seconds, and that request takes the slot back. +The exception is an `execute-dynamic-code` snippet that still cannot return, for example because a command blocks Unity's main thread. Polling `uloop status` never frees its slot, because status only reads the slot. Send a tool command other than `execute-dynamic-code` (such as `uloop get-logs`) instead: the slot is taken back 5 seconds after that command first finds it held, and the CLI keeps retrying the busy command through those 5 seconds, so the command then runs. diff --git a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/cancellation.md b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/cancellation.md index 24e17b278..3c67ecaed 100644 --- a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/cancellation.md +++ b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Skill/references/cancellation.md @@ -59,6 +59,6 @@ uloop stops waiting for the snippet and frees the Editor for the next command. A What the CLI that sent the snippet sees depends on why `ct` fired: -- Yielded to a foreground request, or stopped by `uloop cancel`: the connection is still open, so the CLI receives `Success: false` with `ErrorMessage` "Execution was cancelled or timed out". While a command blocks Unity's main thread, the snippet cannot return, and `uloop status` alone never frees its slot. The first tool command other than `execute-dynamic-code` that arrives after the cancel is rejected as BUSY and starts a 5-second grace period; one sent after that period takes the slot back. +- Yielded to a foreground request, or stopped by `uloop cancel`: the connection is still open, so the CLI receives `Success: false` with `ErrorMessage` "Execution was cancelled or timed out". While a command blocks Unity's main thread, the snippet cannot return, and `uloop status` alone never frees its slot. A tool command other than `execute-dynamic-code` frees it: the slot is taken back 5 seconds after that command first finds it held, and the CLI keeps retrying the busy command through those 5 seconds, so the command then runs. - The CLI disconnected: no response is sent; the CLI that was waiting is already gone. - Domain reload, Editor quit, or server stop/restart: no response arrives. The CLI treats it as a lost connection: after a domain reload it prints "Connection lost during execute-dynamic-code", waits for the Editor to come back, and then reports the disconnect as an error. Check the Editor with `uloop status` before sending the next command.