aboutsummaryrefslogtreecommitdiff
path: root/crates/dinggerd/src/hooks.rs
AgeCommit message (Collapse)Author
3 daysshow each pi agent's context use: a meter in the panel and search, text in ↵Igor Soarez
the menu
6 daysshow what pi agents cost today, this week and this month, counting forked ↵Igor Soarez
replies once
7 daysmark forgotten drafts in pi agents' input boxes, and keep them across a ↵Igor Soarez
crash or restore
7 daysreload pi agents on an old extension: versioned extension, control socket, ↵Igor Soarez
menu item and reload-agents
10 daysmark as unread: make an idle agent done again, to come back to itIgor Soarez
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).
11 dayshooks: answer each message, so the extension sends a report onceIgor Soarez
the pi extension (herdr's protocol) waits for a reply line and sends a report again after 0.5 s without one; dingger never replied. every report arrived twice and took about 2 s, delaying the next one (the live log: 196 of 400 reports repeated the previous one, 0.49 s later). the daemon now answers each line with {"ok":true}; senders that do not read replies (dingger poke, importance, the claude hook) are unaffected. e2e step 21: one report, done in 0.38 s; without replies, two, 2.08 s.
11 daysimportance: mark an agent high, normal, or lowIgor Soarez
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.
2026-10-23restore Claude Code agents in their own sessionsIgor Soarez
`dingger install claude` adds a SessionStart hook to Claude's settings (every other key and hook kept, in order; the original backed up; written through a symlink; installing again changes nothing). The hook is `dingger hook claude`: it reports the session's transcript for its tmux pane, prints nothing (Claude adds SessionStart output to the conversation) and always exits 0. Claude's state still comes from its screen. restore types `claude --resume <id>` in the directory the session ran in (elsewhere, Claude moves the conversation there). it resumes an agent only when its session file exists: a Claude session that never got a message has no transcript, and `--resume` fails ("No conversation found"). the snapshot now keeps an agent's command line too, so such a pane gets it retyped instead of nothing. - the hook receiver takes a report without a state: only the session. - a report that arrives after the watcher listed the panes is not dropped as "agent gone": Claude reports once, as it starts. - serde_json preserve_order (indexmap is already a dependency, via toml). e2e step 18: install against a settings file like a real one, a fake claude running the exact installed hook command, resume by id, retype without a transcript, the resumed agent reporting again. a server that restore starts now gets the test's plain shells: it got the user's login shell, whose profile put the real claude first, which started (stuck at its trust dialog) during development. every test restore now checks that no real pi or claude runs in a test pane.
2026-10-23restore, step 2: resume agents, retype command linesIgor Soarez
- 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 <file>` 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.
2026-10-23restore, step 1: layout snapshots and dingger restoreIgor Soarez
the daemon saves the tmux layout when it changes: sessions, windows (index, name, automatic-rename, layout, size, zoom, active), and panes (directory, active, agent). a new history file for layout changes, a rewrite for details, the last 50 kept, nothing saved while tmux is down. when a different server runs (or none), the old server's last snapshot is pinned as the restore candidate before the new server's snapshots start. dingger restore rebuilds missing sessions by tmux id: panes split from the newest so they keep their order, the saved layout applied at the saved size, then the size released so windows follow clients again. a bare session of the same name (a terminal's `new-session -As 0`) is replaced, its clients switched over first; used sessions are kept. --dry-run, --list, --from, --session. e2e step 16 kills a server with a client attached like ghostty's, restarts it, restores, and compares every window and pane; splitting the wrong pane, not replacing bare sessions, not pinning, or keeping the saved size each fail it. e2e keeps its state in its temp dir.
2026-10-20pi extension: identify the pane from TMUX_PANEIgor Soarez
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.
2026-10-20rebuild the status model as herdr's state machine, event-driven and testedIgor Soarez
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
2026-10-19menu bar: herdr-style colored state glyphs; tmux segment before the clockIgor Soarez
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.