@gavin-hu/my-pi-agent 0.18.0 → 0.19.0

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 (249) hide show
  1. package/README.md +52 -44
  2. package/README.zh-CN.md +46 -41
  3. package/docs/extensions/README.md +34 -0
  4. package/docs/extensions/board.md +344 -0
  5. package/{extensions/bytefly/README.md → docs/extensions/bytefly.md} +446 -404
  6. package/{extensions/check/README.md → docs/extensions/check.md} +83 -68
  7. package/{extensions/context/README.md → docs/extensions/context.md} +40 -23
  8. package/docs/extensions/file-browse.md +554 -0
  9. package/{extensions/file-open/README.md → docs/extensions/file-open.md} +31 -23
  10. package/{extensions/file-read/README.md → docs/extensions/file-read.md} +47 -55
  11. package/{extensions/goal/README.md → docs/extensions/goal.md} +115 -131
  12. package/{extensions/job/README.md → docs/extensions/job.md} +61 -50
  13. package/{extensions/memory/README.md → docs/extensions/memory.md} +71 -60
  14. package/{extensions/plan/README.md → docs/extensions/plan.md} +390 -317
  15. package/{extensions/question/README.md → docs/extensions/question.md} +54 -31
  16. package/{extensions/rewind/README.md → docs/extensions/rewind.md} +115 -122
  17. package/docs/extensions/session-bus.md +285 -0
  18. package/{extensions/subagent/README.md → docs/extensions/subagent.md} +165 -118
  19. package/docs/extensions/team.md +226 -0
  20. package/{extensions/todo/README.md → docs/extensions/todo.md} +85 -88
  21. package/{extensions/watch/README.md → docs/extensions/watch.md} +54 -36
  22. package/{extensions/web-access/README.md → docs/extensions/web-access.md} +93 -50
  23. package/{extensions/wechat/README.md → docs/extensions/wechat.md} +258 -162
  24. package/{extensions/worktree/README.md → docs/extensions/worktree.md} +115 -72
  25. package/extensions/board/commands.ts +87 -0
  26. package/extensions/board/config.ts +36 -0
  27. package/extensions/board/format.ts +72 -0
  28. package/extensions/board/index.ts +40 -0
  29. package/extensions/board/input.ts +54 -0
  30. package/extensions/board/join.ts +42 -0
  31. package/extensions/board/page.ts +312 -0
  32. package/extensions/board/result.ts +99 -0
  33. package/extensions/board/rows.ts +48 -0
  34. package/extensions/board/schema.ts +75 -0
  35. package/extensions/board/seat.ts +29 -0
  36. package/extensions/board/snapshot.ts +41 -0
  37. package/extensions/board/tools.ts +279 -0
  38. package/extensions/board/transport/router.ts +172 -0
  39. package/extensions/board/transport/server.ts +149 -0
  40. package/extensions/board/types.ts +54 -0
  41. package/extensions/board/view/actions.ts +306 -0
  42. package/extensions/board/view/client.ts +41 -0
  43. package/extensions/board/view/keys.ts +121 -0
  44. package/extensions/board/view/live.ts +397 -0
  45. package/extensions/board/view/page.ts +158 -0
  46. package/extensions/board/view/render.ts +496 -0
  47. package/extensions/board/view/styles.ts +211 -0
  48. package/extensions/board/view/team.ts +305 -0
  49. package/extensions/board/writes.ts +99 -0
  50. package/extensions/bytefly/panel/commands.ts +40 -7
  51. package/extensions/bytefly/panel/settings-runtime.ts +0 -6
  52. package/extensions/bytefly/panel/settings.ts +3 -20
  53. package/extensions/bytefly/panel/tui/settings-view.ts +1 -1
  54. package/extensions/bytefly/picker/discovery.ts +0 -2
  55. package/extensions/bytefly/picker/types.ts +0 -2
  56. package/extensions/bytefly/update/check.ts +39 -0
  57. package/extensions/bytefly/update/format.ts +15 -0
  58. package/extensions/bytefly/update/register.ts +73 -34
  59. package/extensions/bytefly/update/state.ts +6 -3
  60. package/extensions/context/tui.ts +1 -1
  61. package/extensions/file-browse/{config.ts → config/config.ts} +1 -1
  62. package/extensions/file-browse/{files.ts → files/files.ts} +1 -14
  63. package/extensions/file-browse/{paths.ts → files/paths.ts} +1 -1
  64. package/extensions/file-browse/{git.ts → git/git.ts} +10 -3
  65. package/extensions/file-browse/index.ts +4 -4
  66. package/extensions/file-browse/{live.ts → live/live.ts} +20 -69
  67. package/extensions/file-browse/{router.ts → transport/router.ts} +21 -15
  68. package/extensions/file-browse/transport/server.ts +110 -0
  69. package/extensions/file-browse/{chrome.ts → view/chrome.ts} +4 -4
  70. package/extensions/file-browse/{client.ts → view/client.ts} +1 -1
  71. package/extensions/file-browse/{diff-html.ts → view/diff-html.ts} +3 -3
  72. package/extensions/file-browse/{html.ts → view/html.ts} +14 -12
  73. package/extensions/file-browse/{icons.ts → view/icons.ts} +1 -1
  74. package/extensions/file-browse/{markdown.ts → view/markdown.ts} +7 -7
  75. package/extensions/file-browse/{raw-html.ts → view/raw-html.ts} +1 -1
  76. package/extensions/file-browse/{styles.ts → view/styles.ts} +29 -5
  77. package/extensions/file-browse/{tree.ts → view/tree.ts} +1 -1
  78. package/extensions/file-browse/{urls.ts → view/urls.ts} +1 -1
  79. package/extensions/file-read/schema.ts +2 -1
  80. package/extensions/memory/store.ts +12 -5
  81. package/extensions/plan/blocks.ts +23 -2
  82. package/extensions/plan/commands.ts +21 -16
  83. package/extensions/plan/feedback.ts +147 -38
  84. package/extensions/plan/index.ts +6 -4
  85. package/extensions/plan/plan-index.ts +43 -9
  86. package/extensions/plan/plan-text.ts +33 -23
  87. package/extensions/plan/plans.ts +8 -11
  88. package/extensions/plan/policy.ts +20 -12
  89. package/extensions/plan/review.ts +3 -27
  90. package/extensions/plan/runtime.ts +21 -9
  91. package/extensions/plan/sections.ts +112 -0
  92. package/extensions/plan/steps.ts +3 -22
  93. package/extensions/plan/tools/exit.ts +11 -95
  94. package/extensions/plan/tui.ts +5 -34
  95. package/extensions/plan/types.ts +0 -8
  96. package/extensions/plan/web-client.ts +1 -1
  97. package/extensions/plan/web-page.ts +0 -7
  98. package/extensions/plan/web-router.ts +3 -57
  99. package/extensions/plan/web-share-session.ts +24 -30
  100. package/extensions/plan/web-streams.ts +14 -14
  101. package/extensions/plan/web-tokens.ts +2 -2
  102. package/extensions/plan/web.ts +66 -120
  103. package/extensions/rewind/{commands.ts → commands/commands.ts} +5 -5
  104. package/extensions/rewind/{review.ts → commands/review.ts} +13 -13
  105. package/extensions/rewind/{rewind.ts → commands/rewind.ts} +6 -6
  106. package/extensions/rewind/{config.ts → config/config.ts} +1 -1
  107. package/extensions/rewind/index.ts +3 -3
  108. package/extensions/rewind/{policy.ts → state/policy.ts} +4 -4
  109. package/extensions/rewind/{runtime.ts → state/runtime.ts} +11 -11
  110. package/extensions/rewind/{snapshot-run.ts → state/snapshot-run.ts} +1 -1
  111. package/extensions/rewind/{timeline.ts → state/timeline.ts} +2 -2
  112. package/extensions/rewind/{format.ts → text/format.ts} +3 -3
  113. package/extensions/rewind/{git.ts → transport/git.ts} +2 -2
  114. package/extensions/rewind/{restore.ts → transport/restore.ts} +2 -2
  115. package/extensions/rewind/{snapshot.ts → transport/snapshot.ts} +1 -1
  116. package/extensions/rewind/{store.ts → transport/store.ts} +1 -1
  117. package/extensions/rewind/{checklist.ts → view/checklist.ts} +6 -6
  118. package/extensions/rewind/{hunk-picker.ts → view/hunk-picker.ts} +8 -8
  119. package/extensions/rewind/{path-picker.ts → view/path-picker.ts} +7 -7
  120. package/extensions/rewind/{review-tui.ts → view/review-tui.ts} +11 -11
  121. package/extensions/rewind/{rewind-tui.ts → view/rewind-tui.ts} +17 -11
  122. package/extensions/rewind/{status.ts → view/status.ts} +4 -4
  123. package/extensions/session-bus/commands.ts +17 -69
  124. package/extensions/session-bus/format.ts +22 -45
  125. package/extensions/session-bus/index.ts +13 -7
  126. package/extensions/session-bus/join-request.ts +40 -0
  127. package/extensions/session-bus/mail-watch.ts +134 -0
  128. package/extensions/session-bus/notice.ts +32 -4
  129. package/{lib/claims → extensions/session-bus}/overlap.ts +11 -18
  130. package/extensions/session-bus/protocol.ts +19 -49
  131. package/extensions/session-bus/resolve.ts +67 -0
  132. package/extensions/session-bus/result.ts +16 -51
  133. package/extensions/session-bus/rows.ts +4 -2
  134. package/extensions/session-bus/runtime.ts +129 -238
  135. package/extensions/session-bus/store.ts +42 -6
  136. package/extensions/session-bus/tools.ts +31 -66
  137. package/extensions/session-bus/tui.ts +4 -4
  138. package/extensions/session-bus/types.ts +13 -13
  139. package/extensions/subagent/agents.ts +9 -8
  140. package/extensions/subagent/invocation.ts +4 -2
  141. package/extensions/team/commands.ts +112 -0
  142. package/extensions/team/dispatch.ts +133 -0
  143. package/extensions/team/format.ts +105 -0
  144. package/extensions/team/index.ts +33 -0
  145. package/extensions/team/schema.ts +29 -0
  146. package/extensions/team/tools.ts +246 -0
  147. package/extensions/team/types.ts +48 -0
  148. package/extensions/web-access/fetch/page.ts +4 -1
  149. package/extensions/web-access/fetch/structured.ts +447 -0
  150. package/extensions/web-access/fetch/tool.ts +2 -1
  151. package/extensions/web-access/search/tool.ts +2 -1
  152. package/extensions/wechat/bridge.ts +76 -92
  153. package/extensions/wechat/{commands.ts → commands/commands.ts} +26 -18
  154. package/extensions/wechat/{doctor.ts → commands/doctor.ts} +5 -5
  155. package/extensions/wechat/{history.ts → commands/history.ts} +1 -1
  156. package/extensions/wechat/{login.ts → commands/login.ts} +3 -3
  157. package/extensions/wechat/{slash.ts → commands/slash.ts} +38 -2
  158. package/extensions/wechat/dialog/channels.ts +136 -0
  159. package/extensions/wechat/{interaction.ts → dialog/interaction.ts} +37 -37
  160. package/extensions/wechat/{prompt.ts → dialog/prompt.ts} +2 -2
  161. package/extensions/wechat/{remote-ui.ts → dialog/remote-ui.ts} +1 -1
  162. package/extensions/wechat/index.ts +10 -10
  163. package/extensions/wechat/{admission.ts → peer/admission.ts} +2 -2
  164. package/extensions/wechat/{peer-session.ts → peer/peer-session.ts} +2 -2
  165. package/extensions/wechat/{peer-worker.ts → peer/peer-worker.ts} +100 -22
  166. package/extensions/wechat/{presence.ts → peer/presence.ts} +8 -7
  167. package/extensions/wechat/{reply-pipeline.ts → peer/reply-pipeline.ts} +1 -1
  168. package/extensions/wechat/{credentials.ts → state/credentials.ts} +2 -2
  169. package/extensions/wechat/{state.ts → state/state.ts} +14 -2
  170. package/extensions/wechat/{format.ts → text/format.ts} +3 -3
  171. package/extensions/wechat/{notices.ts → text/notices.ts} +8 -0
  172. package/extensions/wechat/tools.ts +29 -12
  173. package/extensions/wechat/{client.ts → transport/client.ts} +26 -2
  174. package/extensions/wechat/{deliver.ts → transport/deliver.ts} +72 -28
  175. package/extensions/wechat/{inbound.ts → transport/inbound.ts} +5 -5
  176. package/extensions/wechat/{media.ts → transport/media.ts} +11 -5
  177. package/extensions/wechat/transport/outbound.ts +142 -0
  178. package/extensions/wechat/{poll.ts → transport/poll.ts} +3 -3
  179. package/extensions/wechat/types.ts +10 -1
  180. package/extensions/wechat/{login-screen.ts → view/login-screen.ts} +4 -4
  181. package/extensions/wechat/{status-screen.ts → view/status-screen.ts} +31 -26
  182. package/extensions/worktree/guard.ts +3 -5
  183. package/lib/README.md +8 -6
  184. package/lib/board/card.ts +290 -0
  185. package/lib/board/links.ts +117 -0
  186. package/lib/board/page-pointer.ts +256 -0
  187. package/lib/board/round-text.ts +72 -0
  188. package/lib/board/round.ts +89 -0
  189. package/lib/board/rules.ts +304 -0
  190. package/lib/board/session.ts +105 -0
  191. package/lib/board/store.ts +310 -0
  192. package/lib/board/view.ts +150 -0
  193. package/lib/errors.ts +12 -4
  194. package/lib/format.ts +13 -0
  195. package/lib/http/guard.ts +112 -0
  196. package/lib/http/index.ts +16 -5
  197. package/lib/http/serve.ts +234 -0
  198. package/lib/http/server.ts +12 -10
  199. package/lib/http/sink.ts +110 -0
  200. package/lib/http/token.ts +38 -0
  201. package/lib/policy.ts +12 -2
  202. package/lib/requirements.ts +6 -7
  203. package/lib/session-label.ts +64 -0
  204. package/lib/session-roster.ts +130 -0
  205. package/lib/session-tool.ts +184 -0
  206. package/lib/tool-names.ts +25 -2
  207. package/lib/tui/glyphs.ts +2 -0
  208. package/lib/web/README.md +5 -2
  209. package/lib/web/components.ts +5 -0
  210. package/lib/web/runtime.ts +7 -1
  211. package/lib/web/tokens.ts +25 -2
  212. package/package.json +26 -14
  213. package/skills/next/SKILL.md +4 -3
  214. package/skills/project-docs/SKILL.md +133 -0
  215. package/skills/project-docs/references/audit.md +131 -0
  216. package/skills/project-docs/references/changelog.md +105 -0
  217. package/skills/project-docs/references/discovery.md +127 -0
  218. package/skills/project-docs/references/readme.md +80 -0
  219. package/skills/project-docs/references/release.md +84 -0
  220. package/skills/project-docs/references/roadmap.md +93 -0
  221. package/skills/project-docs/scripts/state.ts +316 -0
  222. package/skills/refactor/SKILL.md +100 -125
  223. package/skills/refactor/references/handoff.md +35 -0
  224. package/skills/refactor/references/signals.md +54 -0
  225. package/skills/refactor/scripts/inventory.ts +587 -0
  226. package/extensions/file-browse/README.md +0 -506
  227. package/extensions/file-browse/server.ts +0 -141
  228. package/extensions/plan/split.ts +0 -87
  229. package/extensions/session-bus/README.md +0 -247
  230. package/extensions/session-bus/team.ts +0 -102
  231. package/extensions/wechat/outbound.ts +0 -100
  232. package/lib/plans/sections.ts +0 -172
  233. package/lib/plans/setting.ts +0 -52
  234. package/lib/plans/split.ts +0 -215
  235. package/lib/plans/store.ts +0 -19
  236. package/lib/plans/team.ts +0 -151
  237. package/skills/changelog/SKILL.md +0 -148
  238. package/skills/parallel-dev/SKILL.md +0 -150
  239. /package/extensions/file-browse/{rev.ts → live/rev.ts} +0 -0
  240. /package/extensions/file-browse/{diff.ts → text/diff.ts} +0 -0
  241. /package/extensions/file-browse/{fuzzy.ts → text/fuzzy.ts} +0 -0
  242. /package/extensions/file-browse/{live-client.ts → view/live-client.ts} +0 -0
  243. /package/extensions/file-browse/{palette.ts → view/palette.ts} +0 -0
  244. /package/extensions/rewind/{patch.ts → text/patch.ts} +0 -0
  245. /package/extensions/rewind/{paths.ts → text/paths.ts} +0 -0
  246. /package/extensions/rewind/{queue.ts → transport/queue.ts} +0 -0
  247. /package/extensions/rewind/{temp-index.ts → transport/temp-index.ts} +0 -0
  248. /package/extensions/wechat/{lock.ts → state/lock.ts} +0 -0
  249. /package/extensions/wechat/{channel.ts → text/channel.ts} +0 -0
@@ -0,0 +1,226 @@
1
+ # team — the lead's round over the board
2
+
3
+ `board` owns the cards. `team` owns the lead's three actions: `assign` hands a card to a
4
+ session that is **already running**, `offer` hands one to whoever is free, and `reclaim`
5
+ takes one back when its owner has gone quiet. The first two are a write on the board and a
6
+ message on the session bus; `reclaim` writes the card alone, so it needs no bus at all. The
7
+ report says what the peer will actually do with it.
8
+
9
+ Nothing starts a process. A message lands in the peer's inbox and is reported at its next
10
+ turn, so `assign` and `offer` are messages, not launches.
11
+
12
+ <img src="../../assets/screens/team.png" alt="The Team tab as a dashboard: on the left, a Needs a decision queue of rows with a coloured kind edge, the reason on its own line, and a Reclaim or Claim action on the row edge, or no action on a card that waits; on the right, a Teammates list with a state dot, label, you chip, mail badge, a not listening chip on a peer whose watch is off, and a line of what each holds; and one muted Done line under both" width="100%">
13
+
14
+ *The Team tab: the cards that need the lead, then who is on the team — including an
15
+ owner whose label nothing answers to, and a teammate that is not listening.*
16
+
17
+ ## Capabilities
18
+
19
+ | Capability | How | What you get |
20
+ |---|---|---|
21
+ | Join the team | `/board` as the lead, `/team join <label>` as a teammate, or any card you work | The session is on the team under a label peers address, listening, and `/team` and the Team tab list it |
22
+ | Hand a feature to a running session | `team` action `assign` | The card is `doing`, owned by that session, and a handoff is in its inbox |
23
+ | Tell the peer what to do first | `team` action `assign` with `note` | The handoff carries the card, its paths, its plan, and that line |
24
+ | Hand a feature to whoever is free | `team` action `offer` | Every other live session gets an event naming the card; the card stays unowned until one claims it |
25
+ | Know whether a message will be woken for | an `assign` or `offer` result, `/team`, and the Team tab | All three name the peers whose record says their next turn is when it lands: the result as `Not listening: <labels>`, `/team` as a lowercase `not listening:` inside its presence line, the Team tab as a chip |
26
+ | Take a card back | `team` action `reclaim` | The owner clears, the card returns to To do, and its notes stay |
27
+ | Keep the round to the team | `team` | Only `assign`, `offer`, and `reclaim`: no card is created, moved, planned, or finished here — that is `board`'s surface |
28
+ | See the round without a browser | `/team` | `buildRound`'s summary — the one the page's Team tab is served — laid out as text by `formatRound`: what needs a decision, who holds what, then one presence line for who is live, who is not listening, and who has gone quiet. Ten lines at most, then `… N more`; an empty board is one line |
29
+
30
+ ## Quickstart
31
+
32
+ ```
33
+ lead: /board → on the team as team-lead, listening
34
+ peer: /team join billing-core → on the team as billing-core, listening
35
+ lead: team({action:"assign", card:"4", to:"billing-core", note:"start with the webhook"})
36
+ → #4 handed off to billing-core.
37
+ peer: board({action:"plan", card:"4", path:"…"}) → the card records its plan
38
+ peer: board({action:"note", card:"4", text:"…"}) → a phase, as it happens
39
+ later: team({action:"reclaim", card:"4"}) → #4 reclaimed from billing-core
40
+ ```
41
+
42
+ Work nobody has taken instead of work you are routing:
43
+
44
+ ```
45
+ lead: team({action:"offer", card:"5", note:"whoever is free"})
46
+ → #5 offered to billing-core, billing-web.
47
+ peer: board({action:"claim", card:"5"}) → the offer becomes work
48
+ ```
49
+
50
+ ## Joining the team
51
+
52
+ You do not configure a team; there are no members to add. The team is the session bus,
53
+ the local coordination channel between Pi sessions on this repository, and staying off
54
+ its surface is the point: `board` and `team` join this session for you.
55
+
56
+ | You are | Action | What happens |
57
+ |---|---|---|
58
+ | The lead | `/board` | The session joins as `team-lead` — the page's own seat name — and listens, so a handoff or an offer is reported at its next turn |
59
+ | A teammate | `/team join billing-core` | The session joins as `billing-core`, and Pi's footer and `/resume` show that name |
60
+ | Either | any `board` action that writes | The first one joins the session under the name it already has, and listens |
61
+ | A viewer | `/board` on a session that is not serving | The page is somebody else's to serve, so this session joins under its own label instead of the seat |
62
+
63
+ Joining is listening: mail is reported at that session's next turn, and **no turn is
64
+ raised** for it, so being on the team never spends a turn. `/team` says who is live and
65
+ who is live but not listening. `/board status` repeats only what the last `/board` in this
66
+ session did about the team — a join made by `/team join` or by a card write is not on that
67
+ line, and a page that is not running prints no team clause at all.
68
+
69
+ **The seat is not a session.** The page writes as `team-lead` with an id of its own, so a
70
+ card it claimed comes back with `reclaim` rather than a `release` from the terminal. The
71
+ label is shared with the session that serves the page; the ids are not.
72
+
73
+ ## Tool
74
+
75
+ `team` is one action-shaped tool. `readOnlyHint: false`, `destructiveHint: true`,
76
+ `openWorldHint: false`, `executionMode: "sequential"`.
77
+
78
+ | Action | Parameters | Behaviour |
79
+ |---|---|---|
80
+ | `assign` | `card`, `to`, `note?` | Checks the card's preconditions, sends the handoff as a `handoff` message, then records the owner |
81
+ | `offer` | `card`, `note?` | Checks the preconditions a claim would meet, broadcasts the card as an `event` to every other live session, and writes nothing |
82
+ | `reclaim` | `card` | Clears the owner and returns the card to To do, keeping its notes and plan |
83
+
84
+ `card` takes `4` or `#4`. `to` is a teammate's label or id, as `/team` lists them — a
85
+ label nobody answers to cannot be handed a card, and the result says so.
86
+
87
+ ### The order is the point
88
+
89
+ `assign` sends **before** it assigns, and each failure leaves a state the report names:
90
+
91
+ | What failed | What the card is | What the lead reads |
92
+ |---|---|---|
93
+ | the card is missing, `done`, held by a third session, or waiting on unfinished work | untouched | the refusal, with no message sent at all |
94
+ | the send | untouched — `todo`, no owner | `#4 was not handed off to billing-core: …`, plus `The card is unchanged.` |
95
+ | the assignment, after a successful send | whatever the other writer left | `#4 was handed off to billing-core, but the card changed before it could be assigned: …`, and the peer is told to `board claim` it |
96
+
97
+ `offer` has no order to get wrong, because it writes nothing: a refused broadcast leaves
98
+ the card exactly as the rules just read it, and the first `board claim` is what turns an
99
+ offer into work. Two peers claiming at once are settled by the store's compare-and-set —
100
+ one claim wins, and the other is refused with the owner named.
101
+
102
+ `delivered` in the details means a message file was written for that label — not that the
103
+ peer read it. `notListening` names the peers it reached whose record says they are not
104
+ listening, so the message waits for their next turn.
105
+
106
+ The owner `assign` records is the **session**, not just its name: before writing the card
107
+ it reads the team list and keeps the id of the session behind that label. Ownership is
108
+ compared by id first (`lib/board/rules.ts`), so a peer that renames itself still holds its
109
+ card; a card assigned by label alone — an older one, or one whose session the bus could
110
+ not name — goes back to `reclaim` if its holder renames.
111
+
112
+ ### What the peer does on waking
113
+
114
+ A handoff is an instruction, not a notification: the peer's notice is headlined
115
+ `1 handoff`, carries enough of the body to be actionable, and says the work is theirs —
116
+ read it with `session inbox`, record the plan, and start. The card is already theirs
117
+ (`assign` wrote the owner), so there is no claim step; the peer plans, works, notes, and
118
+ moves it to `done` or `blocked`.
119
+
120
+ An offer is advice: nothing is theirs until they claim it, and a card nobody claims stays
121
+ `todo`, so it keeps showing up as unassigned in the round.
122
+
123
+ ## Security
124
+
125
+ - The tool writes the board store and calls one other extension's tool (`session`); it
126
+ starts no process, opens no socket, and reads no credential.
127
+ - A handoff or an offer is a message to other local sessions, so it carries nothing the
128
+ board would not show: the card's title, paths, plan, and the lead's note. An offer names
129
+ every session on the repository as a recipient, so it says nothing it would not say to
130
+ each of them.
131
+
132
+ ## Limitations
133
+
134
+ - **Nothing here can start a peer's turn.** A message is reported at the recipient's next
135
+ turn; a session that is not listening at all reads it only when it reads its inbox.
136
+ - **`to` must be on the team.** A session that never opened the board, joined, or worked a
137
+ card cannot be handed a card, and an offer to nobody says so.
138
+ - **`team-lead` is a label, not a person.** The seat is not a session, so its actor id
139
+ `board:team-lead` answers no message; the label is the one the session serving the page
140
+ joins under, so a handoff to `team-lead` reaches whoever is serving the page just now —
141
+ and is refused, like any other label, when nobody is. The seat's cards come back with
142
+ `reclaim`.
143
+ - **A label-only owner is fragile.** If the bus could not name the session behind `to`,
144
+ the card records the label, and a rename would need a `reclaim` and a fresh handoff.
145
+ - **An offer is advice.** A card can sit offered indefinitely; the round keeps counting it
146
+ as unassigned, which is the honest signal that nobody took it.
147
+ - **An offer arrives as an `event`.** A burst that mixes one with ordinary messages is
148
+ headlined as new messages rather than named as an offer.
149
+ - **Liveness is a heartbeat; listening is a field.** A running session rewrites its record
150
+ every ten seconds and the record is believed for ten minutes, so `quiet:` means the
151
+ record went stale rather than that the peer died, while `not listening` is the separate
152
+ `watching` field, written when the session joins or arms and disarms its watch.
153
+ - **Plan mode makes this unavailable.** `session` is a mutating tool, so while plan mode is
154
+ active the bus call is refused. Assign after the plan is approved.
155
+ - **Ownership is advisory.** `assign` does not create a path claim; the peer still claims
156
+ the paths it edits through `session`.
157
+
158
+ ## Pi integration
159
+
160
+ - **`board`**: the same store, the same cards, the same rules — `team` calls `lib/board/`
161
+ directly, so a card it hands over is one `board` can see. Either extension works without
162
+ the other: without `team` the page still reclaims, and without `board` there is nothing
163
+ to assign.
164
+ - **`session-bus`**: the transport, and the one extension `team` talks to — through
165
+ `lib/session-tool.ts`, whose `join` is an in-process request (a command context cannot
166
+ call a tool) and whose `peers` and `send` are nested tool calls. The bus's actions and
167
+ labels never appear on this extension's user surface — `team` and `board` are the
168
+ surface — but its name does, in the sentence an unloaded bus produces: `team: not on the
169
+ team — the session bus is not loaded`. Every call returns that sentence instead of
170
+ throwing, which is the "card untouched" row above.
171
+ - **`plan`**: the handoff names the plan file when one is recorded, so the peer knows
172
+ whether it is executing a plan or writing one.
173
+ - **`subagent`**: a child process is a different Pi session; hand a card to a peer
174
+ session, not to a child you started for one question.
175
+
176
+ ## Design notes
177
+
178
+ - **Two extensions, not one tool with a flag.** `board` is the work list any session uses
179
+ and must work with the bus disabled; `team` is the lead's round and is the only thing
180
+ that needs the bus. If the round ever stops needing it, `team` folds back in.
181
+ - **Joining is the bus's, wearing `board`'s clothes.** A lead should not have to learn a
182
+ second tool to be on a team, so `/board` joins, a card write joins, and `/team join` is
183
+ the one naming action. `wake` stays a bus-level choice and is never armed on a user's
184
+ behalf, because it spends turns.
185
+ - **Push and pull are two verbs.** `assign` writes the owner and sends one message;
186
+ `offer` broadcasts and writes nothing. One verb with an optional `to` would have two
187
+ outcomes and one details shape, and a reader could not tell which happened.
188
+ - **The offer's preconditions are a dry-run claim.** `applyOp` judges what a peer is about
189
+ to do, so a card nobody may claim is refused before anything is sent, and the refusal is
190
+ the rules' own sentence rather than a second copy.
191
+ - **Listening is a record field.** The bus writes it when a session joins or stops
192
+ listening, so the report, `/team`, and the Team tab read one fact from disk instead of
193
+ guessing from the send.
194
+ - **Send first.** A card assigned without a message is a card nobody knows about, while a
195
+ message for a card that then changed is one the peer can still claim.
196
+ - **`reclaim` is here, and in `board`.** It is the compensation for a dispatch that did not
197
+ land, so it belongs in the lead's context — and it is the same operation `board` and its
198
+ page offer, because a card whose owner went away must not need a second extension to come
199
+ back.
200
+ - **Rejected: `reclaim` messaging the previous owner.** A compensation path must not depend
201
+ on a second delivery — the reclaim is what you do when the owner is gone, so its message
202
+ would fail exactly when it is needed. The card is the record.
203
+ - **Rejected: a fifth message kind for an offer.** An offer is something that happened on
204
+ the board, which is what `event` already means; a new kind would change the protocol, the
205
+ notice, and every reader for one verb.
206
+
207
+ ## Files
208
+
209
+ | File | Responsibility |
210
+ |---|---|
211
+ | `index.ts` | The factory: gate on `PI_DISABLED_EXTENSIONS`, build the session, register the tool and the command |
212
+ | `schema.ts` | The tool's three actions and their parameters |
213
+ | `tools.ts` | The dispatch order, the refusals, and the report |
214
+ | `dispatch.ts` | The handoff and offer text, the bus behind a seam, and who is not listening |
215
+ | `commands.ts` | `/team` — the round, and `/team join [<label>]` |
216
+ | `format.ts` | The model-facing text for a handoff, an offer, a failure, a race, and a reclaim |
217
+ | `types.ts` | The tool's name, actions, and details |
218
+
219
+ The store, the rules, and the session identity are `lib/board/`; the join, the peer list,
220
+ and the message send are `lib/session-tool.ts` — see [`lib/README.md`](../../lib/README.md).
221
+
222
+ ## Testing
223
+
224
+ `test/suite-manifest.ts` declares `team/{commands,dispatch,tools,extension}`: the handoff
225
+ and offer text, the bus's answers behind the seam, the assign/offer/reclaim ordering
226
+ against a recording dispatch, `/team` and `/team join`, and the factory.
@@ -1,92 +1,63 @@
1
1
  # todo — a TodoWrite-style task list for Pi
2
2
 
3
- A whole-list task tracker for Pi, modelled on Claude Code's `TodoWrite`. The
4
- model sends the complete list on every call; the previous list is discarded, so
5
- there are no ids to juggle and no add/toggle bookkeeping. The list follows the
6
- active session branch and is restored on `/resume` and `/tree`. When a run is
7
- about to end after mutating work without a todo update, the extension injects
8
- one short reminder and forces a continuation, so the list does not silently
9
- drift behind the work.
10
-
11
- <img src="../../assets/todo.png" alt="The /todos screen: a Todos title rule, a 3/7 completed summary, and the list with check, spinner, and circle glyphs" width="100%">
12
-
13
- ```
3
+ A whole-list task tracker for Pi, modelled on Claude Code's `TodoWrite`: the
4
+ model sends the complete list on every call, so there are no ids to juggle and
5
+ no add/toggle bookkeeping. The list follows the active session branch and is
6
+ rebuilt on `/resume` and `/tree`, and the extension keeps it honest by asking for
7
+ one update when work lands without one.
8
+
9
+ <img src="../../assets/screens/todo.png" alt="The /todos screen: a Todos title rule, a 3/7 completed summary, and the list with check, spinner, and circle glyphs" width="100%">
10
+
11
+ ## Capabilities
12
+
13
+ Status records what a reader can rely on: **Shipped** is in a tagged release, **Unreleased** is complete in the current tree but not yet tagged, and **Partial** has one released part and one unreleased part. Rows are ordered by user value, highest first.
14
+
15
+ | Capability | How | What you get | Status |
16
+ |---|---|---|---|
17
+ | Whole-list tracking | `todo` | One call replaces the list; an empty list clears it. | Shipped |
18
+ | Live progress rail | the `todo` widget | Progress and the active item, one line above the editor. | Shipped |
19
+ | Atomic validation | `todo` validation | At most one `in_progress`, at most 50 items, unique non-empty content, and a known status; a violation returns an error with the unchanged list. | Shipped |
20
+ | Terminal-safe normalization | sanitized `content` and `activeForm` | Control characters, newlines, tabs, and repeated spaces collapse to one terminal-safe line before the list is stored. | Shipped |
21
+ | Compact model result | the `todo` result | The model gets progress and the active item, not an echo of the whole list, so every update does not repeat the table. | Shipped |
22
+ | Transcript rendering | the `todo` transcript block | The call line carries progress; the collapsed rows lead with active work, the expanded rows keep submission order, and a cap ends with `… N more`. | Shipped |
23
+ | Full list on demand | `/todos` | A scrollable screen in the order the model submitted. | Shipped |
24
+ | Headless listing | `/todos` outside `tui` | RPC, print, and JSON modes get the same list as a notification, with no terminal screen. | Shipped |
25
+ | Branch-following state | tool-result `details` | The list survives `/resume` and `/tree`, and abandoned branches never leak in. | Shipped |
26
+ | Lag reminder | the `agent_before_settle` nudge | One hidden reminder and one forced continuation when mutating work lands without a todo update. | Shipped |
27
+ | Structured result | `structuredContent` | Codemode scripts read the list as data. | Shipped |
28
+
29
+ ## Quickstart
30
+
31
+ ```bash
14
32
  pi --extension ./extensions/todo # load just this extension
15
33
  pi -e . # load the whole @gavin-hu/my-pi-agent package
16
- pi install ./ # install the package
34
+ pi install ./ # install the package for a user
17
35
  ```
18
36
 
19
- ## Tool rendering
20
-
21
- How the `todo` call and its result look in the transcript — the call line as a headline and the body indented under it:
22
-
23
- <img src="../../assets/tools/todo.png" alt="The todo tool's result: the call line and an indented body with the progress line and the task rows below it, each led by a check, spinner, or circle glyph" width="100%">
24
-
25
- ## What it does
26
-
27
- - Registers one model-callable tool, `todo`, that **replaces** the task list.
28
- An empty list clears it. The tool result is a compact summary — progress plus
29
- the current item — not an echo of the list the model just sent.
30
- - Each item has `content`, a `status` (`pending` | `in_progress` |
31
- `completed`), and an optional `activeForm` ("Running tests") shown while the
32
- item is in progress.
33
- - Shows a persistent widget above the editor while the list has unfinished
34
- work (hidden when empty or fully completed), and a scrollable `/todos`
35
- screen on demand. It is the bottom rail, below the [`goal`](../goal/) widget
36
- (`Goal / Todos`).
37
- - Stores the list in tool-result `details`, so it follows the active session
38
- branch and survives `/resume` and `/tree` — abandoned branches never leak
39
- into the current list, and stored lists are re-sanitized on replay.
40
- - Keeps the list honest: when a run is about to settle after a successful
41
- mutating tool call (`write`/`edit`/`bash`/`powershell`, or a tool declaring
42
- `readOnlyHint: false`) without a todo update, it injects one hidden reminder
43
- and forces a single continuation. A failed or blocked call changed nothing, so
44
- it never triggers one. It fires while the list has unfinished items, and —
45
- when no list exists at all — only after three such calls in one turn, at least
46
- one of them a file write (`write`/`edit`, or a tool declaring
47
- `readOnlyHint: false`), so repeated `bash` inspection is not mistaken for
48
- unplanned work. A list the model wrote or cleared during the turn is left
49
- alone. At most one reminder per user turn, and it is dropped on the next user
50
- turn, so a reader can never trigger it.
51
- - Declares an `outputSchema` and returns matching `structuredContent`
52
- (`{ todos, action, error? }`), so codemode/scripts can read the list as data.
53
-
54
- The widget is a one-line summary — progress plus the current item — so the
55
- active task is always visible without expanding the list:
56
-
57
- ```
58
- Todos · 1/3 · Writing the tests
37
+ ```text
38
+ /todos # show the list, and its progress
59
39
  ```
60
40
 
61
- `/todos` is the expanded view and keeps the model's list order (use it to see
62
- the plan as written).
41
+ The model calls `todo` with the complete list; the widget appears above the
42
+ editor while anything is unfinished.
63
43
 
64
- In the transcript, the call line carries progress only (`todo → 1/4`); the
65
- result keeps the `✓` / `◒` / `◌` rows, directly under the call line. The item
66
- text lives on the rows, so the call line never repeats it. Collapsed, it leads
67
- with active work; expanded, it keeps the submitted order:
44
+ ## Tool
68
45
 
69
- ```
70
- todo → 1/4
71
- ◒ Writing tests
72
- ◌ Ship it
73
- ◌ Write docs
74
- ✓ Write schema
75
- ```
46
+ <img src="../../assets/tools/todo.png" alt="The todo tool's result: the call line and an indented body with the progress line and the task rows below it, each led by a check, spinner, or circle glyph" width="100%">
76
47
 
77
- Progress lives on the call line, so the rows have no footer; the persistent
78
- widget and `/todos` still show the count. A capped collapsed rail ends with
79
- `… N more` and the `app.tools.expand` hint.
48
+ `todo` — `exposure: "direct"` (the default), `executionMode: "sequential"`,
49
+ read-write, closed-world, idempotent. It replaces the list; an empty list clears
50
+ it. The result is a compact summary — progress plus the current item — not an
51
+ echo of the list the model just sent.
80
52
 
81
- ## Tool
53
+ | Parameter | Type | Meaning |
54
+ |---|---|---|
55
+ | `todos` | array, required | The complete list. Pass `[]` to clear. |
56
+ | `todos[].content` | string | Imperative description, e.g. `"Write the parser tests"`. |
57
+ | `todos[].status` | `pending` \| `in_progress` \| `completed` | Where the item stands. |
58
+ | `todos[].activeForm` | string | Present-continuous label, required when the item is `in_progress` ("Running tests"). |
82
59
 
83
- | Field | Value |
84
- |---|---|
85
- | `name` | `todo` |
86
- | `todos` | The complete list. Pass `[]` to clear. |
87
- | `todos[].content` | Imperative description, e.g. `"Write the parser tests"`. |
88
- | `todos[].status` | `pending`, `in_progress`, or `completed`. |
89
- | `todos[].activeForm` | Present-continuous label, required when the item is `in_progress`. |
60
+ Result (`details` / `structuredContent`): `{ todos, action, error? }`.
90
61
 
91
62
  Validation rules (a violation leaves the list unchanged):
92
63
 
@@ -108,20 +79,45 @@ unknown `status` or a wrong argument type is rejected by Pi before `execute`
108
79
  runs, so the host's validation message is returned instead; the list is still
109
80
  unchanged.
110
81
 
111
- ```
82
+ ```text
112
83
  todo({ todos: [{ content: "Write the parser tests", status: "in_progress", activeForm: "Writing the parser tests" }] })
113
84
  todo({ todos: [] }) // clear
114
85
  ```
115
86
 
116
- ### `/todos` command
87
+ The widget is a one-line summary — progress plus the current item — so the
88
+ active task is always visible without expanding the list:
89
+
90
+ ```text
91
+ Todos · 1/3 · Writing the tests
92
+ ```
93
+
94
+ In the transcript, the call line carries progress only (`todo → 1/4`); the
95
+ result keeps the `✓` / `◒` / `◌` rows, directly under the call line. The item
96
+ text lives on the rows, so the call line never repeats it. Collapsed, it leads
97
+ with active work; expanded, it keeps the submitted order:
98
+
99
+ ```text
100
+ todo → 1/4
101
+ ◒ Writing tests
102
+ ◌ Ship it
103
+ ◌ Write docs
104
+ ✓ Write schema
105
+ ```
106
+
107
+ Progress lives on the call line, so the rows have no footer; the persistent
108
+ widget and `/todos` still show the count. A capped collapsed rail ends with
109
+ `… N more` and the `app.tools.expand` hint.
110
+
111
+ ## Commands
117
112
 
118
113
  | Command | What it does |
119
114
  |---|---|
120
115
  | `/todos` | Show the current list. An interactive, scrollable screen in `tui` mode; a notification elsewhere. |
121
116
 
122
- On the `/todos` screen: `↑`/`↓` or `j`/`k` scroll one row, `PgUp`/`PgDn` page,
123
- `g`/`G` or `Home`/`End` jump to the ends, and `Esc` (or `Ctrl-C`) closes. The
124
- window grows and shrinks with the terminal height, so the footer hint stays
117
+ `/todos` is the expanded view and keeps the model's list order, so it shows the
118
+ plan as written. On the screen: `↑`/`↓` or `j`/`k` scroll one row, `PgUp`/`PgDn`
119
+ page, `g`/`G` or `Home`/`End` jump to the ends, and `Esc` (or `Ctrl-C`) closes.
120
+ The window grows and shrinks with the terminal height, so the footer hint stays
125
121
  visible.
126
122
 
127
123
  The TUI label is status-dependent: an `in_progress` row shows its `activeForm`
@@ -135,13 +131,14 @@ The tool works in every mode. The persistent widget and the `/todos` screen
135
131
  require interactive (`tui`) mode; RPC and non-interactive runs simply get the
136
132
  tool result. The lag reminder is mode-independent, because it rides the agent
137
133
  boundary and not the terminal UI. While any dock screen is open (`/todos`,
138
- `/jobs`, `/rewind`, or the `question` questionnaire), the above-editor
139
- rails are hidden and return on close.
134
+ `/jobs`, `/rewind`, or the `question` questionnaire), the above-editor rails are
135
+ hidden and return on close.
140
136
 
141
137
  ## Configuration
142
138
 
143
- The widget presentation is configurable from the `todo` section of the shared
144
- `bytefly.json` (global document, then project document, project wins):
139
+ One key, in the `todo` section of the shared `bytefly.json` (global document,
140
+ then project document, project wins; resolution rules in
141
+ [`../configuration.md`](../configuration.md#todo--the-todo-widget)):
145
142
 
146
143
  | Key | Default | Meaning |
147
144
  |---|---|---|
@@ -201,7 +198,7 @@ depends on config.
201
198
  a goal update would sink the list; the list subscribes on `pi.events` and
202
199
  re-runs `setWidget` whenever a rail above it changes, keeping the stack
203
200
  `Goal / Todos`. It never announces, so the chain cannot ping-pong. See
204
- [`lib/rails.ts`](../../lib/rails.ts) and [`goal`](../goal/).
201
+ [`lib/rails.ts`](../../lib/rails.ts) and [`goal`](./goal.md).
205
202
  - **The reminder is a bounded nudge, not a nag.** A prompt guideline cannot
206
203
  guarantee a timely update, so the extension backs it with one forced
207
204
  continuation. `tool_execution_end` counts a successful call by an explicit
@@ -237,8 +234,7 @@ depends on config.
237
234
  | File | Responsibility |
238
235
  |---|---|
239
236
  | `index.ts` | Factory: wire the runtime, tool, command, reminder events, and session events. |
240
- | `types.ts` | `Todo`, `TodoStatus`, `TodoDetails`. |
241
- | `types.ts` | `Todo` and `TodoDetails`; re-exports `TodoStatus` / `TODO_STATUSES` from `lib/todo-state.ts`. |
237
+ | `types.ts` | `Todo`, `TodoDetails`; re-exports `TodoStatus` / `TODO_STATUSES` from `lib/todo-state.ts`. |
242
238
  | `schema.ts` | TypeBox parameters; the parser and limits live in `lib/todo-state.ts`. |
243
239
  | `state.ts` | Status queries over the replayed list (pure). |
244
240
  | `nudge.ts` | Lag detection: mutating and file-write classification, due check, reminder text (pure). |
@@ -248,3 +244,4 @@ depends on config.
248
244
  | `runtime.ts` | Session-scoped state, lag tracking, and widget synchronization. |
249
245
  | `tools.ts` | `todo` tool registration and rendering. |
250
246
  | `commands.ts` | `/todos`. |
247
+ | `tools.test.ts`, `runtime.test.ts`, `ui.test.ts` | The capability suites, declared in `test/suite-manifest.ts`. |
@@ -6,41 +6,56 @@ the TUI, reported to the model at the next turn, or — when a watcher opts into
6
6
  `wake` — raise one triggered turn as soon as the agent is idle, including right
7
7
  after a running turn settles.
8
8
 
9
- ```
9
+ ## Capabilities
10
+
11
+ Status records what a reader can rely on: **Shipped** is in a tagged release, **Unreleased** is complete in the current tree but not yet tagged, and **Partial** has one released part and one unreleased part. Rows are ordered by user value, highest first.
12
+
13
+ ### Watching
14
+
15
+ | Capability | How | What you get | Status |
16
+ |---|---|---|---|
17
+ | Tail a source | `watch add` | A file is followed from its current end unless `fromStart` is set, and a shrinking file is re-read from the start; a command's merged stdout/stderr is streamed. | Shipped |
18
+ | Line matching | `watch add` | An include pattern with an optional exclude list, case-insensitive unless `caseSensitive` is set. | Shipped |
19
+ | Match delivery | `watch add`, `watch matches` | Pending matches are reported to the model at the start of the next turn. | Shipped |
20
+ | Wake turn | `watch add`, the `wakeOnMatch` key | One triggered turn as soon as the agent is idle, including right after a running turn settles. | Shipped |
21
+ | Desktop alert | `my-pi-agent/watch-match`, `notify.enabled`, `notify.matchFound` | A wake match leaves the terminal as an OS notification. | Shipped |
22
+ | Live status | `watch add`, the `showStatus` key | A `⊚ N` running chip and a `! N` unreported-match chip, plus one alert per unreported burst. | Shipped |
23
+ | Command outcome | `watch status`, `watch list` | A command watcher reports its exit code or error, including the captured signal, instead of only "stopped". | Shipped |
24
+ | Liveness detail | `watch list`, `watch status` | A running watcher names its pid and elapsed time; list rows sort running watchers first, newest next. | Shipped |
25
+ | Transcript rendering | the `watch` transcript block | The outcome line, at most eight rows, and a `(ctrl+o to expand)` hint keep the transcript compact. | Shipped |
26
+ | Sanitized output | `watch matches`, `/watch matches` | Model- and remote-authored output is stripped of control characters before it reaches a terminal or a notification. | Shipped |
27
+
28
+ ### Reading and managing
29
+
30
+ | Capability | How | What you get | Status |
31
+ |---|---|---|---|
32
+ | Watcher inventory | `watch list`, `watch status`, `/watch list` | Each watcher's source, state, and line and match counts, singly or as a table. | Shipped |
33
+ | Matched lines | `watch matches`, `/watch matches` | The matching lines, sanitized and read from a bounded history. | Shipped |
34
+ | Raw output | `watch logs`, `/watch logs` | The merged output tail, for checking a pattern that matches nothing. | Shipped |
35
+ | Watcher screen | `/watch` | A dock screen with the watcher list, the focused watcher's detail, and its matches pane. | Shipped |
36
+ | Stopping and cleanup | `watch stop`, `watch clear`, `/watch stop`, `/watch clear` | A running watcher stops, stopped ones are removed, and `all` takes the running ones too. | Shipped |
37
+
38
+ ## Quickstart
39
+
40
+ ```bash
10
41
  pi --extension ./extensions/watch # load just this extension
11
42
  pi -e . # load the whole @gavin-hu/my-pi-agent package
12
43
  pi install ./ # install the package
13
44
  ```
14
45
 
15
- ## Tool rendering
46
+ ```text
47
+ watch add { "path": "server.log", "pattern": "ERROR|FATAL", "label": "server" }
48
+ /watch
49
+ ```
16
50
 
17
- How the `watch` call and its result look in the transcript — the call line as a headline and the body indented under it:
51
+ ## Tool
18
52
 
19
53
  <img src="../../assets/tools/watch.png" alt="The watch tool's result: the call line and an indented body with a running file watcher and an exited command watcher" width="100%">
20
54
 
21
- ## What it does
22
-
23
- - Registers one model-callable tool, `watch`, with actions `add`, `list`,
24
- `status`, `matches`, `logs`, `stop`, and `clear`.
25
- - Tails a text file by polling it, or a shell command by streaming its merged
26
- stdout/stderr. A file is followed from its current end unless `fromStart` is
27
- set; a shrinking file (rotation/truncation) is re-read from the start.
28
- - Matches with a regular expression and an optional exclude list,
29
- case-insensitive by default and case-sensitive with `caseSensitive: true`.
30
- - Shows a `⊚ N` running-watcher chip and a `! N` unreported-match chip, raises a
31
- `ctx.ui.notify` alert once per unreported burst, and offers a `/watch` screen.
32
- - Reports pending matches to the model at the start of the next turn, or wakes a
33
- turn for a watcher added with `wake: true` as soon as the agent is idle. A wake
34
- that arrives mid-turn is raised once that run settles. A wake also leaves the
35
- terminal: the
36
- match is announced on `my-pi-agent/watch-match`, which bytefly's
37
- [notify](../bytefly/README.md#notifications) capability turns into a desktop
38
- notification when `notify.enabled` and `notify.matchFound` are on.
39
- - Watchers are session-only: `shutdown` flushes a partial line, sends SIGTERM to
40
- every command's process tree (escalating to SIGKILL after a short grace), and
41
- clears the chips.
42
-
43
- ## Tool
55
+ `watch` — `exposure: "direct"` (the default), `executionMode: "sequential"`,
56
+ read-write, destructive, open-world, not idempotent. One tool with seven
57
+ actions; a watcher outlives the call, so `add` returns an id immediately and the
58
+ rest read it back.
44
59
 
45
60
  | Field | Value |
46
61
  |---|---|
@@ -104,15 +119,10 @@ capped at eight rows, minus the selection-marker column (`listRow`'s
104
119
  render the tail of the sanitized lines, with the hidden count noted above and
105
120
  `(ctrl+o to expand)`.
106
121
 
107
- ### Command: /watch
108
-
109
- <img src="../../assets/watch.png" alt="The /watch screen: a Watch title rule, a `2 watchers · 1 running · 3 matches` summary, a selected file watcher and a finished command watcher, the focused watcher's path and pattern with its line and match counts, and the key hints" width="100%">
122
+ ## Commands
110
123
 
111
- A bare `/watch` opens a dock screen listing watchers. `j`/`k` (and the wheel)
112
- move, `Enter`/`l` opens a watcher's matches, `x`/`K` stops a running watcher
113
- behind a `y/N` confirm, `d` clears stopped ones behind a `y/N` confirm, `Esc`
114
- closes, and `g`/`G` and PgUp/PgDn scroll the matches pane. Subcommands work in
115
- every mode:
124
+ `/watch` manages watchers in every mode; a bare `/watch` opens the screen in the
125
+ TUI and prints the list everywhere else.
116
126
 
117
127
  | Command | Effect |
118
128
  |---|---|
@@ -123,6 +133,13 @@ every mode:
123
133
  | `/watch stop <id>` | Stop a watcher. |
124
134
  | `/watch clear [all]` | Remove stopped watchers, or every watcher with `all`. |
125
135
 
136
+ <img src="../../assets/screens/watch.png" alt="The /watch screen: a Watch title rule, a `2 watchers · 1 running · 3 matches` summary, a selected file watcher and a finished command watcher, the focused watcher's path and pattern with its line and match counts, and the key hints" width="100%">
137
+
138
+ A bare `/watch` opens a dock screen listing watchers. `j`/`k` (and the wheel)
139
+ move, `Enter`/`l` opens a watcher's matches, `x`/`K` stops a running watcher
140
+ behind a `y/N` confirm, `d` clears stopped ones behind a `y/N` confirm, `Esc`
141
+ closes, and `g`/`G` and PgUp/PgDn scroll the matches pane.
142
+
126
143
  ## Behaviour by mode
127
144
 
128
145
  - **tui** — the status chips, the match alert, and the `/watch` dock screen.
@@ -132,8 +149,9 @@ every mode:
132
149
 
133
150
  ## Configuration
134
151
 
135
- The `watch` section of the shared `bytefly.json` (global then project). Every
136
- value is clamped; a missing or malformed document is ignored.
152
+ The `watch` section of the shared `bytefly.json`; resolution rules in
153
+ [`../configuration.md`](../configuration.md#watch--file-and-command-watchers).
154
+ Every value is clamped; a missing or malformed document is ignored.
137
155
 
138
156
  | Key | Default | Meaning |
139
157
  |---|---|---|