Files
agent-ui/docs/agent-bar.md
josedario87 4aaeb8844f feat: Add tree file view for git status, AgentBar dock, and settings updates
- Add StatusTree component with collapsible directory hierarchy for staged/unstaged/untracked files
- Replace flat file lists in GitPage with tree view showing file type icons and git status badges
- Add AgentBar arc dock with per-agent terminal frame and voice modal
- Update ejecutor settings with hooks for claude-status reporting
2026-02-15 14:21:18 -06:00

11 KiB

Agent Bar — Sistema de Burbujas por Agente

Resumen

Cada agente de Claude Code (main, ejecutor, etc.) puede tener su propia burbuja flotante en la UI. Las burbujas muestran animaciones en tiempo real cuando el agente procesa, usa herramientas, recibe notificaciones, etc.

Arquitectura

Agente (hooks en settings.json)
    |
    v
POST /api/claude-status  { status, agent, tool? }
    |
    v
server/routes/claude-status.ts  (valida y reenvía)
    |
    v
Terminal Server :4103  /claude-status  (broadcast WebSocket)
    |
    v
AgentBar.vue  (escucha ws://...:4103, matchea por agent id)
    |
    v
CSS classes + keyframes  (animaciones en la burbuja)

Archivos clave

Archivo Función
.claude/ui.json Config visual del agente main
.claude-ejecutor/ui.json Config visual de ejecutor
.claude-ejecutor/settings.json Hooks que envían status de ejecutor
server/routes/agents.ts API GET /api/agents — descubre agentes
server/routes/claude-status.ts API POST /api/claude-status — recibe status
server/services/terminal.ts Broadcast WebSocket a todos los clientes
frontend/src/components/AgentBar.vue Burbujas flotantes + animaciones
frontend/src/config/endpoints.ts URLs de WebSocket (dev vs HTTPS)

Cómo agregar un nuevo agente

1. Crear directorio .claude-<nombre>/

.claude-miagente/
  ui.json
  settings.json
  CLAUDE.md          (opcional — instrucciones del agente)

2. Configurar ui.json

{
  "label": "Mi Agente",
  "shortLabel": "MA",
  "color": "#8b5cf6",
  "gradient": "linear-gradient(135deg, #8b5cf6, #7c3aed)",
  "terminalBg": "#0f0a1a",
  "terminalBorder": "#8b5cf6",
  "enabled": true
}
Campo Descripción
label Nombre completo (tooltip, titlebar del terminal)
shortLabel 1-3 caracteres que se muestran dentro de la burbuja
color Color base (hex). Usado para box-shadow, glow, prompt
gradient Fondo de la burbuja (CSS gradient)
terminalBg Background del terminal frame mockup
terminalBorder Color del borde del terminal frame
enabled true = visible en la barra, false = oculto

3. Configurar hooks en settings.json

Los hooks notifican a la UI cuando el agente hace algo. Agregar dentro de settings.json:

{
  "env": {
    "AGENT_NAME": "miagente"
  },
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [{
          "type": "command",
          "command": "powershell -NoProfile -Command \"try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"processing\\\",\\\"agent\\\":\\\"miagente\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"",
          "timeout": 5000
        }]
      }
    ],
    "PreToolUse": [
      {
        "matcher": ".*",
        "hooks": [{
          "type": "command",
          "command": "powershell -NoProfile -Command \"try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"toolUse\\\",\\\"agent\\\":\\\"miagente\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"",
          "timeout": 5000
        }]
      }
    ],
    "PostToolUse": [
      {
        "matcher": ".*",
        "hooks": [{
          "type": "command",
          "command": "powershell -NoProfile -Command \"try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"toolDone\\\",\\\"agent\\\":\\\"miagente\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"",
          "timeout": 5000
        }]
      }
    ],
    "Notification": [
      {
        "matcher": ".*",
        "hooks": [{
          "type": "command",
          "command": "powershell -NoProfile -Command \"try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"notification\\\",\\\"agent\\\":\\\"miagente\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"",
          "timeout": 5000
        }]
      }
    ],
    "Stop": [
      {
        "hooks": [{
          "type": "command",
          "command": "powershell -NoProfile -Command \"try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"idle\\\",\\\"agent\\\":\\\"miagente\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"",
          "timeout": 5000
        }]
      }
    ]
  }
}

Importante: El campo "AGENT_NAME" en env es necesario para que los hooks heredados de .claude/settings.local.json (main) se desactiven automáticamente. Sin esto, el agente dispararía tanto sus hooks como los de main.

4. Resultado

La burbuja aparece automáticamente en la barra inferior. GET /api/agents la descubre al escanear .claude-<nombre>/ui.json.


Status disponibles

El payload POST /api/claude-status acepta:

{
  "status": "processing",
  "agent": "ejecutor",
  "tool": "render_vue_component"
}
Status Hook que lo dispara Animación default Descripción
processing UserPromptSubmit Pulse naranja + thinking dots Agente procesando input
toolUse PreToolUse Flash blanco expandiéndose Usando una herramienta
toolDone PostToolUse Reset de reading/writing Herramienta completada
reading PreToolUse (Read/Glob/Grep) Bubble cyan + wobble + ojo Leyendo archivos
writing PreToolUse (Edit/Write) Bubble verde + scale + lápiz Escribiendo archivos
notification Notification Bounce vertical (4x) Notificación del agente
idle Stop Vuelve al color original Agente inactivo
permissionRequest PermissionRequest Rojo alert + triángulo shake Esperando permiso
sessionStart SessionStart (solo App.vue FAB) Sesión iniciada

Animaciones custom por agente

Cada agente puede tener sus propias animaciones sobreescribiendo las defaults con el selector [data-agent="<id>"] en AgentBar.vue.

Ejemplo: Ejecutor

Ejecutor tiene animaciones completamente distintas definidas en el CSS de AgentBar.vue:

/* Ejecutor Processing: Heartbeat — doble latido cardiaco */
.agent-bubble[data-agent="ejecutor"].processing {
  animation: ej-heartbeat 1.2s ease-in-out infinite !important;
}

/* Ejecutor Tool Flash: Shockwave crimson — doble anillo rojo */
.agent-bubble[data-agent="ejecutor"].tool-flash::after {
  background: rgba(239, 68, 68, 0.5) !important;
  animation: ej-shockwave 0.6s ease-out forwards !important;
}

/* Ejecutor Notification: Glitch — jitter + skew + chromatic split */
.agent-bubble[data-agent="ejecutor"].notification {
  animation: ej-glitch 0.15s steps(2) 8 !important;
}
Status Ejecutor Default
processing Heartbeat — doble latido, ember glow rojo/naranja, arcos girando Pulse naranja suave
toolUse Crimson Shockwave — doble anillo rojo expandiéndose Flash blanco simple
notification Glitch — jitter rápido, skew, split chromático cyan/red Bounce vertical
reading Infrared — glow rojo barriendo alrededor del borde Cyan wobble
writing Forge — pulso white-hot a rojo, brightness boost Verde scale pulse

Contenido interno

Cuando ejecutor está en processing, en vez de los 3 thinking dots genéricos, muestra un ember ring: dos arcos concéntricos girando en direcciones opuestas (blanco/naranja y rojo/crimson).

Agregar animaciones custom a otro agente

  1. Agregar @keyframes con prefijo único (ej: ma- para "miagente")
  2. Sobreescribir con .agent-bubble[data-agent="miagente"].processing { ... }
  3. Opcionalmente agregar contenido inner custom en el template con v-if="agent.id === 'miagente'"

Herencia de hooks y el guard AGENT_NAME

Claude Code hereda hooks de .claude/settings.local.json a todos los perfiles. Esto significa que si main tiene hooks, ejecutor también los dispara.

Solución implementada:

  1. Cada agente secundario define "AGENT_NAME": "<id>" en su env de settings.json
  2. Los hooks de main tienen un guard al inicio:
if($env:AGENT_NAME){exit}
  1. Cuando main corre → AGENT_NAME no existe → hooks de main disparan normalmente
  2. Cuando ejecutor corre → AGENT_NAME="ejecutor" → hooks de main hacen exit → solo disparan los hooks propios de ejecutor

Probar animaciones manualmente

Se puede simular cualquier status con curl:

# Processing (heartbeat)
curl -X POST http://localhost:4101/api/claude-status \
  -H "Content-Type: application/json" \
  -d '{"status":"processing","agent":"ejecutor"}'

# Tool use (shockwave)
curl -X POST http://localhost:4101/api/claude-status \
  -H "Content-Type: application/json" \
  -d '{"status":"toolUse","agent":"ejecutor","tool":"render_vue_component"}'

# Notification (glitch)
curl -X POST http://localhost:4101/api/claude-status \
  -H "Content-Type: application/json" \
  -d '{"status":"notification","agent":"ejecutor"}'

# Reset
curl -X POST http://localhost:4101/api/claude-status \
  -H "Content-Type: application/json" \
  -d '{"status":"idle","agent":"ejecutor"}'

API Reference

GET /api/agents

Retorna array de agentes descubiertos:

[
  {
    "id": "main",
    "name": "Claude Code (main)",
    "directory": ".claude",
    "files": [...],
    "uiConfig": {
      "label": "Main",
      "shortLabel": "M",
      "color": "#6366f1",
      "gradient": "linear-gradient(135deg, #6366f1, #8b5cf6)",
      "terminalBg": "#0f0a1a",
      "terminalBorder": "#6366f1",
      "enabled": false
    }
  },
  {
    "id": "ejecutor",
    "name": "Ejecutor",
    "directory": ".claude-ejecutor",
    "files": [...],
    "uiConfig": {
      "label": "Ejecutor",
      "shortLabel": "EJ",
      "color": "#ef4444",
      "gradient": "linear-gradient(135deg, #ef4444, #dc2626)",
      "terminalBg": "#0a0f1a",
      "terminalBorder": "#ef4444",
      "enabled": true
    }
  }
]
  • uiConfig: null si no existe ui.json en el directorio del agente
  • enabled: true por default si ui.json existe pero no tiene el campo

POST /api/claude-status

{
  "status": "processing",
  "agent": "ejecutor",
  "tool": "render_vue_component"
}
  • status (requerido): uno de los valores de ClaudeStatus
  • agent (opcional): id del agente. Default: "main"
  • tool (opcional): nombre de la herramienta en uso

GET /api/agents/file?path=.claude-ejecutor/ui.json

Lee un archivo de configuración de agente.

POST /api/agents/file?path=.claude-ejecutor/ui.json

{ "content": "{...}" }

Escribe un archivo de configuración. Valida JSON si el archivo termina en .json. Paths restringidos a .claude/*, .claude-*/*, CLAUDE.md, .mcp.json.