← back to Rentv 2026
ops/buildloop-cycle.sh
42 lines
#!/bin/bash
# RENTV autonomous build loop — one /yoloforever cycle, headless + SANDBOXED.
# Installed via launchd (com.steve.rentv-buildloop) so it survives restarts.
#
# SAFETY MODEL: this runs `claude -p` with a RESTRICTED allowedTools set — it can
# read/edit/write files and run git/node/npm, but it has NO tool to run deploy.sh,
# curl, or any paid API. So it CANNOT deploy to prod or spend money; gated work is
# structurally forced to a draft in ~/.claude/yolo-queue/pending-approval/ for Steve.
# Stop it: launchctl bootout gui/$(id -u)/com.steve.rentv-buildloop
set -euo pipefail
cd "$HOME/Projects/rentv" || exit 1
mkdir -p "$HOME/Projects/rentv/ops"
LOG="$HOME/Projects/rentv/ops/buildloop.log"
# Trim the log if it grows past ~5MB.
[ -f "$LOG" ] && [ "$(wc -c <"$LOG")" -gt 5000000 ] && tail -c 2000000 "$LOG" > "$LOG.tmp" && mv "$LOG.tmp" "$LOG"
PROMPT='/yoloforever continue — autonomous build loop for the RENTV project (~/Projects/rentv, live at rentv.agentabrams.com; surfaces: pr-intelligence CRM, Content Studio, Deal Desk, /audience, /blog + front-end). STANDING OBJECTIVE: harden/extend rentv. THIS cycle pick ONE highest-value SAFE reversible item and finish it: a real bug fix, UI/UX polish, enforce Steve standing rules (admin cards show created date+time; every data point hrefs deeper; grids have sort+density; lists have adjustable/expandable columns), CA/AZ data completeness, Content Studio / Deal Desk / Boomer improvements, dedup/quality. VERIFY any claimed defect by reproducing before fixing (drop phantoms). Do reversible work directly (edit + node --check) and git commit each success (author steve@designerwallcoverings.com). YOU HAVE NO DEPLOY OR SPEND TOOL — for any gated action (prod deploy, DNS, ElevenLabs/Gemini/HeyGen spend, send-to-list, publish, remote push) WRITE a memo to ~/.claude/yolo-queue/pending-approval/ with APPROVE/REVISE/BLOCK; never attempt to run it. If nothing safe+valuable remains, write a short ops/buildloop.log note and exit. One increment per run; do not loop internally.'
# `claude` is a shell function in interactive shells; launchd runs non-interactive, so pin the real binary.
CLAUDE_BIN="$HOME/.local/bin/claude"; [ -x "$CLAUDE_BIN" ] || CLAUDE_BIN="$(command -v claude)"
# TK-10105: the heygen CLI authenticates from EITHER ~/.heygen/credentials OR $HEYGEN_API_KEY.
# Under launchd/sandbox the credentials file may be unreachable and this env is empty, so the
# CLI dies with "Not logged in" / "no API key found". Export the valid key from the heygen skill
# .env (the secrets-manager routes HEYGEN_API_KEY there) so any auth/list/whoami check works.
# NOTE: this only enables AUTH — actual renders spend money and remain gated: this loop has no
# curl/spend tool and the prompt forces HeyGen renders to a pending-approval memo, never a live call.
HEYGEN_ENV="$HOME/.claude/skills/heygen-video/.env"
if [ -z "${HEYGEN_API_KEY:-}" ] && [ -f "$HEYGEN_ENV" ]; then
HEYGEN_API_KEY="$(grep -E '^HEYGEN_API_KEY=' "$HEYGEN_ENV" | head -1 | cut -d= -f2- | tr -d '\r\n')"
export HEYGEN_API_KEY
fi
{
echo "==================== $(date '+%Y-%m-%d %H:%M:%S') cycle start ===================="
"$CLAUDE_BIN" -p "$PROMPT" \
--model sonnet \
--allowedTools "Read" "Edit" "Write" "Grep" "Glob" "Bash(git:*)" "Bash(node:*)" "Bash(npm:*)" \
2>&1 || echo "[cycle exited non-zero: $?]"
echo "==================== $(date '+%Y-%m-%d %H:%M:%S') cycle end ===================="
} >> "$LOG" 2>&1