SLOPSHOPPER

prompt-upgrade

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

newpanecommandtoastmodel
v0.1.0MITupdated 2026-10-23kumpan/claude-mods/prompt-upgrade
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · prompt-upgrade
│ ┃ prompt-upgrade ✕ › fix the failing auth test and add an audit log call │ ┃ Run /prompt-upgrade <prompt> to start. │ ⏺ Read(src/auth.ts) │ ⎿ Read 6 lines │ ⏺ Update(src/auth.ts) │ ⎿ Added 2 lines, removed 1 line │ ⏺ Bash(bun test) │ ⎿ 3 pass, 1 fail │ │ ● Done. refresh now rejects expired claims and logs an audit event. │ │ ✻ Worked for 42s · done 4:20 PM │ │ › /prompt-upgrade │ ⎿ prompt-upgrade: Usage: /prompt-upgrade <your short prompt> │ │ ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts

Draws

Pane · prompt-upgrade
Run /prompt-upgrade <prompt> to start.
README

prompt-upgrade

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

Use

/prompt-upgrade add a login page
  1. A fast model (Haiku) picks the 2 to 4 questions whose answers would change the result most, and skips what your prompt already says. You answer them in the usual question dialog; dismiss it to stop asking and rewrite with what you have.
  2. Sonnet rewrites your prompt, in your language, using your answers. It is told not to invent files or requirements nobody gave.
  3. The result opens in a Prompt upgrade pane with four buttons:
  4. Run sends it as your next prompt.
  5. Edit puts it in the prompt box to change before sending.
  6. Copy puts it on the clipboard.
  7. Close dismisses the pane.

Good to know

  • Two model calls per upgrade, through your session's own credentials. The conversation is not sent: only your short prompt and your answers.
  • It does not read your code, so the rewrite is as specific as your answers. Use Edit to add file names.
  • Needs an interactive session (questions and pane): in claude -p the command reports the failure.
Source 2 files
hooks/register.tsx 125 lines
1import { 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 &lt;prompt&gt; 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}
125
types/index.d.ts 6 lines
1declare module 'claude-code' {
2  interface PluginState {
3    'prompt-upgrade': { upgraded: string }
4  }
5}
6