carmoji
v0.3.7
Published
Bring your CarMoji pet to life from Claude Code / Codex — a LAN bridge that streams coding events to the CarMoji iOS app
Maintainers
Readme
CarMoji Bridge
Forwards coding events from Claude Code, Codex, Gemini CLI, Cursor, Qoder, Factory, CodeBuddy, Copilot CLI, Kimi Code CLI, Trae, Qwen Code, Cline and friends on your computer to the CarMoji pet on your phone, over a WebSocket on the same Wi-Fi. The pet wakes up when a session starts, "types" while the agent edits, pores over a document while it reads and searches, gets nervous while tests run, celebrates finished turns, and pleads with big puppy eyes when the agent is waiting for your approval.
Setup (once)
On the phone: open CarMoji, tap the laptop icon (top-left), turn on Listen on this Wi-Fi, and note the 4-digit pairing code.
On your computer (Node.js ≥ 18):
npx carmoji pair <code>The phone is discovered automatically via Bonjour — no IP addresses. Pairing sends a test event, so the pet celebrates when it works.
With several phones (or a simulator) on the network,
pairlists what it found and asks which one you mean. You can also skip discovery and pair by the address shown on the Code Buddy sheet:npx carmoji pair <code> 192.168.1.23:8737(Working from a clone of this repo,
cd bridge && npm install && node carmoji.js …does the same thing.)npx carmoji discoverlists every CarMoji it can see without pairing — handy for checking connectivity. On macOS discovery goes through the system'sdns-sd, which handles Macs full of VPN tunnels and virtual interfaces; other platforms use a JS mDNS fallback.Pairing is additive: pair again with a second phone and every event fans out to all paired devices (approval requests go to all of them too — the first tap wins). When devices are already paired,
pairasks whether to keep them.npx carmoji unpairforgets a device — by name or address, orunpair allfor a clean slate.
Claude Code
To get reactions in every project:
npx carmoji install claudemerges the hooks into ~/.claude/settings.json, preserving everything
already there (a backup is saved to settings.json.bak first, reruns are
no-ops, and a file that doesn't parse is refused untouched). Prefer to do
it by hand? npx carmoji config claude prints the snippet instead. Every
command answers --help; npx carmoji hook --help documents the JSON the
hook accepts, and npx carmoji uninstall <tool>|all takes it all out again.
All the other tools
npx carmoji tools # what's supported, what's detected
npx carmoji install codex # one tool…
npx carmoji install all # …or hooks for everything found on this machine
npx carmoji uninstall <tool>| Tool | Hooks written to | Notes |
| --- | --- | --- |
| claude | ~/.claude/settings.json | full event coverage |
| codex | $CODEX_HOME/hooks.json | turn, tool, and permission events via Codex hooks |
| gemini | ~/.gemini/settings.json | Before/After tool + agent events |
| cursor | ~/.cursor/hooks.json | shell/read/edit/MCP events |
| trae, traecn | ~/.trae{,-cn}/hooks.json | Trae IDE (Cursor-style events) |
| qoder, qoderwork | ~/.qoder{work}/settings.json | Claude Code forks |
| factory | ~/.factory/settings.json | sends as source droid |
| codebuddy | ~/.codebuddy/settings.json | Claude Code fork |
| qwen | ~/.qwen/settings.json | Claude fork, ms timeouts |
| copilot | ~/.copilot/hooks/carmoji.json | GitHub Copilot CLI |
| kimi | ~/.kimi/config.toml | TOML [[hooks]] blocks |
| cline | ~/Documents/Cline/Hooks/ | one script per event |
Everything merges non-destructively: a backup is written next to each
config, reruns are idempotent, only carmoji-owned entries are ever
removed, and unparseable files are refused untouched. install all skips
tools that aren't on the machine.
Codex requires new or changed command hooks to be trusted. After installing,
open /hooks in Codex, review the CarMoji commands, and trust them. The
installer removes a direct legacy CarMoji notify command, or removes only
CarMoji when another notifier has it chained via --previous-notify.
All of them funnel through the same carmoji hook entry point
(--source names the tool, --event supplies the event name for tools
whose payloads lack one), so the pet reacts identically no matter which
agent is working — each with its own colored dot on the dashboard.
Older Codex versions without hooks support can still use
npx carmoji install-codex-notify, which sets the notify program in
~/.codex/config.toml (turn completions only). OpenCode users running the
community omo plugin get events for free — omo fires your Claude Code
hooks, which already carry carmoji.
Try it without a coding agent
npx carmoji test start # wakes up, excited
npx carmoji test edit # focused typing face
npx carmoji test run # nervous side-eye
npx carmoji test permission # pleading + floating "?"
npx carmoji test approval # Allow/Deny card; waits and prints your tap
npx carmoji test done # celebration with hearts
npx carmoji test error # dizzy collapse
npx carmoji test end # contented sighMultiple agents & token speed
- Every message carries the Claude Code
session_id(or Codex conversation id), so several agents can run at once. The pet plays the latest event, but if one agent is still blocked waiting for your approval, it returns to the pleading face after another agent's reaction — and stops pleading as soon as the blocked session moves again. - The bridge counts each session's cumulative output tokens by tailing Claude
message-usage records or Codex
token_countrecords incrementally (per-session byte offsets in~/.config/carmoji/sessions/). While in buddy mode, the dashboard's speed readout switches from km/h to tok/s — combined across all agents — with the active agent count below it. Codex samples arrive at hook boundaries rather than for every streamed token, so its tok/s value is an approximation. Codex documents its transcript format as unstable; unknown future formats safely fall back to the working-time display.
What the phone shows
In buddy mode the dashboard swaps the car for a little robot sprite that scans while agents work, goes wide-eyed with a "?" when one waits for your approval, and dozes when everyone's idle — with a dot per connected agent and the active project's name underneath. The laptop button gets a green dot while agents are live, and GPS pauses to save battery. Prompt snippets and permission messages (first ~140 chars) are sent to the phone as brief toasts; they stay on your LAN and are only accepted by the paired device.
Plan-window usage
The buddy dashboard's corner shows your rolling 5-hour and 7-day token totals ("5H 2.5M / 7D 13.7M"), summed from the local Claude Code transcripts the same way community usage tools do (input + output + cache-write tokens; cache reads excluded). It's your real consumption, not an official percentage of plan quota — there's no public API for that. Recomputed at turn boundaries with a 5-minute cache so hooks stay fast.
Answer permissions from the phone (Claude Code and Codex)
carmoji install claude and carmoji install codex hook each agent's
PermissionRequest event, which fires only when a permission dialog is
about to appear —
allowlisted tools and auto-accepted edits never trigger it, so nothing
ever slows down a call that would have run anyway. When it fires, the
phone chimes (a doorbell sound used by nothing else), shows big pleading
eyes, and pops an Allow / Deny card with the command text — your tap
settles the prompt on the computer via the hook's official decision
output. No answer within ~55 s falls through to the normal terminal
prompt (npx carmoji test approval exercises the whole loop).
For Claude Code, simple AskUserQuestion pickers ride the same hook: one
single-select question with 2–4 labeled options becomes a tappable option
card on the phone (npx carmoji test question demos it), and your pick is
fed back as the answer. Richer questions — several at once, multi-select,
free text — still show as a pleading toast and are answered in the terminal.
Codex questions use its App Server requestUserInput protocol rather than
lifecycle hooks, so they remain terminal-only; Codex Allow/Deny permission
approvals do work from the phone.
Older Claude Code versions without the PermissionRequest hook can use
npx carmoji config gate instead: a legacy PreToolUse gate with a tool
matcher (e.g. Bash). Unlike PermissionRequest, that variant fires on
every matched call, allowlisted or not, so keep its matcher narrow.
How it works
- The app runs an
NWListenerWebSocket server on port 8737, advertised via Bonjour as_carmoji._tcp. LAN only; every frame must carry the pairing code. carmoji.js hookis invoked by the coding agent's hooks with event JSON on stdin, maps it to a wire event, sends one frame (≤0.9 s budget), and always exits 0 — it can never slow down or break a coding session. When unpaired it exits immediately.- Pairing state lives in
~/.config/carmoji/config.json.
