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

My Claude Code mods. Each mod lives in its own folder under mods/ and is listed in .claude-plugin/marketplace.json.
/plugin install prompt-refiner --marketplace jadedh385/claude-mods
Answer y to add the marketplace, then pick a scope.
| Mod | What 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. |
mods/<name>/ with .claude-plugin/plugin.json and hooks/..claude-plugin/marketplace.json.claude plugin validate mods/<name> and claude plugin test mods/<name>.version in the mod's plugin.json whenever it changes.hooks/register.tsx 151 lines1import 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