AI Tokenomics in your terminal: a live side panel with tokens per request, session spend, context, tools and time. By Alantia.

A free Claude Code mod by Alantia, strategy and business transformation firm.
Most teams running AI agents can't answer a simple question: what is this costing us, right now?
/consumo opens a live side panel in Claude Code that shows, as you work:
| Metric | What it tells you |
|---|---|
| New tokens · last request | What the model read for the first time and wrote back for your last message (all model calls in that turn, subagents included) |
| Spend · session | What the session has cost so far, the same figure as /cost |
| Input / output / cache reread | Where the tokens of the last request went, with cached context counted apart so it doesn't inflate the total |
| Context | How much of the context window is in use, with a green, amber and red bar |
| Tools and time | Tools called in the session and time elapsed |
Type each line at the Claude Code prompt and press Enter. Start the line with the /, with no spaces before it: if Claude answers you in text, the line was sent as a chat message instead of running as a command.
First, add Alantia's marketplace:
/plugin marketplace add Alantia-Consulting-Group/consumo-mod
Then install the mod and choose the user scope so it stays in every session:
/plugin install consumo@alantia
If you prefer a regular shell (PowerShell, Terminal), run these two lines instead:
claude plugin marketplace add Alantia-Consulting-Group/consumo-mod
claude plugin install consumo@alantia
/consumo
The panel opens beside the conversation. The numbers fill in after Claude's next answer and update with every request. Esc closes it.
To change the brand, accent color or language (es / en), open /config and look for consumo.
| Where | What you get |
|---|---|
| Claude Code in a terminal or VS Code | The live side panel |
| Claude Code sessions in the Claude app (cloud), where a side panel can't be docked | The same figures as a text summary in the chat |
| Regular Claude chat (claude.ai, desktop or mobile) | Mods don't run there. See below |
Without the mod. In Claude Code, the built-in /cost shows the session's spend and /context shows what fills the context window. In the regular Claude chat there is no live token counter: you can ask Claude "Explain where this conversation's usage went" for an estimate, and see your plan's usage in your account settings on claude.ai.
Plugins can run code on your machine, so only install the ones you trust. /consumo is open source and fits in one file, hooks/register.js: it only reads the usage figures Claude Code already keeps for the session (the same ones behind the status line and /cost). It makes no network calls, writes no files and sends nothing anywhere.
AI that isn't measured can't be managed. At Alantia we help organizations turn AI into business results, and part of that is AI Tokenomics: knowing which model to use, where, and at what cost. This panel is a small, practical piece of that discipline, and it's yours.
Want to bring AI into your operation with people first and results measured? Let's talk: alantia.ai · hola@alantia.ai
Un mod gratuito para Claude Code de Alantia, firma de estrategia y transformación empresarial.
Si usas agentes de IA, seguro te has preguntado: ¿cuánto me está costando esto, ahora mismo?
/consumo abre un panel lateral en Claude Code que te muestra en vivo:
| Métrica | Qué te dice |
|---|---|
| Tokens nuevos · último pedido | Lo que el modelo leyó por primera vez y lo que escribió en tu último mensaje (todas las llamadas del turno, subagentes incluidos) |
| Gasto · sesión | Lo que lleva costando la sesión, la misma cifra de /cost |
| Entrada / salida / caché releída | En qué se fueron los tokens del último pedido; el contexto en caché se cuenta aparte para no inflar el total |
| Contexto | Qué tanto de la ventana de contexto está ocupada, con una barra verde, ámbar y roja |
| Herramientas y tiempo | Herramientas usadas en la sesión y tiempo transcurrido |
Escribe cada línea en el prompt de Claude Code y presiona Enter. La línea debe empezar con la /, sin espacios antes: si Claude te responde con texto, la línea se envió como mensaje y no se ejecutó como comando.
Primero agrega el marketplace de Alantia:
/plugin marketplace add Alantia-Consulting-Group/consumo-mod
Después instala el mod y elige el alcance user para que quede en todas tus sesiones:
/plugin install consumo@alantia
Si prefieres una terminal normal (PowerShell, Terminal), usa estas dos líneas:
claude plugin marketplace add Alantia-Consulting-Group/consumo-mod
claude plugin install consumo@alantia
/consumo
El panel se abre junto a la conversación. Las cifras se llenan después de la siguiente respuesta de Claude y se actualizan con cada pedido. Esc lo cierra.
Para cambiar la marca, el color de acento o el idioma (es / en), abre /config y busca consumo.
| Dónde | Qué obtienes |
|---|---|
| Claude Code en terminal o VS Code | El panel lateral en vivo |
| Sesiones de Claude Code en la app de Claude (en la nube), donde no se puede acoplar un panel | Las mismas cifras como resumen de texto en el chat |
| Chat normal de Claude (claude.ai, escritorio o móvil) | Ahí los mods no funcionan. Mira abajo |
Sin el mod. En Claude Code, el comando integrado /cost muestra el gasto de la sesión y /context muestra qué ocupa la ventana de contexto. En el chat normal de Claude no hay un contador de tokens en vivo: puedes pedirle a Claude "Explícame en qué se fue el uso de esta conversación" para obtener un estimado, y ver el uso de tu plan en la configuración de tu cuenta en claude.ai.
Los plugins pueden ejecutar código en tu equipo, así que instala solo los que te den confianza. /consumo es de código abierto y cabe en un solo archivo, hooks/register.js: solo lee las cifras de uso que Claude Code ya lleva de la sesión (las mismas de la barra de estado y de /cost). No hace llamadas a internet, no escribe archivos y no envía nada a ningún lado.
La IA que no se mide, no se puede gestionar. En Alantia ayudamos a las organizaciones a convertir la IA en resultados de negocio, y parte de eso es Tokenomics de IA: saber qué modelo usar, dónde y con qué costo. Este panel es una pieza pequeña y práctica de esa disciplina, y es tuya.
¿Quieres llevar la IA a tu operación con las personas primero y resultados medidos? Hablemos: alantia.ai · hola@alantia.ai
People first · amplified by technology · driving business impact
MIT License · © 2026 Alantia Consulting Group
hooks/register.js 287 lines1// ─────────────────────────────────────────────────────────────
2// Mod "consumo" by Alantia · AI Tokenomics en tu terminal
3// Panel lateral con métricas de la sesión de Claude Code
4// Abre con /consumo · Esc lo cierra
5// Marca, color de acento e idioma se cambian en /config
6// Funciona local: solo lee las cifras que Claude Code ya lleva de la sesión
7// y no envía nada a ningún lado.
8// ─────────────────────────────────────────────────────────────
9
10const PANE = 'consumo'
11const PANE_COLUMNS = 34 // ancho pedido para el panel acoplado
12const INNER = PANE_COLUMNS - 4 // ancho útil, descontando el marco del panel
13const ALANTIA_TURQUOISE = '#1AD3E6' // turquesa tecnológico del manual de marca
14
15// Textos del panel en español e inglés
16const COPY = {
17 es: {
18 command: 'Abre el panel de consumo de Alantia',
19 title: 'Consumo',
20 subtitle: 'AI Tokenomics · tu sesión',
21 live: 'en vivo',
22 newTokens: 'tokens nuevos',
23 lastRequest: 'último pedido',
24 spend: 'gasto',
25 session: 'sesión',
26 input: '↑ entrada ',
27 output: '↓ salida ',
28 cache: '⟳ caché releída ',
29 call: ' llamada',
30 calls: ' llamadas',
31 context: 'contexto',
32 free: ' libres',
33 tools: ' herramientas',
34 },
35 en: {
36 command: 'Open the Alantia usage panel',
37 title: 'Usage',
38 subtitle: 'AI Tokenomics · your session',
39 live: 'live',
40 newTokens: 'new tokens',
41 lastRequest: 'last request',
42 spend: 'spend',
43 session: 'session',
44 input: '↑ input ',
45 output: '↓ output ',
46 cache: '⟳ cache reread ',
47 call: ' call',
48 calls: ' calls',
49 context: 'context',
50 free: ' free',
51 tools: ' tools',
52 },
53}
54
55// Estado del panel (se reinicia si el mod se recarga)
56// Un "pedido" es el turno completo: todas las llamadas al modelo que hace
57// Claude para responder un mensaje, subagentes incluidos.
58let turn = { fresh: 0, output: 0, cached: 0, calls: 0 } // turno en curso
59let last = { fresh: 0, output: 0, cached: 0, calls: 0 } // último pedido
60let tools = 0 // herramientas usadas en la sesión
61let loadedAt = Date.now()
62// Contexto, gasto e inicio de sesión los lleva Claude Code; aquí solo se leen
63let usage = null
64
65// ── Helpers de formato ──────────────────────────────────────
66function fmtTokens(n) {
67 if (n >= 1_000_000) return trim(n / 1_000_000) + 'M'
68 if (n >= 1000) return trim(n / 1000) + 'k'
69 return String(n)
70}
71function trim(n) {
72 return n.toFixed(1).replace(/\.0$/, '')
73}
74function fmtClock(ms) {
75 const s = Math.max(0, Math.floor(ms / 1000))
76 const hh = Math.floor(s / 3600)
77 const mm = String(Math.floor((s % 3600) / 60)).padStart(2, '0')
78 const ss = String(s % 60).padStart(2, '0')
79 return hh > 0 ? hh + ':' + mm + ':' + ss : mm + ':' + ss
80}
81// Color del contexto según el nivel: verde, ámbar y rojo del tema
82function levelColor(pct) {
83 return pct > 85 ? 'error' : pct > 60 ? 'warning' : 'success'
84}
85
86// Lee las cifras de la sesión (las mismas de la barra de estado y /cost)
87async function refresh($) {
88 try {
89 usage = await $.session.usage()
90 } catch {
91 // sin lectura: el panel conserva la anterior
92 }
93 $.ui.invalidate('ui.render')
94}
95
96
97// Resumen en texto para pantallas que no acomodan el panel lateral
98function summary(T) {
99 const ctx = usage?.context
100 const tokens = ctx?.tokens ?? 0
101 const win = ctx?.window ?? 0
102 const pct = ctx?.percent ?? (win ? Math.min(100, Math.round((tokens / win) * 100)) : 0)
103 const cost = usage?.cost
104 const startedAt = usage?.startedAt ?? loadedAt
105 return [
106 '▲ ALANTIA · ' + T.subtitle,
107 T.newTokens + ' (' + T.lastRequest + '): ' + fmtTokens(last.fresh + last.output) +
108 ' · ' + T.spend + ' (' + T.session + '): ' + (cost ? '$' + cost.usd.toFixed(2) : '—'),
109 T.input + fmtTokens(last.fresh) + ' ' + T.output + fmtTokens(last.output) + ' ' + T.cache + fmtTokens(last.cached),
110 T.context + ': ' + pct + '% (' + fmtTokens(tokens) + ' / ' + (win ? fmtTokens(win) : '—') + ')',
111 '⚒ ' + tools + T.tools + ' · ⏱ ' + fmtClock(Date.now() - startedAt),
112 'People first · amplified by technology · driving business impact · alantia.ai',
113 ].join('\n')
114}
115
116export function register(on, options) {
117 const BRAND = String(options?.marca || 'Alantia').toUpperCase()
118 const ACCENT = String(options?.acento || ALANTIA_TURQUOISE)
119 const T = COPY[options?.idioma === 'en' ? 'en' : 'es']
120
121 // 1) Al iniciar: registra /consumo y arranca el reloj de la sesión
122 on('session.start', async ($, e, next) => {
123 await $.command.register({
124 name: 'consumo',
125 description: T.command,
126 immediate: true, // se puede abrir mientras Claude trabaja
127 })
128 loadedAt = Date.now()
129 await refresh($)
130 // Redibuja cada segundo para que avance el reloj
131 $.clock.every(1000, () => $.ui.invalidate('ui.render'))
132 return next(e)
133 })
134
135 // 2) /consumo abre el panel
136 on('command.run', { command: 'consumo' }, async ($) => {
137 await refresh($)
138 const opened = await $.ui.open({ id: PANE, title: T.title, closeOnEscape: true, columns: PANE_COLUMNS })
139 // Si esta pantalla no puede acomodar el panel, las cifras salen como texto en el chat
140 if (!opened?.isPlaced) return { text: summary(T) }
141 return {}
142 })
143
144 // 3) Cuenta herramientas (observa y deja pasar)
145 // Solo observa: si algo falla aquí, la herramienta sigue su curso
146 on('tool.call', async ($, e, next) => {
147 tools += 1
148 $.ui.invalidate('ui.render')
149 return next(e)
150 }).catch(($, e, next) => next(e))
151
152 // 4) Nuevo pedido: reinicia el acumulador del turno
153 on('turn.start', async ($, e, next) => {
154 if (!e.agentId) turn = { fresh: 0, output: 0, cached: 0, calls: 0 }
155 return next(e)
156 })
157
158 // 5) Cada llamada al modelo: suma los tokens del pedido
159 on('turn.step', async function* ($, e, next) {
160 const result = yield* next(e)
161 const u = result.usage
162 if (u) {
163 // Entrada nueva: lo que el modelo lee por primera vez (se cachea o no).
164 // La caché leída es el contexto que se reenvía en cada llamada; se cuenta
165 // aparte porque sumarla infla el total: un pedido de 3 llamadas sobre un
166 // contexto de 70k "consumiría" 210k sin haber añadido casi nada.
167 turn.fresh += (u.input_tokens ?? 0) + (u.cache_creation_input_tokens ?? 0)
168 turn.output += u.output_tokens ?? 0
169 turn.cached += u.cache_read_input_tokens ?? 0
170 turn.calls += 1
171 last = { ...turn }
172 await refresh($)
173 }
174 return result
175 })
176
177 // 6) Dibuja el panel
178 on('ui.render', { component: 'Pane' }, async ($, e, next) => {
179 if (e.requestId !== PANE) return next(e)
180 const { Box, Text } = $.ui.resolve(e)
181
182 const ctx = usage?.context
183 const ctxTokens = ctx?.tokens ?? 0
184 const ctxWindow = ctx?.window ?? 0
185 const pct = ctx?.percent ?? (ctxWindow ? Math.min(100, Math.round((ctxTokens / ctxWindow) * 100)) : 0)
186 const level = levelColor(pct)
187 const cost = usage?.cost
188 const startedAt = usage?.startedAt ?? loadedAt
189
190 const barWidth = INNER - 4 // borde y relleno de la tarjeta de contexto
191 const filled = Math.min(barWidth, Math.round((pct / 100) * barWidth))
192 const blink = Math.floor(Date.now() / 1000) % 2 === 0
193
194 const label = (text) => Text({ dimColor: true, children: [text] })
195 const row = (key, children) =>
196 Box({ key, flexDirection: 'row', justifyContent: 'space-between', children })
197
198 // Cifra protagonista: etiqueta tenue arriba, valor en negrita, nota debajo
199 const stat = (key, title, value, note, color) =>
200 Box({
201 key,
202 flexDirection: 'column',
203 flexGrow: 1,
204 children: [label(title), Text({ bold: true, color, children: [value] }), label(note)],
205 })
206
207 return Box({
208 flexDirection: 'column',
209 gap: 1,
210 children: [
211 // Encabezado: la A que asciende del isotipo, la marca y el pulso en vivo
212 Box({
213 key: 'head',
214 flexDirection: 'column',
215 children: [
216 row('brand', [
217 Text({ bold: true, color: ACCENT, children: ['▲ ' + BRAND] }),
218 Box({
219 flexDirection: 'row',
220 gap: 1,
221 children: [Text({ color: ACCENT, dimColor: !blink, children: ['●'] }), label(T.live)],
222 }),
223 ]),
224 label(' ' + T.subtitle),
225 Text({ color: ACCENT, children: ['━'.repeat(INNER)] }),
226 ],
227 }),
228 Box({
229 key: 'stats',
230 flexDirection: 'row',
231 gap: 2,
232 children: [
233 stat('tok', T.newTokens, fmtTokens(last.fresh + last.output), T.lastRequest),
234 stat('usd', T.spend, cost ? '$' + cost.usd.toFixed(2) : '—', T.session, ACCENT),
235 ],
236 }),
237 Box({
238 key: 'split',
239 flexDirection: 'column',
240 children: [
241 row('io', [label(T.input + fmtTokens(last.fresh)), label(T.output + fmtTokens(last.output))]),
242 row('cache', [
243 label(T.cache + fmtTokens(last.cached)),
244 label(last.calls + (last.calls === 1 ? T.call : T.calls)),
245 ]),
246 ],
247 }),
248 Box({
249 key: 'ctx',
250 flexDirection: 'column',
251 borderStyle: 'round',
252 borderColor: ACCENT,
253 paddingX: 1,
254 children: [
255 row('ctxHead', [label(T.context), Text({ bold: true, color: level, children: [pct + '%'] })]),
256 Box({
257 flexDirection: 'row',
258 children: [
259 Text({ color: level, children: ['━'.repeat(filled)] }),
260 label('╌'.repeat(barWidth - filled)),
261 ],
262 }),
263 row('ctxFoot', [
264 label(fmtTokens(ctxTokens) + ' / ' + (ctxWindow ? fmtTokens(ctxWindow) : '—')),
265 label(ctxWindow ? fmtTokens(Math.max(0, ctxWindow - ctxTokens)) + T.free : ' '),
266 ]),
267 ],
268 }),
269 row('foot', [label('⚒ ' + tools + T.tools), label('⏱ ' + fmtClock(Date.now() - startedAt))]),
270 // Firma: el lema de Alantia, completo, en inglés y en orden;
271 // la frase central en el acento, como en el manual de marca
272 Box({
273 key: 'sign',
274 flexDirection: 'column',
275 children: [
276 Text({ color: ACCENT, dimColor: true, children: ['─'.repeat(INNER)] }),
277 Text({ bold: true, children: ['People first ·'] }),
278 Text({ bold: true, color: ACCENT, children: ['amplified by technology ·'] }),
279 Text({ bold: true, children: ['driving business impact'] }),
280 label('alantia.ai'),
281 ],
282 }),
283 ],
284 })
285 })
286}
287