@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,451 @@
1
+ """
2
+ XCResult bundle parser.
3
+
4
+ Extracts structured data from xcresult bundles using xcresulttool.
5
+ """
6
+
7
+ import json
8
+ import re
9
+ import subprocess
10
+ import sys
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+
15
+ class XCResultParser:
16
+ """
17
+ Parse xcresult bundles to extract build/test data.
18
+
19
+ Uses xcresulttool to extract structured JSON data from Apple's
20
+ xcresult bundle format.
21
+ """
22
+
23
+ def __init__(self, xcresult_path: Path, stderr: str = ""):
24
+ """
25
+ Initialize parser.
26
+
27
+ Args:
28
+ xcresult_path: Path to xcresult bundle
29
+ stderr: Optional stderr output for fallback parsing
30
+ """
31
+ self.xcresult_path = xcresult_path
32
+ self.stderr = stderr
33
+
34
+ if xcresult_path and not xcresult_path.exists():
35
+ raise FileNotFoundError(f"XCResult bundle not found: {xcresult_path}")
36
+
37
+ def get_build_results(self) -> dict | None:
38
+ """
39
+ Get build results as JSON.
40
+
41
+ Returns:
42
+ Parsed JSON dict or None on error
43
+ """
44
+ return self._run_xcresulttool(["get", "build-results"])
45
+
46
+ def get_test_results(self) -> dict | None:
47
+ """
48
+ Get test results summary as JSON.
49
+
50
+ Returns:
51
+ Parsed JSON dict or None on error
52
+ """
53
+ return self._run_xcresulttool(["get", "test-results", "summary"])
54
+
55
+ def get_failed_tests(self) -> list[dict]:
56
+ """
57
+ Get failed test details from xcresult bundle.
58
+
59
+ Returns:
60
+ List of dicts with test_name and failure_message.
61
+ Returns [] if parsing fails or no failures found.
62
+ """
63
+ try:
64
+ data = self._run_xcresulttool(["get", "test-results", "tests"])
65
+ if not data:
66
+ return []
67
+
68
+ failed = []
69
+ # The structure varies by Xcode version — walk recursively
70
+ nodes = data if isinstance(data, list) else data.get("testNodes", [])
71
+ self._collect_failed_tests(nodes, failed)
72
+ return failed
73
+
74
+ except Exception as e:
75
+ print(f"Warning: Could not parse failed tests: {e}", file=sys.stderr)
76
+ return []
77
+
78
+ def _collect_failed_tests(self, nodes: list, failed: list[dict]) -> None:
79
+ """Recursively collect failed test cases from test node tree."""
80
+ if not isinstance(nodes, list):
81
+ return
82
+
83
+ for node in nodes:
84
+ if not isinstance(node, dict):
85
+ continue
86
+
87
+ is_test_case = node.get("nodeType") == "Test Case"
88
+ is_failed = node.get("result") == "Failed"
89
+
90
+ if is_test_case and is_failed:
91
+ failed.append(
92
+ {
93
+ "test_name": node.get("name", "Unknown"),
94
+ "failure_message": node.get("details", ""),
95
+ }
96
+ )
97
+
98
+ # Recurse into children
99
+ children = node.get("children", [])
100
+ self._collect_failed_tests(children, failed)
101
+
102
+ def get_build_log(self) -> str | None:
103
+ """
104
+ Get build log as plain text.
105
+
106
+ Returns:
107
+ Build log string or None on error
108
+ """
109
+ result = self._run_xcresulttool(["get", "log", "--type", "build"], parse_json=False)
110
+ return result if result else None
111
+
112
+ def count_issues(self) -> tuple[int, int]:
113
+ """
114
+ Count errors and warnings from build results.
115
+
116
+ Returns:
117
+ Tuple of (error_count, warning_count)
118
+ """
119
+ error_count = 0
120
+ warning_count = 0
121
+
122
+ build_results = self.get_build_results()
123
+
124
+ if build_results:
125
+ try:
126
+ # Try top-level errors/warnings first (newer xcresult format)
127
+ if "errors" in build_results and isinstance(build_results.get("errors"), list):
128
+ error_count = len(build_results["errors"])
129
+ if "warnings" in build_results and isinstance(build_results.get("warnings"), list):
130
+ warning_count = len(build_results["warnings"])
131
+
132
+ # If not found, try legacy format: actions[0].buildResult.issues
133
+ if error_count == 0 and warning_count == 0:
134
+ actions = build_results.get("actions", {}).get("_values", [])
135
+ if actions:
136
+ build_result = actions[0].get("buildResult", {})
137
+ issues = build_result.get("issues", {})
138
+
139
+ # Count errors
140
+ error_summaries = issues.get("errorSummaries", {}).get("_values", [])
141
+ error_count = len(error_summaries)
142
+
143
+ # Count warnings
144
+ warning_summaries = issues.get("warningSummaries", {}).get("_values", [])
145
+ warning_count = len(warning_summaries)
146
+
147
+ except (KeyError, IndexError, TypeError) as e:
148
+ print(f"Warning: Could not parse issue counts from xcresult: {e}", file=sys.stderr)
149
+
150
+ # If no errors found in xcresult but stderr available, count stderr errors
151
+ if error_count == 0 and self.stderr:
152
+ stderr_errors = self._parse_stderr_errors()
153
+ error_count = len(stderr_errors)
154
+
155
+ return (error_count, warning_count)
156
+
157
+ def get_errors(self) -> list[dict]:
158
+ """
159
+ Get detailed error information.
160
+
161
+ Returns:
162
+ List of error dicts with message, file, line info
163
+ """
164
+ build_results = self.get_build_results()
165
+ errors = []
166
+
167
+ # Try to get errors from xcresult
168
+ if build_results:
169
+ try:
170
+ # Try top-level errors first (newer xcresult format)
171
+ if "errors" in build_results and isinstance(build_results.get("errors"), list):
172
+ for error in build_results["errors"]:
173
+ errors.append(
174
+ {
175
+ "message": error.get("message", "Unknown error"),
176
+ "type": error.get("issueType", "error"),
177
+ "location": self._extract_location_from_url(error.get("sourceURL")),
178
+ }
179
+ )
180
+
181
+ # If not found, try legacy format: actions[0].buildResult.issues
182
+ if not errors:
183
+ actions = build_results.get("actions", {}).get("_values", [])
184
+ if actions:
185
+ build_result = actions[0].get("buildResult", {})
186
+ issues = build_result.get("issues", {})
187
+ error_summaries = issues.get("errorSummaries", {}).get("_values", [])
188
+
189
+ for error in error_summaries:
190
+ errors.append(
191
+ {
192
+ "message": error.get("message", {}).get(
193
+ "_value", "Unknown error"
194
+ ),
195
+ "type": error.get("issueType", {}).get("_value", "error"),
196
+ "location": self._extract_location(error),
197
+ }
198
+ )
199
+
200
+ except (KeyError, IndexError, TypeError) as e:
201
+ print(f"Warning: Could not parse errors from xcresult: {e}", file=sys.stderr)
202
+
203
+ # If no errors found in xcresult but stderr available, parse stderr
204
+ if not errors and self.stderr:
205
+ errors = self._parse_stderr_errors()
206
+
207
+ return errors
208
+
209
+ def get_warnings(self) -> list[dict]:
210
+ """
211
+ Get detailed warning information.
212
+
213
+ Returns:
214
+ List of warning dicts with message, file, line info
215
+ """
216
+ build_results = self.get_build_results()
217
+ if not build_results:
218
+ return []
219
+
220
+ warnings = []
221
+
222
+ try:
223
+ # Try top-level warnings first (newer xcresult format)
224
+ if "warnings" in build_results and isinstance(build_results.get("warnings"), list):
225
+ for warning in build_results["warnings"]:
226
+ warnings.append(
227
+ {
228
+ "message": warning.get("message", "Unknown warning"),
229
+ "type": warning.get("issueType", "warning"),
230
+ "location": self._extract_location_from_url(warning.get("sourceURL")),
231
+ }
232
+ )
233
+
234
+ # If not found, try legacy format: actions[0].buildResult.issues
235
+ if not warnings:
236
+ actions = build_results.get("actions", {}).get("_values", [])
237
+ if not actions:
238
+ return []
239
+
240
+ build_result = actions[0].get("buildResult", {})
241
+ issues = build_result.get("issues", {})
242
+ warning_summaries = issues.get("warningSummaries", {}).get("_values", [])
243
+
244
+ for warning in warning_summaries:
245
+ warnings.append(
246
+ {
247
+ "message": warning.get("message", {}).get("_value", "Unknown warning"),
248
+ "type": warning.get("issueType", {}).get("_value", "warning"),
249
+ "location": self._extract_location(warning),
250
+ }
251
+ )
252
+
253
+ except (KeyError, IndexError, TypeError) as e:
254
+ print(f"Warning: Could not parse warnings: {e}", file=sys.stderr)
255
+
256
+ return warnings
257
+
258
+ def _extract_location(self, issue: dict) -> dict:
259
+ """
260
+ Extract file location from issue.
261
+
262
+ Args:
263
+ issue: Issue dict from xcresult
264
+
265
+ Returns:
266
+ Location dict with file, line, column
267
+ """
268
+ location = {"file": None, "line": None, "column": None}
269
+
270
+ try:
271
+ doc_location = issue.get("documentLocationInCreatingWorkspace", {})
272
+ location["file"] = doc_location.get("url", {}).get("_value")
273
+ location["line"] = doc_location.get("startingLineNumber", {}).get("_value")
274
+ location["column"] = doc_location.get("startingColumnNumber", {}).get("_value")
275
+ except (KeyError, TypeError):
276
+ pass
277
+
278
+ return location
279
+
280
+ def _extract_location_from_url(self, source_url: str | None) -> dict:
281
+ """
282
+ Extract file location from sourceURL (newer xcresult format).
283
+
284
+ Args:
285
+ source_url: Source URL like "file:///path/to/file.swift#StartingLineNumber=134&..."
286
+
287
+ Returns:
288
+ Location dict with file, line, column
289
+ """
290
+ location = {"file": None, "line": None, "column": None}
291
+
292
+ if not source_url:
293
+ return location
294
+
295
+ try:
296
+ # Split URL and fragment
297
+ if "#" in source_url:
298
+ file_part, fragment = source_url.split("#", 1)
299
+
300
+ # Extract file path
301
+ location["file"] = file_part.replace("file://", "")
302
+
303
+ # Parse fragment parameters
304
+ params = {}
305
+ for param in fragment.split("&"):
306
+ if "=" in param:
307
+ key, value = param.split("=", 1)
308
+ params[key] = value
309
+
310
+ # Extract line and column
311
+ location["line"] = (
312
+ int(params.get("StartingLineNumber", 0)) + 1
313
+ if "StartingLineNumber" in params
314
+ else None
315
+ )
316
+ location["column"] = (
317
+ int(params.get("StartingColumnNumber", 0)) + 1
318
+ if "StartingColumnNumber" in params
319
+ else None
320
+ )
321
+ else:
322
+ # No fragment, just file path
323
+ location["file"] = source_url.replace("file://", "")
324
+
325
+ except (ValueError, AttributeError):
326
+ pass
327
+
328
+ return location
329
+
330
+ def _run_xcresulttool(self, args: list[str], parse_json: bool = True) -> Any | None:
331
+ """
332
+ Run xcresulttool command.
333
+
334
+ Args:
335
+ args: Command arguments (after 'xcresulttool')
336
+ parse_json: Whether to parse output as JSON
337
+
338
+ Returns:
339
+ Parsed JSON dict, plain text, or None on error
340
+ """
341
+ if not self.xcresult_path:
342
+ return None
343
+
344
+ cmd = ["xcrun", "xcresulttool"] + args + ["--path", str(self.xcresult_path)]
345
+
346
+ try:
347
+ result = subprocess.run(cmd, capture_output=True, text=True, check=True)
348
+
349
+ if parse_json:
350
+ return json.loads(result.stdout)
351
+ return result.stdout
352
+
353
+ except subprocess.CalledProcessError as e:
354
+ print(f"Error running xcresulttool: {e}", file=sys.stderr)
355
+ print(f"stderr: {e.stderr}", file=sys.stderr)
356
+ return None
357
+ except json.JSONDecodeError as e:
358
+ print(f"Error parsing JSON from xcresulttool: {e}", file=sys.stderr)
359
+ return None
360
+
361
+ def _parse_stderr_errors(self) -> list[dict]:
362
+ """
363
+ Parse common errors from stderr output as fallback.
364
+
365
+ Returns:
366
+ List of error dicts parsed from stderr
367
+ """
368
+ errors = []
369
+
370
+ if not self.stderr:
371
+ return errors
372
+
373
+ # Pattern 0: Swift/Clang compilation errors (e.g., "/path/file.swift:135:59: error: message")
374
+ compilation_error_pattern = (
375
+ r"^(?P<file>[^:]+):(?P<line>\d+):(?P<column>\d+):\s*error:\s*(?P<message>.+?)$"
376
+ )
377
+ for match in re.finditer(compilation_error_pattern, self.stderr, re.MULTILINE):
378
+ errors.append(
379
+ {
380
+ "message": match.group("message").strip(),
381
+ "type": "compilation",
382
+ "location": {
383
+ "file": match.group("file"),
384
+ "line": int(match.group("line")),
385
+ "column": int(match.group("column")),
386
+ },
387
+ }
388
+ )
389
+
390
+ # Pattern 1: xcodebuild top-level errors (e.g., "xcodebuild: error: Unable to find...")
391
+ xcodebuild_error_pattern = r"xcodebuild:\s*error:\s*(?P<message>.*?)(?:\n\n|\Z)"
392
+ for match in re.finditer(xcodebuild_error_pattern, self.stderr, re.DOTALL):
393
+ message = match.group("message").strip()
394
+ # Clean up multi-line messages
395
+ message = " ".join(line.strip() for line in message.split("\n") if line.strip())
396
+ errors.append(
397
+ {
398
+ "message": message,
399
+ "type": "build",
400
+ "location": {"file": None, "line": None, "column": None},
401
+ }
402
+ )
403
+
404
+ # Pattern 2: Provisioning profile errors
405
+ provisioning_pattern = r"error:.*?provisioning profile.*?(?:doesn't|does not|cannot).*?(?P<message>.*?)(?:\n|$)"
406
+ for match in re.finditer(provisioning_pattern, self.stderr, re.IGNORECASE):
407
+ errors.append(
408
+ {
409
+ "message": f"Provisioning profile error: {match.group('message').strip()}",
410
+ "type": "provisioning",
411
+ "location": {"file": None, "line": None, "column": None},
412
+ }
413
+ )
414
+
415
+ # Pattern 3: Code signing errors
416
+ signing_pattern = r"error:.*?(?:code sign|signing).*?(?P<message>.*?)(?:\n|$)"
417
+ for match in re.finditer(signing_pattern, self.stderr, re.IGNORECASE):
418
+ errors.append(
419
+ {
420
+ "message": f"Code signing error: {match.group('message').strip()}",
421
+ "type": "signing",
422
+ "location": {"file": None, "line": None, "column": None},
423
+ }
424
+ )
425
+
426
+ # Pattern 4: Generic compilation errors (but not if already captured)
427
+ if not errors:
428
+ generic_error_pattern = r"^(?:\*\*\s)?(?:error|❌):\s*(?P<message>.*?)(?:\n|$)"
429
+ for match in re.finditer(generic_error_pattern, self.stderr, re.MULTILINE):
430
+ message = match.group("message").strip()
431
+ errors.append(
432
+ {
433
+ "message": message,
434
+ "type": "build",
435
+ "location": {"file": None, "line": None, "column": None},
436
+ }
437
+ )
438
+
439
+ # Pattern 5: Specific "No profiles" error
440
+ if "No profiles for" in self.stderr:
441
+ no_profile_pattern = r"No profiles for '(?P<bundle_id>.*?)' were found"
442
+ for match in re.finditer(no_profile_pattern, self.stderr):
443
+ errors.append(
444
+ {
445
+ "message": f"No provisioning profile found for bundle ID '{match.group('bundle_id')}'",
446
+ "type": "provisioning",
447
+ "location": {"file": None, "line": None, "column": None},
448
+ }
449
+ )
450
+
451
+ return errors
@@ -0,0 +1,111 @@
1
+ ---
2
+ name: ios-visual-qa-strategist
3
+ description: Plan and execute iOS app QA with adaptive visual reasoning and minimal screenshots. Use when Codex needs to test an iOS, Swift, SwiftUI, UIKit, Xcode, or App Store build by inspecting screenshots, simulator/device state, accessibility output, logs, Maestro/XCTest flows, or user-provided screen images; especially when the goal is to infer the highest-value test path while reducing redundant screenshot capture.
4
+ ---
5
+
6
+ # iOS Visual QA Strategist
7
+
8
+ Use this skill to act as a QA strategist that sees the app like a user, reasons from available screen evidence, and captures only the screenshots needed to prove meaningful UI state.
9
+
10
+ ## Core Principle
11
+
12
+ Prefer cheap evidence before visual evidence:
13
+
14
+ 1. Read specs, tasks, source, route definitions, accessibility identifiers, and known flows.
15
+ 2. Use accessibility tree, test logs, console logs, and deterministic assertions to navigate.
16
+ 3. Capture screenshots only at high-value visual checkpoints.
17
+ 4. Let each screenshot answer multiple assertions.
18
+ 5. State confidence honestly when a flow was inferred rather than fully observed.
19
+
20
+ Do not treat screenshots as the only source of truth. Screenshots are strong for layout, content, and visible state; they are weak for persistence, network correctness, analytics, hidden navigation state, and business logic.
21
+
22
+ ## Workflow
23
+
24
+ ### 1. Establish Test Context
25
+
26
+ Identify:
27
+
28
+ - app target, scheme, simulator/device, OS version, and launch method
29
+ - feature under test and expected user outcome
30
+ - available automation tools: Maestro, XCTest, simctl, accessibility inspector, app logs, existing test files
31
+ - visual artifacts already provided by the user or stored in `docs/design/`, `docs/screenshots/`, `.kiro/specs/`, or test output folders
32
+
33
+ If this is an AWKit-managed project, respect its build/test wrapper rules. Do not bypass project automation by calling native build commands when the project requires a wrapper.
34
+
35
+ ### 2. Build a Screen Model
36
+
37
+ Before interacting heavily, infer:
38
+
39
+ - entry points and navigation graph
40
+ - critical flows and the shortest representative path through them
41
+ - states that need visual proof: initial, loading, populated, empty, error, disabled, modal, keyboard, permission, and destructive-confirmation
42
+ - likely layout risks: text overflow, clipped controls, hidden safe-area content, keyboard overlap, dynamic type, localization, long names, and dark mode
43
+
44
+ For unfamiliar codebases, search for accessibility identifiers, screen names, route names, SwiftUI `View` types, UIKit controllers, and existing UI tests.
45
+
46
+ ### 3. Write a Minimal-Capture Test Plan
47
+
48
+ Create a concise test plan with:
49
+
50
+ - primary user flow
51
+ - secondary/risk flows
52
+ - evidence type for each step: `accessibility`, `log`, `assertion`, `screenshot`, or `user-provided-image`
53
+ - screenshot budget and the reason each screenshot is worth taking
54
+ - stop conditions: what evidence is enough to pass, fail, or request clarification
55
+
56
+ Read `references/minimal-capture-policy.md` when deciding screenshot budget.
57
+
58
+ ### 4. Execute Adaptively
59
+
60
+ For each step:
61
+
62
+ 1. Predict expected screen state.
63
+ 2. Navigate using the least expensive reliable signal.
64
+ 3. Verify with accessibility/log/assertion first.
65
+ 4. Capture a screenshot only when visual proof adds information.
66
+ 5. Inspect the screenshot for multiple checks before taking another.
67
+ 6. If the screenshot contradicts the predicted state, revise the test plan instead of continuing blindly.
68
+
69
+ Read `references/visual-reasoning-heuristics.md` when analyzing screenshots.
70
+ Read `references/ios-tool-selection.md` when choosing between Maestro, XCTest, simctl, and manual simulator interaction.
71
+
72
+ ### 5. Report Findings
73
+
74
+ Always report:
75
+
76
+ - flows tested and flows intentionally skipped
77
+ - screenshot count and why each screenshot was necessary
78
+ - pass/fail findings with evidence
79
+ - visual issues, functional issues, and uncertainty separated
80
+ - recommended next test with the highest information gain
81
+
82
+ Use clear confidence labels:
83
+
84
+ - `High`: observed directly with screenshot or deterministic assertion
85
+ - `Medium`: verified through accessibility/logs and inferred visual continuity
86
+ - `Low`: inferred from code/spec only or blocked by missing evidence
87
+
88
+ ## Screenshot Budget Rules
89
+
90
+ Default target for a single feature smoke pass:
91
+
92
+ - 1 entry screenshot
93
+ - 1 screenshot per distinct screen class
94
+ - 1 screenshot for the most important post-action state
95
+ - 1 failure screenshot when a bug is found
96
+
97
+ Add more only when the new capture can answer a question that the previous evidence cannot.
98
+
99
+ ## Anti-Patterns
100
+
101
+ - Do not screenshot after every tap.
102
+ - Do not continue a scripted path after the visible state diverges from the plan.
103
+ - Do not mark visual QA complete using only logs or accessibility when the user asked for visual inspection.
104
+ - Do not ignore accessibility output; it often removes the need for another screenshot.
105
+ - Do not over-test low-risk decorative states while skipping checkout, auth, save, delete, onboarding, or permission flows.
106
+
107
+ ## Resources
108
+
109
+ - `references/minimal-capture-policy.md` - screenshot budget and capture decision rules
110
+ - `references/visual-reasoning-heuristics.md` - what to inspect in screenshots
111
+ - `references/ios-tool-selection.md` - choosing iOS QA tools and evidence types
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "iOS Visual QA Strategist"
3
+ short_description: "Plan iOS QA with minimal screenshots"
4
+ default_prompt: "Use $ios-visual-qa-strategist to test this iOS app visually with the fewest screenshots needed."
@@ -0,0 +1,61 @@
1
+ # iOS Tool Selection
2
+
3
+ Choose the cheapest reliable tool for the assertion.
4
+
5
+ ## Maestro
6
+
7
+ Use Maestro when:
8
+
9
+ - testing end-to-end user flows
10
+ - tapping visible text or accessibility identifiers
11
+ - capturing screenshots during flow checkpoints
12
+ - validating onboarding, auth, forms, tabs, sheets, and common navigation
13
+
14
+ Avoid Maestro as the only evidence for:
15
+
16
+ - low-level UIKit/SwiftUI component behavior
17
+ - hidden business logic
18
+ - persistence unless paired with relaunch or data checks
19
+
20
+ ## XCTest / XCUITest
21
+
22
+ Use XCTest or XCUITest when:
23
+
24
+ - the repo already has UI tests
25
+ - deterministic assertions are more important than ad hoc exploration
26
+ - testing accessibility identifiers, navigation state, or repeated regression flows
27
+ - needing CI-ready tests after an exploratory bug is found
28
+
29
+ Prefer adding or updating tests only after the exploratory visual pass identifies stable assertions.
30
+
31
+ ## simctl
32
+
33
+ Use `simctl` when:
34
+
35
+ - launching, terminating, or relaunching the app
36
+ - resetting permissions or app data
37
+ - recording screenshots or videos when project policy allows
38
+ - controlling simulator state in a repeatable way
39
+
40
+ Respect project wrappers and automation rules for build/test commands.
41
+
42
+ ## Accessibility Tree
43
+
44
+ Use accessibility output when:
45
+
46
+ - checking whether a control exists, is enabled, or has expected text
47
+ - avoiding redundant screenshots after navigation
48
+ - confirming screen identity cheaply
49
+ - extracting labels from dense UIs
50
+
51
+ Accessibility does not prove layout quality. Capture a screenshot when visual arrangement matters.
52
+
53
+ ## Logs
54
+
55
+ Use logs when:
56
+
57
+ - confirming network, persistence, analytics, or error conditions
58
+ - explaining why a visible state failed to update
59
+ - distinguishing UI bug from backend/data issue
60
+
61
+ Logs do not prove what the user saw. Pair them with screenshot evidence for user-facing failures.
@@ -0,0 +1,56 @@
1
+ # Minimal Capture Policy
2
+
3
+ Use screenshots as expensive evidence. Every screenshot must have a reason before it is taken.
4
+
5
+ ## Capture Decision
6
+
7
+ Take a screenshot when at least one condition is true:
8
+
9
+ - It is the first visual observation of the feature.
10
+ - A new screen class appears.
11
+ - The test reaches a visually important state: loaded data, empty state, error state, disabled state, destructive confirmation, permission prompt, modal, sheet, keyboard, or toast.
12
+ - Accessibility/log evidence confirms behavior but cannot prove layout or visible content.
13
+ - A bug is suspected and the screenshot will preserve evidence.
14
+ - The user explicitly asked for visual proof.
15
+
16
+ Skip a screenshot when:
17
+
18
+ - The accessibility tree already proves the expected state and no layout risk changed.
19
+ - The action only toggled hidden internal state.
20
+ - The next step will immediately produce a more informative screen.
21
+ - A previous screenshot of the same screen state already proves the assertion.
22
+
23
+ ## Budget Heuristics
24
+
25
+ For one focused feature:
26
+
27
+ - 2-4 screenshots is usually enough for smoke QA.
28
+ - 4-7 screenshots is reasonable for a multi-screen critical flow.
29
+ - More than 7 screenshots needs a clear reason, such as visual regression, localization, dynamic type, dark mode, or a bug investigation.
30
+
31
+ For broad app exploration:
32
+
33
+ - Capture one screenshot per major screen category.
34
+ - Prefer a contact sheet or grouped artifacts if many screens must be compared.
35
+ - Do not exhaustively capture every tab, modal, or variant unless the task is specifically visual inventory.
36
+
37
+ ## High-Information Captures
38
+
39
+ Prefer screenshots that prove several things at once:
40
+
41
+ - navigation arrived at the right screen
42
+ - important content is visible
43
+ - primary action is reachable
44
+ - layout respects safe areas
45
+ - loading/empty/error state resolved correctly
46
+
47
+ If a screenshot only proves "the button was tapped", avoid it.
48
+
49
+ ## Failure Captures
50
+
51
+ When a failure occurs:
52
+
53
+ 1. Capture the failing visible state.
54
+ 2. Save or summarize the relevant accessibility tree/log excerpt.
55
+ 3. Note the exact prior action.
56
+ 4. Stop or branch the test plan; do not keep executing stale assumptions.