Hace un tiempo publiqué un artículo explicando cómo tenía montado un sistema para trabajar con OpenCode dentro de contenedores DDEV en mis proyectos Drupal. Varias personas me escribieron pidiéndome que lo compartiera. No pude. Tenía datos de clientes dentro, tareas específicas de proyectos concretos, referencias a repos privados, y no era tan fácil como copiar y pegar.
Luego vino el cambio de Anthropic: quitaron la integración de la API de Claude con OpenCode dentro de la suscripción Max. Si quería seguir usando Opus y Sonnet con plan Max me tocaba volver a Claude Code para esas tareas. Aproveché ese refactor obligado para reorganizar todo mi sistema DDEV de trabajo con IA, extraer la parte genérica, y hacerla pública.
Llevo mucho tiempo trabajando de forma parecida a esta, pero con todo montado de manera bastante rudimentaria. Crear un proyecto nuevo y dejarlo configurado con todas las herramientas me tardaba más de lo que me gustaba reconocer. Unificarlo y publicarlo me ha servido para limpiar la arquitectura de golpe y, de paso, dejarlo listo para que cualquier persona lo use.
El resultado es DDEV AI Workspace, un meta add-on que instala todo el stack con un comando, más seis add-ons sueltos por si solo quieres instalar uno de los componentes. En este artículo te cuento qué hay, cómo encaja todo y qué esperar de cada parte.
Qué instalas con un comando
Dentro del directorio de tu proyecto Drupal solo necesitas esto:
ddev add-on get trebormc/ddev-ai-workspace
ddev restart
A partir de ahí tienes siete add-ons montados y enlazados entre sí:
ddev-opencode: contenedor con OpenCode, para usar cualquier proveedor (Anthropic, OpenAI, OpenCode Zen o incluso modelos gratuitos).ddev-claude-code: contenedor con Claude Code, para cuando quieras tirar del plan Max de Anthropic.ddev-ralph: un orquestador autónomo que delega tareas a OpenCode o Claude Code y las ejecuta sin supervisión, pensado para runs nocturnos.ddev-beads: task tracker git-backed. Los agentes anotan ahí el estado de las tareas y puedes revisar el progreso.ddev-playwright-mcp: Chromium headless detrás de un endpoint MCP, para que los agentes hagan screenshots o pruebas visuales.ddev-agents-sync: el pegamento de los agentes. Sincroniza el repo de agentes, resuelve tokens de modelos y genera configuraciones específicas para cada CLI.ddev-ai-ssh: el pegamento de las comunicaciones. Instala sshd en el contenedorweby genera un par de claves SSH exclusivas del proyecto, para que el resto de contenedores puedan conectarse sin necesidad de tocar el socket de Docker del host.
Todos estos contenedores viven dentro de la red interna del proyecto DDEV. Se ven entre ellos, pueden comunicarse con el contenedor web de Drupal, pero no tienen acceso a tu máquina host. La IA opera dentro de un sandbox, no en tu sistema.
Por qué instalar los dos CLIs
Una pregunta que me hacen bastante: ¿para qué dos herramientas de IA si hacen básicamente lo mismo?
Pues precisamente porque no hacen lo mismo, y porque la respuesta corta es que he acabado necesitando los dos por un motivo más económico que técnico.
A día de hoy los modelos que mejor me funcionan para desarrollo Drupal son los de Anthropic: Opus y Sonnet. Y la forma más barata de usarlos, con bastante diferencia, es la suscripción Max. Pagar la API directa se dispara rápido en cuanto tiras un poco de los modelos grandes en sesiones largas.
Hasta hace no mucho podía usar mi suscripción Max de Anthropic dentro de OpenCode, así que tenía lo mejor de los dos mundos: la interfaz que prefiero y los modelos que me funcionan bien, sin pagar dos veces. Anthropic cerró ese acceso y ahora la suscripción solo se puede usar desde Claude Code. Si quería seguir aprovechándola sin pasar por caja con la API, me tocaba volver a Claude Code para las tareas que requieren Opus o Sonnet. Eso es lo que hago ahora.
A nivel personal, OpenCode me gusta bastante más. La interfaz me parece más pulida, el flujo de trabajo más flexible, y el sistema de agentes y subagentes me encaja mucho mejor con cómo pienso las tareas. Si Anthropic no hubiera cerrado la puerta, seguiría con un único CLI. Pero las cosas son como son, y Claude Code con suscripción Max sale mucho más a cuenta que pagar API, así que ahí lo tengo.
Para todo lo que no requiere Opus ni Sonnet uso OpenCode con otros proveedores. Está OpenCode Zen (bastante más barato que la API directa de Anthropic) y cualquier otro proveedor que quiera probar. Para tareas más mecánicas (generar tests, aplicar correcciones de PHPCS, explorar código) esto me funciona perfectamente, así que no vale la pena cargarle cada petición a la suscripción Max cuando hay opciones más baratas para lo mismo.
El propio OpenCode también trae acceso a modelos gratuitos. Son bastante limitados y se quedan cortos cuando quieres sacar trabajo de verdad, pero para juguetear o hacer pruebas puntuales cumplen. Ahora bien, ojo con esto: la mayoría de estos modelos gratuitos usan los datos que les envías para entrenar. Dependiendo del tipo de cliente con el que trabajes (una agencia con clientes grandes, código bajo NDA, datos sensibles), usarlos directamente en proyectos de cliente es mala idea. Yo los uso para pruebas propias o en proyectos donde el cliente ya lo sabe y le da igual. En cualquier otro caso, mejor tirar de un proveedor de pago con política de privacidad clara.
Si tu caso es distinto al mío (vas a pagar API directa, solo vas a usar proveedores que no sean Anthropic, o al revés, solo vas a usar la suscripción Max), puedes instalar únicamente el CLI que te haga falta y ahorrarte el otro.
Instalación modular, si tu flujo lo pide
El meta add-on es la opción que yo recomiendo, pero cada componente se puede instalar por separado si tu forma de trabajar lo requiere:
# Solo OpenCode (arrastra Playwright, Beads y Agents Sync)
ddev add-on get trebormc/ddev-opencode
# Solo Claude Code
ddev add-on get trebormc/ddev-claude-code
# Solo Ralph (necesita OpenCode o Claude Code ya instalados)
ddev add-on get trebormc/ddev-ralph
Dicho esto, mi recomendación sigue siendo tirar del meta add-on. Con un único comando lo tienes todo instalado y configurado, sin tener que preocuparte por dependencias ni por el orden de instalación. Si al final solo usas Claude Code, tendrás OpenCode arrancándose con cada ddev start sin que lo uses. Esto te consume algo de RAM (poca, pero no cero), así que tenlo en cuenta si trabajas con equipos ajustados o con varios proyectos DDEV levantados a la vez. A cambio, lo tienes ahí por si algún día te hace falta. Ir componente por componente es más engorroso de configurar y de mantener alineado cuando saque actualizaciones. Salvo que haya un motivo concreto y de peso para separarlo, un único comando es mucho más simple y menos trabajo.
Cómo encajan las piezas
El diagrama de arquitectura del repo lo explica bastante bien, pero resumiendo cómo se comunican los contenedores:
Los CLIs (OpenCode y Claude Code) hablan con el contenedor web de Drupal por SSH, usando la red interna de DDEV. Ahí ejecutan Drush, Composer, PHPUnit o cualquier comando que necesiten. Hablan con Playwright por HTTP/MCP para todo lo que sea navegador. Y delegan el tracking de tareas a Beads, también por SSH.
Al principio montaba esta comunicación con docker exec, pero eso implicaba dar acceso al socket de Docker del host a algunos contenedores, y me parecía un riesgo innecesario. Lo que habilita el esquema actual es el add-on ddev-ai-ssh: instala sshd en el contenedor web y genera un par de claves SSH exclusivas del proyecto. Así, por ejemplo, Ralph se conecta por SSH al contenedor de Claude Code para lanzarle una tarea, y Claude Code a su vez se conecta por SSH al contenedor web para ejecutar un Drush. Todo dentro del mismo perímetro, sin tocar el host.
Como las claves son exclusivas de cada proyecto, si tienes varios proyectos DDEV arrancados a la vez quedan aislados entre sí. Los contenedores del proyecto A no pueden hablar con los del proyecto B. Cada entorno es un sandbox cerrado, sin cruces.
Ralph es el que tiene el rol más interesante: no ejecuta IA él mismo, sino que lanza sesiones de OpenCode o Claude Code con un prompt muy estructurado y vigila que la tarea se complete. Está pensado para dejarlo corriendo una noche con una lista de issues en Beads y que por la mañana haya intentado resolverlas todas. Después de eso toca revisión humana, siempre.
Agents Sync es la pieza menos llamativa pero la más importante para el día a día. En cada ddev start clona o actualiza el repo de agentes, aplica envsubst sobre los tokens de modelos y genera dos carpetas distintas: una para OpenCode y otra para Claude Code, con los agentes y reglas ya formateados como espera cada herramienta. Eso me permite mantener un único set de agentes y que funcione en los dos CLIs sin duplicar nada.
El repositorio drupal-ai-agents
Dentro de todo el stack hay una pieza que no es un contenedor. Es drupal-ai-agents, un repositorio Git con los agentes, reglas y skills específicamente pensados para desarrollo Drupal 10/11. No se instala como add-on de DDEV: lo sincroniza automáticamente ddev-agents-sync en cada ddev start, descargando la versión más reciente y dejándola lista para que los CLIs la usen.
Esto es lo que hace que OpenCode o Claude Code, que por sí solos son herramientas genéricas, se comporten como CLIs especializados en Drupal desde el primer momento. Sin este repo, tendrías que configurar tú mismo agentes, reglas y skills específicos de Drupal en cada proyecto nuevo.
Contiene tres cosas.
Primero, 10 agentes especializados. Cada uno con un rol concreto: drupal-dev para backend, drupal-theme para frontend, drupal-test-generator para generar tests, code-review como quality gate, applier para aplicar cambios mecánicos con bloques SEARCH/REPLACE, ralph-planner para generar planes de trabajo, y unos cuantos más.
Segundo, 12 sets de reglas. Algunas globales, otras scopeadas por tipo de fichero. Por ejemplo, drupal-coding-standards.md se activa solo con *.php y obliga a strict types, indentación de 2 espacios, inyección de dependencias y cache metadata correcto. twig-patterns.md se activa con *.twig y recuerda lo básico: presentación solo, renderizar campos completos, cache bubbling, anti-patterns habituales. Hay otra regla explícita que bloquea que los agentes hagan commits o push. Los commits siempre los hago yo; esto es intencional y más adelante explico por qué.
Tercero, 24 skills reutilizables. Skills son recetas que los agentes invocan cuando las necesitan. Hay skills para cada tipo de test de Drupal (unit, kernel, functional, functionaljs, behat, playwright), para scaffolding de módulos, para config management, para debugging, para Drush, para performance audit, para profiling con Xdebug, y más.
Todo esto funciona con un sistema de tokens de modelos. En vez de hardcodear nombres de modelo concretos (el Opus de Anthropic, el último GPT de OpenAI, el modelo que toque en cada momento) en cada agente, uso tokens tipo ${MODEL_SMART}, ${MODEL_NORMAL}, ${MODEL_CHEAP}, ${MODEL_APPLIER}. En un único fichero (.env.agents) defino qué modelo real corresponde a cada token, para cada CLI por separado. Si mañana sale un modelo mejor o más barato, cambio una línea y todos los agentes se actualizan. Sin reescribir diez agentes uno por uno.
El otro truco útil es el fat frontmatter. Cada agente .md tiene un único frontmatter con los campos específicos de OpenCode y de Claude Code mezclados. El script de sync se encarga de generar dos versiones, una para cada CLI, con solo los campos que cada uno entiende.
El punto importante aquí no es el detalle técnico, es que me permite tener un único sitio de la verdad para cada agente, sin duplicar. Si mejoro una skill, la edito una vez y queda aplicada en los dos CLIs automáticamente. Si mañana aparece un CLI nuevo interesante, basta con ampliar el frontmatter y seguir trabajando. Para una agencia con un equipo donde unos prefieren Claude Code y otros OpenCode, o que se está planteando cambiar de herramienta en algún momento, este punto es clave: el repo de agentes no queda atado a una tecnología concreta. Si hay que cambiar de CLI, cambias de CLI, pero tu librería de agentes y skills sigue siendo la misma.
Tu propio repositorio de agentes
Los agentes, skills y reglas que vienen por defecto son los que uso yo. Y como vengo diciendo, reflejan mi forma de trabajar y los patrones de los proyectos Drupal en los que estoy metido. Soy consciente de que hay gente que trabaja distinto y que hay casos en los que los míos no van a encajar bien.
Piensa por ejemplo en alguien trabajando con versiones de Drupal más nuevas, o incluso con ramas de desarrollo que todavía no son estables. Los patrones y las APIs cambian, y tiene sentido mantener un repo de agentes específico para ese contexto. O piensa en una agencia con un equipo de varios desarrolladores: todos deberían trabajar con los mismos agentes y skills, y esos agentes deberían evolucionar con el equipo, no con lo que yo decida publicar en el mío.
Para esto hay una variable de entorno, AGENTS_REPOS, donde defines la lista de repositorios de los que se descargan los agentes y skills. Puedes usar solo los míos (que vienen por defecto), solo los tuyos, o combinar varios. Cuando combinas, los repos que van después sobrescriben a los anteriores si hay ficheros con el mismo nombre. Así puedes partir del mío como base y solo sustituir los agentes concretos que quieras cambiar, sin tener que mantener un fork completo.
La estructura del repo es la que está en drupal-ai-agents: puedes clonarlo y adaptarlo, o montar uno nuevo desde cero siguiendo la misma convención. Una agencia que monte su propio repo tiene control total: actualiza sus agentes, sus skills, sus reglas, y todo el equipo se sincroniza automáticamente en el siguiente ddev start de cada desarrollador.
A mí personalmente los míos me sirven para lo que hago, pero no tengo ninguna pretensión de que sean los correctos para todo el mundo. Esta es la vía para adaptarlo a tu caso.
Un caso de uso real
Un ejemplo de cómo uso todo esto en el día a día.
Abro Claude Code con ddev cc y empiezo contándole qué quiero implementar. No me quedo en una descripción rápida: intento documentar lo máximo posible cómo lo haría yo como programador, qué patrones uso habitualmente, qué dependencias quiero, cómo quiero que esté estructurado. Cuantos más detalles le doy al principio, menos correcciones tengo que hacer después.
A veces propone un planteamiento distinto al mío. Entonces toca discutir un rato. Si lo que propone tiene sentido y mejora lo que tenía pensado, cambio de idea. Si no, le explico por qué prefiero mi enfoque y seguimos. Este ida y vuelta antes de implementar nada me ahorra tener que revertir código más tarde.
Cuando ya tenemos claro cómo lo vamos a montar, le digo que implemente. A partir de ahí, Claude Code va invocando a los agentes que corresponden en cada momento: el de backend para servicios y módulos, el de frontend cuando toca Twig o CSS, el de revisión para validar lo que se ha escrito. Le digo también que no se olvide de generar los tests, lo que dispara al agente correspondiente para cada tipo de test que encaje con el código que acabamos de escribir.
Cuando ha terminado, en el working directory tengo todos los ficheros nuevos y modificados. Pero no hay commit. Esto es intencional y en la siguiente sección explico por qué.
Los commits los hago yo, siempre
Lo menciono arriba de pasada pero merece su propia sección, porque es una decisión consciente y no un descuido.
El repo de agentes tiene una regla que bloquea que ningún agente haga git commit ni git push. Todo lo que hace la IA se queda en el working directory hasta que yo revise el diff con mis ojos y haga yo mismo el commit. Da igual si estoy trabajando en modo interactivo con OpenCode o Claude Code, o si estoy dejando a Ralph corriendo de forma autónoma: los commits no se automatizan.
La razón es que, a día de hoy, no me fío lo suficiente de lo que me devuelve la IA. Me he encontrado en muchas ocasiones con implementaciones que no son correctas o que no son como yo las haría. Le explico cómo lo haría yo, la IA me da la razón (casi siempre, por cómo están entrenados los modelos), me modifica el código, y a veces se carga algo que estaba bien. Si eso se hubiera commiteado automáticamente, el daño ya estaría hecho.
Revisar antes de commitear me fuerza a leer lo que ha producido la IA, a pensar si me convence y a intervenir si hace falta. Es el paso humano que no pienso ceder todavía. Quizá dentro de un par de años, con modelos más fiables, lo replantee. Hoy no.
Qué esperar (y qué no)
Sin vender esto como una varita mágica. Hay varias cosas que conviene tener en mente.
Está en desarrollo activo. Lo uso a diario en mis proyectos y lo voy afinando con lo que me sale. Es muy posible que queden aristas que todavía no he detectado. Si te cruzas con alguna, escríbeme por LinkedIn o abre un issue en el repo correspondiente y cuando pueda lo miro.
Playwright es el punto más frágil. Al reorganizar la arquitectura para hacerla pública se me han roto cosas que antes me funcionaban en mi setup privado, y Playwright es de lejos donde más problemas he tenido. A veces se desconecta y toca reiniciar DDEV para que vuelva. Tuve que pelearme con temas de permisos para que pudiera generar capturas y dejarlas accesibles al proyecto. Y la última actualización de Playwright cambió algunas cosas que he ido arreglando sobre la marcha. Probablemente aparezcan más problemas de este tipo.
Los modelos siguen alucinando. Si dejas a Ralph corriendo una noche, por la mañana te puedes encontrar con cambios que compilan pero hacen cosas distintas a lo que pediste. Yo lo uso para tareas acotadas y repetitivas (arreglar warnings de PHPStan en una lista de ficheros, por ejemplo), no para decisiones de arquitectura. Y como decía, nada se commitea sin que yo lo haya revisado.
Los tokens de modelos hay que calibrarlos. El sistema es cómodo pero hay que probar cada modelo nuevo antes de asignarle el token SMART. Un modelo que en benchmarks generales pinta estupendo a veces genera PHP que no compila. Lo que marca la diferencia en agentes Drupal no es el benchmark general, es cómo maneja tipado estricto, convenciones de inyección de dependencias y los patrones concretos que usamos.
Está diseñado por mí y para cómo yo trabajo. He intentado hacerlo genérico, pero muchas decisiones (prioridad del módulo Audit, patrones concretos de testing, reglas de commit) reflejan mi flujo. Si el tuyo es distinto, recuerda que puedes usar tu propio repositorio de agentes como expliqué antes.
Dónde encontrarlo
Todo está en GitHub bajo mi usuario y con licencia Apache-2.0:
- ddev-ai-workspace: el meta add-on que instala todo de golpe.
- drupal-ai-agents: el repo de configuración con los agentes, reglas y skills.
- Los add-ons individuales también tienen su propio README con detalles específicos.
Si lo que hay aquí te parece útil, te agradezco mucho una estrella en los repos. Me sirve para tener señal real de si este trabajo está llegando a gente y gustando, y ayuda a que más devs Drupal lo encuentren en GitHub. Creo que esto es una herramienta útil para el sector, y cuanta más gente la use en proyectos reales, mejor va a funcionar y más vamos a ganar como comunidad.
Feedback bienvenido. Si lo pruebas y algo no te funciona, si detectas un comportamiento raro, o si echas en falta algún skill o agente para un caso de uso Drupal concreto, contáctame por LinkedIn o abre un issue en el repo que corresponda. Como digo, esto lo tengo vivo y lo voy afinando con lo que me va saliendo. Cuanto más feedback real, mejor va a funcionar.