signalbox.nvim is an attention-first Neovim control surface for persistent coding agents managed by Herdr.
It is named after a railway signal box: it does not drive the trains or replace their engines. It gives one operator a compact view of traffic, highlights the routes that need intervention, and provides safe controls at the junctions.
[!NOTE] This is an unofficial community integration. It is not maintained or endorsed by the Herdr project.
When I delegate work to several coding agents while editing, help me notice only the work that needs human attention, inspect it without touring terminal panes, intervene, and return after Neovim restarts.
That job shapes the product:
blocked and done come before terminal topology.blocked/done agents elsewhere by defaultINPUT → RESULT guide for every board keySnacks.lazygit and Diffview actions from the selected agent's cwd:checkhealth signalbox, including detection-manifest freshnessInstall Herdr's richer agent integrations once:
herdr integration install codex
herdr integration install claude
With lazy.nvim or LazyVim:
{
"nwiizo/signalbox.nvim",
event = "VeryLazy",
cmd = {
"Signalbox",
"SignalboxRefresh",
"SignalboxUpdateAll",
"SignalboxStart",
"SignalboxResume",
"SignalboxAttach",
"SignalboxPrompt",
"SignalboxRename",
"SignalboxSendVisual",
"SignalboxSendFile",
"SignalboxSendDiagnostics",
"SignalboxHealth",
},
keys = {
{ "<C-g>", "<cmd>Signalbox<cr>", mode = { "n", "t" }, desc = "Agent Signalbox" },
},
opts = {},
}
<C-g> is only a recommendation. Signalbox defines no global mappings itself.
event = "VeryLazy" starts background monitoring before the board is first opened; commands and keys can still load it earlier. With the default auto_start_server = true, it may also start the detached Herdr server on Neovim launch. Omit the event if you want Signalbox and Herdr to start only on demand and do not need ambient notifications.
Open the board with :Signalbox.
╭──────── Signalbox !1 ✓1 ────────╮ ╭──────── reviewer · read-only ────────────╮
│ this project + attention elsewhere │ │ ... │
│ A: 2 working elsewhere │ │ The agent is waiting for approval to │
│ signalbox.nvim │ │ update the public API. │
│ ! reviewer claude blocked │ │ │
│ * implementation codex working │ │ │
│ ✓ tests codex done │ │ │
╰────────────────────────────────────╯ ╰──────────────────────────────────────────╯
Board mappings are buffer-local:
| Key | Action |
|---|---|
<CR> / i |
Leave the read-only preview and attach to the selected agent's interactive terminal |
p / s |
Prompt the selected agent; retry a saved initial-instruction draft when one exists |
e |
Toggle recent output / Herdr state-detection explanation |
n |
Give the selected agent a stable role name |
a |
Assign an unused project-based name, enter the first instruction, start, and attach |
R |
Resume a saved Claude Code or Codex conversation inside a new Herdr-managed agent |
g |
Open Snacks Lazygit at the selected agent's repository |
d |
Open Diffview at the selected agent's repository |
v |
Toggle recent-output preview |
A |
Toggle default attention view / all agents |
r |
Refresh immediately |
q |
Close |
? |
Replace the right pane with an input-to-result operation guide |
<Tab> |
Move between the agent list and the right pane |
The default view keeps agents from the current Git root and also surfaces blocked or completed work elsewhere. When active work is filtered out, the second line shows A: N working elsewhere; press A to reveal the whole Herdr session.
Press ? for an INPUT → RESULT guide that explains the text or key to enter and the resulting action, including cross-Space attach and returning from an attached terminal. If the recent-output preview is hidden, the guide opens the right pane temporarily; pressing ? again restores the previous preview setting.
Codex or Claude Code may show an update, trust, hook, login, or other setup screen before it can accept an instruction. Signalbox asks Herdr to confirm that the initial prompt made the agent start working. If Herdr reports agent_prompt_stalled, Signalbox warns immediately and keeps the instruction in memory. Clear the provider screen, return with <C-g>, select the agent, and press p; the original instruction is already filled in, so Enter retries it. The draft is discarded once work begins and is never written to disk.
The right pane is deliberately a read-only, ephemeral view of recent output. Its title says read-only; press i or <CR> from either pane to replace the board with an interactive Herdr terminal in insert mode.
When Neovim itself runs inside Herdr and the selected agent belongs to another Space, attach first moves the Herdr pane hosting Neovim into that Space and follows it. Herdr keeps the Neovim process running during the move. Neovim instances outside Herdr attach without moving a pane.
Use <C-g> (Ctrl-g) as the single Signalbox key. In a normal editor buffer it opens the board and starts Herdr on demand; in a Signalbox-attached terminal its buffer-local mapping takes priority, detaches the Neovim client, leaves the Herdr agent running, and returns directly to the board. A single control chord has no mapping timeout in which Codex or Claude Code can consume a partial sequence. The terminal key is configurable with terminal.return_key.
| Command | Description |
|---|---|
:Signalbox |
Toggle the attention board |
:SignalboxRefresh |
Refresh Herdr state immediately |
:SignalboxUpdateAll |
Live-handoff an outdated server, update configured integrations, and update manifests |
:SignalboxStart [codex|claude] |
Assign an unused project-based name, enter the first instruction, start, and attach |
:SignalboxResume [codex|claude] |
Open the provider's resume picker inside Herdr and attach |
:SignalboxAttach[!] [target] |
Attach; ! explicitly takes over another direct client |
:SignalboxPrompt [target] |
Prompt an agent |
:SignalboxRename [target] |
Give an agent a stable role name |
:[range]SignalboxPrompt [target] |
Prompt with complete lines from a range |
:'<,'>SignalboxSendVisual [target] |
Prompt with the exact visual selection |
:SignalboxSendFile [target] |
Send the saved current-file reference |
:SignalboxSendDiagnostics [target] |
Send current-buffer LSP diagnostics |
:SignalboxHealth |
Run :checkhealth signalbox |
Targets accept stable terminal IDs, current pane IDs, or an unambiguous agent name.
Use R on the board or :SignalboxResume codex / :SignalboxResume claude when a conversation that started outside Herdr should become persistent and observable. Exit the original Codex or Claude Code client first. Signalbox never kills it or attempts to move its live process.
After confirmation, Signalbox creates a Herdr-managed pane, opens the provider's native resume picker, and attaches immediately. Select the exact saved conversation there; Signalbox deliberately does not choose the latest session because multiple conversations may share one repository. Current Herdr integrations then report the native session identity so Herdr can restore that conversation after a server restart.
:checkhealth signalbox reads Herdr's active detection-manifest status without changing it. A missing check time or one older than seven days is a warning with the explicit herdr server update-agent-manifests recovery command; local overrides are reported separately because they intentionally shadow remote rules.
:SignalboxUpdateAll is the explicit maintenance path. It preserves panes by live-handoffing only when the running server protocol is incompatible, installs outdated or missing Codex/Claude integrations that are present in agents, then updates Herdr's detection manifests. It does not install or upgrade the Herdr binary because Signalbox does not own its package manager.
statusline() returns only states that need attention:
require("signalbox").statusline()
-- "SB !1 ✓1" or "" when there is nothing to handle
It can be used from Incline, lualine, or another renderer after Signalbox is loaded. ~ marks last-known data after a refresh failure.
Signalbox observes Herdr's semantic agent state and sends a Neovim notification once when an agent changes to blocked or done. It does not forward Herdr's own toast. The initial snapshot stays quiet, while statusline() and the board still expose existing attention.
Blocked notifications are suppressed while the board is open or the matching live attached terminal is focused because the approval state is already visible. Completion notifications are always delivered through the current vim.notify provider, including while an attention surface is visible. Closing a surface alone does not replay a suppressed blocked event; if the agent leaves the attention state and later returns, the new transition can notify normally. An already delivered notification stays deduplicated for the same Herdr revision. Herdr's [ui.toast] delivery is configured independently; enabling both a Herdr desktop/terminal toast and Signalbox notifications intentionally produces two notification surfaces.
require("signalbox").setup({
herdr_cmd = "herdr",
auto_start_server = true,
agent_start_timeout_ms = 30000,
agents = {
codex = { args = {} },
claude = { args = {} },
},
refresh = {
board_ms = 1000,
background_ms = 5000,
timeout_ms = 3000,
},
board = {
width = 0.9,
height = 0.9,
preview = true,
preview_ratio = 0.58,
preview_lines = 80,
},
terminal = {
side = "right",
width = 0.4,
auto_insert = true,
return_key = "<C-g>",
},
})
Agent args are appended after Herdr's canonical agent command without using a shell:
agents = {
codex = { args = { "--profile", "work" } },
}
:SignalboxResume.The repository checks the canonical herdrdev/herdr latest stable release daily. If it differs from the currently verified Herdr version (currently 0.8.0), GitHub Actions opens a deduplicated compatibility-audit issue with CLI, schema, and real-agent smoke-test checkpoints. See the upstream audit runbook.
See the design notes for the job model and ownership boundary.
make check
Tests run in a clean headless Neovim and do not require a test framework plugin.
Friend License (MIT-equivalent).