SLOPSHOPPER

cut-input

Copy the prompt box to the clipboard and clear it: Ctrl+Shift+U cuts the whole draft, and a Cut button shows while the box has text.

newbandtoastprompt
★ 2v0.2.0MITupdated 2026-10-08shdennlin/agent-plugins/plugins/cut-input
A shopper browsing a rack in a slop shop
README

cut-input

A mod (function hooks that run inside Claude Code) that copies the prompt box to the system clipboard and clears it.

  • Ctrl+Shift+U: cuts the whole draft, copying it to the clipboard and clearing the box, no matter where the cursor is or how many lines the draft has.
  • ✂ Cut input button: shown above the prompt, right-aligned, only while the box has text. Click it to copy the whole draft and clear the box. Gone again once the box is empty or the prompt is sent.

If the copy fails the draft is kept and a toast says so.

The button joins the band above the prompt instead of replacing it: it draws what the plugins beneath it drew and adds itself, so mods such as image-peek keep their own band (0.1.0 hid them; fixed in 0.1.1).

Install

/plugin install cut-input --marketplace shdennlin/agent-plugins

Needs Claude Code 2.1.289+ (hook modules).

Develop

claude plugin validate plugins/cut-input
claude plugin test plugins/cut-input
claude --plugin-dir ./plugins/cut-input   # lays .claude-plugin/types/ for tsc -p plugins/cut-input
Source 2 files
hooks/register.tsx 82 lines
1import { atom, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4const hasText = atom({ plugin: 'cut-input', key: 'hasText' } as const, false)
5
6export const register: Register = on => {
7  // ctrl+shift+u cuts the whole draft, regardless of cursor position
8  on('prompt.edit', async ($, e, next) => {
9    const isKillAll = e.key?.ctrl === true && e.key.shift === true && e.key.key === 'u'
10
11    if (isKillAll && e.text !== '') {
12      const { isCopied } = await $.ui.copy({ text: e.text })
13
14      if (!isCopied) {
15        return next(e)
16      }
17
18      await update($, hasText, () => false)
19
20      return { text: '', cursor: 0 }
21    }
22
23    const result = await next(e)
24
25    await update($, hasText, () => result.text !== '')
26
27    return result
28  })
29
30  on('prompt.fill', async ($, e, next) => {
31    const result = await next(e)
32    await update($, hasText, () => result.isFilled ? e.mode !== 'replace' || e.text !== '' : false)
33
34    return result
35  })
36
37  on('prompt.submit', async ($, e, next) => {
38    update($, hasText, () => false).catch(() => {})
39
40    return next(e)
41  })
42
43  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
44    if (e.props.hasSurvey || !(await read($, hasText))) {
45      return next(e)
46    }
47
48    const { Box, Button } = $.ui.resolve(e)
49    const below = await next(e)
50
51    return (
52      <Box flexDirection="column">
53        {below}
54        <Box justifyContent="flex-end">
55          <Button
56            key="cut"
57            label="✂ Cut input"
58            onPress={async press => {
59              const { text } = await $.prompt.read()
60
61              if (text === '') {
62                return
63              }
64
65              const { isCopied } = await $.ui.copy({ text, surface: press.surface })
66
67              if (!isCopied) {
68                $.ui.toast('Copy failed, input kept')
69                return
70              }
71
72              await $.prompt.fill({ text: '' })
73              await update($, hasText, () => false)
74              $.ui.toast('Copied and cleared')
75            }}
76          />
77        </Box>
78      </Box>
79    )
80  })
81}
82
types/index.d.ts 8 lines
1export type CutInputKey = 'hasText'
2
3declare module 'claude-code' {
4  interface PluginState {
5    'cut-input': { hasText: boolean }
6  }
7}
8