wherdr 1.3.1 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/_nuxt/{AgentView.C4YOwvZG.css → AgentView.DzFWBd2A.css} +1 -1
  3. package/.output/public/_nuxt/B4Yk2tB1.js +23 -0
  4. package/.output/public/_nuxt/BBw_0a4M.js +6 -0
  5. package/.output/public/_nuxt/{B6qBWHWB.js → BSy6OCIY.js} +1 -1
  6. package/.output/public/_nuxt/BX5PM2JQ.js +1 -0
  7. package/.output/public/_nuxt/BgPDnf2S.js +1 -0
  8. package/.output/public/_nuxt/BkOxQzbS.js +2 -0
  9. package/.output/public/_nuxt/{WLQjK8uJ.js → CKhgznq4.js} +45 -46
  10. package/.output/public/_nuxt/{C2T9fQ4f.js → CP8o925U.js} +1 -1
  11. package/.output/public/_nuxt/CkbmecwS.js +1 -0
  12. package/.output/public/_nuxt/Cr3SrGky.js +1 -0
  13. package/.output/public/_nuxt/DAg9fiVh.js +89 -0
  14. package/.output/public/_nuxt/DT8qZ0Wv.js +1 -0
  15. package/.output/public/_nuxt/DaaJIRB3.js +1 -0
  16. package/.output/public/_nuxt/DcJi16XA.js +1 -0
  17. package/.output/public/_nuxt/VpjCgSXu.js +36 -0
  18. package/.output/public/_nuxt/builds/latest.json +1 -1
  19. package/.output/public/_nuxt/builds/meta/c735b6c0-1bd0-47d7-a208-d310f0a06287.json +1 -0
  20. package/.output/public/_nuxt/dPxB2L94.js +89 -0
  21. package/.output/public/_nuxt/{entry.cKR16VqG.css → entry.CGWKmYU3.css} +1 -1
  22. package/.output/public/_nuxt/hePW80VL.js +1 -0
  23. package/.output/public/_nuxt/xYX85SPI.js +1 -0
  24. package/.output/public/sw.js +1 -1
  25. package/.output/server/chunks/_/branding.mjs +1 -1
  26. package/.output/server/chunks/_/filePaths.mjs +25 -0
  27. package/.output/server/chunks/_/files.mjs +204 -0
  28. package/.output/server/chunks/_/machineOrder.mjs +1 -1
  29. package/.output/server/chunks/_/onboarding.mjs +1 -1
  30. package/.output/server/chunks/_/package.mjs +97 -0
  31. package/.output/server/chunks/_/pluginPolicy.mjs +93 -0
  32. package/.output/server/chunks/_/plugins.mjs +4 -94
  33. package/.output/server/chunks/_/projectBoard.mjs +120 -29
  34. package/.output/server/chunks/_/projectDocs.mjs +106 -0
  35. package/.output/server/chunks/_/projectInbox.mjs +135 -0
  36. package/.output/server/chunks/_/projectThreads.mjs +46 -0
  37. package/.output/server/chunks/_/projectThreads2.mjs +61 -0
  38. package/.output/server/chunks/_/projectsActions.mjs +1 -1
  39. package/.output/server/chunks/_/projectsPlugin.mjs +1 -1
  40. package/.output/server/chunks/_/restart.mjs +2 -312
  41. package/.output/server/chunks/_/reveal.mjs +2 -24
  42. package/.output/server/chunks/_/selfupdate.mjs +83 -0
  43. package/.output/server/chunks/_/updates.mjs +91 -0
  44. package/.output/server/chunks/_/worktrees.mjs +1 -1
  45. package/.output/server/chunks/_/ws.mjs +1 -1
  46. package/.output/server/chunks/nitro/nitro.mjs +2660 -1061
  47. package/.output/server/chunks/raw/bump-homebrew.mjs +1 -1
  48. package/.output/server/chunks/raw/herdr-plugin.mjs +1 -1
  49. package/.output/server/chunks/raw/release-ready.mjs +5 -0
  50. package/.output/server/chunks/routes/api/_..._.mjs +6 -2
  51. package/.output/server/chunks/routes/api/agents.post.mjs +6 -2
  52. package/.output/server/chunks/routes/api/ask-tab.post.mjs +6 -2
  53. package/.output/server/chunks/routes/api/attach.post.mjs +6 -2
  54. package/.output/server/chunks/routes/api/auth/disable.post.mjs +6 -2
  55. package/.output/server/chunks/routes/api/auth/lock-all.post.mjs +6 -2
  56. package/.output/server/chunks/routes/api/auth/lock.post.mjs +6 -2
  57. package/.output/server/chunks/routes/api/auth/login/options.post.mjs +6 -2
  58. package/.output/server/chunks/routes/api/auth/login/verify.post.mjs +6 -2
  59. package/.output/server/chunks/routes/api/auth/max-session.post.mjs +6 -2
  60. package/.output/server/chunks/routes/api/auth/register/options.post.mjs +6 -2
  61. package/.output/server/chunks/routes/api/auth/register/verify.post.mjs +6 -2
  62. package/.output/server/chunks/routes/api/auth/status.get.mjs +6 -2
  63. package/.output/server/chunks/routes/api/board.get.mjs +147 -0
  64. package/.output/server/chunks/routes/api/changes.get.mjs +7 -3
  65. package/.output/server/chunks/routes/api/chat/image.get.mjs +7 -3
  66. package/.output/server/chunks/routes/api/chat.get.mjs +7 -3
  67. package/.output/server/chunks/routes/api/choose.post.mjs +34 -4
  68. package/.output/server/chunks/routes/api/claude-statusline.post.mjs +7 -3
  69. package/.output/server/chunks/routes/api/close/status.get.mjs +7 -3
  70. package/.output/server/chunks/routes/api/close.post.mjs +6 -2
  71. package/.output/server/chunks/routes/api/codex-update.post.mjs +7 -3
  72. package/.output/server/chunks/routes/api/commands.get.mjs +7 -3
  73. package/.output/server/chunks/routes/api/config.get.mjs +8 -3
  74. package/.output/server/chunks/routes/api/dirs.get.mjs +7 -3
  75. package/.output/server/chunks/routes/api/dismiss-stopped.post.mjs +43 -0
  76. package/.output/server/chunks/routes/api/dismiss.post.mjs +7 -3
  77. package/.output/server/chunks/routes/api/effort.post.mjs +7 -3
  78. package/.output/server/chunks/routes/api/efforts.get.mjs +7 -3
  79. package/.output/server/chunks/routes/api/file.post.mjs +7 -3
  80. package/.output/server/chunks/routes/api/files/download.get.mjs +53 -0
  81. package/.output/server/chunks/routes/api/files/read.get.mjs +45 -0
  82. package/.output/server/chunks/routes/api/files.get.mjs +44 -0
  83. package/.output/server/chunks/routes/api/gitroot.get.mjs +7 -3
  84. package/.output/server/chunks/routes/api/health.get.mjs +36 -0
  85. package/.output/server/chunks/routes/api/index.get.mjs +6 -2
  86. package/.output/server/chunks/routes/api/input.post.mjs +18 -4
  87. package/.output/server/chunks/routes/api/interrupt.post.mjs +7 -3
  88. package/.output/server/chunks/routes/api/isgit.get.mjs +7 -3
  89. package/.output/server/chunks/routes/api/machine/awake.get.mjs +7 -3
  90. package/.output/server/chunks/routes/api/machine/awake.post.mjs +7 -3
  91. package/.output/server/chunks/routes/api/machine/order.get.mjs +6 -2
  92. package/.output/server/chunks/routes/api/machine/order.post.mjs +6 -2
  93. package/.output/server/chunks/routes/api/machine/rename.post.mjs +7 -3
  94. package/.output/server/chunks/routes/api/menu.post.mjs +7 -3
  95. package/.output/server/chunks/routes/api/model.post.mjs +7 -3
  96. package/.output/server/chunks/routes/api/models.get.mjs +7 -3
  97. package/.output/server/chunks/routes/api/nav.post.mjs +10 -5
  98. package/.output/server/chunks/routes/api/onboarding.get.mjs +6 -2
  99. package/.output/server/chunks/routes/api/onboarding.post.mjs +6 -2
  100. package/.output/server/chunks/routes/api/phone.get.mjs +6 -7
  101. package/.output/server/chunks/routes/api/phone.post.mjs +6 -7
  102. package/.output/server/chunks/routes/api/plugins/actions.get.mjs +7 -2
  103. package/.output/server/chunks/routes/api/plugins/invoke.post.mjs +7 -2
  104. package/.output/server/chunks/routes/api/plugins/profiles.get.mjs +7 -2
  105. package/.output/server/chunks/routes/api/plugins/projects.get.mjs +7 -3
  106. package/.output/server/chunks/routes/api/plugins/projects.post.mjs +7 -3
  107. package/.output/server/chunks/routes/api/plugins/thread-limits.get.mjs +7 -3
  108. package/.output/server/chunks/routes/api/plugins/thread-limits.post.mjs +7 -3
  109. package/.output/server/chunks/routes/api/project/inbox.get.mjs +43 -0
  110. package/.output/server/chunks/routes/api/project/inbox.post.mjs +43 -0
  111. package/.output/server/chunks/routes/api/project/memory.get.mjs +42 -0
  112. package/.output/server/chunks/routes/api/project/report.get.mjs +7 -3
  113. package/.output/server/chunks/routes/api/project/routines.get.mjs +43 -0
  114. package/.output/server/chunks/routes/api/project/settings.get.mjs +41 -0
  115. package/.output/server/chunks/routes/api/project/thread.get.mjs +44 -0
  116. package/.output/server/chunks/routes/api/project/thread.post.mjs +45 -0
  117. package/.output/server/chunks/routes/api/prompt.post.mjs +26 -4
  118. package/.output/server/chunks/routes/api/push/quiet.post.mjs +7 -3
  119. package/.output/server/chunks/routes/api/push/subscribe.post.mjs +7 -3
  120. package/.output/server/chunks/routes/api/push/test.post.mjs +7 -3
  121. package/.output/server/chunks/routes/api/push/unsubscribe.post.mjs +7 -3
  122. package/.output/server/chunks/routes/api/quotas.get.mjs +7 -3
  123. package/.output/server/chunks/routes/api/rename.post.mjs +6 -2
  124. package/.output/server/chunks/routes/api/requeue.post.mjs +7 -3
  125. package/.output/server/chunks/routes/api/restart.get.mjs +6 -2
  126. package/.output/server/chunks/routes/api/restart.post.mjs +6 -2
  127. package/.output/server/chunks/routes/api/reveal.post.mjs +7 -2
  128. package/.output/server/chunks/routes/api/screen-key.post.mjs +7 -3
  129. package/.output/server/chunks/routes/api/screen.get.mjs +10 -4
  130. package/.output/server/chunks/routes/api/search.get.mjs +7 -3
  131. package/.output/server/chunks/routes/api/seen.post.mjs +7 -3
  132. package/.output/server/chunks/routes/api/sessions.get.mjs +7 -3
  133. package/.output/server/chunks/routes/api/sessions.post.mjs +7 -3
  134. package/.output/server/chunks/routes/api/space.post.mjs +19 -15
  135. package/.output/server/chunks/routes/api/state.get.mjs +7 -3
  136. package/.output/server/chunks/routes/api/unqueue.post.mjs +7 -3
  137. package/.output/server/chunks/routes/api/update/status.get.mjs +38 -0
  138. package/.output/server/chunks/routes/api/update.get.mjs +12 -51
  139. package/.output/server/chunks/routes/api/update.post.mjs +41 -0
  140. package/.output/server/chunks/routes/api/upload.post.mjs +7 -3
  141. package/.output/server/chunks/routes/api/worktrees/remove.post.mjs +6 -2
  142. package/.output/server/chunks/routes/api/worktrees.get.mjs +6 -2
  143. package/.output/server/chunks/routes/favicon.ico.get.mjs +6 -2
  144. package/.output/server/chunks/routes/icons/_name_.get.mjs +7 -3
  145. package/.output/server/chunks/routes/renderer.mjs +8 -4
  146. package/.output/server/chunks/routes/uploads/_...name_.get.mjs +7 -3
  147. package/.output/server/chunks/routes/ws/_..._.mjs +7 -3
  148. package/.output/server/chunks/routes/ws/events.mjs +7 -3
  149. package/.output/server/chunks/routes/ws/term.mjs +9 -5
  150. package/.output/server/chunks/virtual/precomputed.mjs +1 -1
  151. package/.output/server/index.mjs +7 -3
  152. package/.output/server/package.json +1 -1
  153. package/README.md +440 -46
  154. package/bin/install-docker-skill.sh +20 -0
  155. package/bin/lib/commands.mjs +47 -9
  156. package/bin/lib/core.mjs +18 -2
  157. package/bin/lib/install-skill.sh +100 -0
  158. package/bin/lib/skill.mjs +16 -0
  159. package/bin/lib/tailnet.d.mts +12 -3
  160. package/bin/lib/tailnet.mjs +103 -25
  161. package/bin/lib/updater.mjs +320 -0
  162. package/bin/lib/verify-release.mjs +20 -0
  163. package/package.json +4 -2
  164. package/skills/wherdr/SKILL.md +180 -0
  165. package/.output/public/_nuxt/BDNMzG2s.js +0 -1
  166. package/.output/public/_nuxt/BMIe8p_3.js +0 -2
  167. package/.output/public/_nuxt/BpQH8Ho1.js +0 -1
  168. package/.output/public/_nuxt/BrAeJIZR.js +0 -30
  169. package/.output/public/_nuxt/C12eXl5z.js +0 -1
  170. package/.output/public/_nuxt/CXG-cOdh.js +0 -1
  171. package/.output/public/_nuxt/CYxHnMNH.js +0 -83
  172. package/.output/public/_nuxt/CctS2Xn6.js +0 -65
  173. package/.output/public/_nuxt/CiYar6hg.js +0 -6
  174. package/.output/public/_nuxt/DLLQgP0d.js +0 -1
  175. package/.output/public/_nuxt/Dwh-l2rQ.js +0 -17
  176. package/.output/public/_nuxt/bLP7Jj-b.js +0 -1
  177. package/.output/public/_nuxt/builds/meta/6466b0ab-7935-460c-abdf-2ea632dae6d0.json +0 -1
  178. package/.output/public/_nuxt/whFLy_Ne.js +0 -23
  179. package/.output/server/chunks/_/phone.mjs +0 -343
  180. package/.output/server/chunks/routes/ws/mirror.mjs +0 -157
package/README.md CHANGED
@@ -7,13 +7,28 @@
7
7
  <p align="center"><b>A complete workspace for the coding agents running in <a href="https://herdr.dev">Herdr</a>, on your
8
8
  computer and on your phone.</b></p>
9
9
 
10
- <p align="center"><a href="https://wherdr.dev"><b>wherdr.dev</b></a> · <code>curl -fsSL https://wherdr.dev/install | sh</code> · <code>herdr plugin install afloury/wherdr</code></p>
10
+ <p align="center"><a href="https://wherdr.dev"><b>wherdr.dev</b></a> · <a href="#ask-your-ai-agent">Ask your AI agent</a></p>
11
+
12
+ <p align="center"><a href="https://wherdr.dev/demo/"><b>Try it in your browser — no install</b></a>: the real app on scripted agents, nothing runs.</p>
11
13
 
12
14
  <p align="center"><a href="https://github.com/afloury/wherdr/actions/workflows/ci.yml"><img src="https://github.com/afloury/wherdr/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI"></a></p>
13
15
 
14
16
  ## Quick start
15
17
 
16
- On the computer that runs [Herdr](https://herdr.dev) (≥ 0.9.1), macOS or Linux, one command:
18
+ ### Ask your AI agent
19
+
20
+ **Recommended.** Paste this into Claude Code, Codex or any coding agent: it installs and sets up wherdr for you.
21
+
22
+ ```text
23
+ Read https://wherdr.dev/agent.md and help me set up wherdr. Check what is already installed first, and ask me before installing anything.
24
+ ```
25
+
26
+ It follows [our setup guide for agents](https://wherdr.dev/agent.md): checks what you already have,
27
+ asks before installing anything, sets up your phone over Tailscale, never exposes wherdr to the Internet.
28
+
29
+ ### Or run the installer yourself
30
+
31
+ On the computer that runs [Herdr](https://herdr.dev) (≥ 0.9.1), macOS or Linux:
17
32
 
18
33
  ```sh
19
34
  curl -fsSL https://wherdr.dev/install | sh
@@ -36,6 +51,13 @@ saved on the server (`data/onboarding.json`), so your other devices do not show
36
51
  it from Settings › About › **Setup guide**. On a phone that already opens wherdr through its
37
52
  tailnet address, the phone step becomes Add to Home Screen.
38
53
 
54
+ **Passkeys are tied to the address they are created on.** Once wherdr's tailnet address is
55
+ published and answers, the guide opened on `localhost` shows **Continue on
56
+ <machine>.<tailnet>.ts.net**, which reopens it at the same step on that address, and its Security
57
+ step (like Settings › Security) sends the passkey lock there. Nothing redirects on its own:
58
+ `localhost` stays valid on the computer. The installer, the Herdr plugin and `wherdr open` open
59
+ that address too when it is already published.
60
+
39
61
  wherdr is a small self-hosted web app that talks to the Herdr server on your machine. **On a
40
62
  computer**, it can replace the Herdr terminal client day to day: your spaces, tabs and split panes
41
63
  side by side, live conversations and real terminals, drag-and-drop layout, keyboard shortcuts
@@ -69,13 +91,32 @@ it on `localhost`, or from your other devices over Tailscale.
69
91
  *Unofficial project. Not affiliated with or endorsed by Herdr, Anthropic or OpenAI. Claude,
70
92
  Claude Code, Codex and other product names are trademarks of their respective owners.*
71
93
 
94
+ ## Why wherdr
95
+
96
+ - **Herdr's splits, live.** A tab with several panes is drawn in Herdr's real layout, every pane
97
+ live; drag a pane to move it or a divider to resize, and the change goes to Herdr itself.
98
+ - **A passkey lock.** Face ID, Touch ID, Windows Hello or Android unlock the app; once a passkey
99
+ exists, nothing about your agents is served to a locked session.
100
+ - **The agent's diff.** Git status and diff of each agent's working folder, one click away from
101
+ its conversation.
102
+ - **The herdr-projects board.** A coordinator and its threads grouped under their project, with
103
+ the project's task lists as a live board and one-click replies.
104
+ - **Keyboard and global search.** One shortcut searches every agent and conversation; next
105
+ agent, new tab, pane swaps and `1`–`9` answers never need the mouse.
106
+ - **Real time, and private.** The real terminal over a WebSocket, a push when an agent needs
107
+ you, and your phone through Tailscale: nothing is on the Internet.
108
+
109
+ Details in [Features](#features) and [Security](#security).
110
+
72
111
  ## Contents
73
112
 
74
113
  - [Quick start](#quick-start)
114
+ - [Why wherdr](#why-wherdr)
75
115
  - [Features](#features)
76
116
  - [How it works](#how-it-works)
77
117
  - [Requirements](#requirements)
78
- - [Installation](#installation): [the one command](#the-one-command) ·
118
+ - [Installation](#installation): [ask your AI agent](#ask-your-ai-agent-1) ·
119
+ [or run the installer yourself](#the-one-command) ·
79
120
  [which setup?](#which-setup) ·
80
121
  [always-on server + Tailscale](#recommended-always-on-server--tailscale) ·
81
122
  [other private networks](#other-private-networks) ·
@@ -99,16 +140,22 @@ Claude Code, Codex and other product names are trademarks of their respective ow
99
140
 
100
141
  ### Everywhere
101
142
 
102
- - **Live agent list**, one line per Herdr space, grouped by state (your turn, working, ready),
103
- with a preview of each agent's last answer. When an agent asks for permission or asks a
104
- question, the options show on its card: **answer in one tap** without opening it.
143
+ - **Live agent list**, one line per Herdr space, grouped by state (your turn, working, ready).
144
+ The list is compact by default: agent, title, project badge and state. When an agent asks for
145
+ permission or asks a question, its card opens on the question and its options: **answer in one
146
+ tap** without opening it; the card is one line again once answered. Turn off Settings ›
147
+ Appearance › Home screen › **Compact list** for detailed cards, with the folder, the model and
148
+ a preview of each agent's last answer.
105
149
  - **Conversation view**: the agent's real transcript (Claude Code, Codex and omp), rendered as
106
150
  Markdown, with grouped tool calls, images (full-screen viewer with pinch / wheel zoom and pan), timestamps, search and infinite scroll back to the
107
151
  first message. Reading a conversation never touches the terminal, so it never resizes a pane.
108
152
  A command the agent proposes (shell code block, or inline `! command`) can be copied without
109
153
  its prompt, or run in the agent's shell mode (`!`, Claude Code, Codex and omp) after you
110
154
  confirm the full command.
111
- - **Terminal view**: the real terminal (xterm.js, WebGL or DOM renderer) with take-over. On a
155
+ - **Terminal view**: the real terminal (xterm.js, WebGL or DOM renderer) with take-over. Opening
156
+ it resizes the real pane to your screen (or its cell in a split); closing it gives the pane the size it had before
157
+ (after 30 seconds when the connection just dropped, e.g. a locked phone), unless a Herdr
158
+ client attached meanwhile laid it out its own way. On a
112
159
  computer, drag over the text on screen to select it: releasing the button copies it, as do
113
160
  `⌘C` / `Ctrl+Shift+C`. The history is browsed with the wheel, not by dragging past the edge.
114
161
  Links (OSC 8 hyperlinks and plain `http(s)://` URLs, even wrapped over several rows) open in a
@@ -116,10 +163,28 @@ Claude Code, Codex and other product names are trademarks of their respective ow
116
163
  line wrapped by the width is copied in one piece, without the breaks between its rows. An image
117
164
  pasted with `⌘V` (Mac), here or in a side-by-side terminal, goes to the agent's machine and its
118
165
  path is pasted, which omp and Claude Code attach as an image, as in the conversation.
166
+ - **Interactive agent screens**: in a conversation, an uncertain menu or panel opens the
167
+ pane's real terminal in a modal window, with arrows, Enter, Escape, Tab, digits and y/n.
168
+ The rest of the app waits behind a scrim (the window fills the screen on a phone); in a
169
+ split, only the cell in use opens it. It closes after the agent's message field returns.
170
+ Close it yourself at any time with its button or a click on the scrim: that releases the
171
+ terminal without pressing Escape, which goes to the agent, and the same interaction stays
172
+ closed until you reopen it. Existing native questions, approvals, menus, waiting screens and
173
+ Claude settings cards keep their controls. The window is a fallback after those
174
+ recognizers, with two explicit exceptions: Codex's two-column `/usage` menu and
175
+ its Agent Command Center. The slash menu still lists and sends commands. On a phone, the window
176
+ temporarily fits the real pane to its width and restores its size on release, using
177
+ the same lease as Terminal view; it does not shrink 120 columns into unreadable text.
178
+ A pane already shown in Terminal keeps its existing live view. Try `/usage` on the
179
+ Codex agent in the browser demo (scripted, no account data).
180
+ In the compact list, native questions keep their preview and one-tap answers.
181
+ Unrecognized blocking screens offer an **Open interactive terminal** button.
119
182
  - **Composer**: send messages while the agent works (queued, cancellable), attach or paste
120
183
  photos and the files the agent can read (Claude Code: PDFs, notebooks, text and code of any
121
184
  extension; Codex: notebooks, text and code), run slash commands (`/compact`, `/clear`, `/context`, `/usage`…) and see the output
122
- of local commands. Stop button while the agent works.
185
+ of local commands. Stop button while the agent works. A long pasted text (log, output) becomes
186
+ a "Pasted text · N lines" card, sent to the agent as it is and shown the same way in the
187
+ conversation, where it opens in full with Copy; long typed messages fold with Show more.
123
188
  - **Codex updates and limits**: in a Codex conversation, a new Codex version shows above the
124
189
  field with an **Update** button (runs Codex's official update command on that machine, after
125
190
  confirmation), then **Restart to update** on the same conversation. When wherdr cannot run it
@@ -127,6 +192,20 @@ Claude Code, Codex and other product names are trademarks of their respective ow
127
192
  resumes the same conversation (only while Codex is idle). Codex's own weekly-limit warning
128
193
  (its footer gauge, or its startup heads-up) is repeated above the field, with its values, while
129
194
  Codex shows it.
195
+ - **After Codex updates itself and exits**: a **Your turn** card offers **Restart Codex**
196
+ for a new conversation, with the original launch arguments. For an existing conversation,
197
+ choose **Resume conversation** (its exact session, with the original options) or **Start
198
+ fresh**. If wherdr cannot identify the session, Resume opens Codex's picker rather than
199
+ choosing another pane's latest conversation. The usual attention notification applies.
200
+ Nothing restarts automatically: the pane must still show the same empty shell prompt,
201
+ with the shell in the foreground. Unrecognized shell prompts require a manual restart
202
+ in the terminal. wherdr must have observed Codex before it exited.
203
+ **Dismiss** puts the card away for good: the pane is a plain shell again, to use as you like.
204
+ Project panel messages wait in the queue until the agent returns; if it does not (Dismiss, or
205
+ a Codex that simply left), their text goes back into the pane's message field. Continuation
206
+ prompts (unfinished quotes or heredocs) refuse a restart too. If the final
207
+ transcript read fails, both conversation choices remain available; a session
208
+ contradicted by a guessed match is resumed through Codex's picker.
130
209
  - **Model and effort pickers** for Claude Code, Codex and omp (omp: its
131
210
  "Switch Model" selector, `/switch` or `/model`, and its thinking level, changed with its own
132
211
  ⇧⇥ cycle). Changes apply **to the current session only**; wherdr never changes your default
@@ -134,6 +213,30 @@ Claude Code, Codex and other product names are trademarks of their respective ow
134
213
  - **New agent**: pick an installed agent (or a plain terminal), a folder, optionally a separate
135
214
  Git **worktree** and branch, resume the last conversation, and queue a first message.
136
215
  - **Changes view**: Git status and diff of an agent's working folder; worktree management.
216
+ Review the diff like a pull request: comment on one line or on a **block of lines**. The code
217
+ itself is plain text — a click on it does nothing, so it can be selected and copied.
218
+ - **Mouse**: a **+** appears in the gutter of the hovered line; click it to comment that line,
219
+ or press it and drag over other lines to comment the block. With an editor open, Shift+click
220
+ another line to change its block.
221
+ - **Touch**: tap a line number to choose the line, tap another line to extend the block, then
222
+ **Comment** in the bar pinned at the bottom. Nothing is written by the first tap.
223
+ - **Keyboard**: one tab stop per file; arrow keys move between lines, Shift+arrows choose a
224
+ block, Enter opens the editor. In the editor, ⌘/Ctrl+Enter saves and Escape cancels (a second
225
+ Escape if text would be lost); the window stays open.
226
+
227
+ A block stays within one hunk (the other hunks dim while it is chosen) and may mix deleted and
228
+ new lines. Each comment is a thread under the last line of its block, headed by its lines
229
+ ("Lines 12–18"), with **Edit** and a two-step **Delete**; the lines it covers are marked in
230
+ the gutter. Comments, including the text of an editor left open, are saved on this device for
231
+ that agent, across refreshes and reloads. A block that leaves the diff, or whose code changed
232
+ between its first and last lines, is flagged above the diff and keeps the excerpt that was
233
+ commented.
234
+ **Send review** stays pinned with the number of comments; it groups them by file as
235
+ `file:12-18 — remark` (`file:12` for one line) followed by a short excerpt of the block, and
236
+ appends them to the conversation's message draft, preserving existing text; it never sends a
237
+ message. Deleted lines use the old file's numbers, explicitly labelled. **Discard** clears the
238
+ local review. Works on remote agents and in the demo too, with no writes to the agent's
239
+ machine. Browser storage must be available to retain comments after closing the view.
137
240
  - **Several machines**: agents of the SSH machines registered in Herdr (`herdr machine add`)
138
241
  appear in their own section; everything works the same on a remote agent.
139
242
  - **Named Herdr sessions**, **quotas** (remaining Claude and Codex usage limits), **Herdr plugin
@@ -144,7 +247,10 @@ Claude Code, Codex and other product names are trademarks of their respective ow
144
247
  ### On a computer
145
248
 
146
249
  - **Split panes, live**: a tab with several panes is drawn with Herdr's real layout; every pane
147
- shows its conversation or terminal live, and the one you click becomes interactive.
250
+ shows its conversation or terminal live. Every visible terminal fills its cell and follows
251
+ window and divider resizing, keeping its instance and connection when focus changes.
252
+ Click a cell to give it the keyboard. Leaving the tab restores each terminal's original size,
253
+ unless a Herdr client has resized it meanwhile.
148
254
  - **Rearrange the layout**: drag a pane onto another to move it, drag a divider to resize
149
255
  (arrow keys work on a focused divider); changes go to Herdr itself.
150
256
  - **Spaces and tabs**: sidebar with every space, tabs at the top, active tab remembered per space.
@@ -153,7 +259,8 @@ Claude Code, Codex and other product names are trademarks of their respective ow
153
259
  - **Keyboard shortcuts** (`Mod` is ⌘ on macOS, Ctrl elsewhere; `Mod+/` or `?` lists them all,
154
260
  also in Settings › Computer):
155
261
  - `Ctrl/⌘+K` global search across agents and conversations, `Alt+↑/↓` previous / next agent
156
- in the list, `Mod+,` settings.
262
+ in the list, `Mod+,` settings, `H` the [Board](#works-great-with-herdr-projects) (outside a
263
+ text field and the terminal).
157
264
  - `Mod+Alt+N` new agent in a new space, `Mod+Alt+T` new tab in the current space,
158
265
  `Mod+Alt+W` close the current pane (after confirmation). They work from the terminal too.
159
266
  - With [herdr-projects](#works-great-with-herdr-projects) on the agent's machine: `Mod+Alt+P`
@@ -170,6 +277,9 @@ Claude Code, Codex and other product names are trademarks of their respective ow
170
277
  under their project, and a side panel with the project's task lists (to test, to decide,
171
278
  to review, in progress, in queue, blocked, to do, backlog — each optional) and one-click
172
279
  replies to the coordinator.
280
+ - **Board**: every agent, and with herdr-projects every project, thread and task, in four columns
281
+ (Blocked, Todo, Ongoing, Finished). Drag a task card to another column, or use its **Move
282
+ to…** menu, to ask the coordinator to move it.
173
283
  - **Settings** in a sidebar, with desktop-only options such as the content width.
174
284
 
175
285
  ### On a phone
@@ -199,7 +309,10 @@ phone / laptop ──private network──> HTTPS (tailscale serve, private prox
199
309
 
200
310
  wherdr runs on the same machine as the Herdr server (the "server" below). It uses Herdr's local
201
311
  socket API and CLI, reads the agents' transcript files, and serves a client-only Nuxt app. Data
202
- it keeps (VAPID keys, push subscriptions, passkeys, recent folders) lives in `data/`.
312
+ it keeps (VAPID keys, push subscriptions, passkeys, recent folders) lives in `data/`. So do the
313
+ long texts pasted into the message field, which the server keeps to show them as cards on every
314
+ device: `data/pastes.json`, in clear, readable by the server's user only (mode 600), at most the
315
+ last 100 texts and 2 MB, each one deleted 30 days after it was last sent.
203
316
 
204
317
  Built with Nuxt 4, Nuxt UI 4, Tailwind 4 and TypeScript; the Herdr gateway (API, WebSockets,
205
318
  Web Push, lock) runs in Nitro.
@@ -220,7 +333,20 @@ Web Push, lock) runs in Nitro.
220
333
 
221
334
  ## Installation
222
335
 
223
- ### The one command
336
+ ### Ask your AI agent
337
+
338
+ **Recommended.** Paste this into Claude Code, Codex or any coding agent: it installs and sets up wherdr for you.
339
+
340
+ ```text
341
+ Read https://wherdr.dev/agent.md and help me set up wherdr. Check what is already installed first, and ask me before installing anything.
342
+ ```
343
+
344
+ It follows [our setup guide for agents](https://wherdr.dev/agent.md): checks what you already have,
345
+ asks before installing anything, sets up your phone over Tailscale, never exposes wherdr to the Internet.
346
+
347
+ <a id="the-one-command"></a>
348
+
349
+ ### Or run the installer yourself
224
350
 
225
351
  On the computer that runs Herdr, as your user, on macOS or Linux:
226
352
 
@@ -243,7 +369,10 @@ The script ([read it first](https://wherdr.dev/install), source in
243
369
  opens the setup guide in your browser (first install) and binds **prefix+i** to its panel;
244
370
  5. on a computer **without a screen** (no `DISPLAY` on Linux, or an SSH session): opens nothing,
245
371
  prints the local address, an `ssh -L` tunnel to reach it from your computer, and the phone
246
- steps (`wherdr phone`: Tailscale state, the address, the QR code once it answers).
372
+ steps (`wherdr phone`: Tailscale state, the address, the QR code once it answers). With
373
+ Docker, it offers to run `tailscale serve` for you (asked first), and wherdr enables the
374
+ published address by itself: no browser is needed on the server. Run the command again at
375
+ any time to check the phone address.
247
376
 
248
377
  Settings, as environment variables (`curl -fsSL https://wherdr.dev/install | WHERDR_PORT=7684 sh`):
249
378
 
@@ -254,7 +383,7 @@ Settings, as environment variables (`curl -fsSL https://wherdr.dev/install | WHE
254
383
  | `WHERDR_MODE` | — | `native`: Node.js even when Docker is there. `docker`: no plugin, Docker Compose by hand in `WHERDR_DIR` (Linux; see below). |
255
384
  | `WHERDR_YES` | — | `1` answers yes to every question (non-interactive use). |
256
385
  | `WHERDR_NO_BROWSER` | — | `1` never opens a browser. |
257
- | `WHERDR_REF` | latest | Git tag or branch of the plugin (or of `docker-compose.yml` with `WHERDR_MODE=docker`). |
386
+ | `WHERDR_REF` | main | Git tag or branch of the plugin (or of `docker-compose.yml` with `WHERDR_MODE=docker`). |
258
387
  | `WHERDR_PLUGIN` | `afloury/wherdr` | A local checkout to link and build instead (development, tests). |
259
388
 
260
389
  **Docker by hand, with the script:** `curl -fsSL https://wherdr.dev/install | WHERDR_MODE=docker sh`
@@ -302,7 +431,7 @@ Edit `.env` (every value has a default; `HOST_HOME` defaults to your `$HOME`):
302
431
  | `HOST_HOME` | `/home/alice` | Your home folder on the server (default: `$HOME`). Mounted at the same path, read-only. |
303
432
  | `PUID` / `PGID` | `1000` | Your user and group IDs (`id -u`, `id -g`). |
304
433
  | `PORT` | `7683` | Listening port, published on `127.0.0.1` only. |
305
- | `APP_URL` | `https://server.example.ts.net:7683/` | Private HTTPS address of the app (step 3; also used as the Web Push contact and an allowed host). Use `http://localhost:7683/` for local testing. |
434
+ | `APP_URL` | `http://localhost:7683/` | Leave it with Tailscale: wherdr adopts the address of step 3 by itself. Set a private HTTPS address only for [another private network](#other-private-networks) (Web Push contact and allowed host; it then always wins). |
306
435
  | `HOST_LABEL` | `server` | Name of this machine in the app. |
307
436
  | `TZ` | `Europe/Paris` | Time zone for timestamps and logs. |
308
437
  | `WHERDR_UPDATE_CHECK` | `off` | Disable the daily check for a new release (default `on`). |
@@ -340,11 +469,38 @@ tailscale serve --bg --https=7683 http://127.0.0.1:7683
340
469
  ```
341
470
 
342
471
  Tailscale Serve listens on HTTPS port 7683 of your tailnet name, with a valid certificate, and
343
- forwards to wherdr's local HTTP port. Only devices of your tailnet can reach it. Then open
344
- **Settings › Phone** in wherdr on the server (`http://localhost:7683/#/settings?section=phone`),
345
- paste that address: wherdr checks it answers, sets it as `APP_URL` (saved in `data/app-url.json`;
346
- an HTTPS `APP_URL` in `.env` wins) and shows its QR code. Without Docker, the same page publishes
347
- wherdr itself: one button, asked first.
472
+ forwards to wherdr's local HTTP port. Only devices of your tailnet can reach it.
473
+
474
+ **wherdr enables that address by itself**, on a server without a screen too: at startup, every
475
+ 30 seconds, and when an address it does not know yet is opened, it reads what `tailscale serve`
476
+ publishes on this machine and adopts the HTTPS address that points to its own port (allowed
477
+ host and `APP_URL`, saved in `data/app-url.json`; an HTTPS `APP_URL` in `.env` wins). An address
478
+ removed from `tailscale serve` is refused again. Nothing a request says can enable an address:
479
+ only Tailscale's own state on the machine does, and an address that `tailscale funnel` opens to
480
+ the Internet is never adopted. Without Docker, wherdr runs the `tailscale` command; in Docker
481
+ it reads Tailscale's socket, mounted by `docker-compose.yml` (`/var/run/tailscale`). After
482
+ Tailscale itself restarts (an update), restart the container.
483
+
484
+ What that mount gives: Tailscale's local API, with the rights of the host user the container
485
+ runs as. Anyone may read it; if that user is Tailscale's operator
486
+ (`tailscale set --operator=$USER`), the API also accepts changes from it (serve, funnel…), and
487
+ `:ro` on the folder does not prevent that. wherdr itself only sends `GET` requests. This is not
488
+ a new privilege: the container already drives Herdr, which is a shell of that same user. To do
489
+ without it, remove the line from `docker-compose.yml`: wherdr then no longer finds the address
490
+ by itself, and the one command (or the plugin's phone step) run after `tailscale serve` hands
491
+ it over.
492
+
493
+ **Settings › Phone** on the server (`http://localhost:7683/#/settings?section=phone`) shows the
494
+ state and the QR code; without Docker it also publishes wherdr itself: one button, asked first.
495
+ From a terminal, `wherdr phone` (npm, Homebrew) or the [one command](#the-one-command) run again
496
+ (Docker, Herdr plugin) checks the same thing, and
497
+ `curl -fsS http://127.0.0.1:7683/api/phone` prints the state as JSON. A container started from
498
+ an older `docker-compose.yml` has no Tailscale socket: paste the address in Settings › Phone,
499
+ or run the one command again, which sends it.
500
+
501
+ If the phone shows **"This address isn't enabled yet"** (403), the address it opened is not one
502
+ `tailscale serve` publishes for wherdr's port on that machine: the page gives the command to run
503
+ there.
348
504
 
349
505
  **Why HTTPS matters.** Browsers only allow three things on `https://` (or on `localhost` itself):
350
506
 
@@ -384,8 +540,8 @@ works with any private network that gives it an **HTTPS address with a valid cer
384
540
  - **`APP_URL` set to that address** (`.env` with Docker, or the environment of `wherdr` /
385
541
  `npm start`), then restart wherdr. Its hostname is allowed automatically and it enables Web
386
542
  Push. Add `HERDR_WEB_ALLOWED_HOSTS` only if you also open wherdr under another name. The
387
- one-click address check in **Settings › Phone** only knows `*.ts.net` addresses; an HTTPS
388
- `APP_URL` in the environment always wins.
543
+ automatic adoption and the address check in **Settings › Phone** only know the `*.ts.net`
544
+ addresses of `tailscale serve`; an HTTPS `APP_URL` in the environment always wins.
389
545
  - **Never exposed without authentication.** wherdr keeps listening on `127.0.0.1`; only the
390
546
  proxy listens on the private network. Turn on the passkey lock as soon as another device can
391
547
  reach it.
@@ -464,8 +620,8 @@ wherdr phone # phone state, the app's phone setup page, QR code o
464
620
  | `wherdr start` · `stop` · `restart` | Background server, pid and log in `~/wherdr`. `start` says *started* only once the port answers; otherwise it prints the end of the log. With a login service installed, they drive the service. |
465
621
  | `wherdr status` | Address, process, service, data folder and log. |
466
622
  | `wherdr logs` (`-f` to follow, `-n 100`) | The log, `~/wherdr/wherdr.log`. |
467
- | `wherdr open` | Opens wherdr in the browser. |
468
- | `wherdr phone` | Whether your phone can reach wherdr (Tailscale, `tailscale serve`, the address answering), the link to **Settings › Phone** where you publish it in one click, and the QR code once the address answers. |
623
+ | `wherdr open` | Opens wherdr in the browser: its tailnet address when published and answering, else `localhost`. |
624
+ | `wherdr phone` | Whether your phone can reach wherdr (Tailscale, `tailscale serve`, the address answering), the `tailscale serve` command to publish it when it is not (wherdr then enables the address by itself), the link to **Settings › Phone**, and the QR code once the address answers. |
469
625
  | `wherdr service install` · `uninstall` | Start at login: a LaunchAgent on macOS (`~/Library/LaunchAgents/dev.wherdr.plist`), a `systemd --user` unit on Linux (`loginctl enable-linger` keeps it running while you are logged out). |
470
626
  | `wherdr doctor` | Checks Node/Bun, Herdr and its socket, the port, the service, the data folder and `APP_URL`. |
471
627
 
@@ -553,8 +709,8 @@ setup by itself:
553
709
  `~/wherdr` (`docker-compose.yml`, `.env`), the same files as `WHERDR_MODE=docker` of
554
710
  [the one command](#the-one-command).
555
711
  - **Node.js 22** everywhere else, and always on macOS (Docker Desktop cannot reach Herdr's
556
- socket): the plugin downloads the prebuilt npm package of its version (or builds wherdr in its
557
- own folder when that fails), copies it to `~/wherdr/app` and runs it detached from there, with
712
+ socket): the plugin downloads the prebuilt package of its version from GitHub Releases and
713
+ verifies its SHA-256 checksum, copies it to `~/wherdr/app` and runs it detached from there, with
558
714
  its pid and log (`wherdr.log`) in `~/wherdr`. Herdr builds plugins in a temporary folder and
559
715
  then moves them, so the server never runs from the plugin folder. Bun works too when Node.js 22
560
716
  is missing. Without either, the install stops and says so.
@@ -594,7 +750,7 @@ Without a key: `herdr plugin action invoke panel --plugin afloury.wherdr`. The p
594
750
  | Action | What it does |
595
751
  |---|---|
596
752
  | `panel` | The **wherdr** panel (a popup) |
597
- | `open` | Opens wherdr in the browser |
753
+ | `open` | Opens wherdr in the browser (its tailnet address when published and answering) |
598
754
 
599
755
  Up to 1.2.0 the panel action was called `wherdr`: change a key bound to `afloury.wherdr.wherdr` to
600
756
  `afloury.wherdr.panel` (Herdr has no action aliases; the install warns about such a key and
@@ -676,6 +832,45 @@ home screen and in Settings › About, with the release notes and the exact comm
676
832
  Hide it until the next version with ×. Set `WHERDR_UPDATE_CHECK=off` to disable the check. The
677
833
  installed version is shown in Settings › About.
678
834
 
835
+ GitHub release packages (including the bundled agent skill), Docker images, and Homebrew
836
+ updates are published without waiting for npm moderation. Native plugin installs and updates
837
+ use the GitHub `.tgz` and verify its SHA-256 checksum before extraction. A failed one-tap
838
+ download or checksum check leaves the running version in place. Plugin installation tries
839
+ GitHub first, then the prebuilt npm package of the exact same version (with its registry integrity hash), and only builds from source when neither works.
840
+ This also keeps older 1.3.x releases without GitHub archives installable with Node.js or Bun.
841
+ Packages predating the bundled skill retain the plugin checkout's commands and skill, so
842
+ skill refresh, installation and uninstallation remain available.
843
+ The installer fetches `main` explicitly unless you set `WHERDR_REF`.
844
+
845
+ The SHA-256 checksum detects damaged downloads. It is served beside the archive, so someone
846
+ who can replace both files can make them match; it does not prove the publisher’s identity
847
+ or protect against replacement of the archive and checksum together.
848
+
849
+ `npm install -g`, `npx`, `bunx` and `pnpm dlx` still need npm to serve the wherdr package.
850
+ Their banner waits for registry availability; Homebrew's banner waits for its formula.
851
+ Versions through 1.3.2 use GitHub's `latest` marker and an npm-based updater. That marker only
852
+ advances once npm **and** Homebrew are ready, so their existing Update button continues to work.
853
+ To update an older native plugin earlier, rerun the installer or press **U** in its Herdr panel.
854
+ See [Releasing](docs/RELEASING.md) for the publication order and recovery steps.
855
+
856
+ **Update in one tap.** When wherdr runs from the Herdr plugin (Node.js mode), a global npm install
857
+ or Homebrew, the banner has an **Update** button instead of the command, for a session opened on
858
+ this computer (`localhost`) or unlocked with a passkey. wherdr installs the new version in a
859
+ process of its own, restarts, and checks that `/api/health` reports the new version within 60
860
+ seconds; the app shows the progress and reloads on the new version. If the new version does not
861
+ answer, wherdr goes back to the previous one by itself (the plugin keeps the previous copy in
862
+ `~/wherdr/app.prev`, npm reinstalls the previous version, Homebrew points back to the previous keg)
863
+ and the app says so. Every step is logged in `~/wherdr/update.log`.
864
+
865
+ | Setup | What Update does |
866
+ | --- | --- |
867
+ | Herdr plugin, Node.js | Verifies and unpacks the GitHub release package of the new version into `~/wherdr/app`, keeps the old copy, restarts |
868
+ | `npm install -g wherdr` | `npm install -g wherdr@X.Y.Z`, then restarts (login service included) |
869
+ | Homebrew | `brew upgrade wherdr`, then `brew services restart wherdr` |
870
+
871
+ Docker shows the command instead: replacing its own container would need the Docker socket (root
872
+ on the host) inside it. `npx wherdr` runs from a temporary folder: run `npx wherdr@latest` again.
873
+
679
874
  | Setup | Update command (in the wherdr folder) |
680
875
  | --- | --- |
681
876
  | Docker, published image (default) | `docker compose pull && docker compose up -d` |
@@ -734,8 +929,9 @@ All settings are environment variables (`.env` with Docker).
734
929
  | --- | --- | --- |
735
930
  | `PORT` | `7683` | Listening port. |
736
931
  | `HOST` | `127.0.0.1` (`npm start`) | Listening address without Docker. Keep it on loopback. |
737
- | `HOST_LABEL` | server hostname (`wherdr` in Docker) | Name of this machine in the app (can be renamed in the app). |
738
- | `APP_URL` | *(empty)* | App address and allowed host; a valid HTTPS URL enables Web Push. HTTP, empty or invalid values disable Web Push with a warning. |
932
+ | `HOST_LABEL` | server hostname | Name of this machine in the app (can be renamed in the app). In Docker the installer writes the host's short name; left empty, wherdr shows the name Tailscale gives the machine, else `wherdr`. |
933
+ | `APP_URL` | *(empty)* | App address and allowed host; a valid HTTPS URL enables Web Push. HTTP, empty or invalid values disable Web Push with a warning. With Tailscale, leave it empty: wherdr adopts the address `tailscale serve` publishes. |
934
+ | `TAILNET_REFRESH_MS` | `30000` | How often wherdr reads again what `tailscale serve` publishes for its port. |
739
935
  | `HERDR_WEB_ALLOWED_HOSTS` | *(empty)* | Additional hostnames or IP addresses allowed to open the app, comma-separated. |
740
936
  | `PASSKEY_USER` | `wherdr` | User name stored with the passkeys. |
741
937
  | `DATA_DIR` | `./data` | VAPID keys, push subscriptions, passkeys, recent folders. |
@@ -756,6 +952,92 @@ All settings are environment variables (`.env` with Docker).
756
952
  In the app, **Settings** holds per-device preferences: language, theme, notifications, terminal
757
953
  text size and renderer, typing effect, what the home screen shows, and the passkey lock.
758
954
 
955
+ **Settings › Conversation › Reply style** chooses how an agent's reply offers to be quoted in
956
+ the field, question by question and point by point (a list item or a paragraph that asks
957
+ nothing), so that one message answers them all:
958
+
959
+ | Style | A question | A point |
960
+ | --- | --- | --- |
961
+ | **Icon** (default) | A small `↳` after it, in the middle or at the end of the message. | A grey `↳` at its end: on hover on a computer, always there and pale on a phone. |
962
+ | **Tap the text** | No button: the question is underlined, tap or click it. | Click it on a computer. On a phone, tap it, then **↳ Discuss**: a single tap never quotes, so reading, scrolling and selecting text stay as they are. |
963
+ | **List under the message** | A number after it, and the questions listed under the message. | **+ Quote a point** under the message, then the point. Offered when the reply has a list item to quote, or two points or more: not under one paragraph of prose. |
964
+
965
+ Every one of these is a button reachable with the keyboard (`Tab`, then `Enter`), and selecting a
966
+ passage still offers **Reply** on it. A quoted question or point shows `✓`.
967
+
968
+ **One syntax for every reference.** Whatever you reply to, the field gets one line that names it,
969
+ and your answer goes on the lines below:
970
+
971
+ ```
972
+ ↳ Quote message 11:37 ~k3f : I can merge the PR —
973
+ Go ahead.
974
+ ↳ Quote point 11:37 ~k3f : Shall I tag the release today? —
975
+ Yes
976
+ ↳ Quote decision 11:37 #3 ~k3f : Publish the release notes on the site? —
977
+ No
978
+ ```
979
+
980
+ | Line | Written by |
981
+ | --- | --- |
982
+ | `↳ Quote message <time> ~<code> : <start of the message> —` | **Reply** under an agent message. |
983
+ | `↳ Quote point <time> ~<code> : <excerpt> —` | `↳` on a question or a point, **Reply** on a selected passage. |
984
+ | `↳ Quote decision <time> #<n> ~<code> : <question> —` | **Yes**, **No** or `↳` on a row of a decision table. |
985
+ | `↳ Quote decisions <time> #<rows> ~<code> : <questions> —` | **OK to all** on a decision table. |
986
+ | `↳ Decision: <task> —`, `↳ Problem: <task> —`, `↳ Question: <task> —`, `↳ Detail on <task> —`, `↳ Feedback on <task> —` (and their French labels) | **Answer**, **Problem**, **Question**, **Clarify** and **Comment** in the Project panel. |
987
+
988
+ - The time is the one shown on the agent's message, so an answer to an earlier message is never
989
+ read as an answer to the last one. The types are the same words in every language.
990
+ - The code after `~` is three characters wherdr derives from the message (and the rank of the
991
+ table, from the second table of a message on). It tells apart two messages of the same minute
992
+ and two tables of one message, whatever the excerpt shows. The field draws it dimmed, like
993
+ the words before the excerpt; the quote of a sent message leaves it out. The rows of **OK to
994
+ all** are the numbers the table really has: `#1-4`, or `#1,4` when rows 2 and 3 do not exist.
995
+ - A reference is a whole line: it starts with `↳` in the first column and ends with ` —` at the
996
+ end of the line, and only there, so an excerpt may hold dashes of its own. The excerpt is plain
997
+ text on one line; a long one is cut with `…` (a message keeps its start, a passage its start
998
+ and its end).
999
+ - Several references fit in one message, each followed by its answer.
1000
+ - Anything else is your own text: a `>` at the start of a line, a `↳` line of an unknown type or
1001
+ with words after its dash. Text you paste never becomes a reference either: a pasted line that
1002
+ reads like one gets a `\` before its `↳`. A long paste, shown as a **Pasted text** card, is
1003
+ sent exactly as it was pasted.
1004
+ - In the conversation, each reference of a sent message shows as a quote; tap it to see the
1005
+ message it names. Messages sent earlier with `> ` lines still show as quotes.
1006
+
1007
+ **Settings › Conversation › Quoted replies** chooses how the field shows these lines: **Tokens,
1008
+ native field** (default) draws each one as a token in the usual field, **Plain text** shows the
1009
+ same lines as text, a fallback if the tokens misbehave. The message sent is the same.
1010
+
1011
+ A **closed question**, one that yes or no answers in full, also gets **Yes** and **No** next to
1012
+ its `↳` (at the end of its row in the list style). A tap refers to the question and writes the
1013
+ answer under it, in the language of the interface, exactly as Reply and typing would; nothing is sent, so several questions can be
1014
+ answered in a row before sending. The keyboard stays down on a phone. Tapping the other answer
1015
+ replaces the first; words you typed under a quote are never replaced. The rule is cautious, and
1016
+ in doubt only Reply is offered: a single sentence that opens like a yes/no question (`Shall I…`,
1017
+ `Do you…`, `Is it…`, `Want me to…`, `Est-ce que…`, `Je lance… ?`, `On…`, `Dois-je…`) or ends
1018
+ with `, OK?` / `, d'accord ?`, with no alternative (`or`, `ou`), no interrogative word (`which`,
1019
+ `how`, `quel`, `comment`…) and no negation.
1020
+
1021
+ A **decision table**, a Markdown table whose rows are numbered questions as a coordinator writes
1022
+ them, gets a panel right under it: **OK to all** refers to the whole table and writes `OK to all`
1023
+ under it (a second tap takes it back), and each row has **Yes**, **No** and `↳`, which refer to
1024
+ the row by its number and write `Yes` or `No` under it, or nothing for an answer in words; a row
1025
+ that asks an open question has `↳` alone. Every answer names the message and the row, even for
1026
+ the last message: the agent may write another one before you send. In French: `OK tout`, `Oui`,
1027
+ `Non`. Here too everything is composed in the field and you send. A table qualifies when it has a
1028
+ number column (a header `#`, `N°`, `No`, `Num`, `ID` or `Q` over short numbers, each once) and a
1029
+ question column, known by its header (`Question`, `Decision`, `Choix`…), by cells that all end
1030
+ with `?`, or by an advice column next to it (`Avis`, `Recommendation`, `Suggestion`…). Any other
1031
+ table is left as it is.
1032
+
1033
+ Above the field, **N questions unanswered** counts the questions of the agent's last message
1034
+ (and the rows of its decision tables) that your draft does not answer yet; tap it to scroll to
1035
+ the first one. A question counts once it is quoted, a row once it is answered, quoted or covered
1036
+ by **OK to all**; when the message asks one thing only, any words you type answer it. The line
1037
+ goes when everything is answered and when you send, and it never shows for earlier messages,
1038
+ while the agent is working, or while it waits on a prompt. It hangs from the top edge of the
1039
+ field, so nothing moves when it comes or goes.
1040
+
759
1041
  ## Several machines (SSH)
760
1042
 
761
1043
  wherdr also shows the agents of the **SSH machines registered in Herdr** — the ones the Herdr
@@ -838,14 +1120,15 @@ worktree. It is optional; with it installed, wherdr adds:
838
1120
  grouped by repository (`Repo web-shop · 2 worktrees`), with their thread number and state.
839
1121
  - **Project panel** next to the coordinator (a side panel on a computer, a tab on a phone). It
840
1122
  reads the project's `TASKS.md` and shows the lists it has, always in this order, with the open
841
- threads live (state, progress, report) and resolved threads under Done:
1123
+ threads live (state, progress, report) and resolved threads under Done (**Tasks** tab; the
1124
+ **Threads**, **Inbox**, **Routines**, **Settings** and **Memory** tabs are described below):
842
1125
 
843
1126
  | List | Meaning | Buttons |
844
1127
  | --- | --- | --- |
845
1128
  | **To test** | You confirm the work | Confirm, Problem, Question |
846
1129
  | **To decide** | Only you can unblock it | Question, Answer |
847
1130
  | **To review** | Pull requests for you | Reviewed, Comment |
848
- | **In progress** | What threads are really doing | (live threads) |
1131
+ | **In progress** | What threads are really doing | (live threads) Add info |
849
1132
  | **In queue** | Decided: the coordinator launches the first one as soon as a thread slot frees | Move up / down, Launch now, Clarify, Remove from queue |
850
1133
  | **Blocked** | Waiting for someone or something external | Unblock, Clarify |
851
1134
  | **To do** | To do soon, in your priority order; never launched on its own | Move up / down, Queue it, Launch now, Clarify, Back to backlog |
@@ -856,9 +1139,76 @@ worktree. It is optional; with it installed, wherdr adds:
856
1139
  project may only need Backlog, In progress and Done); any other `##` list shows in a neutral
857
1140
  style after Backlog. French titles and common synonyms are recognized (À tester, En file,
858
1141
  À faire, Plus tard…). Every button sends a ready-made message to the coordinator, which edits
859
- `TASKS.md` (wherdr never writes it). The In queue header shows the thread slots in use
860
- (`max_parallel_threads` in `PROJECT.md`) and the next task, plus the machine's global limit
861
- when one is set (`2 of 2 thread slots in use on <machine> (all projects)`).
1142
+ `TASKS.md` (wherdr never writes it). **Add info**, on a working thread, opens a text field:
1143
+ what you type goes to the coordinator, which passes it on to that thread as is. The In queue
1144
+ header shows the thread slots in use (`max_parallel_threads` in `PROJECT.md`) and the next
1145
+ task, plus the machine's global limit when one is set
1146
+ (`2 of 2 thread slots in use on <machine> (all projects)`).
1147
+ - **Threads tab** of the Project panel: every thread of the project, resolved ones included.
1148
+ Open threads are grouped as herdr-projects groups them (Your turn, Ready for review, Working…),
1149
+ each with its machine and agent profile; resolved threads stay in their own folded section,
1150
+ shown by pages of 30. One filter covers both (ID, title, machine, profile). A tap opens the
1151
+ thread's record: state, its **Next** list, branch, dates, and these actions:
1152
+
1153
+ | Action | Command | Asks first |
1154
+ | --- | --- | --- |
1155
+ | **Acknowledge** | `thread ack`: you have seen its report | |
1156
+ | **Restart** | `thread restart`: bring back a thread whose pane is gone or whose start failed | |
1157
+ | **Stop** | `thread stop`: Escape in its agent's pane | Yes |
1158
+ | **Resolve** | `thread resolve`: final copy, then its worktree, merged branch and tab are cleaned up (report and library kept) | Yes |
1159
+
1160
+ Open agent, View report and Pull request are there too when they apply. wherdr only runs
1161
+ these `herdr-projects` commands on the coordinator's machine (and reads `thread show --json`);
1162
+ it writes no thread file itself, and herdr-projects has the last word (it refuses to restart a
1163
+ running thread, for instance): its answer is shown as is. With Docker, **Resolve** runs in
1164
+ the container, where the home folder is read-only: a thread of this machine is resolved and
1165
+ its open workspace removed through Herdr, but its merged branch, or a worktree whose workspace
1166
+ was already closed, may stay. herdr-projects lists what it kept, and
1167
+ `herdr-projects sweep <project>` on the host removes it.
1168
+ - **Inbox tab**: the project's unhandled inbox items, what herdr-projects tells the coordinator
1169
+ (a thread's new report, a routine due, a machine out of reach…), oldest first, up to 100. An
1170
+ item about a thread shows the thread's title and opens it. **Done** (✓) marks one item as
1171
+ handled with `herdr-projects inbox done`; a coordinator that has not read it yet will not see
1172
+ it any more. Summaries are shown as plain text.
1173
+ - **Routines tab**: each routine's schedule, whether it is enabled, its kind (prompt only, or a
1174
+ command and its approval) and its last and next run. Read-only: wherdr never approves,
1175
+ switches or runs a routine, and does not expose the plugin's safety settings.
1176
+ - **Settings tab**, read-only: what the project's `PROJECT.md` sets, the keys
1177
+ `herdr-projects set` changes (name, goal, coordinator and thread profiles,
1178
+ `max_parallel_threads`, `auto_resolve_days`, `nudge`, `mute`, repositories). A key the file
1179
+ leaves out shows the plugin's default, marked as such. Safety settings are not shown.
1180
+ - **Memory tab**, read-only: `MEMORY.md` (the index) and the `.md` files of `memory/`, with the
1181
+ title and hook the index gives them; a file the index does not mention is listed by name. A
1182
+ tap opens one, rendered as Markdown. The coordinator writes memory, wherdr never does.
1183
+
1184
+ With six tabs, the bar scrolls sideways on a phone and in a narrow side panel; a fade on its
1185
+ edge says there are more.
1186
+
1187
+ herdr-projects (0.2.34) has no JSON output for the inbox or the routines: wherdr reads the
1188
+ front matter of the project's `inbox/*.md` files, and the tab-separated lines of
1189
+ `herdr-projects routine list`, showing the run texts (`last … · next …`) as printed. No command
1190
+ prints a project's settings or memory either: those two tabs read the files named above.
1191
+ - **Board** (`H`, or the board button above the agent list): every project, thread, task and
1192
+ agent at a glance, in four columns. Agents outside projects show too. Each card opens what it
1193
+ stands for: a project or task opens its Project panel, a thread opens its agent (or its report
1194
+ once closed), an agent opens its conversation. A project card takes the most urgent column of
1195
+ its cards and counts them; Finished shows the 5 most recent threads and done tasks of each
1196
+ project, **Show all** the rest.
1197
+
1198
+ | Card | Blocked | Todo | Ongoing | Finished |
1199
+ | --- | --- | --- | --- | --- |
1200
+ | **Task** (its `TASKS.md` list) | Blocked | To test, To decide, To review, In queue, To do, Backlog, other lists | In progress without an open thread | Done, or a checked line |
1201
+ | **Thread** | Its agent is your turn, or *waiting on you* | *Ready for review* | Working, landing, idle | Resolved |
1202
+ | **Agent** | Your turn | | Working, starting, restarting | Done, ready |
1203
+
1204
+ Moving a **task** card (drag and drop on a computer, **Move to…** everywhere) sends the
1205
+ coordinator the Project panel's message when one says it (**Launch now** from To do or In
1206
+ queue, **Launch** from Backlog, **Unblock** from Blocked, **Tested** from To test, **Reviewed**
1207
+ from To review), otherwise `↳ Mark blocked: …`, `↳ Move to To do: …` or `↳ Mark done: …`. The
1208
+ card shows *Sent* until the coordinator has moved the line; only the allowed columns are
1209
+ offered, and To decide tasks wait for your answer. Thread, agent and project cards follow
1210
+ what they do and do not move. The server reads every project at once
1211
+ (`GET /api/board`); a project on an offline machine keeps its last read, marked as such.
862
1212
  - **Max threads per machine**, across all projects: herdr-projects only limits threads per
863
1213
  project, so three coordinators could still start a dozen threads on one small machine. Set a
864
1214
  limit per machine in **Settings → Plugins → herdr-projects** (empty `—` = no global limit;
@@ -913,17 +1263,57 @@ worktree. It is optional; with it installed, wherdr adds:
913
1263
 
914
1264
  The Project panel only works well when the coordinator follows its conventions: which lists
915
1265
  mean what, the badges, `(agent → t-NNNN)`, and what to do with each message the buttons send
916
- (`✓ Tested: …`, `✗ Problem: … — …`, `↳ Queue: …`…). They are written once, as an agent skill:
1266
+ (`✓ Tested: …`, `↳ Queue: …`, `↳ Problem: … —` or `↳ Decision: … —` with the answer below it…). They are written once, as an agent skill:
917
1267
  [`skills/wherdr/SKILL.md`](skills/wherdr/SKILL.md).
918
1268
 
919
- - **Herdr plugin**: the install links the skill for every coordinator, whatever its agent:
920
- `~/.claude/skills/wherdr` (Claude Code, or `$CLAUDE_CONFIG_DIR/skills`) and
921
- `~/.agents/skills/wherdr` (Codex, omp), each only when that agent is installed. Both point to a
922
- copy in `~/wherdr/skills/wherdr`, refreshed on each update. An existing `wherdr` skill that is
923
- not this link is left alone. Remove the links with `sh scripts/herdr-plugin.sh skill uninstall`
924
- in the plugin folder.
925
- - **Other installs**: link or copy `skills/wherdr` into your agent's skills folder yourself, or
926
- paste **Settings → Plugins → herdr-projects → Copy rules for the coordinator** to it.
1269
+ The npm package (also used by Homebrew) and Docker image include the skill from that
1270
+ release. Installation is best effort: a missing agent or an unwritable home never blocks
1271
+ wherdr. It links `~/.claude/skills/wherdr` (or `$CLAUDE_CONFIG_DIR/skills/wherdr`) only
1272
+ when the Claude configuration folder exists, and `~/.agents/skills/wherdr` only when
1273
+ `.agents`, `.codex` or `.omp` exists. Links point to a stable copy in
1274
+ `${WHERDR_DIR:-$HOME/wherdr}/skills/wherdr`. Existing skills from elsewhere are left alone.
1275
+ Relative `WHERDR_DIR` resolves from the working directory; relative `CLAUDE_CONFIG_DIR`
1276
+ resolves from HOME, including when saved in a login service. Unchanged content is not rewritten.
1277
+ A broken link is repaired only when local metadata proves wherdr created it.
1278
+
1279
+ | Install mode | Skill installation and refresh |
1280
+ | --- | --- |
1281
+ | npm / npx / Bun / pnpm | On `wherdr`, `wherdr run`, `wherdr start` or `wherdr service install`; after updating the package, the next start refreshes it. |
1282
+ | Homebrew | On `wherdr` startup, including the Homebrew service; restart the service after `brew upgrade`. |
1283
+ | One-tap update (npm, Homebrew, native plugin) | Restarting wherdr refreshes the skill from the newly installed package; rollback restores the previous copy when that package also bundles the skill. |
1284
+ | Herdr plugin, Node.js | On plugin installation / reinstallation, and on startup from its installed package. |
1285
+ | Herdr plugin, Docker | On plugin installation and Update, extracted from the pulled image on the host. |
1286
+ | One-command installer, Docker | After each pull, including reruns and `WHERDR_MODE=docker`; saves host helpers in `$WHERDR_DIR/skill-installer`. |
1287
+ | Docker by hand | Run the host-side command below after the initial pull and after each image update. |
1288
+ | From source | Use `node bin/wherdr.mjs` after building, or run `sh bin/lib/install-skill.sh` yourself after updating; `npm start` alone does not install the skill. |
1289
+
1290
+ Docker cannot write the host's agent folders through its read-only home mount. From the
1291
+ repository checkout on the **host**, after pulling your image, run:
1292
+
1293
+ ```sh
1294
+ sh bin/install-docker-skill.sh ghcr.io/afloury/wherdr:latest
1295
+ ```
1296
+
1297
+ Pass your image tag if different. The helper creates a stopped temporary container, copies
1298
+ only `/app/skills/wherdr/SKILL.md` out of it, uses the local host installer, then removes
1299
+ the container. No host home is mounted into it. Repeat this command after each manual Docker update; Docker has no one-tap
1300
+ update in the app. Manual failures (including older images without the skill) return a
1301
+ nonzero status with diagnostics; plugin and one-command installation warn and continue. Without a checkout, use
1302
+ `sh "${WHERDR_DIR:-$HOME/wherdr}/skill-installer/bin/install-docker-skill.sh" <image>` after the one-command install.
1303
+
1304
+ Remove managed links with `sh bin/lib/install-skill.sh uninstall` (or
1305
+ `sh scripts/herdr-plugin.sh skill uninstall` in the plugin folder). Uninstall leaves
1306
+ `~/.local/state/wherdr/skill-disabled`: subsequent starts and updates respect it, even if
1307
+ `WHERDR_DIR` changes. To re-enable, explicitly run `sh bin/lib/install-skill.sh install`
1308
+ or the plugin's `skill install` (or the Docker host helper above). `WHERDR_SKILL=off`
1309
+ disables all installation and refresh, including explicit install; export it for interactive
1310
+ starts or set it when installing the login service to preserve it there.
1311
+
1312
+ Two limitations remain: rollback to an older package without the helper (such as 1.3.2)
1313
+ leaves the failed release's skill in place. With multiple wherdr versions sharing an account,
1314
+ the last one started supplies the skill; there is no version comparison.
1315
+
1316
+ You can also paste **Settings → Plugins → herdr-projects → Copy rules for the coordinator** to your agent.
927
1317
 
928
1318
  ## Herdr plugins
929
1319
 
@@ -969,7 +1359,9 @@ container talks to the Herdr server running on the host.
969
1359
  (port forwarding, public reverse proxy, ngrok…); the only exception is a Cloudflare Tunnel
970
1360
  behind Cloudflare Access, with its risks.
971
1361
  - Requests are accepted only for `localhost`, loopback addresses, the runtime's hostname, the
972
- hostname in `APP_URL`, and the runtime's network interface addresses (those of the container
1362
+ hostname in `APP_URL`, the tailnet name `tailscale serve` publishes for wherdr's own port
1363
+ (read from Tailscale on the machine, never from a request, and only while it is published),
1364
+ and the runtime's network interface addresses (those of the container
973
1365
  with Docker). Add other private names or IP addresses to `HERDR_WEB_ALLOWED_HOSTS`
974
1366
  (comma-separated) before using them. An unlisted name returns HTTP 403 (`host` / "Host not
975
1367
  allowed"); add it to `HERDR_WEB_ALLOWED_HOSTS`, then restart wherdr. This host check also
@@ -1017,8 +1409,10 @@ See [SECURITY.md](SECURITY.md) to report a vulnerability.
1017
1409
  (`herdr integration install claude|codex|omp`) and start a new agent.
1018
1410
  - **Passkeys or notifications unavailable**: the page must be served over HTTPS (or
1019
1411
  `localhost`). On iPhone, notifications need the app installed on the home screen.
1020
- - **HTTP 403 when opening the app through another name**: add that private hostname or IP
1021
- address to `HERDR_WEB_ALLOWED_HOSTS`, or use the hostname in `APP_URL`, then restart wherdr.
1412
+ - **HTTP 403 "This address isn't enabled yet"**: for a tailnet address, run the command the
1413
+ page gives on the machine: it tells whether `tailscale serve` publishes wherdr's port there.
1414
+ For another private name, add the hostname or IP address to `HERDR_WEB_ALLOWED_HOSTS`, or use
1415
+ the hostname in `APP_URL`, then restart wherdr.
1022
1416
  - **Remote machine offline**: `docker compose logs | grep machine` shows its state. Test SSH as
1023
1417
  wherdr does, from the container:
1024
1418
  `docker compose exec herdr-web ssh -o BatchMode=yes laptop '~/.local/bin/herdr status server'`.