# Agent Guidelines — QMK VIA RGB Tool ## Objective Cross-platform Go CLI library for programmatic/agent-friendly control of VIA-compatible keyboard RGB lighting (starting with Impact 80). ## Architecture ``` cmd/ # Cobra-based CLI entrypoints internal/device/ # HID device discovery (VID/PID matching) internal/via/ # VIA protocol implementation + LED subsystem internal/rgb/ # Effects, values, color definitions keyboards.json # VID/PID database + keyboard metadata ``` ## CLI Interface ``` qmk-rgb keyboard info qmk-rgb rgb effect breathing qmk-rgb rgb effect rainbow_moving_chevron qmk-rgb rgb effect rainbow_moving_chevron --zone backlight qmk-rgb rgb brightness # 0-255 qmk-rgb rgb speed # 0-255 qmk-rgb rgb color # Six hexadecimal digits qmk-rgb rgb mode # Raw zone-specific effect ID qmk-rgb rgb enable qmk-rgb rgb disable qmk-rgb rgb info ``` `rgb` has a persistent `--zone` flag accepting `logo`, `backlight`, or `side`. Without `--zone`, commands target Logo, Backlight, and Side in that order. With `--zone`, commands target exactly one zone. Unsupported default targets are skipped with a stderr warning; unsupported explicit-zone effects and unknown names fail before the device is opened. ### Impact 80 Effect Families Logo and Side use IDs 0–6: `none`, `wave`, `fixed_wave`, `spectrum`, `breathing`, `light`, and `shutdown`. Backlight uses the complete ID 0–45 family: ```text none, solid_color, alphas_mods, gradient_up_down, gradient_left_right, breathing, band_sat, band_val, band_pinwheel_sat, band_pinwheel_val, band_spiral_sat, band_spiral_val, cycle_all, cycle_left_right, cycle_up_down, cycle_out_in, cycle_out_in_dual, rainbow_moving_chevron, cycle_pinwheel, cycle_spiral, dual_beacon, rainbow_beacon, rainbow_pinwheels, flower_blooming, raindrops, jellybean_raindrops, hue_breathing, hue_pendulum, hue_wave, pixel_flow, digital_rain, solid_reactive, solid_reactive_wide, solid_reactive_multiwide, solid_reactive_cross, solid_reactive_multicross, solid_reactive_nexus, solid_reactive_multinexus, splash, multisplash, solid_splash, solid_multisplash, starlight, starlight_dual_hue, starlight_dual_sat, riverflow ``` ID 39 is `multisplash`; ID 41 is `solid_multisplash`. Supported aliases are `off` → `none`, `breathe` → `breathing`, `rainbow` (zone-dependent), `rainbow_wave` (Logo/Side), `solid` (zone-dependent), and legacy `static`. Do not assume a numeric effect ID is valid on every zone; use the zone-aware name resolver or deliberately use `rgb mode` as a raw escape hatch. `rgb info` emits a `zones` array containing each selected zone's channel, enabled state, effect name and ID, brightness, speed, and color. The top-level summary comes from the first selected zone. A failed zone includes an `error` field while other zone results remain available; the command prints JSON before returning non-zero. ## VIA Protocol Essentials - Transport: QMK Raw HID over a 32-byte hidraw report, report number `0` - Message types: - `0x07` — Custom set value - `0x08` — Custom get value - Impact 80 lighting channels: `0x02` logo, `0x03` backlight, `0x04` side - RGB value IDs: brightness `0x01`, effect `0x02`, speed `0x03`, color `0x04` - No separate enable handshake is used ## Keyboards (Known VID/PID) | Keyboard | VID | PID | |------------------|--------|--------| | Wobkey Rainy 75 | 0x6666 | 0x0001 | | Wobkey Impact 80 | 0x36B0 | 0x309F | `keyboards.json` maps VID+PID → VIA protocol config + RGB layout metadata. ## Stack - Go 1.26+ (cross-platform: Linux, macOS, Windows) - `github.com/sstallion/go-hid` for HID access - `github.com/spf13/cobra` for CLI framework ## Constraints - CLI-first, no GUI — designed for automation and agent consumption - Output should be machine-parseable (JSON when possible) - Cross-platform from day one ## Code vs Documentation All documentation (README.md, comments, AGENTS.md) must stay in sync with the code. When code and documentation conflict, **ask the user** before deciding which one to change. Do not silently pick a winner — explicitly state the conflict and get direction. ## Go Development ### Tooling - `gopls` — Go Language Server, built into Go toolchain (LSP, diagnostics, go-to-def, rename, references) - `gofmt` / `goimports` — formatting. `goimports` adds/removes imports automatically. Always run before committing. - `go test` — built-in test framework. Tests live in `*_test.go` files alongside source. - `go vet` — static analysis. Run before committing. - `go build` — compiles without installing. Fast, cached. - `go mod tidy` — adds missing deps, removes unused ones. Run after every import change. ### Style (Effective Go + Code Review Comments) - **Formatting**: always `gofmt` or `goimports`. Tabs for indentation, no line-length limit but avoid uncomfortably long lines. - **Names**: `MixedCaps` / `mixedCaps`, no underscores. Short local variables (`i`, `c`, `r`). Descriptive for globals. - **Initialisms**: consistent case — `URL`, `ID`, `HTTP` → `appID`, `urlPony`, `ServeHTTP`. - **Comments**: doc comments start with the name, end with period. `// Package rgb provides RGB effect handling.` - **Imports**: standard library first, blank line, then third-party. Grouped. ### Error Handling - Return errors, never `panic` for normal control flow. - Indent error flow early — happy path at minimal indentation: ```go if err != nil { return err } // normal code ``` - Error strings: lowercase, no period — `fmt.Errorf("device not found")`. - Do not discard errors with `_`. ### Interfaces & Types - Define interfaces in the **consumer** package, not the producer. - Use pointer receivers when in doubt (mutation, large structs, sync fields). - Return concrete types from constructors — let consumers mock if needed. - Empty slices: `var t []string` (nil), not `t := []string{}` (non-nil), unless JSON encoding requires `[]`. ### Concurrency - Pass `context.Context` as **first parameter**. Never store in structs. - Clear goroutine lifetimes — document when/why they exit. Avoid leaks via channels. - Prefer synchronous functions — callers can add concurrency, not remove it. ### Testing - Table-driven tests preferred for multiple cases. - Failure messages: `t.Errorf("Effect(%q) = %d, want %d", input, got, want)`. - Use `*_test.go` files. Test package should match source package (internal tests). - Example functions (`func Example...`) double as docs and tests. ### Modules & Packages - Module path: `github.com//` (when published). - Package names: short, single-word, lowercase. No `util`, `common`, `api`, `types`. - Zero values should be useful (e.g. `bytes.Buffer`, `sync.Mutex`). ## graphify This project has a knowledge graph at graphify-out/ with god nodes, community structure, and cross-file relationships. When the user types `/graphify`, use the installed graphify skill or instructions before doing anything else. Rules: - For codebase questions, first run `graphify query ""` when graphify-out/graph.json exists. Use `graphify path "" ""` for relationships and `graphify explain ""` for focused concepts. These return a scoped subgraph, usually much smaller than GRAPH_REPORT.md or raw grep output. - Dirty graphify-out/ files are expected after hooks or incremental updates; dirty graph files are not a reason to skip graphify. Only skip graphify if the task is about stale or incorrect graph output, or the user explicitly says not to use it. - If graphify-out/wiki/index.md exists, use it for broad navigation instead of raw source browsing. - Read graphify-out/GRAPH_REPORT.md only for broad architecture review or when query/path/explain do not surface enough context. - After modifying code, run `graphify update .` to keep the graph current (AST-only, no API cost).