Show local AI agent status on a NuPhy keyboard's side lights.
简体中文 · Download · Maige on X
NuphyBar is a lightweight native macOS menu-bar app. It receives lifecycle events from Codex, Claude Code, Antigravity, OpenCode, and other local agents, sends a standard Bluetooth keyboard LED output report when the state changes, and lets custom keyboard firmware render the animation locally.
It never reads keystrokes and does not stream animation frames over Bluetooth. The Mac sends one two-byte report per state change; the keyboard renders every animation frame itself.
Important
The firmware in the current Release is only for the NuPhy Air60 V2 ANSI. Never flash the Air60 V2 binary to an Air75 V2, Air96 V2, Halo, Gem80, or any other model. A firmware image for the wrong model can make the keyboard unusable.
| Agent state | Air60 V2 right side light |
|---|---|
| Idle | No override; stock rainbow/battery effect returns |
| Working | A continuous blue brightness wave moves across all five LEDs |
| Waiting/error | An amber double pulse on all five LEDs |
| Complete | A green breathing effect on all five LEDs |
The stock cyan Caps Lock indicator remains on the left side. Agent state only uses the right side light.
| Model | Transport | Status | Light area |
|---|---|---|---|
| Air60 V2 ANSI | Bluetooth Low Energy | Supported | Five right-side RGB LEDs |
Bluetooth typing, the left Caps Lock indicator, every Agent state, reconnection, and sustained typing stability have been tested on real hardware.
| Model | Expected effort | Why |
|---|---|---|
| Air75 V2 | Low to medium | Same Air V2 QMK family and the same left/right side-light roles |
| Air96 V2 | Low to medium | Same Air V2 QMK family; NuPhy's own firmware already maps Num Lock to the right side light |
These models can reuse NuphyBar's HID state protocol and effect model, but their LED indices, function addresses, firmware baseline, and memory layout must be verified independently. The Air60 V2 .bin cannot be reused.
| Model | Available lighting | Notes |
|---|---|---|
| Halo65 V2 QMK | Halolight / nameplate | Needs a ring or segmented effect rather than a five-step bar |
| Halo75 V2 QMK | Halolight / nameplate | Requires model-specific firmware and effects |
| Halo96 V2 QMK | Halolight / right light | NuPhy's firmware already contains a right-side Num Lock indication path |
| Gem80 tri-mode | RGB light bar / nameplate | Only the Bluetooth-capable tri-mode variant is in scope |
- Air V1, Halo V1, Field75, and other models on the older NuPhy firmware line;
- Air60 HE, Air75 HE, Field75 HE, and other HE/IO models;
- Air V3, Halo V2 IO, Kick75 IO, BH65, and other NuPhy IO products;
- the wired-only Gem80, because the current app implements BLE HID output only.
NuPhy IO and QMK are different firmware stacks. Having a light bar is not sufficient for this patch. See NuPhy's firmware catalog and QMK firmware releases.
NuphyBar uses an event-driven path. The Mac sends the current state; the keyboard generates the animation locally.
flowchart TB
A["① Agent hook records a lifecycle event"]
B["② Atomic state file + macOS notification"]
C["③ NuphyBar combines active sessions"]
D["④ Persistent HID session sends 2 bytes"]
E["⑤ Keyboard firmware renders every frame"]
A --> B --> C --> D
D -->|Bluetooth LE| E
| Component | Responsibility | What it does not do |
|---|---|---|
| Agent hook | Atomically update local state and post a system notification | Control the keyboard directly |
| NuphyBar | Combine concurrent sessions and send one report when the displayed state changes | Poll every second or stream animation frames |
| Keyboard firmware | Turn a state into a wave, double pulse, or breathing animation | Read Agent content |
In other words, Bluetooth carries “working,” not a continuous sequence such as “light LED 1, then LED 2.”
The local state file is the durable source of truth; the macOS notification is only the wake-up signal. NuphyBar reads the file at launch and whenever a lifecycle event arrives, then creates one timer for the next state expiration. Normal operation has no Agent-state polling. If system notification registration fails, a five-second fallback poll keeps the app functional.
Earlier releases repeatedly scanned for the keyboard and opened it for each check or command. NuphyBar 0.5.9 instead keeps one non-exclusive HID manager active and listens for macOS device connection/removal callbacks.
flowchart LR
A["HID ready"]
B["Discard stale session"]
C["Retry after 1 · 2 · 5 · 10 · 30 s"]
D["Replay latest Agent state"]
A -->|report failure or Mac wake| B --> C --> D --> A
- A failed output report invalidates the old HID session instead of immediately hammering the same stale device handle.
- Mac wake proactively rebuilds the HID session; the user does not need to power-cycle the keyboard.
- When delivery becomes ready again, NuphyBar replays the current combined Agent state.
- If another Agent event arrives while a report is being sent, it is coalesced into one immediate follow-up refresh.
- Complete and error indications expire after about 15 seconds using an exact deadline, then idle lighting is restored.
NuphyBar does not add a private BLE GATT service. It reuses the keyboard's existing standard LED output report:
| HID bit | Value | NuphyBar meaning |
|---|---|---|
| Num Lock | 0x01 |
Working |
| Caps Lock | 0x02 |
Reserved for the stock left Caps indicator |
| Scroll Lock | 0x04 |
Waiting/error |
| Num + Scroll | 0x05 |
Complete |
| No Num/Scroll | 0x00 |
Idle; restore the stock effect |
The complete report is only two bytes: [Report ID 1, state mask]. Caps Lock is added independently, so the stock left indicator remains functional.
Only the Num and Scroll bits are available, so the safe protocol supports three non-idle states. Error and waiting intentionally share the amber attention effect. A distinct fourth state would require a new wireless protocol rather than another value in this two-bit channel.
Early experiments streamed animation frames over Bluetooth. Real hardware eventually froze the light strip and stopped typing. The release design no longer does that:
- NuphyBar sends one HID report only when the final display state changes;
- the keyboard retains NuPhy's stock wireless polling interval;
- every wave, pulse, and breathing frame is generated by a local keyboard timer.
Bluetooth therefore receives only an occasional state value, not an animation frame rate. Typing and animation stay on separate paths.
stable-v7 is not a rebuild of an older public QMK tree. It applies an audited minimal hook to NuPhy's official Air60 V2 v2.1.5 firmware:
- official baseline SHA-256:
cd0425f548a01416d1c3c25208ff74867fffd20165520c7c2eaa56000ff347bf - NuphyBar firmware SHA-256:
c573c7939a53994b50f29313744f27f9af30b90cd064f13fc019f87710b89ac0 - only four official bytes at
0x080028EA–0x080028EDare changed; - those bytes redirect the original
sys_led_show()call to a hook at0x08010E00; - the hook calls the original
sys_led_show()first, preserving Caps Lock; - USB and idle states return without overriding stock behavior;
- the added effect code is 332 bytes and uses no
.dataor.bss; - UART, RF polling, key reports, sleep, pairing, and USB input are untouched;
- the builder verifies machine-code signatures at critical official functions and refuses the wrong baseline;
- the verifier proves that only the call site and appended hook differ from the official image.
The source, reproducible builder, and tests are in firmware/air60-v2.
Note
While an Agent is active, its state temporarily replaces the right-side battery indication. Stock battery/rainbow lighting returns at idle. Do not rely on the right side light as your only battery check during a long task.
Requirements:
- macOS 14 or later;
- an Apple Silicon Mac;
- a NuPhy keyboard with compatible custom firmware;
- a Bluetooth Low Energy keyboard connection, not USB.
Steps:
- Download
NuphyBar-0.5.9-macOS-arm64.dmgfrom Releases. - Open the DMG and drag NuphyBar to Applications.
- On first launch, if macOS blocks the app, Control-click it and choose Open, or approve it in System Settings → Privacy & Security.
- Grant Input Monitoring when prompted. This allows HID output to the keyboard; NuphyBar does not read or store keystrokes.
- Reopen NuphyBar and confirm the exact keyboard model and Bluetooth connection on the Keyboard tab.
- Enable the desired integrations on the Agent tab, then start a new task in that agent.
The Release DMG is ad-hoc signed and is not yet notarized with an Apple Developer ID. Its full source, build script, and checksums are public.
| Agent | Integration | Main lifecycle events |
|---|---|---|
| Codex | ~/.codex/hooks.json |
prompt submit, permission, tool completion, stop |
| Claude Code | ~/.claude/settings.json |
prompt, permission, input notification, tool completion, session end |
| Antigravity | ~/.gemini/config/plugins/nuphybar |
model invocation, fully-idle completion, execution error |
| OpenCode | global local plugin | busy, idle, error, permission |
| Grok Build | personal hooks file | prompt, tool, failure, permission, stop |
| Hermes | local lifecycle plugin | LLM call, approval, session completion |
| OpenClaw | managed local hook | message received, result sent, stop |
The installer changes only entries that it owns or marks. It refuses to overwrite an unmarked user file with the same name. Codex may still ask you to trust each newly installed hook.
Display priority is:
error/waiting > working > complete > idle
One completed session never hides another session that is still working. Completion is retained for about 15 seconds, and stale active sessions are pruned automatically.
Read firmware/air60-v2/README.md first. In short:
- Verify that the keyboard is a NuPhy Air60 V2 ANSI.
- Export the current VIA keymap.
- Download
NuphyBar-Air60-V2-stable-v7.binand verify its SHA-256. - Keep NuPhy's official Air60 V2 v2.1.5 recovery firmware ready.
- Connect USB and enter STM32 DFU. The NuPhy/QMK source documents holding the top-left Esc key while plugging in; you can also follow NuPhy's update instructions.
- Select the correct
.binin QMK Toolbox and flash it. Never unplug or power off during the write. - Restart, switch back to Bluetooth, verify typing and Caps Lock first, then test NuphyBar states.
Advanced users may use the following only after confirming that the detected STM32 DFU device is the intended keyboard:
dfu-util -a 0 -s 0x08000000:leave -D NuphyBar-Air60-V2-stable-v7.binFlashing is a destructive checkpoint. Never let a script infer the model, and never begin without a recovery image.
Requires macOS 14+ and the Swift 6.1 toolchain.
git clone https://github.com/itsmaiGe/NuphyBar.git
cd NuphyBar
swift test
./script/package_release.shThe DMG is written to dist/NuphyBar-0.5.9-macOS-arm64.dmg.
To build, install, and run locally:
./script/build_and_run.shInstall the build tools:
brew install arm-none-eabi-gcc@8 arm-none-eabi-binutils dfu-utilDownload NuPhy's official Air60 V2 ANSI v2.1.5 firmware and run:
./firmware/air60-v2/build.sh \
/path/to/QMK_firmware_nuphy_air60_v2_ansi_v2.1.5.binThe build runs effect and Thumb branch tests, validates the official baseline, compiles the hook, adds the DFU suffix, and verifies the final layout. GCC 8.5.0 reproduces the stable-v7 Release binary byte for byte.
Ready-to-use local coding-agent prompts and mandatory safety checkpoints are provided here:
AI may inspect source, implement effects, run tests, and compile. Entering DFU, confirming the exact model, and authorizing the final flash must remain a separate human confirmation step.
- no keystroke reading, logging, or uploading;
- hooks send only the provider, coarse state, and a local session identifier;
- the state file stays local and contains no prompts or responses;
- no cloud service, analytics SDK, or background network API;
- config edits preserve unrelated user settings and refuse unmarked conflicts.
See SECURITY.md. Never attach sensitive local configuration to a public issue.
Sources/
AgentLightApp/ macOS menu app and settings UI
AgentLightCore/ state aggregation, hook mapping, integration installer
AgentLightHID/ NuPhy BLE HID discovery and output reports
AgentLightCLI/ short-lived helper bundled inside the app
firmware/air60-v2/ stable-v7 hook, builder, verifier, and tests
Design/ source artwork for the app and menu-bar logos
script/ app build, local install, and DMG packaging
Tests/ Swift tests
- macOS app, ordinary project scripts, and documentation:
MIT - QMK/NuPhy-derived code and patches under
firmware/:GPL-2.0-or-later - third-party Agent icons and trademarks belong to their owners and are used only for identification; see
THIRD_PARTY_NOTICES.md.
NuphyBar is a community project and is not affiliated with or endorsed by NuPhy, OpenAI, Anthropic, or any other agent vendor.