Asks a few quick questions, then rewrites your short prompt into a detailed one you can edit, copy or run with one click.

Asks you a few quick questions, then rewrites a short prompt into a much more detailed one you can edit, copy or run with one click.
/plugin install prompt-upgrade@kumpan-mods
/prompt-upgrade add a login page
claude -p the command reports the failure.hooks/register.tsx 125 lines1import { atom, read, update } from 'claude-code'
2import type { Engine, Register } from 'claude-code'
3
4const PANE = 'prompt-upgrade'
5const upgraded = atom({ plugin: 'prompt-upgrade', key: 'upgraded' } as const, '')
6
7const QUESTIONS_PROMPT = (prompt: string) => [
8 'A user wrote this short prompt for a coding assistant:',
9 '',
10 prompt,
11 '',
12 'Ask the 2 to 4 questions whose answers would change the result most (scope, constraints, stack, output form, what done looks like).',
13 'Skip anything the prompt already says. Each question has 2 to 4 short answer options.',
14 'Reply with JSON only: [{"question": "...?", "header": "max 12 chars", "options": ["...", "..."]}]',
15].join('\n')
16
17const REWRITE_PROMPT = (prompt: string, answers: [string, string][]) => [
18 'Rewrite this short prompt into one detailed prompt for a coding assistant. Keep the user\'s intent and voice, in their language.',
19 'Turn their answers into concrete requirements; add goal, context, constraints and what done looks like where the answers support them.',
20 'Do not invent facts, file names or requirements nobody gave. Reply with the new prompt only, no preamble.',
21 '',
22 `Short prompt: ${prompt}`,
23 '',
24 'Answers:',
25 ...(answers.length ? answers.map(([q, a]) => `- ${q} ${a}`) : ['- (none)']),
26].join('\n')
27
28type Question = { question: string; header?: string; options: string[] }
29
30// Models wrap JSON in fences or prose; take the outermost array.
31export const parseQuestions = (text: string): Question[] => {
32 const raw = text.match(/\[[\s\S]*\]/)?.[0]
33 if (!raw) return []
34 try {
35 return (JSON.parse(raw) as Question[])
36 .filter(q => q?.question && Array.isArray(q.options) && q.options.length >= 2)
37 .slice(0, 4)
38 .map(q => ({ ...q, options: q.options.slice(0, 4) }))
39 } catch {
40 return []
41 }
42}
43
44const upgrade = async ($: Engine, prompt: string): Promise<string> => {
45 const q = await $.model.complete({ model: 'haiku', prompt: QUESTIONS_PROMPT(prompt), effort: 'low', timeoutMs: 30000 })
46 const answers: [string, string][] = []
47 // A dismissed question rejects: stop asking, rewrite with what we have.
48 for (const { question, header, options } of q.isAnswered ? parseQuestions(q.text) : []) {
49 const a = await $.ui.ask(question, { options, header: header?.slice(0, 12) }).catch(() => undefined)
50 if (a === undefined) break
51 answers.push([question, a])
52 }
53 const r = await $.model.complete({ model: 'sonnet', prompt: REWRITE_PROMPT(prompt, answers), maxTokens: 4000, timeoutMs: 90000 })
54 if (!r.isAnswered) throw new Error(`no reply (${r.reason})`)
55 return r.text.trim()
56}
57
58export const register: Register = on => {
59 on('session.start', async ($, e, next) => {
60 await $.command.register({
61 name: 'prompt-upgrade',
62 description: 'Upgrade a short prompt into a detailed one: /prompt-upgrade <your prompt>',
63 })
64 return next(e)
65 })
66
67 on('command.run', { command: 'prompt-upgrade' }, async ($, e) => {
68 const prompt = e.args.trim()
69 if (!prompt) return { text: 'Usage: /prompt-upgrade <your short prompt>' }
70 $.ui.toast('Upgrading your prompt…')
71 try {
72 await update($, upgraded, () => '')
73 const text = await upgrade($, prompt)
74 await update($, upgraded, () => text)
75 await $.ui.open({ id: PANE, title: 'Prompt upgrade', focus: true })
76 return { text: 'Upgraded prompt is in the Prompt upgrade pane.' }
77 } catch (err) {
78 return { text: 'Prompt upgrade failed: ' + String(err) }
79 }
80 })
81
82 on('ui.render', { component: 'Pane', requestId: PANE }, async ($, e) => {
83 const { Box, Text, Button, Markdown } = $.ui.resolve(e)
84 const text = await read($, upgraded)
85 if (!text) return <Text dimColor>Run /prompt-upgrade <prompt> to start.</Text>
86 const close = () => $.ui.close({ id: PANE })
87
88 return (
89 <Box flexDirection="column" gap={1}>
90 <Markdown text={text} />
91 <Box gap={1}>
92 <Button
93 variant="primary"
94 onPress={async () => {
95 await $.prompt.submit({ text })
96 await close()
97 }}
98 >
99 Run
100 </Button>
101 <Button
102 onPress={async () => {
103 await $.prompt.fill({ text })
104 await close()
105 }}
106 >
107 Edit
108 </Button>
109 <Button
110 onPress={async press => {
111 const r = await $.ui.copy({ text, surface: press.surface })
112 $.ui.toast(r.isCopied ? 'Copied' : 'Could not copy')
113 }}
114 >
115 Copy
116 </Button>
117 <Button role="dismiss" onPress={close}>
118 Close
119 </Button>
120 </Box>
121 </Box>
122 )
123 })
124}
125types/index.d.ts 6 lines1declare module 'claude-code' {
2 interface PluginState {
3 'prompt-upgrade': { upgraded: string }
4 }
5}
6