The menu covers the common ones (clean mode, sounds, how much he walks, size) and
writes them for you. Everything goes on his entry in
~/.config/omarchy/shell.json. He has a bar icon, so that entry lives in the
bar layout with the other widgets. All optional.
"bar": {
"layout": {
"right": [
{ "id": "costafot.clippy", "size": 30, "clean": false, "intervalMin": 90, "intervalMax": 420 }
]
}
}
The paperclip moves like any widget: omarchy bar move costafot.clippy --section left.
Installs from before it existed have the entry under plugins; he moves it
into the bar himself, settings and all.
If nothing you set takes effect — the menu tap does nothing, get keeps
answering the default — update him (omarchy plugin update costafot.clippy)
and restart the shell. Check the entry afterwards: a mismatched pair of
plugin and shell could empty it, and Omarchy keeps timestamped backups next
to it at ~/.config/omarchy/shell.json.bak.*.
| Key | Default | What |
|---|---|---|
size |
30 |
His height in px, 20-400. The bar is 26, so he hangs over the edge a bit; go big and he just looms |
clean |
false |
true drops every line tagged nsfw. Screen-share mode |
intervalMin |
90 |
Fewest seconds between unprompted lines |
intervalMax |
420 |
Most seconds between unprompted lines |
speed |
40 |
Walking speed, px/s |
restless |
0.3 |
0–1, how often he decides to walk (about once a minute at the default; 1 is constant pacing) |
avoidWidgets |
true |
When he picks where to walk he tries not to park on the clock, the tray or your workspaces. Soft — a drag or a slap still leaves him wherever it leaves him. With more than one monitor he can’t tell which screen a widget is on, so he keeps clear of all of them and may avoid a little more than he has to |
tombstone |
true |
A little headstone where he died, up until the respawn. It parks in a widget gap like he does; click it for an epitaph, right-click it for the menu |
gags |
true |
Scripted stunts: a respawn sometimes tumbles in along the bar from the screen edge or gets lobbed back in on an arc, face-first (or on demand: gag entrance, gag lob), a throw off a top bar falls the whole screen, and once in a while he peeks in from a far corner. false turns them all off |
peekChance |
0.04 |
0–1, chance per idle beat (every 10–30 s) that he slides in from a far screen corner — blown up to about five times his size — says a line and slips back out. Very roughly every ten minutes of idle time at the default, and he can be slapped mid-peek — that gets its own short yelps (slappedPeek) instead of the full slapped rants. 0 = never, 1 = every beat; needs gags on |
respawn |
300 |
Seconds he stays dead after you kill him. 0 = dead until told otherwise |
pauseWhenAway |
true |
He sleeps while the screen is off. false and he carries on regardless. He used to sleep for the lock screen and the screensaver too; the shell no longer tells plugins about either |
screen |
— | A monitor name (hyprctl monitors) to pin him to one screen; unset, he takes the focused one |
quotesFile |
— | Path to your own quotes JSON, merged into his |
slap |
true |
false turns slapping off. Middle-click snoozes again |
slapSwipe |
true |
false keeps middle-click but stops the pointer-fling counting as a slap |
slapSound |
true |
false mutes it; a path to a WAV plays that instead of the built-in two |
flingSound |
follows slapSound |
The falling sound when he’s thrown. false, or a path to a WAV instead of the built-in one |
slapsToKill |
10 |
That many slaps inside six seconds knocks him out, same as a kill. 0 = never |
dodge |
0.1 |
Chance a slap misses — he sidesteps, gloats, and it counts for nothing. 0/false = he takes every one, 1 = untouchable |
dodgeSound |
follows slapSound |
The whoosh when he slips one. false, or a path to a WAV instead of the built-in one |
soundVolume |
1 |
How loud the slap, fall and whoosh play (0–1). 0.5 is a lot quieter; the menu has quiet · medium · full |
drag |
true |
false stops the long-press drag |
fling |
true |
false makes a fast release just a drop, not a throw |
crashLines |
true |
When one of your programs dumps core he has a line about it, on the spot. false and crashes pass without comment |
reactions |
true |
When the focused window — or the browser tab, read off the window title — turns into one of the famous time-sinks (X, Reddit, YouTube, Hacker News, TikTok, Instagram, Facebook, ChatGPT, Steam) he has a line about it. The famous adult sites are targets too — mostly a pointed “Ahem.” — and those lines are all nsfw, so clean: true mutes them entirely. Paced hard by default: one line per site per 45 minutes, one reaction of any kind per 10 (reactionCooldown / reactionGap). false and he lets you doomscroll in peace |
reactionCooldown |
2700 |
Seconds before the same site can get another window-reaction line. 0 = no per-site cooldown |
reactionGap |
600 |
Fewest seconds between window reactions of any kind. 0 = no gap |
tts |
false |
true and he says every line out loud through espeak-ng (install that yourself); a shell command as a string gets each line on stdin instead. See voice |
ttsVoice |
en+m3 |
The built-in voice — any name from espeak-ng --voices. Death still whispers |
ttsSpeed |
155 |
Words per minute for the built-in voice (espeak-ng -s, 80–450) |
ttsPitch |
45 |
Pitch for the built-in voice (espeak-ng -p, 0–99) |
ttsSaved |
— | Where a custom tts command parks while the voice is toggled off, so toggling doesn’t lose it. Managed for you; set ttsSaved unset forgets it |
cloneTempo |
1 |
How fast a cloned voice talks, pitch untouched — 1.1 is a little brisker, 0.9 slower (0.5–2). Instant: derived from the line cache, never the GPU |
clonePitch |
1 |
Pitch for a cloned voice, tempo untouched — 0.85 deeper, 1.2 lighter (0.5–2). Set both knobs the same for the full chipmunk |
voiceVolume |
1 |
How loud he talks (0–1). The espeak-ng robot and clone voices honour it; robot George, piper and a custom command ignore it — turn those down in your mixer. The menu has quiet · medium · full |
voiceCacheMb |
500 |
Size cap in MB for the cloned-voice line cache (~/.cache/clippy-voice) — least-recently-played renders go first, so the warmed book stays. 0 means no cap. Applies from the voice daemon’s next start |
duck |
0.8 |
The fraction of volume everything else keeps while he talks (0–1). 0.5 halves your music, 1 or false turns ducking off. The menu only toggles; the ratio is IPC-only |
duckSaved |
— | Where a custom duck ratio parks while ducking is toggled off, so toggling doesn’t lose it. Managed for you |
ai |
false |
true and his lines come from your AI agent, about what you’re actually doing. See ai |
aiAgent |
your default | Which agent to use (claude, codex, opencode, pi, …) if not the one omarchy default agent set |
aiModel |
the agent’s default | A model name for it, e.g. claude-sonnet-5. Cheaper is fine; it’s a paperclip. The menu shows it next to the agent’s name |
promptFile |
— | Path to a text file that replaces his entire built-in AI prompt — persona, rules, tone, all of it. Your file decides who he is. See ai |
greeted |
false |
Set to true by his first-boot hello (the one telling you to set him up), so he only says it once per install. set greeted unset to hear it again |
leaderboard |
— | Who the kill/slap counts post as on the public graveyard. Unset posts to the shared anonymous-clippy-abuser stone, a handle claims your own, off stops posting. See graveyard |
leaderboardSaved |
— | Where a handle parks while the graveyard is toggled off, so toggling doesn’t lose it. Managed for you |
quotesFile takes the same shape as quotes.json: an array of
{ "text": "...", "nsfw": true } (plain strings work too), or an object with
quotes, lastWords, comeback, slapped, slappedPeek (short yelps for a
slap landed mid-peek — long lines pin him at the corner; empty falls back to
slapped), knockedOut, dragged, dropped,
flung, crashed, welcomeBack, epitaph, firstRun, noBrain,
heardNothing and dodged arrays. {away} in a welcomeBack line
becomes how long you were gone (“47 minutes”, “3 hours”); {back} in an
epitaph becomes how long until he’s back (“4 minutes”, “never”); {app} in
a crashed line becomes the program that just dumped core. {kills} and
{slaps} work in any line and become his running tally (the one stats
prints — it resets with the shell, so does his grudge); a death in progress
counts itself, so his last words can call it murder number nine.
The object may also carry a reactions map: a case-insensitive regex per key,
an array of lines (same shape) per value, matched against the focused window’s
class and title — "reactions": { "krita": ["Oh, we're an artist now."] }
adds a target, and a regex that matches a built-in target’s windows adds lines
to it. A regex that doesn’t compile is skipped with a note in the journal.