diff options
| author | Igor Soarez <igor@soarez.org> | 2026-11-10 17:30:26 +0200 |
|---|---|---|
| committer | Igor Soarez <igor@soarez.org> | 2026-11-10 17:30:26 +0200 |
| commit | 4cad1009c9d07cdfdab28fe9e073ccb35b54dfd7 (patch) | |
| tree | f742ceac80ae61d1e893cfd57e080e84410e28cc /README.md | |
| parent | 87001d7d380173c3d76246d4c0ecdd7b037a619f (diff) | |
readme: lead with what dingger shows, with real renders of made-up agents; move the details to docs/reference.md; dingger status: fit the agent column to its longest nameHEADmaster
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 524 |
1 files changed, 197 insertions, 327 deletions
@@ -1,342 +1,212 @@ -# dingger - -macOS agent watcher for tmux panes: herdr's agent panel, without herdr. -dingger watches every pane in your tmux server, detects which AI coding agent -runs in each (claude, codex, pi, gemini, and 19 others), tracks herdr's five -statuses, and tells you which agents are waiting for you: in the menu bar, in -the tmux status line, and with a notification and a sound. - -dingger owns no terminals. It is a client of your tmux server. The detection -engine and rule manifests are extracted from -[herdr](https://github.com/herdrdev/herdr) (Apache-2.0). - -## Statuses (herdr's, verbatim) - -| status | meaning | symbol | color | priority | -|---------|--------------------------------------------------|--------|--------|----------| -| blocked | needs your input | `×` | red | 4 | -| done | finished a turn while you were looking elsewhere | `✓` | teal | 3 | -| working | turn in flight | `◐` | yellow | 2 | -| idle | at its prompt, nothing new since you looked | `○` | green | 1 | - -The state machine is herdr's `apply_pane_state_change` and -`mark_active_tab_seen`, implemented as one pure function (`watcher::advance`) -and unit-tested against herdr's own test cases: - -- a turn that finishes while you look at another window becomes **done**; -- a turn that finishes in the window you are looking at stays **idle**; -- looking at a window (a tmux window is herdr's tab) turns its done agents - **idle**, as does jumping to one from the menu bar or status line; -- an agent seen for the first time is **idle**, not done. - -dingger watches tmux focus through tmux hooks, so attendance updates the -moment you switch windows; agent reports apply in order, so a turn shorter -than a poll is still a completion. - -## Surfaces - -- **Menu bar** (`menubar/main.swift`): `×1 ✓3 ◐2` in herdr's colors; the - menu lists every agent in priority order, oldest waiting first. Clicking a - row jumps to that pane. -- **Floating panel** (`menubar/panel.swift`): an always-on-top HUD that never - takes focus, on every Space and over full-screen apps. It lists the agents - waiting for you (blocked, done) and summarizes the rest - (`◐ 2 working · ○ 15 idle`); click a row to jump there, drag to move it, - right-click an idle agent in the full list to mark it unread. - **⌃⌥⌘E** (or "Show Panel" in the menu) turns it on or off; off stays off - until you turn it back on. While on, "Panel Shows" picks *Only When Agents - Are Waiting* (default: it hides itself while nothing waits; turning it on - then shows it for 3 seconds so you see it is armed; compact) or *Always* - (with a "Show all / Show less" toggle for the full list). It grows down - to the bottom of the screen at most; a longer list scrolls, with the - summary line kept under it. The panel is always dark, in herdr's - palette, so it reads the same over any desktop. -- **Jump shortcut**: **⌃⌥⌘U** jumps to the top waiting agent from anywhere, - like clicking the tmux status line. -- **Agent search**: **⌃⌥⇧⌘U** opens a Spotlight-style search over every - agent. Type to fuzzy-filter (each word must match, letters in order, the - name, session, window, harness, status, or directory; or, as written, - a forgotten draft); ↑↓ or ⌃N ⌃P / ⌃J ⌃K - select, ↩ or a click jumps, esc closes, ⌃U clears, ⌘U (or a right-click) - marks the selected idle agent unread. With nothing typed, - agents are in the menu's order, so ↩ jumps to the top waiting agent. It - takes the keyboard without activating dingger, so focus returns where it - was. -- **Restore**: dingger saves your tmux layout as it changes (sessions, - windows, panes, layouts, directories) under - `~/.local/state/dingger/snapshots/`. After a crash, shutdown, or - `kill-server`, `dingger restore` brings back what is missing (`--dry-run` - to preview, `--list` for saved snapshots, `--session` for some). Sessions - that exist are left alone, except a bare one of the same name (what a - terminal creates on startup), which is replaced with its clients moved - over. Agents are resumed in their own sessions: pi with `pi --session - <file>` (reported by the pi extension), Claude Code with `claude --resume - <id>` (reported by its SessionStart hook, `dingger install claude`), in - the directory they ran in. A session that never got a message has nothing - to resume, so its command line is typed instead. Agents that were - mid-turn are listed. Other panes get the command line that was running typed at the - prompt, not run: exactly as typed with the zsh hook (`dingger install - zsh`, then source the file it names from your `.zshrc`), otherwise the - program's arguments. An agent or program that exits within a minute - before tmux stops (a shutdown) is still restored. - When tmux stops, the panel (first row: "↺ Restore 9 sessions · 22 - agents", or "Not now"), the menu (all, or one session at a time), and the - status line (`↺9`, clickable) offer the restore; it runs through your - login shell, like a terminal starting tmux, and a notification - summarizes (full report: `~/.local/state/dingger/restore.log`). -- All shortcuts live in `~/.config/dingger/config.toml` (menu bar, - Settings…), reloaded on save. -- **Jumping to an agent** (any click, a shortcut, search) shows it in a - terminal window and raises that window. With several terminal windows, - one already on the agent's session wins (it shows the agent without - switching), then the focused one, then the one used last. In Ghostty - (1.3+), the exact window, tab, and split is raised: dingger learns which - terminal holds each tmux client when it gains focus (tmux's focus hook - asks Ghostty which terminal is focused; nothing visible changes). - Other terminals: the app is activated. -- **tmux status line** (`packaging/tmux/dingger.tmux`): the same summary after - the clock. Clicking it jumps to the top waiting agent (the menu's top row), - skipping the window you already look at. -- **Notifications**: "needs attention" (blocked) and "finished" (done), with - herdr's sounds; silent for completions you are already watching. A pi - run that ends in an error (after pi's own retries) is still done, but - says so: "failed" with the error as one readable line (`401: invalid - x-api-key`), with the "needs attention" sound, and `failed 3m · <why>` - in red where the wait shows (plain text in the menu; `dingger status` - too), until you have seen it or it runs again. -- **Importance**: mark an agent high, normal (the default), or low. Within a - status, more important agents are listed first, so a jump goes to them - first; status still comes first (a blocked low agent before a done high - one). A high agent shows a peach `!` before its name (in a column of its - own, so names line up); a low agent's name is dimmed, its second line - says "low", and it makes no sound (it - still notifies, and waits in line like any other). In pi, type - `/importance high|normal|low` (no argument: the current level); pi saves - it with the session, so it comes back after `/reload`, a restart, or a - restore. For other agents, - run `dingger importance high|normal|low` in the agent's pane (in Claude - Code: `! dingger importance high`, which Claude may answer with a model - turn), or pass `--pane`; it lasts until the agent exits. -- **Mark as unread**: like in mail, an idle agent (one you have seen) can be - made done again, to come back to later: right-click it in the panel's - full list, ⌘U in agent search, `/unread` in pi, or `dingger unread - [--pane]`. It goes last among done agents of its importance, with no - sound or notification, and stays done until you come back to it: you - leave its tmux window and return, or jump to it. Only idle agents can be - marked: a working, blocked, or done one is left alone. Claude Code has no - `/unread`, and `! dingger unread` does not work in it: Claude (2.1.311) - answers a `!` command with a model turn, so the agent is working and the - mark is refused. Use the panel or search for Claude agents. -- **What agents cost** (pi): the menu's top line totals what pi agents - spent today, this week, and this month (`$8.86 today · $457 this week · - $8.86 this month`); each agent's line adds what its session spent today - (at the right edge in the panel and search, after the place in the - menu), and `dingger status` shows both. Periods start at - 00:00 UTC (a week on Monday, a month on its 1st). The daemon reads pi's - session files (`~/.pi/agent/sessions`, and those of running agents - elsewhere) in a thread of its own every 15 seconds, only what was - appended since. Each reply counts once, for the session it first appeared - in: a fork (`/fork`, `pi --fork`) copies its parent's replies into its - own file, and summing every file would count them again (in one month - here, $1,024 of $2,991). The totals include agents since closed or - started over. Amounts are what pi prices each call at (its model - configuration), counted as pi's footer counts them: replies, compaction - and branch summaries, and other recorded calls. Claude Code records no - prices, so it is not counted. -- **Context use** (pi): how full each agent's context window is, the - number pi's own footer shows. The panel and search draw it as a small - meter at the right edge, after the cost (gray; yellow above 70% and red - above 90%, as in pi; the number on hover); the menu adds it as text - (`$0.70 · 10%`), and `dingger status` has a CTX column. The extension - reports it after each message, compaction, model change, and turn; right - after compacting pi does not know it until the next reply, and neither - does dingger (no meter). Agents on an older extension show none until - they reload. -- **Session names** (pi): every pi session gets a short name of what it is - about, from the model, after its first run settles; it is refreshed - every 8 of your messages, and an old unnamed session is named when you - resume it. A name you give (`/name`, `pi --name`, `/autoname <name>`) is - never replaced; `/autoname` alone renames now. It runs in every pi - session (print runs and outside tmux too) and costs one short model call - per naming; `PI_AUTONAME_MODEL=provider/model-id` picks a cheaper model. - It replaces the `pi-auto-session-name` package (remove it: `pi remove - git:github.com/Optomatica/pi-auto-session-name`), and keeps the names - that package gave automatic. -- **Forgotten drafts** (pi): an agent with text left in its input box shows - `✎` before its name wherever you are not looking at it (where you look, - you may be typing it). Idle agents with one come first among idle agents; - the compact panel counts them (`✎ 2 drafts`, a click jumps to the - first); search finds an agent by its draft's words (as written, not - fuzzily); `dingger status` marks them too. No sound, no notification. - The extension reads the input box half a second after you stop typing, - and keeps each session's draft in a file - (`~/.local/state/dingger/drafts/`) that puts it back in the input box - when the session comes back: after a crash, a restore, or `pi --session`. - The draft is kept on the pane as one line (`@dingger_draft`), never in - dingger's log. - -## Where state comes from - -1. **Agent hooks** (authoritative): `dingger install pi` installs a pi - extension that reports working/blocked/idle over - `~/.local/state/dingger/agent.sock`. Running pi agents load it when - reloaded (see "Keeping pi agents on the current extension" below). - It also reports the session's name (`/name`, or pi's own), which is - the agent's name in dingger whatever the terminal title says: any - program in the pane can set a title. Agents without it (Claude Code, a - pi on an older extension) are named from their terminal title. - Blocked covers a dialog that opens during a run (an extension asking - before it goes on), until you answer it; one you open while the agent - is idle changes nothing. A run you cancel (Escape) ends idle, not done, - with no "finished" notification, even when it settles after you look - away. - `dingger install claude` adds a SessionStart hook to - `~/.claude/settings.json` (keeping everything else; the original is - backed up to `settings.json.bak-dingger`) that reports only Claude's - session, for restore; its state comes from screen matching. -2. **Screen matching** (fallback): `capture-pane` text through herdr's - manifests, for agents without hooks. - -All per-pane state lives in tmux pane options (`@agent_state`, -`@dingger_since`, `@dingger_hook`, `@dingger_importance`, -`@dingger_extension`, `@dingger_control`, `@dingger_draft`, -`@dingger_cost`, `@dingger_context`, `@dingger_error`), so restarting -dingger loses nothing. - -### Keeping pi agents on the current extension - -When the daemon starts, it updates the installed pi extension if this -dingger's differs (only its own file, which says so on its first line). -Running agents keep the one they loaded, and report its version. While -some run an old one, the menu bar menu offers **Reload N Agents on an Old -Extension**; `dingger reload-agents` does the same. Each agent reloads -(pi's `/reload`) when: - -- it is idle (done included: it stays done), -- it is not on screen, where you may be typing into it, -- and its input box is empty: the extension checks this itself, at the - moment it reloads, so nothing you type is lost or sent. - -The others reload once they are; the request stays until all are, or the -daemon restarts. dingger sends no keystrokes: it asks the extension over a -socket of its own (`~/.local/state/dingger/pi-<pid>-<id>.sock`), and the -extension runs the reload as a command (`/dingger-reload`, which you can -also type). A reload also resets pi's other extensions: a pending alarm -is lost, MCP servers reconnect. - -An agent on an extension from before this (it reports no version) takes -no requests: the menu says how many need `/reload` by hand, once. - -The pi extension's reports carry the agent's importance. Other pi -extensions can follow it on pi's event bus: `dingger:importance` with -`{ level }`, at session start and on every change; it is saved in the -session as a custom entry of type `dingger-importance`. +<h1 align="center">dingger</h1> + +<p align="center"> + <b>Know which of your AI coding agents is waiting for you.</b><br> + herdr's agent panel for the tmux you already run, on macOS. +</p> + + + +You run a dozen coding agents in tmux panes. One finished ten minutes ago, +one is stuck on a permission prompt, one hit a rate limit, and you are +reading a diff somewhere else. dingger watches every pane in your tmux +server, works out which agent runs where and what it is doing, and tells you +who is waiting for you. Then it takes you there in one keystroke. + +- **Every agent at a glance.** It detects 22 agents: Claude Code, Codex, pi, + Gemini, opencode, Cursor, GitHub Copilot, Amp, and 14 more. +- **Waiting agents come to you.** A notification and a sound when an agent + finishes or needs your input, and silence when you are already watching it. +- **One key to the next agent.** <kbd>⌃⌥⌘U</kbd> jumps to the next agent + waiting for you, and raises the exact Ghostty window, tab, and split. +- **Find any agent.** <kbd>⌃⌥⇧⌘U</kbd> opens a Spotlight-style fuzzy search. +- **Survives a restart.** dingger saves your tmux layout as you work, and + after a reboot or crash brings back every session, window, and pane, with + each agent resuming its own conversation. +- **Nothing to migrate.** No new terminal, no new multiplexer: dingger is a + client of your tmux server, and keeps its state in tmux options. + +## Statuses + +dingger tracks [herdr](https://github.com/herdrdev/herdr)'s statuses, with +herdr's state machine: + +| | status | meaning | +|---|---|---| +| `×` | **blocked** | needs your input: a permission prompt, a question, a dialog | +| `✓` | **done** | finished a turn while you were looking elsewhere | +| `◐` | **working** | a turn is in flight | +| `○` | **idle** | at its prompt, nothing new since you looked | + +A turn that finishes in the tmux window you are looking at stays idle. +Looking at a window marks its agents seen. Waiting agents are listed blocked +first, then done, each oldest first. The top one is the next waiting agent: +where a jump goes. + +## The floating panel + + + +An always-on-top panel that never takes focus, on every Space and over +full-screen apps. Click a row to jump to that agent, drag the panel anywhere. +It can show itself only while agents are waiting, or always, with the full +list a click away. <kbd>⌃⌥⌘E</kbd> turns it on and off. + +Each row says where the agent is (session, window, harness) and for how long +it has waited. pi agents also show what they spent today and how full their +context window is. + +## Agent search + + + +<kbd>⌃⌥⇧⌘U</kbd> from anywhere. Type a few letters of an agent's name, +session, window, harness, status, or directory, or a word of a draft you left +in its input box. <kbd>↩</kbd> jumps there. With nothing typed, +<kbd>↩</kbd> goes to the next waiting agent. Focus goes back +where it was when you close it. + +## The tmux status line + + + +The same counts after the clock, in your terminal theme's own colors. Click +them to jump to the next waiting agent. + +The menu bar shows the counts too. Its menu lists every agent and what pi +agents spent today, this week, and this month. + +## Restore after a restart + +dingger saves your tmux layout as it changes: sessions, windows, panes, +layouts, directories, and what runs in each pane. When tmux stops (a +restart, a crash, `kill-server`), the panel, the menu, and the status line +offer to bring it all back. Agents resume their own sessions: pi with +`pi --session`, Claude Code with `claude --resume`. Other programs get their +command line typed at the prompt but not run, so nothing starts behind +your back. + +## More for pi agents + +With dingger's pi extension (`dingger install pi`): + +- **Exact states.** The extension reports state directly, including dialogs + during a run, cancelled runs, and failed ones: "failed 3m · 429: rate limit + exceeded". +- **Session names.** Every session gets a short name of what it is about, + from the model, kept current as the work moves on. A name you give it with + `/name` is never replaced. +- **What agents cost.** Today, this week, and this month, counted the way + pi's footer counts, with forked sessions counted once. +- **Context use.** A meter per agent that turns yellow above 70% and red + above 90%. +- **Forgotten drafts.** `✎` marks an agent with text left in its input box, + and restore puts the draft back. +- **Importance.** `/importance high` puts an agent ahead of the others with + its status and marks it `!`. A low agent is quiet and dimmed. +- **Mark as unread.** `/unread` turns an agent done again, to come back to. +- **Kept current.** When dingger updates its extension, the menu offers to + reload the agents on the old one. Each reloads only when it is idle, out of + sight, and has an empty input box. + +Claude Code and the other agents get the rest: detection from the screen, +statuses, notifications, jumping, search, and restore. -## Usage +## Install + +dingger runs on macOS, with tmux. You need Rust, and Xcode's command line +tools for the menu bar app. ```bash -dingger watch # the daemon (run by launchd, see below) -dingger status # agents in priority order, with cost and context use -dingger focus [--pane ID] # jump to an agent (default: the top waiter) -dingger importance [LEVEL] # an agent's importance: high, normal, low -dingger unread [--pane ID] # mark an idle agent unread (done again) -dingger reload-agents # reload pi agents on an old extension -dingger install pi # install the pi state-reporting extension -dingger install claude # report Claude Code sessions, for restore -dingger install zsh # record typed command lines, for restore -dingger manifests update # refresh manifests from herdr's catalog +# the daemon and CLI +cargo build --release +cp target/release/dingger ~/.local/bin/dingger +codesign --force --sign - ~/.local/bin/dingger # sign the copy, or macOS kills it + +# the menu bar app, the panel, and search +swiftc -O -o target/dingger-menu menubar/*.swift # bundle it as ~/Applications/dingger.app ``` -## Install +1. **Start at login.** Adjust the user path in + `packaging/launchd/dev.dingger.{dingger,menubar}.plist`, copy both to + `~/Library/LaunchAgents/`, then run + `launchctl bootstrap gui/$(id -u) <plist>` for each. +2. **Add the status line.** At the end of your tmux config, add the + following line, then reload with `tmux source-file ~/.tmux.conf`: + + ```tmux + source-file /path/to/dingger/packaging/tmux/dingger.tmux + ``` + + It replaces `status-right`, turns on `focus-events`, and adds hooks + that tell dingger where you are looking. Clicking the counts needs + `set -g mouse on`. +3. **Connect your agents** (optional, recommended): + + ```bash + dingger install pi # pi: exact states, names, cost, drafts, restore + dingger install claude # Claude Code: its session, for restore + dingger install zsh # your exact command lines, for restore + ``` + +Shortcuts live in `~/.config/dingger/config.toml` (Settings… in the menu). +[Install in detail](docs/reference.md#install-in-detail) covers what each +step changes. + +## Usage ```bash -cargo build --release -cp target/release/dingger ~/.local/bin/dingger # do not strip: invalidates -codesign --force --sign - ~/.local/bin/dingger # the signature, macOS kills it -swiftc -O -o target/dingger-menu menubar/*.swift # menu bar + panel, bundled - # as ~/Applications/dingger.app +dingger status # every agent, in the order they wait +dingger focus # jump to the next waiting agent +dingger restore --dry-run # what a restore would bring back +dingger importance high # in an agent's pane: list it first +dingger unread # in an agent's pane: mark it unread +dingger reload-agents # move pi agents onto the current extension +dingger manifests update # refresh detection rules from herdr's catalog +``` + +`dingger status`, abridged: + +```text +STATUS WHERE AGENT COST CTX NAME +blocked infra · certs pi $0.42 18% ! Rotate staging TLS certificates +blocked api · db claude Approve migration for the orders table +failed web · deps pi $0.97 54% Upgrade to React 19 · 429: rate limit exceeded +done api · tests pi $1.84 38% Fix flaky retry test in the HTTP client +working api · perf pi $3.10 76% Profile the slow search endpoint +idle cli · config pi $2.15 61% ✎ Refactor the config loader + +spent: $23.40 today · $187.15 this week · $612.80 this month (UTC) ``` -- launchd agents (start at login, restart on exit): - `packaging/launchd/dev.dingger.{dingger,menubar}.plist`; adjust the user - path, copy to `~/Library/LaunchAgents/`, `launchctl bootstrap gui/$(id -u) <plist>`. -- tmux: you add the snippet yourself; no `dingger install` command does it. - Put this line at the end of your tmux config (`~/.tmux.conf`, or a file - it sources): - - ```tmux - source-file /path/to/dingger/packaging/tmux/dingger.tmux - ``` - - Then reload: `tmux source-file ~/.tmux.conf`. The snippet: - - **replaces** `status-right` with the restore offer, `%d-%b %H:%M`, and - the agent counts. Your own `status-right` is lost, so put the line after - it, or edit the snippet's `status-right` to keep what you had; - - turns on `focus-events`, which raising the right Ghostty window needs; - - adds hooks that report focus and navigation to dingger. They use index - `[77]`, so they keep your own hooks on the same events, and reloading - does not duplicate them; - - binds clicks on the right of the status line, which needs - `set -g mouse on`. - - The line sources the snippet from the repo, so a changed snippet applies - at the next reload. - -dingger does not need tmux running first; it attaches when a server appears. +## How it works + +A daemon (`dingger watch`) polls your tmux server every second and wakes at +once on agent reports and tmux focus hooks. It decides each pane's status +from the agent's own reports where an integration exists, and otherwise +from the screen, through herdr's detection rules. It writes everything to +tmux pane options, so the menu bar app, the panel, and the status line are +stateless readers, and restarting dingger loses nothing. dingger owns no +terminals, and does not need tmux to be running when it starts. + +The [reference](docs/reference.md) has the details: every feature's exact +behavior, where state comes from, and how the pi extension is kept current. ## Testing ```bash -cargo test # engine tests + herdr state-machine tests -tests/e2e.sh # end to end against an isolated tmux server -tests/pi-live.sh <pi> # a real pi binary against dingger (new pi releases) -tests/shutdown.sh [dingger] [rounds] # restore after the ways an OS stops tmux +cargo test # engine and state-machine tests +tests/e2e.sh # end to end, on a tmux server of its own +tests/pi-live.sh <pi> # a real pi release, with a fake model +tests/shutdown.sh # restore after the ways an OS stops tmux ``` -`tests/e2e.sh` starts its own tmux server, its own daemon (polling only every -30 s, so everything asserted must arrive through events), fake agents driven -over the hook protocol, and a real tmux client whose focus it controls. It -asserts every status transition, attendance by navigation, both click paths -(including the real menu bar binary), the floating panel's visibility and -layout rules and content (compact, full, toggle), agent search (matching, -ranking, selection), the status-line segment, latency with a -deliberately slow notifier, and state surviving a daemon restart. It never -touches your tmux server. - -`tests/pi-live.sh <pi binary>` checks a real pi release on a tmux server and -daemon of its own, with a fake local model (`tests/fake-model.py`) and its -own pi agent directory: no network, no credentials, your pi untouched. It -covers identification, the extension's state and session reports, a turn, -`/reload`, an extension's blocked event, dialogs (blocked during a run, -nothing while idle), a cancelled run (idle, not done), a failed run (done, -with why; cleared when seen or run again), names (the session's, not the -terminal title's; named after the first run, renamed every 8 messages, -yours kept, print runs and resumed old sessions named), screen detection -without the -extension, restore (after tmux and its real pis are stopped as a restart -stops them: SIGTERM to all at once, then SIGKILL), reloading an agent on an old extension (and refusing -while busy or with a draft), drafts (reported, cleared, kept across a -crash and put back by restore), cost (priced replies, counted once across -a real `pi --fork`), context use (pi's footer's number, through a -compaction), and exit. To try a release without installing it: -`npm install --prefix /tmp/pi-next @earendil-works/pi-coding-agent@<version>` -(through `sfw`), then `tests/pi-live.sh /tmp/pi-next/node_modules/.bin/pi`. - -`tests/shutdown.sh` stops tmux the ways a restart or logout can, each on -servers, a daemon and a state directory of its own, with fake agents: SIGTERM -to tmux; to the agents first; the daemon first; the terminal quitting first; -and, for 20 rounds each by default, SIGTERM and SIGKILL to everything at once -(tmux, its panes, the terminal's client, the daemon) in a random order at a -random moment. After each it logs in again (a fresh daemon if its own was -stopped; a terminal running `tmux new-session -As 0`) and checks the old -layout is offered and restoring brings back every session, window, pane, -directory, agent and command. - -## Layout - -- `crates/detect-core`: the detection engine and 22 manifests, from herdr. -- `crates/dinggerd`: the `dingger` binary (watcher, hooks, focus, CLI). -- `menubar/`: the menu bar app (`model.swift` rows and colors, `panel.swift` - the floating panel, `hotkey.swift` the global shortcut and settings). -- `packaging/`: launchd agents and the tmux snippet. -- `tests/e2e.sh`: the end-to-end suite; `tests/pi-live.sh`: a real pi. +None of them touch your tmux server, your agents, or your pi setup. +[Testing](docs/reference.md#testing) lists what each covers. The README's +images are real renders of made-up agents: `docs/screenshots.sh` makes them. + +## Credits + +dingger's detection engine and agent rules come from +[herdr](https://github.com/herdrdev/herdr) (Apache-2.0), and so do its +statuses, state machine, colors, and sounds. |
