uloop-cli 0.53.3 → 0.54.1
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.
- package/dist/cli.bundle.cjs +543 -2491
- package/dist/cli.bundle.cjs.map +4 -4
- package/package.json +2 -3
- package/src/default-tools.json +1 -1
- package/src/project-root.ts +104 -6
- package/src/skills/deprecated-skills.ts +9 -0
- package/src/skills/skill-definitions/cli-only/uloop-get-project-info/SKILL.md +1 -1
- package/src/skills/skill-definitions/cli-only/uloop-get-version/SKILL.md +1 -1
- package/src/skills/skills-command.ts +4 -1
- package/src/skills/skills-manager.ts +436 -105
- package/src/skills/target-config.ts +1 -0
- package/src/version.ts +1 -1
- package/scripts/generate-bundled-skills.ts +0 -258
- package/src/skills/bundled-skills.ts +0 -2325
package/dist/cli.bundle.cjs
CHANGED
|
@@ -5486,13 +5486,13 @@ var DirectUnityClient = class {
|
|
|
5486
5486
|
requestId = 0;
|
|
5487
5487
|
receiveBuffer = Buffer.alloc(0);
|
|
5488
5488
|
async connect() {
|
|
5489
|
-
return new Promise((
|
|
5489
|
+
return new Promise((resolve2, reject) => {
|
|
5490
5490
|
this.socket = new net.Socket();
|
|
5491
5491
|
this.socket.on("error", (error) => {
|
|
5492
5492
|
reject(new Error(`Connection error: ${error.message}`));
|
|
5493
5493
|
});
|
|
5494
5494
|
this.socket.connect(this.port, this.host, () => {
|
|
5495
|
-
|
|
5495
|
+
resolve2();
|
|
5496
5496
|
});
|
|
5497
5497
|
});
|
|
5498
5498
|
}
|
|
@@ -5508,7 +5508,7 @@ var DirectUnityClient = class {
|
|
|
5508
5508
|
};
|
|
5509
5509
|
const requestJson = JSON.stringify(request);
|
|
5510
5510
|
const framedMessage = createFrame(requestJson);
|
|
5511
|
-
return new Promise((
|
|
5511
|
+
return new Promise((resolve2, reject) => {
|
|
5512
5512
|
const socket = this.socket;
|
|
5513
5513
|
const timeoutId = setTimeout(() => {
|
|
5514
5514
|
reject(new Error(`Request timed out after ${NETWORK_TIMEOUT_MS}ms`));
|
|
@@ -5535,7 +5535,7 @@ var DirectUnityClient = class {
|
|
|
5535
5535
|
reject(new Error(`Unity error: ${response.error.message}`));
|
|
5536
5536
|
return;
|
|
5537
5537
|
}
|
|
5538
|
-
|
|
5538
|
+
resolve2(response.result);
|
|
5539
5539
|
};
|
|
5540
5540
|
socket.on("data", onData);
|
|
5541
5541
|
socket.write(framedMessage);
|
|
@@ -5572,11 +5572,16 @@ var EXCLUDED_DIRS = /* @__PURE__ */ new Set([
|
|
|
5572
5572
|
"Logs",
|
|
5573
5573
|
"Library"
|
|
5574
5574
|
]);
|
|
5575
|
-
function
|
|
5575
|
+
function isUnityProject(dirPath) {
|
|
5576
5576
|
const hasAssets = (0, import_fs.existsSync)((0, import_path.join)(dirPath, "Assets"));
|
|
5577
5577
|
const hasProjectSettings = (0, import_fs.existsSync)((0, import_path.join)(dirPath, "ProjectSettings"));
|
|
5578
|
-
|
|
5579
|
-
|
|
5578
|
+
return hasAssets && hasProjectSettings;
|
|
5579
|
+
}
|
|
5580
|
+
function hasUloopInstalled(dirPath) {
|
|
5581
|
+
return (0, import_fs.existsSync)((0, import_path.join)(dirPath, "UserSettings/UnityMcpSettings.json"));
|
|
5582
|
+
}
|
|
5583
|
+
function isUnityProjectWithUloop(dirPath) {
|
|
5584
|
+
return isUnityProject(dirPath) && hasUloopInstalled(dirPath);
|
|
5580
5585
|
}
|
|
5581
5586
|
function findUnityProjectsInChildren(startPath, maxDepth) {
|
|
5582
5587
|
const projects = [];
|
|
@@ -5643,6 +5648,68 @@ function findUnityProjectRoot(startPath = process.cwd()) {
|
|
|
5643
5648
|
}
|
|
5644
5649
|
return findUnityProjectInParents(startPath);
|
|
5645
5650
|
}
|
|
5651
|
+
function getUnityProjectStatus(startPath = process.cwd()) {
|
|
5652
|
+
const unityProjectWithUloop = findUnityProjectRoot(startPath);
|
|
5653
|
+
if (unityProjectWithUloop) {
|
|
5654
|
+
return { found: true, path: unityProjectWithUloop, hasUloop: true };
|
|
5655
|
+
}
|
|
5656
|
+
const unityProjectWithoutUloop = findUnityProjectWithoutUloop(startPath);
|
|
5657
|
+
if (unityProjectWithoutUloop) {
|
|
5658
|
+
return { found: true, path: unityProjectWithoutUloop, hasUloop: false };
|
|
5659
|
+
}
|
|
5660
|
+
return { found: false, path: null, hasUloop: false };
|
|
5661
|
+
}
|
|
5662
|
+
function findUnityProjectWithoutUloop(startPath) {
|
|
5663
|
+
const childProject = findUnityProjectInChildrenWithoutUloop(startPath, CHILD_SEARCH_MAX_DEPTH);
|
|
5664
|
+
if (childProject) {
|
|
5665
|
+
return childProject;
|
|
5666
|
+
}
|
|
5667
|
+
return findUnityProjectInParentsWithoutUloop(startPath);
|
|
5668
|
+
}
|
|
5669
|
+
function findUnityProjectInChildrenWithoutUloop(startPath, maxDepth) {
|
|
5670
|
+
function scan(currentPath, depth) {
|
|
5671
|
+
if (depth > maxDepth || !(0, import_fs.existsSync)(currentPath)) {
|
|
5672
|
+
return null;
|
|
5673
|
+
}
|
|
5674
|
+
if (isUnityProject(currentPath)) {
|
|
5675
|
+
return currentPath;
|
|
5676
|
+
}
|
|
5677
|
+
let entries;
|
|
5678
|
+
try {
|
|
5679
|
+
entries = (0, import_fs.readdirSync)(currentPath, { withFileTypes: true });
|
|
5680
|
+
} catch {
|
|
5681
|
+
return null;
|
|
5682
|
+
}
|
|
5683
|
+
for (const entry of entries) {
|
|
5684
|
+
if (!entry.isDirectory() || EXCLUDED_DIRS.has(entry.name)) {
|
|
5685
|
+
continue;
|
|
5686
|
+
}
|
|
5687
|
+
const result = scan((0, import_path.join)(currentPath, entry.name), depth + 1);
|
|
5688
|
+
if (result) {
|
|
5689
|
+
return result;
|
|
5690
|
+
}
|
|
5691
|
+
}
|
|
5692
|
+
return null;
|
|
5693
|
+
}
|
|
5694
|
+
return scan(startPath, 0);
|
|
5695
|
+
}
|
|
5696
|
+
function findUnityProjectInParentsWithoutUloop(startPath) {
|
|
5697
|
+
let currentPath = startPath;
|
|
5698
|
+
while (true) {
|
|
5699
|
+
if (isUnityProject(currentPath)) {
|
|
5700
|
+
return currentPath;
|
|
5701
|
+
}
|
|
5702
|
+
const isGitRoot = (0, import_fs.existsSync)((0, import_path.join)(currentPath, ".git"));
|
|
5703
|
+
if (isGitRoot) {
|
|
5704
|
+
return null;
|
|
5705
|
+
}
|
|
5706
|
+
const parentPath = (0, import_path.dirname)(currentPath);
|
|
5707
|
+
if (parentPath === currentPath) {
|
|
5708
|
+
return null;
|
|
5709
|
+
}
|
|
5710
|
+
currentPath = parentPath;
|
|
5711
|
+
}
|
|
5712
|
+
}
|
|
5646
5713
|
|
|
5647
5714
|
// src/port-resolver.ts
|
|
5648
5715
|
var DEFAULT_PORT = 8700;
|
|
@@ -5692,7 +5759,7 @@ var import_path3 = require("path");
|
|
|
5692
5759
|
|
|
5693
5760
|
// src/default-tools.json
|
|
5694
5761
|
var default_tools_default = {
|
|
5695
|
-
version: "0.
|
|
5762
|
+
version: "0.54.1",
|
|
5696
5763
|
tools: [
|
|
5697
5764
|
{
|
|
5698
5765
|
name: "compile",
|
|
@@ -6123,7 +6190,7 @@ function getCacheFilePath() {
|
|
|
6123
6190
|
}
|
|
6124
6191
|
|
|
6125
6192
|
// src/version.ts
|
|
6126
|
-
var VERSION = "0.
|
|
6193
|
+
var VERSION = "0.54.1";
|
|
6127
6194
|
|
|
6128
6195
|
// src/spinner.ts
|
|
6129
6196
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
@@ -6186,7 +6253,7 @@ function suppressStdinEcho() {
|
|
|
6186
6253
|
var RETRY_DELAY_MS = 500;
|
|
6187
6254
|
var MAX_RETRIES = 3;
|
|
6188
6255
|
function sleep(ms) {
|
|
6189
|
-
return new Promise((
|
|
6256
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
6190
6257
|
}
|
|
6191
6258
|
function isRetryableError(error) {
|
|
6192
6259
|
if (!(error instanceof Error)) {
|
|
@@ -6414,2446 +6481,193 @@ var import_fs5 = require("fs");
|
|
|
6414
6481
|
var import_path5 = require("path");
|
|
6415
6482
|
var import_os = require("os");
|
|
6416
6483
|
|
|
6417
|
-
//
|
|
6418
|
-
var
|
|
6419
|
-
|
|
6420
|
-
//
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
// ../Editor/Api/McpTools/Compile/SKILL.md
|
|
6424
|
-
var SKILL_default3 = '---\nname: uloop-compile\ndescription: Compile Unity project via uloop CLI. Use when you need to: (1) Verify C# code compiles successfully after editing scripts, (2) Check for compile errors or warnings, (3) Validate script changes before running tests.\n---\n\n# uloop compile\n\nExecute Unity project compilation.\n\n## Usage\n\n```bash\nuloop compile [--force-recompile]\n```\n\n## Parameters\n\n| Parameter | Type | Description |\n|-----------|------|-------------|\n| `--force-recompile` | boolean | Force full recompilation (triggers Domain Reload) |\n\n## Examples\n\n```bash\n# Check compilation\nuloop compile\n\n# Force full recompilation\nuloop compile --force-recompile\n```\n\n## Output\n\nReturns JSON:\n- `Success`: boolean\n- `ErrorCount`: number\n- `WarningCount`: number\n\n## Troubleshooting\n\nIf CLI hangs or shows "Unity is busy" errors after compilation, stale lock files may be preventing connection. Run the following to clean them up:\n\n```bash\nuloop fix\n```\n\nThis removes any leftover lock files (`compiling.lock`, `domainreload.lock`, `serverstarting.lock`) from the Unity project\'s Temp directory.\n';
|
|
6425
|
-
|
|
6426
|
-
// ../Editor/Api/McpTools/ControlPlayMode/SKILL.md
|
|
6427
|
-
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";
|
|
6428
|
-
|
|
6429
|
-
// ../Editor/Api/McpTools/ExecuteDynamicCode/SKILL.md
|
|
6430
|
-
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";
|
|
6431
|
-
|
|
6432
|
-
// ../Editor/Api/McpTools/ExecuteMenuItem/SKILL.md
|
|
6433
|
-
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';
|
|
6434
|
-
|
|
6435
|
-
// ../Editor/Api/McpTools/FindGameObjects/SKILL.md
|
|
6436
|
-
var SKILL_default7 = '---\nname: uloop-find-game-objects\ndescription: Find GameObjects with search criteria via uloop CLI. Use when you need to: (1) Locate GameObjects by name pattern, (2) Find objects by tag or layer, (3) Search for objects with specific component types.\n---\n\n# uloop find-game-objects\n\nFind GameObjects with search criteria.\n\n## Usage\n\n```bash\nuloop find-game-objects [options]\n```\n\n## Parameters\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `--name-pattern` | string | - | Name pattern to search |\n| `--search-mode` | string | `Contains` | Search mode: `Exact`, `Path`, `Regex`, `Contains` |\n| `--required-components` | array | - | Required components |\n| `--tag` | string | - | Tag filter |\n| `--layer` | string | - | Layer filter |\n| `--max-results` | integer | `20` | Maximum number of results |\n| `--include-inactive` | boolean | `false` | Include inactive GameObjects |\n\n## Examples\n\n```bash\n# Find by name\nuloop find-game-objects --name-pattern "Player"\n\n# Find with component\nuloop find-game-objects --required-components Rigidbody\n\n# Find by tag\nuloop find-game-objects --tag "Enemy"\n\n# Regex search\nuloop find-game-objects --name-pattern "UI_.*" --search-mode Regex\n```\n\n## Output\n\nReturns JSON array of matching GameObjects with paths and components.\n';
|
|
6437
|
-
|
|
6438
|
-
// ../Editor/Api/McpTools/FocusUnityWindow/SKILL.md
|
|
6439
|
-
var SKILL_default8 = "---\nname: uloop-focus-window\ndescription: Bring Unity Editor window to front via uloop CLI. Use when you need to: (1) Focus Unity Editor before capturing screenshots, (2) Ensure Unity window is visible for visual checks, (3) Bring Unity to foreground for user interaction.\n---\n\n# uloop focus-window\n\nBring Unity Editor window to front.\n\n## Usage\n\n```bash\nuloop focus-window\n```\n\n## Parameters\n\nNone.\n\n## Examples\n\n```bash\n# Focus Unity Editor\nuloop focus-window\n```\n\n## Output\n\nReturns JSON confirming the window was focused.\n\n## Notes\n\n- Useful before `uloop capture-unity-window` to ensure the target window is visible\n- Brings the main Unity Editor window to the foreground\n";
|
|
6440
|
-
|
|
6441
|
-
// ../Editor/Api/McpTools/GetHierarchy/SKILL.md
|
|
6442
|
-
var SKILL_default9 = '---\nname: uloop-get-hierarchy\ndescription: Get Unity Hierarchy structure via uloop CLI. Use when you need to: (1) Inspect scene structure and GameObject tree, (2) Find GameObjects and their parent-child relationships, (3) Check component attachments on objects.\n---\n\n# uloop get-hierarchy\n\nGet Unity Hierarchy structure.\n\n## Usage\n\n```bash\nuloop get-hierarchy [options]\n```\n\n## Parameters\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `--root-path` | string | - | Root GameObject path to start from |\n| `--max-depth` | integer | `-1` | Maximum depth (-1 for unlimited) |\n| `--include-components` | boolean | `true` | Include component information |\n| `--include-inactive` | boolean | `true` | Include inactive GameObjects |\n| `--include-paths` | boolean | `false` | Include full path information |\n\n## Examples\n\n```bash\n# Get entire hierarchy\nuloop get-hierarchy\n\n# Get hierarchy from specific root\nuloop get-hierarchy --root-path "Canvas/UI"\n\n# Limit depth\nuloop get-hierarchy --max-depth 2\n\n# Without components\nuloop get-hierarchy --include-components false\n```\n\n## Output\n\nReturns JSON with hierarchical structure of GameObjects and their components.\n';
|
|
6443
|
-
|
|
6444
|
-
// ../Editor/Api/McpTools/GetLogs/SKILL.md
|
|
6445
|
-
var SKILL_default10 = '---\nname: uloop-get-logs\ndescription: Retrieve Unity Console logs via uloop CLI. Use when you need to: (1) Check for errors or warnings after operations, (2) Debug runtime issues in Unity Editor, (3) Investigate unexpected behavior or exceptions.\n---\n\n# uloop get-logs\n\nRetrieve logs from Unity Console.\n\n## Usage\n\n```bash\nuloop get-logs [options]\n```\n\n## Parameters\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `--log-type` | string | `All` | Log type filter: `Error`, `Warning`, `Log`, `All` |\n| `--max-count` | integer | `100` | Maximum number of logs to retrieve |\n| `--search-text` | string | - | Text to search within logs |\n| `--include-stack-trace` | boolean | `true` | Include stack trace in output |\n| `--use-regex` | boolean | `false` | Use regex for search |\n| `--search-in-stack-trace` | boolean | `false` | Search within stack trace |\n\n## Examples\n\n```bash\n# Get all logs\nuloop get-logs\n\n# Get only errors\nuloop get-logs --log-type Error\n\n# Search for specific text\nuloop get-logs --search-text "NullReference"\n\n# Regex search\nuloop get-logs --search-text "Missing.*Component" --use-regex\n```\n\n## Output\n\nReturns JSON array of log entries with message, type, and optional stack trace.\n';
|
|
6446
|
-
|
|
6447
|
-
// ../Editor/Api/McpTools/GetMenuItems/SKILL.md
|
|
6448
|
-
var SKILL_default11 = '---\nname: uloop-get-menu-items\ndescription: Retrieve Unity MenuItems via uloop CLI. Use when you need to: (1) Discover available menu commands in Unity Editor, (2) Find menu paths for automation, (3) Prepare for executing menu items programmatically.\n---\n\n# uloop get-menu-items\n\nRetrieve Unity MenuItems.\n\n## Usage\n\n```bash\nuloop get-menu-items [options]\n```\n\n## Parameters\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `--filter-text` | string | - | Filter text |\n| `--filter-type` | string | `contains` | Filter type: `contains`, `exact`, `startswith` |\n| `--max-count` | integer | `200` | Maximum number of items |\n| `--include-validation` | boolean | `false` | Include validation functions |\n\n## Examples\n\n```bash\n# List all menu items\nuloop get-menu-items\n\n# Filter by text\nuloop get-menu-items --filter-text "GameObject"\n\n# Exact match\nuloop get-menu-items --filter-text "File/Save" --filter-type exact\n```\n\n## Output\n\nReturns JSON array of menu items with paths and metadata.\n\n## Notes\n\nUse with `uloop execute-menu-item` to run discovered menu commands.\n';
|
|
6449
|
-
|
|
6450
|
-
// ../Editor/Api/McpTools/UnitySearchProviderDetails/SKILL.md
|
|
6451
|
-
var SKILL_default12 = "---\nname: uloop-get-provider-details\ndescription: Get Unity Search provider details via uloop CLI. Use when you need to: (1) Discover available search providers, (2) Understand search capabilities and filters, (3) Configure searches with specific provider options.\n---\n\n# uloop get-provider-details\n\nGet detailed information about Unity Search providers.\n\n## Usage\n\n```bash\nuloop get-provider-details [options]\n```\n\n## Parameters\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `--provider-id` | string | - | Specific provider ID to query |\n| `--active-only` | boolean | `false` | Only show active providers |\n| `--include-descriptions` | boolean | `true` | Include descriptions |\n| `--sort-by-priority` | boolean | `true` | Sort by priority |\n\n## Examples\n\n```bash\n# List all providers\nuloop get-provider-details\n\n# Get specific provider\nuloop get-provider-details --provider-id asset\n\n# Active providers only\nuloop get-provider-details --active-only\n```\n\n## Output\n\nReturns JSON:\n- `Providers`: array of provider info (ID, name, description, priority)\n\n## Notes\n\nUse provider IDs with `uloop unity-search --providers` option.\n";
|
|
6452
|
-
|
|
6453
|
-
// ../Editor/Api/McpTools/RunTests/SKILL.md
|
|
6454
|
-
var SKILL_default13 = '---\nname: uloop-run-tests\ndescription: Execute Unity Test Runner via uloop CLI. Use when you need to: (1) Run unit tests (EditMode tests), (2) Run integration tests (PlayMode tests), (3) Verify code changes don\'t break existing functionality.\n---\n\n# uloop run-tests\n\nExecute Unity Test Runner.\n\n## Usage\n\n```bash\nuloop run-tests [options]\n```\n\n## Parameters\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `--test-mode` | string | `EditMode` | Test mode: `EditMode`, `PlayMode` |\n| `--filter-type` | string | `all` | Filter type: `all`, `exact`, `regex`, `assembly` |\n| `--filter-value` | string | - | Filter value (test name, pattern, or assembly) |\n| `--save-xml` | boolean | `false` | Save test results as XML |\n\n## Examples\n\n```bash\n# Run all EditMode tests\nuloop run-tests\n\n# Run PlayMode tests\nuloop run-tests --test-mode PlayMode\n\n# Run specific test\nuloop run-tests --filter-type exact --filter-value "MyTest.TestMethod"\n\n# Run tests matching pattern\nuloop run-tests --filter-type regex --filter-value ".*Integration.*"\n```\n\n## Output\n\nReturns JSON with test results including pass/fail counts and details.\n';
|
|
6455
|
-
|
|
6456
|
-
// ../Editor/Api/McpTools/UnitySearch/SKILL.md
|
|
6457
|
-
var SKILL_default14 = '---\nname: uloop-unity-search\ndescription: Search Unity project via uloop CLI. Use when you need to: (1) Find assets by name or type (scenes, prefabs, scripts, materials), (2) Search for project resources using Unity\'s search system, (3) Locate files within the Unity project.\n---\n\n# uloop unity-search\n\nSearch Unity project using Unity Search.\n\n## Usage\n\n```bash\nuloop unity-search [options]\n```\n\n## Parameters\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `--search-query` | string | - | Search query |\n| `--providers` | array | - | Search providers (e.g., `asset`, `scene`, `find`) |\n| `--max-results` | integer | `50` | Maximum number of results |\n| `--save-to-file` | boolean | `false` | Save results to file |\n\n## Examples\n\n```bash\n# Search for assets\nuloop unity-search --search-query "Player"\n\n# Search with specific provider\nuloop unity-search --search-query "t:Prefab" --providers asset\n\n# Limit results\nuloop unity-search --search-query "*.cs" --max-results 20\n```\n\n## Output\n\nReturns JSON array of search results with paths and metadata.\n\n## Notes\n\nUse `uloop get-provider-details` to discover available search providers.\n';
|
|
6458
|
-
|
|
6459
|
-
// src/skills/bundled-skills.ts
|
|
6460
|
-
var BUNDLED_SKILLS = [
|
|
6461
|
-
{
|
|
6462
|
-
name: "uloop-capture-window",
|
|
6463
|
-
dirName: "uloop-capture-window",
|
|
6464
|
-
content: SKILL_default
|
|
6465
|
-
},
|
|
6466
|
-
{
|
|
6467
|
-
name: "uloop-clear-console",
|
|
6468
|
-
dirName: "uloop-clear-console",
|
|
6469
|
-
content: SKILL_default2
|
|
6470
|
-
},
|
|
6471
|
-
{
|
|
6472
|
-
name: "uloop-compile",
|
|
6473
|
-
dirName: "uloop-compile",
|
|
6474
|
-
content: SKILL_default3
|
|
6475
|
-
},
|
|
6476
|
-
{
|
|
6477
|
-
name: "uloop-control-play-mode",
|
|
6478
|
-
dirName: "uloop-control-play-mode",
|
|
6479
|
-
content: SKILL_default4
|
|
6480
|
-
},
|
|
6481
|
-
{
|
|
6482
|
-
name: "uloop-execute-dynamic-code",
|
|
6483
|
-
dirName: "uloop-execute-dynamic-code",
|
|
6484
|
-
content: SKILL_default5,
|
|
6485
|
-
additionalFiles: {
|
|
6486
|
-
"examples/asset-operations.md": `# Asset Operations
|
|
6487
|
-
|
|
6488
|
-
Code examples for AssetDatabase operations using \`execute-dynamic-code\`.
|
|
6489
|
-
|
|
6490
|
-
## Find Assets by Type
|
|
6491
|
-
|
|
6492
|
-
\`\`\`csharp
|
|
6493
|
-
using UnityEditor;
|
|
6494
|
-
using System.Collections.Generic;
|
|
6495
|
-
|
|
6496
|
-
string[] prefabGuids = AssetDatabase.FindAssets("t:Prefab");
|
|
6497
|
-
List<string> paths = new List<string>();
|
|
6498
|
-
|
|
6499
|
-
foreach (string guid in prefabGuids)
|
|
6500
|
-
{
|
|
6501
|
-
paths.Add(AssetDatabase.GUIDToAssetPath(guid));
|
|
6502
|
-
}
|
|
6503
|
-
return $"Found {paths.Count} prefabs";
|
|
6504
|
-
\`\`\`
|
|
6505
|
-
|
|
6506
|
-
## Find Assets by Name
|
|
6507
|
-
|
|
6508
|
-
\`\`\`csharp
|
|
6509
|
-
using UnityEditor;
|
|
6510
|
-
using System.Collections.Generic;
|
|
6511
|
-
|
|
6512
|
-
string searchName = "Player";
|
|
6513
|
-
string[] guids = AssetDatabase.FindAssets(searchName);
|
|
6514
|
-
List<string> paths = new List<string>();
|
|
6515
|
-
|
|
6516
|
-
foreach (string guid in guids)
|
|
6517
|
-
{
|
|
6518
|
-
paths.Add(AssetDatabase.GUIDToAssetPath(guid));
|
|
6519
|
-
}
|
|
6520
|
-
return $"Found {paths.Count} assets matching '{searchName}'";
|
|
6521
|
-
\`\`\`
|
|
6522
|
-
|
|
6523
|
-
## Find Assets in Folder
|
|
6524
|
-
|
|
6525
|
-
\`\`\`csharp
|
|
6526
|
-
using UnityEditor;
|
|
6527
|
-
using System.Collections.Generic;
|
|
6528
|
-
|
|
6529
|
-
string folder = "Assets/Prefabs";
|
|
6530
|
-
string[] guids = AssetDatabase.FindAssets("t:Prefab", new[] { folder });
|
|
6531
|
-
List<string> paths = new List<string>();
|
|
6532
|
-
|
|
6533
|
-
foreach (string guid in guids)
|
|
6534
|
-
{
|
|
6535
|
-
paths.Add(AssetDatabase.GUIDToAssetPath(guid));
|
|
6536
|
-
}
|
|
6537
|
-
return $"Found {paths.Count} prefabs in {folder}";
|
|
6538
|
-
\`\`\`
|
|
6539
|
-
|
|
6540
|
-
## Duplicate Asset
|
|
6541
|
-
|
|
6542
|
-
\`\`\`csharp
|
|
6543
|
-
using UnityEditor;
|
|
6544
|
-
|
|
6545
|
-
string sourcePath = "Assets/Materials/MyMaterial.mat";
|
|
6546
|
-
string destPath = "Assets/Materials/MyMaterial_Backup.mat";
|
|
6547
|
-
|
|
6548
|
-
bool success = AssetDatabase.CopyAsset(sourcePath, destPath);
|
|
6549
|
-
return success ? $"Copied to {destPath}" : "Copy failed";
|
|
6550
|
-
\`\`\`
|
|
6551
|
-
|
|
6552
|
-
## Move Asset
|
|
6553
|
-
|
|
6554
|
-
\`\`\`csharp
|
|
6555
|
-
using UnityEditor;
|
|
6556
|
-
|
|
6557
|
-
string sourcePath = "Assets/OldFolder/MyAsset.asset";
|
|
6558
|
-
string destPath = "Assets/NewFolder/MyAsset.asset";
|
|
6559
|
-
|
|
6560
|
-
string error = AssetDatabase.MoveAsset(sourcePath, destPath);
|
|
6561
|
-
return string.IsNullOrEmpty(error) ? $"Moved to {destPath}" : $"Error: {error}";
|
|
6562
|
-
\`\`\`
|
|
6563
|
-
|
|
6564
|
-
## Rename Asset
|
|
6565
|
-
|
|
6566
|
-
\`\`\`csharp
|
|
6567
|
-
using UnityEditor;
|
|
6568
|
-
|
|
6569
|
-
string assetPath = "Assets/Materials/OldName.mat";
|
|
6570
|
-
string newName = "NewName";
|
|
6571
|
-
|
|
6572
|
-
string error = AssetDatabase.RenameAsset(assetPath, newName);
|
|
6573
|
-
return string.IsNullOrEmpty(error) ? $"Renamed to {newName}" : $"Error: {error}";
|
|
6574
|
-
\`\`\`
|
|
6575
|
-
|
|
6576
|
-
## Rename Asset (Undo-supported)
|
|
6577
|
-
|
|
6578
|
-
\`\`\`csharp
|
|
6579
|
-
using UnityEditor;
|
|
6484
|
+
// src/skills/deprecated-skills.ts
|
|
6485
|
+
var DEPRECATED_SKILLS = [
|
|
6486
|
+
"uloop-capture-window"
|
|
6487
|
+
// renamed to uloop-screenshot in v0.54.0
|
|
6488
|
+
];
|
|
6580
6489
|
|
|
6581
|
-
//
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6490
|
+
// src/skills/skills-manager.ts
|
|
6491
|
+
var EXCLUDED_DIRS2 = /* @__PURE__ */ new Set([
|
|
6492
|
+
"node_modules",
|
|
6493
|
+
".git",
|
|
6494
|
+
"Temp",
|
|
6495
|
+
"obj",
|
|
6496
|
+
"Build",
|
|
6497
|
+
"Builds",
|
|
6498
|
+
"Logs",
|
|
6499
|
+
"Skill"
|
|
6500
|
+
]);
|
|
6501
|
+
var EXCLUDED_FILES = /* @__PURE__ */ new Set([".meta", ".DS_Store", ".gitkeep"]);
|
|
6502
|
+
var SkillsPathConstants = class _SkillsPathConstants {
|
|
6503
|
+
static PACKAGES_DIR = "Packages";
|
|
6504
|
+
static SRC_DIR = "src";
|
|
6505
|
+
static SKILLS_DIR = "skills";
|
|
6506
|
+
static EDITOR_DIR = "Editor";
|
|
6507
|
+
static API_DIR = "Api";
|
|
6508
|
+
static MCP_TOOLS_DIR = "McpTools";
|
|
6509
|
+
static SKILL_DIR = "Skill";
|
|
6510
|
+
static LIBRARY_DIR = "Library";
|
|
6511
|
+
static PACKAGE_CACHE_DIR = "PackageCache";
|
|
6512
|
+
static ASSETS_DIR = "Assets";
|
|
6513
|
+
static MANIFEST_FILE = "manifest.json";
|
|
6514
|
+
static SKILL_FILE = "SKILL.md";
|
|
6515
|
+
static CLI_ONLY_DIR = "skill-definitions";
|
|
6516
|
+
static CLI_ONLY_SUBDIR = "cli-only";
|
|
6517
|
+
static DIST_PARENT_DIR = "..";
|
|
6518
|
+
static FILE_PROTOCOL = "file:";
|
|
6519
|
+
static PATH_PROTOCOL = "path:";
|
|
6520
|
+
static PACKAGE_NAME = "io.github.hatayama.uloopmcp";
|
|
6521
|
+
static PACKAGE_NAME_ALIAS = "io.github.hatayama.uLoopMCP";
|
|
6522
|
+
static PACKAGE_NAMES = [
|
|
6523
|
+
_SkillsPathConstants.PACKAGE_NAME,
|
|
6524
|
+
_SkillsPathConstants.PACKAGE_NAME_ALIAS
|
|
6525
|
+
];
|
|
6526
|
+
};
|
|
6527
|
+
function getGlobalSkillsDir(target) {
|
|
6528
|
+
return (0, import_path5.join)((0, import_os.homedir)(), target.projectDir, "skills");
|
|
6586
6529
|
}
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
{
|
|
6602
|
-
return "No object selected";
|
|
6530
|
+
function getProjectSkillsDir(target) {
|
|
6531
|
+
const status = getUnityProjectStatus();
|
|
6532
|
+
if (!status.found) {
|
|
6533
|
+
throw new Error(
|
|
6534
|
+
"Not inside a Unity project. Run this command from within a Unity project directory."
|
|
6535
|
+
);
|
|
6536
|
+
}
|
|
6537
|
+
if (!status.hasUloop) {
|
|
6538
|
+
throw new Error(
|
|
6539
|
+
`uLoopMCP is not installed in this Unity project (${status.path}).
|
|
6540
|
+
Please install uLoopMCP package first, then run this command again.`
|
|
6541
|
+
);
|
|
6542
|
+
}
|
|
6543
|
+
return (0, import_path5.join)(status.path, target.projectDir, "skills");
|
|
6603
6544
|
}
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
{
|
|
6608
|
-
return "Selected object is not an asset (scene object)";
|
|
6545
|
+
function getSkillPath(skillDirName, target, global) {
|
|
6546
|
+
const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
6547
|
+
return (0, import_path5.join)(baseDir, skillDirName, target.skillFileName);
|
|
6609
6548
|
}
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
## Load Asset at Path
|
|
6614
|
-
|
|
6615
|
-
\`\`\`csharp
|
|
6616
|
-
using UnityEditor;
|
|
6617
|
-
|
|
6618
|
-
string path = "Assets/Prefabs/Player.prefab";
|
|
6619
|
-
GameObject asset = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
|
6620
|
-
|
|
6621
|
-
if (asset == null)
|
|
6622
|
-
{
|
|
6623
|
-
return $"Asset not found at {path}";
|
|
6549
|
+
function isSkillInstalled(skill, target, global) {
|
|
6550
|
+
const skillPath = getSkillPath(skill.dirName, target, global);
|
|
6551
|
+
return (0, import_fs5.existsSync)(skillPath);
|
|
6624
6552
|
}
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
6553
|
+
function isSkillOutdated(skill, target, global) {
|
|
6554
|
+
const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
6555
|
+
const skillDir = (0, import_path5.join)(baseDir, skill.dirName);
|
|
6556
|
+
const skillPath = (0, import_path5.join)(skillDir, target.skillFileName);
|
|
6557
|
+
if (!(0, import_fs5.existsSync)(skillPath)) {
|
|
6558
|
+
return false;
|
|
6559
|
+
}
|
|
6560
|
+
const installedContent = (0, import_fs5.readFileSync)(skillPath, "utf-8");
|
|
6561
|
+
if (installedContent !== skill.content) {
|
|
6562
|
+
return true;
|
|
6563
|
+
}
|
|
6564
|
+
if ("additionalFiles" in skill && skill.additionalFiles) {
|
|
6565
|
+
const additionalFiles = skill.additionalFiles;
|
|
6566
|
+
for (const [relativePath, expectedContent] of Object.entries(additionalFiles)) {
|
|
6567
|
+
const filePath = (0, import_path5.join)(skillDir, relativePath);
|
|
6568
|
+
if (!(0, import_fs5.existsSync)(filePath)) {
|
|
6569
|
+
return true;
|
|
6570
|
+
}
|
|
6571
|
+
const installedFileContent = (0, import_fs5.readFileSync)(filePath);
|
|
6572
|
+
if (!installedFileContent.equals(expectedContent)) {
|
|
6573
|
+
return true;
|
|
6574
|
+
}
|
|
6642
6575
|
}
|
|
6576
|
+
}
|
|
6577
|
+
return false;
|
|
6643
6578
|
}
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
string path = "Assets/Prefabs/Player.prefab";
|
|
6653
|
-
string guid = AssetDatabase.AssetPathToGUID(path);
|
|
6654
|
-
|
|
6655
|
-
bool exists = !string.IsNullOrEmpty(guid);
|
|
6656
|
-
return exists ? $"Asset exists: {path}" : $"Asset not found: {path}";
|
|
6657
|
-
\`\`\`
|
|
6658
|
-
|
|
6659
|
-
## Get Asset Dependencies
|
|
6660
|
-
|
|
6661
|
-
\`\`\`csharp
|
|
6662
|
-
using UnityEditor;
|
|
6663
|
-
|
|
6664
|
-
string assetPath = "Assets/Prefabs/Player.prefab";
|
|
6665
|
-
string[] dependencies = AssetDatabase.GetDependencies(assetPath, true);
|
|
6666
|
-
|
|
6667
|
-
return $"Asset has {dependencies.Length} dependencies";
|
|
6668
|
-
\`\`\`
|
|
6669
|
-
|
|
6670
|
-
## Refresh AssetDatabase
|
|
6671
|
-
|
|
6672
|
-
\`\`\`csharp
|
|
6673
|
-
using UnityEditor;
|
|
6674
|
-
|
|
6675
|
-
AssetDatabase.Refresh();
|
|
6676
|
-
return "AssetDatabase refreshed";
|
|
6677
|
-
\`\`\`
|
|
6678
|
-
`,
|
|
6679
|
-
"examples/batch-operations.md": `# Batch Operations
|
|
6680
|
-
|
|
6681
|
-
Code examples for batch processing using \`execute-dynamic-code\`.
|
|
6682
|
-
|
|
6683
|
-
## Batch Modify Selected Objects
|
|
6684
|
-
|
|
6685
|
-
\`\`\`csharp
|
|
6686
|
-
using UnityEditor;
|
|
6687
|
-
|
|
6688
|
-
GameObject[] selected = Selection.gameObjects;
|
|
6689
|
-
if (selected.Length == 0)
|
|
6690
|
-
{
|
|
6691
|
-
return "No GameObjects selected";
|
|
6692
|
-
}
|
|
6693
|
-
|
|
6694
|
-
int undoGroup = Undo.GetCurrentGroup();
|
|
6695
|
-
Undo.SetCurrentGroupName("Batch Modify");
|
|
6696
|
-
|
|
6697
|
-
foreach (GameObject obj in selected)
|
|
6698
|
-
{
|
|
6699
|
-
Undo.RecordObject(obj.transform, "");
|
|
6700
|
-
obj.transform.localScale = Vector3.one * 2;
|
|
6701
|
-
}
|
|
6702
|
-
|
|
6703
|
-
Undo.CollapseUndoOperations(undoGroup);
|
|
6704
|
-
return $"Scaled {selected.Length} objects (Single undo step)";
|
|
6705
|
-
\`\`\`
|
|
6706
|
-
|
|
6707
|
-
## Edit Multiple Objects with SerializedObject
|
|
6708
|
-
|
|
6709
|
-
\`\`\`csharp
|
|
6710
|
-
using UnityEditor;
|
|
6711
|
-
|
|
6712
|
-
GameObject[] selected = Selection.gameObjects;
|
|
6713
|
-
if (selected.Length == 0)
|
|
6714
|
-
{
|
|
6715
|
-
return "No GameObjects selected";
|
|
6716
|
-
}
|
|
6717
|
-
|
|
6718
|
-
List<Transform> transforms = new List<Transform>();
|
|
6719
|
-
foreach (GameObject obj in selected)
|
|
6720
|
-
{
|
|
6721
|
-
transforms.Add(obj.transform);
|
|
6722
|
-
}
|
|
6723
|
-
|
|
6724
|
-
SerializedObject serializedObj = new SerializedObject(transforms.ToArray());
|
|
6725
|
-
SerializedProperty positionProp = serializedObj.FindProperty("m_LocalPosition");
|
|
6726
|
-
positionProp.vector3Value = Vector3.zero;
|
|
6727
|
-
serializedObj.ApplyModifiedProperties();
|
|
6728
|
-
|
|
6729
|
-
return $"Reset position of {selected.Length} objects";
|
|
6730
|
-
\`\`\`
|
|
6731
|
-
|
|
6732
|
-
## Batch Add Component
|
|
6733
|
-
|
|
6734
|
-
\`\`\`csharp
|
|
6735
|
-
using UnityEditor;
|
|
6736
|
-
|
|
6737
|
-
GameObject[] selected = Selection.gameObjects;
|
|
6738
|
-
if (selected.Length == 0)
|
|
6739
|
-
{
|
|
6740
|
-
return "No GameObjects selected";
|
|
6579
|
+
function getSkillStatus(skill, target, global) {
|
|
6580
|
+
if (!isSkillInstalled(skill, target, global)) {
|
|
6581
|
+
return "not_installed";
|
|
6582
|
+
}
|
|
6583
|
+
if (isSkillOutdated(skill, target, global)) {
|
|
6584
|
+
return "outdated";
|
|
6585
|
+
}
|
|
6586
|
+
return "installed";
|
|
6741
6587
|
}
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6588
|
+
function parseFrontmatter(content) {
|
|
6589
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
6590
|
+
if (!frontmatterMatch) {
|
|
6591
|
+
return {};
|
|
6592
|
+
}
|
|
6593
|
+
const frontmatterMap = /* @__PURE__ */ new Map();
|
|
6594
|
+
const lines = frontmatterMatch[1].split("\n");
|
|
6595
|
+
for (const line of lines) {
|
|
6596
|
+
const colonIndex = line.indexOf(":");
|
|
6597
|
+
if (colonIndex === -1) {
|
|
6598
|
+
continue;
|
|
6599
|
+
}
|
|
6600
|
+
const key = line.slice(0, colonIndex).trim();
|
|
6601
|
+
const rawValue = line.slice(colonIndex + 1).trim();
|
|
6602
|
+
let parsedValue = rawValue;
|
|
6603
|
+
if (rawValue === "true") {
|
|
6604
|
+
parsedValue = true;
|
|
6605
|
+
} else if (rawValue === "false") {
|
|
6606
|
+
parsedValue = false;
|
|
6753
6607
|
}
|
|
6608
|
+
frontmatterMap.set(key, parsedValue);
|
|
6609
|
+
}
|
|
6610
|
+
return Object.fromEntries(frontmatterMap);
|
|
6754
6611
|
}
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6612
|
+
var warnedLegacyPaths = /* @__PURE__ */ new Set();
|
|
6613
|
+
function warnLegacySkillStructure(toolPath, legacySkillMdPath) {
|
|
6614
|
+
if (warnedLegacyPaths.has(legacySkillMdPath)) {
|
|
6615
|
+
return;
|
|
6616
|
+
}
|
|
6617
|
+
warnedLegacyPaths.add(legacySkillMdPath);
|
|
6618
|
+
console.error("\x1B[33m" + "=".repeat(70) + "\x1B[0m");
|
|
6619
|
+
console.error("\x1B[33mWarning: Legacy skill structure detected\x1B[0m");
|
|
6620
|
+
console.error(` Path: ${legacySkillMdPath}`);
|
|
6621
|
+
console.error("");
|
|
6622
|
+
console.error(" The skill structure has changed. Please migrate to the new format:");
|
|
6623
|
+
console.error(' 1. Create a "Skill" folder in the tool directory');
|
|
6624
|
+
console.error(" 2. Move SKILL.md and any additional files/folders into Skill/");
|
|
6625
|
+
console.error("");
|
|
6626
|
+
console.error(" Expected structure:");
|
|
6627
|
+
console.error(" ToolName/");
|
|
6628
|
+
console.error(" \u2514\u2500\u2500 Skill/");
|
|
6629
|
+
console.error(" \u251C\u2500\u2500 SKILL.md");
|
|
6630
|
+
console.error(" \u2514\u2500\u2500 (any additional files or directories)");
|
|
6631
|
+
console.error("\x1B[33m" + "=".repeat(70) + "\x1B[0m");
|
|
6632
|
+
console.error("");
|
|
6769
6633
|
}
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
{
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
if (mat != null)
|
|
6779
|
-
{
|
|
6780
|
-
mat.color = Color.white;
|
|
6781
|
-
EditorUtility.SetDirty(mat);
|
|
6782
|
-
modified++;
|
|
6634
|
+
function scanEditorFolderForSkills(editorPath, skills, sourceType, warnLegacy = true) {
|
|
6635
|
+
if (!(0, import_fs5.existsSync)(editorPath)) {
|
|
6636
|
+
return;
|
|
6637
|
+
}
|
|
6638
|
+
const entries = (0, import_fs5.readdirSync)(editorPath, { withFileTypes: true });
|
|
6639
|
+
for (const entry of entries) {
|
|
6640
|
+
if (EXCLUDED_DIRS2.has(entry.name)) {
|
|
6641
|
+
continue;
|
|
6783
6642
|
}
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
}
|
|
6811
|
-
|
|
6812
|
-
Undo.CollapseUndoOperations(undoGroup);
|
|
6813
|
-
return $"Renamed {selected.Length} objects";
|
|
6814
|
-
\`\`\`
|
|
6815
|
-
|
|
6816
|
-
## Batch Set Layer
|
|
6817
|
-
|
|
6818
|
-
\`\`\`csharp
|
|
6819
|
-
using UnityEditor;
|
|
6820
|
-
|
|
6821
|
-
GameObject[] selected = Selection.gameObjects;
|
|
6822
|
-
if (selected.Length == 0)
|
|
6823
|
-
{
|
|
6824
|
-
return "No GameObjects selected";
|
|
6825
|
-
}
|
|
6826
|
-
|
|
6827
|
-
int layer = LayerMask.NameToLayer("Default");
|
|
6828
|
-
|
|
6829
|
-
int undoGroup = Undo.GetCurrentGroup();
|
|
6830
|
-
Undo.SetCurrentGroupName("Batch Set Layer");
|
|
6831
|
-
|
|
6832
|
-
foreach (GameObject obj in selected)
|
|
6833
|
-
{
|
|
6834
|
-
Undo.RecordObject(obj, "");
|
|
6835
|
-
obj.layer = layer;
|
|
6836
|
-
}
|
|
6837
|
-
|
|
6838
|
-
Undo.CollapseUndoOperations(undoGroup);
|
|
6839
|
-
return $"Set layer of {selected.Length} objects to Default";
|
|
6840
|
-
\`\`\`
|
|
6841
|
-
|
|
6842
|
-
## Batch Set Tag
|
|
6843
|
-
|
|
6844
|
-
\`\`\`csharp
|
|
6845
|
-
using UnityEditor;
|
|
6846
|
-
|
|
6847
|
-
GameObject[] selected = Selection.gameObjects;
|
|
6848
|
-
if (selected.Length == 0)
|
|
6849
|
-
{
|
|
6850
|
-
return "No GameObjects selected";
|
|
6851
|
-
}
|
|
6852
|
-
|
|
6853
|
-
int undoGroup = Undo.GetCurrentGroup();
|
|
6854
|
-
Undo.SetCurrentGroupName("Batch Set Tag");
|
|
6855
|
-
|
|
6856
|
-
foreach (GameObject obj in selected)
|
|
6857
|
-
{
|
|
6858
|
-
Undo.RecordObject(obj, "");
|
|
6859
|
-
obj.tag = "Enemy";
|
|
6860
|
-
}
|
|
6861
|
-
|
|
6862
|
-
Undo.CollapseUndoOperations(undoGroup);
|
|
6863
|
-
return $"Tagged {selected.Length} objects as Enemy";
|
|
6864
|
-
\`\`\`
|
|
6865
|
-
|
|
6866
|
-
## Batch Modify ScriptableObjects
|
|
6867
|
-
|
|
6868
|
-
\`\`\`csharp
|
|
6869
|
-
using UnityEditor;
|
|
6870
|
-
|
|
6871
|
-
string[] guids = AssetDatabase.FindAssets("t:ScriptableObject", new[] { "Assets/Data" });
|
|
6872
|
-
if (guids.Length == 0)
|
|
6873
|
-
{
|
|
6874
|
-
return "No ScriptableObjects found";
|
|
6875
|
-
}
|
|
6876
|
-
|
|
6877
|
-
int modified = 0;
|
|
6878
|
-
foreach (string guid in guids)
|
|
6879
|
-
{
|
|
6880
|
-
string path = AssetDatabase.GUIDToAssetPath(guid);
|
|
6881
|
-
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
|
|
6882
|
-
if (so == null) continue;
|
|
6883
|
-
|
|
6884
|
-
SerializedObject serializedObj = new SerializedObject(so);
|
|
6885
|
-
SerializedProperty prop = serializedObj.FindProperty("isEnabled");
|
|
6886
|
-
if (prop != null)
|
|
6887
|
-
{
|
|
6888
|
-
prop.boolValue = true;
|
|
6889
|
-
serializedObj.ApplyModifiedProperties();
|
|
6890
|
-
EditorUtility.SetDirty(so);
|
|
6891
|
-
modified++;
|
|
6892
|
-
}
|
|
6893
|
-
}
|
|
6894
|
-
|
|
6895
|
-
AssetDatabase.SaveAssets();
|
|
6896
|
-
return $"Enabled {modified} ScriptableObjects";
|
|
6897
|
-
\`\`\`
|
|
6898
|
-
|
|
6899
|
-
## Batch Remove Component
|
|
6900
|
-
|
|
6901
|
-
\`\`\`csharp
|
|
6902
|
-
using UnityEditor;
|
|
6903
|
-
|
|
6904
|
-
GameObject[] selected = Selection.gameObjects;
|
|
6905
|
-
if (selected.Length == 0)
|
|
6906
|
-
{
|
|
6907
|
-
return "No GameObjects selected";
|
|
6908
|
-
}
|
|
6909
|
-
|
|
6910
|
-
int undoGroup = Undo.GetCurrentGroup();
|
|
6911
|
-
Undo.SetCurrentGroupName("Batch Remove Rigidbody");
|
|
6912
|
-
|
|
6913
|
-
int removedCount = 0;
|
|
6914
|
-
foreach (GameObject obj in selected)
|
|
6915
|
-
{
|
|
6916
|
-
Rigidbody rb = obj.GetComponent<Rigidbody>();
|
|
6917
|
-
if (rb != null)
|
|
6918
|
-
{
|
|
6919
|
-
Undo.DestroyObjectImmediate(rb);
|
|
6920
|
-
removedCount++;
|
|
6921
|
-
}
|
|
6922
|
-
}
|
|
6923
|
-
|
|
6924
|
-
Undo.CollapseUndoOperations(undoGroup);
|
|
6925
|
-
return $"Removed Rigidbody from {removedCount} objects";
|
|
6926
|
-
\`\`\`
|
|
6927
|
-
|
|
6928
|
-
## Batch Set Static Flags
|
|
6929
|
-
|
|
6930
|
-
\`\`\`csharp
|
|
6931
|
-
using UnityEditor;
|
|
6932
|
-
|
|
6933
|
-
GameObject[] selected = Selection.gameObjects;
|
|
6934
|
-
if (selected.Length == 0)
|
|
6935
|
-
{
|
|
6936
|
-
return "No GameObjects selected";
|
|
6937
|
-
}
|
|
6938
|
-
|
|
6939
|
-
int undoGroup = Undo.GetCurrentGroup();
|
|
6940
|
-
Undo.SetCurrentGroupName("Batch Set Static");
|
|
6941
|
-
|
|
6942
|
-
foreach (GameObject obj in selected)
|
|
6943
|
-
{
|
|
6944
|
-
Undo.RecordObject(obj, "");
|
|
6945
|
-
GameObjectUtility.SetStaticEditorFlags(obj, StaticEditorFlags.BatchingStatic | StaticEditorFlags.OccludeeStatic);
|
|
6946
|
-
}
|
|
6947
|
-
|
|
6948
|
-
Undo.CollapseUndoOperations(undoGroup);
|
|
6949
|
-
return $"Set static flags on {selected.Length} objects";
|
|
6950
|
-
\`\`\`
|
|
6951
|
-
|
|
6952
|
-
## Batch Process with Progress Bar
|
|
6953
|
-
|
|
6954
|
-
\`\`\`csharp
|
|
6955
|
-
using UnityEditor;
|
|
6956
|
-
|
|
6957
|
-
string[] guids = AssetDatabase.FindAssets("t:Texture2D");
|
|
6958
|
-
if (guids.Length == 0)
|
|
6959
|
-
{
|
|
6960
|
-
return "No textures found";
|
|
6961
|
-
}
|
|
6962
|
-
|
|
6963
|
-
int processed = 0;
|
|
6964
|
-
foreach (string guid in guids)
|
|
6965
|
-
{
|
|
6966
|
-
string path = AssetDatabase.GUIDToAssetPath(guid);
|
|
6967
|
-
TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter;
|
|
6968
|
-
if (importer != null && importer.maxTextureSize > 1024)
|
|
6969
|
-
{
|
|
6970
|
-
importer.maxTextureSize = 1024;
|
|
6971
|
-
importer.SaveAndReimport();
|
|
6972
|
-
processed++;
|
|
6973
|
-
}
|
|
6974
|
-
|
|
6975
|
-
if (processed % 10 == 0)
|
|
6976
|
-
{
|
|
6977
|
-
EditorUtility.DisplayProgressBar("Processing Textures", path, (float)processed / guids.Length);
|
|
6978
|
-
}
|
|
6979
|
-
}
|
|
6980
|
-
|
|
6981
|
-
EditorUtility.ClearProgressBar();
|
|
6982
|
-
return $"Resized {processed} textures to max 1024";
|
|
6983
|
-
\`\`\`
|
|
6984
|
-
|
|
6985
|
-
## Batch Align Objects
|
|
6986
|
-
|
|
6987
|
-
\`\`\`csharp
|
|
6988
|
-
using UnityEditor;
|
|
6989
|
-
|
|
6990
|
-
GameObject[] selected = Selection.gameObjects;
|
|
6991
|
-
if (selected.Length < 2)
|
|
6992
|
-
{
|
|
6993
|
-
return "Select at least 2 objects";
|
|
6994
|
-
}
|
|
6995
|
-
|
|
6996
|
-
int undoGroup = Undo.GetCurrentGroup();
|
|
6997
|
-
Undo.SetCurrentGroupName("Align Objects");
|
|
6998
|
-
|
|
6999
|
-
float startX = selected[0].transform.position.x;
|
|
7000
|
-
float spacing = 2f;
|
|
7001
|
-
|
|
7002
|
-
for (int i = 0; i < selected.Length; i++)
|
|
7003
|
-
{
|
|
7004
|
-
Undo.RecordObject(selected[i].transform, "");
|
|
7005
|
-
Vector3 pos = selected[i].transform.position;
|
|
7006
|
-
pos.x = startX + (i * spacing);
|
|
7007
|
-
selected[i].transform.position = pos;
|
|
7008
|
-
}
|
|
7009
|
-
|
|
7010
|
-
Undo.CollapseUndoOperations(undoGroup);
|
|
7011
|
-
return $"Aligned {selected.Length} objects with {spacing}m spacing";
|
|
7012
|
-
\`\`\`
|
|
7013
|
-
|
|
7014
|
-
## Batch Rename Assets (Undo-supported)
|
|
7015
|
-
|
|
7016
|
-
\`\`\`csharp
|
|
7017
|
-
using UnityEditor;
|
|
7018
|
-
|
|
7019
|
-
// ObjectNames.SetNameSmart() supports Undo (AssetDatabase.RenameAsset() does NOT)
|
|
7020
|
-
Object[] selected = Selection.objects;
|
|
7021
|
-
if (selected.Length == 0)
|
|
7022
|
-
{
|
|
7023
|
-
return "No assets selected";
|
|
7024
|
-
}
|
|
7025
|
-
|
|
7026
|
-
for (int i = 0; i < selected.Length; i++)
|
|
7027
|
-
{
|
|
7028
|
-
string newName = $"{i:D3}_{selected[i].name}";
|
|
7029
|
-
ObjectNames.SetNameSmart(selected[i], newName);
|
|
7030
|
-
}
|
|
7031
|
-
|
|
7032
|
-
AssetDatabase.SaveAssets();
|
|
7033
|
-
return $"Renamed {selected.Length} assets";
|
|
7034
|
-
\`\`\`
|
|
7035
|
-
|
|
7036
|
-
## Batch Replace Material
|
|
7037
|
-
|
|
7038
|
-
\`\`\`csharp
|
|
7039
|
-
using UnityEditor;
|
|
7040
|
-
|
|
7041
|
-
GameObject[] selected = Selection.gameObjects;
|
|
7042
|
-
if (selected.Length == 0)
|
|
7043
|
-
{
|
|
7044
|
-
return "No GameObjects selected";
|
|
7045
|
-
}
|
|
7046
|
-
|
|
7047
|
-
string materialPath = "Assets/Materials/NewMaterial.mat";
|
|
7048
|
-
Material newMat = AssetDatabase.LoadAssetAtPath<Material>(materialPath);
|
|
7049
|
-
if (newMat == null)
|
|
7050
|
-
{
|
|
7051
|
-
return $"Material not found at {materialPath}";
|
|
7052
|
-
}
|
|
7053
|
-
|
|
7054
|
-
int undoGroup = Undo.GetCurrentGroup();
|
|
7055
|
-
Undo.SetCurrentGroupName("Batch Replace Material");
|
|
7056
|
-
|
|
7057
|
-
int replaced = 0;
|
|
7058
|
-
foreach (GameObject obj in selected)
|
|
7059
|
-
{
|
|
7060
|
-
MeshRenderer renderer = obj.GetComponent<MeshRenderer>();
|
|
7061
|
-
if (renderer != null)
|
|
7062
|
-
{
|
|
7063
|
-
Undo.RecordObject(renderer, "");
|
|
7064
|
-
renderer.sharedMaterial = newMat;
|
|
7065
|
-
replaced++;
|
|
7066
|
-
}
|
|
7067
|
-
}
|
|
7068
|
-
|
|
7069
|
-
Undo.CollapseUndoOperations(undoGroup);
|
|
7070
|
-
return $"Replaced material on {replaced} objects";
|
|
7071
|
-
\`\`\`
|
|
7072
|
-
|
|
7073
|
-
`,
|
|
7074
|
-
"examples/cleanup-operations.md": `# Cleanup Operations
|
|
7075
|
-
|
|
7076
|
-
Code examples for project cleanup operations using \`execute-dynamic-code\`.
|
|
7077
|
-
|
|
7078
|
-
## Detect Missing Scripts on GameObject
|
|
7079
|
-
|
|
7080
|
-
\`\`\`csharp
|
|
7081
|
-
using UnityEditor;
|
|
7082
|
-
|
|
7083
|
-
GameObject selected = Selection.activeGameObject;
|
|
7084
|
-
if (selected == null)
|
|
7085
|
-
{
|
|
7086
|
-
return "No GameObject selected";
|
|
7087
|
-
}
|
|
7088
|
-
|
|
7089
|
-
int missingCount = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(selected);
|
|
7090
|
-
return $"{selected.name} has {missingCount} missing script(s)";
|
|
7091
|
-
\`\`\`
|
|
7092
|
-
|
|
7093
|
-
## Remove Missing Scripts from GameObject
|
|
7094
|
-
|
|
7095
|
-
\`\`\`csharp
|
|
7096
|
-
using UnityEditor;
|
|
7097
|
-
|
|
7098
|
-
GameObject selected = Selection.activeGameObject;
|
|
7099
|
-
if (selected == null)
|
|
7100
|
-
{
|
|
7101
|
-
return "No GameObject selected";
|
|
7102
|
-
}
|
|
7103
|
-
|
|
7104
|
-
int removedCount = GameObjectUtility.RemoveMonoBehavioursWithMissingScript(selected);
|
|
7105
|
-
return $"Removed {removedCount} missing script(s) from {selected.name}";
|
|
7106
|
-
\`\`\`
|
|
7107
|
-
|
|
7108
|
-
## Scan Scene for Missing Scripts
|
|
7109
|
-
|
|
7110
|
-
\`\`\`csharp
|
|
7111
|
-
using UnityEditor;
|
|
7112
|
-
|
|
7113
|
-
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
|
|
7114
|
-
List<string> objectsWithMissing = new List<string>();
|
|
7115
|
-
|
|
7116
|
-
foreach (GameObject obj in allObjects)
|
|
7117
|
-
{
|
|
7118
|
-
int count = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(obj);
|
|
7119
|
-
if (count > 0)
|
|
7120
|
-
{
|
|
7121
|
-
objectsWithMissing.Add($"{obj.name} ({count})");
|
|
7122
|
-
}
|
|
7123
|
-
}
|
|
7124
|
-
|
|
7125
|
-
if (objectsWithMissing.Count == 0)
|
|
7126
|
-
{
|
|
7127
|
-
return "No missing scripts found in scene";
|
|
7128
|
-
}
|
|
7129
|
-
|
|
7130
|
-
return $"Objects with missing scripts: {string.Join(", ", objectsWithMissing)}";
|
|
7131
|
-
\`\`\`
|
|
7132
|
-
|
|
7133
|
-
## Remove All Missing Scripts from Scene
|
|
7134
|
-
|
|
7135
|
-
\`\`\`csharp
|
|
7136
|
-
using UnityEditor;
|
|
7137
|
-
|
|
7138
|
-
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
|
|
7139
|
-
int totalRemoved = 0;
|
|
7140
|
-
|
|
7141
|
-
int undoGroup = Undo.GetCurrentGroup();
|
|
7142
|
-
Undo.SetCurrentGroupName("Remove All Missing Scripts");
|
|
7143
|
-
|
|
7144
|
-
foreach (GameObject obj in allObjects)
|
|
7145
|
-
{
|
|
7146
|
-
int removed = GameObjectUtility.RemoveMonoBehavioursWithMissingScript(obj);
|
|
7147
|
-
totalRemoved += removed;
|
|
7148
|
-
}
|
|
7149
|
-
|
|
7150
|
-
Undo.CollapseUndoOperations(undoGroup);
|
|
7151
|
-
return $"Removed {totalRemoved} missing scripts from scene";
|
|
7152
|
-
\`\`\`
|
|
7153
|
-
|
|
7154
|
-
## Detect Missing References in Component
|
|
7155
|
-
|
|
7156
|
-
\`\`\`csharp
|
|
7157
|
-
using UnityEditor;
|
|
7158
|
-
|
|
7159
|
-
GameObject selected = Selection.activeGameObject;
|
|
7160
|
-
if (selected == null)
|
|
7161
|
-
{
|
|
7162
|
-
return "No GameObject selected";
|
|
7163
|
-
}
|
|
7164
|
-
|
|
7165
|
-
List<string> missingRefs = new List<string>();
|
|
7166
|
-
|
|
7167
|
-
Component[] components = selected.GetComponents<Component>();
|
|
7168
|
-
foreach (Component comp in components)
|
|
7169
|
-
{
|
|
7170
|
-
if (comp == null) continue;
|
|
7171
|
-
|
|
7172
|
-
SerializedObject so = new SerializedObject(comp);
|
|
7173
|
-
SerializedProperty prop = so.GetIterator();
|
|
7174
|
-
|
|
7175
|
-
while (prop.NextVisible(true))
|
|
7176
|
-
{
|
|
7177
|
-
if (prop.propertyType == SerializedPropertyType.ObjectReference)
|
|
7178
|
-
{
|
|
7179
|
-
if (prop.objectReferenceValue == null && prop.objectReferenceInstanceIDValue != 0)
|
|
7180
|
-
{
|
|
7181
|
-
missingRefs.Add($"{comp.GetType().Name}.{prop.name}");
|
|
7182
|
-
}
|
|
7183
|
-
}
|
|
7184
|
-
}
|
|
7185
|
-
}
|
|
7186
|
-
|
|
7187
|
-
if (missingRefs.Count == 0)
|
|
7188
|
-
{
|
|
7189
|
-
return "No missing references found";
|
|
7190
|
-
}
|
|
7191
|
-
|
|
7192
|
-
return $"Missing references: {string.Join(", ", missingRefs)}";
|
|
7193
|
-
\`\`\`
|
|
7194
|
-
|
|
7195
|
-
## Scan Scene for Missing References
|
|
7196
|
-
|
|
7197
|
-
\`\`\`csharp
|
|
7198
|
-
using UnityEditor;
|
|
7199
|
-
|
|
7200
|
-
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
|
|
7201
|
-
List<string> results = new List<string>();
|
|
7202
|
-
|
|
7203
|
-
foreach (GameObject obj in allObjects)
|
|
7204
|
-
{
|
|
7205
|
-
Component[] components = obj.GetComponents<Component>();
|
|
7206
|
-
foreach (Component comp in components)
|
|
7207
|
-
{
|
|
7208
|
-
if (comp == null) continue;
|
|
7209
|
-
|
|
7210
|
-
SerializedObject so = new SerializedObject(comp);
|
|
7211
|
-
SerializedProperty prop = so.GetIterator();
|
|
7212
|
-
|
|
7213
|
-
while (prop.NextVisible(true))
|
|
7214
|
-
{
|
|
7215
|
-
if (prop.propertyType == SerializedPropertyType.ObjectReference)
|
|
7216
|
-
{
|
|
7217
|
-
if (prop.objectReferenceValue == null && prop.objectReferenceInstanceIDValue != 0)
|
|
7218
|
-
{
|
|
7219
|
-
results.Add($"{obj.name}/{comp.GetType().Name}.{prop.name}");
|
|
7220
|
-
}
|
|
7221
|
-
}
|
|
7222
|
-
}
|
|
7223
|
-
}
|
|
7224
|
-
}
|
|
7225
|
-
|
|
7226
|
-
if (results.Count == 0)
|
|
7227
|
-
{
|
|
7228
|
-
return "No missing references found in scene";
|
|
7229
|
-
}
|
|
7230
|
-
|
|
7231
|
-
return $"Missing references ({results.Count}): {string.Join(", ", results.Take(10))}...";
|
|
7232
|
-
\`\`\`
|
|
7233
|
-
|
|
7234
|
-
## Find Unused Materials in Project
|
|
7235
|
-
|
|
7236
|
-
\`\`\`csharp
|
|
7237
|
-
using UnityEditor;
|
|
7238
|
-
|
|
7239
|
-
string[] materialGuids = AssetDatabase.FindAssets("t:Material");
|
|
7240
|
-
HashSet<string> usedMaterials = new HashSet<string>();
|
|
7241
|
-
|
|
7242
|
-
string[] prefabGuids = AssetDatabase.FindAssets("t:Prefab");
|
|
7243
|
-
foreach (string guid in prefabGuids)
|
|
7244
|
-
{
|
|
7245
|
-
string path = AssetDatabase.GUIDToAssetPath(guid);
|
|
7246
|
-
string[] deps = AssetDatabase.GetDependencies(path, true);
|
|
7247
|
-
foreach (string dep in deps)
|
|
7248
|
-
{
|
|
7249
|
-
if (dep.EndsWith(".mat"))
|
|
7250
|
-
{
|
|
7251
|
-
usedMaterials.Add(dep);
|
|
7252
|
-
}
|
|
7253
|
-
}
|
|
7254
|
-
}
|
|
7255
|
-
|
|
7256
|
-
List<string> unusedMaterials = new List<string>();
|
|
7257
|
-
foreach (string guid in materialGuids)
|
|
7258
|
-
{
|
|
7259
|
-
string path = AssetDatabase.GUIDToAssetPath(guid);
|
|
7260
|
-
if (!usedMaterials.Contains(path))
|
|
7261
|
-
{
|
|
7262
|
-
unusedMaterials.Add(path);
|
|
7263
|
-
}
|
|
7264
|
-
}
|
|
7265
|
-
|
|
7266
|
-
return $"Found {unusedMaterials.Count} potentially unused materials";
|
|
7267
|
-
\`\`\`
|
|
7268
|
-
|
|
7269
|
-
## Find Empty GameObjects
|
|
7270
|
-
|
|
7271
|
-
\`\`\`csharp
|
|
7272
|
-
using UnityEditor;
|
|
7273
|
-
|
|
7274
|
-
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
|
|
7275
|
-
List<string> emptyObjects = new List<string>();
|
|
7276
|
-
|
|
7277
|
-
foreach (GameObject obj in allObjects)
|
|
7278
|
-
{
|
|
7279
|
-
Component[] components = obj.GetComponents<Component>();
|
|
7280
|
-
if (components.Length == 1 && obj.transform.childCount == 0)
|
|
7281
|
-
{
|
|
7282
|
-
emptyObjects.Add(obj.name);
|
|
7283
|
-
}
|
|
7284
|
-
}
|
|
7285
|
-
|
|
7286
|
-
if (emptyObjects.Count == 0)
|
|
7287
|
-
{
|
|
7288
|
-
return "No empty GameObjects found";
|
|
7289
|
-
}
|
|
7290
|
-
|
|
7291
|
-
return $"Empty objects ({emptyObjects.Count}): {string.Join(", ", emptyObjects.Take(20))}";
|
|
7292
|
-
\`\`\`
|
|
7293
|
-
|
|
7294
|
-
## Find Duplicate Names in Hierarchy
|
|
7295
|
-
|
|
7296
|
-
\`\`\`csharp
|
|
7297
|
-
using UnityEditor;
|
|
7298
|
-
|
|
7299
|
-
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
|
|
7300
|
-
Dictionary<string, int> nameCounts = new Dictionary<string, int>();
|
|
7301
|
-
|
|
7302
|
-
foreach (GameObject obj in allObjects)
|
|
7303
|
-
{
|
|
7304
|
-
if (nameCounts.ContainsKey(obj.name))
|
|
7305
|
-
{
|
|
7306
|
-
nameCounts[obj.name]++;
|
|
7307
|
-
}
|
|
7308
|
-
else
|
|
7309
|
-
{
|
|
7310
|
-
nameCounts[obj.name] = 1;
|
|
7311
|
-
}
|
|
7312
|
-
}
|
|
7313
|
-
|
|
7314
|
-
List<string> duplicates = new List<string>();
|
|
7315
|
-
foreach (KeyValuePair<string, int> kvp in nameCounts)
|
|
7316
|
-
{
|
|
7317
|
-
if (kvp.Value > 1)
|
|
7318
|
-
{
|
|
7319
|
-
duplicates.Add($"{kvp.Key} ({kvp.Value})");
|
|
7320
|
-
}
|
|
7321
|
-
}
|
|
7322
|
-
|
|
7323
|
-
if (duplicates.Count == 0)
|
|
7324
|
-
{
|
|
7325
|
-
return "No duplicate names found";
|
|
7326
|
-
}
|
|
7327
|
-
|
|
7328
|
-
return $"Duplicate names: {string.Join(", ", duplicates.Take(15))}";
|
|
7329
|
-
\`\`\`
|
|
7330
|
-
|
|
7331
|
-
## Check for Broken Prefab Instances
|
|
7332
|
-
|
|
7333
|
-
\`\`\`csharp
|
|
7334
|
-
using UnityEditor;
|
|
7335
|
-
|
|
7336
|
-
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
|
|
7337
|
-
List<string> brokenPrefabs = new List<string>();
|
|
7338
|
-
|
|
7339
|
-
foreach (GameObject obj in allObjects)
|
|
7340
|
-
{
|
|
7341
|
-
if (PrefabUtility.IsPartOfPrefabInstance(obj))
|
|
7342
|
-
{
|
|
7343
|
-
GameObject prefabAsset = PrefabUtility.GetCorrespondingObjectFromSource(obj);
|
|
7344
|
-
if (prefabAsset == null)
|
|
7345
|
-
{
|
|
7346
|
-
brokenPrefabs.Add(obj.name);
|
|
7347
|
-
}
|
|
7348
|
-
}
|
|
7349
|
-
}
|
|
7350
|
-
|
|
7351
|
-
if (brokenPrefabs.Count == 0)
|
|
7352
|
-
{
|
|
7353
|
-
return "No broken prefab instances found";
|
|
7354
|
-
}
|
|
7355
|
-
|
|
7356
|
-
return $"Broken prefab instances: {string.Join(", ", brokenPrefabs)}";
|
|
7357
|
-
\`\`\`
|
|
7358
|
-
|
|
7359
|
-
## Find Objects with Negative Scale
|
|
7360
|
-
|
|
7361
|
-
\`\`\`csharp
|
|
7362
|
-
using UnityEditor;
|
|
7363
|
-
|
|
7364
|
-
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
|
|
7365
|
-
List<string> negativeScale = new List<string>();
|
|
7366
|
-
|
|
7367
|
-
foreach (GameObject obj in allObjects)
|
|
7368
|
-
{
|
|
7369
|
-
Vector3 scale = obj.transform.localScale;
|
|
7370
|
-
if (scale.x < 0 || scale.y < 0 || scale.z < 0)
|
|
7371
|
-
{
|
|
7372
|
-
negativeScale.Add($"{obj.name} ({scale})");
|
|
7373
|
-
}
|
|
7374
|
-
}
|
|
7375
|
-
|
|
7376
|
-
if (negativeScale.Count == 0)
|
|
7377
|
-
{
|
|
7378
|
-
return "No objects with negative scale found";
|
|
7379
|
-
}
|
|
7380
|
-
|
|
7381
|
-
return $"Negative scale objects: {string.Join(", ", negativeScale.Take(10))}";
|
|
7382
|
-
\`\`\`
|
|
7383
|
-
|
|
7384
|
-
## Remove Empty Parent GameObjects
|
|
7385
|
-
|
|
7386
|
-
\`\`\`csharp
|
|
7387
|
-
using UnityEditor;
|
|
7388
|
-
|
|
7389
|
-
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
|
|
7390
|
-
|
|
7391
|
-
int undoGroup = Undo.GetCurrentGroup();
|
|
7392
|
-
Undo.SetCurrentGroupName("Remove Empty Parents");
|
|
7393
|
-
|
|
7394
|
-
int removedCount = 0;
|
|
7395
|
-
foreach (GameObject obj in allObjects)
|
|
7396
|
-
{
|
|
7397
|
-
if (obj == null) continue;
|
|
7398
|
-
|
|
7399
|
-
Component[] components = obj.GetComponents<Component>();
|
|
7400
|
-
if (components.Length == 1 && obj.transform.childCount == 0)
|
|
7401
|
-
{
|
|
7402
|
-
Undo.DestroyObjectImmediate(obj);
|
|
7403
|
-
removedCount++;
|
|
7404
|
-
}
|
|
7405
|
-
}
|
|
7406
|
-
|
|
7407
|
-
Undo.CollapseUndoOperations(undoGroup);
|
|
7408
|
-
return $"Removed {removedCount} empty GameObjects";
|
|
7409
|
-
\`\`\`
|
|
7410
|
-
|
|
7411
|
-
## Find Large Meshes
|
|
7412
|
-
|
|
7413
|
-
\`\`\`csharp
|
|
7414
|
-
using UnityEditor;
|
|
7415
|
-
|
|
7416
|
-
string[] meshGuids = AssetDatabase.FindAssets("t:Mesh");
|
|
7417
|
-
List<string> largeMeshes = new List<string>();
|
|
7418
|
-
int threshold = 10000;
|
|
7419
|
-
|
|
7420
|
-
foreach (string guid in meshGuids)
|
|
7421
|
-
{
|
|
7422
|
-
string path = AssetDatabase.GUIDToAssetPath(guid);
|
|
7423
|
-
Mesh mesh = AssetDatabase.LoadAssetAtPath<Mesh>(path);
|
|
7424
|
-
if (mesh != null && mesh.vertexCount > threshold)
|
|
7425
|
-
{
|
|
7426
|
-
largeMeshes.Add($"{path} ({mesh.vertexCount} verts)");
|
|
7427
|
-
}
|
|
7428
|
-
}
|
|
7429
|
-
|
|
7430
|
-
if (largeMeshes.Count == 0)
|
|
7431
|
-
{
|
|
7432
|
-
return $"No meshes with more than {threshold} vertices found";
|
|
7433
|
-
}
|
|
7434
|
-
|
|
7435
|
-
return $"Large meshes: {string.Join(", ", largeMeshes.Take(10))}";
|
|
7436
|
-
\`\`\`
|
|
7437
|
-
|
|
7438
|
-
## Validate Asset References
|
|
7439
|
-
|
|
7440
|
-
\`\`\`csharp
|
|
7441
|
-
using UnityEditor;
|
|
7442
|
-
|
|
7443
|
-
string[] guids = AssetDatabase.FindAssets("t:ScriptableObject", new[] { "Assets/Data" });
|
|
7444
|
-
List<string> invalidRefs = new List<string>();
|
|
7445
|
-
|
|
7446
|
-
foreach (string guid in guids)
|
|
7447
|
-
{
|
|
7448
|
-
string path = AssetDatabase.GUIDToAssetPath(guid);
|
|
7449
|
-
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
|
|
7450
|
-
if (so == null) continue;
|
|
7451
|
-
|
|
7452
|
-
SerializedObject serializedObj = new SerializedObject(so);
|
|
7453
|
-
SerializedProperty prop = serializedObj.GetIterator();
|
|
7454
|
-
|
|
7455
|
-
while (prop.NextVisible(true))
|
|
7456
|
-
{
|
|
7457
|
-
if (prop.propertyType == SerializedPropertyType.ObjectReference)
|
|
7458
|
-
{
|
|
7459
|
-
if (prop.objectReferenceValue == null && prop.objectReferenceInstanceIDValue != 0)
|
|
7460
|
-
{
|
|
7461
|
-
invalidRefs.Add($"{path}: {prop.name}");
|
|
7462
|
-
}
|
|
7463
|
-
}
|
|
7464
|
-
}
|
|
7465
|
-
}
|
|
7466
|
-
|
|
7467
|
-
if (invalidRefs.Count == 0)
|
|
7468
|
-
{
|
|
7469
|
-
return "All asset references are valid";
|
|
7470
|
-
}
|
|
7471
|
-
|
|
7472
|
-
return $"Invalid references ({invalidRefs.Count}): {string.Join(", ", invalidRefs.Take(10))}";
|
|
7473
|
-
\`\`\`
|
|
7474
|
-
|
|
7475
|
-
`,
|
|
7476
|
-
"examples/material-operations.md": `# Material Operations
|
|
7477
|
-
|
|
7478
|
-
Code examples for Material operations using \`execute-dynamic-code\`.
|
|
7479
|
-
|
|
7480
|
-
## Create a New Material
|
|
7481
|
-
|
|
7482
|
-
\`\`\`csharp
|
|
7483
|
-
using UnityEditor;
|
|
7484
|
-
|
|
7485
|
-
Shader shader = Shader.Find("Standard");
|
|
7486
|
-
Material mat = new Material(shader);
|
|
7487
|
-
mat.name = "MyMaterial";
|
|
7488
|
-
string path = "Assets/Materials/MyMaterial.mat";
|
|
7489
|
-
AssetDatabase.CreateAsset(mat, path);
|
|
7490
|
-
AssetDatabase.SaveAssets();
|
|
7491
|
-
return $"Material created at {path}";
|
|
7492
|
-
\`\`\`
|
|
7493
|
-
|
|
7494
|
-
## Set Material Color
|
|
7495
|
-
|
|
7496
|
-
\`\`\`csharp
|
|
7497
|
-
using UnityEditor;
|
|
7498
|
-
|
|
7499
|
-
string matPath = "Assets/Materials/MyMaterial.mat";
|
|
7500
|
-
Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
|
|
7501
|
-
mat.SetColor("_Color", new Color(1f, 0.5f, 0f, 1f));
|
|
7502
|
-
EditorUtility.SetDirty(mat);
|
|
7503
|
-
AssetDatabase.SaveAssets();
|
|
7504
|
-
return "Material color set to orange";
|
|
7505
|
-
\`\`\`
|
|
7506
|
-
|
|
7507
|
-
## Set Material Properties (Float, Vector)
|
|
7508
|
-
|
|
7509
|
-
\`\`\`csharp
|
|
7510
|
-
using UnityEditor;
|
|
7511
|
-
|
|
7512
|
-
string matPath = "Assets/Materials/MyMaterial.mat";
|
|
7513
|
-
Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
|
|
7514
|
-
|
|
7515
|
-
mat.SetFloat("_Metallic", 0.8f);
|
|
7516
|
-
mat.SetFloat("_Glossiness", 0.6f);
|
|
7517
|
-
mat.SetVector("_EmissionColor", new Vector4(1, 1, 0, 1));
|
|
7518
|
-
|
|
7519
|
-
EditorUtility.SetDirty(mat);
|
|
7520
|
-
AssetDatabase.SaveAssets();
|
|
7521
|
-
return "Material properties updated";
|
|
7522
|
-
\`\`\`
|
|
7523
|
-
|
|
7524
|
-
## Assign Texture to Material
|
|
7525
|
-
|
|
7526
|
-
\`\`\`csharp
|
|
7527
|
-
using UnityEditor;
|
|
7528
|
-
|
|
7529
|
-
string matPath = "Assets/Materials/MyMaterial.mat";
|
|
7530
|
-
string texPath = "Assets/Textures/MyTexture.png";
|
|
7531
|
-
|
|
7532
|
-
Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
|
|
7533
|
-
Texture2D tex = AssetDatabase.LoadAssetAtPath<Texture2D>(texPath);
|
|
7534
|
-
|
|
7535
|
-
mat.SetTexture("_MainTex", tex);
|
|
7536
|
-
EditorUtility.SetDirty(mat);
|
|
7537
|
-
AssetDatabase.SaveAssets();
|
|
7538
|
-
return $"Assigned {tex.name} to material";
|
|
7539
|
-
\`\`\`
|
|
7540
|
-
|
|
7541
|
-
## Assign Material to GameObject
|
|
7542
|
-
|
|
7543
|
-
\`\`\`csharp
|
|
7544
|
-
using UnityEditor;
|
|
7545
|
-
|
|
7546
|
-
string matPath = "Assets/Materials/MyMaterial.mat";
|
|
7547
|
-
Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
|
|
7548
|
-
|
|
7549
|
-
GameObject selected = Selection.activeGameObject;
|
|
7550
|
-
if (selected == null)
|
|
7551
|
-
{
|
|
7552
|
-
return "No GameObject selected";
|
|
7553
|
-
}
|
|
7554
|
-
|
|
7555
|
-
Renderer renderer = selected.GetComponent<Renderer>();
|
|
7556
|
-
if (renderer == null)
|
|
7557
|
-
{
|
|
7558
|
-
return "Selected object has no Renderer";
|
|
7559
|
-
}
|
|
7560
|
-
|
|
7561
|
-
renderer.sharedMaterial = mat;
|
|
7562
|
-
EditorUtility.SetDirty(selected);
|
|
7563
|
-
return $"Assigned {mat.name} to {selected.name}";
|
|
7564
|
-
\`\`\`
|
|
7565
|
-
|
|
7566
|
-
## Enable/Disable Material Keywords
|
|
7567
|
-
|
|
7568
|
-
\`\`\`csharp
|
|
7569
|
-
using UnityEditor;
|
|
7570
|
-
|
|
7571
|
-
string matPath = "Assets/Materials/MyMaterial.mat";
|
|
7572
|
-
Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
|
|
7573
|
-
|
|
7574
|
-
mat.EnableKeyword("_EMISSION");
|
|
7575
|
-
mat.globalIlluminationFlags = MaterialGlobalIlluminationFlags.RealtimeEmissive;
|
|
7576
|
-
|
|
7577
|
-
EditorUtility.SetDirty(mat);
|
|
7578
|
-
AssetDatabase.SaveAssets();
|
|
7579
|
-
return "Emission enabled on material";
|
|
7580
|
-
\`\`\`
|
|
7581
|
-
|
|
7582
|
-
## Find All Materials Using a Shader
|
|
7583
|
-
|
|
7584
|
-
\`\`\`csharp
|
|
7585
|
-
using UnityEditor;
|
|
7586
|
-
|
|
7587
|
-
string shaderName = "Standard";
|
|
7588
|
-
string[] guids = AssetDatabase.FindAssets("t:Material");
|
|
7589
|
-
List<string> matchingMaterials = new List<string>();
|
|
7590
|
-
|
|
7591
|
-
foreach (string guid in guids)
|
|
7592
|
-
{
|
|
7593
|
-
string path = AssetDatabase.GUIDToAssetPath(guid);
|
|
7594
|
-
Material mat = AssetDatabase.LoadAssetAtPath<Material>(path);
|
|
7595
|
-
if (mat != null && mat.shader != null && mat.shader.name == shaderName)
|
|
7596
|
-
{
|
|
7597
|
-
matchingMaterials.Add(path);
|
|
7598
|
-
}
|
|
7599
|
-
}
|
|
7600
|
-
return $"Found {matchingMaterials.Count} materials using {shaderName}";
|
|
7601
|
-
\`\`\`
|
|
7602
|
-
|
|
7603
|
-
## Duplicate Material
|
|
7604
|
-
|
|
7605
|
-
\`\`\`csharp
|
|
7606
|
-
using UnityEditor;
|
|
7607
|
-
|
|
7608
|
-
string sourcePath = "Assets/Materials/MyMaterial.mat";
|
|
7609
|
-
string destPath = "Assets/Materials/MyMaterial_Copy.mat";
|
|
7610
|
-
|
|
7611
|
-
Material source = AssetDatabase.LoadAssetAtPath<Material>(sourcePath);
|
|
7612
|
-
Material copy = new Material(source);
|
|
7613
|
-
AssetDatabase.CreateAsset(copy, destPath);
|
|
7614
|
-
AssetDatabase.SaveAssets();
|
|
7615
|
-
return $"Material duplicated to {destPath}";
|
|
7616
|
-
\`\`\`
|
|
7617
|
-
`,
|
|
7618
|
-
"examples/prefab-operations.md": `# Prefab Operations
|
|
7619
|
-
|
|
7620
|
-
Code examples for Prefab operations using \`execute-dynamic-code\`.
|
|
7621
|
-
|
|
7622
|
-
## Create a Prefab from GameObject
|
|
7623
|
-
|
|
7624
|
-
\`\`\`csharp
|
|
7625
|
-
using UnityEditor;
|
|
7626
|
-
|
|
7627
|
-
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
7628
|
-
cube.name = "MyCube";
|
|
7629
|
-
string path = "Assets/Prefabs/MyCube.prefab";
|
|
7630
|
-
PrefabUtility.SaveAsPrefabAsset(cube, path);
|
|
7631
|
-
Object.DestroyImmediate(cube);
|
|
7632
|
-
return $"Prefab created at {path}";
|
|
7633
|
-
\`\`\`
|
|
7634
|
-
|
|
7635
|
-
## Instantiate a Prefab
|
|
7636
|
-
|
|
7637
|
-
\`\`\`csharp
|
|
7638
|
-
using UnityEditor;
|
|
7639
|
-
|
|
7640
|
-
string prefabPath = "Assets/Prefabs/MyCube.prefab";
|
|
7641
|
-
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
|
7642
|
-
if (prefab == null)
|
|
7643
|
-
{
|
|
7644
|
-
return $"Prefab not found at {prefabPath}";
|
|
7645
|
-
}
|
|
7646
|
-
|
|
7647
|
-
GameObject instance = (GameObject)PrefabUtility.InstantiatePrefab(prefab);
|
|
7648
|
-
instance.transform.position = new Vector3(0, 1, 0);
|
|
7649
|
-
return $"Instantiated {instance.name}";
|
|
7650
|
-
\`\`\`
|
|
7651
|
-
|
|
7652
|
-
## Add Component to Prefab
|
|
7653
|
-
|
|
7654
|
-
\`\`\`csharp
|
|
7655
|
-
using UnityEditor;
|
|
7656
|
-
|
|
7657
|
-
string prefabPath = "Assets/Prefabs/MyCube.prefab";
|
|
7658
|
-
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
|
7659
|
-
string assetPath = AssetDatabase.GetAssetPath(prefab);
|
|
7660
|
-
|
|
7661
|
-
using (PrefabUtility.EditPrefabContentsScope scope = new PrefabUtility.EditPrefabContentsScope(assetPath))
|
|
7662
|
-
{
|
|
7663
|
-
GameObject root = scope.prefabContentsRoot;
|
|
7664
|
-
if (root.GetComponent<Rigidbody>() == null)
|
|
7665
|
-
{
|
|
7666
|
-
root.AddComponent<Rigidbody>();
|
|
7667
|
-
}
|
|
7668
|
-
}
|
|
7669
|
-
return "Added Rigidbody to prefab";
|
|
7670
|
-
\`\`\`
|
|
7671
|
-
|
|
7672
|
-
## Modify Prefab Properties
|
|
7673
|
-
|
|
7674
|
-
\`\`\`csharp
|
|
7675
|
-
using UnityEditor;
|
|
7676
|
-
|
|
7677
|
-
string prefabPath = "Assets/Prefabs/MyCube.prefab";
|
|
7678
|
-
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
|
7679
|
-
|
|
7680
|
-
using (PrefabUtility.EditPrefabContentsScope scope = new PrefabUtility.EditPrefabContentsScope(prefabPath))
|
|
7681
|
-
{
|
|
7682
|
-
GameObject root = scope.prefabContentsRoot;
|
|
7683
|
-
root.transform.localScale = new Vector3(2, 2, 2);
|
|
7684
|
-
|
|
7685
|
-
MeshRenderer renderer = root.GetComponent<MeshRenderer>();
|
|
7686
|
-
if (renderer != null)
|
|
7687
|
-
{
|
|
7688
|
-
renderer.sharedMaterial.color = Color.red;
|
|
7689
|
-
}
|
|
7690
|
-
}
|
|
7691
|
-
return "Modified prefab properties";
|
|
7692
|
-
\`\`\`
|
|
7693
|
-
|
|
7694
|
-
## Find All Prefab Instances in Scene
|
|
7695
|
-
|
|
7696
|
-
\`\`\`csharp
|
|
7697
|
-
using UnityEditor;
|
|
7698
|
-
using System.Collections.Generic;
|
|
7699
|
-
|
|
7700
|
-
string prefabPath = "Assets/Prefabs/MyCube.prefab";
|
|
7701
|
-
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
|
7702
|
-
if (prefab == null)
|
|
7703
|
-
{
|
|
7704
|
-
return $"Prefab not found at {prefabPath}";
|
|
7705
|
-
}
|
|
7706
|
-
|
|
7707
|
-
List<GameObject> instances = new List<GameObject>();
|
|
7708
|
-
|
|
7709
|
-
foreach (GameObject obj in Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None))
|
|
7710
|
-
{
|
|
7711
|
-
if (PrefabUtility.GetCorrespondingObjectFromSource(obj) == prefab)
|
|
7712
|
-
{
|
|
7713
|
-
instances.Add(obj);
|
|
7714
|
-
}
|
|
7715
|
-
}
|
|
7716
|
-
return $"Found {instances.Count} instances of {prefab.name}";
|
|
7717
|
-
\`\`\`
|
|
7718
|
-
|
|
7719
|
-
## Apply Prefab Overrides
|
|
7720
|
-
|
|
7721
|
-
\`\`\`csharp
|
|
7722
|
-
using UnityEditor;
|
|
7723
|
-
|
|
7724
|
-
GameObject selected = Selection.activeGameObject;
|
|
7725
|
-
if (selected == null)
|
|
7726
|
-
{
|
|
7727
|
-
return "No GameObject selected";
|
|
7728
|
-
}
|
|
7729
|
-
|
|
7730
|
-
if (!PrefabUtility.IsPartOfPrefabInstance(selected))
|
|
7731
|
-
{
|
|
7732
|
-
return "Selected object is not a prefab instance";
|
|
7733
|
-
}
|
|
7734
|
-
|
|
7735
|
-
PrefabUtility.ApplyPrefabInstance(selected, InteractionMode.UserAction);
|
|
7736
|
-
return $"Applied overrides from {selected.name} to prefab";
|
|
7737
|
-
\`\`\`
|
|
7738
|
-
`,
|
|
7739
|
-
"examples/scene-operations.md": `# Scene Operations
|
|
7740
|
-
|
|
7741
|
-
Code examples for Scene and Hierarchy operations using \`execute-dynamic-code\`.
|
|
7742
|
-
|
|
7743
|
-
## Create GameObject
|
|
7744
|
-
|
|
7745
|
-
\`\`\`csharp
|
|
7746
|
-
GameObject obj = new GameObject("MyObject");
|
|
7747
|
-
obj.transform.position = new Vector3(0, 1, 0);
|
|
7748
|
-
return $"Created {obj.name}";
|
|
7749
|
-
\`\`\`
|
|
7750
|
-
|
|
7751
|
-
## Create UI GameObject (under Canvas)
|
|
7752
|
-
|
|
7753
|
-
\`\`\`csharp
|
|
7754
|
-
using UnityEngine.UI;
|
|
7755
|
-
|
|
7756
|
-
// UI objects require RectTransform, which is auto-added when parented to Canvas
|
|
7757
|
-
GameObject canvas = GameObject.Find("Canvas");
|
|
7758
|
-
if (canvas == null)
|
|
7759
|
-
{
|
|
7760
|
-
return "Canvas not found in scene";
|
|
7761
|
-
}
|
|
7762
|
-
|
|
7763
|
-
GameObject uiObj = new GameObject("MyUIElement");
|
|
7764
|
-
uiObj.transform.SetParent(canvas.transform, false);
|
|
7765
|
-
uiObj.AddComponent<RectTransform>();
|
|
7766
|
-
uiObj.AddComponent<Image>();
|
|
7767
|
-
return $"Created UI element: {uiObj.name}";
|
|
7768
|
-
\`\`\`
|
|
7769
|
-
|
|
7770
|
-
## Create Primitive
|
|
7771
|
-
|
|
7772
|
-
\`\`\`csharp
|
|
7773
|
-
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
7774
|
-
cube.name = "MyCube";
|
|
7775
|
-
cube.transform.position = new Vector3(2, 0, 0);
|
|
7776
|
-
return $"Created {cube.name}";
|
|
7777
|
-
\`\`\`
|
|
7778
|
-
|
|
7779
|
-
## Add Component to GameObject
|
|
7780
|
-
|
|
7781
|
-
\`\`\`csharp
|
|
7782
|
-
GameObject selected = Selection.activeGameObject;
|
|
7783
|
-
if (selected == null)
|
|
7784
|
-
{
|
|
7785
|
-
return "No GameObject selected";
|
|
7786
|
-
}
|
|
7787
|
-
|
|
7788
|
-
Rigidbody rb = selected.AddComponent<Rigidbody>();
|
|
7789
|
-
rb.mass = 2f;
|
|
7790
|
-
rb.useGravity = true;
|
|
7791
|
-
return $"Added Rigidbody to {selected.name}";
|
|
7792
|
-
\`\`\`
|
|
7793
|
-
|
|
7794
|
-
## Find GameObject by Name
|
|
7795
|
-
|
|
7796
|
-
\`\`\`csharp
|
|
7797
|
-
GameObject obj = GameObject.Find("Player");
|
|
7798
|
-
if (obj == null)
|
|
7799
|
-
{
|
|
7800
|
-
return "GameObject 'Player' not found";
|
|
7801
|
-
}
|
|
7802
|
-
return $"Found: {obj.name} at {obj.transform.position}";
|
|
7803
|
-
\`\`\`
|
|
7804
|
-
|
|
7805
|
-
## Find GameObjects by Tag
|
|
7806
|
-
|
|
7807
|
-
\`\`\`csharp
|
|
7808
|
-
GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");
|
|
7809
|
-
return $"Found {enemies.Length} GameObjects with tag 'Enemy'";
|
|
7810
|
-
\`\`\`
|
|
7811
|
-
|
|
7812
|
-
## Set Parent
|
|
7813
|
-
|
|
7814
|
-
\`\`\`csharp
|
|
7815
|
-
GameObject child = GameObject.Find("Child");
|
|
7816
|
-
GameObject parent = GameObject.Find("Parent");
|
|
7817
|
-
|
|
7818
|
-
if (child == null || parent == null)
|
|
7819
|
-
{
|
|
7820
|
-
return "Child or Parent not found";
|
|
7821
|
-
}
|
|
7822
|
-
|
|
7823
|
-
child.transform.SetParent(parent.transform);
|
|
7824
|
-
return $"Set {child.name}'s parent to {parent.name}";
|
|
7825
|
-
\`\`\`
|
|
7826
|
-
|
|
7827
|
-
## Get All Children
|
|
7828
|
-
|
|
7829
|
-
\`\`\`csharp
|
|
7830
|
-
GameObject parent = Selection.activeGameObject;
|
|
7831
|
-
if (parent == null)
|
|
7832
|
-
{
|
|
7833
|
-
return "No GameObject selected";
|
|
7834
|
-
}
|
|
7835
|
-
|
|
7836
|
-
List<string> children = new List<string>();
|
|
7837
|
-
foreach (Transform child in parent.transform)
|
|
7838
|
-
{
|
|
7839
|
-
children.Add(child.name);
|
|
7840
|
-
}
|
|
7841
|
-
return $"Children: {string.Join(", ", children)}";
|
|
7842
|
-
\`\`\`
|
|
7843
|
-
|
|
7844
|
-
## Wire Component References
|
|
7845
|
-
|
|
7846
|
-
\`\`\`csharp
|
|
7847
|
-
using UnityEditor;
|
|
7848
|
-
|
|
7849
|
-
GameObject player = GameObject.Find("Player");
|
|
7850
|
-
GameObject target = GameObject.Find("Target");
|
|
7851
|
-
|
|
7852
|
-
if (player == null || target == null)
|
|
7853
|
-
{
|
|
7854
|
-
return "Player or Target not found";
|
|
7855
|
-
}
|
|
7856
|
-
|
|
7857
|
-
MonoBehaviour script = player.GetComponent("PlayerController") as MonoBehaviour;
|
|
7858
|
-
if (script == null)
|
|
7859
|
-
{
|
|
7860
|
-
return "PlayerController not found on Player";
|
|
7861
|
-
}
|
|
7862
|
-
|
|
7863
|
-
SerializedObject serializedScript = new SerializedObject(script);
|
|
7864
|
-
SerializedProperty targetProp = serializedScript.FindProperty("target");
|
|
7865
|
-
|
|
7866
|
-
if (targetProp != null)
|
|
7867
|
-
{
|
|
7868
|
-
targetProp.objectReferenceValue = target.transform;
|
|
7869
|
-
serializedScript.ApplyModifiedProperties();
|
|
7870
|
-
return "Target reference wired";
|
|
7871
|
-
}
|
|
7872
|
-
return "Property 'target' not found";
|
|
7873
|
-
\`\`\`
|
|
7874
|
-
|
|
7875
|
-
## Load Scene (Editor)
|
|
7876
|
-
|
|
7877
|
-
\`\`\`csharp
|
|
7878
|
-
using UnityEditor.SceneManagement;
|
|
7879
|
-
|
|
7880
|
-
string scenePath = "Assets/Scenes/MainMenu.unity";
|
|
7881
|
-
EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single);
|
|
7882
|
-
return $"Loaded scene: {scenePath}";
|
|
7883
|
-
\`\`\`
|
|
7884
|
-
|
|
7885
|
-
## Save Current Scene
|
|
7886
|
-
|
|
7887
|
-
\`\`\`csharp
|
|
7888
|
-
using UnityEditor.SceneManagement;
|
|
7889
|
-
|
|
7890
|
-
UnityEngine.SceneManagement.Scene scene = EditorSceneManager.GetActiveScene();
|
|
7891
|
-
EditorSceneManager.SaveScene(scene);
|
|
7892
|
-
return $"Saved scene: {scene.name}";
|
|
7893
|
-
\`\`\`
|
|
7894
|
-
|
|
7895
|
-
## Create New Scene
|
|
7896
|
-
|
|
7897
|
-
\`\`\`csharp
|
|
7898
|
-
using UnityEditor.SceneManagement;
|
|
7899
|
-
|
|
7900
|
-
UnityEngine.SceneManagement.Scene newScene = EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects, NewSceneMode.Single);
|
|
7901
|
-
return $"Created new scene: {newScene.name}";
|
|
7902
|
-
\`\`\`
|
|
7903
|
-
|
|
7904
|
-
## Get All Root GameObjects in Scene
|
|
7905
|
-
|
|
7906
|
-
\`\`\`csharp
|
|
7907
|
-
UnityEngine.SceneManagement.Scene scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
|
|
7908
|
-
GameObject[] roots = scene.GetRootGameObjects();
|
|
7909
|
-
|
|
7910
|
-
List<string> names = new List<string>();
|
|
7911
|
-
foreach (GameObject root in roots)
|
|
7912
|
-
{
|
|
7913
|
-
names.Add(root.name);
|
|
7914
|
-
}
|
|
7915
|
-
return $"Root objects: {string.Join(", ", names)}";
|
|
7916
|
-
\`\`\`
|
|
7917
|
-
|
|
7918
|
-
## Destroy GameObject
|
|
7919
|
-
|
|
7920
|
-
\`\`\`csharp
|
|
7921
|
-
GameObject obj = GameObject.Find("OldObject");
|
|
7922
|
-
if (obj == null)
|
|
7923
|
-
{
|
|
7924
|
-
return "GameObject not found";
|
|
7925
|
-
}
|
|
7926
|
-
|
|
7927
|
-
Object.DestroyImmediate(obj);
|
|
7928
|
-
return "GameObject destroyed";
|
|
7929
|
-
\`\`\`
|
|
7930
|
-
|
|
7931
|
-
## Duplicate GameObject
|
|
7932
|
-
|
|
7933
|
-
\`\`\`csharp
|
|
7934
|
-
GameObject selected = Selection.activeGameObject;
|
|
7935
|
-
if (selected == null)
|
|
7936
|
-
{
|
|
7937
|
-
return "No GameObject selected";
|
|
7938
|
-
}
|
|
7939
|
-
|
|
7940
|
-
GameObject copy = Object.Instantiate(selected);
|
|
7941
|
-
copy.name = selected.name + "_Copy";
|
|
7942
|
-
copy.transform.position = selected.transform.position + Vector3.right * 2;
|
|
7943
|
-
return $"Created duplicate: {copy.name}";
|
|
7944
|
-
\`\`\`
|
|
7945
|
-
|
|
7946
|
-
## Set Active/Inactive
|
|
7947
|
-
|
|
7948
|
-
\`\`\`csharp
|
|
7949
|
-
GameObject obj = GameObject.Find("MyObject");
|
|
7950
|
-
if (obj == null)
|
|
7951
|
-
{
|
|
7952
|
-
return "GameObject not found";
|
|
7953
|
-
}
|
|
7954
|
-
|
|
7955
|
-
obj.SetActive(!obj.activeSelf);
|
|
7956
|
-
return $"{obj.name} is now {(obj.activeSelf ? "active" : "inactive")}";
|
|
7957
|
-
\`\`\`
|
|
7958
|
-
|
|
7959
|
-
## Modify Transform
|
|
7960
|
-
|
|
7961
|
-
\`\`\`csharp
|
|
7962
|
-
GameObject selected = Selection.activeGameObject;
|
|
7963
|
-
if (selected == null)
|
|
7964
|
-
{
|
|
7965
|
-
return "No GameObject selected";
|
|
7966
|
-
}
|
|
7967
|
-
|
|
7968
|
-
selected.transform.position = new Vector3(0, 5, 0);
|
|
7969
|
-
selected.transform.rotation = Quaternion.Euler(0, 45, 0);
|
|
7970
|
-
selected.transform.localScale = new Vector3(2, 2, 2);
|
|
7971
|
-
return "Transform modified";
|
|
7972
|
-
\`\`\`
|
|
7973
|
-
`,
|
|
7974
|
-
"examples/scriptableobject.md": `# ScriptableObject Operations
|
|
7975
|
-
|
|
7976
|
-
Code examples for ScriptableObject operations using \`execute-dynamic-code\`.
|
|
7977
|
-
|
|
7978
|
-
## Create ScriptableObject Instance
|
|
7979
|
-
|
|
7980
|
-
\`\`\`csharp
|
|
7981
|
-
using UnityEditor;
|
|
7982
|
-
|
|
7983
|
-
ScriptableObject so = ScriptableObject.CreateInstance<ScriptableObject>();
|
|
7984
|
-
string path = "Assets/Data/MyData.asset";
|
|
7985
|
-
AssetDatabase.CreateAsset(so, path);
|
|
7986
|
-
AssetDatabase.SaveAssets();
|
|
7987
|
-
return $"ScriptableObject created at {path}";
|
|
7988
|
-
\`\`\`
|
|
7989
|
-
|
|
7990
|
-
## Create Custom ScriptableObject
|
|
7991
|
-
|
|
7992
|
-
\`\`\`csharp
|
|
7993
|
-
using UnityEditor;
|
|
7994
|
-
|
|
7995
|
-
ScriptableObject so = ScriptableObject.CreateInstance("MyCustomSO");
|
|
7996
|
-
if (so == null)
|
|
7997
|
-
{
|
|
7998
|
-
return "Type 'MyCustomSO' not found. Ensure the class exists.";
|
|
7999
|
-
}
|
|
8000
|
-
|
|
8001
|
-
string path = "Assets/Data/MyCustomData.asset";
|
|
8002
|
-
AssetDatabase.CreateAsset(so, path);
|
|
8003
|
-
AssetDatabase.SaveAssets();
|
|
8004
|
-
return $"Created {so.GetType().Name} at {path}";
|
|
8005
|
-
\`\`\`
|
|
8006
|
-
|
|
8007
|
-
## Modify ScriptableObject with SerializedObject
|
|
8008
|
-
|
|
8009
|
-
\`\`\`csharp
|
|
8010
|
-
using UnityEditor;
|
|
8011
|
-
|
|
8012
|
-
string path = "Assets/Data/MyData.asset";
|
|
8013
|
-
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
|
|
8014
|
-
|
|
8015
|
-
if (so == null)
|
|
8016
|
-
{
|
|
8017
|
-
return $"Asset not found at {path}";
|
|
8018
|
-
}
|
|
8019
|
-
|
|
8020
|
-
SerializedObject serializedObj = new SerializedObject(so);
|
|
8021
|
-
SerializedProperty prop = serializedObj.FindProperty("myField");
|
|
8022
|
-
|
|
8023
|
-
if (prop != null)
|
|
8024
|
-
{
|
|
8025
|
-
prop.stringValue = "New Value";
|
|
8026
|
-
serializedObj.ApplyModifiedProperties();
|
|
8027
|
-
EditorUtility.SetDirty(so);
|
|
8028
|
-
AssetDatabase.SaveAssets();
|
|
8029
|
-
return "Property updated";
|
|
8030
|
-
}
|
|
8031
|
-
return "Property 'myField' not found";
|
|
8032
|
-
\`\`\`
|
|
8033
|
-
|
|
8034
|
-
## Set Int/Float/Bool Properties
|
|
8035
|
-
|
|
8036
|
-
\`\`\`csharp
|
|
8037
|
-
using UnityEditor;
|
|
8038
|
-
|
|
8039
|
-
string path = "Assets/Data/GameSettings.asset";
|
|
8040
|
-
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
|
|
8041
|
-
|
|
8042
|
-
SerializedObject serializedObj = new SerializedObject(so);
|
|
8043
|
-
|
|
8044
|
-
SerializedProperty intProp = serializedObj.FindProperty("maxHealth");
|
|
8045
|
-
if (intProp != null) intProp.intValue = 100;
|
|
8046
|
-
|
|
8047
|
-
SerializedProperty floatProp = serializedObj.FindProperty("moveSpeed");
|
|
8048
|
-
if (floatProp != null) floatProp.floatValue = 5.5f;
|
|
8049
|
-
|
|
8050
|
-
SerializedProperty boolProp = serializedObj.FindProperty("isEnabled");
|
|
8051
|
-
if (boolProp != null) boolProp.boolValue = true;
|
|
8052
|
-
|
|
8053
|
-
serializedObj.ApplyModifiedProperties();
|
|
8054
|
-
EditorUtility.SetDirty(so);
|
|
8055
|
-
AssetDatabase.SaveAssets();
|
|
8056
|
-
return "Properties updated";
|
|
8057
|
-
\`\`\`
|
|
8058
|
-
|
|
8059
|
-
## Set Reference Properties
|
|
8060
|
-
|
|
8061
|
-
\`\`\`csharp
|
|
8062
|
-
using UnityEditor;
|
|
8063
|
-
|
|
8064
|
-
string soPath = "Assets/Data/CharacterData.asset";
|
|
8065
|
-
string prefabPath = "Assets/Prefabs/Player.prefab";
|
|
8066
|
-
|
|
8067
|
-
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(soPath);
|
|
8068
|
-
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
|
8069
|
-
|
|
8070
|
-
if (so == null)
|
|
8071
|
-
{
|
|
8072
|
-
return $"ScriptableObject not found at {soPath}";
|
|
8073
|
-
}
|
|
8074
|
-
if (prefab == null)
|
|
8075
|
-
{
|
|
8076
|
-
return $"Prefab not found at {prefabPath}";
|
|
8077
|
-
}
|
|
8078
|
-
|
|
8079
|
-
SerializedObject serializedObj = new SerializedObject(so);
|
|
8080
|
-
SerializedProperty prop = serializedObj.FindProperty("playerPrefab");
|
|
8081
|
-
|
|
8082
|
-
if (prop != null)
|
|
8083
|
-
{
|
|
8084
|
-
prop.objectReferenceValue = prefab;
|
|
8085
|
-
serializedObj.ApplyModifiedProperties();
|
|
8086
|
-
EditorUtility.SetDirty(so);
|
|
8087
|
-
AssetDatabase.SaveAssets();
|
|
8088
|
-
return "Reference set successfully";
|
|
8089
|
-
}
|
|
8090
|
-
return "Property not found";
|
|
8091
|
-
\`\`\`
|
|
8092
|
-
|
|
8093
|
-
## Set Array/List Properties
|
|
8094
|
-
|
|
8095
|
-
\`\`\`csharp
|
|
8096
|
-
using UnityEditor;
|
|
8097
|
-
|
|
8098
|
-
string path = "Assets/Data/ItemDatabase.asset";
|
|
8099
|
-
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
|
|
8100
|
-
|
|
8101
|
-
if (so == null)
|
|
8102
|
-
{
|
|
8103
|
-
return $"Asset not found at {path}";
|
|
8104
|
-
}
|
|
8105
|
-
|
|
8106
|
-
SerializedObject serializedObj = new SerializedObject(so);
|
|
8107
|
-
SerializedProperty arrayProp = serializedObj.FindProperty("items");
|
|
8108
|
-
|
|
8109
|
-
if (arrayProp != null && arrayProp.isArray)
|
|
8110
|
-
{
|
|
8111
|
-
arrayProp.ClearArray();
|
|
8112
|
-
arrayProp.InsertArrayElementAtIndex(0);
|
|
8113
|
-
arrayProp.GetArrayElementAtIndex(0).stringValue = "Sword";
|
|
8114
|
-
arrayProp.InsertArrayElementAtIndex(1);
|
|
8115
|
-
arrayProp.GetArrayElementAtIndex(1).stringValue = "Shield";
|
|
8116
|
-
|
|
8117
|
-
serializedObj.ApplyModifiedProperties();
|
|
8118
|
-
EditorUtility.SetDirty(so);
|
|
8119
|
-
AssetDatabase.SaveAssets();
|
|
8120
|
-
return "Array updated with 2 items";
|
|
8121
|
-
}
|
|
8122
|
-
return "Array property not found";
|
|
8123
|
-
\`\`\`
|
|
8124
|
-
|
|
8125
|
-
## Find All ScriptableObjects of Type
|
|
8126
|
-
|
|
8127
|
-
\`\`\`csharp
|
|
8128
|
-
using UnityEditor;
|
|
8129
|
-
using System.Collections.Generic;
|
|
8130
|
-
|
|
8131
|
-
string typeName = "GameSettings";
|
|
8132
|
-
string[] guids = AssetDatabase.FindAssets($"t:{typeName}");
|
|
8133
|
-
List<string> paths = new List<string>();
|
|
8134
|
-
|
|
8135
|
-
foreach (string guid in guids)
|
|
8136
|
-
{
|
|
8137
|
-
paths.Add(AssetDatabase.GUIDToAssetPath(guid));
|
|
8138
|
-
}
|
|
8139
|
-
return $"Found {paths.Count} {typeName} assets";
|
|
8140
|
-
\`\`\`
|
|
8141
|
-
|
|
8142
|
-
## Duplicate ScriptableObject
|
|
8143
|
-
|
|
8144
|
-
\`\`\`csharp
|
|
8145
|
-
using UnityEditor;
|
|
8146
|
-
|
|
8147
|
-
string sourcePath = "Assets/Data/Template.asset";
|
|
8148
|
-
string destPath = "Assets/Data/NewInstance.asset";
|
|
8149
|
-
|
|
8150
|
-
ScriptableObject source = AssetDatabase.LoadAssetAtPath<ScriptableObject>(sourcePath);
|
|
8151
|
-
if (source == null)
|
|
8152
|
-
{
|
|
8153
|
-
return $"Source asset not found at {sourcePath}";
|
|
8154
|
-
}
|
|
8155
|
-
|
|
8156
|
-
ScriptableObject copy = Object.Instantiate(source);
|
|
8157
|
-
AssetDatabase.CreateAsset(copy, destPath);
|
|
8158
|
-
AssetDatabase.SaveAssets();
|
|
8159
|
-
return $"Duplicated to {destPath}";
|
|
8160
|
-
\`\`\`
|
|
8161
|
-
|
|
8162
|
-
## List All Properties of ScriptableObject
|
|
8163
|
-
|
|
8164
|
-
\`\`\`csharp
|
|
8165
|
-
using UnityEditor;
|
|
8166
|
-
using System.Collections.Generic;
|
|
8167
|
-
|
|
8168
|
-
string path = "Assets/Data/MyData.asset";
|
|
8169
|
-
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
|
|
8170
|
-
|
|
8171
|
-
if (so == null)
|
|
8172
|
-
{
|
|
8173
|
-
return $"Asset not found at {path}";
|
|
8174
|
-
}
|
|
8175
|
-
|
|
8176
|
-
SerializedObject serializedObj = new SerializedObject(so);
|
|
8177
|
-
SerializedProperty prop = serializedObj.GetIterator();
|
|
8178
|
-
|
|
8179
|
-
List<string> properties = new List<string>();
|
|
8180
|
-
while (prop.NextVisible(true))
|
|
8181
|
-
{
|
|
8182
|
-
properties.Add($"{prop.name} ({prop.propertyType})");
|
|
8183
|
-
}
|
|
8184
|
-
return string.Join(", ", properties);
|
|
8185
|
-
\`\`\`
|
|
8186
|
-
`,
|
|
8187
|
-
"examples/selection-operations.md": `# Selection Operations
|
|
8188
|
-
|
|
8189
|
-
Code examples for Selection operations using \`execute-dynamic-code\`.
|
|
8190
|
-
|
|
8191
|
-
## Get Selected GameObjects
|
|
8192
|
-
|
|
8193
|
-
\`\`\`csharp
|
|
8194
|
-
using UnityEditor;
|
|
8195
|
-
using System.Collections.Generic;
|
|
8196
|
-
|
|
8197
|
-
GameObject[] selected = Selection.gameObjects;
|
|
8198
|
-
if (selected.Length == 0)
|
|
8199
|
-
{
|
|
8200
|
-
return "No GameObjects selected";
|
|
8201
|
-
}
|
|
8202
|
-
|
|
8203
|
-
List<string> names = new List<string>();
|
|
8204
|
-
foreach (GameObject obj in selected)
|
|
8205
|
-
{
|
|
8206
|
-
names.Add(obj.name);
|
|
8207
|
-
}
|
|
8208
|
-
return $"Selected: {string.Join(", ", names)}";
|
|
8209
|
-
\`\`\`
|
|
8210
|
-
|
|
8211
|
-
## Get Active (Last Selected) GameObject
|
|
8212
|
-
|
|
8213
|
-
\`\`\`csharp
|
|
8214
|
-
using UnityEditor;
|
|
8215
|
-
|
|
8216
|
-
GameObject active = Selection.activeGameObject;
|
|
8217
|
-
if (active == null)
|
|
8218
|
-
{
|
|
8219
|
-
return "No active GameObject";
|
|
8220
|
-
}
|
|
8221
|
-
return $"Active: {active.name}";
|
|
8222
|
-
\`\`\`
|
|
8223
|
-
|
|
8224
|
-
## Set Selection Programmatically
|
|
8225
|
-
|
|
8226
|
-
\`\`\`csharp
|
|
8227
|
-
using UnityEditor;
|
|
8228
|
-
|
|
8229
|
-
GameObject obj = GameObject.Find("Player");
|
|
8230
|
-
if (obj == null)
|
|
8231
|
-
{
|
|
8232
|
-
return "GameObject 'Player' not found";
|
|
8233
|
-
}
|
|
8234
|
-
|
|
8235
|
-
Selection.activeGameObject = obj;
|
|
8236
|
-
return $"Selected {obj.name}";
|
|
8237
|
-
\`\`\`
|
|
8238
|
-
|
|
8239
|
-
## Select Multiple GameObjects
|
|
8240
|
-
|
|
8241
|
-
\`\`\`csharp
|
|
8242
|
-
using UnityEditor;
|
|
8243
|
-
|
|
8244
|
-
GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");
|
|
8245
|
-
if (enemies.Length == 0)
|
|
8246
|
-
{
|
|
8247
|
-
return "No enemies found";
|
|
8248
|
-
}
|
|
8249
|
-
|
|
8250
|
-
Selection.objects = enemies;
|
|
8251
|
-
return $"Selected {enemies.Length} enemies";
|
|
8252
|
-
\`\`\`
|
|
8253
|
-
|
|
8254
|
-
## Get Top-Level Transforms Only
|
|
8255
|
-
|
|
8256
|
-
\`\`\`csharp
|
|
8257
|
-
using UnityEditor;
|
|
8258
|
-
using System.Collections.Generic;
|
|
8259
|
-
|
|
8260
|
-
Transform[] transforms = Selection.GetTransforms(SelectionMode.TopLevel);
|
|
8261
|
-
if (transforms.Length == 0)
|
|
8262
|
-
{
|
|
8263
|
-
return "No transforms selected";
|
|
8264
|
-
}
|
|
8265
|
-
|
|
8266
|
-
List<string> names = new List<string>();
|
|
8267
|
-
foreach (Transform t in transforms)
|
|
8268
|
-
{
|
|
8269
|
-
names.Add(t.name);
|
|
8270
|
-
}
|
|
8271
|
-
return $"Top-level: {string.Join(", ", names)}";
|
|
8272
|
-
\`\`\`
|
|
8273
|
-
|
|
8274
|
-
## Get Deep Selection (Including Children)
|
|
8275
|
-
|
|
8276
|
-
\`\`\`csharp
|
|
8277
|
-
using UnityEditor;
|
|
8278
|
-
|
|
8279
|
-
Transform[] transforms = Selection.GetTransforms(SelectionMode.Deep);
|
|
8280
|
-
if (transforms.Length == 0)
|
|
8281
|
-
{
|
|
8282
|
-
return "No transforms selected";
|
|
8283
|
-
}
|
|
8284
|
-
|
|
8285
|
-
return $"Deep selection count: {transforms.Length}";
|
|
8286
|
-
\`\`\`
|
|
8287
|
-
|
|
8288
|
-
## Get Editable Objects Only
|
|
8289
|
-
|
|
8290
|
-
\`\`\`csharp
|
|
8291
|
-
using UnityEditor;
|
|
8292
|
-
using System.Collections.Generic;
|
|
8293
|
-
|
|
8294
|
-
Transform[] transforms = Selection.GetTransforms(SelectionMode.Editable);
|
|
8295
|
-
if (transforms.Length == 0)
|
|
8296
|
-
{
|
|
8297
|
-
return "No editable transforms selected";
|
|
8298
|
-
}
|
|
8299
|
-
|
|
8300
|
-
List<string> names = new List<string>();
|
|
8301
|
-
foreach (Transform t in transforms)
|
|
8302
|
-
{
|
|
8303
|
-
names.Add(t.name);
|
|
8304
|
-
}
|
|
8305
|
-
return $"Editable: {string.Join(", ", names)}";
|
|
8306
|
-
\`\`\`
|
|
8307
|
-
|
|
8308
|
-
## Get Selected Assets
|
|
8309
|
-
|
|
8310
|
-
\`\`\`csharp
|
|
8311
|
-
using UnityEditor;
|
|
8312
|
-
using System.Collections.Generic;
|
|
8313
|
-
|
|
8314
|
-
Object[] selectedAssets = Selection.GetFiltered<Object>(SelectionMode.Assets);
|
|
8315
|
-
if (selectedAssets.Length == 0)
|
|
8316
|
-
{
|
|
8317
|
-
return "No assets selected";
|
|
8318
|
-
}
|
|
8319
|
-
|
|
8320
|
-
List<string> paths = new List<string>();
|
|
8321
|
-
foreach (Object asset in selectedAssets)
|
|
8322
|
-
{
|
|
8323
|
-
paths.Add(AssetDatabase.GetAssetPath(asset));
|
|
8324
|
-
}
|
|
8325
|
-
return $"Assets: {string.Join(", ", paths)}";
|
|
8326
|
-
\`\`\`
|
|
8327
|
-
|
|
8328
|
-
## Get Selected Asset GUIDs
|
|
8329
|
-
|
|
8330
|
-
\`\`\`csharp
|
|
8331
|
-
using UnityEditor;
|
|
8332
|
-
using System.Collections.Generic;
|
|
8333
|
-
|
|
8334
|
-
string[] guids = Selection.assetGUIDs;
|
|
8335
|
-
if (guids.Length == 0)
|
|
8336
|
-
{
|
|
8337
|
-
return "No assets selected";
|
|
8338
|
-
}
|
|
8339
|
-
|
|
8340
|
-
List<string> paths = new List<string>();
|
|
8341
|
-
foreach (string guid in guids)
|
|
8342
|
-
{
|
|
8343
|
-
paths.Add(AssetDatabase.GUIDToAssetPath(guid));
|
|
8344
|
-
}
|
|
8345
|
-
return $"Selected assets: {string.Join(", ", paths)}";
|
|
8346
|
-
\`\`\`
|
|
8347
|
-
|
|
8348
|
-
## Select All Children of Selected Object
|
|
8349
|
-
|
|
8350
|
-
\`\`\`csharp
|
|
8351
|
-
using UnityEditor;
|
|
8352
|
-
using UnityEngine;
|
|
8353
|
-
using System.Collections.Generic;
|
|
8354
|
-
|
|
8355
|
-
GameObject parent = Selection.activeGameObject;
|
|
8356
|
-
if (parent == null)
|
|
8357
|
-
{
|
|
8358
|
-
return "No GameObject selected";
|
|
8359
|
-
}
|
|
8360
|
-
|
|
8361
|
-
List<GameObject> children = new List<GameObject>();
|
|
8362
|
-
foreach (Transform child in parent.GetComponentsInChildren<Transform>())
|
|
8363
|
-
{
|
|
8364
|
-
if (child != parent.transform)
|
|
8365
|
-
{
|
|
8366
|
-
children.Add(child.gameObject);
|
|
8367
|
-
}
|
|
8368
|
-
}
|
|
8369
|
-
|
|
8370
|
-
if (children.Count == 0)
|
|
8371
|
-
{
|
|
8372
|
-
return "No children found";
|
|
8373
|
-
}
|
|
8374
|
-
|
|
8375
|
-
Selection.objects = children.ToArray();
|
|
8376
|
-
return $"Selected {children.Count} children";
|
|
8377
|
-
\`\`\`
|
|
8378
|
-
|
|
8379
|
-
## Filter Selection by Component
|
|
8380
|
-
|
|
8381
|
-
\`\`\`csharp
|
|
8382
|
-
using UnityEditor;
|
|
8383
|
-
using System.Collections.Generic;
|
|
8384
|
-
|
|
8385
|
-
GameObject[] selected = Selection.gameObjects;
|
|
8386
|
-
List<GameObject> withRigidbody = new List<GameObject>();
|
|
8387
|
-
|
|
8388
|
-
foreach (GameObject obj in selected)
|
|
8389
|
-
{
|
|
8390
|
-
if (obj.GetComponent<Rigidbody>() != null)
|
|
8391
|
-
{
|
|
8392
|
-
withRigidbody.Add(obj);
|
|
8393
|
-
}
|
|
8394
|
-
}
|
|
8395
|
-
|
|
8396
|
-
if (withRigidbody.Count == 0)
|
|
8397
|
-
{
|
|
8398
|
-
return "No objects with Rigidbody in selection";
|
|
8399
|
-
}
|
|
8400
|
-
|
|
8401
|
-
Selection.objects = withRigidbody.ToArray();
|
|
8402
|
-
return $"Filtered to {withRigidbody.Count} objects with Rigidbody";
|
|
8403
|
-
\`\`\`
|
|
8404
|
-
|
|
8405
|
-
## Check if Object is Selected
|
|
8406
|
-
|
|
8407
|
-
\`\`\`csharp
|
|
8408
|
-
using UnityEditor;
|
|
8409
|
-
|
|
8410
|
-
GameObject player = GameObject.Find("Player");
|
|
8411
|
-
if (player == null)
|
|
8412
|
-
{
|
|
8413
|
-
return "Player not found";
|
|
8414
|
-
}
|
|
8415
|
-
|
|
8416
|
-
bool isSelected = Selection.Contains(player);
|
|
8417
|
-
return $"Player is {(isSelected ? "" : "not ")}selected";
|
|
8418
|
-
\`\`\`
|
|
8419
|
-
|
|
8420
|
-
## Clear Selection
|
|
8421
|
-
|
|
8422
|
-
\`\`\`csharp
|
|
8423
|
-
using UnityEditor;
|
|
8424
|
-
|
|
8425
|
-
Selection.activeObject = null;
|
|
8426
|
-
return "Selection cleared";
|
|
8427
|
-
\`\`\`
|
|
8428
|
-
|
|
8429
|
-
## Select Objects by Layer
|
|
8430
|
-
|
|
8431
|
-
\`\`\`csharp
|
|
8432
|
-
using UnityEditor;
|
|
8433
|
-
using System.Collections.Generic;
|
|
8434
|
-
|
|
8435
|
-
int layer = LayerMask.NameToLayer("UI");
|
|
8436
|
-
GameObject[] allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
|
|
8437
|
-
List<GameObject> layerObjects = new List<GameObject>();
|
|
8438
|
-
|
|
8439
|
-
foreach (GameObject obj in allObjects)
|
|
8440
|
-
{
|
|
8441
|
-
if (obj.layer == layer)
|
|
8442
|
-
{
|
|
8443
|
-
layerObjects.Add(obj);
|
|
8444
|
-
}
|
|
8445
|
-
}
|
|
8446
|
-
|
|
8447
|
-
if (layerObjects.Count == 0)
|
|
8448
|
-
{
|
|
8449
|
-
return "No objects found on UI layer";
|
|
8450
|
-
}
|
|
8451
|
-
|
|
8452
|
-
Selection.objects = layerObjects.ToArray();
|
|
8453
|
-
return $"Selected {layerObjects.Count} objects on UI layer";
|
|
8454
|
-
\`\`\`
|
|
8455
|
-
|
|
8456
|
-
## Ping Object in Hierarchy/Project
|
|
8457
|
-
|
|
8458
|
-
\`\`\`csharp
|
|
8459
|
-
using UnityEditor;
|
|
8460
|
-
|
|
8461
|
-
GameObject obj = GameObject.Find("Player");
|
|
8462
|
-
if (obj == null)
|
|
8463
|
-
{
|
|
8464
|
-
return "Player not found";
|
|
8465
|
-
}
|
|
8466
|
-
|
|
8467
|
-
EditorGUIUtility.PingObject(obj);
|
|
8468
|
-
return $"Pinged {obj.name} in Hierarchy";
|
|
8469
|
-
\`\`\`
|
|
8470
|
-
|
|
8471
|
-
## Focus on Selected Object in Scene View
|
|
8472
|
-
|
|
8473
|
-
\`\`\`csharp
|
|
8474
|
-
using UnityEditor;
|
|
8475
|
-
|
|
8476
|
-
if (Selection.activeGameObject == null)
|
|
8477
|
-
{
|
|
8478
|
-
return "No GameObject selected";
|
|
8479
|
-
}
|
|
8480
|
-
|
|
8481
|
-
SceneView.FrameLastActiveSceneView();
|
|
8482
|
-
return "Focused on selected object";
|
|
8483
|
-
\`\`\`
|
|
8484
|
-
|
|
8485
|
-
`,
|
|
8486
|
-
"examples/undo-operations.md": `# Undo Operations
|
|
8487
|
-
|
|
8488
|
-
Code examples for Undo-supported operations using \`execute-dynamic-code\`.
|
|
8489
|
-
|
|
8490
|
-
## Record Property Change (Undo.RecordObject)
|
|
8491
|
-
|
|
8492
|
-
\`\`\`csharp
|
|
8493
|
-
using UnityEditor;
|
|
8494
|
-
|
|
8495
|
-
GameObject selected = Selection.activeGameObject;
|
|
8496
|
-
if (selected == null)
|
|
8497
|
-
{
|
|
8498
|
-
return "No GameObject selected";
|
|
8499
|
-
}
|
|
8500
|
-
|
|
8501
|
-
Undo.RecordObject(selected.transform, "Move Object");
|
|
8502
|
-
selected.transform.position = new Vector3(0, 5, 0);
|
|
8503
|
-
return $"Moved {selected.name} (Undo available)";
|
|
8504
|
-
\`\`\`
|
|
8505
|
-
|
|
8506
|
-
## Record Multiple Objects
|
|
8507
|
-
|
|
8508
|
-
\`\`\`csharp
|
|
8509
|
-
using UnityEditor;
|
|
8510
|
-
|
|
8511
|
-
GameObject[] selectedObjects = Selection.gameObjects;
|
|
8512
|
-
if (selectedObjects.Length == 0)
|
|
8513
|
-
{
|
|
8514
|
-
return "No GameObjects selected";
|
|
8515
|
-
}
|
|
8516
|
-
|
|
8517
|
-
Object[] transforms = new Object[selectedObjects.Length];
|
|
8518
|
-
for (int i = 0; i < selectedObjects.Length; i++)
|
|
8519
|
-
{
|
|
8520
|
-
transforms[i] = selectedObjects[i].transform;
|
|
8521
|
-
}
|
|
8522
|
-
|
|
8523
|
-
Undo.RecordObjects(transforms, "Move Multiple Objects");
|
|
8524
|
-
foreach (GameObject obj in selectedObjects)
|
|
8525
|
-
{
|
|
8526
|
-
obj.transform.position += Vector3.up * 2;
|
|
8527
|
-
}
|
|
8528
|
-
return $"Moved {selectedObjects.Length} objects (Undo available)";
|
|
8529
|
-
\`\`\`
|
|
8530
|
-
|
|
8531
|
-
## Complete Object Undo (For Complex Changes)
|
|
8532
|
-
|
|
8533
|
-
\`\`\`csharp
|
|
8534
|
-
using UnityEditor;
|
|
8535
|
-
|
|
8536
|
-
GameObject selected = Selection.activeGameObject;
|
|
8537
|
-
if (selected == null)
|
|
8538
|
-
{
|
|
8539
|
-
return "No GameObject selected";
|
|
8540
|
-
}
|
|
8541
|
-
|
|
8542
|
-
Undo.RegisterCompleteObjectUndo(selected, "Complete Object Change");
|
|
8543
|
-
selected.name = "RenamedObject";
|
|
8544
|
-
selected.layer = LayerMask.NameToLayer("Default");
|
|
8545
|
-
selected.tag = "Untagged";
|
|
8546
|
-
return $"Modified object completely (Undo available)";
|
|
8547
|
-
\`\`\`
|
|
8548
|
-
|
|
8549
|
-
## Add Component with Undo
|
|
8550
|
-
|
|
8551
|
-
\`\`\`csharp
|
|
8552
|
-
using UnityEditor;
|
|
8553
|
-
|
|
8554
|
-
GameObject selected = Selection.activeGameObject;
|
|
8555
|
-
if (selected == null)
|
|
8556
|
-
{
|
|
8557
|
-
return "No GameObject selected";
|
|
8558
|
-
}
|
|
8559
|
-
|
|
8560
|
-
Rigidbody rb = Undo.AddComponent<Rigidbody>(selected);
|
|
8561
|
-
rb.mass = 2f;
|
|
8562
|
-
rb.useGravity = true;
|
|
8563
|
-
return $"Added Rigidbody to {selected.name} (Undo available)";
|
|
8564
|
-
\`\`\`
|
|
8565
|
-
|
|
8566
|
-
## Set Parent with Undo
|
|
8567
|
-
|
|
8568
|
-
\`\`\`csharp
|
|
8569
|
-
using UnityEditor;
|
|
8570
|
-
|
|
8571
|
-
GameObject child = GameObject.Find("Child");
|
|
8572
|
-
GameObject parent = GameObject.Find("Parent");
|
|
8573
|
-
|
|
8574
|
-
if (child == null || parent == null)
|
|
8575
|
-
{
|
|
8576
|
-
return "Child or Parent not found";
|
|
8577
|
-
}
|
|
8578
|
-
|
|
8579
|
-
Undo.SetTransformParent(child.transform, parent.transform, "Set Parent");
|
|
8580
|
-
return $"Set {child.name}'s parent to {parent.name} (Undo available)";
|
|
8581
|
-
\`\`\`
|
|
8582
|
-
|
|
8583
|
-
## Create GameObject with Undo
|
|
8584
|
-
|
|
8585
|
-
\`\`\`csharp
|
|
8586
|
-
using UnityEditor;
|
|
8587
|
-
|
|
8588
|
-
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
8589
|
-
cube.name = "UndoableCube";
|
|
8590
|
-
cube.transform.position = new Vector3(0, 1, 0);
|
|
8591
|
-
Undo.RegisterCreatedObjectUndo(cube, "Create Cube");
|
|
8592
|
-
return $"Created {cube.name} (Undo available)";
|
|
8593
|
-
\`\`\`
|
|
8594
|
-
|
|
8595
|
-
## Destroy GameObject with Undo
|
|
8596
|
-
|
|
8597
|
-
\`\`\`csharp
|
|
8598
|
-
using UnityEditor;
|
|
8599
|
-
|
|
8600
|
-
GameObject obj = GameObject.Find("ObjectToDelete");
|
|
8601
|
-
if (obj == null)
|
|
8602
|
-
{
|
|
8603
|
-
return "GameObject not found";
|
|
8604
|
-
}
|
|
8605
|
-
|
|
8606
|
-
Undo.DestroyObjectImmediate(obj);
|
|
8607
|
-
return "GameObject destroyed (Undo available)";
|
|
8608
|
-
\`\`\`
|
|
8609
|
-
|
|
8610
|
-
## Named Undo Group
|
|
8611
|
-
|
|
8612
|
-
\`\`\`csharp
|
|
8613
|
-
using UnityEditor;
|
|
8614
|
-
|
|
8615
|
-
GameObject selected = Selection.activeGameObject;
|
|
8616
|
-
if (selected == null)
|
|
8617
|
-
{
|
|
8618
|
-
return "No GameObject selected";
|
|
8619
|
-
}
|
|
8620
|
-
|
|
8621
|
-
Undo.SetCurrentGroupName("Complex Transform Operation");
|
|
8622
|
-
|
|
8623
|
-
Undo.RecordObject(selected.transform, "");
|
|
8624
|
-
selected.transform.position = Vector3.zero;
|
|
8625
|
-
selected.transform.rotation = Quaternion.identity;
|
|
8626
|
-
selected.transform.localScale = Vector3.one;
|
|
8627
|
-
|
|
8628
|
-
return "Reset transform (Single undo step)";
|
|
8629
|
-
\`\`\`
|
|
8630
|
-
|
|
8631
|
-
## Collapse Multiple Operations into One Undo
|
|
8632
|
-
|
|
8633
|
-
\`\`\`csharp
|
|
8634
|
-
using UnityEditor;
|
|
8635
|
-
|
|
8636
|
-
int undoGroup = Undo.GetCurrentGroup();
|
|
8637
|
-
Undo.SetCurrentGroupName("Batch Operation");
|
|
8638
|
-
|
|
8639
|
-
GameObject cube1 = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
8640
|
-
cube1.name = "Cube1";
|
|
8641
|
-
Undo.RegisterCreatedObjectUndo(cube1, "");
|
|
8642
|
-
|
|
8643
|
-
GameObject cube2 = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
8644
|
-
cube2.name = "Cube2";
|
|
8645
|
-
cube2.transform.position = Vector3.right * 2;
|
|
8646
|
-
Undo.RegisterCreatedObjectUndo(cube2, "");
|
|
8647
|
-
|
|
8648
|
-
GameObject cube3 = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
8649
|
-
cube3.name = "Cube3";
|
|
8650
|
-
cube3.transform.position = Vector3.right * 4;
|
|
8651
|
-
Undo.RegisterCreatedObjectUndo(cube3, "");
|
|
8652
|
-
|
|
8653
|
-
Undo.CollapseUndoOperations(undoGroup);
|
|
8654
|
-
return "Created 3 cubes (Single undo step)";
|
|
8655
|
-
\`\`\`
|
|
8656
|
-
|
|
8657
|
-
## Modify ScriptableObject with Undo
|
|
8658
|
-
|
|
8659
|
-
\`\`\`csharp
|
|
8660
|
-
using UnityEditor;
|
|
8661
|
-
|
|
8662
|
-
string path = "Assets/Data/GameSettings.asset";
|
|
8663
|
-
ScriptableObject so = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
|
|
8664
|
-
if (so == null)
|
|
8665
|
-
{
|
|
8666
|
-
return $"Asset not found at {path}";
|
|
8667
|
-
}
|
|
8668
|
-
|
|
8669
|
-
Undo.RecordObject(so, "Modify Settings");
|
|
8670
|
-
SerializedObject serializedObj = new SerializedObject(so);
|
|
8671
|
-
SerializedProperty prop = serializedObj.FindProperty("maxHealth");
|
|
8672
|
-
if (prop != null)
|
|
8673
|
-
{
|
|
8674
|
-
prop.intValue = 200;
|
|
8675
|
-
serializedObj.ApplyModifiedProperties();
|
|
8676
|
-
}
|
|
8677
|
-
return "Modified ScriptableObject (Undo available)";
|
|
8678
|
-
\`\`\`
|
|
8679
|
-
|
|
8680
|
-
## Modify Material with Undo
|
|
8681
|
-
|
|
8682
|
-
\`\`\`csharp
|
|
8683
|
-
using UnityEditor;
|
|
8684
|
-
|
|
8685
|
-
string path = "Assets/Materials/MyMaterial.mat";
|
|
8686
|
-
Material mat = AssetDatabase.LoadAssetAtPath<Material>(path);
|
|
8687
|
-
if (mat == null)
|
|
8688
|
-
{
|
|
8689
|
-
return $"Material not found at {path}";
|
|
8690
|
-
}
|
|
8691
|
-
|
|
8692
|
-
Undo.RecordObject(mat, "Change Material Color");
|
|
8693
|
-
mat.color = Color.red;
|
|
8694
|
-
return "Changed material color (Undo available)";
|
|
8695
|
-
\`\`\`
|
|
8696
|
-
|
|
8697
|
-
`
|
|
8698
|
-
}
|
|
8699
|
-
},
|
|
8700
|
-
{
|
|
8701
|
-
name: "uloop-execute-menu-item",
|
|
8702
|
-
dirName: "uloop-execute-menu-item",
|
|
8703
|
-
content: SKILL_default6
|
|
8704
|
-
},
|
|
8705
|
-
{
|
|
8706
|
-
name: "uloop-find-game-objects",
|
|
8707
|
-
dirName: "uloop-find-game-objects",
|
|
8708
|
-
content: SKILL_default7
|
|
8709
|
-
},
|
|
8710
|
-
{
|
|
8711
|
-
name: "uloop-focus-window",
|
|
8712
|
-
dirName: "uloop-focus-window",
|
|
8713
|
-
content: SKILL_default8
|
|
8714
|
-
},
|
|
8715
|
-
{
|
|
8716
|
-
name: "uloop-get-hierarchy",
|
|
8717
|
-
dirName: "uloop-get-hierarchy",
|
|
8718
|
-
content: SKILL_default9
|
|
8719
|
-
},
|
|
8720
|
-
{
|
|
8721
|
-
name: "uloop-get-logs",
|
|
8722
|
-
dirName: "uloop-get-logs",
|
|
8723
|
-
content: SKILL_default10
|
|
8724
|
-
},
|
|
8725
|
-
{
|
|
8726
|
-
name: "uloop-get-menu-items",
|
|
8727
|
-
dirName: "uloop-get-menu-items",
|
|
8728
|
-
content: SKILL_default11
|
|
8729
|
-
},
|
|
8730
|
-
{
|
|
8731
|
-
name: "uloop-get-provider-details",
|
|
8732
|
-
dirName: "uloop-get-provider-details",
|
|
8733
|
-
content: SKILL_default12
|
|
8734
|
-
},
|
|
8735
|
-
{
|
|
8736
|
-
name: "uloop-run-tests",
|
|
8737
|
-
dirName: "uloop-run-tests",
|
|
8738
|
-
content: SKILL_default13
|
|
8739
|
-
},
|
|
8740
|
-
{
|
|
8741
|
-
name: "uloop-unity-search",
|
|
8742
|
-
dirName: "uloop-unity-search",
|
|
8743
|
-
content: SKILL_default14
|
|
8744
|
-
}
|
|
8745
|
-
];
|
|
8746
|
-
|
|
8747
|
-
// src/skills/skills-manager.ts
|
|
8748
|
-
var EXCLUDED_DIRS2 = /* @__PURE__ */ new Set(["node_modules", ".git", "Temp", "obj", "Build", "Builds", "Logs"]);
|
|
8749
|
-
function getGlobalSkillsDir(target) {
|
|
8750
|
-
return (0, import_path5.join)((0, import_os.homedir)(), target.projectDir, "skills");
|
|
8751
|
-
}
|
|
8752
|
-
function getProjectSkillsDir(target) {
|
|
8753
|
-
const projectRoot = findUnityProjectRoot();
|
|
8754
|
-
if (!projectRoot) {
|
|
8755
|
-
throw new Error(
|
|
8756
|
-
"Not inside a Unity project. Run this command from within a Unity project directory."
|
|
8757
|
-
);
|
|
8758
|
-
}
|
|
8759
|
-
return (0, import_path5.join)(projectRoot, target.projectDir, "skills");
|
|
8760
|
-
}
|
|
8761
|
-
function getSkillPath(skillDirName, target, global) {
|
|
8762
|
-
const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
8763
|
-
return (0, import_path5.join)(baseDir, skillDirName, target.skillFileName);
|
|
8764
|
-
}
|
|
8765
|
-
function isSkillInstalled(skill, target, global) {
|
|
8766
|
-
const skillPath = getSkillPath(skill.dirName, target, global);
|
|
8767
|
-
return (0, import_fs5.existsSync)(skillPath);
|
|
8768
|
-
}
|
|
8769
|
-
function isSkillOutdated(skill, target, global) {
|
|
8770
|
-
const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
8771
|
-
const skillDir = (0, import_path5.join)(baseDir, skill.dirName);
|
|
8772
|
-
const skillPath = (0, import_path5.join)(skillDir, target.skillFileName);
|
|
8773
|
-
if (!(0, import_fs5.existsSync)(skillPath)) {
|
|
8774
|
-
return false;
|
|
8775
|
-
}
|
|
8776
|
-
const installedContent = (0, import_fs5.readFileSync)(skillPath, "utf-8");
|
|
8777
|
-
if (installedContent !== skill.content) {
|
|
8778
|
-
return true;
|
|
8779
|
-
}
|
|
8780
|
-
if ("additionalFiles" in skill && skill.additionalFiles) {
|
|
8781
|
-
const additionalFiles = skill.additionalFiles;
|
|
8782
|
-
for (const [relativePath, expectedContent] of Object.entries(additionalFiles)) {
|
|
8783
|
-
const filePath = (0, import_path5.join)(skillDir, relativePath);
|
|
8784
|
-
if (!(0, import_fs5.existsSync)(filePath)) {
|
|
8785
|
-
return true;
|
|
8786
|
-
}
|
|
8787
|
-
const installedFileContent = (0, import_fs5.readFileSync)(filePath, "utf-8");
|
|
8788
|
-
if (installedFileContent !== expectedContent) {
|
|
8789
|
-
return true;
|
|
8790
|
-
}
|
|
8791
|
-
}
|
|
8792
|
-
}
|
|
8793
|
-
return false;
|
|
8794
|
-
}
|
|
8795
|
-
function getSkillStatus(skill, target, global) {
|
|
8796
|
-
if (!isSkillInstalled(skill, target, global)) {
|
|
8797
|
-
return "not_installed";
|
|
8798
|
-
}
|
|
8799
|
-
if (isSkillOutdated(skill, target, global)) {
|
|
8800
|
-
return "outdated";
|
|
8801
|
-
}
|
|
8802
|
-
return "installed";
|
|
8803
|
-
}
|
|
8804
|
-
function parseFrontmatter(content) {
|
|
8805
|
-
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
8806
|
-
if (!frontmatterMatch) {
|
|
8807
|
-
return {};
|
|
8808
|
-
}
|
|
8809
|
-
const frontmatter = {};
|
|
8810
|
-
const lines = frontmatterMatch[1].split("\n");
|
|
8811
|
-
for (const line of lines) {
|
|
8812
|
-
const colonIndex = line.indexOf(":");
|
|
8813
|
-
if (colonIndex === -1) {
|
|
8814
|
-
continue;
|
|
8815
|
-
}
|
|
8816
|
-
const key = line.slice(0, colonIndex).trim();
|
|
8817
|
-
const value = line.slice(colonIndex + 1).trim();
|
|
8818
|
-
if (value === "true") {
|
|
8819
|
-
frontmatter[key] = true;
|
|
8820
|
-
} else if (value === "false") {
|
|
8821
|
-
frontmatter[key] = false;
|
|
8822
|
-
} else {
|
|
8823
|
-
frontmatter[key] = value;
|
|
8824
|
-
}
|
|
8825
|
-
}
|
|
8826
|
-
return frontmatter;
|
|
8827
|
-
}
|
|
8828
|
-
function scanEditorFolderForSkills(editorPath, skills) {
|
|
8829
|
-
if (!(0, import_fs5.existsSync)(editorPath)) {
|
|
8830
|
-
return;
|
|
8831
|
-
}
|
|
8832
|
-
const entries = (0, import_fs5.readdirSync)(editorPath, { withFileTypes: true });
|
|
8833
|
-
for (const entry of entries) {
|
|
8834
|
-
if (EXCLUDED_DIRS2.has(entry.name)) {
|
|
8835
|
-
continue;
|
|
8836
|
-
}
|
|
8837
|
-
const fullPath = (0, import_path5.join)(editorPath, entry.name);
|
|
8838
|
-
if (entry.isDirectory()) {
|
|
8839
|
-
const skillMdPath = (0, import_path5.join)(fullPath, "SKILL.md");
|
|
8840
|
-
if ((0, import_fs5.existsSync)(skillMdPath)) {
|
|
8841
|
-
const content = (0, import_fs5.readFileSync)(skillMdPath, "utf-8");
|
|
8842
|
-
const frontmatter = parseFrontmatter(content);
|
|
8843
|
-
if (frontmatter.internal === true) {
|
|
8844
|
-
continue;
|
|
8845
|
-
}
|
|
8846
|
-
const name = typeof frontmatter.name === "string" ? frontmatter.name : entry.name;
|
|
8847
|
-
skills.push({
|
|
8848
|
-
name,
|
|
8849
|
-
dirName: name,
|
|
8850
|
-
content,
|
|
8851
|
-
sourcePath: skillMdPath
|
|
8852
|
-
});
|
|
8853
|
-
}
|
|
8854
|
-
scanEditorFolderForSkills(fullPath, skills);
|
|
8855
|
-
}
|
|
8856
|
-
}
|
|
6643
|
+
const fullPath = (0, import_path5.join)(editorPath, entry.name);
|
|
6644
|
+
if (entry.isDirectory()) {
|
|
6645
|
+
const skillDir = (0, import_path5.join)(fullPath, SkillsPathConstants.SKILL_DIR);
|
|
6646
|
+
const skillMdPath = (0, import_path5.join)(skillDir, SkillsPathConstants.SKILL_FILE);
|
|
6647
|
+
const legacySkillMdPath = (0, import_path5.join)(fullPath, SkillsPathConstants.SKILL_FILE);
|
|
6648
|
+
if (warnLegacy && !(0, import_fs5.existsSync)(skillMdPath) && (0, import_fs5.existsSync)(legacySkillMdPath)) {
|
|
6649
|
+
warnLegacySkillStructure(fullPath, legacySkillMdPath);
|
|
6650
|
+
}
|
|
6651
|
+
if ((0, import_fs5.existsSync)(skillMdPath)) {
|
|
6652
|
+
const content = (0, import_fs5.readFileSync)(skillMdPath, "utf-8");
|
|
6653
|
+
const frontmatter = parseFrontmatter(content);
|
|
6654
|
+
if (frontmatter.internal === true) {
|
|
6655
|
+
continue;
|
|
6656
|
+
}
|
|
6657
|
+
const name = typeof frontmatter.name === "string" ? frontmatter.name : entry.name;
|
|
6658
|
+
const additionalFiles = collectSkillFolderFiles(skillDir);
|
|
6659
|
+
skills.push({
|
|
6660
|
+
name,
|
|
6661
|
+
dirName: name,
|
|
6662
|
+
content,
|
|
6663
|
+
sourcePath: skillMdPath,
|
|
6664
|
+
additionalFiles,
|
|
6665
|
+
sourceType
|
|
6666
|
+
});
|
|
6667
|
+
}
|
|
6668
|
+
scanEditorFolderForSkills(fullPath, skills, sourceType, warnLegacy);
|
|
6669
|
+
}
|
|
6670
|
+
}
|
|
8857
6671
|
}
|
|
8858
6672
|
function findEditorFolders(basePath, maxDepth = 2) {
|
|
8859
6673
|
const editorFolders = [];
|
|
@@ -8877,7 +6691,7 @@ function findEditorFolders(basePath, maxDepth = 2) {
|
|
|
8877
6691
|
scan(basePath, 0);
|
|
8878
6692
|
return editorFolders;
|
|
8879
6693
|
}
|
|
8880
|
-
function collectProjectSkills() {
|
|
6694
|
+
function collectProjectSkills(excludedRoots = []) {
|
|
8881
6695
|
const projectRoot = findUnityProjectRoot();
|
|
8882
6696
|
if (!projectRoot) {
|
|
8883
6697
|
return [];
|
|
@@ -8885,9 +6699,9 @@ function collectProjectSkills() {
|
|
|
8885
6699
|
const skills = [];
|
|
8886
6700
|
const seenNames = /* @__PURE__ */ new Set();
|
|
8887
6701
|
const searchPaths = [
|
|
8888
|
-
(0, import_path5.join)(projectRoot,
|
|
8889
|
-
(0, import_path5.join)(projectRoot,
|
|
8890
|
-
(0, import_path5.join)(projectRoot,
|
|
6702
|
+
(0, import_path5.join)(projectRoot, SkillsPathConstants.ASSETS_DIR),
|
|
6703
|
+
(0, import_path5.join)(projectRoot, SkillsPathConstants.PACKAGES_DIR),
|
|
6704
|
+
(0, import_path5.join)(projectRoot, SkillsPathConstants.LIBRARY_DIR, SkillsPathConstants.PACKAGE_CACHE_DIR)
|
|
8891
6705
|
];
|
|
8892
6706
|
for (const searchPath of searchPaths) {
|
|
8893
6707
|
if (!(0, import_fs5.existsSync)(searchPath)) {
|
|
@@ -8895,11 +6709,14 @@ function collectProjectSkills() {
|
|
|
8895
6709
|
}
|
|
8896
6710
|
const editorFolders = findEditorFolders(searchPath, 3);
|
|
8897
6711
|
for (const editorFolder of editorFolders) {
|
|
8898
|
-
scanEditorFolderForSkills(editorFolder, skills);
|
|
6712
|
+
scanEditorFolderForSkills(editorFolder, skills, "project");
|
|
8899
6713
|
}
|
|
8900
6714
|
}
|
|
8901
6715
|
const uniqueSkills = [];
|
|
8902
6716
|
for (const skill of skills) {
|
|
6717
|
+
if (isUnderExcludedRoots(skill.sourcePath, excludedRoots)) {
|
|
6718
|
+
continue;
|
|
6719
|
+
}
|
|
8903
6720
|
if (!seenNames.has(skill.name)) {
|
|
8904
6721
|
seenNames.add(skill.name);
|
|
8905
6722
|
uniqueSkills.push(skill);
|
|
@@ -8908,21 +6725,13 @@ function collectProjectSkills() {
|
|
|
8908
6725
|
return uniqueSkills;
|
|
8909
6726
|
}
|
|
8910
6727
|
function getAllSkillStatuses(target, global) {
|
|
8911
|
-
const
|
|
8912
|
-
|
|
8913
|
-
status: getSkillStatus(skill, target, global),
|
|
8914
|
-
path: isSkillInstalled(skill, target, global) ? getSkillPath(skill.dirName, target, global) : void 0,
|
|
8915
|
-
source: "bundled"
|
|
8916
|
-
}));
|
|
8917
|
-
const projectSkills = collectProjectSkills();
|
|
8918
|
-
const bundledNames = new Set(BUNDLED_SKILLS.map((s) => s.name));
|
|
8919
|
-
const projectStatuses = projectSkills.filter((skill) => !bundledNames.has(skill.name)).map((skill) => ({
|
|
6728
|
+
const allSkills = collectAllSkills();
|
|
6729
|
+
return allSkills.map((skill) => ({
|
|
8920
6730
|
name: skill.name,
|
|
8921
6731
|
status: getSkillStatus(skill, target, global),
|
|
8922
6732
|
path: isSkillInstalled(skill, target, global) ? getSkillPath(skill.dirName, target, global) : void 0,
|
|
8923
|
-
source: "project"
|
|
6733
|
+
source: skill.sourceType === "project" ? "project" : "bundled"
|
|
8924
6734
|
}));
|
|
8925
|
-
return [...bundledStatuses, ...projectStatuses];
|
|
8926
6735
|
}
|
|
8927
6736
|
function installSkill(skill, target, global) {
|
|
8928
6737
|
const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
@@ -8935,7 +6744,7 @@ function installSkill(skill, target, global) {
|
|
|
8935
6744
|
for (const [relativePath, content] of Object.entries(additionalFiles)) {
|
|
8936
6745
|
const fullPath = (0, import_path5.join)(skillDir, relativePath);
|
|
8937
6746
|
(0, import_fs5.mkdirSync)((0, import_path5.dirname)(fullPath), { recursive: true });
|
|
8938
|
-
(0, import_fs5.writeFileSync)(fullPath, content
|
|
6747
|
+
(0, import_fs5.writeFileSync)(fullPath, content);
|
|
8939
6748
|
}
|
|
8940
6749
|
}
|
|
8941
6750
|
}
|
|
@@ -8954,58 +6763,48 @@ function installAllSkills(target, global) {
|
|
|
8954
6763
|
updated: 0,
|
|
8955
6764
|
skipped: 0,
|
|
8956
6765
|
bundledCount: 0,
|
|
8957
|
-
projectCount: 0
|
|
6766
|
+
projectCount: 0,
|
|
6767
|
+
deprecatedRemoved: 0
|
|
8958
6768
|
};
|
|
8959
|
-
|
|
8960
|
-
|
|
8961
|
-
|
|
8962
|
-
|
|
8963
|
-
|
|
8964
|
-
|
|
8965
|
-
installSkill(skill, target, global);
|
|
8966
|
-
result.updated++;
|
|
8967
|
-
} else {
|
|
8968
|
-
result.skipped++;
|
|
6769
|
+
const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
6770
|
+
for (const deprecatedName of DEPRECATED_SKILLS) {
|
|
6771
|
+
const deprecatedDir = (0, import_path5.join)(baseDir, deprecatedName);
|
|
6772
|
+
if ((0, import_fs5.existsSync)(deprecatedDir)) {
|
|
6773
|
+
(0, import_fs5.rmSync)(deprecatedDir, { recursive: true, force: true });
|
|
6774
|
+
result.deprecatedRemoved++;
|
|
8969
6775
|
}
|
|
8970
6776
|
}
|
|
8971
|
-
|
|
8972
|
-
const projectSkills =
|
|
8973
|
-
const
|
|
8974
|
-
for (const skill of
|
|
8975
|
-
if (bundledNames.has(skill.name)) {
|
|
8976
|
-
continue;
|
|
8977
|
-
}
|
|
6777
|
+
const allSkills = collectAllSkills();
|
|
6778
|
+
const projectSkills = allSkills.filter((skill) => skill.sourceType === "project");
|
|
6779
|
+
const nonProjectSkills = allSkills.filter((skill) => skill.sourceType !== "project");
|
|
6780
|
+
for (const skill of allSkills) {
|
|
8978
6781
|
const status = getSkillStatus(skill, target, global);
|
|
8979
6782
|
if (status === "not_installed") {
|
|
8980
6783
|
installSkill(skill, target, global);
|
|
8981
6784
|
result.installed++;
|
|
8982
|
-
result.projectCount++;
|
|
8983
6785
|
} else if (status === "outdated") {
|
|
8984
6786
|
installSkill(skill, target, global);
|
|
8985
6787
|
result.updated++;
|
|
8986
|
-
result.projectCount++;
|
|
8987
6788
|
} else {
|
|
8988
6789
|
result.skipped++;
|
|
8989
|
-
result.projectCount++;
|
|
8990
6790
|
}
|
|
8991
6791
|
}
|
|
6792
|
+
result.bundledCount = nonProjectSkills.length;
|
|
6793
|
+
result.projectCount = projectSkills.length;
|
|
8992
6794
|
return result;
|
|
8993
6795
|
}
|
|
8994
6796
|
function uninstallAllSkills(target, global) {
|
|
8995
6797
|
const result = { removed: 0, notFound: 0 };
|
|
8996
|
-
|
|
8997
|
-
|
|
6798
|
+
const baseDir = global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
6799
|
+
for (const deprecatedName of DEPRECATED_SKILLS) {
|
|
6800
|
+
const deprecatedDir = (0, import_path5.join)(baseDir, deprecatedName);
|
|
6801
|
+
if ((0, import_fs5.existsSync)(deprecatedDir)) {
|
|
6802
|
+
(0, import_fs5.rmSync)(deprecatedDir, { recursive: true, force: true });
|
|
8998
6803
|
result.removed++;
|
|
8999
|
-
} else {
|
|
9000
|
-
result.notFound++;
|
|
9001
6804
|
}
|
|
9002
6805
|
}
|
|
9003
|
-
const
|
|
9004
|
-
const
|
|
9005
|
-
for (const skill of projectSkills) {
|
|
9006
|
-
if (bundledNames.has(skill.name)) {
|
|
9007
|
-
continue;
|
|
9008
|
-
}
|
|
6806
|
+
const allSkills = collectAllSkills();
|
|
6807
|
+
for (const skill of allSkills) {
|
|
9009
6808
|
if (uninstallSkill(skill, target, global)) {
|
|
9010
6809
|
result.removed++;
|
|
9011
6810
|
} else {
|
|
@@ -9018,10 +6817,260 @@ function getInstallDir(target, global) {
|
|
|
9018
6817
|
return global ? getGlobalSkillsDir(target) : getProjectSkillsDir(target);
|
|
9019
6818
|
}
|
|
9020
6819
|
function getTotalSkillCount() {
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
|
|
9024
|
-
|
|
6820
|
+
return collectAllSkills().length;
|
|
6821
|
+
}
|
|
6822
|
+
function collectAllSkills() {
|
|
6823
|
+
const projectRoot = findUnityProjectRoot();
|
|
6824
|
+
const packageRoot = projectRoot ? resolvePackageRoot(projectRoot) : null;
|
|
6825
|
+
const packageSkills = packageRoot ? collectPackageSkillsFromRoot(packageRoot) : [];
|
|
6826
|
+
const cliOnlySkills = collectCliOnlySkills();
|
|
6827
|
+
const projectSkills = collectProjectSkills(packageRoot ? [packageRoot] : []);
|
|
6828
|
+
return dedupeSkillsByName([packageSkills, cliOnlySkills, projectSkills]);
|
|
6829
|
+
}
|
|
6830
|
+
function collectPackageSkillsFromRoot(packageRoot) {
|
|
6831
|
+
const mcpToolsRoot = (0, import_path5.join)(
|
|
6832
|
+
packageRoot,
|
|
6833
|
+
SkillsPathConstants.EDITOR_DIR,
|
|
6834
|
+
SkillsPathConstants.API_DIR,
|
|
6835
|
+
SkillsPathConstants.MCP_TOOLS_DIR
|
|
6836
|
+
);
|
|
6837
|
+
if (!(0, import_fs5.existsSync)(mcpToolsRoot)) {
|
|
6838
|
+
return [];
|
|
6839
|
+
}
|
|
6840
|
+
const skills = [];
|
|
6841
|
+
scanEditorFolderForSkills(mcpToolsRoot, skills, "package");
|
|
6842
|
+
return skills;
|
|
6843
|
+
}
|
|
6844
|
+
function collectCliOnlySkills() {
|
|
6845
|
+
const cliOnlyRoot = (0, import_path5.resolve)(
|
|
6846
|
+
__dirname,
|
|
6847
|
+
SkillsPathConstants.DIST_PARENT_DIR,
|
|
6848
|
+
SkillsPathConstants.SRC_DIR,
|
|
6849
|
+
SkillsPathConstants.SKILLS_DIR,
|
|
6850
|
+
SkillsPathConstants.CLI_ONLY_DIR,
|
|
6851
|
+
SkillsPathConstants.CLI_ONLY_SUBDIR
|
|
6852
|
+
);
|
|
6853
|
+
if (!(0, import_fs5.existsSync)(cliOnlyRoot)) {
|
|
6854
|
+
return [];
|
|
6855
|
+
}
|
|
6856
|
+
const skills = [];
|
|
6857
|
+
scanEditorFolderForSkills(cliOnlyRoot, skills, "cli-only", false);
|
|
6858
|
+
return skills;
|
|
6859
|
+
}
|
|
6860
|
+
function isExcludedFile(fileName) {
|
|
6861
|
+
if (EXCLUDED_FILES.has(fileName)) {
|
|
6862
|
+
return true;
|
|
6863
|
+
}
|
|
6864
|
+
for (const pattern of EXCLUDED_FILES) {
|
|
6865
|
+
if (fileName.endsWith(pattern)) {
|
|
6866
|
+
return true;
|
|
6867
|
+
}
|
|
6868
|
+
}
|
|
6869
|
+
return false;
|
|
6870
|
+
}
|
|
6871
|
+
function collectSkillFolderFilesRecursive(baseDir, currentDir, additionalFiles) {
|
|
6872
|
+
const entries = (0, import_fs5.readdirSync)(currentDir, { withFileTypes: true });
|
|
6873
|
+
for (const entry of entries) {
|
|
6874
|
+
if (isExcludedFile(entry.name)) {
|
|
6875
|
+
continue;
|
|
6876
|
+
}
|
|
6877
|
+
const fullPath = (0, import_path5.join)(currentDir, entry.name);
|
|
6878
|
+
const relativePath = fullPath.slice(baseDir.length + 1);
|
|
6879
|
+
if (entry.isDirectory()) {
|
|
6880
|
+
if (EXCLUDED_DIRS2.has(entry.name)) {
|
|
6881
|
+
continue;
|
|
6882
|
+
}
|
|
6883
|
+
collectSkillFolderFilesRecursive(baseDir, fullPath, additionalFiles);
|
|
6884
|
+
} else if (entry.isFile()) {
|
|
6885
|
+
if (entry.name === SkillsPathConstants.SKILL_FILE) {
|
|
6886
|
+
continue;
|
|
6887
|
+
}
|
|
6888
|
+
additionalFiles[relativePath] = (0, import_fs5.readFileSync)(fullPath);
|
|
6889
|
+
}
|
|
6890
|
+
}
|
|
6891
|
+
}
|
|
6892
|
+
function collectSkillFolderFiles(skillDir) {
|
|
6893
|
+
if (!(0, import_fs5.existsSync)(skillDir)) {
|
|
6894
|
+
return void 0;
|
|
6895
|
+
}
|
|
6896
|
+
const additionalFiles = {};
|
|
6897
|
+
collectSkillFolderFilesRecursive(skillDir, skillDir, additionalFiles);
|
|
6898
|
+
return Object.keys(additionalFiles).length > 0 ? additionalFiles : void 0;
|
|
6899
|
+
}
|
|
6900
|
+
function dedupeSkillsByName(skillGroups) {
|
|
6901
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
6902
|
+
const merged = [];
|
|
6903
|
+
for (const group of skillGroups) {
|
|
6904
|
+
for (const skill of group) {
|
|
6905
|
+
if (seenNames.has(skill.name)) {
|
|
6906
|
+
continue;
|
|
6907
|
+
}
|
|
6908
|
+
seenNames.add(skill.name);
|
|
6909
|
+
merged.push(skill);
|
|
6910
|
+
}
|
|
6911
|
+
}
|
|
6912
|
+
return merged;
|
|
6913
|
+
}
|
|
6914
|
+
function resolvePackageRoot(projectRoot) {
|
|
6915
|
+
const candidates = [];
|
|
6916
|
+
candidates.push((0, import_path5.join)(projectRoot, SkillsPathConstants.PACKAGES_DIR, SkillsPathConstants.SRC_DIR));
|
|
6917
|
+
const manifestPaths = resolveManifestPackagePaths(projectRoot);
|
|
6918
|
+
for (const manifestPath of manifestPaths) {
|
|
6919
|
+
candidates.push(manifestPath);
|
|
6920
|
+
}
|
|
6921
|
+
for (const packageName of SkillsPathConstants.PACKAGE_NAMES) {
|
|
6922
|
+
candidates.push((0, import_path5.join)(projectRoot, SkillsPathConstants.PACKAGES_DIR, packageName));
|
|
6923
|
+
}
|
|
6924
|
+
const directRoot = resolveFirstPackageRoot(candidates);
|
|
6925
|
+
if (directRoot) {
|
|
6926
|
+
return directRoot;
|
|
6927
|
+
}
|
|
6928
|
+
return resolvePackageCacheRoot(projectRoot);
|
|
6929
|
+
}
|
|
6930
|
+
function resolveManifestPackagePaths(projectRoot) {
|
|
6931
|
+
const manifestPath = (0, import_path5.join)(
|
|
6932
|
+
projectRoot,
|
|
6933
|
+
SkillsPathConstants.PACKAGES_DIR,
|
|
6934
|
+
SkillsPathConstants.MANIFEST_FILE
|
|
6935
|
+
);
|
|
6936
|
+
if (!(0, import_fs5.existsSync)(manifestPath)) {
|
|
6937
|
+
return [];
|
|
6938
|
+
}
|
|
6939
|
+
const manifestContent = (0, import_fs5.readFileSync)(manifestPath, "utf-8");
|
|
6940
|
+
let manifestJson;
|
|
6941
|
+
try {
|
|
6942
|
+
manifestJson = JSON.parse(manifestContent);
|
|
6943
|
+
} catch (error) {
|
|
6944
|
+
console.warn("Failed to parse manifest.json; skipping manifest-based path resolution.", error);
|
|
6945
|
+
return [];
|
|
6946
|
+
}
|
|
6947
|
+
const dependencies = manifestJson.dependencies;
|
|
6948
|
+
if (!dependencies) {
|
|
6949
|
+
return [];
|
|
6950
|
+
}
|
|
6951
|
+
const resolvedPaths = [];
|
|
6952
|
+
for (const [dependencyName, dependencyValue] of Object.entries(dependencies)) {
|
|
6953
|
+
if (!isTargetPackageName(dependencyName)) {
|
|
6954
|
+
continue;
|
|
6955
|
+
}
|
|
6956
|
+
const localPath = resolveLocalDependencyPath(dependencyValue, projectRoot);
|
|
6957
|
+
if (localPath) {
|
|
6958
|
+
resolvedPaths.push(localPath);
|
|
6959
|
+
}
|
|
6960
|
+
}
|
|
6961
|
+
return resolvedPaths;
|
|
6962
|
+
}
|
|
6963
|
+
function resolveLocalDependencyPath(dependencyValue, projectRoot) {
|
|
6964
|
+
if (dependencyValue.startsWith(SkillsPathConstants.FILE_PROTOCOL)) {
|
|
6965
|
+
const rawPath = dependencyValue.slice(SkillsPathConstants.FILE_PROTOCOL.length);
|
|
6966
|
+
return resolveDependencyPath(rawPath, projectRoot);
|
|
6967
|
+
}
|
|
6968
|
+
if (dependencyValue.startsWith(SkillsPathConstants.PATH_PROTOCOL)) {
|
|
6969
|
+
const rawPath = dependencyValue.slice(SkillsPathConstants.PATH_PROTOCOL.length);
|
|
6970
|
+
return resolveDependencyPath(rawPath, projectRoot);
|
|
6971
|
+
}
|
|
6972
|
+
return null;
|
|
6973
|
+
}
|
|
6974
|
+
function resolveDependencyPath(rawPath, projectRoot) {
|
|
6975
|
+
const trimmed = rawPath.trim();
|
|
6976
|
+
if (!trimmed) {
|
|
6977
|
+
return null;
|
|
6978
|
+
}
|
|
6979
|
+
let normalizedPath = trimmed;
|
|
6980
|
+
if (normalizedPath.startsWith("//")) {
|
|
6981
|
+
normalizedPath = normalizedPath.slice(2);
|
|
6982
|
+
}
|
|
6983
|
+
if ((0, import_path5.isAbsolute)(normalizedPath)) {
|
|
6984
|
+
return normalizedPath;
|
|
6985
|
+
}
|
|
6986
|
+
return (0, import_path5.resolve)(projectRoot, normalizedPath);
|
|
6987
|
+
}
|
|
6988
|
+
function resolveFirstPackageRoot(candidates) {
|
|
6989
|
+
for (const candidate of candidates) {
|
|
6990
|
+
const resolvedRoot = resolvePackageRootCandidate(candidate);
|
|
6991
|
+
if (resolvedRoot) {
|
|
6992
|
+
return resolvedRoot;
|
|
6993
|
+
}
|
|
6994
|
+
}
|
|
6995
|
+
return null;
|
|
6996
|
+
}
|
|
6997
|
+
function resolvePackageCacheRoot(projectRoot) {
|
|
6998
|
+
const packageCacheDir = (0, import_path5.join)(
|
|
6999
|
+
projectRoot,
|
|
7000
|
+
SkillsPathConstants.LIBRARY_DIR,
|
|
7001
|
+
SkillsPathConstants.PACKAGE_CACHE_DIR
|
|
7002
|
+
);
|
|
7003
|
+
if (!(0, import_fs5.existsSync)(packageCacheDir)) {
|
|
7004
|
+
return null;
|
|
7005
|
+
}
|
|
7006
|
+
const entries = (0, import_fs5.readdirSync)(packageCacheDir, { withFileTypes: true });
|
|
7007
|
+
for (const entry of entries) {
|
|
7008
|
+
if (!entry.isDirectory()) {
|
|
7009
|
+
continue;
|
|
7010
|
+
}
|
|
7011
|
+
if (!isTargetPackageCacheDir(entry.name)) {
|
|
7012
|
+
continue;
|
|
7013
|
+
}
|
|
7014
|
+
const candidate = (0, import_path5.join)(packageCacheDir, entry.name);
|
|
7015
|
+
const resolvedRoot = resolvePackageRootCandidate(candidate);
|
|
7016
|
+
if (resolvedRoot) {
|
|
7017
|
+
return resolvedRoot;
|
|
7018
|
+
}
|
|
7019
|
+
}
|
|
7020
|
+
return null;
|
|
7021
|
+
}
|
|
7022
|
+
function resolvePackageRootCandidate(candidate) {
|
|
7023
|
+
if (!(0, import_fs5.existsSync)(candidate)) {
|
|
7024
|
+
return null;
|
|
7025
|
+
}
|
|
7026
|
+
const directToolsPath = (0, import_path5.join)(
|
|
7027
|
+
candidate,
|
|
7028
|
+
SkillsPathConstants.EDITOR_DIR,
|
|
7029
|
+
SkillsPathConstants.API_DIR,
|
|
7030
|
+
SkillsPathConstants.MCP_TOOLS_DIR
|
|
7031
|
+
);
|
|
7032
|
+
if ((0, import_fs5.existsSync)(directToolsPath)) {
|
|
7033
|
+
return candidate;
|
|
7034
|
+
}
|
|
7035
|
+
const nestedRoot = (0, import_path5.join)(candidate, SkillsPathConstants.PACKAGES_DIR, SkillsPathConstants.SRC_DIR);
|
|
7036
|
+
const nestedToolsPath = (0, import_path5.join)(
|
|
7037
|
+
nestedRoot,
|
|
7038
|
+
SkillsPathConstants.EDITOR_DIR,
|
|
7039
|
+
SkillsPathConstants.API_DIR,
|
|
7040
|
+
SkillsPathConstants.MCP_TOOLS_DIR
|
|
7041
|
+
);
|
|
7042
|
+
if ((0, import_fs5.existsSync)(nestedToolsPath)) {
|
|
7043
|
+
return nestedRoot;
|
|
7044
|
+
}
|
|
7045
|
+
return null;
|
|
7046
|
+
}
|
|
7047
|
+
function isTargetPackageName(name) {
|
|
7048
|
+
const normalized = name.toLowerCase();
|
|
7049
|
+
return SkillsPathConstants.PACKAGE_NAMES.some(
|
|
7050
|
+
(packageName) => packageName.toLowerCase() === normalized
|
|
7051
|
+
);
|
|
7052
|
+
}
|
|
7053
|
+
function isTargetPackageCacheDir(dirName) {
|
|
7054
|
+
const normalized = dirName.toLowerCase();
|
|
7055
|
+
return SkillsPathConstants.PACKAGE_NAMES.some(
|
|
7056
|
+
(packageName) => normalized.startsWith(`${packageName.toLowerCase()}@`)
|
|
7057
|
+
);
|
|
7058
|
+
}
|
|
7059
|
+
function isUnderExcludedRoots(targetPath, excludedRoots) {
|
|
7060
|
+
for (const root of excludedRoots) {
|
|
7061
|
+
if (isPathUnder(targetPath, root)) {
|
|
7062
|
+
return true;
|
|
7063
|
+
}
|
|
7064
|
+
}
|
|
7065
|
+
return false;
|
|
7066
|
+
}
|
|
7067
|
+
function isPathUnder(childPath, parentPath) {
|
|
7068
|
+
const resolvedChild = (0, import_path5.resolve)(childPath);
|
|
7069
|
+
const resolvedParent = (0, import_path5.resolve)(parentPath);
|
|
7070
|
+
if (resolvedChild === resolvedParent) {
|
|
7071
|
+
return true;
|
|
7072
|
+
}
|
|
7073
|
+
return resolvedChild.startsWith(resolvedParent + import_path5.sep);
|
|
9025
7074
|
}
|
|
9026
7075
|
|
|
9027
7076
|
// src/skills/target-config.ts
|
|
@@ -9114,7 +7163,7 @@ uloop Skills Status:`);
|
|
|
9114
7163
|
}
|
|
9115
7164
|
console.log("");
|
|
9116
7165
|
}
|
|
9117
|
-
console.log(`Total: ${getTotalSkillCount()}
|
|
7166
|
+
console.log(`Total: ${getTotalSkillCount()} skills`);
|
|
9118
7167
|
}
|
|
9119
7168
|
function getStatusIcon(status) {
|
|
9120
7169
|
switch (status) {
|
|
@@ -9152,6 +7201,9 @@ Installing uloop skills (${location})...`);
|
|
|
9152
7201
|
console.log(` \x1B[32m\u2713\x1B[0m Installed: ${result.installed}`);
|
|
9153
7202
|
console.log(` \x1B[33m\u2191\x1B[0m Updated: ${result.updated}`);
|
|
9154
7203
|
console.log(` \x1B[90m-\x1B[0m Skipped (up-to-date): ${result.skipped}`);
|
|
7204
|
+
if (result.deprecatedRemoved > 0) {
|
|
7205
|
+
console.log(` \x1B[31m\u2717\x1B[0m Deprecated removed: ${result.deprecatedRemoved}`);
|
|
7206
|
+
}
|
|
9155
7207
|
console.log(` Location: ${dir}`);
|
|
9156
7208
|
console.log("");
|
|
9157
7209
|
}
|