#!/usr/bin/env bash # Plukker neste ugjorte oppgave fra tasks.md og starter en Claude Code-sesjon. # Hopper over oppgaver som er pågående [~], har åpent spørsmål [?], eller # er blokkert [!], inkludert alle oppgaver som avhenger av dem. # # Støtter parallell kjøring: markerer oppgaven som [~] (pågår) før start, # setter den til [x] eller tilbake til [ ] avhengig av resultat. # # Bruk: # ./scripts/run-next-task.sh # kjør neste oppgave # ./scripts/run-next-task.sh --dry # vis hvilken oppgave som er neste # ./scripts/run-next-task.sh --status # vis status for alle oppgaver # ./scripts/run-next-task.sh --unstale # frigjør [~] oppgaver eldre enn 60 min set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" TASKS="$ROOT/tasks.md" # --- Avhengighetskart: fase → faser den avhenger av --- declare -A PHASE_DEPS=( [1]="" [2]="1" [3]="2" [4]="2" [5]="3 4" [6]="2" [7]="6" [8]="5" [9]="3" [10]="2" [11]="5 6 7" [12]="1 2 3 4 5 6 7 8 9 10 11 13 14 15 16 17 18 19 20 21" [13]="3 4" [14]="6 13" [15]="3 10" [16]="11 13" [17]="" [18]="10 13" [19]="3 13" [20]="19" [21]="2" [22]="12" [23]="22" [24]="23" [25]="24" [26]="25" ) # --- Finn blokkerte faser --- # En fase er blokkert hvis den har en [?], [!] eller [~] oppgave blocked_phases() { local phases="" for phase in $(seq 1 21); do if grep -qP "^\- \[(\?|!|~)\] ${phase}\." "$TASKS" 2>/dev/null; then phases="$phases $phase" fi done echo "$phases" } # Sjekk om en fase er tilgjengelig phase_available() { local phase=$1 local blocked="$2" # Sjekk om denne fasen selv er blokkert if echo "$blocked" | grep -qw "$phase"; then return 1 fi # Sjekk om avhengige faser er blokkert eller har ugjorte oppgaver local deps="${PHASE_DEPS[$phase]}" for dep in $deps; do if echo "$blocked" | grep -qw "$dep"; then return 1 fi # Sjekk at alle oppgaver i avhengig fase er ferdige if grep -qP "^\- \[ \] ${dep}\." "$TASKS" 2>/dev/null; then return 1 fi done return 0 } # Sjekk at forrige oppgave i samme fase er ferdig prev_task_done() { local task_id=$1 # f.eks. "2.4" local phase=$(echo "$task_id" | cut -d. -f1) local num=$(echo "$task_id" | cut -d. -f2) if [[ "$num" == "1" ]]; then return 0 # Første oppgave i fasen, ingen forrige fi local prev_num=$((num - 1)) local prev_id="${phase}.${prev_num}" # Forrige oppgave må være [x] if grep -qP "^\- \[x\] ${prev_id} " "$TASKS" 2>/dev/null; then return 0 fi return 1 } # --- Frigjør stale [~] oppgaver --- if [[ "${1:-}" == "--unstale" ]]; then echo "Sjekker etter stale [~] oppgaver (>60 min)..." now=$(date +%s) changed=false while IFS= read -r line; do # Hent timestamp fra linjen under (> Påbegynt: 2026-03-17T14:30) line_num=$(echo "$line" | cut -d: -f1) next_line=$((line_num + 1)) ts=$(sed -n "${next_line}p" "$TASKS" | grep -oP '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}' || echo "") if [[ -n "$ts" ]]; then started=$(date -d "$ts" +%s 2>/dev/null || echo 0) elapsed=$(( (now - started) / 60 )) if [[ $elapsed -gt 60 ]]; then task_text=$(echo "$line" | cut -d: -f2-) echo " Frigjør (${elapsed} min gammel): $task_text" sed -i "${line_num}s/^\- \[~\]/- [ ]/" "$TASKS" sed -i "${next_line}d" "$TASKS" # Fjern påbegynt-linjen changed=true fi fi done < <(grep -n '^\- \[~\]' "$TASKS") if $changed; then echo "Oppdatert. Commit manuelt om ønsket." else echo "Ingen stale oppgaver funnet." fi exit 0 fi # --- Status-visning --- if [[ "${1:-}" == "--status" ]]; then echo "=== Synops oppgavestatus ===" echo "" done=$(grep -cP '^\- \[x\]' "$TASKS" || true) todo=$(grep -cP '^\- \[ \]' "$TASKS" || true) in_progress=$(grep -cP '^\- \[~\]' "$TASKS" || true) questions=$(grep -cP '^\- \[\?\]' "$TASKS" || true) blocked_count=$(grep -cP '^\- \[!\]' "$TASKS" || true) done=${done:-0}; todo=${todo:-0}; in_progress=${in_progress:-0} questions=${questions:-0}; blocked_count=${blocked_count:-0} total=$((done + todo + in_progress + questions + blocked_count)) echo "Ferdige: $done / $total" echo "Pågår: $in_progress" echo "Gjenstår: $todo" echo "Spørsmål: $questions" echo "Blokkert: $blocked_count" echo "" if [[ $in_progress -gt 0 ]]; then echo "--- Pågår ---" grep -A1 '^\- \[~\]' "$TASKS" || true echo "" fi if [[ $questions -gt 0 ]]; then echo "--- Åpne spørsmål ---" grep -A2 '^\- \[\?\]' "$TASKS" || true echo "" fi if [[ $blocked_count -gt 0 ]]; then echo "--- Blokkerte oppgaver ---" grep -A2 '^\- \[!\]' "$TASKS" || true fi exit 0 fi # --- Finn neste oppgave --- blocked=$(blocked_phases) next_task="" line_num="" task_text="" task_id="" while IFS= read -r line; do num=$(echo "$line" | cut -d: -f1) text=$(echo "$line" | cut -d: -f2- | sed 's/^- \[ \] //') # Hent oppgave-ID (f.eks. "1.3") id=$(echo "$text" | grep -oP '^\d+\.\d+' || echo "") phase=$(echo "$text" | grep -oP '^\d+' || echo "") if [[ -n "$phase" ]] && phase_available "$phase" "$blocked" && prev_task_done "$id"; then next_task="$line" line_num="$num" task_text="$text" task_id="$id" break fi done < <(grep -n '^\- \[ \]' "$TASKS") if [[ -z "$next_task" ]]; then if [[ -n "$blocked" ]]; then echo "Ingen tilgjengelige oppgaver. Blokkerte/pågående faser:$blocked" echo "Kjør --status for detaljer." else echo "Alle oppgaver er gjort!" fi exit 0 fi echo "Neste oppgave (linje $line_num):" echo " $task_text" echo "" if [[ "${1:-}" == "--dry" ]]; then exit 0 fi # --- Marker oppgaven som pågående --- timestamp=$(date +%Y-%m-%dT%H:%M) sed -i "${line_num}s/^\- \[ \]/- [~]/" "$TASKS" sed -i "${line_num}a\\ > Påbegynt: ${timestamp}" "$TASKS" cd "$ROOT" git add tasks.md git commit -m "Starter oppgave ${task_id}" --no-verify 2>/dev/null || true git push origin main 2>/dev/null || true # --- Bygg prompt --- PROMPT="Du skal implementere neste oppgave i Synops-prosjektet. Du jobber autonomt — ingen bruker er tilgjengelig for spørsmål underveis. ## Oppgave $task_text ## Arbeidsflyt 1. **Orienter deg.** Les \`CLAUDE.md\` for prosjektkontekst. Les dokumentene som refereres i oppgaven. Les \`tasks.md\` for å forstå hvor prosjektet står (hvilke oppgaver er ferdige, hva er tilgjengelig). 2. **Pull siste endringer.** Kjør \`git pull origin main\` først — andre agenter kan ha pushet endringer. 3. **Implementer.** Skriv kode. Du kjører direkte på serveren — bygg, test og deploy her. 4. **Verifiser.** Kompilering, curl-test, kjør relevante tester. 5. **Oppdater dokumentasjon.** Hvis implementeringen avviker fra eksisterende docs, oppdater dem. Nye tekniske beslutninger dokumenteres i relevante filer under \`docs/\`. Docs skal alltid reflektere faktisk tilstand. 6. **Oppdater tasks.md.** Endre \`- [~]\` til \`- [x]\` for oppgave ${task_id}. Fjern \`> Påbegynt: ...\`-linjen under oppgaven. 7. **Commit og push.** Commit alle endringer (kode + docs + tasks.md) med en beskrivende melding. Push til forgejo (\`git push origin main\`). Flere commits underveis er OK — push jevnlig så andre agenter ser fremgangen. ## Hvis noe blokkerer Hvis du trenger avklaring fra Vegard: - Endre oppgavens status fra \`- [~]\` til \`- [?]\` i tasks.md - Fjern påbegynt-linjen, legg til spørsmålet: \`\`\` - [?] ${task_id} ... > Spørsmål: > Kontekst: \`\`\` - Commit og push. Avslutt sesjonen. Hvis du støter på et teknisk problem du ikke kan løse: - Endre status fra \`- [~]\` til \`- [!]\` i tasks.md - Dokumenter problemet. Commit og push. Avslutt. ## Regler - Jobb kun på oppgave ${task_id}. Ikke start på neste. - Ikke deploy til produksjon uten eksplisitt godkjenning. - Følg eksisterende arkitektur og konvensjoner i \`docs/\`. - Hold det enkelt — minimum viable for oppgaven. - Dokumentasjon er like viktig som kode. Neste sesjon har ingen kontekst fra denne — alt den vet kommer fra kode, docs og git-historikk. - Skriv commit-meldinger som forklarer *hvorfor*, ikke bare *hva*. - Push jevnlig underveis — ikke samle opp alt til slutt. - Du kan spinne opp subagenter (Agent-tool) for parallelt arbeid der det er egnet — f.eks. research i docs, utforske kodebasen, eller kjøre uavhengige deloppgaver samtidig. Bruk det aktivt for å jobbe effektivt." # --- Velg modell og effort basert på fase --- CLAUDE_ARGS="--dangerously-skip-permissions" phase_num="${task_id%%.*}" case "$phase_num" in 23) # Validering krever grundig gjennomgang CLAUDE_ARGS="$CLAUDE_ARGS --model claude-opus-4-6" echo "Fase 23 (validering): bruker Opus med høy effort" ;; esac echo "Starter Claude Code-sesjon for oppgave ${task_id}..." cd "$ROOT" claude -p $CLAUDE_ARGS "$PROMPT" exit_code=$? # --- Hvis claude krasjer, sett oppgaven tilbake til [ ] --- if [[ $exit_code -ne 0 ]]; then echo "Claude-sesjonen feilet (exit code $exit_code). Tilbakestiller oppgave ${task_id}." # Sjekk om oppgaven fortsatt er [~] (ikke allerede endret av sesjonen) if grep -qP "^\- \[~\] ${task_id} " "$TASKS"; then sed -i "/^\- \[~\] ${task_id} /s/^\- \[~\]/- [ ]/" "$TASKS" # Fjern påbegynt-linjen line_after=$(grep -n "^\- \[ \] ${task_id} " "$TASKS" | cut -d: -f1) if [[ -n "$line_after" ]]; then next=$((line_after + 1)) if sed -n "${next}p" "$TASKS" | grep -q '> Påbegynt:'; then sed -i "${next}d" "$TASKS" fi fi git add tasks.md git commit -m "Tilbakestill oppgave ${task_id} etter feilet sesjon" --no-verify 2>/dev/null || true git push origin main 2>/dev/null || true fi fi