{"slug": "daemonless-ticket-queue-poller-for-the-sonu-plugin-cron-wrapper-that-wakes-a-a", "title": "Daemonless ticket-queue poller for the sonu plugin — cron wrapper that wakes a headless agent session only when a trigger label or in-flight ticket branch exists", "summary": "PrabhdeepSingh's sonu plugin introduces a daemonless ticket-queue poller, a cron wrapper that wakes a headless agent session only when a trigger label or in-flight ticket branch exists. The script, factory-poll.sh, uses flock for serialization and checks for specific labels and ticket branches to minimize API calls and token usage.", "body_md": "| #!/usr/bin/env bash | |\n| # factory-poll.sh — the entire \"daemon\" for the sonu plugin's ticket queue. | |\n| # | |\n| # Polls a repo's ticket queue and, only when there is something to do, spawns | |\n| # one headless agent session that runs /sonu:factory (scan, claim, route, | |\n| # build in a worktree, sweep). All the intelligence lives in the plugin; | |\n| # this script only decides whether it's worth waking the agent. | |\n| # | |\n| # Requires: the sonu plugin installed (github.com/PrabhdeepSingh/claude-plugins), | |\n| # an authenticated `gh` CLI, and a tracker configured via /sonu:factory init. | |\n| # | |\n| # Install (cron, every 15 minutes): | |\n| # */15 * * * * $HOME/.sonu/factory-poll.sh | |\n| # Or on macOS, a launchd plist with StartInterval 900. | |\n| # | |\n| # Notes: | |\n| # - flock keeps runs serialized (one at a time), matching one-ticket-per-pass. | |\n| # - The trigger labels are queried separately: `gh issue list` with two | |\n| # --label flags is AND semantics, and a ticket only ever carries one trigger. | |\n| # - In-flight ticket/* branches also wake the agent, or the close-the-loop | |\n| # sweep would starve once triggers are consumed. | |\n| # - Empty queue costs two API calls and zero tokens. | |\n| set -u | |\n| REPO_DIR=\"/path/to/repo\" # <-- set me | |\n| cd \"$REPO_DIR\" || exit 1 | |\n| mkdir -p \"$HOME/.sonu\" | |\n| exec 9>\"$HOME/.sonu/factory.lock\" | |\n| flock -n 9 || exit 0 # a previous run is still going | |\n| queued=0 | |\n| for label in factory-ready-for-spec factory-ready-to-implement; do | |\n| n=$(gh issue list --state open --label \"$label\" --json number --jq 'length' 2>/dev/null || echo 0) | |\n| queued=$((queued + n)) | |\n| done | |\n| # Tickets mid-flight carry no trigger by definition; their ticket/* branches | |\n| # are what the sweep needs to see (status flips, worktree cleanup). | |\n| inflight=$(git branch --list 'ticket/*' | wc -l | tr -d ' ') | |\n| [ \"$((queued + inflight))\" -gt 0 ] || exit 0 # nothing queued, nothing to sweep | |\n| timeout 2h claude -p \"/sonu:factory\" >> \"$HOME/.sonu/factory.log\" 2>&1 |", "url": "https://wpnews.pro/news/daemonless-ticket-queue-poller-for-the-sonu-plugin-cron-wrapper-that-wakes-a-a", "canonical_source": "https://gist.github.com/PrabhdeepSingh/ab71a961d46e9d6e72f202fb8c5bd06b", "published_at": "2026-08-01 00:47:18+00:00", "updated_at": "2026-08-01 01:00:07.875003+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents"], "entities": ["PrabhdeepSingh", "sonu plugin", "claude-plugins", "gh CLI"], "alternates": {"html": "https://wpnews.pro/news/daemonless-ticket-queue-poller-for-the-sonu-plugin-cron-wrapper-that-wakes-a-a", "markdown": "https://wpnews.pro/news/daemonless-ticket-queue-poller-for-the-sonu-plugin-cron-wrapper-that-wakes-a-a.md", "text": "https://wpnews.pro/news/daemonless-ticket-queue-poller-for-the-sonu-plugin-cron-wrapper-that-wakes-a-a.txt", "jsonld": "https://wpnews.pro/news/daemonless-ticket-queue-poller-for-the-sonu-plugin-cron-wrapper-that-wakes-a-a.jsonld"}}