#!/usr/bin/env bash # TVAnarchy task runner. Thin, discoverable wrapper over the repo's build/test # scripts so the common actions have one obvious name. Add a target by giving it # a `task::` function below — it shows up in `./run help` automatically. # # ./run # Debug build + relaunch (the everyday dev loop) # ./run dev # same # ./run deploy # Release install + relaunch (+ iOS when a phone is up) # ./run test # Run Xcode unit tests # ./run clean # Remove generated xcodeproj + build/ # ./run governor # Run the governor (portable-net-tv) # ./run mcp # Run the MCP server (stdio) # ./run bridge # Run the HTTP bridge for iOS # ./run typecheck # TS typecheck for helpers # ./run test:all # App tests + governor + mcp + search set -euo pipefail cd "$(dirname "$0")" DD="${TVANARCHY_DD:-build/dd}" relaunch_tv_anarchy() { local app="$1" if pgrep -fq "TVAnarchy.app/Contents/MacOS"; then echo "→ quit running TVAnarchy" osascript -e 'quit app "TVAnarchy"' || true for _ in $(seq 1 20); do pgrep -fq "TVAnarchy.app/Contents/MacOS" || break sleep 0.5 done fi echo "→ open $app" open "$app" } # Debug build from build/dd and relaunch — the default `./run`. # Everyday loop: stamp → xcodegen → debug build → install to Applications → relaunch. task::dev() { echo "→ stamp build identity" tools/stamp-build.sh echo "→ xcodegen generate" xcodegen generate >/dev/null local app="$DD/Build/Products/Debug/TVAnarchy.app" echo "→ xcodebuild (Debug → $app)" xcodebuild -scheme TVAnarchy -configuration Debug -derivedDataPath "$DD" \ -destination 'platform=macOS' build CODE_SIGNING_ALLOWED=NO \ 2>&1 | grep -E 'BUILD SUCCEEDED|BUILD FAILED|error:' || true [ -d "$app" ] || { echo "✗ build produced no app at $app" >&2; exit 1; } # Install to /Applications (or ~/Applications) so Spotlight launches this build, # not a stale copy left from an old release cut. . tools/platform.sh local dest dest="$(tva_resolve_dest mac)" mkdir -p "$(dirname "$dest")" rm -rf "$dest" cp -R "$app" "$dest" VER=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$dest/Contents/Info.plist") BUILD=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleVersion' "$dest/Contents/Info.plist") echo "✓ installed v$VER (build $BUILD) → $dest" relaunch_tv_anarchy "$dest" } # Release install to Applications (+ iOS when reachable). See deploy.sh. task::deploy() { ./deploy.sh "$@"; } # plum: build Release and install to ~/Applications or /Applications (no relaunch). task::update:plum() { ./build-install.sh "$@"; } # Regenerate the Xcode project from project.yml. task::generate() { xcodegen generate; } # Run the unit-test bundle (regenerates the project first so new files are picked # up). Pass extra args straight through, e.g. `./run test -only-testing:...`. task::help() { usage; } task::test() { xcodegen generate >/dev/null xcodebuild test -project TVAnarchy.xcodeproj -scheme TVAnarchy \ -destination 'platform=macOS' "$@" } # Clean generated Xcode project and derived data (safe; no source touch). task::clean() { echo "→ rm -rf build/ TVAnarchy.xcodeproj" rm -rf build/ TVAnarchy.xcodeproj } # Typecheck the TypeScript helpers (governor + mcp). task::typecheck() { echo "→ governor typecheck" (cd governor && bun run typecheck) echo "→ mcp typecheck" (cd mcp && bun run typecheck) } # Run the governor (portable-net-tv) — watch tracking + fleet engine on plum. # Example: ./run governor fleet status task::governor() { (cd governor && bun run src/index.ts "$@") } # Run the mcp stdio server (for MCP clients like Claude). task::mcp() { (cd mcp && bun run src/index.ts "$@") } # Run the HTTP bridge (for iOS companion; default :8787). task::bridge() { (cd mcp && bun run src/http.ts "$@") } # Deploy to phone (iOS build + sideload via tools/deploy-phone.sh). task::deploy:phone() { tools/deploy-phone.sh "$@"; } # Run full tests: app unit tests + governor + mcp (+ search pytest if present). task::test:all() { task::test echo "→ governor test" (cd governor && bun test) echo "→ mcp test" (cd mcp && bun test) if [ -f search/pyproject.toml ]; then echo "→ search pytest" (cd search && uv run pytest -q || true) fi } usage() { echo "usage: ./run [target] [args...]" echo echo " ./run, ./run dev Debug build (stamp + xcodegen + Debug xcodebuild) + install to" echo " Applications (or ~/Applications) + quit + relaunch TVAnarchy." echo " This is the normal local dev loop (uses build/dd by default;" echo " set TVANARCHY_DD to relocate)." echo echo "Common targets:" echo " deploy Release build + publish flow (see deploy.sh)" echo " update:plum Release build + install (no relaunch; see build-install.sh)" echo " deploy:phone Build + install to connected iOS device" echo " test Run macOS unit tests (xcodebuild test)" echo " test:all App tests + governor bun test + mcp bun test + search pytest" echo " clean rm -rf build/ TVAnarchy.xcodeproj" echo " generate xcodegen generate (project.yml → .xcodeproj)" echo " governor [...] Run governor (portable-net-tv) with args (e.g. fleet status)" echo " mcp [...] Run mcp stdio server" echo " bridge [...] Run mcp HTTP bridge (for iOS companion)" echo " typecheck tsc --noEmit in governor/ and mcp/" echo echo "All targets (auto-discovered):" declare -F | sed -n 's/^declare -f task::/ /p' | grep -v ' help$' echo echo "See docs/operations.md and the top of this file for more." } main() { local target="${1:-dev}" [ "$target" != help ] || { usage; exit 0; } shift || true if ! declare -F "task::$target" >/dev/null; then echo "✗ unknown target: $target" >&2 echo >&2 usage >&2 exit 1 fi "task::$target" "$@" } main "$@"