Banda sobre el prompt: modelo, esfuerzo, contexto, límites de 5 h y semanal con su reinicio, costo y duración de la sesión

Dos mods para Claude Code que muestran en pantalla lo que normalmente no se ve: qué hacen tus subagentes, cuánto te queda de cuota y cómo va la caché de prompts.
El panel de agent-bots en la app de escritorio, con agentes y tareas de ejemplo.
Aviso: la API de mods de Claude Code es de acceso anticipado y puede cambiar entre versiones. Probados con Claude Code 2.1.292 y 2.1.299.
| Mod | Qué hace | Dónde se ve |
|---|---|---|
agent-bots | Panel lateral «Agentes y tareas»: cada subagente (y cada comando en segundo plano) como un robot animado, la lista de tareas de la sesión en vivo y tres anillos: cuota de 5 h, cuota semanal y caché de prompts | App de escritorio (en terminal, versión de texto) |
medidor | Banda sobre el prompt: modelo y esfuerzo, contexto, límites de 5 h y semana con su reinicio, costo, duración y estado de la caché con cuenta regresiva | Terminal |
Los dos se complementan: medidor solo dibuja en la terminal y agent-bots hace lo mismo en la app de escritorio. Puedes instalar solo uno.
/plugin marketplace add gabriel9004/claude-code-mods
/plugin install agent-bots@claude-code-mods
/plugin install medidor@claude-code-mods
/reload-plugins
Luego /tareas o /bots abre el panel y /medidor [completo|compacto|oculto] cambia la banda.
Un mod se ejecuta con acceso a la sesión, así que conviene saber qué toca. Ninguno envía datos a ningún sitio ni guarda nada en disco.
session.usage), el uso de tokens de cada petición para la caché y tres variables de entorno de solo lectura (FORCE_PROMPT_CACHING_5M, CLAUDE_CODE_PROMPT_CACHE_TTL, ENABLE_PROMPT_CACHING_1H). Registra una herramienta propia, tareas, y solo añade una línea al prompt de sistema si la sesión no trae ninguna herramienta de tareas.Cada mod trae sus pruebas:
cd agent-bots && claude plugin validate . && claude plugin test .
cd ../medidor && claude plugin validate .
MIT. La lógica de la caché toma ideas de otro proyecto MIT; ver NOTICE.md.
Two Claude Code mods: agent-bots (a desktop side panel with animated robots for subagents, a live task list and quota/cache rings) and medidor (a terminal band with model, context, 5 h / weekly limits, cost and prompt-cache countdown). The mods API is early access and may change.
/plugin marketplace add gabriel9004/claude-code-mods
/plugin install agent-bots@claude-code-mods
/plugin install medidor@claude-code-mods
MIT licensed. The UI text is in Spanish.
hooks/register.tsx 307 lines1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register } from 'claude-code'
3
4import type { Medida, Modo, Muestra, Ttl, Ventana } from '../types'
5import { aciertos, aconsejar, decidirTtl, observarTtl, reloj, restanteMs, tokens, tokensPrompt } from './cache'
6import type { Consejo } from './cache'
7
8const medida = atom({ plugin: 'medidor', key: 'medida' } as const, null)
9const modelo = atom({ plugin: 'medidor', key: 'modelo' } as const, '')
10const esfuerzo = atom({ plugin: 'medidor', key: 'esfuerzo' } as const, null)
11const ahora = atom({ plugin: 'medidor', key: 'ahora' } as const, 0)
12const modo = atom({ plugin: 'medidor', key: 'modo' } as const, 'completo')
13const avisado = atom({ plugin: 'medidor', key: 'avisado' } as const, 0)
14const muestras = atom({ plugin: 'medidor', key: 'muestras' } as const, [])
15const ttlActual = atom({ plugin: 'medidor', key: 'ttl' } as const, '5m')
16const ttlFuente = atom({ plugin: 'medidor', key: 'ttlFuente' } as const, 'por defecto')
17
18const DIAS = ['dom', 'lun', 'mar', 'mié', 'jue', 'vie', 'sáb']
19const MODOS: Modo[] = ['completo', 'compacto', 'oculto']
20const AVISO_CACHE_MS = 60_000 // amarillo y aviso cuando falta esto para que venza
21const COMPACTAR_DESDE = 100_000 // con más tokens, una caché vencida sugiere /compact
22const AVISAR_DESDE = 20_000 // prompts menores no merecen aviso de vencimiento
23
24// «claude-opus-5-5[1m]» → «Opus 5.5 · 1M»
25function nombreModelo(id: string): string {
26 if (!id) return '…'
27 const largo = /\[1m\]/i.test(id) ? ' · 1M' : ''
28 const limpio = id.replace(/\[.*?\]/g, '').replace(/^claude-/, '')
29 const m = limpio.match(/^([a-z]+)-(\d+)(?:-(\d+))?/i)
30 if (!m || !m[1] || !m[2]) return limpio + largo
31 const familia = m[1].charAt(0).toUpperCase() + m[1].slice(1)
32 return `${familia} ${m[2]}${m[3] && m[3].length <= 2 ? '.' + m[3] : ''}${largo}`
33}
34
35function color(pct: number | undefined): string {
36 if (pct === undefined) return 'gray'
37 if (pct >= 90) return 'red'
38 if (pct >= 70) return 'yellow'
39 return 'green'
40}
41
42function barra(pct: number | undefined, celdas: number): [string, string] {
43 const llenas = Math.max(0, Math.min(celdas, Math.round(((pct ?? 0) / 100) * celdas)))
44 return ['▰'.repeat(llenas), '▱'.repeat(celdas - llenas)]
45}
46
47function duracion(ms: number): string {
48 if (ms <= 0) return 'ya'
49 const min = Math.floor(ms / 60_000)
50 const d = Math.floor(min / 1440)
51 const h = Math.floor((min % 1440) / 60)
52 const m = min % 60
53 if (d > 0) return `${d}d ${h}h`
54 if (h > 0) return `${h}h ${String(m).padStart(2, '0')}m`
55 return `${m}m`
56}
57
58function hora(fecha: Date): string {
59 return `${String(fecha.getHours()).padStart(2, '0')}:${String(fecha.getMinutes()).padStart(2, '0')}`
60}
61
62function reinicio(v: Ventana | undefined, t: number, conDia: boolean): string {
63 if (!v?.resetsAt) return ''
64 const f = new Date(v.resetsAt)
65 const cuando = conDia ? `${DIAS[f.getDay()]} ${hora(f)}` : hora(f)
66 return `${duracion(f.getTime() - t)} (${cuando})`
67}
68
69const COLOR_CONSEJO: Record<Consejo['tipo'], string> = {
70 fria: 'gray',
71 'sin-cache': 'gray',
72 caliente: 'green',
73 pronto: 'yellow',
74 vencida: 'red',
75 fallo: 'red',
76}
77const ICONO_CONSEJO: Record<Consejo['tipo'], string> = {
78 fria: '○',
79 'sin-cache': '○',
80 caliente: '●',
81 pronto: '▲',
82 vencida: '✖',
83 fallo: '✖',
84}
85
86async function medir($: EngineInterface) {
87 const u = await $.session.usage()
88 const m: Medida = {
89 startedAt: u.startedAt,
90 tokens: u.context.tokens,
91 window: u.context.window,
92 percent: u.context.percent,
93 limites: u.rateLimits.map(r => ({ kind: r.kind, percentUsed: r.percentUsed, resetsAt: r.resetsAt })),
94 usd: u.cost?.usd,
95 }
96 await update($, medida, () => m)
97 await update($, ahora, () => Date.now())
98 return m
99}
100
101export const register: Register = on => {
102 // Variables del módulo: se reinician en cada recarga, a propósito.
103 let env: { force5m?: string; ttlVar?: string; enable1h?: string } = {}
104 let observado: Ttl | undefined
105 let marcaReloj = ''
106 let avisadaCache = 0
107
108 on('session.start', async ($, e, next) => {
109 await $.command.register({
110 name: 'medidor',
111 description: 'Banda de uso: /medidor [completo|compacto|oculto] (sin argumento, alterna)',
112 })
113 const nada = () => undefined
114 env = {
115 force5m: await $.env.get('FORCE_PROMPT_CACHING_5M').catch(nada),
116 ttlVar: await $.env.get('CLAUDE_CODE_PROMPT_CACHE_TTL').catch(nada),
117 enable1h: await $.env.get('ENABLE_PROMPT_CACHING_1H').catch(nada),
118 }
119 const id = await $.session.model()
120 await update($, modelo, () => id)
121 const m = await medir($)
122 const base = decidirTtl(env, m.limites)
123 await update($, ttlActual, () => base.ttl)
124 await update($, ttlFuente, () => base.fuente)
125
126 // Cada segundo: redibuja solo si cambió lo que se ve (minuto de los
127 // reinicios o segundo de la cuenta regresiva de la caché), y avisa a 60 s.
128 $.clock.every(1000, async () => {
129 const t = Date.now()
130 const lista = await read($, muestras)
131 const ultima = lista[lista.length - 1]
132 const ttl = await read($, ttlActual)
133 const resta = ultima ? restanteMs(ultima, ttl, t) : 0
134 const marca = `${Math.floor(t / 60_000)}|${Math.ceil(resta / 1000)}`
135 if (marca !== marcaReloj) {
136 marcaReloj = marca
137 await update($, ahora, () => t)
138 }
139 if (ultima && tokensPrompt(ultima) >= AVISAR_DESDE && resta > 0 && resta <= AVISO_CACHE_MS && avisadaCache !== ultima.inicio) {
140 avisadaCache = ultima.inicio
141 $.ui.toast(`La caché vence en ${reloj(resta)}: cualquier mensaje la renueva; si no, se reescriben ${tokens(tokensPrompt(ultima))}`)
142 }
143 })
144 return next(e)
145 })
146
147 on('session.measure', async ($, e, next) => {
148 const m = await medir($)
149 const cinco = m.limites.find(l => l.kind === 'five_hour')
150 if (cinco) {
151 const ya = await read($, avisado)
152 const umbral = cinco.percentUsed >= 95 ? 95 : cinco.percentUsed >= 80 ? 80 : 0
153 if (umbral > ya) $.ui.toast(`Límite de 5 h al ${cinco.percentUsed}%: se reinicia en ${reinicio(cinco, Date.now(), false)}`)
154 if (umbral !== ya) await update($, avisado, () => umbral)
155 }
156 return next(e)
157 })
158
159 // Cada petición del hilo principal (los subagentes tienen su propio prefijo):
160 // modelo, esfuerzo y qué hizo la caché con ella.
161 on('turn.step', async function* ($, e, next) {
162 if (e.agentId) return yield* next(e)
163 const inicio = Date.now()
164 await update($, modelo, () => e.model)
165 await update($, esfuerzo, () => (e.effort === undefined ? null : String(e.effort)))
166 const r = yield* next(e)
167 if (r.usage) {
168 const nueva: Muestra = {
169 modelo: r.usage.model || e.model,
170 inicio,
171 leido: r.usage.cache_read_input_tokens,
172 escrito: r.usage.cache_creation_input_tokens,
173 nuevo: r.usage.input_tokens,
174 }
175 const lista = [...(await read($, muestras)), nueva].slice(-2)
176 await update($, muestras, () => lista)
177 // La cuenta puede cambiar en plena sesión (suscripción agotada → créditos).
178 const base = decidirTtl(env, ((await read($, medida))?.limites) ?? [])
179 observado = observarTtl(lista[0] === nueva ? undefined : lista[0], nueva, observado)
180 await update($, ttlActual, () => observado ?? base.ttl)
181 await update($, ttlFuente, () => (observado ? 'observado en el tráfico' : base.fuente))
182 }
183 return r
184 })
185
186 on('command.run', { command: 'medidor' }, async ($, e) => {
187 const pedido = e.args.trim().toLowerCase() as Modo
188 const actual = await read($, modo)
189 const nuevo = MODOS.includes(pedido) ? pedido : MODOS[(MODOS.indexOf(actual) + 1) % MODOS.length]
190 await update($, modo, () => nuevo)
191 await medir($)
192 const ttl = await read($, ttlActual)
193 const fuente = await read($, ttlFuente)
194 return { text: `Medidor en modo ${nuevo}. Caché de ${ttl === '1h' ? '1 h' : '5 min'} (${fuente}).` }
195 })
196
197 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
198 const m = await read($, medida)
199 const vista = await read($, modo)
200 // Solo en la terminal: en la app de escritorio lo muestra el panel de agent-bots (si lo tienes instalado).
201 if (e.surface !== 'terminal') return next(e)
202 if (e.props.hasSurvey || vista === 'oculto' || m === null) return next(e)
203
204 const { Box, Text } = $.ui.resolve(e)
205 const t = (await read($, ahora)) || Date.now()
206 const id = await read($, modelo)
207 const esf = await read($, esfuerzo)
208 const ancho = e.viewport?.columns ?? 120
209 const compacto = vista === 'compacto' || ancho < 100
210 const celdas = compacto ? 5 : 8
211 const sep = <Text dimColor> │ </Text>
212
213 const ctx = m.percent
214 const [cl, cv] = barra(ctx, celdas)
215 const cinco = m.limites.find(l => l.kind === 'five_hour')
216 const semana = m.limites.find(l => l.kind === 'seven_day')
217 const otros = m.limites.filter(l => l.kind !== 'five_hour' && l.kind !== 'seven_day')
218
219 // Caché de prompts
220 const lista = await read($, muestras)
221 const ultima = lista[lista.length - 1]
222 const previa = lista.length > 1 ? lista[0] : undefined
223 const ttl = await read($, ttlActual)
224 const consejo = aconsejar(ultima, previa, ttl, t, AVISO_CACHE_MS, COMPACTAR_DESDE)
225 const resta = ultima ? restanteMs(ultima, ttl, t) : 0
226 const pctCache = ultima ? Math.round(aciertos(ultima) * 100) : undefined
227 const colorReloj = resta <= AVISO_CACHE_MS ? 'red' : resta / (ttl === '1h' ? 3_600_000 : 300_000) <= 0.4 ? 'yellow' : 'green'
228 const [kl, kv] = barra(pctCache, celdas)
229
230 const limite = (etq: string, v: Ventana | undefined, conDia: boolean) => {
231 if (!v) return <Text dimColor>{etq} —</Text>
232 const [ll, lv] = barra(v.percentUsed, celdas)
233 return (
234 <Text>
235 <Text bold>{etq} </Text>
236 <Text color={color(v.percentUsed)}>{ll}</Text>
237 <Text dimColor>{lv}</Text>
238 <Text color={color(v.percentUsed)}> {v.percentUsed}%</Text>
239 {v.resetsAt && <Text dimColor> ↻ {reinicio(v, t, conDia)}</Text>}
240 </Text>
241 )
242 }
243
244 const cache = ultima ? (
245 <Text>
246 <Text bold>caché </Text>
247 <Text color={COLOR_CONSEJO[consejo.tipo]}>{kl}</Text>
248 <Text dimColor>{kv}</Text>
249 <Text color={COLOR_CONSEJO[consejo.tipo]}> {pctCache}%</Text>
250 {!compacto && (
251 <Text dimColor>
252 {' '}leído {tokens(ultima.leido)} · escrito {tokens(ultima.escrito)} · nuevo {tokens(ultima.nuevo)}
253 </Text>
254 )}
255 <Text color={colorReloj}> ⏱ {reloj(resta)}</Text>
256 <Text dimColor> {ttl === '1h' ? '1h' : '5m'}</Text>
257 <Text color={COLOR_CONSEJO[consejo.tipo]}> {ICONO_CONSEJO[consejo.tipo]} </Text>
258 <Text color={consejo.tipo === 'caliente' ? undefined : COLOR_CONSEJO[consejo.tipo]} dimColor={consejo.tipo === 'caliente'}>
259 {compacto ? consejo.texto.split(':')[0] : consejo.texto}
260 </Text>
261 </Text>
262 ) : (
263 <Text dimColor>caché ○ {consejo.texto}</Text>
264 )
265
266 return (
267 <Box flexDirection="column">
268 <Box flexDirection="row" flexWrap="wrap">
269 <Text>
270 <Text color="magenta" bold>◆ {nombreModelo(id)}</Text>
271 <Text dimColor> · </Text>
272 <Text color="cyan">{esf ?? 'esfuerzo —'}</Text>
273 </Text>
274 {sep}
275 <Text>
276 <Text bold>ctx </Text>
277 <Text color={color(ctx)}>{cl}</Text>
278 <Text dimColor>{cv}</Text>
279 <Text color={color(ctx)}> {ctx === undefined ? '—' : `${ctx}%`}</Text>
280 {!compacto && m.tokens !== undefined && (
281 <Text dimColor> {tokens(m.tokens)}/{tokens(m.window)}</Text>
282 )}
283 </Text>
284 {sep}
285 {limite('5h', cinco, false)}
286 {sep}
287 {limite('7d', semana, true)}
288 {otros.map(o => (
289 <Text key={o.kind}>
290 <Text dimColor> │ </Text>
291 {limite(o.kind, o, true)}
292 </Text>
293 ))}
294 {!compacto && (
295 <Text>
296 <Text dimColor> │ </Text>
297 {m.usd !== undefined && <Text color="green">${m.usd.toFixed(2)}</Text>}
298 <Text dimColor> · {duracion(t - m.startedAt)}</Text>
299 </Text>
300 )}
301 </Box>
302 {cache}
303 </Box>
304 )
305 })
306}
307hooks/cache.ts 111 lines1// Lógica pura de la caché de prompts (sin `$`). Ideas tomadas de
2// davila7/claude-code-templates · observability/prompt-cache-control (MIT,
3// commit 8da17d67), reescritas y simplificadas para el medidor:
4// - la caché vive 5 min por defecto y 1 h si se pide; cada lectura la renueva
5// gratis y la vida se cuenta desde el INICIO de la petición
6// - prompt = input_tokens (sin caché) + cache_read + cache_creation
7// - escribir cuesta 1,25× (5 min) o 2× (1 h) la entrada; leer ≈ 0,1×: lo caro es
8// que venza con un contexto grande
9// - un cambio del prefijo (modelo, esfuerzo, herramientas, system prompt,
10// CLAUDE.md) hace que la siguiente petición escriba en vez de leer
11import type { Muestra, Ttl } from '../types'
12
13export type Consejo = {
14 tipo: 'fria' | 'sin-cache' | 'caliente' | 'pronto' | 'vencida' | 'fallo'
15 texto: string
16}
17
18const HOLGURA_MS = 10_000
19
20export const ttlMs = (ttl: Ttl) => (ttl === '1h' ? 3_600_000 : 300_000)
21export const tokensPrompt = (m: Muestra) => m.leido + m.escrito + m.nuevo
22export const aciertos = (m: Muestra) => (tokensPrompt(m) === 0 ? 0 : m.leido / tokensPrompt(m))
23
24export function restanteMs(m: Muestra, ttl: Ttl, ahora: number): number {
25 if (m.leido + m.escrito === 0) return 0
26 return Math.max(0, m.inicio + ttlMs(ttl) - ahora)
27}
28
29/** Duración que pide Claude Code según el entorno y la cuenta (orden de su documentación). */
30export function decidirTtl(
31 env: { force5m?: string; ttlVar?: string; enable1h?: string },
32 limites: { kind: string; percentUsed: number }[],
33): { ttl: Ttl; fuente: string } {
34 const si = (v?: string) => v === '1' || v?.toLowerCase() === 'true'
35 if (si(env.force5m)) return { ttl: '5m', fuente: 'FORCE_PROMPT_CACHING_5M' }
36 if (env.ttlVar === '5m' || env.ttlVar === '1h') return { ttl: env.ttlVar, fuente: 'CLAUDE_CODE_PROMPT_CACHE_TTL' }
37 if (si(env.enable1h)) return { ttl: '1h', fuente: 'ENABLE_PROMPT_CACHING_1H' }
38 const plan = limites.filter(l => l.kind === 'five_hour' || l.kind === 'seven_day')
39 if (plan.length === 0) return { ttl: '5m', fuente: 'sin suscripción visible' }
40 if (plan.some(l => l.percentUsed >= 100)) return { ttl: '5m', fuente: 'créditos de uso' }
41 return { ttl: '1h', fuente: 'suscripción' }
42}
43
44/**
45 * Lo que el tráfico dice de la duración: un acierto más de 5 min después de la
46 * petición anterior prueba 1 h; un fallo entre 5 y 60 min con el mismo modelo y
47 * un prompt que no encogió apunta a 5 min (más débil: un prefijo cambiado se ve igual).
48 */
49export function observarTtl(prev: Muestra | undefined, cur: Muestra, conocido: Ttl | undefined): Ttl | undefined {
50 if (!prev || prev.leido + prev.escrito === 0 || cur.modelo !== prev.modelo) return conocido
51 const hueco = cur.inicio - prev.inicio
52 const antes = tokensPrompt(prev)
53 if (hueco <= ttlMs('5m') + HOLGURA_MS) return conocido
54 if (cur.leido >= antes * 0.5) return '1h'
55 if (conocido === '1h') return conocido
56 const vencio = cur.escrito > 0 && tokensPrompt(cur) >= antes * 0.7 && hueco < ttlMs('1h') + HOLGURA_MS
57 return vencio ? '5m' : conocido
58}
59
60/** Por qué una petición que debía leer la caché la escribió; undefined si no falló. */
61export function causaFallo(prev: Muestra | undefined, cur: Muestra, ttl: Ttl): string | undefined {
62 if (!prev) return undefined
63 const antes = tokensPrompt(prev)
64 // Un prompt que encogió es /compact o /clear, no un fallo.
65 if (antes === 0 || tokensPrompt(cur) < antes * 0.7) return undefined
66 if (cur.leido >= antes * 0.5 || cur.escrito === 0) return undefined
67 if (cur.modelo !== prev.modelo) return 'cambió el modelo'
68 if (cur.inicio - prev.inicio > ttlMs(ttl)) return `había vencido (${ttl === '1h' ? '1 h' : '5 min'})`
69 return 'cambió el prefijo (esfuerzo, herramientas, system prompt o CLAUDE.md)'
70}
71
72export function aconsejar(
73 ultima: Muestra | undefined,
74 previa: Muestra | undefined,
75 ttl: Ttl,
76 ahora: number,
77 avisoMs: number,
78 compactarDesde: number,
79): Consejo {
80 if (!ultima) return { tipo: 'fria', texto: 'sin peticiones aún: la primera escribe la caché' }
81 if (ultima.leido + ultima.escrito === 0) return { tipo: 'sin-cache', texto: 'esta petición no usó caché' }
82 const resta = restanteMs(ultima, ttl, ahora)
83 const tam = tokensPrompt(ultima)
84 if (resta <= 0) {
85 return tam >= compactarDesde
86 ? { tipo: 'vencida', texto: `vencida: el próximo mensaje reescribe ${tokens(tam)}; /compact antes, o /clear si terminaste` }
87 : { tipo: 'vencida', texto: `vencida: solo ${tokens(tam)} por reconstruir, sigue` }
88 }
89 if (resta <= avisoMs) return { tipo: 'pronto', texto: 'vence pronto: cualquier mensaje la renueva gratis' }
90 const fallo = causaFallo(previa, ultima, ttl)
91 if (fallo) return { tipo: 'fallo', texto: `falló: ${fallo}` }
92 return { tipo: 'caliente', texto: 'caliente: sigue' }
93}
94
95export function tokens(n: number): string {
96 if (n < 1000) return String(n)
97 if (n < 100_000) return `${(n / 1000).toFixed(1).replace(/\.0$/, '')}k`
98 if (n < 1_000_000) return `${Math.round(n / 1000)}k`
99 return `${(n / 1_000_000).toFixed(1).replace(/\.0$/, '')}M`
100}
101
102/** m:ss, o h:mm:ss desde una hora. */
103export function reloj(ms: number): string {
104 const t = Math.max(0, Math.ceil(ms / 1000))
105 const h = Math.floor(t / 3600)
106 const m = Math.floor((t % 3600) / 60)
107 const s = t % 60
108 const dos = (n: number) => String(n).padStart(2, '0')
109 return h > 0 ? `${h}:${dos(m)}:${dos(s)}` : `${m}:${dos(s)}`
110}
111types/index.d.ts 41 lines1export type Ventana = { kind: string; percentUsed: number; resetsAt?: string }
2
3export type Medida = {
4 startedAt: number
5 tokens?: number
6 window: number
7 percent?: number
8 limites: Ventana[]
9 usd?: number
10}
11
12export type Modo = 'completo' | 'compacto' | 'oculto'
13
14export type Ttl = '5m' | '1h'
15
16/** Una petición del hilo principal, como la reportó la API. */
17export type Muestra = {
18 modelo: string
19 /** ms desde la época al empezar la petición: la vida de la caché cuenta desde aquí */
20 inicio: number
21 leido: number
22 escrito: number
23 nuevo: number
24}
25
26declare module 'claude-code' {
27 interface PluginState {
28 medidor: {
29 medida: Medida | null
30 modelo: string
31 esfuerzo: string | null
32 ahora: number
33 modo: Modo
34 avisado: number
35 muestras: Muestra[]
36 ttl: Ttl
37 ttlFuente: string
38 }
39 }
40}
41