Proveedores personalizados
La pestaña Providers del panel muestra lo que ADHDev puede detectar y controlar en una máquina — no es un lugar para crear un proveedor nuevo desde cero. Para añadir soporte a un CLI, IDE o agente que ADHDev aún no conoce, escribes un pequeño archivo JSON (un "manifiesto de proveedor") y lo colocas en una carpeta que el daemon ya vigila. Esta página recorre ese flujo de principio a fin.
Qué es un proveedor
ADHDev tiene cuatro categorías de proveedor:
cli— un agente de terminal controlado mediante un PTY (pseudo terminal), como Claude Code o Codex CLI. El daemon escribe en la terminal y vuelve a leer la pantalla.acp— un agente que habla el Agent Client Protocol sobre stdio (mensajes estructurados, no lectura de pantalla).ide— un editor de código controlado por CDP (Cursor, forks de VS Code, etc.), inspeccionado y controlado vía Chrome DevTools Protocol.extension— un webview de extensión de IDE controlado por CDP (Cline, Roo Code, etc.).
En la práctica, cli es la categoría que vale la pena escribir a mano. Un proveedor CLI es un manifiesto JSON más una especificación JSON de máquina de estados que describe cómo reconocer en pantalla "generando" vs. "inactivo" vs. "esperando aprobación" — sin necesidad de código.
Los proveedores ide y extension son otra historia: necesitan scripts de automatización CDP reales (JavaScript que se ejecuta contra la conexión DevTools del IDE para abrir paneles, leer el chat, enviar mensajes, etc.). Eso es un esfuerzo bastante mayor que describir los estados de pantalla de una terminal, y queda fuera del alcance de esta página — consulta las referencias de la guía del Provider SDK enlazada más abajo si aun así quieres ir por ahí. Los proveedores acp también son viables a mano (no requieren lectura de pantalla de un PTY) pero se añaden con menos frecuencia que un CLI.
El resto de esta página trata sobre proveedores CLI.
Dónde van los archivos
El daemon carga proveedores desde un pequeño conjunto de directorios, en este orden de precedencia (lo posterior gana sobre lo anterior para el mismo tipo de proveedor):
- Upstream —
<configDir>/providers/.upstream/— el paquete oficial, sincronizado automáticamente. No lo edites a mano; se sobrescribe. - Fuentes externas —
<configDir>/external/<source-name>/— fuentes git de terceros que registras desde el panel Sources del dashboard. - Anulaciones de usuario —
<configDir>/providers/(todo excepto.upstream/) — tus propios manifiestos personalizados o de anulación. Esto siempre gana. En una instalación normal<configDir>es~/.adhdev, así que esto es~/.adhdev/providers/.
Cada proveedor vive en:
<root>/<category>/<type>/
provider.v1.json ← manifiesto (obligatorio)
specs/
<version>.json ← especificación de máquina de estados FSM (obligatoria para cli)Con colocar un cli/<type>/provider.v1.json en ~/.adhdev/providers/ es suficiente — el daemon no necesita que lo registres en ningún otro sitio.
Si no aparece nada, comprueba si ya hay un providerDir configurado. Un providerDir explícito (definido desde el panel Sources o la configuración — ver "Apuntar el daemon a otra carpeta" más abajo) reemplaza la carpeta por defecto ~/.adhdev/providers/ como carpeta de anulaciones de usuario; no se suma a ella. El propio log del daemon registra esto como userDirSource: "explicit". Si una máquina ya tiene uno configurado, tu nuevo manifiesto debe ir en esa carpeta en lugar de ~/.adhdev/providers/, y la recarga en caliente solo vigila esa única carpeta activa (ver "Cómo saber qué carpeta está activa" más abajo).
Qué significa exactamente "anular upstream"
Si creas un proveedor cuyo type coincide con uno que el daemon ya distribuye (digamos, un claude-cli ajustado), tu copia en ~/.adhdev/providers/cli/claude-cli/ se carga en lugar de la de upstream. El daemon lo registra explícitamente en el log (⚠ OVERRIDES upstream), así que es visible cuando ocurre. Esta es también la forma de parchear un solo campo de un proveedor integrado sin esperar a una corrección de upstream — copia el directorio completo del proveedor a tu carpeta de usuario y edítalo ahí.
Apuntar el daemon a otra carpeta
Por defecto, el daemon solo vigila ~/.adhdev/providers/ para las anulaciones de usuario. Si quieres mantener tus proveedores personalizados en otro lugar (por ejemplo, un checkout de git que estás editando activamente), define un directorio de proveedor explícito:
- Dashboard: página Machine → pestaña Providers → Advanced → configura Provider source mode (
normalono-upstream) y Provider directory, luego Apply & reload.no-upstreamdeshabilita por completo el paquete upstream auto-sincronizado y sirve solo tu propio directorio más lo que haya enexternal/. - Archivo de configuración: define
providerDir(y opcionalmenteproviderSourceMode) en la configuración del daemon.
En cualquiera de los dos casos, aplicar el cambio invoca el comando set_provider_source_config del daemon, que recarga el mapa de proveedores inmediatamente — sin necesidad de reiniciar el daemon.
Cómo saber qué carpeta está activa
Como un providerDir explícito reemplaza el valor por defecto en lugar de sumarse a él, conviene confirmar qué carpeta está vigilando realmente el daemon antes de investigar por qué no carga un manifiesto:
- Dashboard: página Machine → pestaña Providers → Advanced muestra el valor actual de Provider directory (en blanco significa el valor por defecto,
~/.adhdev/providers/). - Log del daemon: busca la línea
Hot-reload watcher active: <dir>—<dir>es la carpeta exacta que se está vigilando. El log del daemon vive en~/.adhdev/logs/daemon-YYYY-MM-DD.logsin importar el tipo de instalación (daemon cloud, standalone o instancia preview).
Recarga en caliente para la carpeta de anulaciones de usuario
Mientras el daemon está en ejecución, vigila tu única carpeta de anulaciones de usuario (~/.adhdev/providers/, o lo que hayas puesto en providerDir) en busca de cambios en archivos .js y .json — solo hay una carpeta de anulaciones de usuario activa a la vez, nunca las dos. Editar un manifiesto ahí se recoge en unos 300ms — sin reinicio, sin recarga manual. Esta vigilancia no cubre .upstream/ ni el árbol de fuentes external/; los cambios ahí necesitan adhdev provider reload (o una actualización del dashboard) para surtir efecto.
Inicio rápido: adhdev provider init
La forma más rápida de tener los dos archivos de abajo en disco es dejar que la CLI los genere:
adhdev provider init my-cli --dir ./my-cliEsto genera provider.v1.json + specs/1.0.json con la forma v1 actual — la misma que esta página recorre a mano a continuación —, con el nombre del binario derivado de <type> (quitando un -cli/-acp final, o pasando --binary explícitamente) y expresiones regulares de relleno en la spec que todavía tienes que sustituir por patrones que realmente coincidan con la salida en pantalla de tu comando objetivo. adhdev provider init my-acp --category acp --dir ./my-acp hace lo mismo para un proveedor ACP declarativo (un único provider.v1.json, sin archivo de spec — ver "Qué es un proveedor" arriba). init rechaza --category ide y --category extension: esos necesitan scripts de automatización CDP escritos a mano y específicos de un IDE/webview, así que no existe una plantilla genérica que produjera algo que realmente funcione.
Ejecuta adhdev provider validate ./my-cli justo después — fallará en las expresiones regulares de relleno (nunca coinciden), que es justo la idea: te dice exactamente qué sigue siendo un stub frente a lo que el schema ya acepta.
Un proveedor CLI mínimo y funcional
Los proveedores CLI se enrutan a través de un motor declarativo de máquina de estados finita (la "especificación" o "spec"). Una spec es obligatoria — el motor CLI antiguo basado en scripts fue eliminado, y un proveedor CLI sin una spec resoluble ahora falla al lanzarse con un error explícito en lugar de recurrir a algo más débil. Así que un proveedor mínimo son dos archivos: el manifiesto, y la spec mínima válida. (Esta es la misma forma que genera adhdev provider init arriba — sigue leyendo para ver cómo funciona cada pieza si estás editando los archivos generados o escribiéndolos a mano.)
Elegir el comando objetivo
Para un manifiesto que realmente puedas probar en vivo, elige un comando interactivo real, inofensivo y ya instalado. python3 -q es una buena opción: viene prácticamente en cualquier máquina macOS y Linux, arranca en modo "silencioso" (se salta el banner de versión, así que hay menos que comparar), y su prompt >>> es un marcador simple y estable para "inactivo" que no depende de la configuración del shell como sí lo hace un prompt bash crudo (PS1).
provider.v1.json
{
"$schema": "https://registry.adhf.dev/schemas/v1/cli/provider.schema.json",
"type": "python-repl",
"name": "Python REPL",
"category": "cli",
"binary": "python3",
"spawn": {
"command": "python3",
"args": ["-q"],
"shell": false
},
"compatibility": [
{ "ideVersion": ">=0.0.0", "spec": "specs/1.0.json" }
]
}Solo los campos que el schema realmente exige (type, name, category, binary, spawn) más compatibility, que es cómo el loader encuentra el archivo de spec de abajo.
specs/1.0.json
{
"$schema": "adhdev:cli/spec@4",
"id": "python-repl",
"name": "Python REPL",
"binary": "python3",
"send_message": {
"submit_key": "\r"
},
"states": [
{ "id": "starting", "label": "Starting", "initial": true, "status": "idle" },
{ "id": "idle", "label": "Ready", "status": "idle" },
{ "id": "busy", "label": "Evaluating", "status": "generating" }
],
"transitions": [
{
"label": "startup → idle",
"from": "starting",
"to": "idle",
"when": { "matches": ">>>\\s*(?=\\n|$)" }
},
{
"label": "idle → busy",
"from": "idle",
"to": "busy",
"min_hold_ms": 200,
"when": { "not": { "matches": ">>>\\s*(?=\\n|$)" } }
},
{
"label": "busy → idle",
"from": "busy",
"to": "idle",
"min_hold_ms": 300,
"when": {
"all": [
{ "matches": ">>>\\s*(?=\\n|$)" },
{ "stable_ms": 500 }
]
}
}
]
}Esto es el mínimo real que exige el validador de FSM: id, binary, send_message.submit_key, un states[] no vacío con exactamente un estado initial: true, y un array transitions[]. Todo lo demás que hay en una spec de proveedor real (detección de modales de aprobación, ventanas de sección, historial de chat nativo, modos de auto-aprobación, etc.) se añade sobre esta forma base — consulta las specs integradas bajo adhdev-providers/cli/*/specs/ para ver ejemplos más completos una vez que este funcione.
¿Por qué
(?=\n|$)y no un$a secas? Un patrónmatchesse ejecuta contra todo el buffer de pantalla, no línea por línea, y el motor lo compila sin la banderampor defecto — así que un$sin escapar se ancla al final de toda la pantalla, no al final de la línea del prompt, y silenciosamente nunca coincidiría mientras haya algo debajo del prompt. El linter de la spec incluye en su lista blanca un par de modismos de límite de línea que funcionan sinmjustamente por esto ((?=\n|$)para fin de línea,(?:^|\n)para inicio de línea); usar uno de esos en lugar de un ancla a secas es lo que hace que este patrón coincida de forma fiable con la línea del prompt.
Coloca ambos archivos en ~/.adhdev/providers/cli/python-repl/provider.v1.json y ~/.adhdev/providers/cli/python-repl/specs/1.0.json, habilita primero el proveedor (adhdev provider enable python-repl o el interruptor de Machine Providers del dashboard — la propia comprobación de detección del dashboard rechaza un proveedor deshabilitado, ver más abajo), y luego lanza una sesión contra él. Como python3 ya está instalado, no hay nada más que configurar antes de probarlo.
Los archivos del ejemplo de arriba también están guardados juntos, listos para copiar tal cual, en custom-provider-example/ junto a la fuente de esta guía.
Validar un proveedor
adhdev provider validate <path>comprueba unprovider.v1.json(o elprovider.jsonheredado) contra el mismo JSON Schema v1 que usa el daemon en tiempo de carga, e informa a qué nivel pertenece. Para un manifiestocli, también resuelve la spec de la misma forma que lo hace el daemon al lanzar (compatibility[].spec→specs/default.json→spec.json) y la pasa por el mismo validador FSM que usa el daemon — un manifiesto que pasa el schema pero no tiene una spec resoluble, o una spec con una máquina de estados mal formada, se reporta aquí como inválido en lugar de fallar solo más tarde al lanzar (formatNoResolvableSpecError). Los manifiestosacpno tienen el concepto de spec y se saltan este paso. Para el ejemplo de arriba imprimetier: extended-legacyypython-repl@unversioned— ambos son normales e inofensivos:extended-legacysimplemente significa "ninguno de los otros dos niveles":extendedsignifica que el manifiesto declara un bloqueoverrides(hooks JS personalizados),declarative-onlysignifica que declara un bloquetui. Un manifiesto puramente enrutado por spec como este — sinoverrides, sintui— cae enextended-legacypor eliminación. No significa que falte algo.unversionedsimplemente significa que el manifiesto no tiene un campoproviderVersion. El schema no lo exige y el loader tampoco depende de él para que un proveedor CLI cargue o funcione — existe solo para queprovider list/validatetengan algo que imprimir. Añade un campo"providerVersion": "1.0.0"si quieres que se muestre un valor real, pero para un manifiesto como este es puramente cosmético.
adhdev provider list(añade--jsonpara salida legible por máquina) muestra todos los proveedores que el daemon tiene actualmente cargados, de todos los directorios fuente, con el pin de versión de la spec.adhdev provider detect [type]comprueba si el binario subyacente realmente está instalado y en elPATH. Ejecutado desde la CLI, esto omite deliberadamente la comprobación de habilitación, así que funciona incluso antes de habilitar el proveedor. El equivalente del dashboard/daemon (detect_provider) es más estricto: rechaza conProvider is disabled on this machinesi el proveedor aún no está habilitado. Así que habilita el proveedor primero si vas a comprobar la detección desde el dashboard en lugar de la CLI.- Editar el manifiesto en tu carpeta de anulaciones de usuario mientras el daemon está en ejecución dispara el vigilante de recarga en caliente descrito arriba — revisa el log del daemon en busca de
File changed: provider.v1.json, reloading...y cualquier advertencia de validación⚠.
No existe un flujo de "fix" o auto-reparación en el dashboard para un proveedor escrito a mano y fuera del árbol como este — esa herramienta apunta al diseño de scripts del propio paquete de proveedores integrado, no a manifiestos personalizados que mantienes tú mismo.
Compartir un proveedor
Si quieres que tu proveedor pase a formar parte del conjunto integrado que todo el mundo recibe automáticamente, contribúyelo upstream a github.com/vilmire/adhdev-providers — el mismo repositorio desde el que se construye el paquete .upstream/ del daemon. Abre un pull request añadiendo tu directorio cli/<type>/ siguiendo la guía de contribución del propio repositorio. Ten en cuenta que entrar ahí hace que un proveedor forme parte del inventario integrado; no lo convierte automáticamente en un proveedor verificado — consulta Problemas de proveedor para ver la diferencia.
Hasta entonces (o en su lugar), un directorio de proveedor es solo una carpeta — puedes ponerlo bajo control de versiones, compartirlo como un zip, o apuntar el providerDir de un compañero al mismo checkout de git.
Documentación relacionada
- Problemas de proveedor — qué hacer cuando un proveedor integrado se comporta mal.
- Agentes CLI — usar los agentes CLI que ADHDev ya soporta.
- Compatibilidad y advertencias
