uloop-cli 0.51.0 → 0.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5621,7 +5621,7 @@ var import_path3 = require("path");
5621
5621
 
5622
5622
  // src/default-tools.json
5623
5623
  var default_tools_default = {
5624
- version: "0.51.0",
5624
+ version: "0.52.0",
5625
5625
  tools: [
5626
5626
  {
5627
5627
  name: "compile",
@@ -6052,7 +6052,7 @@ function getCacheFilePath() {
6052
6052
  }
6053
6053
 
6054
6054
  // src/version.ts
6055
- var VERSION = "0.51.0";
6055
+ var VERSION = "0.52.0";
6056
6056
 
6057
6057
  // src/spinner.ts
6058
6058
  var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
@@ -6356,7 +6356,7 @@ var SKILL_default3 = '---\nname: uloop-compile\ndescription: Compile Unity proje
6356
6356
  var SKILL_default4 = "---\nname: uloop-control-play-mode\ndescription: Control Unity Editor play mode via uloop CLI. Use when you need to: (1) Start play mode for testing, (2) Stop play mode after testing, (3) Pause play mode for debugging.\n---\n\n# uloop control-play-mode\n\nControl Unity Editor play mode (play/stop/pause).\n\n## Usage\n\n```bash\nuloop control-play-mode [options]\n```\n\n## Parameters\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `--action` | string | `Play` | Action to perform: `Play`, `Stop`, `Pause` |\n\n## Examples\n\n```bash\n# Start play mode\nuloop control-play-mode --action Play\n\n# Stop play mode\nuloop control-play-mode --action Stop\n\n# Pause play mode\nuloop control-play-mode --action Pause\n```\n\n## Output\n\nReturns JSON with the current play mode state:\n- `IsPlaying`: Whether Unity is currently in play mode\n- `IsPaused`: Whether play mode is paused\n- `Message`: Description of the action performed\n\n## Notes\n\n- Play action starts the game in the Unity Editor (also resumes from pause)\n- Stop action exits play mode and returns to edit mode\n- Pause action pauses the game while remaining in play mode\n- Useful for automated testing workflows\n";
6357
6357
 
6358
6358
  // ../Editor/Api/McpTools/ExecuteDynamicCode/SKILL.md
6359
- var SKILL_default5 = "---\nname: uloop-execute-dynamic-code\ndescription: Execute C# code dynamically in Unity Editor via uloop CLI. Use for editor automation: (1) Prefab/material wiring and AddComponent operations, (2) Reference wiring with SerializedObject, (3) Scene/hierarchy edits and batch operations. NOT for file I/O or script authoring.\n---\n\n# uloop execute-dynamic-code\n\nExecute C# code dynamically in Unity Editor.\n\n## Usage\n\n```bash\nuloop execute-dynamic-code --code '<c# code>'\n```\n\n## Parameters\n\n| Parameter | Type | Description |\n|-----------|------|-------------|\n| `--code` | string | C# code to execute (direct statements, no class wrapper) |\n| `--compile-only` | boolean | Compile without execution |\n| `--auto-qualify-unity-types-once` | boolean | Auto-qualify Unity types |\n\n## Code Format\n\nWrite direct statements only (no classes/namespaces/methods). Return is optional.\n\n```csharp\n// Using directives at top are hoisted\nusing UnityEngine;\nvar x = Mathf.PI;\nreturn x;\n```\n\n## String Literals (Shell-specific)\n\n| Shell | Method |\n|-------|--------|\n| bash/zsh/MINGW64/Git Bash | `'Debug.Log(\"Hello!\");'` |\n| PowerShell | `'Debug.Log(\"\"Hello!\"\");'` |\n\n## Allowed Operations\n\n- Prefab/material wiring (PrefabUtility)\n- AddComponent + reference wiring (SerializedObject)\n- Scene/hierarchy edits\n- Inspector modifications\n\n## Forbidden Operations\n\n- System.IO.* (File/Directory/Path)\n- AssetDatabase.CreateFolder / file writes\n- Create/edit .cs/.asmdef files\n\n## Examples\n\n### bash / zsh / MINGW64 / Git Bash\n\n```bash\nuloop execute-dynamic-code --code 'return Selection.activeGameObject?.name;'\nuloop execute-dynamic-code --code 'new GameObject(\"MyObject\");'\nuloop execute-dynamic-code --code 'UnityEngine.Debug.Log(\"Hello from CLI!\");'\n```\n\n### PowerShell\n\n```powershell\nuloop execute-dynamic-code --code 'return Selection.activeGameObject?.name;'\nuloop execute-dynamic-code --code 'new GameObject(\"\"MyObject\"\");'\nuloop execute-dynamic-code --code 'UnityEngine.Debug.Log(\"\"Hello from CLI!\"\");'\n```\n\n## Output\n\nReturns JSON with execution result or compile errors.\n\n## Notes\n\nFor file/directory operations, use terminal commands instead.\n";
6359
+ var SKILL_default5 = "---\nname: uloop-execute-dynamic-code\ndescription: Execute C# code dynamically in Unity Editor via uloop CLI. Use for editor automation: (1) Prefab/material wiring and AddComponent operations, (2) Reference wiring with SerializedObject, (3) Scene/hierarchy edits and batch operations. NOT for file I/O or script authoring.\n---\n\n# uloop execute-dynamic-code\n\nExecute C# code dynamically in Unity Editor.\n\n## Usage\n\n```bash\nuloop execute-dynamic-code --code '<c# code>'\n```\n\n## Parameters\n\n| Parameter | Type | Description |\n|-----------|------|-------------|\n| `--code` | string | C# code to execute (direct statements, no class wrapper) |\n| `--compile-only` | boolean | Compile without execution |\n| `--auto-qualify-unity-types-once` | boolean | Auto-qualify Unity types |\n\n## Code Format\n\nWrite direct statements only (no classes/namespaces/methods). Return is optional.\n\n```csharp\n// Using directives at top are hoisted\nusing UnityEngine;\nvar x = Mathf.PI;\nreturn x;\n```\n\n## String Literals (Shell-specific)\n\n| Shell | Method |\n|-------|--------|\n| bash/zsh/MINGW64/Git Bash | `'Debug.Log(\"Hello!\");'` |\n| PowerShell | `'Debug.Log(\"\"Hello!\"\");'` |\n\n## Allowed Operations\n\n- Prefab/material wiring (PrefabUtility)\n- AddComponent + reference wiring (SerializedObject)\n- Scene/hierarchy edits\n- Inspector modifications\n\n## Forbidden Operations\n\n- System.IO.* (File/Directory/Path)\n- AssetDatabase.CreateFolder / file writes\n- Create/edit .cs/.asmdef files\n\n## Examples\n\n### bash / zsh / MINGW64 / Git Bash\n\n```bash\nuloop execute-dynamic-code --code 'return Selection.activeGameObject?.name;'\nuloop execute-dynamic-code --code 'new GameObject(\"MyObject\");'\nuloop execute-dynamic-code --code 'UnityEngine.Debug.Log(\"Hello from CLI!\");'\n```\n\n### PowerShell\n\n```powershell\nuloop execute-dynamic-code --code 'return Selection.activeGameObject?.name;'\nuloop execute-dynamic-code --code 'new GameObject(\"\"MyObject\"\");'\nuloop execute-dynamic-code --code 'UnityEngine.Debug.Log(\"\"Hello from CLI!\"\");'\n```\n\n## Output\n\nReturns JSON with execution result or compile errors.\n\n## Notes\n\nFor file/directory operations, use terminal commands instead.\n\n## Code Examples by Category\n\nFor detailed code examples, refer to these files:\n\n- **Prefab operations**: See [examples/prefab-operations.md](examples/prefab-operations.md)\n - Create prefabs, instantiate, add components, modify properties\n- **Material operations**: See [examples/material-operations.md](examples/material-operations.md)\n - Create materials, set shaders/textures, modify properties\n- **Asset operations**: See [examples/asset-operations.md](examples/asset-operations.md)\n - Find/search assets, duplicate, move, rename, load\n- **ScriptableObject**: See [examples/scriptableobject.md](examples/scriptableobject.md)\n - Create ScriptableObjects, modify with SerializedObject\n- **Scene operations**: See [examples/scene-operations.md](examples/scene-operations.md)\n - Create/modify GameObjects, set parents, wire references, load scenes\n";
6360
6360
 
6361
6361
  // ../Editor/Api/McpTools/ExecuteMenuItem/SKILL.md
6362
6362
  var SKILL_default6 = '---\nname: uloop-execute-menu-item\ndescription: Execute Unity MenuItem via uloop CLI. Use when you need to: (1) Trigger menu commands programmatically, (2) Automate editor actions (save, build, refresh), (3) Run custom menu items defined in scripts.\n---\n\n# uloop execute-menu-item\n\nExecute Unity MenuItem.\n\n## Usage\n\n```bash\nuloop execute-menu-item --menu-item-path "<path>"\n```\n\n## Parameters\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `--menu-item-path` | string | - | Menu item path (e.g., "GameObject/Create Empty") |\n| `--use-reflection-fallback` | boolean | `true` | Use reflection fallback |\n\n## Examples\n\n```bash\n# Create empty GameObject\nuloop execute-menu-item --menu-item-path "GameObject/Create Empty"\n\n# Save scene\nuloop execute-menu-item --menu-item-path "File/Save"\n\n# Open project settings\nuloop execute-menu-item --menu-item-path "Edit/Project Settings..."\n```\n\n## Output\n\nReturns JSON with execution result.\n\n## Notes\n\n- Use `uloop get-menu-items` to discover available menu paths\n- Some menu items may require specific context or selection\n';
@@ -6410,7 +6410,2221 @@ var BUNDLED_SKILLS = [
6410
6410
  {
6411
6411
  name: "uloop-execute-dynamic-code",
6412
6412
  dirName: "uloop-execute-dynamic-code",
6413
- content: SKILL_default5
6413
+ content: SKILL_default5,
6414
+ additionalFiles: {
6415
+ "examples/asset-operations.md": `# Asset Operations
6416
+
6417
+ Code examples for AssetDatabase operations using \`execute-dynamic-code\`.
6418
+
6419
+ ## Find Assets by Type
6420
+
6421
+ \`\`\`csharp
6422
+ using UnityEditor;
6423
+ using System.Collections.Generic;
6424
+
6425
+ string[] prefabGuids = AssetDatabase.FindAssets("t:Prefab");
6426
+ List<string> paths = new List<string>();
6427
+
6428
+ foreach (string guid in prefabGuids)
6429
+ {
6430
+ paths.Add(AssetDatabase.GUIDToAssetPath(guid));
6431
+ }
6432
+ return $"Found {paths.Count} prefabs";
6433
+ \`\`\`
6434
+
6435
+ ## Find Assets by Name
6436
+
6437
+ \`\`\`csharp
6438
+ using UnityEditor;
6439
+ using System.Collections.Generic;
6440
+
6441
+ string searchName = "Player";
6442
+ string[] guids = AssetDatabase.FindAssets(searchName);
6443
+ List<string> paths = new List<string>();
6444
+
6445
+ foreach (string guid in guids)
6446
+ {
6447
+ paths.Add(AssetDatabase.GUIDToAssetPath(guid));
6448
+ }
6449
+ return $"Found {paths.Count} assets matching '{searchName}'";
6450
+ \`\`\`
6451
+
6452
+ ## Find Assets in Folder
6453
+
6454
+ \`\`\`csharp
6455
+ using UnityEditor;
6456
+ using System.Collections.Generic;
6457
+
6458
+ string folder = "Assets/Prefabs";
6459
+ string[] guids = AssetDatabase.FindAssets("t:Prefab", new[] { folder });
6460
+ List<string> paths = new List<string>();
6461
+
6462
+ foreach (string guid in guids)
6463
+ {
6464
+ paths.Add(AssetDatabase.GUIDToAssetPath(guid));
6465
+ }
6466
+ return $"Found {paths.Count} prefabs in {folder}";
6467
+ \`\`\`
6468
+
6469
+ ## Duplicate Asset
6470
+
6471
+ \`\`\`csharp
6472
+ using UnityEditor;
6473
+
6474
+ string sourcePath = "Assets/Materials/MyMaterial.mat";
6475
+ string destPath = "Assets/Materials/MyMaterial_Backup.mat";
6476
+
6477
+ bool success = AssetDatabase.CopyAsset(sourcePath, destPath);
6478
+ return success ? $"Copied to {destPath}" : "Copy failed";
6479
+ \`\`\`
6480
+
6481
+ ## Move Asset
6482
+
6483
+ \`\`\`csharp
6484
+ using UnityEditor;
6485
+
6486
+ string sourcePath = "Assets/OldFolder/MyAsset.asset";
6487
+ string destPath = "Assets/NewFolder/MyAsset.asset";
6488
+
6489
+ string error = AssetDatabase.MoveAsset(sourcePath, destPath);
6490
+ return string.IsNullOrEmpty(error) ? $"Moved to {destPath}" : $"Error: {error}";
6491
+ \`\`\`
6492
+
6493
+ ## Rename Asset
6494
+
6495
+ \`\`\`csharp
6496
+ using UnityEditor;
6497
+
6498
+ string assetPath = "Assets/Materials/OldName.mat";
6499
+ string newName = "NewName";
6500
+
6501
+ string error = AssetDatabase.RenameAsset(assetPath, newName);
6502
+ return string.IsNullOrEmpty(error) ? $"Renamed to {newName}" : $"Error: {error}";
6503
+ \`\`\`
6504
+
6505
+ ## Rename Asset (Undo-supported)
6506
+
6507
+ \`\`\`csharp
6508
+ using UnityEditor;
6509
+
6510
+ // ObjectNames.SetNameSmart() supports Undo (AssetDatabase.RenameAsset() does NOT)
6511
+ Object selected = Selection.activeObject;
6512
+ if (selected == null)
6513
+ {
6514
+ return "No asset selected";
6515
+ }
6516
+
6517
+ string oldName = selected.name;
6518
+ ObjectNames.SetNameSmart(selected, "NewName");
6519
+ AssetDatabase.SaveAssets();
6520
+ return $"Renamed {oldName} to {selected.name}";
6521
+ \`\`\`
6522
+
6523
+ ## Get Asset Path from Object
6524
+
6525
+ \`\`\`csharp
6526
+ using UnityEditor;
6527
+
6528
+ GameObject selected = Selection.activeGameObject;
6529
+ if (selected == null)
6530
+ {
6531
+ return "No object selected";
6532
+ }
6533
+
6534
+ string path = AssetDatabase.GetAssetPath(selected);
6535
+ if (string.IsNullOrEmpty(path))
6536
+ {
6537
+ return "Selected object is not an asset (scene object)";
6538
+ }
6539
+ return $"Asset path: {path}";
6540
+ \`\`\`
6541
+
6542
+ ## Load Asset at Path
6543
+
6544
+ \`\`\`csharp
6545
+ using UnityEditor;
6546
+
6547
+ string path = "Assets/Prefabs/Player.prefab";
6548
+ GameObject asset = AssetDatabase.LoadAssetAtPath<GameObject>(path);
6549
+
6550
+ if (asset == null)
6551
+ {
6552
+ return $"Asset not found at {path}";
6553
+ }
6554
+ return $"Loaded: {asset.name}";
6555
+ \`\`\`
6556
+
6557
+ ## Get All Assets of Type
6558
+
6559
+ \`\`\`csharp
6560
+ using UnityEditor;
6561
+
6562
+ string[] scriptGuids = AssetDatabase.FindAssets("t:MonoScript");
6563
+ int count = 0;
6564
+
6565
+ foreach (string guid in scriptGuids)
6566
+ {
6567
+ string path = AssetDatabase.GUIDToAssetPath(guid);
6568
+ if (path.StartsWith("Assets/"))
6569
+ {
6570
+ count++;
6571
+ }
6572
+ }
6573
+ return $"Found {count} scripts in Assets folder";
6574
+ \`\`\`
6575
+
6576
+ ## Check if Asset Exists
6577
+
6578
+ \`\`\`csharp
6579
+ using UnityEditor;
6580
+
6581
+ string path = "Assets/Prefabs/Player.prefab";
6582
+ string guid = AssetDatabase.AssetPathToGUID(path);
6583
+
6584
+ bool exists = !string.IsNullOrEmpty(guid);
6585
+ return exists ? $"Asset exists: {path}" : $"Asset not found: {path}";
6586
+ \`\`\`
6587
+
6588
+ ## Get Asset Dependencies
6589
+
6590
+ \`\`\`csharp
6591
+ using UnityEditor;
6592
+
6593
+ string assetPath = "Assets/Prefabs/Player.prefab";
6594
+ string[] dependencies = AssetDatabase.GetDependencies(assetPath, true);
6595
+
6596
+ return $"Asset has {dependencies.Length} dependencies";
6597
+ \`\`\`
6598
+
6599
+ ## Refresh AssetDatabase
6600
+
6601
+ \`\`\`csharp
6602
+ using UnityEditor;
6603
+
6604
+ AssetDatabase.Refresh();
6605
+ return "AssetDatabase refreshed";
6606
+ \`\`\`
6607
+ `,
6608
+ "examples/batch-operations.md": `# Batch Operations
6609
+
6610
+ Code examples for batch processing using \`execute-dynamic-code\`.
6611
+
6612
+ ## Batch Modify Selected Objects
6613
+
6614
+ \`\`\`csharp
6615
+ using UnityEditor;
6616
+
6617
+ GameObject[] selected = Selection.gameObjects;
6618
+ if (selected.Length == 0)
6619
+ {
6620
+ return "No GameObjects selected";
6621
+ }
6622
+
6623
+ int undoGroup = Undo.GetCurrentGroup();
6624
+ Undo.SetCurrentGroupName("Batch Modify");
6625
+
6626
+ foreach (GameObject obj in selected)
6627
+ {
6628
+ Undo.RecordObject(obj.transform, "");
6629
+ obj.transform.localScale = Vector3.one * 2;
6630
+ }
6631
+
6632
+ Undo.CollapseUndoOperations(undoGroup);
6633
+ return $"Scaled {selected.Length} objects (Single undo step)";
6634
+ \`\`\`
6635
+
6636
+ ## Edit Multiple Objects with SerializedObject
6637
+
6638
+ \`\`\`csharp
6639
+ using UnityEditor;
6640
+
6641
+ GameObject[] selected = Selection.gameObjects;
6642
+ if (selected.Length == 0)
6643
+ {
6644
+ return "No GameObjects selected";
6645
+ }
6646
+
6647
+ List<Transform> transforms = new List<Transform>();
6648
+ foreach (GameObject obj in selected)
6649
+ {
6650
+ transforms.Add(obj.transform);
6651
+ }
6652
+
6653
+ SerializedObject serializedObj = new SerializedObject(transforms.ToArray());
6654
+ SerializedProperty positionProp = serializedObj.FindProperty("m_LocalPosition");
6655
+ positionProp.vector3Value = Vector3.zero;
6656
+ serializedObj.ApplyModifiedProperties();
6657
+
6658
+ return $"Reset position of {selected.Length} objects";
6659
+ \`\`\`
6660
+
6661
+ ## Batch Add Component
6662
+
6663
+ \`\`\`csharp
6664
+ using UnityEditor;
6665
+
6666
+ GameObject[] selected = Selection.gameObjects;
6667
+ if (selected.Length == 0)
6668
+ {
6669
+ return "No GameObjects selected";
6670
+ }
6671
+
6672
+ int undoGroup = Undo.GetCurrentGroup();
6673
+ Undo.SetCurrentGroupName("Batch Add Rigidbody");
6674
+
6675
+ int addedCount = 0;
6676
+ foreach (GameObject obj in selected)
6677
+ {
6678
+ if (obj.GetComponent<Rigidbody>() == null)
6679
+ {
6680
+ Undo.AddComponent<Rigidbody>(obj);
6681
+ addedCount++;
6682
+ }
6683
+ }
6684
+
6685
+ Undo.CollapseUndoOperations(undoGroup);
6686
+ return $"Added Rigidbody to {addedCount} objects";
6687
+ \`\`\`
6688
+
6689
+ ## Batch Process Assets with StartAssetEditing
6690
+
6691
+ \`\`\`csharp
6692
+ using UnityEditor;
6693
+
6694
+ string[] guids = AssetDatabase.FindAssets("t:Material", new[] { "Assets/Materials" });
6695
+ if (guids.Length == 0)
6696
+ {
6697
+ return "No materials found";
6698
+ }
6699
+
6700
+ AssetDatabase.StartAssetEditing();
6701
+
6702
+ int modified = 0;
6703
+ foreach (string guid in guids)
6704
+ {
6705
+ string path = AssetDatabase.GUIDToAssetPath(guid);
6706
+ Material mat = AssetDatabase.LoadAssetAtPath<Material>(path);
6707
+ if (mat != null)
6708
+ {
6709
+ mat.color = Color.white;
6710
+ EditorUtility.SetDirty(mat);
6711
+ modified++;
6712
+ }
6713
+ }
6714
+
6715
+ AssetDatabase.StopAssetEditing();
6716
+ AssetDatabase.SaveAssets();
6717
+
6718
+ return $"Reset color of {modified} materials";
6719
+ \`\`\`
6720
+
6721
+ ## Batch Rename GameObjects
6722
+
6723
+ \`\`\`csharp
6724
+ using UnityEditor;
6725
+
6726
+ GameObject[] selected = Selection.gameObjects;
6727
+ if (selected.Length == 0)
6728
+ {
6729
+ return "No GameObjects selected";
6730
+ }
6731
+
6732
+ int undoGroup = Undo.GetCurrentGroup();
6733
+ Undo.SetCurrentGroupName("Batch Rename");
6734
+
6735
+ for (int i = 0; i < selected.Length; i++)
6736
+ {
6737
+ Undo.RecordObject(selected[i], "");
6738
+ selected[i].name = $"Item_{i:D3}";
6739
+ }
6740
+
6741
+ Undo.CollapseUndoOperations(undoGroup);
6742
+ return $"Renamed {selected.Length} objects";
6743
+ \`\`\`
6744
+
6745
+ ## Batch Set Layer
6746
+
6747
+ \`\`\`csharp
6748
+ using UnityEditor;
6749
+
6750
+ GameObject[] selected = Selection.gameObjects;
6751
+ if (selected.Length == 0)
6752
+ {
6753
+ return "No GameObjects selected";
6754
+ }
6755
+
6756
+ int layer = LayerMask.NameToLayer("Default");
6757
+
6758
+ int undoGroup = Undo.GetCurrentGroup();
6759
+ Undo.SetCurrentGroupName("Batch Set Layer");
6760
+
6761
+ foreach (GameObject obj in selected)
6762
+ {
6763
+ Undo.RecordObject(obj, "");
6764
+ obj.layer = layer;
6765
+ }
6766
+
6767
+ Undo.CollapseUndoOperations(undoGroup);
6768
+ return $"Set layer of {selected.Length} objects to Default";
6769
+ \`\`\`
6770
+
6771
+ ## Batch Set Tag
6772
+
6773
+ \`\`\`csharp
6774
+ using UnityEditor;
6775
+
6776
+ GameObject[] selected = Selection.gameObjects;
6777
+ if (selected.Length == 0)
6778
+ {
6779
+ return "No GameObjects selected";
6780
+ }
6781
+
6782
+ int undoGroup = Undo.GetCurrentGroup();
6783
+ Undo.SetCurrentGroupName("Batch Set Tag");
6784
+
6785
+ foreach (GameObject obj in selected)
6786
+ {
6787
+ Undo.RecordObject(obj, "");
6788
+ obj.tag = "Enemy";
6789
+ }
6790
+
6791
+ Undo.CollapseUndoOperations(undoGroup);
6792
+ return $"Tagged {selected.Length} objects as Enemy";
6793
+ \`\`\`
6794
+
6795
+ ## Batch Modify ScriptableObjects
6796
+
6797
+ \`\`\`csharp
6798
+ using UnityEditor;
6799
+
6800
+ string[] guids = AssetDatabase.FindAssets("t:ScriptableObject", new[] { "Assets/Data" });
6801
+ if (guids.Length == 0)
6802
+ {
6803
+ return "No ScriptableObjects found";
6804
+ }
6805
+
6806
+ int modified = 0;
6807
+ foreach (string guid in guids)
6808
+ {
6809
+ string path = AssetDatabase.GUIDToAssetPath(guid);
6810
+ ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
6811
+ if (so == null) continue;
6812
+
6813
+ SerializedObject serializedObj = new SerializedObject(so);
6814
+ SerializedProperty prop = serializedObj.FindProperty("isEnabled");
6815
+ if (prop != null)
6816
+ {
6817
+ prop.boolValue = true;
6818
+ serializedObj.ApplyModifiedProperties();
6819
+ EditorUtility.SetDirty(so);
6820
+ modified++;
6821
+ }
6822
+ }
6823
+
6824
+ AssetDatabase.SaveAssets();
6825
+ return $"Enabled {modified} ScriptableObjects";
6826
+ \`\`\`
6827
+
6828
+ ## Batch Remove Component
6829
+
6830
+ \`\`\`csharp
6831
+ using UnityEditor;
6832
+
6833
+ GameObject[] selected = Selection.gameObjects;
6834
+ if (selected.Length == 0)
6835
+ {
6836
+ return "No GameObjects selected";
6837
+ }
6838
+
6839
+ int undoGroup = Undo.GetCurrentGroup();
6840
+ Undo.SetCurrentGroupName("Batch Remove Rigidbody");
6841
+
6842
+ int removedCount = 0;
6843
+ foreach (GameObject obj in selected)
6844
+ {
6845
+ Rigidbody rb = obj.GetComponent<Rigidbody>();
6846
+ if (rb != null)
6847
+ {
6848
+ Undo.DestroyObjectImmediate(rb);
6849
+ removedCount++;
6850
+ }
6851
+ }
6852
+
6853
+ Undo.CollapseUndoOperations(undoGroup);
6854
+ return $"Removed Rigidbody from {removedCount} objects";
6855
+ \`\`\`
6856
+
6857
+ ## Batch Set Static Flags
6858
+
6859
+ \`\`\`csharp
6860
+ using UnityEditor;
6861
+
6862
+ GameObject[] selected = Selection.gameObjects;
6863
+ if (selected.Length == 0)
6864
+ {
6865
+ return "No GameObjects selected";
6866
+ }
6867
+
6868
+ int undoGroup = Undo.GetCurrentGroup();
6869
+ Undo.SetCurrentGroupName("Batch Set Static");
6870
+
6871
+ foreach (GameObject obj in selected)
6872
+ {
6873
+ Undo.RecordObject(obj, "");
6874
+ GameObjectUtility.SetStaticEditorFlags(obj, StaticEditorFlags.BatchingStatic | StaticEditorFlags.OccludeeStatic);
6875
+ }
6876
+
6877
+ Undo.CollapseUndoOperations(undoGroup);
6878
+ return $"Set static flags on {selected.Length} objects";
6879
+ \`\`\`
6880
+
6881
+ ## Batch Process with Progress Bar
6882
+
6883
+ \`\`\`csharp
6884
+ using UnityEditor;
6885
+
6886
+ string[] guids = AssetDatabase.FindAssets("t:Texture2D");
6887
+ if (guids.Length == 0)
6888
+ {
6889
+ return "No textures found";
6890
+ }
6891
+
6892
+ int processed = 0;
6893
+ foreach (string guid in guids)
6894
+ {
6895
+ string path = AssetDatabase.GUIDToAssetPath(guid);
6896
+ TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter;
6897
+ if (importer != null && importer.maxTextureSize > 1024)
6898
+ {
6899
+ importer.maxTextureSize = 1024;
6900
+ importer.SaveAndReimport();
6901
+ processed++;
6902
+ }
6903
+
6904
+ if (processed % 10 == 0)
6905
+ {
6906
+ EditorUtility.DisplayProgressBar("Processing Textures", path, (float)processed / guids.Length);
6907
+ }
6908
+ }
6909
+
6910
+ EditorUtility.ClearProgressBar();
6911
+ return $"Resized {processed} textures to max 1024";
6912
+ \`\`\`
6913
+
6914
+ ## Batch Align Objects
6915
+
6916
+ \`\`\`csharp
6917
+ using UnityEditor;
6918
+
6919
+ GameObject[] selected = Selection.gameObjects;
6920
+ if (selected.Length < 2)
6921
+ {
6922
+ return "Select at least 2 objects";
6923
+ }
6924
+
6925
+ int undoGroup = Undo.GetCurrentGroup();
6926
+ Undo.SetCurrentGroupName("Align Objects");
6927
+
6928
+ float startX = selected[0].transform.position.x;
6929
+ float spacing = 2f;
6930
+
6931
+ for (int i = 0; i < selected.Length; i++)
6932
+ {
6933
+ Undo.RecordObject(selected[i].transform, "");
6934
+ Vector3 pos = selected[i].transform.position;
6935
+ pos.x = startX + (i * spacing);
6936
+ selected[i].transform.position = pos;
6937
+ }
6938
+
6939
+ Undo.CollapseUndoOperations(undoGroup);
6940
+ return $"Aligned {selected.Length} objects with {spacing}m spacing";
6941
+ \`\`\`
6942
+
6943
+ ## Batch Rename Assets (Undo-supported)
6944
+
6945
+ \`\`\`csharp
6946
+ using UnityEditor;
6947
+
6948
+ // ObjectNames.SetNameSmart() supports Undo (AssetDatabase.RenameAsset() does NOT)
6949
+ Object[] selected = Selection.objects;
6950
+ if (selected.Length == 0)
6951
+ {
6952
+ return "No assets selected";
6953
+ }
6954
+
6955
+ for (int i = 0; i < selected.Length; i++)
6956
+ {
6957
+ string newName = $"{i:D3}_{selected[i].name}";
6958
+ ObjectNames.SetNameSmart(selected[i], newName);
6959
+ }
6960
+
6961
+ AssetDatabase.SaveAssets();
6962
+ return $"Renamed {selected.Length} assets";
6963
+ \`\`\`
6964
+
6965
+ ## Batch Replace Material
6966
+
6967
+ \`\`\`csharp
6968
+ using UnityEditor;
6969
+
6970
+ GameObject[] selected = Selection.gameObjects;
6971
+ if (selected.Length == 0)
6972
+ {
6973
+ return "No GameObjects selected";
6974
+ }
6975
+
6976
+ string materialPath = "Assets/Materials/NewMaterial.mat";
6977
+ Material newMat = AssetDatabase.LoadAssetAtPath<Material>(materialPath);
6978
+ if (newMat == null)
6979
+ {
6980
+ return $"Material not found at {materialPath}";
6981
+ }
6982
+
6983
+ int undoGroup = Undo.GetCurrentGroup();
6984
+ Undo.SetCurrentGroupName("Batch Replace Material");
6985
+
6986
+ int replaced = 0;
6987
+ foreach (GameObject obj in selected)
6988
+ {
6989
+ MeshRenderer renderer = obj.GetComponent<MeshRenderer>();
6990
+ if (renderer != null)
6991
+ {
6992
+ Undo.RecordObject(renderer, "");
6993
+ renderer.sharedMaterial = newMat;
6994
+ replaced++;
6995
+ }
6996
+ }
6997
+
6998
+ Undo.CollapseUndoOperations(undoGroup);
6999
+ return $"Replaced material on {replaced} objects";
7000
+ \`\`\`
7001
+
7002
+ `,
7003
+ "examples/cleanup-operations.md": `# Cleanup Operations
7004
+
7005
+ Code examples for project cleanup operations using \`execute-dynamic-code\`.
7006
+
7007
+ ## Detect Missing Scripts on GameObject
7008
+
7009
+ \`\`\`csharp
7010
+ using UnityEditor;
7011
+
7012
+ GameObject selected = Selection.activeGameObject;
7013
+ if (selected == null)
7014
+ {
7015
+ return "No GameObject selected";
7016
+ }
7017
+
7018
+ int missingCount = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(selected);
7019
+ return $"{selected.name} has {missingCount} missing script(s)";
7020
+ \`\`\`
7021
+
7022
+ ## Remove Missing Scripts from GameObject
7023
+
7024
+ \`\`\`csharp
7025
+ using UnityEditor;
7026
+
7027
+ GameObject selected = Selection.activeGameObject;
7028
+ if (selected == null)
7029
+ {
7030
+ return "No GameObject selected";
7031
+ }
7032
+
7033
+ int removedCount = GameObjectUtility.RemoveMonoBehavioursWithMissingScript(selected);
7034
+ return $"Removed {removedCount} missing script(s) from {selected.name}";
7035
+ \`\`\`
7036
+
7037
+ ## Scan Scene for Missing Scripts
7038
+
7039
+ \`\`\`csharp
7040
+ using UnityEditor;
7041
+
7042
+ GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
7043
+ List<string> objectsWithMissing = new List<string>();
7044
+
7045
+ foreach (GameObject obj in allObjects)
7046
+ {
7047
+ int count = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(obj);
7048
+ if (count > 0)
7049
+ {
7050
+ objectsWithMissing.Add($"{obj.name} ({count})");
7051
+ }
7052
+ }
7053
+
7054
+ if (objectsWithMissing.Count == 0)
7055
+ {
7056
+ return "No missing scripts found in scene";
7057
+ }
7058
+
7059
+ return $"Objects with missing scripts: {string.Join(", ", objectsWithMissing)}";
7060
+ \`\`\`
7061
+
7062
+ ## Remove All Missing Scripts from Scene
7063
+
7064
+ \`\`\`csharp
7065
+ using UnityEditor;
7066
+
7067
+ GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
7068
+ int totalRemoved = 0;
7069
+
7070
+ int undoGroup = Undo.GetCurrentGroup();
7071
+ Undo.SetCurrentGroupName("Remove All Missing Scripts");
7072
+
7073
+ foreach (GameObject obj in allObjects)
7074
+ {
7075
+ int removed = GameObjectUtility.RemoveMonoBehavioursWithMissingScript(obj);
7076
+ totalRemoved += removed;
7077
+ }
7078
+
7079
+ Undo.CollapseUndoOperations(undoGroup);
7080
+ return $"Removed {totalRemoved} missing scripts from scene";
7081
+ \`\`\`
7082
+
7083
+ ## Detect Missing References in Component
7084
+
7085
+ \`\`\`csharp
7086
+ using UnityEditor;
7087
+
7088
+ GameObject selected = Selection.activeGameObject;
7089
+ if (selected == null)
7090
+ {
7091
+ return "No GameObject selected";
7092
+ }
7093
+
7094
+ List<string> missingRefs = new List<string>();
7095
+
7096
+ Component[] components = selected.GetComponents<Component>();
7097
+ foreach (Component comp in components)
7098
+ {
7099
+ if (comp == null) continue;
7100
+
7101
+ SerializedObject so = new SerializedObject(comp);
7102
+ SerializedProperty prop = so.GetIterator();
7103
+
7104
+ while (prop.NextVisible(true))
7105
+ {
7106
+ if (prop.propertyType == SerializedPropertyType.ObjectReference)
7107
+ {
7108
+ if (prop.objectReferenceValue == null && prop.objectReferenceInstanceIDValue != 0)
7109
+ {
7110
+ missingRefs.Add($"{comp.GetType().Name}.{prop.name}");
7111
+ }
7112
+ }
7113
+ }
7114
+ }
7115
+
7116
+ if (missingRefs.Count == 0)
7117
+ {
7118
+ return "No missing references found";
7119
+ }
7120
+
7121
+ return $"Missing references: {string.Join(", ", missingRefs)}";
7122
+ \`\`\`
7123
+
7124
+ ## Scan Scene for Missing References
7125
+
7126
+ \`\`\`csharp
7127
+ using UnityEditor;
7128
+
7129
+ GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
7130
+ List<string> results = new List<string>();
7131
+
7132
+ foreach (GameObject obj in allObjects)
7133
+ {
7134
+ Component[] components = obj.GetComponents<Component>();
7135
+ foreach (Component comp in components)
7136
+ {
7137
+ if (comp == null) continue;
7138
+
7139
+ SerializedObject so = new SerializedObject(comp);
7140
+ SerializedProperty prop = so.GetIterator();
7141
+
7142
+ while (prop.NextVisible(true))
7143
+ {
7144
+ if (prop.propertyType == SerializedPropertyType.ObjectReference)
7145
+ {
7146
+ if (prop.objectReferenceValue == null && prop.objectReferenceInstanceIDValue != 0)
7147
+ {
7148
+ results.Add($"{obj.name}/{comp.GetType().Name}.{prop.name}");
7149
+ }
7150
+ }
7151
+ }
7152
+ }
7153
+ }
7154
+
7155
+ if (results.Count == 0)
7156
+ {
7157
+ return "No missing references found in scene";
7158
+ }
7159
+
7160
+ return $"Missing references ({results.Count}): {string.Join(", ", results.Take(10))}...";
7161
+ \`\`\`
7162
+
7163
+ ## Find Unused Materials in Project
7164
+
7165
+ \`\`\`csharp
7166
+ using UnityEditor;
7167
+
7168
+ string[] materialGuids = AssetDatabase.FindAssets("t:Material");
7169
+ HashSet<string> usedMaterials = new HashSet<string>();
7170
+
7171
+ string[] prefabGuids = AssetDatabase.FindAssets("t:Prefab");
7172
+ foreach (string guid in prefabGuids)
7173
+ {
7174
+ string path = AssetDatabase.GUIDToAssetPath(guid);
7175
+ string[] deps = AssetDatabase.GetDependencies(path, true);
7176
+ foreach (string dep in deps)
7177
+ {
7178
+ if (dep.EndsWith(".mat"))
7179
+ {
7180
+ usedMaterials.Add(dep);
7181
+ }
7182
+ }
7183
+ }
7184
+
7185
+ List<string> unusedMaterials = new List<string>();
7186
+ foreach (string guid in materialGuids)
7187
+ {
7188
+ string path = AssetDatabase.GUIDToAssetPath(guid);
7189
+ if (!usedMaterials.Contains(path))
7190
+ {
7191
+ unusedMaterials.Add(path);
7192
+ }
7193
+ }
7194
+
7195
+ return $"Found {unusedMaterials.Count} potentially unused materials";
7196
+ \`\`\`
7197
+
7198
+ ## Find Empty GameObjects
7199
+
7200
+ \`\`\`csharp
7201
+ using UnityEditor;
7202
+
7203
+ GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
7204
+ List<string> emptyObjects = new List<string>();
7205
+
7206
+ foreach (GameObject obj in allObjects)
7207
+ {
7208
+ Component[] components = obj.GetComponents<Component>();
7209
+ if (components.Length == 1 && obj.transform.childCount == 0)
7210
+ {
7211
+ emptyObjects.Add(obj.name);
7212
+ }
7213
+ }
7214
+
7215
+ if (emptyObjects.Count == 0)
7216
+ {
7217
+ return "No empty GameObjects found";
7218
+ }
7219
+
7220
+ return $"Empty objects ({emptyObjects.Count}): {string.Join(", ", emptyObjects.Take(20))}";
7221
+ \`\`\`
7222
+
7223
+ ## Find Duplicate Names in Hierarchy
7224
+
7225
+ \`\`\`csharp
7226
+ using UnityEditor;
7227
+
7228
+ GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
7229
+ Dictionary<string, int> nameCounts = new Dictionary<string, int>();
7230
+
7231
+ foreach (GameObject obj in allObjects)
7232
+ {
7233
+ if (nameCounts.ContainsKey(obj.name))
7234
+ {
7235
+ nameCounts[obj.name]++;
7236
+ }
7237
+ else
7238
+ {
7239
+ nameCounts[obj.name] = 1;
7240
+ }
7241
+ }
7242
+
7243
+ List<string> duplicates = new List<string>();
7244
+ foreach (KeyValuePair<string, int> kvp in nameCounts)
7245
+ {
7246
+ if (kvp.Value > 1)
7247
+ {
7248
+ duplicates.Add($"{kvp.Key} ({kvp.Value})");
7249
+ }
7250
+ }
7251
+
7252
+ if (duplicates.Count == 0)
7253
+ {
7254
+ return "No duplicate names found";
7255
+ }
7256
+
7257
+ return $"Duplicate names: {string.Join(", ", duplicates.Take(15))}";
7258
+ \`\`\`
7259
+
7260
+ ## Check for Broken Prefab Instances
7261
+
7262
+ \`\`\`csharp
7263
+ using UnityEditor;
7264
+
7265
+ GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
7266
+ List<string> brokenPrefabs = new List<string>();
7267
+
7268
+ foreach (GameObject obj in allObjects)
7269
+ {
7270
+ if (PrefabUtility.IsPartOfPrefabInstance(obj))
7271
+ {
7272
+ GameObject prefabAsset = PrefabUtility.GetCorrespondingObjectFromSource(obj);
7273
+ if (prefabAsset == null)
7274
+ {
7275
+ brokenPrefabs.Add(obj.name);
7276
+ }
7277
+ }
7278
+ }
7279
+
7280
+ if (brokenPrefabs.Count == 0)
7281
+ {
7282
+ return "No broken prefab instances found";
7283
+ }
7284
+
7285
+ return $"Broken prefab instances: {string.Join(", ", brokenPrefabs)}";
7286
+ \`\`\`
7287
+
7288
+ ## Find Objects with Negative Scale
7289
+
7290
+ \`\`\`csharp
7291
+ using UnityEditor;
7292
+
7293
+ GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
7294
+ List<string> negativeScale = new List<string>();
7295
+
7296
+ foreach (GameObject obj in allObjects)
7297
+ {
7298
+ Vector3 scale = obj.transform.localScale;
7299
+ if (scale.x < 0 || scale.y < 0 || scale.z < 0)
7300
+ {
7301
+ negativeScale.Add($"{obj.name} ({scale})");
7302
+ }
7303
+ }
7304
+
7305
+ if (negativeScale.Count == 0)
7306
+ {
7307
+ return "No objects with negative scale found";
7308
+ }
7309
+
7310
+ return $"Negative scale objects: {string.Join(", ", negativeScale.Take(10))}";
7311
+ \`\`\`
7312
+
7313
+ ## Remove Empty Parent GameObjects
7314
+
7315
+ \`\`\`csharp
7316
+ using UnityEditor;
7317
+
7318
+ GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
7319
+
7320
+ int undoGroup = Undo.GetCurrentGroup();
7321
+ Undo.SetCurrentGroupName("Remove Empty Parents");
7322
+
7323
+ int removedCount = 0;
7324
+ foreach (GameObject obj in allObjects)
7325
+ {
7326
+ if (obj == null) continue;
7327
+
7328
+ Component[] components = obj.GetComponents<Component>();
7329
+ if (components.Length == 1 && obj.transform.childCount == 0)
7330
+ {
7331
+ Undo.DestroyObjectImmediate(obj);
7332
+ removedCount++;
7333
+ }
7334
+ }
7335
+
7336
+ Undo.CollapseUndoOperations(undoGroup);
7337
+ return $"Removed {removedCount} empty GameObjects";
7338
+ \`\`\`
7339
+
7340
+ ## Find Large Meshes
7341
+
7342
+ \`\`\`csharp
7343
+ using UnityEditor;
7344
+
7345
+ string[] meshGuids = AssetDatabase.FindAssets("t:Mesh");
7346
+ List<string> largeMeshes = new List<string>();
7347
+ int threshold = 10000;
7348
+
7349
+ foreach (string guid in meshGuids)
7350
+ {
7351
+ string path = AssetDatabase.GUIDToAssetPath(guid);
7352
+ Mesh mesh = AssetDatabase.LoadAssetAtPath<Mesh>(path);
7353
+ if (mesh != null && mesh.vertexCount > threshold)
7354
+ {
7355
+ largeMeshes.Add($"{path} ({mesh.vertexCount} verts)");
7356
+ }
7357
+ }
7358
+
7359
+ if (largeMeshes.Count == 0)
7360
+ {
7361
+ return $"No meshes with more than {threshold} vertices found";
7362
+ }
7363
+
7364
+ return $"Large meshes: {string.Join(", ", largeMeshes.Take(10))}";
7365
+ \`\`\`
7366
+
7367
+ ## Validate Asset References
7368
+
7369
+ \`\`\`csharp
7370
+ using UnityEditor;
7371
+
7372
+ string[] guids = AssetDatabase.FindAssets("t:ScriptableObject", new[] { "Assets/Data" });
7373
+ List<string> invalidRefs = new List<string>();
7374
+
7375
+ foreach (string guid in guids)
7376
+ {
7377
+ string path = AssetDatabase.GUIDToAssetPath(guid);
7378
+ ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
7379
+ if (so == null) continue;
7380
+
7381
+ SerializedObject serializedObj = new SerializedObject(so);
7382
+ SerializedProperty prop = serializedObj.GetIterator();
7383
+
7384
+ while (prop.NextVisible(true))
7385
+ {
7386
+ if (prop.propertyType == SerializedPropertyType.ObjectReference)
7387
+ {
7388
+ if (prop.objectReferenceValue == null && prop.objectReferenceInstanceIDValue != 0)
7389
+ {
7390
+ invalidRefs.Add($"{path}: {prop.name}");
7391
+ }
7392
+ }
7393
+ }
7394
+ }
7395
+
7396
+ if (invalidRefs.Count == 0)
7397
+ {
7398
+ return "All asset references are valid";
7399
+ }
7400
+
7401
+ return $"Invalid references ({invalidRefs.Count}): {string.Join(", ", invalidRefs.Take(10))}";
7402
+ \`\`\`
7403
+
7404
+ `,
7405
+ "examples/material-operations.md": `# Material Operations
7406
+
7407
+ Code examples for Material operations using \`execute-dynamic-code\`.
7408
+
7409
+ ## Create a New Material
7410
+
7411
+ \`\`\`csharp
7412
+ using UnityEditor;
7413
+
7414
+ Shader shader = Shader.Find("Standard");
7415
+ Material mat = new Material(shader);
7416
+ mat.name = "MyMaterial";
7417
+ string path = "Assets/Materials/MyMaterial.mat";
7418
+ AssetDatabase.CreateAsset(mat, path);
7419
+ AssetDatabase.SaveAssets();
7420
+ return $"Material created at {path}";
7421
+ \`\`\`
7422
+
7423
+ ## Set Material Color
7424
+
7425
+ \`\`\`csharp
7426
+ using UnityEditor;
7427
+
7428
+ string matPath = "Assets/Materials/MyMaterial.mat";
7429
+ Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
7430
+ mat.SetColor("_Color", new Color(1f, 0.5f, 0f, 1f));
7431
+ EditorUtility.SetDirty(mat);
7432
+ AssetDatabase.SaveAssets();
7433
+ return "Material color set to orange";
7434
+ \`\`\`
7435
+
7436
+ ## Set Material Properties (Float, Vector)
7437
+
7438
+ \`\`\`csharp
7439
+ using UnityEditor;
7440
+
7441
+ string matPath = "Assets/Materials/MyMaterial.mat";
7442
+ Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
7443
+
7444
+ mat.SetFloat("_Metallic", 0.8f);
7445
+ mat.SetFloat("_Glossiness", 0.6f);
7446
+ mat.SetVector("_EmissionColor", new Vector4(1, 1, 0, 1));
7447
+
7448
+ EditorUtility.SetDirty(mat);
7449
+ AssetDatabase.SaveAssets();
7450
+ return "Material properties updated";
7451
+ \`\`\`
7452
+
7453
+ ## Assign Texture to Material
7454
+
7455
+ \`\`\`csharp
7456
+ using UnityEditor;
7457
+
7458
+ string matPath = "Assets/Materials/MyMaterial.mat";
7459
+ string texPath = "Assets/Textures/MyTexture.png";
7460
+
7461
+ Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
7462
+ Texture2D tex = AssetDatabase.LoadAssetAtPath<Texture2D>(texPath);
7463
+
7464
+ mat.SetTexture("_MainTex", tex);
7465
+ EditorUtility.SetDirty(mat);
7466
+ AssetDatabase.SaveAssets();
7467
+ return $"Assigned {tex.name} to material";
7468
+ \`\`\`
7469
+
7470
+ ## Assign Material to GameObject
7471
+
7472
+ \`\`\`csharp
7473
+ using UnityEditor;
7474
+
7475
+ string matPath = "Assets/Materials/MyMaterial.mat";
7476
+ Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
7477
+
7478
+ GameObject selected = Selection.activeGameObject;
7479
+ if (selected == null)
7480
+ {
7481
+ return "No GameObject selected";
7482
+ }
7483
+
7484
+ Renderer renderer = selected.GetComponent<Renderer>();
7485
+ if (renderer == null)
7486
+ {
7487
+ return "Selected object has no Renderer";
7488
+ }
7489
+
7490
+ renderer.sharedMaterial = mat;
7491
+ EditorUtility.SetDirty(selected);
7492
+ return $"Assigned {mat.name} to {selected.name}";
7493
+ \`\`\`
7494
+
7495
+ ## Enable/Disable Material Keywords
7496
+
7497
+ \`\`\`csharp
7498
+ using UnityEditor;
7499
+
7500
+ string matPath = "Assets/Materials/MyMaterial.mat";
7501
+ Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
7502
+
7503
+ mat.EnableKeyword("_EMISSION");
7504
+ mat.globalIlluminationFlags = MaterialGlobalIlluminationFlags.RealtimeEmissive;
7505
+
7506
+ EditorUtility.SetDirty(mat);
7507
+ AssetDatabase.SaveAssets();
7508
+ return "Emission enabled on material";
7509
+ \`\`\`
7510
+
7511
+ ## Find All Materials Using a Shader
7512
+
7513
+ \`\`\`csharp
7514
+ using UnityEditor;
7515
+
7516
+ string shaderName = "Standard";
7517
+ string[] guids = AssetDatabase.FindAssets("t:Material");
7518
+ List<string> matchingMaterials = new List<string>();
7519
+
7520
+ foreach (string guid in guids)
7521
+ {
7522
+ string path = AssetDatabase.GUIDToAssetPath(guid);
7523
+ Material mat = AssetDatabase.LoadAssetAtPath<Material>(path);
7524
+ if (mat != null && mat.shader != null && mat.shader.name == shaderName)
7525
+ {
7526
+ matchingMaterials.Add(path);
7527
+ }
7528
+ }
7529
+ return $"Found {matchingMaterials.Count} materials using {shaderName}";
7530
+ \`\`\`
7531
+
7532
+ ## Duplicate Material
7533
+
7534
+ \`\`\`csharp
7535
+ using UnityEditor;
7536
+
7537
+ string sourcePath = "Assets/Materials/MyMaterial.mat";
7538
+ string destPath = "Assets/Materials/MyMaterial_Copy.mat";
7539
+
7540
+ Material source = AssetDatabase.LoadAssetAtPath<Material>(sourcePath);
7541
+ Material copy = new Material(source);
7542
+ AssetDatabase.CreateAsset(copy, destPath);
7543
+ AssetDatabase.SaveAssets();
7544
+ return $"Material duplicated to {destPath}";
7545
+ \`\`\`
7546
+ `,
7547
+ "examples/prefab-operations.md": `# Prefab Operations
7548
+
7549
+ Code examples for Prefab operations using \`execute-dynamic-code\`.
7550
+
7551
+ ## Create a Prefab from GameObject
7552
+
7553
+ \`\`\`csharp
7554
+ using UnityEditor;
7555
+
7556
+ GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
7557
+ cube.name = "MyCube";
7558
+ string path = "Assets/Prefabs/MyCube.prefab";
7559
+ PrefabUtility.SaveAsPrefabAsset(cube, path);
7560
+ Object.DestroyImmediate(cube);
7561
+ return $"Prefab created at {path}";
7562
+ \`\`\`
7563
+
7564
+ ## Instantiate a Prefab
7565
+
7566
+ \`\`\`csharp
7567
+ using UnityEditor;
7568
+
7569
+ string prefabPath = "Assets/Prefabs/MyCube.prefab";
7570
+ GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
7571
+ if (prefab == null)
7572
+ {
7573
+ return $"Prefab not found at {prefabPath}";
7574
+ }
7575
+
7576
+ GameObject instance = (GameObject)PrefabUtility.InstantiatePrefab(prefab);
7577
+ instance.transform.position = new Vector3(0, 1, 0);
7578
+ return $"Instantiated {instance.name}";
7579
+ \`\`\`
7580
+
7581
+ ## Add Component to Prefab
7582
+
7583
+ \`\`\`csharp
7584
+ using UnityEditor;
7585
+
7586
+ string prefabPath = "Assets/Prefabs/MyCube.prefab";
7587
+ GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
7588
+ string assetPath = AssetDatabase.GetAssetPath(prefab);
7589
+
7590
+ using (PrefabUtility.EditPrefabContentsScope scope = new PrefabUtility.EditPrefabContentsScope(assetPath))
7591
+ {
7592
+ GameObject root = scope.prefabContentsRoot;
7593
+ if (root.GetComponent<Rigidbody>() == null)
7594
+ {
7595
+ root.AddComponent<Rigidbody>();
7596
+ }
7597
+ }
7598
+ return "Added Rigidbody to prefab";
7599
+ \`\`\`
7600
+
7601
+ ## Modify Prefab Properties
7602
+
7603
+ \`\`\`csharp
7604
+ using UnityEditor;
7605
+
7606
+ string prefabPath = "Assets/Prefabs/MyCube.prefab";
7607
+ GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
7608
+
7609
+ using (PrefabUtility.EditPrefabContentsScope scope = new PrefabUtility.EditPrefabContentsScope(prefabPath))
7610
+ {
7611
+ GameObject root = scope.prefabContentsRoot;
7612
+ root.transform.localScale = new Vector3(2, 2, 2);
7613
+
7614
+ MeshRenderer renderer = root.GetComponent<MeshRenderer>();
7615
+ if (renderer != null)
7616
+ {
7617
+ renderer.sharedMaterial.color = Color.red;
7618
+ }
7619
+ }
7620
+ return "Modified prefab properties";
7621
+ \`\`\`
7622
+
7623
+ ## Find All Prefab Instances in Scene
7624
+
7625
+ \`\`\`csharp
7626
+ using UnityEditor;
7627
+ using System.Collections.Generic;
7628
+
7629
+ string prefabPath = "Assets/Prefabs/MyCube.prefab";
7630
+ GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
7631
+ if (prefab == null)
7632
+ {
7633
+ return $"Prefab not found at {prefabPath}";
7634
+ }
7635
+
7636
+ List<GameObject> instances = new List<GameObject>();
7637
+
7638
+ foreach (GameObject obj in Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None))
7639
+ {
7640
+ if (PrefabUtility.GetCorrespondingObjectFromSource(obj) == prefab)
7641
+ {
7642
+ instances.Add(obj);
7643
+ }
7644
+ }
7645
+ return $"Found {instances.Count} instances of {prefab.name}";
7646
+ \`\`\`
7647
+
7648
+ ## Apply Prefab Overrides
7649
+
7650
+ \`\`\`csharp
7651
+ using UnityEditor;
7652
+
7653
+ GameObject selected = Selection.activeGameObject;
7654
+ if (selected == null)
7655
+ {
7656
+ return "No GameObject selected";
7657
+ }
7658
+
7659
+ if (!PrefabUtility.IsPartOfPrefabInstance(selected))
7660
+ {
7661
+ return "Selected object is not a prefab instance";
7662
+ }
7663
+
7664
+ PrefabUtility.ApplyPrefabInstance(selected, InteractionMode.UserAction);
7665
+ return $"Applied overrides from {selected.name} to prefab";
7666
+ \`\`\`
7667
+ `,
7668
+ "examples/scene-operations.md": `# Scene Operations
7669
+
7670
+ Code examples for Scene and Hierarchy operations using \`execute-dynamic-code\`.
7671
+
7672
+ ## Create GameObject
7673
+
7674
+ \`\`\`csharp
7675
+ GameObject obj = new GameObject("MyObject");
7676
+ obj.transform.position = new Vector3(0, 1, 0);
7677
+ return $"Created {obj.name}";
7678
+ \`\`\`
7679
+
7680
+ ## Create UI GameObject (under Canvas)
7681
+
7682
+ \`\`\`csharp
7683
+ using UnityEngine.UI;
7684
+
7685
+ // UI objects require RectTransform, which is auto-added when parented to Canvas
7686
+ GameObject canvas = GameObject.Find("Canvas");
7687
+ if (canvas == null)
7688
+ {
7689
+ return "Canvas not found in scene";
7690
+ }
7691
+
7692
+ GameObject uiObj = new GameObject("MyUIElement");
7693
+ uiObj.transform.SetParent(canvas.transform, false);
7694
+ uiObj.AddComponent<RectTransform>();
7695
+ uiObj.AddComponent<Image>();
7696
+ return $"Created UI element: {uiObj.name}";
7697
+ \`\`\`
7698
+
7699
+ ## Create Primitive
7700
+
7701
+ \`\`\`csharp
7702
+ GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
7703
+ cube.name = "MyCube";
7704
+ cube.transform.position = new Vector3(2, 0, 0);
7705
+ return $"Created {cube.name}";
7706
+ \`\`\`
7707
+
7708
+ ## Add Component to GameObject
7709
+
7710
+ \`\`\`csharp
7711
+ GameObject selected = Selection.activeGameObject;
7712
+ if (selected == null)
7713
+ {
7714
+ return "No GameObject selected";
7715
+ }
7716
+
7717
+ Rigidbody rb = selected.AddComponent<Rigidbody>();
7718
+ rb.mass = 2f;
7719
+ rb.useGravity = true;
7720
+ return $"Added Rigidbody to {selected.name}";
7721
+ \`\`\`
7722
+
7723
+ ## Find GameObject by Name
7724
+
7725
+ \`\`\`csharp
7726
+ GameObject obj = GameObject.Find("Player");
7727
+ if (obj == null)
7728
+ {
7729
+ return "GameObject 'Player' not found";
7730
+ }
7731
+ return $"Found: {obj.name} at {obj.transform.position}";
7732
+ \`\`\`
7733
+
7734
+ ## Find GameObjects by Tag
7735
+
7736
+ \`\`\`csharp
7737
+ GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");
7738
+ return $"Found {enemies.Length} GameObjects with tag 'Enemy'";
7739
+ \`\`\`
7740
+
7741
+ ## Set Parent
7742
+
7743
+ \`\`\`csharp
7744
+ GameObject child = GameObject.Find("Child");
7745
+ GameObject parent = GameObject.Find("Parent");
7746
+
7747
+ if (child == null || parent == null)
7748
+ {
7749
+ return "Child or Parent not found";
7750
+ }
7751
+
7752
+ child.transform.SetParent(parent.transform);
7753
+ return $"Set {child.name}'s parent to {parent.name}";
7754
+ \`\`\`
7755
+
7756
+ ## Get All Children
7757
+
7758
+ \`\`\`csharp
7759
+ GameObject parent = Selection.activeGameObject;
7760
+ if (parent == null)
7761
+ {
7762
+ return "No GameObject selected";
7763
+ }
7764
+
7765
+ List<string> children = new List<string>();
7766
+ foreach (Transform child in parent.transform)
7767
+ {
7768
+ children.Add(child.name);
7769
+ }
7770
+ return $"Children: {string.Join(", ", children)}";
7771
+ \`\`\`
7772
+
7773
+ ## Wire Component References
7774
+
7775
+ \`\`\`csharp
7776
+ using UnityEditor;
7777
+
7778
+ GameObject player = GameObject.Find("Player");
7779
+ GameObject target = GameObject.Find("Target");
7780
+
7781
+ if (player == null || target == null)
7782
+ {
7783
+ return "Player or Target not found";
7784
+ }
7785
+
7786
+ MonoBehaviour script = player.GetComponent("PlayerController") as MonoBehaviour;
7787
+ if (script == null)
7788
+ {
7789
+ return "PlayerController not found on Player";
7790
+ }
7791
+
7792
+ SerializedObject serializedScript = new SerializedObject(script);
7793
+ SerializedProperty targetProp = serializedScript.FindProperty("target");
7794
+
7795
+ if (targetProp != null)
7796
+ {
7797
+ targetProp.objectReferenceValue = target.transform;
7798
+ serializedScript.ApplyModifiedProperties();
7799
+ return "Target reference wired";
7800
+ }
7801
+ return "Property 'target' not found";
7802
+ \`\`\`
7803
+
7804
+ ## Load Scene (Editor)
7805
+
7806
+ \`\`\`csharp
7807
+ using UnityEditor.SceneManagement;
7808
+
7809
+ string scenePath = "Assets/Scenes/MainMenu.unity";
7810
+ EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single);
7811
+ return $"Loaded scene: {scenePath}";
7812
+ \`\`\`
7813
+
7814
+ ## Save Current Scene
7815
+
7816
+ \`\`\`csharp
7817
+ using UnityEditor.SceneManagement;
7818
+
7819
+ UnityEngine.SceneManagement.Scene scene = EditorSceneManager.GetActiveScene();
7820
+ EditorSceneManager.SaveScene(scene);
7821
+ return $"Saved scene: {scene.name}";
7822
+ \`\`\`
7823
+
7824
+ ## Create New Scene
7825
+
7826
+ \`\`\`csharp
7827
+ using UnityEditor.SceneManagement;
7828
+
7829
+ UnityEngine.SceneManagement.Scene newScene = EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects, NewSceneMode.Single);
7830
+ return $"Created new scene: {newScene.name}";
7831
+ \`\`\`
7832
+
7833
+ ## Get All Root GameObjects in Scene
7834
+
7835
+ \`\`\`csharp
7836
+ UnityEngine.SceneManagement.Scene scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
7837
+ GameObject[] roots = scene.GetRootGameObjects();
7838
+
7839
+ List<string> names = new List<string>();
7840
+ foreach (GameObject root in roots)
7841
+ {
7842
+ names.Add(root.name);
7843
+ }
7844
+ return $"Root objects: {string.Join(", ", names)}";
7845
+ \`\`\`
7846
+
7847
+ ## Destroy GameObject
7848
+
7849
+ \`\`\`csharp
7850
+ GameObject obj = GameObject.Find("OldObject");
7851
+ if (obj == null)
7852
+ {
7853
+ return "GameObject not found";
7854
+ }
7855
+
7856
+ Object.DestroyImmediate(obj);
7857
+ return "GameObject destroyed";
7858
+ \`\`\`
7859
+
7860
+ ## Duplicate GameObject
7861
+
7862
+ \`\`\`csharp
7863
+ GameObject selected = Selection.activeGameObject;
7864
+ if (selected == null)
7865
+ {
7866
+ return "No GameObject selected";
7867
+ }
7868
+
7869
+ GameObject copy = Object.Instantiate(selected);
7870
+ copy.name = selected.name + "_Copy";
7871
+ copy.transform.position = selected.transform.position + Vector3.right * 2;
7872
+ return $"Created duplicate: {copy.name}";
7873
+ \`\`\`
7874
+
7875
+ ## Set Active/Inactive
7876
+
7877
+ \`\`\`csharp
7878
+ GameObject obj = GameObject.Find("MyObject");
7879
+ if (obj == null)
7880
+ {
7881
+ return "GameObject not found";
7882
+ }
7883
+
7884
+ obj.SetActive(!obj.activeSelf);
7885
+ return $"{obj.name} is now {(obj.activeSelf ? "active" : "inactive")}";
7886
+ \`\`\`
7887
+
7888
+ ## Modify Transform
7889
+
7890
+ \`\`\`csharp
7891
+ GameObject selected = Selection.activeGameObject;
7892
+ if (selected == null)
7893
+ {
7894
+ return "No GameObject selected";
7895
+ }
7896
+
7897
+ selected.transform.position = new Vector3(0, 5, 0);
7898
+ selected.transform.rotation = Quaternion.Euler(0, 45, 0);
7899
+ selected.transform.localScale = new Vector3(2, 2, 2);
7900
+ return "Transform modified";
7901
+ \`\`\`
7902
+ `,
7903
+ "examples/scriptableobject.md": `# ScriptableObject Operations
7904
+
7905
+ Code examples for ScriptableObject operations using \`execute-dynamic-code\`.
7906
+
7907
+ ## Create ScriptableObject Instance
7908
+
7909
+ \`\`\`csharp
7910
+ using UnityEditor;
7911
+
7912
+ ScriptableObject so = ScriptableObject.CreateInstance<ScriptableObject>();
7913
+ string path = "Assets/Data/MyData.asset";
7914
+ AssetDatabase.CreateAsset(so, path);
7915
+ AssetDatabase.SaveAssets();
7916
+ return $"ScriptableObject created at {path}";
7917
+ \`\`\`
7918
+
7919
+ ## Create Custom ScriptableObject
7920
+
7921
+ \`\`\`csharp
7922
+ using UnityEditor;
7923
+
7924
+ ScriptableObject so = ScriptableObject.CreateInstance("MyCustomSO");
7925
+ if (so == null)
7926
+ {
7927
+ return "Type 'MyCustomSO' not found. Ensure the class exists.";
7928
+ }
7929
+
7930
+ string path = "Assets/Data/MyCustomData.asset";
7931
+ AssetDatabase.CreateAsset(so, path);
7932
+ AssetDatabase.SaveAssets();
7933
+ return $"Created {so.GetType().Name} at {path}";
7934
+ \`\`\`
7935
+
7936
+ ## Modify ScriptableObject with SerializedObject
7937
+
7938
+ \`\`\`csharp
7939
+ using UnityEditor;
7940
+
7941
+ string path = "Assets/Data/MyData.asset";
7942
+ ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
7943
+
7944
+ if (so == null)
7945
+ {
7946
+ return $"Asset not found at {path}";
7947
+ }
7948
+
7949
+ SerializedObject serializedObj = new SerializedObject(so);
7950
+ SerializedProperty prop = serializedObj.FindProperty("myField");
7951
+
7952
+ if (prop != null)
7953
+ {
7954
+ prop.stringValue = "New Value";
7955
+ serializedObj.ApplyModifiedProperties();
7956
+ EditorUtility.SetDirty(so);
7957
+ AssetDatabase.SaveAssets();
7958
+ return "Property updated";
7959
+ }
7960
+ return "Property 'myField' not found";
7961
+ \`\`\`
7962
+
7963
+ ## Set Int/Float/Bool Properties
7964
+
7965
+ \`\`\`csharp
7966
+ using UnityEditor;
7967
+
7968
+ string path = "Assets/Data/GameSettings.asset";
7969
+ ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
7970
+
7971
+ SerializedObject serializedObj = new SerializedObject(so);
7972
+
7973
+ SerializedProperty intProp = serializedObj.FindProperty("maxHealth");
7974
+ if (intProp != null) intProp.intValue = 100;
7975
+
7976
+ SerializedProperty floatProp = serializedObj.FindProperty("moveSpeed");
7977
+ if (floatProp != null) floatProp.floatValue = 5.5f;
7978
+
7979
+ SerializedProperty boolProp = serializedObj.FindProperty("isEnabled");
7980
+ if (boolProp != null) boolProp.boolValue = true;
7981
+
7982
+ serializedObj.ApplyModifiedProperties();
7983
+ EditorUtility.SetDirty(so);
7984
+ AssetDatabase.SaveAssets();
7985
+ return "Properties updated";
7986
+ \`\`\`
7987
+
7988
+ ## Set Reference Properties
7989
+
7990
+ \`\`\`csharp
7991
+ using UnityEditor;
7992
+
7993
+ string soPath = "Assets/Data/CharacterData.asset";
7994
+ string prefabPath = "Assets/Prefabs/Player.prefab";
7995
+
7996
+ ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(soPath);
7997
+ GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
7998
+
7999
+ if (so == null)
8000
+ {
8001
+ return $"ScriptableObject not found at {soPath}";
8002
+ }
8003
+ if (prefab == null)
8004
+ {
8005
+ return $"Prefab not found at {prefabPath}";
8006
+ }
8007
+
8008
+ SerializedObject serializedObj = new SerializedObject(so);
8009
+ SerializedProperty prop = serializedObj.FindProperty("playerPrefab");
8010
+
8011
+ if (prop != null)
8012
+ {
8013
+ prop.objectReferenceValue = prefab;
8014
+ serializedObj.ApplyModifiedProperties();
8015
+ EditorUtility.SetDirty(so);
8016
+ AssetDatabase.SaveAssets();
8017
+ return "Reference set successfully";
8018
+ }
8019
+ return "Property not found";
8020
+ \`\`\`
8021
+
8022
+ ## Set Array/List Properties
8023
+
8024
+ \`\`\`csharp
8025
+ using UnityEditor;
8026
+
8027
+ string path = "Assets/Data/ItemDatabase.asset";
8028
+ ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
8029
+
8030
+ if (so == null)
8031
+ {
8032
+ return $"Asset not found at {path}";
8033
+ }
8034
+
8035
+ SerializedObject serializedObj = new SerializedObject(so);
8036
+ SerializedProperty arrayProp = serializedObj.FindProperty("items");
8037
+
8038
+ if (arrayProp != null && arrayProp.isArray)
8039
+ {
8040
+ arrayProp.ClearArray();
8041
+ arrayProp.InsertArrayElementAtIndex(0);
8042
+ arrayProp.GetArrayElementAtIndex(0).stringValue = "Sword";
8043
+ arrayProp.InsertArrayElementAtIndex(1);
8044
+ arrayProp.GetArrayElementAtIndex(1).stringValue = "Shield";
8045
+
8046
+ serializedObj.ApplyModifiedProperties();
8047
+ EditorUtility.SetDirty(so);
8048
+ AssetDatabase.SaveAssets();
8049
+ return "Array updated with 2 items";
8050
+ }
8051
+ return "Array property not found";
8052
+ \`\`\`
8053
+
8054
+ ## Find All ScriptableObjects of Type
8055
+
8056
+ \`\`\`csharp
8057
+ using UnityEditor;
8058
+ using System.Collections.Generic;
8059
+
8060
+ string typeName = "GameSettings";
8061
+ string[] guids = AssetDatabase.FindAssets($"t:{typeName}");
8062
+ List<string> paths = new List<string>();
8063
+
8064
+ foreach (string guid in guids)
8065
+ {
8066
+ paths.Add(AssetDatabase.GUIDToAssetPath(guid));
8067
+ }
8068
+ return $"Found {paths.Count} {typeName} assets";
8069
+ \`\`\`
8070
+
8071
+ ## Duplicate ScriptableObject
8072
+
8073
+ \`\`\`csharp
8074
+ using UnityEditor;
8075
+
8076
+ string sourcePath = "Assets/Data/Template.asset";
8077
+ string destPath = "Assets/Data/NewInstance.asset";
8078
+
8079
+ ScriptableObject source = AssetDatabase.LoadAssetAtPath<ScriptableObject>(sourcePath);
8080
+ if (source == null)
8081
+ {
8082
+ return $"Source asset not found at {sourcePath}";
8083
+ }
8084
+
8085
+ ScriptableObject copy = Object.Instantiate(source);
8086
+ AssetDatabase.CreateAsset(copy, destPath);
8087
+ AssetDatabase.SaveAssets();
8088
+ return $"Duplicated to {destPath}";
8089
+ \`\`\`
8090
+
8091
+ ## List All Properties of ScriptableObject
8092
+
8093
+ \`\`\`csharp
8094
+ using UnityEditor;
8095
+ using System.Collections.Generic;
8096
+
8097
+ string path = "Assets/Data/MyData.asset";
8098
+ ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
8099
+
8100
+ if (so == null)
8101
+ {
8102
+ return $"Asset not found at {path}";
8103
+ }
8104
+
8105
+ SerializedObject serializedObj = new SerializedObject(so);
8106
+ SerializedProperty prop = serializedObj.GetIterator();
8107
+
8108
+ List<string> properties = new List<string>();
8109
+ while (prop.NextVisible(true))
8110
+ {
8111
+ properties.Add($"{prop.name} ({prop.propertyType})");
8112
+ }
8113
+ return string.Join(", ", properties);
8114
+ \`\`\`
8115
+ `,
8116
+ "examples/selection-operations.md": `# Selection Operations
8117
+
8118
+ Code examples for Selection operations using \`execute-dynamic-code\`.
8119
+
8120
+ ## Get Selected GameObjects
8121
+
8122
+ \`\`\`csharp
8123
+ using UnityEditor;
8124
+ using System.Collections.Generic;
8125
+
8126
+ GameObject[] selected = Selection.gameObjects;
8127
+ if (selected.Length == 0)
8128
+ {
8129
+ return "No GameObjects selected";
8130
+ }
8131
+
8132
+ List<string> names = new List<string>();
8133
+ foreach (GameObject obj in selected)
8134
+ {
8135
+ names.Add(obj.name);
8136
+ }
8137
+ return $"Selected: {string.Join(", ", names)}";
8138
+ \`\`\`
8139
+
8140
+ ## Get Active (Last Selected) GameObject
8141
+
8142
+ \`\`\`csharp
8143
+ using UnityEditor;
8144
+
8145
+ GameObject active = Selection.activeGameObject;
8146
+ if (active == null)
8147
+ {
8148
+ return "No active GameObject";
8149
+ }
8150
+ return $"Active: {active.name}";
8151
+ \`\`\`
8152
+
8153
+ ## Set Selection Programmatically
8154
+
8155
+ \`\`\`csharp
8156
+ using UnityEditor;
8157
+
8158
+ GameObject obj = GameObject.Find("Player");
8159
+ if (obj == null)
8160
+ {
8161
+ return "GameObject 'Player' not found";
8162
+ }
8163
+
8164
+ Selection.activeGameObject = obj;
8165
+ return $"Selected {obj.name}";
8166
+ \`\`\`
8167
+
8168
+ ## Select Multiple GameObjects
8169
+
8170
+ \`\`\`csharp
8171
+ using UnityEditor;
8172
+
8173
+ GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");
8174
+ if (enemies.Length == 0)
8175
+ {
8176
+ return "No enemies found";
8177
+ }
8178
+
8179
+ Selection.objects = enemies;
8180
+ return $"Selected {enemies.Length} enemies";
8181
+ \`\`\`
8182
+
8183
+ ## Get Top-Level Transforms Only
8184
+
8185
+ \`\`\`csharp
8186
+ using UnityEditor;
8187
+ using System.Collections.Generic;
8188
+
8189
+ Transform[] transforms = Selection.GetTransforms(SelectionMode.TopLevel);
8190
+ if (transforms.Length == 0)
8191
+ {
8192
+ return "No transforms selected";
8193
+ }
8194
+
8195
+ List<string> names = new List<string>();
8196
+ foreach (Transform t in transforms)
8197
+ {
8198
+ names.Add(t.name);
8199
+ }
8200
+ return $"Top-level: {string.Join(", ", names)}";
8201
+ \`\`\`
8202
+
8203
+ ## Get Deep Selection (Including Children)
8204
+
8205
+ \`\`\`csharp
8206
+ using UnityEditor;
8207
+
8208
+ Transform[] transforms = Selection.GetTransforms(SelectionMode.Deep);
8209
+ if (transforms.Length == 0)
8210
+ {
8211
+ return "No transforms selected";
8212
+ }
8213
+
8214
+ return $"Deep selection count: {transforms.Length}";
8215
+ \`\`\`
8216
+
8217
+ ## Get Editable Objects Only
8218
+
8219
+ \`\`\`csharp
8220
+ using UnityEditor;
8221
+ using System.Collections.Generic;
8222
+
8223
+ Transform[] transforms = Selection.GetTransforms(SelectionMode.Editable);
8224
+ if (transforms.Length == 0)
8225
+ {
8226
+ return "No editable transforms selected";
8227
+ }
8228
+
8229
+ List<string> names = new List<string>();
8230
+ foreach (Transform t in transforms)
8231
+ {
8232
+ names.Add(t.name);
8233
+ }
8234
+ return $"Editable: {string.Join(", ", names)}";
8235
+ \`\`\`
8236
+
8237
+ ## Get Selected Assets
8238
+
8239
+ \`\`\`csharp
8240
+ using UnityEditor;
8241
+ using System.Collections.Generic;
8242
+
8243
+ Object[] selectedAssets = Selection.GetFiltered<Object>(SelectionMode.Assets);
8244
+ if (selectedAssets.Length == 0)
8245
+ {
8246
+ return "No assets selected";
8247
+ }
8248
+
8249
+ List<string> paths = new List<string>();
8250
+ foreach (Object asset in selectedAssets)
8251
+ {
8252
+ paths.Add(AssetDatabase.GetAssetPath(asset));
8253
+ }
8254
+ return $"Assets: {string.Join(", ", paths)}";
8255
+ \`\`\`
8256
+
8257
+ ## Get Selected Asset GUIDs
8258
+
8259
+ \`\`\`csharp
8260
+ using UnityEditor;
8261
+ using System.Collections.Generic;
8262
+
8263
+ string[] guids = Selection.assetGUIDs;
8264
+ if (guids.Length == 0)
8265
+ {
8266
+ return "No assets selected";
8267
+ }
8268
+
8269
+ List<string> paths = new List<string>();
8270
+ foreach (string guid in guids)
8271
+ {
8272
+ paths.Add(AssetDatabase.GUIDToAssetPath(guid));
8273
+ }
8274
+ return $"Selected assets: {string.Join(", ", paths)}";
8275
+ \`\`\`
8276
+
8277
+ ## Select All Children of Selected Object
8278
+
8279
+ \`\`\`csharp
8280
+ using UnityEditor;
8281
+ using UnityEngine;
8282
+ using System.Collections.Generic;
8283
+
8284
+ GameObject parent = Selection.activeGameObject;
8285
+ if (parent == null)
8286
+ {
8287
+ return "No GameObject selected";
8288
+ }
8289
+
8290
+ List<GameObject> children = new List<GameObject>();
8291
+ foreach (Transform child in parent.GetComponentsInChildren<Transform>())
8292
+ {
8293
+ if (child != parent.transform)
8294
+ {
8295
+ children.Add(child.gameObject);
8296
+ }
8297
+ }
8298
+
8299
+ if (children.Count == 0)
8300
+ {
8301
+ return "No children found";
8302
+ }
8303
+
8304
+ Selection.objects = children.ToArray();
8305
+ return $"Selected {children.Count} children";
8306
+ \`\`\`
8307
+
8308
+ ## Filter Selection by Component
8309
+
8310
+ \`\`\`csharp
8311
+ using UnityEditor;
8312
+ using System.Collections.Generic;
8313
+
8314
+ GameObject[] selected = Selection.gameObjects;
8315
+ List<GameObject> withRigidbody = new List<GameObject>();
8316
+
8317
+ foreach (GameObject obj in selected)
8318
+ {
8319
+ if (obj.GetComponent<Rigidbody>() != null)
8320
+ {
8321
+ withRigidbody.Add(obj);
8322
+ }
8323
+ }
8324
+
8325
+ if (withRigidbody.Count == 0)
8326
+ {
8327
+ return "No objects with Rigidbody in selection";
8328
+ }
8329
+
8330
+ Selection.objects = withRigidbody.ToArray();
8331
+ return $"Filtered to {withRigidbody.Count} objects with Rigidbody";
8332
+ \`\`\`
8333
+
8334
+ ## Check if Object is Selected
8335
+
8336
+ \`\`\`csharp
8337
+ using UnityEditor;
8338
+
8339
+ GameObject player = GameObject.Find("Player");
8340
+ if (player == null)
8341
+ {
8342
+ return "Player not found";
8343
+ }
8344
+
8345
+ bool isSelected = Selection.Contains(player);
8346
+ return $"Player is {(isSelected ? "" : "not ")}selected";
8347
+ \`\`\`
8348
+
8349
+ ## Clear Selection
8350
+
8351
+ \`\`\`csharp
8352
+ using UnityEditor;
8353
+
8354
+ Selection.activeObject = null;
8355
+ return "Selection cleared";
8356
+ \`\`\`
8357
+
8358
+ ## Select Objects by Layer
8359
+
8360
+ \`\`\`csharp
8361
+ using UnityEditor;
8362
+ using System.Collections.Generic;
8363
+
8364
+ int layer = LayerMask.NameToLayer("UI");
8365
+ GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
8366
+ List<GameObject> layerObjects = new List<GameObject>();
8367
+
8368
+ foreach (GameObject obj in allObjects)
8369
+ {
8370
+ if (obj.layer == layer)
8371
+ {
8372
+ layerObjects.Add(obj);
8373
+ }
8374
+ }
8375
+
8376
+ if (layerObjects.Count == 0)
8377
+ {
8378
+ return "No objects found on UI layer";
8379
+ }
8380
+
8381
+ Selection.objects = layerObjects.ToArray();
8382
+ return $"Selected {layerObjects.Count} objects on UI layer";
8383
+ \`\`\`
8384
+
8385
+ ## Ping Object in Hierarchy/Project
8386
+
8387
+ \`\`\`csharp
8388
+ using UnityEditor;
8389
+
8390
+ GameObject obj = GameObject.Find("Player");
8391
+ if (obj == null)
8392
+ {
8393
+ return "Player not found";
8394
+ }
8395
+
8396
+ EditorGUIUtility.PingObject(obj);
8397
+ return $"Pinged {obj.name} in Hierarchy";
8398
+ \`\`\`
8399
+
8400
+ ## Focus on Selected Object in Scene View
8401
+
8402
+ \`\`\`csharp
8403
+ using UnityEditor;
8404
+
8405
+ if (Selection.activeGameObject == null)
8406
+ {
8407
+ return "No GameObject selected";
8408
+ }
8409
+
8410
+ SceneView.FrameLastActiveSceneView();
8411
+ return "Focused on selected object";
8412
+ \`\`\`
8413
+
8414
+ `,
8415
+ "examples/undo-operations.md": `# Undo Operations
8416
+
8417
+ Code examples for Undo-supported operations using \`execute-dynamic-code\`.
8418
+
8419
+ ## Record Property Change (Undo.RecordObject)
8420
+
8421
+ \`\`\`csharp
8422
+ using UnityEditor;
8423
+
8424
+ GameObject selected = Selection.activeGameObject;
8425
+ if (selected == null)
8426
+ {
8427
+ return "No GameObject selected";
8428
+ }
8429
+
8430
+ Undo.RecordObject(selected.transform, "Move Object");
8431
+ selected.transform.position = new Vector3(0, 5, 0);
8432
+ return $"Moved {selected.name} (Undo available)";
8433
+ \`\`\`
8434
+
8435
+ ## Record Multiple Objects
8436
+
8437
+ \`\`\`csharp
8438
+ using UnityEditor;
8439
+
8440
+ GameObject[] selectedObjects = Selection.gameObjects;
8441
+ if (selectedObjects.Length == 0)
8442
+ {
8443
+ return "No GameObjects selected";
8444
+ }
8445
+
8446
+ Object[] transforms = new Object[selectedObjects.Length];
8447
+ for (int i = 0; i < selectedObjects.Length; i++)
8448
+ {
8449
+ transforms[i] = selectedObjects[i].transform;
8450
+ }
8451
+
8452
+ Undo.RecordObjects(transforms, "Move Multiple Objects");
8453
+ foreach (GameObject obj in selectedObjects)
8454
+ {
8455
+ obj.transform.position += Vector3.up * 2;
8456
+ }
8457
+ return $"Moved {selectedObjects.Length} objects (Undo available)";
8458
+ \`\`\`
8459
+
8460
+ ## Complete Object Undo (For Complex Changes)
8461
+
8462
+ \`\`\`csharp
8463
+ using UnityEditor;
8464
+
8465
+ GameObject selected = Selection.activeGameObject;
8466
+ if (selected == null)
8467
+ {
8468
+ return "No GameObject selected";
8469
+ }
8470
+
8471
+ Undo.RegisterCompleteObjectUndo(selected, "Complete Object Change");
8472
+ selected.name = "RenamedObject";
8473
+ selected.layer = LayerMask.NameToLayer("Default");
8474
+ selected.tag = "Untagged";
8475
+ return $"Modified object completely (Undo available)";
8476
+ \`\`\`
8477
+
8478
+ ## Add Component with Undo
8479
+
8480
+ \`\`\`csharp
8481
+ using UnityEditor;
8482
+
8483
+ GameObject selected = Selection.activeGameObject;
8484
+ if (selected == null)
8485
+ {
8486
+ return "No GameObject selected";
8487
+ }
8488
+
8489
+ Rigidbody rb = Undo.AddComponent<Rigidbody>(selected);
8490
+ rb.mass = 2f;
8491
+ rb.useGravity = true;
8492
+ return $"Added Rigidbody to {selected.name} (Undo available)";
8493
+ \`\`\`
8494
+
8495
+ ## Set Parent with Undo
8496
+
8497
+ \`\`\`csharp
8498
+ using UnityEditor;
8499
+
8500
+ GameObject child = GameObject.Find("Child");
8501
+ GameObject parent = GameObject.Find("Parent");
8502
+
8503
+ if (child == null || parent == null)
8504
+ {
8505
+ return "Child or Parent not found";
8506
+ }
8507
+
8508
+ Undo.SetTransformParent(child.transform, parent.transform, "Set Parent");
8509
+ return $"Set {child.name}'s parent to {parent.name} (Undo available)";
8510
+ \`\`\`
8511
+
8512
+ ## Create GameObject with Undo
8513
+
8514
+ \`\`\`csharp
8515
+ using UnityEditor;
8516
+
8517
+ GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
8518
+ cube.name = "UndoableCube";
8519
+ cube.transform.position = new Vector3(0, 1, 0);
8520
+ Undo.RegisterCreatedObjectUndo(cube, "Create Cube");
8521
+ return $"Created {cube.name} (Undo available)";
8522
+ \`\`\`
8523
+
8524
+ ## Destroy GameObject with Undo
8525
+
8526
+ \`\`\`csharp
8527
+ using UnityEditor;
8528
+
8529
+ GameObject obj = GameObject.Find("ObjectToDelete");
8530
+ if (obj == null)
8531
+ {
8532
+ return "GameObject not found";
8533
+ }
8534
+
8535
+ Undo.DestroyObjectImmediate(obj);
8536
+ return "GameObject destroyed (Undo available)";
8537
+ \`\`\`
8538
+
8539
+ ## Named Undo Group
8540
+
8541
+ \`\`\`csharp
8542
+ using UnityEditor;
8543
+
8544
+ GameObject selected = Selection.activeGameObject;
8545
+ if (selected == null)
8546
+ {
8547
+ return "No GameObject selected";
8548
+ }
8549
+
8550
+ Undo.SetCurrentGroupName("Complex Transform Operation");
8551
+
8552
+ Undo.RecordObject(selected.transform, "");
8553
+ selected.transform.position = Vector3.zero;
8554
+ selected.transform.rotation = Quaternion.identity;
8555
+ selected.transform.localScale = Vector3.one;
8556
+
8557
+ return "Reset transform (Single undo step)";
8558
+ \`\`\`
8559
+
8560
+ ## Collapse Multiple Operations into One Undo
8561
+
8562
+ \`\`\`csharp
8563
+ using UnityEditor;
8564
+
8565
+ int undoGroup = Undo.GetCurrentGroup();
8566
+ Undo.SetCurrentGroupName("Batch Operation");
8567
+
8568
+ GameObject cube1 = GameObject.CreatePrimitive(PrimitiveType.Cube);
8569
+ cube1.name = "Cube1";
8570
+ Undo.RegisterCreatedObjectUndo(cube1, "");
8571
+
8572
+ GameObject cube2 = GameObject.CreatePrimitive(PrimitiveType.Cube);
8573
+ cube2.name = "Cube2";
8574
+ cube2.transform.position = Vector3.right * 2;
8575
+ Undo.RegisterCreatedObjectUndo(cube2, "");
8576
+
8577
+ GameObject cube3 = GameObject.CreatePrimitive(PrimitiveType.Cube);
8578
+ cube3.name = "Cube3";
8579
+ cube3.transform.position = Vector3.right * 4;
8580
+ Undo.RegisterCreatedObjectUndo(cube3, "");
8581
+
8582
+ Undo.CollapseUndoOperations(undoGroup);
8583
+ return "Created 3 cubes (Single undo step)";
8584
+ \`\`\`
8585
+
8586
+ ## Modify ScriptableObject with Undo
8587
+
8588
+ \`\`\`csharp
8589
+ using UnityEditor;
8590
+
8591
+ string path = "Assets/Data/GameSettings.asset";
8592
+ ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
8593
+ if (so == null)
8594
+ {
8595
+ return $"Asset not found at {path}";
8596
+ }
8597
+
8598
+ Undo.RecordObject(so, "Modify Settings");
8599
+ SerializedObject serializedObj = new SerializedObject(so);
8600
+ SerializedProperty prop = serializedObj.FindProperty("maxHealth");
8601
+ if (prop != null)
8602
+ {
8603
+ prop.intValue = 200;
8604
+ serializedObj.ApplyModifiedProperties();
8605
+ }
8606
+ return "Modified ScriptableObject (Undo available)";
8607
+ \`\`\`
8608
+
8609
+ ## Modify Material with Undo
8610
+
8611
+ \`\`\`csharp
8612
+ using UnityEditor;
8613
+
8614
+ string path = "Assets/Materials/MyMaterial.mat";
8615
+ Material mat = AssetDatabase.LoadAssetAtPath<Material>(path);
8616
+ if (mat == null)
8617
+ {
8618
+ return $"Material not found at {path}";
8619
+ }
8620
+
8621
+ Undo.RecordObject(mat, "Change Material Color");
8622
+ mat.color = Color.red;
8623
+ return "Changed material color (Undo available)";
8624
+ \`\`\`
8625
+
8626
+ `
8627
+ }
6414
8628
  },
6415
8629
  {
6416
8630
  name: "uloop-execute-menu-item",
@@ -6465,7 +8679,13 @@ function getGlobalSkillsDir(target) {
6465
8679
  return (0, import_path5.join)((0, import_os.homedir)(), target.projectDir, "skills");
6466
8680
  }
6467
8681
  function getProjectSkillsDir(target) {
6468
- return (0, import_path5.join)(process.cwd(), target.projectDir, "skills");
8682
+ const projectRoot = findUnityProjectRoot();
8683
+ if (!projectRoot) {
8684
+ throw new Error(
8685
+ "Not inside a Unity project. Run this command from within a Unity project directory."
8686
+ );
8687
+ }
8688
+ return (0, import_path5.join)(projectRoot, target.projectDir, "skills");
6469
8689
  }
6470
8690
  function getSkillPath(skillDirName, target, global) {
6471
8691
  const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
@@ -6476,12 +8696,30 @@ function isSkillInstalled(skill, target, global) {
6476
8696
  return (0, import_fs4.existsSync)(skillPath);
6477
8697
  }
6478
8698
  function isSkillOutdated(skill, target, global) {
6479
- const skillPath = getSkillPath(skill.dirName, target, global);
8699
+ const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
8700
+ const skillDir = (0, import_path5.join)(baseDir, skill.dirName);
8701
+ const skillPath = (0, import_path5.join)(skillDir, target.skillFileName);
6480
8702
  if (!(0, import_fs4.existsSync)(skillPath)) {
6481
8703
  return false;
6482
8704
  }
6483
8705
  const installedContent = (0, import_fs4.readFileSync)(skillPath, "utf-8");
6484
- return installedContent !== skill.content;
8706
+ if (installedContent !== skill.content) {
8707
+ return true;
8708
+ }
8709
+ if ("additionalFiles" in skill && skill.additionalFiles) {
8710
+ const additionalFiles = skill.additionalFiles;
8711
+ for (const [relativePath, expectedContent] of Object.entries(additionalFiles)) {
8712
+ const filePath = (0, import_path5.join)(skillDir, relativePath);
8713
+ if (!(0, import_fs4.existsSync)(filePath)) {
8714
+ return true;
8715
+ }
8716
+ const installedFileContent = (0, import_fs4.readFileSync)(filePath, "utf-8");
8717
+ if (installedFileContent !== expectedContent) {
8718
+ return true;
8719
+ }
8720
+ }
8721
+ }
8722
+ return false;
6485
8723
  }
6486
8724
  function getSkillStatus(skill, target, global) {
6487
8725
  if (!isSkillInstalled(skill, target, global)) {
@@ -6569,7 +8807,10 @@ function findEditorFolders(basePath, maxDepth = 2) {
6569
8807
  return editorFolders;
6570
8808
  }
6571
8809
  function collectProjectSkills() {
6572
- const projectRoot = process.cwd();
8810
+ const projectRoot = findUnityProjectRoot();
8811
+ if (!projectRoot) {
8812
+ return [];
8813
+ }
6573
8814
  const skills = [];
6574
8815
  const seenNames = /* @__PURE__ */ new Set();
6575
8816
  const searchPaths = [
@@ -6618,6 +8859,14 @@ function installSkill(skill, target, global) {
6618
8859
  const skillPath = (0, import_path5.join)(skillDir, target.skillFileName);
6619
8860
  (0, import_fs4.mkdirSync)(skillDir, { recursive: true });
6620
8861
  (0, import_fs4.writeFileSync)(skillPath, skill.content, "utf-8");
8862
+ if ("additionalFiles" in skill && skill.additionalFiles) {
8863
+ const additionalFiles = skill.additionalFiles;
8864
+ for (const [relativePath, content] of Object.entries(additionalFiles)) {
8865
+ const fullPath = (0, import_path5.join)(skillDir, relativePath);
8866
+ (0, import_fs4.mkdirSync)((0, import_path5.dirname)(fullPath), { recursive: true });
8867
+ (0, import_fs4.writeFileSync)(fullPath, content, "utf-8");
8868
+ }
8869
+ }
6621
8870
  }
6622
8871
  function uninstallSkill(skill, target, global) {
6623
8872
  const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);