Draws each tool call as one summary line and hides its output unless it fails. Ctrl+O shows everything.

Three Claude Code mods.
/optimize <draft>): Opus 5.5 (medium effort) rewrites your draft as one measurable Goal: paragraph. Press it again to restore your draft.While your output style is named ADHD, adds the rules from ~/.claude/output-styles/adhd.md beside each prompt, so long sessions keep the style. It follows the style the session actually uses, including one picked in the app. It does nothing without that file.
Draws each tool call in the terminal as one plain-English line of 20 words or fewer, for example "Check if the job scanner is still running". It hides tool output unless a call fails or is stopped. Press ctrl+o to see the full commands and output. It also asks Claude to write each command description for a beginner. Tool lines are gray so replies stand out, a fully bold first line is drawn orange, and replies wrap at 94 columns on wide terminals.
At the prompt of a terminal session:
/plugin install prompt-optimizer --marketplace caleb-marks/caleb-mods
/plugin install adhd-reminder --marketplace caleb-marks/caleb-mods
/plugin install quiet-tools --marketplace caleb-marks/caleb-mods
From a shell:
claude plugin install prompt-optimizer --marketplace caleb-marks/caleb-mods
claude plugin install adhd-reminder --marketplace caleb-marks/caleb-mods
claude plugin install quiet-tools --marketplace caleb-marks/caleb-mods
In the desktop app, where /plugin does not run: click + next to the prompt box, then Plugins, Add marketplace with caleb-marks/caleb-mods, then Add plugin.
hooks/register.tsx 165 lines1import type { Register } from 'claude-code'
2
3// Caleb's palette: blue while running, muted gray when done (no pure white).
4// Tool lines are gray so Claude's replies stand out from the steps.
5const RUNNING = '#5FA8FF'
6const DONE = '#6E6A66'
7const TEXT = '#8A8580'
8const MAX = 100
9// Replies wrap at this width so lines stay short enough to scan on a wide terminal.
10const REPLY_COLUMNS = 94
11// A reply's opening line when it is bold as a whole, drawn orange.
12const HEADLINE = /^\s*\*\*([^*\n]*[^*\s][^*\n]*)\*\*[ \t]*(?:\r?\n|$)/
13const REPLY_FG = '#E8E4DE'
14const HEADLINE_COLOR = '#FF9E3D'
15
16type Input = Record<string, unknown>
17
18const str = (v: unknown): string => (typeof v === 'string' ? v : '')
19const base = (p: unknown): string => str(p).split(/[\\/]/).pop() ?? ''
20const firstLine = (s: string): string =>
21 s.split(/\r?\n/).map(l => l.trim()).find(l => l !== '') ?? ''
22const cut = (s: string): string => {
23 const chars = Array.from(s.replace(/[\x00-\x1f\x7f]/g, ' '))
24 return chars.length > MAX ? `${chars.slice(0, MAX - 1).join('')}…` : chars.join('')
25}
26
27// The tool row shows the model's `description`, so ask for one a beginner can read.
28const DESCRIBE_RULE = {
29 id: 'quiet-tools:describe',
30 scope: 'session',
31 text: [
32 '# Tool call descriptions',
33 'The person sees only the `description` you give each Bash, PowerShell or Agent call, as one line. The person is a beginner.',
34 '- Always give a `description`.',
35 '- Write one plain-English sentence of 20 words or fewer that says what the step does and why.',
36 '- Use everyday words. Do not use code, flags, file paths, cmdlet names or jargon. Use only a file name when it helps.',
37 '- Good: "Check if the job scanner is still running". Bad: "Get-Process job-scan | Select-Object Name, Id".',
38 ].join('\n'),
39} as const
40
41// Tools whose `description` input says what the call does. Other tools (many MCP
42// ones) use `description` for content, so they get a summary built from the input.
43const DESCRIBED = new Set(['Bash', 'PowerShell', 'Agent', 'Task'])
44
45// One short sentence for a call: the model's own description when it wrote one,
46// else a summary built from the input.
47const summarize = (tool: string, raw: unknown): string => {
48 const i = (raw ?? {}) as Input
49 const described = DESCRIBED.has(tool) ? firstLine(str(i.description)) : ''
50 if (described) return described
51
52 switch (tool) {
53 case 'Bash':
54 case 'PowerShell':
55 return 'Run a command'
56 case 'Agent':
57 case 'Task':
58 return 'Ask a helper agent to do part of the work'
59 case 'Read':
60 return `Read the file ${base(i.file_path)}`
61 case 'Edit':
62 return `Change the file ${base(i.file_path)}`
63 case 'Write':
64 return `Save the file ${base(i.file_path)}`
65 case 'NotebookEdit':
66 return `Change the notebook ${base(i.notebook_path)}`
67 case 'Grep': {
68 const pattern = str(i.pattern)
69 return /^[\w .-]{1,30}$/.test(pattern) ? `Search the files for "${pattern}"` : 'Search the files for some text'
70 }
71 case 'Glob':
72 return 'Look for files by name'
73 case 'WebFetch': {
74 const host = /^https?:\/\/([^/]+)/.exec(str(i.url))?.[1]
75 return host ? `Open a web page on ${host}` : 'Open a web page'
76 }
77 case 'WebSearch':
78 return `Search the web for "${str(i.query)}"`
79 case 'Skill':
80 return `Follow the ${str(i.skill)} instructions`
81 case 'ToolSearch':
82 return 'Get more tools ready'
83 case 'AskUserQuestion':
84 return 'Ask you a question'
85 case 'Artifact':
86 return 'Work on a web page'
87 default: {
88 const name = tool.startsWith('mcp__') ? tool.split('__').slice(1).join(' ') : tool
89 return name.replace(/_/g, ' ')
90 }
91 }
92}
93
94export const register: Register = on => {
95 on('prompt.compose', async ($, e, next) => {
96 const { sections } = await next(e)
97 // Two loaded copies (dev folder and installed) must not add the rule twice.
98 return { sections: [...sections.filter(s => s.id !== DESCRIBE_RULE.id), DESCRIBE_RULE] }
99 })
100
101 on('ui.render', { component: 'ToolUse' }, ($, e, next) => {
102 const p = e.props
103 // Terminal only. Full view (ctrl+o, --verbose), failures and interrupts keep the engine's row.
104 if (e.surface !== 'terminal' || p.isExpanded || p.isErrored || p.isInterrupted) return next(e)
105
106 const { Box, Text } = $.ui.resolve(e)
107 return (
108 <Box>
109 <Text color={p.isRunning ? RUNNING : DONE}>● </Text>
110 <Text color={TEXT} wrap="truncate-end">{cut(summarize(p.tool, p.input))}</Text>
111 </Box>
112 )
113 }).catch(($, e, next) => next(e))
114
115 on('ui.render', { component: 'ToolResult' }, ($, e, next) => {
116 const p = e.props
117 const isInterrupted = (p.output as { interrupted?: unknown } | null)?.interrupted === true
118 if (e.surface !== 'terminal' || p.isExpanded || p.isErrored || isInterrupted) return next(e)
119 const { Box } = $.ui.resolve(e)
120 return <Box />
121 }).catch(($, e, next) => next(e))
122
123 // Replies are drawn here with Markdown, not the engine's node: the engine refuses its
124 // own node under a Box with a width, and the width is what keeps lines short.
125 on('ui.render', { component: 'AssistantMessage' }, ($, e, next) => {
126 if (e.surface !== 'terminal' || e.props.isSummary) return next(e)
127 const { Box, Text, Markdown } = $.ui.resolve(e)
128 const columns = e.viewport?.columns ?? 0
129 const width = columns > REPLY_COLUMNS ? REPLY_COLUMNS : undefined
130
131 // A reply that opens with a fully bold line: draw that line orange.
132 const match = HEADLINE.exec(e.props.text)
133 const headline = match ? (match[1] ?? '').replace(/`/g, '') : ''
134 const rest = match ? e.props.text.slice(match[0].length).replace(/^(?:[ \t]*\r?\n)+/, '') : e.props.text
135 const bullet = e.props.isFirstOfReply ? '● ' : ' '
136
137 return (
138 // A blank row above a headline sets the answer apart from the tool lines over it.
139 <Box flexDirection="column" width={width} marginTop={headline ? 1 : 0}>
140 {headline ? (
141 // No dot: the headline starts in the dot column, left of the tool lines' text.
142 <Text color={HEADLINE_COLOR} bold>{headline}</Text>
143 ) : null}
144 {headline && rest ? <Text> </Text> : null}
145 {rest ? (
146 <Box>
147 <Text color={REPLY_FG}>{headline ? ' ' : bullet}</Text>
148 <Box flexDirection="column" flexGrow={1}>
149 <Markdown text={rest} />
150 </Box>
151 </Box>
152 ) : null}
153 </Box>
154 )
155 }).catch(($, e, next) => {
156 $.ui.log(`quiet-tools: reply drawing failed: ${next.error?.message ?? 'unknown'}`, { to: 'debug' })
157 return next(e)
158 })
159
160 // Drop the "(ctrl+b to run in background)" line; the key still works.
161 on('ui.render', { component: 'ToolProgress' }, ($, e, next) =>
162 next({ ...e, props: { ...e.props, hint: '' } }),
163 )
164}
165