@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.
- package/bin/awk.js +576 -84
- package/core/CLAUDE.md +1 -1
- package/core/GEMINI.md +148 -167
- package/core/GEMINI.md.bak +149 -116
- package/core/skill-runtime-manifest.json +3 -0
- package/docs/Claude Fable 5.md +3826 -0
- package/docs/android_kotlin_system_instruction.md +210 -0
- package/docs/brainstorm_ponytail_integration.md +146 -0
- package/docs/brainstorm_smart_setup.md +113 -0
- package/docs/deep-research-report (1).md +293 -0
- package/docs/history/GEMINI.v1.md +135 -0
- package/docs/history/brainstorm_antigravity_unified_architecture.v1.md +105 -0
- package/docs/history/implementation_plan.v1.md +58 -0
- package/package.json +4 -1
- package/scripts/artifact-storage.js +130 -0
- package/scripts/automation-gate.js +40 -7
- package/scripts/claude-plan.js +76 -0
- package/scripts/dependency-manager.js +210 -0
- package/scripts/exec-rtk.js +11 -5
- package/scripts/i18n-helper.js +381 -0
- package/scripts/multi-model-pipeline.js +144 -0
- package/skill-packs/mobile-ios/pack.json +4 -2
- package/skill-packs/reverse-engineering/pack.json +1 -0
- package/skills/CATALOG.md +20 -0
- package/skills/GEMINI.md +9 -1
- package/skills/TRIGGER_INDEX.md +10 -0
- package/skills/ai-music/SKILL.md +275 -0
- package/skills/android-re-analyzer/SKILL.md +238 -0
- package/skills/android-re-analyzer/references/api-extraction-patterns.md +119 -0
- package/skills/android-re-analyzer/references/call-flow-analysis.md +176 -0
- package/skills/android-re-analyzer/references/fernflower-usage.md +115 -0
- package/skills/android-re-analyzer/references/jadx-usage.md +116 -0
- package/skills/android-re-analyzer/references/setup-guide.md +221 -0
- package/skills/android-re-analyzer/scripts/check-deps.sh +129 -0
- package/skills/android-re-analyzer/scripts/decompile.sh +375 -0
- package/skills/android-re-analyzer/scripts/find-api-calls.sh +118 -0
- package/skills/android-re-analyzer/scripts/install-dep.sh +448 -0
- package/skills/animal-island-ui-style/SKILL.md +1450 -0
- package/skills/app-store-review-agent/SKILL.md +164 -0
- package/skills/app-store-review-agent/references/guidelines/README.md +154 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/ai_apps.md +37 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/all_apps.md +50 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/crypto_finance.md +31 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/games.md +31 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/health_fitness.md +31 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/kids.md +27 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/macos.md +38 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/social_ugc.md +32 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/subscription_iap.md +34 -0
- package/skills/app-store-review-agent/references/guidelines/by-app-type/vpn.md +18 -0
- package/skills/app-store-review-agent/references/rules/design/minimum_functionality.md +96 -0
- package/skills/app-store-review-agent/references/rules/design/sign_in_with_apple.md +54 -0
- package/skills/app-store-review-agent/references/rules/entitlements/unused_entitlements.md +83 -0
- package/skills/app-store-review-agent/references/rules/metadata/accurate_metadata.md +54 -0
- package/skills/app-store-review-agent/references/rules/metadata/apple_trademark.md +99 -0
- package/skills/app-store-review-agent/references/rules/metadata/china_storefront.md +72 -0
- package/skills/app-store-review-agent/references/rules/metadata/competitor_terms.md +56 -0
- package/skills/app-store-review-agent/references/rules/metadata/subscription_metadata.md +81 -0
- package/skills/app-store-review-agent/references/rules/privacy/privacy_manifest.md +84 -0
- package/skills/app-store-review-agent/references/rules/privacy/unnecessary_data.md +60 -0
- package/skills/app-store-review-agent/references/rules/subscription/misleading_pricing.md +63 -0
- package/skills/app-store-review-agent/references/rules/subscription/missing_tos_pp.md +54 -0
- package/skills/awf-ponytail/SKILL.md +91 -0
- package/skills/awf-ponytail-review/SKILL.md +67 -0
- package/skills/awf-session-restore/SKILL.md +3 -3
- package/skills/brainstorm-agent/SKILL.md +11 -2
- package/skills/brainstorm-agent/templates/brief-template.md +8 -0
- package/skills/claude-planner/SKILL.md +47 -0
- package/skills/code-review/SKILL.md +87 -0
- package/skills/expo-game-development/SKILL.md +163 -0
- package/skills/flutter/LICENSE.txt +202 -0
- package/skills/flutter/SKILL.md +127 -0
- package/skills/flutter-project-creater/LICENSE.txt +202 -0
- package/skills/flutter-project-creater/SKILL.md +106 -0
- package/skills/game-developer/SKILL.md +163 -0
- package/skills/game-developer/references/ecs-patterns.md +501 -0
- package/skills/game-developer/references/multiplayer-networking.md +475 -0
- package/skills/game-developer/references/performance-optimization.md +422 -0
- package/skills/game-developer/references/unity-patterns.md +271 -0
- package/skills/game-developer/references/unreal-cpp.md +352 -0
- package/skills/generate-gui-assets/SKILL.md +305 -0
- package/skills/generate-gui-assets/agents/openai.yaml +4 -0
- package/skills/generate-gui-assets/references/catalog-schema.md +58 -0
- package/skills/generate-gui-assets/references/extraction-techniques.md +21 -0
- package/skills/generate-gui-assets/references/prompt-patterns.md +58 -0
- package/skills/generate-gui-assets/scripts/__pycache__/clean_chroma_edges.cpython-311.pyc +0 -0
- package/skills/generate-gui-assets/scripts/build_gui_contact_sheet.py +51 -0
- package/skills/generate-gui-assets/scripts/clean_chroma_edges.py +262 -0
- package/skills/generate-gui-assets/scripts/copy_approved_icons.py +64 -0
- package/skills/generate-gui-assets/scripts/prepare_gui_asset_run.py +91 -0
- package/skills/generate-gui-assets/scripts/suggest_grid_options.py +63 -0
- package/skills/generate-gui-assets/scripts/validate_gui_catalog.py +50 -0
- package/skills/godot-game-development/SKILL.md +142 -0
- package/skills/hatch-pet/LICENSE.txt +201 -0
- package/skills/hatch-pet/SKILL.md +420 -0
- package/skills/hatch-pet/agents/openai.yaml +4 -0
- package/skills/hatch-pet/references/animation-rows.md +29 -0
- package/skills/hatch-pet/references/codex-pet-contract.md +35 -0
- package/skills/hatch-pet/references/qa-rubric.md +60 -0
- package/skills/hatch-pet/scripts/__pycache__/clean_chroma_edges.cpython-311.pyc +0 -0
- package/skills/hatch-pet/scripts/clean_chroma_edges.py +262 -0
- package/skills/hatch-pet/scripts/compose_atlas.py +150 -0
- package/skills/hatch-pet/scripts/derive_running_left_from_running_right.py +143 -0
- package/skills/hatch-pet/scripts/extract_strip_frames.py +323 -0
- package/skills/hatch-pet/scripts/finalize_pet_run.py +382 -0
- package/skills/hatch-pet/scripts/generate_pet_images.py +287 -0
- package/skills/hatch-pet/scripts/inspect_frames.py +246 -0
- package/skills/hatch-pet/scripts/make_contact_sheet.py +96 -0
- package/skills/hatch-pet/scripts/package_custom_pet.py +108 -0
- package/skills/hatch-pet/scripts/pet_job_status.py +117 -0
- package/skills/hatch-pet/scripts/prepare_pet_run.py +673 -0
- package/skills/hatch-pet/scripts/queue_pet_repairs.py +172 -0
- package/skills/hatch-pet/scripts/record_imagegen_result.py +250 -0
- package/skills/hatch-pet/scripts/render_animation_videos.py +134 -0
- package/skills/hatch-pet/scripts/render_animation_videos.sh +5 -0
- package/skills/hatch-pet/scripts/validate_atlas.py +139 -0
- package/skills/i18n-orchestrator/SKILL.md +37 -0
- package/skills/ios-simulator-skill/SKILL.md +390 -0
- package/skills/ios-simulator-skill/scripts/accessibility_audit.py +300 -0
- package/skills/ios-simulator-skill/scripts/app_launcher.py +326 -0
- package/skills/ios-simulator-skill/scripts/app_state_capture.py +400 -0
- package/skills/ios-simulator-skill/scripts/appearance.py +385 -0
- package/skills/ios-simulator-skill/scripts/build_and_test.py +348 -0
- package/skills/ios-simulator-skill/scripts/clipboard.py +103 -0
- package/skills/ios-simulator-skill/scripts/common/__init__.py +61 -0
- package/skills/ios-simulator-skill/scripts/common/cache_utils.py +289 -0
- package/skills/ios-simulator-skill/scripts/common/device_utils.py +462 -0
- package/skills/ios-simulator-skill/scripts/common/env_config.py +35 -0
- package/skills/ios-simulator-skill/scripts/common/hang_pipeline.py +862 -0
- package/skills/ios-simulator-skill/scripts/common/hang_sessions.py +490 -0
- package/skills/ios-simulator-skill/scripts/common/idb_utils.py +180 -0
- package/skills/ios-simulator-skill/scripts/common/screenshot_utils.py +338 -0
- package/skills/ios-simulator-skill/scripts/container.py +668 -0
- package/skills/ios-simulator-skill/scripts/gesture.py +394 -0
- package/skills/ios-simulator-skill/scripts/hang_watcher.py +1533 -0
- package/skills/ios-simulator-skill/scripts/keyboard.py +391 -0
- package/skills/ios-simulator-skill/scripts/localization_audit.py +483 -0
- package/skills/ios-simulator-skill/scripts/location.py +467 -0
- package/skills/ios-simulator-skill/scripts/log_monitor.py +493 -0
- package/skills/ios-simulator-skill/scripts/model_inspector.py +645 -0
- package/skills/ios-simulator-skill/scripts/navigator.py +461 -0
- package/skills/ios-simulator-skill/scripts/privacy_manager.py +310 -0
- package/skills/ios-simulator-skill/scripts/push_notification.py +240 -0
- package/skills/ios-simulator-skill/scripts/screen_mapper.py +296 -0
- package/skills/ios-simulator-skill/scripts/sim_health_check.sh +245 -0
- package/skills/ios-simulator-skill/scripts/sim_list.py +299 -0
- package/skills/ios-simulator-skill/scripts/simctl_boot.py +312 -0
- package/skills/ios-simulator-skill/scripts/simctl_create.py +316 -0
- package/skills/ios-simulator-skill/scripts/simctl_delete.py +357 -0
- package/skills/ios-simulator-skill/scripts/simctl_erase.py +351 -0
- package/skills/ios-simulator-skill/scripts/simctl_shutdown.py +290 -0
- package/skills/ios-simulator-skill/scripts/simulator_selector.py +375 -0
- package/skills/ios-simulator-skill/scripts/status_bar.py +250 -0
- package/skills/ios-simulator-skill/scripts/test_recorder.py +323 -0
- package/skills/ios-simulator-skill/scripts/visual_diff.py +235 -0
- package/skills/ios-simulator-skill/scripts/xcode/__init__.py +13 -0
- package/skills/ios-simulator-skill/scripts/xcode/builder.py +397 -0
- package/skills/ios-simulator-skill/scripts/xcode/cache.py +204 -0
- package/skills/ios-simulator-skill/scripts/xcode/config.py +178 -0
- package/skills/ios-simulator-skill/scripts/xcode/reporter.py +343 -0
- package/skills/ios-simulator-skill/scripts/xcode/xcresult.py +451 -0
- package/skills/ios-visual-qa-strategist/SKILL.md +111 -0
- package/skills/ios-visual-qa-strategist/agents/openai.yaml +4 -0
- package/skills/ios-visual-qa-strategist/references/ios-tool-selection.md +61 -0
- package/skills/ios-visual-qa-strategist/references/minimal-capture-policy.md +56 -0
- package/skills/ios-visual-qa-strategist/references/visual-reasoning-heuristics.md +53 -0
- package/skills/orchestrator/SKILL.md +0 -20
- package/skills/persistent-storage/SKILL.md +55 -0
- package/skills/short-maker/SKILL.md +23 -0
- package/skills/short-maker/scripts/effects.js +56 -0
- package/skills/short-maker/scripts/shortmaker-bridge.js +332 -0
- package/skills/short-maker/scripts/videomix.js +601 -0
- package/skills/short-maker/templates/hyperframes/cinematic-character.template.html +172 -0
- package/skills/short-maker/templates/hyperframes/index.template.html +194 -0
- package/skills/smali-to-kotlin/SKILL.md +128 -0
- package/skills/smali-to-kotlin/examples/getting-started/tech-stack.md +58 -0
- package/skills/smali-to-kotlin/examples/pipeline/data-ui-parity.md +118 -0
- package/skills/smali-to-kotlin/examples/pipeline/scanner-and-bootstrap.md +106 -0
- package/skills/smali-to-kotlin/library-patterns.md +189 -0
- package/skills/smali-to-kotlin/phase-0-discovery.md +128 -0
- package/skills/smali-to-kotlin/phase-1-architecture.md +166 -0
- package/skills/smali-to-kotlin/phase-2-blueprint-ui.md +347 -0
- package/skills/smali-to-kotlin/phase-2-blueprint.md +228 -0
- package/skills/smali-to-kotlin/phase-3-build.md +248 -0
- package/skills/smali-to-kotlin/phase-3-logic-build.md +268 -0
- package/skills/smali-to-kotlin/smali-reading-guide.md +310 -0
- package/skills/smali-to-kotlin/templates/app-map.md +101 -0
- package/skills/smali-to-kotlin/templates/architecture.md +142 -0
- package/skills/smali-to-kotlin/templates/blueprint.md +145 -0
- package/skills/spec-gate/SKILL.md +6 -2
- package/skills/symphony-enforcer/SKILL.md +8 -0
- package/skills/symphony-enforcer/examples/mindful-stop.md +2 -0
- package/skills/symphony-enforcer/examples/three-phase.md +16 -0
- package/skills/symphony-enforcer/examples/trigger-points.md +7 -1
- package/skills/unity-game-development/SKILL.md +231 -0
- package/skills/verification-gate/SKILL.md +4 -2
- package/skills/video-edit/SKILL.md +36 -0
- package/skills/video-edit/scripts/video_edit.py +324 -0
- package/templates/setup-mapping.json +48 -0
- package/templates/specs/design-template.md +161 -71
- package/templates/specs/requirements-template.md +65 -133
- package/templates/specs/task-spec-template.xml +3 -0
- package/workflows/_uncategorized/critic.md +40 -0
- package/workflows/_uncategorized/git-rebase-flow.md +81 -0
- package/workflows/_uncategorized/image-gen.md +118 -0
- package/workflows/_uncategorized/multi-model-pipeline.md +60 -0
- package/workflows/_uncategorized/pixel-gen.md +86 -0
- package/workflows/_uncategorized/pixel-setup.md +90 -0
- package/workflows/_uncategorized/ponytail-review.md +59 -0
- package/workflows/_uncategorized/reverse-android-build.md +222 -0
- package/workflows/_uncategorized/reverse-android-design.md +139 -0
- package/workflows/_uncategorized/reverse-android-discover.md +150 -0
- package/workflows/_uncategorized/reverse-android-scan.md +158 -0
- package/workflows/_uncategorized/reverse-android.md +143 -0
- package/workflows/_uncategorized/reverse-ios-build.md +240 -0
- package/workflows/_uncategorized/reverse-ios-design.md +112 -0
- package/workflows/_uncategorized/reverse-ios-discover.md +120 -0
- package/workflows/_uncategorized/reverse-ios-scan.md +155 -0
- package/workflows/_uncategorized/reverse-ios.md +152 -0
- package/workflows/_uncategorized/safety-router.md +34 -0
- package/workflows/_uncategorized/teach.md +89 -0
- package/workflows/_uncategorized/verify-ui.md +53 -0
- package/workflows/_uncategorized/visualize-screenshots.md +34 -0
- package/workflows/ads/ads-analyst.md +201 -0
- package/workflows/ads/ads-audit.md +106 -0
- package/workflows/ads/ads-optimize.md +97 -0
- package/workflows/ads/ads-targeting.md +241 -0
- package/workflows/ads/adsExpert.md +160 -0
- package/workflows/ads/smali-ads-config.md +400 -0
- package/workflows/ads/smali-ads-flow.md +331 -0
- package/workflows/ads/smali-ads-interstitial.md +377 -0
- package/workflows/ads/smali-ads-native.md +382 -0
- package/workflows/context/teach.md +89 -0
- package/workflows/gitnexus.md +8 -8
- package/workflows/lifecycle/brainstorm.md +43 -0
- package/workflows/lifecycle/code.md +5 -0
- package/workflows/lifecycle/init.md +23 -5
- package/workflows/lifecycle/multi-model-pipeline.md +60 -0
- package/workflows/quality/ponytail-review.md +59 -0
- package/workflows/roles/critic.md +40 -0
- package/workflows/roles/safety-router.md +34 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
iOS Simulator Listing with Progressive Disclosure
|
|
4
|
+
|
|
5
|
+
Lists available simulators with token-efficient summaries.
|
|
6
|
+
Full details available on demand via cache IDs.
|
|
7
|
+
|
|
8
|
+
Achieves 96% token reduction (57k→2k tokens) for common queries.
|
|
9
|
+
|
|
10
|
+
Usage Examples:
|
|
11
|
+
# Concise summary (default)
|
|
12
|
+
python scripts/sim_list.py
|
|
13
|
+
|
|
14
|
+
# Get full details for cached list
|
|
15
|
+
python scripts/sim_list.py --get-details <cache-id>
|
|
16
|
+
|
|
17
|
+
# Get recommendations
|
|
18
|
+
python scripts/sim_list.py --suggest
|
|
19
|
+
|
|
20
|
+
# Filter by device type
|
|
21
|
+
python scripts/sim_list.py --device-type iPhone
|
|
22
|
+
|
|
23
|
+
Output (default):
|
|
24
|
+
Simulator Summary [cache-sim-20251028-143052]
|
|
25
|
+
├─ Total: 47 devices
|
|
26
|
+
├─ Available: 31
|
|
27
|
+
└─ Booted: 1
|
|
28
|
+
|
|
29
|
+
✓ iPhone 16 Pro (iOS 18.1) [ABC-123...]
|
|
30
|
+
|
|
31
|
+
Use --get-details cache-sim-20251028-143052 for full list
|
|
32
|
+
|
|
33
|
+
Technical Details:
|
|
34
|
+
- Uses xcrun simctl list devices
|
|
35
|
+
- Caches results with 1-hour TTL
|
|
36
|
+
- Reduces output by 96% by default
|
|
37
|
+
- Token efficiency: summary = ~30 tokens, full list = ~1500 tokens
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
import argparse
|
|
41
|
+
import json
|
|
42
|
+
import subprocess
|
|
43
|
+
import sys
|
|
44
|
+
from typing import Any
|
|
45
|
+
|
|
46
|
+
from common import get_cache
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class SimulatorLister:
|
|
50
|
+
"""Lists iOS simulators with progressive disclosure."""
|
|
51
|
+
|
|
52
|
+
def __init__(self):
|
|
53
|
+
"""Initialize lister with cache."""
|
|
54
|
+
self.cache = get_cache()
|
|
55
|
+
|
|
56
|
+
def list_simulators(self) -> dict:
|
|
57
|
+
"""
|
|
58
|
+
Get list of all simulators.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Dict with structure:
|
|
62
|
+
{
|
|
63
|
+
"devices": [...],
|
|
64
|
+
"runtimes": [...],
|
|
65
|
+
"total_devices": int,
|
|
66
|
+
"available_devices": int,
|
|
67
|
+
"booted_devices": [...]
|
|
68
|
+
}
|
|
69
|
+
"""
|
|
70
|
+
try:
|
|
71
|
+
result = subprocess.run(
|
|
72
|
+
["xcrun", "simctl", "list", "devices", "--json"],
|
|
73
|
+
capture_output=True,
|
|
74
|
+
text=True,
|
|
75
|
+
check=True,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
return json.loads(result.stdout)
|
|
79
|
+
except (subprocess.CalledProcessError, json.JSONDecodeError):
|
|
80
|
+
return {"devices": {}, "runtimes": []}
|
|
81
|
+
|
|
82
|
+
def parse_devices(self, sim_data: dict) -> list[dict]:
|
|
83
|
+
"""
|
|
84
|
+
Parse simulator data into flat list.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
List of device dicts with runtime info
|
|
88
|
+
"""
|
|
89
|
+
devices = []
|
|
90
|
+
|
|
91
|
+
devices_by_runtime = sim_data.get("devices", {})
|
|
92
|
+
|
|
93
|
+
for runtime_str, device_list in devices_by_runtime.items():
|
|
94
|
+
# Extract iOS version from runtime string
|
|
95
|
+
# Format: "iOS 18.1", "tvOS 18", etc.
|
|
96
|
+
runtime_name = runtime_str.replace(" Simulator", "").strip()
|
|
97
|
+
|
|
98
|
+
for device in device_list:
|
|
99
|
+
devices.append(
|
|
100
|
+
{
|
|
101
|
+
"name": device.get("name"),
|
|
102
|
+
"udid": device.get("udid"),
|
|
103
|
+
"state": device.get("state"),
|
|
104
|
+
"runtime": runtime_name,
|
|
105
|
+
"is_available": device.get("isAvailable", False),
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
return devices
|
|
110
|
+
|
|
111
|
+
def get_concise_summary(self, devices: list[dict]) -> dict:
|
|
112
|
+
"""
|
|
113
|
+
Generate concise summary with cache ID.
|
|
114
|
+
|
|
115
|
+
Returns 96% fewer tokens than full list.
|
|
116
|
+
"""
|
|
117
|
+
booted = [d for d in devices if d["state"] == "Booted"]
|
|
118
|
+
available = [d for d in devices if d["is_available"]]
|
|
119
|
+
iphone = [d for d in available if "iPhone" in d["name"]]
|
|
120
|
+
|
|
121
|
+
# Cache full list for later retrieval
|
|
122
|
+
cache_id = self.cache.save(
|
|
123
|
+
{
|
|
124
|
+
"devices": devices,
|
|
125
|
+
"timestamp": __import__("datetime").datetime.now().isoformat(),
|
|
126
|
+
},
|
|
127
|
+
"simulator-list",
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
"cache_id": cache_id,
|
|
132
|
+
"summary": {
|
|
133
|
+
"total_devices": len(devices),
|
|
134
|
+
"available_devices": len(available),
|
|
135
|
+
"booted_devices": len(booted),
|
|
136
|
+
},
|
|
137
|
+
"quick_access": {
|
|
138
|
+
"booted": booted[:3] if booted else [],
|
|
139
|
+
"recommended_iphone": iphone[:3] if iphone else [],
|
|
140
|
+
},
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
def get_full_list(
|
|
144
|
+
self,
|
|
145
|
+
cache_id: str,
|
|
146
|
+
device_type: str | None = None,
|
|
147
|
+
runtime: str | None = None,
|
|
148
|
+
) -> list[dict] | None:
|
|
149
|
+
"""
|
|
150
|
+
Retrieve full simulator list from cache.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
cache_id: Cache ID from concise summary
|
|
154
|
+
device_type: Filter by type (iPhone, iPad, etc.)
|
|
155
|
+
runtime: Filter by iOS version
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
List of devices matching filters
|
|
159
|
+
"""
|
|
160
|
+
data = self.cache.get(cache_id)
|
|
161
|
+
if not data:
|
|
162
|
+
return None
|
|
163
|
+
|
|
164
|
+
devices = data.get("devices", [])
|
|
165
|
+
|
|
166
|
+
# Apply filters
|
|
167
|
+
if device_type:
|
|
168
|
+
devices = [d for d in devices if device_type in d["name"]]
|
|
169
|
+
if runtime:
|
|
170
|
+
devices = [d for d in devices if runtime.lower() in d["runtime"].lower()]
|
|
171
|
+
|
|
172
|
+
return devices
|
|
173
|
+
|
|
174
|
+
def suggest_simulators(self, limit: int = 4) -> list[dict]:
|
|
175
|
+
"""
|
|
176
|
+
Get simulator recommendations.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
List of recommended simulators (best candidates for building)
|
|
180
|
+
"""
|
|
181
|
+
all_sims = self.list_simulators()
|
|
182
|
+
devices = self.parse_devices(all_sims)
|
|
183
|
+
|
|
184
|
+
# Score devices for recommendations
|
|
185
|
+
scored = []
|
|
186
|
+
for device in devices:
|
|
187
|
+
score = 0
|
|
188
|
+
|
|
189
|
+
# Prefer booted
|
|
190
|
+
if device["state"] == "Booted":
|
|
191
|
+
score += 10
|
|
192
|
+
# Prefer available
|
|
193
|
+
if device["is_available"]:
|
|
194
|
+
score += 5
|
|
195
|
+
# Prefer recent iOS versions
|
|
196
|
+
ios_version = device["runtime"]
|
|
197
|
+
if "18" in ios_version:
|
|
198
|
+
score += 3
|
|
199
|
+
elif "17" in ios_version:
|
|
200
|
+
score += 2
|
|
201
|
+
# Prefer iPhones over other types
|
|
202
|
+
if "iPhone" in device["name"]:
|
|
203
|
+
score += 1
|
|
204
|
+
|
|
205
|
+
scored.append({"device": device, "score": score})
|
|
206
|
+
|
|
207
|
+
# Sort by score and return top N
|
|
208
|
+
scored.sort(key=lambda x: x["score"], reverse=True)
|
|
209
|
+
return [s["device"] for s in scored[:limit]]
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def format_device(device: dict) -> str:
|
|
213
|
+
"""Format device for display."""
|
|
214
|
+
state_icon = "✓" if device["state"] == "Booted" else " "
|
|
215
|
+
avail_icon = "●" if device["is_available"] else "○"
|
|
216
|
+
name = device["name"]
|
|
217
|
+
runtime = device["runtime"]
|
|
218
|
+
udid_short = device["udid"][:8] + "..."
|
|
219
|
+
return f"{state_icon} {avail_icon} {name} ({runtime}) [{udid_short}]"
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def main():
|
|
223
|
+
"""Main entry point."""
|
|
224
|
+
parser = argparse.ArgumentParser(description="List iOS simulators with progressive disclosure")
|
|
225
|
+
parser.add_argument(
|
|
226
|
+
"--get-details",
|
|
227
|
+
metavar="CACHE_ID",
|
|
228
|
+
help="Get full details for cached simulator list",
|
|
229
|
+
)
|
|
230
|
+
parser.add_argument("--suggest", action="store_true", help="Get simulator recommendations")
|
|
231
|
+
parser.add_argument(
|
|
232
|
+
"--device-type",
|
|
233
|
+
help="Filter by device type (iPhone, iPad, Apple Watch, etc.)",
|
|
234
|
+
)
|
|
235
|
+
parser.add_argument("--runtime", help="Filter by iOS version (e.g., iOS-18, iOS-17)")
|
|
236
|
+
parser.add_argument("--json", action="store_true", help="Output as JSON")
|
|
237
|
+
|
|
238
|
+
args = parser.parse_args()
|
|
239
|
+
|
|
240
|
+
lister = SimulatorLister()
|
|
241
|
+
|
|
242
|
+
# Get full list with details
|
|
243
|
+
if args.get_details:
|
|
244
|
+
devices = lister.get_full_list(
|
|
245
|
+
args.get_details, device_type=args.device_type, runtime=args.runtime
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
if devices is None:
|
|
249
|
+
print(f"Error: Cache ID not found or expired: {args.get_details}")
|
|
250
|
+
sys.exit(1)
|
|
251
|
+
|
|
252
|
+
if args.json:
|
|
253
|
+
print(json.dumps(devices, indent=2))
|
|
254
|
+
else:
|
|
255
|
+
print(f"Simulators ({len(devices)}):\n")
|
|
256
|
+
for device in devices:
|
|
257
|
+
print(f" {format_device(device)}")
|
|
258
|
+
|
|
259
|
+
# Get recommendations
|
|
260
|
+
elif args.suggest:
|
|
261
|
+
suggestions = lister.suggest_simulators()
|
|
262
|
+
|
|
263
|
+
if args.json:
|
|
264
|
+
print(json.dumps(suggestions, indent=2))
|
|
265
|
+
else:
|
|
266
|
+
print("Recommended Simulators:\n")
|
|
267
|
+
for i, device in enumerate(suggestions, 1):
|
|
268
|
+
print(f"{i}. {format_device(device)}")
|
|
269
|
+
|
|
270
|
+
# Default: concise summary
|
|
271
|
+
else:
|
|
272
|
+
all_sims = lister.list_simulators()
|
|
273
|
+
devices = lister.parse_devices(all_sims)
|
|
274
|
+
summary = lister.get_concise_summary(devices)
|
|
275
|
+
|
|
276
|
+
if args.json:
|
|
277
|
+
print(json.dumps(summary, indent=2))
|
|
278
|
+
else:
|
|
279
|
+
# Human-readable concise output
|
|
280
|
+
cache_id = summary["cache_id"]
|
|
281
|
+
s = summary["summary"]
|
|
282
|
+
q = summary["quick_access"]
|
|
283
|
+
|
|
284
|
+
print(f"Simulator Summary [{cache_id}]")
|
|
285
|
+
print(f"├─ Total: {s['total_devices']} devices")
|
|
286
|
+
print(f"├─ Available: {s['available_devices']}")
|
|
287
|
+
print(f"└─ Booted: {s['booted_devices']}")
|
|
288
|
+
|
|
289
|
+
if q["booted"]:
|
|
290
|
+
print()
|
|
291
|
+
for device in q["booted"]:
|
|
292
|
+
print(f" {format_device(device)}")
|
|
293
|
+
|
|
294
|
+
print()
|
|
295
|
+
print(f"Use --get-details {cache_id} for full list")
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
if __name__ == "__main__":
|
|
299
|
+
main()
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Boot iOS simulators and wait for readiness.
|
|
4
|
+
|
|
5
|
+
This script boots one or more simulators and optionally waits for them to reach
|
|
6
|
+
a ready state. It measures boot time and provides progress feedback.
|
|
7
|
+
|
|
8
|
+
Key features:
|
|
9
|
+
- Boot by UDID or device name
|
|
10
|
+
- Wait for device readiness with configurable timeout
|
|
11
|
+
- Measure boot performance
|
|
12
|
+
- Batch boot operations (boot all, boot by type)
|
|
13
|
+
- Progress reporting for CI/CD pipelines
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import argparse
|
|
17
|
+
import subprocess
|
|
18
|
+
import sys
|
|
19
|
+
import time
|
|
20
|
+
|
|
21
|
+
from common.device_utils import (
|
|
22
|
+
get_booted_device_udid,
|
|
23
|
+
list_simulators,
|
|
24
|
+
resolve_device_identifier,
|
|
25
|
+
)
|
|
26
|
+
from common.env_config import env_float, env_int
|
|
27
|
+
|
|
28
|
+
DEFAULT_BOOT_TIMEOUT = env_int("IOS_SIM_BOOT_TIMEOUT", 300)
|
|
29
|
+
BOOT_SUBPROCESS_TIMEOUT = env_int("IOS_SIM_BOOT_SUBPROCESS_TIMEOUT", 60)
|
|
30
|
+
POLL_INTERVAL_SECONDS = env_float("IOS_SIM_POLL_INTERVAL", 0.5, min_value=0.05)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class SimulatorBooter:
|
|
34
|
+
"""Boot iOS simulators with optional readiness waiting."""
|
|
35
|
+
|
|
36
|
+
def __init__(self, udid: str | None = None):
|
|
37
|
+
"""Initialize booter with optional device UDID."""
|
|
38
|
+
self.udid = udid
|
|
39
|
+
|
|
40
|
+
def boot(
|
|
41
|
+
self, wait_ready: bool = False, timeout_seconds: int = DEFAULT_BOOT_TIMEOUT
|
|
42
|
+
) -> tuple[bool, str]:
|
|
43
|
+
"""
|
|
44
|
+
Boot simulator and optionally wait for readiness.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
wait_ready: Wait for device to be ready before returning
|
|
48
|
+
timeout_seconds: Maximum seconds to wait for readiness
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
(success, message) tuple
|
|
52
|
+
"""
|
|
53
|
+
if not self.udid:
|
|
54
|
+
return False, "Error: Device UDID not specified"
|
|
55
|
+
|
|
56
|
+
start_time = time.time()
|
|
57
|
+
|
|
58
|
+
# Check if already booted
|
|
59
|
+
try:
|
|
60
|
+
booted = get_booted_device_udid()
|
|
61
|
+
if booted == self.udid:
|
|
62
|
+
elapsed = time.time() - start_time
|
|
63
|
+
return True, (f"Device already booted: {self.udid} " f"[checked in {elapsed:.1f}s]")
|
|
64
|
+
except RuntimeError:
|
|
65
|
+
pass # No booted device, proceed with boot
|
|
66
|
+
|
|
67
|
+
# Execute boot command
|
|
68
|
+
try:
|
|
69
|
+
cmd = ["xcrun", "simctl", "boot", self.udid]
|
|
70
|
+
result = subprocess.run(
|
|
71
|
+
cmd,
|
|
72
|
+
check=False,
|
|
73
|
+
capture_output=True,
|
|
74
|
+
text=True,
|
|
75
|
+
timeout=BOOT_SUBPROCESS_TIMEOUT,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
if result.returncode != 0:
|
|
79
|
+
error = result.stderr.strip()
|
|
80
|
+
return False, f"Boot failed: {error}"
|
|
81
|
+
except subprocess.TimeoutExpired:
|
|
82
|
+
return False, "Boot command timed out"
|
|
83
|
+
except Exception as e:
|
|
84
|
+
return False, f"Boot error: {e}"
|
|
85
|
+
|
|
86
|
+
# Optionally wait for readiness
|
|
87
|
+
if wait_ready:
|
|
88
|
+
ready, wait_message = self._wait_for_ready(timeout_seconds)
|
|
89
|
+
elapsed = time.time() - start_time
|
|
90
|
+
if ready:
|
|
91
|
+
return True, (f"Device booted and ready: {self.udid} " f"[{elapsed:.1f}s total]")
|
|
92
|
+
return False, wait_message
|
|
93
|
+
|
|
94
|
+
elapsed = time.time() - start_time
|
|
95
|
+
return True, (
|
|
96
|
+
f"Device booted: {self.udid} [boot in {elapsed:.1f}s] "
|
|
97
|
+
"(use --wait-ready to wait for availability)"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def _wait_for_ready(self, timeout_seconds: int = DEFAULT_BOOT_TIMEOUT) -> tuple[bool, str]:
|
|
101
|
+
"""
|
|
102
|
+
Wait for device to reach ready state.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
timeout_seconds: Maximum seconds to wait
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
(success, message) tuple
|
|
109
|
+
"""
|
|
110
|
+
start_time = time.time()
|
|
111
|
+
poll_interval = POLL_INTERVAL_SECONDS
|
|
112
|
+
checks = 0
|
|
113
|
+
|
|
114
|
+
while time.time() - start_time < timeout_seconds:
|
|
115
|
+
try:
|
|
116
|
+
checks += 1
|
|
117
|
+
# Check if device responds to simctl commands
|
|
118
|
+
result = subprocess.run(
|
|
119
|
+
["xcrun", "simctl", "spawn", self.udid, "launchctl", "list"],
|
|
120
|
+
check=False,
|
|
121
|
+
capture_output=True,
|
|
122
|
+
text=True,
|
|
123
|
+
timeout=5,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
if result.returncode == 0:
|
|
127
|
+
elapsed = time.time() - start_time
|
|
128
|
+
return True, (
|
|
129
|
+
f"Device ready: {self.udid} " f"[{elapsed:.1f}s, {checks} checks]"
|
|
130
|
+
)
|
|
131
|
+
except (subprocess.TimeoutExpired, RuntimeError):
|
|
132
|
+
pass # Not ready yet
|
|
133
|
+
|
|
134
|
+
time.sleep(poll_interval)
|
|
135
|
+
|
|
136
|
+
elapsed = time.time() - start_time
|
|
137
|
+
return False, (
|
|
138
|
+
f"Boot timeout: Device did not reach ready state "
|
|
139
|
+
f"within {elapsed:.1f}s ({checks} checks)"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
@staticmethod
|
|
143
|
+
def boot_all() -> tuple[int, int]:
|
|
144
|
+
"""
|
|
145
|
+
Boot all available simulators.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
(succeeded, failed) tuple with counts
|
|
149
|
+
"""
|
|
150
|
+
simulators = list_simulators(state="available")
|
|
151
|
+
succeeded = 0
|
|
152
|
+
failed = 0
|
|
153
|
+
|
|
154
|
+
for sim in simulators:
|
|
155
|
+
booter = SimulatorBooter(udid=sim["udid"])
|
|
156
|
+
success, _message = booter.boot(wait_ready=False)
|
|
157
|
+
if success:
|
|
158
|
+
succeeded += 1
|
|
159
|
+
else:
|
|
160
|
+
failed += 1
|
|
161
|
+
|
|
162
|
+
return succeeded, failed
|
|
163
|
+
|
|
164
|
+
@staticmethod
|
|
165
|
+
def boot_by_type(device_type: str) -> tuple[int, int]:
|
|
166
|
+
"""
|
|
167
|
+
Boot all simulators of a specific type.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
device_type: Device type filter (e.g., "iPhone", "iPad")
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
(succeeded, failed) tuple with counts
|
|
174
|
+
"""
|
|
175
|
+
simulators = list_simulators(state="available")
|
|
176
|
+
succeeded = 0
|
|
177
|
+
failed = 0
|
|
178
|
+
|
|
179
|
+
for sim in simulators:
|
|
180
|
+
if device_type.lower() in sim["name"].lower():
|
|
181
|
+
booter = SimulatorBooter(udid=sim["udid"])
|
|
182
|
+
success, _message = booter.boot(wait_ready=False)
|
|
183
|
+
if success:
|
|
184
|
+
succeeded += 1
|
|
185
|
+
else:
|
|
186
|
+
failed += 1
|
|
187
|
+
|
|
188
|
+
return succeeded, failed
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def main():
|
|
192
|
+
"""Main entry point."""
|
|
193
|
+
parser = argparse.ArgumentParser(description="Boot iOS simulators and wait for readiness")
|
|
194
|
+
parser.add_argument(
|
|
195
|
+
"--udid",
|
|
196
|
+
help="Device UDID or name (required unless using --all or --type)",
|
|
197
|
+
)
|
|
198
|
+
parser.add_argument(
|
|
199
|
+
"--name",
|
|
200
|
+
help="Device name (alternative to --udid)",
|
|
201
|
+
)
|
|
202
|
+
parser.add_argument(
|
|
203
|
+
"--wait-ready",
|
|
204
|
+
action="store_true",
|
|
205
|
+
help="Wait for device to reach ready state",
|
|
206
|
+
)
|
|
207
|
+
parser.add_argument(
|
|
208
|
+
"--timeout",
|
|
209
|
+
type=int,
|
|
210
|
+
default=DEFAULT_BOOT_TIMEOUT,
|
|
211
|
+
help=(
|
|
212
|
+
f"Timeout for --wait-ready in seconds "
|
|
213
|
+
f"(default: {DEFAULT_BOOT_TIMEOUT}, override via IOS_SIM_BOOT_TIMEOUT)"
|
|
214
|
+
),
|
|
215
|
+
)
|
|
216
|
+
parser.add_argument(
|
|
217
|
+
"--all",
|
|
218
|
+
action="store_true",
|
|
219
|
+
help="Boot all available simulators",
|
|
220
|
+
)
|
|
221
|
+
parser.add_argument(
|
|
222
|
+
"--type",
|
|
223
|
+
help="Boot all simulators of a specific type (e.g., iPhone, iPad)",
|
|
224
|
+
)
|
|
225
|
+
parser.add_argument(
|
|
226
|
+
"--json",
|
|
227
|
+
action="store_true",
|
|
228
|
+
help="Output as JSON",
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
args = parser.parse_args()
|
|
232
|
+
|
|
233
|
+
# Handle batch operations
|
|
234
|
+
if args.all:
|
|
235
|
+
succeeded, failed = SimulatorBooter.boot_all()
|
|
236
|
+
if args.json:
|
|
237
|
+
import json
|
|
238
|
+
|
|
239
|
+
print(
|
|
240
|
+
json.dumps(
|
|
241
|
+
{
|
|
242
|
+
"action": "boot_all",
|
|
243
|
+
"succeeded": succeeded,
|
|
244
|
+
"failed": failed,
|
|
245
|
+
"total": succeeded + failed,
|
|
246
|
+
}
|
|
247
|
+
)
|
|
248
|
+
)
|
|
249
|
+
else:
|
|
250
|
+
total = succeeded + failed
|
|
251
|
+
print(f"Boot summary: {succeeded}/{total} succeeded, " f"{failed} failed")
|
|
252
|
+
sys.exit(0 if failed == 0 else 1)
|
|
253
|
+
|
|
254
|
+
if args.type:
|
|
255
|
+
succeeded, failed = SimulatorBooter.boot_by_type(args.type)
|
|
256
|
+
if args.json:
|
|
257
|
+
import json
|
|
258
|
+
|
|
259
|
+
print(
|
|
260
|
+
json.dumps(
|
|
261
|
+
{
|
|
262
|
+
"action": "boot_by_type",
|
|
263
|
+
"type": args.type,
|
|
264
|
+
"succeeded": succeeded,
|
|
265
|
+
"failed": failed,
|
|
266
|
+
"total": succeeded + failed,
|
|
267
|
+
}
|
|
268
|
+
)
|
|
269
|
+
)
|
|
270
|
+
else:
|
|
271
|
+
total = succeeded + failed
|
|
272
|
+
print(f"Boot {args.type} summary: {succeeded}/{total} succeeded, " f"{failed} failed")
|
|
273
|
+
sys.exit(0 if failed == 0 else 1)
|
|
274
|
+
|
|
275
|
+
# Resolve device identifier
|
|
276
|
+
device_id = args.udid or args.name
|
|
277
|
+
if not device_id:
|
|
278
|
+
print("Error: Specify --udid, --name, --all, or --type", file=sys.stderr)
|
|
279
|
+
sys.exit(1)
|
|
280
|
+
|
|
281
|
+
try:
|
|
282
|
+
udid = resolve_device_identifier(device_id)
|
|
283
|
+
except RuntimeError as e:
|
|
284
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
285
|
+
sys.exit(1)
|
|
286
|
+
|
|
287
|
+
# Boot device
|
|
288
|
+
booter = SimulatorBooter(udid=udid)
|
|
289
|
+
success, message = booter.boot(wait_ready=args.wait_ready, timeout_seconds=args.timeout)
|
|
290
|
+
|
|
291
|
+
if args.json:
|
|
292
|
+
import json
|
|
293
|
+
|
|
294
|
+
print(
|
|
295
|
+
json.dumps(
|
|
296
|
+
{
|
|
297
|
+
"action": "boot",
|
|
298
|
+
"device_id": device_id,
|
|
299
|
+
"udid": udid,
|
|
300
|
+
"success": success,
|
|
301
|
+
"message": message,
|
|
302
|
+
}
|
|
303
|
+
)
|
|
304
|
+
)
|
|
305
|
+
else:
|
|
306
|
+
print(message)
|
|
307
|
+
|
|
308
|
+
sys.exit(0 if success else 1)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
if __name__ == "__main__":
|
|
312
|
+
main()
|