Barra de progresso das tarefas (passo atual, 3/7, cor por avanço) acima do prompt

Mods pessoais para Claude Code (terminal e aba Code do app desktop).
| Mod | O que faz |
|---|---|
task-progress | Barra de progresso acima do prompt com passo atual, contador (3/7) e cor que vai do vermelho ao verde. |
claude plugin marketplace add DiogoHSM/claude-mods
claude plugin install task-progress@diogo-mods --scope user
Ou, no prompt do Claude Code:
/plugin install task-progress --marketplace DiogoHSM/claude-mods
claude plugin marketplace add ~/Developer/Github/claude-mods
claude plugin install task-progress@diogo-mods --scope user
Depois de editar um mod, rode /reload-plugins.
claude plugin validate task-progress
claude plugin test task-progresshooks/register.tsx 108 lines1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4import type { Step } from '../types'
5
6const steps = atom({ plugin: 'task-progress', key: 'steps' } as const, [] as Step[])
7
8export function colorFor(ratio: number): string {
9 if (ratio >= 1) return '#22c55e'
10 if (ratio >= 0.75) return '#84cc16'
11 if (ratio >= 0.5) return '#eab308'
12 if (ratio >= 0.25) return '#f97316'
13 return '#ef4444'
14}
15
16
17export const register: Register = on => {
18 on('tool.call', { tool: 'TodoWrite' }, async ($, e, next) => {
19 const ran = await next(e)
20 if (ran.deny === undefined && !ran.isError) {
21 await update($, steps, () =>
22 e.todos.map((t, i) => ({ id: String(i), subject: t.content, active: t.activeForm, status: t.status })),
23 )
24 }
25 return ran
26 })
27
28 on('tool.call', { tool: 'TaskCreate' }, async ($, e, next) => {
29 const ran = await next(e)
30 if (ran.deny === undefined && !ran.isError) {
31 const id = ran.result.task.id
32 await update($, steps, s => [
33 ...(s.every(x => x.status === 'completed') ? [] : s.filter(x => x.id !== id)),
34 { id, subject: e.subject, active: e.activeForm ?? e.subject, status: 'pending' },
35 ])
36 }
37 return ran
38 })
39
40 on('tool.call', { tool: 'TaskUpdate' }, async ($, e, next) => {
41 const ran = await next(e)
42 if (ran.deny === undefined && !ran.isError) {
43 await update($, steps, s =>
44 e.status === 'deleted'
45 ? s.filter(x => x.id !== e.taskId)
46 : s.map(x =>
47 x.id === e.taskId
48 ? {
49 ...x,
50 subject: e.subject ?? x.subject,
51 active: e.activeForm ?? x.active,
52 status: e.status ?? x.status,
53 }
54 : x,
55 ),
56 )
57 }
58 return ran
59 })
60
61 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
62 const list = await read($, steps)
63 if (e.props.hasSurvey || list.length === 0) return next(e)
64
65 const total = list.length
66 const done = list.filter(s => s.status === 'completed').length
67
68 const current = list.find(s => s.status === 'in_progress')
69 const position = current ? list.indexOf(current) + 1 : Math.min(done + 1, total)
70 const ratio = done / total
71 const color = colorFor(ratio)
72
73 const label = done === total ? 'Concluído' : current ? current.active : (list[done]?.subject ?? '')
74 const counter = ` ${position}/${total}`
75 const { Box, Text } = $.ui.resolve(e)
76
77 if (e.surface === 'terminal') {
78 const width = Math.max(10, e.props.bodyColumns - counter.length - 1)
79 const filled = Math.round(ratio * width)
80 return (
81 <Box flexDirection="column">
82 <Box>
83 <Text color={color}>{'█'.repeat(filled)}</Text>
84 <Text dimColor>{'░'.repeat(width - filled)}</Text>
85 <Text color={color} bold>{counter}</Text>
86 </Box>
87 <Text dimColor wrap="truncate-end">{label}</Text>
88 </Box>
89 )
90 }
91
92 const pct = `${Math.round(ratio * 100)}%`
93 return (
94 <Box flexDirection="column">
95 <Box flexDirection="row" alignItems="center" gap={1}>
96 <Box flexGrow={1} flexDirection="row" backgroundColor="subtle" overflow="hidden">
97 <Box width={pct} backgroundColor={color}>
98 <Text> </Text>
99 </Box>
100 </Box>
101 <Text color={color} bold>{counter.trim()}</Text>
102 </Box>
103 <Text dimColor wrap="truncate-end">{label}</Text>
104 </Box>
105 )
106 })
107}
108types/index.d.ts 8 lines1export type Step = { id: string; subject: string; active: string; status: 'pending' | 'in_progress' | 'completed' }
2
3declare module 'claude-code' {
4 interface PluginState {
5 'task-progress': { steps: Step[] }
6 }
7}
8