Skip to content

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):

  1. Upstream — <configDir>/providers/.upstream/ — el paquete oficial, sincronizado automáticamente. No lo edites a mano; se sobrescribe.
  2. Fuentes externas — <configDir>/external/<source-name>/ — fuentes git de terceros que registras desde el panel Sources del dashboard.
  3. 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 (normal o no-upstream) y Provider directory, luego Apply & reload. no-upstream deshabilita por completo el paquete upstream auto-sincronizado y sirve solo tu propio directorio más lo que haya en external/.
  • Archivo de configuración: define providerDir (y opcionalmente providerSourceMode) 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.log sin 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:

bash
adhdev provider init my-cli --dir ./my-cli

Esto 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 ​

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 ​

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ón matches se ejecuta contra todo el buffer de pantalla, no línea por línea, y el motor lo compila sin la bandera m por 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 sin m justamente 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 un provider.v1.json (o el provider.json heredado) contra el mismo JSON Schema v1 que usa el daemon en tiempo de carga, e informa a qué nivel pertenece. Para un manifiesto cli, 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 manifiestos acp no tienen el concepto de spec y se saltan este paso. Para el ejemplo de arriba imprime tier: extended-legacy y python-repl@unversioned — ambos son normales e inofensivos:
    • extended-legacy simplemente significa "ninguno de los otros dos niveles": extended significa que el manifiesto declara un bloque overrides (hooks JS personalizados), declarative-only significa que declara un bloque tui. Un manifiesto puramente enrutado por spec como este — sin overrides, sin tui — cae en extended-legacy por eliminación. No significa que falte algo.
    • unversioned simplemente significa que el manifiesto no tiene un campo providerVersion. El schema no lo exige y el loader tampoco depende de él para que un proveedor CLI cargue o funcione — existe solo para que provider list/validate tengan 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 --json para 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 el PATH. 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 con Provider is disabled on this machine si 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 ​

La documentación de la nube alojada está aquí. La documentación de código abierto y autoalojada está en el repositorio OSS.