diff --git a/.agents/skills/uloop-execute-dynamic-code/references/cancellation.md b/.agents/skills/uloop-execute-dynamic-code/references/cancellation.md index fb9dbbea2..3c67ecaed 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, 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/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 65% rename from .agents/skills/uloop-cancel/SKILL.md rename to .agents/skills/uloop-status/references/cancel.md index df4da5e47..e21d0d071 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,12 @@ 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: +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. -- 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. +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 fb9dbbea2..3c67ecaed 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, 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/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 65% rename from .claude/skills/uloop-cancel/SKILL.md rename to .claude/skills/uloop-status/references/cancel.md index df4da5e47..e21d0d071 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,12 @@ 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: +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. -- 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. +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/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 65% rename from Packages/src/Editor/CliOnlyTools~/Cancel/Skill/SKILL.md rename to Packages/src/Editor/CliOnlyTools~/Status/Skill/references/cancel.md index df4da5e47..e21d0d071 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,12 @@ 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: +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. -- 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. +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 fb9dbbea2..3c67ecaed 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, 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/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 0cfe6c71e..6f5f493c0 100644 --- a/README.md +++ b/README.md @@ -243,11 +243,10 @@ 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 -- `/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-get-logs` - Get console logs - `/uloop-run-tests` - Run tests diff --git a/README_ja.md b/README_ja.md index bb7e37785..1f3f65488 100644 --- a/README_ja.md +++ b/README_ja.md @@ -243,11 +243,10 @@ V2 のプロジェクトの Settings ウィンドウで **Update CLI** や **Dow
-バンドルされている全22個のSkills一覧 +バンドルされている全21個のSkills一覧 - `/uloop-launch` - 正しいバージョンでUnityを起動 -- `/uloop-status` - Unityが今コマンドを受け付けられるかを確認 -- `/uloop-cancel` - BUSYでコマンドが拒否され続けるときに、Unityが実行中のコマンドをキャンセル +- `/uloop-status` - Unityが今コマンドを受け付けられるかを確認し、BUSYでコマンドが拒否され続けるときは実行中のコマンドをキャンセル - `/uloop-compile` - コンパイルの実行 - `/uloop-get-logs` - Consoleログの取得 - `/uloop-run-tests` - テストの実行 diff --git a/cli/common/tooldocs/skill_guidance.go b/cli/common/tooldocs/skill_guidance.go index 03aa21998..b556172ff 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 32f5d47cd..95e7e6a6f 100644 --- a/cli/dispatcher/shared-inputs-stamp.json +++ b/cli/dispatcher/shared-inputs-stamp.json @@ -1,4 +1,4 @@ { "schemaVersion": 1, - "sharedInputsHash": "0a0862142bfe42dd8abf5f68e895b6ded074284d" + "sharedInputsHash": "faa1b83079893ecb47a5cf672efb5b07bc16c600" } 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 2c39fdf55..b57025b49 100644 --- a/cli/project-runner/shared-inputs-stamp.json +++ b/cli/project-runner/shared-inputs-stamp.json @@ -1,4 +1,4 @@ { "schemaVersion": 1, - "sharedInputsHash": "6e27b965cbea814d35f4df1634fe18faa82e46bd" + "sharedInputsHash": "2543dd10ed36b76084828884dd356c38fcc769fe" }