Pill de contexto sob o prompt, borda laranja nas suas mensagens e ícones coloridos por tipo de execução

Mods pessoais para Claude Code (terminal e aba Code do app desktop).
| Mod | O que faz |
|---|---|
agentes-pixel | Painel lateral com pixel art de cada subagent (modelo, status, tempo, tokens, resumo), bolinhas de status das tarefas e barra do plano acima do prompt. /agentes reabre o painel. |
polimento-visual | Pill de contexto usado/total abaixo do prompt, borda laranja nas suas mensagens e ícones coloridos por tipo de ferramenta. |
claude plugin marketplace add DiogoHSM/claude-mods
claude plugin install agentes-pixel@diogo-mods --scope user
claude plugin install polimento-visual@diogo-mods --scope user
Ou, no prompt do Claude Code:
/plugin install agentes-pixel --marketplace DiogoHSM/claude-mods
/plugin install polimento-visual --marketplace DiogoHSM/claude-mods
No app desktop: + ao lado do prompt → Plugins → Add marketplace com DiogoHSM/claude-mods → Add plugin.
claude plugin marketplace add ~/Developer/Github/claude-mods
claude plugin install agentes-pixel@diogo-mods --scope user
Depois de editar um mod, rode /reload-plugins.
claude plugin validate agentes-pixel
claude plugin test agentes-pixelhooks/register.tsx 216 lines1import { atom, read, update } from 'claude-code'
2import type { EngineInterface, Register } from 'claude-code'
3
4const context = atom({ plugin: 'polimento-visual', key: 'context' } as const, null)
5
6const PERSON = new Set(['composer', 'bridge', 'sdk'])
7
8type Kind = 'read' | 'search' | 'edit' | 'shell' | 'web' | 'agent' | 'plan' | 'skill' | 'mcp' | 'other'
9
10const COLOR: Record<Kind, string> = {
11 read: '#378ADD',
12 search: '#378ADD',
13 edit: '#D4537E',
14 shell: '#888780',
15 web: '#1D9E75',
16 agent: '#7F77DD',
17 plan: '#639922',
18 skill: '#D85A30',
19 mcp: '#BA7517',
20 other: '#888780',
21}
22
23const GLYPH: Record<Kind, string> = {
24 read: '▤',
25 search: '⌕',
26 edit: '✎',
27 shell: '›_',
28 web: '◍',
29 agent: '◉',
30 plan: '☰',
31 skill: '✦',
32 mcp: '⧉',
33 other: '•',
34}
35
36const PATHS: Record<Kind, string> = {
37 read: '<path d="M5 2.5h4.5L12 5v8.5H5z"/><path d="M9.5 2.5V5H12M7 8h3M7 10.5h3"/>',
38 search: '<circle cx="7.5" cy="7.5" r="3.5"/><path d="M10 10l3 3"/>',
39 edit: '<path d="M3.5 12.5l1-3 6-6 2 2-6 6z"/><path d="M9.5 4.5l2 2"/>',
40 shell: '<path d="M3.5 5l3 3-3 3M8 11.5h4.5"/>',
41 web: '<circle cx="8" cy="8" r="5"/><path d="M3 8h10M8 3c-2 2.5-2 7.5 0 10M8 3c2 2.5 2 7.5 0 10"/>',
42 agent: '<rect x="3.5" y="5" width="9" height="7" rx="2"/><path d="M8 2.5V5M6.5 8.5v.5M9.5 8.5v.5"/>',
43 plan: '<path d="M3 4.5l1 1 1.5-2M3 9.5l1 1 1.5-2M7.5 4.5h5.5M7.5 9.5h5.5M7.5 13h5.5"/>',
44 skill: '<path d="M8 2.5l1.5 4 4 1.5-4 1.5-1.5 4-1.5-4-4-1.5 4-1.5z"/>',
45 mcp: '<path d="M6 2.5v3M10 2.5v3M4.5 5.5h7v2a3.5 3.5 0 0 1-7 0zM8 11v2.5"/>',
46 other: '<circle cx="8" cy="8" r="2"/>',
47}
48
49function kindOf(tool: string): Kind {
50 if (['Read', 'NotebookRead', 'LS'].includes(tool)) return 'read'
51 if (['Grep', 'Glob', 'ToolSearch', 'LSP'].includes(tool)) return 'search'
52 if (['Edit', 'Write', 'MultiEdit', 'NotebookEdit'].includes(tool)) return 'edit'
53 if (['Bash', 'BashOutput', 'KillShell', 'Monitor'].includes(tool)) return 'shell'
54 if (['WebFetch', 'WebSearch'].includes(tool) || /^mcp__(Claude_Browser|claude-in-chrome|plugin_playwright|plugin_chrome-devtools)/.test(tool)) {
55 return 'web'
56 }
57 if (['Agent', 'Task', 'SendMessage', 'Workflow', 'TaskStop'].includes(tool)) return 'agent'
58 if (['TaskCreate', 'TaskUpdate', 'TaskList', 'TaskGet', 'TodoWrite', 'EnterPlanMode', 'ExitPlanMode'].includes(tool) || tool.startsWith('mcp__ccd_turn__')) {
59 return 'plan'
60 }
61 if (tool === 'Skill') return 'skill'
62 if (tool.startsWith('mcp__')) return 'mcp'
63
64 return 'other'
65}
66
67function icon(kind: Kind, color = COLOR[kind]): string {
68
69 return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="18" height="18"><rect x="0" y="0" width="16" height="16" rx="4" fill="${color}" fill-opacity="0.16"/><g fill="none" stroke="${color}" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round">${PATHS[kind]}</g></svg>`
70}
71
72function short(n: number): string {
73 if (n < 1000) return `${n}`
74 if (n < 1_000_000) return `${Math.round(n / 1000)}k`
75
76 return `${(n / 1_000_000).toFixed(n % 1_000_000 === 0 ? 0 : 1)}M`
77}
78
79async function refreshContext($: EngineInterface) {
80 const usage = await $.session.usage()
81 const { tokens, window } = usage.context
82 if (tokens === undefined || !window) return
83 const percent = usage.context.percent ?? (tokens / window) * 100
84 await update($, context, () => ({ tokens, window, percent }))
85}
86
87export const register: Register = on => {
88 on('session.start', async ($, e, next) => {
89 const started = await next(e)
90 void refreshContext($)
91
92 return started
93 })
94
95 on('turn.step', async function* ($, e, next) {
96 const step = yield* next(e)
97 if (e.agentId === undefined) void refreshContext($)
98
99 return step
100 })
101
102 on('turn.complete', async ($, e, next) => {
103 const done = await next(e)
104 if (e.agentId === undefined) void refreshContext($)
105
106 return done
107 })
108
109 on('ui.render', { component: 'PromptHint' }, async ($, e, next) => {
110 const use = await read($, context)
111 const own = await next(e)
112 if (!use) return own
113 const { Box, Text } = $.ui.resolve(e)
114 const tone = use.percent < 50 ? 'success' : use.percent < 80 ? 'warning' : 'error'
115
116 return (
117 <Box flexDirection="row" justifyContent="space-between" alignItems="center" gap={1}>
118 <Box flexShrink={1}>
119 {own}
120 </Box>
121 <Box borderStyle="round" borderColor="subtle" paddingX={1} flexShrink={0}>
122 <Text size="caption" tabular>
123 <Text color={tone}>●</Text>{' '}
124 <Text dimColor>{`${short(use.tokens)} / ${short(use.window)} · ${Math.round(use.percent)}%`}</Text>
125 </Text>
126 </Box>
127 </Box>
128 )
129 })
130
131 on('ui.render', { component: 'UserMessage' }, async ($, e, next) => {
132 const own = await next(e)
133 if (!PERSON.has(e.props.origin.kind) || e.props.from !== undefined) return own
134 const { Box, Text } = $.ui.resolve(e)
135 if (e.surface === 'terminal') {
136 return (
137 <Box borderStyle="round" borderColor="claude" paddingX={1}>
138 {own}
139 </Box>
140 )
141 }
142
143 return (
144 <Box flexDirection="row" marginY={1}>
145 <Box flexShrink={1} borderStyle="round" borderColor="claude" paddingX={1}>
146 <Text>{e.props.text}</Text>
147 </Box>
148 </Box>
149 )
150 })
151
152 on('ui.render', { component: 'AssistantMessage' }, async ($, e, next) => {
153 const own = await next(e)
154 if (!e.props.isFirstOfReply) return own
155 const { Box, Text } = $.ui.resolve(e)
156 const Svg = e.surface === 'terminal' ? undefined : $.ui.resolve(e).Svg
157
158 return (
159 <Box flexDirection="row" gap={1} alignItems="flex-start">
160 {Svg ? (
161 <Svg source={icon('skill')} alt="Resposta do Claude" width={18} height={18} />
162 ) : (
163 <Text color="claude">{GLYPH.skill}</Text>
164 )}
165 <Box flexGrow={1} flexShrink={1}>
166 {own}
167 </Box>
168 </Box>
169 )
170 })
171
172 on('ui.render', { component: 'ToolUse' }, async ($, e, next) => {
173 const own = await next(e)
174 if (e.props.tool === 'mcp__ccd_turn__update_plan') return own
175 const kind = kindOf(e.props.tool)
176 const color = e.props.isErrored ? '#E24B4A' : COLOR[kind]
177 const { Box, Text } = $.ui.resolve(e)
178 const Svg = e.surface === 'terminal' ? undefined : $.ui.resolve(e).Svg
179
180 return (
181 <Box flexDirection="row" gap={1} alignItems="flex-start">
182 {Svg ? (
183 <Svg source={icon(kind, color)} alt={e.props.tool} width={18} height={18} />
184 ) : (
185 <Text color={color}>{GLYPH[kind]}</Text>
186 )}
187 <Box flexGrow={1} flexShrink={1}>
188 {own}
189 </Box>
190 </Box>
191 )
192 })
193
194 on('ui.render', { component: 'ToolGroup' }, async ($, e, next) => {
195 const own = await next(e)
196 const first = e.props.calls[0]
197 if (!first) return own
198 const kind = kindOf(first.tool)
199 const { Box, Text } = $.ui.resolve(e)
200 const Svg = e.surface === 'terminal' ? undefined : $.ui.resolve(e).Svg
201
202 return (
203 <Box flexDirection="row" gap={1} alignItems="flex-start">
204 {Svg ? (
205 <Svg source={icon(kind)} alt={first.tool} width={18} height={18} />
206 ) : (
207 <Text color={COLOR[kind]}>{GLYPH[kind]}</Text>
208 )}
209 <Box flexGrow={1} flexShrink={1}>
210 {own}
211 </Box>
212 </Box>
213 )
214 })
215}
216types/index.d.ts 8 lines1export type ContextUse = { tokens: number; window: number; percent: number }
2
3declare module 'claude-code' {
4 interface PluginState {
5 'polimento-visual': { context: ContextUse | null }
6 }
7}
8