@drakulavich/peon-pet
v0.1.2
Published
A macOS desktop pet for Peon-Ping — an orc that reacts to your Claude Code events. Native Bun + AppKit (no Electron).
Maintainers
Readme
peon-pet
A macOS desktop pet for Peon-Ping — an orc that reacts to your Claude Code events with sprite animations. Built on Bun + AppKit + Three.js (no Electron — a native NSPanel driven via bun:ffi, rendering the Three.js scene in system WebKit).
Sits in the bottom-left corner of your screen and floats over all windows. Clicks pass through to whatever's underneath — except when you hover the orc itself, where it captures the mouse so you can read tooltips and drag it around.
Requirements
- macOS (the shell is AppKit-native; Linux/Windows are non-goals)
- Bun 1.3+
- Xcode Command Line Tools (
clang, to build the native shim) —xcode-select --install - peon-ping installed and running
Install (npm)
bunx @drakulavich/peon-pet # run it once
# or install globally:
bun add -g @drakulavich/peon-pet && peon-petRequires macOS (Apple Silicon or Intel), Bun, and peon-ping running.
A prebuilt universal (arm64 + x86_64) native shim ships in the package — no Xcode needed. Pass --character <name>
or --corner <corner> as below.
Run from source (development)
git clone https://github.com/drakulavich/peon-pet
cd peon-pet
bun install
bun run start # builds the native shim, then launches the petbun run start compiles native/libpeonshell.dylib (a tiny AppKit shim) and runs
src/main.ts. Check your dock for the Peon-Ping logo.
Install permanently (auto-start at login)
./install.shInstalls a macOS LaunchAgent that starts peon-pet at login and restarts it if it quits. Logs go to /tmp/peon-pet.log.
To remove:
./uninstall.shControls
Right-click the Peon-Ping dock icon for a menu:
- Hide Pet / Show Pet — toggle visibility (orc + any sub-agent mini-pets) without quitting
- Quit — exit completely
You can also drag the orc with the mouse to move it, and quit with Ctrl-C in the
foreground (or ./uninstall.sh to remove the LaunchAgent). Only one instance runs at
a time — launching a second just exits.
Characters
The bundled skin is orc. You can add your own and select it in config or with a CLI flag:
bun run build:native # once
bun src/main.ts --character my-skin # or --corner top-rightConfig lives at ~/Library/Application Support/peon-pet/peon-pet-config.json (the
legacy Electron Peon Pet dir is reused automatically if you're upgrading):
{ "character": "my-skin", "corner": "bottom-right" }Custom skins: drop your own PNGs in
~/Library/Application Support/peon-pet/characters/<name>/ —
sprite-atlas.png, borders.png, bg.png, dock-icon.png. Any asset you don't
provide falls back to the bundled orc. Precedence for the active character is
--character > config > orc.
Animations
| Claude Code event | Animation | |---|---| | Session start / resume | Waking up (plays once) | | Prompt submit | Typing | | Task complete (Stop) | Celebrate | | Permission request / context compact | Alarmed | | Tool failure | Annoyed |
The orc stays in typing mode while any session is actively working (event within last 30 s). Returns to sleeping after 30 s of inactivity.
Session dots
Up to 10 glowing orbs appear above the orc — one per tracked Claude Code session:
- Bright pulsing green — active (event within last 30 s)
- Dim green — idle (last event 30 s–2 min ago)
Sessions are dropped automatically after 10 min of inactivity (the JSONL transcript model has no explicit "session end" — they decay).
Hover over a dot to see the project folder and status. Hover anywhere on the widget to see all active project names.
Sub-agents
When a session spawns sub-agents, a mini-orc (up to 5) pops up stacked above the main pet for each active sub-agent and disappears when it finishes. The parent session is kept "awake" while its sub-agents run, so the orc doesn't fall asleep during long sub-agent tasks.
Architecture
src/main.ts composition root: window + watcher + session tracker
src/shell/
types.ts NativeShell boundary (interface)
appkit.ts real shell — bun:ffi → native/libpeonshell.dylib
fake.ts in-memory shell for headless tests
src/app/ ported logic (session tracker, jsonl watcher, anim, …)
native/peonshell.m thin AppKit/WebKit C shim (NSPanel + WKWebView +
peon-asset:// scheme handler); built to libpeonshell.dylib
renderer/ unchanged Three.js renderer, loaded over peon-asset://The only file that touches bun:ffi is src/shell/appkit.ts. Everything above the
NativeShell boundary is tested against FakeShell, so the suite runs headlessly.
Development
bun run dev # builds the shim, runs with native diagnostics (--dev)
bun test # 182 tests, headless (no window opened)
bun run build:native # rebuild native/libpeonshell.dylib after editing the shimSpecs of record live under openspec/specs/ (capabilities), with shipped changes
archived in openspec/changes/archive/.
The pet reacts to Claude Code automatically: just use Claude Code and watch the orc
wake / type / celebrate. With bun run dev the terminal echoes each reaction, e.g.
→ orc: typing (UserPromptSubmit).
Valid events: SessionStart, Stop, UserPromptSubmit, PermissionRequest, PostToolUseFailure, PreCompact
Sprite atlas
The orc sprite sheet is a 6×6 pixel art atlas (renderer/assets/orc-sprite-atlas.png, 3072×3072). Row layout:
| Row | Animation | |---|---| | 0 | Sleeping | | 1 | Waking | | 2 | Typing | | 3 | Alarmed | | 4 | Celebrate | | 5 | Annoyed |
See docs/sprite-atlas-prompt.md for the generation prompt used with image models.
