| #!/usr/bin/env bash | |
| # factory-poll.sh — the entire "daemon" for the sonu plugin's ticket queue. | |
| # | |
| # Polls a repo's ticket queue and, only when there is something to do, spawns | |
| # one headless agent session that runs /sonu:factory (scan, claim, route, | |
| # build in a worktree, sweep). All the intelligence lives in the plugin; | |
| # this script only decides whether it's worth waking the agent. | |
| # | |
| # Requires: the sonu plugin installed (github.com/PrabhdeepSingh/claude-plugins), | |
| # an authenticated gh CLI, and a tracker configured via /sonu:factory init. | |
| # | |
| # Install (cron, every 15 minutes): | |
| # /15 * * * * $HOME/.sonu/factory-poll.sh | |
| # Or on macOS, a launchd plist with StartInterval 900. | |
| # | |
| # Notes: | |
| # - flock keeps runs serialized (one at a time), matching one-ticket-per-pass. | |
| # - The trigger labels are queried separately: gh issue list with two | |
| # --label flags is AND semantics, and a ticket only ever carries one trigger. | |
| # - In-flight ticket/ branches also wake the agent, or the close-the-loop | |
| # sweep would starve once triggers are consumed. | |
| # - Empty queue costs two API calls and zero tokens. | |
| set -u | |
| REPO_DIR="/path/to/repo" # <-- set me | |
| cd "$REPO_DIR" || exit 1 | |
| mkdir -p "$HOME/.sonu" | |
| exec 9>"$HOME/.sonu/factory.lock" | |
| flock -n 9 || exit 0 # a previous run is still going | |
| queued=0 | |
| for label in factory-ready-for-spec factory-ready-to-implement; do | |
| n=$(gh issue list --state open --label "$label" --json number --jq 'length' 2>/dev/null || echo 0) | |
| queued=$((queued + n)) | |
| done | | | # Tickets mid-flight carry no trigger by definition; their ticket/* branches | | | # are what the sweep needs to see (status flips, worktree cleanup). | |
| inflight=$(git branch --list 'ticket/*' | wc -l | tr -d ' ') | |
| [ "$((queued + inflight))" -gt 0 ] || exit 0 # nothing queued, nothing to sweep | |
| timeout 2h claude -p "/sonu:factory" >> "$HOME/.sonu/factory.log" 2>&1 |