Put text on your clipboard from the prompt, cleaned of terminal clutter.

Put text on your clipboard straight from the prompt, cleaned of the clutter a terminal drags along with it, so you never have to select and copy text out of the terminal by hand.
/clip <text> puts that text on your clipboard.If /clip already belongs to something of yours, a skill or another mod, Clip answers to /clipboard <text> instead.
Clip strips box edges, prompt marks and trailing spaces from every line, and blank lines from the top and bottom, before it copies.
It uses your system's own clipboard program: PowerShell's Set-Clipboard on Windows, pbcopy on macOS, and wl-copy or xclip on Linux. Nothing is sent anywhere else.
Nothing goes to any service. Here is each thing, plainly.
What it sends, and where.
/clip goes to your own clipboard program, on this machine, through its standard input. That is the only place it goes.$.process.run, Claude Code's call for starting a program on your machine. Clip uses it only to start the clipboard program below.Which programs it runs, and why.
powershell.exe -NoProfile -Command "$input | Set-Clipboard"pbcopywl-copyxclip -selection clipboard/clip.What it puts in the prompts it submits.
What each hook does.
session.start: adds the /clip command, or /clipboard when /clip is taken.command.run for clip and clipboard: cleans the text you typed, runs the clipboard program, and says how many characters it copied.Tested with Claude Code 2.1.287 on Windows.
See every Rostech mod, live, at rostech.app.
hooks/register.ts 60 lines1import type { EngineInterface, Register } from 'claude-code'
2
3// Text copied out of a terminal drags the terminal along with it: box edges,
4// prompt marks and padding. Clip strips those before the text lands.
5function clean(text: string): string {
6 const lines = text.split(/\r?\n/).map(line =>
7 line
8 .replace(/^\s*[│┃|]\s?/, '')
9 .replace(/\s?[│┃|]\s*$/, '')
10 .replace(/^\s*[>❯$]\s+/, '')
11 .replace(/\s+$/, ''),
12 )
13 while (lines.length && !lines[0]) lines.shift()
14 while (lines.length && !lines[lines.length - 1]) lines.pop()
15 return lines.join('\n')
16}
17
18// Each system's own clipboard program, tried in order; the first that runs wins.
19const COPIERS: readonly (readonly string[])[] = [
20 ['powershell.exe', '-NoProfile', '-Command', '$input | Set-Clipboard'],
21 ['pbcopy'],
22 ['wl-copy'],
23 ['xclip', '-selection', 'clipboard'],
24]
25
26async function copy($: EngineInterface, text: string): Promise<boolean> {
27 for (const argv of COPIERS) {
28 const ran = await $.process.run(argv, { stdin: text, timeoutMs: 5000 }).catch(() => null)
29 if (ran && ran.exitCode === 0) return true
30 }
31 return false
32}
33
34export const register: Register = on => {
35 // /clip can already belong to something of yours, a skill or another mod. When
36 // it does, the mod answers to /clipboard instead, and the start carries on.
37 on('session.start', async ($, e, next) => {
38 const NAMES = ['clip', 'clipboard']
39 for (const name of NAMES) {
40 const ok = await $.command
41 .register({ name, description: `Put text on your clipboard, cleaned of terminal clutter. /${name} <text>` })
42 .then(() => true, () => false)
43 if (ok) break
44 }
45 return next(e)
46 })
47
48 on('command.run', { command: 'clip' }, async ($, e) => ({ text: await clipText($, e.args) }))
49 on('command.run', { command: 'clipboard' }, async ($, e) => ({ text: await clipText($, e.args) }))
50}
51
52async function clipText($: EngineInterface, args: string): Promise<string> {
53 const text = clean(args)
54 if (!text) return 'Nothing to copy. /clip <text> puts that text on your clipboard.'
55 const ok = await copy($, text)
56 if (!ok) return 'No clipboard program answered on this machine, so nothing was copied.'
57 const chars = [...text].length
58 return `📎 On your clipboard: ${chars} character${chars === 1 ? '' : 's'}.`
59}
60