mcpforunityserver 9.3.0b20260128055651__tar.gz → 9.3.0b20260129104751__tar.gz

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.
Files changed (123) hide show
  1. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/PKG-INFO +21 -1
  2. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/README.md +20 -0
  3. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/pyproject.toml +1 -1
  4. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/commands/animation.py +6 -9
  5. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/commands/asset.py +50 -80
  6. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/commands/audio.py +14 -22
  7. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/commands/batch.py +20 -33
  8. mcpforunityserver-9.3.0b20260129104751/src/cli/commands/code.py +182 -0
  9. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/commands/component.py +33 -55
  10. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/commands/editor.py +122 -188
  11. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/commands/gameobject.py +60 -83
  12. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/commands/instance.py +28 -36
  13. mcpforunityserver-9.3.0b20260129104751/src/cli/commands/lighting.py +123 -0
  14. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/commands/material.py +39 -68
  15. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/commands/prefab.py +63 -81
  16. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/commands/scene.py +30 -54
  17. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/commands/script.py +32 -50
  18. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/commands/shader.py +43 -55
  19. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/commands/texture.py +53 -51
  20. mcpforunityserver-9.3.0b20260129104751/src/cli/commands/tool.py +58 -0
  21. mcpforunityserver-9.3.0b20260129104751/src/cli/commands/ui.py +258 -0
  22. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/commands/vfx.py +84 -138
  23. mcpforunityserver-9.3.0b20260129104751/src/cli/utils/confirmation.py +37 -0
  24. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/utils/connection.py +32 -2
  25. mcpforunityserver-9.3.0b20260129104751/src/cli/utils/constants.py +23 -0
  26. mcpforunityserver-9.3.0b20260129104751/src/cli/utils/parsers.py +112 -0
  27. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/core/config.py +0 -4
  28. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/core/telemetry.py +20 -2
  29. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/mcpforunityserver.egg-info/PKG-INFO +21 -1
  30. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/mcpforunityserver.egg-info/SOURCES.txt +10 -2
  31. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/resources/active_tool.py +1 -1
  32. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/resources/custom_tools.py +1 -1
  33. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/resources/editor_state.py +1 -1
  34. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/resources/gameobject.py +4 -4
  35. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/resources/layers.py +1 -1
  36. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/resources/menu_items.py +1 -1
  37. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/resources/prefab.py +3 -3
  38. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/resources/prefab_stage.py +1 -1
  39. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/resources/project_info.py +1 -1
  40. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/resources/selection.py +1 -1
  41. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/resources/tags.py +1 -1
  42. mcpforunityserver-9.3.0b20260129104751/src/services/resources/tests.py +87 -0
  43. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/resources/unity_instances.py +1 -1
  44. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/resources/windows.py +1 -1
  45. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/__init__.py +3 -1
  46. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/find_gameobjects.py +32 -11
  47. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/manage_gameobject.py +11 -66
  48. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/manage_material.py +4 -37
  49. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/manage_prefabs.py +16 -7
  50. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/manage_script.py +1 -1
  51. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/manage_texture.py +10 -96
  52. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/run_tests.py +67 -4
  53. mcpforunityserver-9.3.0b20260129104751/src/services/tools/utils.py +348 -0
  54. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/transport/models.py +1 -0
  55. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/transport/plugin_hub.py +2 -1
  56. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/transport/plugin_registry.py +3 -0
  57. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/transport/unity_transport.py +0 -51
  58. mcpforunityserver-9.3.0b20260129104751/src/utils/focus_nudge.py +589 -0
  59. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/tests/test_cli.py +1 -1
  60. mcpforunityserver-9.3.0b20260129104751/tests/test_cli_commands_characterization.py +1051 -0
  61. mcpforunityserver-9.3.0b20260129104751/tests/test_core_infrastructure_characterization.py +1350 -0
  62. mcpforunityserver-9.3.0b20260129104751/tests/test_models_characterization.py +933 -0
  63. mcpforunityserver-9.3.0b20260129104751/tests/test_param_normalizer.py +151 -0
  64. mcpforunityserver-9.3.0b20260129104751/tests/test_transport_characterization.py +1118 -0
  65. mcpforunityserver-9.3.0b20260129104751/tests/test_utilities_characterization.py +0 -0
  66. mcpforunityserver-9.3.0b20260128055651/src/cli/commands/code.py +0 -189
  67. mcpforunityserver-9.3.0b20260128055651/src/cli/commands/lighting.py +0 -128
  68. mcpforunityserver-9.3.0b20260128055651/src/cli/commands/tool.py +0 -61
  69. mcpforunityserver-9.3.0b20260128055651/src/cli/commands/ui.py +0 -263
  70. mcpforunityserver-9.3.0b20260128055651/src/services/resources/tests.py +0 -55
  71. mcpforunityserver-9.3.0b20260128055651/src/services/tools/utils.py +0 -131
  72. mcpforunityserver-9.3.0b20260128055651/src/utils/focus_nudge.py +0 -321
  73. mcpforunityserver-9.3.0b20260128055651/src/utils/reload_sentinel.py +0 -9
  74. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/LICENSE +0 -0
  75. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/setup.cfg +0 -0
  76. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/__init__.py +0 -0
  77. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/commands/__init__.py +0 -0
  78. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/main.py +0 -0
  79. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/utils/__init__.py +0 -0
  80. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/utils/config.py +0 -0
  81. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/utils/output.py +0 -0
  82. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/cli/utils/suggestions.py +0 -0
  83. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/core/__init__.py +0 -0
  84. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/core/logging_decorator.py +0 -0
  85. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/core/telemetry_decorator.py +0 -0
  86. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/main.py +0 -0
  87. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/mcpforunityserver.egg-info/dependency_links.txt +0 -0
  88. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/mcpforunityserver.egg-info/entry_points.txt +0 -0
  89. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/mcpforunityserver.egg-info/requires.txt +0 -0
  90. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/mcpforunityserver.egg-info/top_level.txt +0 -0
  91. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/models/__init__.py +0 -0
  92. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/models/models.py +0 -0
  93. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/models/unity_response.py +0 -0
  94. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/__init__.py +0 -0
  95. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/custom_tool_service.py +0 -0
  96. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/registry/__init__.py +0 -0
  97. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/registry/resource_registry.py +0 -0
  98. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/registry/tool_registry.py +0 -0
  99. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/resources/__init__.py +0 -0
  100. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/state/external_changes_scanner.py +0 -0
  101. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/batch_execute.py +0 -0
  102. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/debug_request_context.py +0 -0
  103. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/execute_custom_tool.py +0 -0
  104. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/execute_menu_item.py +0 -0
  105. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/find_in_file.py +0 -0
  106. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/manage_asset.py +0 -0
  107. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/manage_components.py +0 -0
  108. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/manage_editor.py +0 -0
  109. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/manage_scene.py +0 -0
  110. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/manage_scriptable_object.py +0 -0
  111. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/manage_shader.py +0 -0
  112. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/manage_vfx.py +0 -0
  113. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/preflight.py +0 -0
  114. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/read_console.py +0 -0
  115. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/refresh_unity.py +0 -0
  116. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/script_apply_edits.py +0 -0
  117. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/services/tools/set_active_instance.py +0 -0
  118. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/transport/__init__.py +0 -0
  119. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/transport/legacy/port_discovery.py +0 -0
  120. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/transport/legacy/stdio_port_registry.py +0 -0
  121. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/transport/legacy/unity_connection.py +0 -0
  122. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/transport/unity_instance_middleware.py +0 -0
  123. {mcpforunityserver-9.3.0b20260128055651 → mcpforunityserver-9.3.0b20260129104751}/src/utils/module_discovery.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcpforunityserver
3
- Version: 9.3.0b20260128055651
3
+ Version: 9.3.0b20260129104751
4
4
  Summary: MCP for Unity Server: A Unity package for Unity Editor integration via the Model Context Protocol (MCP).
5
5
  Author-email: Marcus Sanatan <msanatan@gmail.com>, David Sarno <david.sarno@gmail.com>, Wu Shutong <martinwfire@gmail.com>
6
6
  License-Expression: MIT
@@ -163,6 +163,26 @@ The server connects to Unity Editor automatically when both are running. No addi
163
163
 
164
164
  ---
165
165
 
166
+ ## MCP Resources
167
+
168
+ The server provides read-only MCP resources for querying Unity Editor state. Resources provide up-to-date information about your Unity project without modifying it.
169
+
170
+ **Accessing Resources:**
171
+
172
+ Resources are accessed by their URI (not their name). Always use `ListMcpResources` to get the correct URI format.
173
+
174
+ **Example URIs:**
175
+ - `mcpforunity://editor/state` - Editor readiness snapshot
176
+ - `mcpforunity://project/tags` - All project tags
177
+ - `mcpforunity://scene/gameobject/{instance_id}` - GameObject details by ID
178
+ - `mcpforunity://prefab/{encoded_path}` - Prefab info by asset path
179
+
180
+ **Important:** Resource names use underscores (e.g., `editor_state`) but URIs use slashes/hyphens (e.g., `mcpforunity://editor/state`). Always use the URI from `ListMcpResources()` when reading resources.
181
+
182
+ **All resource descriptions now include their URI** for easy reference. List available resources to see the complete catalog with URIs.
183
+
184
+ ---
185
+
166
186
  ## Example Prompts
167
187
 
168
188
  Once connected, try these commands in your AI assistant:
@@ -122,6 +122,26 @@ The server connects to Unity Editor automatically when both are running. No addi
122
122
 
123
123
  ---
124
124
 
125
+ ## MCP Resources
126
+
127
+ The server provides read-only MCP resources for querying Unity Editor state. Resources provide up-to-date information about your Unity project without modifying it.
128
+
129
+ **Accessing Resources:**
130
+
131
+ Resources are accessed by their URI (not their name). Always use `ListMcpResources` to get the correct URI format.
132
+
133
+ **Example URIs:**
134
+ - `mcpforunity://editor/state` - Editor readiness snapshot
135
+ - `mcpforunity://project/tags` - All project tags
136
+ - `mcpforunity://scene/gameobject/{instance_id}` - GameObject details by ID
137
+ - `mcpforunity://prefab/{encoded_path}` - Prefab info by asset path
138
+
139
+ **Important:** Resource names use underscores (e.g., `editor_state`) but URIs use slashes/hyphens (e.g., `mcpforunity://editor/state`). Always use the URI from `ListMcpResources()` when reading resources.
140
+
141
+ **All resource descriptions now include their URI** for easy reference. List available resources to see the complete catalog with URIs.
142
+
143
+ ---
144
+
125
145
  ## Example Prompts
126
146
 
127
147
  Once connected, try these commands in your AI assistant:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mcpforunityserver"
3
- version = "9.3.0b20260128055651"
3
+ version = "9.3.0b20260129104751"
4
4
  description = "MCP for Unity Server: A Unity package for Unity Editor integration via the Model Context Protocol (MCP)."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -1,12 +1,12 @@
1
1
  """Animation CLI commands - placeholder for future implementation."""
2
2
 
3
- import sys
4
3
  import click
5
4
  from typing import Optional, Any
6
5
 
7
6
  from cli.utils.config import get_config
8
7
  from cli.utils.output import format_output, print_error, print_info
9
- from cli.utils.connection import run_command, UnityConnectionError
8
+ from cli.utils.connection import run_command, handle_unity_errors
9
+ from cli.utils.constants import SEARCH_METHOD_CHOICE_BASIC
10
10
 
11
11
 
12
12
  @click.group()
@@ -26,10 +26,11 @@ def animation():
26
26
  )
27
27
  @click.option(
28
28
  "--search-method",
29
- type=click.Choice(["by_name", "by_path", "by_id"]),
29
+ type=SEARCH_METHOD_CHOICE_BASIC,
30
30
  default=None,
31
31
  help="How to find the target."
32
32
  )
33
+ @handle_unity_errors
33
34
  def play(target: str, state_name: str, layer: int, search_method: Optional[str]):
34
35
  """Play an animation state on a target's Animator.
35
36
 
@@ -53,12 +54,8 @@ def play(target: str, state_name: str, layer: int, search_method: Optional[str])
53
54
  if search_method:
54
55
  params["searchMethod"] = search_method
55
56
 
56
- try:
57
- result = run_command("manage_components", params, config)
58
- click.echo(format_output(result, config.format))
59
- except UnityConnectionError as e:
60
- print_error(str(e))
61
- sys.exit(1)
57
+ result = run_command("manage_components", params, config)
58
+ click.echo(format_output(result, config.format))
62
59
 
63
60
 
64
61
  @animation.command("set-parameter")
@@ -7,7 +7,9 @@ from typing import Optional, Any
7
7
 
8
8
  from cli.utils.config import get_config
9
9
  from cli.utils.output import format_output, print_error, print_success
10
- from cli.utils.connection import run_command, UnityConnectionError
10
+ from cli.utils.connection import run_command, handle_unity_errors
11
+ from cli.utils.parsers import parse_json_dict_or_exit
12
+ from cli.utils.confirmation import confirm_destructive_action
11
13
 
12
14
 
13
15
  @click.group()
@@ -41,6 +43,7 @@ def asset():
41
43
  type=int,
42
44
  help="Page number (1-based)."
43
45
  )
46
+ @handle_unity_errors
44
47
  def search(pattern: str, path: str, filter_type: Optional[str], limit: int, page: int):
45
48
  """Search for assets.
46
49
 
@@ -64,12 +67,8 @@ def search(pattern: str, path: str, filter_type: Optional[str], limit: int, page
64
67
  if filter_type:
65
68
  params["filterType"] = filter_type
66
69
 
67
- try:
68
- result = run_command("manage_asset", params, config)
69
- click.echo(format_output(result, config.format))
70
- except UnityConnectionError as e:
71
- print_error(str(e))
72
- sys.exit(1)
70
+ result = run_command("manage_asset", params, config)
71
+ click.echo(format_output(result, config.format))
73
72
 
74
73
 
75
74
  @asset.command("info")
@@ -79,6 +78,7 @@ def search(pattern: str, path: str, filter_type: Optional[str], limit: int, page
79
78
  is_flag=True,
80
79
  help="Generate preview thumbnail (may be large)."
81
80
  )
81
+ @handle_unity_errors
82
82
  def info(path: str, preview: bool):
83
83
  """Get detailed information about an asset.
84
84
 
@@ -95,12 +95,8 @@ def info(path: str, preview: bool):
95
95
  "generatePreview": preview,
96
96
  }
97
97
 
98
- try:
99
- result = run_command("manage_asset", params, config)
100
- click.echo(format_output(result, config.format))
101
- except UnityConnectionError as e:
102
- print_error(str(e))
103
- sys.exit(1)
98
+ result = run_command("manage_asset", params, config)
99
+ click.echo(format_output(result, config.format))
104
100
 
105
101
 
106
102
  @asset.command("create")
@@ -111,6 +107,7 @@ def info(path: str, preview: bool):
111
107
  default=None,
112
108
  help='Initial properties as JSON.'
113
109
  )
110
+ @handle_unity_errors
114
111
  def create(path: str, asset_type: str, properties: Optional[str]):
115
112
  """Create a new asset.
116
113
 
@@ -129,20 +126,12 @@ def create(path: str, asset_type: str, properties: Optional[str]):
129
126
  }
130
127
 
131
128
  if properties:
132
- try:
133
- params["properties"] = json.loads(properties)
134
- except json.JSONDecodeError as e:
135
- print_error(f"Invalid JSON for properties: {e}")
136
- sys.exit(1)
137
-
138
- try:
139
- result = run_command("manage_asset", params, config)
140
- click.echo(format_output(result, config.format))
141
- if result.get("success"):
142
- print_success(f"Created {asset_type}: {path}")
143
- except UnityConnectionError as e:
144
- print_error(str(e))
145
- sys.exit(1)
129
+ params["properties"] = parse_json_dict_or_exit(properties, "properties")
130
+
131
+ result = run_command("manage_asset", params, config)
132
+ click.echo(format_output(result, config.format))
133
+ if result.get("success"):
134
+ print_success(f"Created {asset_type}: {path}")
146
135
 
147
136
 
148
137
  @asset.command("delete")
@@ -152,6 +141,7 @@ def create(path: str, asset_type: str, properties: Optional[str]):
152
141
  is_flag=True,
153
142
  help="Skip confirmation prompt."
154
143
  )
144
+ @handle_unity_errors
155
145
  def delete(path: str, force: bool):
156
146
  """Delete an asset.
157
147
 
@@ -162,23 +152,19 @@ def delete(path: str, force: bool):
162
152
  """
163
153
  config = get_config()
164
154
 
165
- if not force:
166
- click.confirm(f"Delete asset '{path}'?", abort=True)
155
+ confirm_destructive_action("Delete", "asset", path, force)
167
156
 
168
- try:
169
- result = run_command(
170
- "manage_asset", {"action": "delete", "path": path}, config)
171
- click.echo(format_output(result, config.format))
172
- if result.get("success"):
173
- print_success(f"Deleted: {path}")
174
- except UnityConnectionError as e:
175
- print_error(str(e))
176
- sys.exit(1)
157
+ result = run_command(
158
+ "manage_asset", {"action": "delete", "path": path}, config)
159
+ click.echo(format_output(result, config.format))
160
+ if result.get("success"):
161
+ print_success(f"Deleted: {path}")
177
162
 
178
163
 
179
164
  @asset.command("duplicate")
180
165
  @click.argument("source")
181
166
  @click.argument("destination")
167
+ @handle_unity_errors
182
168
  def duplicate(source: str, destination: str):
183
169
  """Duplicate an asset.
184
170
 
@@ -194,19 +180,16 @@ def duplicate(source: str, destination: str):
194
180
  "destination": destination,
195
181
  }
196
182
 
197
- try:
198
- result = run_command("manage_asset", params, config)
199
- click.echo(format_output(result, config.format))
200
- if result.get("success"):
201
- print_success(f"Duplicated to: {destination}")
202
- except UnityConnectionError as e:
203
- print_error(str(e))
204
- sys.exit(1)
183
+ result = run_command("manage_asset", params, config)
184
+ click.echo(format_output(result, config.format))
185
+ if result.get("success"):
186
+ print_success(f"Duplicated to: {destination}")
205
187
 
206
188
 
207
189
  @asset.command("move")
208
190
  @click.argument("source")
209
191
  @click.argument("destination")
192
+ @handle_unity_errors
210
193
  def move(source: str, destination: str):
211
194
  """Move an asset to a new location.
212
195
 
@@ -222,19 +205,16 @@ def move(source: str, destination: str):
222
205
  "destination": destination,
223
206
  }
224
207
 
225
- try:
226
- result = run_command("manage_asset", params, config)
227
- click.echo(format_output(result, config.format))
228
- if result.get("success"):
229
- print_success(f"Moved to: {destination}")
230
- except UnityConnectionError as e:
231
- print_error(str(e))
232
- sys.exit(1)
208
+ result = run_command("manage_asset", params, config)
209
+ click.echo(format_output(result, config.format))
210
+ if result.get("success"):
211
+ print_success(f"Moved to: {destination}")
233
212
 
234
213
 
235
214
  @asset.command("rename")
236
215
  @click.argument("path")
237
216
  @click.argument("new_name")
217
+ @handle_unity_errors
238
218
  def rename(path: str, new_name: str):
239
219
  """Rename an asset.
240
220
 
@@ -255,18 +235,15 @@ def rename(path: str, new_name: str):
255
235
  "destination": destination,
256
236
  }
257
237
 
258
- try:
259
- result = run_command("manage_asset", params, config)
260
- click.echo(format_output(result, config.format))
261
- if result.get("success"):
262
- print_success(f"Renamed to: {new_name}")
263
- except UnityConnectionError as e:
264
- print_error(str(e))
265
- sys.exit(1)
238
+ result = run_command("manage_asset", params, config)
239
+ click.echo(format_output(result, config.format))
240
+ if result.get("success"):
241
+ print_success(f"Renamed to: {new_name}")
266
242
 
267
243
 
268
244
  @asset.command("import")
269
245
  @click.argument("path")
246
+ @handle_unity_errors
270
247
  def import_asset(path: str):
271
248
  """Import/reimport an asset.
272
249
 
@@ -276,19 +253,16 @@ def import_asset(path: str):
276
253
  """
277
254
  config = get_config()
278
255
 
279
- try:
280
- result = run_command(
281
- "manage_asset", {"action": "import", "path": path}, config)
282
- click.echo(format_output(result, config.format))
283
- if result.get("success"):
284
- print_success(f"Imported: {path}")
285
- except UnityConnectionError as e:
286
- print_error(str(e))
287
- sys.exit(1)
256
+ result = run_command(
257
+ "manage_asset", {"action": "import", "path": path}, config)
258
+ click.echo(format_output(result, config.format))
259
+ if result.get("success"):
260
+ print_success(f"Imported: {path}")
288
261
 
289
262
 
290
263
  @asset.command("mkdir")
291
264
  @click.argument("path")
265
+ @handle_unity_errors
292
266
  def mkdir(path: str):
293
267
  """Create a folder.
294
268
 
@@ -299,12 +273,8 @@ def mkdir(path: str):
299
273
  """
300
274
  config = get_config()
301
275
 
302
- try:
303
- result = run_command(
304
- "manage_asset", {"action": "create_folder", "path": path}, config)
305
- click.echo(format_output(result, config.format))
306
- if result.get("success"):
307
- print_success(f"Created folder: {path}")
308
- except UnityConnectionError as e:
309
- print_error(str(e))
310
- sys.exit(1)
276
+ result = run_command(
277
+ "manage_asset", {"action": "create_folder", "path": path}, config)
278
+ click.echo(format_output(result, config.format))
279
+ if result.get("success"):
280
+ print_success(f"Created folder: {path}")
@@ -6,7 +6,8 @@ from typing import Optional, Any
6
6
 
7
7
  from cli.utils.config import get_config
8
8
  from cli.utils.output import format_output, print_error, print_info
9
- from cli.utils.connection import run_command, UnityConnectionError
9
+ from cli.utils.connection import run_command, handle_unity_errors
10
+ from cli.utils.constants import SEARCH_METHOD_CHOICE_BASIC
10
11
 
11
12
 
12
13
  @click.group()
@@ -24,10 +25,11 @@ def audio():
24
25
  )
25
26
  @click.option(
26
27
  "--search-method",
27
- type=click.Choice(["by_name", "by_path", "by_id"]),
28
+ type=SEARCH_METHOD_CHOICE_BASIC,
28
29
  default=None,
29
30
  help="How to find the target."
30
31
  )
32
+ @handle_unity_errors
31
33
  def play(target: str, clip: Optional[str], search_method: Optional[str]):
32
34
  """Play audio on a target's AudioSource.
33
35
 
@@ -52,22 +54,19 @@ def play(target: str, clip: Optional[str], search_method: Optional[str]):
52
54
  if search_method:
53
55
  params["searchMethod"] = search_method
54
56
 
55
- try:
56
- result = run_command("manage_components", params, config)
57
- click.echo(format_output(result, config.format))
58
- except UnityConnectionError as e:
59
- print_error(str(e))
60
- sys.exit(1)
57
+ result = run_command("manage_components", params, config)
58
+ click.echo(format_output(result, config.format))
61
59
 
62
60
 
63
61
  @audio.command("stop")
64
62
  @click.argument("target")
65
63
  @click.option(
66
64
  "--search-method",
67
- type=click.Choice(["by_name", "by_path", "by_id"]),
65
+ type=SEARCH_METHOD_CHOICE_BASIC,
68
66
  default=None,
69
67
  help="How to find the target."
70
68
  )
69
+ @handle_unity_errors
71
70
  def stop(target: str, search_method: Optional[str]):
72
71
  """Stop audio on a target's AudioSource.
73
72
 
@@ -88,12 +87,8 @@ def stop(target: str, search_method: Optional[str]):
88
87
  if search_method:
89
88
  params["searchMethod"] = search_method
90
89
 
91
- try:
92
- result = run_command("manage_components", params, config)
93
- click.echo(format_output(result, config.format))
94
- except UnityConnectionError as e:
95
- print_error(str(e))
96
- sys.exit(1)
90
+ result = run_command("manage_components", params, config)
91
+ click.echo(format_output(result, config.format))
97
92
 
98
93
 
99
94
  @audio.command("volume")
@@ -101,10 +96,11 @@ def stop(target: str, search_method: Optional[str]):
101
96
  @click.argument("level", type=float)
102
97
  @click.option(
103
98
  "--search-method",
104
- type=click.Choice(["by_name", "by_path", "by_id"]),
99
+ type=SEARCH_METHOD_CHOICE_BASIC,
105
100
  default=None,
106
101
  help="How to find the target."
107
102
  )
103
+ @handle_unity_errors
108
104
  def volume(target: str, level: float, search_method: Optional[str]):
109
105
  """Set audio volume on a target's AudioSource.
110
106
 
@@ -125,9 +121,5 @@ def volume(target: str, level: float, search_method: Optional[str]):
125
121
  if search_method:
126
122
  params["searchMethod"] = search_method
127
123
 
128
- try:
129
- result = run_command("manage_components", params, config)
130
- click.echo(format_output(result, config.format))
131
- except UnityConnectionError as e:
132
- print_error(str(e))
133
- sys.exit(1)
124
+ result = run_command("manage_components", params, config)
125
+ click.echo(format_output(result, config.format))
@@ -7,7 +7,8 @@ from typing import Optional, Any
7
7
 
8
8
  from cli.utils.config import get_config
9
9
  from cli.utils.output import format_output, print_error, print_success, print_info
10
- from cli.utils.connection import run_command, UnityConnectionError
10
+ from cli.utils.connection import run_command, handle_unity_errors
11
+ from cli.utils.parsers import parse_json_list_or_exit
11
12
 
12
13
 
13
14
  @click.group()
@@ -20,6 +21,7 @@ def batch():
20
21
  @click.argument("file", type=click.Path(exists=True))
21
22
  @click.option("--parallel", is_flag=True, help="Execute read-only commands in parallel.")
22
23
  @click.option("--fail-fast", is_flag=True, help="Stop on first failure.")
24
+ @handle_unity_errors
23
25
  def batch_run(file: str, parallel: bool, fail_fast: bool):
24
26
  """Execute commands from a JSON file.
25
27
 
@@ -67,29 +69,26 @@ def batch_run(file: str, parallel: bool, fail_fast: bool):
67
69
 
68
70
  click.echo(f"Executing {len(commands)} commands...")
69
71
 
70
- try:
71
- result = run_command("batch_execute", params, config)
72
- click.echo(format_output(result, config.format))
73
-
74
- if isinstance(result, dict):
75
- results = result.get("data", {}).get("results", [])
76
- succeeded = sum(1 for r in results if r.get("success"))
77
- failed = len(results) - succeeded
78
-
79
- if failed == 0:
80
- print_success(
81
- f"All {succeeded} commands completed successfully")
82
- else:
83
- print_info(f"{succeeded} succeeded, {failed} failed")
84
- except UnityConnectionError as e:
85
- print_error(str(e))
86
- sys.exit(1)
72
+ result = run_command("batch_execute", params, config)
73
+ click.echo(format_output(result, config.format))
74
+
75
+ if isinstance(result, dict):
76
+ results = result.get("data", {}).get("results", [])
77
+ succeeded = sum(1 for r in results if r.get("success"))
78
+ failed = len(results) - succeeded
79
+
80
+ if failed == 0:
81
+ print_success(
82
+ f"All {succeeded} commands completed successfully")
83
+ else:
84
+ print_info(f"{succeeded} succeeded, {failed} failed")
87
85
 
88
86
 
89
87
  @batch.command("inline")
90
88
  @click.argument("commands_json")
91
89
  @click.option("--parallel", is_flag=True, help="Execute read-only commands in parallel.")
92
90
  @click.option("--fail-fast", is_flag=True, help="Stop on first failure.")
91
+ @handle_unity_errors
93
92
  def batch_inline(commands_json: str, parallel: bool, fail_fast: bool):
94
93
  """Execute commands from inline JSON.
95
94
 
@@ -104,15 +103,7 @@ def batch_inline(commands_json: str, parallel: bool, fail_fast: bool):
104
103
  """
105
104
  config = get_config()
106
105
 
107
- try:
108
- commands = json.loads(commands_json)
109
- except json.JSONDecodeError as e:
110
- print_error(f"Invalid JSON: {e}")
111
- sys.exit(1)
112
-
113
- if not isinstance(commands, list):
114
- print_error("Commands must be an array")
115
- sys.exit(1)
106
+ commands = parse_json_list_or_exit(commands_json, "commands")
116
107
 
117
108
  if len(commands) > 25:
118
109
  print_error(f"Maximum 25 commands per batch, got {len(commands)}")
@@ -124,12 +115,8 @@ def batch_inline(commands_json: str, parallel: bool, fail_fast: bool):
124
115
  if fail_fast:
125
116
  params["failFast"] = True
126
117
 
127
- try:
128
- result = run_command("batch_execute", params, config)
129
- click.echo(format_output(result, config.format))
130
- except UnityConnectionError as e:
131
- print_error(str(e))
132
- sys.exit(1)
118
+ result = run_command("batch_execute", params, config)
119
+ click.echo(format_output(result, config.format))
133
120
 
134
121
 
135
122
  @batch.command("template")