====== 20. Estructura de un skill ======
Qué es una //skill//, su frontmatter y cómo se redacta están en [[cursos:sdd:08-skills|Skills]]. Aquí, la estructura completa de una //skill//: qué carpetas y ficheros puede tener, qué secciones lleva el ''SKILL.md'' según el tipo y cómo se prueba.
Más información:
* [[https://agentskills.io/specification|Specification - Agent Skills]]: el estándar de las //skills//: ''SKILL.md'', ficheros de apoyo y carga por niveles.
* [[https://code.claude.com/docs/en/skills|Claude Code: skills]]: dónde se guardan, cómo se lanzan y cómo se pasan argumentos con ''$ARGUMENTS''.
* [[https://github.com/github/spec-kit|GitHub Spec Kit]]: de sus comandos sale la estructura de las //skills// de procedimiento (User Input, Outline).
* [[https://github.com/anthropics/skills/tree/main/skills/skill-creator|skill-creator]]: la //skill// de Anthropic para crear y evaluar //skills//.
Cada //skill// es una carpeta dentro de la carpeta de //skills// del proyecto. La carpeta en concreto depende de la herramienta:
* **OpenCode**: ''.opencode/skills/''.
* **Claude Code**: ''.claude/skills/''.
OpenCode también lee ''.claude/skills/'', así que con esa carpeta una misma //skill// vale para las dos.
----
===== 1. El nombre =====
El nombre de la //skill// (su carpeta y su ''name'') empieza por un prefijo que dice de qué tipo es:
* **''k-''**, de //knowledge//, conocimiento en inglés: las que **saben** cosas. Describen las convenciones del proyecto y las cargan otras //skills// y los agentes: ''k-servicios'', ''k-tests''.
* **''do-''**, «hacer» en inglés: las que **hacen** cosas. Ejecutan un proceso y las lanzas tú: ''do-crear-servicio'', ''do-revisar-pr''.
Detrás del prefijo, en //kebab-case//, el tema en las ''k-'' y un verbo en las ''do-''.
Ventajas del prefijo:
* **No chocan con los comandos de Claude Code**, que también se lanzan con ''/''. Una //skill// llamada ''init'' o ''context'' se confunde con ''/init'' o ''/context''; ''/do-revisar-pr'' no coincide con ningún comando.
* **Escribiendo ''/do-'' sale todo lo que se puede lanzar**: el autocompletado enseña solo las //skills// que hacen cosas, sin las de conocimiento, que no se lanzan a mano.
===== 2. Estructura de carpetas y ficheros de un skill que "Sabe" =====
Para explicar la estructura de carpetas de un skill , lo vamos a hacer con un ejemplo de un skill que "sabe" como son los servicios en Spring [[https://anilr9.medium.com/understanding-service-in-spring-boot-a-complete-guide-with-examples-b08825e8ccd8|Understanding @Service in Spring Boot: A Complete Guide with Examples]]
.opencode/k-servicios/
├── SKILL.md
├── transacciones.md
├── templates/
│ └── service-impl.md
├── examples/
│ └── pedido-service.md
├── references/
│ └── spring-transactional.md
├── scripts/
│ └── sin-transaccion.py
├── assets/
│ └── errores.schema.json
└── evals/
└── update-sin-filtrar/
Qué hace cada pieza está en [[cursos:sdd:08-skills#la_carpeta|Skills]]. Lo que distingue a cada carpeta es **quién la lee y cuándo entra en contexto**:
^ Carpeta o fichero ^ Contiene ^ Ejemplo ^
| ''SKILL.md'' | Instrucciones e índice | |
| ''.md'' | Son ficheros extra que deberían estar en SKILL.md pero que los sacamos fuera para que el agente no tenga que leer todo si no lo necesita | transacciones.md |
| ''templates/'' | Plantillas literales con huecos. Es para cuando el skill debe generar algún tipo de fichero para que tenga una guia de como debe ser. | service-impl.md |
| ''examples/'' | Ejemplo de como queda lo que tiene que generar. Un buen ejemplo siempre ayuda mucho al agente a hacer su trabajo. Mejor un buen ejemplo que una buena descripción | pedido-service.md |
| ''references/'' | Documentación de referencia. Podría ser la documentación oficial de Spring que ponemos aqui | spring-transactional.md |
| ''scripts/'' | Script en bash o python , etc que ejecuta el skill para hacer su tarea. Si algo se puede hacer con un script en vez de usar más tokens, es mejor crear un script. | sin-transaccion.py |
| ''assets/'' | Esquemas, XSD, imágenes, etc | errores.schema.json |
| ''evals/'' | Dentro hay carpetas con los test unitarios para probar el skill | update-sin-filtrar/ |
===== 3. SKILL.md =====
En el fichero ''SKILLS.md'' está toda la explicación/descripción de lo que debe saber o hacer el skill, sin embargo no es adecuado tener ficheros demasiado largos. Por ese motivo están los ficheros ''.md'' que son simplemete secciones del fichero ''SKILLS.md'' y que se referencian desde ''SKILLS.md''.
El motivo de hace esto es paa ahorrar contexto. A veces un skill trata de varios temas relacionados. Por ejemplo, si vamos a cear un CRUD, podríamos tener un skill que sepa hacer CRUDs y en ese caso hablaría sobre controladores, servicios y repositorios. Cuando la IA está programando el controlador no necesita saber como se hace un servicio o un repositorio.
===== 3. El índice =====
La tabla índice del ''SKILL.md'' ([[cursos:sdd:08-skills#el_skillmd|cómo es]]) va **justo después de la frase de entrada**, antes de cualquier otra sección, y tiene **todos** los ficheros:
* **Un fichero que no está en la tabla es un huérfano**: no lo carga nadie.
* **''evals/'' y ''assets/'' van como una sola fila** de carpeta: nadie los lee como instrucciones.
* **Cada fichero se cita además donde se usa**, diciendo cuándo cargarlo. La tabla da la visión de conjunto; la cita, el momento.
✅ En la Fase 2, copia `templates/service-impl.md` y rellena los huecos.
❌ Ver también `templates/service-impl.md`. (no dice cuándo)
* **Todo ''.md'' de más de 100 líneas lleva su propio ''## Índice''** en las primeras 40 líneas. El modelo a veces lee solo el principio de un fichero largo: si el índice está abajo, no sabe qué hay.
----
===== 4. Dos tipos de skill =====
Cada tipo tiene su estructura fija de ''SKILL.md''. Se distinguen por [[#el_nombre|el prefijo]]:
^ ^ Conocimiento ^ Procedimiento ^
| Qué hace | Describe convenciones, patrones y vocabulario | Ejecuta un proceso por fases |
| Quién la usa | Otras //skills// y agentes, que la cargan | Tú, con ''/do-crear-servicio R-12'' |
| Argumentos | No | ''$ARGUMENTS'' |
| Fases y ''STOP conditions'' | No | ✓ |
Si hacen falta las dos cosas, son dos //skills//: ''k-servicios'' con las reglas y ''do-crear-servicio'' con el proceso, que carga la primera. Mezcladas, el conocimiento solo se aprovecha al lanzar el proceso.
==== Skill de conocimiento ====
---
name: k-servicios
description: Convenciones de los servicios de Pedidos (transacciones, validación,
errores de negocio). Usar al escribir, revisar o probar una clase *Service.
---
# k-servicios
Reglas de las clases `*Service` del servidor. La cargan `do-crear-servicio` y `do-revisar-pr`.
## Índice
| Fichero | Qué contiene | Cuándo cargarlo |
|---|---|---|
| `transacciones.md` | Qué métodos son transaccionales y cómo | Al escribir un método que guarda |
| `templates/service-impl.md` | Esqueleto de un servicio nuevo | Al crear un servicio |
## 1. Conceptos clave
## 2. Convenciones del proyecto
## 3. Patrones recomendados
## 4. Ejemplos ✅/❌
## 5. Anti-patrones
* **H1 igual que el ''name''.** Y nada por debajo de ''####'': si hace falta un quinto nivel, la estructura está mal.
* **Frase de entrada**: qué cubre y **quién la usa**, para que el modelo sepa si es la que busca.
==== Skill de procedimiento ====
Sigue la estructura de los comandos de [[https://github.com/github/spec-kit|Spec Kit]]:
---
name: do-crear-servicio
description: Crea un servicio de Pedidos con su test a partir de un requisito de
specs/. Usar cuando se pida crear, añadir o generar un servicio o una clase *Service.
---
# do-crear-servicio
Eres un desarrollador del servidor: conviertes un requisito de `specs/` en un `*Service` con su test.
## Índice
| Fichero | Qué contiene | Cuándo cargarlo |
|---|---|---|
| `templates/service-impl.md` | Esqueleto del servicio | En la Fase 2 |
| `scripts/sin-test.py` | Lista los requisitos sin test | Se ejecuta en la Fase 3 |
| `evals/` | Casos de prueba de la skill | Solo al evaluarla |
## User Input
```text
$ARGUMENTS
```
You **MUST** consider the user input before proceeding (if not empty). Se espera el
identificador del requisito (`R-12`) y, opcionalmente, el nombre del servicio.
## Outline
1. **Leer** el requisito (Fase 1).
2. **Escribir** el servicio y su test (Fase 2).
3. **Comprobar** que el requisito tiene test y pasa (Fase 3).
**STOP conditions**:
- No hay ningún requisito con ese identificador → **ERROR** y detente.
- El servicio ya existe → **STOP** y pregunta antes de sobrescribirlo.
## 1. Fase 1 — Leer el requisito
1. Busca el identificador en `specs/`.
2. Carga la skill `k-servicios`.
## 2. Fase 2 — Escribir el servicio
1. Copia `templates/service-impl.md` y rellena los huecos.
2. Escribe el test con el identificador del requisito en su nombre.
## 3. Fase 3 — Comprobar
1. Ejecuta `python3 .claude/skills/do-crear-servicio/scripts/sin-test.py specs/ src/test/java`.
2. Ejecuta `./mvnw -pl servidor test`.
**LIMIT**: máximo 3 vueltas de corrección; si sigue fallando, avisa con lo que queda.
## Quick Guidelines
- Un servicio por agregado.
- Ningún requisito sin su test.
- Si el requisito es ambiguo, pregunta: no lo interpretes.
Las secciones, en este orden:
* **Frase de entrada**: el **rol** que asume el modelo y **qué transforma en qué**.
* **''## User Input''**: un bloque ''text'' con ''$ARGUMENTS'', que Claude Code sustituye por lo que escribes detrás de ''/do-crear-servicio''. La línea en inglés es la de Spec Kit, tal cual, y la siguiente dice qué argumentos espera.
* **''## Outline''**: las fases numeradas, un verbo cada una, y al final las **''STOP conditions''**: cuándo aborta (''ERROR'') y cuándo para a preguntar (''STOP'').
* **Una sección por fase** del Outline, con sus pasos.
* **''## Quick Guidelines''**: lo esencial en **10 puntos como máximo**, al final del cuerpo.
**''$ARGUMENTS'' se sustituye en todo el fichero**, no solo en ''User Input''. Si una //skill// tiene que //hablar// del placeholder, como ''k-skill'', no puede escribirlo junto: lo cuenta partido («''$'' seguido de ''ARGUMENTS''»). Escrito tal cual, al cargarla aparece en su lugar lo que hayas escrito tú.
----
===== 5. evals/: las pruebas de la skill =====
Una //skill// se prueba como el código: una batería de casos que se repite **al editarla y al cambiar de modelo**. Cada caso responde a dos preguntas:
* **¿Se activa?** Que se cargue solo dice que el modelo la encontró, no que la siguiera.
* **¿Aporta?** El caso se ejecuta **con la //skill// y sin ella**, y la diferencia de nota es lo que aporta. Si sin ella también aprueba, sobra para ese caso.
.claude/skills/k-servicios/evals/
├── update-sin-filtrar/ <- un caso, con el nombre de lo que prueba
│ ├── prompt.md <- lo que escribiría el usuario
│ ├── fixture.sh <- opcional: crea los ficheros de partida
│ └── graders/
│ ├── filtra-campos.md <- un evaluador por fichero
│ └── skill-activada.md
└── results/ <- las notas de cada evaluación; va en .gitignore
* **''prompt.md''**: lo que pediría un usuario, **sin nombrar la //skill//**: si la nombra, se prueba que obedece, no que se activa.
---
description: El update debe filtrar los campos que manda el cliente.
max_turns: 30
---
Añade a PedidoServiceImpl un método update que permita cambiar la dirección y el teléfono del pedido.
* **''fixture.sh''**: deja en una carpeta limpia los ficheros mínimos de partida, aquí un ''PedidoServiceImpl.java'' sin ''update''.
* **''graders/''**: un evaluador por fichero, con su ''type'' en el frontmatter. Sobre el resultado, con un modelo que hace de juez:
---
type: llm
focus: { source: file, path: PedidoServiceImpl.java }
---
PASS si el update solo deja cambiar la dirección y el teléfono.
FAIL si copia sobre la entidad todo lo que recibe, o si el cliente puede cambiar el estado o el importe.
Y sobre el proceso, con un script:
---
type: tool_used
tool: Skill
input_match: '"skill"\s*:\s*"k-servicios"'
scored: false
---
Los tipos de evaluador:
^ Tipo ^ Aprueba cuando ^ Lo aplica ^
| ''regex'' | Un patrón aparece (o no) en la respuesta o en un fichero | Un script |
| ''tool_used'' | Se llamó a una herramienta, con ''min'' y ''max'' veces | Un script |
| ''tool_order'' | Una herramienta se llamó antes que otra | Un script |
| ''file_exists'' | Se creó un fichero que cumple el patrón | Un script |
| ''llm'' | Un juez vota PASS en dos de tres votos | Un subagente juez |
Cómo se hacen bien:
* **Al menos 3 casos**, sacados de **fallos reales** del modelo sin la //skill//, y **uno negativo**: un prompt parecido en el que la //skill// no debe activarse.
* **Al menos 2 evaluadores por caso**: uno sobre el resultado y otro sobre el proceso.
* **Deterministas primero**: ''regex'' sobre ficheros largos; ''llm'' solo para salidas cortas y con un PASS y un FAIL concretos.
* **Cada caso se ejecuta 3 veces** con y 3 sin: el modelo no responde siempre igual.
* **El juez, de otro modelo** que el evaluado: un modelo tiende a aprobar lo suyo.
**''skill-activada'' lleva ''scored: false''.** Sin la //skill// siempre suspende, así que si contara en la nota inflaría la diferencia: solo dice si se activó.
**El modelo evaluado no puede leer ''evals/''.** Si ve los criterios, aprueba por imitación. Por eso el caso se ejecuta en una carpeta aparte que prepara ''fixture.sh'', y ''fixture.sh'' no apunta a nada de ''evals/''.
----
===== Ejercicios =====
==== Ejercicio 1 ====
Esta //skill// tiene cinco fallos de estructura. Encuéntralos y di cómo se arregla cada uno.
.claude/skills/K_Controladores/
├── skill.md
├── validacion/
│ └── reglas.md
├── agents/
│ └── revisor.md
├── templates/
│ └── controller-template.md
└── examples/
└── pedido-controller.md
==== Ejercicio 2 ====
Escribe la //skill// de conocimiento ''k-tests'' con las convenciones de los tests de tu proyecto: frontmatter, frase de entrada, tabla índice y las cinco secciones. Pon en ''templates/'' el esqueleto de una clase de test.
==== Ejercicio 3 ====
Escribe la //skill// de procedimiento ''do-crear-test'', que recibe un identificador de requisito y escribe su test cargando ''k-tests''. Tiene que tener ''User Input'', ''Outline'' con sus ''STOP conditions'', una sección por fase y ''Quick Guidelines''. Pruébala con ''/do-crear-test R-3''.
==== Ejercicio 4 ====
Crea la carpeta ''evals/'' de ''do-crear-test'' con tres casos: dos que salgan de fallos que hayas visto al pedir tests sin la //skill// y uno negativo. Rellena la tabla:
^ Caso ^ Prompt ^ Evaluador del resultado ^ Evaluador del proceso ^
| | | | |
| | | | |
| | | | |