Skip to main content

Drive any wired VIA-compatible RGB keyboard from Claude Code status

Project description

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 boot/logon/unlock reconcile 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 is forgotten after this many minutes.
[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)"] --> 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) restores your lighting when no Claude is running. When the last session ends, the lighting captured at first SessionStart is restored.

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

claude_color-0.2.0.tar.gz (35.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

claude_color-0.2.0-py3-none-any.whl (24.4 kB view details)

Uploaded Python 3

File details

Details for the file claude_color-0.2.0.tar.gz.

File metadata

  • Download URL: claude_color-0.2.0.tar.gz
  • Upload date:
  • Size: 35.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for claude_color-0.2.0.tar.gz
Algorithm Hash digest
SHA256 abcccb96b75d84e3d4b8697fef3aa837e00025c2484447b74101c9de935da3c4
MD5 59cecf604e981a9c9d84d2cff3a57a43
BLAKE2b-256 07d51190a5cc57aeca334f214ce743b2ff32786c95ec5beb33166085461a0fbc

See more details on using hashes here.

Provenance

The following attestation bundles were made for claude_color-0.2.0.tar.gz:

Publisher: release.yml on Edocksil/claude-color

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file claude_color-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: claude_color-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 24.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for claude_color-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 82844d1776a95694e9b0094cf2f4e33ee8797d3be8d3c3838b8fc64c49a42490
MD5 5abe1349ebc75323b8b059b528f9d898
BLAKE2b-256 2741f4d53b6b9e18cd0fe89b116fa762a1c1842a820cd107cca3d781e21fea4a

See more details on using hashes here.

Provenance

The following attestation bundles were made for claude_color-0.2.0-py3-none-any.whl:

Publisher: release.yml on Edocksil/claude-color

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page