run.sh.template 1.4 KB

12345678910111213141516171819202122232425262728
  1. #!/usr/bin/env bash
  2. # loop-run.sh - one tick of the <loop-name> loop. RUNNER-AGNOSTIC: point any scheduler
  3. # at it - cron, Windows Task Scheduler, a systemd timer, process-compose, or run by hand.
  4. # No GitHub Actions required. The scheduler is the authorizer; this runs a gated `claude -p`
  5. # (dontAsk + an allowlist), never bypassPermissions on a shared host. See
  6. # references/claude-code-loops.md for cron / Task Scheduler wiring.
  7. set -uo pipefail
  8. HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  9. cd "$HERE"
  10. # 1. Kill switch first - a PAUSED sentinel here, or the 'loop-pause' label if you use one.
  11. if [ -f PAUSED ]; then echo "<loop-name>: paused (PAUSED sentinel) - skipping tick" >&2; exit 0; fi
  12. command -v claude >/dev/null 2>&1 || { echo "<loop-name>: 'claude' not on PATH" >&2; exit 5; }
  13. # 2. Run one tick. SAME prompt every time (cache-friendly); fresh context each run.
  14. # Add this loop's own tools to --allowedTools (e.g. 'Bash(gh pr list:*)').
  15. claude -p "$(cat run.md)" \
  16. --permission-mode <permission-mode> \
  17. --append-system-prompt "$(cat STATE.md)" \
  18. --allowedTools 'Read' 'Write(STATE.md)' 'Write(run-log.md)' \
  19. --max-turns 30
  20. # 3. Persist STATE + run-log if this dir lives in a git repo (skipped otherwise).
  21. if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
  22. git add STATE.md run-log.md 2>/dev/null || true
  23. git diff --cached --quiet 2>/dev/null || git commit -q -m "chore(loop): <loop-name> tick" || true
  24. fi