all repos — dingger @ 7649396bf30a65096fc3944e1404835e661f00af

Watches the AI coding agents in your tmux panes on macOS and tells you which need you, from the menu bar, a floating panel, the status line and notifications; jumps to them, and restores tmux sessions and agents after a crash

7649396b
panel: on/off is persistent; what it shows is a separate setting

hiding the panel with the shortcut used to last only until another agent
started waiting: one action meant both "not now" and "off", with an
invisible override on top of a three-way mode. now there are two visible
settings: the panel is on or off (ctrl+opt+cmd+e and a checkmarked "Show
Panel" item), and while on it shows always or only when agents are waiting
("Panel Shows"). off stays off. turning it on with nothing to show peeks
for 3 seconds. the old mode setting migrates.

selftest covers the rules; it fails if a waiting agent can show a panel
that is off.
Igor Soarez igor@soarez.org
Thu, 22 Oct 2026 00:02:16 +0300
aa4d6fcd
fix clicks from the menu bar app: find tmux without homebrew on PATH

launchd starts the menu bar app with PATH=/usr/bin:/bin:/usr/sbin:/sbin,
so the `dingger focus` it runs for every click (panel rows, menu rows, the
jump shortcut) could not find homebrew's tmux and failed with a misleading
"no tmux client attached"; the app discarded the error. dingger now finds
tmux on PATH or in the usual install locations (DINGGER_TMUX overrides),
reports a missing tmux as such, and the app logs failed jumps.

also: panel rows own every click inside them (labels took the clicks and
counted as window-drag background).

e2e step 14 repeats the menu bar row click and dingger focus under
launchd's environment; both fail with the previous binaries.
Igor Soarez igor@soarez.org
Tue, 20 Oct 2026 15:01:18 +0300
eed9c9c1
panel: readable everywhere, compact/full toggle, change-driven; jump shortcut

- contrast: the summary used attributed-string colors (never vibrant) and
  the subtitles a vibrant system color, so they blended differently over
  light and dark desktops. the panel now uses herdr's mocha colors
  explicitly over a dark tint, identical on any background.
- layout: compact in "when agents are waiting"; in "always" a show all /
  show less toggle switches to the full list (waiting, separator, rest).
- rows are single-line with a trailing ellipsis instead of wrapping.
- render only when the content changes, and log each render, instead of
  rebuilding every view every second.
- --snapshot-panel renders the real panel offscreen over white and black
  after a different first render (stale content would show).
- jump shortcut ctrl+opt+cmd+u (jump_hotkey) runs dingger focus; shortcuts
  are registered by id. an unedited first config file is upgraded.

selftest covers the layout rules and content; e2e step 13 checks the full
list, its separator, and the toggle labels.
Igor Soarez igor@soarez.org
Tue, 20 Oct 2026 14:51:07 +0300
0f843854
floating panel: always-on-top agent HUD with a global shortcut

a non-activating HUD panel (every Space, over full-screen apps, never takes
focus) listing the agents waiting for you, with working and idle summarized
on one line; rows jump to the agent, drag moves the panel and it remembers
its place. modes: when agents are waiting (default), always, off; ctrl+opt+
cmd+e shows or hides it on top of the mode, and a manual hide lasts until
another agent starts waiting. the shortcut is a carbon hotkey (no
accessibility permission; clashes are reported in the menu) configured in
~/.config/dingger/config.toml, reloaded on save. the menu bar app is split
into model, panel, hotkey and main.

tested: --selftest checks the visibility rules (it fails if a newly waiting
agent no longer re-shows a dismissed panel); e2e step 13 checks the panel
content through the real binary.
Igor Soarez igor@soarez.org
Tue, 20 Oct 2026 14:24:11 +0300
6bccc5f7
menu rows lead with the agent's session name; harness and place move below

herdr's sidebar shows the session name in place of the harness label (via
the user's herdr-session-name extension). dingger reads it from the title
pi already sets ("π - <name> - <dir>"); other agents use herdr's
terminal_title_stripped rule, and claude's default "Claude Code" title or
tmux's host-name title count as no name. the daemon stores it as
@agent_name for every surface.

menu rows: colored status symbol + name as the title (the harness, dimmed,
only when unnamed), and a native subtitle "session · window · harness",
plus how long done/blocked agents have been waiting; one separator between
agents waiting for you and the rest. dingger status shows the same.
Igor Soarez igor@soarez.org
Tue, 20 Oct 2026 13:59:29 +0300
3731a803
pi extension: identify the pane from TMUX_PANE

the extension asked tmux for "the current pane", which resolves to the
session's active pane, not the pane pi runs in, so agents in background
windows reported their state onto another pane. tmux sets TMUX_PANE for
every process it starts in a pane; use it.

e2e step 12 loads the shipped extension with a fake pi from a pane that
is not its session's active pane and asserts the report lands there; it
fails against the previous extension. hook reports log at info again.
Igor Soarez igor@soarez.org
Tue, 20 Oct 2026 13:04:18 +0300
4af6a4f6
rebuild the status model as herdr's state machine, event-driven and tested

the seen logic was inverted: first observations were marked done and real
completions idle, so the menu showed teal for agents that did nothing and
green for agents that finished, and attending changed nothing visible.

- watcher::advance is herdr's apply_pane_state_change + mark_active_tab_seen
  as a pure function, unit-tested against herdr's own cases; attendance is
  window-level like herdr's active tab
- hook reports are events applied in order, so a turn shorter than a poll is
  still a completion
- event-driven: hook reports, dingger focus, and tmux focus hooks wake the
  watcher; notifications are delivered off-thread (the macos call blocked
  ticks for 2+ seconds) after the pane options are written
- all per-pane state persists in tmux pane options, so restarts and deploys
  lose nothing, including the authoritative hook state
- every click goes through dingger focus: the menu bar shells out to it and
  the status-line binding runs it; it picks the right client, skips the
  window already shown, and activates the terminal app hosting the client
- the ready queue is ordered like the menu (blocked first, then oldest done)
- menu bar and tmux segment render herdr's symbols in herdr's palette
- tests/e2e.sh: 60 assertions against an isolated tmux server with a real
  client, a 30s poll interval, and a slow fake notifier
Igor Soarez igor@soarez.org
Tue, 20 Oct 2026 12:43:07 +0300
943fc940
write @agent_state on attendance changes, not just transitions

the seen demotion (done -> idle when the user focuses the pane) only
updated the daemon's internal track; @agent_state stayed done forever,
so the menu bar and status line never reflected attendance. the display
status is now written whenever it differs from the pane option,
regardless of what triggered it.
Igor Soarez igor@soarez.org
Tue, 20 Oct 2026 12:06:03 +0300
3509e48c
adopt herdr's five-status model with symbols style and focus detection

statuses are now herdr's verbatim: working (yellow), blocked (red),
done (teal, finished and unattended), idle (green, attended since its
last transition), unknown (gray) - priority blocked > done > working >
idle > unknown. the daemon watches tmux focus (pane_active of attached
clients) to mark panes attended, which is what separates done from idle.
menu bar renders symbols-style glyphs with herdr's colors in priority
order; the tmux segment stays after the clock and renders done/blocked/
working counts from numeric options. deployment note: do not strip the
binary - it invalidates the ad-hoc signature and macOS kills it on
launch.
Igor Soarez igor@soarez.org
Tue, 20 Oct 2026 11:53:23 +0300
fed066bc
menu bar: herdr-style colored state glyphs; tmux segment before the clock

the design panel's monochrome template rendering made every row look
alike; restore herdr's agent-panel visual language - green ready, yellow
working, red blocked, filled when unseen in the queue. the tmux segment
moves left of the clock so it is visible at a glance; the status item
logs what it observes for diagnosis.
Igor Soarez igor@soarez.org
Mon, 19 Oct 2026 19:30:03 +0300
7081a103
menu rows show window names, not indexes

display target is session:window-name (pane index only when split);
switching keeps using the pane id. add --print debug mode to inspect the
rendered rows without opening the menu.
Igor Soarez igor@soarez.org
Mon, 19 Oct 2026 19:14:17 +0300
ea1dda2f
ready-queue fifo with click-to-focus; redesign presentation per design panel

daemon tracks when each agent became ready (idle/blocked, unacked) and
publishes fifo queue + numeric counts as server-global options; unknown
to idle transitions stay out of the queue (restart spam). new dingger
focus subcommand jumps to the longest-waiting pane and acks it, bound to
status-right clicks in tmux. menu bar redesigned per two-model design
panel: monochrome sf-symbol bell + queue depth, queue-ordered rows with
state glyphs and right-aligned targets, row clicks ack like focus does.
tmux segment renders glyph+count with a single red accent for blocked;
fixes unescaped comma in the conditional format that truncated the
segment entirely.
Igor Soarez igor@soarez.org
Mon, 19 Oct 2026 16:55:30 +0300
9c6b1042
menu bar app: live agent states from tmux pane options

single-file appkit status item (menubar/main.swift, LSUIElement) polling
@agent/@agent_state pane options every 2s: counts in the bar, per-pane
rows with click-to-focus via switch-client. daemon now also writes an
@agent label per pane so the menu bar and tmux status-right can render
agent names without duplicating identification logic.
Igor Soarez igor@soarez.org
Mon, 19 Oct 2026 16:35:40 +0300
3bace4ac
wait for tmux at startup, force utf-8 for tmux clients, add launchd packaging

running under launchd, the tmux client defaults to the c locale, which
makes tmux mangle tab-separated -f output into underscores, so every
pane parsed as one field and the watcher silently saw nothing; invoke
tmux with -u so format output is locale-independent. drop the startup
fail-fast: the watcher now waits for a tmux server and attaches when one
appears, which is the right shape for a login agent. ship the launchd
plist under packaging/launchd.
Igor Soarez igor@soarez.org
Mon, 19 Oct 2026 16:19:10 +0300
ab33633c
initial dingger: tmux agent watcher with herdr's detection engine

extract detect-core from herdr's src/detect (engine + 22 manifests,
tests unchanged) and add the dingger binary: tmux capture-pane watcher,
process-tree agent identification, herdr's notification policy, embedded
notification sounds, @agent_state write-back, and catalog-based manifest
refresh. viewer screens are non-observations per the engine contract;
reviewed by a three-model panel (opus 5.5, gpt 6.1 sol, kimi k3) with
all confirmed findings applied as deletions.
Igor Soarez igor@soarez.org
Mon, 19 Oct 2026 16:02:57 +0300