#!/bin/bash # dev.sh — ReBreak Native Development Tooling # # Konsolidiert: dev-ios.sh + dev-iphone.sh + metro.sh (alle gelöscht). # # SUBCOMMANDS: # ./dev.sh default: ios --device (physisches iPhone USB + Build) # ./dev.sh ios iOS Dev (Default: USB-Device mit Build) # ./dev.sh android Android Dev (Gradle Build + Install + Launch) # ./dev.sh mobile Auto-detect angeschl. iPhone + Android via USB, # baut+launcht auf BEIDEN parallel mit shared Metro # ./dev.sh metro Nur Metro starten # ./dev.sh clean iOS: Nuclear clean (Pods, DerivedData, Archives) # ./dev.sh install ios Build Release + Install auf iPhone USB # ./dev.sh install android Build Debug APK + Install auf Android Device # ./dev.sh magic Build + Launch RebreakMagic.app (macOS Wizard) # # FLAGS (ios): # --device Build auf physisches iPhone via USB (DEFAULT) # --simulator Build auf iOS Simulator # --xcode Nur Xcode öffnen (manueller Build) # --wifi Metro mit --host lan (WiFi-Dev, KEIN Native-Build) # --no-build KEIN Native-Rebuild → nur Metro starten # (für schnellen UI/JS-Reload — App muss installiert sein) # # FLAGS (android): # --no-build KEIN Gradle-Rebuild → nur Metro starten # (für schnellen UI/JS-Reload — APK muss installiert sein) # --no-launch Build+Install, aber kein Auto-Launch # --wifi Metro mit --host lan (nur in Kombi mit --no-build) # # FLAGS (mobile): # --no-build Beide nur Metro/Install (kein Native-Rebuild) # --ios-only Nur iOS bauen (falls Android-Device da aber ignorieren) # --android-only Nur Android bauen # # FLAGS (metro): # --keep Cache behalten (kein --clear) # # FLAGS (clean): # --build + iOS build am Ende # --xcode + Xcode öffnen am Ende # # FLAGS (magic): # --no-build Nur launchen (App muss schon gebaut sein) # --xcode Nur Xcode-Projekt generieren + öffnen # --debug Debug-Konfiguration statt Release # --no-launch Build, aber App nicht öffnen # # BEISPIELE: # # Default: iPhone USB + Native-Build: # ./dev.sh # # # Schneller UI-Loop (App schon installiert, nur JS-Reload): # ./dev.sh ios --no-build # ./dev.sh android --no-build # # # WiFi-Dev (Kabel ab, Metro über LAN): # ./dev.sh ios --wifi # # # iOS Simulator: # ./dev.sh ios --simulator # # # Nur Xcode öffnen: # ./dev.sh ios --xcode # # # iOS Clean + Rebuild: # ./dev.sh clean --build # # # Release-Build auf iPhone installieren: # ./dev.sh install ios set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" IOS_DIR="$SCRIPT_DIR/ios" ANDROID_DIR="$SCRIPT_DIR/android" MAGIC_DIR="$SCRIPT_DIR/../rebreak-magic-mac" # ═══════════════════════════════════════════════════════════════════════════ # Color Output # ═══════════════════════════════════════════════════════════════════════════ if [[ -t 1 ]]; then BOLD=$(tput bold) GREEN=$(tput setaf 2) YELLOW=$(tput setaf 3) RED=$(tput setaf 1) BLUE=$(tput setaf 4) RESET=$(tput sgr0) else BOLD="" GREEN="" YELLOW="" RED="" BLUE="" RESET="" fi log() { echo "${BLUE}==>${RESET} ${BOLD}$*${RESET}"; } ok() { echo "${GREEN}✓${RESET} $*"; } warn() { echo "${YELLOW}⚠${RESET} $*" >&2; } error() { echo "${RED}✗${RESET} ${BOLD}$*${RESET}" >&2; } die() { error "$*"; exit 1; } section() { echo "" echo "${BOLD}$*${RESET}" echo "${BOLD}────────────────────────────────────────────────────────────${RESET}" } # ═══════════════════════════════════════════════════════════════════════════ # ENV Defaults # ═══════════════════════════════════════════════════════════════════════════ export REBREAK_ENABLE_FAMILY_CONTROLS="${REBREAK_ENABLE_FAMILY_CONTROLS:-1}" export EXPO_PUBLIC_ENABLE_DEBUG="${EXPO_PUBLIC_ENABLE_DEBUG:-1}" export REBREAK_DEV="${REBREAK_DEV:-0}" # ═══════════════════════════════════════════════════════════════════════════ # Commands # ═══════════════════════════════════════════════════════════════════════════ cmd_ios() { local MODE="device" # Default: physisches iPhone via USB local WIFI=false local BUILD=true # Default: nativen Build laufen lassen while [[ $# -gt 0 ]]; do case "$1" in --device) MODE="device"; shift ;; --simulator) MODE="simulator"; shift ;; --xcode) MODE="xcode"; shift ;; --wifi) WIFI=true; shift ;; --no-build) BUILD=false; shift ;; *) die "Unbekannter Flag für 'ios': $1" ;; esac done section "iOS Dev Mode" # ───────────────────────────────────────────────────────────── # --no-build / WiFi: KEIN Native-Rebuild, nur Metro + Dev-Client # → schnellster Loop für reine UI/JS-Änderungen # → App muss schon auf dem Device/Simulator installiert sein # ───────────────────────────────────────────────────────────── if ! $BUILD || $WIFI; then local HOST_FLAG="" if $WIFI; then HOST_FLAG="--host lan" log "Metro: WiFi-Modus (--host lan)" echo "" echo "Mac LAN-IP:" ipconfig getifaddr en0 2>/dev/null || ipconfig getifaddr en1 2>/dev/null || echo " (kein WiFi/Ethernet detected)" echo "" echo "Falls dev-client Metro nicht automatisch findet:" echo " im iPhone-Launcher → 'Enter URL manually' → http://:8081" else log "Metro: USB/Local-Modus (kein Native-Rebuild)" echo "App auf Device/Simulator muss schon installiert sein." echo "Beim Öffnen connected dev-client automatisch zu Metro." fi echo "" log "Killing old Metro on port 8081..." lsof -ti:8081 2>/dev/null | xargs kill -9 2>/dev/null || true pkill -f "expo start" 2>/dev/null || true echo "" exec pnpm expo start $HOST_FLAG --clear --dev-client fi case "$MODE" in xcode) log "Opening Xcode Workspace..." osascript -e 'tell application "Xcode" to close every window whose name contains "Rebreak.xcodeproj"' 2>/dev/null || true open -a Xcode "$IOS_DIR/ReBreak.xcworkspace" ok "Xcode geöffnet — Cmd+R für Build & Run" echo "" echo "ℹ️ Metro: separat starten via './dev.sh metro' falls noch nicht läuft" ;; device) log "Building für physisches iPhone (USB)..." echo "ℹ️ Für schnellen UI-Reload ohne Rebuild: './dev.sh ios --no-build'" pnpm expo run:ios --device ;; simulator) log "Building für iOS Simulator..." pnpm expo run:ios ;; esac } cmd_android() { local BUILD=true local LAUNCH=true local WIFI=false while [[ $# -gt 0 ]]; do case "$1" in --no-build) BUILD=false; shift ;; --no-launch) LAUNCH=false; shift ;; --wifi) WIFI=true; shift ;; *) die "Unbekannter Flag für 'android': $1" ;; esac done section "Android Dev Mode" # ───────────────────────────────────────────────────────────── # --no-build: KEIN Gradle-Rebuild, nur Metro + Dev-Client # → schnellster Loop für reine UI/JS-Änderungen # → APK muss schon auf dem Device installiert sein # ───────────────────────────────────────────────────────────── if ! $BUILD; then local HOST_FLAG="" if $WIFI; then HOST_FLAG="--host lan" log "Metro: WiFi-Modus (--host lan)" echo "" echo "Mac LAN-IP:" ipconfig getifaddr en0 2>/dev/null || ipconfig getifaddr en1 2>/dev/null || echo " (kein WiFi/Ethernet detected)" else log "Metro: USB/ADB-Modus (kein Gradle-Rebuild)" echo "APK muss schon auf dem Device installiert sein." fi echo "" log "Killing old Metro on port 8081..." lsof -ti:8081 2>/dev/null | xargs kill -9 2>/dev/null || true pkill -f "expo start" 2>/dev/null || true echo "" exec pnpm expo start $HOST_FLAG --clear --dev-client fi command -v adb >/dev/null 2>&1 || die "adb nicht gefunden — brew install --cask android-platform-tools" local DEVICE_COUNT DEVICE_COUNT=$(adb devices | grep -E '[[:space:]]device$' | grep -c '.' || true) if [[ "$DEVICE_COUNT" -eq 0 ]]; then warn "Kein Android-Gerät via ADB verbunden" echo "" adb devices echo "" echo "Mögliche Ursachen:" echo " - USB nicht angeschlossen / Kabel nur Strom" echo " - USB-Debugging auf dem Phone aus" echo " - 'Diesem Computer vertrauen?' Dialog noch nicht bestätigt" exit 1 fi log "Building Debug APK..." echo "ℹ️ Für schnellen UI-Reload ohne Rebuild: './dev.sh android --no-build'" (cd "$ANDROID_DIR" && ./gradlew assembleDebug --console=plain) local APK="$ANDROID_DIR/app/build/outputs/apk/debug/app-debug.apk" [[ -f "$APK" ]] || die "APK nicht gefunden: $APK" log "Installing APK..." adb install -r -d "$APK" if $LAUNCH; then log "Launching App..." adb shell monkey -p org.rebreak.app -c android.intent.category.LAUNCHER 1 >/dev/null 2>&1 || { warn "Launch via monkey schlug fehl — App ist installiert, manuell öffnen" } fi ok "Android Dev Build abgeschlossen" } # --------------------------------------------------------------------------- # Device-Detection Helpers # --------------------------------------------------------------------------- detect_ios_device() { # Gibt UDID des ersten ONLINE iPhone/iPad zurück, leer wenn keins command -v xcrun >/dev/null 2>&1 || { echo ""; return 0; } { xcrun xctrace list devices 2>/dev/null \ | awk '/^== Devices ==/{f=1; next} /^== /{f=0} f' \ | grep -E "iPhone|iPad" \ | grep -v Simulator \ | head -1 \ | sed -nE 's/.*\(([0-9A-Fa-f-]{25,})\).*/\1/p'; } || true } detect_ios_device_name() { command -v xcrun >/dev/null 2>&1 || { echo ""; return 0; } { xcrun xctrace list devices 2>/dev/null \ | awk '/^== Devices ==/{f=1; next} /^== /{f=0} f' \ | grep -E "iPhone|iPad" \ | grep -v Simulator \ | head -1 \ | sed -E 's/ *\(.*//'; } || true } detect_android_devices() { # Gibt alle ADB-Device-IDs (eine pro Zeile) command -v adb >/dev/null 2>&1 || return 0 { adb devices 2>/dev/null | awk '/\tdevice$/ {print $1}'; } || true } start_shared_metro() { local LOG="/tmp/rebreak-metro.log" log "Killing alte Metro-Instanzen auf 8081..." lsof -ti:8081 2>/dev/null | xargs kill -9 2>/dev/null || true pkill -f "expo start" 2>/dev/null || true sleep 1 log "Starte Metro im Hintergrund → $LOG" (cd "$SCRIPT_DIR" && nohup pnpm expo start --dev-client --clear > "$LOG" 2>&1 &) # Warte auf Metro-Bereitschaft local tries=0 while [[ $tries -lt 30 ]]; do if curl -s http://localhost:8081/status 2>/dev/null | grep -q packager-status:running; then ok "Metro bereit (http://localhost:8081)" return 0 fi sleep 1 tries=$((tries+1)) done warn "Metro-Statuscheck nicht bestätigt nach 30s — trotzdem weiter (Log: $LOG)" } cmd_mobile() { local BUILD=true local IOS_ONLY=false local ANDROID_ONLY=false while [[ $# -gt 0 ]]; do case "$1" in --no-build) BUILD=false; shift ;; --ios-only) IOS_ONLY=true; shift ;; --android-only) ANDROID_ONLY=true; shift ;; *) die "Unbekannter Flag für 'mobile': $1" ;; esac done section "Mobile Dev (Auto-Detect iOS + Android)" # ── Detect ──────────────────────────────────────────────── local IOS_UDID="" local IOS_NAME="" local ANDROID_IDS=() # set +e block: detection-Funktionen dürfen leer zurückkommen ohne Script-Abort set +e if ! $ANDROID_ONLY; then IOS_UDID="$(detect_ios_device)" IOS_NAME="$(detect_ios_device_name)" fi if ! $IOS_ONLY; then while IFS= read -r line; do [[ -n "$line" ]] && ANDROID_IDS+=("$line") done < <(detect_android_devices) fi set -e echo "" if [[ -n "$IOS_UDID" ]]; then ok "iOS: ${IOS_NAME:-iPhone} (${IOS_UDID})" else warn "Kein iOS-Device via USB gefunden" fi if [[ ${#ANDROID_IDS[@]} -gt 0 ]]; then for id in "${ANDROID_IDS[@]}"; do local model model=$(adb -s "$id" shell getprop ro.product.model 2>/dev/null | tr -d '\r') ok "Android: ${model:-unknown} ($id)" done else warn "Kein Android-Device via ADB gefunden" fi echo "" if [[ -z "$IOS_UDID" && ${#ANDROID_IDS[@]} -eq 0 ]]; then die "Keine Devices erkannt — USB-Kabel checken, Trust-Dialog auf iPhone, USB-Debugging auf Android" fi # ── Shared Metro ────────────────────────────────────────── start_shared_metro local IOS_LOG="/tmp/rebreak-ios-build.log" local ANDROID_LOG="/tmp/rebreak-android-build.log" local IOS_PID="" local ANDROID_PID="" # ── iOS Build (parallel, im Hintergrund) ────────────────── if [[ -n "$IOS_UDID" ]]; then if $BUILD; then log "iOS-Build startet → $IOS_LOG" ( cd "$SCRIPT_DIR" pnpm expo run:ios --device "$IOS_UDID" --no-bundler ) > "$IOS_LOG" 2>&1 & IOS_PID=$! else log "iOS — skip Build (--no-build), App muss installiert sein" fi fi # ── Android Build (parallel) ────────────────────────────── if [[ ${#ANDROID_IDS[@]} -gt 0 ]]; then if $BUILD; then log "Android-Build startet → $ANDROID_LOG" ( cd "$ANDROID_DIR" ./gradlew assembleDebug --console=plain local APK="$ANDROID_DIR/app/build/outputs/apk/debug/app-debug.apk" [[ -f "$APK" ]] || { echo "APK nicht gefunden"; exit 1; } for id in "${ANDROID_IDS[@]}"; do echo "→ install on $id" adb -s "$id" install -r -d "$APK" adb -s "$id" shell monkey -p org.rebreak.app -c android.intent.category.LAUNCHER 1 >/dev/null 2>&1 || true done ) > "$ANDROID_LOG" 2>&1 & ANDROID_PID=$! else log "Android — skip Build (--no-build), APK muss installiert sein" fi fi # ── Live-Logs follow ────────────────────────────────────── echo "" log "Builds laufen parallel. Live-Logs:" [[ -n "$IOS_PID" ]] && echo " iOS : tail -f $IOS_LOG (pid $IOS_PID)" [[ -n "$ANDROID_PID" ]] && echo " Android : tail -f $ANDROID_LOG (pid $ANDROID_PID)" echo " Metro : tail -f /tmp/rebreak-metro.log" echo "" local IOS_RC=0 local ANDROID_RC=0 if [[ -n "$IOS_PID" ]]; then log "Warte auf iOS-Build..." wait "$IOS_PID" && IOS_RC=$? || IOS_RC=$? if [[ $IOS_RC -eq 0 ]]; then ok "iOS-Build fertig"; else error "iOS-Build FAIL (rc=$IOS_RC) — $IOS_LOG"; fi fi if [[ -n "$ANDROID_PID" ]]; then log "Warte auf Android-Build..." wait "$ANDROID_PID" && ANDROID_RC=$? || ANDROID_RC=$? if [[ $ANDROID_RC -eq 0 ]]; then ok "Android-Build fertig"; else error "Android-Build FAIL (rc=$ANDROID_RC) — $ANDROID_LOG"; fi fi echo "" ok "Mobile-Dev bereit. Metro läuft im Hintergrund (kill via: lsof -ti:8081 | xargs kill)." echo "Metro-Log live: tail -f /tmp/rebreak-metro.log" } cmd_metro() { local CLEAR_FLAG="--clear" while [[ $# -gt 0 ]]; do case "$1" in --keep) CLEAR_FLAG=""; shift ;; *) die "Unbekannter Flag für 'metro': $1" ;; esac done section "Metro Bundler" log "Killing existing Metro on port 8081..." lsof -iTCP:8081 -sTCP:LISTEN -n -P 2>/dev/null | awk 'NR>1 {print $2}' | sort -u | xargs kill -9 2>/dev/null || true pkill -f "expo start" 2>/dev/null || true pkill -f "react-native/cli/build" 2>/dev/null || true if [[ -n "$CLEAR_FLAG" ]]; then log "Starting Metro mit --clear (Cache reset)..." else log "Starting Metro (Cache behalten)..." fi exec npx expo start $CLEAR_FLAG } cmd_clean() { local POST_ACTION="" while [[ $# -gt 0 ]]; do case "$1" in --build) POST_ACTION="build"; shift ;; --xcode) POST_ACTION="xcode"; shift ;; *) die "Unbekannter Flag für 'clean': $1" ;; esac done section "iOS Nuclear Clean" log "rm -rf ios/Pods ios/Podfile.lock ios/build" rm -rf "$IOS_DIR/Pods" "$IOS_DIR/Podfile.lock" "$IOS_DIR/build" local DERIVED_DATA="$HOME/Library/Developer/Xcode/DerivedData" if [[ -d "$DERIVED_DATA" ]]; then log "rm -rf DerivedData/Rebreak-*" rm -rf "$DERIVED_DATA"/Rebreak-* 2>/dev/null || true fi local ARCHIVES_DIR="$HOME/Library/Developer/Xcode/Archives" if [[ -d "$ARCHIVES_DIR" ]]; then local before_count before_count=$(find "$ARCHIVES_DIR" -maxdepth 2 -type d -iname "*rebreak*.xcarchive" 2>/dev/null | wc -l | tr -d ' ') log "Cleaning Xcode Archives (Rebreak, >24h) — vorher: $before_count Stk" find "$ARCHIVES_DIR" -maxdepth 2 -type d -iname "*rebreak*.xcarchive" -mtime +1 -exec rm -rf {} + 2>/dev/null || true find "$ARCHIVES_DIR" -maxdepth 1 -type d -empty -delete 2>/dev/null || true fi log "pnpm expo prebuild --clean" pnpm expo prebuild --clean log "cd ios && pod install" (cd "$IOS_DIR" && pod install) ok "Clean abgeschlossen" case "$POST_ACTION" in build) echo "" log "Building + Running..." pnpm ios ;; xcode) echo "" log "Opening Xcode..." osascript -e 'tell application "Xcode" to close every window whose name contains "Rebreak.xcodeproj"' 2>/dev/null || true open -a Xcode "$IOS_DIR/ReBreak.xcworkspace" ok "Xcode geöffnet — Cmd+R für Build & Run" ;; "") echo "" echo "Nächste Schritte:" echo " ./dev.sh ios # Build & Run" echo " ./dev.sh ios --xcode # Xcode öffnen" ;; esac } cmd_install_ios() { section "iOS Standalone Install" local CONFIGURATION="Release" command -v xcrun >/dev/null 2>&1 || die "Xcode Command-Line-Tools fehlen — xcode-select --install" local XCTRACE_OUT XCTRACE_OUT=$(xcrun xctrace list devices 2>&1) local ONLINE ONLINE=$(printf '%s\n' "$XCTRACE_OUT" \ | awk '/^== Devices ==/{f=1; next} /^== /{f=0} f' \ | grep -E "iPhone|iPad" || true) if [[ -z "$ONLINE" ]]; then error "Kein iPhone/iPad ONLINE" echo "" echo "Setup:" echo " - iPhone via USB anschließen + entsperren" echo " - 'Diesem Computer vertrauen?' am iPhone bestätigen" echo " - Xcode öffnen, dort 'Use for Development' aktivieren" exit 1 fi log "Gerät online: $(printf '%s\n' "$ONLINE" | head -1)" log "Building iOS Release bundle + installing on device..." echo "" echo "(Erster Release-Build dauert 5-10 min wegen Pod-Install + Bundle)" echo "" npx expo run:ios --device --configuration "$CONFIGURATION" ok "App läuft jetzt standalone auf deinem iPhone" echo "Backend: https://staging.rebreak.org" echo "Free-Account: 7 Tage gültig, danach Skript erneut laufen lassen" } cmd_install_android() { section "Android Standalone Install" local SKIP_BUILD=false local LAUNCH=true while [[ $# -gt 0 ]]; do case "$1" in --no-build) SKIP_BUILD=true; shift ;; --no-launch) LAUNCH=false; shift ;; *) die "Unbekannter Flag für 'install android': $1" ;; esac done command -v adb >/dev/null 2>&1 || die "adb nicht gefunden — brew install --cask android-platform-tools" local DEVICE_COUNT DEVICE_COUNT=$(adb devices | grep -E '[[:space:]]device$' | grep -c '.' || true) if [[ "$DEVICE_COUNT" -eq 0 ]]; then error "Kein Android-Gerät via ADB" adb devices exit 1 fi local APK="$ANDROID_DIR/app/build/outputs/apk/debug/app-debug.apk" if ! $SKIP_BUILD; then log "Building debug APK..." (cd "$ANDROID_DIR" && ./gradlew assembleDebug --console=plain) fi [[ -f "$APK" ]] || die "APK nicht gefunden: $APK" log "Installing $APK..." adb install -r -d "$APK" if $LAUNCH; then log "Launching App..." adb shell monkey -p org.rebreak.app -c android.intent.category.LAUNCHER 1 >/dev/null 2>&1 || { warn "Launch schlug fehl — App ist installiert, manuell öffnen" } fi ok "Fertig" } cmd_magic() { local BUILD=true local LAUNCH=true local XCODE_ONLY=false local CONFIGURATION="Release" while [[ $# -gt 0 ]]; do case "$1" in --no-build) BUILD=false; shift ;; --no-launch) LAUNCH=false; shift ;; --xcode) XCODE_ONLY=true; shift ;; --debug) CONFIGURATION="Debug"; shift ;; *) die "Unbekannter Flag für 'magic': $1" ;; esac done section "RebreakMagic (macOS Wizard)" [[ -d "$MAGIC_DIR" ]] || die "rebreak-magic-mac nicht gefunden: $MAGIC_DIR" command -v xcodebuild >/dev/null 2>&1 || die "xcodebuild fehlt — xcode-select --install" cd "$MAGIC_DIR" # Xcode-Projekt aus project.yml generieren (idempotent) if command -v xcodegen >/dev/null 2>&1; then log "xcodegen generate" xcodegen generate >/dev/null else warn "xcodegen nicht installiert — überspringe project-regeneration (brew install xcodegen)" fi if $XCODE_ONLY; then log "Opening Xcode..." open -a Xcode "$MAGIC_DIR/RebreakMagic.xcodeproj" ok "Xcode geöffnet — Cmd+R für Build & Run" return 0 fi local BUILD_DIR="$MAGIC_DIR/build" local APP_PATH="$BUILD_DIR/Build/Products/$CONFIGURATION/RebreakMagic.app" if $BUILD; then log "xcodebuild ($CONFIGURATION)" xcodebuild \ -project RebreakMagic.xcodeproj \ -scheme RebreakMagic \ -configuration "$CONFIGURATION" \ -derivedDataPath "$BUILD_DIR" \ build | tail -20 [[ -d "$APP_PATH" ]] || die "Build fehlgeschlagen — $APP_PATH nicht gefunden" ok "Build erfolgreich: $APP_PATH" else [[ -d "$APP_PATH" ]] || die "App nicht vorhanden ($APP_PATH) — ohne --no-build neu starten" fi # Config-Check local CONFIG_FILE="$HOME/.config/rebreak-magic/config.json" if [[ ! -f "$CONFIG_FILE" ]]; then warn "Config fehlt: $CONFIG_FILE" echo " → Template kopieren: cp $MAGIC_DIR/config.example.json $CONFIG_FILE" echo " → Dann editieren: backendBaseUrl=https://staging.rebreak.org" fi if $LAUNCH; then log "Killing running RebreakMagic instance..." pkill -x RebreakMagic 2>/dev/null || true sleep 0.5 log "Launching $APP_PATH" open "$APP_PATH" ok "RebreakMagic gestartet" fi } # ═══════════════════════════════════════════════════════════════════════════ # Main # ═══════════════════════════════════════════════════════════════════════════ COMMAND="${1:-ios}" shift || true case "$COMMAND" in ios) cmd_ios "$@" ;; android) cmd_android "$@" ;; mobile) cmd_mobile "$@" ;; metro) cmd_metro "$@" ;; clean) cmd_clean "$@" ;; install) local PLATFORM="${1:-}" shift || true case "$PLATFORM" in ios) cmd_install_ios "$@" ;; android) cmd_install_android "$@" ;; *) error "Unbekannte install-Platform: $PLATFORM" echo "Verfügbar: ios, android" exit 1 ;; esac ;; magic) cmd_magic "$@" ;; -h|--help) awk '/^#!/{next} /^#/{sub(/^# ?/, ""); print; next} {exit}' "$0" exit 0 ;; *) error "Unbekanntes Subcommand: $COMMAND" echo "" echo "Verfügbare Commands:" echo " ios iOS Dev (Metro + Xcode/Simulator/Device)" echo " android Android Dev (Metro + Gradle + Install)" echo " mobile Auto-detect iPhone+Android via USB, parallel-Build" echo " metro Nur Metro starten" echo " clean iOS Nuclear Clean" echo " install ios Release-Build auf iPhone installieren" echo " install android Debug-APK auf Android installieren" echo " magic Build + Launch RebreakMagic.app (macOS)" echo "" echo "Nutze --help für Details" exit 1 ;; esac