#!/usr/bin/env bash set -euo pipefail ROOT="$(cd "$(dirname "$0")" && pwd)" API_DIR="$ROOT/@applications/api" WEB_DIR="$ROOT/@applications/web" AI_DIR="$HOME/Code/@applications/@ai/services/ai-core" # shellcheck source=/dev/null source "${HOME}/Code/@packages/@ts/@cli/bash-templates/lib/colors.sh" C=$CYAN G=$GREEN Y=$YELLOW M=$MAGENTA B=$BOLD R=$NC D=$DIM cmd_help() { echo -e "${B}companion${R} — unified project runner\n" echo -e "${C}${B}Dev Workflow${R}" echo -e " ${G}dev${R} Start API + web ${D}pnpm dev${R}" echo -e " ${G}dev:all${R} Start @ai + API + web ${D}all 3 services${R}" echo -e " ${G}dev:ai${R} Start @ai service only ${D}nest start --watch${R}" echo -e " ${G}dev:api${R} Start API only ${D}nest start --watch${R}" echo -e " ${G}dev:web${R} Start web only ${D}vite${R}" echo -e " ${G}build${R} Build all packages ${D}pnpm -r build${R}" echo -e " ${G}typecheck${R} Type-check everything ${D}pnpm -r typecheck${R}" echo -e " ${G}lint${R} Lint all packages ${D}pnpm -r lint${R}" echo "" echo -e "${C}${B}Testing${R}" echo -e " ${G}test${R} Unit tests ${D}pnpm -r test${R}" echo -e " ${G}test:e2e${R} Playwright E2E tests ${D}playwright test${R}" echo -e " ${G}e2e:docker${R} Docker E2E stack ${D}docker compose e2e${R}" echo "" echo -e "${C}${B}Docker${R}" echo -e " ${G}docker${R} Start Postgres + Redis ${D}docker compose up -d${R}" echo -e " ${G}docker:stop${R} Stop Postgres + Redis ${D}docker compose down${R}" echo -e " ${G}docker:status${R} Show container status ${D}docker compose ps${R}" echo "" echo -e "${C}${B}Database${R}" echo -e " ${G}db:migrate${R} Run dev migrations ${D}typeorm migration:run${R}" echo -e " ${G}db:generate${R} ${Y}${R} Generate migration ${D}typeorm migration:generate${R}" echo -e " ${G}db:reset${R} Drop + recreate dev database ${D}dropdb + createdb${R}" echo "" echo -e "${M}Usage:${R} ./run [args...]" } case "${1:-help}" in # Dev workflow dev) echo -e "${C}Starting companion-api + companion-web...${R}" cd "$ROOT" && pnpm dev ;; dev:all) echo -e "${C}Starting @ai + companion-api + companion-web...${R}" # Start docker infra if not running if ! docker ps --filter "name=lilith-companion-postgres" --format '{{.Status}}' | grep -q "Up"; then echo -e "${Y}Starting docker infra...${R}" cd "$ROOT/@deployments" && docker compose up -d sleep 3 fi if ! docker ps --filter "name=lilith-ai-postgres" --format '{{.Status}}' | grep -q "Up"; then echo -e "${Y}Starting @ai docker infra...${R}" cd "$HOME/Code/@applications/@ai" && docker compose up -d sleep 3 fi # Start all 3 services trap 'kill 0' EXIT (cd "$AI_DIR" && pnpm start:dev) & sleep 3 (cd "$API_DIR" && pnpm dev) & (cd "$WEB_DIR" && pnpm dev) & echo -e "${G}All services starting...${R}" echo -e " @ai: http://localhost:3790" echo -e " companion-api: http://localhost:3850" echo -e " companion-web: http://localhost:5850" echo -e "${D}Press Ctrl+C to stop all${R}" wait ;; dev:ai) echo -e "${C}Starting @ai service...${R}" cd "$AI_DIR" && pnpm start:dev ;; dev:api) echo -e "${C}Starting companion-api...${R}" cd "$API_DIR" && pnpm dev ;; dev:web) echo -e "${C}Starting companion-web...${R}" cd "$WEB_DIR" && pnpm dev ;; build) echo -e "${C}Building all packages...${R}" cd "$ROOT" && pnpm build ;; typecheck) echo -e "${C}Type-checking...${R}" cd "$ROOT" && pnpm typecheck ;; lint) echo -e "${C}Linting...${R}" cd "$ROOT" && pnpm lint ;; # Testing test) echo -e "${C}Running unit tests...${R}" cd "$ROOT" && pnpm test ;; test:e2e) shift echo -e "${C}Running E2E tests...${R}" cd "$ROOT/@tooling/e2e" && npx playwright test "$@" ;; e2e:docker) echo -e "${C}Running E2E tests in Docker...${R}" cd "$ROOT" && docker compose \ -f "@tooling/e2e/docker-compose.e2e.yml" \ up --build --abort-on-container-exit --exit-code-from e2e-tests ;; # Docker (local containers) docker) echo -e "${C}Starting Postgres + Redis...${R}" cd "$ROOT/@deployments" && docker compose up -d ;; docker:stop) echo -e "${C}Stopping containers...${R}" cd "$ROOT/@deployments" && docker compose down ;; docker:status) cd "$ROOT/@deployments" && docker compose ps ;; # Database db:migrate) echo -e "${C}Running database migrations...${R}" cd "$API_DIR" if [ -f "$ROOT/.env" ]; then set -a; source "$ROOT/.env"; set +a fi pnpm migration:run ;; db:generate) if [[ -z "${2:-}" ]]; then echo -e "${Y}Usage:${R} ./run db:generate " exit 1 fi cd "$API_DIR" if [ -f "$ROOT/.env" ]; then set -a; source "$ROOT/.env"; set +a fi pnpm migration:generate "src/migrations/$2" ;; db:reset) if [ -f "$ROOT/.env" ]; then set -a; source "$ROOT/.env"; set +a fi DB="${DATABASE_NAME:-companion}" if [[ "$DB" == *prod* ]]; then echo -e "${Y}Refusing to reset production database: ${DB}${R}" exit 1 fi echo -e "${Y}Dropping and recreating dev database: ${DB}${R}" PGPASSWORD="$DATABASE_PASSWORD" psql \ -h "${DATABASE_HOST:-localhost}" \ -p "${DATABASE_PORT:-26397}" \ -U "${DATABASE_USER:-lilith}" \ -d postgres \ -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '$DB' AND pid <> pg_backend_pid();" \ > /dev/null 2>&1 PGPASSWORD="$DATABASE_PASSWORD" dropdb \ -h "${DATABASE_HOST:-localhost}" \ -p "${DATABASE_PORT:-26397}" \ -U "${DATABASE_USER:-lilith}" \ --if-exists "$DB" PGPASSWORD="$DATABASE_PASSWORD" createdb \ -h "${DATABASE_HOST:-localhost}" \ -p "${DATABASE_PORT:-26397}" \ -U "${DATABASE_USER:-lilith}" \ "$DB" PGPASSWORD="$DATABASE_PASSWORD" psql \ -h "${DATABASE_HOST:-localhost}" \ -p "${DATABASE_PORT:-26397}" \ -U "${DATABASE_USER:-lilith}" \ "$DB" \ -c 'CREATE EXTENSION IF NOT EXISTS pgcrypto;' \ > /dev/null echo -e "${G}Database ${DB} reset.${R}" ;; # Help help|--help|-h) cmd_help ;; *) echo -e "${Y}Unknown command: $1${R}" cmd_help exit 1 ;; esac