<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dingger.git/crates/dinggerd/src/platform.rs, 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-10-23T10:59:59+00:00</updated>
<entry>
<title>restore, step 3: offer it in the panel, menu, and status line</title>
<updated>2026-10-23T10:59:59+00:00</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-10-23T10:59:59+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dingger.git/commit/?id=9c9c9dad93b16781ec2fc40bd7582db62240171c'/>
<id>urn:sha1:9c9c9dad93b16781ec2fc40bd7582db62240171c</id>
<content type='text'>
when tmux stops (and its sessions are missing from the running server, or
no server runs), the daemon publishes the restore offer: offer.json for the
menu bar app and @dingger_restore for the status line, withdrawn once
restored or dismissed.

- panel: a first row "↺ Restore 9 sessions · 22 agents" with when it was
  saved and how many agents were mid-turn; clicking restores, "Not now"
  dismisses. an offer shows the panel like a waiting agent; off stays off.
- menu: the offer on top; restore all, one session, or not now.
- status line: a clickable blue ↺N before the clock.
- restore runs through the user's login shell, as a terminal starts tmux,
  so restored panes get a real environment, not launchd's; --report writes
  restore.log and notifies a summary. restore --dismiss.
- commands log only warnings by default; SIGPIPE ends them quietly.

e2e: the offer through the real menu bar binary, the summary notification,
the offer clearing, a restore with no tmux running, and not now staying
dismissed when a terminal starts tmux. the second test daemon is started
directly (it leaked through a function's $! before, and dozens of leaked
daemons fought over the test socket and loaded the machine); each run uses
its own socket and checks its daemons stop.
</content>
</entry>
<entry>
<title>restore, step 2: resume agents, retype command lines</title>
<updated>2026-10-23T10:20:11+00:00</updated>
<author>
<name>Igor Soarez</name>
<email>igor@soarez.org</email>
</author>
<published>2026-10-23T10:20:11+00:00</published>
<link rel='alternate' type='text/html' href='https://git.soarez.net/sz/dingger.git/commit/?id=c082f5044eedd2294d72f920d1018e40d73aab33'/>
<id>urn:sha1:c082f5044eedd2294d72f920d1018e40d73aab33</id>
<content type='text'>
- the pi extension reports its session file; the daemon keeps it in the
  pane option @agent_session, and snapshots carry it. restore runs
  `pi --session &lt;file&gt;` in the rebuilt pane, one agent every 0.5s, then
  reports which agents run and which were interrupted mid-turn.
- other panes: the command line running in them is typed at the new
  prompt but not run. exactly as typed with the zsh hook (dingger install
  zsh: preexec/precmd keep @dingger_cmd), else the foreground program's
  exact argv from the kernel (KERN_PROCARGS2), shell-quoted.
- "at a prompt" is the pane's own shell owning the terminal's foreground
  process group, not a name check: `sh -c ...` is a running program.
- an agent or command that exits keeps its place in snapshots for 60s, so
  a shutdown that stops programs before tmux still restores them.
- typing waits for each new prompt (text typed earlier echoes twice).

e2e step 16 covers resume with a session path containing a space, the
interrupted report, an agent that exits just before the server dies, a
quoted command retyped not run, and the hook's text; dropping the grace
period, judging prompts by name, running retyped commands, or not storing
the session reference each fail it. the test's fake pi is guarded so a
real pi never starts.
</content>
</entry>
</feed>
