posts

the whole thing is a file watcher

2026-05-20 · launch day

The card looks like magic. Your Discord profile shows the model you are running, the project you are in, the tool Claude is using right now, and it clears the second you close the session. People assume there is a plugin inside Claude Code, or some API polling loop, or worse.

There is none of that. The whole thing is a file watcher with good manners.

the hook side

Claude Code fires lifecycle hooks: SessionStart, PreToolUse, PostToolUse, Stop, SessionEnd. The setup command registers a tiny hook on each of them. Each hook does exactly one thing: write a small JSON state file and exit.

That is the entire footprint inside your session. No network calls, no daemon logic, nothing that can slow Claude down or break a session if claude-rpc has a bug. If the daemon is dead, the hook still just writes a file and leaves.

the daemon side

A separate long lived daemon owns the Discord half. Discord exposes a local IPC socket on every desktop install, a unix socket on macOS and Linux, a named pipe on Windows. The daemon connects once, holds the socket, and watches the state file plus the live transcript on disk.

When something changes, it pushes a presence frame. Frames rotate: current model and project, the file or tool in flight, hours today, lifetime tokens and cost, subscription usage. When you git push from a session, it flips to a shipped frame for a bit, because shipping deserves a frame.

When the session ends, SessionEnd writes the final state and the card clears instantly. No ghost presence three minutes after you closed the terminal.

the stats side

Lifetime numbers come from a third piece: a scanner that walks ~/.claude/projects/**/*.jsonl, the transcripts Claude Code already keeps on disk, and aggregates active time, tokens, cost, streaks, and languages. It caches incrementally, so a rescan after a long day touches only the new lines.

why this shape

The hooks are the only code that runs inside your session, so they have to be near zero cost. The daemon can afford to be long lived because it does almost nothing. The scanner can afford to be thorough because it runs on its own schedule. Three small parts, one file between them.

Boring design, in the best sense. Try it: npx claude-rpc setup

← all posts