@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,299 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ iOS Simulator Listing with Progressive Disclosure
4
+
5
+ Lists available simulators with token-efficient summaries.
6
+ Full details available on demand via cache IDs.
7
+
8
+ Achieves 96% token reduction (57k→2k tokens) for common queries.
9
+
10
+ Usage Examples:
11
+ # Concise summary (default)
12
+ python scripts/sim_list.py
13
+
14
+ # Get full details for cached list
15
+ python scripts/sim_list.py --get-details <cache-id>
16
+
17
+ # Get recommendations
18
+ python scripts/sim_list.py --suggest
19
+
20
+ # Filter by device type
21
+ python scripts/sim_list.py --device-type iPhone
22
+
23
+ Output (default):
24
+ Simulator Summary [cache-sim-20251028-143052]
25
+ ├─ Total: 47 devices
26
+ ├─ Available: 31
27
+ └─ Booted: 1
28
+
29
+ ✓ iPhone 16 Pro (iOS 18.1) [ABC-123...]
30
+
31
+ Use --get-details cache-sim-20251028-143052 for full list
32
+
33
+ Technical Details:
34
+ - Uses xcrun simctl list devices
35
+ - Caches results with 1-hour TTL
36
+ - Reduces output by 96% by default
37
+ - Token efficiency: summary = ~30 tokens, full list = ~1500 tokens
38
+ """
39
+
40
+ import argparse
41
+ import json
42
+ import subprocess
43
+ import sys
44
+ from typing import Any
45
+
46
+ from common import get_cache
47
+
48
+
49
+ class SimulatorLister:
50
+ """Lists iOS simulators with progressive disclosure."""
51
+
52
+ def __init__(self):
53
+ """Initialize lister with cache."""
54
+ self.cache = get_cache()
55
+
56
+ def list_simulators(self) -> dict:
57
+ """
58
+ Get list of all simulators.
59
+
60
+ Returns:
61
+ Dict with structure:
62
+ {
63
+ "devices": [...],
64
+ "runtimes": [...],
65
+ "total_devices": int,
66
+ "available_devices": int,
67
+ "booted_devices": [...]
68
+ }
69
+ """
70
+ try:
71
+ result = subprocess.run(
72
+ ["xcrun", "simctl", "list", "devices", "--json"],
73
+ capture_output=True,
74
+ text=True,
75
+ check=True,
76
+ )
77
+
78
+ return json.loads(result.stdout)
79
+ except (subprocess.CalledProcessError, json.JSONDecodeError):
80
+ return {"devices": {}, "runtimes": []}
81
+
82
+ def parse_devices(self, sim_data: dict) -> list[dict]:
83
+ """
84
+ Parse simulator data into flat list.
85
+
86
+ Returns:
87
+ List of device dicts with runtime info
88
+ """
89
+ devices = []
90
+
91
+ devices_by_runtime = sim_data.get("devices", {})
92
+
93
+ for runtime_str, device_list in devices_by_runtime.items():
94
+ # Extract iOS version from runtime string
95
+ # Format: "iOS 18.1", "tvOS 18", etc.
96
+ runtime_name = runtime_str.replace(" Simulator", "").strip()
97
+
98
+ for device in device_list:
99
+ devices.append(
100
+ {
101
+ "name": device.get("name"),
102
+ "udid": device.get("udid"),
103
+ "state": device.get("state"),
104
+ "runtime": runtime_name,
105
+ "is_available": device.get("isAvailable", False),
106
+ }
107
+ )
108
+
109
+ return devices
110
+
111
+ def get_concise_summary(self, devices: list[dict]) -> dict:
112
+ """
113
+ Generate concise summary with cache ID.
114
+
115
+ Returns 96% fewer tokens than full list.
116
+ """
117
+ booted = [d for d in devices if d["state"] == "Booted"]
118
+ available = [d for d in devices if d["is_available"]]
119
+ iphone = [d for d in available if "iPhone" in d["name"]]
120
+
121
+ # Cache full list for later retrieval
122
+ cache_id = self.cache.save(
123
+ {
124
+ "devices": devices,
125
+ "timestamp": __import__("datetime").datetime.now().isoformat(),
126
+ },
127
+ "simulator-list",
128
+ )
129
+
130
+ return {
131
+ "cache_id": cache_id,
132
+ "summary": {
133
+ "total_devices": len(devices),
134
+ "available_devices": len(available),
135
+ "booted_devices": len(booted),
136
+ },
137
+ "quick_access": {
138
+ "booted": booted[:3] if booted else [],
139
+ "recommended_iphone": iphone[:3] if iphone else [],
140
+ },
141
+ }
142
+
143
+ def get_full_list(
144
+ self,
145
+ cache_id: str,
146
+ device_type: str | None = None,
147
+ runtime: str | None = None,
148
+ ) -> list[dict] | None:
149
+ """
150
+ Retrieve full simulator list from cache.
151
+
152
+ Args:
153
+ cache_id: Cache ID from concise summary
154
+ device_type: Filter by type (iPhone, iPad, etc.)
155
+ runtime: Filter by iOS version
156
+
157
+ Returns:
158
+ List of devices matching filters
159
+ """
160
+ data = self.cache.get(cache_id)
161
+ if not data:
162
+ return None
163
+
164
+ devices = data.get("devices", [])
165
+
166
+ # Apply filters
167
+ if device_type:
168
+ devices = [d for d in devices if device_type in d["name"]]
169
+ if runtime:
170
+ devices = [d for d in devices if runtime.lower() in d["runtime"].lower()]
171
+
172
+ return devices
173
+
174
+ def suggest_simulators(self, limit: int = 4) -> list[dict]:
175
+ """
176
+ Get simulator recommendations.
177
+
178
+ Returns:
179
+ List of recommended simulators (best candidates for building)
180
+ """
181
+ all_sims = self.list_simulators()
182
+ devices = self.parse_devices(all_sims)
183
+
184
+ # Score devices for recommendations
185
+ scored = []
186
+ for device in devices:
187
+ score = 0
188
+
189
+ # Prefer booted
190
+ if device["state"] == "Booted":
191
+ score += 10
192
+ # Prefer available
193
+ if device["is_available"]:
194
+ score += 5
195
+ # Prefer recent iOS versions
196
+ ios_version = device["runtime"]
197
+ if "18" in ios_version:
198
+ score += 3
199
+ elif "17" in ios_version:
200
+ score += 2
201
+ # Prefer iPhones over other types
202
+ if "iPhone" in device["name"]:
203
+ score += 1
204
+
205
+ scored.append({"device": device, "score": score})
206
+
207
+ # Sort by score and return top N
208
+ scored.sort(key=lambda x: x["score"], reverse=True)
209
+ return [s["device"] for s in scored[:limit]]
210
+
211
+
212
+ def format_device(device: dict) -> str:
213
+ """Format device for display."""
214
+ state_icon = "✓" if device["state"] == "Booted" else " "
215
+ avail_icon = "●" if device["is_available"] else "○"
216
+ name = device["name"]
217
+ runtime = device["runtime"]
218
+ udid_short = device["udid"][:8] + "..."
219
+ return f"{state_icon} {avail_icon} {name} ({runtime}) [{udid_short}]"
220
+
221
+
222
+ def main():
223
+ """Main entry point."""
224
+ parser = argparse.ArgumentParser(description="List iOS simulators with progressive disclosure")
225
+ parser.add_argument(
226
+ "--get-details",
227
+ metavar="CACHE_ID",
228
+ help="Get full details for cached simulator list",
229
+ )
230
+ parser.add_argument("--suggest", action="store_true", help="Get simulator recommendations")
231
+ parser.add_argument(
232
+ "--device-type",
233
+ help="Filter by device type (iPhone, iPad, Apple Watch, etc.)",
234
+ )
235
+ parser.add_argument("--runtime", help="Filter by iOS version (e.g., iOS-18, iOS-17)")
236
+ parser.add_argument("--json", action="store_true", help="Output as JSON")
237
+
238
+ args = parser.parse_args()
239
+
240
+ lister = SimulatorLister()
241
+
242
+ # Get full list with details
243
+ if args.get_details:
244
+ devices = lister.get_full_list(
245
+ args.get_details, device_type=args.device_type, runtime=args.runtime
246
+ )
247
+
248
+ if devices is None:
249
+ print(f"Error: Cache ID not found or expired: {args.get_details}")
250
+ sys.exit(1)
251
+
252
+ if args.json:
253
+ print(json.dumps(devices, indent=2))
254
+ else:
255
+ print(f"Simulators ({len(devices)}):\n")
256
+ for device in devices:
257
+ print(f" {format_device(device)}")
258
+
259
+ # Get recommendations
260
+ elif args.suggest:
261
+ suggestions = lister.suggest_simulators()
262
+
263
+ if args.json:
264
+ print(json.dumps(suggestions, indent=2))
265
+ else:
266
+ print("Recommended Simulators:\n")
267
+ for i, device in enumerate(suggestions, 1):
268
+ print(f"{i}. {format_device(device)}")
269
+
270
+ # Default: concise summary
271
+ else:
272
+ all_sims = lister.list_simulators()
273
+ devices = lister.parse_devices(all_sims)
274
+ summary = lister.get_concise_summary(devices)
275
+
276
+ if args.json:
277
+ print(json.dumps(summary, indent=2))
278
+ else:
279
+ # Human-readable concise output
280
+ cache_id = summary["cache_id"]
281
+ s = summary["summary"]
282
+ q = summary["quick_access"]
283
+
284
+ print(f"Simulator Summary [{cache_id}]")
285
+ print(f"├─ Total: {s['total_devices']} devices")
286
+ print(f"├─ Available: {s['available_devices']}")
287
+ print(f"└─ Booted: {s['booted_devices']}")
288
+
289
+ if q["booted"]:
290
+ print()
291
+ for device in q["booted"]:
292
+ print(f" {format_device(device)}")
293
+
294
+ print()
295
+ print(f"Use --get-details {cache_id} for full list")
296
+
297
+
298
+ if __name__ == "__main__":
299
+ main()
@@ -0,0 +1,312 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Boot iOS simulators and wait for readiness.
4
+
5
+ This script boots one or more simulators and optionally waits for them to reach
6
+ a ready state. It measures boot time and provides progress feedback.
7
+
8
+ Key features:
9
+ - Boot by UDID or device name
10
+ - Wait for device readiness with configurable timeout
11
+ - Measure boot performance
12
+ - Batch boot operations (boot all, boot by type)
13
+ - Progress reporting for CI/CD pipelines
14
+ """
15
+
16
+ import argparse
17
+ import subprocess
18
+ import sys
19
+ import time
20
+
21
+ from common.device_utils import (
22
+ get_booted_device_udid,
23
+ list_simulators,
24
+ resolve_device_identifier,
25
+ )
26
+ from common.env_config import env_float, env_int
27
+
28
+ DEFAULT_BOOT_TIMEOUT = env_int("IOS_SIM_BOOT_TIMEOUT", 300)
29
+ BOOT_SUBPROCESS_TIMEOUT = env_int("IOS_SIM_BOOT_SUBPROCESS_TIMEOUT", 60)
30
+ POLL_INTERVAL_SECONDS = env_float("IOS_SIM_POLL_INTERVAL", 0.5, min_value=0.05)
31
+
32
+
33
+ class SimulatorBooter:
34
+ """Boot iOS simulators with optional readiness waiting."""
35
+
36
+ def __init__(self, udid: str | None = None):
37
+ """Initialize booter with optional device UDID."""
38
+ self.udid = udid
39
+
40
+ def boot(
41
+ self, wait_ready: bool = False, timeout_seconds: int = DEFAULT_BOOT_TIMEOUT
42
+ ) -> tuple[bool, str]:
43
+ """
44
+ Boot simulator and optionally wait for readiness.
45
+
46
+ Args:
47
+ wait_ready: Wait for device to be ready before returning
48
+ timeout_seconds: Maximum seconds to wait for readiness
49
+
50
+ Returns:
51
+ (success, message) tuple
52
+ """
53
+ if not self.udid:
54
+ return False, "Error: Device UDID not specified"
55
+
56
+ start_time = time.time()
57
+
58
+ # Check if already booted
59
+ try:
60
+ booted = get_booted_device_udid()
61
+ if booted == self.udid:
62
+ elapsed = time.time() - start_time
63
+ return True, (f"Device already booted: {self.udid} " f"[checked in {elapsed:.1f}s]")
64
+ except RuntimeError:
65
+ pass # No booted device, proceed with boot
66
+
67
+ # Execute boot command
68
+ try:
69
+ cmd = ["xcrun", "simctl", "boot", self.udid]
70
+ result = subprocess.run(
71
+ cmd,
72
+ check=False,
73
+ capture_output=True,
74
+ text=True,
75
+ timeout=BOOT_SUBPROCESS_TIMEOUT,
76
+ )
77
+
78
+ if result.returncode != 0:
79
+ error = result.stderr.strip()
80
+ return False, f"Boot failed: {error}"
81
+ except subprocess.TimeoutExpired:
82
+ return False, "Boot command timed out"
83
+ except Exception as e:
84
+ return False, f"Boot error: {e}"
85
+
86
+ # Optionally wait for readiness
87
+ if wait_ready:
88
+ ready, wait_message = self._wait_for_ready(timeout_seconds)
89
+ elapsed = time.time() - start_time
90
+ if ready:
91
+ return True, (f"Device booted and ready: {self.udid} " f"[{elapsed:.1f}s total]")
92
+ return False, wait_message
93
+
94
+ elapsed = time.time() - start_time
95
+ return True, (
96
+ f"Device booted: {self.udid} [boot in {elapsed:.1f}s] "
97
+ "(use --wait-ready to wait for availability)"
98
+ )
99
+
100
+ def _wait_for_ready(self, timeout_seconds: int = DEFAULT_BOOT_TIMEOUT) -> tuple[bool, str]:
101
+ """
102
+ Wait for device to reach ready state.
103
+
104
+ Args:
105
+ timeout_seconds: Maximum seconds to wait
106
+
107
+ Returns:
108
+ (success, message) tuple
109
+ """
110
+ start_time = time.time()
111
+ poll_interval = POLL_INTERVAL_SECONDS
112
+ checks = 0
113
+
114
+ while time.time() - start_time < timeout_seconds:
115
+ try:
116
+ checks += 1
117
+ # Check if device responds to simctl commands
118
+ result = subprocess.run(
119
+ ["xcrun", "simctl", "spawn", self.udid, "launchctl", "list"],
120
+ check=False,
121
+ capture_output=True,
122
+ text=True,
123
+ timeout=5,
124
+ )
125
+
126
+ if result.returncode == 0:
127
+ elapsed = time.time() - start_time
128
+ return True, (
129
+ f"Device ready: {self.udid} " f"[{elapsed:.1f}s, {checks} checks]"
130
+ )
131
+ except (subprocess.TimeoutExpired, RuntimeError):
132
+ pass # Not ready yet
133
+
134
+ time.sleep(poll_interval)
135
+
136
+ elapsed = time.time() - start_time
137
+ return False, (
138
+ f"Boot timeout: Device did not reach ready state "
139
+ f"within {elapsed:.1f}s ({checks} checks)"
140
+ )
141
+
142
+ @staticmethod
143
+ def boot_all() -> tuple[int, int]:
144
+ """
145
+ Boot all available simulators.
146
+
147
+ Returns:
148
+ (succeeded, failed) tuple with counts
149
+ """
150
+ simulators = list_simulators(state="available")
151
+ succeeded = 0
152
+ failed = 0
153
+
154
+ for sim in simulators:
155
+ booter = SimulatorBooter(udid=sim["udid"])
156
+ success, _message = booter.boot(wait_ready=False)
157
+ if success:
158
+ succeeded += 1
159
+ else:
160
+ failed += 1
161
+
162
+ return succeeded, failed
163
+
164
+ @staticmethod
165
+ def boot_by_type(device_type: str) -> tuple[int, int]:
166
+ """
167
+ Boot all simulators of a specific type.
168
+
169
+ Args:
170
+ device_type: Device type filter (e.g., "iPhone", "iPad")
171
+
172
+ Returns:
173
+ (succeeded, failed) tuple with counts
174
+ """
175
+ simulators = list_simulators(state="available")
176
+ succeeded = 0
177
+ failed = 0
178
+
179
+ for sim in simulators:
180
+ if device_type.lower() in sim["name"].lower():
181
+ booter = SimulatorBooter(udid=sim["udid"])
182
+ success, _message = booter.boot(wait_ready=False)
183
+ if success:
184
+ succeeded += 1
185
+ else:
186
+ failed += 1
187
+
188
+ return succeeded, failed
189
+
190
+
191
+ def main():
192
+ """Main entry point."""
193
+ parser = argparse.ArgumentParser(description="Boot iOS simulators and wait for readiness")
194
+ parser.add_argument(
195
+ "--udid",
196
+ help="Device UDID or name (required unless using --all or --type)",
197
+ )
198
+ parser.add_argument(
199
+ "--name",
200
+ help="Device name (alternative to --udid)",
201
+ )
202
+ parser.add_argument(
203
+ "--wait-ready",
204
+ action="store_true",
205
+ help="Wait for device to reach ready state",
206
+ )
207
+ parser.add_argument(
208
+ "--timeout",
209
+ type=int,
210
+ default=DEFAULT_BOOT_TIMEOUT,
211
+ help=(
212
+ f"Timeout for --wait-ready in seconds "
213
+ f"(default: {DEFAULT_BOOT_TIMEOUT}, override via IOS_SIM_BOOT_TIMEOUT)"
214
+ ),
215
+ )
216
+ parser.add_argument(
217
+ "--all",
218
+ action="store_true",
219
+ help="Boot all available simulators",
220
+ )
221
+ parser.add_argument(
222
+ "--type",
223
+ help="Boot all simulators of a specific type (e.g., iPhone, iPad)",
224
+ )
225
+ parser.add_argument(
226
+ "--json",
227
+ action="store_true",
228
+ help="Output as JSON",
229
+ )
230
+
231
+ args = parser.parse_args()
232
+
233
+ # Handle batch operations
234
+ if args.all:
235
+ succeeded, failed = SimulatorBooter.boot_all()
236
+ if args.json:
237
+ import json
238
+
239
+ print(
240
+ json.dumps(
241
+ {
242
+ "action": "boot_all",
243
+ "succeeded": succeeded,
244
+ "failed": failed,
245
+ "total": succeeded + failed,
246
+ }
247
+ )
248
+ )
249
+ else:
250
+ total = succeeded + failed
251
+ print(f"Boot summary: {succeeded}/{total} succeeded, " f"{failed} failed")
252
+ sys.exit(0 if failed == 0 else 1)
253
+
254
+ if args.type:
255
+ succeeded, failed = SimulatorBooter.boot_by_type(args.type)
256
+ if args.json:
257
+ import json
258
+
259
+ print(
260
+ json.dumps(
261
+ {
262
+ "action": "boot_by_type",
263
+ "type": args.type,
264
+ "succeeded": succeeded,
265
+ "failed": failed,
266
+ "total": succeeded + failed,
267
+ }
268
+ )
269
+ )
270
+ else:
271
+ total = succeeded + failed
272
+ print(f"Boot {args.type} summary: {succeeded}/{total} succeeded, " f"{failed} failed")
273
+ sys.exit(0 if failed == 0 else 1)
274
+
275
+ # Resolve device identifier
276
+ device_id = args.udid or args.name
277
+ if not device_id:
278
+ print("Error: Specify --udid, --name, --all, or --type", file=sys.stderr)
279
+ sys.exit(1)
280
+
281
+ try:
282
+ udid = resolve_device_identifier(device_id)
283
+ except RuntimeError as e:
284
+ print(f"Error: {e}", file=sys.stderr)
285
+ sys.exit(1)
286
+
287
+ # Boot device
288
+ booter = SimulatorBooter(udid=udid)
289
+ success, message = booter.boot(wait_ready=args.wait_ready, timeout_seconds=args.timeout)
290
+
291
+ if args.json:
292
+ import json
293
+
294
+ print(
295
+ json.dumps(
296
+ {
297
+ "action": "boot",
298
+ "device_id": device_id,
299
+ "udid": udid,
300
+ "success": success,
301
+ "message": message,
302
+ }
303
+ )
304
+ )
305
+ else:
306
+ print(message)
307
+
308
+ sys.exit(0 if success else 1)
309
+
310
+
311
+ if __name__ == "__main__":
312
+ main()