86252b7d2c
El rename de la terminal en FleetView se hace ahora con alt+r dentro de la TUI, que escribe el campo .rename del goal directamente. Se elimina el slash command rename.md y la nota del hook lo documenta, dejando libre el built-in /rename de Claude Code para renombrar la sesión. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
133 lines
6.6 KiB
Bash
Executable File
133 lines
6.6 KiB
Bash
Executable File
#!/bin/bash
|
|
# UserPromptSubmit hook del sistema de objetivo+fase por terminal.
|
|
#
|
|
# 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: <texto> fija/cambia el objetivo a mano (meta:/goal: equivalen).
|
|
# objetivo: clear lo borra (tambien -, none, borrar, quitar, reset).
|
|
# dod: <texto> fija un DoD a mano.
|
|
# dod: clear lo borra.
|
|
# pausa marca la fase en en_pausa (Ctrl-C no dispara hooks).
|
|
|
|
INPUT=$(cat)
|
|
SID=$(printf '%s' "$INPUT" | jq -r '.session_id // empty' 2>/dev/null)
|
|
[ -z "$SID" ] && exit 0
|
|
|
|
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 <reason> al usuario.
|
|
block() { jq -n --arg r "$1" '{decision:"block", reason:$r}'; exit 0; }
|
|
|
|
# --- objetivo: <texto> (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:]]+$//')
|
|
case "$NEWGOAL" in
|
|
-|clear|none|borrar|quitar|reset)
|
|
rm -f "$F"
|
|
block "🎯 Objetivo de esta terminal borrado." ;;
|
|
esac
|
|
if [ -f "$F" ]; then
|
|
PH=$(jq -r '.phase // "planificando"' "$F" 2>/dev/null)
|
|
DD=$(jq -r '.dod // ""' "$F" 2>/dev/null)
|
|
else
|
|
PH="planificando"; DD=""
|
|
fi
|
|
TMP="${F}.tmp.$$"
|
|
if jq -n --arg g "$NEWGOAL" --arg p "$PH" --arg d "$DD" \
|
|
'{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
|
|
block "🎯 Objetivo fijado: ${NEWGOAL}"
|
|
fi
|
|
|
|
# Nota: el rename de FleetView se hace ahora con alt+r DENTRO de la TUI (escribe
|
|
# el campo .rename del goal directamente). Ya no se captura /rename en este hook,
|
|
# asi el built-in /rename de Claude Code queda libre para renombrar la sesion.
|
|
|
|
# --- dod: <texto> ---
|
|
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" ] || 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"
|
|
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
|
|
block "🏁 DoD fijado: ${NEWDOD}"
|
|
fi
|
|
|
|
# --- pausa (marca manual; Ctrl-C no dispara hooks en Claude Code) ---
|
|
case "$PROMPT_TRIM" in
|
|
pausa|pause|pausar|"en pausa"|/pausa)
|
|
[ -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
|
|
block "⏸️ Fase marcada en pausa." ;;
|
|
esac
|
|
|
|
# --- prompt NORMAL: pasa al agente + acumula para refinar el DoD + estado ---
|
|
# Distinguimos tres situaciones por el flag .provisional del goal file:
|
|
# - no existe el archivo -> primer prompt: ponemos objetivo PROVISIONAL = tu
|
|
# texto + lanzamos autogen (haiku) que lo definira.
|
|
# - existe pero .provisional -> autogen aun no termino (o fallo): conservamos el
|
|
# provisional, acumulamos el prompt y relanzamos
|
|
# autogen (idempotente, self-healing).
|
|
# - existe y NO provisional -> objetivo definitivo: acumulamos prompt y solo
|
|
# refinamos el DoD; el objetivo no se toca.
|
|
PROV="false"
|
|
[ -f "$F" ] && PROV=$(jq -r '.provisional // false' "$F" 2>/dev/null)
|
|
|
|
if [ -f "$F" ] && [ "$PROV" != "true" ]; 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 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
|
|
mv "$TMP" "$F"
|
|
else
|
|
rm -f "$TMP"
|
|
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\". 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 definitivo todavia. Mostramos de inmediato un objetivo PROVISIONAL
|
|
# igual a tu propio texto (truncado), para que el statusline no quede vacio
|
|
# mientras haiku genera el real en background. autogen pisara este provisional
|
|
# con el definitivo al terminar (su guard respeta .provisional).
|
|
if [ "${#PROMPT_TRIM}" -ge 12 ]; then
|
|
TMP="${F}.tmp.$$"
|
|
if [ -f "$F" ]; then
|
|
# Ya habia provisional: conserva su goal, solo acumula el prompt.
|
|
jq --arg p "$PROMPT_TRIM" '.prompts = ((.prompts // []) + [$p])[-12:]' "$F" > "$TMP" 2>/dev/null && mv "$TMP" "$F" || rm -f "$TMP"
|
|
else
|
|
PROV_GOAL=$(printf '%s' "$PROMPT_TRIM" | head -c 70)
|
|
jq -n --arg g "$PROV_GOAL" --arg p "$PROMPT_TRIM" \
|
|
'{goal:$g, phase:"planificando", history:["planificando"], prompts:[$p], provisional:true}' > "$TMP" 2>/dev/null && mv "$TMP" "$F" || rm -f "$TMP"
|
|
fi
|
|
nohup bash "$HOME/.claude/hooks/goal_autogen.sh" "$SID" "$F" "$PROMPT" >/dev/null 2>&1 &
|
|
fi
|
|
echo "GOAL-TRACKER: file=$F (objetivo PROVISIONAL = tu texto; generando el objetivo+DoD real con haiku en background). El usuario tambien puede fijarlo con \"objetivo: ...\" / \"dod: ...\"."
|
|
fi
|
|
exit 0
|