#!/usr/bin/env bash # Plukk neste oppgave fra tasks/ og kjør med Claude Code. # Bruk: # ./scripts/run-next-task.sh # kjør én oppgave # ./scripts/run-next-task.sh --loop # kjør oppgaver i loop # ./scripts/run-next-task.sh --dry # vis neste uten å kjøre # ./scripts/run-next-task.sh --status # vis status set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" TASKS="$ROOT/tasks" ACTIVE="$TASKS/active" DONE="$TASKS/done" LOGS="$ROOT/logs" LOCKFILE="/tmp/synops-task-runner.lock" mkdir -p "$ACTIVE" "$DONE" "$LOGS" # Lås — hindrer dobbeltstart if [ -f "$LOCKFILE" ]; then PID=$(cat "$LOCKFILE" 2>/dev/null || echo "") if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then echo "Allerede en task-runner aktiv (PID $PID)" exit 0 fi rm -f "$LOCKFILE" fi echo $$ > "$LOCKFILE" trap 'rm -f "$LOCKFILE"' EXIT # --- Krasj-deteksjon: frigjør stale oppgaver --- unstale() { for f in "$ACTIVE"/*.md; do [ -f "$f" ] || continue age_min=$(( ($(date +%s) - $(stat -c %Y "$f")) / 60 )) if [ "$age_min" -gt 60 ]; then echo "Frigjør stale oppgave: $(basename "$f") ($age_min min)" mv "$f" "$TASKS/" fi done } # --- Status --- if [[ "${1:-}" == "--status" ]]; then todo=$(ls "$TASKS"/*.md 2>/dev/null | grep -v README | wc -l) active=$(ls "$ACTIVE"/*.md 2>/dev/null | wc -l) done_count=$(ls "$DONE"/*.md 2>/dev/null | wc -l) echo "=== Synops oppgavestatus ===" echo "Gjenstår: $todo" echo "Aktiv: $active" echo "Fullført: $done_count" if [ "$active" -gt 0 ]; then echo "" echo "--- Aktive ---" ls "$ACTIVE"/*.md 2>/dev/null | xargs -I{} basename {} fi echo "" echo "--- Neste 5 ---" ls "$TASKS"/*.md 2>/dev/null | grep -v README | sort | head -5 | xargs -I{} basename {} exit 0 fi # --- Kjør én oppgave --- run_one() { unstale TASK=$(ls "$TASKS"/*.md 2>/dev/null | grep -v README | sort | head -1) if [ -z "$TASK" ]; then echo "Ingen oppgaver å gjøre" return 1 fi TASKNAME=$(basename "$TASK") echo "=== Plukker: $TASKNAME ===" if [[ "${1:-}" == "--dry" ]]; then cat "$TASK" return 0 fi # Flytt til active mv "$TASK" "$ACTIVE/$TASKNAME" TASK_CONTENT=$(cat "$ACTIVE/$TASKNAME") cd "$ROOT" git pull origin main 2>/dev/null || true # Kjør Claude Code LOGFILE="$LOGS/task-$(date +%Y%m%d-%H%M)-$TASKNAME.log" timeout 1800 claude -p " Du har fått denne oppgaven: $TASK_CONTENT Arbeidsmappe: $ROOT Arbeidsflyt: 1. Les CLAUDE.md for prosjektkontekst 2. Les refererte docs/proposals 3. Implementer 4. Verifiser (cargo check, npm run build) 5. Commit og push 6. Rapporter hva du gjorde Regler: - Les relevante filer før du endrer dem - Gjør minimale, fokuserte endringer - Push jevnlig underveis " --dangerously-skip-permissions 2>&1 | tee "$LOGFILE" EXIT_CODE=${PIPESTATUS[0]} # Verifiser at det faktisk ble gjort kodeendringer (ikke bare snakk) cd "$ROOT" COMMITS_AFTER=$(git log --oneline --since="5 minutes ago" -- ':(exclude)tasks/' | wc -l) FILES_CHANGED=$(git diff --name-only HEAD~${COMMITS_AFTER:-1}..HEAD -- ':(exclude)tasks/' 2>/dev/null | wc -l) if [ "$EXIT_CODE" -eq 0 ] && [ "${FILES_CHANGED:-0}" -gt 0 ]; then mv "$ACTIVE/$TASKNAME" "$DONE/$(date +%Y-%m-%d)-$TASKNAME" echo "=== Fullført: $TASKNAME ($FILES_CHANGED filer endret, $COMMITS_AFTER commits) ===" git add -A tasks/ && git commit -m "Task fullført: $TASKNAME" --no-verify 2>/dev/null || true git push origin main 2>/dev/null || true return 0 else mv "$ACTIVE/$TASKNAME" "$TASKS/$TASKNAME" if [ "$EXIT_CODE" -ne 0 ]; then echo "=== Feilet (exit $EXIT_CODE): $TASKNAME ===" else echo "=== Ingen kodeendring: $TASKNAME (flyttet tilbake til kø) ===" fi return 1 fi } # --- Dry run --- if [[ "${1:-}" == "--dry" ]]; then unstale TASK=$(ls "$TASKS"/*.md 2>/dev/null | grep -v README | sort | head -1) if [ -z "$TASK" ]; then echo "Ingen oppgaver" else echo "Neste: $(basename "$TASK")" echo "---" cat "$TASK" fi exit 0 fi # --- Loop-modus --- if [[ "${1:-}" == "--loop" ]]; then echo "=== Task runner loop startet ($(date)) ===" while true; do if ! run_one; then echo "Ingen flere oppgaver eller feil. Venter 5 min..." sleep 300 else echo "Venter 30 sek før neste oppgave..." sleep 30 fi done fi # --- Enkel kjøring --- run_one