Hasta ahora en esta serie hemos visto Hooks, que garantizan cómo se comporta el agente, y Skills, que definen qué sabe el agente. En este artículo vemos el tercer pilar de la extensibilidad de Claude Code: los Custom Commands, que definen qué puede hacer el agente cuando tú se lo pides explícitamente.
La distinción con las Skills es importante y vale la pena tenerla clara desde el principio. Una Skill se activa automáticamente cuando el agente detecta que la tarea actual es relevante; tú no tienes que hacer nada. Un Custom Command se activa cuando tú escribes /nombre — es una decisión consciente del developer, no del agente. Eso los hace perfectos para flujos de trabajo repetitivos que siempre quieres ejecutar de la misma forma, en el momento que tú decides.
En este artículo cubrimos la anatomía completa de un comando, el sistema de argumentos con $ARGUMENTS y posicionales, el frontmatter avanzado, cómo ejecutar shell desde dentro de un comando, los namespaces, la selección de modelo por comando, la integración con Tasks para workflows multi-paso, y los 5 ejemplos prácticos de la serie.
Sin más, vamos al tema.
Contenido
- Commands vs. Skills: cuándo usar cada uno
- Anatomía de un Custom Command
- El sistema de argumentos
- Frontmatter avanzado
- Ejecutar shell desde un comando: el prefijo !
- Ubicaciones y namespaces
- Selección de modelo por comando
- Integración con Tasks: workflows multi-paso
- 5 ejemplos prácticos
- Catálogo de comandos del repositorio
- Buenas prácticas
- Conclusión
Commands vs. Skills: cuándo usar cada uno
La confusión entre Commands y Skills es comprensible porque ambos usan Markdown y ambos extienden las capacidades del agente. La diferencia está en quién decide cuándo activarlos.
| Característica | Custom Command | Skill |
|---|---|---|
| Activación | Tú escribes /nombre |
Claude detecta automáticamente por contexto |
| Control | Total — ocurre cuando tú decides | Delegado al modelo |
| Argumentos | Sí — $ARGUMENTS, $1, $2 |
No directamente |
| Ideal para | Flujos de trabajo repetitivos con pasos definidos: commits, scaffolding, análisis específicos | Conocimiento de dominio que aplica en múltiples contextos: convenciones, patrones, guías |
| Estructura | Un archivo .md |
Un directorio con SKILL.md, scripts, templates, referencias |
| Ubicación | .claude/commands/ o ~/.claude/commands/ |
.claude/skills/ o ~/.claude/skills/ |
Una forma de pensarlo: las Skills son el conocimiento del agente; los Commands son su lista de tareas. "Sé cómo hacer TDD en Spring Boot" es una Skill. "Haz este commit ahora siguiendo Conventional Commits" es un Command.
Anatomía de un Custom Command
Un Custom Command es un archivo Markdown guardado en .claude/commands/. El nombre del archivo (sin la extensión) se convierte en el slash command. Un archivo llamado scaffold.md se invoca con /scaffold.
---
description: Genera un commit con mensaje Conventional Commits
argument-hint: [tipo] [descripción opcional]
allowed-tools: Bash
model: haiku
---
# Commit con Conventional Commits
Genera y ejecuta un git commit siguiendo el estándar
Conventional Commits para los cambios actuales.
El tipo de commit es: $1
La descripción adicional (si se proporcionó) es: $2
Pasos:
1. Ejecuta `git diff --staged` para ver los cambios
2. Analiza los cambios y genera un mensaje descriptivo
3. Formato: tipo(scope): descripción en imperativo
4. Ejecuta el commit
Cuando ejecutas /commit feat "agrega validación de email", Claude lee el archivo, sustituye $1 por feat y $2 por agrega validación de email, y ejecuta las instrucciones con ese contexto.
El sistema de argumentos
Hay tres formas de usar argumentos en un comando, cada una con su caso de uso específico.
$ARGUMENTS — todos los argumentos como una cadena
La forma más simple: todo lo que escribas después del nombre del comando se inyecta como texto en $ARGUMENTS.
# .claude/commands/explain.md
Explica en detalle qué hace el siguiente código Java,
incluyendo complejidad algorítmica si aplica:
$ARGUMENTS
# Uso:
/explain public List<User> findActiveUsers() { return repo.findByActiveTrue(); }
$1, $2, $3 — argumentos posicionales
Cuando el comando tiene estructura definida y necesitas separar los argumentos por posición. Los argumentos se separan por espacios; los que contienen espacios van entre comillas.
# .claude/commands/scaffold-service.md
Genera la estructura completa de un servicio Spring Boot.
Nombre del servicio: $1
Módulo donde va: $2
Descripción de lo que hace: $3
Crea:
- La interfaz $1Service en src/main/java/.../services/
- La implementación $1ServiceImpl
- El test unitario $1ServiceTest con JUnit 5 y Mockito
- El request/response DTO correspondiente
# Uso:
/scaffold-service Payment payments "Procesa pagos con tarjeta de crédito"
$ARGUMENTS condicional — un comando con múltiples modos
Una de las técnicas más poderosas: un solo comando que se comporta diferente según el argumento recibido. Combinando $ARGUMENTS con instrucciones condicionales en el prompt se pueden crear comandos versátiles donde /review security y /review performance activan análisis completamente diferentes desde el mismo archivo.
# .claude/commands/review.md
---
description: Análisis de código — modo según argumento
argument-hint: security | performance | architecture | all
---
Analiza el diff actual (git diff main...HEAD) con el siguiente foco:
$ARGUMENTS
Instrucciones según el foco:
- Si es "security": busca SQL injection, XSS, secrets hardcodeados,
endpoints sin autenticación y configuraciones inseguras de Spring Security
- Si es "performance": busca queries N+1, FetchType.EAGER en colecciones,
operaciones síncronas bloqueantes en Spring WebFlux, y memory leaks
- Si es "architecture": busca violaciones de capas, acoplamiento circular,
clases con múltiples responsabilidades y dependencias hacia adentro
- Si es "all" o sin argumento: corre los tres análisis anteriores
# Los tres modos desde el mismo archivo:
/review security
/review performance
/review architecture
/review # sin argumento → modo "all"
Frontmatter avanzado
El frontmatter YAML es opcional, pero marca la diferencia entre un comando básico y uno profesional. Estos son todos los campos disponibles:
| Campo | Tipo | Descripción |
|---|---|---|
description |
string | Aparece en /help y en el autocompletado. Describe qué hace el comando. |
argument-hint |
string | Texto de ayuda que aparece al escribir el comando. Describe qué argumentos acepta. |
allowed-tools |
string | Herramientas disponibles durante la ejecución. Limita el scope sin pedir confirmación. |
model |
string | Modelo a usar para este comando específico: haiku, sonnet, opus. |
---
description: Genera el changelog entre dos tags de Git
argument-hint: [tag-anterior] [tag-nuevo]
allowed-tools: Bash
model: haiku
---
/ en la sesión, Claude Code muestra la lista de comandos disponibles con su description. Cuando empieza a escribir el nombre del comando, muestra el argument-hint. Estos dos campos juntos convierten tus comandos en herramientas autodocumentadas.
Ejecutar shell desde un comando: el prefijo !
Esta es la capacidad más poderosa y menos conocida de los Custom Commands: puedes ejecutar comandos de shell dentro del archivo Markdown del comando usando el prefijo !. El output del comando se inyecta en el contexto antes de que Claude empiece a procesar las instrucciones.
# .claude/commands/commit.md
# El ! ejecuta el comando y vuelca el output al contexto
---
description: Genera y ejecuta un commit con Conventional Commits
allowed-tools: Bash
model: haiku
---
Genera un commit para los siguientes cambios staged:
!git diff --staged
!git status --short
Usa el estándar Conventional Commits:
- Tipo: feat | fix | refactor | test | docs | chore | perf
- Formato: tipo(scope): descripción en imperativo (max 72 chars)
- Si hay múltiples cambios de distintos tipos, haz un commit por tipo
Después de generar el mensaje, ejecútalo con git commit.
La diferencia con pedirle al agente que ejecute git diff --staged es que el shell se ejecuta antes de que el agente empiece a pensar. El agente recibe el output como parte del prompt inicial, no como resultado de una herramienta que tuvo que invocar. Esto lo hace más rápido y consume menos tokens.
! dentro de un archivo .md de comando ejecuta el shell cuando el comando se invoca y vuelca el output al contexto inicial. El ! que escribes directamente en la sesión interactiva ejecuta el shell en ese momento y vuelca el output a la conversación activa. Mismo prefijo, mismo efecto, diferente momento de ejecución.
Ubicaciones y namespaces
Los Custom Commands tienen dos alcances, igual que las Skills, y Claude Code los distingue con un sistema de namespaces:
→ Se invoca como /nombre o /project:nombre
→ Se versiona en Git con el proyecto
→ Disponible para todo el equipo
Scope personal (~/.claude/commands/):
→ Se invoca como /user:nombre
→ No se versiona
→ Disponible en todos tus proyectos
Namespacing por subdirectorios:
.claude/commands/git/commit.md → /git:commit
.claude/commands/git/branch.md → /git:branch
.claude/commands/db/migrate.md → /db:migrate
Los subdirectorios dentro de .claude/commands/ crean namespaces automáticamente. Esto es muy útil cuando tienes muchos comandos: en lugar de una lista plana de 20 comandos, los organizas en grupos temáticos que el autocompletado presenta de forma organizada.
# Estructura recomendada para proyectos con muchos comandos
.claude/commands/
├── git/
│ ├── commit.md → /git:commit
│ ├── branch.md → /git:branch
│ └── pr.md → /git:pr
├── gen/
│ ├── service.md → /gen:service
│ ├── controller.md → /gen:controller
│ └── migration.md → /gen:migration
├── fix/
│ ├── tests.md → /fix:tests
│ └── build.md → /fix:build
└── review.md → /review
review-, gen-, fix- o la organización por subdirectorios hace que el autocompletado con / muestre los comandos agrupados, lo que facilita encontrarlos rápido incluso cuando tienes más de 10.
Selección de modelo por comando
Una de las funcionalidades más valiosas del frontmatter es la selección de modelo. No todas las tareas necesitan el mismo nivel de inteligencia, y usar el modelo correcto para cada tarea puede hacer una diferencia enorme en velocidad y costo.
| Modelo | Cuándo usarlo en un comando | Ejemplo |
|---|---|---|
haiku |
Tareas mecánicas y repetitivas: formatear commits, generar nombres de ramas, crear boilerplate simple | /commit, /branch, /changelog |
sonnet |
Tareas de implementación estándar: scaffolding de servicios, generación de tests, refactorizaciones acotadas | /gen:service, /fix:tests |
opus |
Análisis complejos: revisiones de arquitectura, análisis de seguridad, debugging de problemas difíciles | /review architecture, /security-audit |
# Comando rápido con Haiku — responde en segundos
---
description: Crea una rama con nombre estándar del equipo
argument-hint: descripcion-del-feature
model: haiku
allowed-tools: Bash
---
Crea una rama Git con el nombre estándar del equipo para: $ARGUMENTS
Formato: feature/PROJ-NNN-descripcion-en-kebab-case
Si no hay número de ticket en la descripción, usa feature/descripcion.
Ejecuta: git checkout -b [nombre-generado]
Integración con Tasks: workflows multi-paso
Los Custom Commands pueden orquestar flujos de trabajo complejos que implican múltiples pasos, archivos y decisiones. La forma más efectiva de hacerlo es estructurando el comando como una lista de Tasks que el agente ejecuta secuencialmente, con puntos de verificación entre cada una.
# .claude/commands/gen/feature.md
# Scaffolding completo de un feature — ejecuta múltiples pasos con verificación
---
description: Genera el scaffolding completo de un feature nuevo
argument-hint: [nombre-del-feature] [modulo]
allowed-tools: Read, Write, Bash
---
Genera el scaffolding completo para el feature "$1" en el módulo "$2".
## Task 1: Análisis previo
Antes de crear ningún archivo:
- Lee la estructura actual del módulo $2 en src/main/java
- Identifica las convenciones de naming existentes
- Lista los archivos que crearás (pide confirmación antes de continuar)
## Task 2: Estructura de capas
Crea los archivos en este orden exacto:
1. Entity / Domain object
2. Repository interface
3. Service interface + implementación
4. Controller REST
5. Request/Response DTOs
Sigue las convenciones identificadas en Task 1.
## Task 3: Tests
Para cada clase creada, genera el test correspondiente:
- Tests unitarios con Mockito para servicios
- Tests de integración con Testcontainers para repositorios
## Task 4: Verificación
- Compila: ./gradlew compileJava
- Corre los tests nuevos: ./gradlew test --tests "*$1*"
- Reporta qué pasó y qué queda pendiente
La clave de este patrón es que cada Task tiene un objetivo claro y verificable. Si Task 1 falla (por ejemplo, el módulo no existe), el agente lo reporta antes de crear archivos. Si Task 4 falla, el agente tiene el contexto completo para diagnosticar qué salió mal.
5 ejemplos prácticos
Ejemplo 1 (Java / Gradle): Commit con Conventional Commits
El comando más usado en el día a día. Usa Haiku (rápido y económico), ejecuta git diff --staged automáticamente y genera el mensaje sin que tengas que copiar nada.
# .claude/commands/git/commit.md
---
description: Genera y ejecuta un commit con Conventional Commits
allowed-tools: Bash
model: haiku
---
Genera un commit para estos cambios:
!git diff --staged
!git status --short
Reglas:
- Formato: tipo(scope): descripción en imperativo (max 72 chars)
- Tipos: feat | fix | refactor | test | docs | chore | perf | ci
- El scope es el módulo o paquete Java afectado (en minúsculas)
- Si hay breaking change, añade "!" después del tipo: feat!:
- Si hay múltiples tipos de cambios, propón splits en commits separados
Cuando tengas el mensaje, ejecuta el commit.
Si hay cambios sin stage, avisa antes de proceder.
# Uso: simplemente invoca el comando — no necesitas argumentos
/git:commit
Ejemplo 2 (Java / Gradle): Scaffolding de servicio Spring Boot
Genera la estructura completa de un servicio — interfaz, implementación, test — siguiendo las convenciones del proyecto. Acepta argumentos posicionales para el nombre y módulo.
# .claude/commands/gen/service.md
---
description: Genera interfaz, implementación y test de un servicio Spring Boot
argument-hint: NombreServicio modulo "descripción"
allowed-tools: Read, Write, Bash
---
Genera la estructura completa del servicio "$1" en el módulo "$2".
Descripción de lo que hace: $3
Antes de crear nada:
- Lee el directorio src/main/java para identificar el package base
- Verifica si existe ya el módulo $2 y sus convenciones
Crea en orden:
1. Interfaz: $1Service.java
2. Implementación: $1ServiceImpl.java con @Service y @RequiredArgsConstructor
3. Test: $1ServiceTest.java con @ExtendWith(MockitoExtension.class)
Convenciones obligatorias:
- Inyección por constructor (no @Autowired en campos)
- Lombok para boilerplate (@RequiredArgsConstructor, @Slf4j)
- Métodos del test: nombreMetodo_condicion_resultadoEsperado()
Al terminar: ./gradlew compileJava para verificar que compila.
/gen:service Payment payments "Procesa pagos con tarjeta de crédito y PayPal"
Ejemplo 3 (Java / Gradle): Fix automático de tests fallidos
Ejecuta los tests, captura los fallos y le pide al agente que los corrija en una sola invocación. Útil después de una refactorización o cuando el build de CI está roto.
# .claude/commands/fix/tests.md
---
description: Corre los tests, analiza los fallos y los corrige
argument-hint: [patrón de test opcional]
allowed-tools: Read, Write, Bash
---
!./gradlew test $ARGUMENTS 2>&1 | tail -60
Analiza los tests fallidos del output anterior y corrígelos.
Proceso:
1. Identifica la causa raíz de cada fallo (¿cambio en la API?, ¿dato de prueba roto?, ¿bug real?)
2. Distingue entre: test que falló por cambio legítimo de comportamiento vs. bug introducido
3. Si es cambio legítimo: actualiza el test para reflejar el nuevo comportamiento correcto
4. Si es bug: corrige el código de producción, no el test
5. Vuelve a correr los tests para confirmar que todos pasan
No hagas commit — solo corrije y verifica.
# Correr todos los tests fallidos:
/fix:tests
# Solo los tests de un módulo específico:
/fix:tests --tests "*PaymentService*"
Ejemplo 4 (Genérico): Generador de changelog
Genera el changelog entre dos tags de Git con el formato del equipo, listo para copiar al CHANGELOG.md o a la descripción del release.
# .claude/commands/git/changelog.md
---
description: Genera changelog entre dos tags de Git
argument-hint: [tag-anterior] [tag-nuevo]
allowed-tools: Bash
model: haiku
---
!git log $1..$2 --pretty="%s (%h)" --no-merges
Genera un changelog estructurado basado en los commits anteriores.
Formato de salida:
## [versión] — fecha
### ✨ Nuevas funcionalidades
- descripción de cada feat commit
### 🐛 Correcciones
- descripción de cada fix commit
### ♻️ Refactorizaciones
- descripción de cada refactor commit (si son significativas)
### 🔧 Cambios internos
- resto de commits (chore, ci, docs, test)
Reglas:
- Convierte los mensajes técnicos a lenguaje comprensible para el equipo
- Agrupa commits del mismo módulo
- Omite commits triviales (bump version, fix typo)
- Breaking changes van al principio con ⚠️
/git:changelog v1.3.0 v1.4.0
Ejemplo 5 (Genérico): Explicación de código para documentación
Explica un archivo o clase en lenguaje natural y genera el Javadoc correspondiente. Útil para documentar código heredado o revisar documentación existente.
# .claude/commands/doc/explain.md
---
description: Explica código y genera Javadoc para clases o métodos
argument-hint: ruta/al/Archivo.java [método opcional]
allowed-tools: Read, Write
---
Lee y analiza: $1
Si se especificó un método ($2), enfócate en él.
Si no, analiza la clase completa.
Genera dos secciones:
## 1. Explicación en lenguaje natural
- Qué hace esta clase/método y por qué existe
- Cuáles son sus responsabilidades principales
- Con qué otras clases se relaciona (dependencias y dependientes)
- Casos de uso típicos
- Posibles problemas o limitaciones
## 2. Javadoc generado
Javadoc completo listo para añadir al código:
- @param para cada parámetro con descripción útil
- @return describiendo el valor de retorno
- @throws para cada excepción con la condición que la dispara
- Ejemplo de uso si el método no es trivial
Pregunta si quieres que aplique el Javadoc directamente al archivo.
# Documentar una clase completa:
/doc:explain src/main/java/com/miapp/pagos/PaymentService.java
# Documentar un método específico:
/doc:explain src/main/java/com/miapp/pagos/PaymentService.java charge
Catálogo de comandos del repositorio
El repositorio que acompaña esta serie incluye todos los comandos descritos en este artículo, organizados por namespace. Están disponibles en GitHub.
| Comando | Modelo | Descripción | Tipo |
|---|---|---|---|
/git:commit | Haiku | Genera y ejecuta un commit con Conventional Commits a partir del staged area | Genérico |
/git:branch | Haiku | Crea una rama con el formato estándar del equipo a partir de una descripción | Genérico |
/git:changelog | Haiku | Genera changelog entre dos tags con secciones por tipo de commit | Genérico |
/gen:service | Sonnet | Scaffolding completo de servicio Spring Boot: interfaz, implementación y test | Java |
/gen:controller | Sonnet | Genera un controller REST con endpoints CRUD completos y documentación OpenAPI | Java |
/gen:migration | Sonnet | Genera una migración Flyway con naming convention y rollback documentado | Java |
/gen:feature | Sonnet | Scaffolding completo de un feature: entidad, repo, servicio, controller, tests | Java |
/fix:tests | Sonnet | Corre los tests, analiza los fallos y los corrige distinguiendo bug vs. test obsoleto | Genérico |
/fix:build | Sonnet | Corre el build, captura los errores de compilación y los corrige en orden de dependencia | Java |
/review | Opus | Code review del diff actual — modos: security | performance | architecture | all | Genérico |
/doc:explain | Sonnet | Explica una clase o método y genera el Javadoc correspondiente | Genérico |
/doc:readme | Sonnet | Genera o actualiza el README de un módulo siguiendo la plantilla del equipo | Genérico |
Buenas prácticas
Empieza simple, itera
El mejor Custom Command no es el más completo sino el que realmente usas. Empieza con un comando de 5 líneas que resuelva tu problema más repetitivo, y añade complejidad solo cuando notes que le falta algo. Un comando de 50 líneas que nunca usas vale menos que uno de 10 que invocas 20 veces al día.
Usa el modelo correcto para cada tarea
No necesitas un modelo potente para corregir un punto y coma: añade model: haiku en el frontmatter y los comandos de tareas mecánicas se ejecutan casi al instante. Reserva Sonnet para implementación y Opus para análisis complejos.
Versiona los comandos en Git
Versionar .claude/commands/ en Git hace que todos los miembros del equipo tengan acceso a los mismos comandos sin configuración manual. Los comandos del proyecto son parte de la infraestructura del proyecto, igual que los scripts de build o los hooks de CI.
El ! es más eficiente que pedirle al agente que ejecute
Cuando sabes exactamente qué información necesitas al inicio del comando, usa ! para capturarla. El agente recibe el output como parte del contexto inicial y no necesita invocar herramientas para obtenerlo, lo que ahorra turnos y tokens.
Documenta con description y argument-hint
Aunque el frontmatter es opcional, description y argument-hint son los dos campos que más valor aportan por el costo que tienen (dos líneas). Sin ellos, en tres semanas ni tú recuerdas qué hace /gen:service.
Conclusión
Los Custom Commands son la capa de automatización más directa y controlable de Claude Code. A diferencia de los Hooks (que se disparan automáticamente) y las Skills (que el agente activa cuando las considera relevantes), los Commands ocurren exactamente cuando tú lo decides y exactamente como tú los definiste.
Los tres patrones que más impactan en la productividad diaria:
- El trío de Git (
/git:commit,/git:branch,/git:pr) elimina la fricción mental de recordar convenciones de naming y formato en cada operación de versión. - Los generadores con Tasks (
/gen:feature,/gen:service) convierten una tarea de 30 minutos en una de 3, con la garantía de que el resultado sigue las convenciones del equipo. - El
/reviewcon modos hace que una revisión de código completa sea tan fácil como un/review allantes de abrir un PR.
Puedes encontrar todo el código de los commandos que creamos en el repositorio del tutorial en GitHub.
En el próximo artículo veremos MCP Servers: cómo conectar Claude Code a bases de datos, GitHub, Jira y otras herramientas externas para que el agente tenga acceso a información que va más allá del repositorio local.
© javatutoriales.com – Serie: Desarrollo Asistido por IA