---
name: swl:metricas
description: Muestra métricas acumuladas de la sesión de trabajo actual: tokens consumidos, costo estimado en USD, tool calls y distribución por herramienta. Cargar cuando el usuario quiera ver el consumo de la sesión, verificar el presupuesto o entender qué herramientas dominan el costo.
allowed_tools: ["Read", "Write", "Bash", "Glob", "Grep"]
---

# /swl:metricas — Métricas de la sesión actual

Muestra las métricas acumuladas de la sesión de trabajo actual: tokens,
costo estimado, herramientas más usadas y estado del presupuesto configurado.

## Cuándo usar

- Para saber cuánto se ha consumido en la sesión antes de continuar
- Para identificar qué operaciones generan más costo
- Para verificar si se está cerca del límite de presupuesto
- Al final de una sesión para registrar el consumo real

## Subcomandos

```
/swl:metricas             — Resumen de métricas de la sesión
/swl:metricas detalle     — Desglose completo por herramienta y timeline
/swl:metricas fases       — Progreso de fases del proyecto desde feature-list.json
/swl:metricas historico   — Abre el dashboard histórico multi-sesión (alias de /swl:dashboard)
```

---

## Paso 1 — Leer fuentes de datos disponibles

### Fuente 1: archivo de métricas de sesión

```bash
# Bridge de tracking generado por el hook de costos
ls /tmp/swl-costs-*.json 2>/dev/null | sort -t- -k3 -n | tail -1
```

Si existe, leerlo. Contiene el acumulado de tool calls, tokens y costos de la sesión.

### Fuente 2: métricas persistidas del proyecto

```bash
cat .planning/METRICAS.md 2>/dev/null || echo "(sin métricas previas guardadas)"
```

### Fuente 3: configuración de presupuesto

```bash
cat manifiestos/hooks-config.json | python3 -c "
import json, sys
cfg = json.load(sys.stdin)
budget = cfg.get('costBudget', {})
print('maxUsd:', budget.get('maxUsd', 'no configurado'))
print('maxTokens:', budget.get('maxTokens', 'no configurado'))
print('alertAt:', budget.get('alertAt', 'no configurado'))
" 2>/dev/null || echo "(sin presupuesto configurado)"
```

---

## Paso 2 — Calcular y mostrar resumen

Construir el resumen con los datos disponibles. Si no hay bridge activo,
usar estimaciones basadas en el historial de `.planning/METRICAS.md`.

### Formato de resumen

```
╔══════════════════════════════════════════════════════╗
║          SWL — Métricas de Sesión                    ║
╚══════════════════════════════════════════════════════╝

Sesión actual
─────────────────────────────────────────────────────
  Tool calls totales:    NNN
  Tokens consumidos:     ~NNN,NNN (entrada + salida)
  Costo estimado:        ~$N.NNNN USD
  Modelo predominante:   claude-sonnet / claude-opus
  Duración estimada:     ~N minutos

Presupuesto
─────────────────────────────────────────────────────
  Límite configurado:    $N.NN USD / NNN,NNN tokens
  Consumido:             NN%  [████████░░] N.NN USD
  Estado:                OK / ALERTA / EXCEDIDO

Top 5 herramientas por costo
─────────────────────────────────────────────────────
  1. Bash          NNN calls    ~$N.NNNN
  2. Read          NNN calls    ~$N.NNNN
  3. Write         NNN calls    ~$N.NNNN
  4. Grep          NNN calls    ~$N.NNNN
  5. Edit          NNN calls    ~$N.NNNN
```

Si el estado es ALERTA (>= `alertAt` del presupuesto):
indicar con mensaje visible y sugerir `/swl:compactar` para reducir contexto.

Si el estado es EXCEDIDO:
indicar claramente y recomendar revisar la configuración en `manifiestos/hooks-config.json`.

---

## Paso 3 (detalle) — Desglose completo

Solo si se invocó `/swl:metricas detalle`:

### Tabla completa por herramienta

```
Herramienta    Calls    Tokens entrada    Tokens salida    Costo USD
─────────────────────────────────────────────────────────────────────
Bash            NNN         NNN,NNN           NNN,NNN       $N.NNNN
Read            NNN         NNN,NNN           NNN,NNN       $N.NNNN
Write           NNN         NNN,NNN           NNN,NNN       $N.NNNN
Edit            NNN         NNN,NNN           NNN,NNN       $N.NNNN
Grep            NNN         NNN,NNN           NNN,NNN       $N.NNNN
Glob            NNN         NNN,NNN           NNN,NNN       $N.NNNN
Agent           NNN         NNN,NNN           NNN,NNN       $N.NNNN
Skill           NNN         NNN,NNN           NNN,NNN       $N.NNNN
─────────────────────────────────────────────────────────────────────
TOTAL           NNN         NNN,NNN           NNN,NNN       $N.NNNN
```

### Comparativa histórica (si existe usage.db)

Ejecutar el siguiente script Python para comparar la sesión actual contra el promedio
histórico de los últimos 30 días almacenado en `~/.claude/usage.db`:

```bash
python3 - << 'EOF'
import sqlite3, json, os
from pathlib import Path
from datetime import datetime, timedelta, timezone

db = Path.home() / ".claude" / "usage.db"
if not db.exists():
    print("(sin base de datos histórica — ejecuta /swl:dashboard primero para generarla)")
else:
    conn = sqlite3.connect(db)
    conn.row_factory = sqlite3.Row
    ahora = datetime.now(timezone.utc)
    hace30 = (ahora - timedelta(days=30)).isoformat()
    hoy = ahora.strftime("%Y-%m-%d")

    # Promedio de los últimos 30 días por sesión
    res = conn.execute("""
        SELECT COUNT(DISTINCT session_id) as sesiones,
               ROUND(AVG(input_tokens + output_tokens), 0) as avg_tokens_por_turno,
               SUM(input_tokens + output_tokens) as total_tokens
        FROM turns WHERE timestamp >= ?
    """, (hace30,)).fetchone()

    # Top 5 agentes SWL históricos
    try:
        agentes = conn.execute("""
            SELECT agent_name, COUNT(*) as n
            FROM turns
            WHERE agent_name IS NOT NULL AND timestamp >= ?
            GROUP BY agent_name ORDER BY n DESC LIMIT 5
        """, (hace30,)).fetchall()
        top_agentes = ", ".join(f"{r['agent_name']}({r['n']})" for r in agentes) or "(sin datos)"
    except Exception:
        top_agentes = "(columna agent_name no disponible — requiere nuevo scan)"

    # Top 5 skills SWL históricos
    try:
        skills = conn.execute("""
            SELECT skill_name, COUNT(*) as n
            FROM turns
            WHERE skill_name IS NOT NULL AND timestamp >= ?
            GROUP BY skill_name ORDER BY n DESC LIMIT 5
        """, (hace30,)).fetchall()
        top_skills = ", ".join(f"{r['skill_name']}({r['n']})" for r in skills) or "(sin datos)"
    except Exception:
        top_skills = "(columna skill_name no disponible — requiere nuevo scan)"

    conn.close()
    print(f"\nComparativa histórica (últimos 30 días)")
    print(f"{'─'*52}")
    print(f"  Sesiones registradas:    {res['sesiones'] or 0}")
    print(f"  Total tokens:            {(res['total_tokens'] or 0):,}")
    print(f"  Avg tokens/turno:        {int(res['avg_tokens_por_turno'] or 0):,}")
    print(f"  Top agentes SWL:         {top_agentes}")
    print(f"  Top skills SWL:          {top_skills}")
    print(f"\n  Para ver gráficas completas: /swl:dashboard")
EOF
```

### Desglose por agente SWL (sesión actual)

Leer el bridge `/tmp/swl-costs-*.json` activo y extraer `costePorAgente`:

```bash
python3 - << 'EOF'
import json, glob, os
bridges = sorted(glob.glob(os.path.join(os.environ.get('TMPDIR', '/tmp'), 'swl-costs-*.json')))
if not bridges:
    print("(sin datos de agentes en esta sesión)")
else:
    data = json.load(open(bridges[-1]))
    agentes = data.get('costePorAgente', {})
    if not agentes:
        print("(no se invocaron agentes SWL en esta sesión)")
    else:
        total_costo = data.get('costoTotal', 0)
        print(f"\nAgentes SWL — sesión actual")
        print("─" * 68)
        print(f"  {'Agente':<35} {'Calls':>6}  {'Tokens':>9}  {'Costo':>8}  {'%':>5}")
        print("─" * 68)
        for nombre, d in sorted(agentes.items(), key=lambda x: -x[1].get('costo', 0)):
            pct = f"{d['costo']/total_costo*100:.1f}%" if total_costo > 0 else "—"
            nombre_c = nombre[:35]
            print(f"  {nombre_c:<35} {d.get('llamadas',0):>6}  {d.get('tokens',0):>9,}  ${d.get('costo',0):>7.4f}  {pct:>5}")
        print("─" * 68)
EOF
```

### Timeline de actividad

Agrupar las tool calls por bloque de tiempo (5 minutos) y mostrar la distribución
de actividad a lo largo de la sesión. Útil para identificar picos de consumo
asociados a tareas específicas.

---

## Paso 4 — Persistir en METRICAS.md (opcional)

Si el usuario lo solicita o si se detecta fin de sesión (via checkpoint):

```bash
cat >> .planning/METRICAS.md << 'EOF'

## Sesión YYYY-MM-DD HH:MM

- Tool calls: NNN
- Tokens: ~NNN,NNN
- Costo estimado: ~$N.NNNN USD
- Modelo: claude-sonnet
- Duración: ~N min
- Comando que más consumió: Bash (NN%)
EOF
```

---

---

## Subcomando: `fases` — Progreso de fases del proyecto

Si el usuario invoca `/swl:metricas fases`, muestra el estado actual de las
fases del proyecto leyendo el JSON derivado de `HOJA-RUTA.md`.

### Generación y lectura del estado

```bash
# Regenerar el JSON desde HOJA-RUTA.md (fuente de verdad markdown)
node scripts/derivar-feature-list.js

# Leer el JSON generado en .planning/feature-list.json
cat .planning/feature-list.json | python3 -c "
import json, sys
data = json.load(sys.stdin)
t = data['totales']
total = t['fases'] or 1
pct = (t['completadas'] / total) * 100

print(f\"\\nSWL — Progreso de fases del proyecto\")
print('─' * 60)
print(f\"  Total de fases:        {t['fases']}\")
print(f\"  Completadas:           {t['completadas']}  ({pct:.0f}%)\")
print(f\"  En progreso:           {t['en_progreso']}\")
print(f\"  Pendientes:            {t['pendientes']}\")
print(f\"  Bloqueadas:            {t['bloqueadas']}\")
print(f\"  Spec lista:            {t['spec_listas']}\")
print('─' * 60)
print()
for f in data['fases']:
    estado = f['estado']
    marca = {'completado': '✓', 'en_progreso': '◐', 'pendiente': '○',
             'bloqueado': '✗', 'spec_listo': '◔'}.get(estado, '?')
    nombre = f['nombre'][:48]
    art = f.get('artefactos') or {}
    plan = ' [PLAN]' if art.get('plan_md') else ''
    resumen = ' [RESUMEN]' if art.get('resumen_md') else ''
    print(f\"  {marca} Fase {f['numero']:>2}: {nombre:<50}{plan}{resumen}\")
print()
print(f\"  Fuente: {data['fuente']}  ·  Generado: {data['generado_en']}\")
"
```

### Lo que se reporta

- **Totales**: cuántas fases hay y cuántas en cada estado canónico (`completado`,
  `en_progreso`, `pendiente`, `bloqueado`, `spec_listo`).
- **Lista de fases** con marca visual de estado y artefactos presentes
  (`[PLAN]` si existe `0N-PLAN.md`, `[RESUMEN]` si existe `0N-RESUMEN.md`).
- **Timestamp** de generación para detectar staleness.

### Cuándo usar

- Antes de retomar trabajo en una fase, para ver el estado actualizado.
- Al cerrar una sesión productiva, para verificar progreso vs HOJA-RUTA.md.
- Para auditoría programática (consumir el JSON desde un hook o dashboard externo).

### Detección de drift

Si `HOJA-RUTA.md` se modifica pero `feature-list.json` no se regenera, los datos
quedan desactualizados. Para verificar:

```bash
node scripts/derivar-feature-list.js --check
# exit 0 = sincronizado, exit 2 = drift detectado
```

`/swl:metricas fases` siempre regenera primero, así que ve estado fresco.

---

## Subcomando: `historico`

Si el usuario invoca `/swl:metricas historico`, redirigir inmediatamente a:

```
Invocar Skill("swl-dashboard") con subcomando "dashboard".
```

Muestra el historial completo multi-sesión con gráficas interactivas en
http://localhost:8080. Ver `/swl:dashboard` para más detalles.

---

## Notas de precisión

Los costos son estimaciones basadas en los precios publicados de la API de Anthropic.
El costo real puede variar según:
- Modelo efectivamente usado (fallback a haiku en contextos de bajo riesgo)
- Caché de prompt (reduce costo de tokens de entrada repetidos)
- Variaciones de precio por región o plan

Para costos exactos, consultar el dashboard de Anthropic Console.
