fix(goals): /rename solo para FleetView + objetivo provisional

- /rename escribe el nombre en FleetView (.rename del goal). NO renombra el titulo
  de la sesion de Claude Code: el built-in /rename usa estado interno y no re-lee
  el transcript, asi que un evento ai-title no cambia el prompt bar (comprobado).
- objetivo provisional: el primer prompt fija un goal placeholder hasta que haiku
  genera el definitivo, para que el statusline no quede vacio.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
agent
2026-06-17 01:09:11 +02:00
parent 8e53e0818e
commit e976fb303a
4 changed files with 86 additions and 30 deletions
+4
View File
@@ -0,0 +1,4 @@
---
description: Renombra esta terminal/Claude en FleetView (no toca el título de la sesión)
---
rename: $ARGUMENTS
+27 -8
View File
@@ -9,8 +9,12 @@ SID="$1"
F="$2"
PROMPT="$3"
# Si ya existe objetivo (lo creo otro proceso o el usuario), no pisar.
[ -f "$F" ] && exit 0
# Si ya existe objetivo DEFINITIVO (usuario manual u otro autogen ya termino), no
# pisar. Un archivo PROVISIONAL (.provisional=true) SI se pisa: es el placeholder
# (= texto del usuario) que pusimos para que el statusline no quede vacio.
if [ -f "$F" ] && [ "$(jq -r '.provisional // false' "$F" 2>/dev/null)" != "true" ]; then
exit 0
fi
PY="$HOME/fn_registry/python/.venv/bin/python3"
ASK="$HOME/fn_registry/python/functions/core/ask_llm.py"
@@ -34,14 +38,29 @@ DOD=$(printf '%s' "$JSON" | jq -r '.dod // ""' 2>/dev/null)
EMOJIS=$(printf '%s' "$JSON" | jq -r '.emojis // ""' 2>/dev/null)
[ -z "$GOAL" ] && exit 0
# Carrera: si entre tanto se creo el archivo, no pisar.
[ -f "$F" ] && exit 0
# Carrera: si entre tanto aparecio un objetivo DEFINITIVO (manual), respetarlo.
# Si solo esta el provisional, lo pisamos abajo con el definitivo.
if [ -f "$F" ] && [ "$(jq -r '.provisional // false' "$F" 2>/dev/null)" != "true" ]; then
exit 0
fi
TMP="${F}.tmp.$$"
if jq -n --arg g "$GOAL" --arg d "$DOD" --arg e "$EMOJIS" --arg p "$P" \
'{goal:$g, phase:"planificando", history:["planificando"], prompts:[$p]} | (if $d != "" then .dod=$d else . end) | (if $e != "" then .emojis=$e else . end)' > "$TMP" 2>/dev/null; then
mv "$TMP" "$F"
if [ -f "$F" ]; then
# Pisar el provisional: fija goal/dod/emojis definitivos y quita el flag,
# preservando phase/history/prompts que el provisional ya hubiera acumulado.
if jq --arg g "$GOAL" --arg d "$DOD" --arg e "$EMOJIS" \
'del(.provisional) | .goal=$g | (if $d != "" then .dod=$d else . end) | (if $e != "" then .emojis=$e else . end)' \
"$F" > "$TMP" 2>/dev/null; then
mv "$TMP" "$F"
else
rm -f "$TMP"
fi
else
rm -f "$TMP"
if jq -n --arg g "$GOAL" --arg d "$DOD" --arg e "$EMOJIS" --arg p "$P" \
'{goal:$g, phase:"planificando", history:["planificando"], prompts:[$p]} | (if $d != "" then .dod=$d else . end) | (if $e != "" then .emojis=$e else . end)' > "$TMP" 2>/dev/null; then
mv "$TMP" "$F"
else
rm -f "$TMP"
fi
fi
exit 0
+42 -21
View File
@@ -52,27 +52,26 @@ if [ -n "$GOAL_LINE" ]; then
block "🎯 Objetivo fijado: ${NEWGOAL}"
fi
# --- /rename <texto>: guarda el nombre para FleetView (.rename del goal) pero NO
# bloquea, para que el comando NATIVO /rename de Claude Code renombre tambien
# la sesion (prompt bar + /resume picker). Por eso NO debe existir
# ~/.claude/commands/rename.md: competiria con el built-in y lo taparia. ---
RENAME_LINE=$(printf '%s' "$PROMPT" | grep -ioE '^[[:space:]]*/rename([[:space:]]+.*)?$' | head -1)
# --- /rename <texto> (o rename: <texto> via el command rename.md): pone el nombre
# de la terminal en FleetView (.rename del goal) y bloquea el prompt.
# NO renombra el titulo de la sesion de Claude Code: el built-in /rename usa
# estado interno y NO re-lee el transcript, asi que appendear un evento ai-title
# no cambia el prompt bar (comprobado 2026-06-17). El rename queda solo para
# FleetView. ---
RENAME_LINE=$(printf '%s' "$PROMPT" | grep -ioE '^[[:space:]]*(/rename|rename[[:space:]]*:)[[:space:]]*.*$' | head -1)
if [ -n "$RENAME_LINE" ]; then
NEWNAME=$(printf '%s' "$RENAME_LINE" | sed -E 's#^[[:space:]]*/rename[[:space:]]*##; s/[[:space:]]+$//')
NEWNAME=$(printf '%s' "$RENAME_LINE" | sed -E 's#^[[:space:]]*(/rename|rename[[:space:]]*:)[[:space:]]*##; s/[[:space:]]+$//')
case "$NEWNAME" in
""|-|clear|none|borrar|quitar|reset)
# /rename sin texto: Claude Code auto-genera; borramos el override de FleetView.
[ -f "$F" ] && { TMP="${F}.tmp.$$"; jq 'del(.rename)' "$F" > "$TMP" 2>/dev/null && mv "$TMP" "$F"; } ;;
*)
if [ -f "$F" ]; then
TMP="${F}.tmp.$$"; jq --arg n "$NEWNAME" '.rename=$n' "$F" > "$TMP" 2>/dev/null && mv "$TMP" "$F"
else
TMP="${F}.tmp.$$"; jq -n --arg n "$NEWNAME" '{rename:$n, phase:"planificando", prompts:[]}' > "$TMP" 2>/dev/null && mv "$TMP" "$F"
fi ;;
[ -f "$F" ] && { TMP="${F}.tmp.$$"; jq 'del(.rename)' "$F" > "$TMP" 2>/dev/null && mv "$TMP" "$F"; }
block "🏷️ Nombre de la terminal borrado." ;;
esac
# NO bloquear: el prompt sigue su curso y el built-in /rename de Claude Code
# renombra la sesion. El hook solo capturo el nombre para la lista de FleetView.
exit 0
if [ -f "$F" ]; then
TMP="${F}.tmp.$$"; jq --arg n "$NEWNAME" '.rename=$n' "$F" > "$TMP" 2>/dev/null && mv "$TMP" "$F"
else
TMP="${F}.tmp.$$"; jq -n --arg n "$NEWNAME" '{rename:$n, phase:"planificando", prompts:[]}' > "$TMP" 2>/dev/null && mv "$TMP" "$F"
fi
block "🏷️ Renombrado en FleetView: ${NEWNAME}"
fi
# --- dod: <texto> ---
@@ -103,7 +102,18 @@ case "$PROMPT_TRIM" in
esac
# --- prompt NORMAL: pasa al agente + acumula para refinar el DoD + estado ---
if [ -f "$F" ]; then
# 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)
@@ -119,11 +129,22 @@ if [ -f "$F" ]; then
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: autogenerar objetivo + DoD desde el primer prompt sustantivo,
# en background (no bloquea). Se omite para prompts triviales (saludos, ok...).
# 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 (sin objetivo aun). Autogenerando objetivo+DoD desde tu prompt en background (haiku). El usuario tambien puede fijarlo con \"objetivo: ...\" / \"dod: ...\"."
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
+13 -1
View File
@@ -323,9 +323,21 @@ 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)
EMOJIS=$(jq -r '.emojis // ""' "$GOAL_FILE" 2>/dev/null)
PROVISIONAL=$(jq -r '.provisional // false' "$GOAL_FILE" 2>/dev/null)
if [ -n "$GOAL" ]; then
GC=$(goal_color "$SESSION_ID")
LEFT="${GC}🎯 ${GOAL}${RESET}"
# Prefijo del objetivo:
# - provisional (= tu propio texto, mientras haiku genera el real) -> ⏳ atenuado.
# - los 3 emojis generados (representan la tarea, igual que FleetView).
# - fallback al marcador generico de objetivo.
if [ "$PROVISIONAL" = "true" ]; then
LEFT="${GC}${DIM}${GOAL}${RESET}"
elif [ -n "$EMOJIS" ]; then
LEFT="${GC}${EMOJIS} ${GOAL}${RESET}"
else
LEFT="${GC}🎯 ${GOAL}${RESET}"
fi
LINE0="${LEFT}"