@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,178 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration management for iOS Simulator Skill.
|
|
3
|
+
|
|
4
|
+
Handles loading, validation, and auto-updating of project-local config files.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import sys
|
|
9
|
+
from datetime import UTC, datetime
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Config:
|
|
15
|
+
"""
|
|
16
|
+
Project-local configuration with auto-learning.
|
|
17
|
+
|
|
18
|
+
Config file location: .claude/skills/<skill-directory-name>/config.json
|
|
19
|
+
|
|
20
|
+
The skill directory name is auto-detected from the installation location,
|
|
21
|
+
so configs work regardless of what users name the skill directory.
|
|
22
|
+
|
|
23
|
+
Auto-updates last_used_simulator after successful builds.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
DEFAULT_CONFIG = {
|
|
27
|
+
"device": {
|
|
28
|
+
"preferred_simulator": None,
|
|
29
|
+
"preferred_os_version": None,
|
|
30
|
+
"fallback_to_any_iphone": True,
|
|
31
|
+
"last_used_simulator": None,
|
|
32
|
+
"last_used_at": None,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
def __init__(self, data: dict[str, Any], config_path: Path):
|
|
37
|
+
"""
|
|
38
|
+
Initialize config.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
data: Config data dict
|
|
42
|
+
config_path: Path to config file
|
|
43
|
+
"""
|
|
44
|
+
self.data = data
|
|
45
|
+
self.config_path = config_path
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def load(project_dir: Path | None = None) -> "Config":
|
|
49
|
+
"""
|
|
50
|
+
Load config from project directory.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
project_dir: Project root (defaults to cwd)
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Config instance (creates default if not found)
|
|
57
|
+
|
|
58
|
+
Note:
|
|
59
|
+
The skill directory name is auto-detected from the installation location,
|
|
60
|
+
so configs work regardless of what users name the skill directory.
|
|
61
|
+
"""
|
|
62
|
+
if project_dir is None:
|
|
63
|
+
project_dir = Path.cwd()
|
|
64
|
+
|
|
65
|
+
# Auto-detect skill directory name from actual installation location
|
|
66
|
+
# This file is at: skill/scripts/xcode/config.py
|
|
67
|
+
# Navigate up to skill/ directory and use its name
|
|
68
|
+
skill_root = Path(__file__).parent.parent.parent # xcode/ -> scripts/ -> skill/
|
|
69
|
+
skill_name = skill_root.name
|
|
70
|
+
|
|
71
|
+
config_path = project_dir / ".claude" / "skills" / skill_name / "config.json"
|
|
72
|
+
|
|
73
|
+
# Load existing config
|
|
74
|
+
if config_path.exists():
|
|
75
|
+
try:
|
|
76
|
+
with open(config_path) as f:
|
|
77
|
+
data = json.load(f)
|
|
78
|
+
|
|
79
|
+
# Merge with defaults (in case new fields added)
|
|
80
|
+
merged = Config._merge_with_defaults(data)
|
|
81
|
+
return Config(merged, config_path)
|
|
82
|
+
|
|
83
|
+
except json.JSONDecodeError as e:
|
|
84
|
+
print(f"Warning: Invalid JSON in {config_path}: {e}", file=sys.stderr)
|
|
85
|
+
print("Using default config", file=sys.stderr)
|
|
86
|
+
return Config(Config.DEFAULT_CONFIG.copy(), config_path)
|
|
87
|
+
except Exception as e:
|
|
88
|
+
print(f"Warning: Could not load config: {e}", file=sys.stderr)
|
|
89
|
+
return Config(Config.DEFAULT_CONFIG.copy(), config_path)
|
|
90
|
+
|
|
91
|
+
# Return default config (will be created on first save)
|
|
92
|
+
return Config(Config.DEFAULT_CONFIG.copy(), config_path)
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def _merge_with_defaults(data: dict[str, Any]) -> dict[str, Any]:
|
|
96
|
+
"""
|
|
97
|
+
Merge user config with defaults.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
data: User config data
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Merged config with all default fields
|
|
104
|
+
"""
|
|
105
|
+
merged = Config.DEFAULT_CONFIG.copy()
|
|
106
|
+
|
|
107
|
+
# Deep merge device section
|
|
108
|
+
if "device" in data:
|
|
109
|
+
merged["device"].update(data["device"])
|
|
110
|
+
|
|
111
|
+
return merged
|
|
112
|
+
|
|
113
|
+
def save(self) -> None:
|
|
114
|
+
"""
|
|
115
|
+
Save config to file atomically.
|
|
116
|
+
|
|
117
|
+
Uses temp file + rename for atomic writes.
|
|
118
|
+
Creates parent directories if needed.
|
|
119
|
+
"""
|
|
120
|
+
try:
|
|
121
|
+
# Create parent directories
|
|
122
|
+
self.config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
123
|
+
|
|
124
|
+
# Atomic write: temp file + rename
|
|
125
|
+
temp_path = self.config_path.with_suffix(".tmp")
|
|
126
|
+
|
|
127
|
+
with open(temp_path, "w") as f:
|
|
128
|
+
json.dump(self.data, f, indent=2)
|
|
129
|
+
f.write("\n") # Trailing newline
|
|
130
|
+
|
|
131
|
+
# Atomic rename
|
|
132
|
+
temp_path.replace(self.config_path)
|
|
133
|
+
|
|
134
|
+
except Exception as e:
|
|
135
|
+
print(f"Warning: Could not save config: {e}", file=sys.stderr)
|
|
136
|
+
|
|
137
|
+
def update_last_used_simulator(self, name: str) -> None:
|
|
138
|
+
"""
|
|
139
|
+
Update last used simulator and timestamp.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
name: Simulator name (e.g., "iPhone 16 Pro")
|
|
143
|
+
"""
|
|
144
|
+
self.data["device"]["last_used_simulator"] = name
|
|
145
|
+
self.data["device"]["last_used_at"] = datetime.now(UTC).isoformat()
|
|
146
|
+
|
|
147
|
+
def get_preferred_simulator(self) -> str | None:
|
|
148
|
+
"""
|
|
149
|
+
Get preferred simulator.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Simulator name or None
|
|
153
|
+
|
|
154
|
+
Priority:
|
|
155
|
+
1. preferred_simulator (manual preference)
|
|
156
|
+
2. last_used_simulator (auto-learned)
|
|
157
|
+
3. None (use auto-detection)
|
|
158
|
+
"""
|
|
159
|
+
device = self.data.get("device", {})
|
|
160
|
+
|
|
161
|
+
# Manual preference takes priority
|
|
162
|
+
if device.get("preferred_simulator"):
|
|
163
|
+
return device["preferred_simulator"]
|
|
164
|
+
|
|
165
|
+
# Auto-learned preference
|
|
166
|
+
if device.get("last_used_simulator"):
|
|
167
|
+
return device["last_used_simulator"]
|
|
168
|
+
|
|
169
|
+
return None
|
|
170
|
+
|
|
171
|
+
def should_fallback_to_any_iphone(self) -> bool:
|
|
172
|
+
"""
|
|
173
|
+
Check if fallback to any iPhone is enabled.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
True if should fallback, False otherwise
|
|
177
|
+
"""
|
|
178
|
+
return self.data.get("device", {}).get("fallback_to_any_iphone", True)
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Build/test output formatting.
|
|
3
|
+
|
|
4
|
+
Provides multiple output formats with progressive disclosure support.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
|
|
9
|
+
from common.env_config import env_int
|
|
10
|
+
|
|
11
|
+
BUILD_SUMMARY_CAP = env_int("IOS_SIM_BUILD_SUMMARY_CAP", 15)
|
|
12
|
+
BUILD_VERBOSE_CAP = env_int("IOS_SIM_BUILD_VERBOSE_CAP", 100)
|
|
13
|
+
BUILD_LOG_TAIL = env_int("IOS_SIM_LOG_TAIL", 200)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class OutputFormatter:
|
|
17
|
+
"""
|
|
18
|
+
Format build/test results for display.
|
|
19
|
+
|
|
20
|
+
Supports ultra-minimal default output, verbose mode, and JSON output.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def format_minimal(
|
|
25
|
+
status: str,
|
|
26
|
+
error_count: int,
|
|
27
|
+
warning_count: int,
|
|
28
|
+
xcresult_id: str,
|
|
29
|
+
test_info: dict | None = None,
|
|
30
|
+
hints: list[str] | None = None,
|
|
31
|
+
errors: list[dict] | None = None,
|
|
32
|
+
failed_tests: list[dict] | None = None,
|
|
33
|
+
) -> str:
|
|
34
|
+
"""
|
|
35
|
+
Format ultra-minimal output (5-10 tokens on success, more on failure).
|
|
36
|
+
|
|
37
|
+
On failure, automatically surfaces top errors/failed tests inline so agents
|
|
38
|
+
don't need a second round-trip with --get-errors.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
status: Build status (SUCCESS/FAILED)
|
|
42
|
+
error_count: Number of errors
|
|
43
|
+
warning_count: Number of warnings
|
|
44
|
+
xcresult_id: XCResult bundle ID
|
|
45
|
+
test_info: Optional test results dict
|
|
46
|
+
hints: Optional list of actionable hints
|
|
47
|
+
errors: Optional error list to surface on failure
|
|
48
|
+
failed_tests: Optional failed test list to surface on failure
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Minimal formatted string
|
|
52
|
+
"""
|
|
53
|
+
lines = []
|
|
54
|
+
|
|
55
|
+
if test_info:
|
|
56
|
+
# Test mode
|
|
57
|
+
total = test_info.get("total", 0)
|
|
58
|
+
passed = test_info.get("passed", 0)
|
|
59
|
+
failed = test_info.get("failed", 0)
|
|
60
|
+
duration = test_info.get("duration", 0.0)
|
|
61
|
+
|
|
62
|
+
test_status = "PASS" if failed == 0 else "FAIL"
|
|
63
|
+
lines.append(
|
|
64
|
+
f"Tests: {test_status} ({passed}/{total} passed, {duration:.1f}s) [{xcresult_id}]"
|
|
65
|
+
)
|
|
66
|
+
else:
|
|
67
|
+
# Build mode
|
|
68
|
+
lines.append(
|
|
69
|
+
f"Build: {status} ({error_count} errors, {warning_count} warnings) [{xcresult_id}]"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Surface errors inline on failure
|
|
73
|
+
if status == "FAILED" and errors:
|
|
74
|
+
lines.append("")
|
|
75
|
+
lines.append(OutputFormatter.format_errors(errors, limit=BUILD_SUMMARY_CAP))
|
|
76
|
+
|
|
77
|
+
# Surface failed tests inline on failure
|
|
78
|
+
if failed_tests:
|
|
79
|
+
lines.append("")
|
|
80
|
+
lines.append(
|
|
81
|
+
OutputFormatter.format_test_failures(failed_tests, limit=BUILD_SUMMARY_CAP)
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Add hints if provided and build failed
|
|
85
|
+
if hints and status == "FAILED":
|
|
86
|
+
lines.append("")
|
|
87
|
+
lines.extend(hints)
|
|
88
|
+
|
|
89
|
+
return "\n".join(lines)
|
|
90
|
+
|
|
91
|
+
@staticmethod
|
|
92
|
+
def format_test_failures(failed_tests: list[dict], limit: int = BUILD_SUMMARY_CAP) -> str:
|
|
93
|
+
"""
|
|
94
|
+
Format failed test details.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
failed_tests: List of dicts with test_name and failure_message
|
|
98
|
+
limit: Maximum failures to show
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Formatted failure list
|
|
102
|
+
"""
|
|
103
|
+
if not failed_tests:
|
|
104
|
+
return "No test failures found."
|
|
105
|
+
|
|
106
|
+
lines = [f"Failed tests ({len(failed_tests)}):"]
|
|
107
|
+
lines.append("")
|
|
108
|
+
|
|
109
|
+
for i, test in enumerate(failed_tests[:limit], 1):
|
|
110
|
+
name = test.get("test_name", "Unknown")
|
|
111
|
+
message = test.get("failure_message", "")
|
|
112
|
+
lines.append(f"{i}. {name}")
|
|
113
|
+
if message:
|
|
114
|
+
lines.append(f" {message}")
|
|
115
|
+
lines.append("")
|
|
116
|
+
|
|
117
|
+
if len(failed_tests) > limit:
|
|
118
|
+
lines.append(f"... and {len(failed_tests) - limit} more failures")
|
|
119
|
+
|
|
120
|
+
return "\n".join(lines)
|
|
121
|
+
|
|
122
|
+
@staticmethod
|
|
123
|
+
def format_errors(errors: list[dict], limit: int = BUILD_VERBOSE_CAP) -> str:
|
|
124
|
+
"""
|
|
125
|
+
Format error details.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
errors: List of error dicts
|
|
129
|
+
limit: Maximum errors to show
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Formatted error list
|
|
133
|
+
"""
|
|
134
|
+
if not errors:
|
|
135
|
+
return "No errors found."
|
|
136
|
+
|
|
137
|
+
lines = [f"Errors ({len(errors)}):"]
|
|
138
|
+
lines.append("")
|
|
139
|
+
|
|
140
|
+
for i, error in enumerate(errors[:limit], 1):
|
|
141
|
+
message = error.get("message", "Unknown error")
|
|
142
|
+
location = error.get("location", {})
|
|
143
|
+
|
|
144
|
+
# Format location
|
|
145
|
+
loc_parts = []
|
|
146
|
+
if location.get("file"):
|
|
147
|
+
file_path = location["file"].replace("file://", "")
|
|
148
|
+
loc_parts.append(file_path)
|
|
149
|
+
if location.get("line"):
|
|
150
|
+
loc_parts.append(f"line {location['line']}")
|
|
151
|
+
|
|
152
|
+
location_str = ":".join(loc_parts) if loc_parts else "unknown location"
|
|
153
|
+
|
|
154
|
+
lines.append(f"{i}. {message}")
|
|
155
|
+
lines.append(f" Location: {location_str}")
|
|
156
|
+
lines.append("")
|
|
157
|
+
|
|
158
|
+
if len(errors) > limit:
|
|
159
|
+
lines.append(f"... and {len(errors) - limit} more errors")
|
|
160
|
+
|
|
161
|
+
return "\n".join(lines)
|
|
162
|
+
|
|
163
|
+
@staticmethod
|
|
164
|
+
def format_warnings(warnings: list[dict], limit: int = BUILD_VERBOSE_CAP) -> str:
|
|
165
|
+
"""
|
|
166
|
+
Format warning details.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
warnings: List of warning dicts
|
|
170
|
+
limit: Maximum warnings to show
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
Formatted warning list
|
|
174
|
+
"""
|
|
175
|
+
if not warnings:
|
|
176
|
+
return "No warnings found."
|
|
177
|
+
|
|
178
|
+
lines = [f"Warnings ({len(warnings)}):"]
|
|
179
|
+
lines.append("")
|
|
180
|
+
|
|
181
|
+
for i, warning in enumerate(warnings[:limit], 1):
|
|
182
|
+
message = warning.get("message", "Unknown warning")
|
|
183
|
+
location = warning.get("location", {})
|
|
184
|
+
|
|
185
|
+
# Format location
|
|
186
|
+
loc_parts = []
|
|
187
|
+
if location.get("file"):
|
|
188
|
+
file_path = location["file"].replace("file://", "")
|
|
189
|
+
loc_parts.append(file_path)
|
|
190
|
+
if location.get("line"):
|
|
191
|
+
loc_parts.append(f"line {location['line']}")
|
|
192
|
+
|
|
193
|
+
location_str = ":".join(loc_parts) if loc_parts else "unknown location"
|
|
194
|
+
|
|
195
|
+
lines.append(f"{i}. {message}")
|
|
196
|
+
lines.append(f" Location: {location_str}")
|
|
197
|
+
lines.append("")
|
|
198
|
+
|
|
199
|
+
if len(warnings) > limit:
|
|
200
|
+
lines.append(f"... and {len(warnings) - limit} more warnings")
|
|
201
|
+
|
|
202
|
+
return "\n".join(lines)
|
|
203
|
+
|
|
204
|
+
@staticmethod
|
|
205
|
+
def format_log(log: str, lines: int = BUILD_LOG_TAIL) -> str:
|
|
206
|
+
"""
|
|
207
|
+
Format build log (show last N lines).
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
log: Full build log
|
|
211
|
+
lines: Number of lines to show
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
Formatted log excerpt
|
|
215
|
+
"""
|
|
216
|
+
if not log:
|
|
217
|
+
return "No build log available."
|
|
218
|
+
|
|
219
|
+
log_lines = log.strip().split("\n")
|
|
220
|
+
|
|
221
|
+
if len(log_lines) <= lines:
|
|
222
|
+
return log
|
|
223
|
+
|
|
224
|
+
# Show last N lines
|
|
225
|
+
excerpt = log_lines[-lines:]
|
|
226
|
+
return f"... (showing last {lines} lines of {len(log_lines)})\n\n" + "\n".join(excerpt)
|
|
227
|
+
|
|
228
|
+
@staticmethod
|
|
229
|
+
def format_json(data: dict) -> str:
|
|
230
|
+
"""
|
|
231
|
+
Format data as JSON.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
data: Data to format
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
Pretty-printed JSON string
|
|
238
|
+
"""
|
|
239
|
+
return json.dumps(data, indent=2)
|
|
240
|
+
|
|
241
|
+
@staticmethod
|
|
242
|
+
def generate_hints(errors: list[dict]) -> list[str]:
|
|
243
|
+
"""
|
|
244
|
+
Generate actionable hints based on error types.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
errors: List of error dicts
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
List of hint strings
|
|
251
|
+
"""
|
|
252
|
+
hints = []
|
|
253
|
+
error_types: set[str] = set()
|
|
254
|
+
|
|
255
|
+
# Collect error types
|
|
256
|
+
for error in errors:
|
|
257
|
+
error_type = error.get("type", "unknown")
|
|
258
|
+
error_types.add(error_type)
|
|
259
|
+
|
|
260
|
+
# Generate hints based on error types
|
|
261
|
+
if "provisioning" in error_types:
|
|
262
|
+
hints.append("Provisioning profile issue detected:")
|
|
263
|
+
hints.append(" • Ensure you have a valid provisioning profile for iOS Simulator")
|
|
264
|
+
hints.append(
|
|
265
|
+
' • For simulator builds, use CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO'
|
|
266
|
+
)
|
|
267
|
+
hints.append(" • Or specify simulator explicitly: --simulator 'iPhone 16 Pro'")
|
|
268
|
+
|
|
269
|
+
if "signing" in error_types:
|
|
270
|
+
hints.append("Code signing issue detected:")
|
|
271
|
+
hints.append(" • For simulator builds, code signing is not required")
|
|
272
|
+
hints.append(" • Ensure build settings target iOS Simulator, not physical device")
|
|
273
|
+
hints.append(" • Check destination: platform=iOS Simulator,name=<device>")
|
|
274
|
+
|
|
275
|
+
if not error_types or "build" in error_types:
|
|
276
|
+
# Generic hints when error type is unknown
|
|
277
|
+
if any("destination" in error.get("message", "").lower() for error in errors):
|
|
278
|
+
hints.append("Device selection issue detected:")
|
|
279
|
+
hints.append(" • List available simulators: xcrun simctl list devices available")
|
|
280
|
+
hints.append(" • Specify simulator: --simulator 'iPhone 16 Pro'")
|
|
281
|
+
|
|
282
|
+
return hints
|
|
283
|
+
|
|
284
|
+
@staticmethod
|
|
285
|
+
def format_verbose(
|
|
286
|
+
status: str,
|
|
287
|
+
error_count: int,
|
|
288
|
+
warning_count: int,
|
|
289
|
+
xcresult_id: str,
|
|
290
|
+
errors: list[dict] | None = None,
|
|
291
|
+
warnings: list[dict] | None = None,
|
|
292
|
+
test_info: dict | None = None,
|
|
293
|
+
) -> str:
|
|
294
|
+
"""
|
|
295
|
+
Format verbose output with error/warning details.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
status: Build status
|
|
299
|
+
error_count: Error count
|
|
300
|
+
warning_count: Warning count
|
|
301
|
+
xcresult_id: XCResult ID
|
|
302
|
+
errors: Optional error list
|
|
303
|
+
warnings: Optional warning list
|
|
304
|
+
test_info: Optional test results
|
|
305
|
+
|
|
306
|
+
Returns:
|
|
307
|
+
Verbose formatted output
|
|
308
|
+
"""
|
|
309
|
+
lines = []
|
|
310
|
+
|
|
311
|
+
# Header
|
|
312
|
+
if test_info:
|
|
313
|
+
total = test_info.get("total", 0)
|
|
314
|
+
passed = test_info.get("passed", 0)
|
|
315
|
+
failed = test_info.get("failed", 0)
|
|
316
|
+
duration = test_info.get("duration", 0.0)
|
|
317
|
+
|
|
318
|
+
test_status = "PASS" if failed == 0 else "FAIL"
|
|
319
|
+
lines.append(f"Tests: {test_status}")
|
|
320
|
+
lines.append(f" Total: {total}")
|
|
321
|
+
lines.append(f" Passed: {passed}")
|
|
322
|
+
lines.append(f" Failed: {failed}")
|
|
323
|
+
lines.append(f" Duration: {duration:.1f}s")
|
|
324
|
+
else:
|
|
325
|
+
lines.append(f"Build: {status}")
|
|
326
|
+
|
|
327
|
+
lines.append(f"XCResult: {xcresult_id}")
|
|
328
|
+
lines.append("")
|
|
329
|
+
|
|
330
|
+
# Errors
|
|
331
|
+
if errors and len(errors) > 0:
|
|
332
|
+
lines.append(OutputFormatter.format_errors(errors, limit=BUILD_VERBOSE_CAP))
|
|
333
|
+
lines.append("")
|
|
334
|
+
|
|
335
|
+
# Warnings
|
|
336
|
+
if warnings and len(warnings) > 0:
|
|
337
|
+
lines.append(OutputFormatter.format_warnings(warnings, limit=BUILD_VERBOSE_CAP))
|
|
338
|
+
lines.append("")
|
|
339
|
+
|
|
340
|
+
# Summary
|
|
341
|
+
lines.append(f"Summary: {error_count} errors, {warning_count} warnings")
|
|
342
|
+
|
|
343
|
+
return "\n".join(lines)
|