SLOPSHOPPER

previsao-do-contexto

Previsão do tempo da janela de contexto, numa linha acima do prompt.

newband
v0.3.0no licenseupdated 2026-10-10dandevddqm/automatizacoes/previsao-do-contexto
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · previsao-do-contexto
› fix the failing auth test and add an audit log call ⏺ Read(src/auth.ts) ⎿ Read 6 lines ⏺ Update(src/auth.ts) ⎿ Added 2 lines, removed 1 line ⏺ Bash(bun test) ⎿ 3 pass, 1 fail ● Done. refresh now rejects expired claims and logs an audit event. ✻ Worked for 42s · done 4:20 PM ⟨Claude Code's own drawing⟩ ⛅ Nublado 49% · 97k / 200k ▄ +97k no último turno ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts

Draws

Band
⟨Claude Code's own drawing⟩ ⛅ Nublado 49% · 97k / 200k ▄ +97k no último turno
README

automatizacoes

Mods do Claude Code, em português.

ModO que faz
previsao-do-contextoPrevisão do tempo da janela de contexto: ícone e palavra por faixa de uso (🌞 Limpo, ⛅ Nublado, ☔ Chuva, ⚡ Tempestade, 🔥 Compacta logo), porcentagem e tokens, mini gráfico dos últimos 12 turnos e quanto o último turno somou.
u (pasta task-progress)Barra da tarefa: etapas E x/y, progresso T, tempo decorrido e estimado, e uso das janelas de 5 horas e semanal na linha de status.
recomendar-perguntasAo fazer uma recomendação, o Claude abre uma janela com 3 a 5 perguntas (ou mais), com opções, antes de seguir.

Com u e previsao-do-contexto instalados, a barra da tarefa e a previsão aparecem na mesma linha, as tarefas primeiro. O previsao-do-contexto funciona sozinho; sem o u, mostra só a previsão.

Instalação

No prompt de uma sessão do Claude Code no terminal (aceite com y adicionar o marketplace e escolha o escopo user):

/plugin install previsao-do-contexto --marketplace dandevddqm/automatizacoes
/plugin install u --marketplace dandevddqm/automatizacoes
/plugin install recomendar-perguntas --marketplace dandevddqm/automatizacoes

Depois de instalado no terminal, no escopo de usuário, o mod também carrega no Claude Code desktop dessa máquina. O comando /plugin install não funciona dentro do desktop.

Os mods ficam em arquivos locais de cada computador: instale em cada máquina onde quiser usá-los.

Atualizar

Ao mudar um mod, suba a versão no plugin.json dele; sem isso o Claude Code continua usando a cópia em cache.

claude plugin marketplace update automatizacoes
claude plugin update previsao-do-contexto@automatizacoes --scope user

Testes

claude plugin validate previsao-do-contexto
claude plugin test previsao-do-contexto
Source 4 files
hooks/register.tsx 68 lines
1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4import type { Previsao } from '../types'
5import { juntar } from './faixa'
6import { barrinhas, clima, formatarTokens, textoDelta } from './clima'
7
8const previsao = atom({ plugin: 'previsao-do-contexto', key: 'previsao' } as const, null)
9
10const TURNOS = 12
11
12export const register: Register = on => {
13  on('turn.complete', async ($, e, next) => {
14    // Só a conversa principal; subagentes têm a própria janela.
15    if (e.agentId === undefined) {
16      const { context } = await $.session.usage()
17      const tokens = context.tokens ?? 0
18      const percentual = Math.round(context.percent ?? (context.window > 0 ? (tokens / context.window) * 100 : 0))
19
20      await update($, previsao, (anterior: Previsao | null): Previsao => ({
21        tokens,
22        janela: context.window,
23        percentual,
24        delta: tokens - (anterior?.tokens ?? 0),
25        historico: [...(anterior?.historico ?? []), percentual].slice(-TURNOS),
26      }))
27    }
28
29    return next(e)
30  })
31
32  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
33    // A faixa acima do prompt é uma só e este hook é o mais externo: o que outro
34    // mod desenha abaixo (a barra de tarefas do task-progress, por exemplo) vem
35    // em `next` e é mantido, na mesma linha, antes da previsão.
36    const abaixo = await next(e)
37    const atual = await read($, previsao)
38
39    if (e.props.hasSurvey || atual === null) {
40      return abaixo
41    }
42
43    const { Box, Text } = $.ui.resolve(e)
44    const { simbolo, palavra, cor } = clima(atual.percentual)
45
46    const linha = (
47      <Box>
48        <Text color={cor} bold>
49          {simbolo} {palavra}
50        </Text>
51        <Text dimColor>
52          {'  '}
53          {atual.percentual}% · {formatarTokens(atual.tokens)} / {formatarTokens(atual.janela)}
54          {'  '}
55        </Text>
56        <Text color={cor}>{barrinhas(atual.historico)}</Text>
57        <Text dimColor>
58          {'  '}
59          {textoDelta(atual.delta)}
60        </Text>
61      </Box>
62    )
63
64    // As tarefas do outro mod ficam sempre em primeiro lugar, na mesma linha da previsão.
65    return abaixo ? (juntar(abaixo, linha) as typeof linha) : linha
66  })
67}
68
hooks/faixa.ts 32 lines
1type No = { type?: string; props?: Record<string, string | number | boolean>; children?: unknown[] }
2
3const isNo = (x: unknown): x is No => typeof x === 'object' && x !== null
4
5/**
6 * Junta, numa só linha, o que outro mod desenhou na faixa (`abaixo`) com a previsão (`linha`):
7 * a barra do outro mod vem primeiro, a previsão depois, e o botão de fechar dele, se houver,
8 * continua no fim. Se o desenho do outro mod não for um Box com botão no final, os dois ficam
9 * lado a lado do mesmo jeito.
10 */
11export const juntar = (abaixo: unknown, linha: unknown): No => {
12  const grupo = (filhos: unknown[]): No => ({
13    type: 'Box',
14    props: { flexDirection: 'row', flexShrink: 1, overflow: 'hidden', gap: 2 },
15    children: filhos,
16  })
17
18  if (isNo(abaixo) && abaixo.type === 'Box' && Array.isArray(abaixo.children) && abaixo.children.length > 0) {
19    const ultimo = abaixo.children[abaixo.children.length - 1]
20
21    if (isNo(ultimo) && ultimo.type === 'Button') {
22      return {
23        type: 'Box',
24        props: abaixo.props,
25        children: [grupo([...abaixo.children.slice(0, -1), linha]), ultimo],
26      }
27    }
28  }
29
30  return { type: 'Box', props: { flexDirection: 'row', gap: 2, overflow: 'hidden' }, children: [abaixo, linha] }
31}
32
hooks/clima.ts 27 lines
1const BARRAS = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█']
2
3export type Clima = { simbolo: string; palavra: string; cor: string }
4
5export const clima = (percentual: number): Clima => {
6  if (percentual >= 90) return { simbolo: '🔥', palavra: 'Compacta logo', cor: 'red' }
7  if (percentual >= 75) return { simbolo: '⚡', palavra: 'Tempestade', cor: 'magenta' }
8  if (percentual >= 50) return { simbolo: '☔', palavra: 'Chuva', cor: 'blue' }
9  if (percentual >= 25) return { simbolo: '⛅', palavra: 'Nublado', cor: 'cyan' }
10  return { simbolo: '🌞', palavra: 'Limpo', cor: 'yellow' }
11}
12
13export const formatarTokens = (n: number): string => {
14  const abs = Math.abs(n)
15  if (abs >= 1_000_000) return `${(abs / 1_000_000).toFixed(abs % 1_000_000 === 0 ? 0 : 1)}M`
16  if (abs >= 1_000) return `${Math.round(abs / 1_000)}k`
17  return String(abs)
18}
19
20export const barrinhas = (historico: readonly number[]): string =>
21  historico
22    .map(p => BARRAS[Math.min(BARRAS.length - 1, Math.max(0, Math.floor((p / 100) * BARRAS.length)))])
23    .join('')
24
25export const textoDelta = (delta: number): string =>
26  `${delta < 0 ? '-' : '+'}${formatarTokens(delta)} no último turno`
27
types/index.d.ts 19 lines
1export type Previsao = {
2  /** Tokens usados na janela ao fim do último turno. */
3  tokens: number
4  /** Tamanho da janela de contexto do modelo. */
5  janela: number
6  /** Percentual usado, 0 a 100. */
7  percentual: number
8  /** Quanto o último turno somou (negativo após compactar ou limpar). */
9  delta: number
10  /** Percentual usado ao fim de cada um dos últimos 12 turnos. */
11  historico: number[]
12}
13
14declare module 'claude-code' {
15  interface PluginState {
16    'previsao-do-contexto': { previsao: Previsao | null }
17  }
18}
19