Teardownspublic

Clawd on Desk

I tried to justify installing it with measurements and failed. The record says so, in place.

I installed it, and that install was not justified on productivity grounds. Held against my own setup, four of the seven things this app sells were already running, and measurement cut the remaining three down to one and a half. The value came from somewhere else entirely — this repository is a machine for injecting its own wiring into twenty-six other people’s config files, without breaking them, in a way that can be undone, and three of those methods came home as specifications.

layer 1 — wiring injection (install · every launch) 26 installers hooks into 21 agents' configs layer 2 — event send hook script one process per event layer 3 — display pet window (Electron) many sessions → one frame POST 127.0.0.1:23333/state state is a one-way notification — kill the app and the agent runs on only permissions travel back — your click returns as the response body this is the one place the app sits inside the agent's decision path All the risk is concentrated in one feature. Which also means it breaks loudly when it breaks
It never modifies the agent. It only registers its own script against the hook interface each agent already publishes.

What the thing actually is

A desktop pet. Not the kind that follows your cursor — it subscribes to the lifecycle events of AI coding agents. Send a prompt and it thinks; run a tool and it types; spawn a subagent and it juggles; hit a permission request and it raises a speech bubble; leave it alone and it sleeps.

It supports twenty-one agents: Claude Code, Codex CLI, Copilot CLI, Gemini CLI, Cursor Agent, Kiro, opencode and more. Their config schemas differ wildly — JSON, JSON-with-comments, plugin packages, Python plugins — so it carries twenty-six separate installers.

It isn’t toy-sized. Hooks and source JavaScript alone come to 5.8 MB, and there are 384 test files with a set per adapter. 5,894 stars, 1,906 commits, 91 contributors.

How events become state

Not a 1:1 mapping. It counts concurrent sessions and subagents and raises a tier — the same event becomes a different animation depending on how many sessions are running.

Agent event State Animation
idle idle cursor-tracking · random patrol
prompt submitted thinking thought bubble
tool running — 1 session working typing
tool running — 3+ sessions working (building) construction
1 subagent juggling headphones
2+ subagents juggling (tier 2) three-ball juggle
permission request attention permission card + terminal auto-focus
task complete celebration celebrating + sound

That second-to-last row becomes a problem later.

The wiring method is what this repo really is

It looks like a pixel crab; what’s actually valuable is how it avoids breaking other people’s config files. For anyone who installs hooks and cron jobs by hand, that part is a textbook.

① The installer plants its own uninstaller. At install time it copies a removal script into the install folder and records the user’s home directory as it was at that moment into a marker file. On removal it reads that file, restores the environment it was installed under, and strips only its own wiring out of twenty-one configs. Two points — recovery still works if the repository disappears, and it finds the right target even if the environment has changed since. It runs the removal script through the app binary used as a Node runtime, so it works even where Node isn’t installed.

② Append, never overwrite. The comment at the top of the installer states the contract: “Does NOT overwrite existing hooks — appends to arrays.” Write to a temp file, swap atomically, back up only files that already existed, and cap how many backups are kept. If a non-array value turns up, wrap it in an array rather than replacing it.

③ Decide ownership by marker. If a status line already exists, check the marker string and if it belongs to someone else, leave it alone and just log. Chaining happens only on an explicit request.

All three came across as specifications, not code. It’s AGPL-3.0, and since the thing runs a local HTTP server the network clause isn’t a dead letter — one copied line would put the whole derivative under a disclosure obligation.

A confirmed defect — the tier the docs promise never rises

I checked that “2+ subagents → three-ball juggle” row directly, judging from two independent sources, the log and the screen, with the reference artwork pulled from the repository to fix what each animation looks like.

Subagents Log Screen
1 state=juggling headphones as specified
2 state=juggling still headphones violates spec
after exit back to state=working typing as specified

The events arrive precisely and the logical state transitions; the artwork stays on the one-subagent tier. Reproduced twice, with the one-subagent control behaving correctly.

This is where I nearly got it wrong. Seeing state=juggling in the log, I could have closed it as “juggling works.” The tier isn’t logged — which asset got picked exists only on screen. Judge a visual state without a screenshot and you verify half of it and pass.

There was a wrong answer to rule out too. Per the docs, “two concurrent sessions” uses the same headphone artwork, so what I saw could have been session count rather than subagents. Confirming the log said juggling (not working) at capture time ruled it out. Skip that check and the conclusion inverts.

Filed upstream, with the three exclusions and a suggestion: log the tier and the asset name.

There’s a trap in the docs, too. One investigation document says that with the app not running, the agent auto-denies every file edit, and closes by saying it’s waiting on an upstream fix. That upstream issue was closed three months earlier. Only the document stopped there — read it cold and you’d conclude it still breaks today.

Measured against my setup, four were already there

I took a pre-install snapshot and diffed. Hooks 15 → 30, zero lost, status line untouched. That’s ③ above working as advertised, and it’s what ruled out the destructive risk.

4 already had it completion alerts · status line away notices · session end 3 actually new live session count · dashboard terminal auto-focus what my setup already had the 7 things Clawd sells and after measuring, that 3 became 1.5 — it can't tell one subagent from two
Judged against what was in my settings.json before installing. Four of the seven were already running.

The four duplicates, specifically. Completion alerts — a stop hook was already raising a toast. Current state — the status line already showed it, permanently. Away notification — that was already going to Slack automatically. Session-end handling — two hooks already did it.

I went looking for a tool because I felt I needed completion alerts, and the thing doing that already existed. The real yield here is that comparison table, not the app — fill it in first next time and an adoption decision takes minutes.

The verdict — I wrote down that it was taste

The app Kept — destructive risk ruled out by measurement. The case is thin, as below
The three specifications (uninstaller, safe config write, marker ownership) Adopted — each maps 1:1 to an incident of my own
Copying code ⛔ Forbidden — AGPL-3.0. No conditions, no revisit

This install was not justified on productivity grounds. I installed it because I wanted to.

Writing that sentence down, in place, was the most useful thing this investigation produced. Because later the memory rewrites itself into “I added it for the efficiency.”

The cost gets the same honesty. The hook spawns an extra process per tool call, so a pipeline run could slow down — but I didn’t measure it. That’s deduced from the wiring, not observed, so it stays an estimate. Promoting it means running the same input before and after and comparing stage times.

The safety verdict has an expiry date too. What I checked was one snapshot immediately after install, and this app re-syncs its hooks on every launch. The same merge repeats from now on, and if an update changes that logic it changes quietly. Re-checking the hook count and my own hooks after the next update is what keeps this verdict alive.

⚠ And a standing guard. If a tool use comes back denied when I never denied it, suspect the app being closed while its permission hook is still wired. That single dashed line in the diagram above is the path — and the symptom is quiet enough to lose an entire unattended run.