SLOPSHOPPER

consumo

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

newpaneguardcommandtimer
v0.3.0MITupdated 2026-10-20Alantia-Consulting-Group/consumo-mod
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · consumo
│ ┃ Consumo ✕ › fix the failing auth test and add an audit log call │ ┃ ▲ ALANTIA ● en vivo │ ┃ AI Tokenomics · tu sesión ⏺ Read(src/auth.ts) │ ┃ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⎿ Read 6 lines │ ┃ ⏺ Update(src/auth.ts) │ ┃ tokens nuevos gasto ⎿ Added 2 lines, removed 1 line │ ┃ 0 $0.42 ⏺ Bash(bun test) │ ┃ último pedido sesión ⎿ 3 pass, 1 fail │ ┃ │ ┃ ↑ entrada 0 ↓ salida 0 ● Done. refresh now rejects expired claims and logs an audit event. │ ┃ ⟳ caché releída 0 0 llamadas │ ┃ ✻ Worked for 42s · done 4:20 PM │ ┃ ╭──────────────────────────────────────────╮ │ ┃ │ contexto 49% │ › /consumo │ ┃ │ ━━━━━━━━━━━━━╌╌╌╌╌╌╌╌╌╌╌╌╌ │ │ ┃ │ 97.4k / 200k 102.6k libres │ │ ┃ ╰──────────────────────────────────────────╯ │ ┃ │ ┃ ⚒ 9 herramientas ⏱ 9176:52:07 │ ┃ │ ┃ ────────────────────────────── │ ┃ People first · │ ┃ amplified by technology · │ ┃ driving business impact │ ┃ alantia.ai │ ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts

Draws

Pane · Consumo
▲ ALANTIA ● en vivo AI Tokenomics · tu sesión ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ tokens nuevos gasto 0 $0.42 último pedido sesión ↑ entrada 0 ↓ salida 0 ⟳ caché releída 0 0 llamadas ╭──────────────────────────────────────────────────────────╮ │ contexto 49% │ │ ━━━━━━━━━━━━━╌╌╌╌╌╌╌╌╌╌╌╌╌ │ │ 97.4k / 200k 102.6k libres │ ╰──────────────────────────────────────────────────────────╯ ⚒ 9 herramientas ⏱ 9176:52:07 ────────────────────────────── People first · amplified by technology · driving business impact alantia.ai
README

/consumo · AI Tokenomics in your terminal

A free Claude Code mod by Alantia, strategy and business transformation firm.

Español abajo

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:

MetricWhat it tells you
New tokens · last requestWhat the model read for the first time and wrote back for your last message (all model calls in that turn, subagents included)
Spend · sessionWhat the session has cost so far, the same figure as /cost
Input / output / cache rereadWhere the tokens of the last request went, with cached context counted apart so it doesn't inflate the total
ContextHow much of the context window is in use, with a green, amber and red bar
Tools and timeTools called in the session and time elapsed

Install in Claude Code (terminal or VS Code)

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

Use it

/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 it works

WhereWhat you get
Claude Code in a terminal or VS CodeThe live side panel
Claude Code sessions in the Claude app (cloud), where a side panel can't be dockedThe 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.

Privacy and trust

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.

Why we built it

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


Español

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étricaQué te dice
Tokens nuevos · último pedidoLo que el modelo leyó por primera vez y lo que escribió en tu último mensaje (todas las llamadas del turno, subagentes incluidos)
Gasto · sesiónLo que lleva costando la sesión, la misma cifra de /cost
Entrada / salida / caché releídaEn qué se fueron los tokens del último pedido; el contexto en caché se cuenta aparte para no inflar el total
ContextoQué tanto de la ventana de contexto está ocupada, con una barra verde, ámbar y roja
Herramientas y tiempoHerramientas usadas en la sesión y tiempo transcurrido

Instalación en Claude Code (terminal o VS Code)

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

Cómo usarlo

/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 funciona

DóndeQué obtienes
Claude Code en terminal o VS CodeEl panel lateral en vivo
Sesiones de Claude Code en la app de Claude (en la nube), donde no se puede acoplar un panelLas 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.

Privacidad y confianza

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.

Por qué lo hicimos

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

Source 1 files
hooks/register.js 287 lines
1// ─────────────────────────────────────────────────────────────
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