From 50290a71e7bc681783d1eb30bef8668cc40e89b7 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Sat, 6 Jun 2026 16:23:20 +0200 Subject: [PATCH] feat(statusline): objetivo fijo (identificativo), solo el DoD se refina MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplifica el modelo segun feedback: - El OBJETIVO (target) es el identificativo de la terminal: se genera una vez y NUNCA cambia automaticamente. goal_refine deja de tocarlo. - goal_refine ahora ajusta SOLO el DoD para mantenerlo coherente con los prompts. - Se elimina la deteccion de cambio de tarea y el icono de alerta ⚠️ (campo alert ya no se escribe ni se lee; queda inocuo en JSONs antiguos). - Se elimina el comando 'recalcular' y goal_refine.sh modo force. Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude/hooks/goal_refine.sh | 59 +++++++++-------------------------- .claude/hooks/goal_tracker.sh | 40 +++++++++--------------- .claude/statusline.sh | 8 +---- 3 files changed, 31 insertions(+), 76 deletions(-) diff --git a/.claude/hooks/goal_refine.sh b/.claude/hooks/goal_refine.sh index 25a5062..02c7ae4 100755 --- a/.claude/hooks/goal_refine.sh +++ b/.claude/hooks/goal_refine.sh @@ -1,22 +1,13 @@ #!/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`). +# Ajusta SOLO el DoD para mantenerlo coherente con los prompts del usuario hacia +# el objetivo. El OBJETIVO es fijo (identificativo de la terminal) y NUNCA se +# toca aqui. Lo lanza goal_tracker.sh en background (no bloquea). Usa ask_llm +# (haiku, API directa; nunca `claude -p`, ver regla llm_invocation.md). # -# 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. +# Args: SID="$1" F="$2" -FORCE="$3" [ -f "$F" ] || exit 0 PY="$HOME/fn_registry/python/.venv/bin/python3" @@ -30,48 +21,28 @@ 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 +SYS="Dado un OBJETIVO fijo de una terminal de trabajo y los prompts del usuario, define o ajusta el DoD (definition of done): la condicion concreta de 'terminado' para ese objetivo, coherente con lo que el usuario va pidiendo. El OBJETIVO no se toca, solo el DoD. Responde SOLO JSON en una linea, sin markdown: {\"dod\":\"...\"}. dod en espanol, breve, maximo 9 palabras. Si el DoD actual ya es adecuado, devuelvelo igual." -PROMPT="OBJETIVO ACTUAL: ${GOAL} +PROMPT="OBJETIVO (fijo, no lo cambies): ${GOAL} DoD ACTUAL: ${DOD} PROMPTS DEL USUARIO (orden cronologico): ${PROMPTS} -Responde el JSON:" +Responde el JSON con el DoD:" 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) +[ -z "$ND" ] && exit 0 -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 +TMP="${F}.tmp.$$" +if jq --arg d "$ND" '.dod=$d' "$F" > "$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 8a84ad7..3dc867a 100755 --- a/.claude/hooks/goal_tracker.sh +++ b/.claude/hooks/goal_tracker.sh @@ -1,21 +1,20 @@ #!/bin/bash # UserPromptSubmit hook del sistema de objetivo+fase por terminal. # -# Comandos META que lee directamente de tu prompt. Estos se ejecutan FUERA DE -# BANDA: el hook hace su efecto y BLOQUEA el prompt (decision=block), asi el -# agente NO lo recibe ni responde — sigue idle con lo suyo. Solo ves una -# confirmacion breve. -# objetivo: fija el objetivo de la terminal (meta:/goal: equivalen). +# Modelo: +# - El OBJETIVO (target) es el IDENTIFICATIVO de la terminal: se genera una vez +# (del primer prompt, o a mano con "objetivo: ...") y NUNCA cambia solo. +# - El DoD SI se ajusta con tus prompts para reflejar la condicion de terminado. +# - La FASE la mantienen los hooks de fase: PostToolUse (activo) y Stop (reposo). +# +# Comandos META (se ejecutan FUERA DE BANDA: el hook hace su efecto y BLOQUEA el +# prompt con decision=block, asi el agente NO lo recibe ni responde; solo ves una +# confirmacion breve): +# objetivo: fija/cambia el objetivo a mano (meta:/goal: equivalen). # objetivo: clear lo borra (tambien -, none, borrar, quitar, reset). -# dod: fija un Definition of Done corto. +# dod: fija un DoD a mano. # dod: clear lo borra. -# recalcular regenera objetivo+DoD desde tus prompts y limpia alerta. # pausa marca la fase en en_pausa (Ctrl-C no dispara hooks). -# -# Cualquier OTRO prompt es normal: pasa al agente, se acumula para refinar el -# objetivo y se inyecta el estado actual como contexto. -# -# La FASE la mantienen los hooks de fase: PostToolUse (activo) y Stop (reposo). INPUT=$(cat) SID=$(printf '%s' "$INPUT" | jq -r '.session_id // empty' 2>/dev/null) @@ -28,7 +27,7 @@ PROMPT_TRIM=$(printf '%s' "$PROMPT" | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$ # Bloquea el prompt (no llega al agente) y muestra al usuario. block() { jq -n --arg r "$1" '{decision:"block", reason:$r}'; exit 0; } -# --- objetivo: (preserva el DoD si ya existia) --- +# --- objetivo: (manual; preserva el DoD si ya existia) --- GOAL_LINE=$(printf '%s' "$PROMPT" | grep -ioE '^[[:space:]]*(objetivo|meta|goal)[[:space:]]*:[[:space:]]*.+' | head -1) if [ -n "$GOAL_LINE" ]; then NEWGOAL=$(printf '%s' "$GOAL_LINE" | sed -E 's/^[^:]*:[[:space:]]*//; s/[[:space:]]+$//') @@ -69,14 +68,6 @@ if [ -n "$DOD_LINE" ]; then block "🏁 DoD fijado: ${NEWDOD}" fi -# --- recalcular (regenera objetivo+DoD desde los prompts; limpia la alerta) --- -case "$PROMPT_TRIM" in - recalcular|recalcula|replantea|replantear|/recalcular) - [ -f "$F" ] || block "No hay objetivo que recalcular." - nohup bash "$HOME/.claude/hooks/goal_refine.sh" "$SID" "$F" force >/dev/null 2>&1 & - block "🔄 Recalculando objetivo+DoD desde tus prompts…" ;; -esac - # --- pausa (marca manual; Ctrl-C no dispara hooks en Claude Code) --- case "$PROMPT_TRIM" in pausa|pause|pausar|"en pausa"|/pausa) @@ -88,13 +79,12 @@ case "$PROMPT_TRIM" in block "⏸️ Fase marcada en pausa." ;; esac -# --- prompt NORMAL: pasa al agente + acumula para refinar + inyecta estado --- +# --- prompt NORMAL: pasa al agente + acumula para refinar el DoD + estado --- if [ -f "$F" ]; then G=$(jq -r '.goal // ""' "$F" 2>/dev/null) P=$(jq -r '.phase // ""' "$F" 2>/dev/null) D=$(jq -r '.dod // ""' "$F" 2>/dev/null) - # Acumular el prompt y refinar objetivo+DoD coherentes (background). Si es una - # tarea totalmente distinta, goal_refine marca alert=true (mezcla de tareas). + # Acumular el prompt y ajustar SOLO el DoD (background). El objetivo no cambia. if [ "${#PROMPT_TRIM}" -ge 12 ]; then TMP="${F}.tmp.$$" if jq --arg p "$PROMPT_TRIM" '.prompts = ((.prompts // []) + [$p])[-12:]' "$F" > "$TMP" 2>/dev/null; then @@ -104,7 +94,7 @@ if [ -f "$F" ]; then fi nohup bash "$HOME/.claude/hooks/goal_refine.sh" "$SID" "$F" >/dev/null 2>&1 & fi - 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 objetivo/DoD se autorefinan con tus prompts; si ves ⚠️ es que la terminal mezcla tareas. Comandos meta del usuario (no los uses tu): objetivo:/dod:/recalcular/pausa." + echo "GOAL-TRACKER: file=$F | goal=\"$G\" dod=\"$D\" phase=\"$P\". El objetivo es fijo (identificativo de la terminal, NO lo cambies). El DoD se ajusta solo con los prompts; la fase la mantienen los hooks (PostToolUse=activo, Stop=reposo) — NO la escribas. Comandos meta del usuario (no los uses tu): objetivo:/dod:/pausa." else # Sin objetivo: autogenerar objetivo + DoD desde el primer prompt sustantivo, # en background (no bloquea). Se omite para prompts triviales (saludos, ok...). diff --git a/.claude/statusline.sh b/.claude/statusline.sh index e1cd5bc..76e1913 100755 --- a/.claude/statusline.sh +++ b/.claude/statusline.sh @@ -304,15 +304,9 @@ if [ -n "$SESSION_ID" ] && [ -f "$GOAL_FILE" ]; then GOAL=$(jq -r '.goal // ""' "$GOAL_FILE" 2>/dev/null) PHASE=$(jq -r '.phase // ""' "$GOAL_FILE" 2>/dev/null) DOD=$(jq -r '.dod // ""' "$GOAL_FILE" 2>/dev/null) - ALERT=$(jq -r '.alert // false' "$GOAL_FILE" 2>/dev/null) if [ -n "$GOAL" ]; then GC=$(goal_color "$SESSION_ID") - # ⚠️ si la terminal esta mezclando tareas (objetivo cambio por completo). - if [ "$ALERT" = "true" ]; then - LEFT="\033[1;31m⚠️\033[0m ${GC}🎯 ${GOAL}${RESET}" - else - LEFT="${GC}🎯 ${GOAL}${RESET}" - fi + LEFT="${GC}🎯 ${GOAL}${RESET}" LINE0="${LEFT}"