Arnés de QA y despliegue para la línea de comandos, y servidor MCP para agentes. Un mismo ciclo —probar en local, desplegar, pasar la batería contra lo desplegado, dar un veredicto— sea cual sea el proyecto.
Lo que lo distingue de un script de CI: no se fía de git. Le pregunta al destino qué tiene de verdad y detecta deriva — código que alguien editó directamente en el entorno y que un despliegue borraría sin avisar.
tipo en .qabot.json |
Para qué | Despliega con |
|---|---|---|
appsscript (por defecto) |
Google Apps Script | clasp |
cloudrun |
web sobre Cloud Run | gcloud run deploy |
generico |
cualquier otro proyecto | lo que declare el proyecto |
Los comandos son los mismos (estado, cambios, local, desplegar,
remoto, ciclo, doctor); cada tipo los implementa con sus piezas.
Lo que sigue describe el tipo appsscript; para Cloud Run y para cualquier
otro proyecto, ver el final.
Módulo independiente: se instala dentro del proyecto destino con
qabot install local, y a partir de ahí ese proyecto lleva sus propias
pruebas en su propio repositorio.
Las pruebas las escribe un modelo local (gpt-oss:20b vía Ollama): el
código y los diffs no salen de la máquina.
Necesitas bash, python3 y node (18 o superior). Nada más: qabot no tiene
dependencias de npm y no trae node_modules.
git clone https://github.com/FlEtsv/qabot.git
ln -sf "$PWD/qabot/bin/qabot" ~/.local/bin/qabot # una vezComprueba que responde:
qabot ayudaDespués, en cada proyecto. Si no es Apps Script ni Cloud Run, empieza por la detección:
cd /mi/proyecto
qabot detectar # mira qué hay y propone
qabot detectar --escribir # lo guarda en .qabot.json
qabot doctor # qué falta todavíaPara Apps Script:
cd /mi/proyecto && qabot install localCopia dentro del proyecto:
| Archivo | De quién es |
|---|---|
src/QA.js |
del módulo — se sobrescribe al reinstalar (así se actualiza) |
tools/qa_local.js, tools/qa_generar.py |
del módulo — se sobrescriben |
src/QA_Proyecto.js |
tuyo — solo se crea si no existe |
src/QA_Suites.js |
tuyo — solo se crea si no existe |
- Adapta
src/QA_Proyecto.js: entornos, qué vigilar, guardianes. - Script Properties del proyecto Apps Script:
QA_HABILITADO=true,QA_ENTORNO=STAGING,QA_TOKEN=<algo largo>. - En tu
doGet:if (params.vista === 'qa') return manejarVistaQA_(params); - Rellena
.qabot.json(no se sube a git: lleva tokens).
qabot no se fía solo de git: le pregunta al proyecto Apps Script qué tiene
de verdad, usando la Apps Script API con las credenciales que ya dejó
clasp login.
qabot estado # metadatos, versiones y despliegues reales
qabot cambios # remoto vs tu copia, archivo a archivo y función a funcióncambios detecta deriva: código que está en el proyecto y no en tu copia
—alguien editó en el editor web— que un clasp push --force borraría sin
avisar. Por eso qabot desplegar lo comprueba antes de empujar y se detiene
si encuentra deriva.
qabot doctor [entorno]Comprueba node, la sesión de clasp (renovando el token de verdad, no mirando si el archivo existe), Ollama, la configuración del entorno, el acceso por API y el endpoint de QA. Sabe distinguir de quién es cada problema —clasp, gcloud, una Script Property, el acceso del despliegue— y en un terminal pregunta lo que falte y lo guarda. En modo no interactivo (CI, un agente) no pregunta: informa y sale con código 1.
qabot generar HEAD~1 # el modelo escribe pruebas para lo que cambiaste
qabot revisar # el modelo critica sus propias pruebas
qabot local # segundos, sin desplegar nada
qabot ciclo # local → staging → batería real → veredicto
qabot desplegar produccion # solo con lo anterior en verde (pide confirmación)- Política por entorno y por servicio, aplicada por código. Cada entorno declara en qué puede escribir (p. ej. producción: Drive y Sheets sí, ERP no). Lo no permitido se bloquea de verdad durante la suite: la llamada lanza excepción. Un entorno no declarado no puede escribir en nada.
- Todo lo que se crea se deshace, en orden inverso, pasen o fallen las pruebas. Lo que no se pueda deshacer se reporta como huérfano.
- Se verifica que el entorno queda como estaba: huella de hojas y carpetas antes y después. "Las pruebas pasan" y "el entorno quedó bien" son dos preguntas distintas y el informe responde a las dos.
pruebas/correr.shqabot vigila el código de otros proyectos; esto vigila el suyo. Comprueba los mecanismos que, si se rompieran en silencio, dejarían el arnés dando falsa seguridad: que se niega a desplegar con deriva, que la configuración de un entorno no viaja a otro, que el sello de versión no miente, que el guardián bloquea de verdad y que lo creado se deshace en orden inverso.
No tocan ningún proyecto ni ninguna cuenta: cada caso monta un proyecto de
mentira en un directorio temporal, con dobles de clasp y de la Apps Script
API. Dos bloques: pruebas/arnes.js (el núcleo, en Node) y pruebas/cli.sh
(el comando, en bash).
cd /mi/proyecto-web && qabot install cloudrunCopia tools/qa_http.mjs y tools/qa_version.mjs (del módulo, se
sobrescriben) y crea qa/probes.mjs (tuyo). Rellena .qabot.json:
{
"tipo": "cloudrun",
"predeterminado": "produccion",
"entornos": {
"produccion": {
"servicio": "mi-servicio",
"region": "europe-west1",
"proyecto": "mi-proyecto-gcp"
}
}
}| Apps Script | Cloud Run | |
|---|---|---|
| Desplegar | clasp push |
gcloud run deploy --source |
| Deriva | proyecto remoto vs copia local | SHA desplegado vs HEAD local |
| Batería | doGet?vista=qa, dentro del proyecto |
sondas HTTP, desde fuera |
La diferencia de fondo está en la batería. En Apps Script el código de prueba corre dentro del proyecto, con sus permisos, y por eso necesita un token y tiene que deshacer lo que crea. En Cloud Run corre fuera, contra la URL pública y sin sesión: no puede inspeccionar el estado interno, pero comprueba exactamente lo que ve un desconocido, que es donde un fallo de autorización se convierte en una fuga. No hace falta abrir ningún endpoint de QA, y la batería no necesita deshacer nada porque solo lee.
qabot desplegar deja en la variable BUILD_INFO del servicio qué commit
subió, si el árbol estaba sucio y los últimos diez cambios. qabot cambios la
lee y la compara con tu HEAD:
- lo desplegado es tu HEAD → al día;
- tu copia va por delante → te dice cuántos commits y cuáles;
- lo desplegado no es antecesor de tu HEAD → alguien desplegó desde otra rama o desde otra máquina.
Esto importa porque gcloud run deploy --source sube el directorio de
trabajo, no el commit: sin la ficha, un despliegue hecho con cambios sin
commitear es irreproducible y nada lo delata.
Apps Script y Cloud Run los sabe de memoria. Para todo lo demás qabot no adivina: mira el repositorio, propone lo que puede deducir de ficheros reales, y deja por escrito lo que no sabe.
qabot detectarReconoce Node (npm, pnpm, yarn, bun), Python, Go, Rust, Maven, Gradle,
Composer, Ruby y Makefile, y deduce los comandos solo de lo que el proyecto
declara: un script de package.json, un objetivo del Makefile. Lo que no
esté declarado no se propone — se pregunta.
Esa es la regla que lo mantiene honesto. Un comando inventado haría que el arnés dijera "verde" sobre algo que nadie ha ejecutado, y eso es peor que no tener arnés.
{
"tipo": "generico",
"comandos": {
"local": "npm test",
"construir": "npm run build",
"bateria": "npm run e2e",
"desplegar": { "staging": "make deploy staging" }
}
}Solo local es obligatorio. Lo que falte se salta diciéndolo, en vez de
fingir que ha ido bien.
Lo que este tipo no puede hacer, y lo dice en su salida: un proyecto
genérico no expone qué tiene desplegado, así que la deriva contra el entorno
real no se comprueba. Eso solo lo saben appsscript y cloudrun, que sí
pueden preguntárselo al destino.
El ciclo completo sobre un proyecto genérico:
Y cuando algo falta, doctor dice qué y cómo conseguirlo:
qabot se expone a cualquier agente que hable MCP — Claude Code, Codex, el que sea. No hay nada específico de un cliente.
claude mcp add --scope user qabot -- node /ruta/a/qabot/mcp/servidor.mjsPara otros agentes, la entrada equivalente en su configuración:
{
"mcpServers": {
"qabot": { "command": "node", "args": ["/ruta/a/qabot/mcp/servidor.mjs"] }
}
}Nueve herramientas: qabot_detectar, qabot_configurar, qabot_estado,
qabot_doctor, qabot_cambios, qabot_local, qabot_bateria,
qabot_ciclo, qabot_desplegar.
El servidor habla JSON-RPC por stdio a pelo, sin SDK y sin dependencias:
meterle un árbol de node_modules a una herramienta de la que cuelgan
proyectos reales es más huella de la que justifica exponer nueve comandos.
qabot_detectar devuelve, además de su propuesta, una lista preguntas. Si
no está vacía, el agente tiene instrucciones de trasladárselas al usuario con
sus palabras y llamar después a qabot_configurar con las respuestas.
Funciona igual en cualquier cliente porque la duda viaja como dato en la respuesta, no como una capacidad del agente:
agente → qabot_detectar
qabot → "reconozco Node; no encuentro batería ni despliegue" + 2 preguntas
agente → se las hace al usuario
usuario → contesta
agente → qabot_configurar {...} → .qabot.json escrito
Code Timeline lleva el historial revisable de los cambios; qabot los prueba y los despliega. Juntos cierran el ciclo: decides, se registra, se prueba, se despliega, y el resultado vuelve al historial.
Guía paso a paso de la instalación conjunta: INSTALACION-CONJUNTA.md.
Si code-timeline está en el PATH y el repositorio está vinculado allí, cada
qabot ciclo deja constancia del veredicto. No es una dependencia: si no
está instalado, qabot no se entera ni cambia de comportamiento. Ninguno de los
dos necesita al otro para funcionar.
code-timeline qa --listar # los ciclos registrados de este proyectoqabot generar y qabot revisar (las pruebas que escribe el modelo local)
solo funcionan en proyectos Apps Script. En Cloud Run las sondas se escriben a
mano en qa/probes.mjs.
qabot generar y qabot revisar tampoco aplican al tipo generico: ahí las
pruebas son las del propio proyecto, no unas que escriba un modelo.
Apache 2.0 + Commons Clause — ver LICENSE. Úsalo para lo que quieras, también en tu empresa; lo único que no puedes es venderlo. No es open source según la OSI, porque restringe el uso.


