SLOPSHOPPER

grill-buttons

Makes the grilling skill (grill-me, grill-with-docs) ask each round in Claude Code's own question dialog, with buttons, instead of as markdown text.

newtoastprompt
★ 3v0.1.0MITupdated 2026-10-25majinmarco/claude-grill-buttons
A shopper browsing a rack in a slop shop
README

grill-buttons

A Claude Code mod that makes Matt Pocock's grilling skills (/grill-me, /grill-with-docs) ask each round of questions in Claude Code's own question dialog, with a button per answer and the recommended answer first, instead of as ❓ Q1 markdown text. Free-text answers still work through the dialog's "Other" option.

It hooks skill.prompt and rewrites the grilling skill's question format as the skill loads, so it works wherever Claude Code draws that dialog: terminal, desktop app and mobile (Remote Control). It needs the mattpocock-skills plugin installed.

Install

At the prompt of a Claude Code terminal session:

/plugin install grill-buttons --marketplace majinmarco/claude-grill-buttons

Answer y to add the marketplace, then pick a scope (user is the first one offered).

From a shell:

claude plugin install grill-buttons --marketplace majinmarco/claude-grill-buttons

In the desktop app, where /plugin does not run: click + next to the prompt box, then Plugins, Add marketplace with majinmarco/claude-grill-buttons, then Add plugin.

Test

claude plugin test .
Source 1 files
hooks/register.ts 48 lines
1import type { Register } from 'claude-code'
2
3// grill-me and grill-with-docs hand off to the `grilling` skill today, but an
4// older or forked copy may carry the question format itself, so all three are
5// rewritten (as `name` or `<plugin>:name`).
6const isGrilling = (skill: string) =>
7  /(^|:)(grilling|grill-me|grill-with-docs)$/.test(skill)
8
9// Marks text this mod already rewrote, so a second copy of the mod (or a
10// skill loaded twice) leaves it alone.
11const MARK = '<!-- grill-buttons -->'
12
13// The skill's own markdown format block ("Format a round like so:" plus its
14// fenced example) and its "yes accepts the recommendation" line. Both are
15// replaced by the button instructions below.
16const FORMAT_BLOCK = /Format a round like so:\s*```[\s\S]*?```\s*/
17const YES_LINE = /Word each question so "yes" accepts your recommended answer\.\s*/
18
19export const BUTTON_FORMAT = `${MARK}
20Ask each round through the AskUserQuestion tool, never as plain text in your reply: the user answers by clicking buttons.
21
22- One AskUserQuestion entry per frontier question. The tool takes at most 4 questions per call: if the frontier is larger, ask the first 4, then call AskUserQuestion again right away with the next ones, all in the same round.
23- \`question\`: start with its number ("Q1. ..."), end with a question mark. Put the context the user needs to decide (what you found, why it matters, the trade-off) in the question text, in a few sentences at most.
24- \`header\`: a one- or two-word label for the decision, 12 characters at most.
25- \`options\`: 2 to 4 distinct, concrete answers. The first option is your recommended answer, its label ending in " (Recommended)". Keep labels to 1-5 words so they fit a phone or narrow terminal; the detail goes in the description. Each option's \`description\` says what choosing it means and its main consequence. Don't add an "Other" option: the dialog adds one for free-text answers.
26- Use \`multiSelect: true\` only when the answers genuinely combine. Use an option's \`preview\` when comparing code, schemas or layouts side by side helps.
27- Before the call, write at most one short line saying which part of the design tree this round covers. Don't repeat the questions as text.
28- The tool result holds the user's answers. Read any free-text "Other" answer carefully: it may reshape the tree. If the user dismisses the dialog, stop and ask in plain text how they want to continue.
29
30`
31
32export function rewrite(text: string): string {
33  if (text.includes(MARK)) return text
34  const replaced = text.replace(FORMAT_BLOCK, BUTTON_FORMAT).replace(YES_LINE, '')
35  if (replaced !== text) return replaced
36  // The skill's wording changed upstream: keep it and add ours as an override.
37  return `${text.trimEnd()}\n\n## How to ask (overrides any question format above)\n\n${BUTTON_FORMAT}`
38}
39
40export const register: Register = on => {
41  on('skill.prompt', async ($, e, next) => {
42    const out = await next(e)
43    if (!isGrilling(e.skill)) return out
44    $.ui.toast('Grilling questions will use buttons')
45    return { ...out, text: rewrite(out.text) }
46  })
47}
48