SLOPSHOPPER

prompt-refiner

/refine rewrites a rough prompt using project rules and puts it in the prompt box for review

newbandcommandtoaststatusmodel
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · prompt-refiner
› fix the failing auth test and add an audit log call ⏺ 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 › /refine ⎿ prompt-refiner: Usage: /refine <rough prompt> (light cleanup) ⎿ prompt-refiner: /refine full <rough prompt> (Task / Context / Constraints / Done-when) ⎿ prompt-refiner: /refine undo (restore your original text) Prompt: [ Refine ] [ Refine full ] [ Undo ] ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts

Draws

Band
Prompt: [ Refine ] [ Refine full ] [ Undo ]
README

claude-mods

My Claude Code mods. Each mod lives in its own folder under mods/ and is listed in .claude-plugin/marketplace.json.

Install a mod

/plugin install prompt-refiner --marketplace jadedh385/claude-mods

Answer y to add the marketplace, then pick a scope.

Mods

ModWhat it does
prompt-refiner/refine <rough prompt> rewrites it with Haiku using CLAUDE.md and .claude/refine.md, and puts it in the prompt box. A button bar above the prompt (Refine, Refine full, Undo) does the same from whatever is already typed. /refine full ... uses Task / Context / Constraints / Done-when. /refine undo restores your text.

Adding a mod

  1. Create mods/<name>/ with .claude-plugin/plugin.json and hooks/.
  2. Add an entry to .claude-plugin/marketplace.json.
  3. Run claude plugin validate mods/<name> and claude plugin test mods/<name>.
  4. Bump version in the mod's plugin.json whenever it changes.
Source 1 files
hooks/register.tsx 151 lines
1import type { EngineInterface, Register } from 'claude-code'
2
3const COMMAND = 'refine'
4const REFINER_MODEL = 'haiku'
5const MAX_CONTEXT_CHARS = 12000
6const TIMEOUT_MS = 20000
7const CONTEXT_FILES = ['CLAUDE.md', '.claude/refine.md'] as const
8
9const USAGE =
10  'Usage: /refine <rough prompt>  (light cleanup)\n' +
11  '       /refine full <rough prompt>  (Task / Context / Constraints / Done-when)\n' +
12  '       /refine undo  (restore your original text)'
13
14const BASE_RULES = `You rewrite a developer's rough prompt into a clear, executable prompt for Claude Code.
15You are given RAW_PROMPT and PROJECT_CONTEXT (excerpts of the repo's instructions).
16
17Do:
18- Keep the user's intent and scope exactly. Never add features, files, or steps they did not imply.
19- Replace vague terms ("fix", "better", "clean up") with specific, checkable outcomes.
20- Name files, commands, or conventions from PROJECT_CONTEXT only when directly relevant (max 3).
21- Where a needed detail is unknown, write it as [ASK: question] instead of guessing.
22- If RAW_PROMPT is already specific, return it unchanged.
23
24Output ONLY the rewritten prompt. No preamble, no explanation, no markdown fences.`
25
26const FULL_RULES = `
27
28Structure the output with these headings, omitting any that have nothing real to say:
29Task:
30Context:
31Constraints:
32Done when:`
33
34const LIGHT_RULES = `
35
36Keep the user's own wording and structure where it is already clear. Change only what is vague.`
37
38export type RefineRequest =
39  | { kind: 'usage' }
40  | { kind: 'undo' }
41  | { kind: 'refine'; mode: 'light' | 'full'; text: string }
42
43export const parseArgs = (args: string): RefineRequest => {
44  const trimmed = args.trim()
45  if (trimmed === '') return { kind: 'usage' }
46  if (trimmed === 'undo') return { kind: 'undo' }
47  if (/^full\s/.test(trimmed)) {
48    return { kind: 'refine', mode: 'full', text: trimmed.slice(4).trim() }
49  }
50  return { kind: 'refine', mode: 'light', text: trimmed }
51}
52
53export const buildSystem = (mode: 'light' | 'full'): string =>
54  BASE_RULES + (mode === 'full' ? FULL_RULES : LIGHT_RULES)
55
56export const buildPrompt = (raw: string, context: string): string =>
57  `PROJECT_CONTEXT:\n${context === '' ? '(none found)' : context}\n\nRAW_PROMPT:\n${raw}`
58
59export const clampContext = (parts: readonly string[]): string =>
60  parts.filter(part => part.trim() !== '').join('\n\n---\n\n').slice(0, MAX_CONTEXT_CHARS)
61
62type Mode = 'light' | 'full'
63
64const MSG_FILLED =
65  'Refined prompt is in the box. Edit it, then press Enter. (/refine undo restores yours)'
66const MSG_FAILED = 'Refine failed; your text is unchanged.'
67const MSG_EMPTY = 'Type a prompt in the box first, then press Refine.'
68
69let lastOriginal: string | undefined
70let isBusy = false
71
72async function refineText($: EngineInterface, text: string, mode: Mode): Promise<string> {
73  const parts = await Promise.all(CONTEXT_FILES.map(path => $.fs.read(path).catch(() => '')))
74  const result = await $.model.complete({
75    model: REFINER_MODEL,
76    system: buildSystem(mode),
77    prompt: buildPrompt(text, clampContext(parts)),
78    effort: 'low',
79    timeoutMs: TIMEOUT_MS,
80  })
81
82  if (!result.isAnswered || result.text.trim() === '') return MSG_FAILED
83
84  const refined = result.text.trim()
85  lastOriginal = text
86  const filled = await $.prompt.fill({ text: refined })
87
88  return filled.isFilled ? MSG_FILLED : `Could not fill the prompt box. Refined prompt:\n${refined}`
89}
90
91async function undo($: EngineInterface): Promise<string> {
92  if (lastOriginal === undefined) return 'Nothing to undo.'
93  await $.prompt.fill({ text: lastOriginal })
94
95  return 'Restored your original text.'
96}
97
98async function refineBox($: EngineInterface, mode: Mode): Promise<void> {
99  if (isBusy) return
100  isBusy = true
101  try {
102    const { text } = await $.prompt.read()
103    if (text.trim() === '') {
104      $.ui.toast(MSG_EMPTY)
105      return
106    }
107    $.ui.status('Refining prompt...')
108    $.ui.toast(await refineText($, text, mode))
109  } finally {
110    isBusy = false
111    $.ui.status(undefined)
112  }
113}
114
115export const register: Register = on => {
116  on('session.start', async ($, e, next) => {
117    await $.command.register({
118      name: COMMAND,
119      description: 'Rewrite a rough prompt using project rules; review it in the prompt box',
120    })
121
122    return next(e)
123  })
124
125  on('command.run', { command: COMMAND }, async ($, e) => {
126    const request = parseArgs(e.args)
127
128    if (request.kind === 'undo') return { text: await undo($) }
129    if (request.kind === 'usage' || request.text === '') return { text: USAGE }
130
131    return { text: await refineText($, request.text, request.mode) }
132  })
133
134  on('ui.render', { component: 'AbovePrompt' }, ($, e, next) => {
135    if (e.props.hasSurvey) return next(e)
136
137    const { Box, Button, Text } = $.ui.resolve(e)
138
139    return (
140      <Box>
141        <Text dimColor>Prompt: </Text>
142        <Button key="refine" label="Refine" onPress={() => refineBox($, 'light')} />
143        <Text> </Text>
144        <Button key="refine-full" label="Refine full" onPress={() => refineBox($, 'full')} />
145        <Text> </Text>
146        <Button key="undo" label="Undo" onPress={async () => $.ui.toast(await undo($))} />
147      </Box>
148    )
149  })
150}
151