@badgie/cli
v1.1.1
Published
The official Badgie CLI — operate your sports school, club or academy from the terminal. A terminal harness for humans and AI agents, powered by the Badgie MCP.
Maintainers
Readme
Badgie CLI
Opera tu escuela, club o academia deportiva desde la terminal. Para humanos y para agentes.
Badgie CLI es el harness oficial de línea de comandos de Badgie, la plataforma de gestión deportiva. Es un cliente del Badgie MCP (el servidor MCP oficial de Badgie), así que reutiliza todo lo que ya conoces: tu cuenta, tu rol y el catálogo completo de acciones — alumnos, insignias, clases, finanzas, reservas, torneos, comunicaciones, golf y mucho más.
Un binario, dos modos:
- Modo interactivo (REPL) — escribe
badgiey opera tu escuela conversando con la terminal. - Modo comando —
badgie run <accion> --jsonpara scripts, CI y agentes IA.
Instalación
npm i -g @badgie/cliY arranca:
badgieRequiere Node.js ≥ 18.
Quickstart
1. Inicia sesión (abre el navegador — OAuth 2.1 con tu cuenta Badgie):
badgie login2. Comprueba quién eres (rol + escuela):
badgie whoami3. Mira qué puedes hacer (el catálogo se filtra por tu rol):
badgie tools4. Ejecuta acciones:
# Consulta genérica de datos de tu escuela (~346 tablas bajo tu RLS)
badgie run query_data table=badge limit=5
# Argumentos como pares key=value (con dot-notation para anidar)…
badgie run get_top_debtors limit=10
# …o el payload completo en JSON con --args (ideal para acciones anidadas)
badgie run students.set_avatar --args '{"action_type":"students.set_avatar","payload":{"student_id":"…","avatar":"adventurer"}}'5. O simplemente entra al REPL y trabaja en conversación:
badgie ★ Badgie — tu escuela desde la terminal
Escuela Ejemplo · owner · [email protected]
badgie> tools
badgie> run query_data table=student limit=10
badgie> run propose_action --args '{"action_type":"communications.send","payload":{…},"summary":"Recordatorio cuota julio"}'Las escrituras sensibles no se ejecutan directamente: el CLI las propone y el humano las aprueba en el dashboard de Badgie (/dashboard/agent). Human-in-the-loop de serie.
Comandos
| Comando | Qué hace |
|---------|----------|
| badgie | REPL interactivo (banner + sesión conversacional) |
| badgie login | Autoriza el CLI con tu cuenta Badgie (navegador, OAuth 2.1 + PKCE) |
| badgie logout | Cierra la sesión local |
| badgie whoami | Tu identidad: rol, escuela, email |
| badgie tools | Lista las acciones disponibles para tu rol |
| badgie run <accion> [k=v…] [--args '<json>'] [--json] | Ejecuta o propone una acción |
| badgie update [--check] [--json] [--channel <tag>] | Actualiza el CLI a la última versión publicada |
Argumentos de run:
- Pares
key=value— se coercionan a number/boolean/null automáticamente y admiten dot-notation (payload.student_id=…). --args '<json>'— payload base completo en JSON; los pareskey=valuese aplican encima.--json— salida JSON pura (sin colores ni banner), pensada para máquinas.
Mantenerlo al día
El catálogo de acciones se lee del Badgie MCP en vivo, así que las herramientas nuevas aparecen solas. Lo que sí envejece es el harness: por eso el CLI se actualiza solo y avisa cuando se ha quedado atrás.
badgie update # actualiza con el gestor con el que se instaló
badgie update --check # solo mira si hay versión nuevabadgie update no pregunta nada. Detecta cómo está instalado —npm global,
Homebrew, pnpm, yarn, bun, Volta, dependencia de un proyecto, npx, checkout
de fuente— y usa el comando correcto. Cuando no puede hacerlo él (una copia
efímera de npx, una dependencia de proyecto, un directorio sin permiso de
escritura), imprime el comando exacto y por qué no lo lanza. Nunca ejecuta
sudo por su cuenta.
Tres cosas que hace y que no se ven:
- Uno cada vez. El instalador va dentro de un lock de fichero con dueño
identificable, con la ruta de instalación como clave. Dos
badgie updatesimultáneos no lanzan dosnpm install -gsobre el mismo prefix: el segundo espera y, si el primero ya dejó puesta la versión buena y arrancando, lo dice y no instala nada. Un lock huérfano (proceso muerto) caduca solo, y robarlo es atómico. El fichero vive en un directorio de máquina (/var/tmp), no en el temporal del proceso: en macOS$TMPDIRes por usuario y cron/launchd no lo llevan puesto, así que un update programado y uno de la terminal se creaban dos locks distintos para el mismo prefix y lanzaban dos npm a la vez. - Se instala del mismo sitio del que se ha mirado. El registro consultado
viaja en el comando de instalación (
--registryy, para npm, además--@badgie:registry=, porque el registro por scope de un.npmrcgana a--registry). Sin eso, en una máquina con espejo privado se consulta un registro y se instala de otro. - El exit 0 del instalador no vale como prueba, y su
package.jsontampoco. Al terminar se ejecuta el CLI instalado (<prefix>/bin/badgie -v) y se compara lo que contesta con la versión perseguida. Si no arranca, o arranca y dice otra cosa —el instalador no tocó nada, bajó de versión, se llevó eldist/o dejó elbin/vacío—, el comando falla y lo dice. Una cadena en un fichero no demuestra que la instalación sirva; un binario que arranca, sí.
El aviso de versión
Cuando hay una versión nueva, el CLI lo dice al terminar:
▲ Badgie CLI 1.0.4 → 1.1.0
Actualiza con: badgie updateReglas del aviso, pensadas para que no moleste a un agente:
- Va por stderr, nunca por stdout.
- Con
--jsonno se imprime nada en absoluto, ni siquiera por stderr — porque un agente puede estar corriendo con2>&1. - No añade latencia: la consulta al registro la hace un proceso de fondo desacoplado, como mucho una vez al día, y el resultado se usa en la ejecución siguiente. Si no hay red, el CLI funciona igual y calla.
- Solo aparece si stderr es un terminal. En CI no aparece nunca.
- Se apaga con
BADGIE_NO_UPDATE_NOTIFIER=1.
Para agentes
# ¿hay algo que actualizar? (sin parsear texto)
badgie update --check --json | jq -e '.updateAvailable == true' >/dev/null || exit 0
# actualizar: UNA sola vez. Si no llega, sale ≠ 0 y ahí se acaba.
badgie update --json | jq -e '.ok == true and .verified == true' >/dev/null⚠️ No metas
badgie updateen un bucle «hasta quecurrent == latest». No hace falta, y con una instalación que por lo que sea no puede llegar alatestel bucle gira para siempre.badgie updateya comprueba el resultado por ti: o llega y sale con 0, o no llega, sale con ≠ 0 y dice exactamente por qué. Unok:falsees terminal: se lee, no se reintenta.
badgie update --json escribe una línea de JSON por stdout:
{"ok":true,"current":"1.0.4","latest":"1.1.0","channel":"latest","updateAvailable":true,
"performed":true,"installed":"1.1.0","verified":true,"install":{"manager":"npm","scope":"global",
"kind":"npm-global","path":"/opt/homebrew/lib/node_modules/@badgie/cli","writable":true,
"canSelfUpdate":true,"registryPinned":true},
"command":"npm install -g --prefix /opt/homebrew --registry https://registry.npmjs.org --@badgie:registry=https://registry.npmjs.org @badgie/cli@latest",
"registry":"https://registry.npmjs.org"}installed es la versión que ha dicho el CLI instalado al ejecutarlo, no
la que ponga su package.json: se lanza el ejecutable con -v y se lee lo que
contesta. verified:true significa exactamente eso — que arrancó y dijo la
versión que tocaba. Una instalación a medias deja un package.json mintiendo;
un binario que arranca, no.
El contrato de esa línea se verifica antes de escribirla; si algo no cuadrara, la salida se degrada a fallo en vez de publicar una contradicción:
| Garantía | Qué significa |
|---|---|
| performed:true ⇒ ok:true | jamás se declara «he actualizado» dentro de un fallo |
| performed:true ⇒ installed == latest | installed sale de ejecutar el CLI instalado, no de leer su package.json. Nunca se supone |
| performed:true ⇒ verified:true | no hay éxito sin haber arrancado el binario resultante |
| reason:"already-installed" ⇒ verified:true | «ya estaba puesta» también exige comprobarlo ejecutándola |
| verified:false ⇒ ok:false | una verificación fallida jamás convive con un éxito |
| ok:false ⇒ performed:false y hay reason y hay error | un fallo siempre viene explicado |
| exit 0 ⇔ ok:true | el código de salida y el JSON dicen lo mismo |
reason cuando lo hay:
| reason | Cuándo | exit |
|---|---|---|
| updates-disabled | BADGIE_DISABLE_UPDATES=1 | 0 |
| npx-ephemeral, source-checkout, project-dependency, not-writable, unknown-install | no podemos actualizarnos solos; command dice cómo hacerlo | 0 |
| already-installed | otro badgie update simultáneo ya dejó puesta la versión buena; este proceso no instaló nada | 0 |
| update-in-progress | hay otro instalador en curso y no lanzamos un npm encima. No se ha tocado nada: reintenta cuando termine | ≠ 0 |
| version-mismatch | el instalador salió 0 pero el CLI que arranca no es latest (no tocó nada, o bajó de versión) | ≠ 0 |
| installation-broken | el árbol dice una versión y el CLI no arranca. Con --check solo se informa; sin --check, badgie update reinstala para repararlo | ≠ 0 |
| installer-failed | el instalador falló; error lleva su stderr | ≠ 0 |
| internal-inconsistency | red de seguridad: la salida no cuadraba consigo misma | ≠ 0 |
Con --check nunca se instala nada: performed es siempre false y el exit
es 0 (salvo que la instalación esté rota, que es un ok:false).
Reparación
«Al día» no es lo mismo que «funciona». Si el canal no trae nada nuevo pero el
CLI instalado no arranca —el caso clásico: dos instalaciones a la vez se
llevaron por delante <prefix>/bin/badgie y el package.json quedó con la
versión subida—, badgie update no dice «ya estás al día»: reinstala. Esa
salida se distingue por repaired:true con updateAvailable:false:
{"ok":true,"current":"1.1.0","latest":"1.1.0","updateAvailable":false,
"repaired":true,"performed":true,"installed":"1.1.0","verified":true}Cuando el binario ni siquiera arranca para poder ejecutar badgie update, el
paquete sigue ahí: node "$(npm prefix -g)/lib/node_modules/@badgie/cli/dist/bin.js" update.
Por rol
El rol no se elige: se deriva de tu cuenta Badgie en el servidor. Cada rol ve un catálogo distinto.
🏫 Owner (cuenta de escuela)
Lectura universal del negocio + propuestas de acción:
badgie run get_school_profile
badgie run query_data table=fin_invoices limit=20
badgie run get_top_debtors
badgie run list_action_catalog # ~24 tipos de acción proponibles
badgie run list_my_proposals # estado de tus propuestas~51 tools: asistencia, riesgo de abandono, MRR, deuda, ocupación de reservas, progreso de colecciones, nutrición, drills… cada una gated por el módulo correspondiente de tu escuela.
🧑🏫 Teacher (profesor)
Tu día a día, scoped a tus clases y alumnos:
badgie run my_classes_today
badgie run my_students
badgie run award_badge --args '{"student_id":"…","badge_id":"…"}'
badgie run record_attendance --args '{"event_id":"…","records":[…]}'🎽 Student / familia
Tus datos y solo los tuyos:
badgie run my_badges
badgie run my_schedule
badgie run my_payments
badgie run my_golf_statsPara agentes y scripts
El CLI está diseñado para que un agente (OpenClaw, Claude, ChatGPT, un cron de CI…) opere Badgie sin fricción:
--jsonen cualquier comando → salida JSON estable, sin decoración.- Exit codes limpios:
0éxito, distinto de cero en error (con el error legible en stderr). - Mismo backend que un cliente MCP: es el Badgie MCP oficial, así que un agente que ya hable MCP puede conectarse directo a
https://www.badgie.com/api/mcp/mcp; el CLI es la vía cómoda cuando el agente vive en una shell. - Sin estado sorpresa: la sesión vive en
~/.badgie, portable entre invocaciones. - Se mantiene al día solo:
badgie update --check --jsondice si hay versión nueva ybadgie update --jsonla instala, las dos con salida parseable. El aviso de versión jamás contamina un--json.
# Ejemplo: informe diario en un script
badgie run my_classes_today --json | jq '.classes[] | {name, start, students}'Seguridad
- OAuth 2.1 + PKCE:
badgie loginabre el navegador y autorizas con tu cuenta Badgie. El CLI nunca ve tu contraseña. - Scoping por rol, en el servidor: owner / teacher / student se derivan de la base de datos, nunca del cliente. La RLS de Badgie aplica en cada llamada.
- Nunca cross-tenant: jamás verás datos de otra escuela ni de otro alumno.
- Human-in-the-loop: las escrituras sensibles se registran como propuestas que un humano aprueba en el dashboard. Los datos de menores siempre pasan por aprobación humana.
- Tokens en local con permisos estrictos:
~/.badgie/auth.jsonse guarda con modo0600.
Configuración
| Qué | Dónde |
|-----|-------|
| Sesión y tokens | ~/.badgie/auth.json |
| Preferencias del CLI | ~/.badgie/cli-config.json |
| BADGIE_MCP_URL | Override del endpoint MCP (dev/staging). Default: https://www.badgie.com/api/mcp/mcp |
| BADGIE_ORIGIN | Override del origen OAuth. Default: https://www.badgie.com |
| BADGIE_CONFIG_DIR | Reubica ~/.badgie entero (entornos aislados, contenedores) |
| BADGIE_NO_UPDATE_NOTIFIER | 1 calla el aviso de versión y el chequeo de fondo. badgie update sigue funcionando |
| NO_UPDATE_NOTIFIER | Igual que la anterior (convención del ecosistema) |
| BADGIE_DISABLE_UPDATES | 1 calla el aviso y badgie update se niega. Para instalaciones gobernadas |
| BADGIE_UPDATE_CHANNEL | dist-tag a seguir. Default: latest |
| BADGIE_REGISTRY | Registro npm alternativo. Default: https://registry.npmjs.org |
Estado en disco relacionado con actualizaciones: ~/.badgie/cli-update.json
(caché del aviso; borrarlo no rompe nada) y, solo mientras dura una
instalación, un lock en un directorio de máquina —/var/tmp en POSIX (/tmp
de reserva), %ProgramData%\Badgie en Windows— llamado
badgie-cli-update-<hash de la ruta de instalación>.lock. No en el
temporal del proceso: $TMPDIR es distinto por usuario y no lo lleva cron, y
el lock tiene que ser función del prefix, no de quién lo toca. El lock caduca
solo si el proceso que lo tenía muere (o a los 15 minutos).
# Apuntar el CLI a un entorno de desarrollo
BADGIE_MCP_URL=http://localhost:3000/api/mcp/mcp badgie whoamiEnlaces
- 🌐 www.badgie.com/cli — página del CLI
- 📚 Documentación del Badgie MCP — arquitectura, roles y catálogo de acciones
- 🏅 Badgie — la plataforma
- ✉️ [email protected]
Hecho con ⭐ por Badgie Sports App, S.L. · MIT
