Give your AI coding agent a body. When Claude Code, Codex, or any tool finishes, needs your input, or fails, a lightweight animated companion — a painterly butterfly or a full-screen fireworks burst — appears over your workspace, then gets out of the way. No dock icon, no stolen focus, no network, no telemetry.
It plugs into agents the same way a shell hook does: your agent fires a lifecycle event, a tiny CLI turns it into a local URL-scheme message, and the menu-bar app plays the animation. Drop it into any agent that can run a command on an event.
Agents run long. You tab away. You miss the moment it finishes or stalls waiting for approval. A calm ambient signal on your own screen — no notification-center spam, no sound, no focus grab — tells you what happened at a glance and then disappears.
| Companion | Motion | Look |
|---|---|---|
| Butterfly | Airborne | Blender-rendered painterly wings with a live shimmer trail; flies across and tours every display |
| Firecracker | Burst | Procedural SpriteKit fireworks — launches from the middle and bursts at random positions across every screen at once |
Pick one from the menu-bar Companion submenu or Settings. Everything is drawn at runtime — no bundled videos, no proprietary assets.
| State | Signal |
|---|---|
completed |
Calm celebratory presentation, then exits on its own |
input-needed |
Amber attention, re-pulses, and stays available in the menu bar |
failed |
Crimson, sharper/sputtering motion, stays available in the menu bar |
working |
Accepted but hidden unless you enable it in Settings |
Reduce Motion, light/dark appearance, hover-to-pause, and multi-display are all handled. Input-needed and failed events never relaunch on a timer — a single presentation, then a quiet menu-bar entry until you acknowledge them.
Requires macOS 14+, a Swift 6 toolchain, and Apple silicon for the packaging script. Full Xcode is not required to build.
git clone https://github.com/<you>/agent-notifier.git
cd agent-notifier
swift build
./Scripts/package-app.sh # builds + ad-hoc signs the .app and CLI into ../../Artifacts
./Scripts/install-local.sh # installs to ~/Applications, links the CLI, launches itThe app lands in ~/Applications, the helper CLI is linked at
~/.local/bin/butterfly-notify, and the menu-bar process starts.
Gatekeeper: local builds are ad-hoc signed, so macOS will warn the first time. Right-click the app → Open → Open, or clear the quarantine flag:
xattr -dr com.apple.quarantine "~/Applications/Agent Notifier.app". A public binary release still needs Developer ID signing and notarization.
butterfly-notify integrations install claude # or: codex, or: all
butterfly-notify integrations status all
butterfly-notify integrations remove all # fully reversibleThis writes a command hook into ~/.claude/settings.json (Stop, StopFailure,
PermissionRequest, Elicitation, Notification) and/or ~/.codex/hooks.json (Stop,
PermissionRequest). The write is structured, idempotent, and backs up the
existing file first, and only ever touches its own marked hooks — your other
hooks are left untouched. Codex surfaces newly installed command hooks through
its normal trust flow.
If your tool can run a command on an event, you don't need the installer — just call the CLI:
butterfly-notify send --state completed --source my-agent
butterfly-notify send --state input-needed --source my-agent --title "Approval needed"
butterfly-notify send --state failed --source my-agent --project "$PWD"Or pipe a raw Claude/Codex hook JSON payload straight in:
echo '{"hook_event_name":"Stop"}' | butterfly-notify ingest --source claudeThe installer just adds an entry like this — you can write it yourself:
agent lifecycle event
│ (command hook)
▼
butterfly-notify ──encode──▶ butterfly-agent-notifier://event?payload=…
│ │ (local URL scheme, no network)
▼ ▼
/usr/bin/open -g menu-bar app decodes + validates
│
▼
SpriteKit overlay per display
Three small pieces:
ButterflyCore— the event model, URL codec, agent adapters, hook installer, and motion math. Pure Swift, no UI, fully unit-tested.ButterflyNotifyCLI(butterfly-notify) — turns events/hook payloads into a validated local URL and hands it toopen.ButterflyAgentNotifier— theLSUIElementmenu-bar app that renders the companion with AppKit + SpriteKit, one borderless overlay panel per display.
Menu bar → Settings: choose the companion, size, motion intensity, the
completed-event duration, and whether working events show. Launch-at-login and
per-agent hook install/remove/status live there too.
Built to be safe to run and safe to publish:
- No network. No telemetry. No event history. Nothing leaves your machine.
- Events travel only through the registered
butterfly-agent-notifier://URL scheme, delivered withopen— never a shell. - Payloads are size-bounded at both the base64 and decoded layers, JSON is parsed defensively, and every field is validated, whitespace-collapsed, control-character-stripped, and length-capped before it is shown.
- Only bounded
source/title/project/sessionmetadata is accepted. Prompts, transcripts, tool input, and assistant output are never touched. - The hook installer quotes paths for the shell, edits only its own hooks, and backs up your config before writing.
swift build
swift test --enable-swift-testing --disable-xctest # requires full Xcode to run casesOffline check without Xcode (compiles the core with its module and runs a smoke verifier):
swiftc Scripts/core-smoke.swift Sources/ButterflyCore/*.swift -o /tmp/cs && /tmp/csCompanions are a versioned catalog, so new ones don't touch event handling:
- Add a case to
CompanionKindwith amovementStyle(airborne,grounded, orburst) inSources/ButterflyCore/Companion.swift. - Build a
CompanionNoderenderer (procedural SpriteKit — seeFireworkArtwork.swiftfor a self-contained example) and wire it intoCompanionScene.prepare. - Drop a
manifest.jsonunderSources/ButterflyRenderer/Resources/Companions/<name>/.
Developer ID signing + notarization, a downloadable release, direct Cursor hooks, sounds, and community image / Blender companion packs.
MIT © Akshith Gojuru. Contributions welcome — open an issue or PR.