@leejungkiin/awkit 1.7.1 → 1.7.4

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 (245) hide show
  1. package/bin/awk.js +576 -84
  2. package/core/CLAUDE.md +1 -1
  3. package/core/GEMINI.md +148 -167
  4. package/core/GEMINI.md.bak +149 -116
  5. package/core/skill-runtime-manifest.json +3 -0
  6. package/docs/Claude Fable 5.md +3826 -0
  7. package/docs/android_kotlin_system_instruction.md +210 -0
  8. package/docs/brainstorm_ponytail_integration.md +146 -0
  9. package/docs/brainstorm_smart_setup.md +113 -0
  10. package/docs/deep-research-report (1).md +293 -0
  11. package/docs/history/GEMINI.v1.md +135 -0
  12. package/docs/history/brainstorm_antigravity_unified_architecture.v1.md +105 -0
  13. package/docs/history/implementation_plan.v1.md +58 -0
  14. package/package.json +4 -1
  15. package/scripts/artifact-storage.js +130 -0
  16. package/scripts/automation-gate.js +35 -2
  17. package/scripts/claude-plan.js +76 -0
  18. package/scripts/dependency-manager.js +210 -0
  19. package/scripts/exec-rtk.js +11 -5
  20. package/scripts/i18n-helper.js +381 -0
  21. package/scripts/multi-model-pipeline.js +144 -0
  22. package/skill-packs/mobile-ios/pack.json +4 -2
  23. package/skill-packs/reverse-engineering/pack.json +1 -0
  24. package/skills/CATALOG.md +20 -0
  25. package/skills/GEMINI.md +9 -1
  26. package/skills/TRIGGER_INDEX.md +10 -0
  27. package/skills/ai-music/SKILL.md +275 -0
  28. package/skills/android-re-analyzer/SKILL.md +238 -0
  29. package/skills/android-re-analyzer/references/api-extraction-patterns.md +119 -0
  30. package/skills/android-re-analyzer/references/call-flow-analysis.md +176 -0
  31. package/skills/android-re-analyzer/references/fernflower-usage.md +115 -0
  32. package/skills/android-re-analyzer/references/jadx-usage.md +116 -0
  33. package/skills/android-re-analyzer/references/setup-guide.md +221 -0
  34. package/skills/android-re-analyzer/scripts/check-deps.sh +129 -0
  35. package/skills/android-re-analyzer/scripts/decompile.sh +375 -0
  36. package/skills/android-re-analyzer/scripts/find-api-calls.sh +118 -0
  37. package/skills/android-re-analyzer/scripts/install-dep.sh +448 -0
  38. package/skills/animal-island-ui-style/SKILL.md +1450 -0
  39. package/skills/app-store-review-agent/SKILL.md +164 -0
  40. package/skills/app-store-review-agent/references/guidelines/README.md +154 -0
  41. package/skills/app-store-review-agent/references/guidelines/by-app-type/ai_apps.md +37 -0
  42. package/skills/app-store-review-agent/references/guidelines/by-app-type/all_apps.md +50 -0
  43. package/skills/app-store-review-agent/references/guidelines/by-app-type/crypto_finance.md +31 -0
  44. package/skills/app-store-review-agent/references/guidelines/by-app-type/games.md +31 -0
  45. package/skills/app-store-review-agent/references/guidelines/by-app-type/health_fitness.md +31 -0
  46. package/skills/app-store-review-agent/references/guidelines/by-app-type/kids.md +27 -0
  47. package/skills/app-store-review-agent/references/guidelines/by-app-type/macos.md +38 -0
  48. package/skills/app-store-review-agent/references/guidelines/by-app-type/social_ugc.md +32 -0
  49. package/skills/app-store-review-agent/references/guidelines/by-app-type/subscription_iap.md +34 -0
  50. package/skills/app-store-review-agent/references/guidelines/by-app-type/vpn.md +18 -0
  51. package/skills/app-store-review-agent/references/rules/design/minimum_functionality.md +96 -0
  52. package/skills/app-store-review-agent/references/rules/design/sign_in_with_apple.md +54 -0
  53. package/skills/app-store-review-agent/references/rules/entitlements/unused_entitlements.md +83 -0
  54. package/skills/app-store-review-agent/references/rules/metadata/accurate_metadata.md +54 -0
  55. package/skills/app-store-review-agent/references/rules/metadata/apple_trademark.md +99 -0
  56. package/skills/app-store-review-agent/references/rules/metadata/china_storefront.md +72 -0
  57. package/skills/app-store-review-agent/references/rules/metadata/competitor_terms.md +56 -0
  58. package/skills/app-store-review-agent/references/rules/metadata/subscription_metadata.md +81 -0
  59. package/skills/app-store-review-agent/references/rules/privacy/privacy_manifest.md +84 -0
  60. package/skills/app-store-review-agent/references/rules/privacy/unnecessary_data.md +60 -0
  61. package/skills/app-store-review-agent/references/rules/subscription/misleading_pricing.md +63 -0
  62. package/skills/app-store-review-agent/references/rules/subscription/missing_tos_pp.md +54 -0
  63. package/skills/awf-ponytail/SKILL.md +91 -0
  64. package/skills/awf-ponytail-review/SKILL.md +67 -0
  65. package/skills/awf-session-restore/SKILL.md +3 -3
  66. package/skills/brainstorm-agent/SKILL.md +11 -2
  67. package/skills/brainstorm-agent/templates/brief-template.md +8 -0
  68. package/skills/claude-planner/SKILL.md +47 -0
  69. package/skills/code-review/SKILL.md +87 -0
  70. package/skills/expo-game-development/SKILL.md +163 -0
  71. package/skills/flutter/LICENSE.txt +202 -0
  72. package/skills/flutter/SKILL.md +127 -0
  73. package/skills/flutter-project-creater/LICENSE.txt +202 -0
  74. package/skills/flutter-project-creater/SKILL.md +106 -0
  75. package/skills/game-developer/SKILL.md +163 -0
  76. package/skills/game-developer/references/ecs-patterns.md +501 -0
  77. package/skills/game-developer/references/multiplayer-networking.md +475 -0
  78. package/skills/game-developer/references/performance-optimization.md +422 -0
  79. package/skills/game-developer/references/unity-patterns.md +271 -0
  80. package/skills/game-developer/references/unreal-cpp.md +352 -0
  81. package/skills/generate-gui-assets/SKILL.md +305 -0
  82. package/skills/generate-gui-assets/agents/openai.yaml +4 -0
  83. package/skills/generate-gui-assets/references/catalog-schema.md +58 -0
  84. package/skills/generate-gui-assets/references/extraction-techniques.md +21 -0
  85. package/skills/generate-gui-assets/references/prompt-patterns.md +58 -0
  86. package/skills/generate-gui-assets/scripts/__pycache__/clean_chroma_edges.cpython-311.pyc +0 -0
  87. package/skills/generate-gui-assets/scripts/build_gui_contact_sheet.py +51 -0
  88. package/skills/generate-gui-assets/scripts/clean_chroma_edges.py +262 -0
  89. package/skills/generate-gui-assets/scripts/copy_approved_icons.py +64 -0
  90. package/skills/generate-gui-assets/scripts/prepare_gui_asset_run.py +91 -0
  91. package/skills/generate-gui-assets/scripts/suggest_grid_options.py +63 -0
  92. package/skills/generate-gui-assets/scripts/validate_gui_catalog.py +50 -0
  93. package/skills/godot-game-development/SKILL.md +142 -0
  94. package/skills/hatch-pet/LICENSE.txt +201 -0
  95. package/skills/hatch-pet/SKILL.md +420 -0
  96. package/skills/hatch-pet/agents/openai.yaml +4 -0
  97. package/skills/hatch-pet/references/animation-rows.md +29 -0
  98. package/skills/hatch-pet/references/codex-pet-contract.md +35 -0
  99. package/skills/hatch-pet/references/qa-rubric.md +60 -0
  100. package/skills/hatch-pet/scripts/__pycache__/clean_chroma_edges.cpython-311.pyc +0 -0
  101. package/skills/hatch-pet/scripts/clean_chroma_edges.py +262 -0
  102. package/skills/hatch-pet/scripts/compose_atlas.py +150 -0
  103. package/skills/hatch-pet/scripts/derive_running_left_from_running_right.py +143 -0
  104. package/skills/hatch-pet/scripts/extract_strip_frames.py +323 -0
  105. package/skills/hatch-pet/scripts/finalize_pet_run.py +382 -0
  106. package/skills/hatch-pet/scripts/generate_pet_images.py +287 -0
  107. package/skills/hatch-pet/scripts/inspect_frames.py +246 -0
  108. package/skills/hatch-pet/scripts/make_contact_sheet.py +96 -0
  109. package/skills/hatch-pet/scripts/package_custom_pet.py +108 -0
  110. package/skills/hatch-pet/scripts/pet_job_status.py +117 -0
  111. package/skills/hatch-pet/scripts/prepare_pet_run.py +673 -0
  112. package/skills/hatch-pet/scripts/queue_pet_repairs.py +172 -0
  113. package/skills/hatch-pet/scripts/record_imagegen_result.py +250 -0
  114. package/skills/hatch-pet/scripts/render_animation_videos.py +134 -0
  115. package/skills/hatch-pet/scripts/render_animation_videos.sh +5 -0
  116. package/skills/hatch-pet/scripts/validate_atlas.py +139 -0
  117. package/skills/i18n-orchestrator/SKILL.md +37 -0
  118. package/skills/ios-simulator-skill/SKILL.md +390 -0
  119. package/skills/ios-simulator-skill/scripts/accessibility_audit.py +300 -0
  120. package/skills/ios-simulator-skill/scripts/app_launcher.py +326 -0
  121. package/skills/ios-simulator-skill/scripts/app_state_capture.py +400 -0
  122. package/skills/ios-simulator-skill/scripts/appearance.py +385 -0
  123. package/skills/ios-simulator-skill/scripts/build_and_test.py +348 -0
  124. package/skills/ios-simulator-skill/scripts/clipboard.py +103 -0
  125. package/skills/ios-simulator-skill/scripts/common/__init__.py +61 -0
  126. package/skills/ios-simulator-skill/scripts/common/cache_utils.py +289 -0
  127. package/skills/ios-simulator-skill/scripts/common/device_utils.py +462 -0
  128. package/skills/ios-simulator-skill/scripts/common/env_config.py +35 -0
  129. package/skills/ios-simulator-skill/scripts/common/hang_pipeline.py +862 -0
  130. package/skills/ios-simulator-skill/scripts/common/hang_sessions.py +490 -0
  131. package/skills/ios-simulator-skill/scripts/common/idb_utils.py +180 -0
  132. package/skills/ios-simulator-skill/scripts/common/screenshot_utils.py +338 -0
  133. package/skills/ios-simulator-skill/scripts/container.py +668 -0
  134. package/skills/ios-simulator-skill/scripts/gesture.py +394 -0
  135. package/skills/ios-simulator-skill/scripts/hang_watcher.py +1533 -0
  136. package/skills/ios-simulator-skill/scripts/keyboard.py +391 -0
  137. package/skills/ios-simulator-skill/scripts/localization_audit.py +483 -0
  138. package/skills/ios-simulator-skill/scripts/location.py +467 -0
  139. package/skills/ios-simulator-skill/scripts/log_monitor.py +493 -0
  140. package/skills/ios-simulator-skill/scripts/model_inspector.py +645 -0
  141. package/skills/ios-simulator-skill/scripts/navigator.py +461 -0
  142. package/skills/ios-simulator-skill/scripts/privacy_manager.py +310 -0
  143. package/skills/ios-simulator-skill/scripts/push_notification.py +240 -0
  144. package/skills/ios-simulator-skill/scripts/screen_mapper.py +296 -0
  145. package/skills/ios-simulator-skill/scripts/sim_health_check.sh +245 -0
  146. package/skills/ios-simulator-skill/scripts/sim_list.py +299 -0
  147. package/skills/ios-simulator-skill/scripts/simctl_boot.py +312 -0
  148. package/skills/ios-simulator-skill/scripts/simctl_create.py +316 -0
  149. package/skills/ios-simulator-skill/scripts/simctl_delete.py +357 -0
  150. package/skills/ios-simulator-skill/scripts/simctl_erase.py +351 -0
  151. package/skills/ios-simulator-skill/scripts/simctl_shutdown.py +290 -0
  152. package/skills/ios-simulator-skill/scripts/simulator_selector.py +375 -0
  153. package/skills/ios-simulator-skill/scripts/status_bar.py +250 -0
  154. package/skills/ios-simulator-skill/scripts/test_recorder.py +323 -0
  155. package/skills/ios-simulator-skill/scripts/visual_diff.py +235 -0
  156. package/skills/ios-simulator-skill/scripts/xcode/__init__.py +13 -0
  157. package/skills/ios-simulator-skill/scripts/xcode/builder.py +397 -0
  158. package/skills/ios-simulator-skill/scripts/xcode/cache.py +204 -0
  159. package/skills/ios-simulator-skill/scripts/xcode/config.py +178 -0
  160. package/skills/ios-simulator-skill/scripts/xcode/reporter.py +343 -0
  161. package/skills/ios-simulator-skill/scripts/xcode/xcresult.py +451 -0
  162. package/skills/ios-visual-qa-strategist/SKILL.md +111 -0
  163. package/skills/ios-visual-qa-strategist/agents/openai.yaml +4 -0
  164. package/skills/ios-visual-qa-strategist/references/ios-tool-selection.md +61 -0
  165. package/skills/ios-visual-qa-strategist/references/minimal-capture-policy.md +56 -0
  166. package/skills/ios-visual-qa-strategist/references/visual-reasoning-heuristics.md +53 -0
  167. package/skills/orchestrator/SKILL.md +0 -20
  168. package/skills/persistent-storage/SKILL.md +55 -0
  169. package/skills/short-maker/SKILL.md +23 -0
  170. package/skills/short-maker/scripts/effects.js +56 -0
  171. package/skills/short-maker/scripts/shortmaker-bridge.js +332 -0
  172. package/skills/short-maker/scripts/videomix.js +601 -0
  173. package/skills/short-maker/templates/hyperframes/cinematic-character.template.html +172 -0
  174. package/skills/short-maker/templates/hyperframes/index.template.html +194 -0
  175. package/skills/smali-to-kotlin/SKILL.md +128 -0
  176. package/skills/smali-to-kotlin/examples/getting-started/tech-stack.md +58 -0
  177. package/skills/smali-to-kotlin/examples/pipeline/data-ui-parity.md +118 -0
  178. package/skills/smali-to-kotlin/examples/pipeline/scanner-and-bootstrap.md +106 -0
  179. package/skills/smali-to-kotlin/library-patterns.md +189 -0
  180. package/skills/smali-to-kotlin/phase-0-discovery.md +128 -0
  181. package/skills/smali-to-kotlin/phase-1-architecture.md +166 -0
  182. package/skills/smali-to-kotlin/phase-2-blueprint-ui.md +347 -0
  183. package/skills/smali-to-kotlin/phase-2-blueprint.md +228 -0
  184. package/skills/smali-to-kotlin/phase-3-build.md +248 -0
  185. package/skills/smali-to-kotlin/phase-3-logic-build.md +268 -0
  186. package/skills/smali-to-kotlin/smali-reading-guide.md +310 -0
  187. package/skills/smali-to-kotlin/templates/app-map.md +101 -0
  188. package/skills/smali-to-kotlin/templates/architecture.md +142 -0
  189. package/skills/smali-to-kotlin/templates/blueprint.md +145 -0
  190. package/skills/spec-gate/SKILL.md +6 -2
  191. package/skills/symphony-enforcer/SKILL.md +8 -0
  192. package/skills/symphony-enforcer/examples/mindful-stop.md +2 -0
  193. package/skills/symphony-enforcer/examples/three-phase.md +16 -0
  194. package/skills/symphony-enforcer/examples/trigger-points.md +7 -1
  195. package/skills/unity-game-development/SKILL.md +231 -0
  196. package/skills/video-edit/SKILL.md +36 -0
  197. package/skills/video-edit/scripts/video_edit.py +324 -0
  198. package/templates/project-identity/android.json +2 -2
  199. package/templates/project-identity/backend-nestjs.json +2 -2
  200. package/templates/project-identity/expo.json +2 -2
  201. package/templates/project-identity/ios.json +2 -2
  202. package/templates/project-identity/web-nextjs.json +2 -2
  203. package/templates/setup-mapping.json +48 -0
  204. package/templates/specs/design-template.md +161 -71
  205. package/templates/specs/requirements-template.md +65 -133
  206. package/templates/specs/task-spec-template.xml +3 -0
  207. package/workflows/_uncategorized/critic.md +40 -0
  208. package/workflows/_uncategorized/git-rebase-flow.md +81 -0
  209. package/workflows/_uncategorized/image-gen.md +118 -0
  210. package/workflows/_uncategorized/multi-model-pipeline.md +60 -0
  211. package/workflows/_uncategorized/pixel-gen.md +86 -0
  212. package/workflows/_uncategorized/pixel-setup.md +90 -0
  213. package/workflows/_uncategorized/ponytail-review.md +59 -0
  214. package/workflows/_uncategorized/reverse-android-build.md +222 -0
  215. package/workflows/_uncategorized/reverse-android-design.md +139 -0
  216. package/workflows/_uncategorized/reverse-android-discover.md +150 -0
  217. package/workflows/_uncategorized/reverse-android-scan.md +158 -0
  218. package/workflows/_uncategorized/reverse-android.md +143 -0
  219. package/workflows/_uncategorized/reverse-ios-build.md +240 -0
  220. package/workflows/_uncategorized/reverse-ios-design.md +112 -0
  221. package/workflows/_uncategorized/reverse-ios-discover.md +120 -0
  222. package/workflows/_uncategorized/reverse-ios-scan.md +155 -0
  223. package/workflows/_uncategorized/reverse-ios.md +152 -0
  224. package/workflows/_uncategorized/safety-router.md +34 -0
  225. package/workflows/_uncategorized/teach.md +89 -0
  226. package/workflows/_uncategorized/verify-ui.md +53 -0
  227. package/workflows/_uncategorized/visualize-screenshots.md +34 -0
  228. package/workflows/ads/ads-analyst.md +201 -0
  229. package/workflows/ads/ads-audit.md +106 -0
  230. package/workflows/ads/ads-optimize.md +97 -0
  231. package/workflows/ads/ads-targeting.md +241 -0
  232. package/workflows/ads/adsExpert.md +160 -0
  233. package/workflows/ads/smali-ads-config.md +400 -0
  234. package/workflows/ads/smali-ads-flow.md +331 -0
  235. package/workflows/ads/smali-ads-interstitial.md +377 -0
  236. package/workflows/ads/smali-ads-native.md +382 -0
  237. package/workflows/context/teach.md +89 -0
  238. package/workflows/gitnexus.md +8 -8
  239. package/workflows/lifecycle/brainstorm.md +43 -0
  240. package/workflows/lifecycle/code.md +5 -0
  241. package/workflows/lifecycle/init.md +23 -5
  242. package/workflows/lifecycle/multi-model-pipeline.md +60 -0
  243. package/workflows/quality/ponytail-review.md +59 -0
  244. package/workflows/roles/critic.md +40 -0
  245. package/workflows/roles/safety-router.md +34 -0
@@ -0,0 +1,316 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Create iOS simulators dynamically.
4
+
5
+ This script creates new simulators with specified device type and iOS version.
6
+ Useful for CI/CD pipelines that need on-demand test device provisioning.
7
+
8
+ Key features:
9
+ - Create by device type (iPhone 16 Pro, iPad Air, etc.)
10
+ - Specify iOS version (17.0, 18.0, etc.)
11
+ - Custom device naming
12
+ - Return newly created device UDID
13
+ - List available device types and runtimes
14
+ """
15
+
16
+ import argparse
17
+ import subprocess
18
+ import sys
19
+ from typing import Optional
20
+
21
+ from common.device_utils import list_simulators
22
+
23
+
24
+ class SimulatorCreator:
25
+ """Create iOS simulators with specified configurations."""
26
+
27
+ def __init__(self):
28
+ """Initialize simulator creator."""
29
+ pass
30
+
31
+ def create(
32
+ self,
33
+ device_type: str,
34
+ ios_version: str | None = None,
35
+ custom_name: str | None = None,
36
+ ) -> tuple[bool, str, str | None]:
37
+ """
38
+ Create new iOS simulator.
39
+
40
+ Args:
41
+ device_type: Device type (e.g., "iPhone 16 Pro", "iPad Air")
42
+ ios_version: iOS version (e.g., "18.0"). If None, uses latest.
43
+ custom_name: Custom device name. If None, uses default.
44
+
45
+ Returns:
46
+ (success, message, new_udid) tuple
47
+ """
48
+ # Get available device types and runtimes
49
+ available_types = self._get_device_types()
50
+ if not available_types:
51
+ return False, "Failed to get available device types", None
52
+
53
+ # Normalize device type
54
+ device_type_id = None
55
+ for dt in available_types:
56
+ if device_type.lower() in dt["name"].lower():
57
+ device_type_id = dt["identifier"]
58
+ break
59
+
60
+ if not device_type_id:
61
+ return (
62
+ False,
63
+ f"Device type '{device_type}' not found. "
64
+ f"Use --list-devices for available types.",
65
+ None,
66
+ )
67
+
68
+ # Get available runtimes
69
+ available_runtimes = self._get_runtimes()
70
+ if not available_runtimes:
71
+ return False, "Failed to get available runtimes", None
72
+
73
+ # Resolve iOS version
74
+ runtime_id = None
75
+ if ios_version:
76
+ for rt in available_runtimes:
77
+ if ios_version in rt["name"]:
78
+ runtime_id = rt["identifier"]
79
+ break
80
+
81
+ if not runtime_id:
82
+ return (
83
+ False,
84
+ f"iOS version '{ios_version}' not found. "
85
+ f"Use --list-runtimes for available versions.",
86
+ None,
87
+ )
88
+ # Use latest runtime
89
+ elif available_runtimes:
90
+ runtime_id = available_runtimes[-1]["identifier"]
91
+
92
+ if not runtime_id:
93
+ return False, "No iOS runtime available", None
94
+
95
+ # Create device
96
+ try:
97
+ # Build device name
98
+ device_name = (
99
+ custom_name or f"{device_type_id.split('.')[-1]}-{ios_version or 'latest'}"
100
+ )
101
+
102
+ cmd = [
103
+ "xcrun",
104
+ "simctl",
105
+ "create",
106
+ device_name,
107
+ device_type_id,
108
+ runtime_id,
109
+ ]
110
+
111
+ result = subprocess.run(cmd, check=False, capture_output=True, text=True, timeout=60)
112
+
113
+ if result.returncode != 0:
114
+ error = result.stderr.strip() or result.stdout.strip()
115
+ return False, f"Creation failed: {error}", None
116
+
117
+ # Extract UDID from output
118
+ new_udid = result.stdout.strip()
119
+
120
+ return (
121
+ True,
122
+ f"Device created: {device_name} ({device_type}) iOS {ios_version or 'latest'} "
123
+ f"UDID: {new_udid}",
124
+ new_udid,
125
+ )
126
+
127
+ except subprocess.TimeoutExpired:
128
+ return False, "Creation command timed out", None
129
+ except Exception as e:
130
+ return False, f"Creation error: {e}", None
131
+
132
+ @staticmethod
133
+ def _get_device_types() -> list[dict]:
134
+ """
135
+ Get available device types.
136
+
137
+ Returns:
138
+ List of device type dicts with "name" and "identifier" keys
139
+ """
140
+ try:
141
+ cmd = ["xcrun", "simctl", "list", "devicetypes", "-j"]
142
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
143
+
144
+ import json
145
+
146
+ data = json.loads(result.stdout)
147
+ devices = []
148
+
149
+ for device in data.get("devicetypes", []):
150
+ devices.append(
151
+ {
152
+ "name": device.get("name", ""),
153
+ "identifier": device.get("identifier", ""),
154
+ }
155
+ )
156
+
157
+ return devices
158
+ except Exception:
159
+ return []
160
+
161
+ @staticmethod
162
+ def _get_runtimes() -> list[dict]:
163
+ """
164
+ Get available iOS runtimes.
165
+
166
+ Returns:
167
+ List of runtime dicts with "name" and "identifier" keys
168
+ """
169
+ try:
170
+ cmd = ["xcrun", "simctl", "list", "runtimes", "-j"]
171
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
172
+
173
+ import json
174
+
175
+ data = json.loads(result.stdout)
176
+ runtimes = []
177
+
178
+ for runtime in data.get("runtimes", []):
179
+ # Only include iOS runtimes (skip watchOS, tvOS, etc.)
180
+ identifier = runtime.get("identifier", "")
181
+ if "iOS" in identifier or "iOS" in runtime.get("name", ""):
182
+ runtimes.append(
183
+ {
184
+ "name": runtime.get("name", ""),
185
+ "identifier": runtime.get("identifier", ""),
186
+ }
187
+ )
188
+
189
+ # Sort by version number (latest first)
190
+ runtimes.sort(key=lambda r: r.get("identifier", ""), reverse=True)
191
+
192
+ return runtimes
193
+ except Exception:
194
+ return []
195
+
196
+ @staticmethod
197
+ def list_device_types() -> list[dict]:
198
+ """
199
+ List all available device types.
200
+
201
+ Returns:
202
+ List of device types with name and identifier
203
+ """
204
+ return SimulatorCreator._get_device_types()
205
+
206
+ @staticmethod
207
+ def list_runtimes() -> list[dict]:
208
+ """
209
+ List all available iOS runtimes.
210
+
211
+ Returns:
212
+ List of runtimes with name and identifier
213
+ """
214
+ return SimulatorCreator._get_runtimes()
215
+
216
+
217
+ def main():
218
+ """Main entry point."""
219
+ parser = argparse.ArgumentParser(description="Create iOS simulators dynamically")
220
+ parser.add_argument(
221
+ "--device",
222
+ required=False,
223
+ help="Device type (e.g., 'iPhone 16 Pro', 'iPad Air')",
224
+ )
225
+ parser.add_argument(
226
+ "--runtime",
227
+ help="iOS version (e.g., '18.0', '17.0'). Defaults to latest.",
228
+ )
229
+ parser.add_argument(
230
+ "--name",
231
+ help="Custom device name. Defaults to auto-generated.",
232
+ )
233
+ parser.add_argument(
234
+ "--list-devices",
235
+ action="store_true",
236
+ help="List all available device types",
237
+ )
238
+ parser.add_argument(
239
+ "--list-runtimes",
240
+ action="store_true",
241
+ help="List all available iOS runtimes",
242
+ )
243
+ parser.add_argument(
244
+ "--json",
245
+ action="store_true",
246
+ help="Output as JSON",
247
+ )
248
+
249
+ args = parser.parse_args()
250
+
251
+ creator = SimulatorCreator()
252
+
253
+ # Handle info queries
254
+ if args.list_devices:
255
+ devices = creator.list_device_types()
256
+ if args.json:
257
+ import json
258
+
259
+ print(json.dumps({"devices": devices}))
260
+ else:
261
+ print(f"Available device types ({len(devices)}):")
262
+ for dev in devices[:20]: # Show first 20
263
+ print(f" - {dev['name']}")
264
+ if len(devices) > 20:
265
+ print(f" ... and {len(devices) - 20} more")
266
+ sys.exit(0)
267
+
268
+ if args.list_runtimes:
269
+ runtimes = creator.list_runtimes()
270
+ if args.json:
271
+ import json
272
+
273
+ print(json.dumps({"runtimes": runtimes}))
274
+ else:
275
+ print(f"Available iOS runtimes ({len(runtimes)}):")
276
+ for rt in runtimes:
277
+ print(f" - {rt['name']}")
278
+ sys.exit(0)
279
+
280
+ # Create device
281
+ if not args.device:
282
+ print(
283
+ "Error: Specify --device, --list-devices, or --list-runtimes",
284
+ file=sys.stderr,
285
+ )
286
+ sys.exit(1)
287
+
288
+ success, message, new_udid = creator.create(
289
+ device_type=args.device,
290
+ ios_version=args.runtime,
291
+ custom_name=args.name,
292
+ )
293
+
294
+ if args.json:
295
+ import json
296
+
297
+ print(
298
+ json.dumps(
299
+ {
300
+ "action": "create",
301
+ "device_type": args.device,
302
+ "runtime": args.runtime,
303
+ "success": success,
304
+ "message": message,
305
+ "new_udid": new_udid,
306
+ }
307
+ )
308
+ )
309
+ else:
310
+ print(message)
311
+
312
+ sys.exit(0 if success else 1)
313
+
314
+
315
+ if __name__ == "__main__":
316
+ main()
@@ -0,0 +1,357 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Delete iOS simulators permanently.
4
+
5
+ This script permanently removes simulators and frees disk space.
6
+ Includes safety confirmation to prevent accidental deletion.
7
+
8
+ Key features:
9
+ - Delete by UDID or device name
10
+ - Confirmation required for safety
11
+ - Batch delete operations
12
+ - Report freed disk space estimate
13
+ """
14
+
15
+ import argparse
16
+ import subprocess
17
+ import sys
18
+ from typing import Optional
19
+
20
+ from common.device_utils import (
21
+ list_simulators,
22
+ resolve_device_identifier,
23
+ )
24
+
25
+
26
+ class SimulatorDeleter:
27
+ """Delete iOS simulators with safety confirmation."""
28
+
29
+ def __init__(self, udid: str | None = None):
30
+ """Initialize with optional device UDID."""
31
+ self.udid = udid
32
+
33
+ def delete(self, confirm: bool = False) -> tuple[bool, str]:
34
+ """
35
+ Delete simulator permanently.
36
+
37
+ Args:
38
+ confirm: Skip confirmation prompt (for batch operations)
39
+
40
+ Returns:
41
+ (success, message) tuple
42
+ """
43
+ if not self.udid:
44
+ return False, "Error: Device UDID not specified"
45
+
46
+ # Safety confirmation
47
+ if not confirm:
48
+ try:
49
+ response = input(
50
+ f"Permanently delete simulator {self.udid}? " f"(type 'yes' to confirm): "
51
+ )
52
+ if response.lower() != "yes":
53
+ return False, "Deletion cancelled by user"
54
+ except KeyboardInterrupt:
55
+ return False, "Deletion cancelled"
56
+
57
+ # Execute delete command
58
+ try:
59
+ cmd = ["xcrun", "simctl", "delete", self.udid]
60
+ result = subprocess.run(cmd, check=False, capture_output=True, text=True, timeout=60)
61
+
62
+ if result.returncode != 0:
63
+ error = result.stderr.strip() or result.stdout.strip()
64
+ return False, f"Deletion failed: {error}"
65
+
66
+ return True, f"Device deleted: {self.udid} [disk space freed]"
67
+
68
+ except subprocess.TimeoutExpired:
69
+ return False, "Deletion command timed out"
70
+ except Exception as e:
71
+ return False, f"Deletion error: {e}"
72
+
73
+ @staticmethod
74
+ def delete_all(confirm: bool = False) -> tuple[int, int]:
75
+ """
76
+ Delete all simulators permanently.
77
+
78
+ Args:
79
+ confirm: Skip confirmation prompt
80
+
81
+ Returns:
82
+ (succeeded, failed) tuple with counts
83
+ """
84
+ simulators = list_simulators(state=None)
85
+
86
+ if not confirm:
87
+ count = len(simulators)
88
+ try:
89
+ response = input(
90
+ f"Permanently delete ALL {count} simulators? " f"(type 'yes' to confirm): "
91
+ )
92
+ if response.lower() != "yes":
93
+ return 0, count
94
+ except KeyboardInterrupt:
95
+ return 0, count
96
+
97
+ succeeded = 0
98
+ failed = 0
99
+
100
+ for sim in simulators:
101
+ deleter = SimulatorDeleter(udid=sim["udid"])
102
+ success, _message = deleter.delete(confirm=True)
103
+ if success:
104
+ succeeded += 1
105
+ else:
106
+ failed += 1
107
+
108
+ return succeeded, failed
109
+
110
+ @staticmethod
111
+ def delete_by_type(device_type: str, confirm: bool = False) -> tuple[int, int]:
112
+ """
113
+ Delete all simulators of a specific type.
114
+
115
+ Args:
116
+ device_type: Device type filter (e.g., "iPhone", "iPad")
117
+ confirm: Skip confirmation prompt
118
+
119
+ Returns:
120
+ (succeeded, failed) tuple with counts
121
+ """
122
+ simulators = list_simulators(state=None)
123
+ matching = [s for s in simulators if device_type.lower() in s["name"].lower()]
124
+
125
+ if not matching:
126
+ return 0, 0
127
+
128
+ if not confirm:
129
+ count = len(matching)
130
+ try:
131
+ response = input(
132
+ f"Permanently delete {count} {device_type} simulators? "
133
+ f"(type 'yes' to confirm): "
134
+ )
135
+ if response.lower() != "yes":
136
+ return 0, count
137
+ except KeyboardInterrupt:
138
+ return 0, count
139
+
140
+ succeeded = 0
141
+ failed = 0
142
+
143
+ for sim in matching:
144
+ deleter = SimulatorDeleter(udid=sim["udid"])
145
+ success, _message = deleter.delete(confirm=True)
146
+ if success:
147
+ succeeded += 1
148
+ else:
149
+ failed += 1
150
+
151
+ return succeeded, failed
152
+
153
+ @staticmethod
154
+ def delete_old(keep_count: int = 3, confirm: bool = False) -> tuple[int, int]:
155
+ """
156
+ Delete older simulators, keeping most recent versions.
157
+
158
+ Useful for cleanup after testing multiple iOS versions.
159
+ Keeps the most recent N simulators of each type.
160
+
161
+ Args:
162
+ keep_count: Number of recent simulators to keep per type (default: 3)
163
+ confirm: Skip confirmation prompt
164
+
165
+ Returns:
166
+ (succeeded, failed) tuple with counts
167
+ """
168
+ simulators = list_simulators(state=None)
169
+
170
+ # Group by device type
171
+ by_type: dict[str, list] = {}
172
+ for sim in simulators:
173
+ dev_type = sim["type"]
174
+ if dev_type not in by_type:
175
+ by_type[dev_type] = []
176
+ by_type[dev_type].append(sim)
177
+
178
+ # Find candidates for deletion (older ones)
179
+ to_delete = []
180
+ for _dev_type, sims in by_type.items():
181
+ # Sort by runtime (iOS version) - keep newest
182
+ sorted_sims = sorted(sims, key=lambda s: s["runtime"], reverse=True)
183
+ # Mark older ones for deletion
184
+ to_delete.extend(sorted_sims[keep_count:])
185
+
186
+ if not to_delete:
187
+ return 0, 0
188
+
189
+ if not confirm:
190
+ count = len(to_delete)
191
+ try:
192
+ response = input(
193
+ f"Delete {count} older simulators, keeping {keep_count} per type? "
194
+ f"(type 'yes' to confirm): "
195
+ )
196
+ if response.lower() != "yes":
197
+ return 0, count
198
+ except KeyboardInterrupt:
199
+ return 0, count
200
+
201
+ succeeded = 0
202
+ failed = 0
203
+
204
+ for sim in to_delete:
205
+ deleter = SimulatorDeleter(udid=sim["udid"])
206
+ success, _message = deleter.delete(confirm=True)
207
+ if success:
208
+ succeeded += 1
209
+ else:
210
+ failed += 1
211
+
212
+ return succeeded, failed
213
+
214
+
215
+ def main():
216
+ """Main entry point."""
217
+ parser = argparse.ArgumentParser(description="Delete iOS simulators permanently")
218
+ parser.add_argument(
219
+ "--udid",
220
+ help="Device UDID or name (required unless using batch options)",
221
+ )
222
+ parser.add_argument(
223
+ "--name",
224
+ help="Device name (alternative to --udid)",
225
+ )
226
+ parser.add_argument(
227
+ "--yes",
228
+ action="store_true",
229
+ help="Skip confirmation prompt",
230
+ )
231
+ parser.add_argument(
232
+ "--all",
233
+ action="store_true",
234
+ help="Delete all simulators",
235
+ )
236
+ parser.add_argument(
237
+ "--type",
238
+ help="Delete all simulators of a specific type (e.g., iPhone)",
239
+ )
240
+ parser.add_argument(
241
+ "--old",
242
+ type=int,
243
+ metavar="KEEP_COUNT",
244
+ help="Delete older simulators, keeping this many per type (e.g., --old 3)",
245
+ )
246
+ parser.add_argument(
247
+ "--json",
248
+ action="store_true",
249
+ help="Output as JSON",
250
+ )
251
+
252
+ args = parser.parse_args()
253
+
254
+ # Handle batch operations
255
+ if args.all:
256
+ succeeded, failed = SimulatorDeleter.delete_all(confirm=args.yes)
257
+ if args.json:
258
+ import json
259
+
260
+ print(
261
+ json.dumps(
262
+ {
263
+ "action": "delete_all",
264
+ "succeeded": succeeded,
265
+ "failed": failed,
266
+ "total": succeeded + failed,
267
+ }
268
+ )
269
+ )
270
+ else:
271
+ total = succeeded + failed
272
+ print(f"Delete summary: {succeeded}/{total} succeeded, " f"{failed} failed")
273
+ sys.exit(0 if failed == 0 else 1)
274
+
275
+ if args.type:
276
+ succeeded, failed = SimulatorDeleter.delete_by_type(args.type, confirm=args.yes)
277
+ if args.json:
278
+ import json
279
+
280
+ print(
281
+ json.dumps(
282
+ {
283
+ "action": "delete_by_type",
284
+ "type": args.type,
285
+ "succeeded": succeeded,
286
+ "failed": failed,
287
+ "total": succeeded + failed,
288
+ }
289
+ )
290
+ )
291
+ else:
292
+ total = succeeded + failed
293
+ print(f"Delete {args.type} summary: {succeeded}/{total} succeeded, " f"{failed} failed")
294
+ sys.exit(0 if failed == 0 else 1)
295
+
296
+ if args.old is not None:
297
+ succeeded, failed = SimulatorDeleter.delete_old(keep_count=args.old, confirm=args.yes)
298
+ if args.json:
299
+ import json
300
+
301
+ print(
302
+ json.dumps(
303
+ {
304
+ "action": "delete_old",
305
+ "keep_count": args.old,
306
+ "succeeded": succeeded,
307
+ "failed": failed,
308
+ "total": succeeded + failed,
309
+ }
310
+ )
311
+ )
312
+ else:
313
+ total = succeeded + failed
314
+ print(
315
+ f"Delete old summary: {succeeded}/{total} succeeded, "
316
+ f"{failed} failed (kept {args.old} per type)"
317
+ )
318
+ sys.exit(0 if failed == 0 else 1)
319
+
320
+ # Delete single device
321
+ device_id = args.udid or args.name
322
+ if not device_id:
323
+ print("Error: Specify --udid, --name, --all, --type, or --old", file=sys.stderr)
324
+ sys.exit(1)
325
+
326
+ try:
327
+ udid = resolve_device_identifier(device_id)
328
+ except RuntimeError as e:
329
+ print(f"Error: {e}", file=sys.stderr)
330
+ sys.exit(1)
331
+
332
+ # Delete device
333
+ deleter = SimulatorDeleter(udid=udid)
334
+ success, message = deleter.delete(confirm=args.yes)
335
+
336
+ if args.json:
337
+ import json
338
+
339
+ print(
340
+ json.dumps(
341
+ {
342
+ "action": "delete",
343
+ "device_id": device_id,
344
+ "udid": udid,
345
+ "success": success,
346
+ "message": message,
347
+ }
348
+ )
349
+ )
350
+ else:
351
+ print(message)
352
+
353
+ sys.exit(0 if success else 1)
354
+
355
+
356
+ if __name__ == "__main__":
357
+ main()