#!/bin/bash # Mantiene objetivo+DoD coherentes con los prompts acumulados del usuario y avisa # si la terminal empieza a mezclar tareas. Lo lanza goal_tracker.sh en background # (no bloquea). Usa ask_llm (haiku, API directa; nunca `claude -p`). # # Args: [force] # force: regenera siempre objetivo+DoD desde los prompts y limpia la alerta # (lo usa el comando "recalcular"). # # Decision normal (sin force): el modelo devuelve # {"action":"same|refine|switch","goal":"...","dod":"..."} # - same : el objetivo ya describe bien el conjunto -> no se toca nada. # - refine : ajusta objetivo/DoD para englobar mejor TODO lo pedido (estable). # - switch : el ultimo prompt es una tarea COMPLETAMENTE distinta -> alert=true # (una terminal deberia ser una sola tarea). No cambia el objetivo. SID="$1" F="$2" FORCE="$3" [ -f "$F" ] || exit 0 PY="$HOME/fn_registry/python/.venv/bin/python3" ASK="$HOME/fn_registry/python/functions/core/ask_llm.py" [ -x "$PY" ] || exit 0 [ -f "$ASK" ] || exit 0 GOAL=$(jq -r '.goal // ""' "$F" 2>/dev/null) [ -z "$GOAL" ] && exit 0 DOD=$(jq -r '.dod // ""' "$F" 2>/dev/null) PROMPTS=$(jq -r '(.prompts // []) | to_entries | map("\(.key+1). \(.value)") | join("\n")' "$F" 2>/dev/null) [ -z "$PROMPTS" ] && exit 0 if [ "$FORCE" = "force" ]; then SYS="Regenera el OBJETIVO y el DoD de una terminal de trabajo a partir de la lista de prompts del usuario, sintetizando lo que se esta haciendo realmente. Responde SOLO JSON en una linea, sin markdown: {\"action\":\"refine\",\"goal\":\"...\",\"dod\":\"...\"}. goal: maximo 9 palabras en espanol. dod: condicion concreta de terminado, maximo 9 palabras en espanol." else SYS="Mantienes coherentes el OBJETIVO y el DoD de una terminal de trabajo segun los prompts del usuario. Principio: una terminal = una sola tarea. Analiza si el conjunto de prompts sigue describiendo UNA tarea coherente. Responde SOLO JSON en una linea, sin markdown: {\"action\":\"same|refine|switch\",\"goal\":\"...\",\"dod\":\"...\"}. action=same: el objetivo actual ya describe bien el conjunto (deja goal y dod vacios, no se cambia nada). action=refine: conviene ajustar objetivo/DoD para englobar mejor TODO lo pedido, manteniendolo estable y breve (goal y dod max 9 palabras cada uno, en espanol). action=switch: el ULTIMO prompt introduce una tarea COMPLETAMENTE distinta y no relacionada con el objetivo actual (la terminal esta mezclando tareas); deja goal y dod vacios. Se conservador con 'switch': uselo solo ante un cambio claro de tema, no por matices o sub-tareas del mismo objetivo." fi PROMPT="OBJETIVO ACTUAL: ${GOAL} DoD ACTUAL: ${DOD} PROMPTS DEL USUARIO (orden cronologico): ${PROMPTS} Responde el JSON:" RAW=$("$PY" "$ASK" --system "$SYS" "$PROMPT" 2>/dev/null) [ -z "$RAW" ] && exit 0 JSON=$(printf '%s' "$RAW" | tr '\n' ' ' | grep -o '{[^{}]*}' | head -1) [ -z "$JSON" ] && exit 0 ACTION=$(printf '%s' "$JSON" | jq -r '.action // ""' 2>/dev/null) NG=$(printf '%s' "$JSON" | jq -r '.goal // ""' 2>/dev/null) ND=$(printf '%s' "$JSON" | jq -r '.dod // ""' 2>/dev/null) case "$ACTION" in refine) TMP="${F}.tmp.$$" if jq --arg g "$NG" --arg d "$ND" ' (if $g != "" then .goal=$g else . end) | (if $d != "" then .dod=$d else . end) | .alert=false ' "$F" > "$TMP" 2>/dev/null; then mv "$TMP" "$F" else rm -f "$TMP" fi ;; switch) TMP="${F}.tmp.$$" if jq '.alert=true' "$F" > "$TMP" 2>/dev/null; then mv "$TMP" "$F"; else rm -f "$TMP"; fi ;; *) : # same / desconocido -> no tocar ;; esac exit 0