EN · Read in English Explorar gratis

Receta · Intermedio · 20 min

Crear un plugin de Claude Code: init, prueba, eval y marketplace

Crea un plugin de Claude Code con plugin.json y una skill, valídalo, pruébalo con --plugin-dir, pásale un eval y publícalo en tu marketplace. Probado.

Portada: Crear un plugin de Claude Code: init, prueba, eval y marketplace

Probado con Claude Code 2.1.316 · revisado

Ver los archivos en GitHub Gratis · licencia MIT

El problema

Tienes skills, agentes o hooks que te funcionan y quieres usarlos en varios proyectos o pasárselos a tu equipo sin copiar carpetas a mano. Un plugin es justo eso: una carpeta con skills, agentes, hooks o servidores MCP y un manifiesto plugin.json que les pone nombre, para instalarlos de una vez y publicar versiones. El problema es que entre crear la estructura, validarla, comprobar que Claude la usa bien y publicarla hay varios pasos con trampas que la documentación no junta en un solo sitio.

La solución

Vamos a crear commit-es, un plugin que redacta mensajes de commit en español, lo validaremos, lo probaremos sin instalarlo, le pasaremos un eval automático y lo publicaremos en un marketplace propio. Todo probado con Claude Code 2.1.316 en Linux. El plugin completo está en el repositorio gratuito ctrlia-crear-plugin-claude-code.

1. Decide si necesitas un plugin

Si solo quieres una skill para ti o para un proyecto, no necesitas un plugin: basta con .claude/skills/<nombre>/SKILL.md (lo explicamos en la receta de la primera skill). Haz un plugin cuando quieras compartir la configuración con otras personas, instalarla en varios proyectos o publicar versiones.

Un plugin puede llevar skills (skills/), subagentes (agents/), hooks (hooks/hooks.json), servidores MCP (.mcp.json) y estilos de salida, todo en la raíz del plugin; solo plugin.json va dentro de .claude-plugin/. Al pasar a plugin cambian los nombres: las skills llevan el nombre del plugin delante, como /commit-es:mensaje, para que dos plugins puedan tener una skill mensaje sin chocar.

Hay dos maneras de empezar. claude plugin init <nombre> crea un plugin en ~/.claude/skills/<nombre>/ que se carga solo en todas tus sesiones (útil para ti). Para algo que vas a publicar, la documentación crea la carpeta a mano y la prueba con --plugin-dir; es lo que haremos.

2. Crea la estructura y el manifiesto

La estructura final del plugin:

commit-es/
├── .claude-plugin/
│   ├── plugin.json
│   └── marketplace.json        (paso 5)
├── skills/
│   └── mensaje/
│       └── SKILL.md
└── evals/                      (paso 4)

Crea commit-es/.claude-plugin/plugin.json con name (obligatorio, sin espacios: será el prefijo de las skills), description, version, author y license:

{
  "name": "commit-es",
  "description": "Redacta mensajes de commit en español con Conventional Commits",
  "version": "0.1.0",
  "author": { "name": "Ctrl+IA" },
  "license": "MIT"
}

Después crea commit-es/skills/mensaje/SKILL.md. La description de la skill es lo que Claude compara con lo que pide el usuario, así que incluye frases reales como «escribe el commit» o «mensaje de commit». Con allowed-tools (una lista YAML) la skill puede ejecutar git diff y git status sin pedir permiso durante ese turno. Las instrucciones son pasos cortos:

---
name: mensaje
description: Redacta el mensaje de commit de los cambios preparados (git diff --cached) en español y con formato Conventional Commits. Úsala cuando el usuario pida «escribe el commit», «mensaje de commit» o «haz el commit».
allowed-tools:
  - Bash(git diff *)
  - Bash(git status *)
---
1. Ejecuta `git diff --cached --stat` y `git diff --cached`.
2. Si no hay nada preparado pero el usuario describe el cambio, usa su descripción; si no hay ni una cosa ni la otra, dilo y para.
3. Escribe UNA línea de título de ≤ 72 caracteres: `tipo(ámbito): resumen` en español, en imperativo
   (tipos: feat, fix, docs, refactor, test, chore).
4. Deja una línea en blanco y, solo si el cambio no es obvio, 1-3 viñetas con el porqué.
5. Devuelve solo el mensaje, sin comentarios antes ni después. No ejecutes `git commit`.

Si prefieres que te genere el esqueleto, claude plugin init commit-es --with skills crea el manifiesto (con "skills": ["./"]), un SKILL.md raíz y uno de ejemplo, con TODO en la descripción. Recuerda que lo crea en ~/.claude/skills/, no en tu carpeta actual.

3. Valídalo y pruébalo sin instalar nada

Valida el manifiesto y el frontmatter de cada skill:

claude plugin validate ./commit-es

Termina con ✔ Validation passed. En la 2.1.316 también pasa las comprobaciones del directorio de plugins de Anthropic y avisa de lo que faltaría para enviarlo allí: en nuestra prueba pidió un README.md de al menos 40 palabras, un LICENSE (o license en plugin.json) y un icono cuadrado PNG de 512 a 2048 px. Esos avisos no hacen fallar la validación.

Luego cárgalo solo para una sesión y pruébalo con algún cambio preparado con git add:

# Sesión interactiva con el plugin cargado solo esta vez
claude --plugin-dir ./commit-es
# > /commit-es:mensaje

# O sin interfaz, desde un repo con cambios preparados (git add)
claude -p --plugin-dir ../commit-es --output-format json "/commit-es:mensaje" < /dev/null | jq -r '.result'

Si editas archivos del plugin con la sesión abierta, /reload-plugins los recarga. En nuestro repositorio de prueba, claude -p --plugin-dir ../commit-es "/commit-es:mensaje" devolvió feat(calc): añadir función resta y documentar suma en unos 10 segundos. Si algo no aparece, /plugin tiene una pestaña Errors con el motivo, y claude plugin list muestra qué cargó. Si nunca has usado claude -p, la receta de Claude Code headless lo explica.

4. Comprueba que cambia el comportamiento: claude plugin eval

Que un plugin cargue no significa que Claude lo use bien. claude plugin eval (v2.1.269 o posterior; git 2.31 o posterior si tienes git) ejecuta casos de prueba con y sin el plugin y puntúa la diferencia. Cada caso es una carpeta en evals/ con un prompt.md (lo que escribiría un usuario) y uno o más evaluadores en graders/:

claude plugin eval init te los propone en una sesión; claude plugin eval init --bare nombre crea una plantilla vacía. Este es nuestro caso, con cada archivo marcado por un comentario:

<!-- evals/commit-renombrado/prompt.md -->
---
max_turns: 6
allowed_tools: [Read, Glob, Grep, Skill]
---
Escríbeme el mensaje de commit para este cambio: renombré getUser a fetchUser y actualicé las tres llamadas.

<!-- evals/commit-renombrado/graders/formato.md -->
---
type: regex
flags: m
pattern: '^refactor(\([^)]+\))?: .*fetchUser'
---

<!-- evals/commit-renombrado/graders/skill-usada.md -->
---
type: tool_used
tool: Skill
input_match: '"skill"\s*:\s*"(?:[\w-]+:)?mensaje"'
---

<!-- evals/commit-renombrado/graders/criteria.md -->
---
type: llm
---
PASS si la respuesta es un único mensaje de commit en español cuyo título usa el tipo refactor y menciona fetchUser.
FAIL si está en inglés, si incluye texto antes o después del mensaje o si dice que ha ejecutado git commit.

Por defecto cada caso se ejecuta 3 veces con el plugin y 3 sin él, y cada ejecución es una llamada real al modelo que gasta de tu plan. Para iterar barato:

cd commit-es
claude plugin eval . --runs 1 --ablation none --model haiku --max-cost-usd 0.5 --no-publish
# Antes de fiarte del resultado: con las 3 ejecuciones y la comparación sin plugin
claude plugin eval .
# Añade evals/results/ a .gitignore

Nuestro primer eval suspendió (0,33) y nos enseñó dos cosas. El regex con (?m) falló porque los patrones son de JavaScript: los flags van en flags:, como en el archivo de arriba. Y el juez rechazó la respuesta porque, sin Bash en allowed_tools, la skill explicaba que no podía leer el diff antes de dar el mensaje: justo el tipo de fallo que un eval tiene que encontrar.

5. Publícalo en tu propio marketplace

Un marketplace es un archivo .claude-plugin/marketplace.json en un repositorio git que lista tus plugins. Para publicar desde el mismo repositorio del plugin, guárdalo junto a plugin.json con una entrada cuyo source sea "./" y cuyo name sea el mismo que el del manifiesto (si no coinciden, la instalación falla):

{
  "name": "ctrlia",
  "description": "Plugins en español de Ctrl+IA para Claude Code",
  "owner": { "name": "Ctrl+IA" },
  "plugins": [
    { "name": "commit-es", "source": "./" }
  ]
}

Elige para el marketplace un nombre poco común, porque nadie puede tener dos marketplaces con el mismo nombre. La description es opcional, pero sin ella claude plugin validate avisa de que falta. Ejecuta claude plugin validate ., haz commit y súbelo a GitHub. Esto es lo que harán tus usuarios:

claude plugin marketplace add tu-usuario/commit-es
claude plugin install commit-es@ctrlia
# o en un solo paso (v2.1.292 o posterior)
claude plugin install commit-es --marketplace tu-usuario/commit-es
# actualizar cuando subas la versión
claude plugin update commit-es@ctrlia

Las actualizaciones llegan con claude plugin update cuando cambias version. Lo probamos con una carpeta local en lugar de GitHub y funcionó: marketplace add y install con alcance de usuario. Si antes usaste plugin init con el mismo nombre, claude plugin list avisa de que la copia de ~/.claude/skills no se carga porque el nombre ya lo tiene el plugin instalado: cambia uno de los dos nombres.

6. Revisa la seguridad: lo que un plugin puede hacer en tu máquina

Un plugin no es solo texto: sus hooks y servidores MCP ejecutan programas con tus permisos de usuario, y sus skills pueden traer allowed-tools que aprueban comandos. Antes de instalar uno ajeno, lee su hooks/hooks.json, su .mcp.json y los scripts que llame, igual que revisarías un paquete de npm (la página oficial Plugin security and trust detalla qué revisar).

Ni claude plugin validate ni claude plugin eval son una revisión de seguridad: la propia ayuda de eval dice que su aislamiento limita lo que un plugin malicioso puede alcanzar, pero no es una garantía, y que solo evalúes plugins en los que confías. En tus plugins, da el mínimo: allowed-tools con comandos concretos (Bash(git diff *), no Bash), sin secretos en el repositorio y con una versión en plugin.json para que tus usuarios no reciban cambios sin querer. Para quitar un plugin: claude plugin disable o claude plugin uninstall, y para los creados con plugin init, borra su carpeta de ~/.claude/skills. Si tu plugin lleva hooks, la receta de hooks de seguridad explica cómo funcionan.

Compruébalo

Con Claude Code 2.1.316 en Linux, y con HOME y CLAUDE_CONFIG_DIR temporales para no tocar la configuración real, esto es lo que obtuvimos (extracto):

$ claude plugin validate ./commit-es
...
✔ Validation passed

$ claude plugin marketplace add ./commit-es
✔ Successfully added marketplace: ctrlia (declared in user settings)

$ claude plugin install commit-es@ctrlia
✔ Successfully installed plugin: commit-es@ctrlia (scope: user)

$ claude plugin list
Installed plugins:

  ❯ commit-es@ctrlia
    Version: 0.1.0
    Scope: user
    Status: ✔ enabled

Con el plugin cargado, /commit-es:mensaje sobre cambios preparados devolvió feat(calc): añadir función resta y documentar suma. claude plugin eval . --runs 1 --ablation none --model haiku ejecutó el caso de prueba y la primera vez dio 0,33 por los dos fallos del paso 4. No hemos probado todavía a publicar en GitHub de verdad ni el envío al directorio de Anthropic.

Por qué funciona

Claude Code busca el manifiesto en .claude-plugin/plugin.json y carga desde la raíz del plugin las carpetas que conoce (skills/, agents/, hooks/…), poniendo el name del manifiesto delante de cada skill para que no choquen. --plugin-dir carga esa carpeta solo para una sesión, sin instalar nada. Un marketplace es solo un catálogo en git: marketplace add lo registra, install instala el plugin que lista y update lo trae de nuevo cuando cambia version. plugin eval ejecuta el mismo prompt con y sin el plugin y puntúa la diferencia, así que mide si el plugin cambia el comportamiento, no solo si carga. Más detalles en crear plugins, publicar plugins, evals de plugins, la referencia de la CLI de plugins y la página de skills.

Errores típicos

Preguntas frecuentes

¿Por qué mi skill se llama /commit-es:mensaje y no /mensaje?

Las skills de un plugin llevan el nombre del plugin delante para no chocar con las de otros. La excepción es el SKILL.md raíz que crea plugin init, que se invoca como /nombre.

Puse skills/ dentro de .claude-plugin/ y no cargan.

Dentro de .claude-plugin/ solo va plugin.json. Mueve skills/, agents/ y hooks/ a la raíz del plugin y ejecuta /reload-plugins.

«no marketplaces configured»: ¿qué significa?

Que todavía no has añadido ningún marketplace. Añade uno con claude plugin marketplace add owner/repo (o una ruta local) y vuelve a intentar la instalación.

¿Cuánto cuesta un eval?

Cada ejecución es una llamada real al modelo con tus credenciales: por defecto, 3 con el plugin y 3 sin él por caso, más las del juez. Usa --runs 1 --ablation none --model haiku --max-cost-usd mientras iteras; nuestro caso costó menos de un céntimo estimado.

El eval dice «Invalid regular expression» en mi grader regex.

Los patrones son JavaScript: no admiten (?m) ni (?i) en línea. Pon los modificadores en el campo flags, por ejemplo flags: m.

¿Validate me avisa de README, LICENSE e icono aunque dice «Validation passed»?

Son comprobaciones del directorio oficial de Anthropic: solo hacen falta si vas a enviarlo allí. Para un marketplace propio no son obligatorias, aunque un README con la línea de instalación ayuda a tus usuarios.

Toda la biblioteca