@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,394 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ iOS Gesture Controller - Swipes and Complex Gestures
4
+
5
+ Performs navigation gestures like swipes, scrolls, and pinches.
6
+ Token-efficient output for common navigation patterns.
7
+
8
+ This script handles touch gestures for iOS simulator automation. It provides
9
+ directional swipes, multi-swipe scrolling, pull-to-refresh, and pinch gestures.
10
+ Automatically detects screen size from the device for accurate gesture positioning.
11
+
12
+ Key Features:
13
+ - Directional swipes (up, down, left, right)
14
+ - Multi-swipe scrolling with customizable amount
15
+ - Pull-to-refresh gesture
16
+ - Pinch to zoom (in/out)
17
+ - Custom swipe between any two points
18
+ - Drag and drop simulation
19
+ - Auto-detects screen dimensions from device
20
+
21
+ Usage Examples:
22
+ # Simple directional swipe
23
+ python scripts/gesture.py --swipe up --udid <device-id>
24
+
25
+ # Scroll down multiple times
26
+ python scripts/gesture.py --scroll down --scroll-amount 3 --udid <device-id>
27
+
28
+ # Pull to refresh
29
+ python scripts/gesture.py --refresh --udid <device-id>
30
+
31
+ # Custom swipe coordinates
32
+ python scripts/gesture.py --swipe-from 100,500 --swipe-to 100,100 --udid <device-id>
33
+
34
+ # Pinch to zoom
35
+ python scripts/gesture.py --pinch out --udid <device-id>
36
+
37
+ # Long press at coordinates
38
+ python scripts/gesture.py --long-press 200,300 --duration 2.0 --udid <device-id>
39
+
40
+ Output Format:
41
+ Swiped up
42
+ Scrolled down (3x)
43
+ Performed pull to refresh
44
+
45
+ Gesture Details:
46
+ - Swipes use 70% of screen by default (configurable)
47
+ - Scrolls are multiple small 30% swipes with delays
48
+ - Start points are offset from edges for reliability
49
+ - Screen size auto-detected from accessibility tree root element
50
+ - Falls back to iPhone 14 dimensions (390x844) if detection fails
51
+
52
+ Technical Details:
53
+ - Uses `idb ui swipe x1 y1 x2 y2` for gesture execution
54
+ - Duration parameter converts to milliseconds for IDB
55
+ - Automatically fetches screen size on initialization
56
+ - Parses IDB accessibility tree to get root frame dimensions
57
+ - All coordinates calculated as fractions of screen size for device independence
58
+ """
59
+
60
+ import argparse
61
+ import subprocess
62
+ import sys
63
+ import time
64
+
65
+ from common import (
66
+ get_device_screen_size,
67
+ get_screen_size,
68
+ resolve_udid,
69
+ transform_screenshot_coords,
70
+ )
71
+
72
+
73
+ class GestureController:
74
+ """Performs gestures on iOS simulator."""
75
+
76
+ # Standard screen dimensions (will be detected if possible)
77
+ DEFAULT_WIDTH = 390 # iPhone 14
78
+ DEFAULT_HEIGHT = 844
79
+
80
+ def __init__(self, udid: str | None = None):
81
+ """Initialize gesture controller."""
82
+ self.udid = udid
83
+ self.screen_size = self._get_screen_size()
84
+
85
+ def _get_screen_size(self) -> tuple[int, int]:
86
+ """Try to detect screen size from device using shared utility."""
87
+ return get_screen_size(self.udid)
88
+
89
+ def swipe(self, direction: str, distance_ratio: float = 0.7) -> bool:
90
+ """
91
+ Perform directional swipe.
92
+
93
+ Args:
94
+ direction: up, down, left, right
95
+ distance_ratio: How far to swipe (0.0-1.0 of screen)
96
+
97
+ Returns:
98
+ Success status
99
+ """
100
+ width, height = self.screen_size
101
+ center_x = width // 2
102
+ center_y = height // 2
103
+
104
+ # Calculate swipe coordinates based on direction
105
+ if direction == "up":
106
+ start = (center_x, int(height * 0.7))
107
+ end = (center_x, int(height * (1 - distance_ratio + 0.3)))
108
+ elif direction == "down":
109
+ start = (center_x, int(height * 0.3))
110
+ end = (center_x, int(height * (distance_ratio - 0.3 + 0.3)))
111
+ elif direction == "left":
112
+ start = (int(width * 0.8), center_y)
113
+ end = (int(width * (1 - distance_ratio + 0.2)), center_y)
114
+ elif direction == "right":
115
+ start = (int(width * 0.2), center_y)
116
+ end = (int(width * (distance_ratio - 0.2 + 0.2)), center_y)
117
+ else:
118
+ return False
119
+
120
+ return self.swipe_between(start, end)
121
+
122
+ def swipe_between(
123
+ self, start: tuple[int, int], end: tuple[int, int], duration: float = 0.3
124
+ ) -> bool:
125
+ """
126
+ Swipe between two points.
127
+
128
+ Args:
129
+ start: Starting coordinates (x, y)
130
+ end: Ending coordinates (x, y)
131
+ duration: Swipe duration in seconds
132
+
133
+ Returns:
134
+ Success status
135
+ """
136
+ cmd = ["idb", "ui", "swipe"]
137
+ cmd.extend([str(start[0]), str(start[1]), str(end[0]), str(end[1])])
138
+
139
+ # IDB doesn't support duration directly, but we can add delay
140
+ if duration != 0.3:
141
+ cmd.extend(["--duration", str(int(duration * 1000))])
142
+
143
+ if self.udid:
144
+ cmd.extend(["--udid", self.udid])
145
+
146
+ try:
147
+ subprocess.run(cmd, capture_output=True, check=True)
148
+ return True
149
+ except subprocess.CalledProcessError:
150
+ return False
151
+
152
+ def scroll(self, direction: str, amount: int = 3) -> bool:
153
+ """
154
+ Perform multiple small swipes to scroll.
155
+
156
+ Args:
157
+ direction: up, down
158
+ amount: Number of small swipes
159
+
160
+ Returns:
161
+ Success status
162
+ """
163
+ for _ in range(amount):
164
+ if not self.swipe(direction, distance_ratio=0.3):
165
+ return False
166
+ time.sleep(0.2) # Small delay between swipes
167
+ return True
168
+
169
+ def tap_and_hold(self, x: int, y: int, duration: float = 2.0) -> bool:
170
+ """
171
+ Long press at coordinates.
172
+
173
+ Args:
174
+ x, y: Coordinates
175
+ duration: Hold duration in seconds
176
+
177
+ Returns:
178
+ Success status
179
+ """
180
+ # IDB doesn't have native long press, simulate with tap
181
+ # In real implementation, might need to use different approach
182
+ cmd = ["idb", "ui", "tap", str(x), str(y)]
183
+
184
+ if self.udid:
185
+ cmd.extend(["--udid", self.udid])
186
+
187
+ try:
188
+ subprocess.run(cmd, capture_output=True, check=True)
189
+ # Simulate hold with delay
190
+ time.sleep(duration)
191
+ return True
192
+ except subprocess.CalledProcessError:
193
+ return False
194
+
195
+ def pinch(self, direction: str = "out", center: tuple[int, int] | None = None) -> bool:
196
+ """
197
+ Perform pinch gesture (zoom in/out).
198
+
199
+ Args:
200
+ direction: 'in' (zoom out) or 'out' (zoom in)
201
+ center: Center point for pinch
202
+
203
+ Returns:
204
+ Success status
205
+ """
206
+ if not center:
207
+ width, height = self.screen_size
208
+ center = (width // 2, height // 2)
209
+
210
+ # Calculate pinch points
211
+ offset = 100 if direction == "out" else 50
212
+
213
+ if direction == "out":
214
+ # Zoom in - fingers move apart
215
+ start1 = (center[0] - 20, center[1] - 20)
216
+ end1 = (center[0] - offset, center[1] - offset)
217
+ start2 = (center[0] + 20, center[1] + 20)
218
+ end2 = (center[0] + offset, center[1] + offset)
219
+ else:
220
+ # Zoom out - fingers move together
221
+ start1 = (center[0] - offset, center[1] - offset)
222
+ end1 = (center[0] - 20, center[1] - 20)
223
+ start2 = (center[0] + offset, center[1] + offset)
224
+ end2 = (center[0] + 20, center[1] + 20)
225
+
226
+ # Perform two swipes simultaneously (simulated)
227
+ success1 = self.swipe_between(start1, end1)
228
+ success2 = self.swipe_between(start2, end2)
229
+
230
+ return success1 and success2
231
+
232
+ def drag_and_drop(self, start: tuple[int, int], end: tuple[int, int]) -> bool:
233
+ """
234
+ Drag element from one position to another.
235
+
236
+ Args:
237
+ start: Starting coordinates
238
+ end: Ending coordinates
239
+
240
+ Returns:
241
+ Success status
242
+ """
243
+ # Use slow swipe to simulate drag
244
+ return self.swipe_between(start, end, duration=1.0)
245
+
246
+ def refresh(self) -> bool:
247
+ """Pull to refresh gesture."""
248
+ width, _ = self.screen_size
249
+ start = (width // 2, 100)
250
+ end = (width // 2, 400)
251
+ return self.swipe_between(start, end)
252
+
253
+
254
+ def main():
255
+ """Main entry point."""
256
+ parser = argparse.ArgumentParser(description="Perform gestures on iOS simulator")
257
+
258
+ # Gesture options
259
+ parser.add_argument(
260
+ "--swipe", choices=["up", "down", "left", "right"], help="Perform directional swipe"
261
+ )
262
+ parser.add_argument("--swipe-from", help="Custom swipe start coordinates (x,y)")
263
+ parser.add_argument("--swipe-to", help="Custom swipe end coordinates (x,y)")
264
+ parser.add_argument(
265
+ "--scroll", choices=["up", "down"], help="Scroll in direction (multiple small swipes)"
266
+ )
267
+ parser.add_argument(
268
+ "--scroll-amount", type=int, default=3, help="Number of scroll swipes (default: 3)"
269
+ )
270
+ parser.add_argument("--long-press", help="Long press at coordinates (x,y)")
271
+ parser.add_argument(
272
+ "--duration", type=float, default=2.0, help="Duration for long press in seconds"
273
+ )
274
+ parser.add_argument(
275
+ "--pinch", choices=["in", "out"], help="Pinch gesture (in=zoom out, out=zoom in)"
276
+ )
277
+ parser.add_argument("--refresh", action="store_true", help="Pull to refresh gesture")
278
+
279
+ # Coordinate transformation
280
+ parser.add_argument(
281
+ "--screenshot-coords",
282
+ action="store_true",
283
+ help="Interpret swipe coordinates as from a screenshot (requires --screenshot-width/height)",
284
+ )
285
+ parser.add_argument(
286
+ "--screenshot-width",
287
+ type=int,
288
+ help="Screenshot width for coordinate transformation",
289
+ )
290
+ parser.add_argument(
291
+ "--screenshot-height",
292
+ type=int,
293
+ help="Screenshot height for coordinate transformation",
294
+ )
295
+
296
+ parser.add_argument(
297
+ "--udid",
298
+ help="Device UDID (auto-detects booted simulator if not provided)",
299
+ )
300
+
301
+ args = parser.parse_args()
302
+
303
+ # Resolve UDID with auto-detection
304
+ try:
305
+ udid = resolve_udid(args.udid)
306
+ except RuntimeError as e:
307
+ print(f"Error: {e}")
308
+ sys.exit(1)
309
+
310
+ controller = GestureController(udid=udid)
311
+
312
+ # Execute requested gesture
313
+ if args.swipe:
314
+ if controller.swipe(args.swipe):
315
+ print(f"Swiped {args.swipe}")
316
+ else:
317
+ print(f"Failed to swipe {args.swipe}")
318
+ sys.exit(1)
319
+
320
+ elif args.swipe_from and args.swipe_to:
321
+ # Custom swipe
322
+ start = tuple(map(int, args.swipe_from.split(",")))
323
+ end = tuple(map(int, args.swipe_to.split(",")))
324
+
325
+ # Handle coordinate transformation if requested
326
+ if args.screenshot_coords:
327
+ if not args.screenshot_width or not args.screenshot_height:
328
+ print(
329
+ "Error: --screenshot-coords requires --screenshot-width and --screenshot-height"
330
+ )
331
+ sys.exit(1)
332
+
333
+ device_w, device_h = get_device_screen_size(udid)
334
+ start = transform_screenshot_coords(
335
+ start[0],
336
+ start[1],
337
+ args.screenshot_width,
338
+ args.screenshot_height,
339
+ device_w,
340
+ device_h,
341
+ )
342
+ end = transform_screenshot_coords(
343
+ end[0],
344
+ end[1],
345
+ args.screenshot_width,
346
+ args.screenshot_height,
347
+ device_w,
348
+ device_h,
349
+ )
350
+ print("Transformed screenshot coords to device coords")
351
+
352
+ if controller.swipe_between(start, end):
353
+ print(f"Swiped from {start} to {end}")
354
+ else:
355
+ print("Failed to swipe")
356
+ sys.exit(1)
357
+
358
+ elif args.scroll:
359
+ if controller.scroll(args.scroll, args.scroll_amount):
360
+ print(f"Scrolled {args.scroll} ({args.scroll_amount}x)")
361
+ else:
362
+ print(f"Failed to scroll {args.scroll}")
363
+ sys.exit(1)
364
+
365
+ elif args.long_press:
366
+ coords = tuple(map(int, args.long_press.split(",")))
367
+ if controller.tap_and_hold(coords[0], coords[1], args.duration):
368
+ print(f"Long pressed at {coords} for {args.duration}s")
369
+ else:
370
+ print("Failed to long press")
371
+ sys.exit(1)
372
+
373
+ elif args.pinch:
374
+ if controller.pinch(args.pinch):
375
+ action = "Zoomed in" if args.pinch == "out" else "Zoomed out"
376
+ print(action)
377
+ else:
378
+ print(f"Failed to pinch {args.pinch}")
379
+ sys.exit(1)
380
+
381
+ elif args.refresh:
382
+ if controller.refresh():
383
+ print("Performed pull to refresh")
384
+ else:
385
+ print("Failed to refresh")
386
+ sys.exit(1)
387
+
388
+ else:
389
+ parser.print_help()
390
+ sys.exit(1)
391
+
392
+
393
+ if __name__ == "__main__":
394
+ main()