---
name: swl:salud
description: Diagnóstico completo de salud del sistema SWL. Verifica integridad de agentes, skills, comandos, reglas y hooks. Genera un reporte con score por componente y detecta agentes sin versión, skills huérfanos, comandos rotos y reglas contradictorias.
allowed_tools: ["Read", "Write", "Bash", "Glob", "Grep"]
---

# /swl:salud — Diagnóstico de salud del sistema SWL

Inspector de salud del sistema SWL. Verifica integridad de todos los componentes
usando **verificaciones deterministas** (conteos, existencia de campos, sintaxis).
Genera SALUD.md con score objetivo y reproducible.

**Carga**: `Skill("validacion-ci-sistema")` — contiene las reglas de validación por componente (agentes, skills, hooks, comandos, reglas), tabla de exit codes para hooks y checklist de integridad. Delega toda lógica de verificación al skill.

Este comando es de **solo lectura**. No modifica archivos — solo genera SALUD.md. Para corregir problemas, usar `swl:evolucionar`.

## Cuándo usar

- Antes de iniciar proyecto nuevo con SWL
- Después de agregar o modificar agentes/skills manualmente
- Cuando un agente produce outputs inesperados
- Después de merge o pull de cambios al repositorio
- Periódicamente como mantenimiento (mensual)

## Paso 0 — Inventario con Bash (determinista)

```bash
echo "=== INVENTARIO ===" && \
echo "Agentes: $(ls agentes/*.md 2>/dev/null | wc -l)" && \
echo "Skills: $(ls -d habilidades/*/ 2>/dev/null | wc -l)" && \
echo "Comandos: $(ls comandos/swl/*.md 2>/dev/null | wc -l)" && \
echo "Reglas: $(ls reglas/*.md 2>/dev/null | wc -l)" && \
echo "Hooks: $(ls hooks/*.js 2>/dev/null | wc -l)"
```

## Paso 0.5 — Detección automática de tier del proyecto

Antes de ejecutar los pasos de diagnóstico, /swl:salud detecta el tier del proyecto contando archivos relevantes:

```bash
find . -type f \
  -not -path '*/node_modules/*' \
  -not -path '*/.git/*' \
  -not -path '*/dist/*' \
  -not -path '*/build/*' \
  -not -path '*/.next/*' \
  -not -path '*/coverage/*' \
  -not -path '*/__pycache__/*' \
  -not -path '*/.cache/*' \
  -not -path '*/.planning/sessions/*' \
  | wc -l
```

| Tier | Rango | Características esperadas |
|---|---|---|
| **Simple** | <500 archivos | 1 contributor, CI mínimo o ninguno. CLAUDE.md opcional. Skills/agentes opcionales. |
| **Standard** | 500–5,000 archivos | Equipo pequeño con CI. CLAUDE.md + 1-2 reglas. 2-4 skills. Hooks básicos. |
| **Complex** | >5,000 archivos | Múltiples contributors, CI activo. Setup completo de 6 capas: agentes + skills + reglas + hooks + instintos + memoria. |

El tier detectado se reporta al inicio del SALUD.md generado.

**Default ante ambigüedad**: si el conteo cae cerca de 500 (490-510) o 5000 (4900-5100), aplicar el tier superior. Es preferible diagnosticar de más que de menos.

## Paso 1 — Diagnóstico de agentes (determinista)

Ejecutar script Bash que verifica cada agente mecánicamente:

```bash
errors=0; warns=0; ok=0
for f in agentes/*.md; do
  name=$(basename "$f" .md)
  has_name=$(head -30 "$f" | grep -c "^name:")
  has_desc=$(head -30 "$f" | grep -c "^description:")
  has_version=$(head -30 "$f" | grep -c "^version:")
  has_riesgo=$(head -30 "$f" | grep -c "^nivelRiesgo:")
  placeholders=$(grep -cE '\[TODO\]|\[COMPLETAR\]|\[TBD\]' "$f" || true)

  if [ "$has_name" -eq 0 ] || [ "$has_desc" -eq 0 ]; then
    echo "ERROR: $name — falta name o description"
    errors=$((errors+1))
  elif [ "$has_version" -eq 0 ] || [ "$has_riesgo" -eq 0 ]; then
    echo "ADVERTENCIA: $name — falta version o nivelRiesgo"
    warns=$((warns+1))
  elif [ "$placeholders" -gt 0 ]; then
    echo "ADVERTENCIA: $name — placeholders sin reemplazar"
    warns=$((warns+1))
  else
    ok=$((ok+1))
  fi
done
echo "Agentes: $ok OK, $warns advertencias, $errors errores"
```

Score de agentes = `(ok / total) * 100`

**Restricción de tier**: el análisis cruzado de agentes (cross-check entre `exclusiones` declaradas y rutas reales en `manifiestos/modulos.json`) se ejecuta solo en proyectos **Standard** o **Complex**. En tier Simple, reportar el conteo básico y omitir la validación cruzada.

## Paso 2 — Diagnóstico de skills (determinista)

```bash
errors=0; warns=0; ok=0
for d in habilidades/*/; do
  name=$(basename "$d")
  skill="$d/SKILL.md"
  if [ ! -f "$skill" ]; then
    echo "ERROR: $name — sin SKILL.md"
    errors=$((errors+1)); continue
  fi
  lines=$(wc -l < "$skill")
  has_name=$(head -10 "$skill" | grep -c "^name:")
  has_desc=$(head -10 "$skill" | grep -c "^description:")
  content_chars=$(sed -n '/^---$/,/^---$/d; p' "$skill" | wc -c)
  abs_paths=$(grep -cE 'C:\\|/home/|/Users/' "$skill" || true)

  if [ "$has_name" -eq 0 ] || [ "$has_desc" -eq 0 ]; then
    echo "ERROR: $name — frontmatter sin name o description"
    errors=$((errors+1))
  elif [ "$lines" -gt 300 ]; then
    echo "ADVERTENCIA: $name — $lines lineas (max 300)"
    warns=$((warns+1))
  elif [ "$content_chars" -lt 500 ]; then
    echo "ADVERTENCIA: $name — contenido muy corto ($content_chars chars)"
    warns=$((warns+1))
  elif [ "$abs_paths" -gt 0 ]; then
    echo "ADVERTENCIA: $name — paths absolutos detectados"
    warns=$((warns+1))
  else
    ok=$((ok+1))
  fi
done
echo "Skills: $ok OK, $warns advertencias, $errors errores"
```

Detectar skills huérfanos (no referenciados):

```bash
grep -r "Skill(" agentes/ comandos/ 2>/dev/null | grep -o '"[^"]*"' | sort | uniq
```

Score de skills = `((total - errors*10 - warns*2) / total) * 100`, mínimo 0

**Restricción de tier**: la detección de skills huérfanos (no referenciados en ningún agente ni comando) se ejecuta solo en proyectos **Standard** o **Complex**. En tier Simple, donde hay menos de 10 skills esperados, la verificación de huérfanos genera falsos positivos y se omite.

## Paso 3 — Diagnóstico de comandos (determinista)

```bash
errors=0; warns=0; ok=0
for f in comandos/swl/*.md; do
  name=$(basename "$f" .md)
  lines=$(wc -l < "$f")
  has_name=$(head -10 "$f" | grep -c "^name:")
  has_desc=$(head -10 "$f" | grep -c "^description:")
  desc_len=$(head -5 "$f" | grep "^description:" | wc -c)

  if [ "$has_name" -eq 0 ] || [ "$has_desc" -eq 0 ]; then
    echo "ERROR: $name — frontmatter sin name o description"
    errors=$((errors+1))
  elif [ "$desc_len" -lt 30 ]; then
    echo "ADVERTENCIA: $name — description muy breve"
    warns=$((warns+1))
  elif [ "$lines" -gt 300 ]; then
    echo "ADVERTENCIA: $name — $lines lineas (considerar delegar a skill)"
    warns=$((warns+1))
  else
    ok=$((ok+1))
  fi
done
echo "Comandos: $ok OK, $warns advertencias, $errors errores"
```

## Paso 4 — Diagnóstico de reglas (determinista)

```bash
errors=0; warns=0; ok=0
for f in reglas/*.md; do
  name=$(basename "$f" .md)
  lines=$(wc -l < "$f")
  has_h1=$(grep -c "^# " "$f" || true)
  has_checklist=$(grep -c "\- \[ \]" "$f" || true)

  if [ "$lines" -lt 20 ]; then
    echo "ERROR: $name — regla muy corta ($lines lineas)"
    errors=$((errors+1))
  elif [ "$has_h1" -eq 0 ]; then
    echo "ADVERTENCIA: $name — sin titulo H1"
    warns=$((warns+1))
  elif [ "$has_checklist" -eq 0 ]; then
    echo "ADVERTENCIA: $name — sin checklist"
    warns=$((warns+1))
  else
    ok=$((ok+1))
  fi
done
echo "Reglas: $ok OK, $warns advertencias, $errors errores"
```

## Paso 5 — Diagnóstico de hooks (determinista)

```bash
errors=0; warns=0; ok=0
for f in hooks/*.js; do
  name=$(basename "$f")
  if node --check "$f" 2>/dev/null; then
    if grep -q "$name" .claude/settings.json 2>/dev/null; then
      ok=$((ok+1))
    else
      echo "ADVERTENCIA: $name — no registrado en settings.json"
      warns=$((warns+1))
    fi
  else
    echo "ERROR: $name — error de sintaxis Node.js"
    errors=$((errors+1))
  fi
done
echo "Hooks: $ok OK, $warns advertencias, $errors errores"
```

## Paso 5b — Auditoría opcional de skills (opt-in)

Si la variable de entorno `SWL_AUDIT_SKILLS=1` está presente, ejecutar:

```bash
bash scripts/audit-skills.sh
```

Este paso es **no bloqueante**: si el comando falla por cualquier causa (red,
dependencia faltante, paquete no publicado), el diagnóstico continúa normalmente.

**Qué detecta**:
- Prompt injection en contenido de SKILL.md (instrucciones que intentan manipular el modelo).
- Secretos hardcodeados (API keys, tokens, contraseñas en ejemplos).
- URLs sospechosas embebidas en los skills.
- Estructura inválida de SKILL.md (frontmatter incompleto, ausencia de secciones requeridas).

**Requisito**: `pip install uv` (o `pipx install uv`) antes de activar.

**Activación manual**:
```bash
SWL_AUDIT_SKILLS=1 bash scripts/audit-skills.sh
```

Si `SWL_AUDIT_SKILLS` no está definida, el script imprime instrucciones y sale
con código 0 — no afecta el score ni el diagnóstico general.

## Paso 6 — Score global ponderado

```
Score = agentes x 0.30 + skills x 0.25 + comandos x 0.20 + reglas x 0.15 + hooks x 0.10

90-100: Excelente
75-89:  Saludable con mejoras menores
60-74:  Funcional con problemas
<60:    Estado crítico
```

## Paso 5c — Reporte de cobertura de frameworks de seguridad (opt-in)

Si la variable de entorno `SWL_AUDIT_FRAMEWORKS=1` está presente, ejecutar el
auditor de cobertura:

```bash
npx -y @saulwade/swl-ses@latest audit-coverage-frameworks --resumen
```

Propósito: reportar cuántos skills declaran mapeos a los 5 frameworks de
seguridad (NIST CSF 2.0, NIST AI RMF 1.0, MITRE ATLAS v5.4, ATT&CK v18,
D3FEND v1.3) y qué controles están cubiertos. Útil para:

- Verificar que los skills de dominio seguridad declaran mapeos antes de un
  audit de compliance
- Detectar gaps en la cobertura (ej: ningún skill cubre `PR.DS-11` — data
  integrity — si eso importa para el proyecto)
- Producir evidencia para auditores (SOC 2, ISO 27001) sobre controles
  implementados

El reporte NO marca skills sin mapeos como error — solo skills de dominio
seguridad necesitan declararlos. Ver `reglas/skills-estandar.md` sección
"Mapeo a frameworks de seguridad (REC-C01)" para criterios de cuándo declarar
cada framework.

**Filtrado por framework**:

```bash
SWL_AUDIT_FRAMEWORKS=1 npx -y @saulwade/swl-ses@latest audit-coverage-frameworks --framework=nist_ai_rmf --resumen
```

**Persistir snapshot**:

```bash
npx -y @saulwade/swl-ses@latest audit-coverage-frameworks --save
```

Persiste en `.planning/evolucion/cobertura-frameworks.json` para comparación
histórica tras incorporar nuevos skills de seguridad.

Si `SWL_AUDIT_FRAMEWORKS` no está definida, este paso se omite sin mensaje
— los reportes son opt-in por diseño (CLAUDE.md: "Variables de entorno
opt-in para integraciones enterprise").

## Paso 5d — Auditoría SAP-Agents (opt-in)

Si la variable de entorno `SWL_AUDIT_AGENTES=1` está presente, ejecutar el
auditor de agentes:

```bash
npx -y @saulwade/swl-ses@latest audit-agents-gaps --resumen
```

Propósito: reportar cuántos agentes carecen de Exclusion Clause (campo
`exclusiones` o sección `## Cuándo NO invocarme`) y Gotchas explícitos.
Previene agent hijacking por activación tangencial de agentes por similitud
superficial en `description`.

El reporte incluye conteo de excepciones documentadas por ADR (si las hay)
para separar gaps reales de excepciones por diseño.

Si `SWL_AUDIT_AGENTES` no está definida, este paso se omite — los reportes
son opt-in por diseño (CLAUDE.md: "Variables de entorno opt-in para
integraciones enterprise").

## Paso 5f — Calidad de CLAUDE.md (ADR-0016)

Auditoría determinista del archivo `CLAUDE.md` raíz del proyecto según best
practices Anthropic ("The CLAUDE.md file"): líneas totales, bullets
monolíticos, secciones canónicas (Stack/Comandos/Code style/Conventions),
uso de `@references`, placeholders sin reemplazar.

```bash
npx -y @saulwade/swl-ses@latest audit-claudemd --json
```

Veredictos posibles:

| Veredicto | Significado | Score impact |
|---|---|---|
| `OK` | Cumple best practices, sin hallazgos | +1 |
| `WARN` | Oportunidades de mejora (líneas excesivas, bullets gigantes, secciones ausentes, sin @references) | -0.5 |
| `ERROR` | No existe, placeholders sin reemplazar, secciones críticas ausentes | -1 |

Si veredicto != OK, el reporte sugiere ejecutar `/swl:claudemd refactor`
para identificar candidatos a extracción a archivos `@`-referenciados.

Variables de entorno: `SWL_CLAUDEMD_MAX_LINES` (default 200) y
`SWL_CLAUDEMD_MAX_BULLET_CHARS` (default 1000) ajustan los umbrales.
Documentación completa en `@docs/variables-entorno.md`.

Este paso siempre se ejecuta (no es opt-in) — la calidad de CLAUDE.md
es métrica continua del sistema.

## Paso 5g — Smoke test de servidores MCP (opt-in)

Si el proyecto declara servidores MCP en `.claude/settings.json` o en
`~/.cursor/mcp.json` o `~/.claude/settings.json` (global), ejecutar un smoke
test rápido para detectar configuraciones rotas antes de que fallen en uso
interactivo:

```bash
python scripts/mcp-orchestrator.py status --json 2>/dev/null
```

El orchestrator levanta un proceso MCP fresh por cada server configurado y
reporta:

- `status: "OK"` — handshake completo + lista de tools devuelta correctamente
- `status: "ERROR"` — fallo de handshake, autenticación o spawn

Casos detectables que `/swl:salud` debe reportar:

| Síntoma | Causa probable | Acción sugerida |
|---|---|---|
| `40101 Authorization required` | apiKey desincronizada entre cliente y plugin | Verificar `HKCU\Environment` (Windows) o variable de entorno del shell coincide con apiKey del plugin |
| `Server failed to start` | Binario no encontrado en `command` | Verificar path absoluto del binario |
| `Connection refused` | Plugin/servicio destino no corriendo | Iniciar plugin (ej. Obsidian con Local REST API activo) |
| `env=None passed` | Config tiene `"env": {}` literal — rompe herencia | Omitir clave `env` por completo en JSON (no dejar vacía) |

Si no hay servers configurados, este paso se omite silenciosamente.

Este smoke test detecta drift de apiKeys ANTES de que el usuario interactivo
encuentre 40101 en una invocación crítica. Recomendado ejecutar como parte del
flujo `/swl:salud` periódico mensual.

## Paso 5e — Verificación de drift de skills (skills-lock)

Si existe `manifiestos/skills-lock.json`, comparar el hash actual de cada
`habilidades/*/SKILL.md` contra el lock. Detecta ediciones silenciosas de
skills entre release y release.

```bash
npx -y @saulwade/swl-ses@latest generate-skills-lock --check
```

Estados posibles:

| Salida | Significado | Acción |
|--------|-------------|--------|
| `✓ skills-lock OK: N skills sin drift` | Todos los hashes coinciden | Continuar |
| `✗ skills-lock DRIFT detectado` | Skills modificados, nuevos o faltantes | Listar diferencias y proponer regenerar |

Si NO existe el lock (proyecto fresco o pre-1.1.0), este paso se omite y
emite advertencia sugerente: `npx -y @saulwade/swl-ses@latest generate-skills-lock`.

El lock se regenera automáticamente como parte de `/swl:release`. Drift
fuera de release indica ediciones manuales no committed.

## Paso 6b — Formato de salida enriquecido (HealthRow)

El módulo `scripts/lib/health-row.js` genera filas de salud con formato visual enriquecido
para la salida de terminal. Cada fila sigue el patrón:

```
Label              valor/max |████████████████████| indicador
```

Ejemplo visual (modo con color ANSI):

```
Agentes              59/59 |████████████████████| ✓ ok
Skills               52/55 |███████████████████░| ✓ ok
Comandos             37/38 |███████████████████░| ✓ ok
Reglas               18/18 |████████████████████| ✓ ok
Hooks                22/25 |█████████████████░░░| ⚠ warn
Score Global         88/100 |█████████████████░░░| ✓ ok
```

### Umbrales automáticos

| Rango de porcentaje | Estado | Color ANSI  |
|---------------------|--------|-------------|
| >= 80%              | good   | Verde `\x1b[32m` |
| >= 50% y < 80%      | warn   | Amarillo `\x1b[33m` |
| < 50%               | bad    | Rojo `\x1b[31m` |

Respetar `NO_COLOR` (https://no-color.org) y la variable `SWL_ASCII_SIMPLE=1`
para entornos sin soporte Unicode.

### Uso para generar el bloque de salud

```javascript
const { formatearBloqueHealth } = require('./scripts/lib/health-row');

const filas = [
  { label: 'Agentes',      valor: 59,  maximo: 59  },
  { label: 'Skills',       valor: 52,  maximo: 55  },
  { label: 'Comandos',     valor: 37,  maximo: 38  },
  { label: 'Reglas',       valor: 18,  maximo: 18  },
  { label: 'Hooks',        valor: 22,  maximo: 25  },
  { label: 'Score Global', valor: 88,  maximo: 100 },
];

console.log(formatearBloqueHealth(filas));
```

Para override de estado (forzar bad aunque el valor sea alto):

```javascript
const { formatearHealthRow } = require('./scripts/lib/health-row');
const fila = formatearHealthRow({ label: 'Seguridad', valor: 100, maximo: 100, estado: 'bad' });
```

La integración real del formato en la ejecución del comando se implementará en el
código de salida de `/swl:salud`. Este paso solo define la API disponible y la muestra visual.

## Paso 7 — Generar SALUD.md

Escribe el reporte con tabla resumen, errores, advertencias y recomendaciones. Usa los datos exactos de los scripts. Si el script dice 73%, reportar 73%.

## Paso 8 — Resumen en terminal

Presenta resumen con los problemas más críticos. Si hay errores, listarlos todos. Si solo advertencias, listar las top 5.

## Reglas de comportamiento

- SOLO LECTURA. NUNCA modifica archivos excepto SALUD.md.
- Las verificaciones de Pasos 1-5 son scripts Bash deterministas. NO usar juicio subjetivo.
- Los scripts miden: existencia de campos, conteo de líneas, sintaxis. NO miden calidad de redacción.
- Si un check requiere juicio (contradicciones entre reglas), documentar como "requiere revisión manual".
- Los scores son aritmética pura. No ajustar el score "porque parece un sistema sano".
- Si no hay git, omitir verificaciones dependientes y notar la omisión.
- Si detectas un patrón sistémico (mismo error en múltiples componentes), mencionarlo en recomendaciones.

<!-- Patrón de 3 tiers adaptado de Waza/skills/health bajo MIT License (tw93/Waza) -->
