A terminal pet that lives above your Claude Code status line. It hatches from an egg, gets a name, ages, reacts to your coding sessions, and says things.
.---.
( o.o ) Kizuhi
`---' "blobbing in solidarity"
you@laptop my-project (main*) Opus 5 ctx:47% sess:$0.41
The easy way — ask Claude to do it. Paste this into any Claude Code session:
Install shellmate from https://github.com/araujobsd/shellmate — clone it to
~/dev/shellmate and run ./install.sh, then show me what it changed.
Or do it yourself:
git clone https://github.com/araujobsd/shellmate.git ~/dev/shellmate
cd ~/dev/shellmate && ./install.shEither way, reload Claude Code afterwards. Your buddy starts as an egg.
The installer backs up ~/.claude/settings.json first, adds four hooks, sets
statusLine.refreshInterval, and installs the /petbuddy command. If you
already have a status line command it will not overwrite it — it prints the two
lines to add to your own script instead.
Requirements: Claude Code, python3 3.11+. No other dependencies, ever.
It hatches once, then it's yours:
| Stage | Age | What you see |
|---|---|---|
| egg | first 8 hours | an egg that cracks progressively, and mutters |
| hatchling | 8h – 2 days | a small version of your buddy |
| juvenile | 2 – 4 days | full size |
| adult | 4 days on | full size |
python3 -m shellmate --whoami # Kizuhi the blob, 3d old (juvenile), petted 6 times
python3 -m shellmate --all # every buddy, every mood and stage, side by sideSpecies and name are rolled once from a random seed at first hatch and persist in
~/.local/state/shellmate/identity.json. Delete that file to roll a new buddy.
The seed is a random UUID — it is not derived from your username, hostname or
account, so your buddy's identity says nothing about you.
Override the roll any time:
# ~/.config/shellmate/config.toml
character = "octopus"The face reflects the session in that pane, not an aggregate — your buddy in a quiet conversation stays quiet even while another session is busy.
| Mood | Means |
|---|---|
| sleeping | nothing happening in this session |
| working | Claude is working here |
| perked | a turn just finished |
| alert | finished and waiting ~2 minutes |
| alarmed | waiting past 10 minutes, or blocked |
| happy | you just petted it |
| offline | shellmate can't see session state |
Every mood has art for every stage, so a hatchling stays hatchling-sized even
when something needs you — urgency is carried by colour and the ! / !! marks,
not by size. alert, alarmed and offline never animate idly, and offline
does not animate at all: a buddy that can't see session state shouldn't look
alive.
Each species has its own colour and keeps it in every mood, so your buddy looks
like itself all day. The mood colour rides on the trailing marks (z, ?, *,
!, !!, ..) and on its name instead. Species colours are held a minimum
distance from every mark colour — otherwise a tan dog's yellow ! would vanish
into its own body exactly when it mattered.
/petbuddy
Or python3 -m shellmate --pet. Your buddy is visibly happy for 10 seconds and
the count goes in --whoami. Petting never overrides alert, alarmed or
offline — a pet cannot hide the fact that something needs you.
Sixteen. Each has its own face, compact form, hatchling variant, idle behaviour, and about 30 phrases in its own voice — 474 in total across the roster.
Eleven you can roll. One is picked for you at first hatch:
cat =o.o= aloof, faintly judgmental |
owl {o.o} dry, formal |
blob (o.o) literal, absurd |
dog Uo.oU earnest, shouty |
frog @o.o@ blunt, monosyllabic |
ghost ~o.o~ wistful, trails off |
penguin <o.o> pompous, dignified |
robot [o.o] terse, machine-like |
cactus |o.o| stoic, needs nothing |
crab %o.o% sideways thinker |
octopus 8o.o8 frazzled, many-handed |
Two rare ones, each 1 in 100. They roll like any other buddy, they just
almost never come up. --whoami tells you if you got one.
dragon ^o.o^ imperious, bored by mortals |
glitch #0.0# corrupted, speaks in hex |
The dragon is gold. The glitch renders as a moving spectrum — every glyph a different colour — and animates on four frames instead of two.
Three you cannot roll at all. These are set by hand or not at all:
# ~/.config/shellmate/config.toml
character = "ember"ember ▒o.o▒ a small burning thing |
moth ▚o.o▞ drawn to any light |
golem ▙o.o▟ slow, patient, made of stone |
▄▀▄ ▚▚▚ ▞▞▞ ▛▜ ▛▜
▒o.o▒ ▚▖o.o▗▞ ▙▙o.o▟▟
▀▀▀ ▘ ▝ ▙░▟
ember moth golem
They are drawn in Unicode block glyphs rather than ASCII, which is exactly why
they are opt-in. Block glyphs come in two East Asian Width classes, and a
terminal configured with ambiguous-width=double renders them at twice the
expected size. A buddy you rolled by accident has to work everywhere; a buddy you
chose by name does not, and you can simply unset it. Everything rollable stays
pure ASCII for that reason.
Set character back to any name above, or delete the line, to return to your
rolled buddy — your identity is untouched by the override, and --whoami shows
both.
~/.config/shellmate/config.toml — every field optional.
| Field | Default | Notes |
|---|---|---|
character |
"" |
Empty uses your rolled species. Any name from the roster overrides it. |
show_name |
true |
Show the buddy's name beside the sprite. |
show_phrase |
true |
Show what it's saying. A phrase holds for at least 90 seconds so it doesn't flicker as the mood moves; escalation overrides that immediately. |
notify |
false |
Desktop notification when a session is ignored past high_seconds. Off by default — see below. |
poll_seconds |
2.0 |
How often session state is sampled. |
frame_seconds |
0.6 |
Animation frame interval. |
med_seconds |
120 |
Waiting this long → alert. |
high_seconds |
600 |
Waiting this long → alarmed, and notifies if enabled. |
crit_seconds |
1200 |
Waiting this long → notifies a second time if enabled. |
ascii_glyphs |
false |
Pure-ASCII glyphs and box characters, for terminals that render ambiguous-width characters inconsistently. |
Notifications are off by default and that is deliberate. For a solo
interactive session, sitting done for ten minutes is normal — you're reading
output or getting coffee. Toasting you for that is obnoxious. Turn them on if you
run several sessions in parallel and want to be told when one has been ignored.
Claude Code hooks (UserPromptSubmit, Stop, SessionStart, SessionEnd) write
one small JSON file per session to ~/.local/state/shellmate/sessions/. The
status line script reads those, works out the mood for the current session, and
renders a sprite.
Two details that matter if you're reading the code:
The hot path is pure shell. Your status line re-renders about once a second in
every pane. Rendering with Python each time cost ~110 ms and over a CPU-second per
second across ten panes. So a backgrounded cold path pre-renders both animation
frames to disk every couple of seconds, and the per-render path just cats one —
about 10 ms. The frame index comes from the wall clock, so all panes animate in
sync.
Stale caches show as offline. If the cold path breaks, the cached frames stay
on disk and would keep alternating from the clock — the buddy would look alive
while showing dead state. Frames older than ~12 seconds render the offline face
instead. A dead buddy should look dead.
./install.sh --uninstallRemoves what it added and restores your settings. Your buddy's identity survives
in ~/.local/state/shellmate/identity.json — delete that too if you want it gone.
coding-buddy does something similar
and does it well: 19 species, rarity tiers, stats, and speech written by the model
itself through an MCP tool. If you want the richer thing, install that. It needs
Bun and jq.
shellmate is smaller and needs neither — just the python3 you already have.