@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.
- package/README.md +52 -44
- package/README.zh-CN.md +46 -41
- package/docs/extensions/README.md +34 -0
- package/docs/extensions/board.md +344 -0
- package/{extensions/bytefly/README.md → docs/extensions/bytefly.md} +446 -404
- package/{extensions/check/README.md → docs/extensions/check.md} +83 -68
- package/{extensions/context/README.md → docs/extensions/context.md} +40 -23
- package/docs/extensions/file-browse.md +554 -0
- package/{extensions/file-open/README.md → docs/extensions/file-open.md} +31 -23
- package/{extensions/file-read/README.md → docs/extensions/file-read.md} +47 -55
- package/{extensions/goal/README.md → docs/extensions/goal.md} +115 -131
- package/{extensions/job/README.md → docs/extensions/job.md} +61 -50
- package/{extensions/memory/README.md → docs/extensions/memory.md} +71 -60
- package/{extensions/plan/README.md → docs/extensions/plan.md} +390 -317
- package/{extensions/question/README.md → docs/extensions/question.md} +54 -31
- package/{extensions/rewind/README.md → docs/extensions/rewind.md} +115 -122
- package/docs/extensions/session-bus.md +285 -0
- package/{extensions/subagent/README.md → docs/extensions/subagent.md} +165 -118
- package/docs/extensions/team.md +226 -0
- package/{extensions/todo/README.md → docs/extensions/todo.md} +85 -88
- package/{extensions/watch/README.md → docs/extensions/watch.md} +54 -36
- package/{extensions/web-access/README.md → docs/extensions/web-access.md} +93 -50
- package/{extensions/wechat/README.md → docs/extensions/wechat.md} +258 -162
- package/{extensions/worktree/README.md → docs/extensions/worktree.md} +115 -72
- package/extensions/board/commands.ts +87 -0
- package/extensions/board/config.ts +36 -0
- package/extensions/board/format.ts +72 -0
- package/extensions/board/index.ts +40 -0
- package/extensions/board/input.ts +54 -0
- package/extensions/board/join.ts +42 -0
- package/extensions/board/page.ts +312 -0
- package/extensions/board/result.ts +99 -0
- package/extensions/board/rows.ts +48 -0
- package/extensions/board/schema.ts +75 -0
- package/extensions/board/seat.ts +29 -0
- package/extensions/board/snapshot.ts +41 -0
- package/extensions/board/tools.ts +279 -0
- package/extensions/board/transport/router.ts +172 -0
- package/extensions/board/transport/server.ts +149 -0
- package/extensions/board/types.ts +54 -0
- package/extensions/board/view/actions.ts +306 -0
- package/extensions/board/view/client.ts +41 -0
- package/extensions/board/view/keys.ts +121 -0
- package/extensions/board/view/live.ts +397 -0
- package/extensions/board/view/page.ts +158 -0
- package/extensions/board/view/render.ts +496 -0
- package/extensions/board/view/styles.ts +211 -0
- package/extensions/board/view/team.ts +305 -0
- package/extensions/board/writes.ts +99 -0
- package/extensions/bytefly/panel/commands.ts +40 -7
- package/extensions/bytefly/panel/settings-runtime.ts +0 -6
- package/extensions/bytefly/panel/settings.ts +3 -20
- package/extensions/bytefly/panel/tui/settings-view.ts +1 -1
- package/extensions/bytefly/picker/discovery.ts +0 -2
- package/extensions/bytefly/picker/types.ts +0 -2
- package/extensions/bytefly/update/check.ts +39 -0
- package/extensions/bytefly/update/format.ts +15 -0
- package/extensions/bytefly/update/register.ts +73 -34
- package/extensions/bytefly/update/state.ts +6 -3
- package/extensions/context/tui.ts +1 -1
- package/extensions/file-browse/{config.ts → config/config.ts} +1 -1
- package/extensions/file-browse/{files.ts → files/files.ts} +1 -14
- package/extensions/file-browse/{paths.ts → files/paths.ts} +1 -1
- package/extensions/file-browse/{git.ts → git/git.ts} +10 -3
- package/extensions/file-browse/index.ts +4 -4
- package/extensions/file-browse/{live.ts → live/live.ts} +20 -69
- package/extensions/file-browse/{router.ts → transport/router.ts} +21 -15
- package/extensions/file-browse/transport/server.ts +110 -0
- package/extensions/file-browse/{chrome.ts → view/chrome.ts} +4 -4
- package/extensions/file-browse/{client.ts → view/client.ts} +1 -1
- package/extensions/file-browse/{diff-html.ts → view/diff-html.ts} +3 -3
- package/extensions/file-browse/{html.ts → view/html.ts} +14 -12
- package/extensions/file-browse/{icons.ts → view/icons.ts} +1 -1
- package/extensions/file-browse/{markdown.ts → view/markdown.ts} +7 -7
- package/extensions/file-browse/{raw-html.ts → view/raw-html.ts} +1 -1
- package/extensions/file-browse/{styles.ts → view/styles.ts} +29 -5
- package/extensions/file-browse/{tree.ts → view/tree.ts} +1 -1
- package/extensions/file-browse/{urls.ts → view/urls.ts} +1 -1
- package/extensions/file-read/schema.ts +2 -1
- package/extensions/memory/store.ts +12 -5
- package/extensions/plan/blocks.ts +23 -2
- package/extensions/plan/commands.ts +21 -16
- package/extensions/plan/feedback.ts +147 -38
- package/extensions/plan/index.ts +6 -4
- package/extensions/plan/plan-index.ts +43 -9
- package/extensions/plan/plan-text.ts +33 -23
- package/extensions/plan/plans.ts +8 -11
- package/extensions/plan/policy.ts +20 -12
- package/extensions/plan/review.ts +3 -27
- package/extensions/plan/runtime.ts +21 -9
- package/extensions/plan/sections.ts +112 -0
- package/extensions/plan/steps.ts +3 -22
- package/extensions/plan/tools/exit.ts +11 -95
- package/extensions/plan/tui.ts +5 -34
- package/extensions/plan/types.ts +0 -8
- package/extensions/plan/web-client.ts +1 -1
- package/extensions/plan/web-page.ts +0 -7
- package/extensions/plan/web-router.ts +3 -57
- package/extensions/plan/web-share-session.ts +24 -30
- package/extensions/plan/web-streams.ts +14 -14
- package/extensions/plan/web-tokens.ts +2 -2
- package/extensions/plan/web.ts +66 -120
- package/extensions/rewind/{commands.ts → commands/commands.ts} +5 -5
- package/extensions/rewind/{review.ts → commands/review.ts} +13 -13
- package/extensions/rewind/{rewind.ts → commands/rewind.ts} +6 -6
- package/extensions/rewind/{config.ts → config/config.ts} +1 -1
- package/extensions/rewind/index.ts +3 -3
- package/extensions/rewind/{policy.ts → state/policy.ts} +4 -4
- package/extensions/rewind/{runtime.ts → state/runtime.ts} +11 -11
- package/extensions/rewind/{snapshot-run.ts → state/snapshot-run.ts} +1 -1
- package/extensions/rewind/{timeline.ts → state/timeline.ts} +2 -2
- package/extensions/rewind/{format.ts → text/format.ts} +3 -3
- package/extensions/rewind/{git.ts → transport/git.ts} +2 -2
- package/extensions/rewind/{restore.ts → transport/restore.ts} +2 -2
- package/extensions/rewind/{snapshot.ts → transport/snapshot.ts} +1 -1
- package/extensions/rewind/{store.ts → transport/store.ts} +1 -1
- package/extensions/rewind/{checklist.ts → view/checklist.ts} +6 -6
- package/extensions/rewind/{hunk-picker.ts → view/hunk-picker.ts} +8 -8
- package/extensions/rewind/{path-picker.ts → view/path-picker.ts} +7 -7
- package/extensions/rewind/{review-tui.ts → view/review-tui.ts} +11 -11
- package/extensions/rewind/{rewind-tui.ts → view/rewind-tui.ts} +17 -11
- package/extensions/rewind/{status.ts → view/status.ts} +4 -4
- package/extensions/session-bus/commands.ts +17 -69
- package/extensions/session-bus/format.ts +22 -45
- package/extensions/session-bus/index.ts +13 -7
- package/extensions/session-bus/join-request.ts +40 -0
- package/extensions/session-bus/mail-watch.ts +134 -0
- package/extensions/session-bus/notice.ts +32 -4
- package/{lib/claims → extensions/session-bus}/overlap.ts +11 -18
- package/extensions/session-bus/protocol.ts +19 -49
- package/extensions/session-bus/resolve.ts +67 -0
- package/extensions/session-bus/result.ts +16 -51
- package/extensions/session-bus/rows.ts +4 -2
- package/extensions/session-bus/runtime.ts +129 -238
- package/extensions/session-bus/store.ts +42 -6
- package/extensions/session-bus/tools.ts +31 -66
- package/extensions/session-bus/tui.ts +4 -4
- package/extensions/session-bus/types.ts +13 -13
- package/extensions/subagent/agents.ts +9 -8
- package/extensions/subagent/invocation.ts +4 -2
- package/extensions/team/commands.ts +112 -0
- package/extensions/team/dispatch.ts +133 -0
- package/extensions/team/format.ts +105 -0
- package/extensions/team/index.ts +33 -0
- package/extensions/team/schema.ts +29 -0
- package/extensions/team/tools.ts +246 -0
- package/extensions/team/types.ts +48 -0
- package/extensions/web-access/fetch/page.ts +4 -1
- package/extensions/web-access/fetch/structured.ts +447 -0
- package/extensions/web-access/fetch/tool.ts +2 -1
- package/extensions/web-access/search/tool.ts +2 -1
- package/extensions/wechat/bridge.ts +76 -92
- package/extensions/wechat/{commands.ts → commands/commands.ts} +26 -18
- package/extensions/wechat/{doctor.ts → commands/doctor.ts} +5 -5
- package/extensions/wechat/{history.ts → commands/history.ts} +1 -1
- package/extensions/wechat/{login.ts → commands/login.ts} +3 -3
- package/extensions/wechat/{slash.ts → commands/slash.ts} +38 -2
- package/extensions/wechat/dialog/channels.ts +136 -0
- package/extensions/wechat/{interaction.ts → dialog/interaction.ts} +37 -37
- package/extensions/wechat/{prompt.ts → dialog/prompt.ts} +2 -2
- package/extensions/wechat/{remote-ui.ts → dialog/remote-ui.ts} +1 -1
- package/extensions/wechat/index.ts +10 -10
- package/extensions/wechat/{admission.ts → peer/admission.ts} +2 -2
- package/extensions/wechat/{peer-session.ts → peer/peer-session.ts} +2 -2
- package/extensions/wechat/{peer-worker.ts → peer/peer-worker.ts} +100 -22
- package/extensions/wechat/{presence.ts → peer/presence.ts} +8 -7
- package/extensions/wechat/{reply-pipeline.ts → peer/reply-pipeline.ts} +1 -1
- package/extensions/wechat/{credentials.ts → state/credentials.ts} +2 -2
- package/extensions/wechat/{state.ts → state/state.ts} +14 -2
- package/extensions/wechat/{format.ts → text/format.ts} +3 -3
- package/extensions/wechat/{notices.ts → text/notices.ts} +8 -0
- package/extensions/wechat/tools.ts +29 -12
- package/extensions/wechat/{client.ts → transport/client.ts} +26 -2
- package/extensions/wechat/{deliver.ts → transport/deliver.ts} +72 -28
- package/extensions/wechat/{inbound.ts → transport/inbound.ts} +5 -5
- package/extensions/wechat/{media.ts → transport/media.ts} +11 -5
- package/extensions/wechat/transport/outbound.ts +142 -0
- package/extensions/wechat/{poll.ts → transport/poll.ts} +3 -3
- package/extensions/wechat/types.ts +10 -1
- package/extensions/wechat/{login-screen.ts → view/login-screen.ts} +4 -4
- package/extensions/wechat/{status-screen.ts → view/status-screen.ts} +31 -26
- package/extensions/worktree/guard.ts +3 -5
- package/lib/README.md +8 -6
- package/lib/board/card.ts +290 -0
- package/lib/board/links.ts +117 -0
- package/lib/board/page-pointer.ts +256 -0
- package/lib/board/round-text.ts +72 -0
- package/lib/board/round.ts +89 -0
- package/lib/board/rules.ts +304 -0
- package/lib/board/session.ts +105 -0
- package/lib/board/store.ts +310 -0
- package/lib/board/view.ts +150 -0
- package/lib/errors.ts +12 -4
- package/lib/format.ts +13 -0
- package/lib/http/guard.ts +112 -0
- package/lib/http/index.ts +16 -5
- package/lib/http/serve.ts +234 -0
- package/lib/http/server.ts +12 -10
- package/lib/http/sink.ts +110 -0
- package/lib/http/token.ts +38 -0
- package/lib/policy.ts +12 -2
- package/lib/requirements.ts +6 -7
- package/lib/session-label.ts +64 -0
- package/lib/session-roster.ts +130 -0
- package/lib/session-tool.ts +184 -0
- package/lib/tool-names.ts +25 -2
- package/lib/tui/glyphs.ts +2 -0
- package/lib/web/README.md +5 -2
- package/lib/web/components.ts +5 -0
- package/lib/web/runtime.ts +7 -1
- package/lib/web/tokens.ts +25 -2
- package/package.json +26 -14
- package/skills/next/SKILL.md +4 -3
- package/skills/project-docs/SKILL.md +133 -0
- package/skills/project-docs/references/audit.md +131 -0
- package/skills/project-docs/references/changelog.md +105 -0
- package/skills/project-docs/references/discovery.md +127 -0
- package/skills/project-docs/references/readme.md +80 -0
- package/skills/project-docs/references/release.md +84 -0
- package/skills/project-docs/references/roadmap.md +93 -0
- package/skills/project-docs/scripts/state.ts +316 -0
- package/skills/refactor/SKILL.md +100 -125
- package/skills/refactor/references/handoff.md +35 -0
- package/skills/refactor/references/signals.md +54 -0
- package/skills/refactor/scripts/inventory.ts +587 -0
- package/extensions/file-browse/README.md +0 -506
- package/extensions/file-browse/server.ts +0 -141
- package/extensions/plan/split.ts +0 -87
- package/extensions/session-bus/README.md +0 -247
- package/extensions/session-bus/team.ts +0 -102
- package/extensions/wechat/outbound.ts +0 -100
- package/lib/plans/sections.ts +0 -172
- package/lib/plans/setting.ts +0 -52
- package/lib/plans/split.ts +0 -215
- package/lib/plans/store.ts +0 -19
- package/lib/plans/team.ts +0 -151
- package/skills/changelog/SKILL.md +0 -148
- package/skills/parallel-dev/SKILL.md +0 -150
- /package/extensions/file-browse/{rev.ts → live/rev.ts} +0 -0
- /package/extensions/file-browse/{diff.ts → text/diff.ts} +0 -0
- /package/extensions/file-browse/{fuzzy.ts → text/fuzzy.ts} +0 -0
- /package/extensions/file-browse/{live-client.ts → view/live-client.ts} +0 -0
- /package/extensions/file-browse/{palette.ts → view/palette.ts} +0 -0
- /package/extensions/rewind/{patch.ts → text/patch.ts} +0 -0
- /package/extensions/rewind/{paths.ts → text/paths.ts} +0 -0
- /package/extensions/rewind/{queue.ts → transport/queue.ts} +0 -0
- /package/extensions/rewind/{temp-index.ts → transport/temp-index.ts} +0 -0
- /package/extensions/wechat/{lock.ts → state/lock.ts} +0 -0
- /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
|
|
4
|
-
model sends the complete list on every call
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
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
|
-
|
|
62
|
-
|
|
41
|
+
The model calls `todo` with the complete list; the widget appears above the
|
|
42
|
+
editor while anything is unfinished.
|
|
63
43
|
|
|
64
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
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
|
-
|
|
144
|
-
|
|
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`](
|
|
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
|
|
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
|
-
|
|
46
|
+
```text
|
|
47
|
+
watch add { "path": "server.log", "pattern": "ERROR|FATAL", "label": "server" }
|
|
48
|
+
/watch
|
|
49
|
+
```
|
|
16
50
|
|
|
17
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
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
|
-
|
|
112
|
-
|
|
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
|
|
136
|
-
|
|
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
|
|---|---|---|
|