Reskins tool calls with per-tool icons and theme colors; seven themes, switch with /skin.

Eight mods for Claude Code (terminal and the desktop app's Code tab). Requires Claude Code 2.1.287 or newer.
| Mod | What it does | Commands |
|---|---|---|
| savvy-progress | Progress bar above the prompt; an Agents side panel with a pixel crab per subagent showing model, effort, task progress, context, tokens and estimated cost | /savvy, /crab-style <keyword> <color> <hat> |
| tool-skins | Themed icons and colors for tool calls (Tokyo Night, Nord, Dracula, Gruvbox, Solarized, Catppuccin, Rose Pine) | /skin <theme> |
| you-should-know | After a long reply, a Haiku side agent flags costs, risks and actions you might miss | /ysk on, /ysk off, /ysk focus <text> |
| file-tree | A panel showing which files Claude is reading or editing; turns green once committed | /files |
| cache-tax | Warns before a prompt reloads a cold prompt cache, with a token and cost estimate | /keepwarm, /cachetax ttl <minutes> |
| blast-radius | Stops rm -r, Remove-Item -Recurse, git clean -f, git reset --hard and similar, lists the files at stake, and waits for Proceed or Cancel | automatic |
| reflect | Spots corrections in your prompts and offers to save them as rules in CLAUDE.md | /reflect <rule> |
| terminal-browser | A text-mode browser pane; Claude can open pages there too | /browse <url or path> |
Cost figures are estimates at API list prices. you-should-know and reflect make small Haiku calls, which count toward your usage.
At a Claude Code prompt in a terminal:
/plugin install savvy-progress --marketplace Isaacg1000/isaac-mods
Answer y to add the marketplace, then pick the user scope. Repeat with each mod name you want.
Or from a shell, all at once:
claude plugin marketplace add Isaacg1000/isaac-mods
claude plugin install savvy-progress@isaac-mods --scope user
claude plugin install tool-skins@isaac-mods --scope user
claude plugin install you-should-know@isaac-mods --scope user
claude plugin install file-tree@isaac-mods --scope user
claude plugin install cache-tax@isaac-mods --scope user
claude plugin install blast-radius@isaac-mods --scope user
claude plugin install reflect@isaac-mods --scope user
claude plugin install terminal-browser@isaac-mods --scope user
Each mod lives in plugins/<name>/: hooks/register.tsx is the hooks module, types/index.d.ts its state contract. Check and test one with:
claude plugin validate plugins/<name>
claude plugin test plugins/<name>
When this folder is added as a local marketplace (claude plugin marketplace add <this folder>), edits are picked up with /reload-plugins.
hooks/register.tsx 137 lines1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4type Kind = 'read' | 'edit' | 'write' | 'run' | 'search' | 'web' | 'agent' | 'task' | 'mcp' | 'other'
5type Theme = { label: string; colors: Record<Kind, string>; error: string; icons: Record<Kind, string>; editBorder: string }
6
7const GLYPHS: Record<Kind, string> = {
8 read: '◫', edit: '✎', write: '✚', run: '❯', search: '⌕', web: '◍', agent: '✦', task: '☰', mcp: '⇄', other: '•',
9}
10const EMOJI: Record<Kind, string> = {
11 read: '📖', edit: '✏️', write: '📝', run: '⚡', search: '🔍', web: '🌐', agent: '🤖', task: '📋', mcp: '🔌', other: '🔧',
12}
13
14const THEMES: Record<string, Theme> = {
15 'tokyo-night': {
16 label: 'Tokyo Night',
17 colors: { read: '#7aa2f7', edit: '#e0af68', write: '#9ece6a', run: '#bb9af7', search: '#7dcfff', web: '#2ac3de', agent: '#ff9e64', task: '#73daca', mcp: '#b4f9f8', other: '#a9b1d6' },
18 error: '#f7768e', icons: GLYPHS, editBorder: '#e0af68',
19 },
20 nord: {
21 label: 'Nord',
22 colors: { read: '#88c0d0', edit: '#ebcb8b', write: '#a3be8c', run: '#b48ead', search: '#8fbcbb', web: '#81a1c1', agent: '#d08770', task: '#5e81ac', mcp: '#8fbcbb', other: '#d8dee9' },
23 error: '#bf616a', icons: GLYPHS, editBorder: '#ebcb8b',
24 },
25 dracula: {
26 label: 'Dracula',
27 colors: { read: '#8be9fd', edit: '#f1fa8c', write: '#50fa7b', run: '#bd93f9', search: '#8be9fd', web: '#ff79c6', agent: '#ffb86c', task: '#50fa7b', mcp: '#ff79c6', other: '#f8f8f2' },
28 error: '#ff5555', icons: GLYPHS, editBorder: '#ff79c6',
29 },
30 gruvbox: {
31 label: 'Gruvbox',
32 colors: { read: '#83a598', edit: '#fabd2f', write: '#b8bb26', run: '#d3869b', search: '#8ec07c', web: '#83a598', agent: '#fe8019', task: '#8ec07c', mcp: '#d3869b', other: '#ebdbb2' },
33 error: '#fb4934', icons: GLYPHS, editBorder: '#fabd2f',
34 },
35 solarized: {
36 label: 'Solarized',
37 colors: { read: '#268bd2', edit: '#b58900', write: '#859900', run: '#6c71c4', search: '#2aa198', web: '#268bd2', agent: '#cb4b16', task: '#2aa198', mcp: '#d33682', other: '#93a1a1' },
38 error: '#dc322f', icons: GLYPHS, editBorder: '#b58900',
39 },
40 catppuccin: {
41 label: 'Catppuccin Mocha',
42 colors: { read: '#89b4fa', edit: '#f9e2af', write: '#a6e3a1', run: '#cba6f7', search: '#94e2d5', web: '#74c7ec', agent: '#fab387', task: '#94e2d5', mcp: '#f5c2e7', other: '#cdd6f4' },
43 error: '#f38ba8', icons: EMOJI, editBorder: '#f9e2af',
44 },
45 'rose-pine': {
46 label: 'Rose Pine',
47 colors: { read: '#9ccfd8', edit: '#f6c177', write: '#31748f', run: '#c4a7e7', search: '#9ccfd8', web: '#ebbcba', agent: '#eb6f92', task: '#31748f', mcp: '#ebbcba', other: '#e0def4' },
48 error: '#eb6f92', icons: EMOJI, editBorder: '#ebbcba',
49 },
50}
51const NAMES = Object.keys(THEMES)
52
53function kindOf(tool: string): Kind {
54 if (/^mcp__/.test(tool)) return 'mcp'
55 if (/^(Read|NotebookRead|ListMcpResourcesTool|ReadMcpResource)/.test(tool)) return 'read'
56 if (/^(Edit|MultiEdit|NotebookEdit)$/.test(tool)) return 'edit'
57 if (tool === 'Write') return 'write'
58 if (/^(Bash|PowerShell|Monitor)$/.test(tool)) return 'run'
59 if (/^(Glob|Grep|ToolSearch|LSP)$/.test(tool)) return 'search'
60 if (/^Web/.test(tool)) return 'web'
61 if (/^(Agent|Task|Workflow|SendMessage)$/.test(tool)) return 'agent'
62 if (/^(TodoWrite|Task[A-Z])/.test(tool)) return 'task'
63 return 'other'
64}
65
66const theme = atom({ plugin: 'tool-skins', key: 'theme' } as const, 'tokyo-night')
67
68export const register: Register = on => {
69 on('session.start', async ($, e, next) => {
70 await $.command.register({
71 name: 'skin',
72 description: 'Switch the Claude Skins theme (no argument lists them)',
73 argumentHint: `<${NAMES.join('|')}|off>`,
74 })
75 const saved = await $.store.get('theme')
76 if (typeof saved === 'string' && (saved in THEMES || saved === 'off')) await update($, theme, () => saved)
77 return next(e)
78 })
79
80 on('command.run', { command: 'skin' }, async ($, e) => {
81 const wanted = e.args.trim().toLowerCase()
82 const current = await read($, theme)
83 if (!wanted) {
84 const rows = NAMES.map(n => `${n === current ? '▸' : ' '} ${n} (${THEMES[n]!.label})`).join('\n')
85 return { text: `Themes:\n${rows}\n off\nUse /skin <name>.` }
86 }
87 if (!(wanted in THEMES) && wanted !== 'off') return { text: `No theme "${wanted}". Try: ${NAMES.join(', ')}, off` }
88 await update($, theme, () => wanted)
89 await $.store.set('theme', wanted)
90 return { text: wanted === 'off' ? 'Skins off.' : `Skin set to ${THEMES[wanted]!.label}.` }
91 })
92
93 on('ui.render', { component: 'ToolUse' }, async ($, e, next) => {
94 const t = THEMES[await read($, theme)]
95 if (!t) return next(e)
96 const { Box, Text } = $.ui.resolve(e)
97 const kind = kindOf(e.props.tool)
98 const color = e.props.isErrored ? t.error : t.colors[kind]
99 const icon = e.props.isErrored ? '✗' : t.icons[kind]
100 const inner = await next(e)
101 const isEdit = kind === 'edit' || kind === 'write'
102 return (
103 <Box flexDirection="row">
104 <Box width={3} flexShrink={0}>
105 <Text color={color} bold={e.props.isRunning}>
106 {icon}
107 </Text>
108 </Box>
109 {isEdit ? (
110 <Box flexGrow={1} flexShrink={1} flexDirection="column" borderStyle="round" borderColor={t.editBorder} paddingX={1}>
111 {inner}
112 </Box>
113 ) : (
114 <Box flexGrow={1} flexShrink={1} flexDirection="column">
115 {inner}
116 </Box>
117 )}
118 </Box>
119 )
120 })
121
122 on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
123 const t = THEMES[await read($, theme)]
124 if (!t) return next(e)
125 const { Box, Text } = $.ui.resolve(e)
126 const kind: Kind = e.props.mode === 'tool-use' ? 'run' : e.props.mode === 'thinking' ? 'agent' : 'read'
127 return (
128 <Box flexDirection="row">
129 <Box width={3} flexShrink={0}>
130 <Text color={t.colors[kind]}>{t.icons[kind]}</Text>
131 </Box>
132 {await next(e)}
133 </Box>
134 )
135 })
136}
137types/index.d.ts 8 lines1export type SkinName = string
2
3declare module 'claude-code' {
4 interface PluginState {
5 'tool-skins': { theme: SkinName }
6 }
7}
8