mcpforunityserver 9.3.0b20260128055651__py3-none-any.whl → 9.3.0b20260129121506__py3-none-any.whl

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 (61) hide show
  1. cli/commands/animation.py +6 -9
  2. cli/commands/asset.py +50 -80
  3. cli/commands/audio.py +14 -22
  4. cli/commands/batch.py +20 -33
  5. cli/commands/code.py +63 -70
  6. cli/commands/component.py +33 -55
  7. cli/commands/editor.py +122 -188
  8. cli/commands/gameobject.py +60 -83
  9. cli/commands/instance.py +28 -36
  10. cli/commands/lighting.py +54 -59
  11. cli/commands/material.py +39 -68
  12. cli/commands/prefab.py +63 -81
  13. cli/commands/scene.py +30 -54
  14. cli/commands/script.py +32 -50
  15. cli/commands/shader.py +43 -55
  16. cli/commands/texture.py +53 -51
  17. cli/commands/tool.py +24 -27
  18. cli/commands/ui.py +125 -130
  19. cli/commands/vfx.py +84 -138
  20. cli/utils/confirmation.py +37 -0
  21. cli/utils/connection.py +32 -2
  22. cli/utils/constants.py +23 -0
  23. cli/utils/parsers.py +112 -0
  24. core/config.py +0 -4
  25. core/telemetry.py +20 -2
  26. {mcpforunityserver-9.3.0b20260128055651.dist-info → mcpforunityserver-9.3.0b20260129121506.dist-info}/METADATA +21 -1
  27. mcpforunityserver-9.3.0b20260129121506.dist-info/RECORD +103 -0
  28. services/resources/active_tool.py +1 -1
  29. services/resources/custom_tools.py +1 -1
  30. services/resources/editor_state.py +1 -1
  31. services/resources/gameobject.py +4 -4
  32. services/resources/layers.py +1 -1
  33. services/resources/menu_items.py +1 -1
  34. services/resources/prefab.py +3 -3
  35. services/resources/prefab_stage.py +1 -1
  36. services/resources/project_info.py +1 -1
  37. services/resources/selection.py +1 -1
  38. services/resources/tags.py +1 -1
  39. services/resources/tests.py +40 -8
  40. services/resources/unity_instances.py +1 -1
  41. services/resources/windows.py +1 -1
  42. services/tools/__init__.py +3 -1
  43. services/tools/find_gameobjects.py +32 -11
  44. services/tools/manage_gameobject.py +11 -66
  45. services/tools/manage_material.py +4 -37
  46. services/tools/manage_prefabs.py +51 -7
  47. services/tools/manage_script.py +1 -1
  48. services/tools/manage_texture.py +10 -96
  49. services/tools/run_tests.py +67 -4
  50. services/tools/utils.py +217 -0
  51. transport/models.py +1 -0
  52. transport/plugin_hub.py +2 -1
  53. transport/plugin_registry.py +3 -0
  54. transport/unity_transport.py +0 -51
  55. utils/focus_nudge.py +291 -23
  56. mcpforunityserver-9.3.0b20260128055651.dist-info/RECORD +0 -101
  57. utils/reload_sentinel.py +0 -9
  58. {mcpforunityserver-9.3.0b20260128055651.dist-info → mcpforunityserver-9.3.0b20260129121506.dist-info}/WHEEL +0 -0
  59. {mcpforunityserver-9.3.0b20260128055651.dist-info → mcpforunityserver-9.3.0b20260129121506.dist-info}/entry_points.txt +0 -0
  60. {mcpforunityserver-9.3.0b20260128055651.dist-info → mcpforunityserver-9.3.0b20260129121506.dist-info}/licenses/LICENSE +0 -0
  61. {mcpforunityserver-9.3.0b20260128055651.dist-info → mcpforunityserver-9.3.0b20260129121506.dist-info}/top_level.txt +0 -0
cli/commands/animation.py CHANGED
@@ -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")
cli/commands/asset.py CHANGED
@@ -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}")
cli/commands/audio.py CHANGED
@@ -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))
cli/commands/batch.py CHANGED
@@ -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")
cli/commands/code.py CHANGED
@@ -7,7 +7,7 @@ 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_info
10
- from cli.utils.connection import run_command, UnityConnectionError
10
+ from cli.utils.connection import run_command, handle_unity_errors
11
11
 
12
12
 
13
13
  @click.group()
@@ -30,6 +30,7 @@ def code():
30
30
  type=int,
31
31
  help="Number of lines to read."
32
32
  )
33
+ @handle_unity_errors
33
34
  def read(path: str, start_line: Optional[int], line_count: Optional[int]):
34
35
  """Read a source file.
35
36
 
@@ -56,20 +57,16 @@ def read(path: str, start_line: Optional[int], line_count: Optional[int]):
56
57
  if line_count:
57
58
  params["lineCount"] = line_count
58
59
 
59
- try:
60
- result = run_command("manage_script", params, config)
61
- # For read, output content directly if available
62
- if result.get("success") and result.get("data"):
63
- data = result.get("data", {})
64
- if isinstance(data, dict) and "contents" in data:
65
- click.echo(data["contents"])
66
- else:
67
- click.echo(format_output(result, config.format))
60
+ result = run_command("manage_script", params, config)
61
+ # For read, output content directly if available
62
+ if result.get("success") and result.get("data"):
63
+ data = result.get("data", {})
64
+ if isinstance(data, dict) and "contents" in data:
65
+ click.echo(data["contents"])
68
66
  else:
69
67
  click.echo(format_output(result, config.format))
70
- except UnityConnectionError as e:
71
- print_error(str(e))
72
- sys.exit(1)
68
+ else:
69
+ click.echo(format_output(result, config.format))
73
70
 
74
71
 
75
72
  @code.command("search")
@@ -86,6 +83,7 @@ def read(path: str, start_line: Optional[int], line_count: Optional[int]):
86
83
  is_flag=True,
87
84
  help="Make search case-sensitive (default: case-insensitive)."
88
85
  )
86
+ @handle_unity_errors
89
87
  def search(pattern: str, path: str, max_results: int, case_sensitive: bool):
90
88
  """Search for patterns in Unity scripts using regex.
91
89
 
@@ -115,75 +113,70 @@ def search(pattern: str, path: str, max_results: int, case_sensitive: bool):
115
113
  "path": directory,
116
114
  }
117
115
 
118
- try:
119
- result = run_command("manage_script", read_params, config)
120
-
121
- # Handle nested response structure: {status, result: {success, data}}
122
- inner_result = result.get("result", result)
116
+ result = run_command("manage_script", read_params, config)
123
117
 
124
- if not inner_result.get("success") and result.get("status") != "success":
125
- click.echo(format_output(result, config.format))
126
- return
118
+ # Handle nested response structure: {status, result: {success, data}}
119
+ inner_result = result.get("result", result)
127
120
 
128
- # Get file contents from nested data
129
- data = inner_result.get("data", {})
130
- contents = data.get("contents")
121
+ if not inner_result.get("success") and result.get("status") != "success":
122
+ click.echo(format_output(result, config.format))
123
+ return
131
124
 
132
- # Handle base64 encoded content
133
- if not contents and data.get("contentsEncoded") and data.get("encodedContents"):
134
- try:
135
- contents = base64.b64decode(
136
- data["encodedContents"]).decode("utf-8", "replace")
137
- except (ValueError, TypeError):
138
- pass
125
+ # Get file contents from nested data
126
+ data = inner_result.get("data", {})
127
+ contents = data.get("contents")
139
128
 
140
- if not contents:
141
- print_error(f"Could not read file content from {path}")
142
- sys.exit(1)
129
+ # Handle base64 encoded content
130
+ if not contents and data.get("contentsEncoded") and data.get("encodedContents"):
131
+ try:
132
+ contents = base64.b64decode(
133
+ data["encodedContents"]).decode("utf-8", "replace")
134
+ except (ValueError, TypeError):
135
+ pass
143
136
 
144
- # Step 2: Perform regex search locally
145
- flags = re.MULTILINE
146
- if not case_sensitive:
147
- flags |= re.IGNORECASE
137
+ if not contents:
138
+ print_error(f"Could not read file content from {path}")
139
+ sys.exit(1)
148
140
 
149
- try:
150
- regex = re.compile(pattern, flags)
151
- except re.error as e:
152
- print_error(f"Invalid regex pattern: {e}")
153
- sys.exit(1)
141
+ # Step 2: Perform regex search locally
142
+ flags = re.MULTILINE
143
+ if not case_sensitive:
144
+ flags |= re.IGNORECASE
154
145
 
155
- found = list(regex.finditer(contents))
146
+ try:
147
+ regex = re.compile(pattern, flags)
148
+ except re.error as e:
149
+ print_error(f"Invalid regex pattern: {e}")
150
+ sys.exit(1)
156
151
 
157
- if not found:
158
- print_info(f"No matches found for pattern: {pattern}")
159
- return
152
+ found = list(regex.finditer(contents))
160
153
 
161
- results = []
162
- for m in found[:max_results]:
163
- start_idx = m.start()
154
+ if not found:
155
+ print_info(f"No matches found for pattern: {pattern}")
156
+ return
164
157
 
165
- # Calculate line number
166
- line_num = contents.count('\n', 0, start_idx) + 1
158
+ results = []
159
+ for m in found[:max_results]:
160
+ start_idx = m.start()
167
161
 
168
- # Get line content
169
- line_start = contents.rfind('\n', 0, start_idx) + 1
170
- line_end = contents.find('\n', start_idx)
171
- if line_end == -1:
172
- line_end = len(contents)
162
+ # Calculate line number
163
+ line_num = contents.count('\n', 0, start_idx) + 1
173
164
 
174
- line_content = contents[line_start:line_end].strip()
165
+ # Get line content
166
+ line_start = contents.rfind('\n', 0, start_idx) + 1
167
+ line_end = contents.find('\n', start_idx)
168
+ if line_end == -1:
169
+ line_end = len(contents)
175
170
 
176
- results.append({
177
- "line": line_num,
178
- "content": line_content,
179
- "match": m.group(0),
180
- })
171
+ line_content = contents[line_start:line_end].strip()
181
172
 
182
- # Display results
183
- click.echo(f"Found {len(results)} matches (total: {len(found)}):\n")
184
- for match in results:
185
- click.echo(f" Line {match['line']}: {match['content']}")
173
+ results.append({
174
+ "line": line_num,
175
+ "content": line_content,
176
+ "match": m.group(0),
177
+ })
186
178
 
187
- except UnityConnectionError as e:
188
- print_error(str(e))
189
- sys.exit(1)
179
+ # Display results
180
+ click.echo(f"Found {len(results)} matches (total: {len(found)}):\n")
181
+ for match in results:
182
+ click.echo(f" Line {match['line']}: {match['content']}")