8c9919f1f8
Separa el ciclo de trabajo en dos grupos con la fuente adecuada para cada uno: - ACTIVO (mientras se trabaja): lo marca el hook PostToolUse de forma determinista, sin LLM, segun la herramienta usada — Read/Grep/Glob -> investigando; Edit/Write -> haciendo; Bash con tests -> testeando; Bash de lectura (ls/cat/git status...) -> investigando; mcp fn_search/show/... -> investigando. Refleja en tiempo real lo que hace el asistente. - REPOSO (al parar y ceder el control): lo resuelve el Stop hook con ask_llm (haiku) -> hecho / pendiente_revision / bloqueado / en_pausa. Al parar nunca queda en un estado activo. Cambios: - goal_phase_active.sh: nuevo hook PostToolUse (mapa herramienta -> fase activa). - goal_phase_worker.sh: ahora solo produce estados de reposo; se elimina el modo prompt. Mantiene el gate (resuelve reposo solo si hubo trabajo o se venia de activo) y el historial. - goal_tracker.sh: deja de lanzar clasificacion LLM en el prompt (redundante); vuelve a fijar objetivo desde el prompt + informar estado. - statusline.sh: nuevo estado en_pausa (en pausa); set de fases reordenado. - settings.json: registra el hook PostToolUse. Resultado: 1 sola llamada haiku por turno (Stop); el estado activo es gratis y refleja las acciones reales en vez de la intencion del prompt. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
117 lines
4.5 KiB
Bash
Executable File
117 lines
4.5 KiB
Bash
Executable File
#!/bin/bash
|
|
# Worker del Stop hook: resuelve el estado de REPOSO de la tarea cuando el
|
|
# asistente para y cede el control. Clasifica con ask_llm (haiku, API directa;
|
|
# nunca `claude -p`, ver regla llm_invocation.md) y lo escribe en el goal JSON
|
|
# manteniendo el historial.
|
|
#
|
|
# El estado ACTIVO (mientras se trabaja: investigando/haciendo/testeando) lo
|
|
# marca el hook PostToolUse (goal_phase_active.sh), de forma determinista. Este
|
|
# worker SOLO produce estados de reposo: hecho, pendiente_revision, bloqueado,
|
|
# en_pausa.
|
|
#
|
|
# Args: <session_id> <transcript_path> <goal_json>
|
|
|
|
SID="$1"
|
|
TRANSCRIPT="$2"
|
|
F="$3"
|
|
|
|
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
|
|
[ -f "$F" ] || exit 0
|
|
[ -f "$TRANSCRIPT" ] || exit 0
|
|
|
|
GOAL=$(jq -r '.goal // ""' "$F" 2>/dev/null)
|
|
[ -z "$GOAL" ] && exit 0
|
|
CUR=$(jq -r '.phase // ""' "$F" 2>/dev/null)
|
|
|
|
is_active() {
|
|
case "$1" in
|
|
investigando|planificando|haciendo|testeando|puliendo) return 0 ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
# Una pasada de abajo a arriba sobre el turno actual: ultima respuesta de texto
|
|
# del asistente + ultima peticion del usuario + si hubo trabajo (tool_use).
|
|
LAST=""
|
|
USER_MSG=""
|
|
HAS_WORK=0
|
|
while IFS= read -r line; do
|
|
t=$(printf '%s' "$line" | jq -r '.type // empty' 2>/dev/null)
|
|
if [ "$t" = "assistant" ]; then
|
|
if [ -z "$LAST" ]; then
|
|
txt=$(printf '%s' "$line" | jq -r '(.message.content // [])[]? | select(.type=="text") | .text' 2>/dev/null)
|
|
[ -n "$txt" ] && LAST="$txt"
|
|
fi
|
|
if printf '%s' "$line" | jq -e '(.message.content // [])[]? | select(.type=="tool_use")' >/dev/null 2>&1; then
|
|
HAS_WORK=1
|
|
fi
|
|
elif [ "$t" = "user" ]; then
|
|
ctype=$(printf '%s' "$line" | jq -r '.message.content | type' 2>/dev/null)
|
|
if [ "$ctype" = "string" ]; then
|
|
USER_MSG=$(printf '%s' "$line" | jq -r '.message.content' 2>/dev/null)
|
|
break
|
|
fi
|
|
if ! printf '%s' "$line" | jq -e '(.message.content // [])[]? | select(.type=="tool_result")' >/dev/null 2>&1; then
|
|
USER_MSG=$(printf '%s' "$line" | jq -r '(.message.content // [])[]? | select(.type=="text") | .text' 2>/dev/null)
|
|
break
|
|
fi
|
|
fi
|
|
done < <(tac "$TRANSCRIPT")
|
|
|
|
# Solo resolver reposo si hubo trabajo este turno o si veniamos de un estado
|
|
# activo (paramos tras currar). Charla sobre un reposo previo: no tocar.
|
|
if [ "$HAS_WORK" = "0" ] && ! is_active "$CUR"; then
|
|
exit 0
|
|
fi
|
|
|
|
LAST=$(printf '%s' "$LAST" | tail -c 4000)
|
|
[ -z "$LAST" ] && exit 0
|
|
USER_MSG=$(printf '%s' "$USER_MSG" | tail -c 1500)
|
|
|
|
SYS="El asistente acaba de PARAR y cede el control al usuario. Clasifica el estado de REPOSO en que queda la tarea. Responde UNA sola palabra, sin nada mas, de: hecho pendiente_revision bloqueado en_pausa sin_cambio. hecho=el objetivo esta completo y verificado; pendiente_revision=el asistente espera que el humano revise un resultado o decida algo importante; bloqueado=no puede avanzar por un error o por falta de informacion del usuario; en_pausa=hizo un avance y espera la siguiente indicacion, sin estar terminado ni bloqueado ni a la espera de revision; sin_cambio=el turno no altera el estado de reposo actual (charla irrelevante). Usa 'hecho' SOLO si el trabajo esta completo y confirmado, nunca si queda algo pendiente."
|
|
|
|
PROMPT="OBJETIVO DE LA TAREA: ${GOAL}
|
|
|
|
ULTIMA PETICION DEL USUARIO:
|
|
${USER_MSG}
|
|
|
|
ULTIMA RESPUESTA DEL ASISTENTE:
|
|
${LAST}
|
|
|
|
Responde con una sola palabra de la lista permitida:"
|
|
|
|
RAW=$("$PY" "$ASK" --model claude-haiku-4-5-20251001 --system "$SYS" "$PROMPT" 2>/dev/null | tr '[:upper:]' '[:lower:]')
|
|
[ -z "$RAW" ] && exit 0
|
|
|
|
case "$RAW" in
|
|
*sin_cambio*|*sincambio*|*ninguna*|*charla*) exit 0 ;;
|
|
*pendiente*revis*|*revis*) PHASE=pendiente_revision ;;
|
|
*bloque*) PHASE=bloqueado ;;
|
|
*hecho*|*complet*|*termin*|*done*) PHASE=hecho ;;
|
|
*pausa*|*pause*|*espera*|*siguiente*) PHASE=en_pausa ;;
|
|
# Si por error devuelve un estado activo al parar, lo tratamos como pausa.
|
|
investigando|planificando|haciendo|testeando|puliendo) PHASE=en_pausa ;;
|
|
*) exit 0 ;;
|
|
esac
|
|
|
|
# Escribir la fase + mantener historial (append solo si cambia respecto al
|
|
# ultimo; se conservan los ultimos 12 estados).
|
|
TMP="${F}.tmp.$$"
|
|
if jq --arg p "$PHASE" '
|
|
.phase = $p
|
|
| .history = (
|
|
( .history // [] ) as $h
|
|
| ( if ($h | length) > 0 and ($h[-1] == $p) then $h else ($h + [$p]) end )
|
|
| .[-12:]
|
|
)
|
|
' "$F" > "$TMP" 2>/dev/null; then
|
|
mv "$TMP" "$F"
|
|
else
|
|
rm -f "$TMP"
|
|
fi
|
|
exit 0
|