Metadata-Version: 2.4
Name: claude-color
Version: 0.2.0
Summary: Drive any wired VIA-compatible RGB keyboard from Claude Code status
Author: Edocksil
License: MIT
Project-URL: Homepage, https://edocksil.github.io/claude-color/
Project-URL: Documentation, https://edocksil.github.io/claude-color/
Project-URL: Repository, https://github.com/Edocksil/claude-color
Project-URL: Issues, https://github.com/Edocksil/claude-color/issues
Project-URL: Changelog, https://github.com/Edocksil/claude-color/blob/main/CHANGELOG.md
Keywords: claude-code,qmk,via,hid,rgb,keyboard,hooks,status
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Environment :: Console
Classifier: Operating System :: Microsoft :: Windows
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: hidapi>=0.14
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Dynamic: license-file

# claude-color

[![PyPI](https://img.shields.io/pypi/v/claude-color.svg)](https://pypi.org/project/claude-color/)
![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)
![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)
![Platform: Windows](https://img.shields.io/badge/platform-Windows-blue.svg)

**[Live docs & demo → edocksil.github.io/claude-color](https://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](https://pypi.org/project/claude-color/)
  with `pip` (or [uv](https://docs.astral.sh/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](docs/COMPATIBILITY.md).
- Optional but recommended: the [`qmk_hid`](https://github.com/FrameworkComputer/qmk_hid)
  binary on your PATH (primary backend). Without it, the built-in Python `hidapi`
  backend is used.

## Install

From [PyPI](https://pypi.org/project/claude-color/):

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

Then set it up (run from any terminal):

```powershell
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`.

<details><summary><b>Install from source (contributors)</b></summary>

```powershell
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
```

</details>

## 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).

```powershell
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](docs/COMPATIBILITY.md).

## How it works

```mermaid
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](LICENSE) — © 2026 Edocksil.
