funclaude
v1.0.0
Published
Replace Claude Code's spinner verbs with heists, felonies and supervillainy.
Downloads
152
Maintainers
Readme
funclaude
Claude Code shows a random verb while it works — Cogitating…, Noodling…, Reticulating….
funclaude swaps that list for something with a criminal record:
✻ Planting a bomb… (7s · esc to interrupt)
✻ Hijacking a car… (3s · esc to interrupt)
✻ Cracking the vault… (12s · esc to interrupt)It is not a hack or a patch. Claude Code ships a supported setting for this.
The catch: two renderers, two config files
The terminal TUI and the VS Code panel draw their own spinners from different settings files. Writing only one is the single easiest way to conclude "it doesn't work":
| Where you see the spinner | Config file | Key |
| --- | --- | --- |
| Terminal CLI | ~/.claude/settings.json | spinnerVerbs |
| VS Code / Cursor panel | <editor>/User/settings.json | claudeCode.spinnerVerbs |
The extension never reads ~/.claude/settings.json for verbs — its webview calls
vscode.workspace.getConfiguration("claudeCode").get("spinnerVerbs").
funclaude writes every detected target by default, so this is invisible in normal use.
{ "mode": "replace", "verbs": ["Planting a bomb", "..."] }mode: "replace" uses only your verbs; mode: "append" keeps the stock ones and adds yours.
Claude Code appends the … and the shimmer itself, so verbs are written bare.
Reload behaviour differs too. The VS Code panel watches its config
(affectsConfiguration("claudeCode.spinnerVerbs")) and updates live. The terminal CLI caches
settings in memory at startup and never re-reads them, so it needs a new session.
Install
Zero dependencies, Node 18+. Pick whichever fits the machine.
npx — nothing to install:
npx funclaude use mayhem heistGlobal:
npm install -g funclaude
funclaude use mayhem heistAs a Claude Code plugin — adds a /funclaude command, and runs the bundled CLI
directly via ${CLAUDE_PLUGIN_ROOT}, so it needs no npm at all:
/plugin marketplace add kaizenaiofficial2026/funclaude
/plugin install funclaude
/funclaude use mayhem heistFrom a clone:
node bin/funclaude.js list # run it in place
npm link # or put `funclaude` on your PATHUse
funclaude list # browse the packs
funclaude preview mayhem # watch it spin before committing
funclaude use mayhem heist # apply one or more packs
funclaude use chaos --tips # also replace the spinner tips
funclaude add "Defusing the bomb" # append your own
funclaude roll # sample what's currently active
funclaude status # what's set, and where
funclaude off # back to boringThe VS Code panel updates live. The terminal CLI needs a new session (see below).
The 18-character limit
Every verb is at most 18 characters, and funclaude add enforces it. This is not a style
choice — it works around a real bug in the VS Code spinner.
The webview latches its render width on first mount and can never grow it:
let [text, setText] = useState(" ".repeat(width)) // width = maxVerbLength + 3
function writeChar(s, i, ch) { if (i >= s.length) return s; ... } // refuses to extendWhen a settings change arrives after the component mounts — exactly what a live config update
does — the width is still the one derived from the default verb list. Its longest entry is
Flibbertigibbeting (18 chars), giving a width of 21. Anything longer is silently chopped:
Distracting the receptionist → "Distracting the recep"
Walking away from the explosion → "Walking away from the"Staying inside the stock maximum means our width requirement never exceeds the latched one, so
truncation cannot happen regardless of mount timing. --force overrides the check if you only
ever use the terminal CLI, which pads correctly.
Packs
| Pack | Vibe | Sample |
| --- | --- | --- |
| mayhem | Five-star wanted level | Planting a bomb… Hijacking a car… |
| heist | Ocean's Eleven, minus the ten | Cracking the vault… Killing the lasers… |
| villain | Volcano lair, excellent chair | Monologuing… Feeding sharks… |
| spy | Trench coat, dead drop, trust issues | Going dark… Burning my cover… |
| chaos | Deploying on Friday, confidently | Force-pushing main… Dropping the DB… |
| wizard | Arcane, expensive, occasionally on fire | Sacrificing a goat… Rolling for init… |
| pirate | Yarr, ship it | Plunderin' booty… Keelhaulin' a dev… |
| corporate | The most terrifying pack available | Circling back… Boiling the ocean… |
Packs stack — funclaude use spy heist villain unions all three.
Flags
| Flag | Effect |
| --- | --- |
| --append | Keep Claude Code's defaults and add yours on top |
| --tips | Also override the spinner tips — terminal CLI only, the webview has no tip rendering |
| --cli | Only ~/.claude/settings.json |
| --vscode | Only the editor's settings.json |
| --project | ./.claude/settings.json instead of ~/.claude/settings.json |
| --dry-run | Print the settings patch, change nothing |
| --static | Preview without animation |
With no scope flag, every detected target is written. Editors are auto-detected across VS Code, Insiders, VSCodium, Cursor and Windsurf.
Project settings win over user settings, so --project lets one repo have its own theme.
Safety
Writes are a shallow merge — unrelated keys (theme, model, effortLevel, permissions, and
your whole editor config) are left untouched. Every write drops a timestamped
settings.json.funclaude-*.bak beside the original, and funclaude off removes only the keys
this tool sets.
Editor settings.json files are JSONC. Comments survive reading but not rewriting, so if the
target file has any, funclaude warns you and points at the backup.
Verify at any time with:
claude doctor # reports "Invalid settings" if a settings file is malformedPublishing
The repo serves both distribution paths from one tree:
.claude-plugin/plugin.json plugin manifest
.claude-plugin/marketplace.json single-entry marketplace, source "./"
commands/funclaude.md the /funclaude slash command
bin/ lib/ package.json the npm packageIf you fork this, swap the owner handle in package.json, both manifests and this file:
grep -rl kaizenaiofficial2026 . --exclude-dir=.git \
| xargs sed -i '' 's/kaizenaiofficial2026/<your-handle>/g'To release:
claude plugin validate .claude-plugin/plugin.json # both should pass
claude plugin validate .claude-plugin/marketplace.json
npm pack --dry-run # inspect the tarball
npm publish # needs `npm login`
git push -u origin main # enables the plugin routeBump version in both package.json and .claude-plugin/plugin.json — claude plugin tag
checks that they agree.
Adding a pack
Add an entry to lib/themes.js:
noir: {
label: 'Noir',
blurb: 'It was raining. It is always raining.',
emoji: '🚬',
verbs: ['Lighting a smoke', 'Tailing the dame', 'Taking the case'],
tips: ['Everybody lies.'],
},Present participles, no trailing …, 18 characters or fewer. That's the whole contract.
