AGENTS.md 8.7 KB

Agent Guidelines — QMK 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-tool keyboard info
qmk-rgb-tool effect breathing
qmk-rgb-tool effect rainbow_moving_chevron
qmk-rgb-tool effect rainbow_moving_chevron --zone backlight
qmk-rgb-tool brightness <val>       # 0-255
qmk-rgb-tool speed <val>            # 0-255
qmk-rgb-tool color <hex>            # Six hexadecimal digits
qmk-rgb-tool mode <index>           # Raw zone-specific effect ID
qmk-rgb-tool enable
qmk-rgb-tool disable
qmk-rgb-tool info
qmk-rgb-tool save <name>        # Save current RGB state to a profile
qmk-rgb-tool load <name>        # Load a profile and apply it to the keyboard
qmk-rgb-tool delete <name>      # Delete a saved profile
qmk-rgb-tool list               # List saved profiles

--zone is persistent across commands and accepts 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:

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

Consistency

Names used in code must appear identically everywhere. Before any edit:

  1. Binary name — the go build -o target (usually $GOBIN/$module_name) must match every example in README, AGENTS.md, comments, and docs.
  2. CLI command — the root command name (e.g. qmk-rgb-tool) is the binary name; every usage example, CLI reference table, and doc must use the same string.
  3. Module path — the go.mod module path is the source of truth for go install targets.
  4. Zone names — logo, backlight, side must be identical in code, docs, and examples.

When you see a name in one file, grep for it across the whole repo before deciding if a change is consistent.

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/<user>/<project> (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 "<question>" when graphify-out/graph.json exists. Use graphify path "<A>" "<B>" for relationships and graphify explain "<concept>" 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).