# Lint Drupal Module

> Lint review completo de un módulo Drupal 11 combinando 4 fuentes en paralelo — PHPStan level 5 + phpstan-drupal, PHPCS Drupal/DrupalPractice, agente drupal-qa (estándares) y agente drupal-security (OWASP). Dos modos — completo (todo el módulo) y diff (solo archivos cambiados vs develop). Genera informe markdown estructurado en la carpeta del IDE con resumen ejecutivo, hallazgos clasificados por s…

- **Type:** Skill
- **Install:** `agentstack add skill-j4rk0r-claude-skills-lint-drupal-module`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [j4rk0r](https://agentstack.voostack.com/s/j4rk0r)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [j4rk0r](https://github.com/j4rk0r)
- **Source:** https://github.com/j4rk0r/claude-skills/tree/main/skills/lint-drupal-module
- **Website:** https://claude-skills-omega.vercel.app

## Install

```sh
agentstack add skill-j4rk0r-claude-skills-lint-drupal-module
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Lint Review — Módulo Drupal 11

Primera línea del informe generado: **"Español confirmado."**

## Para qué sirve

Combina 4 fuentes de análisis (PHPStan, PHPCS, agente drupal-qa, agente drupal-security) en una sola invocación paralelizada, y consolida los hallazgos en un informe accionable. A mano son ~12 pasos y ~30 minutos; con la skill, lo que tarda la fuente más lenta (~2-5 min en completo, ~30s-1min en diff).

## Fast path

```
1. Identificar módulo (nombre/ruta del usuario, o Glob si no especifica)
2. Detectar modo: completo (default) | diff (si dice "diff", "rápido", "vs develop")
3. Detectar entorno: ddev describe → ejecutar via "ddev exec"
4. Verificar herramientas (vendor/bin/phpstan, vendor/bin/phpcs); instalar si faltan PREGUNTANDO
5. Verificar agentes drupal-qa y drupal-security disponibles (si no, ver "Recovery")
6. Leer references/prompts-agentes.md (necesario para el paso 7)
7. Ejecutar las 4 fuentes EN PARALELO en el mismo mensaje:
   a) Agent drupal-qa  — prompt literal de references/prompts-agentes.md
   b) Agent drupal-security — prompt literal de references/prompts-agentes.md
   c) PHPStan level 5 vía Bash (ver "Ejecución en paralelo")
   d) PHPCS Drupal,DrupalPractice vía Bash
8. Leer references/plantilla-informe.md y consolidar las 4 salidas en un informe markdown
9. Detectar IDE → escribir en /Lint reviews/.md
10. Resumir top bloqueantes en chat; preguntar "arregla todo" / "solo crítico" / "déjalo así"
```

Si cualquier paso falla, **detente** y consulta [`references/edge-cases.md`](references/edge-cases.md). No improvises.

## Referencias bajo demanda

Tres archivos en `references/`, cargados solo cuando los necesitas (progressive disclosure):

| Archivo | Cuándo cargarlo | Por qué |
|---|---|---|
| [`references/prompts-agentes.md`](references/prompts-agentes.md) | Antes del paso 7 (invocar agentes) | Contiene los prompts literales para `drupal-qa` y `drupal-security`. Son largos a propósito — sin brief explícito los agentes devuelven reviews superficiales. Cópialos literales, solo sustituye ``, ``, ``. |
| [`references/plantilla-informe.md`](references/plantilla-informe.md) | Antes del paso 8 (redactar informe) | Plantilla fija. La consistencia entre informes de distintos módulos es lo que hace que el equipo los lea rápido. |
| [`references/edge-cases.md`](references/edge-cases.md) | Cuando algo falle | Síntoma → causa → solución para los problemas más comunes (DDEV, PHPStan, services.yml, OAuth, modo diff). |

Si ya leíste un archivo en esta sesión, no recargues — el contexto lo conserva.

## Modos

### Modo completo (default)
Analiza TODOS los archivos del módulo. Más exhaustivo (~2-5 min). Úsalo:
- Antes de un release
- En módulos recién creados
- Auditorías periódicas
- Cuando el usuario no especifica modo

### Modo diff
Analiza SOLO los archivos cambiados en la rama actual respecto a `origin/develop`. Más rápido (~30s-1min). Úsalo:
- Reviews intermedias durante desarrollo
- Validación pre-push
- Cuando el usuario dice "diff", "rápido", "solo lo que cambié", "vs develop"

Obtener la lista de archivos analizables del módulo (filtrando extensiones que PHPStan/PHPCS pueden procesar):

```bash
cd drupal
git fetch origin develop --quiet
git diff --name-only origin/develop...HEAD \
  | grep "^web/modules/custom//" \
  | grep -E '\.(php|module|inc|install|profile|theme|yml|twig)$'
```

El segundo `grep` es importante: sin él recibirás `.css`, `.md` o imágenes que PHPStan rechaza con "no PHP files found". Para PHPStan específicamente, restringe aún más a `\.(php|module|inc|install|profile|theme)$` (sin yml/twig).

Si el resultado está vacío o estás en `develop` → ver [`references/edge-cases.md`](references/edge-cases.md), sección "Modo diff".

## Identificación del módulo

| Lo que dice el usuario | Acción |
|---|---|
| Nombre exacto (`chat_soporte_tecnico_ia`) | `Glob: "**/web/modules/custom//*.info.yml"` |
| Ruta (`web/modules/custom/foo`) | Validar que existe `/.info.yml` |
| Sin especificar | `Glob: "**/web/modules/custom/*/*.info.yml"`. Si hay 1 → usar; si >1 → listar y preguntar; si 0 → parar |

## Entorno de ejecución

1. `ddev describe` (silencioso) → si OK, todo dentro de DDEV con `ddev exec`.
2. Si no hay DDEV → buscar `vendor/bin/phpstan` directo.
3. Si nada → preguntar al usuario qué entorno usa.

**Path en contenedor DDEV:** verifica con `ddev exec "ls /var/www/html"`. Suele ser `/var/www/html/drupal` o `/var/www/html` según el `docroot` del `.ddev/config.yaml`.

## Instalación de herramientas

Si `vendor/bin/phpstan` no existe:
```bash
ddev composer require --dev phpstan/phpstan mglaman/phpstan-drupal phpstan/phpstan-deprecation-rules
```

**Pedir confirmación antes de instalar.** PHPCS suele venir con `drupal/coder` (verificar con `ddev exec "vendor/bin/phpcs -i" | grep -i drupal`).

## Configuración PHPStan

Crea `phpstan.lint-review.neon` en la raíz del proyecto Drupal (no en el módulo):

```neon
parameters:
  level: 5
  paths:
    - web/modules/custom/
  excludePaths:
    - web/modules/custom//js/vendor/*
    - web/modules/custom//tests/fixtures/*
  reportUnmatchedIgnoredErrors: false
includes:
  - vendor/mglaman/phpstan-drupal/extension.neon
  - vendor/mglaman/phpstan-drupal/rules.neon
  - vendor/phpstan/phpstan-deprecation-rules/rules.neon
```

**No** uses `drupal_root` (deprecated en phpstan-drupal 2.x — ver edge-cases).

## Ejecución en paralelo

**Crítico:** lanza las 4 fuentes en el MISMO mensaje (mismo bloque de tool calls). Sin paralelización pierdes el principal valor de la skill.

### Modo completo

```bash
# PHPStan — usa el bloque paths: del .neon
ddev exec "cd /var/www/html/drupal && vendor/bin/phpstan analyse -c phpstan.lint-review.neon --no-progress --error-format=raw"

# PHPCS — apunta al directorio del módulo. Restringido a archivos PHP con --extensions.
# ⚠️ NUNCA incluir `js` en --extensions: el standard Drupal aplica reglas PHP al JS
# y phpcbf puede convertir null/true/false → NULL/TRUE/FALSE, rompiendo el JS en runtime.
# Los archivos JS se analizan con ESLint/prettier, no con PHPCS (ver edge-cases.md).
ddev exec "cd /var/www/html/drupal && vendor/bin/phpcs --standard=Drupal,DrupalPractice --report=full --extensions=php,module,inc,install,profile,theme --ignore='*/vendor/*,*/js/*' web/modules/custom/"
```

### Modo diff

Tras obtener la lista de archivos cambiados (ver sección "Modo diff"), pásalos como argumentos posicionales — sobreescriben el bloque `paths:` del `.neon`:

```bash
# PHPStan — solo archivos PHP cambiados
ddev exec "cd /var/www/html/drupal && vendor/bin/phpstan analyse -c phpstan.lint-review.neon --no-progress --error-format=raw \
  web/modules/custom//src/Foo.php \
  web/modules/custom//src/Bar.php"

# PHPCS — filtra archivos PHP y YAML (no JS) antes de pasarlos como positional args.
# Pasa SOLO archivos cuya extensión esté en --extensions; si pasas un .js posicional,
# phpcbf lo procesa igualmente aunque no esté en --extensions, rompiendo el JS.
ddev exec "cd /var/www/html/drupal && vendor/bin/phpcs --standard=Drupal,DrupalPractice --report=full --extensions=php,module,inc,install,profile,theme --ignore='*/vendor/*' \
  web/modules/custom//src/Foo.php \
  web/modules/custom//src/Bar.php \
  web/modules/custom//.routing.yml"
```

> **Nota sobre `phpcbf`**: PHPCS a menudo reporta "PHPCBF CAN FIX N OF THESE SNIFF VIOLATIONS AUTOMATICALLY". Si el número es alto (>50% de los ERRORS de PHP, **no cuentes los de JS**), merece la pena ofrecerle al usuario `phpcbf` como acción rápida tras el informe (ver "Después del informe"). Los JS NUNCA deben ir a phpcbf — ver el edge case "phpcbf rompe JavaScript" en `references/edge-cases.md`.

### Agentes (en el mismo mensaje que los dos comandos anteriores)

1. **Agent `drupal-qa`** — prompt literal de [`references/prompts-agentes.md`](references/prompts-agentes.md), sección "Prompt para `drupal-qa`".
2. **Agent `drupal-security`** — prompt literal de la misma referencia, sección "Prompt para `drupal-security`".

En modo diff, pasa la lista de archivos a los agentes con la ruta **completa relativa al repo** (`web/modules/custom//src/Foo.php`), no la subruta del módulo. Los agentes hacen Read sobre esas rutas.

## Consolidación del informe

Sigue la plantilla literal de [`references/plantilla-informe.md`](references/plantilla-informe.md). La estructura es fija: cabecera, resumen ejecutivo, una sección por fuente (PHPStan/PHPCS/QA/Security), acciones priorizadas P0/P1/P2, cobertura buenas prácticas, comandos de verificación.

**Reglas críticas** (todas detalladas en la plantilla):
- Severidades fijas: 🔴 ERROR/CRÍTICO, 🟠 ALTO, 🟡 MEDIO/WARNING, 🟢 BAJO, ℹ️ INFO.
- `archivo:línea` siempre clickable, ruta relativa al proyecto.
- Top 5 bloqueantes con ID corto reusable (`SEC-ALTO-1`, `QA-ROUTING`, etc.).
- P0 deben ser accionables — "añadir `_csrf_request_header_token` en las 9 rutas POST", no "mejorar la seguridad".
- Veredicto categórico al final del resumen ejecutivo.

## Ubicación del informe

**Paso 1 — IDE por env var (PRIORITARIO).** Ejecuta `printenv CLAUDE_CODE_ENTRYPOINT`:
| `CLAUDE_CODE_ENTRYPOINT` | Carpeta |
|---|---|
| `claude-antigravity` | `.antigravity/Lint reviews/` |
| `claude-cursor` | `.cursor/Lint reviews/` |
| `claude-vscode` | `.vscode/Lint reviews/` |
| otros (`cli`, vacío) | continuar al Paso 2 |

**Paso 2 — fallback por existencia:** `.antigravity/` > `.cursor/` > `.vscode/` > `docs/lint-reviews/`.

**Nombre:** `lint-review---.md`. Sobrescribir si existe (es la versión más reciente). NUNCA caigas a detección por carpeta cuando el env var es claro — eso causa el bug de elegir `.cursor/` solo porque sobrevive de un uso anterior del IDE.

## Después del informe

1. **Resumen 3-5 líneas** en chat: total hallazgos, top 3 bloqueantes, veredicto.
2. **Link clickable** al informe (ruta relativa).
3. **Pregunta** al usuario qué hacer (no asumas). Las opciones dependen de lo que encontraste:
   - **"arregla todo"** → delegar a `drupal-backend` con la lista P0 estructurada (archivo:línea + acción)
   - **"solo crítico"** → solo HIGH/CRITICAL de seguridad
   - **"auto-fix PHPCS"** → ofrece esta opción SOLO si PHPCS reportó que phpcbf puede arreglar una cantidad significativa (>50% de los ERRORS de **PHP**, excluyendo los de JS). Ejecuta: `ddev exec "cd /var/www/html/drupal && vendor/bin/phpcbf --standard=Drupal,DrupalPractice --extensions=php,module,inc,install,profile,theme --ignore='*/vendor/*,*/js/*' web/modules/custom/"`. **OBLIGATORIO** el flag `--extensions` para excluir `.js` — sin él, phpcbf convierte `null`/`true`/`false` → `NULL`/`TRUE`/`FALSE` en JavaScript y rompe el código en runtime (ver edge-cases.md). Avisa al usuario de que phpcbf **modifica archivos in-place** y pídele que revise el diff antes de commitear.
   - **"déjalo así"** → cerrar
4. **Recordar:** tras fixes en `routing.yml` o `services.yml` → `drush cr` obligatorio + tests del módulo + re-ejecutar la skill para verificar.

## NEVER (lecciones aprendidas a las malas)

Estas son las trampas que he visto romper la skill en sesiones reales. Cada una incluye el porqué, no solo la regla.

- **NUNCA modifiques archivos del módulo durante la skill.** Solo reportas. *Por qué:* los fixes son una fase posterior con confirmación explícita del usuario; mezclar análisis y fix dificulta auditar qué cambió.
- **NUNCA ejecutes las 4 fuentes en mensajes separados.** *Por qué:* la skill tarda ~4x más y pierdes el principal valor de tener herramientas + agentes corriendo en paralelo. Si tu cliente no soporta tool calls paralelas, dilo en voz alta antes de empezar para que el usuario lo sepa.
- **NUNCA parafrasees los prompts de los agentes.** Cópialos literales desde `prompts-agentes.md`. *Por qué:* sin la checklist explícita los agentes devuelven reviews superficiales (ver el docu del propio prompts-agentes.md).
- **NUNCA marques el veredicto como "APTO" con hallazgos ALTO/CRÍTICO sin resolver.** *Por qué:* el equipo lee solo el veredicto cuando va con prisa; un APTO falso bloquea la cultura de auditar.
- **NUNCA listes `Unsafe usage of new static()` en Controllers como bloqueante.** *Por qué:* es falso positivo conocido de phpstan-drupal con el patrón estándar de Drupal (ver edge-cases).
- **NUNCA elimines aliases FQCN en `services.yml` sin verificar la Forma A vs Forma B.** *Por qué:* la Forma A (alias real con `'@servicio'`) es necesaria para el autowiring del Hook OOP. Eliminarla rompe `drush cr`. Ver edge-cases.
- **NUNCA asumas que los tests funcionales pasan solo porque PHPUnit no falla.** *Por qué:* si PHPStan reporta métodos inexistentes (`getClient()`, `post()` sobre interfaces) en el directorio `tests/`, el test depende del driver actual y romperá silenciosamente en CI cuando cambie. Reportarlo como bloqueante.
- **NUNCA escribas el informe en inglés.** Código, comandos y nombres de clase en inglés; explicaciones en español. *Por qué:* el equipo trabaja en español y los informes mezclados son ruido.
- **NUNCA ejecutes `phpcbf` sobre archivos JavaScript** (ni pasándolos como argumento ni dejando que el standard Drupal los incluya). *Por qué:* el standard Drupal de PHPCS aplica la regla `Drupal.Semantics.ConstantName` a cualquier archivo que pilla, incluyendo `.js`. Phpcbf convierte `null`/`true`/`false` → `NULL`/`TRUE`/`FALSE` en JavaScript, lo que rompe el código en runtime con `ReferenceError`. Usa siempre `--extensions=php,module,inc,install,profile,theme` y `--ignore='*/js/*'` al llamar a phpcbf (y a phpcs por consistencia). Los JS se analizan/arreglan con ESLint, no con PHPCS. Ver el edge case completo en `references/edge-cases.md`.

## Checklist de auto-verificación (antes de entregar)

Verifica cada item leyendo el informe que has generado. Si algo falta, vuelve atrás antes de entregárselo al usuario.

- [ ] Primera línea del informe es exactamente `Español confirmado.`
- [ ] El informe está en `/Lint reviews/lint-review---.md` (verifica con `ls`)
- [ ] Las 4 fuentes aparecen en el informe (PHPStan, PHPCS, drupal-qa, drupal-security). Si alguna no se ejecutó, debe estar marcada como `❌ no ejecutada — `, no omitida silenciosamente.
- [ ] Cada hallazgo tiene `archivo:línea` con ruta relativa al proyecto (no rutas absolutas del contenedor DDEV).
- [ ] Sección "P0 bloqueantes" con acciones concretas. Si una P0 dice "mejorar X", reescríbela hasta que diga "añadir/eliminar/cambiar Y en `archivo:línea`".
- [ ] Tabla de resumen ejecutivo con conteos por fuente y total.
- [ ] Veredicto explícito al final del resumen (`APTO`, `APTO con correcciones menores`, `APTO con correcciones críticas`, `NO APTO`). Nada de "depende".
- [ ] Si hay hallazgos ALTO/CRÍTICO sin resolver, el veredicto NO es "APTO".
- [ ] Sección "Comandos de verificación" al final, con los comandos reales que se usaron (no genéricos).
- [ ] El módulo no ha sido modificado: `git status` sobre la carpeta del módulo no muestra cambios atribuibles a esta skill.
- [ ] La última línea de tu mensaje en chat es una pregunta al usuario (`arregla todo`/`solo crítico`/`déjalo así`), no un asume "ahora arreglo todo".

## Recovery — qué hacer si algo falla

| Síntoma | Acción |
|---|---|
| `references/*.md` no existe | Avisar al usuario, no inventar plantillas |
| DDEV no levantado | Pedir al usuario antes de `ddev start` |
| PHPStan/PHPCS no instalados | Pedir confirmación para instalar (`ddev composer require --dev ...`) |
| Agentes `drupal-qa` o `drupal-security` no disponibles | Continuar con las otras fuentes (PHPStan + PHPCS) y pedir al usuario que confirme si quiere review manual del modelo (más lento, menos rigor) |
| Una de las 4 fuentes falla en runtime | Continuar con las otras 3, marcar la fallida en el informe |
| No se puede crear la carpeta del IDE | Crear con `mkdir -p`; si falla, usar `docs/lint-reviews/` |
| Modo diff sin cambios en el módulo | Ofrecer cambiar a completo |
| Estás en `develop` (no hay rama feature) | Abortar con mensaje claro |
| Cliente no soporta tool calls paralelas | Avisar al usuario al inicio: la skill será 4x

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [j4rk0r](https://github.com/j4rk0r)
- **Source:** [j4rk0r/claude-skills](https://github.com/j4rk0r/claude-skills)
- **License:** MIT
- **Homepage:** https://claude-skills-omega.vercel.app

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-j4rk0r-claude-skills-lint-drupal-module
- Seller: https://agentstack.voostack.com/s/j4rk0r
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
