SLOPSHOPPER

answer-buttons

Three small buttons under Claude's last answer: Plain English, Shorter, and Plain & short

newrows
A shopper browsing a rack in a slop shop
README

answer-buttons

A Claude Code mod that draws three small buttons under Claude's last answer. Part of claude-plugins.

What it does

ButtonWhat it sends
✎ Plain EnglishAsks for the answer again in plain words: what the thing is, one labelled analogy and where it stops being true, where it is, and what breaks if it is wrong
✂ ShorterAsks for the same answer cut to its point in 2 or 3 sentences, keeping any yes/no question and any copyable draft word for word
✎✂ Plain & shortPlain words, held to 2 or 3 sentences

The buttons show only under the last answer, never while Claude is working. From the keyboard: /answer-buttons:shorter and /answer-buttons:plain-short.

Settings

SettingDefaultWhat it does
plain_skillemptyA skill the two Plain buttons run instead of the built in prompt, such as my-plugin:plain-english

What it reads and does

  • Reads: whether Claude is answering and which answer is the latest, to draw the buttons under it.
  • Does: on a press, sends a fixed prompt as your message (Plain English, Shorter, Plain & short), or runs the skill named in plain_skill.
  • Hooks: turn.start and turn.complete only track whether a turn is running. ui.render on the answer adds the button row under the last answer and leaves the answer itself unchanged.
  • Commands it runs: only when you press a Plain button and the plain_skill setting names a skill, it runs that one skill (/<plain_skill> with a fixed argument written in this mod's code, asking it to re-explain your last answer, or to do so in 2 or 3 sentences). With plain_skill empty it runs no command at all. It never runs a command on its own, only on your press.
  • What goes into the prompts it submits: one of three fixed sentences written in this mod's code (ask Claude to re-explain the last answer in plain English, to say it shorter, or both), sent as your own message in the same session. It reads the conversation only to find which answer is the latest, and copies none of its text into the prompt.
  • Privacy: see PRIVACY.md.
  • Never: sends anything anywhere (no network calls, no telemetry).

Install

Needs Claude Code 2.1.287 or later (mods). Tested on 2.1.289.

/plugin marketplace add mutlumehmet/claude-plugins
/plugin install answer-buttons@mehmetmutlu

A mod runs inside Claude Code with your permissions. Read the code before you install any mod. Settings are asked for at install, or later with /plugin configure answer-buttons@mehmetmutlu.

Known gaps

  • Nothing is drawn in the VS Code chat panel; use the commands there.
  • A reply that ends with a hidden block may get no buttons.

About

Mehmet Mutlu: work that's shipped, at mehmetmutlu.dev

Made by Mehmet Mutlu, part of claude-plugins. More of his work at mehmetmutlu.dev.

License

MIT

Source 1 files
hooks/register.tsx 89 lines
1// answer-buttons: three small buttons under Claude's last answer.
2//   ✎ Plain English    asks for that answer again in plain words
3//   ✂ Shorter          asks for the same answer, cut down
4//   ✎✂ Plain & short   plain words, held to 2 or 3 sentences
5// The two Plain buttons send a built in prompt, or run the skill the plain_skill
6// setting names. From the keyboard: /answer-buttons:shorter and
7// /answer-buttons:plain-short (commands/), and that skill's own command.
8
9import type { Register } from 'claude-code'
10
11// Skills are commands, and a mod may not submit a '/' text, so the skill runs
12// through $.command.run
13export const PLAIN_ARGS = 'Re-explain your last answer.'
14export const PLAIN_PROMPT =
15  'Re-explain your last answer in plain English for someone who is not an expert in this area: ' +
16  'first what the thing is, in its own terms, then one analogy labelled as one and where it stops being true, ' +
17  'then where it actually is (a file, a command, a menu) and what breaks if it is wrong. Under 200 words. ' +
18  'Keep any yes/no question and any copyable draft exactly as they were.'
19export const PLAIN_SHORT_ARGS =
20  'Re-explain your last answer in 2 or 3 sentences only: the thing itself, then one labelled analogy. ' +
21  'Keep any yes/no question and any copyable draft exactly as they were.'
22export const PLAIN_SHORT_PROMPT =
23  'Re-explain your last answer in plain English, in 2 or 3 sentences only: the thing itself, then one ' +
24  'labelled analogy. Keep any yes/no question and any copyable draft exactly as they were.'
25export const SHORTER_PROMPT =
26  'Make your last answer shorter: lead with what it means for me in 2 or 3 sentences, ' +
27  'keep any yes/no question and any copyable draft exactly as they were, drop the rest. ' +
28  'Reply in the same language as before.'
29
30// The final text of the last main conversation answer, and whether a turn is
31// running. Module variables: a hot reload clears them, which only hides the
32// buttons until the next answer.
33let lastAnswer = ''
34let isWorking = false
35// The skill the Plain buttons run, from the plain_skill setting; empty sends the built in prompts
36let plainSkill = ''
37export function setPlainSkill(name: string | undefined) {
38  plainSkill = String(name ?? '').trim().replace(/^\//, '')
39}
40
41// True for the text block that ends the last answer
42export function isLastBlock(blockText: string, answer: string): boolean {
43  const block = blockText.trim()
44  return block.length > 0 && answer.trim().endsWith(block)
45}
46
47export const register: Register = (on, options) => {
48  setPlainSkill(options?.plain_skill as string | undefined)
49  on('turn.start', async ($, e, next) => {
50    isWorking = true
51    $.ui.invalidate('ui.render')
52    return next(e)
53  })
54
55  on('turn.complete', async ($, e, next) => {
56    // Subagent turns end inside the main one; only the main answer counts
57    if (!e.agentId) {
58      isWorking = false
59      lastAnswer = e.reason === 'answer' ? e.answer : ''
60      $.ui.invalidate('ui.render')
61    }
62    return next(e)
63  })
64
65  on('ui.render', { component: 'AssistantMessage' }, async ($, e, next) => {
66    const own = await next(e)
67    if (isWorking || e.surface === 'vscode' || e.surface === 'mobile') return own
68    if (!isLastBlock(e.props.text, lastAnswer)) return own
69
70    const { Box, Button } = $.ui.resolve(e)
71    return (
72      <Box flexDirection="column">
73        {own}
74        <Box gap={2}>
75          <Button key="plain" plain dimColor onPress={() => (plainSkill ? $.command.run({ command: plainSkill, args: PLAIN_ARGS }) : $.prompt.submit({ text: PLAIN_PROMPT, asUser: true }))}>
76            ✎ Plain English
77          </Button>
78          <Button key="shorter" plain dimColor onPress={() => $.prompt.submit({ text: SHORTER_PROMPT, asUser: true })}>
79            ✂ Shorter
80          </Button>
81          <Button key="plain-short" plain dimColor onPress={() => (plainSkill ? $.command.run({ command: plainSkill, args: PLAIN_SHORT_ARGS }) : $.prompt.submit({ text: PLAIN_SHORT_PROMPT, asUser: true }))}>
82            ✎✂ Plain & short
83          </Button>
84        </Box>
85      </Box>
86    )
87  })
88}
89