claude-color MIT · Windows · VIA raw-HID

Your keyboard is the Claude Code status light.

A tiny CLI that lights your wired mechanical keyboard to match what Claude Code is doing — so you can glance down instead of at the terminal. No cloud, no telemetry, no extra hardware.

pip install claude-color latest on PyPI ↗

Why claude-color

Correct under real conditions — not just a demo.

Anyone can flash an LED on a hook. The hard part is many sessions, crashes, and sleep. claude-color is built for those.

Concurrency

Multi-session, one keyboard

Run Claude Code in a dozen terminals; the color resolves by priority — error > waiting > working > idle — so the board shows what matters most.

Resilience

Survives crashes & sleep

Sessions are tracked by their Claude process id. Dead ones are pruned, and a reconcile on logon/unlock restores your lighting when nothing is running.

Accuracy

Knows waiting from idle

A real permission prompt turns the board amber; a finished-but-open session goes green — distinguished by Claude Code's notification_type, not guesswork.

Courtesy

Puts your lighting back

It captures your real keyboard color on the way in and restores it on the way out — with a guard so it never captures one of its own status colors.

Reach

Keyboard-generic

Speaks the standard VIA raw-HID protocol (usage page 0xFF60), so any wired VIA keyboard with an RGB matrix works. Validated on the Keychron Q6 Max.

Trust

Local, tested, quiet

Everything runs on your machine over USB — no cloud, no telemetry. Backed by 104 tests, Windows-first today, cross-platform on the roadmap.

How it works

Hooks in, color out.

Claude Code fires lifecycle hooks; claude-color turns them into one winning color across every live session.

Source Claude Code hooks

SessionStart · UserPromptSubmit · Notification · Stop · SessionEnd

→
Handle claude-color hook

Maps each event to a per-session state; records the owning Claude PID.

→
Resolve state.json

Prunes dead PIDs, then picks the winner: error > waiting > working > idle.

→
Light keyboard · VIA HID

Pushes the solid color over USB; restores your baseline when empty.

Out of band, claude-color reconcile runs on logon/unlock to recover the lighting after an ungraceful exit that never fired a Stop hook.

Get started

A few lines, then it just glows.

Requires Windows 11 and a wired VIA keyboard with an RGB matrix. Install from PyPI with pip (or uv). Everything is tunable in config.toml.

# install from PyPI
pip install claude-color

# confirm the keyboard is detected
claude-color doctor

# find your solid + breathing effect indices
claude-color probe

# wire the Claude Code hooks
claude-color install

# show it off: cycle the states, then restore
claude-color test --dwell 30
[colors]working / waiting / idle / error as H,S,V (0–255). Defaults: 180,255,200 · 30,255,220 · 90,255,120 · 0,255,220.
[breathing]Per-state opt-in state = speed (0–255); remove a line to keep it solid. Defaults: waiting = 128, error = 160.
inherit_brightnesstrue (default): status colors use the keyboard's own brightness. false: each state's V.
backendauto (default), qmk_hid, or hidapi.
vid / pidHex device filter — only if several HID devices match. Find yours with claude-color doctor.
solid_effect_indexYour keymap's "solid color" effect index (default 1) — bump it if a solid status shows an animation.
breathing_effect_indexYour keymap's breathing effect index (default 2) — find yours with claude-color probe.
staleness_minutesA stuck session is forgotten after this (default 120).

Roadmap

✓On PyPI nowCustomization →Linux & macOS →Wireless (custom QMK) →Multi-agent · Codex / Cursor

Make it yours

Your statuses, your colors.

Pick a color per state, switch breathing on and set its speed, and choose whether brightness follows your board — the preview updates live and you get a complete config.toml block to copy. HSV on QMK's 0–255 scale is fiddly by hand, so let the picker do the conversion.

workinggenerating / running tools
speed128
waitingneeds your input
speed128
idledone, session alive
speed128
errorAPI / session failure
speed160
working
waiting
idle
error
inherit_brightness = true

Drop the generated block straight into config.toml — a friendlier in-app picker is on the roadmap.

Vibe-coded project dashboard

Ship the vibe. Keep the tokens.

The one dashboard a vibe-coded project actually deserves — not stars or downloads, but the tokens you didn't burn re-deriving something that already works.

Token-meter · what the MVP actually cost

≈ 1 milliontokens saved*

That's the real spend it took to build claude-color's MVP — brainstorming, sniffing the VIA raw-HID protocol, the multi-session, crash, and sleep edge cases, and all 104 tests. pip install claude-color is roughly what you don't spend re-deriving it yourself.

reinvent it yourselfyou, using this →

*Not a marketing round number — over a million tokens genuinely went into this MVP across brainstorming, implementation, debugging, and review. What you'd re-spend depends on your own path.

2,419lines of tested Python you didn't write
104tests you didn't debug at 2 a.m.
1HID protocol you didn't reverse-engineer
3edge cases already handled: multi-session, crashes, sleep

Now it's your turn to save the next person. Got a keyboard that needs a tweak, or an edge case we missed? One PR compounds — everyone after you skips the same rediscovery.

Consider contributing →