/boost asks a few quick questions, then rewrites a short prompt into a detailed one you can run, edit or copy

Three mods for the Code tab of the Claude desktop app (and terminal sessions):
| Mod | What it does | How to use |
|---|---|---|
| auto-model | Reads your first prompt of a session and picks Haiku (quick questions), Sonnet (normal work) or Opus (big builds) for the whole session. It never switches mid-session, so the prompt cache stays intact and you don't pay to re-read the conversation. If you chose a model yourself with /model, it leaves it alone. | Automatic. /auto-model shows the pick; /auto-model off / on; /auto-model opus before your first prompt forces one. |
| prompt-upgrade | Asks you 3 quick questions, then rewrites a one-line prompt into a detailed one you can edit, run, put in the prompt box, or copy. | /boost make a landing page for my honey brand |
| todays-report | Counts the tasks Claude finished, estimates hours saved and what that time is worth — today, this week, month, year. | /report · /report rate 45 to set your hourly value (default $40) |
Model use: auto-model makes one tiny Haiku call per new session; prompt-upgrade makes one Haiku call (questions) and one Sonnet call (the rewrite) each time you run it; todays-report makes none.
The "hours saved" figure is an estimate: 3 min for a question answered, otherwise 5 min + 2 min per tool action + 6 min per file edit, capped at 3 h per task.
hooks/register.tsx 310 lines1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4import type { UpgradeView } from '../types'
5
6// Prompt Upgrade — /boost <short prompt>
7// Asks a few quick questions, then rewrites the short prompt into a detailed
8// one you can edit, copy, put in the prompt box, or run with one click.
9
10const PANE = 'prompt-upgrade'
11const EMPTY: UpgradeView = { phase: 'start', original: '', questions: [], result: '', error: '', round: 0 }
12
13const view = atom({ plugin: 'prompt-upgrade', key: 'view' } as const, EMPTY)
14const answers = atom({ plugin: 'prompt-upgrade', key: 'answers' } as const, [] as string[])
15const draft = atom({ plugin: 'prompt-upgrade', key: 'draft' } as const, '')
16
17const ASK_SYSTEM = `You help someone turn a short request to an AI assistant into a detailed, high-quality prompt.
18Before rewriting, you ask the few questions whose answers would change the result the most:
19the goal or audience, the scope, the format or deliverable, constraints (tools, length, style, deadline), and what "done" looks like.
20Ask 3 questions (never more than 4). Each is one short, plain sentence a busy person can answer in a few words.
21Skip anything the request already answers. Reply with ONLY a JSON array of strings, nothing else.`
22
23const WRITE_SYSTEM = `You rewrite a short request into a detailed prompt for an AI coding/work assistant (Claude Code).
24Write it in the person's own voice ("I want...", "Build...").
25Include, as fits: the goal and why, context, specific requirements, the deliverable and its format, constraints, quality bar, and how to check it is done.
26Use the person's answers; where they skipped a question, choose a sensible default and say so briefly in the prompt.
27Keep it tight: short sections or bullets, no filler, no preamble. Reply with ONLY the finished prompt.`
28
29function parseQuestions(text: string): string[] {
30 const start = text.indexOf('[')
31 const end = text.lastIndexOf(']')
32 if (start >= 0 && end > start) {
33 try {
34 const list = JSON.parse(text.slice(start, end + 1))
35 if (Array.isArray(list)) {
36 const qs = list.map(q => String(q).trim()).filter(q => q.length > 0)
37 if (qs.length > 0) return qs.slice(0, 4)
38 }
39 } catch {
40 // fall through to line parsing
41 }
42 }
43 return text
44 .split('\n')
45 .map(line => line.replace(/^[\s\-*\d.)]+/, '').trim())
46 .filter(line => line.endsWith('?'))
47 .slice(0, 4)
48}
49
50async function begin($: any, original: string) {
51 const text = original.trim()
52 if (text === '') return
53 const before = await read($, view)
54 const round = before.round + 1
55 await update($, answers, () => [])
56 await update($, draft, () => '')
57 await update($, view, () => ({ ...EMPTY, phase: 'asking' as const, original: text, round }))
58
59 const r = await $.model.complete({
60 model: 'haiku',
61 system: ASK_SYSTEM,
62 prompt: 'Request:\n"""\n' + text + '\n"""',
63 maxTokens: 400,
64 effort: 'low',
65 timeoutMs: 30000,
66 })
67 if (!r.isAnswered) {
68 await update($, view, v => ({ ...v, phase: 'error' as const, error: 'Could not get questions (' + r.reason + ').' }))
69 return
70 }
71 const questions = parseQuestions(r.text)
72 if (questions.length === 0) {
73 await write($)
74 return
75 }
76 await update($, view, v => ({ ...v, phase: 'questions' as const, questions }))
77}
78
79async function write($: any) {
80 const v = await read($, view)
81 const given = await read($, answers)
82 await update($, view, cur => ({ ...cur, phase: 'writing' as const }))
83
84 const qa = v.questions
85 .map((q, i) => 'Q: ' + q + '\nA: ' + ((given[i] || '').trim() || '(skipped)'))
86 .join('\n\n')
87 const r = await $.model.complete({
88 model: 'sonnet',
89 system: WRITE_SYSTEM,
90 prompt: 'Short request:\n"""\n' + v.original + '\n"""\n\nQuestions and answers:\n' + (qa || '(none)') + '\n\nThe detailed prompt:',
91 maxTokens: 2000,
92 timeoutMs: 90000,
93 })
94 if (!r.isAnswered) {
95 await update($, view, cur => ({ ...cur, phase: 'error' as const, error: 'Could not write the prompt (' + r.reason + ').' }))
96 return
97 }
98 const result = r.text.trim()
99 await update($, draft, () => result)
100 await update($, view, cur => ({ ...cur, phase: 'done' as const, result }))
101}
102
103async function finalText($: any): Promise<string> {
104 const edited = await read($, draft)
105 const v = await read($, view)
106 return (edited || v.result).trim()
107}
108
109export const register: Register = on => {
110 on('session.start', async ($, e, next) => {
111 const out = await next(e)
112 try {
113 await $.command.register({
114 name: 'boost',
115 description: 'Prompt Upgrade: answer a few questions, get a detailed prompt',
116 argumentHint: '<your short prompt>',
117 })
118 } catch (err) {
119 $.ui.log('prompt-upgrade: could not add /boost (' + String(err) + ')')
120 }
121 return out
122 })
123
124 on('command.run', { command: 'boost' }, async ($, e) => {
125 const opened = await $.ui.open({ id: PANE, title: 'Prompt Upgrade', focus: true })
126 const text = (e.args || '').trim()
127 if (text === '') {
128 const v = await read($, view)
129 await update($, view, () => ({ ...EMPTY, round: v.round + 1 }))
130 } else {
131 void begin($, text)
132 }
133 if (!opened.isPlaced) {
134 return { text: 'Prompt Upgrade could not open its panel here (' + String((opened as any).reason || 'no panel') + '). It works in the Code tab of the desktop app and in a terminal session.' }
135 }
136 return {}
137 })
138
139 on('ui.render', { component: 'Pane', requestId: PANE }, async ($, e) => {
140 const { Box, Text, Button, Input } = $.ui.resolve(e) as any
141 const v = await read($, view)
142 const width = Math.max(30, (e.props as any).bodyColumns ?? 60)
143
144 const header = (
145 <Box flexDirection="column" marginBottom={1}>
146 <Text bold>Prompt Upgrade</Text>
147 {v.original !== '' && <Text dimColor wrap="wrap">Your prompt: {v.original}</Text>}
148 </Box>
149 )
150
151 if (v.phase === 'start') {
152 return (
153 <Box flexDirection="column" width={width}>
154 {header}
155 <Text>Type a short prompt and press Enter. I'll ask a few quick questions, then write a detailed version.</Text>
156 {Input && (
157 <Input
158 key={'start-' + v.round}
159 label="Prompt: "
160 placeholder="e.g. make a landing page for my honey brand"
161 submitLabel="upgrade"
162 autoFocus
163 onSubmit={(text: string) => {
164 void begin($, text)
165 }}
166 />
167 )}
168 </Box>
169 )
170 }
171
172 if (v.phase === 'asking' || v.phase === 'writing') {
173 return (
174 <Box flexDirection="column" width={width}>
175 {header}
176 <Text dimColor>{v.phase === 'asking' ? 'Thinking of a few questions…' : 'Writing your detailed prompt…'}</Text>
177 </Box>
178 )
179 }
180
181 if (v.phase === 'error') {
182 return (
183 <Box flexDirection="column" width={width}>
184 {header}
185 <Text color="red">{v.error}</Text>
186 <Box marginTop={1}>
187 <Button key="retry" label="Try again" variant="primary" onPress={() => { void begin($, v.original) }} />
188 <Text> </Text>
189 <Button key="close" label="Close" role="dismiss" onPress={() => $.ui.close({ id: PANE })} />
190 </Box>
191 </Box>
192 )
193 }
194
195 if (v.phase === 'questions') {
196 return (
197 <Box flexDirection="column" width={width}>
198 {header}
199 <Text dimColor>Answer what you can — skip any. Then press Write my prompt.</Text>
200 {v.questions.map((q, i) => (
201 <Box flexDirection="column" marginTop={1}>
202 <Text wrap="wrap">{i + 1}. {q}</Text>
203 {Input && (
204 <Input
205 key={'a-' + v.round + '-' + i}
206 placeholder="your answer"
207 submitLabel={i === v.questions.length - 1 ? 'write' : 'next'}
208 autoFocus={i === 0 ? true : undefined}
209 onInput={(text: string) => {
210 void update($, answers, list => {
211 const copy = list.slice()
212 copy[i] = text
213 return copy
214 })
215 }}
216 onSubmit={(text: string) => {
217 void (async () => {
218 await update($, answers, list => {
219 const copy = list.slice()
220 copy[i] = text
221 return copy
222 })
223 if (i === v.questions.length - 1) await write($)
224 })()
225 }}
226 />
227 )}
228 </Box>
229 ))}
230 <Box marginTop={1}>
231 <Button key="write" label="Write my prompt" variant="primary" onPress={() => { void write($) }} />
232 <Text> </Text>
233 <Button key="skip" label="Skip questions" onPress={() => { void write($) }} />
234 </Box>
235 </Box>
236 )
237 }
238
239 // done
240 const lines = Math.min(18, Math.max(6, v.result.split('\n').length + 1))
241 return (
242 <Box flexDirection="column" width={width}>
243 {header}
244 <Text dimColor>Your upgraded prompt — edit it right here, then pick what to do with it.</Text>
245 {Input ? (
246 <Input
247 key={'result-' + v.round}
248 value={v.result}
249 rows={lines}
250 submitLabel="run"
251 onInput={(text: string) => { void update($, draft, () => text) }}
252 onSubmit={(text: string) => {
253 void (async () => {
254 await update($, draft, () => text)
255 const t = await finalText($)
256 await $.ui.close({ id: PANE })
257 await $.prompt.submit({ text: t, asUser: true })
258 })()
259 }}
260 />
261 ) : (
262 <Text wrap="wrap">{v.result}</Text>
263 )}
264 <Box marginTop={1} flexWrap="wrap">
265 <Button
266 key="run"
267 label="Run it"
268 variant="primary"
269 onPress={() => {
270 void (async () => {
271 const t = await finalText($)
272 await $.ui.close({ id: PANE })
273 await $.prompt.submit({ text: t, asUser: true })
274 })()
275 }}
276 />
277 <Text> </Text>
278 <Button
279 key="fill"
280 label="Put in prompt box"
281 onPress={() => {
282 void (async () => {
283 const t = await finalText($)
284 await $.prompt.fill({ text: t })
285 await $.ui.close({ id: PANE })
286 })()
287 }}
288 />
289 <Text> </Text>
290 <Button
291 key="copy"
292 label="Copy"
293 onPress={() => {
294 void (async () => {
295 const t = await finalText($)
296 const done = await $.ui.copy({ text: t, surface: e.surface })
297 $.ui.toast(done.isCopied ? 'Prompt copied' : 'Could not copy here — use Put in prompt box')
298 })()
299 }}
300 />
301 <Text> </Text>
302 <Button key="again" label="Start over" onPress={() => { void update($, view, cur => ({ ...EMPTY, round: cur.round + 1 })) }} />
303 <Text> </Text>
304 <Button key="close" label="Close" role="dismiss" onPress={() => $.ui.close({ id: PANE })} />
305 </Box>
306 </Box>
307 )
308 })
309}
310types/index.d.ts 22 lines1export type UpgradePhase = 'start' | 'asking' | 'questions' | 'writing' | 'done' | 'error'
2
3export type UpgradeView = {
4 phase: UpgradePhase
5 /** The short prompt the person started from */
6 original: string
7 /** The quick questions the model asked */
8 questions: string[]
9 /** The detailed prompt, once written */
10 result: string
11 /** What went wrong, in the 'error' phase */
12 error: string
13 /** Bumped on every new run so the fields redraw empty */
14 round: number
15}
16
17declare module 'claude-code' {
18 interface PluginState {
19 'prompt-upgrade': { view: UpgradeView; answers: string[]; draft: string }
20 }
21}
22