From 4aaeb8844fdf802bc7484734f02ab93090b7919b Mon Sep 17 00:00:00 2001 From: josedario87 Date: Sun, 15 Feb 2026 14:21:18 -0600 Subject: [PATCH] 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 --- .../plugins/known_marketplaces.json | 2 +- .claude-ejecutor/settings.json | 63 +- .claude-ejecutor/settings.local.json | 62 ++ .claude/settings.local.json | 18 +- .claude/ui.json | 2 +- docs/agent-bar.md | 334 ++++++++ frontend/src/App.vue | 7 +- frontend/src/components/AgentBar.vue | 753 +++++++++++++++--- frontend/src/components/git/StatusTree.vue | 365 +++++++++ frontend/src/components/git/index.ts | 1 + frontend/src/pages/GitPage.vue | 47 +- server/routes/claude-status.ts | 1 + server/services/terminal.ts | 7 +- 13 files changed, 1489 insertions(+), 173 deletions(-) create mode 100644 .claude-ejecutor/settings.local.json create mode 100644 docs/agent-bar.md create mode 100644 frontend/src/components/git/StatusTree.vue diff --git a/.claude-ejecutor/plugins/known_marketplaces.json b/.claude-ejecutor/plugins/known_marketplaces.json index 4c9e1c2..6f04738 100644 --- a/.claude-ejecutor/plugins/known_marketplaces.json +++ b/.claude-ejecutor/plugins/known_marketplaces.json @@ -5,6 +5,6 @@ "repo": "anthropics/claude-plugins-official" }, "installLocation": "C:\\Users\\jodar\\agent-ui\\.claude-ejecutor\\plugins\\marketplaces\\claude-plugins-official", - "lastUpdated": "2026-02-15T08:27:07.485Z" + "lastUpdated": "2026-02-15T19:50:55.853Z" } } \ No newline at end of file diff --git a/.claude-ejecutor/settings.json b/.claude-ejecutor/settings.json index c0f4984..d7bfbbc 100644 --- a/.claude-ejecutor/settings.json +++ b/.claude-ejecutor/settings.json @@ -1,6 +1,7 @@ { "env": { - "DISABLE_TELEMETRY": "1" + "DISABLE_TELEMETRY": "1", + "AGENT_NAME": "ejecutor" }, "permissions": { "allow": [ @@ -18,5 +19,65 @@ "Task", "NotebookEdit" ] + }, + "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\\\":\\\"ejecutor\\\"}' -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\\\":\\\"ejecutor\\\"}' -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\\\":\\\"ejecutor\\\"}' -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\\\":\\\"ejecutor\\\"}' -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\\\":\\\"ejecutor\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"", + "timeout": 5000 + } + ] + } + ] } } diff --git a/.claude-ejecutor/settings.local.json b/.claude-ejecutor/settings.local.json new file mode 100644 index 0000000..cf53d9d --- /dev/null +++ b/.claude-ejecutor/settings.local.json @@ -0,0 +1,62 @@ +{ + "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\\\":\\\"ejecutor\\\"}' -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\\\",\\\"tool\\\":\\\"$CLAUDE_TOOL_NAME\\\",\\\"agent\\\":\\\"ejecutor\\\"}' -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\\\",\\\"tool\\\":\\\"$CLAUDE_TOOL_NAME\\\",\\\"agent\\\":\\\"ejecutor\\\"}' -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\\\":\\\"ejecutor\\\"}' -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\\\":\\\"ejecutor\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"", + "timeout": 5000 + } + ] + } + ] + } +} diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 13f8dd5..a5f7c57 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -95,7 +95,7 @@ "hooks": [ { "type": "command", - "command": "powershell -NoProfile -Command \"try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"processing\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"", + "command": "powershell -NoProfile -Command \"if($env:AGENT_NAME){exit};try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"processing\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"", "timeout": 5000 } ] @@ -107,7 +107,7 @@ "hooks": [ { "type": "command", - "command": "powershell -NoProfile -Command \"try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"reading\\\",\\\"tool\\\":\\\"$CLAUDE_TOOL_NAME\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"", + "command": "powershell -NoProfile -Command \"if($env:AGENT_NAME){exit};try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"reading\\\",\\\"tool\\\":\\\"$CLAUDE_TOOL_NAME\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"", "timeout": 5000 } ] @@ -117,7 +117,7 @@ "hooks": [ { "type": "command", - "command": "powershell -NoProfile -Command \"try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"writing\\\",\\\"tool\\\":\\\"$CLAUDE_TOOL_NAME\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"", + "command": "powershell -NoProfile -Command \"if($env:AGENT_NAME){exit};try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"writing\\\",\\\"tool\\\":\\\"$CLAUDE_TOOL_NAME\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"", "timeout": 5000 } ] @@ -127,7 +127,7 @@ "hooks": [ { "type": "command", - "command": "powershell -NoProfile -Command \"try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"toolUse\\\",\\\"tool\\\":\\\"$CLAUDE_TOOL_NAME\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"", + "command": "powershell -NoProfile -Command \"if($env:AGENT_NAME){exit};try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"toolUse\\\",\\\"tool\\\":\\\"$CLAUDE_TOOL_NAME\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"", "timeout": 5000 } ] @@ -139,7 +139,7 @@ "hooks": [ { "type": "command", - "command": "powershell -NoProfile -Command \"try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"toolDone\\\",\\\"tool\\\":\\\"$CLAUDE_TOOL_NAME\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"", + "command": "powershell -NoProfile -Command \"if($env:AGENT_NAME){exit};try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"toolDone\\\",\\\"tool\\\":\\\"$CLAUDE_TOOL_NAME\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"", "timeout": 5000 } ] @@ -150,7 +150,7 @@ "hooks": [ { "type": "command", - "command": "powershell -NoProfile -Command \"try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"sessionStart\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"", + "command": "powershell -NoProfile -Command \"if($env:AGENT_NAME){exit};try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"sessionStart\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"", "timeout": 5000 } ] @@ -162,7 +162,7 @@ "hooks": [ { "type": "command", - "command": "powershell -NoProfile -Command \"try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"permissionRequest\\\",\\\"tool\\\":\\\"$CLAUDE_TOOL_NAME\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"", + "command": "powershell -NoProfile -Command \"if($env:AGENT_NAME){exit};try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"permissionRequest\\\",\\\"tool\\\":\\\"$CLAUDE_TOOL_NAME\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"", "timeout": 5000 } ] @@ -174,7 +174,7 @@ "hooks": [ { "type": "command", - "command": "powershell -NoProfile -Command \"try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"notification\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"", + "command": "powershell -NoProfile -Command \"if($env:AGENT_NAME){exit};try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"notification\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"", "timeout": 5000 } ] @@ -185,7 +185,7 @@ "hooks": [ { "type": "command", - "command": "powershell -NoProfile -Command \"try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"idle\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"", + "command": "powershell -NoProfile -Command \"if($env:AGENT_NAME){exit};try { Invoke-RestMethod -Uri 'http://localhost:4101/api/claude-status' -Method POST -Body '{\\\"status\\\":\\\"idle\\\"}' -ContentType 'application/json' -TimeoutSec 2 | Out-Null } catch {}\"", "timeout": 5000 } ] diff --git a/.claude/ui.json b/.claude/ui.json index 6749019..dab23bc 100644 --- a/.claude/ui.json +++ b/.claude/ui.json @@ -5,5 +5,5 @@ "gradient": "linear-gradient(135deg, #6366f1, #8b5cf6)", "terminalBg": "#0f0a1a", "terminalBorder": "#6366f1", - "enabled": true + "enabled": false } diff --git a/docs/agent-bar.md b/docs/agent-bar.md new file mode 100644 index 0000000..ae434f9 --- /dev/null +++ b/docs/agent-bar.md @@ -0,0 +1,334 @@ +# 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-/` + +``` +.claude-miagente/ + ui.json + settings.json + CLAUDE.md (opcional — instrucciones del agente) +``` + +### 2. Configurar `ui.json` + +```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`: + +```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-/ui.json`. + +--- + +## Status disponibles + +El payload `POST /api/claude-status` acepta: + +```json +{ + "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=""]` en `AgentBar.vue`. + +### Ejemplo: Ejecutor + +Ejecutor tiene animaciones completamente distintas definidas en el CSS de `AgentBar.vue`: + +```css +/* 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": ""` en su `env` de `settings.json` +2. Los hooks de main tienen un guard al inicio: + +``` +if($env:AGENT_NAME){exit} +``` + +3. Cuando main corre → `AGENT_NAME` no existe → hooks de main disparan normalmente +4. 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: + +```bash +# 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: + +```json +[ + { + "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` + +```json +{ + "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` + +```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`. diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 6dedb8d..88fd8c6 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -153,7 +153,12 @@ function connectStatusWs() { const msg = JSON.parse(event.data) if (msg.type === 'claude-status') { const status = msg.status as ClaudeStatus - console.log('[App] Claude status:', status, msg.tool) + const agent = msg.agent || 'main' + console.log('[App] Claude status:', status, msg.tool, agent) + + // Only animate the main FAB for 'main' agent — other agents use AgentBar + if (agent !== 'main') return + claudeStatus.value = status claudeTool.value = msg.tool || null diff --git a/frontend/src/components/AgentBar.vue b/frontend/src/components/AgentBar.vue index c43bbe6..0a97332 100644 --- a/frontend/src/components/AgentBar.vue +++ b/frontend/src/components/AgentBar.vue @@ -1,5 +1,6 @@ diff --git a/frontend/src/components/git/index.ts b/frontend/src/components/git/index.ts index 6cbc7f8..5666588 100644 --- a/frontend/src/components/git/index.ts +++ b/frontend/src/components/git/index.ts @@ -4,3 +4,4 @@ export { default as CommitList } from './CommitList.vue' export { default as BranchSelector } from './BranchSelector.vue' export { default as ProjectTree } from './ProjectTree.vue' export { default as FileViewer } from './FileViewer.vue' +export { default as StatusTree } from './StatusTree.vue' diff --git a/frontend/src/pages/GitPage.vue b/frontend/src/pages/GitPage.vue index c9bae62..8f345f8 100644 --- a/frontend/src/pages/GitPage.vue +++ b/frontend/src/pages/GitPage.vue @@ -1,7 +1,7 @@