@leejungkiin/awkit 1.7.1 → 1.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +35 -2
- 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/video-edit/SKILL.md +36 -0
- package/skills/video-edit/scripts/video_edit.py +324 -0
- package/templates/project-identity/android.json +2 -2
- package/templates/project-identity/backend-nestjs.json +2 -2
- package/templates/project-identity/expo.json +2 -2
- package/templates/project-identity/ios.json +2 -2
- package/templates/project-identity/web-nextjs.json +2 -2
- 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,316 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Create iOS simulators dynamically.
|
|
4
|
+
|
|
5
|
+
This script creates new simulators with specified device type and iOS version.
|
|
6
|
+
Useful for CI/CD pipelines that need on-demand test device provisioning.
|
|
7
|
+
|
|
8
|
+
Key features:
|
|
9
|
+
- Create by device type (iPhone 16 Pro, iPad Air, etc.)
|
|
10
|
+
- Specify iOS version (17.0, 18.0, etc.)
|
|
11
|
+
- Custom device naming
|
|
12
|
+
- Return newly created device UDID
|
|
13
|
+
- List available device types and runtimes
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import argparse
|
|
17
|
+
import subprocess
|
|
18
|
+
import sys
|
|
19
|
+
from typing import Optional
|
|
20
|
+
|
|
21
|
+
from common.device_utils import list_simulators
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SimulatorCreator:
|
|
25
|
+
"""Create iOS simulators with specified configurations."""
|
|
26
|
+
|
|
27
|
+
def __init__(self):
|
|
28
|
+
"""Initialize simulator creator."""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
def create(
|
|
32
|
+
self,
|
|
33
|
+
device_type: str,
|
|
34
|
+
ios_version: str | None = None,
|
|
35
|
+
custom_name: str | None = None,
|
|
36
|
+
) -> tuple[bool, str, str | None]:
|
|
37
|
+
"""
|
|
38
|
+
Create new iOS simulator.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
device_type: Device type (e.g., "iPhone 16 Pro", "iPad Air")
|
|
42
|
+
ios_version: iOS version (e.g., "18.0"). If None, uses latest.
|
|
43
|
+
custom_name: Custom device name. If None, uses default.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
(success, message, new_udid) tuple
|
|
47
|
+
"""
|
|
48
|
+
# Get available device types and runtimes
|
|
49
|
+
available_types = self._get_device_types()
|
|
50
|
+
if not available_types:
|
|
51
|
+
return False, "Failed to get available device types", None
|
|
52
|
+
|
|
53
|
+
# Normalize device type
|
|
54
|
+
device_type_id = None
|
|
55
|
+
for dt in available_types:
|
|
56
|
+
if device_type.lower() in dt["name"].lower():
|
|
57
|
+
device_type_id = dt["identifier"]
|
|
58
|
+
break
|
|
59
|
+
|
|
60
|
+
if not device_type_id:
|
|
61
|
+
return (
|
|
62
|
+
False,
|
|
63
|
+
f"Device type '{device_type}' not found. "
|
|
64
|
+
f"Use --list-devices for available types.",
|
|
65
|
+
None,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Get available runtimes
|
|
69
|
+
available_runtimes = self._get_runtimes()
|
|
70
|
+
if not available_runtimes:
|
|
71
|
+
return False, "Failed to get available runtimes", None
|
|
72
|
+
|
|
73
|
+
# Resolve iOS version
|
|
74
|
+
runtime_id = None
|
|
75
|
+
if ios_version:
|
|
76
|
+
for rt in available_runtimes:
|
|
77
|
+
if ios_version in rt["name"]:
|
|
78
|
+
runtime_id = rt["identifier"]
|
|
79
|
+
break
|
|
80
|
+
|
|
81
|
+
if not runtime_id:
|
|
82
|
+
return (
|
|
83
|
+
False,
|
|
84
|
+
f"iOS version '{ios_version}' not found. "
|
|
85
|
+
f"Use --list-runtimes for available versions.",
|
|
86
|
+
None,
|
|
87
|
+
)
|
|
88
|
+
# Use latest runtime
|
|
89
|
+
elif available_runtimes:
|
|
90
|
+
runtime_id = available_runtimes[-1]["identifier"]
|
|
91
|
+
|
|
92
|
+
if not runtime_id:
|
|
93
|
+
return False, "No iOS runtime available", None
|
|
94
|
+
|
|
95
|
+
# Create device
|
|
96
|
+
try:
|
|
97
|
+
# Build device name
|
|
98
|
+
device_name = (
|
|
99
|
+
custom_name or f"{device_type_id.split('.')[-1]}-{ios_version or 'latest'}"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
cmd = [
|
|
103
|
+
"xcrun",
|
|
104
|
+
"simctl",
|
|
105
|
+
"create",
|
|
106
|
+
device_name,
|
|
107
|
+
device_type_id,
|
|
108
|
+
runtime_id,
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
result = subprocess.run(cmd, check=False, capture_output=True, text=True, timeout=60)
|
|
112
|
+
|
|
113
|
+
if result.returncode != 0:
|
|
114
|
+
error = result.stderr.strip() or result.stdout.strip()
|
|
115
|
+
return False, f"Creation failed: {error}", None
|
|
116
|
+
|
|
117
|
+
# Extract UDID from output
|
|
118
|
+
new_udid = result.stdout.strip()
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
True,
|
|
122
|
+
f"Device created: {device_name} ({device_type}) iOS {ios_version or 'latest'} "
|
|
123
|
+
f"UDID: {new_udid}",
|
|
124
|
+
new_udid,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
except subprocess.TimeoutExpired:
|
|
128
|
+
return False, "Creation command timed out", None
|
|
129
|
+
except Exception as e:
|
|
130
|
+
return False, f"Creation error: {e}", None
|
|
131
|
+
|
|
132
|
+
@staticmethod
|
|
133
|
+
def _get_device_types() -> list[dict]:
|
|
134
|
+
"""
|
|
135
|
+
Get available device types.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
List of device type dicts with "name" and "identifier" keys
|
|
139
|
+
"""
|
|
140
|
+
try:
|
|
141
|
+
cmd = ["xcrun", "simctl", "list", "devicetypes", "-j"]
|
|
142
|
+
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
|
143
|
+
|
|
144
|
+
import json
|
|
145
|
+
|
|
146
|
+
data = json.loads(result.stdout)
|
|
147
|
+
devices = []
|
|
148
|
+
|
|
149
|
+
for device in data.get("devicetypes", []):
|
|
150
|
+
devices.append(
|
|
151
|
+
{
|
|
152
|
+
"name": device.get("name", ""),
|
|
153
|
+
"identifier": device.get("identifier", ""),
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
return devices
|
|
158
|
+
except Exception:
|
|
159
|
+
return []
|
|
160
|
+
|
|
161
|
+
@staticmethod
|
|
162
|
+
def _get_runtimes() -> list[dict]:
|
|
163
|
+
"""
|
|
164
|
+
Get available iOS runtimes.
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
List of runtime dicts with "name" and "identifier" keys
|
|
168
|
+
"""
|
|
169
|
+
try:
|
|
170
|
+
cmd = ["xcrun", "simctl", "list", "runtimes", "-j"]
|
|
171
|
+
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
|
172
|
+
|
|
173
|
+
import json
|
|
174
|
+
|
|
175
|
+
data = json.loads(result.stdout)
|
|
176
|
+
runtimes = []
|
|
177
|
+
|
|
178
|
+
for runtime in data.get("runtimes", []):
|
|
179
|
+
# Only include iOS runtimes (skip watchOS, tvOS, etc.)
|
|
180
|
+
identifier = runtime.get("identifier", "")
|
|
181
|
+
if "iOS" in identifier or "iOS" in runtime.get("name", ""):
|
|
182
|
+
runtimes.append(
|
|
183
|
+
{
|
|
184
|
+
"name": runtime.get("name", ""),
|
|
185
|
+
"identifier": runtime.get("identifier", ""),
|
|
186
|
+
}
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# Sort by version number (latest first)
|
|
190
|
+
runtimes.sort(key=lambda r: r.get("identifier", ""), reverse=True)
|
|
191
|
+
|
|
192
|
+
return runtimes
|
|
193
|
+
except Exception:
|
|
194
|
+
return []
|
|
195
|
+
|
|
196
|
+
@staticmethod
|
|
197
|
+
def list_device_types() -> list[dict]:
|
|
198
|
+
"""
|
|
199
|
+
List all available device types.
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
List of device types with name and identifier
|
|
203
|
+
"""
|
|
204
|
+
return SimulatorCreator._get_device_types()
|
|
205
|
+
|
|
206
|
+
@staticmethod
|
|
207
|
+
def list_runtimes() -> list[dict]:
|
|
208
|
+
"""
|
|
209
|
+
List all available iOS runtimes.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
List of runtimes with name and identifier
|
|
213
|
+
"""
|
|
214
|
+
return SimulatorCreator._get_runtimes()
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def main():
|
|
218
|
+
"""Main entry point."""
|
|
219
|
+
parser = argparse.ArgumentParser(description="Create iOS simulators dynamically")
|
|
220
|
+
parser.add_argument(
|
|
221
|
+
"--device",
|
|
222
|
+
required=False,
|
|
223
|
+
help="Device type (e.g., 'iPhone 16 Pro', 'iPad Air')",
|
|
224
|
+
)
|
|
225
|
+
parser.add_argument(
|
|
226
|
+
"--runtime",
|
|
227
|
+
help="iOS version (e.g., '18.0', '17.0'). Defaults to latest.",
|
|
228
|
+
)
|
|
229
|
+
parser.add_argument(
|
|
230
|
+
"--name",
|
|
231
|
+
help="Custom device name. Defaults to auto-generated.",
|
|
232
|
+
)
|
|
233
|
+
parser.add_argument(
|
|
234
|
+
"--list-devices",
|
|
235
|
+
action="store_true",
|
|
236
|
+
help="List all available device types",
|
|
237
|
+
)
|
|
238
|
+
parser.add_argument(
|
|
239
|
+
"--list-runtimes",
|
|
240
|
+
action="store_true",
|
|
241
|
+
help="List all available iOS runtimes",
|
|
242
|
+
)
|
|
243
|
+
parser.add_argument(
|
|
244
|
+
"--json",
|
|
245
|
+
action="store_true",
|
|
246
|
+
help="Output as JSON",
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
args = parser.parse_args()
|
|
250
|
+
|
|
251
|
+
creator = SimulatorCreator()
|
|
252
|
+
|
|
253
|
+
# Handle info queries
|
|
254
|
+
if args.list_devices:
|
|
255
|
+
devices = creator.list_device_types()
|
|
256
|
+
if args.json:
|
|
257
|
+
import json
|
|
258
|
+
|
|
259
|
+
print(json.dumps({"devices": devices}))
|
|
260
|
+
else:
|
|
261
|
+
print(f"Available device types ({len(devices)}):")
|
|
262
|
+
for dev in devices[:20]: # Show first 20
|
|
263
|
+
print(f" - {dev['name']}")
|
|
264
|
+
if len(devices) > 20:
|
|
265
|
+
print(f" ... and {len(devices) - 20} more")
|
|
266
|
+
sys.exit(0)
|
|
267
|
+
|
|
268
|
+
if args.list_runtimes:
|
|
269
|
+
runtimes = creator.list_runtimes()
|
|
270
|
+
if args.json:
|
|
271
|
+
import json
|
|
272
|
+
|
|
273
|
+
print(json.dumps({"runtimes": runtimes}))
|
|
274
|
+
else:
|
|
275
|
+
print(f"Available iOS runtimes ({len(runtimes)}):")
|
|
276
|
+
for rt in runtimes:
|
|
277
|
+
print(f" - {rt['name']}")
|
|
278
|
+
sys.exit(0)
|
|
279
|
+
|
|
280
|
+
# Create device
|
|
281
|
+
if not args.device:
|
|
282
|
+
print(
|
|
283
|
+
"Error: Specify --device, --list-devices, or --list-runtimes",
|
|
284
|
+
file=sys.stderr,
|
|
285
|
+
)
|
|
286
|
+
sys.exit(1)
|
|
287
|
+
|
|
288
|
+
success, message, new_udid = creator.create(
|
|
289
|
+
device_type=args.device,
|
|
290
|
+
ios_version=args.runtime,
|
|
291
|
+
custom_name=args.name,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
if args.json:
|
|
295
|
+
import json
|
|
296
|
+
|
|
297
|
+
print(
|
|
298
|
+
json.dumps(
|
|
299
|
+
{
|
|
300
|
+
"action": "create",
|
|
301
|
+
"device_type": args.device,
|
|
302
|
+
"runtime": args.runtime,
|
|
303
|
+
"success": success,
|
|
304
|
+
"message": message,
|
|
305
|
+
"new_udid": new_udid,
|
|
306
|
+
}
|
|
307
|
+
)
|
|
308
|
+
)
|
|
309
|
+
else:
|
|
310
|
+
print(message)
|
|
311
|
+
|
|
312
|
+
sys.exit(0 if success else 1)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
if __name__ == "__main__":
|
|
316
|
+
main()
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Delete iOS simulators permanently.
|
|
4
|
+
|
|
5
|
+
This script permanently removes simulators and frees disk space.
|
|
6
|
+
Includes safety confirmation to prevent accidental deletion.
|
|
7
|
+
|
|
8
|
+
Key features:
|
|
9
|
+
- Delete by UDID or device name
|
|
10
|
+
- Confirmation required for safety
|
|
11
|
+
- Batch delete operations
|
|
12
|
+
- Report freed disk space estimate
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import argparse
|
|
16
|
+
import subprocess
|
|
17
|
+
import sys
|
|
18
|
+
from typing import Optional
|
|
19
|
+
|
|
20
|
+
from common.device_utils import (
|
|
21
|
+
list_simulators,
|
|
22
|
+
resolve_device_identifier,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class SimulatorDeleter:
|
|
27
|
+
"""Delete iOS simulators with safety confirmation."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, udid: str | None = None):
|
|
30
|
+
"""Initialize with optional device UDID."""
|
|
31
|
+
self.udid = udid
|
|
32
|
+
|
|
33
|
+
def delete(self, confirm: bool = False) -> tuple[bool, str]:
|
|
34
|
+
"""
|
|
35
|
+
Delete simulator permanently.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
confirm: Skip confirmation prompt (for batch operations)
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
(success, message) tuple
|
|
42
|
+
"""
|
|
43
|
+
if not self.udid:
|
|
44
|
+
return False, "Error: Device UDID not specified"
|
|
45
|
+
|
|
46
|
+
# Safety confirmation
|
|
47
|
+
if not confirm:
|
|
48
|
+
try:
|
|
49
|
+
response = input(
|
|
50
|
+
f"Permanently delete simulator {self.udid}? " f"(type 'yes' to confirm): "
|
|
51
|
+
)
|
|
52
|
+
if response.lower() != "yes":
|
|
53
|
+
return False, "Deletion cancelled by user"
|
|
54
|
+
except KeyboardInterrupt:
|
|
55
|
+
return False, "Deletion cancelled"
|
|
56
|
+
|
|
57
|
+
# Execute delete command
|
|
58
|
+
try:
|
|
59
|
+
cmd = ["xcrun", "simctl", "delete", self.udid]
|
|
60
|
+
result = subprocess.run(cmd, check=False, capture_output=True, text=True, timeout=60)
|
|
61
|
+
|
|
62
|
+
if result.returncode != 0:
|
|
63
|
+
error = result.stderr.strip() or result.stdout.strip()
|
|
64
|
+
return False, f"Deletion failed: {error}"
|
|
65
|
+
|
|
66
|
+
return True, f"Device deleted: {self.udid} [disk space freed]"
|
|
67
|
+
|
|
68
|
+
except subprocess.TimeoutExpired:
|
|
69
|
+
return False, "Deletion command timed out"
|
|
70
|
+
except Exception as e:
|
|
71
|
+
return False, f"Deletion error: {e}"
|
|
72
|
+
|
|
73
|
+
@staticmethod
|
|
74
|
+
def delete_all(confirm: bool = False) -> tuple[int, int]:
|
|
75
|
+
"""
|
|
76
|
+
Delete all simulators permanently.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
confirm: Skip confirmation prompt
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
(succeeded, failed) tuple with counts
|
|
83
|
+
"""
|
|
84
|
+
simulators = list_simulators(state=None)
|
|
85
|
+
|
|
86
|
+
if not confirm:
|
|
87
|
+
count = len(simulators)
|
|
88
|
+
try:
|
|
89
|
+
response = input(
|
|
90
|
+
f"Permanently delete ALL {count} simulators? " f"(type 'yes' to confirm): "
|
|
91
|
+
)
|
|
92
|
+
if response.lower() != "yes":
|
|
93
|
+
return 0, count
|
|
94
|
+
except KeyboardInterrupt:
|
|
95
|
+
return 0, count
|
|
96
|
+
|
|
97
|
+
succeeded = 0
|
|
98
|
+
failed = 0
|
|
99
|
+
|
|
100
|
+
for sim in simulators:
|
|
101
|
+
deleter = SimulatorDeleter(udid=sim["udid"])
|
|
102
|
+
success, _message = deleter.delete(confirm=True)
|
|
103
|
+
if success:
|
|
104
|
+
succeeded += 1
|
|
105
|
+
else:
|
|
106
|
+
failed += 1
|
|
107
|
+
|
|
108
|
+
return succeeded, failed
|
|
109
|
+
|
|
110
|
+
@staticmethod
|
|
111
|
+
def delete_by_type(device_type: str, confirm: bool = False) -> tuple[int, int]:
|
|
112
|
+
"""
|
|
113
|
+
Delete all simulators of a specific type.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
device_type: Device type filter (e.g., "iPhone", "iPad")
|
|
117
|
+
confirm: Skip confirmation prompt
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
(succeeded, failed) tuple with counts
|
|
121
|
+
"""
|
|
122
|
+
simulators = list_simulators(state=None)
|
|
123
|
+
matching = [s for s in simulators if device_type.lower() in s["name"].lower()]
|
|
124
|
+
|
|
125
|
+
if not matching:
|
|
126
|
+
return 0, 0
|
|
127
|
+
|
|
128
|
+
if not confirm:
|
|
129
|
+
count = len(matching)
|
|
130
|
+
try:
|
|
131
|
+
response = input(
|
|
132
|
+
f"Permanently delete {count} {device_type} simulators? "
|
|
133
|
+
f"(type 'yes' to confirm): "
|
|
134
|
+
)
|
|
135
|
+
if response.lower() != "yes":
|
|
136
|
+
return 0, count
|
|
137
|
+
except KeyboardInterrupt:
|
|
138
|
+
return 0, count
|
|
139
|
+
|
|
140
|
+
succeeded = 0
|
|
141
|
+
failed = 0
|
|
142
|
+
|
|
143
|
+
for sim in matching:
|
|
144
|
+
deleter = SimulatorDeleter(udid=sim["udid"])
|
|
145
|
+
success, _message = deleter.delete(confirm=True)
|
|
146
|
+
if success:
|
|
147
|
+
succeeded += 1
|
|
148
|
+
else:
|
|
149
|
+
failed += 1
|
|
150
|
+
|
|
151
|
+
return succeeded, failed
|
|
152
|
+
|
|
153
|
+
@staticmethod
|
|
154
|
+
def delete_old(keep_count: int = 3, confirm: bool = False) -> tuple[int, int]:
|
|
155
|
+
"""
|
|
156
|
+
Delete older simulators, keeping most recent versions.
|
|
157
|
+
|
|
158
|
+
Useful for cleanup after testing multiple iOS versions.
|
|
159
|
+
Keeps the most recent N simulators of each type.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
keep_count: Number of recent simulators to keep per type (default: 3)
|
|
163
|
+
confirm: Skip confirmation prompt
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
(succeeded, failed) tuple with counts
|
|
167
|
+
"""
|
|
168
|
+
simulators = list_simulators(state=None)
|
|
169
|
+
|
|
170
|
+
# Group by device type
|
|
171
|
+
by_type: dict[str, list] = {}
|
|
172
|
+
for sim in simulators:
|
|
173
|
+
dev_type = sim["type"]
|
|
174
|
+
if dev_type not in by_type:
|
|
175
|
+
by_type[dev_type] = []
|
|
176
|
+
by_type[dev_type].append(sim)
|
|
177
|
+
|
|
178
|
+
# Find candidates for deletion (older ones)
|
|
179
|
+
to_delete = []
|
|
180
|
+
for _dev_type, sims in by_type.items():
|
|
181
|
+
# Sort by runtime (iOS version) - keep newest
|
|
182
|
+
sorted_sims = sorted(sims, key=lambda s: s["runtime"], reverse=True)
|
|
183
|
+
# Mark older ones for deletion
|
|
184
|
+
to_delete.extend(sorted_sims[keep_count:])
|
|
185
|
+
|
|
186
|
+
if not to_delete:
|
|
187
|
+
return 0, 0
|
|
188
|
+
|
|
189
|
+
if not confirm:
|
|
190
|
+
count = len(to_delete)
|
|
191
|
+
try:
|
|
192
|
+
response = input(
|
|
193
|
+
f"Delete {count} older simulators, keeping {keep_count} per type? "
|
|
194
|
+
f"(type 'yes' to confirm): "
|
|
195
|
+
)
|
|
196
|
+
if response.lower() != "yes":
|
|
197
|
+
return 0, count
|
|
198
|
+
except KeyboardInterrupt:
|
|
199
|
+
return 0, count
|
|
200
|
+
|
|
201
|
+
succeeded = 0
|
|
202
|
+
failed = 0
|
|
203
|
+
|
|
204
|
+
for sim in to_delete:
|
|
205
|
+
deleter = SimulatorDeleter(udid=sim["udid"])
|
|
206
|
+
success, _message = deleter.delete(confirm=True)
|
|
207
|
+
if success:
|
|
208
|
+
succeeded += 1
|
|
209
|
+
else:
|
|
210
|
+
failed += 1
|
|
211
|
+
|
|
212
|
+
return succeeded, failed
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def main():
|
|
216
|
+
"""Main entry point."""
|
|
217
|
+
parser = argparse.ArgumentParser(description="Delete iOS simulators permanently")
|
|
218
|
+
parser.add_argument(
|
|
219
|
+
"--udid",
|
|
220
|
+
help="Device UDID or name (required unless using batch options)",
|
|
221
|
+
)
|
|
222
|
+
parser.add_argument(
|
|
223
|
+
"--name",
|
|
224
|
+
help="Device name (alternative to --udid)",
|
|
225
|
+
)
|
|
226
|
+
parser.add_argument(
|
|
227
|
+
"--yes",
|
|
228
|
+
action="store_true",
|
|
229
|
+
help="Skip confirmation prompt",
|
|
230
|
+
)
|
|
231
|
+
parser.add_argument(
|
|
232
|
+
"--all",
|
|
233
|
+
action="store_true",
|
|
234
|
+
help="Delete all simulators",
|
|
235
|
+
)
|
|
236
|
+
parser.add_argument(
|
|
237
|
+
"--type",
|
|
238
|
+
help="Delete all simulators of a specific type (e.g., iPhone)",
|
|
239
|
+
)
|
|
240
|
+
parser.add_argument(
|
|
241
|
+
"--old",
|
|
242
|
+
type=int,
|
|
243
|
+
metavar="KEEP_COUNT",
|
|
244
|
+
help="Delete older simulators, keeping this many per type (e.g., --old 3)",
|
|
245
|
+
)
|
|
246
|
+
parser.add_argument(
|
|
247
|
+
"--json",
|
|
248
|
+
action="store_true",
|
|
249
|
+
help="Output as JSON",
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
args = parser.parse_args()
|
|
253
|
+
|
|
254
|
+
# Handle batch operations
|
|
255
|
+
if args.all:
|
|
256
|
+
succeeded, failed = SimulatorDeleter.delete_all(confirm=args.yes)
|
|
257
|
+
if args.json:
|
|
258
|
+
import json
|
|
259
|
+
|
|
260
|
+
print(
|
|
261
|
+
json.dumps(
|
|
262
|
+
{
|
|
263
|
+
"action": "delete_all",
|
|
264
|
+
"succeeded": succeeded,
|
|
265
|
+
"failed": failed,
|
|
266
|
+
"total": succeeded + failed,
|
|
267
|
+
}
|
|
268
|
+
)
|
|
269
|
+
)
|
|
270
|
+
else:
|
|
271
|
+
total = succeeded + failed
|
|
272
|
+
print(f"Delete summary: {succeeded}/{total} succeeded, " f"{failed} failed")
|
|
273
|
+
sys.exit(0 if failed == 0 else 1)
|
|
274
|
+
|
|
275
|
+
if args.type:
|
|
276
|
+
succeeded, failed = SimulatorDeleter.delete_by_type(args.type, confirm=args.yes)
|
|
277
|
+
if args.json:
|
|
278
|
+
import json
|
|
279
|
+
|
|
280
|
+
print(
|
|
281
|
+
json.dumps(
|
|
282
|
+
{
|
|
283
|
+
"action": "delete_by_type",
|
|
284
|
+
"type": args.type,
|
|
285
|
+
"succeeded": succeeded,
|
|
286
|
+
"failed": failed,
|
|
287
|
+
"total": succeeded + failed,
|
|
288
|
+
}
|
|
289
|
+
)
|
|
290
|
+
)
|
|
291
|
+
else:
|
|
292
|
+
total = succeeded + failed
|
|
293
|
+
print(f"Delete {args.type} summary: {succeeded}/{total} succeeded, " f"{failed} failed")
|
|
294
|
+
sys.exit(0 if failed == 0 else 1)
|
|
295
|
+
|
|
296
|
+
if args.old is not None:
|
|
297
|
+
succeeded, failed = SimulatorDeleter.delete_old(keep_count=args.old, confirm=args.yes)
|
|
298
|
+
if args.json:
|
|
299
|
+
import json
|
|
300
|
+
|
|
301
|
+
print(
|
|
302
|
+
json.dumps(
|
|
303
|
+
{
|
|
304
|
+
"action": "delete_old",
|
|
305
|
+
"keep_count": args.old,
|
|
306
|
+
"succeeded": succeeded,
|
|
307
|
+
"failed": failed,
|
|
308
|
+
"total": succeeded + failed,
|
|
309
|
+
}
|
|
310
|
+
)
|
|
311
|
+
)
|
|
312
|
+
else:
|
|
313
|
+
total = succeeded + failed
|
|
314
|
+
print(
|
|
315
|
+
f"Delete old summary: {succeeded}/{total} succeeded, "
|
|
316
|
+
f"{failed} failed (kept {args.old} per type)"
|
|
317
|
+
)
|
|
318
|
+
sys.exit(0 if failed == 0 else 1)
|
|
319
|
+
|
|
320
|
+
# Delete single device
|
|
321
|
+
device_id = args.udid or args.name
|
|
322
|
+
if not device_id:
|
|
323
|
+
print("Error: Specify --udid, --name, --all, --type, or --old", file=sys.stderr)
|
|
324
|
+
sys.exit(1)
|
|
325
|
+
|
|
326
|
+
try:
|
|
327
|
+
udid = resolve_device_identifier(device_id)
|
|
328
|
+
except RuntimeError as e:
|
|
329
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
330
|
+
sys.exit(1)
|
|
331
|
+
|
|
332
|
+
# Delete device
|
|
333
|
+
deleter = SimulatorDeleter(udid=udid)
|
|
334
|
+
success, message = deleter.delete(confirm=args.yes)
|
|
335
|
+
|
|
336
|
+
if args.json:
|
|
337
|
+
import json
|
|
338
|
+
|
|
339
|
+
print(
|
|
340
|
+
json.dumps(
|
|
341
|
+
{
|
|
342
|
+
"action": "delete",
|
|
343
|
+
"device_id": device_id,
|
|
344
|
+
"udid": udid,
|
|
345
|
+
"success": success,
|
|
346
|
+
"message": message,
|
|
347
|
+
}
|
|
348
|
+
)
|
|
349
|
+
)
|
|
350
|
+
else:
|
|
351
|
+
print(message)
|
|
352
|
+
|
|
353
|
+
sys.exit(0 if success else 1)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
if __name__ == "__main__":
|
|
357
|
+
main()
|