Skip to content
8 changes: 0 additions & 8 deletions Packages/src/Editor/CliOnlyTools~/FocusWindow/Skill/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ Bring Unity Editor window to front using OS-level commands.
uloop focus-window
```

## Parameters

None.

## Global Options

| Option | Description |
Expand All @@ -36,11 +32,7 @@ Returns JSON with:
- `success`: Whether the focus operation succeeded
- `message`: Status message (e.g. `Unity Editor window focused (PID: 12345)`, or the failure reason such as `Unity project not found` / `No running Unity process found for this project` / `Failed to focus Unity window: <reason>`)

These are the only two fields. There is no PID, window-handle, or platform field returned to the caller.

## Notes

- **Works even when Unity is busy** (compiling, domain reload, etc.)
- Uses OS-level commands (osascript on macOS, PowerShell on Windows)
- Useful before `uloop capture-unity-window` to ensure the target window is visible
- Brings the main Unity Editor window to the foreground
38 changes: 20 additions & 18 deletions Packages/src/Editor/CliOnlyTools~/Launch/Skill/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,24 @@ uloop launch --quit

## Output

- Prints detected Unity version
- Prints project path
May print status/progress lines before the final JSON payload, such as project path, detected Unity version, or readiness wait messages.

The final JSON payload includes:
- `success`: whether the command completed
- `ready`: whether Unity CLI Loop is ready for commands
- `serverReady`: whether the Unity CLI Loop server accepted requests
- `projectIpcReady`: whether the project IPC path accepted tool requests
- `alreadyRunning`: whether an existing Unity process was reused
- `launched`: whether this command launched a Unity process
- `restarted`: whether this command stopped an existing process and launched a new one
- `quit`: whether this command stopped Unity without launching a new process
- `previousProcessId`: process ID stopped by restart or quit, when available
- `currentProcessId`: current Unity process ID, when available
- `projectRoot`: resolved project root
- `message`: readiness summary

## Notes

- If Unity is already running, focuses the existing window and verifies tool readiness
- If the process scan is blocked by the environment (e.g. sandboxed `ps`), plain launch falls back to probing the project IPC; when Unity responds it reports `alreadyRunning: true` without focusing the window instead of failing. `--restart` and `--quit` still fail because they need the process id
- If launching or restarting, prints when it is waiting for Unity CLI Loop server readiness
- If launching or restarting, waits until Unity finishes startup and the CLI can connect to the project
- Successful launch, restart, existing-process, and quit paths return JSON with:
- `success`: whether the command completed
- `ready`: whether Unity CLI Loop is ready for commands
- `serverReady`: whether the Unity CLI Loop server accepted requests
- `projectIpcReady`: whether the project IPC path accepted tool requests
- `alreadyRunning`: whether an existing Unity process was reused
- `launched`: whether this command launched a Unity process
- `restarted`: whether this command stopped an existing process and launched a new one
- `quit`: whether this command stopped Unity without launching a new process
- `previousProcessId`: process ID stopped by restart or quit, when available
- `currentProcessId`: current Unity process ID, when available
- `projectRoot`: resolved project root
- `message`: readiness summary
- If process scan is blocked (e.g. sandboxed `ps`), plain launch falls back to IPC probing; `--restart` and `--quit` still fail because they need the process id
- The command waits until Unity finishes startup and the CLI can connect before returning
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ Use `generation`, `enabledAtUtc`, and the hit sequence fields from the hit or st
- Consider a pause point during E2E passes when transition-frame evidence would add confidence, even if durable state, logs, or screenshots can later confirm the final result.
- Use this before reaching for `Time.timeScale`, sleeps, repeated polling, or after-the-fact `execute-dynamic-code`; those checks can supplement the paused-frame proof, but they are not substitutes.
- If the value you need is a method local, an intermediate calculation, or a branch reason that `execute-dynamic-code` cannot reach, add a focused `Debug.Log` immediately before the marker and read it with `get-logs` while paused. Do not count the breakpoint check complete until the matching log has been read.
- Good pause points include after input is consumed, a command is accepted, a state mutation is committed, an evaluation step resolves, a tracked value changes, a UI/domain state syncs, or a success/failure/end condition is entered.
- Treat the pause like a lightweight breakpoint for one important transition: combine nearby debug logs with paused-frame inspection to confirm the variables and component state at that point.
- Do not treat `simulate-* success=true`, generic action logs, sleeps/retries, testing-only counters, or `Time.timeScale` changes as paused-frame proof.
- Skip this only for ordinary persistent-state checks when you are not validating simulated input delivery, event ordering, or transition-frame fidelity.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,4 @@ Returns JSON with:
- `errorCount` (number): Errors cleared
- `warningCount` (number): Warnings cleared
- `logCount` (number): Info logs cleared
- `message` (string): Description of the result; carries the failure summary when the operation fails (e.g. `"Failed to clear console: ..."`)
- `errorMessage` (string): Currently always empty for this tool — read `message` for failure details
- `message` (string): Description of the result. On failure, this field carries the error summary.
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@ Returns JSON with the current play mode state:

## Notes

- Play action starts the game in the Unity Editor (also resumes from pause)
- Stop action exits play mode and returns to edit mode. If Play Mode was already stopped, `changed` is `false`, `wasAlreadyStopped` is `true`, and `message` is `Play mode was already stopped`.
- Pause action pauses the game while remaining in play mode
- Step action advances exactly one frame and leaves play mode paused (the Editor's Next Frame button; independent of Time.timeScale). Requires PlayMode to be running; repeat to walk transitions frame by frame
- Useful for automated testing workflows
- Stop on an already-stopped Editor sets `changed: false`, `wasAlreadyStopped: true`
- Step is independent of `Time.timeScale`; requires PlayMode to be running
- The command waits for the requested state before returning. Increase `--timeout-seconds` for projects with slow PlayMode entry.
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ This tool can inspect reachable Unity state, such as GameObjects, components, pu
1. Read the relevant reference file(s) from the Code Examples section below
2. Construct C# code based on the reference examples
3. Execute with `--code` or `--code-file` using the active shell's quoting guidance
4. If execution fails, adjust code and retry
5. Report the execution result
4. Report the execution result

## Parameters

Expand Down Expand Up @@ -48,8 +47,7 @@ Returns JSON:
- `result`: string — value of the snippet's `return` statement (empty when omitted)
- `logs`: string[] — execution messages from the dynamic-code tool; read Unity Console `Debug.Log` output with `get-logs`
- `compilationErrors`: object[] — Roslyn diagnostics with `message`, `line`, `column`, `errorCode`, optional `hint` and `suggestions`
- `errorMessage`: string — top-level failure summary (empty on success)
- `error`: string — alias of `errorMessage`
- `error` / `errorMessage`: string — top-level failure summary (empty on success)
- `updatedCode`: string|null — the wrapped form actually compiled (handy when debugging using-statement reordering)
- `diagnosticsSummary`: string|null — compact summary when diagnostics are available
- `diagnostics`: object[] — structured diagnostics; same shape as `compilationErrors`, usually populated together with it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,25 @@

Code examples for AssetDatabase operations using `execute-dynamic-code`.

## Find Assets by Type
## Find Assets

```csharp
using UnityEditor;
using System.Collections.Generic;

string[] prefabGuids = AssetDatabase.FindAssets("t:Prefab");
List<string> paths = new List<string>();

foreach (string guid in prefabGuids)
{
paths.Add(AssetDatabase.GUIDToAssetPath(guid));
}
return $"Found {paths.Count} prefabs";
```

## Find Assets by Name
// By type
string[] guids = AssetDatabase.FindAssets("t:Prefab");
// By name
// string[] guids = AssetDatabase.FindAssets("Player");
// By type in folder
// string[] guids = AssetDatabase.FindAssets("t:Prefab", new[] { "Assets/Prefabs" });

```csharp
using UnityEditor;
using System.Collections.Generic;

string searchName = "Player";
string[] guids = AssetDatabase.FindAssets(searchName);
List<string> paths = new List<string>();

foreach (string guid in guids)
{
paths.Add(AssetDatabase.GUIDToAssetPath(guid));
}
return $"Found {paths.Count} assets matching '{searchName}'";
```

## Find Assets in Folder

```csharp
using UnityEditor;
using System.Collections.Generic;

string folder = "Assets/Prefabs";
string[] guids = AssetDatabase.FindAssets("t:Prefab", new[] { folder });
List<string> paths = new List<string>();

foreach (string guid in guids)
{
paths.Add(AssetDatabase.GUIDToAssetPath(guid));
}
return $"Found {paths.Count} prefabs in {folder}";
return $"Found {paths.Count} assets";
```

## Duplicate Asset
Expand All @@ -76,7 +47,7 @@ string error = AssetDatabase.MoveAsset(sourcePath, destPath);
return string.IsNullOrEmpty(error) ? $"Moved to {destPath}" : $"Error: {error}";
```

## Rename Asset
## Rename Asset (no Undo)

```csharp
using UnityEditor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,56 +78,27 @@ Undo.CollapseUndoOperations(undoGroup);
return $"Removed {totalRemoved} missing scripts from scene";
```

## Detect Missing References in Component
## Scan for Missing References

Scans the entire scene by default. Set `scanSelectionOnly` to `true` only when the task asks for the selected GameObject.

```csharp
using UnityEditor;

bool scanSelectionOnly = false;
GameObject selected = Selection.activeGameObject;
if (selected == null)
if (scanSelectionOnly && selected == null)
{
return "No GameObject selected";
}

List<string> missingRefs = new List<string>();

Component[] components = selected.GetComponents<Component>();
foreach (Component comp in components)
{
if (comp == null) continue;

SerializedObject so = new SerializedObject(comp);
SerializedProperty prop = so.GetIterator();

while (prop.NextVisible(true))
{
if (prop.propertyType == SerializedPropertyType.ObjectReference)
{
if (prop.objectReferenceValue == null && prop.objectReferenceInstanceIDValue != 0)
{
missingRefs.Add($"{comp.GetType().Name}.{prop.name}");
}
}
}
}
GameObject[] targets = scanSelectionOnly
? new[] { selected }
: Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);

if (missingRefs.Count == 0)
{
return "No missing references found";
}

return $"Missing references: {string.Join(", ", missingRefs)}";
```

## Scan Scene for Missing References

```csharp
using UnityEditor;

GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
List<string> results = new List<string>();

foreach (GameObject obj in allObjects)
foreach (GameObject obj in targets)
{
Component[] components = obj.GetComponents<Component>();
foreach (Component comp in components)
Expand All @@ -152,10 +123,10 @@ foreach (GameObject obj in allObjects)

if (results.Count == 0)
{
return "No missing references found in scene";
return "No missing references found";
}

return $"Missing references ({results.Count}): {string.Join(", ", results.Take(10))}...";
return $"Missing references ({results.Count}): {string.Join(", ", results.Take(10))}";
```

## Find Unused Materials in Project
Expand Down Expand Up @@ -196,31 +167,6 @@ foreach (string guid in materialGuids)
return $"Found {unusedMaterials.Count} materials not referenced by prefabs. Verify scene and other asset references manually before deleting.";
```

## Find Empty GameObjects

```csharp
using UnityEditor;

GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
List<string> emptyObjects = new List<string>();

foreach (GameObject obj in allObjects)
{
Component[] components = obj.GetComponents<Component>();
if (components.Length == 1 && obj.transform.childCount == 0)
{
emptyObjects.Add(obj.name);
}
}

if (emptyObjects.Count == 0)
{
return "No empty GameObjects found";
}

return $"Empty objects ({emptyObjects.Count}): {string.Join(", ", emptyObjects.Take(20))}";
```

## Find Duplicate Names in Hierarchy

```csharp
Expand Down Expand Up @@ -311,31 +257,30 @@ if (negativeScale.Count == 0)
return $"Negative scale objects: {string.Join(", ", negativeScale.Take(10))}";
```

## Remove Empty Leaf GameObjects
## Find Empty Leaf GameObjects

```csharp
using UnityEditor;

GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);

int undoGroup = Undo.GetCurrentGroup();
Undo.SetCurrentGroupName("Remove Empty Leaves");

int removedCount = 0;
List<string> emptyObjects = new List<string>();
foreach (GameObject obj in allObjects)
{
if (obj == null) continue;

Component[] components = obj.GetComponents<Component>();
if (components.Length == 1 && obj.transform.childCount == 0)
{
Undo.DestroyObjectImmediate(obj);
removedCount++;
emptyObjects.Add(obj.name);
}
}

Undo.CollapseUndoOperations(undoGroup);
return $"Removed {removedCount} empty leaf GameObjects";
if (emptyObjects.Count == 0)
{
return "No empty leaf GameObjects found";
}

return $"Empty leaf GameObjects ({emptyObjects.Count}): {string.Join(", ", emptyObjects.Take(20))}";
```

## Find Large Meshes
Expand Down
Loading