@leejungkiin/awkit 1.7.0 → 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 (241) 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 +40 -7
  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/verification-gate/SKILL.md +4 -2
  197. package/skills/video-edit/SKILL.md +36 -0
  198. package/skills/video-edit/scripts/video_edit.py +324 -0
  199. package/templates/setup-mapping.json +48 -0
  200. package/templates/specs/design-template.md +161 -71
  201. package/templates/specs/requirements-template.md +65 -133
  202. package/templates/specs/task-spec-template.xml +3 -0
  203. package/workflows/_uncategorized/critic.md +40 -0
  204. package/workflows/_uncategorized/git-rebase-flow.md +81 -0
  205. package/workflows/_uncategorized/image-gen.md +118 -0
  206. package/workflows/_uncategorized/multi-model-pipeline.md +60 -0
  207. package/workflows/_uncategorized/pixel-gen.md +86 -0
  208. package/workflows/_uncategorized/pixel-setup.md +90 -0
  209. package/workflows/_uncategorized/ponytail-review.md +59 -0
  210. package/workflows/_uncategorized/reverse-android-build.md +222 -0
  211. package/workflows/_uncategorized/reverse-android-design.md +139 -0
  212. package/workflows/_uncategorized/reverse-android-discover.md +150 -0
  213. package/workflows/_uncategorized/reverse-android-scan.md +158 -0
  214. package/workflows/_uncategorized/reverse-android.md +143 -0
  215. package/workflows/_uncategorized/reverse-ios-build.md +240 -0
  216. package/workflows/_uncategorized/reverse-ios-design.md +112 -0
  217. package/workflows/_uncategorized/reverse-ios-discover.md +120 -0
  218. package/workflows/_uncategorized/reverse-ios-scan.md +155 -0
  219. package/workflows/_uncategorized/reverse-ios.md +152 -0
  220. package/workflows/_uncategorized/safety-router.md +34 -0
  221. package/workflows/_uncategorized/teach.md +89 -0
  222. package/workflows/_uncategorized/verify-ui.md +53 -0
  223. package/workflows/_uncategorized/visualize-screenshots.md +34 -0
  224. package/workflows/ads/ads-analyst.md +201 -0
  225. package/workflows/ads/ads-audit.md +106 -0
  226. package/workflows/ads/ads-optimize.md +97 -0
  227. package/workflows/ads/ads-targeting.md +241 -0
  228. package/workflows/ads/adsExpert.md +160 -0
  229. package/workflows/ads/smali-ads-config.md +400 -0
  230. package/workflows/ads/smali-ads-flow.md +331 -0
  231. package/workflows/ads/smali-ads-interstitial.md +377 -0
  232. package/workflows/ads/smali-ads-native.md +382 -0
  233. package/workflows/context/teach.md +89 -0
  234. package/workflows/gitnexus.md +8 -8
  235. package/workflows/lifecycle/brainstorm.md +43 -0
  236. package/workflows/lifecycle/code.md +5 -0
  237. package/workflows/lifecycle/init.md +23 -5
  238. package/workflows/lifecycle/multi-model-pipeline.md +60 -0
  239. package/workflows/quality/ponytail-review.md +59 -0
  240. package/workflows/roles/critic.md +40 -0
  241. package/workflows/roles/safety-router.md +34 -0
@@ -0,0 +1,400 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ App State Capture for iOS Simulator
4
+
5
+ Captures complete app state including screenshot, accessibility tree, and logs.
6
+ Optimized for minimal token output.
7
+
8
+ Usage: python scripts/app_state_capture.py [options]
9
+ """
10
+
11
+ import argparse
12
+ import json
13
+ import subprocess
14
+ import sys
15
+ from datetime import datetime
16
+ from pathlib import Path
17
+
18
+ from common import (
19
+ capture_screenshot,
20
+ count_elements,
21
+ get_accessibility_tree,
22
+ resolve_udid,
23
+ )
24
+ from common.env_config import env_int
25
+
26
+ STATE_SUBPROCESS_TIMEOUT = env_int("IOS_SIM_STATE_SUBPROCESS_TIMEOUT", 15)
27
+
28
+
29
+ class AppStateCapture:
30
+ """Captures comprehensive app state for debugging."""
31
+
32
+ def __init__(
33
+ self,
34
+ app_bundle_id: str | None = None,
35
+ udid: str | None = None,
36
+ inline: bool = False,
37
+ screenshot_size: str = "half",
38
+ ):
39
+ """
40
+ Initialize state capture.
41
+
42
+ Args:
43
+ app_bundle_id: Optional app bundle ID for log filtering
44
+ udid: Optional device UDID (uses booted if not specified)
45
+ inline: If True, return screenshots as base64 (for vision-based automation)
46
+ screenshot_size: 'full', 'half', 'quarter', 'thumb' (default: 'half')
47
+ """
48
+ self.app_bundle_id = app_bundle_id
49
+ self.udid = udid
50
+ self.inline = inline
51
+ self.screenshot_size = screenshot_size
52
+
53
+ def capture_screenshot(self, output_path: Path) -> bool:
54
+ """Capture screenshot of current screen."""
55
+ cmd = ["xcrun", "simctl", "io"]
56
+
57
+ if self.udid:
58
+ cmd.append(self.udid)
59
+ else:
60
+ cmd.append("booted")
61
+
62
+ cmd.extend(["screenshot", str(output_path)])
63
+
64
+ try:
65
+ subprocess.run(cmd, capture_output=True, check=True)
66
+ return True
67
+ except subprocess.CalledProcessError:
68
+ return False
69
+
70
+ def capture_accessibility_tree(self, output_path: Path) -> dict:
71
+ """Capture accessibility tree using shared utility."""
72
+ try:
73
+ # Use shared utility to fetch tree
74
+ tree = get_accessibility_tree(self.udid, nested=True)
75
+
76
+ # Save tree
77
+ with open(output_path, "w") as f:
78
+ json.dump(tree, f, indent=2)
79
+
80
+ # Return summary using shared utility
81
+ return {"captured": True, "element_count": count_elements(tree)}
82
+ except Exception as e:
83
+ return {"captured": False, "error": str(e)}
84
+
85
+ def capture_logs(self, output_path: Path, line_limit: int = 100) -> dict:
86
+ """Capture recent app logs."""
87
+ if not self.app_bundle_id:
88
+ # Can't capture logs without app ID
89
+ return {"captured": False, "reason": "No app bundle ID specified"}
90
+
91
+ # Get app name from bundle ID (simplified)
92
+ app_name = self.app_bundle_id.split(".")[-1]
93
+
94
+ cmd = ["xcrun", "simctl", "spawn"]
95
+
96
+ if self.udid:
97
+ cmd.append(self.udid)
98
+ else:
99
+ cmd.append("booted")
100
+
101
+ cmd.extend(
102
+ [
103
+ "log",
104
+ "show",
105
+ "--predicate",
106
+ f'process == "{app_name}"',
107
+ "--last",
108
+ "1m", # Last 1 minute
109
+ "--style",
110
+ "compact",
111
+ ]
112
+ )
113
+
114
+ try:
115
+ result = subprocess.run(
116
+ cmd,
117
+ check=False,
118
+ capture_output=True,
119
+ text=True,
120
+ timeout=STATE_SUBPROCESS_TIMEOUT,
121
+ )
122
+ logs = result.stdout
123
+
124
+ # Limit lines for token efficiency
125
+ lines = logs.split("\n")
126
+ if len(lines) > line_limit:
127
+ lines = lines[-line_limit:]
128
+
129
+ # Save logs
130
+ with open(output_path, "w") as f:
131
+ f.write("\n".join(lines))
132
+
133
+ # Analyze for issues
134
+ warning_count = sum(1 for line in lines if "warning" in line.lower())
135
+ error_count = sum(1 for line in lines if "error" in line.lower())
136
+
137
+ return {
138
+ "captured": True,
139
+ "lines": len(lines),
140
+ "warnings": warning_count,
141
+ "errors": error_count,
142
+ }
143
+ except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e:
144
+ return {"captured": False, "error": str(e)}
145
+
146
+ def capture_device_info(self) -> dict:
147
+ """Get device information."""
148
+ cmd = ["xcrun", "simctl", "list", "devices", "booted"]
149
+
150
+ if self.udid:
151
+ # Specific device info
152
+ cmd = ["xcrun", "simctl", "list", "devices"]
153
+
154
+ try:
155
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
156
+
157
+ # Parse output for device info (simplified)
158
+ lines = result.stdout.split("\n")
159
+ device_info = {}
160
+
161
+ for line in lines:
162
+ if "iPhone" in line or "iPad" in line:
163
+ # Extract device name and state
164
+ parts = line.strip().split("(")
165
+ if parts:
166
+ device_info["name"] = parts[0].strip()
167
+ if len(parts) > 2:
168
+ device_info["udid"] = parts[1].replace(")", "").strip()
169
+ device_info["state"] = parts[2].replace(")", "").strip()
170
+ break
171
+
172
+ return device_info
173
+ except subprocess.CalledProcessError:
174
+ return {}
175
+
176
+ def capture_all(
177
+ self, output_dir: str, log_lines: int = 100, app_name: str | None = None
178
+ ) -> dict:
179
+ """
180
+ Capture complete app state.
181
+
182
+ Args:
183
+ output_dir: Directory to save artifacts
184
+ log_lines: Number of log lines to capture
185
+ app_name: App name for semantic naming (for inline mode)
186
+
187
+ Returns:
188
+ Summary of captured state
189
+ """
190
+ # Create output directory (only if not in inline mode)
191
+ output_path = Path(output_dir)
192
+ timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
193
+ if not self.inline:
194
+ capture_dir = output_path / f"app-state-{timestamp}"
195
+ capture_dir.mkdir(parents=True, exist_ok=True)
196
+ else:
197
+ capture_dir = None
198
+
199
+ summary = {
200
+ "timestamp": datetime.now().isoformat(),
201
+ "screenshot_mode": "inline" if self.inline else "file",
202
+ }
203
+
204
+ if capture_dir:
205
+ summary["output_dir"] = str(capture_dir)
206
+
207
+ # Capture screenshot using new unified utility
208
+ screenshot_result = capture_screenshot(
209
+ self.udid,
210
+ size=self.screenshot_size,
211
+ inline=self.inline,
212
+ app_name=app_name,
213
+ )
214
+
215
+ if self.inline:
216
+ # Inline mode: store base64
217
+ summary["screenshot"] = {
218
+ "mode": "inline",
219
+ "base64": screenshot_result["base64_data"],
220
+ "width": screenshot_result["width"],
221
+ "height": screenshot_result["height"],
222
+ "size_preset": self.screenshot_size,
223
+ }
224
+ else:
225
+ # File mode: save to disk
226
+ screenshot_path = capture_dir / "screenshot.png"
227
+ # Move temp file to target location
228
+ import shutil
229
+
230
+ shutil.move(screenshot_result["file_path"], screenshot_path)
231
+ summary["screenshot"] = {
232
+ "mode": "file",
233
+ "file": "screenshot.png",
234
+ "size_bytes": screenshot_result["size_bytes"],
235
+ }
236
+
237
+ # Capture accessibility tree
238
+ if not self.inline or capture_dir:
239
+ accessibility_path = (capture_dir or output_path) / "accessibility-tree.json"
240
+ else:
241
+ accessibility_path = None
242
+
243
+ if accessibility_path:
244
+ tree_info = self.capture_accessibility_tree(accessibility_path)
245
+ summary["accessibility"] = tree_info
246
+
247
+ # Capture logs (if app ID provided)
248
+ if self.app_bundle_id:
249
+ if not self.inline or capture_dir:
250
+ logs_path = (capture_dir or output_path) / "app-logs.txt"
251
+ else:
252
+ logs_path = None
253
+
254
+ if logs_path:
255
+ log_info = self.capture_logs(logs_path, log_lines)
256
+ summary["logs"] = log_info
257
+
258
+ # Get device info
259
+ device_info = self.capture_device_info()
260
+ if device_info:
261
+ summary["device"] = device_info
262
+ # Save device info (file mode only)
263
+ if capture_dir:
264
+ with open(capture_dir / "device-info.json", "w") as f:
265
+ json.dump(device_info, f, indent=2)
266
+
267
+ # Save summary (file mode only)
268
+ if capture_dir:
269
+ with open(capture_dir / "summary.json", "w") as f:
270
+ json.dump(summary, f, indent=2)
271
+
272
+ # Create markdown summary
273
+ self._create_summary_md(capture_dir, summary)
274
+
275
+ return summary
276
+
277
+ def _create_summary_md(self, capture_dir: Path, summary: dict) -> None:
278
+ """Create markdown summary file."""
279
+ md_path = capture_dir / "summary.md"
280
+
281
+ with open(md_path, "w") as f:
282
+ f.write("# App State Capture\n\n")
283
+ f.write(f"**Timestamp:** {summary['timestamp']}\n\n")
284
+
285
+ if "device" in summary:
286
+ f.write("## Device\n")
287
+ device = summary["device"]
288
+ f.write(f"- Name: {device.get('name', 'Unknown')}\n")
289
+ f.write(f"- UDID: {device.get('udid', 'N/A')}\n")
290
+ f.write(f"- State: {device.get('state', 'Unknown')}\n\n")
291
+
292
+ f.write("## Screenshot\n")
293
+ f.write("![Current Screen](screenshot.png)\n\n")
294
+
295
+ if "accessibility" in summary:
296
+ acc = summary["accessibility"]
297
+ f.write("## Accessibility\n")
298
+ if acc.get("captured"):
299
+ f.write(f"- Elements: {acc.get('element_count', 0)}\n")
300
+ else:
301
+ f.write(f"- Error: {acc.get('error', 'Unknown')}\n")
302
+ f.write("\n")
303
+
304
+ if "logs" in summary:
305
+ logs = summary["logs"]
306
+ f.write("## Logs\n")
307
+ if logs.get("captured"):
308
+ f.write(f"- Lines: {logs.get('lines', 0)}\n")
309
+ f.write(f"- Warnings: {logs.get('warnings', 0)}\n")
310
+ f.write(f"- Errors: {logs.get('errors', 0)}\n")
311
+ else:
312
+ f.write(f"- {logs.get('reason', logs.get('error', 'Not captured'))}\n")
313
+ f.write("\n")
314
+
315
+ f.write("## Files\n")
316
+ f.write("- `screenshot.png` - Current screen\n")
317
+ f.write("- `accessibility-tree.json` - Full UI hierarchy\n")
318
+ if self.app_bundle_id:
319
+ f.write("- `app-logs.txt` - Recent app logs\n")
320
+ f.write("- `device-info.json` - Device details\n")
321
+ f.write("- `summary.json` - Complete capture metadata\n")
322
+
323
+
324
+ def main():
325
+ """Main entry point."""
326
+ parser = argparse.ArgumentParser(description="Capture complete app state for debugging")
327
+ parser.add_argument(
328
+ "--app-bundle-id", help="App bundle ID for log filtering (e.g., com.example.app)"
329
+ )
330
+ parser.add_argument(
331
+ "--output", default=".", help="Output directory (default: current directory)"
332
+ )
333
+ parser.add_argument(
334
+ "--log-lines", type=int, default=100, help="Number of log lines to capture (default: 100)"
335
+ )
336
+ parser.add_argument(
337
+ "--udid",
338
+ help="Device UDID (auto-detects booted simulator if not provided)",
339
+ )
340
+ parser.add_argument(
341
+ "--inline",
342
+ action="store_true",
343
+ help="Return screenshots as base64 (inline mode for vision-based automation)",
344
+ )
345
+ parser.add_argument(
346
+ "--size",
347
+ choices=["full", "half", "quarter", "thumb"],
348
+ default="half",
349
+ help="Screenshot size for token optimization (default: half)",
350
+ )
351
+ parser.add_argument("--app-name", help="App name for semantic screenshot naming")
352
+
353
+ args = parser.parse_args()
354
+
355
+ # Resolve UDID with auto-detection
356
+ try:
357
+ udid = resolve_udid(args.udid)
358
+ except RuntimeError as e:
359
+ print(f"Error: {e}")
360
+ sys.exit(1)
361
+
362
+ # Create capturer
363
+ capturer = AppStateCapture(
364
+ app_bundle_id=args.app_bundle_id,
365
+ udid=udid,
366
+ inline=args.inline,
367
+ screenshot_size=args.size,
368
+ )
369
+
370
+ # Capture state
371
+ try:
372
+ summary = capturer.capture_all(
373
+ output_dir=args.output, log_lines=args.log_lines, app_name=args.app_name
374
+ )
375
+
376
+ # Token-efficient output
377
+ if "output_dir" in summary:
378
+ print(f"State captured: {summary['output_dir']}/")
379
+ else:
380
+ # Inline mode
381
+ print(
382
+ f"State captured (inline mode): {summary['screenshot']['width']}x{summary['screenshot']['height']}"
383
+ )
384
+
385
+ # Report any issues found
386
+ if "logs" in summary and summary["logs"].get("captured"):
387
+ logs = summary["logs"]
388
+ if logs["errors"] > 0 or logs["warnings"] > 0:
389
+ print(f"Issues found: {logs['errors']} errors, {logs['warnings']} warnings")
390
+
391
+ if "accessibility" in summary and summary["accessibility"].get("captured"):
392
+ print(f"Elements: {summary['accessibility']['element_count']}")
393
+
394
+ except Exception as e:
395
+ print(f"Error: {e}")
396
+ sys.exit(1)
397
+
398
+
399
+ if __name__ == "__main__":
400
+ main()