Skip to main content

claude-color

PyPI License: MIT Python 3.11+ Platform: Windows

Live docs & demo → edocksil.github.io/claude-color

Drive a wired VIA-compatible keyboard's RGB from Claude Code status — see at a glance whether Claude is working, waiting for you, idle, or hit an error, without looking at the terminal.

  • working → blue-purple
  • waiting for you (permission / input) → amber, breathing
  • idle (turn done, session alive) → deep green
  • error (a turn ended on an API/session failure) → red, breathing
  • Claude Code exits / no session running → your normal lighting is restored

Why claude-color

Built to be correct under real conditions, not just a demo:

  • Multi-session aware — one keyboard, many Claude Code sessions, resolved by priority (error > waiting > working > idle).
  • Survives crashes & sleep — sessions are tracked by their Claude process; dead ones are pruned, and a reconcile on logon, unlock and every five minutes restores your lighting when no session is running.
  • Accurate status — distinguishes a real permission prompt (amber) from a finished-but-idle session (green) via Claude Code's notification_type.
  • Restores your lighting — captures your real keyboard color and puts it back, with a guard against capturing its own status colors.
  • Windows-first, keyboard-generic (any wired VIA RGB keyboard), 127 tests.

Requirements

  • Windows 11, Python 3.11+. Install from PyPI with pip (or uv).
  • Any wired keyboard exposing a VIA raw-HID interface with an RGB matrix, connected by USB cable. VIA support is most commonly provided by QMK firmware with VIA enabled, but only the VIA protocol is required — not QMK itself. Developed and validated on the Keychron Q6 Max. Wireless is not supported (wired only). See docs/COMPATIBILITY.md.
  • Optional but recommended: the qmk_hid binary on your PATH (primary backend). Without it, the built-in Python hidapi backend is used.

Install

From PyPI:

pip install claude-color           # or: uv tool install claude-color

Then set it up (run from any terminal):

claude-color doctor                # confirm the keyboard is detected
claude-color test                  # cycle working/waiting/idle/error, then restore
claude-color install               # wire the Claude Code hooks into %USERPROFILE%\.claude\settings.json

Make sure your Python (or uv) scripts bin dir is on PATH so Claude Code — and the scheduled reconcile task — can invoke claude-color.

Install from source (contributors)
git clone https://github.com/Edocksil/claude-color && cd claude-color
uv tool install --editable .       # exposes `claude-color` on PATH, tracking your clone
claude-color install

Demo

claude-color test cycles working → waiting → idle → error (showing breathing where configured), then restores your lighting. To eyeball a single state, use claude-color set <state> --timeout <seconds> (applies it briefly, then restores).

claude-color test                 # ~2s per color (quick check)
claude-color test --dwell 30      # hold each color 30s (slow demo)
claude-color set error --timeout 5 # briefly show error, then restore

Configure

Everything is tunable in %LOCALAPPDATA%\claude-color\config.toml (created on first run). Every option and its default:

Option Default What it does
backend "auto" How to talk to the keyboard: auto, qmk_hid, or hidapi.
qmk_hid_path "qmk_hid" Name on PATH (or a full path) of the qmk_hid binary.
vid / pid "" Hex device filter — only needed if several HID devices match. Find yours with claude-color doctor or qmk_hid -l.
solid_effect_index 1 Your keymap's VIA "solid color" effect index. If a solid status shows an animation, find the right index with claude-color probe.
breathing_effect_index 2 Your keymap's VIA breathing effect index. If a breathing status shows a rainbow, find the right index with claude-color probe.
inherit_brightness true true: status colors use the keyboard's own brightness; false: each state's V in [colors] (custom brightness per status). Inheriting needs a backend that can read current lighting — hidapi can; the qmk_hid binary can't, so it falls back to V.
staleness_minutes 120 A stuck session with no known process is forgotten after this many minutes. Sessions with a live Claude process are kept regardless of age, so a long turn is never pruned out from under you.
waiting_max_minutes 10 Amber gives up after this many minutes with no new event, and the session drops back to idle. Interrupting a prompt fires no hook, so without this the amber can outlive the question by hours. Raise it if you often leave a real prompt unanswered for longer; 0 disables it.
[colors].working [180, 255, 200] working color as H, S, V (0–255).
[colors].waiting [30, 255, 220] waiting color (amber).
[colors].idle [90, 255, 120] idle color (green).
[colors].error [0, 255, 220] error color (red).
[breathing].waiting 128 waiting breathes at this speed (0–255).
[breathing].error 160 error breathes at this speed (0–255).

[breathing] is per-state opt-in: a state listed there breathes at the given speed; remove the line to keep it solid, and an empty [breathing] means every state is solid. Only waiting and error breathe by default.

Not sure which effect indices your keymap uses? Run claude-color probe — it cycles the RGB-matrix effects so you can spot which is solid and which breathes, then set solid_effect_index / breathing_effect_index. Known values per board live in docs/COMPATIBILITY.md.

How it works

flowchart LR
    CC["Claude Code hooks"] -->|"SessionStart · UserPromptSubmit · Notification · Stop · StopFailure · SessionEnd"| K["claude-color hook"]
    K --> S["state.json<br/>(per session, by PID)"]
    S -->|"winner: error &gt; waiting &gt; working &gt; idle"| KB["keyboard RGB<br/>(VIA raw-HID)"]
    R["claude-color reconcile<br/>(logon / unlock / every 5 min)"] --> S

Claude Code fires hooks (SessionStart, UserPromptSubmit, Notification, Stop, StopFailure, SessionEnd). Each runs claude-color hook, which updates a per-session state file (%LOCALAPPDATA%\claude-color\state.json) and pushes the highest-priority color (error > waiting > working > idle) across all live sessions — StopFailure fires when a turn ends on an API/session error, setting that session to error. Sessions are tracked by their Claude process id; dead ones are pruned, and a scheduled claude-color reconcile (logon, unlock, and every five minutes) restores your lighting when no Claude is running. When the last session ends, the lighting captured at first SessionStart is restored.

The five-minute tick is also what bounds a stale status. Hooks are fire-and-exit, so a session that goes silent — you interrupt a turn, or dismiss a prompt, neither of which fires any Claude Code hook — has nothing to re-evaluate it. The tick gives waiting_max_minutes something to run on, so a wrong amber clears within about fifteen minutes instead of lasting until the next real event. The task is Windows-only and needs one elevated claude-color install to register; without it the hooks still work, but nothing ticks.

Troubleshooting

  • No color changes: run claude-color doctor. If available: False, check the USB cable (wireless is not supported) and try backend = "hidapi" or set vid/pid.
  • Animation instead of solid color: fix solid_effect_index.
  • Colors never restore: baseline capture may be unsupported on your firmware via the active backend; try backend = "hidapi". Logs are in %LOCALAPPDATA%\claude-color\claude-color.log.
  • Uninstall: claude-color uninstall.

License

MIT — © 2026 Edocksil.

Metadata

Release files for claude-color 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for claude-color 0.2.1
File Size Uploaded
claude_color-0.2.1.tar.gz 43.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for claude-color 0.2.1
File Interpreter ABI Platform
claude_color-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 71.5 kB

Release files / claude_color-0.2.1.tar.gz

Download URL claude_color-0.2.1.tar.gz
Size 43.2 kB
Tags Source
SHA-256 checksum
How to use checksums
20fc0e1b4742be3db91b25ed455e2c9fa309d557af9d9d6b3dc03c93690dff91
BLAKE2b-256 checksum
How to use checksums
f781fb32a5a82d5609aed4e14754b4dec262ac6a46768553664ea590d7e240c8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 15, 2026.

Transparency log

Release files / claude_color-0.2.1-py3-none-any.whl

Download URL claude_color-0.2.1-py3-none-any.whl
Size 28.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b47fb339c6469c689fd3dcdf84e66d14b1ab25aae9fd5053358abad39a091838
BLAKE2b-256 checksum
How to use checksums
6daaf87f314a2526d48ccc468dc9e5bc4546e73a121efda4d97cec4a8577572b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 15, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page