---
name: swl:evaluar-skill
description: Evalúa la calidad de un agente o skill SWL con 10 dimensiones, detecta anti-patrones y asigna badge de calidad (Platino/Oro/Plata/Bronce). Ejecutar antes de hacer merge de agentes o skills nuevas.
argument-hint: <nombre-skill-o-agente> [--agente]
allowed_tools: ["Read", "Bash", "Glob", "Grep"]
---

# /swl:evaluar-skill — Evaluación de calidad de skills y agentes SWL

Evalúa la calidad de una skill o agente SWL con un framework de 2 capas. La
**Capa 1** usa verificaciones deterministas (análisis estático con herramientas
del sistema). La **Capa 2** es evaluación semántica profunda realizada por el
propio modelo al leer el contenido completo.

Al finalizar, emite un reporte con score por dimensión, badge asignado y
recomendaciones priorizadas de mejora.

## Uso

```
/swl:evaluar-skill django-experto           — Evalúa la skill habilidades/django-experto/SKILL.md
/swl:evaluar-skill backend-python-swl --agente  — Evalúa el agente agentes/backend-python-swl.md
/swl:evaluar-skill                          — Lista skills disponibles y pide que se especifique
```

## Paso 0 — Resolver argumento

Si no se pasa argumento, listar las opciones disponibles y detenerse:

```bash
echo "=== SKILLS DISPONIBLES ===" && ls -d habilidades/*/ 2>/dev/null | xargs -I{} basename {} | sort
echo "=== AGENTES DISPONIBLES ===" && ls agentes/*.md 2>/dev/null | xargs -I{} basename {} .md | sort
```

Informar al usuario que especifique el nombre y si es agente agregar `--agente`.

Si se pasa argumento sin `--agente`, buscar en `habilidades/<nombre>/SKILL.md`.
Si el argumento incluye `--agente`, buscar en `agentes/<nombre>.md` (quitar el flag del nombre).

Si el archivo no existe, informar y terminar:

```
No se encontró: habilidades/<nombre>/SKILL.md
Verifica el nombre con /swl:evaluar-skill (sin argumentos) para ver las opciones.
```

## Paso 1 — Capa 1: Análisis estático

Ejecutar todas las verificaciones deterministas. Registrar cada error (E0XX) y
advertencia (W0XX) encontrado. No usar juicio subjetivo en esta capa.

### 1.1 Verificaciones de frontmatter

```bash
SKILL_FILE="habilidades/<nombre>/SKILL.md"  # o agentes/<nombre>.md

# Extraer frontmatter
head -30 "$SKILL_FILE"

# Verificar YAML válido del frontmatter (entre los ---) usando Python
python3 -c "
import sys, re
content = open('$SKILL_FILE').read()
match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL)
if not match:
    print('E001 YAML_INVALIDO: no se encontró bloque frontmatter')
    sys.exit(1)
import yaml
try:
    data = yaml.safe_load(match.group(1))
    print('YAML OK:', list(data.keys()) if data else 'vacío')
except Exception as e:
    print('E001 YAML_INVALIDO:', e)
" 2>/dev/null || echo "E001 YAML_INVALIDO"
```

Verificar campo `name`:
```bash
grep -m1 "^name:" "$SKILL_FILE" || echo "E002 NAME_AUSENTE"
```

Verificar campo `description`:
```bash
desc=$(grep -m1 "^description:" "$SKILL_FILE" | sed 's/^description:[[:space:]]*//')
[ -z "$desc" ] && echo "E003 DESCRIPTION_AUSENTE" || echo "description OK: ${#desc} chars"
```

Verificar formato kebab-case de `name`:
```bash
name_val=$(grep -m1 "^name:" "$SKILL_FILE" | sed 's/^name:[[:space:]]*//')
echo "$name_val" | grep -qE '^[a-z0-9][a-z0-9-]*$' || echo "E004 NAME_FORMATO: '$name_val' no es kebab-case"
```

Verificar longitud de `name` (máx 64 caracteres):
```bash
name_len=$(grep -m1 "^name:" "$SKILL_FILE" | sed 's/^name:[[:space:]]*//' | wc -c)
[ "$name_len" -gt 64 ] && echo "E005 NAME_LONGITUD: $name_len chars" || echo "name length OK"
```

Verificar que `name` no use términos reservados:
```bash
name_val=$(grep -m1 "^name:" "$SKILL_FILE" | sed 's/^name:[[:space:]]*//')
echo "$name_val" | grep -qiE '(anthropic|claude|swl|^tests?$|testing)' && echo "E006 NAME_RESERVADO: '$name_val'" || echo "name reservado OK"
```

Verificar coincidencia entre `name` y nombre del directorio (solo para skills):
```bash
dir_name="<nombre>"
name_val=$(grep -m1 "^name:" "$SKILL_FILE" | sed 's/^name:[[:space:]]*//')
[ "$name_val" != "$dir_name" ] && echo "E007 NAME_DIRECTORIO: name='$name_val' != dir='$dir_name'" || echo "name/dir OK"
```

Verificar longitud de `description` (máx 1,024 caracteres):
```bash
desc_len=$(grep -m1 "^description:" "$SKILL_FILE" | sed 's/^description:[[:space:]]*//' | wc -c)
[ "$desc_len" -gt 1024 ] && echo "E008 DESCRIPTION_LONGITUD: $desc_len chars" || echo "description length OK"
```

### 1.2 Verificaciones de contenido

Verificar presencia de trigger "Cargar cuando" o "Invocar cuando":
```bash
grep -qi "Cargar cuando\|Invocar cuando" "$SKILL_FILE" || echo "E009 MISSING_TRIGGER"
```

Verificar referencias a recursos internos:
```bash
# Encontrar referencias a recursos/X.md dentro del SKILL.md
grep -oE 'recursos/[^)"\s]+\.md' "$SKILL_FILE" | while read ref; do
  dir=$(dirname "$SKILL_FILE")
  [ ! -f "$dir/$ref" ] && echo "E010 REFERENCIA_ROTA: $ref"
done
echo "referencias OK"
```

### 1.3 Verificaciones de advertencias

Verificar si la skill supera 300 líneas:
```bash
lines=$(wc -l < "$SKILL_FILE")
[ "$lines" -gt 300 ] && echo "W001 BLOATED_SKILL: $lines líneas (máx 300)" || echo "W001 OK: $lines líneas"
```

Verificar uso excesivo de directivas absolutas:
```bash
count=$(grep -cE '\bMUST\b|\bALWAYS\b|\bNEVER\b|\bNUNCA\b|\bSIEMPRE\b' "$SKILL_FILE" || echo 0)
[ "$count" -gt 15 ] && echo "W002 OVER_CONSTRAINED: $count ocurrencias" || echo "W002 OK: $count ocurrencias"
```

Verificar archivos huérfanos en recursos/:
```bash
skill_dir=$(dirname "$SKILL_FILE")
if [ -d "$skill_dir/recursos" ]; then
  for rec in "$skill_dir/recursos/"*.md; do
    base=$(basename "$rec")
    grep -q "$base" "$SKILL_FILE" || echo "W003 HUERFANO: recursos/$base no referenciado"
  done
fi
```

Verificar presencia de ejemplos de código si la skill cubre implementación:
```bash
code_blocks=$(grep -c '```' "$SKILL_FILE" || echo 0)
[ "$code_blocks" -eq 0 ] && echo "W004 SIN_EJEMPLO_CODIGO: cero bloques de código" || echo "W004 OK: $code_blocks delimitadores de bloque"
```

Verificar sección de "cuándo NO usar":
```bash
grep -qi "cuándo no\|cuando no\|no usar\|no cargar\|no invocar" "$SKILL_FILE" || echo "W005 SIN_CUANDO_NO_USAR"
```

Verificar presencia de sección Gotchas o equivalente:
```bash
# W008: skills sin sección Gotchas
# Penalización inicial suave (-5) durante período de migración de Fase 3.
# Tras la migración se promoverá a -12 para hacer la sección obligatoria.
grep -qi "Gotcha\|Errores comunes\|trampas\|Errores no obvios\|Fallas conocidas" "$SKILL_FILE" || \
  echo "W008 SIN_GOTCHAS: skill sin seccion Gotchas (penalizacion -5 en robustness)"
```

Verificar uso de campos legacy en inglés (sin alias en español):
```bash
# W010: uso de campos legacy (inglés) sin alias en español (penalización -6 en robustness)
# Promovido 2026-04-24 tras v5.11.2 (2 releases desde introducción) — período de gracia concluido.
node -e "
const fs = require('fs');
const norm = require('./scripts/lib/skill-normalizer.js');
const raw = fs.readFileSync(process.argv[1], 'utf8');
const match = raw.match(/^---\n([\s\S]*?)\n---/);
if (!match) process.exit(0);
const fm = {};
for (const linea of match[1].split(/\r?\n/)) {
  const m = linea.match(/^(\w[\w_-]*):\s*(.+)\$/);
  if (m) fm[m[1]] = m[2];
}
const legacy = norm.detectarUsoLegacy(fm);
const divergente = norm.detectarDivergencias(fm);
[...legacy, ...divergente].forEach(w => process.stderr.write(w.mensaje + '\n'));
" "\$SKILL_FILE" 2>&1 | grep W010 || echo "W010 OK: sin campos legacy"
```

Verificar justificación en directivas absolutas:
```bash
# W009: directivas absolutas sin justificación
# Si hay más de 3 directivas MUST/ALWAYS/NEVER/NUNCA/SIEMPRE y ninguna está
# acompañada de palabras justificativas en la misma línea o la siguiente,
# el modelo obedece pero no puede generalizar a casos edge.
directive_count=$(grep -cE '\bMUST\b|\bALWAYS\b|\bNEVER\b|\bNUNCA\b|\bSIEMPRE\b' "$SKILL_FILE" || echo 0)
if [ "$directive_count" -gt 3 ]; then
  justified=$(grep -E '\bMUST\b|\bALWAYS\b|\bNEVER\b|\bNUNCA\b|\bSIEMPRE\b' "$SKILL_FILE" | \
    grep -ciE 'porque|ya que|para evitar|si no|since|because|para que' || echo 0)
  [ "$justified" -eq 0 ] && echo "W009 DIRECTIVAS_SIN_JUSTIFICACION: $directive_count directivas absolutas sin palabras justificativas (penalizacion -8 en output_quality)" || echo "W009 OK: $justified/$directive_count directivas con justificacion"
fi
```

### 1.4 Resumen de Capa 1

Tabular todos los errores y advertencias encontrados. Aplicar penalizaciones:

| Código | Tipo | Penalización |
|--------|------|-------------|
| E001–E010 | ERROR | Dimensión relacionada → 0 |
| W001 | ADVERTENCIA | -8 en `progressive_disclosure` |
| W002 | ADVERTENCIA | -5 en `scope_calibration` |
| W003 | ADVERTENCIA | -3 en `structural_completeness` |
| W004 | ADVERTENCIA | -10 en `code_template_quality` |
| W005 | ADVERTENCIA | -5 en `scope_calibration` |
| W008 | ADVERTENCIA | -5 en `robustness` (período de migración; se promoverá a -12 tras Fase 3) |
| W009 | ADVERTENCIA | -8 en `output_quality` |
| W010 | ADVERTENCIA | -6 en `robustness` (promovido 2026-04-24 tras v5.11.2; período de migración concluido — campos en español SWL son ahora esperados) |

Mapeo de errores a dimensiones afectadas:
- E001, E002, E003, E008 → `structural_completeness`
- E004, E005, E006, E007 → `ecosystem_coherence`
- E009 → `triggering_accuracy`
- E010 → `structural_completeness`

### 1.5 Métricas cuantitativas de calidad

Calcular métricas de eficiencia de tokens y duplicación:

```bash
SKILL_FILE="habilidades/<nombre>/SKILL.md"

# Estimar tokens del skill
node -e "
const { estimateTokens } = require('./hooks/lib/token-budget');
const fs = require('fs');
const content = fs.readFileSync('$SKILL_FILE', 'utf8');
const tokens = estimateTokens(content, 'mixed');
const lines = content.split('\n').length;
const ratio = (tokens / lines).toFixed(1);
console.log('TOKENS: ' + tokens);
console.log('LINEAS: ' + lines);
console.log('DENSIDAD: ' + ratio + ' tokens/línea');
console.log(tokens > 5000 ? 'W006 TOKEN_EXCESIVO: ' + tokens + ' tokens (máx 5000)' : 'tokens OK');
"
```

Detección de duplicación con otros skills del sistema:

```bash
# Comparar contenido con skills del mismo dominio
node -e "
const { jaccardSimilarity } = require('./hooks/lib/fingerprint-id');
const fs = require('fs');
const path = require('path');
const target = fs.readFileSync('$SKILL_FILE', 'utf8');
const skillsDir = 'habilidades';
const dirs = fs.readdirSync(skillsDir, {withFileTypes:true}).filter(d=>d.isDirectory());
const results = [];
for (const d of dirs) {
  const p = path.join(skillsDir, d.name, 'SKILL.md');
  if (p === '$SKILL_FILE' || !fs.existsSync(p)) continue;
  const content = fs.readFileSync(p, 'utf8');
  const sim = jaccardSimilarity(target, content);
  if (sim > 0.3) results.push({name: d.name, similarity: (sim*100).toFixed(1)});
}
results.sort((a,b) => b.similarity - a.similarity);
if (results.length > 0) {
  console.log('SIMILITUD con otros skills:');
  results.slice(0,5).forEach(r => {
    const flag = r.similarity > 50 ? ' W007 DUPLICADO_POTENCIAL' : '';
    console.log('  ' + r.name + ': ' + r.similarity + '%' + flag);
  });
} else {
  console.log('Sin duplicación detectada con otros skills.');
}
"
```

Mapeo de advertencias nuevas:

| Código | Tipo | Penalización |
|--------|------|-------------|
| W006 | ADVERTENCIA | -8 en `token_efficiency` |
| W007 | ADVERTENCIA | -10 en `orchestration_fitness` (posible duplicación) |

## Paso 2 — Capa 2: Evaluación semántica

Leer el archivo completo con `Read`. Evaluar cada dimensión de 0 a 100 con
criterio honesto: si la skill tiene problemas reales, el score debe reflejarlos.

### Tabla de dimensiones

| Dimensión | Peso | Qué medir |
|-----------|------|-----------|
| `triggering_accuracy` | 25% | ¿La `description` activa la skill en los momentos correctos? ¿El trigger es ni muy amplio ni muy estrecho? ¿Evita activaciones falsas? |
| `orchestration_fitness` | 20% | ¿Coordina bien con otras skills del sistema? ¿Invoca correctamente otras skills con `Skill("nombre")`? ¿Evita duplicar contenido de otras skills? |
| `output_quality` | 15% | ¿Los ejemplos de código son reales y funcionales? ¿Las reglas son específicas, no genéricas? ¿El contenido refleja patrones idiomáticos del framework? ¿Las directivas MUST/ALWAYS/NEVER/NUNCA/SIEMPRE incluyen justificación de por qué la regla existe? Una directiva sin justificación no permite al modelo generalizar a casos edge (ver W009). |
| `scope_calibration` | 12% | ¿El scope es coherente? ¿No cubre demasiado? ¿Hay sección de "cuándo NO usar"? ¿El contenido es específico del dominio declarado? |
| `progressive_disclosure` | 10% | ¿El SKILL.md principal es conciso (< 300 líneas)? ¿El contenido extenso está en `recursos/`? ¿Las referencias a recursos son claras y útiles? |
| `token_efficiency` | 6% | ¿Hay contenido redundante o repetitivo? ¿Los ejemplos son concisos pero completos? ¿Se evita documentación genérica que no agrega valor? |
| `robustness` | 5% | ¿Documenta casos edge y anti-patrones? ¿Cubre los errores más comunes del dominio? |
| `structural_completeness` | 3% | ¿Tiene todas las secciones mínimas requeridas? ¿El frontmatter tiene todos los campos obligatorios? |
| `code_template_quality` | 2% | ¿Los ejemplos de código compilan/ejecutan? ¿Usan sintaxis actual del lenguaje o framework? |
| `ecosystem_coherence` | 2% | ¿El naming sigue las convenciones del sistema SWL (kebab-case)? ¿Los patrones son consistentes con las otras skills? |

**Guía de calibración de scores:**
- 90-100: Excelente — referente del sistema
- 80-89: Sólido — listo para uso en producción
- 70-79: Funcional — mejoras menores posibles
- 60-69: Aceptable — requiere revisión antes de merge
- 40-59: Deficiente — problemas claros que afectan efectividad
- 0-39: Insuficiente — necesita reescritura

## Paso 3 — Cálculo del score final

Calcular score ponderado:

```
score_final = Σ(score_dimensión × peso_dimensión)

Ejemplo:
  triggering_accuracy    = 85 × 0.25 = 21.25
  orchestration_fitness  = 78 × 0.20 = 15.60
  output_quality         = 90 × 0.15 = 13.50
  scope_calibration      = 72 × 0.12 =  8.64
  progressive_disclosure = 80 × 0.10 =  8.00
  token_efficiency       = 70 × 0.06 =  4.20
  robustness             = 65 × 0.05 =  3.25
  structural_completeness= 95 × 0.03 =  2.85
  code_template_quality  = 85 × 0.02 =  1.70
  ecosystem_coherence    = 90 × 0.02 =  1.80
  ─────────────────────────────────────────────
  score_final = 80.79
```

Asignar badge:

```
≥ 90  → Platino  — production-ready, puede usarse como referencia
≥ 80  → Oro      — listo para merge
≥ 70  → Plata    — necesita mejoras menores antes del merge
≥ 60  → Bronce   — requiere revisión antes de merge
< 60  → Sin badge — no hacer merge hasta corregir
```

## Paso 4 — Emitir reporte

Generar el siguiente reporte directamente en la conversación:

```markdown
## Evaluación de skill: [nombre]
**Archivo**: habilidades/[nombre]/SKILL.md  (o agentes/[nombre].md)
**Fecha**: [fecha actual]

### Capa 1 — Análisis estático

[PASS si no hay errores ni advertencias]
[O listar cada E0XX / W0XX con descripción breve]

### Capa 2 — Evaluación semántica

| Dimensión | Peso | Score | Contribución |
|-----------|------|-------|-------------|
| Triggering accuracy | 25% | XX/100 | XX.XX |
| Orchestration fitness | 20% | XX/100 | XX.XX |
| Output quality | 15% | XX/100 | XX.XX |
| Scope calibration | 12% | XX/100 | XX.XX |
| Progressive disclosure | 10% | XX/100 | XX.XX |
| Token efficiency | 6% | XX/100 | XX.XX |
| Robustness | 5% | XX/100 | XX.XX |
| Structural completeness | 3% | XX/100 | XX.XX |
| Code template quality | 2% | XX/100 | XX.XX |
| Ecosystem coherence | 2% | XX/100 | XX.XX |

**Score final: XX.XX/100 — Badge: [🏆/🥇/🥈/🥉/❌] [Platino/Oro/Plata/Bronce/Sin badge]**

### Hallazgos principales

**Fortalezas:**
- [2-3 aspectos bien ejecutados con evidencia específica del contenido]

**Áreas de mejora:**
- [hallazgos específicos con sugerencia concreta y línea o sección afectada]

### Recomendaciones para subir el score

[Lista priorizada de cambios concretos que mejorarían el score,
ordenados de mayor a menor impacto. Incluir la dimensión afectada.]
```

## Paso 3 — Capa 3 opcional: Evals estructurados

Si el artefacto tiene un archivo de evals en formato `skill-evals.schema.json`:

```bash
EVALS_PATH="habilidades/<nombre>/evals/evals.json"
[ -f "$EVALS_PATH" ] && echo "EVALS_DISPONIBLES" || EVALS_PATH="agentes/evals/<nombre>.evals.json"
[ -f "$EVALS_PATH" ] && echo "EVALS_DISPONIBLES" || echo "SIN_EVALS"
```

Si existen, ejecutar la Capa 3:

### 3.1 — Validar el artefacto contra el schema

```bash
# Validación mínima de estructura (zero-deps): comprobar que es JSON válido y
# que tiene los campos obligatorios. Validación completa del schema queda para
# consumidores externos (ajv, etc.) — SWL no arrastra deps.
node -e "
  const ev = require('./$EVALS_PATH');
  const req = ['skill_name','schema_version','evals'];
  const missing = req.filter(k => !(k in ev));
  if (missing.length) { console.error('FALTAN:', missing.join(',')); process.exit(1); }
  if (!Array.isArray(ev.evals) || ev.evals.length === 0) { console.error('evals vacío'); process.exit(1); }
  console.log('OK', ev.evals.length, 'evals');
"
```

Si falla la validación, reportar y continuar solo con Capa 1+2 (no bloquear).

### 3.2 — Ejecutar cada eval contra el skill cargado

Para cada `eval` del artefacto:

1. Cargar el skill con `Skill("<nombre>")`.
2. Enviar el `prompt` exacto al modelo en una respuesta aislada (sin historial).
3. Capturar la respuesta.
4. Para cada entrada de `expectations[]`, verificar si la respuesta la cumple
   (match semántico honesto, no literal).
5. Computar `pass = true` solo si TODAS las expectations se cumplen.
6. Aplicar `weight` (default 1.0) al score.

### 3.3 — Score de Capa 3

```
score_evals = sum(pass_i * weight_i) / sum(weight_i) * 100
```

### 3.4 — Integración con score final

El score final combinado es:

- Sin evals: `score_final = score_capa_1_2`
- Con evals: `score_final = score_capa_1_2 * 0.7 + score_evals * 0.3`

Reportar ambos separadamente para trazabilidad. Si `score_evals < 60`, marcar
alerta: la skill puede ser declarativa pero falla en evaluación ejecutable.

### 3.5 — Reporte por eval

```
| ID | Tags | Peso | Resultado | Expectations fallidas |
|----|------|------|-----------|----------------------|
| 0  | primary-flow | 1.0 | ✓ pass | — |
| 1  | edge-case | 1.0 | ✗ fail | "La respuesta distingue X de Y" |
| anti-pattern-substitution | anti-pattern,regression | 1.5 | ✓ pass | — |

Score evals: 82.4/100 (peso 30% del final)
```

## Reglas de comportamiento

- La Capa 1 es **determinista**: ejecutar los scripts Bash y registrar resultados exactos. No inferir ni interpretar.
- La Capa 2 es **semántica honesta**: si la skill tiene problemas reales el score debe reflejarlos. No inflar scores por cortesía.
- La Capa 3 es **opcional**: si no existe `evals/evals.json`, omitirla sin penalizar. Si existe, es autoridad primaria sobre Capa 2 en caso de discrepancia (los evals son reproducibles; la evaluación semántica no).
- Un skill con Capa 3 activa y `score_evals ≥ 90` es candidato a badge **Platino** aunque Capa 2 sea 85.
- Los evals NUNCA se inventan en este comando — solo se ejecutan los declarados por el autor del skill. Crear evals nuevos es tarea de `/swl:aprender` o del autor del skill directamente, siguiendo `plantillas/skill-evals-template.json`.
- Si el archivo no existe, informar al usuario con el path exacto y terminar.
- Si `--agente` se pasa sin nombre, listar los agentes disponibles y terminar.
- El reporte siempre se emite en la conversación. No crear archivos.
- Las "Fortalezas" deben ser específicas del contenido leído, no genéricas.
- Las "Áreas de mejora" deben incluir la sección o línea aproximada donde se detectó el problema.
- Si la Capa 1 genera errores E001–E010, reflejar el impacto en el score de la dimensión afectada antes de sumar las contribuciones ponderadas.
- Score mínimo aprobado para merge: **80/100** (badge Oro). Por debajo de ese umbral, indicar claramente que **no está listo para merge**.
