1.58.0 (2026-10-12)
This release opens Neru up to other tools. A config can now react to what Neru does through [hooks], and a status bar or script can follow it with neru watch, ask it what it sees with neru query, and talk to it over a versioned IPC protocol or the new Go client. Config files get an editor schema and warnings for unknown keys. On macOS, a leak that grew memory by about 4 GB a day is fixed, and Mission Control and Stage Manager get hints again.
Migration Guide
Warning
Every valid config still loads, but eight behaviours change.
A disabled mode now fails instead of reporting success.
With [grid] enabled = false, neru grid used to exit 0 and do nothing. It now exits 1 with ERR_MODE_DISABLED. A neru run step or hotkey array that names a disabled mode is reported as a failed step. This covers hints, grid, recursive_grid, bisect and monitor_select.
neru services stop keeps the service stopped, across logins, until neru services start.
On macOS, launchd used to restart Neru seconds after a stop. restart on a stopped service now exits non-zero and asks you to run start. On Linux and Windows, start, stop and restart refuse a unit or task that Neru did not write, such as one from nix or home-manager.
neru hints --debug is removed.
Use neru query hints, which lists every element instead of a sample of ten. The hints-probe IPC action is replaced by query-hints.
--on-exit with --repeat is refused.
The steps never ran, since a repeating mode only closes on cancel. A binding that writes both now enters no mode, and neru config validate warns about it. Drop --on-exit from the binding.
neru config dump uses the key names from config.toml.
recursiveGrid is now recursive_grid, hotkeys.bindings is now a flat hotkeys table, and so on. The same applies to the IPC config reply.
exec cmd & no longer blocks the sequence.
A step ends when the shell exits, even if it left a process running. To wait for a command, run it without &.
neru config set no longer splits a list of steps at commas.
Pass a TOML array instead, such as '["exec echo a,b", "idle"]'.
toggle-screen-share fails with ERR_NOT_SUPPORTED on Linux and Windows.
It used to report the overlay as hidden while nothing hid it. Nothing changes on macOS.
Note
hints.on_mission_control_activated and hints.on_mission_control_deactivated are deprecated and removed in v2. Move each value to the same key under [hooks].
Hooks
[hooks]runs steps when something happens in Neru. Each key takes a step or an array of steps, like a binding. Hooks run apart from key handling, so a slow hook never delays a mode, and a hook never retriggers itself from its own steps
| Key | Runs when | exec steps also see
|
|---|---|---|
on_mode_enter, on_mode_exit
| A mode opens or closes | NERU_MODE, and on exit NERU_REASON and NERU_ACTION
|
on_select
| A step sets the target, such as a hint label, a grid cell or a recursive grid level | NERU_MODE, NERU_X, NERU_Y, NERU_ACTION
|
on_app_focus
| Another app comes to the front | NERU_BUNDLE_ID
|
on_enable, on_disable
| Neru resumes or pauses | |
on_config_reload
| A reload finishes | NERU_OK
|
on_ready, on_quit
| The daemon starts or shuts down. on_quit runs exec steps only
| |
on_scroll_invert, on_screen_share_hide
| The toggle switches. Screen share is macOS only | NERU_ON
|
on_cursor_save, on_cursor_restore
| A cursor slot is saved or restored | NERU_SLOT, and on save NERU_X, NERU_Y
|
on_sticky_modifiers
| A sticky modifier is armed or released | NERU_MODIFIERS
|
on_monitor_move, on_screen_change
| Neru moves to another display, or the displays change | NERU_MONITOR on move
|
on_mission_control_activated, on_mission_control_deactivated
| Mission Control opens or closes (macOS) |
Every exec step also sees NERU_EVENT.
[hooks]
on_mode_enter = "exec sketchybar --trigger neru_mode MODE=\"$NERU_MODE\""
on_mode_exit = "exec sketchybar --trigger neru_mode MODE=idle"neru watch
neru watchstreams Neru's status and events as JSON lines. The first line is a snapshot ofneru status --json, and each event after it carries the same names and values hooks see. A slow reader is told how many events it missed, and the daemon never waits on it--eventstreams only the events you name, such asneru watch --event mode_enter,mode_exitneru status --jsongainssticky_modifiers
{"seq":0,"event":"snapshot","status":{"enabled":true,"mode":"idle"}}
{"seq":1,"event":"mode_enter","mode":"hints"}
{"seq":2,"event":"select","mode":"hints","action":"left_click","x":1721,"y":439}neru query
neru queryreports what the daemon sees right now.displays,cursor,window,appandhints, each with--json. Positions use the coordinatesneru action move_mousetakes, andquery appprints the exactbundle_idper-app config matchesquery hintsreports each element's hint label, the oneneru hintswith the same flags would drawneru hints --targetslabels targets another tool supplies, for canvas apps, games or remote desktops that accessibility and vision miss. It takes the JSONquery hints --jsonprints, or-for stdin
neru query hints --json | jq '.hints |= map(select(.width > 20))' \
| neru hints --targets - --action left_clickModes
move_mouseworks as a mode--action. It leaves the cursor on the selection and presses nothing, sohints --action move_mouse --on-exit scrollpicks a hint and opens scroll mode there--on-exitsteps see the selection.execsteps getNERU_X,NERU_Y,NERU_MODEandNERU_ACTION
Config
- A JSON Schema for
config.tomlgives editors completion and error checking. New configs fromneru config initstart with a#:schemaline. To use it in an existing config, add the line from the configuration reference at the top neru config validatewarns about keys Neru does not read, such as a misspelledhints.enabeld. The file still loads
IPC and integrations
- The IPC protocol is versioned. Every reply carries
"protocol": 1, and the number rises only on a breaking change at a major release. The IPC reference now lists every command - A JSON Schema covers IPC replies and watch lines, so a client can generate its types
github.com/y3owk1n/neru/clientis an importable Go client with typed replies for status, the queries and watch--jsononneru doctor,neru rolesandneru services status
Fixes
execsteps no longer hold a background command's output in memory. A long-runningexec cmd &used to grow the daemon without limit, and a hook stuck on one never fired againconfig set,config resetandconfig dumphonour--timeout- Man pages show the installed version instead of
Neru dev
macOS
- Memory no longer grows while Neru runs. Drawing and capture calls leaked on every use, worst while the cursor was hidden, at about 50 KB a second
- Mission Control is detected again. Its hooks run, and with
include_dock_hintson, hints cover the current desktop's windows, the Dock and the expanded Spaces bar - The Stage Manager strip gets hints again with
include_stage_manager_hintson - Quitting with
monitor_selectopen no longer crashes the daemon
Linux
Note
Linux implementations are mainly contributor driven, feel free to help improving it!
- The X11 overlay no longer piles up unread redraw events on every hint, grid or indicator show
- Fixed leaks after a Wayland reconnect, monitor unplug or failed buffer allocation, finished processes left behind by the tray, and keys seen twice after a keyboard replug when the overlay is the input path
Features
- add [hooks] for mode, focus, pause and reload events (#1746) (c8d576e)
- add hooks and watch events for toggles, cursor slots, displays and lifecycle (#1753) (1f449f6)
- cli: add --json to doctor, roles and services status (#1767) (27d4811)
- cli: add
neru watchto stream status and events (#1750) (3f3a6f5) - cli: add neru query for displays, cursor, window, app and hints (#1759) (de39c65)
- config: implement json schema for toml config (#1763) (b02041b)
- config: print config dump under the key names config.toml uses (#1766) (26010dd)
- config: warn about keys that neru doesnt know (#1764) (923c4ed)
- hints: allow external tools supply the targets hints mode labels (#1770) (8514f5c)
- hints: report each element's hint label in query hints (#1769) (e59dee4)
- hooks: add the select event and on_select hook (#1760) (5f52713)
- ipc: add an importable go client package (#1768) (97a79a7)
- ipc: add protocol version on ipc (#1761) (5a9468d)
- ipc: allow filtered watch stream (#1771) (ff4e848)
- ipc: publish a json schema for ipc replies and watch lines (#1765) (76526d9)
- modes: accept
move_mouseas mode--action(#1748) (e2d67d9) - modes: pass the selection to --on-exit steps via vars (#1762) (521355a)
Bug Fixes
- build: stamp the man pages with the release version (#1757) (94ffda3)
- cli: make
neru services stopkeep the service stopped (#1755) (592240e) - darwin: detect mission control and it's hints properly (#1745) (6adbb9d)
- darwin: hint Stage Manager's strip (#1747) (b93858d)
- darwin: more autoreleasepool that are missing (#1776) (35d94ad)
- darwin: stop the daemon crashing on quit with monitor_select open (#1754) (baa0ab8)
- ensure config set, health, toggle-screen-share and the config timeouts behave consistently (#1773) (b793e7a)
- ipc: refuse a disabled mode (#1737) (8db242b)
- linux: stop native leaks in the overlay, keymap and accessibility paths (#1778) (fca5ec1)
- modes: refuse --on-exit with --repeat (#1758) (53d7f68)
- modes: report a mode switch that fails to open as a canceled exit (#1780) (7652a39)
- stop exec steps from waiting on and holding output of background commands (#1777) (74c7458)