diff --git a/.claude/hooks/goal_autogen.sh b/.claude/hooks/goal_autogen.sh new file mode 100755 index 0000000..1a7e5f3 --- /dev/null +++ b/.claude/hooks/goal_autogen.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# Autogeneracion de objetivo + DoD a partir del primer prompt sustantivo de una +# terminal que aun no tiene objetivo. Lo lanza goal_tracker.sh en background (no +# bloquea el turno). Usa ask_llm (haiku, API directa; nunca `claude -p`). +# +# Args: + +SID="$1" +F="$2" +PROMPT="$3" + +# Si ya existe objetivo (lo creo otro proceso o el usuario), no pisar. +[ -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 + +P=$(printf '%s' "$PROMPT" | tail -c 2000) +[ -z "$P" ] && exit 0 + +SYS="Dado el PRIMER mensaje de un usuario a un asistente de codigo en una terminal, infiere un OBJETIVO breve de la tarea (maximo 8 palabras, en espanol, sin comillas) y un DoD breve (definition of done: condicion concreta de 'terminado', maximo 8 palabras, en espanol). Responde SOLO un objeto JSON en una sola linea, sin markdown ni texto extra: {\"goal\":\"...\",\"dod\":\"...\"}. Si el mensaje es un saludo, charla trivial o no describe ninguna tarea, responde exactamente {}." + +RAW=$("$PY" "$ASK" --system "$SYS" "$P" 2>/dev/null) +[ -z "$RAW" ] && exit 0 + +# Extraer el primer objeto JSON de la salida (tolerante a texto/markdown extra). +JSON=$(printf '%s' "$RAW" | tr '\n' ' ' | grep -o '{[^{}]*}' | head -1) +[ -z "$JSON" ] && exit 0 + +GOAL=$(printf '%s' "$JSON" | jq -r '.goal // ""' 2>/dev/null) +DOD=$(printf '%s' "$JSON" | jq -r '.dod // ""' 2>/dev/null) +[ -z "$GOAL" ] && exit 0 + +# Carrera: si entre tanto se creo el archivo, no pisar. +[ -f "$F" ] && exit 0 + +TMP="${F}.tmp.$$" +if jq -n --arg g "$GOAL" --arg d "$DOD" \ + '{goal:$g, phase:"planificando", history:["planificando"]} | if $d != "" then .dod=$d else . end' > "$TMP" 2>/dev/null; then + mv "$TMP" "$F" +else + rm -f "$TMP" +fi +exit 0 diff --git a/.claude/hooks/goal_tracker.sh b/.claude/hooks/goal_tracker.sh index 7d3dbb3..84e2cb9 100755 --- a/.claude/hooks/goal_tracker.sh +++ b/.claude/hooks/goal_tracker.sh @@ -85,6 +85,11 @@ if [ -f "$F" ]; then D=$(jq -r '.dod // ""' "$F" 2>/dev/null) echo "GOAL-TRACKER: file=$F | goal=\"$G\" dod=\"$D\" phase=\"$P\". La fase la mantienen los hooks (PostToolUse=activo, Stop=reposo) — NO la escribas. El usuario fija objetivo con \"objetivo: ...\" y un DoD corto con \"dod: ...\"; si redefine la tarea en lenguaje natural, actualiza \"goal\" en ese JSON." else - echo "GOAL-TRACKER: file=$F (sin objetivo aun). El usuario fija el objetivo con \"objetivo: \" y opcionalmente un DoD con \"dod: \". Si describe una tarea clara sin prefijo, crea {\"goal\":\"\",\"phase\":\"planificando\"} leyendo su prompt. Sin objetivo, ignora." + # Sin objetivo: autogenerar objetivo + DoD desde el primer prompt sustantivo, + # en background (no bloquea). Se omite para prompts triviales (saludos, ok...). + if [ "${#PROMPT_TRIM}" -ge 12 ]; then + nohup bash "$HOME/.claude/hooks/goal_autogen.sh" "$SID" "$F" "$PROMPT" >/dev/null 2>&1 & + fi + echo "GOAL-TRACKER: file=$F (sin objetivo aun). Autogenerando objetivo+DoD desde tu prompt en background (haiku). Tambien puedes fijarlo a mano con \"objetivo: \" / \"dod: \"." fi exit 0