From a3ecb6a4cf06663aaec30c71da7c561976210905 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Sat, 6 Jun 2026 16:11:30 +0200 Subject: [PATCH] feat(statusline): comandos meta fuera de banda (no molestan al agente) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Los comandos del usuario objetivo:/dod:/recalcular/pausa ahora bloquean el prompt en UserPromptSubmit con {"decision":"block","reason":...}: el hook ejecuta su efecto, el usuario ve una confirmacion breve, y el prompt NO llega al modelo — el agente no genera respuesta y sigue idle con su tarea. Antes estos comandos se procesaban como un turno normal e interrumpian al agente. Los prompts normales se siguen pasando al modelo (texto plano como contexto) y acumulan/refían el objetivo. Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude/hooks/goal_tracker.sh | 56 +++++++++++++++++------------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/.claude/hooks/goal_tracker.sh b/.claude/hooks/goal_tracker.sh index ab36c7a..8a84ad7 100755 --- a/.claude/hooks/goal_tracker.sh +++ b/.claude/hooks/goal_tracker.sh @@ -1,15 +1,19 @@ #!/bin/bash # UserPromptSubmit hook del sistema de objetivo+fase por terminal. # -# Comandos que lee directamente de tu prompt (sin intervencion del modelo): +# 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). # objetivo: clear lo borra (tambien -, none, borrar, quitar, reset). -# dod: fija un Definition of Done corto (se muestra junto al objetivo). +# dod: fija un Definition of Done corto. # dod: clear lo borra. -# pausa marca la fase en en_pausa. Util tras interrumpir con Ctrl-C, -# que en Claude Code no dispara ningun hook (no hay forma de -# detectar la interrupcion automaticamente). -# En cualquier otro caso, inyecta el estado actual como contexto para el modelo. +# 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). @@ -21,6 +25,9 @@ F="$HOME/.claude/goals/${SID}.json" PROMPT=$(printf '%s' "$INPUT" | jq -r '.prompt // ""' 2>/dev/null) 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) --- GOAL_LINE=$(printf '%s' "$PROMPT" | grep -ioE '^[[:space:]]*(objetivo|meta|goal)[[:space:]]*:[[:space:]]*.+' | head -1) if [ -n "$GOAL_LINE" ]; then @@ -28,8 +35,7 @@ if [ -n "$GOAL_LINE" ]; then case "$NEWGOAL" in -|clear|none|borrar|quitar|reset) rm -f "$F" - echo "GOAL-TRACKER: objetivo de esta terminal borrado." - exit 0 ;; + block "🎯 Objetivo de esta terminal borrado." ;; esac if [ -f "$F" ]; then PH=$(jq -r '.phase // "planificando"' "$F" 2>/dev/null) @@ -39,62 +45,56 @@ if [ -n "$GOAL_LINE" ]; then fi TMP="${F}.tmp.$$" if jq -n --arg g "$NEWGOAL" --arg p "$PH" --arg d "$DD" \ - '{goal:$g, phase:$p} | if $d != "" then .dod=$d else . end' > "$TMP" 2>/dev/null; then + '{goal:$g, phase:$p, prompts:[]} | if $d != "" then .dod=$d else . end' > "$TMP" 2>/dev/null; then mv "$TMP" "$F" else rm -f "$TMP" fi - echo "GOAL-TRACKER: objetivo fijado desde tu prompt -> \"$NEWGOAL\"." - exit 0 + block "🎯 Objetivo fijado: ${NEWGOAL}" fi # --- dod: --- DOD_LINE=$(printf '%s' "$PROMPT" | grep -ioE '^[[:space:]]*dod[[:space:]]*:[[:space:]]*.+' | head -1) if [ -n "$DOD_LINE" ]; then NEWDOD=$(printf '%s' "$DOD_LINE" | sed -E 's/^[^:]*:[[:space:]]*//; s/[[:space:]]+$//') - [ -f "$F" ] || { echo "GOAL-TRACKER: fija primero un objetivo (\"objetivo: ...\") antes del DoD."; exit 0; } + [ -f "$F" ] || block "Fija primero un objetivo (\"objetivo: ...\") antes del DoD." case "$NEWDOD" in -|clear|none|borrar|quitar|reset) TMP="${F}.tmp.$$" jq 'del(.dod)' "$F" > "$TMP" 2>/dev/null && mv "$TMP" "$F" - echo "GOAL-TRACKER: DoD borrado." - exit 0 ;; + block "🏁 DoD borrado." ;; esac TMP="${F}.tmp.$$" if jq --arg d "$NEWDOD" '.dod=$d' "$F" > "$TMP" 2>/dev/null; then mv "$TMP" "$F"; else rm -f "$TMP"; fi - echo "GOAL-TRACKER: DoD fijado -> \"$NEWDOD\"." - exit 0 + 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" ] || { echo "GOAL-TRACKER: no hay objetivo que recalcular."; exit 0; } + [ -f "$F" ] || block "No hay objetivo que recalcular." nohup bash "$HOME/.claude/hooks/goal_refine.sh" "$SID" "$F" force >/dev/null 2>&1 & - echo "GOAL-TRACKER: recalculando objetivo+DoD desde tus prompts (background)." - exit 0 ;; + 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) - [ -f "$F" ] || exit 0 + [ -f "$F" ] || block "No hay objetivo en esta terminal." TMP="${F}.tmp.$$" if jq '.phase="en_pausa" | .history=((.history // [])+["en_pausa"])[-12:]' "$F" > "$TMP" 2>/dev/null; then mv "$TMP" "$F" fi - echo "GOAL-TRACKER: fase marcada en_pausa." - exit 0 ;; + block "⏸️ Fase marcada en pausa." ;; esac -# --- informativo: estado actual para el modelo --- +# --- prompt NORMAL: pasa al agente + acumula para refinar + inyecta 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 para mantenerlos coherentes con - # todo lo pedido (background). Si el prompt es una tarea totalmente distinta, - # goal_refine marca alert=true (la terminal mezcla tareas). + # Acumular el prompt y refinar objetivo+DoD coherentes (background). Si es una + # tarea totalmente distinta, goal_refine marca alert=true (mezcla de tareas). 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,13 +104,13 @@ 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 usuario fija objetivo con \"objetivo: ...\" y un DoD corto con \"dod: ...\"; si redefine la tarea en lenguaje natural, actualiza \"goal\" en ese JSON. El objetivo/DoD se autorefinan con tus prompts; si ves ⚠️ es que la terminal esta mezclando tareas." + 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." else # 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: \"." + echo "GOAL-TRACKER: file=$F (sin objetivo aun). Autogenerando objetivo+DoD desde tu prompt en background (haiku). El usuario tambien puede fijarlo con \"objetivo: ...\" / \"dod: ...\"." fi exit 0