Painel lateral com todas as sessões ativas: trabalhando, concluída ou esperando resposta. Abra com /sessoes.

Mods que eu mesmo criei para o Claude Code (interface e comportamento: painéis, comandos, hooks). Fonte completa de cada um.
| Mod | Versão | O que faz | Pasta |
|---|---|---|---|
assumption-session | 0.1.0 | Lista as suposições que o Claude faz enquanto constrói, num painel onde você aprova ou corrige. Abra com /suposicoes. | mods/assumption-session |
cofre-chaves | 0.1.0 | Guarda chaves no .env pelo /chave e impede que elas passem pelo chat, pelas ferramentas ou pela saída delas | mods/cofre-chaves |
comandos-mod | 0.1.0 | /comandosmod abre um painel lateral com todos os seus mods e o comando de cada um. | mods/comandos-mod |
plain-answers | 0.1.0 | Faz o Claude responder em linguagem simples, sem jargão. Liga/desliga com /simples. | mods/plain-answers |
prompt-enhance | 0.1.0 | Melhora prompts vagos usando as instruções do projeto e as skills, troca o texto na caixa e só envia quando você clicar em Enviar. | mods/prompt-enhance |
session-dashboard | 0.1.0 | Painel lateral com todas as sessões ativas: trabalhando, concluída ou esperando resposta. Abra com /sessoes. | mods/session-dashboard |
Cada pasta é um plugin do Claude Code (.claude-plugin/plugin.json, hooks/). Para carregar um mod, aponte para a pasta dele pela variável CLAUDE_CODE_PLUGIN_DIRS no settings.json (bloco env), conforme a documentação do Claude Code. Os mods são em português.
Antes de usar, leia o código de cada um. Os mods rodam hooks com acesso aos arquivos do seu projeto.
.claude-plugin/types) não foram incluídos. Para compilar, rode o tsc no mod (há um tsconfig.json em cada um).cofre-chaves não guarda chaves neste repo: o código só contém os padrões usados para detectar chaves e testes com valores fictícios.Não copiei o código destes. Estão listados no repositório claude-code-plugins, com a fonte e o commit exato:
skins (hellosverre/claude-skins)mods-manager (ptpmediabr/mods-manager)usage-weather (ptpmediabr/usage-weather)ideas-shelf (ptpmediabr/ideas-shelf)side-chat (ptpmediabr/side-chat)hooks/register.tsx 95 lines1import type { Register } from 'claude-code'
2
3const PANE = 'sessions'
4const DIR = `${process.env.HOME}/.claude/session-dashboard`
5
6type Row = { id: string; title: string; cwd: string; state: 'working' | 'waiting' | 'done' | 'ended'; at: number }
7
8const LABEL = { working: 'trabalhando', waiting: 'esperando resposta', done: 'concluída', ended: 'encerrada' } as const
9const COLOR = { working: 'yellow', waiting: 'red', done: 'green', ended: 'gray' } as const
10
11let me: Row | null = null
12
13async function save($: any, state: Row['state']) {
14 if (!me) return
15 me = { ...me, state, at: await $.clock.now() }
16 await $.fs.write(`${DIR}/${me.id}.json`, JSON.stringify(me))
17}
18
19export const register: Register = on => {
20 on('session.start', async ($, e, next) => {
21 me = { id: await $.session.id(), title: 'nova sessão', cwd: await $.session.cwd(), state: 'done', at: 0 }
22 await save($, 'done')
23 await $.command.register({ name: 'sessoes', description: 'Painel com as sessões ativas e o status de cada uma' })
24 $.clock.every(2000, () => $.ui.invalidate('ui.render'))
25
26 return next(e)
27 })
28
29 on('command.run', { command: 'sessoes' }, async $ => {
30 await $.ui.open({ id: PANE, title: 'Sessões' })
31
32 return { text: 'Painel de sessões aberto.' }
33 })
34
35 on('prompt.submit', async ($, e, next) => {
36 if (me) me = { ...me, title: e.text.replace(/\s+/g, ' ').slice(0, 60) }
37 await save($, 'working')
38
39 return next(e)
40 })
41
42 on('tool.call', { tool: 'AskUserQuestion' }, async ($, e, next) => {
43 await save($, 'waiting')
44 const r = await next(e)
45 await save($, 'working')
46
47 return r
48 })
49
50 on('turn.complete', async ($, e, next) => {
51 if (e.agentId === undefined) await save($, 'done')
52
53 return next(e)
54 })
55
56 on('session.end', async ($, e, next) => {
57 await save($, 'ended')
58
59 return next(e)
60 })
61
62 on('ui.render', { component: 'Pane', requestId: PANE }, async ($, e) => {
63 const { Box, Text } = $.ui.resolve(e)
64 const now = await $.clock.now()
65 const rows: Row[] = []
66 for (const f of await $.fs.list(DIR).catch(() => [])) {
67 if (!f.name.endsWith('.json')) continue
68 try {
69 rows.push(JSON.parse(await $.fs.read(`${DIR}/${f.name}`)))
70 } catch {}
71 }
72 const live = rows.filter(r => r.state !== 'ended' || now - r.at < 10 * 60_000).sort((a, b) => b.at - a.at)
73 const count = (s: Row['state']) => live.filter(r => r.state === s).length
74
75 return (
76 <Box flexDirection="column">
77 <Text bold>
78 {count('working')} trabalhando · {count('waiting')} esperando · {count('done')} concluídas
79 </Text>
80 {live.length === 0 && <Text dimColor>Nenhuma sessão ainda.</Text>}
81 {live.map(r => (
82 <Box key={r.id} flexDirection="column" marginTop={1}>
83 <Text color={COLOR[r.state]}>
84 ● {LABEL[r.state]}
85 {r.id === me?.id ? ' (esta)' : ''}
86 </Text>
87 <Text>{r.title}</Text>
88 <Text dimColor>{r.cwd.split('/').slice(-2).join('/')}</Text>
89 </Box>
90 ))}
91 </Box>
92 )
93 })
94}
95