Compare commits

...

9 Commits

Author SHA1 Message Date
agent e2b5ac56eb fix(goal-tracker): eliminar regeneracion LLM (haiku) del dod movil en cada prompt
El hook goal_refine.sh regeneraba el campo .dod del goal.json llamando a
ask_llm.py (haiku) en background en CADA UserPromptSubmit de CADA sesion.
Con muchas sesiones de la flota activas esto amplificaba el rate-limit
compartido de la organizacion (una request API por turno por agente).

El .dod movil no lo consume nadie: el parser de la flota
(functions/infra/list_claude_fleet.go, struct goalFile/readGoal) solo lee
goal/phase/emojis/rename/dod_contract/dod_status/role. El criterio que
clasifica la flota (RECLAMA/DICE_TERMINADO/ESTANCADO) es dod_contract +
dod_status, escrito por set_dod_contract.py sin LLM y consumido por
ClassifyFleetTermination. Ese sistema queda intacto.

Cambios:
- goal_refine.sh: convertido en no-op (exit 0) documentado.
- goal_tracker.sh: retirado el disparo de goal_refine + la acumulacion de
  .prompts que solo lo alimentaba; mensaje GOAL-TRACKER actualizado.

El objetivo+DoD inicial los sigue generando goal_autogen.sh una sola vez
por terminal (junto con goal/emojis, que si se usan). El usuario ajusta el
DoD a mano con 'dod: ...'. Resultado: cero llamadas LLM por prompt.
2026-06-21 12:56:14 +02:00
agent 86252b7d2c chore(goals): retirar slash command /rename (lo reemplaza alt+r de FleetView)
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>
2026-06-17 01:23:09 +02:00
agent e976fb303a 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>
2026-06-17 01:09:11 +02:00
agent 8e53e0818e fix(goals): /rename delega al built-in de Claude Code (no bloquea)
El hook capturaba /rename y bloqueaba el prompt, impidiendo que el comando NATIVO
/rename de Claude Code renombrara la sesion. Ahora el hook guarda el nombre para
FleetView (.rename del goal) y NO bloquea, asi el built-in tambien actua. Elimina
commands/rename.md (competia con el built-in y lo tapaba).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 00:36:09 +02:00
agent bb735cad17 feat(goals): emojis de objetivo + /rename + sidecar de contexto para FleetView
- goal_autogen.sh: genera 3 emojis representativos del objetivo (haiku) junto al
  goal+DoD, guardados en goals/<id>.json.
- goal_tracker.sh: comando meta /rename (y rename:) para nombrar la terminal;
  se guarda en goals/<id>.json .rename.
- commands/rename.md: slash command /rename.
- statusline.sh: persiste el % de contexto por sesion en runtime/<id>.json para
  que FleetView lo muestre.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 00:04:41 +02:00
egutierrez 0e8d2d2ff2 chore: auto-commit (1 archivos)
- .claude/settings.json

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-13 21:56:56 +02:00
egutierrez ffb3f9b270 fix(commands): path portable + invocación bash en /git-push y /git-branch
Los comandos hardcodeaban /home/lucas/fn_registry y hacían 'source' del script TBD, lo que fallaba en otros PCs (path inexistente) y bajo zsh (BASH_SOURCE sin definir).

- Path portable: ${FN_REGISTRY_ROOT:-$HOME/fn_registry} — usa la env var si está, si no ~/fn_registry. Válido en cualquier PC del ecosistema.
- Invocación con 'bash <script> <args>' en vez de 'source': los scripts tbd_branch_finish.sh y tbd_branch_create.sh tienen un entry point (if BASH_SOURCE[0] == $0) que llama a la función con los argumentos al ejecutarse directamente. Así funciona aunque la shell de la sesión sea zsh.

No se renombra el archivo del comando; solo se corrige la invocación interna. No incluye .claude/settings.json (cambio ajeno a esta tarea).
2026-06-13 14:47:52 +02:00
egutierrez 1b769a9666 chore: auto-commit (1 archivos)
- .claude/settings.json

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-11 00:16:46 +02:00
egutierrez 963b3bd7e1 fix(install): enlazar hooks y CLAUDE.md, reparar symlinks rotos
install.sh ahora gestiona los hooks goal_*.sh y CLAUDE.md ademas de
skills/agents/commands/settings. Antes quedaban fuera del script, por lo
que al mover repo_Claude de ~/DataProyects a fn_registry/external los
symlinks de hooks/ quedaban colgando y los hooks goal_* fallaban con
"not found".

Cambios:
- Enlace simbolico por archivo de todos los hooks .sh del repo.
- Enlace simbolico de CLAUDE.md (preferencias globales).
- statusline.sh pasa de copia a symlink (elimina backups basura por corrida).
- Logica de relink idempotente: symlink roto o mal-apuntado se borra y
  recrea; solo los archivos reales se respaldan en backup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 21:12:56 +02:00
8 changed files with 242 additions and 89 deletions
+7 -3
View File
@@ -19,10 +19,14 @@ Wrapper sobre `tbd_branch_create_bash_infra`. La función del registry maneja to
2. **Llamar la función del registry**:
```bash
source /home/lucas/fn_registry/bash/functions/infra/tbd_branch_create.sh
tbd_branch_create issue 0021 hot-reload
# Path portable (cualquier PC): FN_REGISTRY_ROOT si está, si no ~/fn_registry.
# Se invoca con `bash` (no `source`): el script llama a tbd_branch_create con
# los argumentos al ejecutarse directamente, y así funciona aunque la shell de
# la sesión sea zsh (evita el fallo de BASH_SOURCE).
FN_TBD="${FN_REGISTRY_ROOT:-$HOME/fn_registry}/bash/functions/infra/tbd_branch_create.sh"
bash "$FN_TBD" issue 0021 hot-reload
# o
tbd_branch_create quick fix-typo-readme
bash "$FN_TBD" quick fix-typo-readme
```
La función:
+5 -2
View File
@@ -81,8 +81,11 @@ Si autocontenido, saltar.
### 5. Cerrar la rama (registry)
```bash
source /home/lucas/fn_registry/bash/functions/infra/tbd_branch_finish.sh
tbd_branch_finish "<descripción corta del merge>"
# Path portable (cualquier PC): FN_REGISTRY_ROOT si está, si no ~/fn_registry.
# Se invoca con `bash` (no `source`): el script tiene un entry point que llama a
# tbd_branch_finish con los argumentos cuando se ejecuta directamente, y así
# funciona aunque la shell de la sesión sea zsh (evita el fallo de BASH_SOURCE).
bash "${FN_REGISTRY_ROOT:-$HOME/fn_registry}/bash/functions/infra/tbd_branch_finish.sh" "<descripción corta del merge>"
```
La función:
+29 -9
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"
@@ -20,7 +24,7 @@ ASK="$HOME/fn_registry/python/functions/core/ask_llm.py"
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 {}."
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), un DoD breve (definition of done: condicion concreta de 'terminado', maximo 8 palabras, en espanol) y EXACTAMENTE 3 EMOJIS que representen visualmente la tarea (3 emojis pegados, sin espacios ni texto entre ellos). Responde SOLO un objeto JSON en una sola linea, sin markdown ni texto extra: {\"goal\":\"...\",\"dod\":\"...\",\"emojis\":\"🔭✨🌌\"}. 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
@@ -31,16 +35,32 @@ JSON=$(printf '%s' "$RAW" | tr '\n' ' ' | grep -o '{[^{}]*}' | head -1)
GOAL=$(printf '%s' "$JSON" | jq -r '.goal // ""' 2>/dev/null)
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 p "$P" \
'{goal:$g, phase:"planificando", history:["planificando"], prompts:[$p]} | if $d != "" then .dod=$d 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
+19 -45
View File
@@ -1,48 +1,22 @@
#!/bin/bash
# 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).
# DESACTIVADO (2026-06-21): este hook regeneraba el campo `.dod` (movil) del
# goal.json llamando a un LLM (haiku via ask_llm.py) en CADA prompt de CADA
# sesion. Con muchas sesiones de la flota activas a la vez eso amplificaba el
# rate-limit compartido de la organizacion ("Server is temporarily limiting
# requests"). Una request API por turno por agente = coste innecesario.
#
# Args: <session_id> <goal_json_file>
SID="$1"
F="$2"
[ -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
GOAL=$(jq -r '.goal // ""' "$F" 2>/dev/null)
[ -z "$GOAL" ] && exit 0
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
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 (fijo, no lo cambies): ${GOAL}
DoD ACTUAL: ${DOD}
PROMPTS DEL USUARIO (orden cronologico):
${PROMPTS}
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
ND=$(printf '%s' "$JSON" | jq -r '.dod // ""' 2>/dev/null)
[ -z "$ND" ] && exit 0
TMP="${F}.tmp.$$"
if jq --arg d "$ND" '.dod=$d' "$F" > "$TMP" 2>/dev/null; then
mv "$TMP" "$F"
else
rm -f "$TMP"
fi
# El `.dod` movil NO lo consume nadie: el parser de la flota
# (functions/infra/list_claude_fleet.go, struct goalFile/readGoal) solo lee
# goal/phase/emojis/rename/dod_contract/dod_status/role; ignora `.dod` por
# completo. El criterio de aceptacion real que clasifica la flota es
# `dod_contract` + `dod_status`, escrito por set_dod_contract.py (sin LLM) y
# consumido por ClassifyFleetTermination. Ese sistema queda intacto.
#
# Por tanto la regeneracion del `.dod` movil con haiku se elimina por completo:
# cero llamadas LLM por prompt. El objetivo+DoD inicial los sigue generando
# goal_autogen.sh una sola vez por terminal (junto con goal/emojis, que si se
# usan); el usuario puede ajustar el DoD a mano con "dod: ...".
#
# Se conserva el archivo como no-op para no romper ningun disparador historico
# (defensa en profundidad). El disparo desde goal_tracker.sh tambien se retiro.
exit 0
+37 -14
View File
@@ -52,6 +52,10 @@ if [ -n "$GOAL_LINE" ]; then
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
@@ -79,28 +83,47 @@ case "$PROMPT_TRIM" in
block "⏸️ Fase marcada en pausa." ;;
esac
# --- prompt NORMAL: pasa al agente + acumula para refinar el DoD + estado ---
if [ -f "$F" ]; then
# --- prompt NORMAL: pasa al agente + estado ---
# Distinguimos dos situaciones por el flag .provisional del goal file:
# - no existe el archivo -> primer prompt: ponemos objetivo PROVISIONAL = tu
# texto + lanzamos autogen (haiku, UNA sola vez)
# que lo definira.
# - existe pero .provisional -> autogen aun no termino (o fallo): conservamos el
# provisional y relanzamos autogen (idempotente,
# self-healing).
# - existe y NO provisional -> objetivo definitivo: solo mostramos estado.
#
# NOTA (2026-06-21): el campo `.dod` movil YA NO se regenera con LLM en cada
# prompt. goal_refine.sh esta desactivado (era una request haiku por turno por
# sesion -> amplificaba el rate-limit compartido de la organizacion). El `.dod`
# movil no lo consume nadie; el criterio que clasifica la flota es `dod_contract`
# + `dod_status` (set_dod_contract.py, sin LLM). El DoD inicial lo fija autogen
# una vez; el usuario lo ajusta a mano con "dod: ...".
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.
echo "GOAL-TRACKER: file=$F | goal=\"$G\" dod=\"$D\" phase=\"$P\". El objetivo es fijo (identificativo de la terminal, NO lo cambies). El DoD inicial lo fija el autogen una vez (sin LLM por prompt); el usuario lo ajusta con \"dod: ...\" — NO lo regeneres tu. 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 jq --arg p "$PROMPT_TRIM" '.prompts = ((.prompts // []) + [$p])[-12:]' "$F" > "$TMP" 2>/dev/null; then
mv "$TMP" "$F"
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
rm -f "$TMP"
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_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: 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). 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
+36 -6
View File
@@ -4,34 +4,57 @@
"Edit(~/.claude/**)",
"Write(~/.claude/**)",
"Edit(.claude/**)",
"Write(.claude/**)"
"Write(.claude/**)",
"Bash(CGO_ENABLED=1 go test *)",
"Bash(sqlite3 *)",
"Read(//home/enmanuel/.claude/**)"
],
"deny": [
"Edit(~/.claude/.git/**)",
"Write(~/.claude/.git/**)",
"Edit(.git/**)",
"Write(.git/**)"
]
],
"defaultMode": "dontAsk"
},
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{ "type": "command", "command": "~/.claude/hooks/goal_tracker.sh" }
{
"type": "command",
"command": "~/.claude/hooks/goal_tracker.sh"
}
]
}
],
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "~/.claude/hooks/goal_notify.sh"
}
]
}
],
"Stop": [
{
"hooks": [
{ "type": "command", "command": "~/.claude/hooks/goal_phase_eval.sh" }
{
"type": "command",
"command": "~/.claude/hooks/goal_phase_eval.sh"
}
]
}
],
"PostToolUse": [
{
"hooks": [
{ "type": "command", "command": "~/.claude/hooks/goal_phase_active.sh" }
{
"type": "command",
"command": "~/.claude/hooks/goal_phase_active.sh"
}
]
}
]
@@ -54,7 +77,14 @@
}
}
},
"language": "Español",
"effortLevel": "xhigh",
"voice": {
"enabled": true,
"mode": "hold"
},
"skipDangerousModePermissionPrompt": true,
"agentPushNotifEnabled": false
"preferredNotifChannel": "notifications_disabled",
"agentPushNotifEnabled": false,
"voiceEnabled": true
}
+32 -1
View File
@@ -45,6 +45,25 @@ if [ "$CONTEXT_PCT" -eq 0 ] && [ "$CONTEXT_USED" -gt 0 ]; then
CONTEXT_PCT=$(echo "scale=0; $CONTEXT_USED * 100 / $CONTEXT_TOTAL" | bc)
fi
# Persistir el contexto por sesión en un sidecar para que fleetview (y otras
# herramientas) puedan mostrarlo sin tener este stdin. El statusline se re-ejecuta
# cada pocos segundos, así que el dato se mantiene fresco mientras la sesión vive.
if [ -n "$SESSION_ID" ]; then
RTDIR="$HOME/.claude/runtime"
mkdir -p "$RTDIR" 2>/dev/null
RTF="$RTDIR/${SESSION_ID}.json"
RTMP="${RTF}.tmp.$$"
if jq -n \
--argjson pct "${CONTEXT_PCT:-0}" \
--argjson used "${CONTEXT_USED:-0}" \
--argjson total "${CONTEXT_TOTAL:-200000}" \
'{ctx_pct:$pct, ctx_used:$used, ctx_total:$total}' > "$RTMP" 2>/dev/null; then
mv "$RTMP" "$RTF" 2>/dev/null
else
rm -f "$RTMP" 2>/dev/null
fi
fi
# Costos
TOTAL_COST=$(echo "$INPUT" | jq -r '.cost.total_cost_usd // 0' | xargs printf "%.3f")
SESSION_DURATION=$(echo "$INPUT" | jq -r '.cost.total_duration_ms // 0')
@@ -304,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}"
+77 -9
View File
@@ -54,20 +54,26 @@ done
echo ""
echo "=== Instalando archivos de configuración ==="
# 1. Status Line Script
# 1. Status Line Script (enlace simbólico)
STATUSLINE_SOURCE="$REPO_DIR/.claude/statusline.sh"
STATUSLINE_TARGET="$CLAUDE_DIR/statusline.sh"
if [ -f "$STATUSLINE_SOURCE" ]; then
if [ -f "$STATUSLINE_TARGET" ]; then
BACKUP="$STATUSLINE_TARGET.backup.$(date +%Y%m%d_%H%M%S)"
echo "Backup: statusline.sh -> $BACKUP"
mv "$STATUSLINE_TARGET" "$BACKUP"
chmod +x "$STATUSLINE_SOURCE"
if [ -L "$STATUSLINE_TARGET" ] && [ "$(readlink "$STATUSLINE_TARGET")" = "$STATUSLINE_SOURCE" ]; then
echo "OK: statusline.sh ya está enlazado correctamente"
else
# Symlink (roto o apuntando mal): borrar; archivo real: backup
if [ -L "$STATUSLINE_TARGET" ]; then
rm -f "$STATUSLINE_TARGET"
elif [ -e "$STATUSLINE_TARGET" ]; then
BACKUP="$STATUSLINE_TARGET.backup.$(date +%Y%m%d_%H%M%S)"
echo "Backup: statusline.sh -> $BACKUP"
mv "$STATUSLINE_TARGET" "$BACKUP"
fi
ln -s "$STATUSLINE_SOURCE" "$STATUSLINE_TARGET"
echo "Enlazado: statusline.sh -> $STATUSLINE_SOURCE"
fi
cp "$STATUSLINE_SOURCE" "$STATUSLINE_TARGET"
chmod +x "$STATUSLINE_TARGET"
echo "Copiado: statusline.sh (ejecutable)"
else
echo "WARN: statusline.sh no encontrado en el repo"
fi
@@ -96,6 +102,66 @@ else
echo "WARN: settings.json no encontrado en el repo"
fi
# 3. CLAUDE.md (enlace simbólico - preferencias globales)
CLAUDEMD_SOURCE="$REPO_DIR/.claude/CLAUDE.md"
CLAUDEMD_TARGET="$CLAUDE_DIR/CLAUDE.md"
if [ -f "$CLAUDEMD_SOURCE" ]; then
if [ -L "$CLAUDEMD_TARGET" ] && [ "$(readlink "$CLAUDEMD_TARGET")" = "$CLAUDEMD_SOURCE" ]; then
echo "OK: CLAUDE.md ya está enlazado correctamente"
else
# Symlink (roto o apuntando mal): borrar; archivo real: backup
if [ -L "$CLAUDEMD_TARGET" ]; then
rm -f "$CLAUDEMD_TARGET"
elif [ -e "$CLAUDEMD_TARGET" ]; then
BACKUP="$CLAUDEMD_TARGET.backup.$(date +%Y%m%d_%H%M%S)"
echo "Backup: CLAUDE.md -> $BACKUP"
mv "$CLAUDEMD_TARGET" "$BACKUP"
fi
ln -s "$CLAUDEMD_SOURCE" "$CLAUDEMD_TARGET"
echo "Enlazado: CLAUDE.md -> $CLAUDEMD_SOURCE"
fi
else
echo "WARN: CLAUDE.md no encontrado en el repo"
fi
# === Instalando hooks (enlace simbólico por archivo) ===
echo ""
echo "=== Instalando hooks ==="
HOOKS_SOURCE_DIR="$REPO_DIR/.claude/hooks"
HOOKS_TARGET_DIR="$CLAUDE_DIR/hooks"
if [ -d "$HOOKS_SOURCE_DIR" ]; then
mkdir -p "$HOOKS_TARGET_DIR"
for hook in "$HOOKS_SOURCE_DIR"/*.sh; do
[ -e "$hook" ] || continue
chmod +x "$hook"
HOOK_NAME="$(basename "$hook")"
HOOK_TARGET="$HOOKS_TARGET_DIR/$HOOK_NAME"
# Si ya es symlink correcto, saltar
if [ -L "$HOOK_TARGET" ] && [ "$(readlink "$HOOK_TARGET")" = "$hook" ]; then
echo "OK: hooks/$HOOK_NAME ya está enlazado correctamente"
continue
fi
# Symlink (roto o apuntando mal): borrar sin backup; archivo real: backup
if [ -L "$HOOK_TARGET" ]; then
rm -f "$HOOK_TARGET"
elif [ -e "$HOOK_TARGET" ]; then
BACKUP="$HOOK_TARGET.backup.$(date +%Y%m%d_%H%M%S)"
echo "Backup: hooks/$HOOK_NAME -> $BACKUP"
mv "$HOOK_TARGET" "$BACKUP"
fi
ln -s "$hook" "$HOOK_TARGET"
echo "Enlazado: hooks/$HOOK_NAME -> $hook"
done
else
echo "WARN: $HOOKS_SOURCE_DIR no existe, saltando hooks"
fi
# === Limpieza de configuración que no debe cambiar ===
echo ""
echo "=== Limpiando configuración inmutable ==="
@@ -189,6 +255,8 @@ echo "Tus comandos y configuración ahora están sincronizados con el repositori
echo ""
echo "Configuración instalada:"
echo " • Skills, Agents y Commands enlazados simbólicamente"
echo " • Hooks (goal_*.sh) enlazados simbólicamente"
echo " • CLAUDE.md (preferencias globales) enlazado"
echo " • Status Line configurada con vibecoding setup"
echo " • Settings.json enlazado (compartido entre repos)"
echo " • Backups viejos limpiados (>7 días)"