<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dingger.git/menubar, branch master</title>
<subtitle>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
</subtitle>
<id>https://git.soarez.net/sz/dingger.git/atom?h=master</id>
<link rel='self' href='https://git.soarez.net/sz/dingger.git/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dingger.git/'/>
<updated>2026-11-03T11:05:57+00:00</updated>
<entry>
<title>show each pi agent's context use: a meter in the panel and search, text in the menu</title>
<updated>2026-11-03T11:05:57+00:00</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-11-03T11:05:57+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dingger.git/commit/?id=bd1eb9ef9b3b89ff5825eb8bf9b2e978167c9ad0'/>
<id>urn:sha1:bd1eb9ef9b3b89ff5825eb8bf9b2e978167c9ad0</id>
<content type='text'>
</content>
</entry>
<entry>
<title>show what pi agents cost today, this week and this month, counting forked replies once</title>
<updated>2026-11-01T08:22:01+00:00</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-11-01T08:22:01+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dingger.git/commit/?id=8146d1db419e8bdacd75920da6f3761abf724853'/>
<id>urn:sha1:8146d1db419e8bdacd75920da6f3761abf724853</id>
<content type='text'>
</content>
</entry>
<entry>
<title>mark forgotten drafts in pi agents' input boxes, and keep them across a crash or restore</title>
<updated>2026-10-30T16:59:43+00:00</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-10-30T16:59:43+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dingger.git/commit/?id=959f0d46cac1a09d0821da599d8dff54d1147892'/>
<id>urn:sha1:959f0d46cac1a09d0821da599d8dff54d1147892</id>
<content type='text'>
</content>
</entry>
<entry>
<title>reload pi agents on an old extension: versioned extension, control socket, menu item and reload-agents</title>
<updated>2026-10-30T15:27:21+00:00</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-10-30T15:27:21+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dingger.git/commit/?id=b0cfd10b80443d3f67d263ff4db177bb53cc90ed'/>
<id>urn:sha1:b0cfd10b80443d3f67d263ff4db177bb53cc90ed</id>
<content type='text'>
</content>
</entry>
<entry>
<title>menu, panel, search: a peach ! in a column of its own; low dimmed more</title>
<updated>2026-10-27T13:27:14+00:00</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-10-27T13:27:14+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dingger.git/commit/?id=9eddef09c769b315f4eabef6eaf4bb58e70d0966'/>
<id>urn:sha1:9eddef09c769b315f4eabef6eaf4bb58e70d0966</id>
<content type='text'>
the ! was the color of the name, read as part of it, and pushed high
names two characters out of line. it is now peach (herdr's palette, no
status uses it) in a fixed-width column every row has, so names line up.
a low agent's name is dimmed to 0.5 (0.7 was hard to see).
</content>
</entry>
<entry>
<title>mark as unread: make an idle agent done again, to come back to it</title>
<updated>2026-10-27T12:12:28+00:00</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-10-27T12:12:28+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dingger.git/commit/?id=50c433e82c368b9861a89deecb74e5f29fa50353'/>
<id>urn:sha1:50c433e82c368b9861a89deecb74e5f29fa50353</id>
<content type='text'>
like "mark as unread" in mail: an agent you have seen (idle) becomes done
again, last among done agents of its importance, with no sound or
notification. it stays done until you come back to it: leave its tmux
window and return, or jump to it. marked while you look at it, it holds
until you look away (`held`); a new turn clears it. only idle agents can
be marked.

- daemon: `pane.mark_unread` on the hook socket; the watcher applies it
  after the tick's observations (mark_unread, advance's hold rule).
- `dingger unread [--pane]`: refuses done, working, blocked, and panes
  without an agent; returns once the agent shows as done.
- pi: /unread (refused while the agent works or is blocked).
- panel: right-click an idle agent in the full list, "Mark as unread".
- search: ⌘U or right-click marks the selected idle agent; the footer
  offers ⌘U when it applies; search stays open.

tests: unit (hold, jump, new turn, idle only), e2e step 22 (queue
position, hold, look away and back, jump, refusals, no sound, menu, the
extension), pi-live in real pi 1.4 (a mark, not a turn: an unknown
command would also end done), swift selftest (menu only for idle, footer).
</content>
</entry>
<entry>
<title>panel: a click that moves a point or two is still a click</title>
<updated>2026-10-27T10:15:03+00:00</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-10-27T10:15:03+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dingger.git/commit/?id=a2378a3ed12e62ea34ad9475084ecb5a387386a5'/>
<id>urn:sha1:a2378a3ed12e62ea34ad9475084ecb5a387386a5</id>
<content type='text'>
any pointer movement between press and release started a panel drag and
swallowed the click, so clicking a row often did nothing and had to be
repeated: in a logged session, 12 of 17 clicks were taken as drags. the
5 that counted waited at most 4 ms and ran `dingger focus` in 60-120 ms.
a drag now needs more than 4 points of movement (isDrag, selftest).
</content>
</entry>
<entry>
<title>importance: mark an agent high, normal, or low</title>
<updated>2026-10-26T14:32:18+00:00</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-10-26T14:32:18+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dingger.git/commit/?id=185af91b4d525db6c192a050b53615772fb5fe11'/>
<id>urn:sha1:185af91b4d525db6c192a050b53615772fb5fe11</id>
<content type='text'>
within a status, more important agents are listed first, and so jumped to
first; status stays first. a high agent shows a bold ! before its name, a
low agent's name is lightly dimmed with "low" on its second line, and it
makes no sound (it still notifies and waits in line).

in pi, /importance high|normal|low (completes the levels; none once one is
typed out, so Enter runs it). the extension saves it in the session as a
custom entry (dingger-importance), so /reload, restarts, /resume and
restore bring it back; /new starts at normal. it shows a level other than
normal in pi's footer, emits dingger:importance { level } for other
extensions, and sends it with every report. for other agents, `dingger
importance [level] [--pane]`; it refuses a pi that reports itself, whose
saved level would win.

the daemon stays the one writer of pane state: it writes
@dingger_importance (unset for normal), which goes with the agent and
survives a daemon restart. `dingger status` marks high and low.

tests: e2e step 21 (order, the option, no sound for low, the menu bar,
the command, the extension through a harness that now matches pi's
extension API), a DINGGER_SOUND seam so sounds are observable, and a
status-line check that read the counts once (flaky) now polls.
pi-live: /importance in real pi 1.4, its footer and session entry, and the
level after a restore; every pi there now uses the test's agent directory
(a restored pi read the user's). negative controls fail each part.
</content>
</entry>
<entry>
<title>session groups: list each pane once, save each group once</title>
<updated>2026-10-23T15:45:06+00:00</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-10-23T15:45:06+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dingger.git/commit/?id=b4f45632debe6b8dc2f44ca6cc1b003d9093ad33'/>
<id>urn:sha1:b4f45632debe6b8dc2f44ca6cc1b003d9093ad33</id>
<content type='text'>
a pane in several sessions (a session group, `new-session -t`, or a
linked window) was listed once per session: every agent of a group showed
twice in the menu, panel, search, `dingger status`, and the counts. and
snapshots saved each session of a group with its own copy of the windows,
so restore would have rebuilt them twice and resumed each agent twice on
one session file.

- a pane keeps its first listing (tmux lists sessions by name) and is
  attended if the user looks at it through any session of the group.
- snapshots keep the first session of each group; the others are not
  restored.

e2e step 19 covers both; the previous binaries fail it. two checks (step
11's and this one) waited for idle after a turn that started from idle,
which passed before the turn was even processed: they now wait for
working first, and the negative control for group attendance fails as it
should. step 18 now waits for the snapshot it later restores from.
</content>
</entry>
<entry>
<title>list working and idle agents most recent first</title>
<updated>2026-10-23T14:58:24+00:00</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-10-23T14:58:24+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dingger.git/commit/?id=12ce9f3f8edc84064458b649e8615247641dad36'/>
<id>urn:sha1:12ce9f3f8edc84064458b649e8615247641dad36</id>
<content type='text'>
within a status, the menu, panel, search, and `dingger status` listed done
and blocked agents oldest first (the queue), and the rest by place. now
working and idle agents are most recent first: the first idle row is the
agent that finished, or that the user looked at, last.

@dingger_since now means when the pane entered its current status, for
every status (it was set only while the pane needed the user), and
survives a daemon restart. the queue selects done and blocked panes by
status, no longer by having a stamp. one ordering rule each in Rust
(watcher::list_order) and Swift (listedBefore), with tests that fail on
the old order; e2e step 11 makes an agent finish while watched and checks
it moves to the top of the idle rows.

agents idle since before this change share one stamp and keep their place
order until they next change.
</content>
</entry>
</feed>
