Instalar y correrlo
Necesita Node ≥ 22.6. No hay paso de build: el CLI se ejecuta desde TypeScript directamente.
Linux — apt
# una vez: la clave y el repo $ sudo install -d -m 0755 /etc/apt/keyrings $ curl -fsSL https://quartermaster.legios.com.ar/apt/legios.gpg \ | sudo tee /etc/apt/keyrings/legios.asc >/dev/null $ echo "deb [signed-by=/etc/apt/keyrings/legios.asc] \ https://quartermaster.legios.com.ar/apt stable main" \ | sudo tee /etc/apt/sources.list.d/legios.list >/dev/null $ sudo apt update && sudo apt install quartermaster
macOS — Homebrew
$ brew install legiosai/tap/quartermaster
Cualquier sistema — npm
$ npm install -g @legios/quartermaster
Desde el código
$ git clone https://github.com/legiosai/quartermaster $ cd quartermaster && make instalar
apt upgrade, Homebrew con brew upgrade, npm con
npm update -g. Ninguno de los tres se actualiza solo sin que
corras ese comando, y el programa no se auto-actualiza: no tiene ningún
canal para hacerlo, y no lo va a tener.
Todas las banderas
Sin argumentos lee del disco y no toca la red. Todo lo que sale por red está detrás de una bandera explícita.
| Bandera | Qué hace |
|---|---|
| qm | Cuota y consumo de cada perfil. Sin red y sin credencial: la cuota sale de la que Claude Code ya dejó en .claude.json. |
| --refrescar | Además le pide el número al endpoint. Necesita un token vigente. |
| --json | La misma información como JSON, para scripts y statuslines. Es un contrato: ver más abajo. |
| --breve | Un renglón y nada más. No lee transcripciones, así que tarda ~85 ms en vez de un segundo. Es lo que va en una statusline. |
| --watch [seg] | Se redibuja cada N segundos. Mínimo 30, por defecto 60. |
| --dias=N | Ventana del consumo local. Por defecto 7. |
| --umbral=N | Sale con código 3 si alguna barra pasa el N %. |
| --esperar | No vuelve hasta que la cuota baje del umbral (80 por defecto). Para encadenar: qm --esperar && codex … |
| --cuenta=NOMBRE | Con --esperar: mirar sólo esa cuenta. |
| --solo=a,b | Mostrar sólo esas cuentas. |
| --ocultar=a,b | Mostrar todas menos esas. |
| --sin-codex | No mirar la cuenta de Codex. |
| --redactado | Con --json: saca mails y rutas de casa. Para comitear una salida. |
| --calentar | Refresca el endpoint de cada perfil y el de Codex, guarda lo que vuelve y no imprime nada. Es lo que corre el item de la barra. |
| --cuentas=a,b | Con --calentar: sólo esas. Cada cuenta salteada es un pedido menos a un endpoint que no es nuestro. |
| --calentar-codex | Igual, pero sólo Codex y sin tocar el llavero. |
| -h, --help | La misma lista, en la terminal. |
Cómo leer lo que muestra
Cada cuenta trae dos números a propósito. El primero es la sesión y el segundo la barra que te frena antes. Contestan preguntas distintas —«¿puedo seguir ahora?» y «¿llego al final de la semana?»— y una sola de las dos deja media respuesta. Cuando la que frena es la sesión, se muestra un número solo.
$ qm --breve personal 23/75%! · teams 9/42% · codex 49/59%
| Marca | Qué significa |
|---|---|
| 23/75% | Sesión al 23 %, la barra que frena al 75 %. |
| ! | El servidor marcó esa barra con aviso — no es un umbral nuestro, viene en la respuesta. |
| ~ | El cache tiene más de 6 horas. El número es viejo, no falso. |
| 42% | Un número solo: la barra que frena es la de sesión. |
cachedUsageUtilization hay barras que no tienen clave propia
arriba. Leyendo sólo las dos famosas —five_hour y
seven_day— se veía 8 % y 59 % mientras la que realmente frenaba
iba al 75 % con aviso del servidor. Un error de 67 puntos, y cómodo.
¿Te vas a chocar antes de que se reinicie?
Es la pregunta que ni el porcentaje ni el consumo contestan solos: «vas 75 %» no dice nada sin saber a qué velocidad subís. qm guarda una serie de lecturas por barra y le ajusta una recta.
ritmo: 12.4 pts/h · 100 % en 1h58m — antes del reinicio
Lo importante no es la recta: es cuándo no se dibuja. Con dos lecturas, o con una ventana que recién arranca, cualquier extrapolación es un número inventado con cara de dato. Entonces dice esto, que es otra cosa:
ritmo: todavía no sé el ritmo: hacen falta 3 lecturas y hay 1
- Se descartan las lecturas de más de 2 horas: el ritmo de hace cuatro no predice el de ahora.
- No se proyecta sobre un movimiento menor a 2 puntos, porque el servidor manda enteros y subir uno no se distingue de un redondeo.
- La serie vive en
~/.cache/quartermaster/historial.jsonly se indexa por elmedidoEnde la cuota, no por cuándo miró qm: leer diez veces el mismo cache deja una muestra.
--json emite la respuesta, no los datos crudos
Las cuatro superficies —GNOME, la barra de macOS, la bandeja de Windows y el navegador— dibujan lo mismo porque ninguna decide nada. La regla que elige qué barra mostrar vivía copiada en Python, JavaScript y Swift, y una regla en tres lenguajes es una respuesta distinta por pantalla el día que alguien edita una. Ahora el CLI emite la respuesta ya resuelta y los dibujantes leen campos.
$ qm --json --redactado
{
"generado": "2026-09-10T23:04:07.315Z",
"plataforma": "linux",
"ventanaDias": 7,
"perfiles": [
{
"producto": "claude",
"perfil": ".claude",
"directorio": "~/.claude",
"cuenta": "***@ejemplo.com",
"plan": "claude_max",
"credencial": "vigente (4h29m)",
"cuota": {
"estado": "ok",
"origen": "endpoint",
"medidoEn": "2026-09-10T22:56:23.201Z",
"edadSegundos": 464,
"ventanas": [ … ],
"mostrar": [ … ],
"frena": { "clave": "weekly_scoped", "porcentaje": 89, … },
"sesion": { "clave": "session", "porcentaje": 36, … },
"semanal": { "clave": "weekly_scoped", "porcentaje": 89, … },
"historia": [ … ]
},
"proyeccion": { … },
"local": { … }
}
]
}
La raíz
| Campo | Qué es |
|---|---|
| generado | ISO 8601. Cuándo corrió qm — no cuándo se midió la cuota. |
| plataforma | linux, darwin o win32. |
| ventanaDias | La ventana del consumo local. Lo que puso --dias. |
| perfiles | Un elemento por cuenta encontrada. Puede ser [] en una máquina sin nada instalado, y eso no es un error: el código de salida sigue siendo 0. |
Cada perfil
| Campo | Qué es |
|---|---|
| producto | claude, codex o el proveedor que guarda opencode. |
| perfil / directorio | El nombre del perfil y de dónde se leyó. Con --redactado la ruta de casa sale como ~. |
| cuenta / plan | El mail y el plan. Con --redactado el mail sale enmascarado. |
| credencial | Frase en castellano: vigente y cuánto le queda, o vencida y qué comando la arregla. |
| cuota.estado | ok, o el motivo por el que no hay número. |
| cuota.origen | endpoint o cache: si el número se pidió o se leyó del disco. |
| cuota.medidoEn | Cuándo se midió la cuota. Es la fecha que importa. |
| cuota.edadSegundos | Cuán viejo es ese número. Arriba de 6 h la superficie le pone ~. |
| cuota.ventanas | Todas las barras, incluidas las que no tienen clave propia arriba. |
| cuota.mostrar | Las que hay que dibujar. Un dibujante que filtra por su cuenta está reimplementando la regla. |
| cuota.frena | La barra que va a frenar primero. Es la que decide el color y el aviso. |
| cuota.sesion / semanal | Las dos que contestan las dos preguntas. Pueden ser la misma. |
| proyeccion | El ritmo, o el motivo por el que todavía no hay ritmo. |
| local | El consumo leído de las transcripciones, en la ventana de --dias. |
mostrar y
frena.
Las cinco pantallas
Todas salen de qm --json. Ninguna sabe qué es una credencial.
La statusline de Claude Code
Donde la herramienta cumple su misión: el número deja de ser algo que te acordás de mirar.
GNOME
qm-indicator dibuja un medidor por cuenta en la barra de arriba, y el panel vive dentro de una extensión propia.
La barra de menú de macOS
qm-barra. Texto vivo, con el título medido y no elegido a ojo.
La bandeja de Windows
qm-tray.ps1. Dibuja un arco: a 16 px un dígito se lee y dos son una mancha.
El navegador
qm-web. Un tablero que se redibuja cada 30 s.
La statusline, en el settings.json del perfil
{
"statusLine": { "type": "command", "command": "qm --breve" }
}
Ojo con lo que no hace: el cache se refresca cuando ese perfil corre Claude Code, así que la statusline de un perfil se actualiza usándolo. Alcanza para enterarte de que vas al 75 % mientras trabajás, y no para vigilar un perfil que no estás usando.
De dónde sale cada número
- La cuota sale de
cachedUsageUtilization, dentro del.claude.jsonde cada perfil. Ya está en el disco: no hace falta ni red ni credencial, y por eso se lee aunque el token esté vencido. - Los perfiles se descubren mirando todos los
CLAUDE_CONFIG_DIRde la máquina, no sólo el directorio por defecto. Un asiento de trabajo y una suscripción personal son dos perfiles, y las demás herramientas ven uno. - El consumo sale de las transcripciones locales, en la ventana de
--dias. Es el piso: siempre hay un número, aun con todos los tokens vencidos. - Codex no deja la cuota en el disco, así que ese cache es nuestro: se refresca solo si está viejo, salvo en
--breve, que tiene que seguir tardando milisegundos. - opencode aporta los proveedores que tenga configurados, con el mismo tratamiento.
Lo que no hace, a propósito
- Nunca refresca un token. Lee credenciales, no las escribe. Refrescar sería correr una carrera contra la escritura del propio Claude Code y arriesgar el refresh token del usuario: cambiar una comodidad de monitoreo por un login roto. Cuando una credencial vence, lo dice y sigue con el resto.
- Sin cuenta, sin nube, sin telemetría. Todo se lee del disco y de la credencial del usuario. Lo único que sale de la máquina es el sondeo de cuota, al mismo host con el que Claude Code ya habla.
- Ese sondeo tiene piso: 60 segundos. Una cadencia adaptativa llegó a bajar a 20 s por cuenta —unas 360 consultas por hora, sostenidas, contra un endpoint no documentado—. Que te limiten por leer tu propia cuota sería una versión autoinfligida del silencio que la herramienta existe para arreglar.
- El adaptador de transcripciones no es opcional. El endpoint no está documentado y puede desaparecer sin aviso; el piso local es lo que garantiza que siempre haya un número.
Códigos de salida
| Código | Cuándo |
|---|---|
| 0 | Todo bien. También en una máquina sin perfiles: la respuesta es "perfiles": [], no un error. |
| 2 | Argumentos inválidos. El mensaje dice cuál. |
| 3 | Con --umbral=N: alguna barra llegó o pasó ese N %. |
# no arrancar un trabajo largo si ya vamos al 80 $ qm --umbral=80 >/dev/null || exit 0 # o directamente esperar a que baje $ qm --esperar --cuenta=codex && codex exec "…"
Si algo no anda
Dice que la credencial está vencida
Es correcto y no rompe nada: la cuota igual se lee, porque sale del disco. El mensaje dice qué comando la arregla. qm no lo corre por vos a propósito.
El número tiene un ~ al lado
El cache tiene más de 6 horas. Corré qm --refrescar, o simplemente usá ese perfil: Claude Code reescribe el cache cuando corre.
Falta una cuenta
Fijate con qm --json si aparece en perfiles. Si no está, su CLAUDE_CONFIG_DIR no está en el ambiente desde el que corrés qm.
En GNOME no aparece la barra de arriba
Hace falta la extensión y una sesión nueva: GNOME carga extensiones al iniciar sesión, así que después de instalarla hay que salir y volver a entrar.
Otra cosa
Abrí un issue. Si podés, pegá la salida de qm --json --redactado: está pensada exactamente para eso — saca mails y rutas de casa para que se pueda comitear.